/*
* jquery.zoomooz-core.js, part of:
* http://janne.aukia.com/zoomooz
*
* Version history:
* 1.04 fixed examples, iphone tuneups, transform offset fix
* 1.03 added closeclick, code structuring
* 1.02 bug fix on endcallback resetting for native animation
* 1.01 declarative syntax and fixes
* 0.92 working scrolling
* 0.91 simplifying code base and scrolling for non-body zoom roots
* 0.90 fixing margin on first body child
* 0.89 support for jquery 1.7
* 0.88 fixed a bug with 90 deg rotations
* 0.87 fixed a bug with settings and a couple of demos
* 0.86 fixed a bug with non-body zoom root
* 0.85 basic IE9 support
* 0.81 basic support for scrolling
* 0.80 refactored position code to a separate file
* 0.72 fixed a bug with skew in Webkit
* 0.71 fixed bugs with FF4
* 0.70 support for non-body zoom root
* 0.69 better settings management
* 0.68 root element tuning
* 0.67 adjustable zoom origin (not fully working yet)
* 0.65 zoom origin to center
* 0.63 basic Opera support
* 0.61 refactored to use CSSMatrix classes
* 0.51 initial public version
*
* LICENCE INFORMATION:
*
* Copyright (c) 2010 Janne Aukia (janne.aukia.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL Version 2 (GPL-LICENSE.txt) licenses.
*
* LICENCE INFORMATION FOR DERIVED FUNCTIONS:
*
* Function computeTotalTransformation based
* on jquery.fn.offset, copyright John Resig, 2010
* (MIT and GPL Version 2).
*
*/
/*jslint sub: true */
(function($) {
"use strict";
//**********************************//
//*** Variables ***//
//**********************************//
var helpers = $.zoomooz.helpers;
var animationSettings = ["duration", "easing", "nativeanimation"];
//**********************************//
//*** Static setup ***//
//**********************************//
setupCssStyles();
//**********************************//
//*** jQuery functions ***//
//**********************************//
if(!$.zoomooz) {
$.zoomooz = {};
}
/* this can be used for setting the default settings for zoomooz explicitly. */
$.zoomooz.setup = function(settings) {
$.zoomooz.defaultSettings = jQuery.extend(constructDefaultSettings(), settings);
};
/* returns the zooming settings of a particular element. used by zoomTarget. */
$.fn.zoomSettings = function(settings) {
var retValue;
this.each(function() {
var $elem = $(this);
retValue = setupElementSettings($elem, settings);
});
return retValue;
}
/* the main zooming method. */
$.fn.zoomTo = function(settings, skipElementSettings) {
this.each(function() {
var $this = $(this);
if(!skipElementSettings) {
settings = $this.zoomSettings(settings);
}
zoomTo($this, settings);
if(settings.debug) {
if($("#debug").length===0) {
$(settings.root).append('
');
} else {
$("#debug").html("");
}
showDebug($this,settings);
} else {
if($("#debug").length!==0) {
$("#debug").html("");
}
}
});
return this;
};
//**********************************//
//*** Setup functions ***//
//**********************************//
function setupElementSettings($elem, baseSettings) {
var settings = jQuery.extend({}, baseSettings);
if(!$.zoomooz.defaultSettings) {
$.zoomooz.setup();
}
var defaultSettings = $.zoomooz.defaultSettings;
var elementSettings = jQuery.extend({},settings);
for(var key in defaultSettings) {
if (defaultSettings.hasOwnProperty(key) && !elementSettings[key]) {
elementSettings[key] = $elem.data(key);
}
}
// FIXME: it would be better, that the animationSettings
// would come from the jquery.zoomooz-anim file somehow
for(var i=0;i
';
$("#debug").append(label);
}
//**********************************//
//*** Calculating element ***//
//*** total transformation ***//
//**********************************//
/* Based on:
* jQuery.fn.offset
*/
function computeTotalTransformation(input, transformationRootElement) {
var elem = input[0];
if( !elem || !elem.ownerDocument ) {
return null;
}
var totalTransformation = new PureCSSMatrix();
var trans;
if ( elem === elem.ownerDocument.body ) {
var bOffset = jQuery.offset.bodyOffset( elem );
trans = new PureCSSMatrix();
trans = trans.translate(bOffset.left, bOffset.top);
totalTransformation = totalTransformation.multiply(trans);
return totalTransformation;
}
var support;
if(jQuery.offset.initialize) {
jQuery.offset.initialize();
support = {
fixedPosition:jQuery.offset.supportsFixedPosition,
doesNotAddBorder:jQuery.offset.doesNotAddBorder,
doesAddBorderForTableAndCells:jQuery.support.doesAddBorderForTableAndCells,
subtractsBorderForOverflowNotVisible:jQuery.offset.subtractsBorderForOverflowNotVisible
}
} else {
support = jQuery.support;
}
var offsetParent = elem.offsetParent;
var doc = elem.ownerDocument;
var computedStyle;
var docElem = doc.documentElement;
var body = doc.body;
var root = transformationRootElement[0];
var defaultView = doc.defaultView;
var prevComputedStyle;
if(defaultView) {
prevComputedStyle = defaultView.getComputedStyle( elem, null );
} else {
prevComputedStyle = elem.currentStyle;
}
/*
function offsetParentInsideRoot($elem, $root) {
// FIXME:
// wondering, should this be $root.closest()
// or $root.parent().closest...
var $viewport = $root.parent();
var $offsetParent = $elem.offsetParent();
return ($viewport[0]==$offsetParent[0]) || $viewport.closest($offsetParent).length==0;
}
console.log("inside root",offsetParentInsideRoot(input, transformationRootElement));
*/
var top = elem.offsetTop;
var left = elem.offsetLeft;
var transformation = constructTransformation().translate(left,top);
transformation = transformation.multiply(constructTransformation(elem));
totalTransformation = transformation.multiply((totalTransformation));
// loop from node down to root
while ( (elem = elem.parentNode) && elem !== root) {
top = 0; left = 0;
if ( support.fixedPosition && prevComputedStyle.position === "fixed" ) {
break;
}
computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
top -= elem.scrollTop;
left -= elem.scrollLeft;
if ( elem === offsetParent ) {
top += elem.offsetTop;
left += elem.offsetLeft;
if ( support.doesNotAddBorder && !(support.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
}
offsetParent = elem.offsetParent;
}
if ( support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
}
prevComputedStyle = computedStyle;
if(elem.offsetParent==root) {
top -= parseFloat($(elem.offsetParent).css("margin-top")) || 0;
left -= parseFloat($(elem.offsetParent).css("margin-left")) || 0;
}
transformation = constructTransformation().translate(left,top);
transformation = transformation.multiply(constructTransformation(elem));
totalTransformation = transformation.multiply(totalTransformation);
}
top = 0;
left = 0;
// fixme: should disable these for non-body roots?
if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
top += body.offsetTop;
left += body.offsetLeft;
}
if ( support.fixedPosition && prevComputedStyle.position === "fixed" ) {
top += Math.max( docElem.scrollTop, body.scrollTop );
left += Math.max( docElem.scrollLeft, body.scrollLeft );
}
var itertrans = (new PureCSSMatrix()).translate(left,top);
totalTransformation = totalTransformation.multiply(itertrans);
return totalTransformation;
}
//**********************************//
//*** Helpers ***//
//**********************************//
function printFixedNumber(x) {
return Number(x).toFixed(6);
}
function constructTransformation(elem) {
var rawTrans = helpers.getElementTransform(elem);
if(!rawTrans) {
return new PureCSSMatrix();
} else {
return new PureCSSMatrix(rawTrans);
}
}
})(jQuery);