';
252 | html += '
' + data.labels.close + '';
253 | html += '
';
254 | html += '
';
255 | html += '
';
256 | if (isImage || isVideo) {
257 | html += '
'; //container, content, boxer
278 |
279 | // Modify Dom
280 | data.$body.append(html);
281 |
282 | // Cache jquery objects
283 | data.$overlay = $("#boxer-overlay");
284 | data.$boxer = $("#boxer");
285 | data.$container = data.$boxer.find(".boxer-container");
286 | data.$content = data.$boxer.find(".boxer-content");
287 | data.$meta = data.$boxer.find(".boxer-meta");
288 | data.$position = data.$boxer.find(".boxer-position");
289 | data.$caption = data.$boxer.find(".boxer-caption");
290 | data.$controls = data.$boxer.find(".boxer-control");
291 |
292 | data.paddingVertical = (!data.isMobile) ? (parseInt(data.$boxer.css("paddingTop"), 10) + parseInt(data.$boxer.css("paddingBottom"), 10)) : (data.$boxer.find(".boxer-close").outerHeight() / 2);
293 | data.paddingHorizontal = (!data.isMobile) ? (parseInt(data.$boxer.css("paddingLeft"), 10) + parseInt(data.$boxer.css("paddingRight"), 10)) : 0;
294 | data.contentHeight = data.$boxer.outerHeight() - data.paddingVertical;
295 | data.contentWidth = data.$boxer.outerWidth() - data.paddingHorizontal;
296 | data.controlHeight = data.$controls.outerHeight();
297 |
298 | // Center
299 | center();
300 |
301 | // Update gallery
302 | if (data.gallery.active) {
303 | updateControls();
304 | }
305 |
306 | // Bind events
307 | data.$window.on("resize.boxer", pub.resize)
308 | .on("keydown.boxer", onKeypress);
309 |
310 | data.$body.on("touchstart.boxer click.boxer", "#boxer-overlay, #boxer .boxer-close", onClose)
311 | .on("touchmove.boxer", killEvent);
312 |
313 | if (data.gallery.active) {
314 | data.$boxer.on("touchstart.boxer click.boxer", ".boxer-control", advanceGallery);
315 | }
316 |
317 | data.$boxer.on(transitionEvent, function(e) {
318 | killEvent(e);
319 |
320 | if ($(e.target).is(data.$boxer)) {
321 | data.$boxer.off(transitionEvent);
322 |
323 | if (isImage) {
324 | loadImage(source);
325 | } else if (isVideo) {
326 | loadVideo(source);
327 | } else if (isUrl) {
328 | loadURL(source);
329 | } else if (isElement) {
330 | cloneElement(source);
331 | } else if (isObject) {
332 | appendObject(data.$object);
333 | } else {
334 | $.error("BOXER: '" + source + "' is not valid.");
335 | }
336 | }
337 | });
338 |
339 | $body.addClass("boxer-open");
340 |
341 | if (!transitionSupported) {
342 | data.$boxer.trigger(transitionEvent);
343 | }
344 |
345 | if (isObject) {
346 | return data.$boxer;
347 | }
348 | }
349 | }
350 |
351 | /**
352 | * @method private
353 | * @name onClose
354 | * @description Closes active instance
355 | * @param e [object] "Event data"
356 | */
357 | function onClose(e) {
358 | killEvent(e);
359 |
360 | if (typeof data.$boxer !== "undefined") {
361 | data.$boxer.on(transitionEvent, function(e) {
362 | killEvent(e);
363 |
364 | if ($(e.target).is(data.$boxer)) {
365 | data.$boxer.off(transitionEvent);
366 |
367 | data.$overlay.remove();
368 | data.$boxer.remove();
369 |
370 | // reset data
371 | data = {};
372 | }
373 | }).addClass("animating");
374 |
375 | $body.removeClass("boxer-open");
376 |
377 | if (!transitionSupported) {
378 | data.$boxer.trigger(transitionEvent);
379 | }
380 |
381 | clearTimer(data.resizeTimer);
382 |
383 | // Clean up
384 | data.$window.off("resize.boxer")
385 | .off("keydown.boxer");
386 |
387 | data.$body.off(".boxer")
388 | .removeClass("boxer-open");
389 |
390 | if (data.gallery.active) {
391 | data.$boxer.off(".boxer");
392 | }
393 |
394 | if (data.isMobile) {
395 | if (data.type === "image" && data.gallery.active) {
396 | data.$container.off(".boxer");
397 | }
398 | }
399 |
400 | data.$window.trigger("close.boxer");
401 | }
402 | }
403 |
404 | /**
405 | * @method private
406 | * @name open
407 | * @description Opens active instance
408 | */
409 | function open() {
410 | var position = calculatePosition(),
411 | durration = data.isMobile ? 0 : data.duration;
412 |
413 | if (!data.isMobile) {
414 | data.$controls.css({
415 | marginTop: ((data.contentHeight - data.controlHeight - data.metaHeight) / 2)
416 | });
417 | }
418 |
419 | if (!data.visible && data.isMobile && data.gallery.active) {
420 | data.$content.on("touchstart.boxer", ".boxer-image", onTouchStart);
421 | }
422 |
423 | if (data.isMobile || data.fixed) {
424 | data.$body.addClass("boxer-open");
425 | }
426 |
427 | data.$boxer.on(transitionEvent, function(e) {
428 | killEvent(e);
429 |
430 | if ($(e.target).is(data.$boxer)) {
431 | data.$boxer.off(transitionEvent);
432 |
433 | data.$container.on(transitionEvent, function(e) {
434 | killEvent(e);
435 |
436 | if ($(e.target).is(data.$container)) {
437 | data.$container.off(transitionEvent);
438 |
439 | data.$boxer.removeClass("animating");
440 |
441 | data.isAnimating = false;
442 | }
443 | });
444 |
445 | data.$boxer.removeClass("loading");
446 |
447 | if (!transitionSupported) {
448 | data.$content.trigger(transitionEvent);
449 | }
450 |
451 | data.visible = true;
452 |
453 | // Fire callback + event
454 | data.callback.apply(data.$boxer);
455 | data.$window.trigger("open.boxer");
456 |
457 | // Start preloading
458 | if (data.gallery.active) {
459 | preloadGallery();
460 | }
461 | }
462 | });
463 |
464 | if (!data.isMobile) {
465 | data.$boxer.css({
466 | height: data.contentHeight + data.paddingVertical,
467 | width: data.contentWidth + data.paddingHorizontal,
468 | top: (!data.fixed) ? position.top : 0
469 | });
470 | }
471 |
472 | // Trigger event in case the content size hasn't changed
473 | var contentHasChanged = (data.oldContentHeight !== data.contentHeight || data.oldContentWidth !== data.contentWidth);
474 |
475 | if (data.isMobile || !transitionSupported || !contentHasChanged) {
476 | data.$boxer.trigger(transitionEvent);
477 | }
478 |
479 | // Track content size changes
480 | data.oldContentHeight = data.contentHeight;
481 | data.oldContentWidth = data.contentWidth;
482 | }
483 |
484 | /**
485 | * @method private
486 | * @name size
487 | * @description Sizes active instance
488 | */
489 | function size() {
490 | if (data.visible && !data.isMobile) {
491 | var position = calculatePosition();
492 |
493 | data.$controls.css({
494 | marginTop: ((data.contentHeight - data.controlHeight - data.metaHeight) / 2)
495 | });
496 |
497 | data.$boxer.css({
498 | height: data.contentHeight + data.paddingVertical,
499 | width: data.contentWidth + data.paddingHorizontal,
500 | top: (!data.fixed) ? position.top : 0
501 | });
502 | }
503 | }
504 |
505 | /**
506 | * @method private
507 | * @name center
508 | * @description Centers instance
509 | */
510 | function center() {
511 | var position = calculatePosition();
512 |
513 | data.$boxer.css({
514 | top: (!data.fixed) ? position.top : 0
515 | });
516 | }
517 |
518 | /**
519 | * @method private
520 | * @name calculatePosition
521 | * @description Calculates positions
522 | * @return [object] "Object containing top and left positions"
523 | */
524 | function calculatePosition() {
525 | if (data.isMobile) {
526 | return {
527 | left: 0,
528 | top: 0
529 | };
530 | }
531 |
532 | var pos = {
533 | left: (data.$window.width() - data.contentWidth - data.paddingHorizontal) / 2,
534 | top: (data.top <= 0) ? ((data.$window.height() - data.contentHeight - data.paddingVertical) / 2) : data.top
535 | };
536 |
537 | if (data.fixed !== true) {
538 | pos.top += data.$window.scrollTop();
539 | }
540 |
541 | return pos;
542 | }
543 |
544 | /**
545 | * @method private
546 | * @name formatCaption
547 | * @description Formats caption
548 | * @param $target [jQuery object] "Target element"
549 | */
550 | function formatCaption($target) {
551 | var title = $target.attr("title");
552 | return (title !== undefined && title.trim() !== "") ? '
' + title.trim() + '
' : "";
553 | }
554 |
555 | /**
556 | * @method private
557 | * @name loadImage
558 | * @description Loads source image
559 | * @param source [string] "Source image URL"
560 | */
561 | function loadImage(source) {
562 | // Cache current image
563 | data.$image = $("
![]()
");
564 |
565 | data.$image.load(function() {
566 | data.$image.off("load, error");
567 |
568 | var naturalSize = calculateNaturalSize(data.$image);
569 |
570 | data.naturalHeight = naturalSize.naturalHeight;
571 | data.naturalWidth = naturalSize.naturalWidth;
572 |
573 | if (data.retina) {
574 | data.naturalHeight /= 2;
575 | data.naturalWidth /= 2;
576 | }
577 |
578 | data.$content.prepend(data.$image);
579 |
580 | if (data.$caption.html() === "") {
581 | data.$caption.hide();
582 | } else {
583 | data.$caption.show();
584 | }
585 |
586 | // Size content to be sure it fits the viewport
587 | sizeImage();
588 | open();
589 | }).error(loadError)
590 | .attr("src", source)
591 | .addClass("boxer-image");
592 |
593 | // If image has already loaded into cache, trigger load event
594 | if (data.$image[0].complete || data.$image[0].readyState === 4) {
595 | data.$image.trigger("load");
596 | }
597 | }
598 |
599 | /**
600 | * @method private
601 | * @name sizeImage
602 | * @description Sizes image to fit in viewport
603 | * @param count [int] "Number of resize attempts"
604 | */
605 | function sizeImage() {
606 | var count = 0;
607 |
608 | data.windowHeight = data.viewportHeight = data.$window.height() - data.paddingVertical;
609 | data.windowWidth = data.viewportWidth = data.$window.width() - data.paddingHorizontal;
610 |
611 | data.contentHeight = Infinity;
612 | data.contentWidth = Infinity;
613 |
614 | data.imageMarginTop = 0;
615 | data.imageMarginLeft = 0;
616 |
617 | while (data.contentHeight > data.viewportHeight && count < 2) {
618 | data.imageHeight = (count === 0) ? data.naturalHeight : data.$image.outerHeight();
619 | data.imageWidth = (count === 0) ? data.naturalWidth : data.$image.outerWidth();
620 | data.metaHeight = (count === 0) ? 0 : data.metaHeight;
621 |
622 | if (count === 0) {
623 | data.ratioHorizontal = data.imageHeight / data.imageWidth;
624 | data.ratioVertical = data.imageWidth / data.imageHeight;
625 |
626 | data.isWide = (data.imageWidth > data.imageHeight);
627 | }
628 |
629 | // Double check min and max
630 | if (data.imageHeight < data.minHeight) {
631 | data.minHeight = data.imageHeight;
632 | }
633 | if (data.imageWidth < data.minWidth) {
634 | data.minWidth = data.imageWidth;
635 | }
636 |
637 | if (data.isMobile) {
638 | // Get meta height before sizing
639 | data.$meta.css({
640 | width: data.windowWidth
641 | });
642 | data.metaHeight = data.$meta.outerHeight(true);
643 |
644 | // Content match viewport
645 | data.contentHeight = data.viewportHeight - data.paddingVertical;
646 | data.contentWidth = data.viewportWidth - data.paddingHorizontal;
647 |
648 | fitImage();
649 |
650 | data.imageMarginTop = (data.contentHeight - data.targetImageHeight - data.metaHeight) / 2;
651 | data.imageMarginLeft = (data.contentWidth - data.targetImageWidth) / 2;
652 | } else {
653 | // Viewport should match window, less margin, padding and meta
654 | if (count === 0) {
655 | data.viewportHeight -= (data.margin + data.paddingVertical);
656 | data.viewportWidth -= (data.margin + data.paddingHorizontal);
657 | }
658 | data.viewportHeight -= data.metaHeight;
659 |
660 | fitImage();
661 |
662 | data.contentHeight = data.targetImageHeight;
663 | data.contentWidth = data.targetImageWidth;
664 | }
665 |
666 | // Modify DOM
667 |
668 | data.$meta.css({
669 | width: data.contentWidth
670 | });
671 |
672 | data.$image.css({
673 | height: data.targetImageHeight,
674 | width: data.targetImageWidth,
675 | marginTop: data.imageMarginTop,
676 | marginLeft: data.imageMarginLeft
677 | });
678 |
679 | if (!data.isMobile) {
680 | data.metaHeight = data.$meta.outerHeight(true);
681 | data.contentHeight += data.metaHeight;
682 | }
683 |
684 | count ++;
685 | }
686 | }
687 |
688 | /**
689 | * @method private
690 | * @name fitImage
691 | * @description Calculates target image size
692 | */
693 | function fitImage() {
694 | var height = (!data.isMobile) ? data.viewportHeight : data.contentHeight - data.metaHeight,
695 | width = (!data.isMobile) ? data.viewportWidth : data.contentWidth;
696 |
697 | if (data.isWide) {
698 | //WIDE
699 | data.targetImageWidth = width;
700 | data.targetImageHeight = data.targetImageWidth * data.ratioHorizontal;
701 |
702 | if (data.targetImageHeight > height) {
703 | data.targetImageHeight = height;
704 | data.targetImageWidth = data.targetImageHeight * data.ratioVertical;
705 | }
706 | } else {
707 | //TALL
708 | data.targetImageHeight = height;
709 | data.targetImageWidth = data.targetImageHeight * data.ratioVertical;
710 |
711 | if (data.targetImageWidth > width) {
712 | data.targetImageWidth = width;
713 | data.targetImageHeight = data.targetImageWidth * data.ratioHorizontal;
714 | }
715 | }
716 |
717 | // MAX
718 | if (data.targetImageWidth > data.imageWidth || data.targetImageHeight > data.imageHeight) {
719 | data.targetImageHeight = data.imageHeight;
720 | data.targetImageWidth = data.imageWidth;
721 | }
722 |
723 | // MIN
724 | if (data.targetImageWidth < data.minWidth || data.targetImageHeight < data.minHeight) {
725 | if (data.targetImageWidth < data.minWidth) {
726 | data.targetImageWidth = data.minWidth;
727 | data.targetImageHeight = data.targetImageWidth * data.ratioHorizontal;
728 | } else {
729 | data.targetImageHeight = data.minHeight;
730 | data.targetImageWidth = data.targetImageHeight * data.ratioVertical;
731 | }
732 | }
733 | }
734 |
735 | /**
736 | * @method private
737 | * @name loadVideo
738 | * @description Loads source video
739 | * @param source [string] "Source video URL"
740 | */
741 | function loadVideo(source) {
742 | data.$videoWrapper = $('
');
743 | data.$video = $('
');
744 |
745 | data.$video.attr("src", source)
746 | .addClass("boxer-video")
747 | .prependTo(data.$videoWrapper);
748 |
749 | data.$content.prepend(data.$videoWrapper);
750 |
751 | sizeVideo();
752 | open();
753 | }
754 |
755 | /**
756 | * @method private
757 | * @name sizeVideo
758 | * @description Sizes video to fit in viewport
759 | */
760 | function sizeVideo() {
761 | // Set initial vars
762 | data.windowHeight = data.viewportHeight = data.contentHeight = data.$window.height() - data.paddingVertical;
763 | data.windowWidth = data.viewportWidth = data.contentWidth = data.$window.width() - data.paddingHorizontal;
764 | data.videoMarginTop = 0;
765 | data.videoMarginLeft = 0;
766 |
767 | if (data.isMobile) {
768 | data.$meta.css({
769 | width: data.windowWidth
770 | });
771 | data.metaHeight = data.$meta.outerHeight(true);
772 | data.viewportHeight -= data.metaHeight;
773 |
774 | data.targetVideoWidth = data.viewportWidth;
775 | data.targetVideoHeight = data.targetVideoWidth * data.videoRatio;
776 |
777 | if (data.targetVideoHeight > data.viewportHeight) {
778 | data.targetVideoHeight = data.viewportHeight;
779 | data.targetVideoWidth = data.targetVideoHeight / data.videoRatio;
780 | }
781 |
782 | data.videoMarginTop = (data.viewportHeight - data.targetVideoHeight) / 2;
783 | data.videoMarginLeft = (data.viewportWidth - data.targetVideoWidth) / 2;
784 | } else {
785 | data.viewportHeight = data.windowHeight - data.margin;
786 | data.viewportWidth = data.windowWidth - data.margin;
787 |
788 | data.targetVideoWidth = (data.videoWidth > data.viewportWidth) ? data.viewportWidth : data.videoWidth;
789 | if (data.targetVideoWidth < data.minWidth) {
790 | data.targetVideoWidth = data.minWidth;
791 | }
792 | data.targetVideoHeight = data.targetVideoWidth * data.videoRatio;
793 |
794 | data.contentHeight = data.targetVideoHeight;
795 | data.contentWidth = data.targetVideoWidth;
796 | }
797 |
798 | // Update dom
799 |
800 | data.$meta.css({
801 | width: data.contentWidth
802 | });
803 |
804 | data.$videoWrapper.css({
805 | height: data.targetVideoHeight,
806 | width: data.targetVideoWidth,
807 | marginTop: data.videoMarginTop,
808 | marginLeft: data.videoMarginLeft
809 | });
810 |
811 | if (!data.isMobile) {
812 | data.metaHeight = data.$meta.outerHeight(true);
813 | data.contentHeight = data.targetVideoHeight + data.metaHeight;
814 | }
815 | }
816 |
817 | /**
818 | * @method private
819 | * @name preloadGallery
820 | * @description Preloads previous and next images in gallery for faster rendering
821 | * @param e [object] "Event Data"
822 | */
823 | function preloadGallery(e) {
824 | var source = '';
825 |
826 | if (data.gallery.index > 0) {
827 | source = data.gallery.$items.eq(data.gallery.index - 1).attr("href");
828 | if (source.indexOf("youtube.com/embed") < 0 && source.indexOf("player.vimeo.com/video") < 0) {
829 | $('

');
830 | }
831 | }
832 | if (data.gallery.index < data.gallery.total) {
833 | source = data.gallery.$items.eq(data.gallery.index + 1).attr("href");
834 | if (source.indexOf("youtube.com/embed") < 0 && source.indexOf("player.vimeo.com/video") < 0) {
835 | $('

');
836 | }
837 | }
838 | }
839 |
840 | /**
841 | * @method private
842 | * @name advanceGallery
843 | * @description Advances gallery base on direction
844 | * @param e [object] "Event Data"
845 | */
846 | function advanceGallery(e) {
847 | killEvent(e);
848 |
849 | var $control = $(this);
850 | if (!data.isAnimating && !$control.hasClass("disabled")) {
851 | data.isAnimating = true;
852 |
853 | data.gallery.index += ($control.hasClass("next")) ? 1 : -1;
854 | if (data.gallery.index > data.gallery.total) {
855 | data.gallery.index = data.gallery.total;
856 | }
857 | if (data.gallery.index < 0) {
858 | data.gallery.index = 0;
859 | }
860 |
861 | data.$container.on(transitionEvent, function(e) {
862 | killEvent(e);
863 |
864 | if ($(e.target).is(data.$container)) {
865 | data.$container.off(transitionEvent);
866 |
867 | if (typeof data.$image !== 'undefined') {
868 | data.$image.remove();
869 | }
870 | if (typeof data.$videoWrapper !== 'undefined') {
871 | data.$videoWrapper.remove();
872 | }
873 | data.$target = data.gallery.$items.eq(data.gallery.index);
874 |
875 | data.$caption.html(data.formatter.apply(data.$body, [data.$target]));
876 | data.$position.find(".current").html(data.gallery.index + 1);
877 |
878 | var source = data.$target.attr("href"),
879 | isVideo = ( source.indexOf("youtube.com/embed") > -1 || source.indexOf("player.vimeo.com/video") > -1 );
880 |
881 | if (isVideo) {
882 | loadVideo(source);
883 | } else {
884 | loadImage(source);
885 | }
886 | updateControls();
887 | }
888 | });
889 |
890 | data.$boxer.addClass("loading animating");
891 |
892 | if (!transitionSupported) {
893 | data.$content.trigger(transitionEvent);
894 | }
895 | }
896 | }
897 |
898 | /**
899 | * @method private
900 | * @name updateControls
901 | * @description Updates gallery control states
902 | */
903 | function updateControls() {
904 | data.$controls.removeClass("disabled");
905 | if (data.gallery.index === 0) {
906 | data.$controls.filter(".previous").addClass("disabled");
907 | }
908 | if (data.gallery.index === data.gallery.total) {
909 | data.$controls.filter(".next").addClass("disabled");
910 | }
911 | }
912 |
913 | /**
914 | * @method private
915 | * @name onKeypress
916 | * @description Handles keypress in gallery
917 | * @param e [object] "Event data"
918 | */
919 | function onKeypress(e) {
920 | if (data.gallery.active && (e.keyCode === 37 || e.keyCode === 39)) {
921 | killEvent(e);
922 |
923 | data.$controls.filter((e.keyCode === 37) ? ".previous" : ".next").trigger("click");
924 | } else if (e.keyCode === 27) {
925 | data.$boxer.find(".boxer-close").trigger("click");
926 | }
927 | }
928 |
929 | /**
930 | * @method private
931 | * @name cloneElement
932 | * @description Clones target inline element
933 | * @param id [string] "Target element id"
934 | */
935 | function cloneElement(id) {
936 | var $clone = $(id).find(">:first-child").clone();
937 | appendObject($clone);
938 | }
939 |
940 | /**
941 | * @method private
942 | * @name loadURL
943 | * @description Load URL into iframe
944 | * @param source [string] "Target URL"
945 | */
946 | function loadURL(source) {
947 | source = source + ((source.indexOf("?") > -1) ? "&"+options.requestKey+"=true" : "?"+options.requestKey+"=true");
948 | var $iframe = $('
');
949 | appendObject($iframe);
950 | }
951 |
952 | /**
953 | * @method private
954 | * @name appendObject
955 | * @description Appends and sizes object
956 | * @param $object [jQuery Object] "Object to append"
957 | */
958 | function appendObject($object) {
959 | data.$content.append($object);
960 | sizeContent($object);
961 | open();
962 | }
963 |
964 | /**
965 | * @method private
966 | * @name sizeContent
967 | * @description Sizes jQuery object to fir in viewport
968 | * @param $object [jQuery Object] "Object to size"
969 | */
970 | function sizeContent($object) {
971 | data.windowHeight = data.$window.height() - data.paddingVertical;
972 | data.windowWidth = data.$window.width() - data.paddingHorizontal;
973 | data.objectHeight = $object.outerHeight(true);
974 | data.objectWidth = $object.outerWidth(true);
975 | data.targetHeight = data.targetHeight || data.$target.data("boxer-height");
976 | data.targetWidth = data.targetWidth || data.$target.data("boxer-width");
977 | data.maxHeight = (data.windowHeight < 0) ? options.minHeight : data.windowHeight;
978 | data.isIframe = $object.is("iframe");
979 | data.objectMarginTop = 0;
980 | data.objectMarginLeft = 0;
981 |
982 | if (!data.isMobile) {
983 | data.windowHeight -= data.margin;
984 | data.windowWidth -= data.margin;
985 | }
986 |
987 | data.contentHeight = (data.targetHeight !== undefined) ? data.targetHeight : (data.isIframe || data.isMobile) ? data.windowHeight : data.objectHeight;
988 | data.contentWidth = (data.targetWidth !== undefined) ? data.targetWidth : (data.isIframe || data.isMobile) ? data.windowWidth : data.objectWidth;
989 |
990 | if ((data.isIframe || data.isObject) && data.isMobile) {
991 | data.contentHeight = data.windowHeight;
992 | data.contentWidth = data.windowWidth;
993 | } else if (data.isObject) {
994 | data.contentHeight = (data.contentHeight > data.windowHeight) ? data.windowHeight : data.contentHeight;
995 | data.contentWidth = (data.contentWidth > data.windowWidth) ? data.windowWidth : data.contentWidth;
996 | }
997 | }
998 |
999 | /**
1000 | * @method private
1001 | * @name loadError
1002 | * @description Error when resource fails to load
1003 | * @param e [object] "Event data"
1004 | */
1005 | function loadError(e) {
1006 | var $error = $('
');
1007 |
1008 | // Clean up
1009 | data.type = "element";
1010 | data.$meta.remove();
1011 |
1012 | data.$image.off("load, error");
1013 |
1014 | appendObject($error);
1015 | }
1016 |
1017 | /**
1018 | * @method private
1019 | * @name onTouchStart
1020 | * @description Handle touch start event
1021 | * @param e [object] "Event data"
1022 | */
1023 | function onTouchStart(e) {
1024 | killEvent(e);
1025 | clearTimer(data.touchTimer);
1026 |
1027 | if (!data.isAnimating) {
1028 | var touch = (typeof e.originalEvent.targetTouches !== "undefined") ? e.originalEvent.targetTouches[0] : null;
1029 | data.xStart = (touch) ? touch.pageX : e.clientX;
1030 | data.leftPosition = 0;
1031 |
1032 | data.touchMax = Infinity;
1033 | data.touchMin = -Infinity;
1034 | data.edge = data.contentWidth * 0.25;
1035 |
1036 | if (data.gallery.index === 0) {
1037 | data.touchMax = 0;
1038 | }
1039 | if (data.gallery.index === data.gallery.total) {
1040 | data.touchMin = 0;
1041 | }
1042 |
1043 | data.$boxer.on("touchmove.boxer", onTouchMove)
1044 | .one("touchend.boxer", onTouchEnd);
1045 | }
1046 | }
1047 |
1048 | /**
1049 | * @method private
1050 | * @name onTouchMove
1051 | * @description Handles touchmove event
1052 | * @param e [object] "Event data"
1053 | */
1054 | function onTouchMove(e) {
1055 | var touch = (typeof e.originalEvent.targetTouches !== "undefined") ? e.originalEvent.targetTouches[0] : null;
1056 |
1057 | data.delta = data.xStart - ((touch) ? touch.pageX : e.clientX);
1058 |
1059 | // Only prevent event if trying to swipe
1060 | if (data.delta > 20) {
1061 | killEvent(e);
1062 | }
1063 |
1064 | data.canSwipe = true;
1065 |
1066 | var newLeft = -data.delta;
1067 | if (newLeft < data.touchMin) {
1068 | newLeft = data.touchMin;
1069 | data.canSwipe = false;
1070 | }
1071 | if (newLeft > data.touchMax) {
1072 | newLeft = data.touchMax;
1073 | data.canSwipe = false;
1074 | }
1075 |
1076 | data.$image.css({ transform: "translate3D("+newLeft+"px,0,0)" });
1077 |
1078 | data.touchTimer = startTimer(data.touchTimer, 300, function() { onTouchEnd(e); });
1079 | }
1080 |
1081 | /**
1082 | * @method private
1083 | * @name onTouchEnd
1084 | * @description Handles touchend event
1085 | * @param e [object] "Event data"
1086 | */
1087 | function onTouchEnd(e) {
1088 | killEvent(e);
1089 | clearTimer(data.touchTimer);
1090 |
1091 | data.$boxer.off("touchmove.boxer touchend.boxer");
1092 |
1093 | if (data.delta) {
1094 | data.$boxer.addClass("animated");
1095 | data.swipe = false;
1096 |
1097 | if (data.canSwipe && (data.delta > data.edge || data.delta < -data.edge)) {
1098 | data.swipe = true;
1099 | if (data.delta <= data.leftPosition) {
1100 | data.$image.css({ transform: "translate3D("+(data.contentWidth)+"px,0,0)" });
1101 | } else {
1102 | data.$image.css({ transform: "translate3D("+(-data.contentWidth)+"px,0,0)" });
1103 | }
1104 | } else {
1105 | data.$image.css({ transform: "translate3D(0,0,0)" });
1106 | }
1107 |
1108 | if (data.swipe) {
1109 | data.$controls.filter( (data.delta <= data.leftPosition) ? ".previous" : ".next" ).trigger("click");
1110 | }
1111 | startTimer(data.resetTimer, data.duration, function() {
1112 | data.$boxer.removeClass("animated");
1113 | });
1114 | }
1115 | }
1116 |
1117 | /**
1118 | * @method private
1119 | * @name calculateNaturalSize
1120 | * @description Determines natural size of target image
1121 | * @param $img [jQuery object] "Source image object"
1122 | * @return [object | boolean] "Object containing natural height and width values or false"
1123 | */
1124 | function calculateNaturalSize($img) {
1125 | var node = $img[0],
1126 | img = new Image();
1127 |
1128 | if (typeof node.naturalHeight !== "undefined") {
1129 | return {
1130 | naturalHeight: node.naturalHeight,
1131 | naturalWidth: node.naturalWidth
1132 | };
1133 | } else {
1134 | if (node.tagName.toLowerCase() === 'img') {
1135 | img.src = node.src;
1136 | return {
1137 | naturalHeight: img.height,
1138 | naturalWidth: img.width
1139 | };
1140 | }
1141 | }
1142 |
1143 | return false;
1144 | }
1145 |
1146 | /**
1147 | * @method private
1148 | * @name killEvent
1149 | * @description Prevents default and stops propagation on event
1150 | * @param e [object] "Event data"
1151 | */
1152 | function killEvent(e) {
1153 | if (e.preventDefault) {
1154 | e.stopPropagation();
1155 | e.preventDefault();
1156 | }
1157 | }
1158 |
1159 | /**
1160 | * @method private
1161 | * @name startTimer
1162 | * @description Starts an internal timer
1163 | * @param timer [int] "Timer ID"
1164 | * @param time [int] "Time until execution"
1165 | * @param callback [int] "Function to execute"
1166 | */
1167 | function startTimer(timer, time, callback) {
1168 | clearTimer(timer);
1169 | return setTimeout(callback, time);
1170 | }
1171 |
1172 | /**
1173 | * @method private
1174 | * @name clearTimer
1175 | * @description Clears an internal timer
1176 | * @param timer [int] "Timer ID"
1177 | */
1178 | function clearTimer(timer) {
1179 | if (timer) {
1180 | clearTimeout(timer);
1181 | timer = null;
1182 | }
1183 | }
1184 |
1185 | /**
1186 | * @method private
1187 | * @name getTransitionEvent
1188 | * @description Retuns a properly prefixed transitionend event
1189 | * @return [string] "Properly prefixed event"
1190 | */
1191 | function getTransitionEvent() {
1192 | var transitions = {
1193 | 'WebkitTransition': 'webkitTransitionEnd',
1194 | 'MozTransition': 'transitionend',
1195 | /* 'MSTransitionEnd': 'msTransition', */
1196 | /* 'msTransition': 'MSTransitionEnd' */
1197 | 'OTransition': 'oTransitionEnd',
1198 | 'transition': 'transitionend'
1199 | },
1200 | test = document.createElement('div');
1201 |
1202 | for (var type in transitions) {
1203 | if (transitions.hasOwnProperty(type) && type in test.style) {
1204 | return transitions[type];
1205 | }
1206 | }
1207 |
1208 | return false;
1209 | }
1210 |
1211 | $.fn.boxer = function(method) {
1212 | if (pub[method]) {
1213 | return pub[method].apply(this, Array.prototype.slice.call(arguments, 1));
1214 | } else if (typeof method === 'object' || !method) {
1215 | return init.apply(this, arguments);
1216 | }
1217 | return this;
1218 | };
1219 |
1220 | $.boxer = function($target, opts) {
1221 | if (pub[$target]) {
1222 | return pub[$target].apply(window, Array.prototype.slice.call(arguments, 1));
1223 | } else {
1224 | if ($target instanceof $) {
1225 | return build.apply(window, [{ data: $.extend({
1226 | $object: $target
1227 | }, options, opts || {}) }]);
1228 | }
1229 | }
1230 | };
1231 | })(jQuery, window);
--------------------------------------------------------------------------------
/jquery.fs.boxer.min.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Boxer v3.3.0 - 2015-04-04
3 | * A jQuery plugin for displaying images, videos or content in a modal overlay. Part of the Formstone Library.
4 | * http://classic.formstone.it/boxer/
5 | *
6 | * Copyright 2015 Ben Plum; MIT Licensed
7 | */
8 |
9 | .boxer-lock{overflow:hidden!important}#boxer-overlay{width:100%;height:100%;position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;background:#000;opacity:0;-webkit-transition:opacity .25s linear;transition:opacity .25s linear}.boxer-open #boxer-overlay{opacity:.75}#boxer{width:200px;height:200px;position:absolute;right:0;left:0;z-index:101;background:#fff;border-radius:3px;box-shadow:0 0 25px #000;opacity:0;margin:0 auto;padding:10px}#boxer *{-webkit-transition:none;transition:none}#boxer,#boxer *{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}#boxer,#boxer *,#boxer :before,#boxer :after{box-sizing:border-box}#boxer.fixed{position:fixed;top:0;bottom:0;margin:auto}#boxer.inline{padding:30px}#boxer.animating{-webkit-transition:height .25s ease,width .25s ease,opacity .25s linear,top .25s ease;transition:height .25s ease,width .25s ease,opacity .25s linear,top .25s ease}#boxer.animating .boxer-container{-webkit-transition:opacity .25s linear .25s;transition:opacity .25s linear .25s}.boxer-open #boxer{opacity:1}#boxer.loading .boxer-container{opacity:0;-webkit-transition:opacity .25s linear;transition:opacity .25s linear}#boxer .boxer-close{width:30px;height:30px;position:absolute;top:-7.5px;right:-7.5px;z-index:105;background:#fff;border-radius:100%;cursor:pointer;display:block;overflow:hidden;padding:0;text-indent:200%;white-space:nowrap}#boxer .boxer-close:before{position:absolute;top:0;right:0;bottom:0;left:0;color:#333;content:"\00d7";display:block;font-size:22px;font-weight:700;line-height:30px;margin:auto;text-align:center;text-indent:0;-webkit-transition:color .15s linear;transition:color .15s linear}.no-opacity #boxer .boxer-close{text-indent:-999px}#boxer .boxer-loading{width:50px;height:50px;position:absolute;top:0;right:0;bottom:0;left:0;z-index:105;display:block;margin:auto;opacity:0;-webkit-transition:opacity .25s linear;transition:opacity .25s linear}#boxer .boxer-loading:before,#boxer .boxer-loading:after{width:100%;height:100%;position:absolute;top:0;right:0;bottom:0;left:0;border-radius:110%;content:'';display:block}#boxer .boxer-loading:before{border:5px solid rgba(51,51,51,.25)}#boxer .boxer-loading:after{-webkit-animation:boxer-loading-spin .75s linear infinite;animation:boxer-loading-spin .75s linear infinite;border:5px solid transparent;border-top-color:#333}#boxer.loading .boxer-loading{opacity:1}@-webkit-keyframes boxer-loading-spin{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes boxer-loading-spin{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}#boxer .boxer-container{width:100%;height:100%;position:relative;z-index:103;background:#fff;overflow:hidden}#boxer .boxer-content{width:100%;background:#fff;opacity:1;overflow:hidden;padding:0}#boxer.inline .boxer-content,#boxer.iframe .boxer-content{width:auto}#boxer .boxer-image{float:left}#boxer .boxer-video{width:100%;height:100%}#boxer .boxer-iframe{width:100%;height:100%;border:none;float:left;overflow:auto}#boxer .boxer-meta{clear:both}#boxer .boxer-control{width:40px;height:40px;position:absolute;top:0;background:#fff;border-radius:100%;box-shadow:0 0 5px rgba(0,0,0,.25);cursor:pointer;display:block;margin-right:auto;margin-left:auto;opacity:1;overflow:hidden;text-indent:200%;-webkit-transition:opacity .15s linear;transition:opacity .15s linear;white-space:nowrap}#boxer .boxer-control:before{width:0;height:0;position:absolute;top:0;right:0;bottom:0;left:0;content:'';margin:auto}#boxer .boxer-control.previous{left:20px}#boxer .boxer-control.previous:before{border-top:8px solid transparent;border-bottom:8px solid transparent;border-right:10.4px solid #333;margin-left:14px}#boxer .boxer-control.next{right:20px}#boxer .boxer-control.next:before{border-top:8px solid transparent;border-bottom:8px solid transparent;border-left:10.4px solid #333;margin-right:14px}#boxer .boxer-control.disabled{opacity:0}.no-opacity #boxer .boxer-control{text-indent:-999px}.no-touch #boxer .boxer-control{opacity:0}.no-touch #boxer:hover .boxer-control{opacity:1}.no-touch #boxer:hover .boxer-control.disabled{opacity:0;cursor:default!important}#boxer .boxer-meta{padding:10px 0 0 0}#boxer .boxer-position{color:#999;font-size:12px;margin:0;padding:15px 15px 0 15px}#boxer .boxer-caption p{color:#666;font-size:14px;margin:0;padding:15px}#boxer .boxer-caption.gallery p{padding-top:0}#boxer .boxer-error{width:250px}#boxer .boxer-error p{color:#900;font-size:14px;margin:0;padding:25px;text-align:center;text-transform:uppercase}#boxer.mobile{width:100%;height:100%;position:fixed;top:0;right:0;bottom:0;left:0;background:#111;border-radius:0;padding:40px 0 0}#boxer.mobile .boxer-close,#boxer.mobile .boxer-close:hover{height:40px;width:40px;top:0;right:0;background:#111;border-radius:0}#boxer.mobile .boxer-close:before,#boxer.mobile .boxer-close:hover:before{color:#ccc;font-size:28px;font-weight:700;line-height:40px}#boxer.mobile .boxer-loading:before{border-color:rgba(153,153,153,.25)}#boxer.mobile .boxer-loading:after{border-top-color:#999}#boxer.mobile .boxer-container{background:#111}#boxer.mobile .boxer-content{background-color:#111}#boxer.mobile .boxer-control{width:50px;height:100%;background:#111;border-radius:0;box-shadow:none;opacity:1}#boxer.mobile .boxer-control.previous{left:0}#boxer.mobile .boxer-control.previous:before{border-right-color:#eee;margin-left:19px}#boxer.mobile .boxer-control.next{right:0}#boxer.mobile .boxer-control.next:before{border-left-color:#eee;margin-right:19px}.no-touch #boxer.mobile .boxer-control,.no-touch #boxer.mobile:hover .boxer-control{opacity:1}.no-touch #boxer.mobile .boxer-control.disabled,.no-touch #boxer.mobile:hover .boxer-control.disabled{opacity:0;cursor:default!important}#boxer.mobile .boxer-meta{width:100%;position:absolute;right:0;bottom:0;left:0;background-color:#111;padding:15px 65px}#boxer.mobile .boxer-position{color:#999;font-size:12px;margin:0;padding:0 15px 0 0}#boxer.mobile .boxer-caption p{color:#eee;font-size:14px;margin:0;padding:0}#boxer.mobile .boxer-image{-webkit-transition:none!important;transition:none!important;-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}#boxer.mobile.animated .boxer-image{-webkit-transition:-webkit-transform .25s ease-out!important;transition:transform .25s ease-out!important}#boxer.mobile.inline .boxer-content,#boxer.mobile.iframe .boxer-content{overflow-x:hidden;overflow-y:scroll;-webkit-overflow-scrolling:touch}
--------------------------------------------------------------------------------
/jquery.fs.boxer.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Boxer v3.3.0 - 2015-04-04
3 | * A jQuery plugin for displaying images, videos or content in a modal overlay. Part of the Formstone Library.
4 | * http://classic.formstone.it/boxer/
5 | *
6 | * Copyright 2015 Ben Plum; MIT Licensed
7 | */
8 |
9 | !function(a,b){"use strict";function c(b){return L.formatter=j,I=a("body"),G=F(),H=G!==!1,H||(G="transitionend.boxer"),a(this).on("click.boxer",a.extend({},L,b||{}),d)}function d(c){if("undefined"==typeof J.$boxer){var d=a(this),f=c.data.$object,g=d[0].href?d[0].href||"":"",i=d[0].hash?d[0].hash||"":"",j=g.toLowerCase().split(".").pop().split(/\#|\?/),l=j[0],m=d.data("boxer-type")||"",o="image"===m||a.inArray(l,c.data.extensions)>-1||"data:image"===g.substr(0,10),p=g.indexOf("youtube.com/embed")>-1||g.indexOf("player.vimeo.com/video")>-1,w="url"===m||!o&&!p&&"http"===g.substr(0,4)&&!i,x="element"===m||!o&&!p&&!w&&"#"===i.substr(0,1),y="undefined"!=typeof f;if(x&&(g=i),a("#boxer").length>1||!(o||p||w||x||y))return;if(C(c),J=a.extend({},{$window:a(b),$body:a("body"),$target:d,$object:f,visible:!1,resizeTimer:null,touchTimer:null,gallery:{active:!1},isMobile:K||c.data.mobile,isAnimating:!0,oldContentHeight:0,oldContentWidth:0},c.data),J.margin*=2,J.type=o?"image":p?"video":"element",o||p){var z=J.$target.data("gallery")||J.$target.attr("rel");"undefined"!=typeof z&&z!==!1&&(J.gallery.active=!0,J.gallery.id=z,J.gallery.$items=a("a[data-gallery= "+J.gallery.id+"], a[rel= "+J.gallery.id+"]"),J.gallery.index=J.gallery.$items.index(J.$target),J.gallery.total=J.gallery.$items.length-1)}var A="";if(J.isMobile||(A+='
'),A+='
',A+='
'+J.labels.close+"",A+='
',A+='
",J.$body.append(A),J.$overlay=a("#boxer-overlay"),J.$boxer=a("#boxer"),J.$container=J.$boxer.find(".boxer-container"),J.$content=J.$boxer.find(".boxer-content"),J.$meta=J.$boxer.find(".boxer-meta"),J.$position=J.$boxer.find(".boxer-position"),J.$caption=J.$boxer.find(".boxer-caption"),J.$controls=J.$boxer.find(".boxer-control"),J.paddingVertical=J.isMobile?J.$boxer.find(".boxer-close").outerHeight()/2:parseInt(J.$boxer.css("paddingTop"),10)+parseInt(J.$boxer.css("paddingBottom"),10),J.paddingHorizontal=J.isMobile?0:parseInt(J.$boxer.css("paddingLeft"),10)+parseInt(J.$boxer.css("paddingRight"),10),J.contentHeight=J.$boxer.outerHeight()-J.paddingVertical,J.contentWidth=J.$boxer.outerWidth()-J.paddingHorizontal,J.controlHeight=J.$controls.outerHeight(),h(),J.gallery.active&&r(),J.$window.on("resize.boxer",M.resize).on("keydown.boxer",s),J.$body.on("touchstart.boxer click.boxer","#boxer-overlay, #boxer .boxer-close",e).on("touchmove.boxer",C),J.gallery.active&&J.$boxer.on("touchstart.boxer click.boxer",".boxer-control",q),J.$boxer.on(G,function(b){C(b),a(b.target).is(J.$boxer)&&(J.$boxer.off(G),o?k(g):p?n(g):w?u(g):x?t(g):y?v(J.$object):a.error("BOXER: '"+g+"' is not valid."))}),I.addClass("boxer-open"),H||J.$boxer.trigger(G),y)return J.$boxer}}function e(b){C(b),"undefined"!=typeof J.$boxer&&(J.$boxer.on(G,function(b){C(b),a(b.target).is(J.$boxer)&&(J.$boxer.off(G),J.$overlay.remove(),J.$boxer.remove(),J={})}).addClass("animating"),I.removeClass("boxer-open"),H||J.$boxer.trigger(G),E(J.resizeTimer),J.$window.off("resize.boxer").off("keydown.boxer"),J.$body.off(".boxer").removeClass("boxer-open"),J.gallery.active&&J.$boxer.off(".boxer"),J.isMobile&&"image"===J.type&&J.gallery.active&&J.$container.off(".boxer"),J.$window.trigger("close.boxer"))}function f(){{var b=i();J.isMobile?0:J.duration}J.isMobile||J.$controls.css({marginTop:(J.contentHeight-J.controlHeight-J.metaHeight)/2}),!J.visible&&J.isMobile&&J.gallery.active&&J.$content.on("touchstart.boxer",".boxer-image",y),(J.isMobile||J.fixed)&&J.$body.addClass("boxer-open"),J.$boxer.on(G,function(b){C(b),a(b.target).is(J.$boxer)&&(J.$boxer.off(G),J.$container.on(G,function(b){C(b),a(b.target).is(J.$container)&&(J.$container.off(G),J.$boxer.removeClass("animating"),J.isAnimating=!1)}),J.$boxer.removeClass("loading"),H||J.$content.trigger(G),J.visible=!0,J.callback.apply(J.$boxer),J.$window.trigger("open.boxer"),J.gallery.active&&p())}),J.isMobile||J.$boxer.css({height:J.contentHeight+J.paddingVertical,width:J.contentWidth+J.paddingHorizontal,top:J.fixed?0:b.top});var c=J.oldContentHeight!==J.contentHeight||J.oldContentWidth!==J.contentWidth;!J.isMobile&&H&&c||J.$boxer.trigger(G),J.oldContentHeight=J.contentHeight,J.oldContentWidth=J.contentWidth}function g(){if(J.visible&&!J.isMobile){var a=i();J.$controls.css({marginTop:(J.contentHeight-J.controlHeight-J.metaHeight)/2}),J.$boxer.css({height:J.contentHeight+J.paddingVertical,width:J.contentWidth+J.paddingHorizontal,top:J.fixed?0:a.top})}}function h(){var a=i();J.$boxer.css({top:J.fixed?0:a.top})}function i(){if(J.isMobile)return{left:0,top:0};var a={left:(J.$window.width()-J.contentWidth-J.paddingHorizontal)/2,top:J.top<=0?(J.$window.height()-J.contentHeight-J.paddingVertical)/2:J.top};return J.fixed!==!0&&(a.top+=J.$window.scrollTop()),a}function j(a){var b=a.attr("title");return void 0!==b&&""!==b.trim()?'
'+b.trim()+"
":""}function k(b){J.$image=a("
![]()
"),J.$image.load(function(){J.$image.off("load, error");var a=B(J.$image);J.naturalHeight=a.naturalHeight,J.naturalWidth=a.naturalWidth,J.retina&&(J.naturalHeight/=2,J.naturalWidth/=2),J.$content.prepend(J.$image),""===J.$caption.html()?J.$caption.hide():J.$caption.show(),l(),f()}).error(x).attr("src",b).addClass("boxer-image"),(J.$image[0].complete||4===J.$image[0].readyState)&&J.$image.trigger("load")}function l(){var a=0;for(J.windowHeight=J.viewportHeight=J.$window.height()-J.paddingVertical,J.windowWidth=J.viewportWidth=J.$window.width()-J.paddingHorizontal,J.contentHeight=1/0,J.contentWidth=1/0,J.imageMarginTop=0,J.imageMarginLeft=0;J.contentHeight>J.viewportHeight&&2>a;)J.imageHeight=0===a?J.naturalHeight:J.$image.outerHeight(),J.imageWidth=0===a?J.naturalWidth:J.$image.outerWidth(),J.metaHeight=0===a?0:J.metaHeight,0===a&&(J.ratioHorizontal=J.imageHeight/J.imageWidth,J.ratioVertical=J.imageWidth/J.imageHeight,J.isWide=J.imageWidth>J.imageHeight),J.imageHeight
a&&(J.targetImageHeight=a,J.targetImageWidth=J.targetImageHeight*J.ratioVertical)):(J.targetImageHeight=a,J.targetImageWidth=J.targetImageHeight*J.ratioVertical,J.targetImageWidth>b&&(J.targetImageWidth=b,J.targetImageHeight=J.targetImageWidth*J.ratioHorizontal)),(J.targetImageWidth>J.imageWidth||J.targetImageHeight>J.imageHeight)&&(J.targetImageHeight=J.imageHeight,J.targetImageWidth=J.imageWidth),(J.targetImageWidth'),J.$video=a(''),J.$video.attr("src",b).addClass("boxer-video").prependTo(J.$videoWrapper),J.$content.prepend(J.$videoWrapper),o(),f()}function o(){J.windowHeight=J.viewportHeight=J.contentHeight=J.$window.height()-J.paddingVertical,J.windowWidth=J.viewportWidth=J.contentWidth=J.$window.width()-J.paddingHorizontal,J.videoMarginTop=0,J.videoMarginLeft=0,J.isMobile?(J.$meta.css({width:J.windowWidth}),J.metaHeight=J.$meta.outerHeight(!0),J.viewportHeight-=J.metaHeight,J.targetVideoWidth=J.viewportWidth,J.targetVideoHeight=J.targetVideoWidth*J.videoRatio,J.targetVideoHeight>J.viewportHeight&&(J.targetVideoHeight=J.viewportHeight,J.targetVideoWidth=J.targetVideoHeight/J.videoRatio),J.videoMarginTop=(J.viewportHeight-J.targetVideoHeight)/2,J.videoMarginLeft=(J.viewportWidth-J.targetVideoWidth)/2):(J.viewportHeight=J.windowHeight-J.margin,J.viewportWidth=J.windowWidth-J.margin,J.targetVideoWidth=J.videoWidth>J.viewportWidth?J.viewportWidth:J.videoWidth,J.targetVideoWidth0&&(c=J.gallery.$items.eq(J.gallery.index-1).attr("href"),c.indexOf("youtube.com/embed")<0&&c.indexOf("player.vimeo.com/video")<0&&a('
')),J.gallery.index'))}function q(b){C(b);var c=a(this);J.isAnimating||c.hasClass("disabled")||(J.isAnimating=!0,J.gallery.index+=c.hasClass("next")?1:-1,J.gallery.index>J.gallery.total&&(J.gallery.index=J.gallery.total),J.gallery.index<0&&(J.gallery.index=0),J.$container.on(G,function(b){if(C(b),a(b.target).is(J.$container)){J.$container.off(G),"undefined"!=typeof J.$image&&J.$image.remove(),"undefined"!=typeof J.$videoWrapper&&J.$videoWrapper.remove(),J.$target=J.gallery.$items.eq(J.gallery.index),J.$caption.html(J.formatter.apply(J.$body,[J.$target])),J.$position.find(".current").html(J.gallery.index+1);var c=J.$target.attr("href"),d=c.indexOf("youtube.com/embed")>-1||c.indexOf("player.vimeo.com/video")>-1;d?n(c):k(c),r()}}),J.$boxer.addClass("loading animating"),H||J.$content.trigger(G))}function r(){J.$controls.removeClass("disabled"),0===J.gallery.index&&J.$controls.filter(".previous").addClass("disabled"),J.gallery.index===J.gallery.total&&J.$controls.filter(".next").addClass("disabled")}function s(a){!J.gallery.active||37!==a.keyCode&&39!==a.keyCode?27===a.keyCode&&J.$boxer.find(".boxer-close").trigger("click"):(C(a),J.$controls.filter(37===a.keyCode?".previous":".next").trigger("click"))}function t(b){var c=a(b).find(">:first-child").clone();v(c)}function u(b){b+=b.indexOf("?")>-1?"&"+L.requestKey+"=true":"?"+L.requestKey+"=true";var c=a('');v(c)}function v(a){J.$content.append(a),w(a),f()}function w(a){J.windowHeight=J.$window.height()-J.paddingVertical,J.windowWidth=J.$window.width()-J.paddingHorizontal,J.objectHeight=a.outerHeight(!0),J.objectWidth=a.outerWidth(!0),J.targetHeight=J.targetHeight||J.$target.data("boxer-height"),J.targetWidth=J.targetWidth||J.$target.data("boxer-width"),J.maxHeight=J.windowHeight<0?L.minHeight:J.windowHeight,J.isIframe=a.is("iframe"),J.objectMarginTop=0,J.objectMarginLeft=0,J.isMobile||(J.windowHeight-=J.margin,J.windowWidth-=J.margin),J.contentHeight=void 0!==J.targetHeight?J.targetHeight:J.isIframe||J.isMobile?J.windowHeight:J.objectHeight,J.contentWidth=void 0!==J.targetWidth?J.targetWidth:J.isIframe||J.isMobile?J.windowWidth:J.objectWidth,(J.isIframe||J.isObject)&&J.isMobile?(J.contentHeight=J.windowHeight,J.contentWidth=J.windowWidth):J.isObject&&(J.contentHeight=J.contentHeight>J.windowHeight?J.windowHeight:J.contentHeight,J.contentWidth=J.contentWidth>J.windowWidth?J.windowWidth:J.contentWidth)}function x(b){var c=a('');J.type="element",J.$meta.remove(),J.$image.off("load, error"),v(c)}function y(a){if(C(a),E(J.touchTimer),!J.isAnimating){var b="undefined"!=typeof a.originalEvent.targetTouches?a.originalEvent.targetTouches[0]:null;J.xStart=b?b.pageX:a.clientX,J.leftPosition=0,J.touchMax=1/0,J.touchMin=-(1/0),J.edge=.25*J.contentWidth,0===J.gallery.index&&(J.touchMax=0),J.gallery.index===J.gallery.total&&(J.touchMin=0),J.$boxer.on("touchmove.boxer",z).one("touchend.boxer",A)}}function z(a){var b="undefined"!=typeof a.originalEvent.targetTouches?a.originalEvent.targetTouches[0]:null;J.delta=J.xStart-(b?b.pageX:a.clientX),J.delta>20&&C(a),J.canSwipe=!0;var c=-J.delta;cJ.touchMax&&(c=J.touchMax,J.canSwipe=!1),J.$image.css({transform:"translate3D("+c+"px,0,0)"}),J.touchTimer=D(J.touchTimer,300,function(){A(a)})}function A(a){C(a),E(J.touchTimer),J.$boxer.off("touchmove.boxer touchend.boxer"),J.delta&&(J.$boxer.addClass("animated"),J.swipe=!1,J.canSwipe&&(J.delta>J.edge||J.delta<-J.edge)?(J.swipe=!0,J.$image.css(J.delta<=J.leftPosition?{transform:"translate3D("+J.contentWidth+"px,0,0)"}:{transform:"translate3D("+-J.contentWidth+"px,0,0)"})):J.$image.css({transform:"translate3D(0,0,0)"}),J.swipe&&J.$controls.filter(J.delta<=J.leftPosition?".previous":".next").trigger("click"),D(J.resetTimer,J.duration,function(){J.$boxer.removeClass("animated")}))}function B(a){var b=a[0],c=new Image;return"undefined"!=typeof b.naturalHeight?{naturalHeight:b.naturalHeight,naturalWidth:b.naturalWidth}:"img"===b.tagName.toLowerCase()?(c.src=b.src,{naturalHeight:c.height,naturalWidth:c.width}):!1}function C(a){a.preventDefault&&(a.stopPropagation(),a.preventDefault())}function D(a,b,c){return E(a),setTimeout(c,b)}function E(a){a&&(clearTimeout(a),a=null)}function F(){var a={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"},b=document.createElement("div");for(var c in a)if(a.hasOwnProperty(c)&&c in b.style)return a[c];return!1}var G,H,I=null,J={},K=/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(b.navigator.userAgent||b.navigator.vendor||b.opera),L={callback:a.noop,customClass:"",extensions:["jpg","sjpg","jpeg","png","gif"],fixed:!1,formatter:a.noop,labels:{close:"Close",count:"of",next:"Next",previous:"Previous"},margin:50,minHeight:100,minWidth:100,mobile:!1,opacity:.75,retina:!1,requestKey:"boxer",top:0,videoRatio:.5625,videoWidth:600},M={close:function(){"undefined"!=typeof J.$boxer&&(J.$boxer.off(".boxer"),J.$overlay.trigger("click"))},defaults:function(b){return L=a.extend(L,b||{}),"object"==typeof this?a(this):!1},destroy:function(){return a(this).off(".boxer")},resize:function(b){return"undefined"!=typeof J.$boxer&&("object"!=typeof b&&(J.targetHeight=arguments[0],J.targetWidth=arguments[1]),"element"===J.type?w(J.$content.find(">:first-child")):"image"===J.type?l():"video"===J.type&&o(),g()),a(this)}};a.fn.boxer=function(a){return M[a]?M[a].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof a&&a?this:c.apply(this,arguments)},a.boxer=function(c,e){return M[c]?M[c].apply(b,Array.prototype.slice.call(arguments,1)):c instanceof a?d.apply(b,[{data:a.extend({$object:c},L,e||{})}]):void 0}}(jQuery,window);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Boxer",
3 | "id": "boxer",
4 | "codename": "jquery.fs.boxer",
5 | "version": "3.3.0",
6 | "description": "A jQuery plugin for displaying images, videos or content in a modal overlay. Part of the Formstone Library.",
7 | "keywords": [
8 | "modal",
9 | "slideshow",
10 | "ui",
11 | "javascript",
12 | "jquery",
13 | "formstone"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/FormstoneClassic/Boxer"
18 | },
19 | "homepage": "http://classic.formstone.it/boxer/",
20 | "demo": "http://classic.formstone.it/components/Boxer/demo/index.html",
21 | "license": "MIT",
22 | "author": {
23 | "name": "Ben Plum",
24 | "email": "mr@benplum.com",
25 | "url": "http://www.benplum.com"
26 | },
27 | "devDependencies": {
28 | "grunt": "~0.4.2",
29 | "grunt-autoprefixer": "^1.0.1",
30 | "grunt-banner": "^0.2.3",
31 | "grunt-contrib-copy": "^0.6.0",
32 | "grunt-contrib-jshint": "~0.7.2",
33 | "grunt-contrib-less": "^0.11.4",
34 | "grunt-contrib-uglify": "~0.2.7",
35 | "grunt-contrib-watch": "^0.6.1",
36 | "grunt-jquerymanifest": "~0.1.3",
37 | "grunt-npm2bower-sync": "~0.3.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/jquery.fs.boxer-config.less:
--------------------------------------------------------------------------------
1 |
2 | // Use these variables when compiling directly
3 |
4 | // Overlay
5 |
6 | @boxer-overlay-background: #000; // #000
7 | @boxer-overlay-opacity: 0.75; // 0.75
8 |
9 | @boxer-transition-duration: 0.25s; // 0.25s
10 | @boxer-transition-timing: ease; // ease
11 |
12 | // Boxer
13 |
14 | @boxer-background: #fff; // #fff
15 | @boxer-padding: 10px; // 10px
16 | @boxer-z-index: 100; // 100
17 |
18 | @boxer-border-radius: 3px; // 3px
19 | @boxer-box-shadow: 0 0 25px #000; // 0 0 25px #000
20 |
21 |
22 | // Close Button
23 |
24 | @boxer-close-background: #fff; // #fff
25 | @boxer-close-font-size: 22px; // 20px
26 | @boxer-close-font-weight: 700; // 700
27 | @boxer-close-color: #333; // #333
28 | @boxer-close-border-radius: 100%; // 100%
29 | @boxer-close-width: 30px; // 30px
30 | @boxer-close-height: 30px; // 30px
31 |
32 | // Container
33 |
34 | @boxer-container-background: #fff; // #fff
35 |
36 | // Content
37 |
38 | @boxer-content-background: #fff; // #fff
39 |
40 | // Controls
41 |
42 | @boxer-control-background: #fff; // #fff
43 | @boxer-control-opacity: 1; // 1
44 | @boxer-control-height: 40px; // 40px
45 | @boxer-control-width: 40px; // 40px
46 |
47 | @boxer-control-transition-duration: 0.15s; // 0.15s
48 | @boxer-control-box-shadow: 0 0 5px fade(#000, 25); // 0 0 5px fade(#000, 25)
49 |
50 | @boxer-control-border-radius: 100%; // 100%
51 |
52 | @boxer-control-previous-left: 20px; // 20px
53 | @boxer-control-next-right: 20px; // 20px
54 |
55 |
56 | // Control Carets
57 |
58 | @boxer-control-caret-color: #333; // #333
59 | @boxer-control-caret-size: 8px; // 8px
60 |
61 |
62 | // Loading Icons
63 |
64 | @boxer-loading-width: 50px; // 50px
65 | @boxer-loading-height: 50px; // 50px
66 | @boxer-loading-color: #333; // #333
67 | @boxer-loading-size: 5px; // 5px
68 | @boxer-loading-animation-duration: 0.75s; // 0.75s
69 |
70 |
71 | // Inline
72 |
73 | @boxer-inline-padding: 30px; // 30px
74 |
75 |
76 | // Meta
77 |
78 | @boxer-meta-padding: 10px 0 0 0; // 10px 0 0 0
79 |
80 | // Position
81 |
82 | @boxer-position-font-size: 12px; // 12px
83 | @boxer-position-text-color: #999; // #999
84 |
85 | @boxer-position-margin: 0; // 0
86 | @boxer-position-padding: 15px 15px 0 15px; // 15px 15px 0 15px
87 |
88 | // Caption
89 |
90 | @boxer-caption-font-size: 14px; // 14px
91 | @boxer-caption-text-color: #666; // #666
92 |
93 | @boxer-caption-margin: 0; // 0
94 | @boxer-caption-padding: 15px; // 15px
95 |
96 | // Error
97 |
98 | @boxer-error-font-size: 14px; // 14px
99 | @boxer-error-text-color: #900; // #900
100 |
101 | @boxer-error-width: 250px; // 250px
102 | @boxer-error-margin: 0; // 0
103 | @boxer-error-padding: 25px; // 25px
104 |
105 |
106 | // Mobile
107 |
108 | @boxer-mobile-background: #111; // #111
109 |
110 |
111 | // Mobile Close
112 |
113 | @boxer-mobile-close-font-size: 28px; // 28px
114 | @boxer-mobile-close-font-weight: 700; // 700
115 | @boxer-mobile-close-text-color: #ccc; // #ccc
116 | @boxer-mobile-close-background: #111; // #111
117 | @boxer-mobile-close-width: 40px; // 40px
118 | @boxer-mobile-close-height: 40px; // 40px
119 |
120 | // Mobile Container
121 |
122 | @boxer-mobile-container-background: #111; // #111
123 |
124 | // Mobile Content
125 |
126 | @boxer-mobile-content-background: #111; // #111
127 |
128 | // Mobile Controls
129 |
130 | @boxer-mobile-control-width: 50px; // 50px
131 | @boxer-mobile-control-background: #111; // #111
132 |
133 |
134 | // Control Carets
135 |
136 | @boxer-mobile-control-carent-color: #eee; // #eee
137 |
138 | // Mobile Meta
139 |
140 | @boxer-mobile-meta-background: #111; // #111
141 | @boxer-mobile-meta-padding: 15px; // 15px
142 |
143 | // Mobile Position
144 |
145 | @boxer-mobile-position-font-size: 12px; // 12px
146 | @boxer-mobile-position-text-color: #999; // #999
147 |
148 | @boxer-mobile-position-margin: 0; // 0
149 | @boxer-mobile-position-padding: 0 15px 0 0; // 0 15px 0 0
150 |
151 | // Mobile Caption
152 |
153 | @boxer-mobile-caption-font-size: 14px; // 14px
154 | @boxer-mobile-caption-text-color: #eee; // #eee
155 |
156 | @boxer-mobile-caption-margin: 0; // 0
157 | @boxer-mobile-caption-padding: 0; // 0
158 |
159 | // Mobile Loading Icon
160 |
161 | @boxer-mobile-loading-color: #999; // #999
--------------------------------------------------------------------------------
/src/jquery.fs.boxer-styles.less:
--------------------------------------------------------------------------------
1 |
2 | .boxer-open {
3 |
4 | }
5 | .boxer-lock {
6 | overflow: hidden !important;
7 | }
8 |
9 | #boxer-overlay {
10 | width: 100%;
11 | height: 100%;
12 |
13 | position: fixed;
14 | top: 0;
15 | right: 0;
16 | bottom: 0;
17 | left: 0;
18 | z-index: @boxer-z-index;
19 |
20 | background: @boxer-overlay-background;
21 | opacity: 0;
22 | transition: opacity 0.25s linear;
23 |
24 | .boxer-open & {
25 | opacity: @boxer-overlay-opacity;
26 | }
27 | }
28 |
29 | #boxer {
30 | width: 200px;
31 | height: 200px;
32 |
33 | position: absolute;
34 | right: 0;
35 | left: 0;
36 | z-index: (@boxer-z-index + 1);
37 |
38 | background: @boxer-background;
39 | border-radius: @boxer-border-radius;
40 | box-shadow: @boxer-box-shadow;
41 | opacity: 0;
42 | margin: 0 auto;
43 | padding: @boxer-padding;
44 |
45 | * {
46 | transition: none;
47 | }
48 |
49 | &,
50 | & * {
51 | user-select: none !important;
52 | }
53 |
54 | &,
55 | & *,
56 | & *:before,
57 | & *:after {
58 | box-sizing: border-box;
59 | }
60 |
61 | // .fixed
62 |
63 | &.fixed {
64 | position: fixed;
65 | top: 0;
66 | bottom: 0;
67 |
68 | margin: auto;
69 | }
70 |
71 | // .inline
72 |
73 | &.inline {
74 | padding: @boxer-inline-padding;
75 | }
76 |
77 | // .animating
78 |
79 | &.animating {
80 | transition:
81 | height @boxer-transition-duration @boxer-transition-timing,
82 | width @boxer-transition-duration @boxer-transition-timing,
83 | opacity @boxer-transition-duration linear,
84 | top @boxer-transition-duration @boxer-transition-timing;
85 |
86 | .boxer-container {
87 | transition: opacity @boxer-transition-duration linear @boxer-transition-duration;
88 | }
89 | }
90 |
91 | // .boxer-open
92 |
93 | .boxer-open & {
94 | opacity: 1;
95 | }
96 |
97 | &.loading .boxer-container {
98 | opacity: 0;
99 | transition: opacity @boxer-transition-duration linear;
100 | }
101 |
102 | // .boxer-close
103 |
104 | .boxer-close {
105 | width: @boxer-close-height;
106 | height: @boxer-close-height;
107 |
108 | position: absolute;
109 | top: -@boxer-close-height / 4;
110 | right: -@boxer-close-width / 4;
111 | z-index: (@boxer-z-index + 5);
112 |
113 | background: @boxer-close-background;
114 | border-radius: @boxer-close-border-radius;
115 | cursor: pointer;
116 | display: block;
117 | overflow: hidden;
118 | padding: 0;
119 | text-indent: 200%;
120 | white-space: nowrap;
121 |
122 | &:before {
123 | position: absolute;
124 | top: 0;
125 | right: 0;
126 | bottom: 0;
127 | left: 0;
128 |
129 | color: @boxer-close-color;
130 | content: "\00d7";
131 | display: block;
132 | font-size: @boxer-close-font-size;
133 | font-weight: @boxer-close-font-weight;
134 | line-height: @boxer-close-height;
135 | margin: auto;
136 | text-align: center;
137 | text-indent: 0;
138 | transition: color 0.15s linear;
139 | }
140 |
141 | // IE8 - opacity check
142 |
143 | .no-opacity & {
144 | text-indent: -999px;
145 | }
146 | }
147 |
148 | // .boxer-loading
149 |
150 | .boxer-loading {
151 | width: @boxer-loading-width;
152 | height: @boxer-loading-height;
153 |
154 | position: absolute;
155 | top: 0;
156 | right: 0;
157 | bottom: 0;
158 | left: 0;
159 | z-index: (@boxer-z-index + 5);
160 |
161 | display: block;
162 | margin: auto;
163 | opacity: 0;
164 | transition: opacity @boxer-transition-duration linear;
165 |
166 | &:before,
167 | &:after {
168 | width: 100%;
169 | height: 100%;
170 |
171 | position: absolute;
172 | top: 0;
173 | right: 0;
174 | bottom: 0;
175 | left: 0;
176 |
177 | border-radius: 110%;
178 | content: '';
179 | display: block;
180 | }
181 |
182 | &:before {
183 | border: @boxer-loading-size solid fade(@boxer-loading-color, 25);
184 | }
185 |
186 | &:after {
187 | animation: boxer-loading-spin @boxer-loading-animation-duration linear infinite;
188 | border: @boxer-loading-size solid transparent;
189 | border-top-color: @boxer-loading-color;
190 | }
191 | }
192 |
193 | &.loading .boxer-loading {
194 | opacity: 1;
195 | }
196 |
197 | @keyframes boxer-loading-spin {
198 | from {
199 | transform: rotate(0deg);
200 | }
201 |
202 | to {
203 | transform: rotate(360deg);
204 | }
205 | }
206 |
207 | // .boxer-container
208 |
209 | .boxer-container {
210 | width: 100%;
211 | height: 100%;
212 |
213 | position: relative;
214 | z-index: (@boxer-z-index + 3);
215 |
216 | background: @boxer-container-background;
217 | overflow: hidden;
218 | }
219 |
220 | // .boxer-content
221 |
222 | .boxer-content {
223 | width: 100%;
224 |
225 | background: @boxer-content-background;
226 | opacity: 1;
227 | overflow: hidden;
228 | padding: 0;
229 | }
230 |
231 | // Inline .boxer-content
232 |
233 | &.inline .boxer-content,
234 | &.iframe .boxer-content {
235 | width: auto;
236 | }
237 |
238 | // .boxer-image
239 |
240 | .boxer-image {
241 | float: left;
242 | }
243 |
244 | // .boxer-video
245 |
246 | .boxer-video {
247 | width: 100%;
248 | height: 100%;
249 | }
250 |
251 | // .boxer-iframe
252 |
253 | .boxer-iframe {
254 | width: 100%;
255 | height: 100%;
256 |
257 | border: none;
258 | float: left;
259 | overflow: auto;
260 | }
261 |
262 | // .boxer-meta
263 |
264 | .boxer-meta {
265 | clear: both;
266 | }
267 |
268 | // .boxer-controls
269 |
270 | .boxer-control {
271 | width: @boxer-control-width;
272 | height: @boxer-control-height;
273 |
274 | position: absolute;
275 | top: 0;
276 |
277 | background: @boxer-control-background;
278 | border-radius: @boxer-control-border-radius;
279 | box-shadow: @boxer-control-box-shadow;
280 | cursor: pointer;
281 | display: block;
282 | margin-right: auto;
283 | margin-left: auto;
284 | opacity: @boxer-control-opacity;
285 | overflow: hidden;
286 | text-indent: 200%;
287 | transition: opacity @boxer-control-transition-duration linear;
288 | white-space: nowrap;
289 |
290 | // Caret
291 | @boxer-control-caret-width: (@boxer-control-caret-size * 1.3);
292 | @boxer-control-carent-margin: (@boxer-control-width - (@boxer-control-caret-size * 1.5)) / 2;
293 |
294 | &:before {
295 | width: 0;
296 | height: 0;
297 |
298 | position: absolute;
299 | top: 0;
300 | right: 0;
301 | bottom: 0;
302 | left: 0;
303 |
304 | content: '';
305 | margin: auto;
306 | }
307 |
308 | &.previous {
309 | left: @boxer-control-previous-left;
310 |
311 | &:before {
312 | border-top: @boxer-control-caret-size solid transparent;
313 | border-bottom: @boxer-control-caret-size solid transparent;
314 | border-right: @boxer-control-caret-width solid @boxer-control-caret-color;
315 | margin-left: @boxer-control-carent-margin;
316 | }
317 | }
318 |
319 | &.next {
320 | right: @boxer-control-next-right;
321 |
322 | &:before {
323 | border-top: @boxer-control-caret-size solid transparent;
324 | border-bottom: @boxer-control-caret-size solid transparent;
325 | border-left: @boxer-control-caret-width solid @boxer-control-caret-color;
326 | margin-right: @boxer-control-carent-margin;
327 | }
328 | }
329 |
330 | &.disabled {
331 | opacity: 0;
332 | }
333 |
334 | // IE8 - opacity check
335 |
336 | .no-opacity & {
337 | text-indent: -999px;
338 | }
339 | }
340 |
341 | // controls hovers
342 |
343 | .no-touch & .boxer-control {
344 | opacity: 0;
345 | }
346 |
347 | .no-touch &:hover .boxer-control {
348 | opacity: @boxer-control-opacity;
349 |
350 | &.disabled {
351 | opacity: 0;
352 | cursor: default !important;
353 | }
354 | }
355 |
356 | // .boxer-meta
357 |
358 | .boxer-meta {
359 | padding: @boxer-meta-padding;
360 | }
361 |
362 | // .boxer-position
363 |
364 | .boxer-position {
365 | color: @boxer-position-text-color;
366 | font-size: @boxer-position-font-size;
367 | margin: @boxer-position-margin;
368 | padding: @boxer-position-padding;
369 | }
370 |
371 | // .boxer-caption
372 |
373 | .boxer-caption {
374 | & p {
375 | color: @boxer-caption-text-color;
376 | font-size: @boxer-caption-font-size;
377 | margin: @boxer-caption-margin;
378 | padding: @boxer-caption-padding;
379 | }
380 |
381 | &.gallery p {
382 | padding-top: 0;
383 | }
384 | }
385 |
386 | // .boxer-error
387 |
388 | .boxer-error {
389 | width: @boxer-error-width;
390 |
391 | p {
392 | color: @boxer-error-text-color;
393 | font-size: @boxer-error-font-size;
394 | margin: @boxer-error-margin;
395 | padding: @boxer-error-padding;
396 | text-align: center;
397 | text-transform: uppercase;
398 | }
399 | }
400 |
401 | // .mobile
402 |
403 | &.mobile {
404 | width: 100%;
405 | height: 100%;
406 |
407 | position: fixed;
408 | top: 0;
409 | right: 0;
410 | bottom: 0;
411 | left: 0;
412 |
413 | background: @boxer-mobile-background;
414 | border-radius: 0;
415 | padding: @boxer-mobile-close-height 0 0;
416 |
417 | // .boxer-close
418 |
419 | .boxer-close,
420 | .boxer-close:hover {
421 | height: @boxer-mobile-close-height;
422 | width: @boxer-mobile-close-width;
423 |
424 | top: 0;
425 | right: 0;
426 |
427 | background: @boxer-mobile-close-background;
428 | border-radius: 0;
429 | }
430 |
431 | .boxer-close:before,
432 | .boxer-close:hover:before {
433 | color: @boxer-mobile-close-text-color;
434 | font-size: @boxer-mobile-close-font-size;
435 | font-weight: @boxer-mobile-close-font-weight;
436 | line-height: @boxer-mobile-close-height;
437 | }
438 |
439 | // .boxer-loading
440 |
441 | .boxer-loading:before {
442 | border-color: fade(@boxer-mobile-loading-color, 25);
443 | }
444 |
445 | .boxer-loading:after {
446 | border-top-color: @boxer-mobile-loading-color;
447 | }
448 |
449 | // .boxer-container
450 |
451 | .boxer-container {
452 | background: @boxer-mobile-container-background;
453 | }
454 |
455 | // .boxer-content
456 |
457 | .boxer-content {
458 | background-color: @boxer-mobile-content-background;
459 | }
460 |
461 | // .boxer-control
462 |
463 | .boxer-control {
464 | width: @boxer-mobile-control-width;
465 | height: 100%;
466 |
467 | background: @boxer-mobile-control-background;
468 | border-radius: 0;
469 | box-shadow: none;
470 | opacity: 1;
471 |
472 | // Caret
473 | @boxer-mobile-control-carent-margin: (@boxer-mobile-control-width - (@boxer-control-caret-size * 1.5)) / 2;
474 |
475 | &.previous {
476 | left: 0;
477 |
478 | &:before {
479 | border-right-color: @boxer-mobile-control-carent-color;
480 | margin-left: @boxer-mobile-control-carent-margin;
481 | }
482 | }
483 |
484 | &.next {
485 | right: 0;
486 |
487 | &:before {
488 | border-left-color: @boxer-mobile-control-carent-color;
489 | margin-right: @boxer-mobile-control-carent-margin;
490 | }
491 | }
492 | }
493 |
494 | // controls hovers
495 |
496 | .no-touch & .boxer-control,
497 | .no-touch &:hover .boxer-control {
498 | opacity: @boxer-control-opacity;
499 |
500 | &.disabled {
501 | opacity: 0;
502 | cursor: default !important;
503 | }
504 | }
505 |
506 | // .boxer-meta
507 | .boxer-meta {
508 | width: 100%;
509 |
510 | position: absolute;
511 | right: 0;
512 | bottom: 0;
513 | left: 0;
514 |
515 | background-color: @boxer-mobile-meta-background;
516 | padding: @boxer-mobile-meta-padding (@boxer-mobile-control-width + @boxer-mobile-meta-padding);
517 | }
518 |
519 | // .boxer-position
520 |
521 | .boxer-position {
522 | color: @boxer-mobile-position-text-color;
523 | font-size: @boxer-mobile-position-font-size;
524 | margin: @boxer-mobile-position-margin;
525 | padding: @boxer-mobile-position-padding;
526 | }
527 |
528 | // .boxer-captions
529 |
530 | .boxer-caption {
531 | p {
532 | color: @boxer-mobile-caption-text-color;
533 | font-size: @boxer-mobile-caption-font-size;
534 | margin: @boxer-mobile-caption-margin;
535 | padding: @boxer-mobile-caption-padding;
536 | }
537 | }
538 |
539 | // .boxer-image
540 |
541 | .boxer-image {
542 | transition: none !important;
543 | transform: translate(0, 0);
544 | }
545 |
546 | // .animated
547 |
548 | &.animated .boxer-image {
549 | transition: transform 0.25s ease-out !important;
550 | }
551 |
552 | // .inline / .iframe
553 |
554 | &.inline .boxer-content,
555 | &.iframe .boxer-content {
556 | overflow-x: hidden;
557 | overflow-y: scroll;
558 | -webkit-overflow-scrolling: touch;
559 | }
560 | }
561 | }
--------------------------------------------------------------------------------
/src/jquery.fs.boxer.js:
--------------------------------------------------------------------------------
1 | ;(function ($, window) {
2 | "use strict";
3 |
4 | var $body = null,
5 | data = {},
6 | trueMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test((window.navigator.userAgent||window.navigator.vendor||window.opera)),
7 | transitionEvent,
8 | transitionSupported;
9 |
10 | /**
11 | * @options
12 | * @param callback [function] <$.noop> "Funciton called after opening instance"
13 | * @param customClass [string] <''> "Class applied to instance"
14 | * @param extensions [array] <"jpg", "sjpg", "jpeg", "png", "gif"> "Image type extensions"
15 | * @param fixed [boolean] "Flag for fixed positioning"
16 | * @param formatter [function] <$.noop> "Caption format function"
17 | * @param labels.close [string] <'Close'> "Close button text"
18 | * @param labels.count [string] <'of'> "Gallery count separator text"
19 | * @param labels.next [string] <'Next'> "Gallery control text"
20 | * @param labels.previous [string] <'Previous'> "Gallery control text"
21 | * @param margin [int] <50> "Margin used when sizing (single side)"
22 | * @param minHeight [int] <100> "Minimum height of modal"
23 | * @param minWidth [int] <100> "Minimum width of modal"
24 | * @param mobile [boolean] "Flag to force 'mobile' rendering"
25 | * @param opacity [number] <0.75> "Overlay target opacity"
26 | * @param retina [boolean] "Use 'retina' sizing (half's natural sizes)"
27 | * @param requestKey [string] <'boxer'> "GET variable for ajax / iframe requests"
28 | * @param top [int] <0> "Target top position; over-rides centering"
29 | * @param videoRadio [number] <0.5625> "Video height / width ratio (9 / 16 = 0.5625)"
30 | * @param videoWidth [int] <600> "Video target width"
31 | */
32 | var options = {
33 | callback: $.noop,
34 | customClass: "",
35 | extensions: [ "jpg", "sjpg", "jpeg", "png", "gif" ],
36 | fixed: false,
37 | formatter: $.noop,
38 | labels: {
39 | close: "Close",
40 | count: "of",
41 | next: "Next",
42 | previous: "Previous"
43 | },
44 | margin: 50,
45 | minHeight: 100,
46 | minWidth: 100,
47 | mobile: false,
48 | opacity: 0.75,
49 | retina: false,
50 | requestKey: "boxer",
51 | top: 0,
52 | videoRatio: 0.5625,
53 | videoWidth: 600
54 | };
55 |
56 | /**
57 | * @events
58 | * @event open.boxer "Modal opened; triggered on window"
59 | * @event close.boxer "Modal closed; triggered on window"
60 | */
61 |
62 | var pub = {
63 |
64 | /**
65 | * @method
66 | * @name close
67 | * @description Closes active instance of plugin
68 | * @example $.boxer("close");
69 | */
70 | close: function() {
71 | if (typeof data.$boxer !== "undefined") {
72 | data.$boxer.off(".boxer");
73 | data.$overlay.trigger("click");
74 | }
75 | },
76 |
77 | /**
78 | * @method
79 | * @name defaults
80 | * @description Sets default plugin options
81 | * @param opts [object] <{}> "Options object"
82 | * @example $.boxer("defaults", opts);
83 | */
84 | defaults: function(opts) {
85 | options = $.extend(options, opts || {});
86 | return (typeof this === 'object') ? $(this) : false;
87 | },
88 |
89 | /**
90 | * @method
91 | * @name destroy
92 | * @description Removes plugin bindings
93 | * @example $(".target").boxer("destroy");
94 | */
95 | destroy: function() {
96 | return $(this).off(".boxer");
97 | },
98 |
99 | /**
100 | * @method
101 | * @name resize
102 | * @description Triggers resize of instance
103 | * @example $.boxer("resize");
104 | * @param height [int | false] "Target height or false to auto size"
105 | * @param width [int | false] "Target width or false to auto size"
106 | */
107 | resize: function(e) {
108 | if (typeof data.$boxer !== "undefined") {
109 | if (typeof e !== "object") {
110 | data.targetHeight = arguments[0];
111 | data.targetWidth = arguments[1];
112 | }
113 |
114 | if (data.type === "element") {
115 | sizeContent(data.$content.find(">:first-child"));
116 | } else if (data.type === "image") {
117 | sizeImage();
118 | } else if (data.type === "video") {
119 | sizeVideo();
120 | }
121 | size();
122 | }
123 |
124 | return $(this);
125 | }
126 | };
127 |
128 | /**
129 | * @method private
130 | * @name init
131 | * @description Initializes plugin
132 | * @param opts [object] "Initialization options"
133 | */
134 | function init(opts) {
135 | options.formatter = formatCaption;
136 |
137 | $body = $("body");
138 | transitionEvent = getTransitionEvent();
139 | transitionSupported = (transitionEvent !== false);
140 |
141 | // no transitions :(
142 | if (!transitionSupported) {
143 | transitionEvent = "transitionend.boxer";
144 | }
145 |
146 | return $(this).on("click.boxer", $.extend({}, options, opts || {}), build);
147 | }
148 |
149 | /**
150 | * @method private
151 | * @name build
152 | * @description Builds target instance
153 | * @param e [object] "Event data"
154 | */
155 | function build(e) {
156 | if (typeof data.$boxer === "undefined") {
157 | // Check target type
158 | var $target = $(this),
159 | $object = e.data.$object,
160 | source = ($target[0].href) ? $target[0].href || "" : "",
161 | hash = ($target[0].hash) ? $target[0].hash || "" : "",
162 | sourceParts = source.toLowerCase().split(".").pop().split(/\#|\?/),
163 | extension = sourceParts[0],
164 | type = $target.data("boxer-type") || "",
165 | isImage = ( (type === "image") || ($.inArray(extension, e.data.extensions) > -1 || source.substr(0, 10) === "data:image") ),
166 | isVideo = ( source.indexOf("youtube.com/embed") > -1 || source.indexOf("player.vimeo.com/video") > -1 ),
167 | isUrl = ( (type === "url") || (!isImage && !isVideo && source.substr(0, 4) === "http" && !hash) ),
168 | isElement = ( (type === "element") || (!isImage && !isVideo && !isUrl && (hash.substr(0, 1) === "#")) ),
169 | isObject = ( (typeof $object !== "undefined") );
170 |
171 | if (isElement) {
172 | source = hash;
173 | }
174 |
175 | // Check if boxer is already active, retain default click
176 | if ($("#boxer").length > 1 || !(isImage || isVideo || isUrl || isElement || isObject)) {
177 | return;
178 | }
179 |
180 | // Kill event
181 | killEvent(e);
182 |
183 | // Cache internal data
184 | data = $.extend({}, {
185 | $window: $(window),
186 | $body: $("body"),
187 | $target: $target,
188 | $object: $object,
189 | visible: false,
190 | resizeTimer: null,
191 | touchTimer: null,
192 | gallery: {
193 | active: false
194 | },
195 | isMobile: (trueMobile || e.data.mobile),
196 | isAnimating: true,
197 | oldContentHeight: 0,
198 | oldContentWidth: 0
199 | }, e.data);
200 |
201 | // Double the margin
202 | data.margin *= 2;
203 |
204 | if (isImage) {
205 | data.type = "image";
206 | } else if (isVideo) {
207 | data.type = "video";
208 | } else {
209 | data.type = "element";
210 | }
211 |
212 | if (isImage || isVideo) {
213 | // Check for gallery
214 | var id = data.$target.data("gallery") || data.$target.attr("rel"); // backwards compatibility
215 |
216 | if (typeof id !== "undefined" && id !== false) {
217 | data.gallery.active = true;
218 | data.gallery.id = id;
219 | data.gallery.$items = $("a[data-gallery= " + data.gallery.id + "], a[rel= " + data.gallery.id + "]"); // backwards compatibility
220 | data.gallery.index = data.gallery.$items.index(data.$target);
221 | data.gallery.total = data.gallery.$items.length - 1;
222 | }
223 | }
224 |
225 | // Assemble HTML
226 | var html = '';
227 | if (!data.isMobile) {
228 | html += '';
229 | }
230 | html += '';
244 | html += '
' + data.labels.close + '';
245 | html += '
';
246 | html += '
';
247 | html += '
';
248 | if (isImage || isVideo) {
249 | html += '
'; //container, content, boxer
270 |
271 | // Modify Dom
272 | data.$body.append(html);
273 |
274 | // Cache jquery objects
275 | data.$overlay = $("#boxer-overlay");
276 | data.$boxer = $("#boxer");
277 | data.$container = data.$boxer.find(".boxer-container");
278 | data.$content = data.$boxer.find(".boxer-content");
279 | data.$meta = data.$boxer.find(".boxer-meta");
280 | data.$position = data.$boxer.find(".boxer-position");
281 | data.$caption = data.$boxer.find(".boxer-caption");
282 | data.$controls = data.$boxer.find(".boxer-control");
283 |
284 | data.paddingVertical = (!data.isMobile) ? (parseInt(data.$boxer.css("paddingTop"), 10) + parseInt(data.$boxer.css("paddingBottom"), 10)) : (data.$boxer.find(".boxer-close").outerHeight() / 2);
285 | data.paddingHorizontal = (!data.isMobile) ? (parseInt(data.$boxer.css("paddingLeft"), 10) + parseInt(data.$boxer.css("paddingRight"), 10)) : 0;
286 | data.contentHeight = data.$boxer.outerHeight() - data.paddingVertical;
287 | data.contentWidth = data.$boxer.outerWidth() - data.paddingHorizontal;
288 | data.controlHeight = data.$controls.outerHeight();
289 |
290 | // Center
291 | center();
292 |
293 | // Update gallery
294 | if (data.gallery.active) {
295 | updateControls();
296 | }
297 |
298 | // Bind events
299 | data.$window.on("resize.boxer", pub.resize)
300 | .on("keydown.boxer", onKeypress);
301 |
302 | data.$body.on("touchstart.boxer click.boxer", "#boxer-overlay, #boxer .boxer-close", onClose)
303 | .on("touchmove.boxer", killEvent);
304 |
305 | if (data.gallery.active) {
306 | data.$boxer.on("touchstart.boxer click.boxer", ".boxer-control", advanceGallery);
307 | }
308 |
309 | data.$boxer.on(transitionEvent, function(e) {
310 | killEvent(e);
311 |
312 | if ($(e.target).is(data.$boxer)) {
313 | data.$boxer.off(transitionEvent);
314 |
315 | if (isImage) {
316 | loadImage(source);
317 | } else if (isVideo) {
318 | loadVideo(source);
319 | } else if (isUrl) {
320 | loadURL(source);
321 | } else if (isElement) {
322 | cloneElement(source);
323 | } else if (isObject) {
324 | appendObject(data.$object);
325 | } else {
326 | $.error("BOXER: '" + source + "' is not valid.");
327 | }
328 | }
329 | });
330 |
331 | $body.addClass("boxer-open");
332 |
333 | if (!transitionSupported) {
334 | data.$boxer.trigger(transitionEvent);
335 | }
336 |
337 | if (isObject) {
338 | return data.$boxer;
339 | }
340 | }
341 | }
342 |
343 | /**
344 | * @method private
345 | * @name onClose
346 | * @description Closes active instance
347 | * @param e [object] "Event data"
348 | */
349 | function onClose(e) {
350 | killEvent(e);
351 |
352 | if (typeof data.$boxer !== "undefined") {
353 | data.$boxer.on(transitionEvent, function(e) {
354 | killEvent(e);
355 |
356 | if ($(e.target).is(data.$boxer)) {
357 | data.$boxer.off(transitionEvent);
358 |
359 | data.$overlay.remove();
360 | data.$boxer.remove();
361 |
362 | // reset data
363 | data = {};
364 | }
365 | }).addClass("animating");
366 |
367 | $body.removeClass("boxer-open");
368 |
369 | if (!transitionSupported) {
370 | data.$boxer.trigger(transitionEvent);
371 | }
372 |
373 | clearTimer(data.resizeTimer);
374 |
375 | // Clean up
376 | data.$window.off("resize.boxer")
377 | .off("keydown.boxer");
378 |
379 | data.$body.off(".boxer")
380 | .removeClass("boxer-open");
381 |
382 | if (data.gallery.active) {
383 | data.$boxer.off(".boxer");
384 | }
385 |
386 | if (data.isMobile) {
387 | if (data.type === "image" && data.gallery.active) {
388 | data.$container.off(".boxer");
389 | }
390 | }
391 |
392 | data.$window.trigger("close.boxer");
393 | }
394 | }
395 |
396 | /**
397 | * @method private
398 | * @name open
399 | * @description Opens active instance
400 | */
401 | function open() {
402 | var position = calculatePosition(),
403 | durration = data.isMobile ? 0 : data.duration;
404 |
405 | if (!data.isMobile) {
406 | data.$controls.css({
407 | marginTop: ((data.contentHeight - data.controlHeight - data.metaHeight) / 2)
408 | });
409 | }
410 |
411 | if (!data.visible && data.isMobile && data.gallery.active) {
412 | data.$content.on("touchstart.boxer", ".boxer-image", onTouchStart);
413 | }
414 |
415 | if (data.isMobile || data.fixed) {
416 | data.$body.addClass("boxer-open");
417 | }
418 |
419 | data.$boxer.on(transitionEvent, function(e) {
420 | killEvent(e);
421 |
422 | if ($(e.target).is(data.$boxer)) {
423 | data.$boxer.off(transitionEvent);
424 |
425 | data.$container.on(transitionEvent, function(e) {
426 | killEvent(e);
427 |
428 | if ($(e.target).is(data.$container)) {
429 | data.$container.off(transitionEvent);
430 |
431 | data.$boxer.removeClass("animating");
432 |
433 | data.isAnimating = false;
434 | }
435 | });
436 |
437 | data.$boxer.removeClass("loading");
438 |
439 | if (!transitionSupported) {
440 | data.$content.trigger(transitionEvent);
441 | }
442 |
443 | data.visible = true;
444 |
445 | // Fire callback + event
446 | data.callback.apply(data.$boxer);
447 | data.$window.trigger("open.boxer");
448 |
449 | // Start preloading
450 | if (data.gallery.active) {
451 | preloadGallery();
452 | }
453 | }
454 | });
455 |
456 | if (!data.isMobile) {
457 | data.$boxer.css({
458 | height: data.contentHeight + data.paddingVertical,
459 | width: data.contentWidth + data.paddingHorizontal,
460 | top: (!data.fixed) ? position.top : 0
461 | });
462 | }
463 |
464 | // Trigger event in case the content size hasn't changed
465 | var contentHasChanged = (data.oldContentHeight !== data.contentHeight || data.oldContentWidth !== data.contentWidth);
466 |
467 | if (data.isMobile || !transitionSupported || !contentHasChanged) {
468 | data.$boxer.trigger(transitionEvent);
469 | }
470 |
471 | // Track content size changes
472 | data.oldContentHeight = data.contentHeight;
473 | data.oldContentWidth = data.contentWidth;
474 | }
475 |
476 | /**
477 | * @method private
478 | * @name size
479 | * @description Sizes active instance
480 | */
481 | function size() {
482 | if (data.visible && !data.isMobile) {
483 | var position = calculatePosition();
484 |
485 | data.$controls.css({
486 | marginTop: ((data.contentHeight - data.controlHeight - data.metaHeight) / 2)
487 | });
488 |
489 | data.$boxer.css({
490 | height: data.contentHeight + data.paddingVertical,
491 | width: data.contentWidth + data.paddingHorizontal,
492 | top: (!data.fixed) ? position.top : 0
493 | });
494 | }
495 | }
496 |
497 | /**
498 | * @method private
499 | * @name center
500 | * @description Centers instance
501 | */
502 | function center() {
503 | var position = calculatePosition();
504 |
505 | data.$boxer.css({
506 | top: (!data.fixed) ? position.top : 0
507 | });
508 | }
509 |
510 | /**
511 | * @method private
512 | * @name calculatePosition
513 | * @description Calculates positions
514 | * @return [object] "Object containing top and left positions"
515 | */
516 | function calculatePosition() {
517 | if (data.isMobile) {
518 | return {
519 | left: 0,
520 | top: 0
521 | };
522 | }
523 |
524 | var pos = {
525 | left: (data.$window.width() - data.contentWidth - data.paddingHorizontal) / 2,
526 | top: (data.top <= 0) ? ((data.$window.height() - data.contentHeight - data.paddingVertical) / 2) : data.top
527 | };
528 |
529 | if (data.fixed !== true) {
530 | pos.top += data.$window.scrollTop();
531 | }
532 |
533 | return pos;
534 | }
535 |
536 | /**
537 | * @method private
538 | * @name formatCaption
539 | * @description Formats caption
540 | * @param $target [jQuery object] "Target element"
541 | */
542 | function formatCaption($target) {
543 | var title = $target.attr("title");
544 | return (title !== undefined && title.trim() !== "") ? '
' + title.trim() + '
' : "";
545 | }
546 |
547 | /**
548 | * @method private
549 | * @name loadImage
550 | * @description Loads source image
551 | * @param source [string] "Source image URL"
552 | */
553 | function loadImage(source) {
554 | // Cache current image
555 | data.$image = $("
![]()
");
556 |
557 | data.$image.load(function() {
558 | data.$image.off("load, error");
559 |
560 | var naturalSize = calculateNaturalSize(data.$image);
561 |
562 | data.naturalHeight = naturalSize.naturalHeight;
563 | data.naturalWidth = naturalSize.naturalWidth;
564 |
565 | if (data.retina) {
566 | data.naturalHeight /= 2;
567 | data.naturalWidth /= 2;
568 | }
569 |
570 | data.$content.prepend(data.$image);
571 |
572 | if (data.$caption.html() === "") {
573 | data.$caption.hide();
574 | } else {
575 | data.$caption.show();
576 | }
577 |
578 | // Size content to be sure it fits the viewport
579 | sizeImage();
580 | open();
581 | }).error(loadError)
582 | .attr("src", source)
583 | .addClass("boxer-image");
584 |
585 | // If image has already loaded into cache, trigger load event
586 | if (data.$image[0].complete || data.$image[0].readyState === 4) {
587 | data.$image.trigger("load");
588 | }
589 | }
590 |
591 | /**
592 | * @method private
593 | * @name sizeImage
594 | * @description Sizes image to fit in viewport
595 | * @param count [int] "Number of resize attempts"
596 | */
597 | function sizeImage() {
598 | var count = 0;
599 |
600 | data.windowHeight = data.viewportHeight = data.$window.height() - data.paddingVertical;
601 | data.windowWidth = data.viewportWidth = data.$window.width() - data.paddingHorizontal;
602 |
603 | data.contentHeight = Infinity;
604 | data.contentWidth = Infinity;
605 |
606 | data.imageMarginTop = 0;
607 | data.imageMarginLeft = 0;
608 |
609 | while (data.contentHeight > data.viewportHeight && count < 2) {
610 | data.imageHeight = (count === 0) ? data.naturalHeight : data.$image.outerHeight();
611 | data.imageWidth = (count === 0) ? data.naturalWidth : data.$image.outerWidth();
612 | data.metaHeight = (count === 0) ? 0 : data.metaHeight;
613 |
614 | if (count === 0) {
615 | data.ratioHorizontal = data.imageHeight / data.imageWidth;
616 | data.ratioVertical = data.imageWidth / data.imageHeight;
617 |
618 | data.isWide = (data.imageWidth > data.imageHeight);
619 | }
620 |
621 | // Double check min and max
622 | if (data.imageHeight < data.minHeight) {
623 | data.minHeight = data.imageHeight;
624 | }
625 | if (data.imageWidth < data.minWidth) {
626 | data.minWidth = data.imageWidth;
627 | }
628 |
629 | if (data.isMobile) {
630 | // Get meta height before sizing
631 | data.$meta.css({
632 | width: data.windowWidth
633 | });
634 | data.metaHeight = data.$meta.outerHeight(true);
635 |
636 | // Content match viewport
637 | data.contentHeight = data.viewportHeight - data.paddingVertical;
638 | data.contentWidth = data.viewportWidth - data.paddingHorizontal;
639 |
640 | fitImage();
641 |
642 | data.imageMarginTop = (data.contentHeight - data.targetImageHeight - data.metaHeight) / 2;
643 | data.imageMarginLeft = (data.contentWidth - data.targetImageWidth) / 2;
644 | } else {
645 | // Viewport should match window, less margin, padding and meta
646 | if (count === 0) {
647 | data.viewportHeight -= (data.margin + data.paddingVertical);
648 | data.viewportWidth -= (data.margin + data.paddingHorizontal);
649 | }
650 | data.viewportHeight -= data.metaHeight;
651 |
652 | fitImage();
653 |
654 | data.contentHeight = data.targetImageHeight;
655 | data.contentWidth = data.targetImageWidth;
656 | }
657 |
658 | // Modify DOM
659 |
660 | data.$meta.css({
661 | width: data.contentWidth
662 | });
663 |
664 | data.$image.css({
665 | height: data.targetImageHeight,
666 | width: data.targetImageWidth,
667 | marginTop: data.imageMarginTop,
668 | marginLeft: data.imageMarginLeft
669 | });
670 |
671 | if (!data.isMobile) {
672 | data.metaHeight = data.$meta.outerHeight(true);
673 | data.contentHeight += data.metaHeight;
674 | }
675 |
676 | count ++;
677 | }
678 | }
679 |
680 | /**
681 | * @method private
682 | * @name fitImage
683 | * @description Calculates target image size
684 | */
685 | function fitImage() {
686 | var height = (!data.isMobile) ? data.viewportHeight : data.contentHeight - data.metaHeight,
687 | width = (!data.isMobile) ? data.viewportWidth : data.contentWidth;
688 |
689 | if (data.isWide) {
690 | //WIDE
691 | data.targetImageWidth = width;
692 | data.targetImageHeight = data.targetImageWidth * data.ratioHorizontal;
693 |
694 | if (data.targetImageHeight > height) {
695 | data.targetImageHeight = height;
696 | data.targetImageWidth = data.targetImageHeight * data.ratioVertical;
697 | }
698 | } else {
699 | //TALL
700 | data.targetImageHeight = height;
701 | data.targetImageWidth = data.targetImageHeight * data.ratioVertical;
702 |
703 | if (data.targetImageWidth > width) {
704 | data.targetImageWidth = width;
705 | data.targetImageHeight = data.targetImageWidth * data.ratioHorizontal;
706 | }
707 | }
708 |
709 | // MAX
710 | if (data.targetImageWidth > data.imageWidth || data.targetImageHeight > data.imageHeight) {
711 | data.targetImageHeight = data.imageHeight;
712 | data.targetImageWidth = data.imageWidth;
713 | }
714 |
715 | // MIN
716 | if (data.targetImageWidth < data.minWidth || data.targetImageHeight < data.minHeight) {
717 | if (data.targetImageWidth < data.minWidth) {
718 | data.targetImageWidth = data.minWidth;
719 | data.targetImageHeight = data.targetImageWidth * data.ratioHorizontal;
720 | } else {
721 | data.targetImageHeight = data.minHeight;
722 | data.targetImageWidth = data.targetImageHeight * data.ratioVertical;
723 | }
724 | }
725 | }
726 |
727 | /**
728 | * @method private
729 | * @name loadVideo
730 | * @description Loads source video
731 | * @param source [string] "Source video URL"
732 | */
733 | function loadVideo(source) {
734 | data.$videoWrapper = $('
');
735 | data.$video = $('
');
736 |
737 | data.$video.attr("src", source)
738 | .addClass("boxer-video")
739 | .prependTo(data.$videoWrapper);
740 |
741 | data.$content.prepend(data.$videoWrapper);
742 |
743 | sizeVideo();
744 | open();
745 | }
746 |
747 | /**
748 | * @method private
749 | * @name sizeVideo
750 | * @description Sizes video to fit in viewport
751 | */
752 | function sizeVideo() {
753 | // Set initial vars
754 | data.windowHeight = data.viewportHeight = data.contentHeight = data.$window.height() - data.paddingVertical;
755 | data.windowWidth = data.viewportWidth = data.contentWidth = data.$window.width() - data.paddingHorizontal;
756 | data.videoMarginTop = 0;
757 | data.videoMarginLeft = 0;
758 |
759 | if (data.isMobile) {
760 | data.$meta.css({
761 | width: data.windowWidth
762 | });
763 | data.metaHeight = data.$meta.outerHeight(true);
764 | data.viewportHeight -= data.metaHeight;
765 |
766 | data.targetVideoWidth = data.viewportWidth;
767 | data.targetVideoHeight = data.targetVideoWidth * data.videoRatio;
768 |
769 | if (data.targetVideoHeight > data.viewportHeight) {
770 | data.targetVideoHeight = data.viewportHeight;
771 | data.targetVideoWidth = data.targetVideoHeight / data.videoRatio;
772 | }
773 |
774 | data.videoMarginTop = (data.viewportHeight - data.targetVideoHeight) / 2;
775 | data.videoMarginLeft = (data.viewportWidth - data.targetVideoWidth) / 2;
776 | } else {
777 | data.viewportHeight = data.windowHeight - data.margin;
778 | data.viewportWidth = data.windowWidth - data.margin;
779 |
780 | data.targetVideoWidth = (data.videoWidth > data.viewportWidth) ? data.viewportWidth : data.videoWidth;
781 | if (data.targetVideoWidth < data.minWidth) {
782 | data.targetVideoWidth = data.minWidth;
783 | }
784 | data.targetVideoHeight = data.targetVideoWidth * data.videoRatio;
785 |
786 | data.contentHeight = data.targetVideoHeight;
787 | data.contentWidth = data.targetVideoWidth;
788 | }
789 |
790 | // Update dom
791 |
792 | data.$meta.css({
793 | width: data.contentWidth
794 | });
795 |
796 | data.$videoWrapper.css({
797 | height: data.targetVideoHeight,
798 | width: data.targetVideoWidth,
799 | marginTop: data.videoMarginTop,
800 | marginLeft: data.videoMarginLeft
801 | });
802 |
803 | if (!data.isMobile) {
804 | data.metaHeight = data.$meta.outerHeight(true);
805 | data.contentHeight = data.targetVideoHeight + data.metaHeight;
806 | }
807 | }
808 |
809 | /**
810 | * @method private
811 | * @name preloadGallery
812 | * @description Preloads previous and next images in gallery for faster rendering
813 | * @param e [object] "Event Data"
814 | */
815 | function preloadGallery(e) {
816 | var source = '';
817 |
818 | if (data.gallery.index > 0) {
819 | source = data.gallery.$items.eq(data.gallery.index - 1).attr("href");
820 | if (source.indexOf("youtube.com/embed") < 0 && source.indexOf("player.vimeo.com/video") < 0) {
821 | $('

');
822 | }
823 | }
824 | if (data.gallery.index < data.gallery.total) {
825 | source = data.gallery.$items.eq(data.gallery.index + 1).attr("href");
826 | if (source.indexOf("youtube.com/embed") < 0 && source.indexOf("player.vimeo.com/video") < 0) {
827 | $('

');
828 | }
829 | }
830 | }
831 |
832 | /**
833 | * @method private
834 | * @name advanceGallery
835 | * @description Advances gallery base on direction
836 | * @param e [object] "Event Data"
837 | */
838 | function advanceGallery(e) {
839 | killEvent(e);
840 |
841 | var $control = $(this);
842 | if (!data.isAnimating && !$control.hasClass("disabled")) {
843 | data.isAnimating = true;
844 |
845 | data.gallery.index += ($control.hasClass("next")) ? 1 : -1;
846 | if (data.gallery.index > data.gallery.total) {
847 | data.gallery.index = data.gallery.total;
848 | }
849 | if (data.gallery.index < 0) {
850 | data.gallery.index = 0;
851 | }
852 |
853 | data.$container.on(transitionEvent, function(e) {
854 | killEvent(e);
855 |
856 | if ($(e.target).is(data.$container)) {
857 | data.$container.off(transitionEvent);
858 |
859 | if (typeof data.$image !== 'undefined') {
860 | data.$image.remove();
861 | }
862 | if (typeof data.$videoWrapper !== 'undefined') {
863 | data.$videoWrapper.remove();
864 | }
865 | data.$target = data.gallery.$items.eq(data.gallery.index);
866 |
867 | data.$caption.html(data.formatter.apply(data.$body, [data.$target]));
868 | data.$position.find(".current").html(data.gallery.index + 1);
869 |
870 | var source = data.$target.attr("href"),
871 | isVideo = ( source.indexOf("youtube.com/embed") > -1 || source.indexOf("player.vimeo.com/video") > -1 );
872 |
873 | if (isVideo) {
874 | loadVideo(source);
875 | } else {
876 | loadImage(source);
877 | }
878 | updateControls();
879 | }
880 | });
881 |
882 | data.$boxer.addClass("loading animating");
883 |
884 | if (!transitionSupported) {
885 | data.$content.trigger(transitionEvent);
886 | }
887 | }
888 | }
889 |
890 | /**
891 | * @method private
892 | * @name updateControls
893 | * @description Updates gallery control states
894 | */
895 | function updateControls() {
896 | data.$controls.removeClass("disabled");
897 | if (data.gallery.index === 0) {
898 | data.$controls.filter(".previous").addClass("disabled");
899 | }
900 | if (data.gallery.index === data.gallery.total) {
901 | data.$controls.filter(".next").addClass("disabled");
902 | }
903 | }
904 |
905 | /**
906 | * @method private
907 | * @name onKeypress
908 | * @description Handles keypress in gallery
909 | * @param e [object] "Event data"
910 | */
911 | function onKeypress(e) {
912 | if (data.gallery.active && (e.keyCode === 37 || e.keyCode === 39)) {
913 | killEvent(e);
914 |
915 | data.$controls.filter((e.keyCode === 37) ? ".previous" : ".next").trigger("click");
916 | } else if (e.keyCode === 27) {
917 | data.$boxer.find(".boxer-close").trigger("click");
918 | }
919 | }
920 |
921 | /**
922 | * @method private
923 | * @name cloneElement
924 | * @description Clones target inline element
925 | * @param id [string] "Target element id"
926 | */
927 | function cloneElement(id) {
928 | var $clone = $(id).find(">:first-child").clone();
929 | appendObject($clone);
930 | }
931 |
932 | /**
933 | * @method private
934 | * @name loadURL
935 | * @description Load URL into iframe
936 | * @param source [string] "Target URL"
937 | */
938 | function loadURL(source) {
939 | source = source + ((source.indexOf("?") > -1) ? "&"+options.requestKey+"=true" : "?"+options.requestKey+"=true");
940 | var $iframe = $('
');
941 | appendObject($iframe);
942 | }
943 |
944 | /**
945 | * @method private
946 | * @name appendObject
947 | * @description Appends and sizes object
948 | * @param $object [jQuery Object] "Object to append"
949 | */
950 | function appendObject($object) {
951 | data.$content.append($object);
952 | sizeContent($object);
953 | open();
954 | }
955 |
956 | /**
957 | * @method private
958 | * @name sizeContent
959 | * @description Sizes jQuery object to fir in viewport
960 | * @param $object [jQuery Object] "Object to size"
961 | */
962 | function sizeContent($object) {
963 | data.windowHeight = data.$window.height() - data.paddingVertical;
964 | data.windowWidth = data.$window.width() - data.paddingHorizontal;
965 | data.objectHeight = $object.outerHeight(true);
966 | data.objectWidth = $object.outerWidth(true);
967 | data.targetHeight = data.targetHeight || data.$target.data("boxer-height");
968 | data.targetWidth = data.targetWidth || data.$target.data("boxer-width");
969 | data.maxHeight = (data.windowHeight < 0) ? options.minHeight : data.windowHeight;
970 | data.isIframe = $object.is("iframe");
971 | data.objectMarginTop = 0;
972 | data.objectMarginLeft = 0;
973 |
974 | if (!data.isMobile) {
975 | data.windowHeight -= data.margin;
976 | data.windowWidth -= data.margin;
977 | }
978 |
979 | data.contentHeight = (data.targetHeight !== undefined) ? data.targetHeight : (data.isIframe || data.isMobile) ? data.windowHeight : data.objectHeight;
980 | data.contentWidth = (data.targetWidth !== undefined) ? data.targetWidth : (data.isIframe || data.isMobile) ? data.windowWidth : data.objectWidth;
981 |
982 | if ((data.isIframe || data.isObject) && data.isMobile) {
983 | data.contentHeight = data.windowHeight;
984 | data.contentWidth = data.windowWidth;
985 | } else if (data.isObject) {
986 | data.contentHeight = (data.contentHeight > data.windowHeight) ? data.windowHeight : data.contentHeight;
987 | data.contentWidth = (data.contentWidth > data.windowWidth) ? data.windowWidth : data.contentWidth;
988 | }
989 | }
990 |
991 | /**
992 | * @method private
993 | * @name loadError
994 | * @description Error when resource fails to load
995 | * @param e [object] "Event data"
996 | */
997 | function loadError(e) {
998 | var $error = $('
');
999 |
1000 | // Clean up
1001 | data.type = "element";
1002 | data.$meta.remove();
1003 |
1004 | data.$image.off("load, error");
1005 |
1006 | appendObject($error);
1007 | }
1008 |
1009 | /**
1010 | * @method private
1011 | * @name onTouchStart
1012 | * @description Handle touch start event
1013 | * @param e [object] "Event data"
1014 | */
1015 | function onTouchStart(e) {
1016 | killEvent(e);
1017 | clearTimer(data.touchTimer);
1018 |
1019 | if (!data.isAnimating) {
1020 | var touch = (typeof e.originalEvent.targetTouches !== "undefined") ? e.originalEvent.targetTouches[0] : null;
1021 | data.xStart = (touch) ? touch.pageX : e.clientX;
1022 | data.leftPosition = 0;
1023 |
1024 | data.touchMax = Infinity;
1025 | data.touchMin = -Infinity;
1026 | data.edge = data.contentWidth * 0.25;
1027 |
1028 | if (data.gallery.index === 0) {
1029 | data.touchMax = 0;
1030 | }
1031 | if (data.gallery.index === data.gallery.total) {
1032 | data.touchMin = 0;
1033 | }
1034 |
1035 | data.$boxer.on("touchmove.boxer", onTouchMove)
1036 | .one("touchend.boxer", onTouchEnd);
1037 | }
1038 | }
1039 |
1040 | /**
1041 | * @method private
1042 | * @name onTouchMove
1043 | * @description Handles touchmove event
1044 | * @param e [object] "Event data"
1045 | */
1046 | function onTouchMove(e) {
1047 | var touch = (typeof e.originalEvent.targetTouches !== "undefined") ? e.originalEvent.targetTouches[0] : null;
1048 |
1049 | data.delta = data.xStart - ((touch) ? touch.pageX : e.clientX);
1050 |
1051 | // Only prevent event if trying to swipe
1052 | if (data.delta > 20) {
1053 | killEvent(e);
1054 | }
1055 |
1056 | data.canSwipe = true;
1057 |
1058 | var newLeft = -data.delta;
1059 | if (newLeft < data.touchMin) {
1060 | newLeft = data.touchMin;
1061 | data.canSwipe = false;
1062 | }
1063 | if (newLeft > data.touchMax) {
1064 | newLeft = data.touchMax;
1065 | data.canSwipe = false;
1066 | }
1067 |
1068 | data.$image.css({ transform: "translate3D("+newLeft+"px,0,0)" });
1069 |
1070 | data.touchTimer = startTimer(data.touchTimer, 300, function() { onTouchEnd(e); });
1071 | }
1072 |
1073 | /**
1074 | * @method private
1075 | * @name onTouchEnd
1076 | * @description Handles touchend event
1077 | * @param e [object] "Event data"
1078 | */
1079 | function onTouchEnd(e) {
1080 | killEvent(e);
1081 | clearTimer(data.touchTimer);
1082 |
1083 | data.$boxer.off("touchmove.boxer touchend.boxer");
1084 |
1085 | if (data.delta) {
1086 | data.$boxer.addClass("animated");
1087 | data.swipe = false;
1088 |
1089 | if (data.canSwipe && (data.delta > data.edge || data.delta < -data.edge)) {
1090 | data.swipe = true;
1091 | if (data.delta <= data.leftPosition) {
1092 | data.$image.css({ transform: "translate3D("+(data.contentWidth)+"px,0,0)" });
1093 | } else {
1094 | data.$image.css({ transform: "translate3D("+(-data.contentWidth)+"px,0,0)" });
1095 | }
1096 | } else {
1097 | data.$image.css({ transform: "translate3D(0,0,0)" });
1098 | }
1099 |
1100 | if (data.swipe) {
1101 | data.$controls.filter( (data.delta <= data.leftPosition) ? ".previous" : ".next" ).trigger("click");
1102 | }
1103 | startTimer(data.resetTimer, data.duration, function() {
1104 | data.$boxer.removeClass("animated");
1105 | });
1106 | }
1107 | }
1108 |
1109 | /**
1110 | * @method private
1111 | * @name calculateNaturalSize
1112 | * @description Determines natural size of target image
1113 | * @param $img [jQuery object] "Source image object"
1114 | * @return [object | boolean] "Object containing natural height and width values or false"
1115 | */
1116 | function calculateNaturalSize($img) {
1117 | var node = $img[0],
1118 | img = new Image();
1119 |
1120 | if (typeof node.naturalHeight !== "undefined") {
1121 | return {
1122 | naturalHeight: node.naturalHeight,
1123 | naturalWidth: node.naturalWidth
1124 | };
1125 | } else {
1126 | if (node.tagName.toLowerCase() === 'img') {
1127 | img.src = node.src;
1128 | return {
1129 | naturalHeight: img.height,
1130 | naturalWidth: img.width
1131 | };
1132 | }
1133 | }
1134 |
1135 | return false;
1136 | }
1137 |
1138 | /**
1139 | * @method private
1140 | * @name killEvent
1141 | * @description Prevents default and stops propagation on event
1142 | * @param e [object] "Event data"
1143 | */
1144 | function killEvent(e) {
1145 | if (e.preventDefault) {
1146 | e.stopPropagation();
1147 | e.preventDefault();
1148 | }
1149 | }
1150 |
1151 | /**
1152 | * @method private
1153 | * @name startTimer
1154 | * @description Starts an internal timer
1155 | * @param timer [int] "Timer ID"
1156 | * @param time [int] "Time until execution"
1157 | * @param callback [int] "Function to execute"
1158 | */
1159 | function startTimer(timer, time, callback) {
1160 | clearTimer(timer);
1161 | return setTimeout(callback, time);
1162 | }
1163 |
1164 | /**
1165 | * @method private
1166 | * @name clearTimer
1167 | * @description Clears an internal timer
1168 | * @param timer [int] "Timer ID"
1169 | */
1170 | function clearTimer(timer) {
1171 | if (timer) {
1172 | clearTimeout(timer);
1173 | timer = null;
1174 | }
1175 | }
1176 |
1177 | /**
1178 | * @method private
1179 | * @name getTransitionEvent
1180 | * @description Retuns a properly prefixed transitionend event
1181 | * @return [string] "Properly prefixed event"
1182 | */
1183 | function getTransitionEvent() {
1184 | var transitions = {
1185 | 'WebkitTransition': 'webkitTransitionEnd',
1186 | 'MozTransition': 'transitionend',
1187 | /* 'MSTransitionEnd': 'msTransition', */
1188 | /* 'msTransition': 'MSTransitionEnd' */
1189 | 'OTransition': 'oTransitionEnd',
1190 | 'transition': 'transitionend'
1191 | },
1192 | test = document.createElement('div');
1193 |
1194 | for (var type in transitions) {
1195 | if (transitions.hasOwnProperty(type) && type in test.style) {
1196 | return transitions[type];
1197 | }
1198 | }
1199 |
1200 | return false;
1201 | }
1202 |
1203 | $.fn.boxer = function(method) {
1204 | if (pub[method]) {
1205 | return pub[method].apply(this, Array.prototype.slice.call(arguments, 1));
1206 | } else if (typeof method === 'object' || !method) {
1207 | return init.apply(this, arguments);
1208 | }
1209 | return this;
1210 | };
1211 |
1212 | $.boxer = function($target, opts) {
1213 | if (pub[$target]) {
1214 | return pub[$target].apply(window, Array.prototype.slice.call(arguments, 1));
1215 | } else {
1216 | if ($target instanceof $) {
1217 | return build.apply(window, [{ data: $.extend({
1218 | $object: $target
1219 | }, options, opts || {}) }]);
1220 | }
1221 | }
1222 | };
1223 | })(jQuery, window);
--------------------------------------------------------------------------------
/src/jquery.fs.boxer.less:
--------------------------------------------------------------------------------
1 |
2 | @import "jquery.fs.boxer-config.less";
3 | @import "jquery.fs.boxer-styles.less";
--------------------------------------------------------------------------------