jquery.fancybox.js 47 KB

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