1
0
mirror of https://github.com/mrclay/minify.git synced 2025-08-12 17:14:24 +02:00

builder app improvements and bookmarklet

JSMin.php : + YUI and IE conditional comment preserving
This commit is contained in:
Steve Clay
2008-09-06 22:35:22 +00:00
parent 7bb535d79c
commit 9a5a67dfaf
11 changed files with 189 additions and 70 deletions

21
README
View File

@@ -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.

View File

@@ -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 '<li id="li' + MUB._uid + '">http://' + location.host + '/<input type=text size=20>'
+ ' <button title="Remove">x</button> <button title="Include Earlier">&uarr;</button>'
+ ' <button title="Include Later">&darr;</button> <span></span></li>';
}
/**
* 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('<p><strong>Found by bookmarklet:</strong> /<a href=#>'
+ location.hash.substr(1).split(',').join('</a> | /<a href=#>')
+ '</a></p>'
);
$('#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;

29
min/builder/bm.js Normal file
View File

@@ -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.');
})();

View File

@@ -45,6 +45,8 @@ and click [Update].</p>
<ol id=sources><li></li></ol>
<div id=add><button>Add file +</button></div>
<div id=bmUris></div>
<p><button id=update>Update</button></p>
<div id=results>
@@ -53,7 +55,7 @@ and click [Update].</p>
<p>Place this URI in your HTML to serve the files above combined, minified, compressed and with cache headers.</p>
<table id=uriTable>
<tr><th>URI</th><td><a id=uriA class=ext>/min</a> <small>(opens in new window)</small></td></tr>
<tr><th>HTML</th><td><input id=uriHtml type=text size=80 readonly></td></tr>
<tr><th>HTML</th><td><input id=uriHtml type=text size=100 readonly></td></tr>
</table>
<h2>How to serve these files as a group</h2>
@@ -63,12 +65,21 @@ and click [Update].</p>
<pre><code>return array(
<span style="color:#666">... your existing groups here ...</span>
<input id=groupConfig size=80 type=text readonly>
<input id=groupConfig size=100 type=text readonly>
);</code></pre>
<p><em>Make sure to replace <code>keyName</code> with a unique key for this group.</em></p>
</div>
<div id=getBm>
<h3>Find URIs on a Page</h3>
<p>You can use the bookmarklet below to fetch all CSS &amp; 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.</p>
<p><a id=bm>Create Minify URIs</a> <small>(right-click, add to favorites/bookmarks)</small></p>
</div>
<hr>
<p>Need help? Search or post to the <a class=ext href="http://groups.google.com/group/minify">Minify discussion list</a>.</p>
<p><small>This app is minified :) <a class=ext href="http://code.google.com/p/minify/source/browse/trunk/min/builder/index.php">view source</a></small></p>
@@ -81,7 +92,6 @@ and click [Update].</p>
document.write('<\script type="text/javascript" src="../?f=' + src + '"><\/script>');
</script>
<!--[ This comment remains because the "[" makes it look like an IE conditional comment :) -->
<?php
$serveOpts = array(

View File

@@ -2,10 +2,14 @@
/**
* jsmin.php - PHP implementation of Douglas Crockford's JSMin.
*
* This is pretty much a direct port of jsmin.c to PHP with just a few
* PHP-specific performance tweaks. Also, whereas jsmin.c reads from stdin and
* outputs to stdout, this library accepts a string as input and returns another
* This is a direct port of jsmin.c to PHP with a few PHP performance tweaks and
* modifications to preserve some comments (see below). Also, rather than using
* stdin/stdout, JSMin::minify() accepts a string as input and returns another
* string as output.
*
* Comments containing IE conditional compilation are preserved, as are multi-line
* comments that begin with "/*!" (for documentation purposes). In the latter case
* newlines are inserted around the comment to enhance readability.
*
* PHP 5 or higher is required.
*
@@ -38,6 +42,7 @@
*
* @package JSMin
* @author Ryan Grove <ryan@wonko.com>
* @author Steve Clay <steve@mrclay.org> (modifications)
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
* @copyright 2008 Ryan Grove <ryan@wonko.com> (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() {

View File

@@ -8,7 +8,7 @@ require 'JSMin.php';
/**
* Compress Javascript using Ryan Grove's JSMin class
*
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
@@ -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));
}
}

View File

@@ -32,4 +32,18 @@ if (is.ua.indexOf('opera') >= 0) {
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*/

View File

@@ -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;}
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*/

View File

@@ -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;}

View File

@@ -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 */

View File

@@ -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();