From 9a5a67dfafc75b4ff76cbf9b1e425f83a80b0512 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 6 Sep 2008 22:35:22 +0000 Subject: [PATCH] builder app improvements and bookmarklet JSMin.php : + YUI and IE conditional comment preserving --- README | 21 +++--- min/builder/_index.js | 71 +++++++++++++++++-- min/builder/bm.js | 29 ++++++++ min/builder/index.php | 16 ++++- min/lib/JSMin.php | 45 ++++++++---- min/lib/Minify/Javascript.php | 21 +----- .../unit_tests/_test_files/js/before.js | 16 ++++- .../unit_tests/_test_files/js/before.min.js | 8 ++- .../_test_files/js/before_noComments.min.js | 3 - .../_test_files/minify/lines_output.js | 15 ++++ .../unit_tests/test_Minify_Javascript.php | 14 ---- 11 files changed, 189 insertions(+), 70 deletions(-) create mode 100644 min/builder/bm.js delete mode 100644 min_extras/unit_tests/_test_files/js/before_noComments.min.js diff --git a/README b/README index aabdd77..eb81447 100644 --- a/README +++ b/README @@ -16,16 +16,6 @@ directory: i.e. you will have: /home/user/www/public_html/min you to the Minify URI Builder application, which will help you quickly start using Minify to serve content on your site. -UNIT TESTING: - -1. Place the /min_extras/ directory as a child of your DOCUMENT_ROOT -directory: i.e. you will have: /home/user/www/public_html/min_extras - -2. To run unit tests, access: http://yourdomain/min_extras/unit_tests/test_all.php - -Other test_*.php files in that directory can be run to test individual -components more verbosely. - FILE ENCODINGS @@ -35,3 +25,14 @@ encodings like ISO 8859/Windows-1252. By default Minify appends Leading UTF-8 BOMs are stripped from all sources to prevent duplication in output files, and files are converted to Unix newlines. + + +UNIT TESTING: + +1. Place the /min_extras/ directory as a child of your DOCUMENT_ROOT +directory: i.e. you will have: /home/user/www/public_html/min_extras + +2. To run unit tests, access: http://yourdomain/min_extras/unit_tests/test_all.php + +Other test_*.php files in that directory can be run to test individual +components more verbosely. diff --git a/min/builder/_index.js b/min/builder/_index.js index ba56d8b..e48064a 100644 --- a/min/builder/_index.js +++ b/min/builder/_index.js @@ -1,41 +1,50 @@ -// @todo update test links when reordering, app instructions var MUB = { _uid : 0 + /** + * Get markup for new source LI element + */ ,newLi : function () { return '
  • http://' + location.host + '/' + ' ' + '
  • '; } + /** + * Add new empty source LI and attach handlers to buttons + */ ,addLi : function () { $('#sources').append(MUB.newLi()); var li = $('#li' + MUB._uid)[0]; $('button[title=Remove]', li).click(function () { + $('#results').hide(); var hadValue = !!$('input', li)[0].value; $(li).remove(); - hadValue && MUB.update(); }); $('button[title$=Earlier]', li).click(function () { $(li).prev('li').find('input').each(function () { + $('#results').hide(); // this = previous li input var tmp = this.value; this.value = $('input', li).val(); $('input', li).val(tmp); MUB.updateAllTestLinks(); - MUB.update(); }); }); $('button[title$=Later]', li).click(function () { $(li).next('li').find('input').each(function () { + $('#results').hide(); // this = next li input var tmp = this.value; this.value = $('input', li).val(); $('input', li).val(tmp); MUB.updateAllTestLinks(); - MUB.update(); }); }); ++MUB._uid; } + /** + * In the context of a source LI element, this will analyze the URI in + * the INPUT and check the URL on the site. + */ ,liUpdateTestLink : function () { // call in context of li element if (! $('input', this)[0].value) return; @@ -58,9 +67,19 @@ var MUB = { ,dataType : 'text' }); } + /** + * Check all source URLs + */ ,updateAllTestLinks : function () { $('#sources li').each(MUB.liUpdateTestLink); } + /** + * In a given array of strings, find the character they all have at + * a particular index + * @param Array arr array of strings + * @param Number pos index to check + * @return mixed a common char or '' if any do not match + */ ,getCommonCharAtPos : function (arr, pos) { var i ,l = arr.length @@ -72,6 +91,10 @@ var MUB = { return ''; return c; } + /** + * Get the shortest URI to minify the set of source files + * @param Array sources URIs + */ ,getBestUri : function (sources) { var pos = 0 ,base = '' @@ -103,6 +126,9 @@ var MUB = { } return uri; } + /** + * Create the Minify URI for the sources + */ ,update : function () { MUB.updateAllTestLinks(); var sources = [] @@ -139,21 +165,56 @@ var MUB = { ); $('#results').show(); } + /** + * Handler for the "Add file +" button + */ ,addButtonClick : function () { + $('#results').hide(); MUB.addLi(); MUB.updateAllTestLinks(); $('#update').show().click(MUB.update); } + /** + * Runs on DOMready + */ ,init : function () { $('#sources').html(''); $('#add button').click(MUB.addButtonClick); + // make easier to copy text out of $('#uriHtml, #groupConfig').click(function () { this.select(); }).focus(function () { this.select(); }); $('a.ext').attr({target:'_blank'}); - MUB.addButtonClick(); + if (location.hash) { + // make links out of URIs from bookmarklet + $('#getBm').hide(); + $('#bmUris').html('

    Found by bookmarklet: /' + + location.hash.substr(1).split(',').join(' | /') + + '

    ' + ); + $('#bmUris a').click(function () { + MUB.addButtonClick(); + $('#sources li:last input').val(this.innerHTML) + MUB.liUpdateTestLink.call($('#sources li:last')[0]); + $('#results').hide(); + return false; + }).attr({title:'Add file +'}); + } else { + // copy bookmarklet code into href + var bmUri = location.pathname.replace(/\/[^\/]*$/, '/bm.js').substr(1); + $.ajax({ + url : '../?f=' + bmUri + ,success : function (code) { + $('#bm')[0].href = code + .replace('%BUILDER_URL%', location.href) + .replace(/\n/g, ' '); + } + ,dataType : 'text' + }); + MUB.addButtonClick(); + } } }; window.onload = MUB.init; \ No newline at end of file diff --git a/min/builder/bm.js b/min/builder/bm.js new file mode 100644 index 0000000..ec06831 --- /dev/null +++ b/min/builder/bm.js @@ -0,0 +1,29 @@ +javascript:(function() { + function add(uri) { + (0 === uri.indexOf(home)) + && (!/[\?&]/.test(uri)) + && uris.push(escape(uri.substr(home.length))); + } + function sheet(ss) { + ss.href && add(ss.href); + if (ss.cssRules) { + var i = 0, r; + while (r = ss.cssRules[i++]) + r.styleSheet && sheet(r.styleSheet); + } + } + var d = document + ,uris = [] + ,i = 0 + ,o + ,home = (location + '').split('/').splice(0, 3).join('/') + '/'; + while (o = d.getElementsByTagName('script')[i++]) + o.src && !(o.type && /vbs/i.test(o.type)) && add(o.src); + i = 0; + while (o = d.styleSheets[i++]) + sheet(o); + if (uris.length) + window.open('%BUILDER_URL%#' + uris.join(',')); + else + alert('No minifiable resources found.'); +})(); \ No newline at end of file diff --git a/min/builder/index.php b/min/builder/index.php index bcfb03b..1445969 100644 --- a/min/builder/index.php +++ b/min/builder/index.php @@ -45,6 +45,8 @@ and click [Update].

    +
    +

    @@ -53,7 +55,7 @@ and click [Update].

    Place this URI in your HTML to serve the files above combined, minified, compressed and with cache headers.

    - +
    URI/min (opens in new window)
    HTML
    HTML

    How to serve these files as a group

    @@ -63,12 +65,21 @@ and click [Update].

    return array(
         ... your existing groups here ...
    -
    +
     );

    Make sure to replace keyName with a unique key for this group.

    +
    +

    Find URIs on a Page

    +

    You can use the bookmarklet below to fetch all CSS & Javascript URIs from a page +on your site. When you active it, this page will open in a new window with a list of +available URIs to add.

    + +

    Create Minify URIs (right-click, add to favorites/bookmarks)

    +
    +

    Need help? Search or post to the Minify discussion list.

    This app is minified :) view source

    @@ -81,7 +92,6 @@ and click [Update].

    document.write('<\script type="text/javascript" src="../?f=' + src + '"><\/script>'); - + * @author Steve Clay (modifications) * @copyright 2002 Douglas Crockford (jsmin.c) * @copyright 2008 Ryan Grove (PHP port) * @license http://opensource.org/licenses/mit-license.php MIT License @@ -242,42 +247,54 @@ class JSMin { } protected function next() { - $c = $this->get(); + $get = $this->get(); - if ($c === '/') { + if ($get === '/') { + $commentContents = ''; switch($this->peek()) { case '/': + // "//" comment for (;;) { - $c = $this->get(); - - if (ord($c) <= self::ORD_LF) { - return $c; + $get = $this->get(); + $commentContents .= $get; + if (ord($get) <= self::ORD_LF) { + return preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $commentContents) + ? "/{$commentContents}" + : $get; } } case '*': + // "/* */" comment $this->get(); - for (;;) { - switch($this->get()) { + $get = $this->get(); + switch($get) { case '*': if ($this->peek() === '/') { $this->get(); - return ' '; + if (0 === strpos($commentContents, '!')) { + // YUI Compressor style + return "\n/*" . substr($commentContents, 1) . "*/\n"; + } + return preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $commentContents) + ? "/*{$commentContents}*/" // IE conditional compilation + : ' '; } break; case null: throw new JSMinException('Unterminated comment.'); } + $commentContents .= $get; } default: - return $c; + return $get; } } - return $c; + return $get; } protected function peek() { diff --git a/min/lib/Minify/Javascript.php b/min/lib/Minify/Javascript.php index 255b005..c372f9e 100644 --- a/min/lib/Minify/Javascript.php +++ b/min/lib/Minify/Javascript.php @@ -8,7 +8,7 @@ require 'JSMin.php'; /** * Compress Javascript using Ryan Grove's JSMin class - * + * * @package Minify * @author Stephen Clay */ @@ -19,28 +19,13 @@ class Minify_Javascript { * * @param string $js * - * @param array $options available options: - * - * 'preserveComments': (default true) multi-line comments that begin - * with "/*!" will be preserved with newlines before and after to - * enhance readability. + * @param array $options available options (none currently) * * @return string */ public static function minify($js, $options = array()) { - if (isset($options['preserveComments']) - && !$options['preserveComments']) { - return trim(JSMin::minify($js)); - } - require_once 'Minify/CommentPreserver.php'; - // recursive calls don't preserve comments - $options['preserveComments'] = false; - return Minify_CommentPreserver::process( - $js - ,array('Minify_Javascript', 'minify') - ,array($options) - ); + return trim(JSMin::minify($js)); } } diff --git a/min_extras/unit_tests/_test_files/js/before.js b/min_extras/unit_tests/_test_files/js/before.js index 37f34c7..daa70f1 100644 --- a/min_extras/unit_tests/_test_files/js/before.js +++ b/min_extras/unit_tests/_test_files/js/before.js @@ -32,4 +32,18 @@ if (is.ua.indexOf('opera') >= 0) { if (is.ua.indexOf('gecko') >= 0) { is.ie = is.ns = false; is.gecko = true; -} \ No newline at end of file +} + +/*@cc_on + /*@if (@_win32) + document.write("OS is 32-bit, browser is IE."); + @else @*/ + document.write("Browser is not IE (ie: is Firefox) or Browser is not 32 bit IE."); + /*@end +@*/ + +//@cc_on/* + +alert("Hello !IE browser"); + +//@cc_on*/ diff --git a/min_extras/unit_tests/_test_files/js/before.min.js b/min_extras/unit_tests/_test_files/js/before.min.js index 3bb55b2..499aab2 100644 --- a/min_extras/unit_tests/_test_files/js/before.min.js +++ b/min_extras/unit_tests/_test_files/js/before.min.js @@ -1,4 +1,3 @@ - /* is.js (c) 2001 Douglas Crockford @@ -9,4 +8,9 @@ var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaE * preserve this comment, too */ is.mac=is.ua.indexOf('mac')>=0;if(is.ua.indexOf('opera')>=0){is.ie=is.ns=false;is.opera=true;} -if(is.ua.indexOf('gecko')>=0){is.ie=is.ns=false;is.gecko=true;} \ No newline at end of file +if(is.ua.indexOf('gecko')>=0){is.ie=is.ns=false;is.gecko=true;}/*@cc_on + /*@if (@_win32) + document.write("OS is 32-bit, browser is IE."); + @else @*/document.write("Browser is not IE (ie: is Firefox) or Browser is not 32 bit IE.");/*@end +@*///@cc_on/* +alert("Hello !IE browser");//@cc_on*/ \ No newline at end of file diff --git a/min_extras/unit_tests/_test_files/js/before_noComments.min.js b/min_extras/unit_tests/_test_files/js/before_noComments.min.js deleted file mode 100644 index 1b4c3d9..0000000 --- a/min_extras/unit_tests/_test_files/js/before_noComments.min.js +++ /dev/null @@ -1,3 +0,0 @@ -var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaEnabled(),ns:navigator.appName=='Netscape',ua:navigator.userAgent.toLowerCase(),version:parseFloat(navigator.appVersion.substr(21))||parseFloat(navigator.appVersion),win:navigator.platform=='Win32'} -is.mac=is.ua.indexOf('mac')>=0;if(is.ua.indexOf('opera')>=0){is.ie=is.ns=false;is.opera=true;} -if(is.ua.indexOf('gecko')>=0){is.ie=is.ns=false;is.gecko=true;} \ No newline at end of file diff --git a/min_extras/unit_tests/_test_files/minify/lines_output.js b/min_extras/unit_tests/_test_files/minify/lines_output.js index 1e078d9..8775dd7 100644 --- a/min_extras/unit_tests/_test_files/minify/lines_output.js +++ b/min_extras/unit_tests/_test_files/minify/lines_output.js @@ -243,3 +243,18 @@ /* 33 */ is.ie = is.ns = false; /* 34 */ is.gecko = true; /* 35 */ } +/* 36 */ +/* 37 */ /*@cc_on +/* 38 *| /*@if (@_win32) +/* 39 *| document.write("OS is 32-bit, browser is IE."); +/* 40 *| @else @*/ +/* 41 */ document.write("Browser is not IE (ie: is Firefox) or Browser is not 32 bit IE."); +/* 42 */ /*@end +/* 43 *| @*/ +/* 44 */ +/* 45 */ //@cc_on/* +/* 46 *| +/* 47 *| alert("Hello !IE browser"); +/* 48 *| +/* 49 *| //@cc_on*/ +/* 50 */ diff --git a/min_extras/unit_tests/test_Minify_Javascript.php b/min_extras/unit_tests/test_Minify_Javascript.php index 86fbfb2..1f0a85e 100644 --- a/min_extras/unit_tests/test_Minify_Javascript.php +++ b/min_extras/unit_tests/test_Minify_Javascript.php @@ -18,20 +18,6 @@ function test_Javascript() echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n"; echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n"; } - - //$src = file_get_contents($thisDir . '/_test_files/js/before.js'); - $minExpected = file_get_contents($thisDir . '/_test_files/js/before_noComments.min.js'); - $minOutput = Minify_Javascript::minify($src, array( - 'preserveComments' => false - )); - - $passed = assertTrue($minExpected == $minOutput, 'Minify_Javascript'); - - if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { - echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n"; - echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n"; - echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n"; - } } test_Javascript();