;(function ($) {
'use strict';
var _self = {
cache: {},
support: {},
objects: {},
/**
* Initializes the plugin
*
* @param {object} options
* @return {object}
*/
init: function (options) {
return this.each(function () {
$(this).unbind('click.lightcase').bind('click.lightcase', function (event) {
event.preventDefault();
$(this).lightcase('start', options);
});
});
},
/**
* Starts the plugin
*
* @param {object} options
* @return {void}
*/
start: function (options) {
_self.origin = lightcase.origin = this;
_self.settings = lightcase.settings = $.extend(true, {
idPrefix: 'lightcase-',
classPrefix: 'lightcase-',
attrPrefix: 'lc-',
transition: 'elastic',
transitionOpen: null,
transitionClose: null,
transitionIn: null,
transitionOut: null,
cssTransitions: true,
speedIn: 250,
speedOut: 250,
width: null,
height: null,
maxWidth: 800,
maxHeight: 500,
forceWidth: false,
forceHeight: false,
liveResize: true,
fullScreenModeForMobile: true,
mobileMatchExpression: /(iphone|ipod|ipad|android|blackberry|symbian)/,
disableShrink: false,
fixedRatio: true,
shrinkFactor: .75,
overlayOpacity: .9,
slideshow: false,
slideshowAutoStart: true,
breakBeforeShow: false,
timeout: 5000,
swipe: true,
useKeys: true,
useCategories: true,
useAsCollection: false,
navigateEndless: true,
closeOnOverlayClick: true,
title: null,
caption: null,
showTitle: true,
showCaption: true,
showSequenceInfo: true,
inline: {
width: 'auto',
height: 'auto'
},
ajax: {
width: 'auto',
height: 'auto',
type: 'get',
dataType: 'html',
data: {}
},
iframe: {
width: 800,
height: 500,
frameborder: 0
},
flash: {
width: 400,
height: 205,
wmode: 'transparent'
},
video: {
width: 400,
height: 225,
poster: '',
preload: 'auto',
controls: true,
autobuffer: true,
autoplay: true,
loop: false
},
attr: 'data-rel',
href: null,
type: null,
typeMapping: {
'image': 'jpg,jpeg,gif,png,bmp',
'flash': 'swf',
'video': 'mp4,mov,ogv,ogg,webm',
'iframe': 'html,php',
'ajax': 'json,txt',
'inline': '#'
},
errorMessage: function () {
return '<p class="' + _self.settings.classPrefix + 'error">' + _self.settings.labels['errorMessage'] + '</p>';
},
labels: {
'errorMessage': 'Source could not be found...',
'sequenceInfo.of': ' of ',
'close': 'Close',
'navigator.prev': 'Prev',
'navigator.next': 'Next',
'navigator.play': 'Play',
'navigator.pause': 'Pause'
},
markup: function () {
_self.objects.body.append(
_self.objects.overlay = $('<div id="' + _self.settings.idPrefix + 'overlay"></div>'),
_self.objects.loading = $('<div id="' + _self.settings.idPrefix + 'loading" class="' + _self.settings.classPrefix + 'icon-spin"></div>'),
_self.objects.case = $('<div id="' + _self.settings.idPrefix + 'case" aria-hidden="true" role="dialog"></div>')
);
_self.objects.case.after(
_self.objects.close = $('<a href="#" class="' + _self.settings.classPrefix + 'icon-close"><span>' + _self.settings.labels['close'] + '</span></a>'),
_self.objects.nav = $('<div id="' + _self.settings.idPrefix + 'nav"></div>')
);
_self.objects.nav.append(
_self.objects.prev = $('<a href="#" class="' + _self.settings.classPrefix + 'icon-prev"><span>' + _self.settings.labels['navigator.prev'] + '</span></a>').hide(),
_self.objects.next = $('<a href="#" class="' + _self.settings.classPrefix + 'icon-next"><span>' + _self.settings.labels['navigator.next'] + '</span></a>').hide(),
_self.objects.play = $('<a href="#" class="' + _self.settings.classPrefix + 'icon-play"><span>' + _self.settings.labels['navigator.play'] + '</span></a>').hide(),
_self.objects.pause = $('<a href="#" class="' + _self.settings.classPrefix + 'icon-pause"><span>' + _self.settings.labels['navigator.pause'] + '</span></a>').hide()
);
_self.objects.case.append(
_self.objects.content = $('<div id="' + _self.settings.idPrefix + 'content"></div>'),
_self.objects.info = $('<div id="' + _self.settings.idPrefix + 'info"></div>')
);
_self.objects.content.append(
_self.objects.contentInner = $('<div class="' + _self.settings.classPrefix + 'contentInner"></div>')
);
_self.objects.info.append(
_self.objects.sequenceInfo = $('<div id="' + _self.settings.idPrefix + 'sequenceInfo"></div>'),
_self.objects.title = $('<h4 id="' + _self.settings.idPrefix + 'title"></h4>'),
_self.objects.caption = $('<p id="' + _self.settings.idPrefix + 'caption"></p>')
);
},
onInit: {},
onStart: {},
onBeforeCalculateDimensions: {},
onAfterCalculateDimensions: {},
onBeforeShow: {},
onFinish: {},
onResize: {},
onClose: {},
onCleanup: {}
},
options,
// Load options from data-lc-options attribute
_self.origin.data ? _self.origin.data('lc-options') : {});
_self.objects.document = $('html');
_self.objects.body = $('body');
// Call onInit hook functions
_self._callHooks(_self.settings.onInit);
_self.objectData = _self._setObjectData(this);
_self._addElements();
_self._open();
_self.dimensions = _self.getViewportDimensions();
},
/**
* Getter method for objects
*
* @param {string} name
* @return {object}
*/
get: function (name) {
return _self.objects[name];
},
/**
* Getter method for objectData
*
* @return {object}
*/
getObjectData: function () {
return _self.objectData;
},
/**
* Sets the object data
*
* @param {object} object
* @return {object} objectData
*/
_setObjectData: function (object) {
var $object = $(object),
objectData = {
this: $(object),
title: _self.settings.title || $object.attr(_self._prefixAttributeName('title')) || $object.attr('title'),
caption: _self.settings.caption || $object.attr(_self._prefixAttributeName('caption')) || $object.children('img').attr('alt'),
url: _self._determineUrl(),
requestType: _self.settings.ajax.type,
requestData: _self.settings.ajax.data,
requestDataType: _self.settings.ajax.dataType,
rel: $object.attr(_self._determineAttributeSelector()),
type: _self.settings.type || _self._verifyDataType(_self._determineUrl()),
isPartOfSequence: _self.settings.useAsCollection || _self._isPartOfSequence($object.attr(_self.settings.attr), ':'),
isPartOfSequenceWithSlideshow: _self._isPartOfSequence($object.attr(_self.settings.attr), ':slideshow'),
currentIndex: $(_self._determineAttributeSelector()).index($object),
sequenceLength: $(_self._determineAttributeSelector()).length
};
// Add sequence info to objectData
objectData.sequenceInfo = (objectData.currentIndex + 1) + _self.settings.labels['sequenceInfo.of'] + objectData.sequenceLength;
// Add next/prev index
objectData.prevIndex = objectData.currentIndex - 1;
objectData.nextIndex = objectData.currentIndex + 1;
return objectData;
},
/**
* Prefixes a data attribute name with defined name from 'settings.attrPrefix'
* to ensure more uniqueness for all lightcase related/used attributes.
*
* @param {string} name
* @return {string}
*/
_prefixAttributeName: function (name) {
return 'data-' + _self.settings.attrPrefix + name;
},
/**
* Determines the link target considering 'settings.href' and data attributes
* but also with a fallback to the default 'href' value.
*
* @return {string}
*/
_determineLinkTarget: function () {
return _self.settings.href || $(_self.origin).attr(_self._prefixAttributeName('href')) || $(_self.origin).attr('href');
},
/**
* Determines the attribute selector to use, depending on
* whether categorized collections are beeing used or not.
*
* @return {string} selector
*/
_determineAttributeSelector: function () {
var $origin = $(_self.origin),
selector = '';
if (typeof _self.cache.selector !== 'undefined') {
selector = _self.cache.selector;
} else if (_self.settings.useCategories === true && $origin.attr(_self._prefixAttributeName('categories'))) {
var categories = $origin.attr(_self._prefixAttributeName('categories')).split(' ');
$.each(categories, function (index, category) {
if (index > 0) {
selector += ',';
}
selector += '[' + _self._prefixAttributeName('categories') + '~="' + category + '"]';
});
} else {
selector = '[' + _self.settings.attr + '="' + $origin.attr(_self.settings.attr) + '"]';
}
_self.cache.selector = selector;
return selector;
},
/**
* Determines the correct resource according to the
* current viewport and density.
*
* @return {string} url
*/
_determineUrl: function () {
var dataUrl = _self._verifyDataUrl(_self._determineLinkTarget()),
width = 0,
density = 0,
supportLevel = '',
url;
$.each(dataUrl, function (index, src) {
switch (_self._verifyDataType(src.url)) {
case 'video':
var video = document.createElement('video'),
videoType = _self._verifyDataType(src.url) + '/' + _self._getFileUrlSuffix(src.url);
// Check if browser can play this type of video format
if (supportLevel !== 'probably' && supportLevel !== video.canPlayType(videoType) && video.canPlayType(videoType) !== '') {
supportLevel = video.canPlayType(videoType);
url = src.url;
}
break;
default:
if (
// Check density
_self._devicePixelRatio() >= src.density &&
src.density >= density &&
// Check viewport width
_self._matchMedia()('screen and (min-width:' + src.width + 'px)').matches &&
src.width >= width
) {
width = src.width;
density = src.density;
url = src.url;
}
break;
}
});
return url;
},
/**
* Normalizes an url and returns information about the resource path,
* the viewport width as well as density if defined.
*
* @param {string} url Path to resource in format of an url or srcset
* @return {object}
*/
_normalizeUrl: function (url) {
var srcExp = /^\d+$/;
return url.split(',').map(function (str) {
var src = {
width: 0,
density: 0
};
str.trim().split(/\s+/).forEach(function (url, i) {
if (i === 0) {
return src.url = url;
}
var value = url.substring(0, url.length - 1),
lastChar = url[url.length - 1],
intVal = parseInt(value, 10),
floatVal = parseFloat(value);
if (lastChar === 'w' && srcExp.test(value)) {
src.width = intVal;
} else if (lastChar === 'h' && srcExp.test(value)) {
src.height = intVal;
} else if (lastChar === 'x' && !isNaN(floatVal)) {
src.density = floatVal;
}
});
return src;
});
},
/**
* Verifies if the link is part of a sequence
*
* @param {string} rel
* @param {string} expression
* @return {boolean}
*/
_isPartOfSequence: function (rel, expression) {
var getSimilarLinks = $('[' + _self.settings.attr + '="' + rel + '"]'),
regexp = new RegExp(expression);
return (regexp.test(rel) && getSimilarLinks.length > 1);
},
/**
* Verifies if the slideshow should be enabled
*
* @return {boolean}
*/
isSlideshowEnabled: function () {
return (_self.objectData.isPartOfSequence && (_self.settings.slideshow === true || _self.objectData.isPartOfSequenceWithSlideshow === true));
},
/**
* Loads the new content to show
*
* @return {void}
*/
_loadContent: function () {
if (_self.cache.originalObject) {
_self._restoreObject();
}
_self._createObject();
},
/**
* Creates a new object
*
* @return {void}
*/
_createObject: function () {
var $object;
// Create object
switch (_self.objectData.type) {
case 'image':
$object = $(new Image());
$object.attr({
// The time expression is required to prevent the binding of an image load
'src': _self.objectData.url,
'alt': _self.objectData.title
});
break;
case 'inline':
$object = $('<div class="' + _self.settings.classPrefix + 'inlineWrap"></div>');
$object.html(_self._cloneObject($(_self.objectData.url)));
// Add custom attributes from _self.settings
$.each(_self.settings.inline, function (name, value) {
$object.attr(_self._prefixAttributeName(name), value);
});
break;
case 'ajax':
$object = $('<div class="' + _self.settings.classPrefix + 'inlineWrap"></div>');
// Add custom attributes from _self.settings
$.each(_self.settings.ajax, function (name, value) {
if (name !== 'data') {
$object.attr(_self._prefixAttributeName(name), value);
}
});
break;
case 'flash':
$object = $('<embed src="' + _self.objectData.url + '" type="application/x-shockwave-flash"></embed>');
// Add custom attributes from _self.settings
$.each(_self.settings.flash, function (name, value) {
$object.attr(name, value);
});
break;
case 'video':
$object = $('<video></video>');
$object.attr('src', _self.objectData.url);
// Add custom attributes from _self.settings
$.each(_self.settings.video, function (name, value) {
$object.attr(name, value);
});
break;
default:
$object = $('<iframe></iframe>');
$object.attr({
'src': _self.objectData.url
});
// Add custom attributes from _self.settings
$.each(_self.settings.iframe, function (name, value) {
$object.attr(name, value);
});
break;
}
_self._addObject($object);
_self._loadObject($object);
},
/**
* Adds the new object to the markup
*
* @param {object} $object
* @return {void}
*/
_addObject: function ($object) {
// Add object to content holder
_self.objects.contentInner.html($object);
// Start loading
_self._loading('start');
// Call onStart hook functions
_self._callHooks(_self.settings.onStart);
// Add sequenceInfo to the content holder or hide if its empty
if (_self.settings.showSequenceInfo === true && _self.objectData.isPartOfSequence) {
_self.objects.sequenceInfo.html(_self.objectData.sequenceInfo);
_self.objects.sequenceInfo.show();
} else {
_self.objects.sequenceInfo.empty();
_self.objects.sequenceInfo.hide();
}
// Add title to the content holder or hide if its empty
if (_self.settings.showTitle === true && _self.objectData.title !== undefined && _self.objectData.title !== '') {
_self.objects.title.html(_self.objectData.title);
_self.objects.title.show();
} else {
_self.objects.title.empty();
_self.objects.title.hide();
}
// Add caption to the content holder or hide if its empty
if (_self.settings.showCaption === true && _self.objectData.caption !== undefined && _self.objectData.caption !== '') {
_self.objects.caption.html(_self.objectData.caption);
_self.objects.caption.show();
} else {
_self.objects.caption.empty();
_self.objects.caption.hide();
}
},
/**
* Loads the new object
*
* @param {object} $object
* @return {void}
*/
_loadObject: function ($object) {
// Load the object
switch (_self.objectData.type) {
case 'inline':
if ($(_self.objectData.url)) {
_self._showContent($object);
} else {
_self.error();
}
break;
case 'ajax':
$.ajax(
$.extend({}, _self.settings.ajax, {
url: _self.objectData.url,
type: _self.objectData.requestType,
dataType: _self.objectData.requestDataType,
data: _self.objectData.requestData,
success: function (data, textStatus, jqXHR) {
// Check for X-Ajax-Location
if (jqXHR.getResponseHeader('X-Ajax-Location')) {
_self.objectData.url = jqXHR.getResponseHeader('X-Ajax-Location');
_self._loadObject($object);
}
else {
// Unserialize if data is transferred as json
if (_self.objectData.requestDataType === 'json') {
_self.objectData.data = data;
} else {
$object.html(data);
}
_self._showContent($object);
}
},
error: function (jqXHR, textStatus, errorThrown) {
_self.error();
}
})
);
break;
case 'flash':
_self._showContent($object);
break;
case 'video':
if (typeof($object.get(0).canPlayType) === 'function' || _self.objects.case.find('video').length === 0) {
_self._showContent($object);
} else {
_self.error();
}
break;
default:
if (_self.objectData.url) {
$object.on('load', function () {
_self._showContent($object);
});
$object.on('error', function () {
_self.error();
});
} else {
_self.error();
}
break;
}
},
/**
* Throws an error message if something went wrong
*
* @return {void}
*/
error: function () {
_self.objectData.type = 'error';
var $object = $('<div class="' + _self.settings.classPrefix + 'inlineWrap"></div>');
$object.html(_self.settings.errorMessage);
_self.objects.contentInner.html($object);
_self._showContent(_self.objects.contentInner);
},
/**
* Calculates the dimensions to fit content
*
* @param {object} $object
* @return {void}
*/
_calculateDimensions: function ($object) {
_self._cleanupDimensions();
if (!$object) return;
// Set default dimensions
var dimensions = {
ratio: 1,
objectWidth: $object.attr('width') ? $object.attr('width') : $object.attr(_self._prefixAttributeName('width')),
objectHeight: $object.attr('height') ? $object.attr('height') : $object.attr(_self._prefixAttributeName('height'))
};
if (!_self.settings.disableShrink) {
// Add calculated maximum width/height to dimensions
dimensions.maxWidth = parseInt(_self.dimensions.windowWidth * _self.settings.shrinkFactor);
dimensions.maxHeight = parseInt(_self.dimensions.windowHeight * _self.settings.shrinkFactor);
// If the auto calculated maxWidth/maxHeight greather than the user-defined one, use that.
if (dimensions.maxWidth > _self.settings.maxWidth) {
dimensions.maxWidth = _self.settings.maxWidth;
}
if (dimensions.maxHeight > _self.settings.maxHeight) {
dimensions.maxHeight = _self.settings.maxHeight;
}
// Calculate the difference between screen width/height and image width/height
dimensions.differenceWidthAsPercent = parseInt(100 / dimensions.maxWidth * dimensions.objectWidth);
dimensions.differenceHeightAsPercent = parseInt(100 / dimensions.maxHeight * dimensions.objectHeight);
switch (_self.objectData.type) {
case 'image':
case 'flash':
case 'video':
case 'iframe':
case 'ajax':
case 'inline':
if (_self.objectData.type === 'image' || _self.settings.fixedRatio === true) {
if (dimensions.differenceWidthAsPercent > 100 && dimensions.differenceWidthAsPercent > dimensions.differenceHeightAsPercent) {
dimensions.objectWidth = dimensions.maxWidth;
dimensions.objectHeight = parseInt(dimensions.objectHeight / dimensions.differenceWidthAsPercent * 100);
}
if (dimensions.differenceHeightAsPercent > 100 && dimensions.differenceHeightAsPercent > dimensions.differenceWidthAsPercent) {
dimensions.objectWidth = parseInt(dimensions.objectWidth / dimensions.differenceHeightAsPercent * 100);
dimensions.objectHeight = dimensions.maxHeight;
}
if (dimensions.differenceHeightAsPercent > 100 && dimensions.differenceWidthAsPercent < dimensions.differenceHeightAsPercent) {
dimensions.objectWidth = parseInt(dimensions.maxWidth / dimensions.differenceHeightAsPercent * dimensions.differenceWidthAsPercent);
dimensions.objectHeight = dimensions.maxHeight;
}
break;
}
case 'error':
if (!isNaN(dimensions.objectWidth) && dimensions.objectWidth > dimensions.maxWidth) {
dimensions.objectWidth = dimensions.maxWidth;
}
break;
default:
if ((isNaN(dimensions.objectWidth) || dimensions.objectWidth > dimensions.maxWidth) && !_self.settings.forceWidth) {
dimensions.objectWidth = dimensions.maxWidth;
}
if (((isNaN(dimensions.objectHeight) && dimensions.objectHeight !== 'auto') || dimensions.objectHeight > dimensions.maxHeight) && !_self.settings.forceHeight) {
dimensions.objectHeight = dimensions.maxHeight;
}
break;
}
}
if (_self.settings.forceWidth) {
try {
dimensions.objectWidth = _self.settings[_self.objectData.type].width;
} catch (e) {
dimensions.objectWidth = _self.settings.width || dimensions.objectWidth;
}
dimensions.maxWidth = null;
}
if ($object.attr(_self._prefixAttributeName('max-width'))) {
dimensions.maxWidth = $object.attr(_self._prefixAttributeName('max-width'));
}
if (_self.settings.forceHeight) {
try {
dimensions.objectHeight = _self.settings[_self.objectData.type].height;
} catch (e) {
dimensions.objectHeight = _self.settings.height || dimensions.objectHeight;
}
dimensions.maxHeight = null;
}
if ($object.attr(_self._prefixAttributeName('max-height'))) {
dimensions.maxHeight = $object.attr(_self._prefixAttributeName('max-height'));
}
_self._adjustDimensions($object, dimensions);
},
/**
* Adjusts the dimensions
*
* @param {object} $object
* @param {object} dimensions
* @return {void}
*/
_adjustDimensions: function ($object, dimensions) {
// Adjust width and height
$object.css({
'width': dimensions.objectWidth,
'height': dimensions.objectHeight,
'max-width': dimensions.maxWidth,
'max-height': dimensions.maxHeight
});
_self.objects.contentInner.css({
'width': $object.outerWidth(),
'height': $object.outerHeight(),
'max-width': '100%'
});
_self.objects.case.css({
'width': _self.objects.contentInner.outerWidth(),
'max-width': '100%'
});
// Adjust margin
_self.objects.case.css({
'margin-top': parseInt(-(_self.objects.case.outerHeight() / 2)),
'margin-left': parseInt(-(_self.objects.case.outerWidth() / 2))
});
},
/**
* Handles the _loading
*
* @param {string} process
* @return {void}
*/
_loading: function (process) {
if (process === 'start') {
_self.objects.case.addClass(_self.settings.classPrefix + 'loading');
_self.objects.loading.show();
} else if (process === 'end') {
_self.objects.case.removeClass(_self.settings.classPrefix + 'loading');
_self.objects.loading.hide();
}
},
/**
* Gets the client screen dimensions
*
* @return {object} dimensions
*/
getViewportDimensions: function () {
return {
windowWidth: $(window).innerWidth(),
windowHeight: $(window).innerHeight()
};
},
/**
* Verifies the url
*
* @param {string} dataUrl
* @return {object} dataUrl Clean url for processing content
*/
_verifyDataUrl: function (dataUrl) {
if (!dataUrl || dataUrl === undefined || dataUrl === '') {
return false;
}
if (dataUrl.indexOf('#') > -1) {
dataUrl = dataUrl.split('#');
dataUrl = '#' + dataUrl[dataUrl.length - 1];
}
return _self._normalizeUrl(dataUrl.toString());
},
//
/**
* Tries to get the (file) suffix of an url
*
* @param {string} url
* @return {string}
*/
_getFileUrlSuffix: function (url) {
var re = /(?:\.([^.]+))?$/;
return re.exec(url.toLowerCase())[1];
},
/**
* Verifies the data type of the content to load
*
* @param {string} url
* @return {string|boolean} Array key if expression matched, else false
*/
_verifyDataType: function (url) {
var typeMapping = _self.settings.typeMapping;
// Early abort if dataUrl couldn't be verified
if (!url) {
return false;
}
// Verify the dataType of url according to typeMapping which
// has been defined in settings.
for (var key in typeMapping) {
if (typeMapping.hasOwnProperty(key)) {
var suffixArr = typeMapping[key].split(',');
for (var i = 0; i < suffixArr.length; i++) {
var suffix = suffixArr[i].toLowerCase(),
regexp = new RegExp('\.(' + suffix + ')$', 'i'),
str = url.toLowerCase().split('?')[0].substr(-5);
if (regexp.test(str) === true || (key === 'inline' && (url.indexOf(suffix) > -1))) {
return key;
}
}
}
}
// If no expression matched, return 'iframe'.
return 'iframe';
},
/**
* Extends html markup with the essential tags
*
* @return {void}
*/
_addElements: function () {
if (typeof _self.objects.case !== 'undefined' && $('#' + _self.objects.case.attr('id')).length) {
return;
}
_self.settings.markup();
},
/**
* Shows the loaded content
*
* @param {object} $object
* @return {void}
*/
_showContent: function ($object) {
// Add data attribute with the object type
_self.objects.document.attr(_self._prefixAttributeName('type'), _self.objectData.type);
_self.cache.object = $object;
// Call onBeforeShow hook functions
_self._callHooks(_self.settings.onBeforeShow);
if (_self.settings.breakBeforeShow) return;
_self.show();
},
/**
* Starts the 'inTransition'
* @return {void}
*/
_startInTransition: function () {
switch (_self.transition.in()) {
case 'scrollTop':
case 'scrollRight':
case 'scrollBottom':
case 'scrollLeft':
case 'scrollHorizontal':
case 'scrollVertical':
_self.transition.scroll(_self.objects.case, 'in', _self.settings.speedIn);
_self.transition.fade(_self.objects.contentInner, 'in', _self.settings.speedIn);
break;
case 'elastic':
if (_self.objects.case.css('opacity') < 1) {
_self.transition.zoom(_self.objects.case, 'in', _self.settings.speedIn);
_self.transition.fade(_self.objects.contentInner, 'in', _self.settings.speedIn);
}
case 'fade':
case 'fadeInline':
_self.transition.fade(_self.objects.case, 'in', _self.settings.speedIn);
_self.transition.fade(_self.objects.contentInner, 'in', _self.settings.speedIn);
break;
default:
_self.transition.fade(_self.objects.case, 'in', 0);
break;
}
// End loading.
_self._loading('end');
_self.isBusy = false;
// Set index of the first item opened
if (!_self.cache.firstOpened) {
_self.cache.firstOpened = _self.objectData.this;
}
// Fade in the info with delay
_self.objects.info.hide();
setTimeout(function () {
_self.transition.fade(_self.objects.info, 'in', _self.settings.speedIn);
}, _self.settings.speedIn);
// Call onFinish hook functions
_self._callHooks(_self.settings.onFinish);
},
/**
* Processes the content to show
*
* @return {void}
*/
_processContent: function () {
_self.isBusy = true;
// Fade out the info at first
_self.transition.fade(_self.objects.info, 'out', 0);
switch (_self.settings.transitionOut) {
case 'scrollTop':
case 'scrollRight':
case 'scrollBottom':
case 'scrollLeft':
case 'scrollVertical':
case 'scrollHorizontal':
if (_self.objects.case.is(':hidden')) {
_self.transition.fade(_self.objects.contentInner, 'out', 0);
_self.transition.fade(_self.objects.case, 'out', 0, 0, function () {
_self._loadContent();
});
} else {
_self.transition.scroll(_self.objects.case, 'out', _self.settings.speedOut, function () {
_self._loadContent();
});
}
break;
case 'fade':
if (_self.objects.case.is(':hidden')) {
_self.transition.fade(_self.objects.case, 'out', 0, 0, function () {
_self._loadContent();
});
} else {
_self.transition.fade(_self.objects.case, 'out', _self.settings.speedOut, 0, function () {
_self._loadContent();
});
}
break;
case 'fadeInline':
case 'elastic':
if (_self.objects.case.is(':hidden')) {
_self.transition.fade(_self.objects.case, 'out', 0, 0, function () {
_self._loadContent();
});
} else {
_self.transition.fade(_self.objects.contentInner, 'out', _self.settings.speedOut, 0, function () {
_self._loadContent();
});
}
break;
default:
_self.transition.fade(_self.objects.case, 'out', 0, 0, function () {
_self._loadContent();
});
break;
}
},
/**
* Handles events for gallery buttons
*
* @return {void}
*/
_handleEvents: function () {
_self._unbindEvents();
_self.objects.nav.children().not(_self.objects.close).hide();
// If slideshow is enabled, show play/pause and start timeout.
if (_self.isSlideshowEnabled()) {
// Only start the timeout if slideshow autostart is enabled and slideshow is not pausing
if (
(_self.settings.slideshowAutoStart === true || _self.isSlideshowStarted) &&
!_self.objects.nav.hasClass(_self.settings.classPrefix + 'paused')
) {
_self._startTimeout();
} else {
_self._stopTimeout();
}
}
if (_self.settings.liveResize) {
_self._watchResizeInteraction();
}
_self.objects.close.click(function (event) {
event.preventDefault();
_self.close();
});
if (_self.settings.closeOnOverlayClick === true) {
_self.objects.overlay.css('cursor', 'pointer').click(function (event) {
event.preventDefault();
_self.close();
});
}
if (_self.settings.useKeys === true) {
_self._addKeyEvents();
}
if (_self.objectData.isPartOfSequence) {
_self.objects.nav.attr(_self._prefixAttributeName('ispartofsequence'), true);
_self.objects.nav.data('items', _self._setNavigation());
_self.objects.prev.click(function (event) {
event.preventDefault();
if (_self.settings.navigateEndless === true || !_self.item.isFirst()) {
_self.objects.prev.unbind('click');
_self.cache.action = 'prev';
_self.objects.nav.data('items').prev.click();
if (_self.isSlideshowEnabled()) {
_self._stopTimeout();
}
}
});
_self.objects.next.click(function (event) {
event.preventDefault();
if (_self.settings.navigateEndless === true || !_self.item.isLast()) {
_self.objects.next.unbind('click');
_self.cache.action = 'next';
_self.objects.nav.data('items').next.click();
if (_self.isSlideshowEnabled()) {
_self._stopTimeout();
}
}
});
if (_self.isSlideshowEnabled()) {
_self.objects.play.click(function (event) {
event.preventDefault();
_self._startTimeout();
});
_self.objects.pause.click(function (event) {
event.preventDefault();
_self._stopTimeout();
});
}
// Enable swiping if activated
if (_self.settings.swipe === true) {
if ($.isPlainObject($.event.special.swipeleft)) {
_self.objects.case.on('swipeleft', function (event) {
event.preventDefault();
_self.objects.next.click();
if (_self.isSlideshowEnabled()) {
_self._stopTimeout();
}
});
}
if ($.isPlainObject($.event.special.swiperight)) {
_self.objects.case.on('swiperight', function (event) {
event.preventDefault();
_self.objects.prev.click();
if (_self.isSlideshowEnabled()) {
_self._stopTimeout();
}
});
}
}
}
},
/**
* Adds the key events
*
* @return {void}
*/
_addKeyEvents: function () {
$(document).bind('keyup.lightcase', function (event) {
// Do nothing if lightcase is in process
if (_self.isBusy) {
return;
}
switch (event.keyCode) {
// Escape key
case 27:
_self.objects.close.click();
break;
// Backward key
case 37:
if (_self.objectData.isPartOfSequence) {
_self.objects.prev.click();
}
break;
// Forward key
case 39:
if (_self.objectData.isPartOfSequence) {
_self.objects.next.click();
}
break;
}
});
},
/**
* Starts the slideshow timeout
*
* @return {void}
*/
_startTimeout: function () {
_self.isSlideshowStarted = true;
_self.objects.play.hide();
_self.objects.pause.show();
_self.cache.action = 'next';
_self.objects.nav.removeClass(_self.settings.classPrefix + 'paused');
_self.timeout = setTimeout(function () {
_self.objects.nav.data('items').next.click();
}, _self.settings.timeout);
},
/**
* Stops the slideshow timeout
*
* @return {void}
*/
_stopTimeout: function () {
_self.objects.play.show();
_self.objects.pause.hide();
_self.objects.nav.addClass(_self.settings.classPrefix + 'paused');
clearTimeout(_self.timeout);
},
/**
* Sets the navigator buttons (prev/next)
*
* @return {object} items
*/
_setNavigation: function () {
var $links = $((_self.cache.selector || _self.settings.attr)),
sequenceLength = _self.objectData.sequenceLength - 1,
items = {
prev: $links.eq(_self.objectData.prevIndex),
next: $links.eq(_self.objectData.nextIndex)
};
if (_self.objectData.currentIndex > 0) {
_self.objects.prev.show();
} else {
items.prevItem = $links.eq(sequenceLength);
}
if (_self.objectData.nextIndex <= sequenceLength) {
_self.objects.next.show();
} else {
items.next = $links.eq(0);
}
if (_self.settings.navigateEndless === true) {
_self.objects.prev.show();
_self.objects.next.show();
}
return items;
},
/**
* Item information/status
*
*/
item: {
/**
* Verifies if the current item is first item.
*
* @return {boolean}
*/
isFirst: function () {
return (_self.objectData.currentIndex === 0);
},
/**
* Verifies if the current item is first item opened.
*
* @return {boolean}
*/
isFirstOpened: function () {
return _self.objectData.this.is(_self.cache.firstOpened);
},
/**
* Verifies if the current item is last item.
*
* @return {boolean}
*/
isLast: function () {
return (_self.objectData.currentIndex === (_self.objectData.sequenceLength - 1));
}
},
/**
* Clones the object for inline elements
*
* @param {object} $object
* @return {object} $clone
*/
_cloneObject: function ($object) {
var $clone = $object.clone(),
objectId = $object.attr('id');
// If element is hidden, cache the object and remove
if ($object.is(':hidden')) {
_self._cacheObjectData($object);
$object.attr('id', _self.settings.idPrefix + 'temp-' + objectId).empty();
} else {
// Prevent duplicated id's
$clone.removeAttr('id');
}
return $clone.show();
},
/**
* Verifies if it is a mobile device
*
* @return {boolean}
*/
isMobileDevice: function () {
var deviceAgent = navigator.userAgent.toLowerCase(),
agentId = deviceAgent.match(_self.settings.mobileMatchExpression);
return agentId ? true : false;
},
/**
* Verifies if css transitions are supported
*
* @return {string|boolean} The transition prefix if supported, else false.
*/
isTransitionSupported: function () {
var body = _self.objects.body.get(0),
isTransitionSupported = false,
transitionMapping = {
'transition': '',
'WebkitTransition': '-webkit-',
'MozTransition': '-moz-',
'OTransition': '-o-',
'MsTransition': '-ms-'
};
for (var key in transitionMapping) {
if (transitionMapping.hasOwnProperty(key) && key in body.style) {
_self.support.transition = transitionMapping[key];
isTransitionSupported = true;
}
}
return isTransitionSupported;
},
/**
* Transition types
*
*/
transition: {
/**
* Returns the correct transition type according to the status of interaction.
*
* @return {string} Transition type
*/
in: function () {
if (_self.settings.transitionOpen && !_self.cache.firstOpened) {
return _self.settings.transitionOpen;
}
return _self.settings.transitionIn;
},
/**
* Fades in/out the object
*
* @param {object} $object
* @param {string} type
* @param {number} speed
* @param {number} opacity
* @param {function} callback
* @return {void} Animates an object
*/
fade: function ($object, type, speed, opacity, callback) {
var isInTransition = type === 'in',
startTransition = {},
startOpacity = $object.css('opacity'),
endTransition = {},
endOpacity = opacity ? opacity: isInTransition ? 1 : 0;
if (!_self.isOpen && isInTransition) return;
startTransition['opacity'] = startOpacity;
endTransition['opacity'] = endOpacity;
$object.css(_self.support.transition + 'transition', 'none');
$object.css(startTransition).show();
// Css transition
if (_self.support.transitions) {
endTransition[_self.support.transition + 'transition'] = speed + 'ms ease';
setTimeout(function () {
$object.css(endTransition);
setTimeout(function () {
$object.css(_self.support.transition + 'transition', '');
if (callback && (_self.isOpen || !isInTransition)) {
callback();
}
}, speed);
}, 15);
} else {
// Fallback to js transition
$object.stop();
$object.animate(endTransition, speed, callback);
}
},
/**
* Scrolls in/out the object
*
* @param {object} $object
* @param {string} type
* @param {number} speed
* @param {function} callback
* @return {void} Animates an object
*/
scroll: function ($object, type, speed, callback) {
var isInTransition = type === 'in',
transition = isInTransition ? _self.settings.transitionIn : _self.settings.transitionOut,
direction = 'left',
startTransition = {},
startOpacity = isInTransition ? 0 : 1,
startOffset = isInTransition ? '-50%' : '50%',
endTransition = {},
endOpacity = isInTransition ? 1 : 0,
endOffset = isInTransition ? '50%' : '-50%';
if (!_self.isOpen && isInTransition) return;
switch (transition) {
case 'scrollTop':
direction = 'top';
break;
case 'scrollRight':
startOffset = isInTransition ? '150%' : '50%';
endOffset = isInTransition ? '50%' : '150%';
break;
case 'scrollBottom':
direction = 'top';
startOffset = isInTransition ? '150%' : '50%';
endOffset = isInTransition ? '50%' : '150%';
break;
case 'scrollHorizontal':
startOffset = isInTransition ? '150%' : '50%';
endOffset = isInTransition ? '50%' : '-50%';
break;
case 'scrollVertical':
direction = 'top';
startOffset = isInTransition ? '-50%' : '50%';
endOffset = isInTransition ? '50%' : '150%';
break;
}
if (_self.cache.action === 'prev') {
switch (transition) {
case 'scrollHorizontal':
startOffset = isInTransition ? '-50%' : '50%';
endOffset = isInTransition ? '50%' : '150%';
break;
case 'scrollVertical':
startOffset = isInTransition ? '150%' : '50%';
endOffset = isInTransition ? '50%' : '-50%';
break;
}
}
startTransition['opacity'] = startOpacity;
startTransition[direction] = startOffset;
endTransition['opacity'] = endOpacity;
endTransition[direction] = endOffset;
$object.css(_self.support.transition + 'transition', 'none');
$object.css(startTransition).show();
// Css transition
if (_self.support.transitions) {
endTransition[_self.support.transition + 'transition'] = speed + 'ms ease';
setTimeout(function () {
$object.css(endTransition);
setTimeout(function () {
$object.css(_self.support.transition + 'transition', '');
if (callback && (_self.isOpen || !isInTransition)) {
callback();
}
}, speed);
}, 15);
} else {
// Fallback to js transition
$object.stop();
$object.animate(endTransition, speed, callback);
}
},
/**
* Zooms in/out the object
*
* @param {object} $object
* @param {string} type
* @param {number} speed
* @param {function} callback
* @return {void} Animates an object
*/
zoom: function ($object, type, speed, callback) {
var isInTransition = type === 'in',
startTransition = {},
startOpacity = $object.css('opacity'),
startScale = isInTransition ? 'scale(0.75)' : 'scale(1)',
endTransition = {},
endOpacity = isInTransition ? 1 : 0,
endScale = isInTransition ? 'scale(1)' : 'scale(0.75)';
if (!_self.isOpen && isInTransition) return;
startTransition['opacity'] = startOpacity;
startTransition[_self.support.transition + 'transform'] = startScale;
endTransition['opacity'] = endOpacity;
$object.css(_self.support.transition + 'transition', 'none');
$object.css(startTransition).show();
// Css transition
if (_self.support.transitions) {
endTransition[_self.support.transition + 'transform'] = endScale;
endTransition[_self.support.transition + 'transition'] = speed + 'ms ease';
setTimeout(function () {
$object.css(endTransition);
setTimeout(function () {
$object.css(_self.support.transition + 'transform', '');
$object.css(_self.support.transition + 'transition', '');
if (callback && (_self.isOpen || !isInTransition)) {
callback();
}
}, speed);
}, 15);
} else {
// Fallback to js transition
$object.stop();
$object.animate(endTransition, speed, callback);
}
}
},
/**
* Calls all the registered functions of a specific hook
*
* @param {object} hooks
* @return {void}
*/
_callHooks: function (hooks) {
if (typeof(hooks) === 'object') {
$.each(hooks, function(index, hook) {
if (typeof(hook) === 'function') {
hook.call(_self.origin);
}
});
}
},
/**
* Caches the object data
*
* @param {object} $object
* @return {void}
*/
_cacheObjectData: function ($object) {
$.data($object, 'cache', {
id: $object.attr('id'),
content: $object.html()
});
_self.cache.originalObject = $object;
},
/**
* Restores the object from cache
*
* @return void
*/
_restoreObject: function () {
var $object = $('[id^="' + _self.settings.idPrefix + 'temp-"]');
$object.attr('id', $.data(_self.cache.originalObject, 'cache').id);
$object.html($.data(_self.cache.originalObject, 'cache').content);
},
/**
* Executes functions for a window resize.
* It stops an eventual timeout and recalculates dimensions.
*
* @param {object} dimensions
* @return {void}
*/
resize: function (event, dimensions) {
if (!_self.isOpen) return;
if (_self.isSlideshowEnabled()) {
_self._stopTimeout();
}
if (typeof dimensions === 'object' && dimensions !== null) {
if (dimensions.width) {
_self.cache.object.attr(
_self._prefixAttributeName('width'),
dimensions.width
);
}
if (dimensions.maxWidth) {
_self.cache.object.attr(
_self._prefixAttributeName('max-width'),
dimensions.maxWidth
);
}
if (dimensions.height) {
_self.cache.object.attr(
_self._prefixAttributeName('height'),
dimensions.height
);
}
if (dimensions.maxHeight) {
_self.cache.object.attr(
_self._prefixAttributeName('max-height'),
dimensions.maxHeight
);
}
}
_self.dimensions = _self.getViewportDimensions();
_self._calculateDimensions(_self.cache.object);
// Call onResize hook functions
_self._callHooks(_self.settings.onResize);
},
/**
* Watches for any resize interaction and caches the new sizes.
*
* @return {void}
*/
_watchResizeInteraction: function () {
$(window).resize(_self.resize);
},
/**
* Stop watching any resize interaction related to _self.
*
* @return {void}
*/
_unwatchResizeInteraction: function () {
$(window).off('resize', _self.resize);
},
/**
* Switches to the fullscreen mode
*
* @return {void}
*/
_switchToFullScreenMode: function () {
_self.settings.shrinkFactor = 1;
_self.settings.overlayOpacity = 1;
$('html').addClass(_self.settings.classPrefix + 'fullScreenMode');
},
/**
* Enters into the lightcase view
*
* @return {void}
*/
_open: function () {
_self.isOpen = true;
_self.support.transitions = _self.settings.cssTransitions ? _self.isTransitionSupported() : false;
_self.support.mobileDevice = _self.isMobileDevice();
if (_self.support.mobileDevice) {
$('html').addClass(_self.settings.classPrefix + 'isMobileDevice');
if (_self.settings.fullScreenModeForMobile) {
_self._switchToFullScreenMode();
}
}
if (!_self.settings.transitionIn) {
_self.settings.transitionIn = _self.settings.transition;
}
if (!_self.settings.transitionOut) {
_self.settings.transitionOut = _self.settings.transition;
}
switch (_self.transition.in()) {
case 'fade':
case 'fadeInline':
case 'elastic':
case 'scrollTop':
case 'scrollRight':
case 'scrollBottom':
case 'scrollLeft':
case 'scrollVertical':
case 'scrollHorizontal':
if (_self.objects.case.is(':hidden')) {
_self.objects.close.css('opacity', 0);
_self.objects.overlay.css('opacity', 0);
_self.objects.case.css('opacity', 0);
_self.objects.contentInner.css('opacity', 0);
}
_self.transition.fade(_self.objects.overlay, 'in', _self.settings.speedIn, _self.settings.overlayOpacity, function () {
_self.transition.fade(_self.objects.close, 'in', _self.settings.speedIn);
_self._handleEvents();
_self._processContent();
});
break;
default:
_self.transition.fade(_self.objects.overlay, 'in', 0, _self.settings.overlayOpacity, function () {
_self.transition.fade(_self.objects.close, 'in', 0);
_self._handleEvents();
_self._processContent();
});
break;
}
_self.objects.document.addClass(_self.settings.classPrefix + 'open');
_self.objects.case.attr('aria-hidden', 'false');
},
/**
* Shows the lightcase by starting the transition
*/
show: function () {
// Call onCalculateDimensions hook functions
_self._callHooks(_self.settings.onBeforeCalculateDimensions);
_self._calculateDimensions(_self.cache.object);
// Call onAfterCalculateDimensions hook functions
_self._callHooks(_self.settings.onAfterCalculateDimensions);
_self._startInTransition();
},
/**
* Escapes from the lightcase view
*
* @return {void}
*/
close: function () {
_self.isOpen = false;
if (_self.isSlideshowEnabled()) {
_self._stopTimeout();
_self.isSlideshowStarted = false;
_self.objects.nav.removeClass(_self.settings.classPrefix + 'paused');
}
_self.objects.loading.hide();
_self._unbindEvents();
_self._unwatchResizeInteraction();
$('html').removeClass(_self.settings.classPrefix + 'open');
_self.objects.case.attr('aria-hidden', 'true');
_self.objects.nav.children().hide();
_self.objects.close.hide();
// Call onClose hook functions
_self._callHooks(_self.settings.onClose);
// Fade out the info at first
_self.transition.fade(_self.objects.info, 'out', 0);
switch (_self.settings.transitionClose || _self.settings.transitionOut) {
case 'fade':
case 'fadeInline':
case 'scrollTop':
case 'scrollRight':
case 'scrollBottom':
case 'scrollLeft':
case 'scrollHorizontal':
case 'scrollVertical':
_self.transition.fade(_self.objects.case, 'out', _self.settings.speedOut, 0, function () {
_self.transition.fade(_self.objects.overlay, 'out', _self.settings.speedOut, 0, function () {
_self.cleanup();
});
});
break;
case 'elastic':
_self.transition.zoom(_self.objects.case, 'out', _self.settings.speedOut, function () {
_self.transition.fade(_self.objects.overlay, 'out', _self.settings.speedOut, 0, function () {
_self.cleanup();
});
});
break;
default:
_self.cleanup();
break;
}
},
/**
* Unbinds all given events
*
* @return {void}
*/
_unbindEvents: function () {
// Unbind overlay event
_self.objects.overlay.unbind('click');
// Unbind key events
$(document).unbind('keyup.lightcase');
// Unbind swipe events
_self.objects.case.unbind('swipeleft').unbind('swiperight');
// Unbind navigator events
_self.objects.prev.unbind('click');
_self.objects.next.unbind('click');
_self.objects.play.unbind('click');
_self.objects.pause.unbind('click');
// Unbind close event
_self.objects.close.unbind('click');
},
/**
* Cleans up the dimensions
*
* @return {void}
*/
_cleanupDimensions: function () {
var opacity = _self.objects.contentInner.css('opacity');
_self.objects.case.css({
'width': '',
'height': '',
'top': '',
'left': '',
'margin-top': '',
'margin-left': ''
});
_self.objects.contentInner.removeAttr('style').css('opacity', opacity);
_self.objects.contentInner.children().removeAttr('style');
},
/**
* Cleanup after aborting lightcase
*
* @return {void}
*/
cleanup: function () {
_self._cleanupDimensions();
_self.objects.loading.hide();
_self.objects.overlay.hide();
_self.objects.case.hide();
_self.objects.prev.hide();
_self.objects.next.hide();
_self.objects.play.hide();
_self.objects.pause.hide();
_self.objects.document.removeAttr(_self._prefixAttributeName('type'));
_self.objects.nav.removeAttr(_self._prefixAttributeName('ispartofsequence'));
_self.objects.contentInner.empty().hide();
_self.objects.info.children().empty();
if (_self.cache.originalObject) {
_self._restoreObject();
}
// Call onCleanup hook functions
_self._callHooks(_self.settings.onCleanup);
// Restore cache
_self.cache = {};
},
/**
* Returns the supported match media or undefined if the browser
* doesn't support match media.
*
* @return {mixed}
*/
_matchMedia: function () {
return window.matchMedia || window.msMatchMedia;
},
/**
* Returns the devicePixelRatio if supported. Else, it simply returns
* 1 as the default.
*
* @return {number}
*/
_devicePixelRatio: function () {
return window.devicePixelRatio || 1;
},
/**
* Checks if method is public
*
* @return {boolean}
*/
_isPublicMethod: function (method) {
return (typeof _self[method] === 'function' && method.charAt(0) !== '_');
},
/**
* Exports all public methods to be accessible, callable
* from global scope.
*
* @return {void}
*/
_export: function () {
window.lightcase = {};
$.each(_self, function (property) {
if (_self._isPublicMethod(property)) {
lightcase[property] = _self[property];
}
});
}
};
_self._export();
$.fn.lightcase = function (method) {
// Method calling logic (only public methods are applied)
if (_self._isPublicMethod(method)) {
return _self[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return _self.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.lightcase');
}
};
})(jQuery);
About Section
NFC Pay was founded with a vision to transform the way people handle transactions. Our journey is defined by a commitment to innovation, security, and convenience. We strive to deliver seamless, user-friendly payment solutions that make everyday transactions effortless and secure. Our mission is to empower you to pay with ease and confidence, anytime, anywhere.
FAQ Section
Here are answers to some common questions about NFC Pay. We aim to provide clear and concise information to help you understand how our platform works and how it can benefit you. If you have any further inquiries, please don’t hesitate to contact our support team.
Download the app and sign up using your email or phone number, then complete the verification process.
Yes, we use advanced encryption and security protocols to protect your payment details.
Absolutely, you can link multiple debit or credit cards to your wallet.
Go to the transfer section, select the recipient, enter the amount, and authorize the transfer.
Use the “Forgot PIN” feature in the app to reset it following the provided instructions.
Sign up for a merchant account through the app and follow the setup instructions to start accepting payments.
Yes, you can view and track your payment status in the account dashboard