jquery.fancybox.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983
  1. /*!
  2. * fancyBox - jQuery Plugin
  3. * version: 2.1.4 (Thu, 10 Jan 2013)
  4. * @requires jQuery v1.6 or later
  5. *
  6. * Examples at http://fancyapps.com/fancybox/
  7. * License: www.fancyapps.com/fancybox/#license
  8. *
  9. * Copyright 2012 Janis Skarnelis - janis@fancyapps.com
  10. *
  11. */
  12. (function (window, document, $, undefined) {
  13. "use strict";
  14. var W = $(window),
  15. D = $(document),
  16. F = $.fancybox = function () {
  17. F.open.apply( this, arguments );
  18. },
  19. IE = navigator.userAgent.match(/msie/),
  20. didUpdate = null,
  21. isTouch = document.createTouch !== undefined,
  22. isQuery = function(obj) {
  23. return obj && obj.hasOwnProperty && obj instanceof $;
  24. },
  25. isString = function(str) {
  26. return str && $.type(str) === "string";
  27. },
  28. isPercentage = function(str) {
  29. return isString(str) && str.indexOf('%') > 0;
  30. },
  31. isScrollable = function(el) {
  32. return (el && !(el.style.overflow && el.style.overflow === 'hidden') && ((el.clientWidth && el.scrollWidth > el.clientWidth) || (el.clientHeight && el.scrollHeight > el.clientHeight)));
  33. },
  34. getScalar = function(orig, dim) {
  35. var value = parseInt(orig, 10) || 0;
  36. if (dim && isPercentage(orig)) {
  37. value = F.getViewport()[ dim ] / 100 * value;
  38. }
  39. return Math.ceil(value);
  40. },
  41. getValue = function(value, dim) {
  42. return getScalar(value, dim) + 'px';
  43. };
  44. $.extend(F, {
  45. // The current version of fancyBox
  46. version: '2.1.4',
  47. defaults: {
  48. padding : 15,
  49. margin : 20,
  50. width : 800,
  51. height : 600,
  52. minWidth : 100,
  53. minHeight : 100,
  54. maxWidth : 9999,
  55. maxHeight : 9999,
  56. autoSize : true,
  57. autoHeight : false,
  58. autoWidth : false,
  59. autoResize : true,
  60. autoCenter : !isTouch,
  61. fitToView : true,
  62. aspectRatio : false,
  63. topRatio : 0.5,
  64. leftRatio : 0.5,
  65. scrolling : 'auto', // 'auto', 'yes' or 'no'
  66. wrapCSS : '',
  67. arrows : true,
  68. closeBtn : true,
  69. closeClick : false,
  70. nextClick : false,
  71. mouseWheel : true,
  72. autoPlay : false,
  73. playSpeed : 3000,
  74. preload : 3,
  75. modal : false,
  76. loop : true,
  77. ajax : {
  78. dataType : 'html',
  79. headers : { 'X-fancyBox': true }
  80. },
  81. iframe : {
  82. scrolling : 'auto',
  83. preload : true
  84. },
  85. swf : {
  86. wmode: 'transparent',
  87. allowfullscreen : 'true',
  88. allowscriptaccess : 'always'
  89. },
  90. keys : {
  91. next : {
  92. 13 : 'left', // enter
  93. 34 : 'up', // page down
  94. 39 : 'left', // right arrow
  95. 40 : 'up' // down arrow
  96. },
  97. prev : {
  98. 8 : 'right', // backspace
  99. 33 : 'down', // page up
  100. 37 : 'right', // left arrow
  101. 38 : 'down' // up arrow
  102. },
  103. close : [27], // escape key
  104. play : [32], // space - start/stop slideshow
  105. toggle : [70] // letter "f" - toggle fullscreen
  106. },
  107. direction : {
  108. next : 'left',
  109. prev : 'right'
  110. },
  111. scrollOutside : true,
  112. // Override some properties
  113. index : 0,
  114. type : null,
  115. href : null,
  116. content : null,
  117. title : null,
  118. // HTML templates
  119. tpl: {
  120. wrap : '<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div></div></div>',
  121. image : '<img class="fancybox-image" src="{href}" alt="" />',
  122. iframe : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen' + (IE ? ' allowtransparency="true"' : '') + '></iframe>',
  123. error : '<p class="fancybox-error">The requested content cannot be loaded.<br/>Please try again later.</p>',
  124. closeBtn : '<a title="Close" class="fancybox-item fancybox-close" href="javascript:;"></a>',
  125. next : '<a title="Next" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>',
  126. prev : '<a title="Previous" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>'
  127. },
  128. // Properties for each animation type
  129. // Opening fancyBox
  130. openEffect : 'fade', // 'elastic', 'fade' or 'none'
  131. openSpeed : 250,
  132. openEasing : 'swing',
  133. openOpacity : true,
  134. openMethod : 'zoomIn',
  135. // Closing fancyBox
  136. closeEffect : 'fade', // 'elastic', 'fade' or 'none'
  137. closeSpeed : 250,
  138. closeEasing : 'swing',
  139. closeOpacity : true,
  140. closeMethod : 'zoomOut',
  141. // Changing next gallery item
  142. nextEffect : 'elastic', // 'elastic', 'fade' or 'none'
  143. nextSpeed : 250,
  144. nextEasing : 'swing',
  145. nextMethod : 'changeIn',
  146. // Changing previous gallery item
  147. prevEffect : 'elastic', // 'elastic', 'fade' or 'none'
  148. prevSpeed : 250,
  149. prevEasing : 'swing',
  150. prevMethod : 'changeOut',
  151. // Enable default helpers
  152. helpers : {
  153. overlay : true,
  154. title : true
  155. },
  156. // Callbacks
  157. onCancel : $.noop, // If canceling
  158. beforeLoad : $.noop, // Before loading
  159. afterLoad : $.noop, // After loading
  160. beforeShow : $.noop, // Before changing in current item
  161. afterShow : $.noop, // After opening
  162. beforeChange : $.noop, // Before changing gallery item
  163. beforeClose : $.noop, // Before closing
  164. afterClose : $.noop // After closing
  165. },
  166. //Current state
  167. group : {}, // Selected group
  168. opts : {}, // Group options
  169. previous : null, // Previous element
  170. coming : null, // Element being loaded
  171. current : null, // Currently loaded element
  172. isActive : false, // Is activated
  173. isOpen : false, // Is currently open
  174. isOpened : false, // Have been fully opened at least once
  175. wrap : null,
  176. skin : null,
  177. outer : null,
  178. inner : null,
  179. player : {
  180. timer : null,
  181. isActive : false
  182. },
  183. // Loaders
  184. ajaxLoad : null,
  185. imgPreload : null,
  186. // Some collections
  187. transitions : {},
  188. helpers : {},
  189. /*
  190. * Static methods
  191. */
  192. open: function (group, opts) {
  193. if (!group) {
  194. return;
  195. }
  196. if (!$.isPlainObject(opts)) {
  197. opts = {};
  198. }
  199. // Close if already active
  200. if (false === F.close(true)) {
  201. return;
  202. }
  203. // Normalize group
  204. if (!$.isArray(group)) {
  205. group = isQuery(group) ? $(group).get() : [group];
  206. }
  207. // Recheck if the type of each element is `object` and set content type (image, ajax, etc)
  208. $.each(group, function(i, element) {
  209. var obj = {},
  210. href,
  211. title,
  212. content,
  213. type,
  214. rez,
  215. hrefParts,
  216. selector;
  217. if ($.type(element) === "object") {
  218. // Check if is DOM element
  219. if (element.nodeType) {
  220. element = $(element);
  221. }
  222. if (isQuery(element)) {
  223. obj = {
  224. href : element.data('fancybox-href') || element.attr('href'),
  225. title : element.data('fancybox-title') || element.attr('title'),
  226. isDom : true,
  227. element : element
  228. };
  229. if ($.metadata) {
  230. $.extend(true, obj, element.metadata());
  231. }
  232. } else {
  233. obj = element;
  234. }
  235. }
  236. href = opts.href || obj.href || (isString(element) ? element : null);
  237. title = opts.title !== undefined ? opts.title : obj.title || '';
  238. content = opts.content || obj.content;
  239. type = content ? 'html' : (opts.type || obj.type);
  240. if (!type && obj.isDom) {
  241. type = element.data('fancybox-type');
  242. if (!type) {
  243. rez = element.prop('class').match(/fancybox\.(\w+)/);
  244. type = rez ? rez[1] : null;
  245. }
  246. }
  247. if (isString(href)) {
  248. // Try to guess the content type
  249. if (!type) {
  250. if (F.isImage(href)) {
  251. type = 'image';
  252. } else if (F.isSWF(href)) {
  253. type = 'swf';
  254. } else if (href.charAt(0) === '#') {
  255. type = 'inline';
  256. } else if (isString(element)) {
  257. type = 'html';
  258. content = element;
  259. }
  260. }
  261. // Split url into two pieces with source url and content selector, e.g,
  262. // "/mypage.html #my_id" will load "/mypage.html" and display element having id "my_id"
  263. if (type === 'ajax') {
  264. hrefParts = href.split(/\s+/, 2);
  265. href = hrefParts.shift();
  266. selector = hrefParts.shift();
  267. }
  268. }
  269. if (!content) {
  270. if (type === 'inline') {
  271. if (href) {
  272. content = $( isString(href) ? href.replace(/.*(?=#[^\s]+$)/, '') : href ); //strip for ie7
  273. } else if (obj.isDom) {
  274. content = element;
  275. }
  276. } else if (type === 'html') {
  277. content = href;
  278. } else if (!type && !href && obj.isDom) {
  279. type = 'inline';
  280. content = element;
  281. }
  282. }
  283. $.extend(obj, {
  284. href : href,
  285. type : type,
  286. content : content,
  287. title : title,
  288. selector : selector
  289. });
  290. group[ i ] = obj;
  291. });
  292. // Extend the defaults
  293. F.opts = $.extend(true, {}, F.defaults, opts);
  294. // All options are merged recursive except keys
  295. if (opts.keys !== undefined) {
  296. F.opts.keys = opts.keys ? $.extend({}, F.defaults.keys, opts.keys) : false;
  297. }
  298. F.group = group;
  299. return F._start(F.opts.index);
  300. },
  301. // Cancel image loading or abort ajax request
  302. cancel: function () {
  303. var coming = F.coming;
  304. if (!coming || false === F.trigger('onCancel')) {
  305. return;
  306. }
  307. F.hideLoading();
  308. if (F.ajaxLoad) {
  309. F.ajaxLoad.abort();
  310. }
  311. F.ajaxLoad = null;
  312. if (F.imgPreload) {
  313. F.imgPreload.onload = F.imgPreload.onerror = null;
  314. }
  315. if (coming.wrap) {
  316. coming.wrap.stop(true, true).trigger('onReset').remove();
  317. }
  318. F.coming = null;
  319. // If the first item has been canceled, then clear everything
  320. if (!F.current) {
  321. F._afterZoomOut( coming );
  322. }
  323. },
  324. // Start closing animation if is open; remove immediately if opening/closing
  325. close: function (event) {
  326. F.cancel();
  327. if (false === F.trigger('beforeClose')) {
  328. return;
  329. }
  330. F.unbindEvents();
  331. if (!F.isActive) {
  332. return;
  333. }
  334. if (!F.isOpen || event === true) {
  335. $('.fancybox-wrap').stop(true).trigger('onReset').remove();
  336. F._afterZoomOut();
  337. } else {
  338. F.isOpen = F.isOpened = false;
  339. F.isClosing = true;
  340. $('.fancybox-item, .fancybox-nav').remove();
  341. F.wrap.stop(true, true).removeClass('fancybox-opened');
  342. F.transitions[ F.current.closeMethod ]();
  343. }
  344. },
  345. // Manage slideshow:
  346. // $.fancybox.play(); - toggle slideshow
  347. // $.fancybox.play( true ); - start
  348. // $.fancybox.play( false ); - stop
  349. play: function ( action ) {
  350. var clear = function () {
  351. clearTimeout(F.player.timer);
  352. },
  353. set = function () {
  354. clear();
  355. if (F.current && F.player.isActive) {
  356. F.player.timer = setTimeout(F.next, F.current.playSpeed);
  357. }
  358. },
  359. stop = function () {
  360. clear();
  361. $('body').unbind('.player');
  362. F.player.isActive = false;
  363. F.trigger('onPlayEnd');
  364. },
  365. start = function () {
  366. if (F.current && (F.current.loop || F.current.index < F.group.length - 1)) {
  367. F.player.isActive = true;
  368. $('body').bind({
  369. 'afterShow.player onUpdate.player' : set,
  370. 'onCancel.player beforeClose.player' : stop,
  371. 'beforeLoad.player' : clear
  372. });
  373. set();
  374. F.trigger('onPlayStart');
  375. }
  376. };
  377. if (action === true || (!F.player.isActive && action !== false)) {
  378. start();
  379. } else {
  380. stop();
  381. }
  382. },
  383. // Navigate to next gallery item
  384. next: function ( direction ) {
  385. var current = F.current;
  386. if (current) {
  387. if (!isString(direction)) {
  388. direction = current.direction.next;
  389. }
  390. F.jumpto(current.index + 1, direction, 'next');
  391. }
  392. },
  393. // Navigate to previous gallery item
  394. prev: function ( direction ) {
  395. var current = F.current;
  396. if (current) {
  397. if (!isString(direction)) {
  398. direction = current.direction.prev;
  399. }
  400. F.jumpto(current.index - 1, direction, 'prev');
  401. }
  402. },
  403. // Navigate to gallery item by index
  404. jumpto: function ( index, direction, router ) {
  405. var current = F.current;
  406. if (!current) {
  407. return;
  408. }
  409. index = getScalar(index);
  410. F.direction = direction || current.direction[ (index >= current.index ? 'next' : 'prev') ];
  411. F.router = router || 'jumpto';
  412. if (current.loop) {
  413. if (index < 0) {
  414. index = current.group.length + (index % current.group.length);
  415. }
  416. index = index % current.group.length;
  417. }
  418. if (current.group[ index ] !== undefined) {
  419. F.cancel();
  420. F._start(index);
  421. }
  422. },
  423. // Center inside viewport and toggle position type to fixed or absolute if needed
  424. reposition: function (e, onlyAbsolute) {
  425. var current = F.current,
  426. wrap = current ? current.wrap : null,
  427. pos;
  428. if (wrap) {
  429. pos = F._getPosition(onlyAbsolute);
  430. if (e && e.type === 'scroll') {
  431. delete pos.position;
  432. wrap.stop(true, true).animate(pos, 200);
  433. } else {
  434. wrap.css(pos);
  435. current.pos = $.extend({}, current.dim, pos);
  436. }
  437. }
  438. },
  439. update: function (e) {
  440. var type = (e && e.type),
  441. anyway = !type || type === 'orientationchange';
  442. if (anyway) {
  443. clearTimeout(didUpdate);
  444. didUpdate = null;
  445. }
  446. if (!F.isOpen || didUpdate) {
  447. return;
  448. }
  449. didUpdate = setTimeout(function() {
  450. var current = F.current;
  451. if (!current || F.isClosing) {
  452. return;
  453. }
  454. F.wrap.removeClass('fancybox-tmp');
  455. if (anyway || type === 'load' || (type === 'resize' && current.autoResize)) {
  456. F._setDimension();
  457. }
  458. if (!(type === 'scroll' && current.canShrink)) {
  459. F.reposition(e);
  460. }
  461. F.trigger('onUpdate');
  462. didUpdate = null;
  463. }, (anyway && !isTouch ? 0 : 300));
  464. },
  465. // Shrink content to fit inside viewport or restore if resized
  466. toggle: function ( action ) {
  467. if (F.isOpen) {
  468. F.current.fitToView = $.type(action) === "boolean" ? action : !F.current.fitToView;
  469. // Help browser to restore document dimensions
  470. if (isTouch) {
  471. F.wrap.removeAttr('style').addClass('fancybox-tmp');
  472. F.trigger('onUpdate');
  473. }
  474. F.update();
  475. }
  476. },
  477. hideLoading: function () {
  478. D.unbind('.loading');
  479. $('#fancybox-loading').remove();
  480. },
  481. showLoading: function () {
  482. var el, viewport;
  483. F.hideLoading();
  484. el = $('<div id="fancybox-loading"><div></div></div>').click(F.cancel).appendTo('body');
  485. // If user will press the escape-button, the request will be canceled
  486. D.bind('keydown.loading', function(e) {
  487. if ((e.which || e.keyCode) === 27) {
  488. e.preventDefault();
  489. F.cancel();
  490. }
  491. });
  492. if (!F.defaults.fixed) {
  493. viewport = F.getViewport();
  494. el.css({
  495. position : 'absolute',
  496. top : (viewport.h * 0.5) + viewport.y,
  497. left : (viewport.w * 0.5) + viewport.x
  498. });
  499. }
  500. },
  501. getViewport: function () {
  502. var locked = (F.current && F.current.locked) || false,
  503. rez = {
  504. x: W.scrollLeft(),
  505. y: W.scrollTop()
  506. };
  507. if (locked) {
  508. rez.w = locked[0].clientWidth;
  509. rez.h = locked[0].clientHeight;
  510. } else {
  511. // See http://bugs.jquery.com/ticket/6724
  512. rez.w = isTouch && window.innerWidth ? window.innerWidth : W.width();
  513. rez.h = isTouch && window.innerHeight ? window.innerHeight : W.height();
  514. }
  515. return rez;
  516. },
  517. // Unbind the keyboard / clicking actions
  518. unbindEvents: function () {
  519. if (F.wrap && isQuery(F.wrap)) {
  520. F.wrap.unbind('.fb');
  521. }
  522. D.unbind('.fb');
  523. W.unbind('.fb');
  524. },
  525. bindEvents: function () {
  526. var current = F.current,
  527. keys;
  528. if (!current) {
  529. return;
  530. }
  531. // Changing document height on iOS devices triggers a 'resize' event,
  532. // that can change document height... repeating infinitely
  533. W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb') + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update);
  534. keys = current.keys;
  535. if (keys) {
  536. D.bind('keydown.fb', function (e) {
  537. var code = e.which || e.keyCode,
  538. target = e.target || e.srcElement;
  539. // Skip esc key if loading, because showLoading will cancel preloading
  540. if (code === 27 && F.coming) {
  541. return false;
  542. }
  543. // Ignore key combinations and key events within form elements
  544. if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !(target && (target.type || $(target).is('[contenteditable]')))) {
  545. $.each(keys, function(i, val) {
  546. if (current.group.length > 1 && val[ code ] !== undefined) {
  547. F[ i ]( val[ code ] );
  548. e.preventDefault();
  549. return false;
  550. }
  551. if ($.inArray(code, val) > -1) {
  552. F[ i ] ();
  553. e.preventDefault();
  554. return false;
  555. }
  556. });
  557. }
  558. });
  559. }
  560. if ($.fn.mousewheel && current.mouseWheel) {
  561. F.wrap.bind('mousewheel.fb', function (e, delta, deltaX, deltaY) {
  562. var target = e.target || null,
  563. parent = $(target),
  564. canScroll = false;
  565. while (parent.length) {
  566. if (canScroll || parent.is('.fancybox-skin') || parent.is('.fancybox-wrap')) {
  567. break;
  568. }
  569. canScroll = isScrollable( parent[0] );
  570. parent = $(parent).parent();
  571. }
  572. if (delta !== 0 && !canScroll) {
  573. if (F.group.length > 1 && !current.canShrink) {
  574. if (deltaY > 0 || deltaX > 0) {
  575. F.prev( deltaY > 0 ? 'down' : 'left' );
  576. } else if (deltaY < 0 || deltaX < 0) {
  577. F.next( deltaY < 0 ? 'up' : 'right' );
  578. }
  579. e.preventDefault();
  580. }
  581. }
  582. });
  583. }
  584. },
  585. trigger: function (event, o) {
  586. var ret, obj = o || F.coming || F.current;
  587. if (!obj) {
  588. return;
  589. }
  590. if ($.isFunction( obj[event] )) {
  591. ret = obj[event].apply(obj, Array.prototype.slice.call(arguments, 1));
  592. }
  593. if (ret === false) {
  594. return false;
  595. }
  596. if (obj.helpers) {
  597. $.each(obj.helpers, function (helper, opts) {
  598. if (opts && F.helpers[helper] && $.isFunction(F.helpers[helper][event])) {
  599. opts = $.extend(true, {}, F.helpers[helper].defaults, opts);
  600. F.helpers[helper][event](opts, obj);
  601. }
  602. });
  603. }
  604. $.event.trigger(event + '.fb');
  605. },
  606. isImage: function (str) {
  607. return isString(str) && str.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$)/i);
  608. },
  609. isSWF: function (str) {
  610. return isString(str) && str.match(/\.(swf)((\?|#).*)?$/i);
  611. },
  612. _start: function (index) {
  613. var coming = {},
  614. obj,
  615. href,
  616. type,
  617. margin,
  618. padding;
  619. index = getScalar( index );
  620. obj = F.group[ index ] || null;
  621. if (!obj) {
  622. return false;
  623. }
  624. coming = $.extend(true, {}, F.opts, obj);
  625. // Convert margin and padding properties to array - top, right, bottom, left
  626. margin = coming.margin;
  627. padding = coming.padding;
  628. if ($.type(margin) === 'number') {
  629. coming.margin = [margin, margin, margin, margin];
  630. }
  631. if ($.type(padding) === 'number') {
  632. coming.padding = [padding, padding, padding, padding];
  633. }
  634. // 'modal' propery is just a shortcut
  635. if (coming.modal) {
  636. $.extend(true, coming, {
  637. closeBtn : false,
  638. closeClick : false,
  639. nextClick : false,
  640. arrows : false,
  641. mouseWheel : false,
  642. keys : null,
  643. helpers: {
  644. overlay : {
  645. closeClick : false
  646. }
  647. }
  648. });
  649. }
  650. // 'autoSize' property is a shortcut, too
  651. if (coming.autoSize) {
  652. coming.autoWidth = coming.autoHeight = true;
  653. }
  654. if (coming.width === 'auto') {
  655. coming.autoWidth = true;
  656. }
  657. if (coming.height === 'auto') {
  658. coming.autoHeight = true;
  659. }
  660. /*
  661. * Add reference to the group, so it`s possible to access from callbacks, example:
  662. * afterLoad : function() {
  663. * this.title = 'Image ' + (this.index + 1) + ' of ' + this.group.length + (this.title ? ' - ' + this.title : '');
  664. * }
  665. */
  666. coming.group = F.group;
  667. coming.index = index;
  668. // Give a chance for callback or helpers to update coming item (type, title, etc)
  669. F.coming = coming;
  670. if (false === F.trigger('beforeLoad')) {
  671. F.coming = null;
  672. return;
  673. }
  674. type = coming.type;
  675. href = coming.href;
  676. if (!type) {
  677. F.coming = null;
  678. //If we can not determine content type then drop silently or display next/prev item if looping through gallery
  679. if (F.current && F.router && F.router !== 'jumpto') {
  680. F.current.index = index;
  681. return F[ F.router ]( F.direction );
  682. }
  683. return false;
  684. }
  685. F.isActive = true;
  686. if (type === 'image' || type === 'swf') {
  687. coming.autoHeight = coming.autoWidth = false;
  688. coming.scrolling = 'visible';
  689. }
  690. if (type === 'image') {
  691. coming.aspectRatio = true;
  692. }
  693. if (type === 'iframe' && isTouch) {
  694. coming.scrolling = 'scroll';
  695. }
  696. // Build the neccessary markup
  697. coming.wrap = $(coming.tpl.wrap).addClass('fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-type-' + type + ' fancybox-tmp ' + coming.wrapCSS).appendTo( coming.parent || 'body' );
  698. $.extend(coming, {
  699. skin : $('.fancybox-skin', coming.wrap),
  700. outer : $('.fancybox-outer', coming.wrap),
  701. inner : $('.fancybox-inner', coming.wrap)
  702. });
  703. $.each(["Top", "Right", "Bottom", "Left"], function(i, v) {
  704. coming.skin.css('padding' + v, getValue(coming.padding[ i ]));
  705. });
  706. F.trigger('onReady');
  707. // Check before try to load; 'inline' and 'html' types need content, others - href
  708. if (type === 'inline' || type === 'html') {
  709. if (!coming.content || !coming.content.length) {
  710. return F._error( 'content' );
  711. }
  712. } else if (!href) {
  713. return F._error( 'href' );
  714. }
  715. if (type === 'image') {
  716. F._loadImage();
  717. } else if (type === 'ajax') {
  718. F._loadAjax();
  719. } else if (type === 'iframe') {
  720. F._loadIframe();
  721. } else {
  722. F._afterLoad();
  723. }
  724. },
  725. _error: function ( type ) {
  726. $.extend(F.coming, {
  727. type : 'html',
  728. autoWidth : true,
  729. autoHeight : true,
  730. minWidth : 0,
  731. minHeight : 0,
  732. scrolling : 'no',
  733. hasError : type,
  734. content : F.coming.tpl.error
  735. });
  736. F._afterLoad();
  737. },
  738. _loadImage: function () {
  739. // Reset preload image so it is later possible to check "complete" property
  740. var img = F.imgPreload = new Image();
  741. img.onload = function () {
  742. this.onload = this.onerror = null;
  743. F.coming.width = this.width;
  744. F.coming.height = this.height;
  745. F._afterLoad();
  746. };
  747. img.onerror = function () {
  748. this.onload = this.onerror = null;
  749. F._error( 'image' );
  750. };
  751. img.src = F.coming.href;
  752. if (img.complete !== true) {
  753. F.showLoading();
  754. }
  755. },
  756. _loadAjax: function () {
  757. var coming = F.coming;
  758. F.showLoading();
  759. F.ajaxLoad = $.ajax($.extend({}, coming.ajax, {
  760. url: coming.href,
  761. error: function (jqXHR, textStatus) {
  762. if (F.coming && textStatus !== 'abort') {
  763. F._error( 'ajax', jqXHR );
  764. } else {
  765. F.hideLoading();
  766. }
  767. },
  768. success: function (data, textStatus) {
  769. if (textStatus === 'success') {
  770. coming.content = data;
  771. F._afterLoad();
  772. }
  773. }
  774. }));
  775. },
  776. _loadIframe: function() {
  777. var coming = F.coming,
  778. iframe = $(coming.tpl.iframe.replace(/\{rnd\}/g, new Date().getTime()))
  779. .attr('scrolling', isTouch ? 'auto' : coming.iframe.scrolling)
  780. .attr('src', coming.href);
  781. // This helps IE
  782. $(coming.wrap).bind('onReset', function () {
  783. try {
  784. $(this).find('iframe').hide().attr('src', '//about:blank').end().empty();
  785. } catch (e) {}
  786. });
  787. if (coming.iframe.preload) {
  788. F.showLoading();
  789. iframe.one('load', function() {
  790. $(this).data('ready', 1);
  791. // iOS will lose scrolling if we resize
  792. if (!isTouch) {
  793. $(this).bind('load.fb', F.update);
  794. }
  795. // Without this trick:
  796. // - iframe won't scroll on iOS devices
  797. // - IE7 sometimes displays empty iframe
  798. $(this).parents('.fancybox-wrap').width('100%').removeClass('fancybox-tmp').show();
  799. F._afterLoad();
  800. });
  801. }
  802. coming.content = iframe.appendTo( coming.inner );
  803. if (!coming.iframe.preload) {
  804. F._afterLoad();
  805. }
  806. },
  807. _preloadImages: function() {
  808. var group = F.group,
  809. current = F.current,
  810. len = group.length,
  811. cnt = current.preload ? Math.min(current.preload, len - 1) : 0,
  812. item,
  813. i;
  814. for (i = 1; i <= cnt; i += 1) {
  815. item = group[ (current.index + i ) % len ];
  816. if (item.type === 'image' && item.href) {
  817. new Image().src = item.href;
  818. }
  819. }
  820. },
  821. _afterLoad: function () {
  822. var coming = F.coming,
  823. previous = F.current,
  824. placeholder = 'fancybox-placeholder',
  825. current,
  826. content,
  827. type,
  828. scrolling,
  829. href,
  830. embed;
  831. F.hideLoading();
  832. if (!coming || F.isActive === false) {
  833. return;
  834. }
  835. if (false === F.trigger('afterLoad', coming, previous)) {
  836. coming.wrap.stop(true).trigger('onReset').remove();
  837. F.coming = null;
  838. return;
  839. }
  840. if (previous) {
  841. F.trigger('beforeChange', previous);
  842. previous.wrap.stop(true).removeClass('fancybox-opened')
  843. .find('.fancybox-item, .fancybox-nav')
  844. .remove();
  845. }
  846. F.unbindEvents();
  847. current = coming;
  848. content = coming.content;
  849. type = coming.type;
  850. scrolling = coming.scrolling;
  851. $.extend(F, {
  852. wrap : current.wrap,
  853. skin : current.skin,
  854. outer : current.outer,
  855. inner : current.inner,
  856. current : current,
  857. previous : previous
  858. });
  859. href = current.href;
  860. switch (type) {
  861. case 'inline':
  862. case 'ajax':
  863. case 'html':
  864. if (current.selector) {
  865. content = $('<div>').html(content).find(current.selector);
  866. } else if (isQuery(content)) {
  867. if (!content.data(placeholder)) {
  868. content.data(placeholder, $('<div class="' + placeholder + '"></div>').insertAfter( content ).hide() );
  869. }
  870. content = content.show().detach();
  871. current.wrap.bind('onReset', function () {
  872. if ($(this).find(content).length) {
  873. content.hide().replaceAll( content.data(placeholder) ).data(placeholder, false);
  874. }
  875. });
  876. }
  877. break;
  878. case 'image':
  879. content = current.tpl.image.replace('{href}', href);
  880. break;
  881. case 'swf':
  882. content = '<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="' + href + '"></param>';
  883. embed = '';
  884. $.each(current.swf, function(name, val) {
  885. content += '<param name="' + name + '" value="' + val + '"></param>';
  886. embed += ' ' + name + '="' + val + '"';
  887. });
  888. content += '<embed src="' + href + '" type="application/x-shockwave-flash" width="100%" height="100%"' + embed + '></embed></object>';
  889. break;
  890. }
  891. if (!(isQuery(content) && content.parent().is(current.inner))) {
  892. current.inner.append( content );
  893. }
  894. // Give a chance for helpers or callbacks to update elements
  895. F.trigger('beforeShow');
  896. // Set scrolling before calculating dimensions
  897. current.inner.css('overflow', scrolling === 'yes' ? 'scroll' : (scrolling === 'no' ? 'hidden' : scrolling));
  898. // Set initial dimensions and start position
  899. F._setDimension();
  900. F.reposition();
  901. F.isOpen = false;
  902. F.coming = null;
  903. F.bindEvents();
  904. if (!F.isOpened) {
  905. $('.fancybox-wrap').not( current.wrap ).stop(true).trigger('onReset').remove();
  906. } else if (previous.prevMethod) {
  907. F.transitions[ previous.prevMethod ]();
  908. }
  909. F.transitions[ F.isOpened ? current.nextMethod : current.openMethod ]();
  910. F._preloadImages();
  911. },
  912. _setDimension: function () {
  913. var viewport = F.getViewport(),
  914. steps = 0,
  915. canShrink = false,
  916. canExpand = false,
  917. wrap = F.wrap,
  918. skin = F.skin,
  919. inner = F.inner,
  920. current = F.current,
  921. width = current.width,
  922. height = current.height,
  923. minWidth = current.minWidth,
  924. minHeight = current.minHeight,
  925. maxWidth = current.maxWidth,
  926. maxHeight = current.maxHeight,
  927. scrolling = current.scrolling,
  928. scrollOut = current.scrollOutside ? current.scrollbarWidth : 0,
  929. margin = current.margin,
  930. wMargin = getScalar(margin[1] + margin[3]),
  931. hMargin = getScalar(margin[0] + margin[2]),
  932. wPadding,
  933. hPadding,
  934. wSpace,
  935. hSpace,
  936. origWidth,
  937. origHeight,
  938. origMaxWidth,
  939. origMaxHeight,
  940. ratio,
  941. width_,
  942. height_,
  943. maxWidth_,
  944. maxHeight_,
  945. iframe,
  946. body;
  947. // Reset dimensions so we could re-check actual size
  948. wrap.add(skin).add(inner).width('auto').height('auto').removeClass('fancybox-tmp');
  949. wPadding = getScalar(skin.outerWidth(true) - skin.width());
  950. hPadding = getScalar(skin.outerHeight(true) - skin.height());
  951. // Any space between content and viewport (margin, padding, border, title)
  952. wSpace = wMargin + wPadding;
  953. hSpace = hMargin + hPadding;
  954. origWidth = isPercentage(width) ? (viewport.w - wSpace) * getScalar(width) / 100 : width;
  955. origHeight = isPercentage(height) ? (viewport.h - hSpace) * getScalar(height) / 100 : height;
  956. if (current.type === 'iframe') {
  957. iframe = current.content;
  958. if (current.autoHeight && iframe.data('ready') === 1) {
  959. try {
  960. if (iframe[0].contentWindow.document.location) {
  961. inner.width( origWidth ).height(9999);
  962. body = iframe.contents().find('body');
  963. if (scrollOut) {
  964. body.css('overflow-x', 'hidden');
  965. }
  966. origHeight = body.height();
  967. }
  968. } catch (e) {}
  969. }
  970. } else if (current.autoWidth || current.autoHeight) {
  971. inner.addClass( 'fancybox-tmp' );
  972. // Set width or height in case we need to calculate only one dimension
  973. if (!current.autoWidth) {
  974. inner.width( origWidth );
  975. }
  976. if (!current.autoHeight) {
  977. inner.height( origHeight );
  978. }
  979. if (current.autoWidth) {
  980. origWidth = inner.width();
  981. }
  982. if (current.autoHeight) {
  983. origHeight = inner.height();
  984. }
  985. inner.removeClass( 'fancybox-tmp' );
  986. }
  987. width = getScalar( origWidth );
  988. height = getScalar( origHeight );
  989. ratio = origWidth / origHeight;
  990. // Calculations for the content
  991. minWidth = getScalar(isPercentage(minWidth) ? getScalar(minWidth, 'w') - wSpace : minWidth);
  992. maxWidth = getScalar(isPercentage(maxWidth) ? getScalar(maxWidth, 'w') - wSpace : maxWidth);
  993. minHeight = getScalar(isPercentage(minHeight) ? getScalar(minHeight, 'h') - hSpace : minHeight);
  994. maxHeight = getScalar(isPercentage(maxHeight) ? getScalar(maxHeight, 'h') - hSpace : maxHeight);
  995. // These will be used to determine if wrap can fit in the viewport
  996. origMaxWidth = maxWidth;
  997. origMaxHeight = maxHeight;
  998. if (current.fitToView) {
  999. maxWidth = Math.min(viewport.w - wSpace, maxWidth);
  1000. maxHeight = Math.min(viewport.h - hSpace, maxHeight);
  1001. }
  1002. maxWidth_ = viewport.w - wMargin;
  1003. maxHeight_ = viewport.h - hMargin;
  1004. if (current.aspectRatio) {
  1005. if (width > maxWidth) {
  1006. width = maxWidth;
  1007. height = getScalar(width / ratio);
  1008. }
  1009. if (height > maxHeight) {
  1010. height = maxHeight;
  1011. width = getScalar(height * ratio);
  1012. }
  1013. if (width < minWidth) {
  1014. width = minWidth;
  1015. height = getScalar(width / ratio);
  1016. }
  1017. if (height < minHeight) {
  1018. height = minHeight;
  1019. width = getScalar(height * ratio);
  1020. }
  1021. } else {
  1022. width = Math.max(minWidth, Math.min(width, maxWidth));
  1023. if (current.autoHeight && current.type !== 'iframe') {
  1024. inner.width( width );
  1025. height = inner.height();
  1026. }
  1027. height = Math.max(minHeight, Math.min(height, maxHeight));
  1028. }
  1029. // Try to fit inside viewport (including the title)
  1030. if (current.fitToView) {
  1031. inner.width( width ).height( height );
  1032. wrap.width( width + wPadding );
  1033. // Real wrap dimensions
  1034. width_ = wrap.width();
  1035. height_ = wrap.height();
  1036. if (current.aspectRatio) {
  1037. while ((width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight) {
  1038. if (steps++ > 19) {
  1039. break;
  1040. }
  1041. height = Math.max(minHeight, Math.min(maxHeight, height - 10));
  1042. width = getScalar(height * ratio);
  1043. if (width < minWidth) {
  1044. width = minWidth;
  1045. height = getScalar(width / ratio);
  1046. }
  1047. if (width > maxWidth) {
  1048. width = maxWidth;
  1049. height = getScalar(width / ratio);
  1050. }
  1051. inner.width( width ).height( height );
  1052. wrap.width( width + wPadding );
  1053. width_ = wrap.width();
  1054. height_ = wrap.height();
  1055. }
  1056. } else {
  1057. width = Math.max(minWidth, Math.min(width, width - (width_ - maxWidth_)));
  1058. height = Math.max(minHeight, Math.min(height, height - (height_ - maxHeight_)));
  1059. }
  1060. }
  1061. if (scrollOut && scrolling === 'auto' && height < origHeight && (width + wPadding + scrollOut) < maxWidth_) {
  1062. width += scrollOut;
  1063. }
  1064. inner.width( width ).height( height );
  1065. wrap.width( width + wPadding );
  1066. width_ = wrap.width();
  1067. height_ = wrap.height();
  1068. canShrink = (width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight;
  1069. canExpand = current.aspectRatio ? (width < origMaxWidth && height < origMaxHeight && width < origWidth && height < origHeight) : ((width < origMaxWidth || height < origMaxHeight) && (width < origWidth || height < origHeight));
  1070. $.extend(current, {
  1071. dim : {
  1072. width : getValue( width_ ),
  1073. height : getValue( height_ )
  1074. },
  1075. origWidth : origWidth,
  1076. origHeight : origHeight,
  1077. canShrink : canShrink,
  1078. canExpand : canExpand,
  1079. wPadding : wPadding,
  1080. hPadding : hPadding,
  1081. wrapSpace : height_ - skin.outerHeight(true),
  1082. skinSpace : skin.height() - height
  1083. });
  1084. if (!iframe && current.autoHeight && height > minHeight && height < maxHeight && !canExpand) {
  1085. inner.height('auto');
  1086. }
  1087. },
  1088. _getPosition: function (onlyAbsolute) {
  1089. var current = F.current,
  1090. viewport = F.getViewport(),
  1091. margin = current.margin,
  1092. width = F.wrap.width() + margin[1] + margin[3],
  1093. height = F.wrap.height() + margin[0] + margin[2],
  1094. rez = {
  1095. position: 'absolute',
  1096. top : margin[0],
  1097. left : margin[3]
  1098. };
  1099. if (current.autoCenter && current.fixed && !onlyAbsolute && height <= viewport.h && width <= viewport.w) {
  1100. rez.position = 'fixed';
  1101. } else if (!current.locked) {
  1102. rez.top += viewport.y;
  1103. rez.left += viewport.x;
  1104. }
  1105. rez.top = getValue(Math.max(rez.top, rez.top + ((viewport.h - height) * current.topRatio)));
  1106. rez.left = getValue(Math.max(rez.left, rez.left + ((viewport.w - width) * current.leftRatio)));
  1107. return rez;
  1108. },
  1109. _afterZoomIn: function () {
  1110. var current = F.current;
  1111. if (!current) {
  1112. return;
  1113. }
  1114. F.isOpen = F.isOpened = true;
  1115. F.wrap.css('overflow', 'visible').addClass('fancybox-opened');
  1116. F.update();
  1117. // Assign a click event
  1118. if ( current.closeClick || (current.nextClick && F.group.length > 1) ) {
  1119. F.inner.css('cursor', 'pointer').bind('click.fb', function(e) {
  1120. if (!$(e.target).is('a') && !$(e.target).parent().is('a')) {
  1121. e.preventDefault();
  1122. F[ current.closeClick ? 'close' : 'next' ]();
  1123. }
  1124. });
  1125. }
  1126. // Create a close button
  1127. if (current.closeBtn) {
  1128. $(current.tpl.closeBtn).appendTo(F.skin).bind('click.fb', function(e) {
  1129. e.preventDefault();
  1130. F.close();
  1131. });
  1132. }
  1133. // Create navigation arrows
  1134. if (current.arrows && F.group.length > 1) {
  1135. if (current.loop || current.index > 0) {
  1136. $(current.tpl.prev).appendTo(F.outer).bind('click.fb', F.prev);
  1137. }
  1138. if (current.loop || current.index < F.group.length - 1) {
  1139. $(current.tpl.next).appendTo(F.outer).bind('click.fb', F.next);
  1140. }
  1141. }
  1142. F.trigger('afterShow');
  1143. // Stop the slideshow if this is the last item
  1144. if (!current.loop && current.index === current.group.length - 1) {
  1145. F.play( false );
  1146. } else if (F.opts.autoPlay && !F.player.isActive) {
  1147. F.opts.autoPlay = false;
  1148. F.play();
  1149. }
  1150. },
  1151. _afterZoomOut: function ( obj ) {
  1152. obj = obj || F.current;
  1153. $('.fancybox-wrap').trigger('onReset').remove();
  1154. $.extend(F, {
  1155. group : {},
  1156. opts : {},
  1157. router : false,
  1158. current : null,
  1159. isActive : false,
  1160. isOpened : false,
  1161. isOpen : false,
  1162. isClosing : false,
  1163. wrap : null,
  1164. skin : null,
  1165. outer : null,
  1166. inner : null
  1167. });
  1168. F.trigger('afterClose', obj);
  1169. }
  1170. });
  1171. /*
  1172. * Default transitions
  1173. */
  1174. F.transitions = {
  1175. getOrigPosition: function () {
  1176. var current = F.current,
  1177. element = current.element,
  1178. orig = current.orig,
  1179. pos = {},
  1180. width = 50,
  1181. height = 50,
  1182. hPadding = current.hPadding,
  1183. wPadding = current.wPadding,
  1184. viewport = F.getViewport();
  1185. if (!orig && current.isDom && element.is(':visible')) {
  1186. orig = element.find('img:first');
  1187. if (!orig.length) {
  1188. orig = element;
  1189. }
  1190. }
  1191. if (isQuery(orig)) {
  1192. pos = orig.offset();
  1193. if (orig.is('img')) {
  1194. width = orig.outerWidth();
  1195. height = orig.outerHeight();
  1196. }
  1197. } else {
  1198. pos.top = viewport.y + (viewport.h - height) * current.topRatio;
  1199. pos.left = viewport.x + (viewport.w - width) * current.leftRatio;
  1200. }
  1201. if (F.wrap.css('position') === 'fixed' || current.locked) {
  1202. pos.top -= viewport.y;
  1203. pos.left -= viewport.x;
  1204. }
  1205. pos = {
  1206. top : getValue(pos.top - hPadding * current.topRatio),
  1207. left : getValue(pos.left - wPadding * current.leftRatio),
  1208. width : getValue(width + wPadding),
  1209. height : getValue(height + hPadding)
  1210. };
  1211. return pos;
  1212. },
  1213. step: function (now, fx) {
  1214. var ratio,
  1215. padding,
  1216. value,
  1217. prop = fx.prop,
  1218. current = F.current,
  1219. wrapSpace = current.wrapSpace,
  1220. skinSpace = current.skinSpace;
  1221. if (prop === 'width' || prop === 'height') {
  1222. ratio = fx.end === fx.start ? 1 : (now - fx.start) / (fx.end - fx.start);
  1223. if (F.isClosing) {
  1224. ratio = 1 - ratio;
  1225. }
  1226. padding = prop === 'width' ? current.wPadding : current.hPadding;
  1227. value = now - padding;
  1228. F.skin[ prop ]( getScalar( prop === 'width' ? value : value - (wrapSpace * ratio) ) );
  1229. F.inner[ prop ]( getScalar( prop === 'width' ? value : value - (wrapSpace * ratio) - (skinSpace * ratio) ) );
  1230. }
  1231. },
  1232. zoomIn: function () {
  1233. var current = F.current,
  1234. startPos = current.pos,
  1235. effect = current.openEffect,
  1236. elastic = effect === 'elastic',
  1237. endPos = $.extend({opacity : 1}, startPos);
  1238. // Remove "position" property that breaks older IE
  1239. delete endPos.position;
  1240. if (elastic) {
  1241. startPos = this.getOrigPosition();
  1242. if (current.openOpacity) {
  1243. startPos.opacity = 0.1;
  1244. }
  1245. } else if (effect === 'fade') {
  1246. startPos.opacity = 0.1;
  1247. }
  1248. F.wrap.css(startPos).animate(endPos, {
  1249. duration : effect === 'none' ? 0 : current.openSpeed,
  1250. easing : current.openEasing,
  1251. step : elastic ? this.step : null,
  1252. complete : F._afterZoomIn
  1253. });
  1254. },
  1255. zoomOut: function () {
  1256. var current = F.current,
  1257. effect = current.closeEffect,
  1258. elastic = effect === 'elastic',
  1259. endPos = {opacity : 0.1};
  1260. if (elastic) {
  1261. endPos = this.getOrigPosition();
  1262. if (current.closeOpacity) {
  1263. endPos.opacity = 0.1;
  1264. }
  1265. }
  1266. F.wrap.animate(endPos, {
  1267. duration : effect === 'none' ? 0 : current.closeSpeed,
  1268. easing : current.closeEasing,
  1269. step : elastic ? this.step : null,
  1270. complete : F._afterZoomOut
  1271. });
  1272. },
  1273. changeIn: function () {
  1274. var current = F.current,
  1275. effect = current.nextEffect,
  1276. startPos = current.pos,
  1277. endPos = { opacity : 1 },
  1278. direction = F.direction,
  1279. distance = 200,
  1280. field;
  1281. startPos.opacity = 0.1;
  1282. if (effect === 'elastic') {
  1283. field = direction === 'down' || direction === 'up' ? 'top' : 'left';
  1284. if (direction === 'down' || direction === 'right') {
  1285. startPos[ field ] = getValue(getScalar(startPos[ field ]) - distance);
  1286. endPos[ field ] = '+=' + distance + 'px';
  1287. } else {
  1288. startPos[ field ] = getValue(getScalar(startPos[ field ]) + distance);
  1289. endPos[ field ] = '-=' + distance + 'px';
  1290. }
  1291. }
  1292. // Workaround for http://bugs.jquery.com/ticket/12273
  1293. if (effect === 'none') {
  1294. F._afterZoomIn();
  1295. } else {
  1296. F.wrap.css(startPos).animate(endPos, {
  1297. duration : current.nextSpeed,
  1298. easing : current.nextEasing,
  1299. complete : F._afterZoomIn
  1300. });
  1301. }
  1302. },
  1303. changeOut: function () {
  1304. var previous = F.previous,
  1305. effect = previous.prevEffect,
  1306. endPos = { opacity : 0.1 },
  1307. direction = F.direction,
  1308. distance = 200;
  1309. if (effect === 'elastic') {
  1310. endPos[ direction === 'down' || direction === 'up' ? 'top' : 'left' ] = ( direction === 'up' || direction === 'left' ? '-' : '+' ) + '=' + distance + 'px';
  1311. }
  1312. previous.wrap.animate(endPos, {
  1313. duration : effect === 'none' ? 0 : previous.prevSpeed,
  1314. easing : previous.prevEasing,
  1315. complete : function () {
  1316. $(this).trigger('onReset').remove();
  1317. }
  1318. });
  1319. }
  1320. };
  1321. /*
  1322. * Overlay helper
  1323. */
  1324. F.helpers.overlay = {
  1325. defaults : {
  1326. closeClick : true, // if true, fancyBox will be closed when user clicks on the overlay
  1327. speedOut : 200, // duration of fadeOut animation
  1328. showEarly : true, // indicates if should be opened immediately or wait until the content is ready
  1329. css : {}, // custom CSS properties
  1330. locked : !isTouch, // if true, the content will be locked into overlay
  1331. fixed : true // if false, the overlay CSS position property will not be set to "fixed"
  1332. },
  1333. overlay : null, // current handle
  1334. fixed : false, // indicates if the overlay has position "fixed"
  1335. // Public methods
  1336. create : function(opts) {
  1337. opts = $.extend({}, this.defaults, opts);
  1338. if (this.overlay) {
  1339. this.close();
  1340. }
  1341. this.overlay = $('<div class="fancybox-overlay"></div>').appendTo( 'body' );
  1342. this.fixed = false;
  1343. if (opts.fixed && F.defaults.fixed) {
  1344. this.overlay.addClass('fancybox-overlay-fixed');
  1345. this.fixed = true;
  1346. }
  1347. },
  1348. open : function(opts) {
  1349. var that = this;
  1350. opts = $.extend({}, this.defaults, opts);
  1351. if (this.overlay) {
  1352. this.overlay.unbind('.overlay').width('auto').height('auto');
  1353. } else {
  1354. this.create(opts);
  1355. }
  1356. if (!this.fixed) {
  1357. W.bind('resize.overlay', $.proxy( this.update, this) );
  1358. this.update();
  1359. }
  1360. if (opts.closeClick) {
  1361. this.overlay.bind('click.overlay', function(e) {
  1362. if ($(e.target).hasClass('fancybox-overlay')) {
  1363. if (F.isActive) {
  1364. F.close();
  1365. } else {
  1366. that.close();
  1367. }
  1368. }
  1369. });
  1370. }
  1371. this.overlay.css( opts.css ).show();
  1372. },
  1373. close : function() {
  1374. $('.fancybox-overlay').remove();
  1375. W.unbind('resize.overlay');
  1376. this.overlay = null;
  1377. if (this.margin !== false) {
  1378. $('body').css('margin-right', this.margin);
  1379. this.margin = false;
  1380. }
  1381. if (this.el) {
  1382. this.el.removeClass('fancybox-lock');
  1383. }
  1384. },
  1385. // Private, callbacks
  1386. update : function () {
  1387. var width = '100%', offsetWidth;
  1388. // Reset width/height so it will not mess
  1389. this.overlay.width(width).height('100%');
  1390. // jQuery does not return reliable result for IE
  1391. if (IE) {
  1392. offsetWidth = Math.max(document.documentElement.offsetWidth, document.body.offsetWidth);
  1393. if (D.width() > offsetWidth) {
  1394. width = D.width();
  1395. }
  1396. } else if (D.width() > W.width()) {
  1397. width = D.width();
  1398. }
  1399. this.overlay.width(width).height(D.height());
  1400. },
  1401. // This is where we can manipulate DOM, because later it would cause iframes to reload
  1402. onReady : function (opts, obj) {
  1403. $('.fancybox-overlay').stop(true, true);
  1404. if (!this.overlay) {
  1405. this.margin = D.height() > W.height() || $('body').css('overflow-y') === 'scroll' ? $('body').css('margin-right') : false;
  1406. this.el = document.all && !document.querySelector ? $('html') : $('body');
  1407. this.create(opts);
  1408. }
  1409. if (opts.locked && this.fixed) {
  1410. obj.locked = this.overlay.append( obj.wrap );
  1411. obj.fixed = false;
  1412. }
  1413. if (opts.showEarly === true) {
  1414. this.beforeShow.apply(this, arguments);
  1415. }
  1416. },
  1417. beforeShow : function(opts, obj) {
  1418. if (obj.locked) {
  1419. this.el.addClass('fancybox-lock');
  1420. if (this.margin !== false) {
  1421. $('body').css('margin-right', getScalar( this.margin ) + obj.scrollbarWidth);
  1422. }
  1423. }
  1424. this.open(opts);
  1425. },
  1426. onUpdate : function() {
  1427. if (!this.fixed) {
  1428. this.update();
  1429. }
  1430. },
  1431. afterClose: function (opts) {
  1432. // Remove overlay if exists and fancyBox is not opening
  1433. // (e.g., it is not being open using afterClose callback)
  1434. if (this.overlay && !F.isActive) {
  1435. this.overlay.fadeOut(opts.speedOut, $.proxy( this.close, this ));
  1436. }
  1437. }
  1438. };
  1439. /*
  1440. * Title helper
  1441. */
  1442. F.helpers.title = {
  1443. defaults : {
  1444. type : 'float', // 'float', 'inside', 'outside' or 'over',
  1445. position : 'bottom' // 'top' or 'bottom'
  1446. },
  1447. beforeShow: function (opts) {
  1448. var current = F.current,
  1449. text = current.title,
  1450. type = opts.type,
  1451. title,
  1452. target;
  1453. if ($.isFunction(text)) {
  1454. text = text.call(current.element, current);
  1455. }
  1456. if (!isString(text) || $.trim(text) === '') {
  1457. return;
  1458. }
  1459. title = $('<div class="fancybox-title fancybox-title-' + type + '-wrap">' + text + '</div>');
  1460. switch (type) {
  1461. case 'inside':
  1462. target = F.skin;
  1463. break;
  1464. case 'outside':
  1465. target = F.wrap;
  1466. break;
  1467. case 'over':
  1468. target = F.inner;
  1469. break;
  1470. default: // 'float'
  1471. target = F.skin;
  1472. title.appendTo('body');
  1473. if (IE) {
  1474. title.width( title.width() );
  1475. }
  1476. title.wrapInner('<span class="child"></span>');
  1477. //Increase bottom margin so this title will also fit into viewport
  1478. F.current.margin[2] += Math.abs( getScalar(title.css('margin-bottom')) );
  1479. break;
  1480. }
  1481. title[ (opts.position === 'top' ? 'prependTo' : 'appendTo') ](target);
  1482. }
  1483. };
  1484. // jQuery plugin initialization
  1485. $.fn.fancybox = function (options) {
  1486. var index,
  1487. that = $(this),
  1488. selector = this.selector || '',
  1489. run = function(e) {
  1490. var what = $(this).blur(), idx = index, relType, relVal;
  1491. if (!(e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) && !what.is('.fancybox-wrap')) {
  1492. relType = options.groupAttr || 'data-fancybox-group';
  1493. relVal = what.attr(relType);
  1494. if (!relVal) {
  1495. relType = 'rel';
  1496. relVal = what.get(0)[ relType ];
  1497. }
  1498. if (relVal && relVal !== '' && relVal !== 'nofollow') {
  1499. what = selector.length ? $(selector) : that;
  1500. what = what.filter('[' + relType + '="' + relVal + '"]');
  1501. idx = what.index(this);
  1502. }
  1503. options.index = idx;
  1504. // Stop an event from bubbling if everything is fine
  1505. if (F.open(what, options) !== false) {
  1506. e.preventDefault();
  1507. }
  1508. }
  1509. };
  1510. options = options || {};
  1511. index = options.index || 0;
  1512. if (!selector || options.live === false) {
  1513. that.unbind('click.fb-start').bind('click.fb-start', run);
  1514. } else {
  1515. D.undelegate(selector, 'click.fb-start').delegate(selector + ":not('.fancybox-item, .fancybox-nav')", 'click.fb-start', run);
  1516. }
  1517. this.filter('[data-fancybox-start=1]').trigger('click');
  1518. return this;
  1519. };
  1520. // Tests that need a body at doc ready
  1521. D.ready(function() {
  1522. if ( $.scrollbarWidth === undefined ) {
  1523. // http://benalman.com/projects/jquery-misc-plugins/#scrollbarwidth
  1524. $.scrollbarWidth = function() {
  1525. var parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body'),
  1526. child = parent.children(),
  1527. width = child.innerWidth() - child.height( 99 ).innerWidth();
  1528. parent.remove();
  1529. return width;
  1530. };
  1531. }
  1532. if ( $.support.fixedPosition === undefined ) {
  1533. $.support.fixedPosition = (function() {
  1534. var elem = $('<div style="position:fixed;top:20px;"></div>').appendTo('body'),
  1535. fixed = ( elem[0].offsetTop === 20 || elem[0].offsetTop === 15 );
  1536. elem.remove();
  1537. return fixed;
  1538. }());
  1539. }
  1540. $.extend(F.defaults, {
  1541. scrollbarWidth : $.scrollbarWidth(),
  1542. fixed : $.support.fixedPosition,
  1543. parent : $('body')
  1544. });
  1545. });
  1546. }(window, document, jQuery));