(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD (Register as an anonymous module)
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory((function(){
if (typeof jQuery !== 'undefined') {
return jQuery;
} else if (typeof Zepto !== 'undefined') {
return Zepto;
}
return $;
})());
}
}(function ($) {
function log(type, msg){
if(window.console){
if(typeof type != 'undefined' && type && typeof msg != 'undefined' && msg){
type = type.toLowerCase();
if(type == 'error'){
console.error(msg);
}else if(type == 'log'){
console.log(msg);
}else{
console.error('"' + type + '" is not supported as console type.');
}
}
}
}
function strToNum(str){
str = $.trim(str.toString());
if(str.toLowerCase() === 'auto'){
return str;
}
return parseFloat(str);
// return Number(str.replace(/[^\d.-]/g, ''));
}
function validateXYperc(val, wh){
var temp = val.toString().match(/(-*)+\d+/)[0]; // '-200%' -> -200
if(getMeasurement(val) == 'px'){
if(val < 0){
return 0;
}else if(val > wh){
return wh;
}else{
return val;
}
}else if(getMeasurement(val) == '%'){
if(temp < 0){
return '0%';
}else if(temp > 100){
return '100%';
}else{
return val;
}
}
}
function getMeasurement(str){ // determine if input is a PX or % value
var ma = str.toString().match(/\d+(.*)/i);
if(ma){
switch($.trim(ma[1])){
case '':
return 'px';
case 'px':
return 'px';
case '%':
return '%';
default:
break;
}
}
return '';
}
var css3Supported = (function(){
/* code available at http://net.tutsplus.com/tutorials/html-css-techniques/quick-tip-detect-css-support-in-browsers-with-javascript/ */
var div = document.createElement('div'),
vendors = 'Khtml Ms O Moz Webkit'.split(' '),
len = vendors.length;
return function(prop) {
if ( prop in div.style ) return true;
prop = prop.replace(/^[a-z]/, function(val) {
return val.toUpperCase();
});
for(var i in vendors){
if ( vendors[i] + prop in div.style ) {
return true;
}
}
return false;
};
})();
/*
Zepto does not come with $.fn.outerWidth() & $.fn.outerHeight()
code: https://gist.github.com/pamelafox/1379704
*/
if (!$.fn.outerHeight || !$.fn.outerWidth) {
if (typeof Array.prototype.forEach != 'function') { // fix for older browsers
Array.prototype.forEach = function(callback){
for (var i = 0; i < this.length; i++){
callback.apply(this, [this[i], i, this]);
}
};
}
['width', 'height'].forEach(function(dimension) {
var offset, Dimension = dimension.replace(/./, function(m) { return m[0].toUpperCase(); });
if (!$.fn['outer' + Dimension]) {
$.fn['outer' + Dimension] = function() {
var elem = this;
if (elem) {
var size = elem[dimension]();
var sides = { 'width': ['left', 'right'], 'height': ['top', 'bottom'] };
sides[dimension].forEach(function(side) {
size += parseInt(elem.css('margin-' + side), 10);
size += parseInt(elem.css('padding-' + side), 10);
size += parseInt(elem.css('border-' + side + '-width'), 10);
});
return size;
} else {
return null;
}
};
}
});
}
/* Fallback for older versions of jQuery */
if(!$.fn.unwrap){
$.fn.unwrap = function(){
this.parent().each(function() {
if ( !$.nodeName( this, 'body' ) ) {
$( this ).replaceWith( this.childNodes );
}
}).end();
};
}
var checkPositionReach = function($elem, scrollCheck){
var $win = $(window),
bounds = $elem.offset(),
viewport = {
top : $win.scrollTop(),
// left : $win.scrollLeft() // Zepto does not support this
left : window.scrollX
};
viewport.right = viewport.left + $win.width();
viewport.bottom = viewport.top + $win.height();
bounds.right = bounds.left + $elem.outerWidth();
bounds.bottom = bounds.top + $elem.outerHeight();
scrollCheck = (scrollCheck) ? strToNum(scrollCheck) : 0;
return (!(
viewport.right < (bounds.left - scrollCheck) ||
viewport.left > (bounds.right + scrollCheck) ||
viewport.bottom < (bounds.top - scrollCheck) ||
viewport.top > (bounds.bottom + scrollCheck)
));
};
var pluginName = 'jqthumb',
$window = $(window),
onDemandScrollEventObj = (function(){
var tmp = ['scroll', 'resize', 'scrolltop'];
var obj = {};
for(var i=0; i')
.css({
'width' : strToNum(optW) + getMeasurement(optW),
'height' : strToNum(optH) + getMeasurement(optH),
'display' : 'none',
'position' : 'relative',
'overflow' : 'hidden'
})
.addClass(options.classname)
.data(pluginName, pluginName); // it would be easy to kill later
$fakeImg = $('
')
.css({
'width' : '100%',
'height' : '100%',
'background-image' : 'url("' + imgUrl + '")',
// '-ms-filter' : '"progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + $oriImage.attr(options.source) + '",sizingMethod="scale")', // this does not work in Zepto
'background-repeat' : 'no-repeat',
'background-position': strToNum(optPosX) + getMeasurement(optPosX) + ' ' + strToNum(optPosY) + getMeasurement(optPosY),
'background-size' : 'cover'
})
.appendTo($wrapper);
if(options.renderPosition.toLowerCase() === 'after'){
$wrapper.insertAfter(obj.oriImg);
}else{
$wrapper.insertBefore(obj.oriImg);
}
$wrapper.show(); // must show first to get resolution
$fakeImg
.css({
'width' : parseFloat(100 * optZ) + '%',
'height' : parseFloat(100 * optZ) + '%',
'position' : 'absolute'
})
.css({ // cannot combine css() as width and height have to be defined before doing calculation
'top' : (function(){
// (cH - pH) / pH * 100 / percentage
var cH = $wrapper.height(),
pH = $fakeImg.height();
if(getMeasurement(optPosY) == '%'){
return '-' + parseFloat((pH - cH) / cH * 100 / (100 / strToNum(optPosY) ) ) + '%';
}
})(),
'left' : (function(){
// (cW - pW) / cW * 100 / percentage
var cW = $wrapper.width(),
pW = $fakeImg.width();
if(getMeasurement(optPosX) == '%'){
return '-' + parseFloat((pW - cW) / cW * 100 / (100 / strToNum(optPosX) ) ) + '%';
}
})()
});
$wrapper.hide();
if (typeof obj.done === 'function'){
obj.done($wrapper);
}
}
function nativeMath(obj){
var oriW = obj.tmpImgDom.width,
oriH = obj.tmpImgDom.height,
optW = ($.trim(options.width.toString().toLowerCase()) === 'auto') ? oriW.toString() : options.width,
optH = ($.trim(options.height.toString().toLowerCase()) === 'auto') ? oriH.toString() : options.height,
optZ = options.zoom,
optPosX = options.position.x,
optPosY = options.position.y,
measure_optW = getMeasurement(optW),
measure_optH = getMeasurement(optH),
optResp = options.responsive,
$wrapper, $fakeImg;
$fakeImg = $(obj.tmpImgDom);
$wrapper = $('');
function calculateReso(){
var ratio = 0;
if(oriW > oriH){ // horizontal
$fakeImg.css({
'width' : 'auto',
'max-height' : 99999999,
'min-height' : 0,
'max-width' : 99999999,
'min-width' : 0,
'height' : $wrapper.height() + 'px'
});
ratio = $fakeImg.height() / $fakeImg.width(); // get ratio
if($fakeImg.width() < $wrapper.width()){
$fakeImg.css({
'width' : $wrapper.width() * optZ,
'height': parseFloat($wrapper.width() * ratio) * optZ
});
}else{
$fakeImg.css({
'width' : $fakeImg.width() * optZ,
'height': parseFloat($fakeImg.width() * ratio) * optZ
});
}
}else{ // vertical
$fakeImg.css({
'width' : $wrapper.width() + 'px',
'max-height' : 99999999,
'min-height' : 0,
'max-width' : 99999999,
'min-width' : 0,
'height' : 'auto'
});
ratio = $fakeImg.width() / $fakeImg.height(); // get ratio
if($fakeImg.height() < $wrapper.height()){
$fakeImg.css({
'width' : parseFloat($wrapper.height() * ratio) * optZ,
'height': $wrapper.height() * optZ
});
}
}
if(optZ < 1){ // workaround for zoom level < 1
var $subContainer = $('');
$subContainer
.css({
'width' : parseFloat(strToNum(optW.toString()) * optZ) + getMeasurement(optW.toString()),
'height' : parseFloat(strToNum(optH.toString()) * optZ) + getMeasurement(optH.toString()),
'position' : 'relative',
'overflow' : 'hidden'
})
.appendTo($fakeImg.parent());
$fakeImg.appendTo($subContainer); // move $fakeImg into $subContainer
}
$fakeImg.css({
'position' : 'absolute',
'left' : (function(){
var x = 0;
if(getMeasurement(optPosX) == '%'){
x = parseFloat(($fakeImg.width() - $fakeImg.parent().width()) / 100 * strToNum(optPosX));
return (x <= 0) ? x + 'px' : '-' + x + 'px';
}else if(getMeasurement(optPosX) == 'px' || isNaN(optPosX) === false){
return strToNum(optPosX) + 'px';
}
})(),
'top' : (function(){
var y = 0;
if(getMeasurement(optPosY) == '%'){
y = parseFloat(($fakeImg.height() - $fakeImg.parent().height()) / 100 * strToNum(optPosY));
return (y <= 0) ? y + 'px' : '-' + y + 'px';
}else if(getMeasurement(optPosY) == 'px' || isNaN(optPosY) === false){
return strToNum(optPosY) + 'px';
}
})()
});
}
if(options.renderPosition.toLowerCase() === 'after'){
$wrapper.insertAfter(obj.oriImg);
}else{
$wrapper.insertBefore(obj.oriImg);
}
$wrapper
.append($fakeImg)
.css({
'position' : 'relative',
'overflow' : 'hidden',
'width' : strToNum(optW) + (measure_optW ? measure_optW : 'px'),
'height' : strToNum(optH) + (measure_optH ? measure_optH : 'px')
})
.data(pluginName, pluginName); // it would be easy to kill later
calculateReso();
if(!isNaN(optResp) && optResp > 0){
$(obj.oriImage).data(dtEvtFnResponsive, function(){
setTimeout(function(){
calculateReso();
}, optResp);
});
$window.bind(onDemandScrollEventObj.resize, $(obj.oriImage).data(dtEvtFnResponsive));
}
$wrapper
.hide()
.addClass(options.classname);
if (typeof obj.done === 'function'){
obj.done($wrapper);
}
}
options.before.apply(self, [self]);
var PluginClass = this,
$oriImage = $(self),
imgUrl = $oriImage.attr(options.source),
doMath = (function(method){
if(method == 'auto'){
if(css3Supported('backgroundSize') === false){ // old browsers need to do calculation to perform same output like "background-size: cover"
return nativeMath;
}
return modernMath; // modern browsers that support CSS3 would be easier
}else if(method == 'modern'){
return modernMath;
}else if(method == 'native'){
return nativeMath;
}else{
log('error', 'Invalid method. Only "auto", "modern" and "native" are allowed.');
}
})(options.method.toString().toLowerCase());
if(doMath){
$oriImage.data(oriStyleDataName, $oriImage.attr('style')); // keep original styles into data
$oriImage.data(renderPosDataName, options.renderPosition); // store render position (before/after) for killing purpose
$oriImage.hide();
if(options.onDemand === true){
PluginClass.demand(self, options, imgUrl, doMath);
}else{
$oriImage.data(dtTmpImg, new Image());
PluginClass.lazyload(PluginClass, self, options, function(img){
PluginClass.processImg(self, options, img, doMath);
$oriImage.removeData(dtTmpImg);
});
}
}else{
$oriImage.data(dtStatus, 'error');
PluginClass.kill($oriImage);
}
},
demand: function(self, options, imgUrl, fnDoMathOnSuccess){
var PluginClass = this,
$oriImage = $(self);
if(options.onDemandEvent === 'scroll'){
$oriImage.wrap(''); // add temporary tag to get its offset().top
var $tmpWrapper = $oriImage.parent();
$tmpWrapper.css({ // set temporarily height
'width' : ((options.width) ? strToNum(options.width) + getMeasurement(options.width) : $oriImage.width() + 'px'),
'height' : ((options.height) ? strToNum(options.height) + getMeasurement(options.height) : $oriImage.height() + 'px')
});
$oriImage.data(dtEvtFnOngoing, function(){ // store event fn into data for unbinding purpose
if( checkPositionReach($tmpWrapper, options.threshold) ){ // check scroll position
$window.unbind(onDemandScrollEventStr, $oriImage.data(dtEvtFnOngoing)); // unbind when it is done process to save CPU usage
$oriImage.removeData(dtEvtFnOngoing);
$oriImage.unwrap(); // remove temporary tag
$oriImage.data(dtTmpImg, new Image());
PluginClass.lazyload(PluginClass, self, options, function(img){
PluginClass.processImg(self, options, img, fnDoMathOnSuccess);
$oriImage.removeData(dtTmpImg);
});
}
});
$window
.bind(onDemandScrollEventStr, $oriImage.data(dtEvtFnOngoing))
.triggerHandler(onDemandScrollEventObj.scroll);
}else if(
options.onDemandEvent === 'click' ||
options.onDemandEvent === 'mouseenter'
){
var $bindTarget = $oriImage.parent(),
bindName = ((options.onDemandEvent === 'click') ? onDemandClickEventName : onDemandMouseEnterEventName);
$oriImage.data(dtEvtFnOneTime, function(){ // store event fn into data for unbinding purpose
$bindTarget.unbind(bindName, $oriImage.data(dtEvtFnOneTime)); // unbind when it is done process to save CPU usage
$oriImage.removeData(dtEvtFnOneTime);
$oriImage.data(dtTmpImg, new Image());
PluginClass.lazyload(PluginClass, self, options, function(img){
PluginClass.processImg(self, options, img, fnDoMathOnSuccess);
$oriImage.removeData(dtTmpImg);
});
$oriImage.data(inViewPortDataName, true);
});
$bindTarget.bind(bindName, $oriImage.data(dtEvtFnOneTime));
}
},
updateGlobal: function(self, obj, options){
self.global.outputElems.push( $(obj)[0] );
self.global.elemCounter++;
grandGlobal.outputElems.push( $(obj)[0] );
if(self.global.elemCounter == self.global.inputElems.length){
options.done.apply(self, [self.global.outputElems]);
}
}
};
$.fn[ pluginName ] = function ( options ) {
var obj = {},
global = {
elemCounter : 0,
outputElems : [],
inputElems : (function(_this){
var $this = $(_this),
total = $this.length,
tempArr = [];
for(var i=0; i