Morf.js - CSS3 Transitions with custom easing functions
CSS3 Transitions with custom easing functions
Native
These are the natively supported easing functions, built into WebKit.
linear
ease
ease-in
ease-out
ease-in-out
Custom
These are custom easing functions (thanks to Robert Penner & Thomas Fuchs) that can produce much more interesting transitions.
Generated Animation CSS
Internally the custom easing function for the transition is faked using CSS animations. Here is the code that is produced on the fly for the most recent "transition".
================================================
FILE: js/modernizr.js
================================================
/*!
* Modernizr v2.0
* http://www.modernizr.com
*
* Copyright (c) 2009-2011 Faruk Ates, Paul Irish, Alex Sexton
* Dual-licensed under the BSD or MIT licenses: www.modernizr.com/license/
*/
/*
* Modernizr tests which native CSS3 and HTML5 features are available in
* the current UA and makes the results available to you in two ways:
* as properties on a global Modernizr object, and as classes on the
* element. This information allows you to progressively enhance
* your pages with a granular level of control over the experience.
*
* Modernizr has an optional (not included) conditional resource loader
* called Modernizr.load(), based on Yepnope.js (yepnopejs.com).
* To get a build that includes Modernizr.load(), as well as choosing
* which tests to include, go to www.modernizr.com/download/
*
* @author Faruk Ates
* @author Paul Irish
* @author Alex Sexton
* @copyright (c) 2009-2011
* @contributor Ben Alman
*/
window.Modernizr = (function( window, document, undefined ) {
var version = '2.0',
Modernizr = {},
// option for enabling the HTML classes to be added
enableClasses = true,
docElement = document.documentElement,
docHead = document.head || document.getElementsByTagName('head')[0],
/**
* Create our "modernizr" element that we do most feature tests on.
*/
mod = 'modernizr',
modElem = document.createElement(mod),
mStyle = modElem.style,
/**
* Create the input element for various Web Forms feature tests.
*/
inputElem = document.createElement('input'),
smile = ':)',
toString = Object.prototype.toString,
// List of property values to set for css tests. See ticket #21
prefixes = ' -webkit- -moz- -o- -ms- -khtml- '.split(' '),
// Following spec is to expose vendor-specific style properties as:
// elem.style.WebkitBorderRadius
// and the following would be incorrect:
// elem.style.webkitBorderRadius
// Webkit ghosts their properties in lowercase but Opera & Moz do not.
// Microsoft foregoes prefixes entirely <= IE8, but appears to
// use a lowercase `ms` instead of the correct `Ms` in IE9
// More here: http://github.com/Modernizr/Modernizr/issues/issue/21
domPrefixes = 'Webkit Moz O ms Khtml'.split(' '),
ns = {'svg': 'http://www.w3.org/2000/svg'},
tests = {},
inputs = {},
attrs = {},
classes = [],
featureName, // used in testing loop
// Inject element with style element and some CSS rules
injectElementWithStyles = function( rule, callback, nodes, testnames ) {
var style, ret, node,
div = document.createElement('div');
if ( parseInt(nodes, 10) ) {
// In order not to give false positives we create a node for each test
// This also allows the method to scale for unspecified uses
while ( nodes-- ) {
node = document.createElement('div');
node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
div.appendChild(node);
}
}
// '].join('');
div.id = mod;
div.innerHTML += style;
docElement.appendChild(div);
ret = callback(div, rule);
div.parentNode.removeChild(div);
return !!ret;
},
// adapted from matchMedia polyfill
// by Scott Jehl and Paul Irish
// gist.github.com/786768
testMediaQuery = function( mq ) {
if ( window.matchMedia ) {
return matchMedia(mq).matches;
}
var bool;
injectElementWithStyles('@media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) {
bool = (window.getComputedStyle ?
getComputedStyle(node, null) :
node.currentStyle)['position'] == 'absolute';
});
return bool;
},
/**
* isEventSupported determines if a given element supports the given event
* function from http://yura.thinkweb2.com/isEventSupported/
*/
isEventSupported = (function() {
var TAGNAMES = {
'select': 'input', 'change': 'input',
'submit': 'form', 'reset': 'form',
'error': 'img', 'load': 'img', 'abort': 'img'
};
function isEventSupported( eventName, element ) {
element = element || document.createElement(TAGNAMES[eventName] || 'div');
eventName = 'on' + eventName;
// When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
var isSupported = eventName in element;
if ( !isSupported ) {
// If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
if ( !element.setAttribute ) {
element = document.createElement('div');
}
if ( element.setAttribute && element.removeAttribute ) {
element.setAttribute(eventName, '');
isSupported = is(element[eventName], 'function');
// If property was created, "remove it" (by setting value to `undefined`)
if ( !is(element[eventName], undefined) ) {
element[eventName] = undefined;
}
element.removeAttribute(eventName);
}
}
element = null;
return isSupported;
}
return isEventSupported;
})();
// hasOwnProperty shim by kangax needed for Safari 2.0 support
var _hasOwnProperty = ({}).hasOwnProperty, hasOwnProperty;
if ( !is(_hasOwnProperty, undefined) && !is(_hasOwnProperty.call, undefined) ) {
hasOwnProperty = function (object, property) {
return _hasOwnProperty.call(object, property);
};
}
else {
hasOwnProperty = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
return ((property in object) && is(object.constructor.prototype[property], undefined));
};
}
/**
* setCss applies given styles to the Modernizr DOM node.
*/
function setCss( str ) {
mStyle.cssText = str;
}
/**
* setCssAll extrapolates all vendor-specific css strings.
*/
function setCssAll( str1, str2 ) {
return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
}
/**
* is returns a boolean for if typeof obj is exactly type.
*/
function is( obj, type ) {
return typeof obj === type;
}
/**
* contains returns a boolean for if substr is found within str.
*/
function contains( str, substr ) {
return !!~('' + str).indexOf(substr);
}
/**
* testProps is a generic CSS / DOM property test; if a browser supports
* a certain property, it won't return undefined for it.
* A supported CSS property returns empty string when its not yet set.
*/
function testProps( props, prefixed ) {
for ( var i in props ) {
if ( mStyle[ props[i] ] !== undefined ) {
return prefixed == 'pfx' ? props[i] : true;
}
}
return false;
}
/**
* testPropsAll tests a list of DOM properties we want to check against.
* We specify literally ALL possible (known and/or likely) properties on
* the element including the non-vendor prefixed one, for forward-
* compatibility.
*/
function testPropsAll( prop, prefixed ) {
var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1),
props = (prop + ' ' + domPrefixes.join(ucProp + ' ') + ucProp).split(' ');
return testProps(props, prefixed);
}
/**
* testBundle tests a list of CSS features that require element and style injection.
* By bundling them together we can reduce the need to touch the DOM multiple times.
*/
/*>>testBundle*/
var testBundle = (function( styles, tests ) {
var style = styles.join(''),
len = tests.length;
injectElementWithStyles(style, function( node, rule ) {
var style = document.styleSheets[document.styleSheets.length - 1],
cssText = style.cssText || style.cssRules[0].cssText,
children = node.childNodes, hash = {};
while ( len-- ) {
hash[children[len].id] = children[len];
}
/*>>touch*/ Modernizr['touch'] = ('ontouchstart' in window) || hash['touch'].offsetTop === 9; /*>>touch*/
/*>>csstransforms3d*/ Modernizr['csstransforms3d'] = hash['csstransforms3d'].offsetLeft === 9; /*>>csstransforms3d*/
/*>>generatedcontent*/Modernizr['generatedcontent'] = hash['generatedcontent'].offsetHeight >= 1; /*>>generatedcontent*/
/*>>fontface*/ Modernizr['fontface'] = /src/i.test(cssText) &&
cssText.indexOf(rule.split(' ')[0]) === 0; /*>>fontface*/
}, len, tests);
})([
// Pass in styles to be injected into document
/*>>fontface*/ '@font-face {font-family:"font";src:url("//:")}' /*>>fontface*/
/*>>touch*/ ,['@media (',prefixes.join('touch-enabled),('),mod,')',
'{#touch{top:9px;position:absolute}}'].join('') /*>>touch*/
/*>>csstransforms3d*/ ,['@media (',prefixes.join('transform-3d),('),mod,')',
'{#csstransforms3d{left:9px;position:absolute}}'].join('')/*>>csstransforms3d*/
/*>>generatedcontent*/,['#generatedcontent:after{content:"',smile,'"}'].join('') /*>>generatedcontent*/
],
[
/*>>fontface*/ 'fontface' /*>>fontface*/
/*>>touch*/ ,'touch' /*>>touch*/
/*>>csstransforms3d*/ ,'csstransforms3d' /*>>csstransforms3d*/
/*>>generatedcontent*/,'generatedcontent' /*>>generatedcontent*/
]);/*>>testBundle*/
/**
* Tests
* -----
*/
tests['flexbox'] = function() {
/**
* setPrefixedValueCSS sets the property of a specified element
* adding vendor prefixes to the VALUE of the property.
* @param {Element} element
* @param {string} property The property name. This will not be prefixed.
* @param {string} value The value of the property. This WILL be prefixed.
* @param {string=} extra Additional CSS to append unmodified to the end of
* the CSS string.
*/
function setPrefixedValueCSS( element, property, value, extra ) {
property += ':';
element.style.cssText = (property + prefixes.join(value + ';' + property)).slice(0, -property.length) + (extra || '');
}
/**
* setPrefixedPropertyCSS sets the property of a specified element
* adding vendor prefixes to the NAME of the property.
* @param {Element} element
* @param {string} property The property name. This WILL be prefixed.
* @param {string} value The value of the property. This will not be prefixed.
* @param {string=} extra Additional CSS to append unmodified to the end of
* the CSS string.
*/
function setPrefixedPropertyCSS( element, property, value, extra ) {
element.style.cssText = prefixes.join(property + ':' + value + ';') + (extra || '');
}
var c = document.createElement('div'),
elem = document.createElement('div');
setPrefixedValueCSS(c, 'display', 'box', 'width:42px;padding:0;');
setPrefixedPropertyCSS(elem, 'box-flex', '1', 'width:10px;');
c.appendChild(elem);
docElement.appendChild(c);
var ret = elem.offsetWidth === 42;
c.removeChild(elem);
docElement.removeChild(c);
return ret;
};
// On the S60 and BB Storm, getContext exists, but always returns undefined
// http://github.com/Modernizr/Modernizr/issues/issue/97/
tests['canvas'] = function() {
var elem = document.createElement('canvas');
return !!(elem.getContext && elem.getContext('2d'));
};
tests['canvastext'] = function() {
return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
};
// This WebGL test may false positive.
// But really it's quite impossible to know whether webgl will succeed until after you create the context.
// You might have hardware that can support a 100x100 webgl canvas, but will not support a 1000x1000 webgl
// canvas. So this feature inference is weak, but intentionally so.
// It is known to false positive in FF4 with certain hardware and the iPad 2.
tests['webgl'] = function() {
return !!window.WebGLRenderingContext;
};
/*
* The Modernizr.touch test only indicates if the browser supports
* touch events, which does not necessarily reflect a touchscreen
* device, as evidenced by tablets running Windows 7 or, alas,
* the Palm Pre / WebOS (touch) phones.
*
* Additionally, Chrome (desktop) used to lie about its support on this,
* but that has since been rectified: http://crbug.com/36415
*
* We also test for Firefox 4 Multitouch Support.
*
* For more info, see: http://modernizr.github.com/Modernizr/touch.html
*/
tests['touch'] = function() {
return Modernizr['touch'];
};
/**
* geolocation tests for the new Geolocation API specification.
* This test is a standards compliant-only test; for more complete
* testing, including a Google Gears fallback, please see:
* http://code.google.com/p/geo-location-javascript/
* or view a fallback solution using google's geo API:
* http://gist.github.com/366184
*/
tests['geolocation'] = function() {
return !!navigator.geolocation;
};
// Per 1.6:
// This used to be Modernizr.crosswindowmessaging but the longer
// name has been deprecated in favor of a shorter and property-matching one.
// The old API is still available in 1.6, but as of 2.0 will throw a warning,
// and in the first release thereafter disappear entirely.
tests['postmessage'] = function() {
return !!window.postMessage;
};
// Web SQL database detection is tricky:
// In chrome incognito mode, openDatabase is truthy, but using it will
// throw an exception: http://crbug.com/42380
// We can create a dummy database, but there is no way to delete it afterwards.
// Meanwhile, Safari users can get prompted on any database creation.
// If they do, any page with Modernizr will give them a prompt:
// http://github.com/Modernizr/Modernizr/issues/closed#issue/113
// We have chosen to allow the Chrome incognito false positive, so that Modernizr
// doesn't litter the web with these test databases. As a developer, you'll have
// to account for this gotcha yourself.
tests['websqldatabase'] = function() {
var result = !!window.openDatabase;
/* if (result){
try {
result = !!openDatabase( mod + "testdb", "1.0", mod + "testdb", 2e4);
} catch(e) {
}
} */
return result;
};
// Vendors had inconsistent prefixing with the experimental Indexed DB:
// - Webkit's implementation is accessible through webkitIndexedDB
// - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB
// For speed, we don't test the legacy (and beta-only) indexedDB
tests['indexedDB'] = function() {
for ( var i = -1, len = domPrefixes.length; ++i < len; ){
if ( window[domPrefixes[i].toLowerCase() + 'IndexedDB'] ){
return true;
}
}
return !!window.indexedDB;
};
// documentMode logic from YUI to filter out IE8 Compat Mode
// which false positives.
tests['hashchange'] = function() {
return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7);
};
// Per 1.6:
// This used to be Modernizr.historymanagement but the longer
// name has been deprecated in favor of a shorter and property-matching one.
// The old API is still available in 1.6, but as of 2.0 will throw a warning,
// and in the first release thereafter disappear entirely.
tests['history'] = function() {
return !!(window.history && history.pushState);
};
tests['draganddrop'] = function() {
return isEventSupported('dragstart') && isEventSupported('drop');
};
// Mozilla is targeting to land MozWebSocket for FF6
// bugzil.la/659324
tests['websockets'] = function() {
for ( var i = -1, len = domPrefixes.length; ++i < len; ){
if ( window[domPrefixes[i] + 'WebSocket'] ){
return true;
}
}
return 'WebSocket' in window;
};
// http://css-tricks.com/rgba-browser-support/
tests['rgba'] = function() {
// Set an rgba() color and check the returned value
setCss('background-color:rgba(150,255,150,.5)');
return contains(mStyle.backgroundColor, 'rgba');
};
tests['hsla'] = function() {
// Same as rgba(), in fact, browsers re-map hsla() to rgba() internally,
// except IE9 who retains it as hsla
setCss('background-color:hsla(120,40%,100%,.5)');
return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla');
};
tests['multiplebgs'] = function() {
// Setting multiple images AND a color on the background shorthand property
// and then querying the style.background property value for the number of
// occurrences of "url(" is a reliable method for detecting ACTUAL support for this!
setCss('background:url(//:),url(//:),red url(//:)');
// If the UA supports multiple backgrounds, there should be three occurrences
// of the string "url(" in the return value for elemStyle.background
return /(url\s*\(.*?){3}/.test(mStyle.background);
};
// In testing support for a given CSS property, it's legit to test:
// `elem.style[styleName] !== undefined`
// If the property is supported it will return an empty string,
// if unsupported it will return undefined.
// We'll take advantage of this quick test and skip setting a style
// on our modernizr element, but instead just testing undefined vs
// empty string.
tests['backgroundsize'] = function() {
return testPropsAll('backgroundSize');
};
tests['borderimage'] = function() {
return testPropsAll('borderImage');
};
// Super comprehensive table about all the unique implementations of
// border-radius: http://muddledramblings.com/table-of-css3-border-radius-compliance
tests['borderradius'] = function() {
return testPropsAll('borderRadius');
};
// WebOS unfortunately false positives on this test.
tests['boxshadow'] = function() {
return testPropsAll('boxShadow');
};
// FF3.0 will false positive on this test
tests['textshadow'] = function() {
return document.createElement('div').style.textShadow === '';
};
tests['opacity'] = function() {
// Browsers that actually have CSS Opacity implemented have done so
// according to spec, which means their return values are within the
// range of [0.0,1.0] - including the leading zero.
setCssAll('opacity:.55');
// The non-literal . in this regex is intentional:
// German Chrome returns this value as 0,55
// https://github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632
return /^0.55$/.test(mStyle.opacity);
};
tests['cssanimations'] = function() {
return testPropsAll('animationName');
};
tests['csscolumns'] = function() {
return testPropsAll('columnCount');
};
tests['cssgradients'] = function() {
/**
* For CSS Gradients syntax, please see:
* http://webkit.org/blog/175/introducing-css-gradients/
* https://developer.mozilla.org/en/CSS/-moz-linear-gradient
* https://developer.mozilla.org/en/CSS/-moz-radial-gradient
* http://dev.w3.org/csswg/css3-images/#gradients-
*/
var str1 = 'background-image:',
str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
str3 = 'linear-gradient(left top,#9f9, white);';
setCss(
(str1 + prefixes.join(str2 + str1) + prefixes.join(str3 + str1)).slice(0, -str1.length)
);
return contains(mStyle.backgroundImage, 'gradient');
};
tests['cssreflections'] = function() {
return testPropsAll('boxReflect');
};
tests['csstransforms'] = function() {
return !!testProps(['transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform']);
};
tests['csstransforms3d'] = function() {
var ret = !!testProps(['perspectiveProperty', 'WebkitPerspective', 'MozPerspective', 'OPerspective', 'msPerspective']);
// Webkit’s 3D transforms are passed off to the browser's own graphics renderer.
// It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in
// some conditions. As a result, Webkit typically recognizes the syntax but
// will sometimes throw a false positive, thus we must do a more thorough check:
if ( ret && 'webkitPerspective' in docElement.style ) {
// Webkit allows this media query to succeed only if the feature is enabled.
// `@media (transform-3d),(-o-transform-3d),(-moz-transform-3d),(-ms-transform-3d),(-webkit-transform-3d),(modernizr){ ... }`
ret = Modernizr['csstransforms3d'];
}
return ret;
};
tests['csstransitions'] = function() {
return testPropsAll('transitionProperty');
};
/*>>fontface*/
// @font-face detection routine by Diego Perini
// http://javascript.nwbox.com/CSSSupport/
tests['fontface'] = function() {
return Modernizr['fontface'];
};
/*>>fontface*/
// CSS generated content detection
tests['generatedcontent'] = function() {
return Modernizr['generatedcontent'];
};
// These tests evaluate support of the video/audio elements, as well as
// testing what types of content they support.
//
// We're using the Boolean constructor here, so that we can extend the value
// e.g. Modernizr.video // true
// Modernizr.video.ogg // 'probably'
//
// Codec values from : http://github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
// thx to NielsLeenheer and zcorpan
// Note: in FF 3.5.1 and 3.5.0, "no" was a return value instead of empty string.
// Modernizr does not normalize for that.
tests['video'] = function() {
var elem = document.createElement('video'),
bool = false;
// IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
try {
if ( bool = !!elem.canPlayType ) {
bool = new Boolean(bool);
bool.ogg = elem.canPlayType('video/ogg; codecs="theora"');
// Workaround required for IE9, which doesn't report video support without audio codec specified.
// bug 599718 @ msft connect
var h264 = 'video/mp4; codecs="avc1.42E01E';
bool.h264 = elem.canPlayType(h264 + '"') || elem.canPlayType(h264 + ', mp4a.40.2"');
bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"');
}
} catch(e) { }
return bool;
};
tests['audio'] = function() {
var elem = document.createElement('audio'),
bool = false;
try {
if ( bool = !!elem.canPlayType ) {
bool = new Boolean(bool);
bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"');
bool.mp3 = elem.canPlayType('audio/mpeg;');
// Mimetypes accepted:
// https://developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
// http://bit.ly/iphoneoscodecs
bool.wav = elem.canPlayType('audio/wav; codecs="1"');
bool.m4a = elem.canPlayType('audio/x-m4a;') || elem.canPlayType('audio/aac;');
}
} catch(e) { }
return bool;
};
// Firefox has made these tests rather unfun.
// In FF4, if disabled, window.localStorage should === null.
// Normally, we could not test that directly and need to do a
// `('localStorage' in window) && ` test first because otherwise Firefox will
// throw http://bugzil.la/365772 if cookies are disabled
// However, in Firefox 4 betas, if dom.storage.enabled == false, just mentioning
// the property will throw an exception. http://bugzil.la/599479
// This looks to be fixed for FF4 Final.
// Because we are forced to try/catch this, we'll go aggressive.
// FWIW: IE8 Compat mode supports these features completely:
// http://www.quirksmode.org/dom/html5.html
// But IE8 doesn't support either with local files
tests['localstorage'] = function() {
try {
return !!localStorage.getItem;
} catch(e) {
return false;
}
};
tests['sessionstorage'] = function() {
try {
return !!sessionStorage.getItem;
} catch(e){
return false;
}
};
tests['webworkers'] = function() {
return !!window.Worker;
};
tests['applicationcache'] = function() {
return !!window.applicationCache;
};
// Thanks to Erik Dahlstrom
tests['svg'] = function() {
return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
};
// specifically for SVG inline in HTML, not within XHTML
// test page: paulirish.com/demo/inline-svg
tests['inlinesvg'] = function() {
var div = document.createElement('div');
div.innerHTML = '';
return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
};
// Thanks to F1lt3r and lucideer, ticket #35
tests['smil'] = function() {
return !!document.createElementNS && /SVG/.test(toString.call(document.createElementNS(ns.svg, 'animate')));
};
tests['svgclippaths'] = function() {
// Possibly returns a false positive in Safari 3.2?
return !!document.createElementNS && /SVG/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
};
// input features and input types go directly onto the ret object, bypassing the tests loop.
// Hold this guy to execute in a moment.
function webforms() {
// Run through HTML5's new input attributes to see if the UA understands any.
// We're using f which is the element created early on
// Mike Taylr has created a comprehensive resource for testing these attributes
// when applied to all input types:
// http://miketaylr.com/code/input-type-attr.html
// spec: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
// Only input placeholder is tested while textarea's placeholder is not.
// Currently Safari 4 and Opera 11 have support only for the input placeholder
// Both tests are available in feature-detects/forms-placeholder.js
Modernizr['input'] = (function( props ) {
for ( var i = 0, len = props.length; i < len; i++ ) {
attrs[ props[i] ] = !!(props[i] in inputElem);
}
return attrs;
})('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
// Run through HTML5's new input types to see if the UA understands any.
// This is put behind the tests runloop because it doesn't return a
// true/false like all the other tests; instead, it returns an object
// containing each input type with its corresponding true/false value
// Big thanks to @miketaylr for the html5 forms expertise. http://miketaylr.com/
Modernizr['inputtypes'] = (function(props) {
for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
inputElem.setAttribute('type', inputElemType = props[i]);
bool = inputElem.type !== 'text';
// We first check to see if the type we give it sticks..
// If the type does, we feed it a textual value, which shouldn't be valid.
// If the value doesn't stick, we know there's input sanitization which infers a custom UI
if ( bool ) {
inputElem.value = smile;
inputElem.style.cssText = 'position:absolute;visibility:hidden;';
if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {
docElement.appendChild(inputElem);
defaultView = document.defaultView;
// Safari 2-4 allows the smiley as a value, despite making a slider
bool = defaultView.getComputedStyle &&
defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
// Mobile android web browser has false positive, so must
// check the height to see if the widget is actually there.
(inputElem.offsetHeight !== 0);
docElement.removeChild(inputElem);
} else if ( /^(search|tel)$/.test(inputElemType) ){
// Spec doesnt define any special parsing or detectable UI
// behaviors so we pass these through as true
// Interestingly, opera fails the earlier test, so it doesn't
// even make it here.
} else if ( /^(url|email)$/.test(inputElemType) ) {
// Real url and email support comes with prebaked validation.
bool = inputElem.checkValidity && inputElem.checkValidity() === false;
} else if ( /^color$/.test(inputElemType) ) {
// chuck into DOM and force reflow for Opera bug in 11.00
// github.com/Modernizr/Modernizr/issues#issue/159
docElement.appendChild(inputElem);
docElement.offsetWidth;
bool = inputElem.value != smile;
docElement.removeChild(inputElem);
} else {
// If the upgraded input compontent rejects the :) text, we got a winner
bool = inputElem.value != smile;
}
}
inputs[ props[i] ] = !!bool;
}
return inputs;
})('search tel url email datetime date month week time datetime-local number range color'.split(' '));
}
// End of test definitions
// -----------------------
// Run through all tests and detect their support in the current UA.
// todo: hypothetically we could be doing an array of tests and use a basic loop here.
for ( var feature in tests ) {
if ( hasOwnProperty(tests, feature) ) {
// run the test, throw the return value into the Modernizr,
// then based on that boolean, define an appropriate className
// and push it into an array of classes we'll join later.
featureName = feature.toLowerCase();
Modernizr[featureName] = tests[feature]();
classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
}
}
// input tests need to run.
Modernizr.input || webforms();
/**
* addTest allows the user to define their own feature tests
* the result will be added onto the Modernizr object,
* as well as an appropriate className set on the html element
*
* @param feature - String naming the feature
* @param test - Function returning true if feature is supported, false if not
*/
Modernizr.addTest = function ( feature, test ) {
if ( typeof feature == "object" ) {
for ( var key in feature ) {
if ( hasOwnProperty( feature, key ) ) {
Modernizr.addTest( key, feature[ key ] );
}
}
} else {
feature = feature.toLowerCase();
if ( Modernizr[feature] !== undefined ) {
// we're going to quit if you're trying to overwrite an existing test
// if we were to allow it, we'd do this:
// var re = new RegExp("\\b(no-)?" + feature + "\\b");
// docElement.className = docElement.className.replace( re, '' );
// but, no rly, stuff 'em.
return;
}
test = typeof test == "boolean" ? test : !!test();
docElement.className += ' ' + (test ? '' : 'no-') + feature;
Modernizr[feature] = test;
}
return Modernizr; // allow chaining.
};
// Reset modElem.cssText to nothing to reduce memory footprint.
setCss('');
modElem = inputElem = null;
//>>BEGIN IEPP
// Enable HTML 5 elements for styling (and printing) in IE.
if ( window.attachEvent && (function(){ var elem = document.createElement('div');
elem.innerHTML = '';
return elem.childNodes.length !== 1; })() ) {
// iepp v2 by @jon_neal & afarkas : github.com/aFarkas/iepp/
(function(win, doc) {
win.iepp = win.iepp || {};
var iepp = win.iepp,
elems = iepp.html5elements || 'abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video',
elemsArr = elems.split('|'),
elemsArrLen = elemsArr.length,
elemRegExp = new RegExp('(^|\\s)('+elems+')', 'gi'),
tagRegExp = new RegExp('<(\/*)('+elems+')', 'gi'),
filterReg = /^\s*[\{\}]\s*$/,
ruleRegExp = new RegExp('(^|[^\\n]*?\\s)('+elems+')([^\\n]*)({[\\n\\w\\W]*?})', 'gi'),
docFrag = doc.createDocumentFragment(),
html = doc.documentElement,
head = html.firstChild,
bodyElem = doc.createElement('body'),
styleElem = doc.createElement('style'),
printMedias = /print|all/,
body;
function shim(doc) {
var a = -1;
while (++a < elemsArrLen)
// Use createElement so IE allows HTML5-named elements in a document
doc.createElement(elemsArr[a]);
}
iepp.getCSS = function(styleSheetList, mediaType) {
if(styleSheetList+'' === undefined){return '';}
var a = -1,
len = styleSheetList.length,
styleSheet,
cssTextArr = [];
while (++a < len) {
styleSheet = styleSheetList[a];
//currently no test for disabled/alternate stylesheets
if(styleSheet.disabled){continue;}
mediaType = styleSheet.media || mediaType;
// Get css from all non-screen stylesheets and their imports
if (printMedias.test(mediaType)) cssTextArr.push(iepp.getCSS(styleSheet.imports, mediaType), styleSheet.cssText);
//reset mediaType to all with every new *not imported* stylesheet
mediaType = 'all';
}
return cssTextArr.join('');
};
iepp.parseCSS = function(cssText) {
var cssTextArr = [],
rule;
while ((rule = ruleRegExp.exec(cssText)) != null){
// Replace all html5 element references with iepp substitute classnames
cssTextArr.push(( (filterReg.exec(rule[1]) ? '\n' : rule[1]) +rule[2]+rule[3]).replace(elemRegExp, '$1.iepp_$2')+rule[4]);
}
return cssTextArr.join('\n');
};
iepp.writeHTML = function() {
var a = -1;
body = body || doc.body;
while (++a < elemsArrLen) {
var nodeList = doc.getElementsByTagName(elemsArr[a]),
nodeListLen = nodeList.length,
b = -1;
while (++b < nodeListLen)
if (nodeList[b].className.indexOf('iepp_') < 0)
// Append iepp substitute classnames to all html5 elements
nodeList[b].className += ' iepp_'+elemsArr[a];
}
docFrag.appendChild(body);
html.appendChild(bodyElem);
// Write iepp substitute print-safe document
bodyElem.className = body.className;
bodyElem.id = body.id;
// Replace HTML5 elements with which is print-safe and shouldn't conflict since it isn't part of html5
bodyElem.innerHTML = body.innerHTML.replace(tagRegExp, '<$1font');
};
iepp._beforePrint = function() {
// Write iepp custom print CSS
styleElem.styleSheet.cssText = iepp.parseCSS(iepp.getCSS(doc.styleSheets, 'all'));
iepp.writeHTML();
};
iepp.restoreHTML = function(){
// Undo everything done in onbeforeprint
bodyElem.innerHTML = '';
html.removeChild(bodyElem);
html.appendChild(body);
};
iepp._afterPrint = function(){
// Undo everything done in onbeforeprint
iepp.restoreHTML();
styleElem.styleSheet.cssText = '';
};
// Shim the document and iepp fragment
shim(doc);
shim(docFrag);
//
if(iepp.disablePP){return;}
// Add iepp custom print style element
head.insertBefore(styleElem, head.firstChild);
styleElem.media = 'print';
styleElem.className = 'iepp-printshim';
win.attachEvent(
'onbeforeprint',
iepp._beforePrint
);
win.attachEvent(
'onafterprint',
iepp._afterPrint
);
})(window, document);
}
//>>END IEPP
// Assign private properties to the return object with prefix
Modernizr._version = version;
// expose these for the plugin API. Look in the source for how to join() them against your input
Modernizr._prefixes = prefixes;
Modernizr._domPrefixes = domPrefixes;
// Modernizr.mq tests a given media query, live against the current state of the window
// A few important notes:
// * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false
// * A max-width or orientation query will be evaluated against the current state, which may change later.
// * You must specify values. Eg. If you are testing support for the min-width media query use:
// Modernizr.mq('(min-width:0)')
// usage:
// Modernizr.mq('only screen and (max-width:768)')
Modernizr.mq = testMediaQuery;
// Modernizr.hasEvent() detects support for a given event, with an optional element to test on
// Modernizr.hasEvent('gesturestart', elem)
Modernizr.hasEvent = isEventSupported;
// Modernizr.testProp() investigates whether a given style property is recognized
// Note that the property names must be provided in the camelCase variant.
// Modernizr.testProp('pointerEvents')
Modernizr.testProp = function(prop){
return testProps([prop]);
};
// Modernizr.testAllProps() investigates whether a given style property,
// or any of its vendor-prefixed variants, is recognized
// Note that the property names must be provided in the camelCase variant.
// Modernizr.testAllProps('boxSizing')
Modernizr.testAllProps = testPropsAll;
// Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards
// Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... })
Modernizr.testStyles = injectElementWithStyles;
// Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input
// Modernizr.prefixed('boxSizing') // 'MozBoxSizing'
// Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style.
// Return values will also be the camelCase variant, if you need to translate that to hypenated style use:
//
// str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
// If you're trying to ascertain which transition end event to bind to, you might do something like...
//
// var transEndEventNames = {
// 'WebkitTransition' : 'webkitTransitionEnd',
// 'MozTransition' : 'transitionend',
// 'OTransition' : 'oTransitionEnd',
// 'msTransition' : 'msTransitionEnd', // maybe?
// 'transition' : 'transitionEnd'
// },
// transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
Modernizr.prefixed = function(prop){
return testPropsAll(prop, 'pfx');
};
// Remove "no-js" class from element, if it exists:
docElement.className = docElement.className.replace(/\bno-js\b/, '')
// Add the new classes to the element.
+ (enableClasses ? ' js ' + classes.join(' ') : '');
return Modernizr;
})(this, this.document);
================================================
FILE: js/morf.js
================================================
/**
* @preserve Morf v0.1.5
* http://www.joelambert.co.uk/morf
*
* Copyright 2011, Joe Lambert.
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
var Morf = function(elem, css, opts) {
var from = {}, to = {},
fromElem = document.createElement('div'),
toElem = document.createElement('div'),
options = {
timingFunction: 'ease',
duration: null,
increment: 0.01,
debug: false,
optimise: true, // Whether the outputted CSS should be optimised
decimalPlaces: 5 // How many decimal places to optimise the WebKitCSSMatrix output to
},
// Define all other var's used in the function
i = rule = ruleName = camel = m1 = m2 = progress = frame = rule = transEvent = val = null, cacheKey = '',
// Setup a scoped reference to ourselves
_this = this,
keyframes = {},
// Create a unique name for this animation
animName = 'anim'+(new Date().getTime()),
/* --- Helper Functions ------------------------------------------------------------------- */
// Event listener for the webkitAnimationEnd Event
animationEndListener = function(event){
elem.removeEventListener('webkitAnimationEnd', animationEndListener, true);
// Dispatch a faux webkitTransitionEnd event to complete the appearance of this being a transition rather than an animation
// TODO: Should we throw an event for each property changed? (event.propertyName = 'opacity' etc)
transEvent = document.createEvent("Event");
transEvent.initEvent("webkitTransitionEnd", true, true);
elem.dispatchEvent(transEvent);
// Reset transition effects after use
elem.style.webkitTransitionTimingFunction = null;
elem.style.webkitTransitionDuration = 0;
if (options.callback) {
options.callback(elem);
}
},
// Adds the CSS to the current page
addKeyframeRule = function(rule) {
if (document.styleSheets && document.styleSheets.length)
document.styleSheets[0].insertRule(rule, 0);
else
{
var style = document.createElement('style');
style.innerHTML = rule;
document.head.appendChild(style);
}
},
// Produces a CSS string representation of the Keyframes
createAnimationCSS = function(kf, name) {
var str = '@-webkit-keyframes '+name+' {\n', f = pos = rule = null, fStr = '';
for(pos in kf)
{
f = kf[pos];
fStr = '\t'+pos+' {\n';
for(rule in f)
fStr += '\t\t'+_this.util.toDash(rule)+': '+f[rule]+';\n';
fStr += "\t}\n\n";
str += fStr;
}
return options.optimise ? optimiseCSS(str+' }') : str+' }';
},
// Replaces scale(0) with 0.0001 to get around the inability to these decompose matrix
sanityCheckTransformString = function(str) {
var scale = str.match(/scale[Y|X|Z]*\([0-9, ]*0[,0-9 ]*\)/g),
i = 0;
if(scale)
{
// There might be multiple scale() properties in the string
for(i = 0; i < scale.length; i++)
str = str.replace(scale[i], scale[i].replace(/([^0-9])0([^0.9])/g, "$10.0001$2"));
}
return str;
},
// WebKitCSSMatrix toString() ALWAYS outputs numbers to 5 decimal places - this helps optimise the string
optimiseCSS = function(str, decimalPlaces) {
decimalPlaces = typeof options.decimalPlaces == 'number' ? options.decimalPlaces : 5;
var matches = str.match(/[0-9\.]+/gm),
i = 0;
if(matches)
{
for(i = 0; i < matches.length; i++)
str = str.replace(matches[i], parseFloat( parseFloat(matches[i]).toFixed(decimalPlaces)));
}
return str;
};
/* --- Helper Functions End --------------------------------------------------------------- */
// Import the options
for(i in opts)
options[i] = opts[i];
// If timingFunction is a natively supported function then just trigger normal transition
if( options.timingFunction === 'ease' ||
options.timingFunction === 'linear' ||
options.timingFunction === 'ease-in' ||
options.timingFunction === 'ease-out' ||
options.timingFunction === 'ease-in-out' ||
/^cubic-bezier/.test(options.timingFunction)) {
elem.style.webkitTransitionDuration = options.duration;
elem.style.webkitTransitionTimingFunction = options.timingFunction;
// Listen for the transitionEnd event to fire the callback if needed
var transitionEndListener = function(event) {
elem.removeEventListener('webkitTransitionEnd', transitionEndListener, true);
// Clean up after ourself
elem.style.webkitTransitionDuration = 0;
elem.style.webkitTransitionTimingFunction = null;
if (options.callback) {
// Delay execution to ensure the clean up CSS has taken effect
setTimeout(function() {
options.callback(elem);
}, 10);
}
};
elem.addEventListener('webkitTransitionEnd', transitionEndListener, true);
setTimeout(function() {
for(rule in css) {
camel = _this.util.toCamel(rule);
elem.style[camel] = css[rule];
}
}, 10);
this.css = '';
return;
}
else
{
// Reset transition properties for this element
elem.style.webkitTransitionTimingFunction = null;
elem.style.webkitTransitionDuration = 0;
}
// Create the key used to cache this animation
cacheKey += options.timingFunction;
// Setup the start and end CSS state
for(rule in css)
{
camel = this.util.toCamel(rule);
toElem.style[camel] = css[rule];
// Set the from/start state
from[rule] = (camel == 'WebkitTransform') ? new WebKitCSSMatrix( sanityCheckTransformString( window.getComputedStyle(elem)['-webkit-transform'] ) ) : window.getComputedStyle(elem)[rule];
// Set the to/end state
to[rule] = (camel == 'WebkitTransform') ? new WebKitCSSMatrix( sanityCheckTransformString( toElem.style.WebkitTransform ) ) : toElem.style[camel];
// Shifty requires numeric values to be a number rather than a string (e.g. for opacity)
from[rule] = from[rule] == (val = parseInt(from[rule], 10)) ? val : from[rule];
to[rule] = to[rule] == (val = parseInt(from[rule], 10)) ? val : to[rule];
// Update the cacheKey
cacheKey += ';' + rule + ':' + from[rule] + '->' + to[rule];
}
// Check the cache to save expensive calculations
if(Morf.cache[cacheKey])
{
this.css = Morf.cache[cacheKey].css;
animName = Morf.cache[cacheKey].name;
}
else
{
// Produce decompositions of matrices here so we don't have to redo it on each iteration
// Decomposing the matrix is expensive so we need to minimise these requests
if(from['-webkit-transform'])
{
m1 = from['-webkit-transform'].decompose();
m2 = to['-webkit-transform'].decompose();
}
// Produce style keyframes
for(progress = 0; progress <= 1; progress += options.increment) {
// Use Shifty.js to work out the interpolated CSS state
frame = Tweenable.util.interpolate(from, to, progress, options.timingFunction);
// Work out the interpolated matrix transformation
if(m1 !== null && m2 !== null)
frame['-webkit-transform'] = m1.tween(m2, progress, Tweenable.prototype.formula[options.timingFunction]);
keyframes[parseInt(progress*100, 10)+'%'] = frame;
}
// Ensure the last frame has been added
keyframes['100%'] = to;
// Add the new animation to the document
this.css = createAnimationCSS(keyframes, animName);
addKeyframeRule(this.css);
Morf.cache[cacheKey] = {css: this.css, name: animName};
}
// Set the final position state as this should be a transition not an animation & the element should end in the 'to' state
for(rule in to)
elem.style[this.util.toCamel(rule)] = to[rule];
// Trigger the animation
elem.addEventListener('webkitAnimationEnd', animationEndListener, true);
elem.style.webkitAnimationDuration = options.duration;
elem.style.webkitAnimationTimingFunction = 'linear';
elem.style.webkitAnimationName = animName;
// Print the animation to the console if the debug switch is given
if(options.debug && window.console && window.console.log)
console.log(this.css);
};
/**
* Convenience function for triggering a transition
* @param {HTMLDom} elem The element to apply the transition to
* @param {Object} css Key value pair of CSS properties
* @param {Object} opts Additional configuration options
*
* Configuration options
* - timingFunction: {String} Name of the easing function to perform
* - duration: {integer} Duration in ms
* - increment: {float} How frequently to generate keyframes (Defaults to 0.01, which is every 1%)
* - debug: {Boolean} Should the generated CSS Animation be printed to the console
*
* @returns {Morf} An instance of the Morf object
*/
Morf.transition = function(elem, css, opts){
return new Morf(elem, css, opts);
};
/**
* Object to cache generated animations
*/
Morf.cache = {};
/**
* Current version
*/
Morf.version = '0.1.5';
// Utilities Placeholder
Morf.prototype.util = {};
/**
* Converts a DOM style string to CSS property name
* @param {String} str A DOM style string
* @returns {String} CSS property name
*/
Morf.prototype.util.toDash = function(str){
str = str.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();});
return /^webkit/.test(str) ? '-'+str : str;
};
/**
* Converts a CSS property name to DOM style string
* @param {String} str A CSS property name
* @returns {String} DOM style string
*/
Morf.prototype.util.toCamel = function(str){
return str.replace(/(\-[a-z])/g, function($1){return $1.toUpperCase().replace('-','');});
};
/**
* WebKitCSSMatrix Extensions
*
* Copyright 2011, Joe Lambert (http://www.joelambert.co.uk)
* Free to use under the MIT license.
* http://joelambert.mit-license.org/
*/
// Wrap this functionality up to prevent poluting the global namespace
(function(){
/**
* A 4 dimensional vector
* @author Joe Lambert
* @constructor
*/
var Vector4 = function(x, y, z, w)
{
this.x = x ? x : 0;
this.y = y ? y : 0;
this.z = z ? z : 0;
this.w = w ? w : 0;
/**
* Ensure that values are not undefined
* @author Joe Lambert
* @returns null
*/
this.checkValues = function() {
this.x = this.x ? this.x : 0;
this.y = this.y ? this.y : 0;
this.z = this.z ? this.z : 0;
this.w = this.w ? this.w : 0;
};
/**
* Get the length of the vector
* @author Joe Lambert
* @returns {float}
*/
this.length = function() {
this.checkValues();
return Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z);
};
/**
* Get a normalised representation of the vector
* @author Joe Lambert
* @returns {Vector4}
*/
this.normalise = function() {
var len = this.length(),
v = new Vector4(this.x / len, this.y / len, this.z / len);
return v;
};
/**
* Vector Dot-Product
* @param {Vector4} v The second vector to apply the product to
* @author Joe Lambert
* @returns {float} The Dot-Product of this and v.
*/
this.dot = function(v) {
return this.x*v.x + this.y*v.y + this.z*v.z + this.w*v.w;
};
/**
* Vector Cross-Product
* @param {Vector4} v The second vector to apply the product to
* @author Joe Lambert
* @returns {Vector4} The Cross-Product of this and v.
*/
this.cross = function(v) {
return new Vector4(this.y*v.z - this.z*v.y, this.z*v.x - this.x*v.z, this.x*v.y - this.y*v.x);
};
/**
* Helper function required for matrix decomposition
* A Javascript implementation of pseudo code available from http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition
* @param {Vector4} aPoint A 3D point
* @param {float} ascl
* @param {float} bscl
* @author Joe Lambert
* @returns {Vector4}
*/
this.combine = function(aPoint, ascl, bscl) {
return new Vector4( (ascl * this.x) + (bscl * aPoint.x),
(ascl * this.y) + (bscl * aPoint.y),
(ascl * this.z) + (bscl * aPoint.z) );
}
};
/**
* Object containing the decomposed components of a matrix
* @author Joe Lambert
* @constructor
*/
var CSSMatrixDecomposed = function(obj) {
obj === undefined ? obj = {} : null;
var components = {perspective: null, translate: null, skew: null, scale: null, rotate: null};
for(var i in components)
this[i] = obj[i] ? obj[i] : new Vector4();
/**
* Tween between two decomposed matrices
* @param {CSSMatrixDecomposed} dm The destination decomposed matrix
* @param {float} progress A float value between 0-1, representing the percentage of completion
* @param {function} fn An easing function following the prototype function(pos){}
* @author Joe Lambert
* @returns {WebKitCSSMatrix} A new matrix for the tweened state
*/
this.tween = function(dm, progress, fn) {
if(fn === undefined)
fn = function(pos) {return pos;}; // Default to a linear easing
if(!dm)
dm = new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose());
var r = new CSSMatrixDecomposed(),
i = index = null,
trans = '';
progress = fn(progress);
for(index in components)
for(i in {x:'x', y:'y', z:'z', w:'w'})
r[index][i] = (this[index][i] + (dm[index][i] - this[index][i]) * progress ).toFixed(5);
trans = 'matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, '+r.perspective.x+', '+r.perspective.y+', '+r.perspective.z+', '+r.perspective.w+') ' +
'translate3d('+r.translate.x+'px, '+r.translate.y+'px, '+r.translate.z+'px) ' +
'rotateX('+r.rotate.x+'rad) rotateY('+r.rotate.y+'rad) rotateZ('+r.rotate.z+'rad) ' +
'matrix3d(1,0,0,0, 0,1,0,0, 0,'+r.skew.z+',1,0, 0,0,0,1) ' +
'matrix3d(1,0,0,0, 0,1,0,0, '+r.skew.y+',0,1,0, 0,0,0,1) ' +
'matrix3d(1,0,0,0, '+r.skew.x+',1,0,0, 0,0,1,0, 0,0,0,1) ' +
'scale3d('+r.scale.x+', '+r.scale.y+', '+r.scale.z+')';
try { r = new WebKitCSSMatrix(trans); return r; }
catch(e) { console.error('Invalid matrix string: '+trans); return '' };
};
};
/**
* Tween between two matrices
* @param {WebKitCSSMatrix} matrix The destination matrix
* @param {float} progress A float value between 0-1, representing the percentage of completion
* @param {function} fn An easing function following the prototype function(pos){}
* @author Joe Lambert
* @returns {WebKitCSSMatrix} A new matrix for the tweened state
*/
WebKitCSSMatrix.prototype.tween = function(matrix, progress, fn) {
if(fn === undefined)
fn = function(pos) {return pos;}; // Default to a linear easing
var m = new WebKitCSSMatrix,
m1 = this.decompose(),
m2 = matrix.decompose(),
r = m.decompose()
trans = '',
index = i = null;
// Tween between the two decompositions
return m1.tween(m2, progress, fn);
};
/**
* Transform a Vector4 object using the current matrix
* @param {Vector4} v The vector to transform
* @author Joe Lambert
* @returns {Vector4} The transformed vector
*/
WebKitCSSMatrix.prototype.transformVector = function(v) {
// TODO: Do we need to mod this for Vector4?
return new Vector4( this.m11*v.x + this.m12*v.y + this.m13*v.z,
this.m21*v.x + this.m22*v.y + this.m23*v.z,
this.m31*v.x + this.m32*v.y + this.m33*v.z );
};
/**
* Transposes the matrix
* @author Joe Lambert
* @returns {WebKitCSSMatrix} The transposed matrix
*/
WebKitCSSMatrix.prototype.transpose = function() {
var matrix = new WebKitCSSMatrix(), n = m = 0;
for (n = 0; n <= 4-2; n++)
{
for (m = n + 1; m <= 4-1; m++)
{
matrix['m'+(n+1)+(m+1)] = this['m'+(m+1)+(n+1)];
matrix['m'+(m+1)+(n+1)] = this['m'+(n+1)+(m+1)];
}
}
return matrix;
};
/**
* Calculates the determinant
* @author Joe Lambert
* @returns {float} The determinant of the matrix
*/
WebKitCSSMatrix.prototype.determinant = function() {
return this.m14 * this.m23 * this.m32 * this.m41-this.m13 * this.m24 * this.m32 * this.m41 -
this.m14 * this.m22 * this.m33 * this.m41+this.m12 * this.m24 * this.m33 * this.m41 +
this.m13 * this.m22 * this.m34 * this.m41-this.m12 * this.m23 * this.m34 * this.m41 -
this.m14 * this.m23 * this.m31 * this.m42+this.m13 * this.m24 * this.m31 * this.m42 +
this.m14 * this.m21 * this.m33 * this.m42-this.m11 * this.m24 * this.m33 * this.m42 -
this.m13 * this.m21 * this.m34 * this.m42+this.m11 * this.m23 * this.m34 * this.m42 +
this.m14 * this.m22 * this.m31 * this.m43-this.m12 * this.m24 * this.m31 * this.m43 -
this.m14 * this.m21 * this.m32 * this.m43+this.m11 * this.m24 * this.m32 * this.m43 +
this.m12 * this.m21 * this.m34 * this.m43-this.m11 * this.m22 * this.m34 * this.m43 -
this.m13 * this.m22 * this.m31 * this.m44+this.m12 * this.m23 * this.m31 * this.m44 +
this.m13 * this.m21 * this.m32 * this.m44-this.m11 * this.m23 * this.m32 * this.m44 -
this.m12 * this.m21 * this.m33 * this.m44+this.m11 * this.m22 * this.m33 * this.m44;
};
/**
* Decomposes the matrix into its component parts.
* A Javascript implementation of the pseudo code available from http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition
* @author Joe Lambert
* @returns {Object} An object with each of the components of the matrix (perspective, translate, skew, scale, rotate) or identity matrix on failure
*/
WebKitCSSMatrix.prototype.decompose = function() {
var matrix = new WebKitCSSMatrix(this.toString()),
perspectiveMatrix = rightHandSide = inversePerspectiveMatrix = transposedInversePerspectiveMatrix =
perspective = translate = row = i = scale = skew = pdum3 = rotate = null;
if (matrix.m33 == 0)
return new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose()); // Return the identity matrix
// Normalize the matrix.
for (i = 1; i <= 4; i++)
for (j = 1; j <= 4; j++)
matrix['m'+i+j] /= matrix.m44;
// perspectiveMatrix is used to solve for perspective, but it also provides
// an easy way to test for singularity of the upper 3x3 component.
perspectiveMatrix = matrix;
for (i = 1; i <= 3; i++)
perspectiveMatrix['m'+i+'4'] = 0;
perspectiveMatrix.m44 = 1;
if (perspectiveMatrix.determinant() == 0)
return new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose()); // Return the identity matrix
// First, isolate perspective.
if (matrix.m14 != 0 || matrix.m24 != 0 || matrix.m34 != 0)
{
// rightHandSide is the right hand side of the equation.
rightHandSide = new Vector4(matrix.m14, matrix.m24, matrix.m34, matrix.m44);
// Solve the equation by inverting perspectiveMatrix and multiplying
// rightHandSide by the inverse.
inversePerspectiveMatrix = perspectiveMatrix.inverse();
transposedInversePerspectiveMatrix = inversePerspectiveMatrix.transpose();
perspective = transposedInversePerspectiveMatrix.transformVector(rightHandSide);
// Clear the perspective partition
matrix.m14 = matrix.m24 = matrix.m34 = 0;
matrix.m44 = 1;
}
else
{
// No perspective.
perspective = new Vector4(0,0,0,1);
}
// Next take care of translation
translate = new Vector4(matrix.m41, matrix.m42, matrix.m43);
matrix.m41 = 0;
matrix.m42 = 0;
matrix.m43 = 0;
// Now get scale and shear. 'row' is a 3 element array of 3 component vectors
row = [
new Vector4(), new Vector4(), new Vector4()
];
for (i = 1; i <= 3; i++)
{
row[i-1].x = matrix['m'+i+'1'];
row[i-1].y = matrix['m'+i+'2'];
row[i-1].z = matrix['m'+i+'3'];
}
// Compute X scale factor and normalize first row.
scale = new Vector4();
skew = new Vector4();
scale.x = row[0].length();
row[0] = row[0].normalise();
// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew.x = row[0].dot(row[1]);
row[1] = row[1].combine(row[0], 1.0, -skew.x);
// Now, compute Y scale and normalize 2nd row.
scale.y = row[1].length();
row[1] = row[1].normalise();
skew.x /= scale.y;
// Compute XZ and YZ shears, orthogonalize 3rd row
skew.y = row[0].dot(row[2]);
row[2] = row[2].combine(row[0], 1.0, -skew.y);
skew.z = row[1].dot(row[2]);
row[2] = row[2].combine(row[1], 1.0, -skew.z);
// Next, get Z scale and normalize 3rd row.
scale.z = row[2].length();
row[2] = row[2].normalise();
skew.y /= scale.z;
skew.y /= scale.z;
// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip. If the determinant
// is -1, then negate the matrix and the scaling factors.
pdum3 = row[1].cross(row[2])
if (row[0].dot(pdum3) < 0)
{
for (i = 0; i < 3; i++)
{
scale.x *= -1;
row[i].x *= -1;
row[i].y *= -1;
row[i].z *= -1;
}
}
// Now, get the rotations out
rotate = new Vector4();
rotate.y = Math.asin(-row[0].z);
if (Math.cos(rotate.y) != 0)
{
rotate.x = Math.atan2(row[1].z, row[2].z);
rotate.z = Math.atan2(row[0].y, row[0].x);
}
else
{
rotate.x = Math.atan2(-row[2].x, row[1].y);
rotate.z = 0;
}
return new CSSMatrixDecomposed({
perspective: perspective,
translate: translate,
skew: skew,
scale: scale,
rotate: rotate
});
};
})();
/**
Mifty - A custom build of Shifty for use with Morf.js.
By Jeremy Kahn - jeremyckahn@gmail.com
v0.4.1
For instructions on how to use Shifty, please consult the README: https://github.com/jeremyckahn/shifty/blob/master/README.md
MIT Lincense. This code free to use, modify, distribute and enjoy.
*/
(function(e){function a(){return+new Date}function b(a,c){for(var j in a)a.hasOwnProperty(j)&&c(a,j)}function h(a,c){b(c,function(c,f){a[f]=c[f]});return a}function k(a,c){b(c,function(c,f){typeof a[f]==="undefined"&&(a[f]=c[f])});return a}function i(a,c,j){var f,a=(a-c.timestamp)/c.duration;for(f in j.current)j.current.hasOwnProperty(f)&&c.to.hasOwnProperty(f)&&(j.current[f]=c.originalState[f]+(c.to[f]-c.originalState[f])*c.easingFunc(a));return j.current}function n(a,c,j,f){var b;for(b=0;b=0;b++)this._hook[a][b]===c&&this._hook[a].splice(b,1);else this._hook[a]=[]};g.prototype.filter={};g.util={now:a,each:b,tweenProps:i,applyFilter:l,simpleCopy:h};g.prototype.formula={linear:function(a){return a}};e.Tweenable=g})(this);
(function(e){e.Tweenable.util.simpleCopy(e.Tweenable.prototype.formula,{easeInQuad:function(a){return Math.pow(a,2)},easeOutQuad:function(a){return-(Math.pow(a-1,2)-1)},easeInOutQuad:function(a){if((a/=0.5)<1)return 0.5*Math.pow(a,2);return-0.5*((a-=2)*a-2)},easeInCubic:function(a){return Math.pow(a,3)},easeOutCubic:function(a){return Math.pow(a-1,3)+1},easeInOutCubic:function(a){if((a/=0.5)<1)return 0.5*Math.pow(a,3);return 0.5*(Math.pow(a-2,3)+2)},easeInQuart:function(a){return Math.pow(a,4)},easeOutQuart:function(a){return-(Math.pow(a-
1,4)-1)},easeInOutQuart:function(a){if((a/=0.5)<1)return 0.5*Math.pow(a,4);return-0.5*((a-=2)*Math.pow(a,3)-2)},easeInQuint:function(a){return Math.pow(a,5)},easeOutQuint:function(a){return Math.pow(a-1,5)+1},easeInOutQuint:function(a){if((a/=0.5)<1)return 0.5*Math.pow(a,5);return 0.5*(Math.pow(a-2,5)+2)},easeInSine:function(a){return-Math.cos(a*(Math.PI/2))+1},easeOutSine:function(a){return Math.sin(a*(Math.PI/2))},easeInOutSine:function(a){return-0.5*(Math.cos(Math.PI*a)-1)},easeInExpo:function(a){return a==
0?0:Math.pow(2,10*(a-1))},easeOutExpo:function(a){return a==1?1:-Math.pow(2,-10*a)+1},easeInOutExpo:function(a){if(a==0)return 0;if(a==1)return 1;if((a/=0.5)<1)return 0.5*Math.pow(2,10*(a-1));return 0.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return-(Math.sqrt(1-a*a)-1)},easeOutCirc:function(a){return Math.sqrt(1-Math.pow(a-1,2))},easeInOutCirc:function(a){if((a/=0.5)<1)return-0.5*(Math.sqrt(1-a*a)-1);return 0.5*(Math.sqrt(1-(a-=2)*a)+1)},easeOutBounce:function(a){return a<1/2.75?7.5625*
a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375},easeInBack:function(a){return a*a*(2.70158*a-1.70158)},easeOutBack:function(a){return(a-=1)*a*(2.70158*a+1.70158)+1},easeInOutBack:function(a){var b=1.70158;if((a/=0.5)<1)return 0.5*a*a*(((b*=1.525)+1)*a-b);return 0.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},elastic:function(a){return-1*Math.pow(4,-8*a)*Math.sin((a*6-1)*2*Math.PI/2)+1},swingFromTo:function(a){var b=1.70158;return(a/=0.5)<
1?0.5*a*a*(((b*=1.525)+1)*a-b):0.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},swingFrom:function(a){return a*a*(2.70158*a-1.70158)},swingTo:function(a){return(a-=1)*a*(2.70158*a+1.70158)+1},bounce:function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375},bouncePast:function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?2-(7.5625*(a-=1.5/2.75)*a+0.75):a<2.5/2.75?2-(7.5625*(a-=2.25/2.75)*a+0.9375):2-(7.5625*(a-=2.625/2.75)*
a+0.984375)},easeFromTo:function(a){if((a/=0.5)<1)return 0.5*Math.pow(a,4);return-0.5*((a-=2)*Math.pow(a,3)-2)},easeFrom:function(a){return Math.pow(a,4)},easeTo:function(a){return Math.pow(a,0.25)}})})(this);
(function(e){if(e.Tweenable)e.Tweenable.util.interpolate=function(a,b,h,k){var i;if(a&&a.from)b=a.to,h=a.position,k=a.easing,a=a.from;i=e.Tweenable.util.simpleCopy({},a);e.Tweenable.util.applyFilter("tweenCreated",i,[i,a,b]);e.Tweenable.util.applyFilter("beforeTween",i,[i,a,b]);h=e.Tweenable.util.tweenProps(h,{originalState:a,to:b,timestamp:0,duration:1,easingFunc:e.Tweenable.prototype.formula[k]||e.Tweenable.prototype.formula.linear},{current:i});e.Tweenable.util.applyFilter("afterTween",h,[h,a,
b]);return h},e.Tweenable.prototype.interpolate=function(a,b,h){a=e.Tweenable.util.interpolate(this.get(),a,b,h);this.set(a);return a}})(this);
/**
* @preserve
* Extra easing functions borrowed from scripty2 (c) 2005-2010 Thomas Fuchs (MIT Licence)
* https://raw.github.com/madrobby/scripty2/master/src/effects/transitions/transitions.js
*/
(function(){
var scripty2 = {
spring: function(pos) {
return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
},
sinusoidal: function(pos) {
return (-Math.cos(pos*Math.PI)/2) + 0.5;
}
};
// Load the Scripty2 functions
for(var t in scripty2)
Tweenable.prototype.formula[t] = scripty2[t];
})();
================================================
FILE: js/morf.noshifty.js
================================================
/**
* @preserve Morf v0.1.5
* http://www.joelambert.co.uk/morf
*
* Copyright 2011, Joe Lambert.
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
var Morf = function(elem, css, opts) {
var from = {}, to = {},
fromElem = document.createElement('div'),
toElem = document.createElement('div'),
options = {
timingFunction: 'ease',
duration: null,
increment: 0.01,
debug: false,
optimise: true, // Whether the outputted CSS should be optimised
decimalPlaces: 5 // How many decimal places to optimise the WebKitCSSMatrix output to
},
// Define all other var's used in the function
i = rule = ruleName = camel = m1 = m2 = progress = frame = rule = transEvent = val = null, cacheKey = '',
// Setup a scoped reference to ourselves
_this = this,
keyframes = {},
// Create a unique name for this animation
animName = 'anim'+(new Date().getTime()),
/* --- Helper Functions ------------------------------------------------------------------- */
// Event listener for the webkitAnimationEnd Event
animationEndListener = function(event){
elem.removeEventListener('webkitAnimationEnd', animationEndListener, true);
// Dispatch a faux webkitTransitionEnd event to complete the appearance of this being a transition rather than an animation
// TODO: Should we throw an event for each property changed? (event.propertyName = 'opacity' etc)
transEvent = document.createEvent("Event");
transEvent.initEvent("webkitTransitionEnd", true, true);
elem.dispatchEvent(transEvent);
// Reset transition effects after use
elem.style.webkitTransitionTimingFunction = null;
elem.style.webkitTransitionDuration = 0;
if (options.callback) {
options.callback(elem);
}
},
// Adds the CSS to the current page
addKeyframeRule = function(rule) {
if (document.styleSheets && document.styleSheets.length)
document.styleSheets[0].insertRule(rule, 0);
else
{
var style = document.createElement('style');
style.innerHTML = rule;
document.head.appendChild(style);
}
},
// Produces a CSS string representation of the Keyframes
createAnimationCSS = function(kf, name) {
var str = '@-webkit-keyframes '+name+' {\n', f = pos = rule = null, fStr = '';
for(pos in kf)
{
f = kf[pos];
fStr = '\t'+pos+' {\n';
for(rule in f)
fStr += '\t\t'+_this.util.toDash(rule)+': '+f[rule]+';\n';
fStr += "\t}\n\n";
str += fStr;
}
return options.optimise ? optimiseCSS(str+' }') : str+' }';
},
// Replaces scale(0) with 0.0001 to get around the inability to these decompose matrix
sanityCheckTransformString = function(str) {
var scale = str.match(/scale[Y|X|Z]*\([0-9, ]*0[,0-9 ]*\)/g),
i = 0;
if(scale)
{
// There might be multiple scale() properties in the string
for(i = 0; i < scale.length; i++)
str = str.replace(scale[i], scale[i].replace(/([^0-9])0([^0.9])/g, "$10.0001$2"));
}
return str;
},
// WebKitCSSMatrix toString() ALWAYS outputs numbers to 5 decimal places - this helps optimise the string
optimiseCSS = function(str, decimalPlaces) {
decimalPlaces = typeof options.decimalPlaces == 'number' ? options.decimalPlaces : 5;
var matches = str.match(/[0-9\.]+/gm),
i = 0;
if(matches)
{
for(i = 0; i < matches.length; i++)
str = str.replace(matches[i], parseFloat( parseFloat(matches[i]).toFixed(decimalPlaces)));
}
return str;
};
/* --- Helper Functions End --------------------------------------------------------------- */
// Import the options
for(i in opts)
options[i] = opts[i];
// If timingFunction is a natively supported function then just trigger normal transition
if( options.timingFunction === 'ease' ||
options.timingFunction === 'linear' ||
options.timingFunction === 'ease-in' ||
options.timingFunction === 'ease-out' ||
options.timingFunction === 'ease-in-out' ||
/^cubic-bezier/.test(options.timingFunction)) {
elem.style.webkitTransitionDuration = options.duration;
elem.style.webkitTransitionTimingFunction = options.timingFunction;
// Listen for the transitionEnd event to fire the callback if needed
var transitionEndListener = function(event) {
elem.removeEventListener('webkitTransitionEnd', transitionEndListener, true);
// Clean up after ourself
elem.style.webkitTransitionDuration = 0;
elem.style.webkitTransitionTimingFunction = null;
if (options.callback) {
// Delay execution to ensure the clean up CSS has taken effect
setTimeout(function() {
options.callback(elem);
}, 10);
}
};
elem.addEventListener('webkitTransitionEnd', transitionEndListener, true);
setTimeout(function() {
for(rule in css) {
camel = _this.util.toCamel(rule);
elem.style[camel] = css[rule];
}
}, 10);
this.css = '';
return;
}
else
{
// Reset transition properties for this element
elem.style.webkitTransitionTimingFunction = null;
elem.style.webkitTransitionDuration = 0;
}
// Create the key used to cache this animation
cacheKey += options.timingFunction;
// Setup the start and end CSS state
for(rule in css)
{
camel = this.util.toCamel(rule);
toElem.style[camel] = css[rule];
// Set the from/start state
from[rule] = (camel == 'WebkitTransform') ? new WebKitCSSMatrix( sanityCheckTransformString( window.getComputedStyle(elem)['-webkit-transform'] ) ) : window.getComputedStyle(elem)[rule];
// Set the to/end state
to[rule] = (camel == 'WebkitTransform') ? new WebKitCSSMatrix( sanityCheckTransformString( toElem.style.WebkitTransform ) ) : toElem.style[camel];
// Shifty requires numeric values to be a number rather than a string (e.g. for opacity)
from[rule] = from[rule] == (val = parseInt(from[rule], 10)) ? val : from[rule];
to[rule] = to[rule] == (val = parseInt(from[rule], 10)) ? val : to[rule];
// Update the cacheKey
cacheKey += ';' + rule + ':' + from[rule] + '->' + to[rule];
}
// Check the cache to save expensive calculations
if(Morf.cache[cacheKey])
{
this.css = Morf.cache[cacheKey].css;
animName = Morf.cache[cacheKey].name;
}
else
{
// Produce decompositions of matrices here so we don't have to redo it on each iteration
// Decomposing the matrix is expensive so we need to minimise these requests
if(from['-webkit-transform'])
{
m1 = from['-webkit-transform'].decompose();
m2 = to['-webkit-transform'].decompose();
}
// Produce style keyframes
for(progress = 0; progress <= 1; progress += options.increment) {
// Use Shifty.js to work out the interpolated CSS state
frame = Tweenable.util.interpolate(from, to, progress, options.timingFunction);
// Work out the interpolated matrix transformation
if(m1 !== null && m2 !== null)
frame['-webkit-transform'] = m1.tween(m2, progress, Tweenable.prototype.formula[options.timingFunction]);
keyframes[parseInt(progress*100, 10)+'%'] = frame;
}
// Ensure the last frame has been added
keyframes['100%'] = to;
// Add the new animation to the document
this.css = createAnimationCSS(keyframes, animName);
addKeyframeRule(this.css);
Morf.cache[cacheKey] = {css: this.css, name: animName};
}
// Set the final position state as this should be a transition not an animation & the element should end in the 'to' state
for(rule in to)
elem.style[this.util.toCamel(rule)] = to[rule];
// Trigger the animation
elem.addEventListener('webkitAnimationEnd', animationEndListener, true);
elem.style.webkitAnimationDuration = options.duration;
elem.style.webkitAnimationTimingFunction = 'linear';
elem.style.webkitAnimationName = animName;
// Print the animation to the console if the debug switch is given
if(options.debug && window.console && window.console.log)
console.log(this.css);
};
/**
* Convenience function for triggering a transition
* @param {HTMLDom} elem The element to apply the transition to
* @param {Object} css Key value pair of CSS properties
* @param {Object} opts Additional configuration options
*
* Configuration options
* - timingFunction: {String} Name of the easing function to perform
* - duration: {integer} Duration in ms
* - increment: {float} How frequently to generate keyframes (Defaults to 0.01, which is every 1%)
* - debug: {Boolean} Should the generated CSS Animation be printed to the console
*
* @returns {Morf} An instance of the Morf object
*/
Morf.transition = function(elem, css, opts){
return new Morf(elem, css, opts);
};
/**
* Object to cache generated animations
*/
Morf.cache = {};
/**
* Current version
*/
Morf.version = '0.1.5';
// Utilities Placeholder
Morf.prototype.util = {};
/**
* Converts a DOM style string to CSS property name
* @param {String} str A DOM style string
* @returns {String} CSS property name
*/
Morf.prototype.util.toDash = function(str){
str = str.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();});
return /^webkit/.test(str) ? '-'+str : str;
};
/**
* Converts a CSS property name to DOM style string
* @param {String} str A CSS property name
* @returns {String} DOM style string
*/
Morf.prototype.util.toCamel = function(str){
return str.replace(/(\-[a-z])/g, function($1){return $1.toUpperCase().replace('-','');});
};
/**
* WebKitCSSMatrix Extensions
*
* Copyright 2011, Joe Lambert (http://www.joelambert.co.uk)
* Free to use under the MIT license.
* http://joelambert.mit-license.org/
*/
// Wrap this functionality up to prevent poluting the global namespace
(function(){
/**
* A 4 dimensional vector
* @author Joe Lambert
* @constructor
*/
var Vector4 = function(x, y, z, w)
{
this.x = x ? x : 0;
this.y = y ? y : 0;
this.z = z ? z : 0;
this.w = w ? w : 0;
/**
* Ensure that values are not undefined
* @author Joe Lambert
* @returns null
*/
this.checkValues = function() {
this.x = this.x ? this.x : 0;
this.y = this.y ? this.y : 0;
this.z = this.z ? this.z : 0;
this.w = this.w ? this.w : 0;
};
/**
* Get the length of the vector
* @author Joe Lambert
* @returns {float}
*/
this.length = function() {
this.checkValues();
return Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z);
};
/**
* Get a normalised representation of the vector
* @author Joe Lambert
* @returns {Vector4}
*/
this.normalise = function() {
var len = this.length(),
v = new Vector4(this.x / len, this.y / len, this.z / len);
return v;
};
/**
* Vector Dot-Product
* @param {Vector4} v The second vector to apply the product to
* @author Joe Lambert
* @returns {float} The Dot-Product of this and v.
*/
this.dot = function(v) {
return this.x*v.x + this.y*v.y + this.z*v.z + this.w*v.w;
};
/**
* Vector Cross-Product
* @param {Vector4} v The second vector to apply the product to
* @author Joe Lambert
* @returns {Vector4} The Cross-Product of this and v.
*/
this.cross = function(v) {
return new Vector4(this.y*v.z - this.z*v.y, this.z*v.x - this.x*v.z, this.x*v.y - this.y*v.x);
};
/**
* Helper function required for matrix decomposition
* A Javascript implementation of pseudo code available from http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition
* @param {Vector4} aPoint A 3D point
* @param {float} ascl
* @param {float} bscl
* @author Joe Lambert
* @returns {Vector4}
*/
this.combine = function(aPoint, ascl, bscl) {
return new Vector4( (ascl * this.x) + (bscl * aPoint.x),
(ascl * this.y) + (bscl * aPoint.y),
(ascl * this.z) + (bscl * aPoint.z) );
}
};
/**
* Object containing the decomposed components of a matrix
* @author Joe Lambert
* @constructor
*/
var CSSMatrixDecomposed = function(obj) {
obj === undefined ? obj = {} : null;
var components = {perspective: null, translate: null, skew: null, scale: null, rotate: null};
for(var i in components)
this[i] = obj[i] ? obj[i] : new Vector4();
/**
* Tween between two decomposed matrices
* @param {CSSMatrixDecomposed} dm The destination decomposed matrix
* @param {float} progress A float value between 0-1, representing the percentage of completion
* @param {function} fn An easing function following the prototype function(pos){}
* @author Joe Lambert
* @returns {WebKitCSSMatrix} A new matrix for the tweened state
*/
this.tween = function(dm, progress, fn) {
if(fn === undefined)
fn = function(pos) {return pos;}; // Default to a linear easing
if(!dm)
dm = new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose());
var r = new CSSMatrixDecomposed(),
i = index = null,
trans = '';
progress = fn(progress);
for(index in components)
for(i in {x:'x', y:'y', z:'z', w:'w'})
r[index][i] = (this[index][i] + (dm[index][i] - this[index][i]) * progress ).toFixed(5);
trans = 'matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, '+r.perspective.x+', '+r.perspective.y+', '+r.perspective.z+', '+r.perspective.w+') ' +
'translate3d('+r.translate.x+'px, '+r.translate.y+'px, '+r.translate.z+'px) ' +
'rotateX('+r.rotate.x+'rad) rotateY('+r.rotate.y+'rad) rotateZ('+r.rotate.z+'rad) ' +
'matrix3d(1,0,0,0, 0,1,0,0, 0,'+r.skew.z+',1,0, 0,0,0,1) ' +
'matrix3d(1,0,0,0, 0,1,0,0, '+r.skew.y+',0,1,0, 0,0,0,1) ' +
'matrix3d(1,0,0,0, '+r.skew.x+',1,0,0, 0,0,1,0, 0,0,0,1) ' +
'scale3d('+r.scale.x+', '+r.scale.y+', '+r.scale.z+')';
try { r = new WebKitCSSMatrix(trans); return r; }
catch(e) { console.error('Invalid matrix string: '+trans); return '' };
};
};
/**
* Tween between two matrices
* @param {WebKitCSSMatrix} matrix The destination matrix
* @param {float} progress A float value between 0-1, representing the percentage of completion
* @param {function} fn An easing function following the prototype function(pos){}
* @author Joe Lambert
* @returns {WebKitCSSMatrix} A new matrix for the tweened state
*/
WebKitCSSMatrix.prototype.tween = function(matrix, progress, fn) {
if(fn === undefined)
fn = function(pos) {return pos;}; // Default to a linear easing
var m = new WebKitCSSMatrix,
m1 = this.decompose(),
m2 = matrix.decompose(),
r = m.decompose()
trans = '',
index = i = null;
// Tween between the two decompositions
return m1.tween(m2, progress, fn);
};
/**
* Transform a Vector4 object using the current matrix
* @param {Vector4} v The vector to transform
* @author Joe Lambert
* @returns {Vector4} The transformed vector
*/
WebKitCSSMatrix.prototype.transformVector = function(v) {
// TODO: Do we need to mod this for Vector4?
return new Vector4( this.m11*v.x + this.m12*v.y + this.m13*v.z,
this.m21*v.x + this.m22*v.y + this.m23*v.z,
this.m31*v.x + this.m32*v.y + this.m33*v.z );
};
/**
* Transposes the matrix
* @author Joe Lambert
* @returns {WebKitCSSMatrix} The transposed matrix
*/
WebKitCSSMatrix.prototype.transpose = function() {
var matrix = new WebKitCSSMatrix(), n = m = 0;
for (n = 0; n <= 4-2; n++)
{
for (m = n + 1; m <= 4-1; m++)
{
matrix['m'+(n+1)+(m+1)] = this['m'+(m+1)+(n+1)];
matrix['m'+(m+1)+(n+1)] = this['m'+(n+1)+(m+1)];
}
}
return matrix;
};
/**
* Calculates the determinant
* @author Joe Lambert
* @returns {float} The determinant of the matrix
*/
WebKitCSSMatrix.prototype.determinant = function() {
return this.m14 * this.m23 * this.m32 * this.m41-this.m13 * this.m24 * this.m32 * this.m41 -
this.m14 * this.m22 * this.m33 * this.m41+this.m12 * this.m24 * this.m33 * this.m41 +
this.m13 * this.m22 * this.m34 * this.m41-this.m12 * this.m23 * this.m34 * this.m41 -
this.m14 * this.m23 * this.m31 * this.m42+this.m13 * this.m24 * this.m31 * this.m42 +
this.m14 * this.m21 * this.m33 * this.m42-this.m11 * this.m24 * this.m33 * this.m42 -
this.m13 * this.m21 * this.m34 * this.m42+this.m11 * this.m23 * this.m34 * this.m42 +
this.m14 * this.m22 * this.m31 * this.m43-this.m12 * this.m24 * this.m31 * this.m43 -
this.m14 * this.m21 * this.m32 * this.m43+this.m11 * this.m24 * this.m32 * this.m43 +
this.m12 * this.m21 * this.m34 * this.m43-this.m11 * this.m22 * this.m34 * this.m43 -
this.m13 * this.m22 * this.m31 * this.m44+this.m12 * this.m23 * this.m31 * this.m44 +
this.m13 * this.m21 * this.m32 * this.m44-this.m11 * this.m23 * this.m32 * this.m44 -
this.m12 * this.m21 * this.m33 * this.m44+this.m11 * this.m22 * this.m33 * this.m44;
};
/**
* Decomposes the matrix into its component parts.
* A Javascript implementation of the pseudo code available from http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition
* @author Joe Lambert
* @returns {Object} An object with each of the components of the matrix (perspective, translate, skew, scale, rotate) or identity matrix on failure
*/
WebKitCSSMatrix.prototype.decompose = function() {
var matrix = new WebKitCSSMatrix(this.toString()),
perspectiveMatrix = rightHandSide = inversePerspectiveMatrix = transposedInversePerspectiveMatrix =
perspective = translate = row = i = scale = skew = pdum3 = rotate = null;
if (matrix.m33 == 0)
return new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose()); // Return the identity matrix
// Normalize the matrix.
for (i = 1; i <= 4; i++)
for (j = 1; j <= 4; j++)
matrix['m'+i+j] /= matrix.m44;
// perspectiveMatrix is used to solve for perspective, but it also provides
// an easy way to test for singularity of the upper 3x3 component.
perspectiveMatrix = matrix;
for (i = 1; i <= 3; i++)
perspectiveMatrix['m'+i+'4'] = 0;
perspectiveMatrix.m44 = 1;
if (perspectiveMatrix.determinant() == 0)
return new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose()); // Return the identity matrix
// First, isolate perspective.
if (matrix.m14 != 0 || matrix.m24 != 0 || matrix.m34 != 0)
{
// rightHandSide is the right hand side of the equation.
rightHandSide = new Vector4(matrix.m14, matrix.m24, matrix.m34, matrix.m44);
// Solve the equation by inverting perspectiveMatrix and multiplying
// rightHandSide by the inverse.
inversePerspectiveMatrix = perspectiveMatrix.inverse();
transposedInversePerspectiveMatrix = inversePerspectiveMatrix.transpose();
perspective = transposedInversePerspectiveMatrix.transformVector(rightHandSide);
// Clear the perspective partition
matrix.m14 = matrix.m24 = matrix.m34 = 0;
matrix.m44 = 1;
}
else
{
// No perspective.
perspective = new Vector4(0,0,0,1);
}
// Next take care of translation
translate = new Vector4(matrix.m41, matrix.m42, matrix.m43);
matrix.m41 = 0;
matrix.m42 = 0;
matrix.m43 = 0;
// Now get scale and shear. 'row' is a 3 element array of 3 component vectors
row = [
new Vector4(), new Vector4(), new Vector4()
];
for (i = 1; i <= 3; i++)
{
row[i-1].x = matrix['m'+i+'1'];
row[i-1].y = matrix['m'+i+'2'];
row[i-1].z = matrix['m'+i+'3'];
}
// Compute X scale factor and normalize first row.
scale = new Vector4();
skew = new Vector4();
scale.x = row[0].length();
row[0] = row[0].normalise();
// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew.x = row[0].dot(row[1]);
row[1] = row[1].combine(row[0], 1.0, -skew.x);
// Now, compute Y scale and normalize 2nd row.
scale.y = row[1].length();
row[1] = row[1].normalise();
skew.x /= scale.y;
// Compute XZ and YZ shears, orthogonalize 3rd row
skew.y = row[0].dot(row[2]);
row[2] = row[2].combine(row[0], 1.0, -skew.y);
skew.z = row[1].dot(row[2]);
row[2] = row[2].combine(row[1], 1.0, -skew.z);
// Next, get Z scale and normalize 3rd row.
scale.z = row[2].length();
row[2] = row[2].normalise();
skew.y /= scale.z;
skew.y /= scale.z;
// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip. If the determinant
// is -1, then negate the matrix and the scaling factors.
pdum3 = row[1].cross(row[2])
if (row[0].dot(pdum3) < 0)
{
for (i = 0; i < 3; i++)
{
scale.x *= -1;
row[i].x *= -1;
row[i].y *= -1;
row[i].z *= -1;
}
}
// Now, get the rotations out
rotate = new Vector4();
rotate.y = Math.asin(-row[0].z);
if (Math.cos(rotate.y) != 0)
{
rotate.x = Math.atan2(row[1].z, row[2].z);
rotate.z = Math.atan2(row[0].y, row[0].x);
}
else
{
rotate.x = Math.atan2(-row[2].x, row[1].y);
rotate.z = 0;
}
return new CSSMatrixDecomposed({
perspective: perspective,
translate: translate,
skew: skew,
scale: scale,
rotate: rotate
});
};
})();
/**
* @preserve
* Extra easing functions borrowed from scripty2 (c) 2005-2010 Thomas Fuchs (MIT Licence)
* https://raw.github.com/madrobby/scripty2/master/src/effects/transitions/transitions.js
*/
(function(){
var scripty2 = {
spring: function(pos) {
return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
},
sinusoidal: function(pos) {
return (-Math.cos(pos*Math.PI)/2) + 0.5;
}
};
// Load the Scripty2 functions
for(var t in scripty2)
Tweenable.prototype.formula[t] = scripty2[t];
})();
================================================
FILE: js/src/WebkitCSSMatrix.ext.js
================================================
/**
* WebKitCSSMatrix Extensions
*
* Copyright 2011, Joe Lambert (http://www.joelambert.co.uk)
* Free to use under the MIT license.
* http://joelambert.mit-license.org/
*/
// Wrap this functionality up to prevent poluting the global namespace
(function(){
/**
* A 4 dimensional vector
* @author Joe Lambert
* @constructor
*/
var Vector4 = function(x, y, z, w)
{
this.x = x ? x : 0;
this.y = y ? y : 0;
this.z = z ? z : 0;
this.w = w ? w : 0;
/**
* Ensure that values are not undefined
* @author Joe Lambert
* @returns null
*/
this.checkValues = function() {
this.x = this.x ? this.x : 0;
this.y = this.y ? this.y : 0;
this.z = this.z ? this.z : 0;
this.w = this.w ? this.w : 0;
};
/**
* Get the length of the vector
* @author Joe Lambert
* @returns {float}
*/
this.length = function() {
this.checkValues();
return Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z);
};
/**
* Get a normalised representation of the vector
* @author Joe Lambert
* @returns {Vector4}
*/
this.normalise = function() {
var len = this.length(),
v = new Vector4(this.x / len, this.y / len, this.z / len);
return v;
};
/**
* Vector Dot-Product
* @param {Vector4} v The second vector to apply the product to
* @author Joe Lambert
* @returns {float} The Dot-Product of this and v.
*/
this.dot = function(v) {
return this.x*v.x + this.y*v.y + this.z*v.z + this.w*v.w;
};
/**
* Vector Cross-Product
* @param {Vector4} v The second vector to apply the product to
* @author Joe Lambert
* @returns {Vector4} The Cross-Product of this and v.
*/
this.cross = function(v) {
return new Vector4(this.y*v.z - this.z*v.y, this.z*v.x - this.x*v.z, this.x*v.y - this.y*v.x);
};
/**
* Helper function required for matrix decomposition
* A Javascript implementation of pseudo code available from http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition
* @param {Vector4} aPoint A 3D point
* @param {float} ascl
* @param {float} bscl
* @author Joe Lambert
* @returns {Vector4}
*/
this.combine = function(aPoint, ascl, bscl) {
return new Vector4( (ascl * this.x) + (bscl * aPoint.x),
(ascl * this.y) + (bscl * aPoint.y),
(ascl * this.z) + (bscl * aPoint.z) );
}
};
/**
* Object containing the decomposed components of a matrix
* @author Joe Lambert
* @constructor
*/
var CSSMatrixDecomposed = function(obj) {
obj === undefined ? obj = {} : null;
var components = {perspective: null, translate: null, skew: null, scale: null, rotate: null};
for(var i in components)
this[i] = obj[i] ? obj[i] : new Vector4();
/**
* Tween between two decomposed matrices
* @param {CSSMatrixDecomposed} dm The destination decomposed matrix
* @param {float} progress A float value between 0-1, representing the percentage of completion
* @param {function} fn An easing function following the prototype function(pos){}
* @author Joe Lambert
* @returns {WebKitCSSMatrix} A new matrix for the tweened state
*/
this.tween = function(dm, progress, fn) {
if(fn === undefined)
fn = function(pos) {return pos;}; // Default to a linear easing
if(!dm)
dm = new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose());
var r = new CSSMatrixDecomposed(),
i = index = null,
trans = '';
progress = fn(progress);
for(index in components)
for(i in {x:'x', y:'y', z:'z', w:'w'})
r[index][i] = (this[index][i] + (dm[index][i] - this[index][i]) * progress ).toFixed(5);
trans = 'matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, '+r.perspective.x+', '+r.perspective.y+', '+r.perspective.z+', '+r.perspective.w+') ' +
'translate3d('+r.translate.x+'px, '+r.translate.y+'px, '+r.translate.z+'px) ' +
'rotateX('+r.rotate.x+'rad) rotateY('+r.rotate.y+'rad) rotateZ('+r.rotate.z+'rad) ' +
'matrix3d(1,0,0,0, 0,1,0,0, 0,'+r.skew.z+',1,0, 0,0,0,1) ' +
'matrix3d(1,0,0,0, 0,1,0,0, '+r.skew.y+',0,1,0, 0,0,0,1) ' +
'matrix3d(1,0,0,0, '+r.skew.x+',1,0,0, 0,0,1,0, 0,0,0,1) ' +
'scale3d('+r.scale.x+', '+r.scale.y+', '+r.scale.z+')';
try { r = new WebKitCSSMatrix(trans); return r; }
catch(e) { console.error('Invalid matrix string: '+trans); return '' };
};
};
/**
* Tween between two matrices
* @param {WebKitCSSMatrix} matrix The destination matrix
* @param {float} progress A float value between 0-1, representing the percentage of completion
* @param {function} fn An easing function following the prototype function(pos){}
* @author Joe Lambert
* @returns {WebKitCSSMatrix} A new matrix for the tweened state
*/
WebKitCSSMatrix.prototype.tween = function(matrix, progress, fn) {
if(fn === undefined)
fn = function(pos) {return pos;}; // Default to a linear easing
var m = new WebKitCSSMatrix,
m1 = this.decompose(),
m2 = matrix.decompose(),
r = m.decompose()
trans = '',
index = i = null;
// Tween between the two decompositions
return m1.tween(m2, progress, fn);
};
/**
* Transform a Vector4 object using the current matrix
* @param {Vector4} v The vector to transform
* @author Joe Lambert
* @returns {Vector4} The transformed vector
*/
WebKitCSSMatrix.prototype.transformVector = function(v) {
// TODO: Do we need to mod this for Vector4?
return new Vector4( this.m11*v.x + this.m12*v.y + this.m13*v.z,
this.m21*v.x + this.m22*v.y + this.m23*v.z,
this.m31*v.x + this.m32*v.y + this.m33*v.z );
};
/**
* Transposes the matrix
* @author Joe Lambert
* @returns {WebKitCSSMatrix} The transposed matrix
*/
WebKitCSSMatrix.prototype.transpose = function() {
var matrix = new WebKitCSSMatrix(), n = m = 0;
for (n = 0; n <= 4-2; n++)
{
for (m = n + 1; m <= 4-1; m++)
{
matrix['m'+(n+1)+(m+1)] = this['m'+(m+1)+(n+1)];
matrix['m'+(m+1)+(n+1)] = this['m'+(n+1)+(m+1)];
}
}
return matrix;
};
/**
* Calculates the determinant
* @author Joe Lambert
* @returns {float} The determinant of the matrix
*/
WebKitCSSMatrix.prototype.determinant = function() {
return this.m14 * this.m23 * this.m32 * this.m41-this.m13 * this.m24 * this.m32 * this.m41 -
this.m14 * this.m22 * this.m33 * this.m41+this.m12 * this.m24 * this.m33 * this.m41 +
this.m13 * this.m22 * this.m34 * this.m41-this.m12 * this.m23 * this.m34 * this.m41 -
this.m14 * this.m23 * this.m31 * this.m42+this.m13 * this.m24 * this.m31 * this.m42 +
this.m14 * this.m21 * this.m33 * this.m42-this.m11 * this.m24 * this.m33 * this.m42 -
this.m13 * this.m21 * this.m34 * this.m42+this.m11 * this.m23 * this.m34 * this.m42 +
this.m14 * this.m22 * this.m31 * this.m43-this.m12 * this.m24 * this.m31 * this.m43 -
this.m14 * this.m21 * this.m32 * this.m43+this.m11 * this.m24 * this.m32 * this.m43 +
this.m12 * this.m21 * this.m34 * this.m43-this.m11 * this.m22 * this.m34 * this.m43 -
this.m13 * this.m22 * this.m31 * this.m44+this.m12 * this.m23 * this.m31 * this.m44 +
this.m13 * this.m21 * this.m32 * this.m44-this.m11 * this.m23 * this.m32 * this.m44 -
this.m12 * this.m21 * this.m33 * this.m44+this.m11 * this.m22 * this.m33 * this.m44;
};
/**
* Decomposes the matrix into its component parts.
* A Javascript implementation of the pseudo code available from http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition
* @author Joe Lambert
* @returns {Object} An object with each of the components of the matrix (perspective, translate, skew, scale, rotate) or identity matrix on failure
*/
WebKitCSSMatrix.prototype.decompose = function() {
var matrix = new WebKitCSSMatrix(this.toString()),
perspectiveMatrix = rightHandSide = inversePerspectiveMatrix = transposedInversePerspectiveMatrix =
perspective = translate = row = i = scale = skew = pdum3 = rotate = null;
if (matrix.m33 == 0)
return new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose()); // Return the identity matrix
// Normalize the matrix.
for (i = 1; i <= 4; i++)
for (j = 1; j <= 4; j++)
matrix['m'+i+j] /= matrix.m44;
// perspectiveMatrix is used to solve for perspective, but it also provides
// an easy way to test for singularity of the upper 3x3 component.
perspectiveMatrix = matrix;
for (i = 1; i <= 3; i++)
perspectiveMatrix['m'+i+'4'] = 0;
perspectiveMatrix.m44 = 1;
if (perspectiveMatrix.determinant() == 0)
return new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose()); // Return the identity matrix
// First, isolate perspective.
if (matrix.m14 != 0 || matrix.m24 != 0 || matrix.m34 != 0)
{
// rightHandSide is the right hand side of the equation.
rightHandSide = new Vector4(matrix.m14, matrix.m24, matrix.m34, matrix.m44);
// Solve the equation by inverting perspectiveMatrix and multiplying
// rightHandSide by the inverse.
inversePerspectiveMatrix = perspectiveMatrix.inverse();
transposedInversePerspectiveMatrix = inversePerspectiveMatrix.transpose();
perspective = transposedInversePerspectiveMatrix.transformVector(rightHandSide);
// Clear the perspective partition
matrix.m14 = matrix.m24 = matrix.m34 = 0;
matrix.m44 = 1;
}
else
{
// No perspective.
perspective = new Vector4(0,0,0,1);
}
// Next take care of translation
translate = new Vector4(matrix.m41, matrix.m42, matrix.m43);
matrix.m41 = 0;
matrix.m42 = 0;
matrix.m43 = 0;
// Now get scale and shear. 'row' is a 3 element array of 3 component vectors
row = [
new Vector4(), new Vector4(), new Vector4()
];
for (i = 1; i <= 3; i++)
{
row[i-1].x = matrix['m'+i+'1'];
row[i-1].y = matrix['m'+i+'2'];
row[i-1].z = matrix['m'+i+'3'];
}
// Compute X scale factor and normalize first row.
scale = new Vector4();
skew = new Vector4();
scale.x = row[0].length();
row[0] = row[0].normalise();
// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew.x = row[0].dot(row[1]);
row[1] = row[1].combine(row[0], 1.0, -skew.x);
// Now, compute Y scale and normalize 2nd row.
scale.y = row[1].length();
row[1] = row[1].normalise();
skew.x /= scale.y;
// Compute XZ and YZ shears, orthogonalize 3rd row
skew.y = row[0].dot(row[2]);
row[2] = row[2].combine(row[0], 1.0, -skew.y);
skew.z = row[1].dot(row[2]);
row[2] = row[2].combine(row[1], 1.0, -skew.z);
// Next, get Z scale and normalize 3rd row.
scale.z = row[2].length();
row[2] = row[2].normalise();
skew.y /= scale.z;
skew.y /= scale.z;
// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip. If the determinant
// is -1, then negate the matrix and the scaling factors.
pdum3 = row[1].cross(row[2])
if (row[0].dot(pdum3) < 0)
{
for (i = 0; i < 3; i++)
{
scale.x *= -1;
row[i].x *= -1;
row[i].y *= -1;
row[i].z *= -1;
}
}
// Now, get the rotations out
rotate = new Vector4();
rotate.y = Math.asin(-row[0].z);
if (Math.cos(rotate.y) != 0)
{
rotate.x = Math.atan2(row[1].z, row[2].z);
rotate.z = Math.atan2(row[0].y, row[0].x);
}
else
{
rotate.x = Math.atan2(-row[2].x, row[1].y);
rotate.z = 0;
}
return new CSSMatrixDecomposed({
perspective: perspective,
translate: translate,
skew: skew,
scale: scale,
rotate: rotate
});
};
})();
================================================
FILE: js/src/morf.js
================================================
/**
* @preserve Morf v@VERSION
* http://www.joelambert.co.uk/morf
*
* Copyright 2011, Joe Lambert.
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
var Morf = function(elem, css, opts) {
var from = {}, to = {},
fromElem = document.createElement('div'),
toElem = document.createElement('div'),
options = {
timingFunction: 'ease',
duration: null,
increment: 0.01,
debug: false,
optimise: true, // Whether the outputted CSS should be optimised
decimalPlaces: 5 // How many decimal places to optimise the WebKitCSSMatrix output to
},
// Define all other var's used in the function
i = rule = ruleName = camel = m1 = m2 = progress = frame = rule = transEvent = val = null, cacheKey = '',
// Setup a scoped reference to ourselves
_this = this,
keyframes = {},
// Create a unique name for this animation
animName = 'anim'+(new Date().getTime()),
/* --- Helper Functions ------------------------------------------------------------------- */
// Event listener for the webkitAnimationEnd Event
animationEndListener = function(event){
elem.removeEventListener('webkitAnimationEnd', animationEndListener, true);
// Dispatch a faux webkitTransitionEnd event to complete the appearance of this being a transition rather than an animation
// TODO: Should we throw an event for each property changed? (event.propertyName = 'opacity' etc)
transEvent = document.createEvent("Event");
transEvent.initEvent("webkitTransitionEnd", true, true);
elem.dispatchEvent(transEvent);
// Reset transition effects after use
elem.style.webkitTransitionTimingFunction = null;
elem.style.webkitTransitionDuration = 0;
if (options.callback) {
options.callback(elem);
}
},
// Adds the CSS to the current page
addKeyframeRule = function(rule) {
if (document.styleSheets && document.styleSheets.length)
document.styleSheets[0].insertRule(rule, 0);
else
{
var style = document.createElement('style');
style.innerHTML = rule;
document.head.appendChild(style);
}
},
// Produces a CSS string representation of the Keyframes
createAnimationCSS = function(kf, name) {
var str = '@-webkit-keyframes '+name+' {\n', f = pos = rule = null, fStr = '';
for(pos in kf)
{
f = kf[pos];
fStr = '\t'+pos+' {\n';
for(rule in f)
fStr += '\t\t'+_this.util.toDash(rule)+': '+f[rule]+';\n';
fStr += "\t}\n\n";
str += fStr;
}
return options.optimise ? optimiseCSS(str+' }') : str+' }';
},
// Replaces scale(0) with 0.0001 to get around the inability to these decompose matrix
sanityCheckTransformString = function(str) {
var scale = str.match(/scale[Y|X|Z]*\([0-9, ]*0[,0-9 ]*\)/g),
i = 0;
if(scale)
{
// There might be multiple scale() properties in the string
for(i = 0; i < scale.length; i++)
str = str.replace(scale[i], scale[i].replace(/([^0-9])0([^0.9])/g, "$10.0001$2"));
}
return str;
},
// WebKitCSSMatrix toString() ALWAYS outputs numbers to 5 decimal places - this helps optimise the string
optimiseCSS = function(str, decimalPlaces) {
decimalPlaces = typeof options.decimalPlaces == 'number' ? options.decimalPlaces : 5;
var matches = str.match(/[0-9\.]+/gm),
i = 0;
if(matches)
{
for(i = 0; i < matches.length; i++)
str = str.replace(matches[i], parseFloat( parseFloat(matches[i]).toFixed(decimalPlaces)));
}
return str;
};
/* --- Helper Functions End --------------------------------------------------------------- */
// Import the options
for(i in opts)
options[i] = opts[i];
// If timingFunction is a natively supported function then just trigger normal transition
if( options.timingFunction === 'ease' ||
options.timingFunction === 'linear' ||
options.timingFunction === 'ease-in' ||
options.timingFunction === 'ease-out' ||
options.timingFunction === 'ease-in-out' ||
/^cubic-bezier/.test(options.timingFunction)) {
elem.style.webkitTransitionDuration = options.duration;
elem.style.webkitTransitionTimingFunction = options.timingFunction;
// Listen for the transitionEnd event to fire the callback if needed
var transitionEndListener = function(event) {
elem.removeEventListener('webkitTransitionEnd', transitionEndListener, true);
// Clean up after ourself
elem.style.webkitTransitionDuration = 0;
elem.style.webkitTransitionTimingFunction = null;
if (options.callback) {
// Delay execution to ensure the clean up CSS has taken effect
setTimeout(function() {
options.callback(elem);
}, 10);
}
};
elem.addEventListener('webkitTransitionEnd', transitionEndListener, true);
setTimeout(function() {
for(rule in css) {
camel = _this.util.toCamel(rule);
elem.style[camel] = css[rule];
}
}, 10);
this.css = '';
return;
}
else
{
// Reset transition properties for this element
elem.style.webkitTransitionTimingFunction = null;
elem.style.webkitTransitionDuration = 0;
}
// Create the key used to cache this animation
cacheKey += options.timingFunction;
// Setup the start and end CSS state
for(rule in css)
{
camel = this.util.toCamel(rule);
toElem.style[camel] = css[rule];
// Set the from/start state
from[rule] = (camel == 'WebkitTransform') ? new WebKitCSSMatrix( sanityCheckTransformString( window.getComputedStyle(elem)['-webkit-transform'] ) ) : window.getComputedStyle(elem)[rule];
// Set the to/end state
to[rule] = (camel == 'WebkitTransform') ? new WebKitCSSMatrix( sanityCheckTransformString( toElem.style.WebkitTransform ) ) : toElem.style[camel];
// Shifty requires numeric values to be a number rather than a string (e.g. for opacity)
from[rule] = from[rule] == (val = parseInt(from[rule], 10)) ? val : from[rule];
to[rule] = to[rule] == (val = parseInt(from[rule], 10)) ? val : to[rule];
// Update the cacheKey
cacheKey += ';' + rule + ':' + from[rule] + '->' + to[rule];
}
// Check the cache to save expensive calculations
if(Morf.cache[cacheKey])
{
this.css = Morf.cache[cacheKey].css;
animName = Morf.cache[cacheKey].name;
}
else
{
// Produce decompositions of matrices here so we don't have to redo it on each iteration
// Decomposing the matrix is expensive so we need to minimise these requests
if(from['-webkit-transform'])
{
m1 = from['-webkit-transform'].decompose();
m2 = to['-webkit-transform'].decompose();
}
// Produce style keyframes
for(progress = 0; progress <= 1; progress += options.increment) {
// Use Shifty.js to work out the interpolated CSS state
frame = Tweenable.util.interpolate(from, to, progress, options.timingFunction);
// Work out the interpolated matrix transformation
if(m1 !== null && m2 !== null)
frame['-webkit-transform'] = m1.tween(m2, progress, Tweenable.prototype.formula[options.timingFunction]);
keyframes[parseInt(progress*100, 10)+'%'] = frame;
}
// Ensure the last frame has been added
keyframes['100%'] = to;
// Add the new animation to the document
this.css = createAnimationCSS(keyframes, animName);
addKeyframeRule(this.css);
Morf.cache[cacheKey] = {css: this.css, name: animName};
}
// Set the final position state as this should be a transition not an animation & the element should end in the 'to' state
for(rule in to)
elem.style[this.util.toCamel(rule)] = to[rule];
// Trigger the animation
elem.addEventListener('webkitAnimationEnd', animationEndListener, true);
elem.style.webkitAnimationDuration = options.duration;
elem.style.webkitAnimationTimingFunction = 'linear';
elem.style.webkitAnimationName = animName;
// Print the animation to the console if the debug switch is given
if(options.debug && window.console && window.console.log)
console.log(this.css);
};
/**
* Convenience function for triggering a transition
* @param {HTMLDom} elem The element to apply the transition to
* @param {Object} css Key value pair of CSS properties
* @param {Object} opts Additional configuration options
*
* Configuration options
* - timingFunction: {String} Name of the easing function to perform
* - duration: {integer} Duration in ms
* - increment: {float} How frequently to generate keyframes (Defaults to 0.01, which is every 1%)
* - debug: {Boolean} Should the generated CSS Animation be printed to the console
*
* @returns {Morf} An instance of the Morf object
*/
Morf.transition = function(elem, css, opts){
return new Morf(elem, css, opts);
};
/**
* Object to cache generated animations
*/
Morf.cache = {};
/**
* Current version
*/
Morf.version = '@VERSION';
================================================
FILE: js/src/morf.utils.js
================================================
// Utilities Placeholder
Morf.prototype.util = {};
/**
* Converts a DOM style string to CSS property name
* @param {String} str A DOM style string
* @returns {String} CSS property name
*/
Morf.prototype.util.toDash = function(str){
str = str.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();});
return /^webkit/.test(str) ? '-'+str : str;
};
/**
* Converts a CSS property name to DOM style string
* @param {String} str A CSS property name
* @returns {String} DOM style string
*/
Morf.prototype.util.toCamel = function(str){
return str.replace(/(\-[a-z])/g, function($1){return $1.toUpperCase().replace('-','');});
};
================================================
FILE: js/src/shifty.fn.scripty2.js
================================================
/**
* @preserve
* Extra easing functions borrowed from scripty2 (c) 2005-2010 Thomas Fuchs (MIT Licence)
* https://raw.github.com/madrobby/scripty2/master/src/effects/transitions/transitions.js
*/
(function(){
var scripty2 = {
spring: function(pos) {
return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
},
sinusoidal: function(pos) {
return (-Math.cos(pos*Math.PI)/2) + 0.5;
}
};
// Load the Scripty2 functions
for(var t in scripty2)
Tweenable.prototype.formula[t] = scripty2[t];
})();