commit e7674226de29124a423f9158a2f089e28ca0776a Author: Jack Lukic Date: Mon Apr 8 19:28:05 2013 -0400 Origami: Initial commit of origami module diff --git a/build/origami.min.css b/build/origami.min.css new file mode 100755 index 000000000..17a699d0e --- /dev/null +++ b/build/origami.min.css @@ -0,0 +1 @@ +.origami.module{position:relative;-webkit-perspective:2000px;-moz-perspective:2000px;-ms-perspective:2000px;perspective:2000px}.origami.module .box{-webkit-transform-style:preserve-3d;-moz-transform-style:preserve-3d;-ms-transform-style:preserve-3d;transform-style:preserve-3d}.origami.module .side{display:none;opacity:1;width:100%;background-color:#fff;-moz-box-shadow:0 1px 3px rgba(0,0,0,0.3);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.3);box-shadow:0 1px 3px rgba(0,0,0,0.3);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden}.origami.module .handle{cursor:pointer;position:absolute;bottom:-28px;right:50%;margin-left:-22px;width:44px;height:44px;background:url(/images/modules/origami-handle.png) no-repeat 0 0}.origami.module .down.handle{background-position:0 -50px}.origami.module.animating .box{position:absolute}.origami.module .animating.side{position:absolute;width:100%;top:0;left:0;z-index:100}.origami.module .hidden.side{opacity:.5}.origami.css.module{-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;transition:all .5s ease-in-out}.origami.css.module .box{-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;transition:all .5s ease-in-out}.origami.css.module .side{-webkit-transition:opacity .5s ease-out;-moz-transition:opacity .5s ease-out;-o-transition:opacity .5s ease-out;-ms-transition:opacity .5s ease-out;transition:opacity .5s ease-out}.origami.module .active.side{display:block} \ No newline at end of file diff --git a/build/origami.min.js b/build/origami.min.js new file mode 100755 index 000000000..08ff89a6d --- /dev/null +++ b/build/origami.min.js @@ -0,0 +1 @@ +(function(a,b,c,d){a.fn.origami=function(b){var h,c=a(this),e=a.extend(!0,{},a.fn.origami.settings,b),f=arguments[0],g=[].slice.call(arguments,1);return c.each(function(){var j,k,t,b=a(this),c=b.find(e.selector.box),i=b.find(e.selector.side),m=(b.selector||"",this),n=b.data("module-"+e.namespace),o="string"==typeof f,p="transitionend msTransitionEnd oTransitionEnd",q=e.namespace,s=(e.metadata,e.className);t={initialize:function(){t.verbose("Initializing module for",m),t.set.defaultSide(),b.data("module-"+q,t)},destroy:function(){t.verbose("Destroying previous module for",m),b.off("."+q)},refresh:function(){t.verbose("Refreshing selector cache for",m),b=a(m),c=a(this).find(e.selector.box),i=a(this).find(e.selector.side)},repaint:function(){b.get(0).offsetWidth},animate:function(a){t.verbose("Animating box with properties",a);var d=function(){t.reset(),t.set.active()};e.useCSS?(t.verbose("Using CSS transitions to animate"),b.addClass(s.animating),t.set.stageSize(),t.repaint(),b.addClass(s.css),j.addClass(s.hidden),c.css(a).one(p,d)):(t.verbose("Using javascript to animate"),b.addClass(s.animating).removeClass(s.css),t.set.stageSize(),t.repaint(),j.animate({opacity:0},e.duration,e.easing),c.animate(a,e.duration,e.easing,d))},reset:function(){t.verbose("Animating states reset"),b.removeClass(s.css).removeClass(s.animating).removeAttr("style"),c.removeAttr("style"),i.removeAttr("style").removeClass(s.hidden),k.removeClass(s.animating).removeAttr("style")},set:{defaultSide:function(){j=b.find("."+e.className.active),k=j.next(e.selector.side).size()>0?j.next(e.selector.side):b.find(e.selector.side).first(),t.verbose("Active side set to",j),t.verbose("Next side set to",k)},stageSize:function(){b.css({width:k.outerWidth(),height:k.outerHeight()})},nextSide:function(a){k=b.find(a),t.verbose("Next side manually set to",k)},active:function(){t.verbose("Setting new side to active",k),i.removeClass(s.active),k.addClass(s.active),t.set.defaultSide()}},getTransform:{up:function(){var a={y:-((j.outerHeight()-k.outerHeight())/2),z:-(j.outerHeight()/2)};return{transform:"translateY("+a.y+"px) translateZ("+a.z+"px) rotateX(-90deg)"}},down:function(){var a={y:-((j.outerHeight()-k.outerHeight())/2),z:-(j.outerHeight()/2)};return{transform:"translateY("+a.y+"px) translateZ("+a.z+"px) rotateX(90deg)"}},left:function(){var a={x:-((j.outerWidth()-k.outerWidth())/2),z:-(j.outerWidth()/2)};return{transform:"translateX("+a.x+"px) translateZ("+a.z+"px) rotateY(90deg)"}},right:function(){var a={x:-((j.outerWidth()-k.outerWidth())/2),z:-(j.outerWidth()/2)};return{transform:"translateX("+a.x+"px) translateZ("+a.z+"px) rotateY(-90deg)"}}},stage:{above:function(){var a={origin:(j.outerHeight()-k.outerHeight())/2,depth:{active:k.outerHeight()/2,next:j.outerHeight()/2}};t.verbose("Setting the initial animation position as above",k,a),j.css({transform:"rotateY(0deg) translateZ("+a.depth.active+"px)"}),k.addClass(s.animating).css({display:"block",top:a.origin+"px",transform:"rotateX(90deg) translateZ("+a.depth.next+"px)"})},below:function(){var a={origin:(j.outerHeight()-k.outerHeight())/2,depth:{active:k.outerHeight()/2,next:j.outerHeight()/2}};t.verbose("Setting the initial animation position as below",k,a),j.css({transform:"rotateY(0deg) translateZ("+a.depth.active+"px)"}),k.addClass(s.animating).css({display:"block",top:a.origin+"px",transform:"rotateX(-90deg) translateZ("+a.depth.next+"px)"})},left:function(){var a={origin:(j.outerWidth()-k.outerWidth())/2,depth:{active:k.outerWidth()/2,next:j.outerWidth()/2}};t.verbose("Setting the initial animation position as left",k,a),j.css({transform:"rotateY(0deg) translateZ("+a.depth.active+"px)"}),k.addClass(s.animating).css({display:"block",left:a.origin+"px",transform:"rotateY(-90deg) translateZ("+a.depth.next+"px)"})},right:function(){var a={origin:(j.outerWidth()-k.outerWidth())/2,depth:{active:k.outerWidth()/2,next:j.outerWidth()/2}};t.verbose("Setting the initial animation position as left",k,a),j.css({transform:"rotateY(0deg) translateZ("+a.depth.active+"px)"}),k.addClass(s.animating).css({display:"block",left:a.origin+"px",transform:"rotateY(90deg) translateZ("+a.depth.next+"px)"})}},flip:{up:function(){t.debug("Flipping up",k),t.stage.above(),t.animate(t.getTransform.up(),m)},down:function(){t.debug("Flipping down",k),t.stage.below(),t.animate(t.getTransform.down(),m)},left:function(){t.debug("Flipping left",k),t.stage.left(),t.animate(t.getTransform.left(),m)},right:function(){t.debug("Flipping right",k),t.stage.right(),t.animate(t.getTransform.right(),m)}},setting:function(a,b){return b===d?e[a]:(e[a]=b,d)},verbose:function(){e.verbose&&t.debug.apply(this,arguments)},debug:function(){var a=[],b=e.moduleName+": "+arguments[0],c=[].slice.call(arguments,1),d=console.info||console.log||function(){};d=Function.prototype.bind.call(d,console),e.debug&&(a.push(b),d.apply(console,a.concat(c)))},error:function(){var a=[],b=e.moduleName+": "+arguments[0],c=[].slice.call(arguments,1),d=console.warn||console.log||function(){};d=Function.prototype.bind.call(d,console),e.debug&&(a.push(b),a.concat(c),d.apply(console,a.concat(c)))},invoke:function(b,c,f){var g,h;return f=f||[].slice.call(arguments,2),"string"==typeof b&&n!==d&&(b=b.split("."),g=b.length-1,a.each(b,function(b,c){return a.isPlainObject(n[c])&&b!=g?(n=n[c],!0):n[c]!==d?(h=n[c],!0):(t.error(e.errors.method),!1)})),a.isFunction(h)?h.apply(c,f):h}},o?h=t.invoke(f,this,g):(n!==d&&t.destroy(),t.initialize())}),h!==d?h:this},a.fn.origami.settings={moduleName:"Origami Module",debug:!0,verbose:!0,namespace:"origami",onChange:function(){},useCSS:!0,duration:1e3,easing:"easeInOutQuad",errors:{api:"You tried to switch to a side that does not exist.",method:"The method you called is not defined"},metadata:{},className:{css:"css",animating:"animating",hidden:"hidden",active:"active"},selector:{box:".box",side:".side"}}})(jQuery,window,document); \ No newline at end of file diff --git a/component.json b/component.json new file mode 100755 index 000000000..b69a9a503 --- /dev/null +++ b/component.json @@ -0,0 +1,30 @@ +{ + "name" : "Origami", + "version" : "0.1", + "description" : "3D animation made easy, using the Semantic module spec.", + + "homepage" : "https://github.com/quirkyinc/", + "author": { + "name" : "Jack Lukic", + "web" : "http://www.github.com/quirkyjack/" + }, + "keywords": [ + "origami", + "animation", + "css3" + ], + "license" : [ + "http://bartaz.mit-license.org/", + "http://www.gnu.org/licenses/" + ], + + "main": [ + "./build/origami.js", + "./build/origami.css" + ], + "dependencies": { + "jquery" : ">=1.8", + "transform2d" : "./dependencies/transform2d.js", + "transform3d" : "./dependencies/transform3d.js" + } +} \ No newline at end of file diff --git a/dependencies/transform2d.js b/dependencies/transform2d.js new file mode 100755 index 000000000..a7e3c07e0 --- /dev/null +++ b/dependencies/transform2d.js @@ -0,0 +1,551 @@ +/* + * transform: A jQuery cssHooks adding cross-browser 2d transform capabilities to $.fn.css() and $.fn.animate() + * + * limitations: + * - requires jQuery 1.4.3+ + * - Should you use the *translate* property, then your elements need to be absolutely positionned in a relatively positionned wrapper **or it will fail in IE678**. + * - transformOrigin is not accessible + * + * latest version and complete README available on Github: + * https://github.com/louisremi/jquery.transform.js + * + * Copyright 2011 @louis_remi + * Licensed under the MIT license. + * + * This saved you an hour of work? + * Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON + * + */ +(function( $, window, document, Math, undefined ) { + +/* + * Feature tests and global variables + */ +var div = document.createElement("div"), + divStyle = div.style, + suffix = "Transform", + testProperties = [ + "O" + suffix, + "ms" + suffix, + "Webkit" + suffix, + "Moz" + suffix + ], + i = testProperties.length, + supportProperty, + supportMatrixFilter, + supportFloat32Array = "Float32Array" in window, + propertyHook, + propertyGet, + rMatrix = /Matrix([^)]*)/, + rAffine = /^\s*matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*(?:,\s*0(?:px)?\s*){2}\)\s*$/, + _transform = "transform", + _transformOrigin = "transformOrigin", + _translate = "translate", + _rotate = "rotate", + _scale = "scale", + _skew = "skew", + _matrix = "matrix"; + +// test different vendor prefixes of these properties +while ( i-- ) { + if ( testProperties[i] in divStyle ) { + $.support[_transform] = supportProperty = testProperties[i]; + $.support[_transformOrigin] = supportProperty + "Origin"; + continue; + } +} +// IE678 alternative +if ( !supportProperty ) { + $.support.matrixFilter = supportMatrixFilter = divStyle.filter === ""; +} + +// px isn't the default unit of these properties +$.cssNumber[_transform] = $.cssNumber[_transformOrigin] = true; + +/* + * fn.css() hooks + */ +if ( supportProperty && supportProperty != _transform ) { + // Modern browsers can use jQuery.cssProps as a basic hook + $.cssProps[_transform] = supportProperty; + $.cssProps[_transformOrigin] = supportProperty + "Origin"; + + // Firefox needs a complete hook because it stuffs matrix with "px" + if ( supportProperty == "Moz" + suffix ) { + propertyHook = { + get: function( elem, computed ) { + return (computed ? + // remove "px" from the computed matrix + $.css( elem, supportProperty ).split("px").join(""): + elem.style[supportProperty] + ); + }, + set: function( elem, value ) { + // add "px" to matrices + elem.style[supportProperty] = /matrix\([^)p]*\)/.test(value) ? + value.replace(/matrix((?:[^,]*,){4})([^,]*),([^)]*)/, _matrix+"$1$2px,$3px"): + value; + } + }; + /* Fix two jQuery bugs still present in 1.5.1 + * - rupper is incompatible with IE9, see http://jqbug.com/8346 + * - jQuery.css is not really jQuery.cssProps aware, see http://jqbug.com/8402 + */ + } else if ( /^1\.[0-5](?:\.|$)/.test($.fn.jquery) ) { + propertyHook = { + get: function( elem, computed ) { + return (computed ? + $.css( elem, supportProperty.replace(/^ms/, "Ms") ): + elem.style[supportProperty] + ); + } + }; + } + /* TODO: leverage hardware acceleration of 3d transform in Webkit only + else if ( supportProperty == "Webkit" + suffix && support3dTransform ) { + propertyHook = { + set: function( elem, value ) { + elem.style[supportProperty] = + value.replace(); + } + } + }*/ + +} else if ( supportMatrixFilter ) { + propertyHook = { + get: function( elem, computed, asArray ) { + var elemStyle = ( computed && elem.currentStyle ? elem.currentStyle : elem.style ), + matrix, data; + + if ( elemStyle && rMatrix.test( elemStyle.filter ) ) { + matrix = RegExp.$1.split(","); + matrix = [ + matrix[0].split("=")[1], + matrix[2].split("=")[1], + matrix[1].split("=")[1], + matrix[3].split("=")[1] + ]; + } else { + matrix = [1,0,0,1]; + } + + if ( ! $.cssHooks[_transformOrigin] ) { + matrix[4] = elemStyle ? parseInt(elemStyle.left, 10) || 0 : 0; + matrix[5] = elemStyle ? parseInt(elemStyle.top, 10) || 0 : 0; + + } else { + data = $._data( elem, "transformTranslate", undefined ); + matrix[4] = data ? data[0] : 0; + matrix[5] = data ? data[1] : 0; + } + + return asArray ? matrix : _matrix+"(" + matrix + ")"; + }, + set: function( elem, value, animate ) { + var elemStyle = elem.style, + currentStyle, + Matrix, + filter, + centerOrigin; + + if ( !animate ) { + elemStyle.zoom = 1; + } + + value = matrix(value); + + // rotate, scale and skew + Matrix = [ + "Matrix("+ + "M11="+value[0], + "M12="+value[2], + "M21="+value[1], + "M22="+value[3], + "SizingMethod='auto expand'" + ].join(); + filter = ( currentStyle = elem.currentStyle ) && currentStyle.filter || elemStyle.filter || ""; + + elemStyle.filter = rMatrix.test(filter) ? + filter.replace(rMatrix, Matrix) : + filter + " progid:DXImageTransform.Microsoft." + Matrix + ")"; + + if ( ! $.cssHooks[_transformOrigin] ) { + + // center the transform origin, from pbakaus's Transformie http://github.com/pbakaus/transformie + if ( (centerOrigin = $.transform.centerOrigin) ) { + elemStyle[centerOrigin == "margin" ? "marginLeft" : "left"] = -(elem.offsetWidth/2) + (elem.clientWidth/2) + "px"; + elemStyle[centerOrigin == "margin" ? "marginTop" : "top"] = -(elem.offsetHeight/2) + (elem.clientHeight/2) + "px"; + } + + // translate + // We assume that the elements are absolute positionned inside a relative positionned wrapper + elemStyle.left = value[4] + "px"; + elemStyle.top = value[5] + "px"; + + } else { + $.cssHooks[_transformOrigin].set( elem, value ); + } + } + }; +} +// populate jQuery.cssHooks with the appropriate hook if necessary +if ( propertyHook ) { + $.cssHooks[_transform] = propertyHook; +} +// we need a unique setter for the animation logic +propertyGet = propertyHook && propertyHook.get || $.css; + +/* + * fn.animate() hooks + */ +$.fx.step.transform = function( fx ) { + var elem = fx.elem, + start = fx.start, + end = fx.end, + pos = fx.pos, + transform = "", + precision = 1E5, + i, startVal, endVal, unit; + + // fx.end and fx.start need to be converted to interpolation lists + if ( !start || typeof start === "string" ) { + + // the following block can be commented out with jQuery 1.5.1+, see #7912 + if ( !start ) { + start = propertyGet( elem, supportProperty ); + } + + // force layout only once per animation + if ( supportMatrixFilter ) { + elem.style.zoom = 1; + } + + // replace "+=" in relative animations (-= is meaningless with transforms) + end = end.split("+=").join(start); + + // parse both transform to generate interpolation list of same length + $.extend( fx, interpolationList( start, end ) ); + start = fx.start; + end = fx.end; + } + + i = start.length; + + // interpolate functions of the list one by one + while ( i-- ) { + startVal = start[i]; + endVal = end[i]; + unit = +false; + + switch ( startVal[0] ) { + + case _translate: + unit = "px"; + case _scale: + unit || ( unit = ""); + + transform = startVal[0] + "(" + + Math.round( (startVal[1][0] + (endVal[1][0] - startVal[1][0]) * pos) * precision ) / precision + unit +","+ + Math.round( (startVal[1][1] + (endVal[1][1] - startVal[1][1]) * pos) * precision ) / precision + unit + ")"+ + transform; + break; + + case _skew + "X": + case _skew + "Y": + case _rotate: + transform = startVal[0] + "(" + + Math.round( (startVal[1] + (endVal[1] - startVal[1]) * pos) * precision ) / precision +"rad)"+ + transform; + break; + } + } + + fx.origin && ( transform = fx.origin + transform ); + + propertyHook && propertyHook.set ? + propertyHook.set( elem, transform, +true ): + elem.style[supportProperty] = transform; +}; + +/* + * Utility functions + */ + +// turns a transform string into its "matrix(A,B,C,D,X,Y)" form (as an array, though) +function matrix( transform ) { + transform = transform.split(")"); + var + trim = $.trim + , i = -1 + // last element of the array is an empty string, get rid of it + , l = transform.length -1 + , split, prop, val + , prev = supportFloat32Array ? new Float32Array(6) : [] + , curr = supportFloat32Array ? new Float32Array(6) : [] + , rslt = supportFloat32Array ? new Float32Array(6) : [1,0,0,1,0,0] + ; + + prev[0] = prev[3] = rslt[0] = rslt[3] = 1; + prev[1] = prev[2] = prev[4] = prev[5] = 0; + + // Loop through the transform properties, parse and multiply them + while ( ++i < l ) { + split = transform[i].split("("); + prop = trim(split[0]); + val = split[1]; + curr[0] = curr[3] = 1; + curr[1] = curr[2] = curr[4] = curr[5] = 0; + + switch (prop) { + case _translate+"X": + curr[4] = parseInt(val, 10); + break; + + case _translate+"Y": + curr[5] = parseInt(val, 10); + break; + + case _translate: + val = val.split(","); + curr[4] = parseInt(val[0], 10); + curr[5] = parseInt(val[1] || 0, 10); + break; + + case _rotate: + val = toRadian(val); + curr[0] = Math.cos(val); + curr[1] = Math.sin(val); + curr[2] = -Math.sin(val); + curr[3] = Math.cos(val); + break; + + case _scale+"X": + curr[0] = +val; + break; + + case _scale+"Y": + curr[3] = val; + break; + + case _scale: + val = val.split(","); + curr[0] = val[0]; + curr[3] = val.length>1 ? val[1] : val[0]; + break; + + case _skew+"X": + curr[2] = Math.tan(toRadian(val)); + break; + + case _skew+"Y": + curr[1] = Math.tan(toRadian(val)); + break; + + case _matrix: + val = val.split(","); + curr[0] = val[0]; + curr[1] = val[1]; + curr[2] = val[2]; + curr[3] = val[3]; + curr[4] = parseInt(val[4], 10); + curr[5] = parseInt(val[5], 10); + break; + } + + // Matrix product (array in column-major order) + rslt[0] = prev[0] * curr[0] + prev[2] * curr[1]; + rslt[1] = prev[1] * curr[0] + prev[3] * curr[1]; + rslt[2] = prev[0] * curr[2] + prev[2] * curr[3]; + rslt[3] = prev[1] * curr[2] + prev[3] * curr[3]; + rslt[4] = prev[0] * curr[4] + prev[2] * curr[5] + prev[4]; + rslt[5] = prev[1] * curr[4] + prev[3] * curr[5] + prev[5]; + + prev = [rslt[0],rslt[1],rslt[2],rslt[3],rslt[4],rslt[5]]; + } + return rslt; +} + +// turns a matrix into its rotate, scale and skew components +// algorithm from http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp +function unmatrix(matrix) { + var + scaleX + , scaleY + , skew + , A = matrix[0] + , B = matrix[1] + , C = matrix[2] + , D = matrix[3] + ; + + // Make sure matrix is not singular + if ( A * D - B * C ) { + // step (3) + scaleX = Math.sqrt( A * A + B * B ); + A /= scaleX; + B /= scaleX; + // step (4) + skew = A * C + B * D; + C -= A * skew; + D -= B * skew; + // step (5) + scaleY = Math.sqrt( C * C + D * D ); + C /= scaleY; + D /= scaleY; + skew /= scaleY; + // step (6) + if ( A * D < B * C ) { + A = -A; + B = -B; + skew = -skew; + scaleX = -scaleX; + } + + // matrix is singular and cannot be interpolated + } else { + // In this case the elem shouldn't be rendered, hence scale == 0 + scaleX = scaleY = skew = 0; + } + + // The recomposition order is very important + // see http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp#l971 + return [ + [_translate, [+matrix[4], +matrix[5]]], + [_rotate, Math.atan2(B, A)], + [_skew + "X", Math.atan(skew)], + [_scale, [scaleX, scaleY]] + ]; +} + +// build the list of transform functions to interpolate +// use the algorithm described at http://dev.w3.org/csswg/css3-2d-transforms/#animation +function interpolationList( start, end ) { + var list = { + start: [], + end: [] + }, + i = -1, l, + currStart, currEnd, currType; + + // get rid of affine transform matrix + ( start == "none" || isAffine( start ) ) && ( start = "" ); + ( end == "none" || isAffine( end ) ) && ( end = "" ); + + // if end starts with the current computed style, this is a relative animation + // store computed style as the origin, remove it from start and end + if ( start && end && !end.indexOf("matrix") && toArray( start ).join() == toArray( end.split(")")[0] ).join() ) { + list.origin = start; + start = ""; + end = end.slice( end.indexOf(")") +1 ); + } + + if ( !start && !end ) { return; } + + // start or end are affine, or list of transform functions are identical + // => functions will be interpolated individually + if ( !start || !end || functionList(start) == functionList(end) ) { + + start && ( start = start.split(")") ) && ( l = start.length ); + end && ( end = end.split(")") ) && ( l = end.length ); + + while ( ++i < l-1 ) { + start[i] && ( currStart = start[i].split("(") ); + end[i] && ( currEnd = end[i].split("(") ); + currType = $.trim( ( currStart || currEnd )[0] ); + + append( list.start, parseFunction( currType, currStart ? currStart[1] : 0 ) ); + append( list.end, parseFunction( currType, currEnd ? currEnd[1] : 0 ) ); + } + + // otherwise, functions will be composed to a single matrix + } else { + list.start = unmatrix(matrix(start)); + list.end = unmatrix(matrix(end)) + } + + return list; +} + +function parseFunction( type, value ) { + var + // default value is 1 for scale, 0 otherwise + defaultValue = +(!type.indexOf(_scale)), + scaleX, + // remove X/Y from scaleX/Y & translateX/Y, not from skew + cat = type.replace( /e[XY]/, "e" ); + + switch ( type ) { + case _translate+"Y": + case _scale+"Y": + + value = [ + defaultValue, + value ? + parseFloat( value ): + defaultValue + ]; + break; + + case _translate+"X": + case _translate: + case _scale+"X": + scaleX = 1; + case _scale: + + value = value ? + ( value = value.split(",") ) && [ + parseFloat( value[0] ), + parseFloat( value.length>1 ? value[1] : type == _scale ? scaleX || value[0] : defaultValue+"" ) + ]: + [defaultValue, defaultValue]; + break; + + case _skew+"X": + case _skew+"Y": + case _rotate: + value = value ? toRadian( value ) : 0; + break; + + case _matrix: + return unmatrix( value ? toArray(value) : [1,0,0,1,0,0] ); + break; + } + + return [[ cat, value ]]; +} + +function isAffine( matrix ) { + return rAffine.test(matrix); +} + +function functionList( transform ) { + return transform.replace(/(?:\([^)]*\))|\s/g, ""); +} + +function append( arr1, arr2, value ) { + while ( value = arr2.shift() ) { + arr1.push( value ); + } +} + +// converts an angle string in any unit to a radian Float +function toRadian(value) { + return ~value.indexOf("deg") ? + parseInt(value,10) * (Math.PI * 2 / 360): + ~value.indexOf("grad") ? + parseInt(value,10) * (Math.PI/200): + parseFloat(value); +} + +// Converts "matrix(A,B,C,D,X,Y)" to [A,B,C,D,X,Y] +function toArray(matrix) { + // remove the unit of X and Y for Firefox + matrix = /([^,]*),([^,]*),([^,]*),([^,]*),([^,p]*)(?:px)?,([^)p]*)(?:px)?/.exec(matrix); + return [matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6]]; +} + +$.transform = { + centerOrigin: "margin" +}; + +})( jQuery, window, document, Math ); \ No newline at end of file diff --git a/dependencies/transform3d.js b/dependencies/transform3d.js new file mode 100755 index 000000000..3f93504de --- /dev/null +++ b/dependencies/transform3d.js @@ -0,0 +1,92 @@ +/* + * transform: A jQuery cssHooks adding 2D/3D transform capabilities to $.fn.css() and $.fn.animate() + * + * Requirements: + * - jQuery 1.5.1+ + * - jquery.transition.js for animations + * - browser implementing W3C's CSS 2DTransforms for 2D tranform + * - browser implementing W3C's CSS 3DTransforms for 3D tranform + * + * latest version and complete README available on Github: + * https://github.com/louisremi/jquery.transform.js + * + * Copyright 2011 @louis_remi + * Licensed under the MIT license. + * + * This saved you an hour of work? + * Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON + * + */ +(function( $, window, document ) { +"use strict"; + +var div = document.createElement("div"), + divStyle = div.style, + prefixes = [ + "O", + "ms", + "Webkit", + "Moz" + ], + prefix, + i = prefixes.length, + properties = [ + "transform", + "transformOrigin", + "transformStyle", + "perspective", + "perspectiveOrigin", + "backfaceVisibility" + ], + property, + j = prefixes.length; + +// Find the right prefix +while ( i-- ) { + if ( prefixes[i] + leadingUppercase( properties[0] ) in divStyle ) { + prefix = prefixes[i]; + continue; + } +} + +// This browser is not compatible with transforms +if ( !prefix ) { return; } + +// Build cssHooks for each property +while ( j-- ) { + property = prefix + leadingUppercase( properties[j] ); + + if ( property in divStyle ) { + + // px isn't the default unit of this property + $.cssNumber[ properties[j] ] = true; + + // populate cssProps + $.cssProps[ properties[j] ] = property; + + // MozTranform requires a complete hook because "px" is required in translate + property === "MozTransform" && ($.cssHooks[ properties[j] ] = { + get: function( elem, computed ) { + return (computed ? + // remove "px" from the computed matrix + $.css( elem, property ).split("px").join(""): + elem.style[property] + ); + }, + set: function( elem, value ) { + // add "px" to matrices + /matrix\([^)p]*\)/.test(value) && ( + value = value.replace(/matrix((?:[^,]*,){4})([^,]*),([^)]*)/, "matrix$1$2px,$3px") + ); + elem.style[property] = value; + } + }); + + } +} + +function leadingUppercase( word ) { + return word.slice(0,1).toUpperCase() + word.slice(1); +} + +})( jQuery, window, document ); \ No newline at end of file diff --git a/src/origami.css b/src/origami.css new file mode 100755 index 000000000..72f3f9c5f --- /dev/null +++ b/src/origami.css @@ -0,0 +1,114 @@ +/******************************* + Origami Module +*******************************/ + +.origami.module { + position: relative; + + -webkit-perspective: 2000px; + -moz-perspective: 2000px; + -ms-perspective: 2000px; + perspective: 2000px; +} + +.origami.module .box { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + -ms-transform-style: preserve-3d; + transform-style: preserve-3d; +} + +.origami.module .side { + display: none; + opacity: 1; + width: 100%; + background-color: #FFFFFF; + + -moz-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3); + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3); + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + -ms-backface-visibility: hidden; + backface-visibility: hidden; +} + +/* Handle (Stubbed as image will fix) */ +.origami.module .handle { + cursor: pointer; + position: absolute; + bottom: -28px; + right: 50%; + + margin-left: -22px; + width: 44px; + height: 44px; + background: url(/images/modules/origami-handle.png) no-repeat 0px 0px; +} +.origami.module .down.handle { + background-position: 0px -50px; +} + + +/*--------------- + States +----------------*/ + +/* Animating */ +.origami.module.animating .box { + position: absolute; +} +.origami.module .animating.side { + position: absolute; + width: 100%; + top: 0px; + left: 0px; + z-index: 100; +} +.origami.module .hidden.side { + opacity: 0.5; +} + +/* Animate using CSS */ +.origami.css.module { + -webkit-transition: + all 0.5s ease-in-out; + ; + -moz-transition: + all 0.5s ease-in-out; + ; + -o-transition: + all 0.5s ease-in-out; + ; + -ms-transition: + all 0.5s ease-in-out; + ; + transition: + all 0.5s ease-in-out; + ; +} +.origami.css.module .box { + -webkit-transition: all 0.5s ease-in-out; + -moz-transition: all 0.5s ease-in-out; + -o-transition: all 0.5s ease-in-out; + -ms-transition: all 0.5s ease-in-out; + transition: all 0.5s ease-in-out; +} +.origami.css.module .side { + -webkit-transition: opacity 0.5s ease-out; + -moz-transition: opacity 0.5s ease-out; + -o-transition: opacity 0.5s ease-out; + -ms-transition: opacity 0.5s ease-out; + transition: opacity 0.5s ease-out; +} + +/* Active */ +.origami.module .active.side { + display: block; +} \ No newline at end of file diff --git a/src/origami.js b/src/origami.js new file mode 100755 index 000000000..80e753fc9 --- /dev/null +++ b/src/origami.js @@ -0,0 +1,502 @@ +/* ****************************** + Module + Origami + Author: Jack Lukic + Created: Mar 28, 2013 + Last revision: Mar 2013 + + Creates a cube which can be rotated + + Usage: + + $origami + .origami() + ; + + $origami + .origami('flip.up') + ; + +****************************** */ + +;(function ( $, window, document, undefined ) { + +$.fn.origami = function(parameters) { + var + $allModules = $(this), + + settings = $.extend(true, {}, $.fn.origami.settings, parameters), + // make arguments available + query = arguments[0], + passedArguments = [].slice.call(arguments, 1), + invokedResponse + ; + $allModules + .each(function() { + var + // selector cache + $module = $(this), + $box = $module.find(settings.selector.box), + $side = $module.find(settings.selector.side), + + $activeSide, + $nextSide, + + // private variables + selector = $module.selector || '', + element = this, + instance = $module.data('module-' + settings.namespace), + methodInvoked = (typeof query == 'string'), + + endTransition = 'transitionend msTransitionEnd oTransitionEnd', + + // shortcuts + namespace = settings.namespace, + metadata = settings.metadata, + className = settings.className, + + module + ; + module = { + + initialize: function() { + module.verbose('Initializing module for', element); + module.set.defaultSide(); + $module + .data('module-' + namespace, module) + ; + }, + + destroy: function() { + module.verbose('Destroying previous module for', element); + $module + .off('.' + namespace) + ; + }, + + refresh: function() { + module.verbose('Refreshing selector cache for', element); + $module = $(element); + $box = $(this).find(settings.selector.box); + $side = $(this).find(settings.selector.side); + }, + + repaint: function() { + var + fakeAssignment = $module.get(0).offsetWidth + ; + }, + + animate: function(propertyObject) { + module.verbose('Animating box with properties', propertyObject); + var + callback = function() { + module.reset(); + module.set.active(); + } + ; + if(settings.useCSS) { + module.verbose('Using CSS transitions to animate'); + $module + .addClass(className.animating) + ; + module.set.stageSize(); + module.repaint(); + $module + .addClass(className.css) + ; + $activeSide + .addClass(className.hidden) + ; + $box + .css(propertyObject) + .one(endTransition, callback) + ; + } + else { + // not yet supported until .animate() is extended to allow RotateX/Y + module.verbose('Using javascript to animate'); + $module + .addClass(className.animating) + .removeClass(className.css) + ; + module.set.stageSize(); + module.repaint(); + $activeSide + .animate({ + opacity: 0 + }, settings.duration, settings.easing) + ; + $box + .animate(propertyObject, settings.duration, settings.easing, callback) + ; + } + }, + + reset: function() { + module.verbose('Animating states reset'); + $module + .removeClass(className.css) + .removeClass(className.animating) + .removeAttr('style') + ; + $box + .removeAttr('style') + ; + $side + .removeAttr('style') + .removeClass(className.hidden) + ; + $nextSide + .removeClass(className.animating) + .removeAttr('style') + ; + }, + + set: { + + defaultSide: function() { + $activeSide = $module.find('.' + settings.className.active); + $nextSide = ( $activeSide.next(settings.selector.side).size() > 0 ) + ? $activeSide.next(settings.selector.side) + : $module.find(settings.selector.side).first() + ; + module.verbose('Active side set to', $activeSide); + module.verbose('Next side set to', $nextSide); + }, + + stageSize: function() { + $module + .css({ + width : $nextSide.outerWidth(), + height : $nextSide.outerHeight() + }) + ; + }, + + nextSide: function(selector) { + $nextSide = $module.find(selector); + module.verbose('Next side manually set to', $nextSide); + }, + + active: function() { + module.verbose('Setting new side to active', $nextSide); + $side + .removeClass(className.active) + ; + $nextSide + .addClass(className.active) + ; + module.set.defaultSide(); + } + + }, + + getTransform: { + up: function() { + var + translate = { + y: -(($activeSide.outerHeight() - $nextSide.outerHeight()) / 2), + z: -($activeSide.outerHeight() / 2) + } + ; + return { + transform: 'translateY(' + translate.y + 'px) translateZ('+ translate.z + 'px) rotateX(-90deg)' + }; + }, + down: function() { + var + translate = { + y: -(($activeSide.outerHeight() - $nextSide.outerHeight()) / 2), + z: -($activeSide.outerHeight() / 2) + } + ; + return { + transform: 'translateY(' + translate.y + 'px) translateZ('+ translate.z + 'px) rotateX(90deg)' + }; + }, + left: function() { + var + translate = { + x : -(($activeSide.outerWidth() - $nextSide.outerWidth()) / 2), + z : -($activeSide.outerWidth() / 2) + } + ; + return { + transform: 'translateX(' + translate.x + 'px) translateZ(' + translate.z + 'px) rotateY(90deg)' + }; + }, + right: function() { + var + translate = { + x : -(($activeSide.outerWidth() - $nextSide.outerWidth()) / 2), + z : -($activeSide.outerWidth() / 2) + } + ; + return { + transform: 'translateX(' + translate.x + 'px) translateZ(' + translate.z + 'px) rotateY(-90deg)' + }; + } + }, + + stage: { + above: function() { + var + box = { + origin : (($activeSide.outerHeight() - $nextSide.outerHeight()) / 2), + depth : { + active : ($nextSide.outerHeight() / 2), + next : ($activeSide.outerHeight() / 2) + } + } + ; + module.verbose('Setting the initial animation position as above', $nextSide, box); + $activeSide + .css({ + 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)' + }) + ; + $nextSide + .addClass(className.animating) + .css({ + 'display' : 'block', + 'top' : box.origin + 'px', + 'transform' : 'rotateX(90deg) translateZ(' + box.depth.next + 'px)' + }) + ; + }, + below: function() { + var + box = { + origin : (($activeSide.outerHeight() - $nextSide.outerHeight()) / 2), + depth : { + active : ($nextSide.outerHeight() / 2), + next : ($activeSide.outerHeight() / 2) + } + } + ; + module.verbose('Setting the initial animation position as below', $nextSide, box); + $activeSide + .css({ + 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)' + }) + ; + $nextSide + .addClass(className.animating) + .css({ + 'display' : 'block', + 'top' : box.origin + 'px', + 'transform' : 'rotateX(-90deg) translateZ(' + box.depth.next + 'px)' + }) + ; + }, + left: function() { + var + box = { + origin : ( ( $activeSide.outerWidth() - $nextSide.outerWidth() ) / 2), + depth : { + active : ($nextSide.outerWidth() / 2), + next : ($activeSide.outerWidth() / 2) + } + } + ; + module.verbose('Setting the initial animation position as left', $nextSide, box); + $activeSide + .css({ + 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)' + }) + ; + $nextSide + .addClass(className.animating) + .css({ + 'display' : 'block', + 'left' : box.origin + 'px', + 'transform' : 'rotateY(-90deg) translateZ(' + box.depth.next + 'px)' + }) + ; + }, + right: function() { + var + box = { + origin : ( ( $activeSide.outerWidth() - $nextSide.outerWidth() ) / 2), + depth : { + active : ($nextSide.outerWidth() / 2), + next : ($activeSide.outerWidth() / 2) + } + } + ; + module.verbose('Setting the initial animation position as left', $nextSide, box); + $activeSide + .css({ + 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)' + }) + ; + $nextSide + .addClass(className.animating) + .css({ + 'display' : 'block', + 'left' : box.origin + 'px', + 'transform' : 'rotateY(90deg) translateZ(' + box.depth.next + 'px)' + }) + ; + } + }, + + flip: { + up: function() { + module.debug('Flipping up', $nextSide); + module.stage.above(); + module.animate( module.getTransform.up(), element); + }, + down: function() { + module.debug('Flipping down', $nextSide); + module.stage.below(); + module.animate( module.getTransform.down(), element); + }, + left: function() { + module.debug('Flipping left', $nextSide); + module.stage.left(); + module.animate(module.getTransform.left(), element); + + }, + right: function() { + module.debug('Flipping right', $nextSide); + module.stage.right(); + module.animate(module.getTransform.right(), element); + } + }, + + /* standard module */ + setting: function(name, value) { + if(value === undefined) { + return settings[name]; + } + settings[name] = value; + }, + verbose: function() { + if(settings.verbose) { + module.debug.apply(this, arguments); + } + }, + debug: function() { + var + output = [], + message = settings.moduleName + ': ' + arguments[0], + variables = [].slice.call( arguments, 1 ), + log = console.info || console.log || function(){} + ; + log = Function.prototype.bind.call(log, console); + if(settings.debug) { + output.push(message); + log.apply(console, output.concat(variables) ); + } + }, + error: function() { + var + output = [], + errorMessage = settings.moduleName + ': ' + arguments[0], + variables = [].slice.call( arguments, 1 ), + log = console.warn || console.log || function(){} + ; + log = Function.prototype.bind.call(log, console); + if(settings.debug) { + output.push(errorMessage); + output.concat(variables); + log.apply(console, output.concat(variables) ); + } + }, + invoke: function(query, context, passedArguments) { + var + maxDepth, + found + ; + passedArguments = passedArguments || [].slice.call( arguments, 2 ); + if(typeof query == 'string' && instance !== undefined) { + query = query.split('.'); + maxDepth = query.length - 1; + $.each(query, function(depth, value) { + if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) { + instance = instance[value]; + return true; + } + else if( instance[value] !== undefined ) { + found = instance[value]; + return true; + } + module.error(settings.errors.method); + return false; + }); + } + if ( $.isFunction( found ) ) { + return found.apply(context, passedArguments); + } + // return retrieved variable or chain + return found; + } + }; + + // check for invoking internal method + if(methodInvoked) { + invokedResponse = module.invoke(query, this, passedArguments); + } + // otherwise initialize + else { + if(instance !== undefined) { + module.destroy(); + } + module.initialize(); + } + }) + ; + // chain or return queried method + return (invokedResponse !== undefined) + ? invokedResponse + : this + ; +}; + +$.fn.origami.settings = { + + // module info + moduleName : 'Origami Module', + + // debug output + debug : true, + // verbose debug output + verbose : true, + + namespace : 'origami', + + // callback occurs on side change + onChange : function() {}, + + useCSS : true, + duration : 1000, + easing : 'easeInOutQuad', + + errors: { + api : 'You tried to switch to a side that does not exist.', + method : 'The method you called is not defined' + }, + + metadata : { + + }, + + className : { + css : 'css', + animating : 'animating', + hidden : 'hidden', + active : 'active' + }, + + selector : { + box : '.box', + side : '.side' + } + +}; + + + +})( jQuery, window , document );