From 83a93454501f1f5fc8035f2ffc2af3ab029101e5 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Thu, 28 Jul 2011 18:09:21 +0800 Subject: [PATCH 1/3] added clip and clipTop/clipRight/clipBottom/clipLeft cssHooks (and test cases) --- clip.js | 294 +++++++++++++++++++++++++++++++++++++++++++++ tests/index.html | 3 + tests/unit/clip.js | 74 ++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 clip.js create mode 100644 tests/unit/clip.js diff --git a/clip.js b/clip.js new file mode 100644 index 0000000..6a63a82 --- /dev/null +++ b/clip.js @@ -0,0 +1,294 @@ +/*! + * Copyright (c) 2011 Jeffery To (http://www.thingsthemselves.com/) + * Clip and clip side (top, right, bottom, left) cssHooks for jQuery + * + * Limitations: + * - Works with jQuery 1.4.3 and higher + * + * Licensed under the MIT License (LICENSE.txt). + */ +(function( $, document ) { +/* + * Notes: + * + * - When accessing individual clip sides, a computed value is returned + * instead of "auto" (see the examples below). + * + * - Animation is also supported for clip sides. + * + * + * Example usage: + * + * var div = + * $('
') + * .css({ width: 100, height: 100, clip: 'auto' }) + * .appendTo('body'); + * + * alert(div.css('clip')); // "auto", except in WebKit and Opera (see + * // browser bugs below) + * + * alert(div.css('clip-right')); // "100px", computed from "auto", again + * // except in Webkit and Opera + * + * div + * .css('clip', 'rect(10px, 90px, 90px, 10px)') + * .css('clip-right', 80); + * + * alert(div.css('clip-right')); // "80px" + * + * alert(div.css('clip')); // "rect(10px, 80px, 90px, 10px)" + * + * div.animate({ clipRight: 10 }, function () { + * alert(div.css('clip-right')); // "10px" + * }); + * + * + * Browser bugs (that can't be worked around by this plugin): + * + * - Opera 10.5+ returns "rect(A, A, C, C)" for elements with + * "clip: rect(A, B, C, D)", where A, B, C, D are pixel values + * + * The element is rendered with the correct clipping, but the values + * returned by getComputedStyle are incorrect. + * + * Opera bug ID: DSK-343270 + * + * - Opera calculates relative lengths (em, and most likely ex) based on + * the font size of the parent element instead of the current element. + * + * The element is rendered with incorrect clipping, and the values + * returned by getComputedStyle match the rendered clipping. + * + * Opera bug ID: DSK-343344 + * + * - WebKit and Opera returns "rect(0px, 0px, 0px, 0px)" for elements + * with "clip: auto" or "clip: rect(auto, auto, auto, auto)". + * + * Opera also returns "rect(0px, 0px, 0px, 0px)" if the element has no + * clip declaration (which defaults to "auto") + * + * WebKit bug report: https://bugs.webkit.org/show_bug.cgi?id=20454 + * Opera bug ID: DSK-343270 + * + * + * Properties added to jQuery.support: + * + * - getClip + * true if clip values are correctly returned by the browser, false + * otherwise (Opera 10.5+) + * + * - getClipAuto + * true if the browser correctly returns "auto" (Firefox, IE), false + * otherwise (WebKit, Opera) + * + * For browsers where getClipAuto is false, "rect(0px, 0px, 0px, 0px)" + * is returned instead of "auto", and so there is no + * way to tell if the element is visible or completely clipped. In + * this case, a workaround is to set a definite clip value, either in + * CSS or JS and avoid "auto". + * + * - setClipAuto + * true if the browser supports setting clip to "auto" through + * JavaScript, false otherwise (IE6-7) + * + * IE6-7 will throw an error if you try to setting clip to "auto", + * e.g. element.style.clip = 'auto'. + * Setting the equivalent "rect(auto, auto, auto, auto)" is safe. + * (This plugin will automatically work around this issue.) + * + * - relativeClip + * true if relative clip values are handled correctly by the browser, + * false otherwise (Opera) + */ + + if ( !$.cssHooks ) { + $.error( "jQuery 1.4.3+ is needed for the clip plugin to work" ); + return; + } + + var sides = [ "top", "right", "bottom", "left" ], + auto = "auto", + getComputed, getComputedSide; + + // do tests, set properties on $.support + // test elements / fake body code shamelessly stolen from jQuery (1.6.2) + (function() { + var div = document.createElement( "div" ), + body = document.getElementsByTagName( "body" )[ 0 ], + testElementParent = body || document.documentElement, + testElement = document.createElement( body ? "div" : "body" ), + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0, + fontSize: "2px" + }, + rect = "rect(1px 4px 2px 3px)", + setClip = function( rect ) { div.style.clip = rect; }, + getClip, i; + + div.style.position = "absolute"; + div.style.fontSize = "1px"; + if ( body ) { + $.extend( testElementStyle, { + position: "absolute", + left: -1000, + top: -1000 + } ); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + $.extend( $.support, { + getClip: true, + getClipAuto: true, + setClipAuto: true, + relativeClip: true + } ); + + // IE6-7 will throw an error if we try to set clip to "auto" + try { + setClip( auto ); + } catch ( e ) { + $.support.setClipAuto = false; + } + + if ( document.defaultView && document.defaultView.getComputedStyle ) { + getClip = function() { + return ( document.defaultView.getComputedStyle( div, null ) || { clip: "" } ).clip.replace( /,/g, "" ); + }; + + setClip( "rect(auto auto auto auto)" ); + $.support.getClipAuto = /auto/.test( getClip() ); + + setClip( rect ); + $.support.getClip = getClip() === rect; + + setClip( rect.replace( /px/g, "em" ) ); + $.support.relativeClip = getClip() === rect; + } + + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); + testElement = body = div = getClip = setClip = null; + })(); + + function normalize( rect ) { + return rect.replace( /[\s,]+/g, ", " ) + .replace( "rect(auto, auto, auto, auto)", auto ); + } + + function split( rect ) { + var obj = {}; + rect = rect + .replace( /^auto$/, "auto auto auto auto" ) + .replace( "rect(", "" ) + .replace( ")", "" ) + .split( /[\s,]+/ ); + $.each( sides, function( i, side ) { + obj[ side ] = rect[ i ] || ""; + } ); + return obj; + } + + function join( obj ) { + var buf = []; + $.each( sides, function( i, side ) { + buf.push( obj[ side ] ); + } ); + return normalize( "rect(" + buf.join( " " ) + ")" ); + } + + if ( document.defaultView && document.defaultView.getComputedStyle ) { + // in standards-loving browsers, use getComputedStyle + // use jQuery's default method to get the value, then normalize + getComputed = function( elem ) { + return normalize( $.css( elem, "clip", true ) ); + }; + getComputedSide = function( elem, side ) { + return split( $.css( elem, "clip" ) )[ side ]; + }; + + } else { + // in IE, clip is split into clipTop, clipRight, etc. on currenStyle + // use jQuery's default method to read the four clip properties, then assemble + getComputed = function( elem ) { + var obj = {}; + $.each( sides, function( i, side ) { + obj[ side ] = $.css( elem, $.camelCase( "clip-" + side ), true ); + } ); + return join( obj ); + }; + getComputedSide = function( elem, side ) { + return $.css( elem, $.camelCase( "clip-" + side ), true ); + }; + } + + $.cssHooks.clip = { + get: function( elem, computed, extra ) { + var val; + if ( !extra ) { + val = computed ? + getComputed( elem ) : + normalize( elem.style.clip ); + } + return val; + }, + + set: $.support.setClipAuto ? + function( elem, value ) { elem.style.clip = value; } : + function( elem, value ) { + elem.style.clip = $.trim( value ).toLowerCase() === auto ? + "rect(auto auto auto auto)" : + value; + } + }; + + $.each( sides, function( i, side ) { + var clipSide = $.camelCase( "clip-" + side ); + + $.cssHooks[ clipSide ] = { + get: function( elem, computed, extra ) { + var val; + if ( !extra ) { + if ( computed ) { + if ( ( val = getComputedSide( elem, side ) ) === auto ) { + switch ( side ) { + case "right": + val = $.css( elem, "width", "border" ); + break; + case "bottom": + val = $.css( elem, "height", "border" ); + break; + default: + val = "0px"; + } + } + + } else { + val = split( elem.style.clip )[ side ]; + } + } + return val; + }, + + set: function( elem, value ) { + var obj = split( $.css( elem, "clip" ) ); + obj[ side ] = value; + $.cssHooks.clip.set( elem, join( obj ) ); + } + }; + + $.fx.step[ clipSide ] = function( fx ) { + $.cssHooks[ clipSide ].set( fx.elem, fx.now + fx.unit ); + }; + } ); + + // XXX support full clip animation? + +})( jQuery, document ); diff --git a/tests/index.html b/tests/index.html index 35944e6..44fee33 100644 --- a/tests/index.html +++ b/tests/index.html @@ -27,6 +27,7 @@ -moz-user-focus: ignore;-webkit-user-focus: ignore;user-focus: ignore; -moz-user-modify: read-only;-webkit-user-modify: read-only;user-modify: read-only; -moz-user-select: none;-webkit-user-select: none;user-select: none; + clip: rect(1px 4px 3px 2px); } @@ -49,6 +50,7 @@ + @@ -67,6 +69,7 @@ +

jQuery-cssHooks Test Suite

diff --git a/tests/unit/clip.js b/tests/unit/clip.js new file mode 100644 index 0000000..f178e46 --- /dev/null +++ b/tests/unit/clip.js @@ -0,0 +1,74 @@ +module("clip"); + +test("clip", 2, function() { + equals( jQuery("#test").css("clip"), "rect(1px, 4px, 3px, 2px)", "returns correct values" ); + equals( jQuery("#test").css("clip", "rect(10px, 40px, 30px, 20px)").css("clip"), "rect(10px, 40px, 30px, 20px)", "sets correct values" ); +}); + +test("clip-top", 3, function() { + equals( jQuery("#test").css("clip-top"), "1px", "returns correct value" ); + equals( jQuery("#test").css("clip-top", "5px").css("clip"), "rect(5px, 4px, 3px, 2px)", "sets correct value" ); + stop(); + jQuery("#test").animate({ clipTop: 10 }, 100, function() { + equals( jQuery("#test").css("clip-top"), "10px", "animates the value properly" ); + start(); + }); +}); + +test("clip-right", 3, function() { + equals( jQuery("#test").css("clip-right"), "4px", "returns correct value" ); + equals( jQuery("#test").css("clip-right", "5px").css("clip"), "rect(1px, 5px, 3px, 2px)", "sets correct value" ); + stop(); + jQuery("#test").animate({ clipRight: 10 }, 100, function() { + equals( jQuery("#test").css("clip-right"), "10px", "animates the value properly" ); + start(); + }); +}); + +test("clip-bottom", 3, function() { + equals( jQuery("#test").css("clip-bottom"), "3px", "returns correct value" ); + equals( jQuery("#test").css("clip-bottom", "5px").css("clip"), "rect(1px, 4px, 5px, 2px)", "sets correct value" ); + stop(); + jQuery("#test").animate({ clipBottom: 10 }, 100, function() { + equals( jQuery("#test").css("clip-bottom"), "10px", "animates the value properly" ); + start(); + }); +}); + +test("clip-left", 3, function() { + equals( jQuery("#test").css("clip-left"), "2px", "returns correct value" ); + equals( jQuery("#test").css("clip-left", "5px").css("clip"), "rect(1px, 4px, 3px, 5px)", "sets correct value" ); + stop(); + jQuery("#test").animate({ clipLeft: 10 }, 100, function() { + equals( jQuery("#test").css("clip-left"), "10px", "animates the value properly" ); + start(); + }); +}); + +test("auto", 5, function() { + jQuery("#test").css("clip", "auto"); + equals( jQuery("#test").css("clip"), "auto", "clip returns correct value" ); + equals( jQuery("#test").css("clip-top"), "0px", "clip-top returns correct computed value" ); + equals( jQuery("#test").css("clip-right"), jQuery("#test").outerWidth() + "px", "clip-right returns correct computed value" ); + equals( jQuery("#test").css("clip-bottom"), jQuery("#test").outerHeight() + "px", "clip-bottom returns correct computed value" ); + equals( jQuery("#test").css("clip-left"), "0px", "clip-left returns correct computed value" ); +}); + +test("rect(auto, auto, auto, auto)", 5, function() { + jQuery("#test").css("clip", "rect(auto, auto, auto, auto)"); + equals( jQuery("#test").css("clip"), "auto", "clip returns correct value" ); + equals( jQuery("#test").css("clip-top"), "0px", "clip-top returns correct computed value" ); + equals( jQuery("#test").css("clip-right"), jQuery("#test").outerWidth() + "px", "clip-right returns correct computed value" ); + equals( jQuery("#test").css("clip-bottom"), jQuery("#test").outerHeight() + "px", "clip-bottom returns correct computed value" ); + equals( jQuery("#test").css("clip-left"), "0px", "clip-left returns correct computed value" ); +}); + +test("relative values", 5, function() { + jQuery("#test").css({ fontSize: "10px", clip: "rect(1em, 4em, 3em, 2em)" }); + equals( jQuery("#test").css("clip"), "rect(10px, 40px, 30px, 20px)", "clip returns correct values" ); + equals( jQuery("#test").css("clip-top"), "10px", "clip-top returns correct value" ); + equals( jQuery("#test").css("clip-right"), "40px", "clip-right returns correct value" ); + equals( jQuery("#test").css("clip-bottom"), "30px", "clip-bottom returns correct value" ); + equals( jQuery("#test").css("clip-left"), "20px", "clip-left returns correct value" ); +}); + From 378f2edc829007edb9917f645c7824297c136970 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Thu, 28 Jul 2011 18:12:17 +0800 Subject: [PATCH 2/3] added clip and myself to the readme (should it be copyright 2010-2011 ?) --- README.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 37df6b0..4cc82db 100644 --- a/README.markdown +++ b/README.markdown @@ -15,6 +15,7 @@ Current Hooks: * color animations for backgroundColor, borderBottomColor, borderLeftColor, borderRightColor, borderTopColor, borderColor, boxShadowColor, color, outlineColor, and textShadowColor * columnCount, columnSpan, columnGap, columnWidth, columnRuleColor, columnRuleStyle, columnRuleWidth * 2D transforms + * clip, clipTop, clipRight, clipBottom, clipLeft # Usage Super simple. Just request the margin, padding, backgroundPosition, boxShadow, etc like you would other CSS properties. @@ -42,4 +43,4 @@ jQuery 1.4.3 introduced the concept of cssHooks. They allow you to hook directly The cssHooks plugin is licensed under the MIT License (LICENSE.txt). -Copyright (c) 2010 [Brandon Aaron](http://brandonaaron.net), [Burin Asavesna](http://helloburin.com), [Tom Ellis](http://www.webmuse.co.uk), [Phil Dokas](http://jetless.org) and [Louis-Rémi Babé](http://twitter.com/louis_remi). +Copyright (c) 2010 [Brandon Aaron](http://brandonaaron.net), [Burin Asavesna](http://helloburin.com), [Tom Ellis](http://www.webmuse.co.uk), [Phil Dokas](http://jetless.org), [Louis-Rémi Babé](http://twitter.com/louis_remi) and [Jeffery To](http://www.thingsthemselves.com/). From 01eef9e7074781226db9e392e1ac2e60981bbd6c Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Thu, 28 Jul 2011 18:15:16 +0800 Subject: [PATCH 3/3] fixed Usage heading in readme --- README.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/README.markdown b/README.markdown index 4cc82db..2af5878 100644 --- a/README.markdown +++ b/README.markdown @@ -16,6 +16,7 @@ Current Hooks: * columnCount, columnSpan, columnGap, columnWidth, columnRuleColor, columnRuleStyle, columnRuleWidth * 2D transforms * clip, clipTop, clipRight, clipBottom, clipLeft + # Usage Super simple. Just request the margin, padding, backgroundPosition, boxShadow, etc like you would other CSS properties.