(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' : 'absolute', 'overflow' : 'hidden', 'left' : '0', 'top' : '0', '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