1
0
mirror of https://github.com/mrclay/minify.git synced 2025-09-06 12:10:45 +02:00

144 Commits
2.1.5 ... 2.3.0

Author SHA1 Message Date
Steve Clay
f4cb31135d Merge pull request #516 from mrclay/23_deprecations
Begin prep for 2.3 release
2016-03-08 06:49:57 -05:00
Steve Clay
afbcadf33c Begin prep for 2.3 release 2016-03-07 16:50:01 -05:00
Steve Clay
1c3c370dd4 Adds server-info.php 2015-09-26 23:27:29 -04:00
Steve Clay
9eaef368e0 Update FirePHP 2015-09-26 23:16:42 -04:00
Steve Clay
5b6b891cc3 Update UriRewriting.wiki.md 2015-09-26 16:03:05 -04:00
Steve Clay
d71b11ec98 Update CommonProblems.wiki.md 2015-09-22 13:16:36 -04:00
Steve Clay
44e35e69c3 Update README.md 2015-09-22 13:12:46 -04:00
Steve Clay
c24af9fe02 Update README.md 2015-09-22 13:09:41 -04:00
Steve Clay
69019960c9 Update README.md 2015-09-22 13:06:25 -04:00
Steve Clay
0dea940e5f README changes 2015-09-04 17:46:03 -04:00
Steve Clay
f1ef53e84e Add wiki docs and point to some of them (lots of work left here) 2015-09-04 17:38:45 -04:00
Kaue Santoja
d233b65d3d Adds config option to concatenate files only 2015-09-03 14:26:35 -04:00
Elan Ruusamäe
cabd595a5a Merge pull request #142 from KTOmega/readme
Make README into Markdown
2015-04-20 14:49:55 +03:00
Kevin Tran
f9b728918a Make README into Markdown 2015-04-19 23:44:22 -07:00
Kevin Tran
73a085b6c1 Rename README.txt to README.md 2015-04-19 23:41:18 -07:00
Steve Clay
3c11ba8232 Merge pull request #128 from fisharebest/gitattributes
Exclude build/development tools from releases using .gitattributes
2014-10-30 18:58:02 -04:00
Greg Roach
ecbb5ae376 Exclude build/development tools from releases using .gitattributes 2014-10-24 09:29:20 +01:00
Elan Ruusamäe
bd9a450694 update cssmin to v2.4.8-4. fixes #123 2014-09-25 14:40:53 +03:00
Elan Ruusamäe
bceffd5afb remove duplicate $keys assignment 2014-09-21 14:31:30 +03:00
acidvertigo
153e08b5a0 Code clean 2014-09-20 14:21:31 +03:00
Elan Ruusamäe
ce109925aa remove two DOS EOL 2014-09-20 10:34:23 +03:00
Elan Ruusamäe
a0c5ecb514 Merge pull request #102 from greensolid-technologies/master
Add support for Wincache
2014-09-19 16:43:51 +03:00
Matthias Fax
6b469640a7 Add Wincache
Updated comments

Check for Wincache existence in constructor

Normalized markup

Add Wincache test

Tie Wincache test to test index
2014-06-12 15:14:47 +02:00
Steve Clay
fb3931f8cd Fixes #97: URIs with spaces are correctly rewritten 2014-04-03 19:53:48 -04:00
Steve Clay
50488ce207 Merge pull request #96 from glensc/patch-2
insert_final_newline to true
2014-03-20 06:47:05 -04:00
Elan Ruusamäe
ebc91b5d33 insert_final_newline to true 2014-03-20 09:17:56 +02:00
Steve Clay
e7728405f5 Merge pull request #95 from glensc/patch-1
.editorconfig: skip style apply in vendor/ (composer stuff)
2014-03-19 12:31:26 -04:00
Elan Ruusamäe
537b4b314a skip style apply in vendor/ (composer stuff) 2014-03-19 17:19:57 +02:00
Steve Clay
4a884a381f Merge pull request #94 from glensc/issue-51
Handle faiure to open cache file for writing
2014-03-19 10:34:30 -04:00
Steve Clay
b018c834da Created .editorconfig 2014-03-19 10:33:07 -04:00
Elan Ruusamäe
27b4f758bc fix passing invalid parametrs to flock. #51 2014-03-19 15:07:57 +02:00
Steve Clay
9231aed49f Adds Bootstrap styling to builder app 2014-03-12 10:35:14 -04:00
Steve Clay
ea7fe90554 Merge branch 'Jimdo-add-tests-closure-compiler' 2014-03-12 09:52:21 -04:00
Steve Clay
5ceada8a22 YUIC unit test updates
bail if jar not found, make test more obviously customizable, don't
fail test if YUIC correctly handles input it used to choke on.
2014-03-12 09:51:53 -04:00
Steve Clay
66f2622705 Clean up unit test messages 2014-03-12 09:49:16 -04:00
Steve Clay
f3a408fd74 Remove unused var, add @throws 2014-03-12 09:48:36 -04:00
Steve Clay
d245bca498 Merge branch 'sambauers-master' 2014-03-12 08:54:23 -04:00
Sam Bauers
c91f746132 2.2.0 release - update VERSION constant 2014-03-12 08:53:30 -04:00
Sam Bauers
853dfd0395 Prepare for 2.2.0 release with updated HISTORY.txt 2014-03-12 08:53:30 -04:00
Joscha Feth
c60b6a2984 fix typo 2014-03-05 19:29:22 +01:00
Joscha Feth
54a53c9c19 Adds tests for the command line based Closure Compiler 2014-03-05 19:20:52 +01:00
Steve Clay
eadf601dbe Cleanup index.php a bit 2014-02-04 11:00:25 -05:00
Steve Clay
30839ab5f0 Allow long expires with "v" query string param 2014-02-04 11:00:11 -05:00
Steve Clay
b4b8606dfd Fix/simplify custom config paths implementation 2014-02-04 10:57:45 -05:00
Steve Clay
86fbe1f948 Merge branch 'joec4i-master' 2014-02-04 10:31:37 -05:00
Steve Clay
a2487c7c1a Simplify identifying file-relative URIs in rewriter 2014-02-04 10:31:00 -05:00
Steve Clay
f3aa19283b Merge pull request #86 from mrclay/cite_is_inline
Remove cite from block elements pattern
2014-02-04 07:08:59 -08:00
Steve Clay
ef726e261b Merge branch 'Jimdo-jimdo-compiler-service-options' 2014-02-04 10:00:22 -05:00
Steve Clay
a35b801ce9 Clean up pull/84 additions 2014-02-04 09:59:46 -05:00
Joscha Feth
5d73cd0886 * Allow additional HTTP options to be passed to the Closure compiler service
* Allow option for overriding the maximum byte size POST limit
2014-02-04 09:59:46 -05:00
Steve Clay
ddee838f7c Merge pull request #88 from sambauers/master
Allow definition of custom config directory.
2014-02-04 06:10:18 -08:00
Sam Bauers
8e74d727f0 Use base for main config key. 2014-02-05 00:59:21 +11:00
Sam Bauers
8e55cd8091 Use base for main config key. 2014-02-05 00:58:46 +11:00
Sam Bauers
2c897a0b59 Fix index names on config paths array. 2014-02-05 00:57:18 +11:00
Sam Bauers
63519b55f6 Remove filenames from include statements. 2014-02-05 00:54:03 +11:00
Sam Bauers
0f4692e0c0 Change custom path explanation to match full custom paths changes. 2014-02-05 00:51:23 +11:00
Sam Bauers
b34d964e88 Allow full custom path including filename 2014-02-05 00:46:08 +11:00
Sam Bauers
ae70500a2d Allow optional custom configs to be optional
This change tests first for the existence of the custom config variable and then the existence of each custom config file in turn. If found the directory path to append to that config file is changed to the custom path.

This allows fallback to the standard config when no custom config is present.

Maybe it would be even better to load both - the standard config, then the custom config, but this might be confusing for groupConfig.php. It could work using array merge, but it wouldn't be particularly clear or easy to explain.
2014-02-04 12:37:40 +11:00
Sam Bauers
b8a0cac0e5 Explain separate config storage in MIN.txt 2014-02-04 12:13:33 +11:00
Sam Bauers
719c22fca4 Include groupsConfig.php in MINIFY_CONFIG_DIR. 2014-02-03 18:19:46 +11:00
Sam Bauers
aceef480c3 Allow definition of custom config directory.
To allow "clean" checkouts of Minify we could use the possibility to define a custom config directory outside the minify directory.

Using this patch a user can define MINIFY_CUSTOM_CONFIG_DIR on a new page outside the minify directory and then include minify's index.php on that page and point their rewrites etc. there instead.

The config.php (and config-test.php) could be copied to the custom directory and edited without causing svn:external/git:submodule conflicts.
2014-02-03 18:19:00 +11:00
Steve Clay
9e4176f193 Remove cite from block elements pattern 2014-01-13 16:50:48 -05:00
Joe Cai
ea866716e7 Fixed the last commit 2014-01-02 22:44:27 +08:00
Joe Cai
bb03aa097d about:blank check with regex 2014-01-02 22:32:41 +08:00
Joe Cai
143046d472 CSS_UriRewriter: do not rewrite about:blank 2014-01-02 11:27:40 +08:00
Steve Clay
5560a664e9 Fix up web-based tool and correct error byte in JSMin exceptions 2013-11-27 18:28:39 -05:00
mrclay.org
f8b62b358b Fixes issue 299 on Windows by reducing length of cache filenames 2013-11-13 20:37:44 -05:00
Steve Clay
218f37fb44 Update CSSmin, adds Minify_CSSmin wrapper, removes units for CSSmin 2013-11-13 20:18:19 -05:00
Steve Clay
e778d4f714 Merge pull request #78 from glensc/cc-url2
allow possibility to configure closure-compiler url
2013-11-08 17:00:44 -08:00
Steve Clay
88367b3f72 Merge pull request #77 from glensc/temp-path-error
report temp path when mktemp fails
2013-11-08 16:58:39 -08:00
Elan Ruusamäe
ba6ec9c1af allow possibility to configure closure-compiler url 2013-11-09 00:43:53 +02:00
Elan Ruusamäe
77f0728eef report temp path when mktemp fails 2013-11-08 23:59:04 +02:00
Steve Clay
94d2596a57 Fixed email in composer.json 2013-09-26 09:10:32 -04:00
Steve Clay
7f13823b52 Fixed composer.json 2013-09-26 09:07:43 -04:00
Steve Clay
e8e9272229 Update composer.json 2013-09-26 09:07:18 -04:00
Steve Clay
681885762d Fixes #73: Handles regex literals directly following return/typeof 2013-09-20 20:09:51 -04:00
Steve Clay
db7fe24493 Prepare 2.1.7 release 2013-07-23 15:58:28 -04:00
Steve Clay
9f46484c1a Strip null bytes from GET vars 2013-07-23 15:52:31 -04:00
Steve Clay
07e032a3f0 Prep for 2.1.6 2013-07-19 22:46:02 -04:00
Steve Clay
3a1f9f55cf Merge pull request #65 from tubalmartin/master
Update CSSmin to latest version 2.4.8
2013-06-24 11:56:00 -07:00
tubalmartin
59a466d6a6 Update CSSmin to latest version 2.4.8 2013-06-24 20:35:17 +02:00
Steve Clay
6f5721d2db Fixes #53 (github) handles division following line break 2013-04-01 11:25:27 -04:00
Steve Clay
d67feea182 Fixed small Lines bug, Refactored JSMin algo to more closely match original 2013-03-29 13:26:10 -04:00
Steve Clay
c95d6bac4e Fixes Gcode issue 256, improve readability 2013-03-29 10:01:17 -04:00
Steve Clay
faec60fff6 Merge pull request #54 from jrobeson/patch-1
remove version from composer.json
2013-03-23 13:12:40 -07:00
Steve Clay
35cddbf5eb Merge pull request #42 from glensc/cc-connection-close
use "Connection: close" of file_get_contents
2013-03-23 13:11:48 -07:00
Johnny Robeson
416296ad6f remove version from composer.json
version isn't needed since a tag for 2.1.5 already exists.
2013-03-23 17:10:42 -03:00
Steve Clay
2089caaf66 Merge pull request #43 from glensc/groupurls
allow setGroup keys be list of groups comma separated
2013-03-23 13:03:09 -07:00
Steve Clay
e228891304 Merge pull request #44 from glensc/composer.json2
add composer.json file
2013-03-23 12:59:25 -07:00
Steve Clay
3f0485e54f Merge pull request #47 from glensc/stack-size
Minify_YUICompressor: add support for increasing stack size
2013-03-23 12:57:51 -07:00
Steve Clay
c1c375cb0e Merge pull request #48 from glensc/script-clean-comments
add jsCleanComments option to Minify_HTML
2013-03-23 12:55:43 -07:00
Steve Clay
7a268d99df Merge pull request #49 from glensc/xcache
add Minify_Cache_XCache - xcache backend
2013-03-23 12:46:37 -07:00
Steve Clay
cb75a4fd30 Fixes #158: Builder works on SSL
http://code.google.com/p/minify/issues/detail?id=158
2013-03-01 09:27:59 -05:00
Steve Clay
db65f55738 Avoid NOTICE errors in _getCommonCharAtPos 2013-01-22 16:15:06 -05:00
Elan Ruusamäe
04f5873e28 upstream uses spaces for formatting 2013-01-18 14:19:59 +02:00
Elan Ruusamäe
7ffdeef7ef add Minify_Cache_XCache - xcache backend
based on APC and Memcache files
2013-01-18 14:17:51 +02:00
Elan Ruusamäe
f6d078785f add jsCleanComments option to Minify_HTML 2013-01-17 00:02:30 +02:00
Elan Ruusamäe
8c54519b32 Minify_YUICompressor: add support for increasing stack size 2013-01-15 14:53:45 +02:00
Elan Ruusamäe
37ff25ef06 add role to author 2012-12-08 01:50:01 +02:00
Elan Ruusamäe
73ae2b74e2 specify version 2012-12-08 01:00:23 +02:00
Elan Ruusamäe
04adfef406 fill more fields (type, license, homepage, deps) 2012-12-08 00:37:38 +02:00
Elan Ruusamäe
2975b0f581 use tabs 2012-12-08 00:37:20 +02:00
Elan Ruusamäe
1daa72f57c use steve's email 2012-12-08 00:15:53 +02:00
Elan Ruusamäe
437d3dcc15 point to mrclay repo 2012-12-07 23:20:31 +02:00
Even André Fiskvik
1662208a10 Added working auto-loader 2012-12-07 23:10:16 +02:00
Even André Fiskvik
828a3bd4f5 test autoload 2012-12-07 23:10:08 +02:00
Even André Fiskvik
f9c0319049 Seems like composer won't search forked code and match on identifier. Changing to Oyatel 2012-12-07 23:10:01 +02:00
Even André Fiskvik
a49a6a2f01 Added composer.json package information 2012-12-07 23:09:51 +02:00
Elan Ruusamäe
32abbfa328 allow setGroup keys be list of groups comma separated
this allows loading multiple groups on page with
Minify_HTML_Helper::getUri, keeping array method for loading list of
sources

the backend side for handling this in Minify_Controller_MinApp is
already present.
2012-12-02 23:20:22 +02:00
Steve Clay
f680ac83e7 Add elapsed time to minifyTextarea, better escaping 2012-11-25 16:34:55 -05:00
Elan Ruusamäe
a82d70b0ba use "Connection: close" of file_get_contents
otherwise php will do keepalive request, and wait timeout seconds before
can return actual response. similar problem does not happen with curl
backend
2012-11-25 15:26:28 +02:00
Steve Clay
7e08cff1f0 Merge pull request #36 from robations/patch-1
Path/require problem in cli script min_extras/cli/minify.php
2012-11-23 15:19:53 -08:00
Steve Clay
85a75541a3 Merge pull request #37 from robations/patch-2
Make cli script more portable
2012-11-23 15:19:04 -08:00
Robert Churchill
3b2fd8dfd6 Make cli script more portable
Changing hash-bang interpreter to `#!/usr/bin/env php`, for better portability with OSs such as BSD.
2012-10-19 19:42:31 +02:00
Robert Churchill
0d5e3295a3 Update min_extras/cli/minify.php
I think the path variable got renamed inconsistently?
2012-10-19 18:32:26 +02:00
Steve Clay
248aff9b72 Updated and renamed CSSmin.php 2012-09-30 19:39:30 -04:00
Steve Clay
884977fde2 Docs cleanup for MrClay\Cli 2012-09-30 19:31:11 -04:00
Steve Clay
335800947b Fixes #271: Allows URI rewriting when handling pre-minified files 2012-09-30 18:44:17 -04:00
Steve Clay
c196dd39df Replaced all class require's with autoloader 2012-09-30 17:51:34 -04:00
Steve Clay
db00fa6eb0 Update master 2012-06-07 15:06:40 -03:00
Steve Clay
41cb743d6c Fixes #262: don't create gzip cache when encodeMethod empty 2012-06-04 12:17:55 -04:00
Steve Clay
666cd6d89a Fixes #261: verify query string params are not arrays 2012-06-04 11:49:44 -04:00
Steve Clay
8fdf3db564 Merge pull request #25 from glensc/master
Add google closure compiler cli version
2012-04-15 14:06:30 -07:00
Elan Ruusamäe
91af676645 set default compilation_level=SIMPLE_OPTIMIZATIONS
anyway that's the default in the jar anyway :)
2012-04-03 18:01:54 +03:00
Elan Ruusamäe
a2b6036430 Add google closure compiler cli version 2012-03-30 00:45:06 +03:00
Steve Clay
dfdcdf874a Merge pull request #24 from glensc/master
need only check readable for .jar file
2012-03-29 13:01:47 -07:00
Elan Ruusamäe
d6621ec185 fix jarFile check not being executable
jarFile does not need to be executable, just readable
2012-03-29 20:31:43 +03:00
Steve Clay
c51ca8ddca Merge pull request #23 from tubalmartin/master
CSSmin.php: some improvements
2012-03-16 13:51:43 -07:00
tubalmartin
73dd77569e Now substring and str_slice methods match exactly their Javascript counterparts behavior.
Minor cleaning/formatting.
Tabs to spaces (Clay seems to use spaces instead of tabs)
2012-03-16 20:48:40 +01:00
Steve Clay
ff45c76484 re-disabled builder 2012-03-16 14:48:33 -04:00
Steve Clay
a2bed51e2b CSSmin: whitespace cleanup 2012-03-16 14:45:45 -04:00
Steve Clay
9ccc1e98a7 CSSmin: make changing PHP settings optional 2012-03-16 14:41:27 -04:00
Steve Clay
9fa1463ab7 CSSmin phpdoc fixes 2012-03-16 14:32:56 -04:00
Steve Clay
6eefd7a7ed Allow CSSmin instance to be re-used for multiple run() calls 2012-03-16 14:26:20 -04:00
Steve Clay
9dba65a0e9 minor unit test tweaks 2012-03-16 14:25:25 -04:00
Steve Clay
a703d325d5 Merge pull request #22 from tubalmartin/master
CSSmin updated to latest commit of YUI compressor!
2012-03-16 09:35:52 -07:00
tubalmartin
b5807b8ad2 Improved substring method. 2012-03-16 03:21:35 +01:00
tubalmartin
c3485ac5a5 CSSmin updated to latest commit of YUI compressor.
CSSmin passes all unit tests successfully!
Added trim() to the file_get_contents() resulting code so that test results match!
2012-03-16 02:40:56 +01:00
Steve Clay
45bea314d6 HTTP Digest auth option for builder 2012-03-15 14:01:39 -04:00
Steve Clay
98f372d7da Docs 2012-03-15 14:01:14 -04:00
Steve Clay
f40f4e4f56 Easier config testing 2012-03-15 14:00:50 -04:00
Steve Clay
2275d21146 test files for quicker field testing 2012-03-15 11:38:01 -04:00
Steve Clay
acbcd84c14 update comment 2012-03-15 10:00:41 -04:00
Steve Clay
5f2c5a4f2d added CSSmin and test 2012-03-14 21:41:24 -04:00
168 changed files with 5799 additions and 2326 deletions

19
.editorconfig Normal file
View File

@@ -0,0 +1,19 @@
# top-most EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
; temporary
trim_trailing_whitespace = false
[*.php]
indent_style = space
indent_size = 4
insert_final_newline = true
[vendor/**]
; Use editor default (possible autodetection).
indent_style =
indent_size =

5
.gitattributes vendored Normal file
View File

@@ -0,0 +1,5 @@
/.editorconfig export-ignore
/.gitignore export-ignore
/.gitattributes export-ignore
/min_extras export-ignore
/min_unit_tests export-ignore

7
.gitignore vendored
View File

@@ -1,5 +1,8 @@
# /
/test
/docs
.idea/
/.idea/
/composer.lock
.DS_Store
/vendor
/.php_cs.cache

View File

@@ -1,6 +1,58 @@
Minify Release History
Version 2.1.5
Version 2.3.0
* Adds `$min_concatOnly` option to just concatenate files
* Deprecates use of Minify_Loader
* Deprecates use of Minify_Logger
* Deprecates use of JSMinPlus
* Deprecates use of FirePHP
* Deprecates use of DooDigestAuth
Version 2.2.1 (2014-10-30)
* Builder styled with Bootstrap (thanks to help from acidvertigo)
* Update CSSmin to v.2.4.8
* Added WinCache
* URLs with spaces properly rewritten
Version 2.2.0 (2014-03-12)
* Fix handling of RegEx in certain situations in JSMin
* Thanks to Vovan-VE for reporting this
* Update composer.json with support info
* Add ability to set ClosureCompiler URL
* Thanks Elan Ruusamäe for the pull request
* Better report of temp directory errors
* Also thanks to Elan Ruusamäe for anatoher pull request
* Updated CSSmin and added Minify_CSSmin wrapper
* Fix windows issue associated with long cache filenames
* Fix issue with web-based tool
* Fix bug in JSMin exceptions
* Fix "about:blank" bug in CSS_UriRewriter
* Cite is no longer a block element in HTML minification
* Allow for definition of custom config locations outside of the min directory
* Thanks Sam Bauers for the pull request
* Allow option for overriding the maximum byte size POST limit for ClosureCompiler and other additions
* Thanks Joscha Feth for the code
* Fixes to file-relative URL identification in UriRewriter
* Allow far-future expiration and file versioning with the "v" querystirng parameter in addition to existing method
* Lots of general code tidy ups
Version 2.1.7 (2013-07-23)
* Fixes arbitrary file inclusion vulnerability on some systems
* Thanks to Matt Mecham for reporting this
Version 2.1.6 (2013-07-19)
* JSMin fixes
* Prevents some Closure Compiler API failures
* Uses autoloading for all class loading
* Multiple group support in HTML Helper
* Cache adaptor for XCache
* Allow setting stack-size in YUI Compressor wrapper
* Adds jsCleanComments option to HTML minifier
* Upgrades CSSmin
* CLI script more portable
* Adds composer.json
Version 2.1.5 (2012-03-10)
* Removed XSS vulnerability
* Disabled builder bby default
* command line tools to minify and rewrite URIs in CSS
@@ -9,7 +61,7 @@ Version 2.1.5
* Closure Compiler uses cURL when allow_url_fopen is off
* Missing file notices when using groups
Version 2.1.4
Version 2.1.4 (2010-07-10)
* Option to minify JS with Closure Compiler API w/ JSMin failover
* Cookie/bookmarklet-based debug mode. No HTML editing!
* Allows 1 file to be missing w/o complete failure
@@ -25,7 +77,7 @@ Version 2.1.4
* Removed annoying maxFiles limit
* mbstring.func_overload usage is safer
Version 2.1.3
Version 2.1.3 (2009-06-30)
* HTTP fixes
* ETag generation now valid (different when gzipped)
* Vary header always sent when Accept-Encoding is sniffed
@@ -37,7 +89,7 @@ Version 2.1.3
* Allow setting contentType in Minify_Source objects
* No more 5.3 deprecation warnings: split() removed
Version 2.1.2
Version 2.1.2 (2009-03-04)
* Javascript fixes
* Debug mode no longer confused by "*/*" in strings/RegExps (jQuery)
* quote characters inside RegExp literals no longer cause exception
@@ -49,14 +101,14 @@ Version 2.1.2
* Builder app doesn't fail on systems without gzdeflate()
* APC caching class included
Version 2.1.1
Version 2.1.1 (2008-10-19)
* Bug fix release
* Detection and workarounds for zlib.output_compression and non-PHP encoding modules
* Zlib not required (mod_rewrite, et.al., can still be used for encoding)
* HTML : More IE conditional comments preserved
* Minify_groupUri() utility fixed
Version 2.1.0
Version 2.1.0 (2008-09-18)
* "min" default application for quick deployment
* Minify URI Builder app & bookmarklet for quickly creating minify URIs
* Relative URIs in CSS file are fixed automatically by default
@@ -97,4 +149,4 @@ Version 1.0.1 (2007-05-05)
* Replaced old JSMin library with a much faster custom implementation.
Version 1.0.0 (2007-05-02)
* First release.
* First release.

49
MIN.txt
View File

@@ -110,9 +110,9 @@ Separate group keys with commas:
FAR-FUTURE EXPIRES HEADERS
Minify can send far-future (one year) Expires headers. To enable this you must
add a number to the querystring (e.g. /min/?g=js&1234 or /min/f=file.js&1234)
and alter it whenever a source file is changed. If you have a build process you
can use a build/source control revision number.
add a number or the parameter "v" to the querystring (e.g. /min/?g=js&1234 or
/min/?g=js&v=1234) and alter it whenever a source file is changed. If you have a
build process you can use a build/source control revision number.
You can alternately use the utility function Minify_getUri() to get a "versioned"
Minify URI for use in your HTML. E.g.:
@@ -130,6 +130,42 @@ $cssUri = Minify_getUri(array(
echo "<link rel=stylesheet href='{$cssUri}'>";
STORING CONFIG FILES OUTSIDE THE MINIFY DIRECTORY
It is possible to store config files (min/config.php, min/config-test.php,
min/groupsConfig.php) in a custom directory outside the Minify directory. This is
useful if you wish to include Minify as an external dependency inside another
project via SVN external or Git submodule inclusion.
For example, let's assume you have a Minify directory "min" in your site root. Then
you could create a new directory called "min-configs" in the site root. Copy any
config files you wish to modify to "min-configs", and modify as desired.
Then create a new file, for example "min.php" in your site root. The contents of
this file could look like this:
<?php
$customConfigDirectory = dirname(__FILE__) . '/min-configs';
$min_customConfigPaths = array(
'base' => $customConfigDirectory . '/config.php',
'test' => $customConfigDirectory . '/config-test.php',
'groups' => $customConfigDirectory . '/groupsConfig.php'
);
include_once 'min/index.php';
You would then reference min.php in your JS and CSS links instead of min/index.php.
This method will affect those using the Minify_getUri() function. You will need
to add options to calls to that function, e.g.:
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/min/utils.php';
$jsUri = Minify_getUri('//js/file.js', array('minAppUri' => '/min.php'));
echo "<script src='{$jsUri}'></script>";
DEBUG MODE
In debug mode, instead of compressing files, Minify sends combined files with
@@ -140,6 +176,11 @@ file. To enable this, set $min_allowDebugFlag to true in config.php and append
Known issue: files with comment-like strings/regexps can cause problems in this mode.
BYPASSING MINIFICATION
See the $min_concatOnly option in config.php.
QUESTIONS?
http://groups.google.com/group/minify
http://groups.google.com/group/minify

79
README.md Normal file
View File

@@ -0,0 +1,79 @@
Welcome to Minify!
==================
Minify is an HTTP server for JS and CSS assets. It compresses and combines files
and serves it with appropriate headers, allowing conditional GET or long-Expires.
| *Before* | ![7 requests](http://mrclay.org/wp-content/uploads/2008/09/fiddler_before.png) |
|----------|-----------------------------------------------------------------|
| *After* | ![2 requests](http://mrclay.org/wp-content/uploads/2008/09/fiddler_after.png) |
The stats above are from a [brief walkthrough](http://mrclay.org/index.php/2008/09/19/minify-21-on-mrclayorg/) which shows how easy it is to set up Minify on an existing site. It eliminated 5 HTTP requests and reduced JS/CSS bandwidth by 70%.
Relative URLs in CSS files are rewritten to compensate for being served from a different directory.
Wordpress User?
---------------
Consider instead using a dedicated WordPress plugin for more deep integration and simpler installation. E.g.:
- [BWP Minify](http://wordpress.org/extend/plugins/bwp-minify/)
- [W3 Total Cache](http://wordpress.org/extend/plugins/w3-total-cache/)
Unfortunately we can't support the WordPress plugins here.
Installation
------------
Place the `/min/` directory as a child of your DOCUMENT_ROOT
directory: i.e. you will have: `/home/example/www/min`
You can see verify that it is working by visiting these two URLs:
- http://example.org/min/?f=min/quick-test.js
- http://example.org/min/?f=min/quick-test.css
If your server supports mod_rewrite, this URL should also work:
- http://example.org/min/f=min/quick-test.js
Configuration & Usage
---------------------
See the MIN.txt file and the [user guide](https://github.com/mrclay/minify/blob/master/docs/UserGuide.wiki.md)
Minify also comes with a [URI Builder application](https://github.com/mrclay/minify/blob/master/docs/BuilderApp.wiki.md) that can help you write URLs
for use with Minify or configure groups of files.
See the [cookbook](https://github.com/mrclay/minify/blob/master/docs/CookBook.wiki.md) for more advanced options for minification.
More [docs are available](https://github.com/mrclay/minify/tree/master/docs).
Support
-------
[Google Group](http://groups.google.com/group/minify)
Unit Testing
------------
1. Place the /min_unit_tests/ directory as a child of your DOCUMENT_ROOT
directory: i.e. you will have: /home/example/www/min_unit_tests
2. To run unit tests, access: http://example.org/min_unit_tests/test_all.php
(If you wish, the other test_*.php files can be run to test individual
components with more verbose output.)
3. Remove /min_unit_tests/ from your DOCUMENT_ROOT when you are done.
Warnings
--------
* Minify is designed for efficiency, but, for very high traffic sites, it will probably serve files slower than your HTTPd due to the CGI overhead of PHP. See the [FAQ](https://github.com/mrclay/minify/blob/master/docs/FAQ.wiki.md#how-fast-is-it) and [CookBook](https://github.com/mrclay/minify/blob/master/docs/CookBook.wiki.md) for more info.
* If you combine a lot of CSS, watch out for [IE's 4096 selectors-per-file limit](http://stackoverflow.com/a/9906889/3779), affects IE 6 through 9.
* Minify *should* work fine with files encoded in UTF-8 or other 8-bit encodings like ISO 8859/Windows-1252. By default Minify appends ";charset=utf-8" to the Content-Type headers it sends.
Acknowledgments
---------------
Minify was inspired by [jscsscomp](http://code.google.com/p/jscsscomp/) by Maxim Martynyuk and by the article [Supercharged JavaScript](http://www.hunlock.com/blogs/Supercharged_Javascript) by Patrick Hunlock.
The [JSMin library](http://www.crockford.com/javascript/jsmin.html) used for !JavaScript minification was originally written by Douglas Crockford and was [ported to PHP](https://github.com/mrclay/jsmin-php) by Ryan Grove specifically for use in Minify.

View File

@@ -1,64 +0,0 @@
WELCOME TO MINIFY!
Minify is an HTTP content server. It compresses sources of content
(usually files), combines the result and serves it with appropriate
HTTP headers. These headers can allow clients to perform conditional
GETs (serving content only when clients do not have a valid cache)
and tell clients to cache the file for a period of time.
More info: http://code.google.com/p/minify/
WORDPRESS USER?
These WP plugins integrate Minify into WordPress's style and script hooks to
get you set up faster.
http://wordpress.org/extend/plugins/bwp-minify/
http://wordpress.org/extend/plugins/w3-total-cache/
INSTALLATION
Place the /min/ directory as a child of your DOCUMENT_ROOT
directory: i.e. you will have: /home/example/www/min
CONFIGURATION & USAGE
See the MIN.txt file and http://code.google.com/p/minify/wiki/UserGuide
Minify also comes with a URI Builder application that can help you write URLs
for use with Minify or configure groups of files.
To enable this, edit min/config.php, set $min_enableBuilder = true; and visit
http://example.org/min/builder/
When you're finished with this, please set $min_enableBuilder = false;
UPGRADING
See UPGRADING.txt for instructions.
UNIT TESTING:
1. Place the /min_unit_tests/ directory as a child of your DOCUMENT_ROOT
directory: i.e. you will have: /home/example/www/min_unit_tests
2. To run unit tests, access: http://example.org/min_unit_tests/test_all.php
(If you wish, the other test_*.php files can be run to test individual
components with more verbose output.)
3. Remove /min_unit_tests/ from your DOCUMENT_ROOT when you are done.
FILE ENCODINGS
Minify *should* work fine with files encoded in UTF-8 or other 8-bit
encodings like ISO 8859/Windows-1252. By default Minify appends
";charset=utf-8" to the Content-Type headers it sends.
Leading UTF-8 BOMs are stripped from all sources to prevent
duplication in output files, and files are converted to Unix newlines.

32
composer.json Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "mrclay/minify",
"type": "library",
"description": "Minify is a PHP5 app that helps you follow several rules for client-side performance. It combines multiple CSS or Javascript files, removes unnecessary whitespace and comments, and serves them with gzip encoding and optimal client-side cache headers",
"homepage": "http://code.google.com/p/minify/",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Stephen Clay",
"email": "steve@mrclay.org",
"role": "Developer"
}
],
"support": {
"email": "minify@googlegroups.com",
"issues": "http://code.google.com/p/minify/issues/list",
"wiki": "http://code.google.com/p/minify/w/list"
},
"autoload": {
"classmap": ["min/lib/"]
},
"require": {
"php": ">=5.2.1",
"ext-pcre": "*"
},
"require-dev": {
"tubalmartin/cssmin": "~2.4.8"
},
"suggest": {
"tubalmartin/cssmin": "Support minify with CSSMin (YUI PHP port)"
}
}

View File

@@ -0,0 +1,29 @@
If you test sites in a subdirectory (e.g. `http://localhost/testSite/`) rather than a virtualhost, then you'll need to adjust the way you use Minify to rewrite CSS correctly.
1. Place the following in `min/config.php`:
```
// Set the document root to be the path of the "site root"
$min_documentRoot = substr(__FILE__, 0, -15);
// Set $sitePrefix to the path of the site from the webserver's real docroot
list($sitePrefix) = explode('/min/index.php', $_SERVER['SCRIPT_NAME'], 2);
// Prepend $sitePrefix to the rewritten URIs in CSS files
$min_symlinks['//' . ltrim($sitePrefix, '/')] = $min_documentRoot;
```
2. In the HTML, make your Minify URIs document-relative (e.g. `min/f=js/file.js` and `../min/f=js/file.js`), not root-relative.
Now the `min` application should operate correctly from a subdirectory and will serve files relative to your "site" root rather than the document root. E.g.
| **environment** | production | testing |
|:----------------|:-----------|:--------|
| **server document root** | `/home/mysite_com/www` | `/var/www` |
| **`$min_documentRoot` ("site root")** | `/home/mysite_com/www` | `/var/www/testSite` |
| **`$sitePrefix`** | (empty) | `/testSite` |
| **Minify URL** | `http://mysite.com/min/f=js/file1.js` | `http://localhost/testSite/min/f=js/file1.js` |
| **file served** | `/home/mysite_com/www/js/file1.js` | `/var/www/testSite/js/file1.js` |
Caveats:
* This configuration may break the Builder application (located at `/min/builder/`) used to create Minify URIs, but you can still [create them by hand](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/README.txt#18).
* Make sure you don't reset `$min_symlinks` to a different value lower in your config file.

9
docs/BuilderApp.wiki.md Normal file
View File

@@ -0,0 +1,9 @@
Minify ships with "Builder", a simple Javascript app for constructing URIs to use with Minify. ([screenshots of the 2.1.0 version](http://www.mrclay.org/index.php/2008/09/19/minify-21-on-mrclayorg/))
It also does some run-time checks of your PHP and Minify configuration to look for problematic settings like [auto-encoding](http://code.google.com/p/minify/wiki/CommonProblems#Output_is_distorted/random_chars).
After installation, this is found at **`http://example.com/min/builder/`**
You must enable it by editing `min/config.php` and setting `$min_enableBuilder = true;`
After use, you should disable it by resetting `$min_enableBuilder = false;`

129
docs/CommonProblems.wiki.md Normal file
View File

@@ -0,0 +1,129 @@
If this page doesn't help, please post a question on our [Google group](http://groups.google.com/group/minify).
## URIs are re-written incorrectly in CSS output
See UriRewriting.
## Files aren't cached in IE6
**Use Minify 2.1.4+**.
For Minify 2.1.3 and below:
1. Open `/min/lib/HTTP/Encoder.php`
1. On line [62](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/HTTP/Encoder.php#62), change `false` to `true`.
## Builder Fails / 400 Errors
**Use Minify 2.1.4+**, and you can see the cause of 400 responses using FirePHP (See [Debugging](Debugging.md)).
## PHP/Apache crashes
[PCRE (which provides regular expressions) commonly crashes PHP](https://www.google.com/search?q=pcre+php+crash) and this is nearly impossible to solve in PHP code. Things to try:
* Raise Apache's [ThreadStackSize](http://stackoverflow.com/a/7597506/3779)
* In [php.ini](http://php.net/manual/en/pcre.configuration.php) raise `pcre.backtrack_limit` and `pcre.recursion_limit` to 1000000. These will allow processing longer strings, but also require a larger stack size.
* Try [this CSSmin configuration](http://code.google.com/p/minify/wiki/CookBook#CSSmin_PHP_port)
## Dealing with Javascript errors
Short answer: **use Minify 2.1.4+, use a pre-compressed version of your file, and rename it `*.min.js` or `*-min.js`**. By default Minify won't try to minify these files (but will still gzip them). The [Compressor Rater](http://compressorrater.thruhere.net/) is handy for compressing files individually.
If the error is in your code, enable [debug mode](Debugging.md) while debugging your code in Firebug or your favorite browser's Javascript debugger. This will insert comments to allow you to keep track of the individual source locations in the combined file.
If you have Java on your web host, you can use the [wrapper for YUI Compressor](http://code.google.com/p/minify/source/browse/min/lib/Minify/YUICompressor.php) instead of JSMin. [This thread](http://groups.google.com/group/minify/browse_thread/thread/f12f25f27e1256fe) shows how a user has done this.
## Javascript isn't being minified
If the filename ends with **`-min.js`** or **`.min.js`**, Minify will assume the file is already compressed and just combine it with any other files.
### Scriptaculous
Scriptaculous 1.8.2 (and probably all 1.x) has an [autoloader script](http://github.com/madrobby/scriptaculous/blob/4b49fd8884920d4ee760b0194431f4f433f878df/src/scriptaculous.js#L54) that requires files to be in a particular place on disk. To serve Scriptaculous modules with Minify, just serve `prototype.js` and the individual support files (e.g. `dragdrop.js`, `effects.js`) and the library should work fine. E.g.:
```
<script src="/min/f=scriptaculous/lib/prototype.js" type="text/javascript"></script>
<script src="/min/b=scriptaculous/src&amp;f=effects.js,dragdrop.js" type="text/javascript"></script>
<script type="text/javascript">
/* DragDrop and Effects modules can be used here. */
</script>
```
## Server cache files won't update
If you upload files using [Coda or Transmit](http://groups.google.com/group/coda-users/browse_thread/thread/572d2dc315ec02e7/) or from a Windows PC to a non-Windows server, your new files may end up with the wrong `mtime` (timestamp) on the server, confusing the cache system.
Setting the [$min\_uploaderHoursBehind option](https://github.com/mrclay/minify/blob/master/min/config.php#L171) in `config.php` can compensate for this.
WinSCP has a [Daylight Saving Time option](http://winscp.net/eng/docs/ui_login_environment#daylight_saving_time) that can prevent this issue.
This can also occur if your files are changed, and the `mtime` is set in the past (e.g. via a `git checkout` operation). If so you'll have to `touch` the changed files or use some other method to make the `mtime` current.
## Can't see changes in browser
Generally changes aren't seen because a) the browser is refusing to send a new request, or b) the server doesn't recognize that your source files have been modified after the server cache was created.
First, place the Minify URL directly in the address bar and refresh.
If a change is not seen, verify that the server cache file is being updated.
## Disable Caching
If you'd like to temporarily disable the cache without using [debug mode](Debugging.md), place these settings at the end of `config.php`:
```
// disable server caching
$min_cachePath = null;
// prevent client caching
$min_serveOptions['maxAge'] = 0;
$min_serveOptions['lastModifiedTime'] = $_SERVER['REQUEST_TIME'];
```
**Don't do this on a production server!** Minify will have to combine, minify, and gzencode on every request.
## Character Encodings
_Please_ use UTF-8. The string processing may work on encodings like Windows-1251 but will certainly fail on radically different ones like UTF-16.
If you consistently use a different encoding, in `config.php` set `$min_serveOptions['contentTypeCharset']` to this encoding to send it in the Content-Type header.
Otherwise, set it to `false` to remove it altogether. You can still, in CSS, use the [@charset](http://www.w3.org/TR/CSS2/syndata.html#x50) directive to tell the browser the encoding, but (a) it must appear first and (b) shouldn't appear later in the output (and Minify won't enforce this).
Moral? To minimize problems, use UTF-8 and remove any `@charset` directives from your CSS.
## @imports can appear in invalid locations in combined CSS files
If you combine CSS files, @import declarations can appear after CSS rules, invalidating the stylesheet. As of version 2.1.2, if Minify detects this, it will prepend a warning to the output CSS file. To resolve this, you can either move your @import statements within your files, or enable the option 'bubbleCssImports'.
## Debug mode can cause a Javascript error
This issue was resolved in version 2.1.2.
Debug mode adds line numbers in comments. Unfortunately, in versions <= 2.1.1, if the source file had a string or regex containing (what looks like) a C-style comment token, the algorithm was confused and the injected comments caused a syntax error.
## Minification can cause a Javascript error
This issue was resolved in version 2.1.2.
In rare instances the [JSMin](http://code.google.com/p/minify/source/browse/tags/release_2.1.1/min/lib/JSMin.php#14) algorithm in versions <= 2.1.1 could be confused by regexes in certain contexts and throw an exception. The workaround was to simply wrap the expression in parenthesis. E.g.
```
// in 2.1.1 and previous
return /'/; // JSMin throws error
return (/'/); // no error
```
## Output is distorted/random chars
What you're seeing is a mismatch between the content encoding the browser expects and what it receives.
The usual problem is that a global PHP or web server configuration is causing the output of PHP scripts to be automatically gzipped. Since Minify already outputs gzipped content, the browser receives "double encoded" content which it interprets as noise. The Builder app in 2.1.4 sometimes can tell you which component is causing the auto-encoding.
## Can't specify more than 10 files via URL
Use Minify 2.1.4+. Before there was a setting to adjust the maximum allowed.
## Directory Listing Denied
This may also appear as "Virtual Directory does not allow contents to be listed". Minify requires that the URI `/min/` (a request for a directory listing) result in the execution of `/min/index.php`. On Apache, you would make sure `index.php` is listed in the [DirectoryIndex directive](http://httpd.apache.org/docs/2.0/mod/mod_dir.html#directoryindex). IIS calls this the [Default Document](http://www.iis.net/ConfigReference/system.webServer/defaultDocument).
## See Also
* [Debugging](Debugging.md)

View File

@@ -0,0 +1,14 @@
# PHP5 Component Classes
| **Class** | **Functionality** |
|:----------|:------------------|
| [Minify](http://code.google.com/p/minify/source/browse/min/lib/Minify.php) | Combine, process, and serve pieces of content (usually CSS/JS files). Almost all behavior is configurable including request handling (via [controllers](http://code.google.com/p/minify/source/browse/min/lib/Minify/Controller/)), HTTP headers & encoding, caching ([file/APC/memcached](http://code.google.com/p/minify/source/browse/min/lib/Minify/Cache/)), and compression functions (by default, the classes below) |
| [JSMin](http://code.google.com/p/minify/source/browse/min/lib/JSMin.php) | A port of [jsmin.c](http://www.crockford.com/javascript/jsmin.html), extended to preserve some multi-line comments and IE conditional comments (since these occasionally have their [uses](http://dean.edwards.name/weblog/2007/03/sniff/)). |
| [Minify\_CSS](http://code.google.com/p/minify/source/browse/min/lib/Minify/CSS.php) | Uses the two classes below to minify CSS and optionally rewrite URIs. |
| [Minify\_CSS\_Compressor](http://code.google.com/p/minify/source/browse/min/lib/Minify/CSS/Compressor.php) | Agressively minifies CSS. Includes a thorough [test suite](http://code.google.com/p/minify/source/browse/min_unit_tests/_test_files/css/) to make sure most common [hacks](http://code.google.com/p/minify/source/browse/min_unit_tests/_test_files/css/hacks.css) and [unusual syntaxes](http://code.google.com/p/minify/source/browse/min_unit_tests/_test_files/css/unusual_strings.css) are preserved. |
| [Minify\_CSS\_UriRewriter](http://code.google.com/p/minify/source/browse/min/lib/Minify/CSS/UriRewriter.php) | In CSS files, rewrites file-relative URIs as root-relative. With [test suite](http://code.google.com/p/minify/source/browse/min_unit_tests/_test_files/css_uriRewriter/) |
| [Minify\_HTML](http://code.google.com/p/minify/source/browse/min/lib/Minify/HTML.php) | Agressively minifies (X)HTML markup. Preserves TEXTAREA/PRE contents, can invoke minifiers on STYLE and SCRIPT blocks, and wraps content in CDATA blocks in XHTML when necessary. Includes [test suite](http://code.google.com/p/minify/source/browse/min_unit_tests/_test_files/html/). |
| [Minify\_YUICompressor](http://code.google.com/p/minify/source/browse/min/lib/Minify/YUICompressor.php) | (Experimental) PHP wrapper for invoking the Java-based [YUI Compressor](http://developer.yahoo.com/yui/compressor/) |
| [Minify\_ImportProcessor](http://code.google.com/p/minify/source/browse/min/lib/Minify/ImportProcessor.php) | Linearize a CSS/JS file by including content specified by CSS import declarations. In CSS files, relative URIs are fixed. This has a test suite but should be considered "experimental". |
| [HTTP\_Encoder](http://code.google.com/p/minify/source/browse/min/lib/HTTP/Encoder.php) | Applies HTTP encoding to content. Can serve content with conditional encoding based on Accept-Encoding headers (parsed according to [HTTP spec](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html)) and known browser bugs. |
| [HTTP\_ConditionalGet](http://code.google.com/p/minify/source/browse/min/lib/HTTP/ConditionalGet.php) | Implements HTTP conditional GETs via a simple, flexible API. |

157
docs/CookBook.wiki.md Normal file
View File

@@ -0,0 +1,157 @@
Unless mentioned, all the following snippets go in `min/config.php`.
## Faster Cache Performance
By default, Minify uses [Minify\_Cache\_File](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Cache/File.php). It uses `readfile`/`fpassthru` to improve performance over most file-based systems, but it's still file IO. I haven't done comparative benchmarks on all three, but APC/Memcache _should_ be faster. In all cases, Minify cache ids begin with `"minify_"`.
### APC
```
require 'lib/Minify/Cache/APC.php';
$min_cachePath = new Minify_Cache_APC();
```
### Memcache
You must create and connect your Memcache object then pass it to `Minify_Cache_Memcache`'s constructor.
```
require 'lib/Minify/Cache/Memcache.php';
$memcache = new Memcache;
$memcache->connect('localhost', 11211);
$min_cachePath = new Minify_Cache_Memcache($memcache);
```
### Zend Platform
Patrick van Dissel has contributed a [cache adapter for Zend Platform](http://code.google.com/p/minify/issues/detail?id=167).
## Closure Compiler API Wrapper
An [experimental wrapper for Google's closure compiler API](https://github.com/mrclay/minify/blob/master/min/lib/Minify/JS/ClosureCompiler.php) is available for compressing Javascript. If the API fails for any reason, JSMin is used as the default backup minifier.
```
$min_serveOptions['minifiers']['application/x-javascript'] = array('Minify_JS_ClosureCompiler', 'minify');
```
## YUICompressor
If your host can execute Java, you can use Minify's YUI Compressor wrapper. You'll need the latest [yuicompressor-x.x.x.jar](http://yuilibrary.com/downloads/#yuicompressor) and a temp directory. Place the .jar in `min/lib`, then:
```
function yuiJs($js) {
Minify_YUICompressor::$jarFile = __DIR__ . '/lib/yuicompressor-x.x.x.jar';
Minify_YUICompressor::$tempDir = '/tmp';
return Minify_YUICompressor::minifyJs($js);
}
$min_serveOptions['minifiers']['application/x-javascript'] = 'yuiJs';
```
To use YUIC for CSS with fixed URIs:
```
function yuiCss($css, $options) {
Minify_YUICompressor::$jarFile = __DIR__ . '/lib/yuicompressor-x.x.x.jar';
Minify_YUICompressor::$tempDir = '/tmp';
$css = Minify_YUICompressor::minifyCss($css);
$css = Minify_CSS_UriRewriter::rewrite(
$css
,$options['currentDir']
,isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT']
,isset($options['symlinks']) ? $options['symlinks'] : array()
);
return $css;
}
$min_serveOptions['minifiers']['text/css'] = 'yuiCss';
```
### CSSmin PHP port
Minify has added Túbal Martín's [PHP port](https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port/blob/master/cssmin.php) of the YUI Compressor's CSSmin. While it is not completely integrated yet, you may try it out:
```
function yuiCssPort($css, $options) {
$compressor = new CSSmin();
$css = $compressor->run($css, 9999999);
$css = Minify_CSS_UriRewriter::rewrite(
$css,
$options['currentDir'],
isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'],
isset($options['symlinks']) ? $options['symlinks'] : array()
);
return $css;
}
$min_serveOptions['minifiers']['text/css'] = 'yuiCssPort';
```
As of commit [218f37](https://github.com/mrclay/minify/commit/218f37fb44f9be2ea138cf9efb8b7f6dc84bad7f), this is easier:
```
$min_serveOptions['minifiers']['text/css'] = array('Minify_CSSmin', 'minify');
```
## JSMin+
Tino Zijdel's [JSMin+](http://crisp.tweakblogs.net/blog/6861/jsmin%2B-version-14.html) has resulted in memory usage problems for many users and will be removed from the Minify codebase in 3.0. If you wish to use it, you should download it outside the Minify directory and link to it:
```
require '/path/to/jsminplus.php';
$min_serveOptions['minifiers']['application/x-javascript'] = array('JSMinPlus', 'minify');
```
## Server-specific Options
You may need to have different options depending on what server you're on. You can do this just how you'd expect:
```
if ($_SERVER['SERVER_NAME'] == 'myTestingWorkstation') {
// testing
$min_allowDebugFlag = true;
$min_errorLogger = true;
$min_enableBuilder = true;
$min_cachePath = 'c:\\WINDOWS\\Temp';
$min_serveOptions['maxAge'] = 0; // see changes immediately
} else {
// production
$min_allowDebugFlag = false;
$min_errorLogger = false;
$min_enableBuilder = false;
$min_cachePath = '/tmp';
$min_serveOptions['maxAge'] = 86400;
}
```
## Site in a Subdirectory
If you test/develop sites in a subdirectory (e.g. `http://localhost/siteA/`), see AlternateFileLayouts.
## Group-specific Options
In "group" requests, `$_GET['g']` holds the group key, so you can code based on it:
```
if (isset($_GET['g'])) {
switch ($_GET['g']) {
case 'js' : $min_serveOptions['maxAge'] = 86400 * 7;
break;
case 'css': $min_serveOptions['contentTypeCharset'] = 'iso-8859-1';
break;
}
}
```
## File/Source-specific Options
See CustomSource.
## Processing Output After Minification
If `$min_serveOptions['postprocessor']` is set to a callback, Minify will pass the minified content to this function with type as the second argument. This allows you to apply changes to your minified content without making your own custom minifier. E.g.:
```
function postProcess($content, $type) {
if ($type === Minify::TYPE_CSS) {
require_once 'CssColorReplacer.php';
return CssColorReplacer::process($content);
}
return $content;
}
$min_serveOptions['postprocessor'] = 'postProcess';
```
This function is only called once immediately after minification and its output is stored in the cache.

153
docs/CustomServer.wiki.md Normal file
View File

@@ -0,0 +1,153 @@
Please see the UserGuide if you're just getting started with Minify. This guide is for more advanced users who wish to implement an HTTP server in PHP using the [Minify](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify.php) class.
The basic steps are:
1. Set up the include\_path
1. Set up caching
1. Choose a Minify controller
1. Set up service and controller options
1. Handle the request
## Set up the include\_path
```
set_include_path('/path/to/min/lib' . PATH_SEPARATOR . get_include_path());
```
## Set up caching
Minify ships with [cache classes](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Cache/) for files, APC, and memcache. Choose one, instantiate it, and pass it to Minify:
```
require 'Minify.php';
require 'Minify/Cache/File.php';
Minify::setCache(new Minify_Cache_File()); // files in directory chosen by Solar_Dir
```
## Choose a Minify controller
Minify uses controller classes to analyze HTTP requests and determine which sources will make up the response. (The content isn't always returned; if the browser's cache is valid, Minify can return a 304 header instead).
The [Files](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Controller/Files.php#9) controller doesn't care about the HTTP request. It just specifies a given array of sources (file paths or [source objects](CustomSource.md)).
The [Groups](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Controller/Groups.php) controller uses `$_SERVER['PATH_INFO']` to choose from an array of source lists. There's an example at the end of this page.
The Files controller is simple, so we'll use it here.
## Set up service and controller options
A single array is used for configuration, and is passed to the `Minify::serve` method. `serve` passes it to the controller, which picks off the keys it needs and returns it to `serve` for Minify's own built-in [options](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify.php#88). This architecture allows the controller to set defaults for `serve`.
The Files controller only needs one key: `files`: the array of sources to be combined and served.
The only `serve` option we'll set is `maxAge` (the default is a measly 1800 seconds).
```
$options = array(
// options for the controller
'files' => array('//js/file1.js', '//js/file2.js', $src),
// options for Minify::serve
'maxAge' => 86400
);
```
(In the above $src is a [Minify\_Source object](CustomSource.md), which allows you to serve non-file content, and/or apply settings to individual sources.)
## Handle the request
```
Minify::serve('Files', $options);
```
That's it...
Minify's default application (sometimes referred to as "min") is implemented this way, in the file [min/index.php](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/index.php). Most of its request handling is encapsulated in its own [MinApp](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Controller/MinApp.php#15) controller.
# The Request Cycle
In handling a request, `Minify::serve` does a number of operations:
1. creates a controller object, unless you pass it a readymade object (let's call this `$ctrl`).
1. calls `$ctrl->setupSources($options)`, which analyzes the request and sets `$ctrl->sources` accordingly to an array of Minify\_Source objects.
1. calls `$ctrl->analyzeSources($options)`, which set `$options` keys for `contentType` and `lastModifiedTime`, based on the sources.
1. calls `$ctrl->mixInDefaultOptions($options)`, to mix the controller's default options with the user's.
1. determines if the browser accepts gzip
1. validates the browser cache (optionally responding with a 304)
1. validates the server cache. If it needs refreshing, `Minify::_combineMinify` is called, which...
* calls `$source->getContent` on each source
* the content is combined before or after minification (depending on individual source options)
1. sets up headers to be sent
1. either returns the headers and content in an array (if `quiet` is set), or sends it to the browser.
# Using the Groups controller
```
$options = array(
'groups' => array(
'js' => array('//js/file1.js', '//js/file2.js'),
'css' => array('//css/file1.css', '//css/file2.css'),
),
);
Minify::serve('Groups', $options);
```
With the above, if you request `http://example.org/myServer.php/css`, Apache will set `$_SERVER['PATH_INFO'] = '/css'` and the sources in `$options['groups']['css']` will be served.
<a href='Hidden comment: Move somewhere else
== Sending far future Expires headers ==
By default Minify enables [http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers conditional GETs] for client-side caching, but in this model the browser has to continuously check back with the server to revalidate its cache. A better method is to send [http://developer.yahoo.com/performance/rules.html#expires far future Expires headers] and change the URL when the file changes. As long as you generate your HTML with PHP, Minify makes it easy to do this.
1. Move the "groups" configuration array into a separate file "_groupsConfig.php":
```
<?php
// configures both Minify::serve() and Minify_Build
return array(
"js1" => array("//js/yourFile1.js", "//js/yourFile2.js")
,"js2" => array("//js/yourFile1.js", "//js/yourFile3.js")
,"jQuery" => array("//js/jquery-1.2.6.js")
,"css" => array("//css/layout.css", "//css/fonts.css")
);
```
2. Adjust "min.php" to use this file:
```
Minify::serve("Groups", array(
"groups" => (require "_groupsConfig.php")
));
```
3. In your HTML-generating script, create Minify_Build objects for each minify URI you"re going to use, and, to each, apply the uri() method:
```
require "Minify/Build.php";
$_gc = (require "_groupsConfig.php");
$js1Build = new Minify_Build($_gc["js1"]);
$cssBuild = new Minify_Build($_gc["css"]);
echo "<link rel="stylesheet" type="text/css" href="" . $cssBuild->uri("/min.php/css") . "" />";
/* ... */
echo "<script type="text/javascript" src="" . $js1Build->uri("/min.php/js1") . ""></script>";
```
5. Open the (X)HTML page in your browser. The Javascript and CSS should "work".
6. View source. The minify URIs should look like: "/min.php/js1?##########" (a unix timestamp)
7. Now that our URI"s are synched with source file changes, we can safely send Expires headers. In "min.php":
```
Minify::serve("Groups", array(
"groups" => (require "_groupsConfig.php")
,"maxAge" => 31536000 // 1 yr
// in 2.0 was "setExpires" => $_SERVER["REQUEST_TIME"] + 31536000
));
```
Now "min.php" will serve files with Expires headers, causing the browser to always retrieve them from cache (until the expiration date).
When you make a change to any of your source Javascript/CSS files, your HTML file will have a different querystring appended to the minify URIs, causing the browser to download it as a new URL, and Minify will automatically rebuild its cache files on the server.
'></a>

138
docs/CustomSource.wiki.md Normal file
View File

@@ -0,0 +1,138 @@
In `groupsConfig.php`, usually you specify source file paths using strings like `/path/to/file.js` or `//js/file1.js` (Minify expands this to `"{$_SERVER['DOCUMENT_ROOT']}/js/file1.js"` ).
Instead of a string, you may substitute a "source object" (an instance of class [Minify\_Source](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Source.php)). This allows you to customize how minification is applied, and/or pull content from a non-file location (e.g. a URL).
### Example: filepath
In the `$spec` array, set the key `filepath` to produce a source based on a file path:
```
$src1 = new Minify_Source(array(
'filepath' => '//js/file1.js',
));
$src2 = new Minify_Source(array(
'filepath' => '//js/file2.js',
));
return array(
'js' => array($src1, $src2)
);
```
Note the above is functionally identical to:
```
return array(
'js' => array('//js/file1.js', '//js/file2.js')
);
```
### Example: Specify a different minifier or none at all
To change minifier, set `minifier` to a [callback](http://php.net/manual/en/language.pseudo-types.php)`*` or the empty string (for none):
**`*`Prepare for `groupsConfig.php` to be executed more than once.** (This is likely if you're using the functions in `/min/utils.php`.) In practice this just means making sure functions are conditionally defined if they don't already exist, etc.
```
if (! function_exists('myMin')) {
function myMin($js) {
require_once '/path/to/jsminplus.php';
return JSMinPlus::minify($js);
}
}
$src1 = new Minify_Source(array(
'filepath' => '//js/file1.js',
'minifier' => 'myMin',
));
$src2 = new Minify_Source(array(
'filepath' => '//js/file2.js',
'minifier' => '', // don't compress
));
```
In the above, `myMin()` is only called when rebuilding the cache. This way `JSMinPlus.php` is only loaded when needed.
**`*`Do _not_ use [create\_function](http://php.net/manual/en/function.create-function.php) or anonymous functions for the minifier.** The internal names of these function tend to vary, causing Minify to create lots of [duplicate cache files](http://code.google.com/p/minify/issues/detail?id=138) (and perform poorly).
## Non-File Sources
You're not limited to flat js/css files, but without `filepath`, the `$spec` array must contain these keys:
* **`id `** a unique string id for this source. (e.g. `'my source'`)
* **`getContentFunc `** a [callback](http://php.net/manual/en/language.pseudo-types.php) that returns the content. The function is only called when the cache is rebuilt.
* **`contentType `** `Minify::TYPE_JS` or `Minify::TYPE_CSS`
* **`lastModified `** a timestamp indicating when the content last changed. (If you can't determine this quickly, you can "fake" it using a step function, causing the cache to be periodically rebuilt.)
### Example: Content from a URL
Here we want to fetch javascript from a URL. We don't know when it will change, so we use a stepping expression to re-fetch it every midnight:
```
if (! function_exists('src1_fetch')) {
function src1_fetch() {
return file_get_contents('http://example.org/javascript.php');
}
}
$src1 = new Minify_Source(array(
'id' => 'source1',
'getContentFunc' => 'src1_fetch',
'contentType' => Minify::TYPE_JS,
'lastModified' => ($_SERVER['REQUEST_TIME'] - $_SERVER['REQUEST_TIME'] % 86400),
));
```
If you know that the URL content only depends on a few local files, you can use the maximum of their `mtime`s as the `lastModified` key:
```
...
$src1 = new Minify_Source(array(
'id' => 'source1',
'getContentFunc' => 'src1_fetch',
'contentType' => Minify::TYPE_JS,
'lastModified' => max(
filemtime('/path/to/javascript.php')
,filemtime('/path/to/javascript_input.css')
),
));
```
## Performance Considerations
Be aware that all the code you put in groupsConfig.php will be evaluated upon every request like `/min/g=...`, so make it as light as possible.
If you wish to keep groupsConfig.php "clean", you can alternately create a separate PHP script that manually sets up sources, caching, options, and calls Minify::serve().
```
<?php // myServer.php
/**
* This script implements a Minify server for a single set of sources.
* If you don't want '.php' in the URL, use mod_rewrite...
*/
// setup Minify
set_include_path('path/to/min/lib' . PATH_SEPARATOR . get_include_path());
require 'Minify.php';
require 'Minify/Cache/File.php';
Minify::setCache(new Minify_Cache_File()); // guesses a temp directory
function src1_fetch() {
return file_get_contents('http://example.org/javascript.php');
}
// setup sources
$sources = array();
$sources[] = new Minify_Source(array(
'id' => 'source1'
,'getContentFunc' => 'src1_fetch'
,'contentType' => Minify::TYPE_JS
,'lastModified' => max(
filemtime('/path/to/javascript.php')
,filemtime('/path/to/javascript_input.js')
)
));
$sources[] = '//file2.js';
$sources[] = '//file3.js';
// setup serve options
$options = array(
'files' => $sources,
'maxAge' => 86400,
);
// handle request
Minify::serve('Files', $options);
```

80
docs/Debugging.wiki.md Normal file
View File

@@ -0,0 +1,80 @@
# Server Errors
| **Code** | **Most likely cause** |
|:---------|:----------------------|
| 400 | Controller failed to return valid set of sources to serve |
| 500 | Minifier threw exception (e.g. JSMin choked on syntax) |
You can find details by enabling FirePHP logging:
1. Install/enable [FirePHP](https://addons.mozilla.org/en-US/firefox/addon/6149).
1. Open Firebug's console
1. Set `$min_errorLogger = true;` in min/config.php
1. Reload the Minify URL
Hopefully you'll see the error appear:
```
Minify: Something bad happened!
```
# Javascript/CSS Problems
When Javascript errors occur, or URIs in CSS files are incorrectly rewritten, enable "debug mode" to ease debugging combined files:
1. Set `$min_allowDebugFlag = 'true'` in min/config.php
1. Append `&debug` to the Minify URI. E.g. `/min/?f=script1.js,script2.js&debug` (or use the bookmarklet provided by /min/builder/)
In "debug mode":
* comments are inserted into the output showing you line numbers in the original file(s)
* no minification is performed
* In CSS, URI rewriting _is_ performed
* In CSS, a leading comment shows how URIs were rewritten.
Example: a combination of two Javascript files in debug mode
```
/* firstFile.js */
/* 1 */ (function () {
/* 2 */ if (window.foo) {
...
/* 11 */ })();
;
/* secondFile.js */
/* 1 */ var Foo = window.Foo || {};
/* 2 */
...
```
Example: Top of CSS output in debug mode
```
docRoot : M:\home\foo\www
currentDir : M:\home\foo\www\css
file-relative URI : typography.css
path prepended : M:\home\foo\www\css\typography.css
docroot stripped : \css\typography.css
traversals removed : /css/typography.css
file-relative URI : ../images/bg.jpg
path prepended : M:\home\foo\www\css\..\images\bg.jpg
docroot stripped : \css\..\images\bg.jpg
traversals removed : /images/bg.jpg
```
### Tips for handling Javascript errors
* Use the latest version (2.1.4 beta as of Dec 2010)
* Try [debug mode](#Javascript/CSS_Problems.md) to make the combined file more readable (and error locations findable)
* Find out if other browsers have the same error
* For pre-minified files, make the filenames end in `.min.js` or `-min.js`, which will prevent Minify from altering them
* Test your scripts in [JSLint](http://www.jslint.com/).
## See Also
* CommonProblems

79
docs/FAQ.wiki.md Normal file
View File

@@ -0,0 +1,79 @@
## Why do the CSS & HTML minifiers add so many line breaks?
TL;DR: Ignore them. They don't add to the output size and if you absolutely want all content on one line you will have to use another tool.
It's [rumored](https://github.com/yui/yuicompressor/blob/master/doc/README#L43) that some source control tools and old browsers don't like very long lines. Compressed files with shorter lines are also easier to diff.
Since both Minify classes are regex-based, it would be very difficult/error-prone to count characters then try to re-establish context to add line breaks. Instead, both classes trade 1 space for 1 line break (`\n`) wherever possible, adding breaks but without adding bytes.
If you can think of another safe & efficient way to limit lines in these two tools without adding bytes, please submit a patch, but this is not something anyone should be worrying about.
## Minify doesn't compress as much as product XYZ. Why not?
Out of the box, Minify uses algorithms available in PHP, which frankly aren't as solid as competing products, but I consider them _good enough_. At this point I don't have time to work on further tweaking, so if you must have _perfect_ minification you can explore the CookBook to plug in other minifiers. If you'd like to propose specific tweaks in the algorithm (and don't have a patch), please propose these in the Google Group, not the issue tracker.
## How fast is it?
With Minify you will ideally serve _fewer_ requests, but Minify can be slower than your HTTPd serving flat files. If you have a high-traffic site with hundreds of simultaneous requests from new users, you should probably:
* Use the [APC/Memcache adapters](CookBook.md).
* Revision your Minify URIs (so far-off Expires headers will be sent). One way to do this is using [predefined groups](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/README.txt#69) and the [Minify\_groupUri()](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/utils.php#13) utility function.
* Place your HTTPd behind a [reverse proxy](http://www.squid-cache.org/Intro/why.dyn) to cache the Minify URLs.
* Benchmark Minify on your development server before rolling out to production.
### Will it get faster?
Ideally, but a couple [other goals](ProjectGoals.md) come first. For Apache users we're designing a feature to enable [minified and pre-encoded files to be served directly from the HTTPd](http://mrclay.org/index.php/2008/05/25/apache-http-encoding-negotiation-notes/). Requests will not execute PHP at all and be blazingly fast (for varying definitions of "blazingly").
## How does it compare with other services?
Yahoo's [Combo Handler](http://yuiblog.com/blog/2008/07/16/combohandler/) and Google's [AJAX Libraries API](http://code.google.com/apis/ajaxlibs/) both serve content from their heavy-duty [CDN](http://en.wikipedia.org/wiki/Content_Delivery_Network)s and _potentially_ increase the chance that your visitor will already have a file in her browser cache. Neither service serves custom content that you provide. You may wish to use these services to serve popular libraries and Minify to serve your code.
## Is this where I get support for...
If you get a link to this page in response to a request for help, please make sure that you're using the software downloaded from [this project](http://code.google.com/p/minify/) (or [on github](https://github.com/mrclay/minify)), and have followed the [directions](UserGuide.md).
There are many projects with "minify" in the title/description but don't have anything to do with this project, or which many only use a few [components](ComponentClasses.md) from this project.
Although you may be able to get support for usage of the components, the [Google Group](http://groups.google.com/group/minify) members/project owners may not be able to offer any helpful advice with unrelated projects.
## Does it support gzip compression?
Yes. Based on the browser's Accept-Encoding header, Minify will serve content encoded with deflate or gzip.
## Does it work with PHP opcode caches like APC and eAccelerator?
Of course, and you can also use [APC for content caching](CookBook.md).
## Can it minify remote files/the output of dynamic scripts?
[Yes](http://code.google.com/p/minify/wiki/CustomSource#Non-File_Sources), but it's not a straightforward setup, and probably best avoided.
## Is there a minifier for HTML?
Yes, but only in the form of a PHP class: [Minify\_HTML](http://code.google.com/p/minify/source/browse/min/lib/Minify/HTML.php).
It also can accept callbacks to minify embedded STYLE and SCRIPT elements.
Since Minify\_HTML is not fast, there's no _easy way_ to integrate it into dynamic pages, and you'll have to search the archives for ideas of how to use it. One opportunity would be when storing HTML (assuming writes are infrequent); e.g., in a DB keep one copy for editing and one minified for serving.
Minify is not suited for _serving_ HTML pages on a site, though it can be done for small numbers of static pages. Look the the [Page controller](http://code.google.com/p/minify/source/browse/min/lib/Minify/Controller/Page.php).
## How does it ensure that the client can't request files it shouldn't have access to?
In 2.1, by default, Minify allows files to be specified using the URI, or using pre-configured sets of files. With URI-specified files, Minify is [very careful](Security.md) to serve only JS/CSS files that are already public on your server, but if you hide public directories--with .htaccess, e.g.--Minify can't know that. Obvious Tip: don't put sensitive info in JS/CSS files inside DOC\_ROOT :)
An included option can disable URI-specified files so Minify will serve only the pre-configured file sets.
## Is it used in production by any large-scale websites?
I'd love to know. 2.1.1 had 54K downloads and I know the library is powering several [plugins](http://mrclay.org/index.php/2009/01/10/minify-getting-out-there/) these days, at least 3 for WordPress.
## Can I use it with my commercial website or product?
Yes. Minify is distributed under the [New BSD License](http://www.opensource.org/licenses/bsd-license.php), which means that you're free to use, modify, and redistribute Minify or derivative works thereof, even for commercial purposes, as long as you comply with a few simple requirements. See the [LICENSE.txt](http://code.google.com/p/minify/source/browse/LICENSE.txt) file for details.
## How can I tell if my server cache is working?
The easiest way is to place a Minify URL directly in your browser's address bar and refresh (F5), which should override the client-side caching that Minify specifies and force Minify to send you a complete response. With cache working, this response should take 100ms or so. Without cache, multiple seconds. (You can get accurate response times using an HTTP inspector like Firebug.)
If you have file access to the server you can check your cache path directly for filenames beginning with "minify

14
docs/MinApp.wiki.md Normal file
View File

@@ -0,0 +1,14 @@
"Min" is the application included in Minify that handles requests to `http://example.com/min/` and responds with compressed/combined content.
When the documentation refers to "Minify" it usually means this application, but sometimes refers to the ComponentClasses.
User-configurable files:
* `/min/config.php`: general configuration
* `/min/groupsConfig.php`: configuration of pre-defined groups of files
Other files of interest:
* `/min/.htaccess`: rewrites URLs for the front controller
* `/min/index.php`: front controller
* `/min/lib/Minify/Controller/MinApp.php`: determines which files are combined based on `$_GET`, sets some default options

View File

@@ -0,0 +1,19 @@
# Unit Testing
0. If you haven't already, install Minify using the UserGuide.
1. Copy the "min\_unit\_tests" directory directly into your DOCUMENT\_ROOT.
2. Browse to http://example.com/min_unit_tests/test_all.php
You should see a list of "PASS"es. You can run the individual test PHP files in http://example.com/min_unit_tests/ for more verbose output.
## Common Problems
### !WARN: environment : Local HTTP request failed. Testing cannot continue.
[test\_environment.php](http://code.google.com/p/minify/source/browse/min_unit_tests/test_environment.php) makes a few local HTTP requests to sniff for `zlib.output_compression` and other auto-encoding behavior, which may break Minify's output. This warning will appear if `allow_url_fopen` is disabled in php.ini, but **does not** necessarily mean there is a problem.
If Minify seems to work fine, ignore the warning. If Minify produces garbled output, enable `allow_url_fopen` in php.ini and re-run the tests. The tests may be able to tell you if PHP or your server is automatically encoding output.
Unless you need it in other scripts, disable `allow_url_fopen` once the issue is resolved. Minify does not need it.

73
docs/UriRewriting.wiki.md Normal file
View File

@@ -0,0 +1,73 @@
## Default operation
Minify uses an algorithm to rewrite relative URIs in CSS output to root-relative URIs so that each link points to the same location it did in its original file.
Say your style sheet `http://example.org/theme/fashion/style.css` contains:
```
body { background: url(bg.jpg); }
```
When Minify serves this content (from `http://example.org/min/f=theme/fashion/style.css` or `http://example.org/min/g=css`) it re-writes the URI so the image is correctly linked:
```
body{background:url(/theme/fashion/bg.jpg)}
```
You can see the steps used to rewrite your URIs by enabling [debug mode](Debugging.md).
## Disable Rewriting
You can disable the automatic rewriting by setting this in min/config.php:
```
$min_serveOptions['rewriteCssUris'] = false;
```
## Manual Rewriting
You can manually rewrite relative URIs in CSS in a couple ways. The simplest is to prepend a string to each relative URI:
```
$min_serveOptions['rewriteCssUris'] = false;
$min_serveOptions['minifierOptions']['text/css']['prependRelativePath'] = '/css/';
```
Or you can run the minified output through a custom [post-processor](CookBook#Processing_Output_After_Minification.md) function.
## Document Root Confusion
Out-of-the-box, Minify gets confused when `min` is placed in a subdirectory of the real document root. There's now a [simple workaround](AlternateFileLayouts.md) for this, making `min` more portable.
## Aliases / Symlinks / Virtual Directories
Whether you use [aliases](http://httpd.apache.org/docs/2.2/mod/mod_alias.html), [symlinks](http://en.wikipedia.org/wiki/Symbolic_link), or [virtual directories](http://msdn.microsoft.com/en-us/library/zwk103ab.aspx), if you make content outside of the DOC\_ROOT available at public URLs, Minify may need manual configuration of the `$min_symlinks` option to rewrite some URIs correctly. Consider this scenario, where `http://example.org/static/style.css` will serve `/etc/static_content/style.css`:
| document root | `/var/www` |
|:------------------|:--------------------------------------------|
| Apache mod\_alias | `Alias /static /etc/static_content` |
| ...or symlink | `ln -s /etc/static_content /var/www/static` |
In `/min/config.php` you'll need the following:
```
// map URL path to file path
$min_symlinks = array(
'//static' => '/etc/static_content'
);
```
This lets Minify know during the rewriting process that an internal file path starting with `/etc/static_content` should be rewritten as a public URI beginning with `/static`.
If your alias target directory is outside of DOC\_ROOT, you'll also need to explicitly allow Minify to serve files from it:
```
$min_serveOptions['minApp']['allowDirs'] = array(
'//', // allow from the normal DOC_ROOT
'/etc/static_content' // allow from our alias target
);
```
### What's my document root?
You can enable the script `min/server-info.php` and open http://example.org/min/server-info.php to find useful `$_SERVER` values. People in the [Google Group](https://groups.google.com/forum/#!forum/minify) might need these to help you.
## It's still not working
1. Make sure you have the [latest version](http://code.google.com/p/minify/downloads/list).
1. Enable [debug mode](Debugging.md), which will show you the URI transformation process.
1. Check that `$_SERVER['DOCUMENT_ROOT']` is a real directory path. If not, URI rewriting will fail. If you cannot fix this in httpd.conf, etc., use the [configuration option](http://code.google.com/p/minify/source/browse/min/config.php?r=292#47).
1. Paste your [debug mode](Debugging.md) comment block into a new post on the [minify mailing list](http://groups.google.com/group/minify).

48
docs/UserGuide.wiki.md Normal file
View File

@@ -0,0 +1,48 @@
If this page doesn't help, please post a question on our [Google group](http://groups.google.com/group/minify).
# Installing Minify for the first time
1. Clone the [minify git repository](https://github.com/mrclay/minify).
1. The distribution contains a folder "min". Copy this into your DOCUMENT\_ROOT so it is a **direct child** of DOCUMENT\_ROOT (e.g. `http://example.com/min/`). Document roots are usually named `htdocs`, `public_html`, or `www`.
1. Test it can serve content: http://example.com/min/?f=min/quick-test.js and http://example.com/min/?f=min/quick-test.css
1. If you want to use the BuilderApp, you must enable it in `min/config.php`.
Note: The BuilderApp will not function properly in subdirectories, but it's not necessary for Minify's functionality.
Done!
(Optional) See TestingMinify if you'd like to run unit tests.
### Hosting on Lighttpd
Minify comes with Apache mod\_rewrite rules, but this does the same for Lighttpd:
```
url.rewrite-once = ( "^/min/([a-z]=.*)" => "/min/index.php?$1" )
```
# Usage
Enable the BuilderApp via your [config file](https://github.com/mrclay/minify/blob/master/min/config.php#L10). The default password is "admin", but even if no password is used there's very little server information disclosed.
Browse to http://example.com/min/
The Minify URI Builder will help you create URIs you can use to minify existing files on your site. You can see screenshots and get a feel for this process from this [walkthrough on mrclay.org](http://mrclay.org/index.php/2008/09/19/minify-21-on-mrclayorg/)
More info here: https://github.com/mrclay/minify/blob/master/MIN.txt
You may want to disable the BuilderApp when not in use.
# Configuration
[min/config.php](https://github.com/mrclay/minify/blob/master/min/config.php) holds general config options.
[min/groupsConfig.php](https://github.com/mrclay/minify/blob/master/min/groupsConfig.php) holds preset groups of files to minify. (The builder application can help with this).
CookBook shows how to customize settings between production/development environments, and between groups.
CustomSource shows how to set some file/source-specific options, or serve content from a PHP script or URL.
# Problems?
See [CommonProblems](https://github.com/mrclay/minify/blob/master/docs/CommonProblems.wiki.md) and [Debugging](https://github.com/mrclay/minify/blob/master/docs/Debugging.wiki.md). You might also try [TestingMinify](https://github.com/mrclay/minify/blob/master/docs/TestingMinify.wiki.md) (running `test_environment.php` in particular).

92
docs/old/History.wiki.md Normal file
View File

@@ -0,0 +1,92 @@
## Version 2.1.5 (2012-03-10)
* Removed XSS vulnerability
* Disabled builder by default
* command line tools to minify and rewrite URIs in CSS
* upgrade (optional) JSMin+ library
* more efficient JS minification when using CC/YUIC
* Closure Compiler uses cURL when allow\_url\_fopen is off
* Missing file notices when using groups
## Version 2.1.4b (2010-07-10)
* Option to minify JS with Closure Compiler API w/ JSMin failover
* Cookie/bookmarklet-based debug mode. No HTML editing!
* Allows 1 file to be missing w/o complete failure
* Combine multiple groups and files in single URI
* More useful HTML helpers for writing versioned URIs
* More detailed error logging, including minifier exceptions
* Builder offers more helpful messages/PHP environment warnings
* Bypass minification based on filename pattern. e.g. foo.min.js / foo-min.css
* JSMin won't choke on common Closure compiler syntaxes (`i+ ++j`)
* Better caching in IE6
* Cache ids are influenced by group/file names
* Debug mode for Javascript doesn't break on common XPath strings (Prototype 1.6)
* Removed annoying maxFiles limit
* mbstring.func\_overload usage is safer
## Version 2.1.3 (2009-06-30)
* CSS fixes
* A few URI rewriting bugs fixed
* comment/whitespace removal no longer breaks some values
* IE6 [pseudo-element selector bug](http://www.crankygeek.com/ie6pebug/) no longer triggered
* HTTP fixes
* Proper Expires handling in webkit (dropped "must-revalidate", which triggered a [webkit bug](http://mrclay.org/index.php/2009/02/24/safari-4-beta-cache-controlmust-revalidate-bug/))
* ETag generation now valid ([must be unique when gzipped](https://issues.apache.org/bugzilla/show_bug.cgi?id=39727))
* Vary header always sent when Accept-Encoding is sniffed
* Dropped deflate encoding, since browser and proxy support [could be buggy](http://stackoverflow.com/questions/883841/).
* File cache now works w/o setting `$min_cachePath`
* No more 5.3 deprecation warnings: `split()` removed
* API: Can set contentType Minify\_Source objects (fixes an annoying [caveat](http://groups.google.com/group/minify/msg/8446d32ee99a4961))
* [Resolved Issue list](http://code.google.com/p/minify/issues/list?can=1&q=label%3ARelease-2.1.2%20status%3AVerified)
## Version 2.1.2 (2009-03-04)
* Javascript fixes
* Debug mode no longer confused by `*/*` in strings/RegExps (jQuery)
* quote characters inside RegExp literals no longer cause exception
* files ending in single-line comments no longer cause code loss
* CSS: data: URLs no longer mangled
* Optional error logging to Firefox's FirePHP extension
* Unit tests to check for common DOCUMENT\_ROOT problems
* DOCUMENT\_ROOT no longer overwritten on IIS servers
* Builder app doesn't fail on systems without gzdeflate()
* APC caching class included
## Version 2.1.1 (2008-10-19)
* Bug fix release
* Detection and workarounds for zlib.output\_compression and non-PHP encoding modules
* Zlib not required (mod\_rewrite, et.al., can still be used for encoding)
* HTML : More IE conditional comments preserved
* Minify\_groupUri() utility fixed
## Version 2.1.0 (2008-09-18)
* "min" default application for quick deployment
* Minify URI Builder app & bookmarklet for quickly creating minify URIs
* Relative URIs in CSS file are fixed automatically by default
* "debug" mode for revealing original line #s in combined files
* Better IIS support
* Improved minifier classes:
* JS: preserves IE conditional comments
* CSS: smaller output, preserves more hacks and valid CSS syntax, shorter line lengths, other bug fixes
* HTML: smaller output, shorter line lengths, other bug fixes
* Default Cache-Control: max-age of 30 minutes
* Conditional GETs supported even when max-age sent
* Experimental memcache cache class (default is files)
* Minify\_Cache\_File has flock()s (by default)
* Workaround for Windows mtime reporting bug
## Version 2.0.0 (2008-05-22)
* Complete code overhaul. Minify is now a PEAR-style class and toolkit for building customized minifying file servers.
* Content-Encoding: deflate/gzip/compress, based on request headers
* Expanded CSS and HTML minifiers with test cases
* Easily plug-in 3rd-party minifiers (like Packer)
* Plug-able front end controller allows changing the way files are chosen
* Compression & encoding modules lazy-loaded as needed (304 responses use minimal code)
* Separate utility classes for HTTP encoding and cache control
## Version 1.0.1 (2007-05-05)
* Fixed various problems resolving pathnames when hosted on an NFS mount.
* Fixed 'undefined constant' notice.
* Replaced old JSMin library with a much faster custom implementation.
## Version 1.0.0 (2007-05-02)
* First release.

View File

@@ -0,0 +1,31 @@
### Browsers, trust your cache
In all responses a `Cache-Control` header is sent, telling the browser it doesn't need to "check in" with the server for some period of time. The ideal request is the one that never leaves the browser!
### Convert a request to source objects
When the browser makes a request like `/min/g=css`, Apache rewrites this to `/min/index.php?g=css`, which calls Minify's front controller.
A separate controller then uses the querystring to establish a "sources" array, specifying exactly which objects (usually files) are to be included in the final output.
### Try browser cache
Minify finds the latest modification time of all the source objects (`filemtime` for files, so if you use a tool that doesn't update this, you might need to `touch` your modified files).
If the browser has sent an `If-Modified-Since` header, and it's valid (the given date is older than the most recent source), then a 304 header is returned, execution stops, and the browser uses its cache copy.
### Try server cache
Minify generates a unique cache ID for the particular set of sources and their options. This is used to maintain a cache (file usually) of the final output.
If the cache is "valid" (younger than the most recently modified source), then its content is sent along with a `Last-Modified` header with the most recent source's modification time, and execution stops.
### Minification has to be done
If any source is younger than the cache, the cache must be rebuilt from the minification process (slow, but infrequently done):
Minify processes each source with a "minifier" function (determined by the content type of the sources and source-specific options), combines them to a single string, saves this to the cache object, then serves it with the `Last-Modified` header as sbove.
#### Content encoding
Minify actually stores a gzipped version of each output in a second cache object. If the browser supports it, Minify streams the pre-compressed content straight from cache (disk usually) to the browser.

View File

@@ -0,0 +1,63 @@
# HTTP Caching in Minify
## Conditional GET
Minify sends all files with Last-Modified and ETag headers. If the browser requests a file again it will send along If-Modified-Since and If-None-Match headers. Minify checks these headers and, if the browser has the latest version, sends back only a slim "304 Not Modified" response (no content), telling the browser to use its cached file.
## Expires Header
Minify also sends Expires and Cache-Control: max-age headers, indicating that the file should be considered valid for a period of time. In future page views the browser will not re-request the file (unless the user refreshes), and instead will use the cached version.
By default, Minify sends an Expires header for 1800 seconds (30 minutes) into the future (configurable via `$min_serveOptions['maxAge']`). This means your file changes may not be seen by users immediately after you make them. If your changes must be seen immediately, you should reduce max-age to 0, but note you will not get as much benefit, as browsers will still have to send requests **every time**.
## Far-off Expires
When pre-set groups are used and a number is appended to the minify URI (e.g. `/min/g=css&456`), then Minify sends an Expires date of 1 year in the future. This is great for caching, but places responsibility on your HTML pages. They _must_ change the number whenever a JS/CSS source file is updated, or the browser will not know to re-request the file. If you're generating your page with PHP, the [Minify\_groupUri](http://code.google.com/p/minify/source/browse/min/utils.php?r=222#11) utility function can make this easier to manage.
# Using `HTTP_ConditionalGet` in other projects
Minify uses the PHP class [HTTP\_ConditionalGet](http://code.google.com/p/minify/source/browse/lib/HTTP/ConditionalGet.php) to implement the conditional GET model. To use this in your own project you'll need the last modification time of your content (for a file, use [filemtime()](http://www.php.net/filemtime)), or a short hash digest of the content (something that changes when the content changes). You'll also want to consider if the content can be stored in public caches, or should only be stored in the user's browser.
## When the last modification time is known
In this example we implement conditional GET for a mostly-static PHP script. The browser needs to redownload the content only when the file is updated.
```
// top of file
$cg = new HTTP_ConditionalGet(array(
'isPublic' => true,
'lastModifiedTime' => filemtime(__FILE__)
));
$cg->sendHeaders();
if ($cg->cacheIsValid) { // 304 already sent
exit();
}
// rest of script
```
For the first request the browser's cache won't be valid, so the full script will execute, sending the full content. On the next, the cache will be valid and the sendHeaders() method will have already set the 304 header, so the script can be halted.
There's also a shortcut static method for this:
```
HTTP_ConditionalGet::check(filemtime(__FILE__), true); // exits if client has cache
// rest of script
```
## When last modification time isn't known
Let's say you have an HTML page in a database, but no modification time. To reduce DB requests, you cache this content in a file/memory, but you'd also like to reduce bandwidth. In this case, what you can do is also cache a hash of the page along with the content. Now you can do this:
```
$cache = getCache();
$cg = new HTTP_ConditionalGet(array(
'isPublic' => true,
'contentHash' => $cache['hash']
));
$cg->sendHeaders();
if ($cg->cacheIsValid) { // 304 already sent
exit();
}
echo $cache['page'];
```
Although Last-Modified cannot be set, ETag will serve the same purposes in most browsers, allowing the conditional GET.

View File

@@ -0,0 +1,13 @@
## 1. Be easy to implement
A PHP user should be able to quickly start serving JS/CSS much more optimally.
## 2. Be extensible/versatile
The user should be able to work Minify into an environment without modifying Minify's source. Right now this should be easy on the controller side, but the Minify class is static, which is fast, but may hinder extensibility.
## 3. Be fast as possible
With the release of 2.0.2 Minify will be much faster, outperforming even Apache's mod\_deflate (which apparently doesn't cache the encoded content like mod\_gzip does).
Since testing has shown that [pre-encoding files and using type-maps on Apache is blazingly fast](http://mrclay.org/index.php/2008/06/03/pre-encoding-vs-mod_deflate/), Minify should work towards maintaining builds of pre-encoded files and letting Apache serve them. It's not clear if there's something similar to type-maps on lighttpd, but it's worth looking into.

15
docs/old/Security.wiki.md Normal file
View File

@@ -0,0 +1,15 @@
This was quickly converted from an e-mail, please consider it "temporary".
## Each file specified by `$_GET['f']` must:
* Have the [same extension, either "css" or "js"](http://code.google.com/p/minify/source/browse/tags/release_2.1.1/min/lib/Minify/Controller/MinApp.php#66),
* Exist, and...
* Have a [realpath() within a whitelist of subdirectories](http://code.google.com/p/minify/source/browse/tags/release_2.1.1/min/lib/Minify/Controller/Base.php#122).
The default whitelist contains only DOCUMENT\_ROOT, but can be [specified](http://code.google.com/p/minify/source/browse/tags/release_2.1.1/min/config.php#57).
Then, a few more steps just to be paranoid:
* If a base was given by `$_GET['b']`, [it can't have ".."](http://code.google.com/p/minify/source/browse/tags/release_2.1.1/min/lib/Minify/Controller/MinApp.php#84).
* `$_GET['f']` [must not contain "//", "\", or "./"](http://code.google.com/p/minify/source/browse/tags/release_2.1.1/min/lib/Minify/Controller/MinApp.php#64).
* There can be [no duplicates](http://code.google.com/p/minify/source/browse/tags/release_2.1.1/min/lib/Minify/Controller/MinApp.php#77) and only a [limited number of files](http://code.google.com/p/minify/source/browse/tags/release_2.1.1/min/config.php#73) can be specified.

View File

@@ -0,0 +1,35 @@
# Minify 2
[Minify 2.0.0](http://code.google.com/p/minify/source/browse/tags/release_2.0.0/) was released May 22, 2008 and represents an architectural redesign of Minify's code and its usage. 2.0 is built as a library of classes allowing users to easily build customized minified-file servers; or add minification, HTTP encoding, or [conditional GET](http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers) to existing projects.
The release includes 3 [example sites](http://code.google.com/p/minify/source/browse/tags/release_2.0.0/web/examples) to demostrate usage and [unit tests](http://code.google.com/p/minify/source/browse/tags/release_2.0.0/web/test/) you can run on your system.
## Documentation
Each PHP file is documented, but, for now, the [README file](http://code.google.com/p/minify/source/browse/tags/release_2.0.0/README) is the best reference for getting started with the library. This [blog post](http://mrclay.org/index.php/2008/03/27/minifying-javascript-and-css-on-mrclayorg/) may also give you some ideas.
The best place for questions is the [minify Google group](http://groups.google.com/group/minify).
### Included HTTP Classes
The two HTTP utility classes, [HTTP\_ConditionalGet](http://code.google.com/p/minify/source/browse/lib/HTTP/ConditionalGet.php) and [HTTP\_Encoder](http://code.google.com/p/minify/source/browse/lib/HTTP/Encoder.php), are already fairly well-tested and include a set of test pages to see how they work. On the [Florida Automated Weather Network](http://fawn.ifas.ufl.edu/) site, these are used especially in scripts that serve data to our Flash components.
Here's an example of using both to conditionally serve a text file gzipped:
```
$cg = new HTTP_ConditionalGet(array(
'lastModifiedTime' => filemtime($filepath)
,'isPublic' => true
));
$cg->sendHeaders();
if ($cg->cacheIsValid) {
// client cache was valid, no content needed
exit();
}
require 'HTTP/Encoder.php';
$he = new HTTP_Encoder(array(
'content' => file_get_contents($filepath)
,'type' => 'text/plain'
));
$he->encode();
$he->sendAll();
```

4
min/builder/.htaccess Normal file
View File

@@ -0,0 +1,4 @@
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
</IfModule>

View File

@@ -26,8 +26,8 @@ var MUB = {
*/
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>';
' <button class="btn btn-danger btn-sm" title="Remove">x</button> <button class="btn btn-default btn-sm" title="Include Earlier">&uarr;</button>' +
' <button class="btn btn-default btn-sm" title="Include Later">&darr;</button> <span></span></li>';
},
/**
* Add new empty source LI and attach handlers to buttons
@@ -36,13 +36,13 @@ var MUB = {
$('#sources').append(MUB.newLi());
var li = $('#li' + MUB._uid)[0];
$('button[title=Remove]', li).click(function () {
$('#results').hide();
$('#results').addClass('hide');
var hadValue = !!$('input', li)[0].value;
$(li).remove();
});
$('button[title$=Earlier]', li).click(function () {
$(li).prev('li').find('input').each(function () {
$('#results').hide();
$('#results').addClass('hide');
// this = previous li input
var tmp = this.value;
this.value = $('input', li).val();
@@ -52,7 +52,7 @@ var MUB = {
});
$('button[title$=Later]', li).click(function () {
$(li).next('li').find('input').each(function () {
$('#results').hide();
$('#results').addClass('hide');
// this = next li input
var tmp = this.value;
this.value = $('input', li).val();
@@ -71,15 +71,15 @@ var MUB = {
return;
var li = this;
$('span', this).html('');
var url = 'http://' + location.host + '/' +
var url = location.protocol + '//' + location.host + '/' +
$('input', this)[0].value.replace(/^\//, '');
$.ajax({
url : url,
complete : function (xhr, stat) {
if ('success' === stat)
$('span', li).html('&#x2713;');
$('span', li).html('<a href="#" class="btn btn-success btn-sm disabled">&#x2713;</a>');
else {
$('span', li).html('<button><b>404! </b> recheck</button>')
$('span', li).html('<button class="btn btn-warning btn-sm"><b>404! </b> recheck</button>')
.find('button').click(function () {
MUB.liUpdateTestLink.call(li);
});
@@ -184,16 +184,16 @@ var MUB = {
markup = '<link type="text/css" rel="stylesheet" href="' + uriH + '" />';
}
$('#uriHtml').val(markup);
$('#results').show();
$('#results').removeClass('hide');
},
/**
* Handler for the "Add file +" button
*/
addButtonClick : function () {
$('#results').hide();
$('#results').addClass('hide');
MUB.addLi();
MUB.updateAllTestLinks();
$('#update').show().click(MUB.update);
$('#update').removeClass('hide').click(MUB.update);
$('#sources li:last input')[0].focus();
},
/**
@@ -201,7 +201,7 @@ var MUB = {
*/
init : function () {
$('#jsDidntLoad').remove();
$('#app').show();
$('#app').removeClass('hide');
$('#sources').html('');
$('#add button').click(MUB.addButtonClick);
// make easier to copy text out of
@@ -213,7 +213,7 @@ var MUB = {
$('a.ext').attr({target:'_blank'});
if (location.hash) {
// make links out of URIs from bookmarklet
$('#getBm').hide();
$('#getBm').addClass('hide');
var i = 0, found = location.hash.substr(1).split(','), l = found.length;
$('#bmUris').html('<p><strong>Found by bookmarklet:</strong> /</p>');
var $p = $('#bmUris p');
@@ -227,7 +227,7 @@ var MUB = {
MUB.addButtonClick();
$('#sources li:last input').val(this.innerHTML);
MUB.liUpdateTestLink.call($('#sources li:last')[0]);
$('#results').hide();
$('#results').addClass('hide');
return false;
}).attr({title:'Add file +'});
} else {
@@ -257,4 +257,4 @@ var MUB = {
MUB.checkRewrite();
}
};
$(MUB.init);
$(MUB.init);

View File

@@ -24,26 +24,19 @@ if (0 === strpos($_SERVER["SERVER_SOFTWARE"], 'Apache/')
require dirname(__FILE__) . '/../config.php';
require "$min_libPath/Minify/Loader.php";
Minify_Loader::register();
if (! $min_enableBuilder) {
header('Location: /');
exit();
header('Content-Type: text/plain');
die('This application is not enabled. See http://code.google.com/p/minify/wiki/BuilderApp');
}
$setIncludeSuccess = set_include_path(dirname(__FILE__) . '/../lib' . PATH_SEPARATOR . get_include_path());
// we do it this way because we want the builder to work after the user corrects
// include_path. (set_include_path returning FALSE is OK).
try {
require_once 'Minify/Cache/File.php';
} catch (Exception $e) {
if (! $setIncludeSuccess) {
echo "Minify: set_include_path() failed. You may need to set your include_path "
."outside of PHP code, e.g., in php.ini.";
} else {
echo $e->getMessage();
}
exit();
if (isset($min_builderPassword)
&& is_string($min_builderPassword)
&& $min_builderPassword !== '') {
DooDigestAuth::http_auth('Minify Builder', array('admin' => $min_builderPassword));
}
require 'Minify.php';
$cachePathCode = '';
if (! isset($min_cachePath) && ! function_exists('sys_get_temp_dir')) {
@@ -56,6 +49,7 @@ ob_start();
<!DOCTYPE html>
<title>Minify URI Builder</title>
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<style>
body {margin:1em 60px;}
h1, h2, h3 {margin-left:-25px; position:relative;}
@@ -118,7 +112,7 @@ and click [Update].</p>
<div id=bmUris></div>
<p><button id=update class=hide>Update</button></p>
<p><button class="btn btn-primary" id=update class=hide>Update</button></p>
<div id=results class=hide>

View File

@@ -1,4 +1,4 @@
<?php
<?php
/**
* AJAX checks for zlib.output_compression
*
@@ -11,7 +11,7 @@ $_oc = ini_get('zlib.output_compression');
require dirname(__FILE__) . '/../config.php';
if (! $min_enableBuilder) {
header('Location: /');
exit();
exit;
}
if (isset($_GET['hello'])) {
@@ -26,8 +26,8 @@ if (isset($_GET['hello'])) {
'content' => 'World!'
,'method' => 'deflate'
));
$he->encode();
$he->sendAll();
$he->encode();
$he->sendAll();
} else {
// echo status "0" or "1"

View File

@@ -1,5 +1,5 @@
<?php
exit();
exit;
/* currently unused.
// capture PHP's default setting (may get overridden in config
@@ -8,7 +8,7 @@ $_oc = ini_get('zlib.output_compression');
// allow access only if builder is enabled
require dirname(__FILE__) . '/../config.php';
if (! $min_enableBuilder) {
exit();
exit;
}
if (isset($_GET['oc'])) {

10
min/config-test.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
/**
* Additional configuration applied when the variable "test" is added to the querystring.
*
* To test config options, place them in this file and add "&test" to your Minify URL.
* Note that if this is on a public server, anyone can execute your test.
*
* @package Minify
*/

View File

@@ -9,8 +9,21 @@
/**
* Allow use of the Minify URI Builder app. Only set this to true while you need it.
**/
$min_enableBuilder = true;
*/
$min_enableBuilder = false;
/**
* Concatenate but do not minify the files. This can be used for testing.
*/
$min_concatOnly = false;
/**
* If non-empty, the Builder will be protected with HTTP Digest auth.
* The username is "admin".
*/
$min_builderPassword = 'admin';
/**
@@ -46,6 +59,7 @@ $min_allowDebugFlag = false;
//$min_cachePath = 'c:\\WINDOWS\\Temp';
//$min_cachePath = '/tmp';
//$min_cachePath = preg_replace('/^\\d+;/', '', session_save_path());
/**
* To use APC/Memcache/ZendPlatform for cache storage, require the class and
* set $min_cachePath to an instance. Example below:
@@ -100,15 +114,16 @@ $min_serveOptions['maxAge'] = 1800;
/**
* To use Google's Closure Compiler API (falling back to JSMin on failure),
* uncomment the following lines:
* To use CSSmin (Túbal Martín's port of the YUI CSS compressor), uncomment the following line:
*/
/*function closureCompiler($js) {
require_once 'Minify/JS/ClosureCompiler.php';
return Minify_JS_ClosureCompiler::minify($js);
}
$min_serveOptions['minifiers']['application/x-javascript'] = 'closureCompiler';
//*/
//$min_serveOptions['minifiers']['text/css'] = array('Minify_CSSmin', 'minify');
/**
* To use Google's Closure Compiler API to minify Javascript (falling back to JSMin
* on failure), uncomment the following line:
*/
//$min_serveOptions['minifiers']['application/x-javascript'] = array('Minify_JS_ClosureCompiler', 'minify');
/**

View File

@@ -9,13 +9,27 @@
define('MINIFY_MIN_DIR', dirname(__FILE__));
// set config path defaults
$min_configPaths = array(
'base' => MINIFY_MIN_DIR . '/config.php',
'test' => MINIFY_MIN_DIR . '/config-test.php',
'groups' => MINIFY_MIN_DIR . '/groupsConfig.php'
);
// check for custom config paths
if (!empty($min_customConfigPaths) && is_array($min_customConfigPaths)) {
$min_configPaths = array_merge($min_configPaths, $min_customConfigPaths);
}
// load config
require MINIFY_MIN_DIR . '/config.php';
require $min_configPaths['base'];
// setup include path
set_include_path($min_libPath . PATH_SEPARATOR . get_include_path());
if (isset($_GET['test'])) {
include $min_configPaths['test'];
}
require 'Minify.php';
require "$min_libPath/Minify/Loader.php";
Minify_Loader::register();
Minify::$uploaderHoursBehind = $min_uploaderHoursBehind;
Minify::setCache(
@@ -35,40 +49,44 @@ foreach ($min_symlinks as $uri => $target) {
}
if ($min_allowDebugFlag) {
require_once 'Minify/DebugDetector.php';
$min_serveOptions['debug'] = Minify_DebugDetector::shouldDebugRequest($_COOKIE, $_GET, $_SERVER['REQUEST_URI']);
}
if (!empty($min_concatOnly)) {
$min_serveOptions['concatOnly'] = true;
}
if ($min_errorLogger) {
require_once 'Minify/Logger.php';
if (true === $min_errorLogger) {
require_once 'FirePHP.php';
$min_errorLogger = FirePHP::getInstance(true);
}
Minify_Logger::setLogger($min_errorLogger);
}
// check for URI versioning
if (preg_match('/&\\d/', $_SERVER['QUERY_STRING'])) {
if (preg_match('/&\\d/', $_SERVER['QUERY_STRING']) || isset($_GET['v'])) {
$min_serveOptions['maxAge'] = 31536000;
}
// need groups config?
if (isset($_GET['g'])) {
// well need groups config
$min_serveOptions['minApp']['groups'] = (require MINIFY_MIN_DIR . '/groupsConfig.php');
$min_serveOptions['minApp']['groups'] = (require $min_configPaths['groups']);
}
if (isset($_GET['f']) || isset($_GET['g'])) {
// serve!
// serve or redirect
if (isset($_GET['f']) || isset($_GET['g'])) {
if (! isset($min_serveController)) {
require 'Minify/Controller/MinApp.php';
$min_serveController = new Minify_Controller_MinApp();
}
}
Minify::serve($min_serveController, $min_serveOptions);
} elseif ($min_enableBuilder) {
header('Location: builder/');
exit();
exit;
} else {
header("Location: /");
exit();
}
header('Location: /');
exit;
}

777
min/lib/CSSmin.php Normal file
View File

@@ -0,0 +1,777 @@
<?php
/*!
* cssmin.php v2.4.8-4
* Author: Tubal Martin - http://tubalmartin.me/
* Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
*
* This is a PHP port of the CSS minification tool distributed with YUICompressor,
* itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
* Permission is hereby granted to use the PHP version under the same
* conditions as the YUICompressor.
*/
/*!
* YUI Compressor
* http://developer.yahoo.com/yui/compressor/
* Author: Julien Lecomte - http://www.julienlecomte.net/
* Copyright (c) 2013 Yahoo! Inc. All rights reserved.
* The copyrights embodied in the content of this file are licensed
* by Yahoo! Inc. under the BSD (revised) open source license.
*/
class CSSmin
{
const NL = '___YUICSSMIN_PRESERVED_NL___';
const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';
private $comments;
private $preserved_tokens;
private $memory_limit;
private $max_execution_time;
private $pcre_backtrack_limit;
private $pcre_recursion_limit;
private $raise_php_limits;
/**
* @param bool|int $raise_php_limits
* If true, PHP settings will be raised if needed
*/
public function __construct($raise_php_limits = TRUE)
{
// Set suggested PHP limits
$this->memory_limit = 128 * 1048576; // 128MB in bytes
$this->max_execution_time = 60; // 1 min
$this->pcre_backtrack_limit = 1000 * 1000;
$this->pcre_recursion_limit = 500 * 1000;
$this->raise_php_limits = (bool) $raise_php_limits;
}
/**
* Minify a string of CSS
* @param string $css
* @param int|bool $linebreak_pos
* @return string
*/
public function run($css = '', $linebreak_pos = FALSE)
{
if (empty($css)) {
return '';
}
if ($this->raise_php_limits) {
$this->do_raise_php_limits();
}
$this->comments = array();
$this->preserved_tokens = array();
$start_index = 0;
$length = strlen($css);
$css = $this->extract_data_urls($css);
// collect all comment blocks...
while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) {
$end_index = $this->index_of($css, '*/', $start_index + 2);
if ($end_index < 0) {
$end_index = $length;
}
$comment_found = $this->str_slice($css, $start_index + 2, $end_index);
$this->comments[] = $comment_found;
$comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
$css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index);
// Set correct start_index: Fixes issue #2528130
$start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
}
// preserve strings so their content doesn't get accidentally minified
$css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);
// Let's divide css code in chunks of 5.000 chars aprox.
// Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
// of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
// long strings and a (sub)pattern matches a number of chars greater than
// the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
// returning NULL and $css would be empty.
$charset = '';
$charset_regexp = '/(@charset)( [^;]+;)/i';
$css_chunks = array();
$css_chunk_length = 5000; // aprox size, not exact
$start_index = 0;
$i = $css_chunk_length; // save initial iterations
$l = strlen($css);
// if the number of characters is 5000 or less, do not chunk
if ($l <= $css_chunk_length) {
$css_chunks[] = $css;
} else {
// chunk css code securely
while ($i < $l) {
$i += 50; // save iterations
if ($l - $start_index <= $css_chunk_length || $i >= $l) {
$css_chunks[] = $this->str_slice($css, $start_index);
break;
}
if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {
// If there are two ending curly braces }} separated or not by spaces,
// join them in the same chunk (i.e. @media blocks)
$next_chunk = substr($css, $i);
if (preg_match('/^\s*\}/', $next_chunk)) {
$i = $i + $this->index_of($next_chunk, '}') + 1;
}
$css_chunks[] = $this->str_slice($css, $start_index, $i);
$start_index = $i;
}
}
}
// Minify each chunk
for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
$css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos);
// Keep the first @charset at-rule found
if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
$charset = strtolower($matches[1]) . $matches[2];
}
// Delete all @charset at-rules
$css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
}
// Update the first chunk and push the charset to the top of the file.
$css_chunks[0] = $charset . $css_chunks[0];
return implode('', $css_chunks);
}
/**
* Sets the memory limit for this script
* @param int|string $limit
*/
public function set_memory_limit($limit)
{
$this->memory_limit = $this->normalize_int($limit);
}
/**
* Sets the maximum execution time for this script
* @param int|string $seconds
*/
public function set_max_execution_time($seconds)
{
$this->max_execution_time = (int) $seconds;
}
/**
* Sets the PCRE backtrack limit for this script
* @param int $limit
*/
public function set_pcre_backtrack_limit($limit)
{
$this->pcre_backtrack_limit = (int) $limit;
}
/**
* Sets the PCRE recursion limit for this script
* @param int $limit
*/
public function set_pcre_recursion_limit($limit)
{
$this->pcre_recursion_limit = (int) $limit;
}
/**
* Try to configure PHP to use at least the suggested minimum settings
*/
private function do_raise_php_limits()
{
$php_limits = array(
'memory_limit' => $this->memory_limit,
'max_execution_time' => $this->max_execution_time,
'pcre.backtrack_limit' => $this->pcre_backtrack_limit,
'pcre.recursion_limit' => $this->pcre_recursion_limit
);
// If current settings are higher respect them.
foreach ($php_limits as $name => $suggested) {
$current = $this->normalize_int(ini_get($name));
// memory_limit exception: allow -1 for "no memory limit".
if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
ini_set($name, $suggested);
}
}
}
/**
* Does bulk of the minification
* @param string $css
* @param int|bool $linebreak_pos
* @return string
*/
private function minify($css, $linebreak_pos)
{
// strings are safe, now wrestle the comments
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
$token = $this->comments[$i];
$placeholder = '/' . self::COMMENT . $i . '___/';
// ! in the first position of the comment means preserve
// so push to the preserved tokens keeping the !
if (substr($token, 0, 1) === '!') {
$this->preserved_tokens[] = $token;
$token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
$css = preg_replace($placeholder, $token_tring, $css, 1);
// Preserve new lines for /*! important comments
$css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css);
$css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css);
continue;
}
// \ in the last position looks like hack for Mac/IE5
// shorten that to /*\*/ and the next one to /**/
if (substr($token, (strlen($token) - 1), 1) === '\\') {
$this->preserved_tokens[] = '\\';
$css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
$i = $i + 1; // attn: advancing the loop
$this->preserved_tokens[] = '';
$css = preg_replace('/' . self::COMMENT . $i . '___/', self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
continue;
}
// keep empty comments after child selectors (IE7 hack)
// e.g. html >/**/ body
if (strlen($token) === 0) {
$start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1));
if ($start_index > 2) {
if (substr($css, $start_index - 3, 1) === '>') {
$this->preserved_tokens[] = '';
$css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
}
}
}
// in all other cases kill the comment
$css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1);
}
// Normalize all whitespace strings to single spaces. Easier to work with that way.
$css = preg_replace('/\s+/', ' ', $css);
// Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
$css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css);
// Shorten & preserve calculations calc(...) since spaces are important
$css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
// Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
// +1.2em to 1.2em, +.8px to .8px, +2% to 2%
$css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);
// Remove leading zeros from integer and float numbers preceded by : or a white-space
// 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);
// Remove trailing zeros from float numbers preceded by : or a white-space
// -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
// Remove trailing .0 -> -9.0 to -9
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);
// Replace 0 length numbers with 0
$css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);
// Remove the spaces before the things that should not have spaces before them.
// But, be careful not to turn "p :link {...}" into "p:link{...}"
// Swap out any pseudo-class colons with the token, and then swap back.
$css = preg_replace_callback('/(?:^|\})[^\{]*\s+\:/', array($this, 'replace_colon'), $css);
// Remove spaces before the things that should not have spaces before them.
$css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
// Restore spaces for !important
$css = preg_replace('/\!important/i', ' !important', $css);
// bring back the colon
$css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);
// retain space for special IE6 cases
$css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);
// no space after the end of a preserved comment
$css = preg_replace('/\*\/ /', '*/', $css);
// lowercase some popular @directives
$css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);
// lowercase some more common pseudo-elements
$css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css);
// lowercase some more common functions
$css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css);
// lower case some common function that can be values
// NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
$css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css);
// Put the space back in some cases, to support stuff like
// @media screen and (-webkit-min-device-pixel-ratio:0){
$css = preg_replace('/\band\(/i', 'and (', $css);
// Remove the spaces after the things that should not have spaces after them.
$css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
// remove unnecessary semicolons
$css = preg_replace('/;+\}/', '}', $css);
// Fix for issue: #2528146
// Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
// to avoid issues on Symbian S60 3.x browsers.
$css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
// Replace 0 <length> and 0 <percentage> values with 0.
// <length> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/length
// <percentage> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/percentage
$css = preg_replace('/([^\\\\]\:|\s)0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%)/iS', '${1}0', $css);
// 0% step in a keyframe? restore the % unit
$css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]+\{)(.*?)(\}\})/iS', array($this, 'replace_keyframe_zero'), $css);
// Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
$css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
// Fix for issue: #2528142
// Replace text-shadow:0; with text-shadow:0 0 0;
$css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);
// Replace background-position:0; with background-position:0 0;
// same for transform-origin
// Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
$css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);
// Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
// Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
// This makes it more likely that it'll get further compressed in the next step.
$css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
$css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);
// Shorten colors from #AABBCC to #ABC or short color name.
$css = $this->compress_hex_colors($css);
// border: none to border:0, outline: none to outline:0
$css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);
// shorter opacity IE filter
$css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);
// Find a fraction that is used for Opera's -o-device-pixel-ratio query
// Add token to add the "\" back in later
$css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
// Remove empty rules.
$css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);
// Add "/" back to fix Opera -o-device-pixel-ratio query
$css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
// Replace multiple semi-colons in a row by a single one
// See SF bug #1980989
$css = preg_replace('/;;+/', ';', $css);
// Restore new lines for /*! important comments
$css = preg_replace('/'. self::NL .'/', "\n", $css);
// Lowercase all uppercase properties
$css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
// Some source control tools don't like it when files containing lines longer
// than, say 8000 characters, are checked in. The linebreak option is used in
// that case to split long lines after a specific column.
if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
$linebreak_pos = (int) $linebreak_pos;
$start_index = $i = 0;
while ($i < strlen($css)) {
$i++;
if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
$css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
$start_index = $i;
}
}
}
// restore preserved comments and strings in reverse order
for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) {
$css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
}
// Trim the final string (for any leading or trailing white spaces)
return trim($css);
}
/**
* Utility method to replace all data urls with tokens before we start
* compressing, to avoid performance issues running some of the subsequent
* regexes against large strings chunks.
*
* @param string $css
* @return string
*/
private function extract_data_urls($css)
{
// Leave data urls alone to increase parse performance.
$max_index = strlen($css) - 1;
$append_index = $index = $last_index = $offset = 0;
$sb = array();
$pattern = '/url\(\s*(["\']?)data\:/i';
// Since we need to account for non-base64 data urls, we need to handle
// ' and ) being part of the data string. Hence switching to indexOf,
// to determine whether or not we have matching string terminators and
// handling sb appends directly, instead of using matcher.append* methods.
while (preg_match($pattern, $css, $m, 0, $offset)) {
$index = $this->index_of($css, $m[0], $offset);
$last_index = $index + strlen($m[0]);
$start_index = $index + 4; // "url(".length()
$end_index = $last_index - 1;
$terminator = $m[1]; // ', " or empty (not quoted)
$found_terminator = FALSE;
if (strlen($terminator) === 0) {
$terminator = ')';
}
while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
$end_index = $this->index_of($css, $terminator, $end_index + 1);
// endIndex == 0 doesn't really apply here
if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
$found_terminator = TRUE;
if (')' != $terminator) {
$end_index = $this->index_of($css, ')', $end_index);
}
}
}
// Enough searching, start moving stuff over to the buffer
$sb[] = $this->str_slice($css, $append_index, $index);
if ($found_terminator) {
$token = $this->str_slice($css, $start_index, $end_index);
$token = preg_replace('/\s+/', '', $token);
$this->preserved_tokens[] = $token;
$preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
$sb[] = $preserver;
$append_index = $end_index + 1;
} else {
// No end terminator found, re-add the whole match. Should we throw/warn here?
$sb[] = $this->str_slice($css, $index, $last_index);
$append_index = $last_index;
}
$offset = $last_index;
}
$sb[] = $this->str_slice($css, $append_index);
return implode('', $sb);
}
/**
* Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
*
* DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
* e.g. #AddressForm { ... }
*
* DOES NOT compress IE filters, which have hex color values (which would break things).
* e.g. filter: chroma(color="#FFFFFF");
*
* DOES NOT compress invalid hex values.
* e.g. background-color: #aabbccdd
*
* @param string $css
* @return string
*/
private function compress_hex_colors($css)
{
// Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
$pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
$_index = $index = $last_index = $offset = 0;
$sb = array();
// See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
$short_safe = array(
'#808080' => 'gray',
'#008000' => 'green',
'#800000' => 'maroon',
'#000080' => 'navy',
'#808000' => 'olive',
'#ffa500' => 'orange',
'#800080' => 'purple',
'#c0c0c0' => 'silver',
'#008080' => 'teal',
'#f00' => 'red'
);
while (preg_match($pattern, $css, $m, 0, $offset)) {
$index = $this->index_of($css, $m[0], $offset);
$last_index = $index + strlen($m[0]);
$is_filter = $m[1] !== null && $m[1] !== '';
$sb[] = $this->str_slice($css, $_index, $index);
if ($is_filter) {
// Restore, maintain case, otherwise filter will break
$sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
} else {
if (strtolower($m[2]) == strtolower($m[3]) &&
strtolower($m[4]) == strtolower($m[5]) &&
strtolower($m[6]) == strtolower($m[7])) {
// Compress.
$hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
} else {
// Non compressible color, restore but lower case.
$hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
}
// replace Hex colors to short safe color names
$sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
}
$_index = $offset = $last_index - strlen($m[8]);
}
$sb[] = $this->str_slice($css, $_index);
return implode('', $sb);
}
/* CALLBACKS
* ---------------------------------------------------------------------------------------------
*/
private function replace_string($matches)
{
$match = $matches[0];
$quote = substr($match, 0, 1);
// Must use addcslashes in PHP to avoid parsing of backslashes
$match = addcslashes($this->str_slice($match, 1, -1), '\\');
// maybe the string contains a comment-like substring?
// one, maybe more? put'em back then
if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
$match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
}
}
// minify alpha opacity in filter strings
$match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);
$this->preserved_tokens[] = $match;
return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
}
private function replace_colon($matches)
{
return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
}
private function replace_calc($matches)
{
$this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]));
return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
}
private function preserve_old_IE_specific_matrix_definition($matches)
{
$this->preserved_tokens[] = $matches[1];
return 'filter:progid:DXImageTransform.Microsoft.Matrix(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
}
private function replace_keyframe_zero($matches)
{
return $matches[1] . preg_replace('/0(\{|,[^\)\{]+\{)/', '0%$1', $matches[2]) . $matches[3];
}
private function rgb_to_hex($matches)
{
// Support for percentage values rgb(100%, 0%, 45%);
if ($this->index_of($matches[1], '%') >= 0){
$rgbcolors = explode(',', str_replace('%', '', $matches[1]));
for ($i = 0; $i < count($rgbcolors); $i++) {
$rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55);
}
} else {
$rgbcolors = explode(',', $matches[1]);
}
// Values outside the sRGB color space should be clipped (0-255)
for ($i = 0; $i < count($rgbcolors); $i++) {
$rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255);
$rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]);
}
// Fix for issue #2528093
if (!preg_match('/[\s\,\);\}]/', $matches[2])){
$matches[2] = ' ' . $matches[2];
}
return '#' . implode('', $rgbcolors) . $matches[2];
}
private function hsl_to_hex($matches)
{
$values = explode(',', str_replace('%', '', $matches[1]));
$h = floatval($values[0]);
$s = floatval($values[1]);
$l = floatval($values[2]);
// Wrap and clamp, then fraction!
$h = ((($h % 360) + 360) % 360) / 360;
$s = $this->clamp_number($s, 0, 100) / 100;
$l = $this->clamp_number($l, 0, 100) / 100;
if ($s == 0) {
$r = $g = $b = $this->round_number(255 * $l);
} else {
$v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
$v1 = (2 * $l) - $v2;
$r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3)));
$g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h));
$b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3)));
}
return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2]));
}
private function lowercase_pseudo_first($matches)
{
return ':first-'. strtolower($matches[1]) .' '. $matches[2];
}
private function lowercase_directives($matches)
{
return '@'. strtolower($matches[1]);
}
private function lowercase_pseudo_elements($matches)
{
return ':'. strtolower($matches[1]);
}
private function lowercase_common_functions($matches)
{
return ':'. strtolower($matches[1]) .'(';
}
private function lowercase_common_functions_values($matches)
{
return $matches[1] . strtolower($matches[2]);
}
private function lowercase_properties($matches)
{
return $matches[1].strtolower($matches[2]).$matches[3];
}
/* HELPERS
* ---------------------------------------------------------------------------------------------
*/
private function hue_to_rgb($v1, $v2, $vh)
{
$vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh;
if ($vh * 2 < 1) return $v2;
if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6;
return $v1;
}
private function round_number($n)
{
return intval(floor(floatval($n) + 0.5), 10);
}
private function clamp_number($n, $min, $max)
{
return min(max($n, $min), $max);
}
/**
* PHP port of Javascript's "indexOf" function for strings only
* Author: Tubal Martin http://blog.margenn.com
*
* @param string $haystack
* @param string $needle
* @param int $offset index (optional)
* @return int
*/
private function index_of($haystack, $needle, $offset = 0)
{
$index = strpos($haystack, $needle, $offset);
return ($index !== FALSE) ? $index : -1;
}
/**
* PHP port of Javascript's "slice" function for strings only
* Author: Tubal Martin http://blog.margenn.com
* Tests: http://margenn.com/tubal/str_slice/
*
* @param string $str
* @param int $start index
* @param int|bool $end index (optional)
* @return string
*/
private function str_slice($str, $start = 0, $end = FALSE)
{
if ($end !== FALSE && ($start < 0 || $end <= 0)) {
$max = strlen($str);
if ($start < 0) {
if (($start = $max + $start) < 0) {
return '';
}
}
if ($end < 0) {
if (($end = $max + $end) < 0) {
return '';
}
}
if ($end <= $start) {
return '';
}
}
$slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start);
return ($slice === FALSE) ? '' : $slice;
}
/**
* Convert strings like "64M" or "30" to int values
* @param mixed $size
* @return int
*/
private function normalize_int($size)
{
if (is_string($size)) {
switch (substr($size, -1)) {
case 'M': case 'm': return $size * 1048576;
case 'K': case 'k': return $size * 1024;
case 'G': case 'g': return $size * 1073741824;
}
}
return (int) $size;
}
}

123
min/lib/DooDigestAuth.php Normal file
View File

@@ -0,0 +1,123 @@
<?php
/**
* DooDigestAuth class file.
*
* @author Leng Sheng Hong <darkredz@gmail.com>
* @link http://www.doophp.com/
* @copyright Copyright &copy; 2009 Leng Sheng Hong
* @license http://www.doophp.com/license
*/
/**
* Handles HTTP digest authentication
*
* <p>HTTP digest authentication can be used with the URI router.
* HTTP digest is much more recommended over the use of HTTP Basic auth which doesn't provide any encryption.
* If you are running PHP on Apache in CGI/FastCGI mode, you would need to
* add the following line to your .htaccess for digest auth to work correctly.</p>
* <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code>
*
* <p>This class is tested under Apache 2.2 and Cherokee web server. It should work in both mod_php and cgi mode.</p>
*
* @author Leng Sheng Hong <darkredz@gmail.com>
* @version $Id: DooDigestAuth.php 1000 2009-07-7 18:27:22
* @package doo.auth
* @since 1.0
*
* @deprecated 2.3 This will be removed in Minify 3.0
*/
class DooDigestAuth{
/**
* Authenticate against a list of username and passwords.
*
* <p>HTTP Digest Authentication doesn't work with PHP in CGI mode,
* you have to add this into your .htaccess <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code></p>
*
* @param string $realm Name of the authentication session
* @param array $users An assoc array of username and password: array('uname1'=>'pwd1', 'uname2'=>'pwd2')
* @param string $fail_msg Message to be displayed if the User cancel the login
* @param string $fail_url URL to be redirect if the User cancel the login
* @return string The username if login success.
*/
public static function http_auth($realm, $users, $fail_msg=NULL, $fail_url=NULL){
$realm = "Restricted area - $realm";
//user => password
//$users = array('admin' => '1234', 'guest' => 'guest');
if(!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && strpos($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 'Digest')===0){
$_SERVER['PHP_AUTH_DIGEST'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
}
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
header('HTTP/1.1 401 Unauthorized');
if($fail_msg!=NULL)
die($fail_msg);
if($fail_url!=NULL)
die("<script>window.location.href = '$fail_url'</script>");
exit;
}
// analyze the PHP_AUTH_DIGEST variable
if (!($data = self::http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($users[$data['username']])){
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
header('HTTP/1.1 401 Unauthorized');
if($fail_msg!=NULL)
die($fail_msg);
if($fail_url!=NULL)
die("<script>window.location.href = '$fail_url'</script>");
exit;
}
// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if ($data['response'] != $valid_response){
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
if($fail_msg!=NULL)
die($fail_msg);
if($fail_url!=NULL)
die("<script>window.location.href = '$fail_url'</script>");
exit;
}
// ok, valid username & password
return $data['username'];
}
/**
* Method to parse the http auth header, works with IE.
*
* Internet Explorer returns a qop="xxxxxxxxxxx" in the header instead of qop=xxxxxxxxxxx as most browsers do.
*
* @param string $txt header string to parse
* @return array An assoc array of the digest auth session
*/
private static function http_digest_parse($txt)
{
$res = preg_match("/username=\"([^\"]+)\"/i", $txt, $match);
$data['username'] = (isset($match[1]))?$match[1]:null;
$res = preg_match('/nonce=\"([^\"]+)\"/i', $txt, $match);
$data['nonce'] = $match[1];
$res = preg_match('/nc=([0-9]+)/i', $txt, $match);
$data['nc'] = $match[1];
$res = preg_match('/cnonce=\"([^\"]+)\"/i', $txt, $match);
$data['cnonce'] = $match[1];
$res = preg_match('/qop=([^,]+)/i', $txt, $match);
$data['qop'] = str_replace('"','',$match[1]);
$res = preg_match('/uri=\"([^\"]+)\"/i', $txt, $match);
$data['uri'] = $match[1];
$res = preg_match('/response=\"([^\"]+)\"/i', $txt, $match);
$data['response'] = $match[1];
return $data;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -7,14 +7,14 @@
* </code>
*
* This is a modified port of jsmin.c. Improvements:
*
*
* Does not choke on some regexp literals containing quote characters. E.g. /'/
*
* Spaces are preserved after some add/sub operators, so they are not mistakenly
*
* Spaces are preserved after some add/sub operators, so they are not mistakenly
* converted to post-inc/dec. E.g. a + ++b -> a+ ++b
*
* Preserves multi-line comments that begin with /*!
*
*
* PHP 5 or higher is required.
*
* Permission is hereby granted to use this version of the library under the
@@ -69,6 +69,7 @@ class JSMin {
protected $lookAhead = null;
protected $output = '';
protected $lastByteOut = '';
protected $keptComment = '';
/**
* Minify Javascript.
@@ -116,8 +117,8 @@ class JSMin {
// determine next command
$command = self::ACTION_KEEP_A; // default
if ($this->a === ' ') {
if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
&& ($this->b === $this->lastByteOut)) {
if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
&& ($this->b === $this->lastByteOut)) {
// Don't delete this space. If we do, the addition/subtraction
// could be parsed as a post-increment
} elseif (! $this->isAlphaNum($this->b)) {
@@ -126,16 +127,17 @@ class JSMin {
} elseif ($this->a === "\n") {
if ($this->b === ' ') {
$command = self::ACTION_DELETE_A_B;
// in case of mbstring.func_overload & 2, must check for null b,
// otherwise mb_strpos will give WARNING
// in case of mbstring.func_overload & 2, must check for null b,
// otherwise mb_strpos will give WARNING
} elseif ($this->b === null
|| (false === strpos('{[(+-', $this->b)
|| (false === strpos('{[(+-!~', $this->b)
&& ! $this->isAlphaNum($this->b))) {
$command = self::ACTION_DELETE_A;
}
} elseif (! $this->isAlphaNum($this->a)) {
if ($this->b === ' '
|| ($this->b === "\n"
|| ($this->b === "\n"
&& (false === strpos('}])+-"\'', $this->a)))) {
$command = self::ACTION_DELETE_A_B;
}
@@ -160,7 +162,8 @@ class JSMin {
*/
protected function action($command)
{
if ($command === self::ACTION_DELETE_A_B
// make sure we don't compress "a + ++b" to "a+++b", etc.
if ($command === self::ACTION_DELETE_A_B
&& $this->b === ' '
&& ($this->a === '+' || $this->a === '-')) {
// Note: we're at an addition/substraction operator; the inputIndex
@@ -170,58 +173,88 @@ class JSMin {
$command = self::ACTION_KEEP_A;
}
}
switch ($command) {
case self::ACTION_KEEP_A:
case self::ACTION_KEEP_A: // 1
$this->output .= $this->a;
if ($this->keptComment) {
$this->output = rtrim($this->output, "\n");
$this->output .= $this->keptComment;
$this->keptComment = '';
}
$this->lastByteOut = $this->a;
// fallthrough
case self::ACTION_DELETE_A:
// fallthrough intentional
case self::ACTION_DELETE_A: // 2
$this->a = $this->b;
if ($this->a === "'" || $this->a === '"') { // string literal
$str = $this->a; // in case needed for exception
while (true) {
for(;;) {
$this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get();
$this->a = $this->get();
if ($this->a === $this->b) { // end quote
break;
}
if (ord($this->a) <= self::ORD_LF) {
if ($this->isEOF($this->a)) {
$byte = $this->inputIndex - 1;
throw new JSMin_UnterminatedStringException(
"JSMin: Unterminated String at byte "
. $this->inputIndex . ": {$str}");
"JSMin: Unterminated String at byte {$byte}: {$str}");
}
$str .= $this->a;
if ($this->a === '\\') {
$this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get();
$str .= $this->a;
}
}
}
// fallthrough
case self::ACTION_DELETE_A_B:
// fallthrough intentional
case self::ACTION_DELETE_A_B: // 3
$this->b = $this->next();
if ($this->b === '/' && $this->isRegexpLiteral()) { // RegExp literal
if ($this->b === '/' && $this->isRegexpLiteral()) {
$this->output .= $this->a . $this->b;
$pattern = '/'; // in case needed for exception
while (true) {
$pattern = '/'; // keep entire pattern in case we need to report it in the exception
for(;;) {
$this->a = $this->get();
$pattern .= $this->a;
if ($this->a === '[') {
for(;;) {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
if ($this->a === ']') {
break;
}
if ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
}
if ($this->isEOF($this->a)) {
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated set in RegExp at byte "
. $this->inputIndex .": {$pattern}");
}
}
}
if ($this->a === '/') { // end pattern
break; // while (true)
} elseif ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
} elseif (ord($this->a) <= self::ORD_LF) {
$this->a = $this->get();
$pattern .= $this->a;
} elseif ($this->isEOF($this->a)) {
$byte = $this->inputIndex - 1;
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated RegExp at byte "
. $this->inputIndex .": {$pattern}");
"JSMin: Unterminated RegExp at byte {$byte}: {$pattern}");
}
$this->output .= $this->a;
$this->lastByteOut = $this->a;
@@ -237,31 +270,43 @@ class JSMin {
*/
protected function isRegexpLiteral()
{
if (false !== strpos("\n{;(,=:[!&|?", $this->a)) { // we aren't dividing
if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
// we obviously aren't dividing
return true;
}
if (' ' === $this->a) {
$length = strlen($this->output);
if ($length < 2) { // weird edge case
return true;
// we have to check for a preceding keyword, and we don't need to pattern
// match over the whole output.
$recentOutput = substr($this->output, -10);
// check if return/typeof directly precede a pattern without a space
foreach (array('return', 'typeof') as $keyword) {
if ($this->a !== substr($keyword, -1)) {
// certainly wasn't keyword
continue;
}
// you can't divide a keyword
if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
if ($this->output === $m[0]) { // odd but could happen
return true;
}
// make sure it's a keyword, not end of an identifier
$charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
if (! $this->isAlphaNum($charBeforeKeyword)) {
if (preg_match("~(^|[\\s\\S])" . substr($keyword, 0, -1) . "$~", $recentOutput, $m)) {
if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
return true;
}
}
}
// check all keywords
if ($this->a === ' ' || $this->a === "\n") {
if (preg_match('~(^|[\\s\\S])(?:case|else|in|return|typeof)$~', $recentOutput, $m)) {
if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
return true;
}
}
}
return false;
}
/**
* Get next char. Convert ctrl char to space.
* Return the next character from stdin. Watch out for lookahead. If the character is a control character,
* translate it to a space or linefeed.
*
* @return string
*/
@@ -270,24 +315,36 @@ class JSMin {
$c = $this->lookAhead;
$this->lookAhead = null;
if ($c === null) {
// getc(stdin)
if ($this->inputIndex < $this->inputLength) {
$c = $this->input[$this->inputIndex];
$this->inputIndex += 1;
} else {
return null;
$c = null;
}
}
if ($c === "\r" || $c === "\n") {
if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
return $c;
}
if ($c === "\r") {
return "\n";
}
if (ord($c) < self::ORD_SPACE) { // control char
return ' ';
}
return $c;
return ' ';
}
/**
* Get next char. If is ctrl character, translate to a space or newline.
* Does $a indicate end of input?
*
* @param string $a
* @return bool
*/
protected function isEOF($a)
{
return ord($a) <= self::ORD_LF;
}
/**
* Get next char (without getting it). If is ctrl character, translate to a space or newline.
*
* @return string
*/
@@ -298,7 +355,7 @@ class JSMin {
}
/**
* Is $c a letter, digit, underscore, dollar sign, escape, or non-ASCII?
* Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
*
* @param string $c
*
@@ -306,77 +363,84 @@ class JSMin {
*/
protected function isAlphaNum($c)
{
return (preg_match('/^[0-9a-zA-Z_\\$\\\\]$/', $c) || ord($c) > 126);
return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
}
/**
* @return string
* Consume a single line comment from input (possibly retaining it)
*/
protected function singleLineComment()
protected function consumeSingleLineComment()
{
$comment = '';
while (true) {
$get = $this->get();
$comment .= $get;
if (ord($get) <= self::ORD_LF) { // EOL reached
if (ord($get) <= self::ORD_LF) { // end of line reached
// if IE conditional comment
if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
return "/{$comment}";
$this->keptComment .= "/{$comment}";
}
return $get;
return;
}
}
}
/**
* @return string
* Consume a multiple line comment from input (possibly retaining it)
*
* @throws JSMin_UnterminatedCommentException
*/
protected function multipleLineComment()
protected function consumeMultipleLineComment()
{
$this->get();
$comment = '';
while (true) {
for(;;) {
$get = $this->get();
if ($get === '*') {
if ($this->peek() === '/') { // end of comment reached
$this->get();
// if comment preserved by YUI Compressor
if (0 === strpos($comment, '!')) {
return "\n/*!" . substr($comment, 1) . "*/\n";
// preserved by YUI Compressor
if (!$this->keptComment) {
// don't prepend a newline if two comments right after one another
$this->keptComment = "\n";
}
$this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
} else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
// IE conditional
$this->keptComment .= "/*{$comment}*/";
}
// if IE conditional comment
if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
return "/*{$comment}*/";
}
return ' ';
return;
}
} elseif ($get === null) {
throw new JSMin_UnterminatedCommentException(
"JSMin: Unterminated comment at byte "
. $this->inputIndex . ": /*{$comment}");
"JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
}
$comment .= $get;
}
}
/**
* Get the next character, skipping over comments.
* Some comments may be preserved.
* Get the next character, skipping over comments. Some comments may be preserved.
*
* @return string
*/
protected function next()
{
$get = $this->get();
if ($get !== '/') {
return $get;
}
switch ($this->peek()) {
case '/': return $this->singleLineComment();
case '*': return $this->multipleLineComment();
default: return $get;
if ($get === '/') {
switch ($this->peek()) {
case '/':
$this->consumeSingleLineComment();
$get = "\n";
break;
case '*':
$this->consumeMultipleLineComment();
$get = ' ';
break;
}
}
return $get;
}
}

View File

@@ -170,7 +170,9 @@ define('KEYWORD_VOID', 'void');
define('KEYWORD_WHILE', 'while');
define('KEYWORD_WITH', 'with');
/**
* @deprecated 2.3 This will be removed in Minify 3.0
*/
class JSMinPlus
{
private $parser;
@@ -197,6 +199,8 @@ class JSMinPlus
public static function minify($js, $filename='')
{
trigger_error(__CLASS__ . ' is deprecated. This will be removed in Minify 3.0', E_USER_DEPRECATED);
static $instance;
// this is a singleton

View File

@@ -3,11 +3,6 @@
* Class Minify
* @package Minify
*/
/**
* Minify_Source
*/
require_once 'Minify/Source.php';
/**
* Minify - Combines, minifies, and caches JavaScript and CSS files on demand.
@@ -29,7 +24,7 @@ require_once 'Minify/Source.php';
*/
class Minify {
const VERSION = '2.1.5';
const VERSION = '2.2.0';
const TYPE_CSS = 'text/css';
const TYPE_HTML = 'text/html';
// there is some debate over the ideal JS Content-Type, but this is the
@@ -85,7 +80,6 @@ class Minify {
public static function setCache($cache = '', $fileLocking = true)
{
if (is_string($cache)) {
require_once 'Minify/Cache/File.php';
self::$_cache = new Minify_Cache_File($cache, $fileLocking);
} else {
self::$_cache = $cache;
@@ -130,6 +124,9 @@ class Minify {
* 'debug' : set to true to minify all sources with the 'Lines' controller, which
* eases the debugging of combined files. This also prevents 304 responses.
* @see Minify_Lines::minify()
*
* 'concatOnly' : set to true to disable minification and simply concatenate the files.
* For JS, no minifier will be used. For CSS, only URI rewriting is still performed.
*
* 'minifiers' : to override Minify's default choice of minifier function for
* a particular content-type, specify your callback under the key of the
@@ -161,9 +158,11 @@ class Minify {
*
* @param array $options controller/serve options
*
* @return mixed null, or, if the 'quiet' option is set to true, an array
* @return null|array if the 'quiet' option is set to true, an array
* with keys "success" (bool), "statusCode" (int), "content" (string), and
* "headers" (array).
*
* @throws Exception
*/
public static function serve($controller, $options = array())
{
@@ -174,10 +173,6 @@ class Minify {
if (is_string($controller)) {
// make $controller into object
$class = 'Minify_Controller_' . $controller;
if (! class_exists($class, false)) {
require_once "Minify/Controller/"
. str_replace('_', '/', $controller) . ".php";
}
$controller = new $class();
/* @var Minify_Controller_Base $controller */
}
@@ -219,8 +214,7 @@ class Minify {
$contentEncoding = self::$_options['encodeMethod'];
} else {
// sniff request header
require_once 'HTTP/Encoder.php';
// depending on what the client accepts, $contentEncoding may be
// depending on what the client accepts, $contentEncoding may be
// 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
// getAcceptedEncoding(false, false) leaves out compress and deflate as options.
list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false);
@@ -231,7 +225,6 @@ class Minify {
}
// check client cache
require_once 'HTTP/ConditionalGet.php';
$cgOptions = array(
'lastModifiedTime' => self::$_options['lastModifiedTime']
,'isPublic' => self::$_options['isPublic']
@@ -261,11 +254,21 @@ class Minify {
$headers = $cg->getHeaders();
unset($cg);
}
if (self::$_options['contentType'] === self::TYPE_CSS
&& self::$_options['rewriteCssUris']) {
foreach($controller->sources as $key => $source) {
if ($source->filepath
if (self::$_options['concatOnly']) {
foreach ($controller->sources as $key => $source) {
if (self::$_options['contentType'] === self::TYPE_JS) {
$source->minifier = "";
} elseif (self::$_options['contentType'] === self::TYPE_CSS) {
$source->minifier = array('Minify_CSS', 'minify');
$source->minifyOptions['compress'] = false;
}
}
}
if (self::$_options['contentType'] === self::TYPE_CSS && self::$_options['rewriteCssUris']) {
foreach ($controller->sources as $key => $source) {
if ($source->filepath
&& !isset($source->minifyOptions['currentDir'])
&& !isset($source->minifyOptions['prependRelativePath'])
) {
@@ -300,7 +303,7 @@ class Minify {
throw $e;
}
self::$_cache->store($cacheId, $content);
if (function_exists('gzencode')) {
if (function_exists('gzencode') && self::$_options['encodeMethod']) {
self::$_cache->store($cacheId . '.gz', gzencode($content, self::$_options['encodeLevel']));
}
}
@@ -445,13 +448,13 @@ class Minify {
header('Content-Type: text/html; charset=utf-8');
echo "<h1>$h1</h1>";
echo "<p>Please see <a href='$url'>$url</a>.</p>";
exit();
exit;
}
/**
* Set up sources to use Minify_Lines
*
* @param array $sources Minify_Source instances
* @param Minify_Source[] $sources Minify_Source instances
*/
protected static function _setupDebug($sources)
{
@@ -468,6 +471,8 @@ class Minify {
* Combines sources and minifies the result.
*
* @return string
*
* @throws Exception
*/
protected static function _combineMinify()
{
@@ -501,7 +506,7 @@ class Minify {
// get next source
$source = null;
if ($i < $l) {
$source = self::$_controller->sources[$i];
$source = self::$_controller->sources[$i];
/* @var Minify_Source $source */
$sourceContent = $source->getContent();
@@ -526,7 +531,6 @@ class Minify {
$imploded = implode($implodeSeparator, $groupToProcessTogether);
$groupToProcessTogether = array();
if ($lastMinifier) {
self::$_controller->loadMinifier($lastMinifier);
try {
$content[] = call_user_func($lastMinifier, $imploded, $lastOptions);
} catch (Exception $e) {
@@ -574,7 +578,7 @@ class Minify {
{
$name = preg_replace('/[^a-zA-Z0-9\\.=_,]/', '', self::$_controller->selectionId);
$name = preg_replace('/\\.+/', '.', $name);
$name = substr($name, 0, 200 - 34 - strlen($prefix));
$name = substr($name, 0, 100 - 34 - strlen($prefix));
$md5 = md5(serialize(array(
Minify_Source::getDigest(self::$_controller->sources)
,self::$_options['minifiers']

View File

@@ -4,8 +4,6 @@
* @package Minify
*/
require_once 'Minify/Source.php';
/**
* Maintain a single last modification time for a group of Minify sources to
* allow use of far off Expires headers in Minify.

View File

@@ -56,6 +56,7 @@ class Minify_CSS {
public static function minify($css, $options = array())
{
$options = array_merge(array(
'compress' => true,
'removeCharsets' => true,
'preserveComments' => true,
'currentDir' => null,
@@ -67,21 +68,20 @@ class Minify_CSS {
if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
}
require_once 'Minify/CSS/Compressor.php';
if (! $options['preserveComments']) {
$css = Minify_CSS_Compressor::process($css, $options);
} else {
require_once 'Minify/CommentPreserver.php';
$css = Minify_CommentPreserver::process(
$css
,array('Minify_CSS_Compressor', 'process')
,array($options)
);
if ($options['compress']) {
if (! $options['preserveComments']) {
$css = Minify_CSS_Compressor::process($css, $options);
} else {
$css = Minify_CommentPreserver::process(
$css
,array('Minify_CSS_Compressor', 'process')
,array($options)
);
}
}
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css;
}
require_once 'Minify/CSS/UriRewriter.php';
if ($options['currentDir']) {
return Minify_CSS_UriRewriter::rewrite(
$css

View File

@@ -70,7 +70,7 @@ class Minify_CSS_UriRewriter {
// rewrite
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
,array(self::$className, '_processUriCB'), $css);
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
$css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
,array(self::$className, '_processUriCB'), $css);
return $css;
@@ -94,7 +94,7 @@ class Minify_CSS_UriRewriter {
// append
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
,array(self::$className, '_processUriCB'), $css);
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
$css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
,array(self::$className, '_processUriCB'), $css);
self::$_prependPath = null;
@@ -282,11 +282,8 @@ class Minify_CSS_UriRewriter {
? $m[1]
: substr($m[1], 1, strlen($m[1]) - 2);
}
// analyze URI
if ('/' !== $uri[0] // root-relative
&& false === strpos($uri, '//') // protocol (non-data)
&& 0 !== strpos($uri, 'data:') // data protocol
) {
// if not root/scheme relative and not starts with scheme
if (!preg_match('~^(/|[a-z]+\:)~', $uri)) {
// URI is file-relative: rewrite depending on options
if (self::$_prependPath === null) {
$uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);

85
min/lib/Minify/CSSmin.php Normal file
View File

@@ -0,0 +1,85 @@
<?php
/**
* Class Minify_CSSmin
* @package Minify
*/
/**
* Wrapper for CSSmin
*
* This class uses CSSmin and Minify_CSS_UriRewriter to minify CSS and rewrite relative URIs.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_CSSmin {
/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options available options:
*
* 'removeCharsets': (default true) remove all @charset at-rules
*
* 'prependRelativePath': (default null) if given, this string will be
* prepended to all relative URIs in import/url declarations
*
* 'currentDir': (default null) if given, this is assumed to be the
* directory of the current CSS file. Using this, minify will rewrite
* all relative URIs in import/url declarations to correctly point to
* the desired files. For this to work, the files *must* exist and be
* visible by the PHP process.
*
* 'symlinks': (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows
* </code>
*
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
* see Minify_CSS_UriRewriter::rewrite
*
* @return string
*/
public static function minify($css, $options = array())
{
$options = array_merge(array(
'compress' => true,
'removeCharsets' => true,
'currentDir' => null,
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
'prependRelativePath' => null,
'symlinks' => array(),
), $options);
if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
}
if ($options['compress']) {
$obj = new CSSmin();
$css = $obj->run($css);
}
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css;
}
if ($options['currentDir']) {
return Minify_CSS_UriRewriter::rewrite(
$css
,$options['currentDir']
,$options['docRoot']
,$options['symlinks']
);
} else {
return Minify_CSS_UriRewriter::prepend(
$css
,$options['prependRelativePath']
);
}
}
}

View File

@@ -98,6 +98,9 @@ class Minify_Cache_File {
{
if ($this->_locking) {
$fp = fopen($this->_path . '/' . $id, 'rb');
if (!$fp) {
return false;
}
flock($fp, LOCK_SH);
$ret = stream_get_contents($fp);
flock($fp, LOCK_UN);
@@ -186,7 +189,6 @@ class Minify_Cache_File {
*/
protected function _log($msg)
{
require_once 'Minify/Logger.php';
Minify_Logger::log($msg);
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* Class Minify_Cache_Wincache
* @package Minify
*/
/**
* WinCache-based cache class for Minify
*
* <code>
* Minify::setCache(new Minify_Cache_WinCache());
* </code>
*
* @package Minify
* @author Matthias Fax
**/
class Minify_Cache_WinCache
{
/**
* Create a Minify_Cache_Wincache object, to be passed to
* Minify::setCache().
*
*
* @param int $expire seconds until expiration (default = 0
* meaning the item will not get an expiration date)
*/
public function __construct($expire = 0)
{
if (!function_exists('wincache_ucache_info')) {
throw new Exception("WinCache for PHP is not installed to be able to use Minify_Cache_WinCache!");
}
$this->_exp = $expire;
}
/**
* Write data to cache.
*
* @param string $id cache id
*
* @param string $data
*
* @return bool success
*/
public function store($id, $data)
{
return wincache_ucache_set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp);
}
/**
* Get the size of a cache entry
*
* @param string $id cache id
*
* @return int size in bytes
*/
public function getSize($id)
{
if (!$this->_fetch($id)) {
return false;
}
return (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload') & 2)) ? mb_strlen($this->_data, '8bit') : strlen($this->_data);
}
/**
* Does a valid cache entry exist?
*
* @param string $id cache id
*
* @param int $srcMtime mtime of the original source file(s)
*
* @return bool exists
*/
public function isValid($id, $srcMtime)
{
return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
}
/**
* Send the cached content to output
*
* @param string $id cache id
*/
public function display($id)
{
echo $this->_fetch($id) ? $this->_data : '';
}
/**
* Fetch the cached content
*
* @param string $id cache id
*
* @return string
*/
public function fetch($id)
{
return $this->_fetch($id) ? $this->_data : '';
}
private $_exp = NULL;
// cache of most recently fetched id
private $_lm = NULL;
private $_data = NULL;
private $_id = NULL;
/**
* Fetch data and timestamp from WinCache, store in instance
*
* @param string $id
*
* @return bool success
*/
private function _fetch($id)
{
if ($this->_id === $id) {
return true;
}
$suc = false;
$ret = wincache_ucache_get($id, $suc);
if (!$suc) {
$this->_id = NULL;
return false;
}
list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id;
return true;
}
}

View File

@@ -0,0 +1,126 @@
<?php
/**
* Class Minify_Cache_XCache
*
* @link http://xcache.lighttpd.net/
* @package Minify
*/
/**
* XCache-based cache class for Minify
* {@see http://xcache.lighttpd.net/wiki/XcacheApi XCache API}
*
* <code>
* Minify::setCache(new Minify_Cache_XCache());
* </code>
*
* @package Minify
* @author Elan Ruusamäe <glen@delfi.ee>
**/
class Minify_Cache_XCache {
/**
* Create a Minify_Cache_XCache object, to be passed to
* Minify::setCache().
*
* @param int $expire seconds until expiration (default = 0
* meaning the item will not get an expiration date)
*/
public function __construct($expire = 0)
{
$this->_exp = $expire;
}
/**
* Write data to cache.
*
* @param string $id cache id
* @param string $data
* @return bool success
*/
public function store($id, $data)
{
return xcache_set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp);
}
/**
* Get the size of a cache entry
*
* @param string $id cache id
* @return int size in bytes
*/
public function getSize($id)
{
if (! $this->_fetch($id)) {
return false;
}
return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
? mb_strlen($this->_data, '8bit')
: strlen($this->_data);
}
/**
* Does a valid cache entry exist?
*
* @param string $id cache id
* @param int $srcMtime mtime of the original source file(s)
* @return bool exists
*/
public function isValid($id, $srcMtime)
{
return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
}
/**
* Send the cached content to output
*
* @param string $id cache id
*/
public function display($id)
{
echo $this->_fetch($id)
? $this->_data
: '';
}
/**
* Fetch the cached content
*
* @param string $id cache id
* @return string
*/
public function fetch($id)
{
return $this->_fetch($id)
? $this->_data
: '';
}
private $_exp = null;
// cache of most recently fetched id
private $_lm = null;
private $_data = null;
private $_id = null;
/**
* Fetch data and timestamp from xcache, store in instance
*
* @param string $id
* @return bool success
*/
private function _fetch($id)
{
if ($this->_id === $id) {
return true;
}
$ret = xcache_get($id);
if (false === $ret) {
$this->_id = null;
return false;
}
list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id;
return true;
}
}

View File

@@ -0,0 +1,139 @@
<?php
/**
* Class Minify_ClosureCompiler
* @package Minify
*/
/**
* Compress Javascript using the Closure Compiler
*
* You must set $jarFile and $tempDir before calling the minify functions.
* Also, depending on your shell's environment, you may need to specify
* the full path to java in $javaExecutable or use putenv() to setup the
* Java environment.
*
* <code>
* Minify_ClosureCompiler::$jarFile = '/path/to/closure-compiler-20120123.jar';
* Minify_ClosureCompiler::$tempDir = '/tmp';
* $code = Minify_ClosureCompiler::minify(
* $code,
* array('compilation_level' => 'SIMPLE_OPTIMIZATIONS')
* );
*
* --compilation_level WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS
*
* </code>
*
* @todo unit tests, $options docs
* @todo more options support (or should just passthru them all?)
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author Elan Ruusamäe <glen@delfi.ee>
*/
class Minify_ClosureCompiler {
const OPTION_CHARSET = 'charset';
const OPTION_COMPILATION_LEVEL = 'compilation_level';
public static $isDebug = false;
/**
* Filepath of the Closure Compiler jar file. This must be set before
* calling minifyJs().
*
* @var string
*/
public static $jarFile = null;
/**
* Writable temp directory. This must be set before calling minifyJs().
*
* @var string
*/
public static $tempDir = null;
/**
* Filepath of "java" executable (may be needed if not in shell's PATH)
*
* @var string
*/
public static $javaExecutable = 'java';
/**
* Minify a Javascript string
*
* @param string $js
*
* @param array $options (verbose is ignored)
*
* @see https://code.google.com/p/closure-compiler/source/browse/trunk/README
*
* @return string
*
* @throws Minify_ClosureCompiler_Exception
*/
public static function minify($js, $options = array())
{
self::_prepare();
if (! ($tmpFile = tempnam(self::$tempDir, 'cc_'))) {
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : could not create temp file in "'.self::$tempDir.'".');
}
file_put_contents($tmpFile, $js);
$cmd = self::_getCmd($options, $tmpFile);
exec($cmd, $output, $result_code);
unlink($tmpFile);
if ($result_code != 0) {
$message = 'Minify_ClosureCompiler : Closure Compiler execution failed.';
if (self::$isDebug) {
exec($cmd . ' 2>&1', $error);
if ($error) {
$message .= "\nReason:\n" . join("\n", $error);
}
}
throw new Minify_ClosureCompiler_Exception($message);
}
return implode("\n", $output);
}
private static function _getCmd($userOptions, $tmpFile)
{
$o = array_merge(
array(
self::OPTION_CHARSET => 'utf-8',
self::OPTION_COMPILATION_LEVEL => 'SIMPLE_OPTIMIZATIONS',
),
$userOptions
);
$charsetOption = $o[self::OPTION_CHARSET];
$cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile)
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $charsetOption)
? " --charset {$charsetOption}"
: '');
foreach (array(self::OPTION_COMPILATION_LEVEL) as $opt) {
if ($o[$opt]) {
$cmd .= " --{$opt} ". escapeshellarg($o[$opt]);
}
}
return $cmd . ' ' . escapeshellarg($tmpFile);
}
private static function _prepare()
{
if (! is_file(self::$jarFile)) {
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.');
}
if (! is_readable(self::$jarFile)) {
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.');
}
if (! is_dir(self::$tempDir)) {
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.');
}
if (! is_writable(self::$tempDir)) {
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.');
}
}
}
class Minify_ClosureCompiler_Exception extends Exception {}

View File

@@ -51,6 +51,7 @@ abstract class Minify_Controller_Base {
,'bubbleCssImports' => false
,'quiet' => false // serve() will send headers and output
,'debug' => false
,'concatOnly' => false
// if you override these, the response codes MUST be directly after
// the first space.
@@ -78,33 +79,6 @@ abstract class Minify_Controller_Base {
return $ret;
}
/**
* Load any code necessary to execute the given minifier callback.
*
* The controller is responsible for loading minification code on demand
* via this method. This built-in function will only load classes for
* static method callbacks where the class isn't already defined. It uses
* the PEAR convention, so, given array('Jimmy_Minifier', 'minCss'), this
* function will include 'Jimmy/Minifier.php'.
*
* If you need code loaded on demand and this doesn't suit you, you'll need
* to override this function in your subclass.
* @see Minify_Controller_Page::loadMinifier()
*
* @param callback $minifierCallback callback of minifier function
*
* @return null
*/
public function loadMinifier($minifierCallback)
{
if (is_array($minifierCallback)
&& is_string($minifierCallback[0])
&& !class_exists($minifierCallback[0], false)) {
require str_replace('_', '/', $minifierCallback[0]) . '.php';
}
}
/**
* Is a user-given file within an allowable directory, existing,
* and having an extension js/css/html/txt ?
@@ -172,9 +146,7 @@ abstract class Minify_Controller_Base {
/**
* instances of Minify_Source, which provide content and any individual minification needs.
*
* @var array
*
* @see Minify_Source
* @var Minify_Source[]
*/
public $sources = array();
@@ -244,7 +216,6 @@ abstract class Minify_Controller_Base {
* @return null
*/
public function log($msg) {
require_once 'Minify/Logger.php';
Minify_Logger::log($msg);
}
}

View File

@@ -4,8 +4,6 @@
* @package Minify
*/
require_once 'Minify/Controller/Base.php';
/**
* Controller class for minifying a set of files
*

View File

@@ -4,8 +4,6 @@
* @package Minify
*/
require_once 'Minify/Controller/Base.php';
/**
* Controller class for serving predetermined groups of minimized sets, selected
* by PATH_INFO

View File

@@ -4,8 +4,6 @@
* @package Minify
*/
require_once 'Minify/Controller/Base.php';
/**
* Controller class for requests to /min/index.php
*
@@ -22,6 +20,13 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
* @return array Minify options
*/
public function setupSources($options) {
// PHP insecure by default: realpath() and other FS functions can't handle null bytes.
foreach (array('g', 'b', 'f') as $key) {
if (isset($_GET[$key])) {
$_GET[$key] = str_replace("\x00", '', (string)$_GET[$key]);
}
}
// filter controller options
$cOptions = array_merge(
array(
@@ -36,7 +41,6 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
$sources = array();
$this->selectionId = '';
$firstMissingResource = null;
if (isset($_GET['g'])) {
// add group(s)
$this->selectionId .= 'g=' . $_GET['g'];
@@ -45,7 +49,6 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
$this->log("Duplicate group key found.");
return $options;
}
$keys = explode(',', $_GET['g']);
foreach ($keys as $key) {
if (! isset($cOptions['groups'][$key])) {
$this->log("A group configuration for \"{$key}\" was not found");
@@ -195,9 +198,12 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
protected function _getFileSource($file, $cOptions)
{
$spec['filepath'] = $file;
if ($cOptions['noMinPattern']
&& preg_match($cOptions['noMinPattern'], basename($file))) {
$spec['minifier'] = '';
if ($cOptions['noMinPattern'] && preg_match($cOptions['noMinPattern'], basename($file))) {
if (preg_match('~\.css$~i', $file)) {
$spec['minifyOptions']['compress'] = false;
} else {
$spec['minifier'] = '';
}
}
return new Minify_Source($spec);
}

View File

@@ -4,8 +4,6 @@
* @package Minify
*/
require_once 'Minify/Controller/Base.php';
/**
* Controller class for serving a single HTML page
*
@@ -59,7 +57,6 @@ class Minify_Controller_Page extends Minify_Controller_Base {
'cssMinifier' => array('Minify_CSS', 'minify')
,'jsMinifier' => array('JSMin', 'minify')
);
$this->_loadCssJsMinifiers = true;
unset($options['minifyAll']);
}
$this->sources[] = new Minify_Source($sourceSpec);
@@ -67,21 +64,5 @@ class Minify_Controller_Page extends Minify_Controller_Base {
$options['contentType'] = Minify::TYPE_HTML;
return $options;
}
protected $_loadCssJsMinifiers = false;
/**
* @see Minify_Controller_Base::loadMinifier()
*/
public function loadMinifier($minifierCallback)
{
if ($this->_loadCssJsMinifiers) {
// Minify will not call for these so we must manually load
// them when Minify/HTML.php is called for.
require_once 'Minify/CSS.php';
require_once 'JSMin.php';
}
parent::loadMinifier($minifierCallback); // load Minify/HTML.php
}
}

View File

@@ -4,8 +4,6 @@
* @package Minify
*/
require_once 'Minify/Controller/Base.php';
/**
* Controller class for emulating version 1 of minify.php (mostly a proof-of-concept)
*
@@ -26,6 +24,11 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
*
*/
public function setupSources($options) {
// PHP insecure by default: realpath() and other FS functions can't handle null bytes.
if (isset($_GET['files'])) {
$_GET['files'] = str_replace("\x00", '', (string)$_GET['files']);
}
self::_setupDefines();
if (MINIFY_USE_CACHE) {
$cacheDir = defined('MINIFY_CACHE_DIR')
@@ -51,8 +54,7 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
) {
return $options;
}
$extension = $m[1];
$files = explode(',', $_GET['files']);
if (count($files) > MINIFY_MAX_FILES) {
return $options;
@@ -63,7 +65,6 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
. DIRECTORY_SEPARATOR;
$prependAbsPaths = $_SERVER['DOCUMENT_ROOT'];
$sources = array();
$goodFiles = array();
$hasBadSource = false;

View File

@@ -1,22 +1,26 @@
<?php
/**
* Class Minify_HTML
* Class Minify_HTML
* @package Minify
*/
/**
* Compress HTML
*
* This is a heavy regex-based removal of whitespace, unnecessary comments and
* This is a heavy regex-based removal of whitespace, unnecessary comments and
* tokens. IE conditional comments are preserved. There are also options to have
* STYLE and SCRIPT blocks compressed by callback functions.
*
* STYLE and SCRIPT blocks compressed by callback functions.
*
* A test suite is available.
*
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_HTML {
/**
* @var boolean
*/
protected $_jsCleanComments = true;
/**
* "Minify" an HTML page
@@ -27,21 +31,21 @@ class Minify_HTML {
*
* 'cssMinifier' : (optional) callback function to process content of STYLE
* elements.
*
*
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored.
*
*
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype.
*
*
* @return string
*/
public static function minify($html, $options = array()) {
$min = new Minify_HTML($html, $options);
$min = new self($html, $options);
return $min->process();
}
/**
* Create a minifier object
*
@@ -51,14 +55,14 @@ class Minify_HTML {
*
* 'cssMinifier' : (optional) callback function to process content of STYLE
* elements.
*
*
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored.
*
*
* 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
*
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype.
*
* @return null
*/
public function __construct($html, $options = array())
{
@@ -72,9 +76,12 @@ class Minify_HTML {
if (isset($options['jsMinifier'])) {
$this->_jsMinifier = $options['jsMinifier'];
}
if (isset($options['jsCleanComments'])) {
$this->_jsCleanComments = (bool)$options['jsCleanComments'];
}
}
/**
* Minify the markeup given in the constructor
*
@@ -124,7 +131,7 @@ class Minify_HTML {
// remove ws around block/undisplayed elements
$this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
.'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
.'|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
.'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
.'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
.'|ul)\\b[^>]*>)/i', '$1', $this->_html);
@@ -213,17 +220,19 @@ class Minify_HTML {
// whitespace surrounding? preserve at least one space
$ws1 = ($m[1] === '') ? '' : ' ';
$ws2 = ($m[4] === '') ? '' : ' ';
// remove HTML comments (and ending "//" if present)
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
if ($this->_jsCleanComments) {
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
}
// remove CDATA section markers
$js = $this->_removeCdata($js);
// minify
$minifier = $this->_jsMinifier
? $this->_jsMinifier
: 'trim';
: 'trim';
$js = call_user_func($minifier, $js);
return $this->_reservePlace($this->_needsCdata($js)

View File

@@ -15,10 +15,10 @@ class Minify_HTML_Helper {
public $minAppUri = '/min';
public $groupsConfigFile = '';
/*
/**
* Get an HTML-escaped Minify URI for a group or set of files
*
* @param mixed $keyOrFiles a group key or array of filepaths/URIs
* @param string|array $keyOrFiles a group key or array of filepaths/URIs
* @param array $opts options:
* 'farExpires' : (default true) append a modified timestamp for cache revving
* 'debug' : (default false) append debug flag
@@ -51,8 +51,12 @@ class Minify_HTML_Helper {
return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']);
}
/*
/**
* Get non-HTML-escaped URI to minify the specified files
*
* @param bool $farExpires
* @param bool $debug
* @return string
*/
public function getRawUri($farExpires = true, $debug = false)
{
@@ -74,6 +78,12 @@ class Minify_HTML_Helper {
return $path;
}
/**
* Set the files that will comprise the URI we're building
*
* @param array $files
* @param bool $checkLastModified
*/
public function setFiles($files, $checkLastModified = true)
{
$this->_groupKey = null;
@@ -94,6 +104,12 @@ class Minify_HTML_Helper {
$this->_filePaths = $files;
}
/**
* Set the group of files that will comprise the URI we're building
*
* @param string $key
* @param bool $checkLastModified
*/
public function setGroup($key, $checkLastModified = true)
{
$this->_groupKey = $key;
@@ -103,13 +119,23 @@ class Minify_HTML_Helper {
}
if (is_file($this->groupsConfigFile)) {
$gc = (require $this->groupsConfigFile);
if (isset($gc[$key])) {
$this->_lastModified = self::getLastModified($gc[$key]);
$keys = explode(',', $key);
foreach ($keys as $key) {
if (isset($gc[$key])) {
$this->_lastModified = self::getLastModified($gc[$key], $this->_lastModified);
}
}
}
}
}
/**
* Get the max(lastModified) of all files
*
* @param array|string $sources
* @param int $lastModified
* @return int
*/
public static function getLastModified($sources, $lastModified = 0)
{
$max = $lastModified;
@@ -142,13 +168,19 @@ class Minify_HTML_Helper {
* @return mixed a common char or '' if any do not match
*/
protected static function _getCommonCharAtPos($arr, $pos) {
$l = count($arr);
if (!isset($arr[0][$pos])) {
return '';
}
$c = $arr[0][$pos];
if ($c === '' || $l === 1)
$l = count($arr);
if ($l === 1) {
return $c;
for ($i = 1; $i < $l; ++$i)
if ($arr[$i][$pos] !== $c)
}
for ($i = 1; $i < $l; ++$i) {
if ($arr[$i][$pos] !== $c) {
return '';
}
}
return $c;
}
@@ -157,11 +189,11 @@ class Minify_HTML_Helper {
*
* @param array $paths root-relative URIs of files
* @param string $minRoot root-relative URI of the "min" application
* @return string
*/
protected static function _getShortestUri($paths, $minRoot = '/min/') {
$pos = 0;
$base = '';
$c;
while (true) {
$c = self::_getCommonCharAtPos($paths, $pos);
if ($c === '') {

View File

@@ -14,13 +14,68 @@
* @todo can use a stream wrapper to unit test this?
*/
class Minify_JS_ClosureCompiler {
const URL = 'http://closure-compiler.appspot.com/compile';
/**
* Minify Javascript code via HTTP request to the Closure Compiler API
* @var string The option key for the maximum POST byte size
*/
const OPTION_MAX_BYTES = 'maxBytes';
/**
* @var string The option key for additional params. @see __construct
*/
const OPTION_ADDITIONAL_OPTIONS = 'additionalParams';
/**
* @var string The option key for the fallback Minifier
*/
const OPTION_FALLBACK_FUNCTION = 'fallbackFunc';
/**
* @var string The option key for the service URL
*/
const OPTION_COMPILER_URL = 'compilerUrl';
/**
* @var int The default maximum POST byte size according to https://developers.google.com/closure/compiler/docs/api-ref
*/
const DEFAULT_MAX_BYTES = 200000;
/**
* @var string[] $DEFAULT_OPTIONS The default options to pass to the compiler service
*
* @note This would be a constant if PHP allowed it
*/
private static $DEFAULT_OPTIONS = array(
'output_format' => 'text',
'compilation_level' => 'SIMPLE_OPTIMIZATIONS'
);
/**
* @var string $url URL of compiler server. defaults to Google's
*/
protected $serviceUrl = 'http://closure-compiler.appspot.com/compile';
/**
* @var int $maxBytes The maximum JS size that can be sent to the compiler server in bytes
*/
protected $maxBytes = self::DEFAULT_MAX_BYTES;
/**
* @var string[] $additionalOptions Additional options to pass to the compiler service
*/
protected $additionalOptions = array();
/**
* @var callable Function to minify JS if service fails. Default is JSMin
*/
protected $fallbackMinifier = array('JSMin', 'minify');
/**
* Minify JavaScript code via HTTP request to a Closure Compiler API
*
* @param string $js input code
* @param array $options unused at this point
* @param array $options Options passed to __construct(). @see __construct
*
* @return string
*/
public static function minify($js, array $options = array())
@@ -30,63 +85,101 @@ class Minify_JS_ClosureCompiler {
}
/**
* @param array $options Options with keys available below:
*
* @param array $options
* fallbackFunc : (callable) function to minify if service unavailable. Default is JSMin.
*
* fallbackFunc : default array($this, 'fallback');
* compilerUrl : (string) URL to closure compiler server
*
* maxBytes : (int) The maximum amount of bytes to be sent as js_code in the POST request.
* Defaults to 200000.
*
* additionalParams : (string[]) Additional parameters to pass to the compiler server. Can be anything named
* in https://developers.google.com/closure/compiler/docs/api-ref except for js_code and
* output_info
*/
public function __construct(array $options = array())
{
$this->_fallbackFunc = isset($options['fallbackMinifier'])
? $options['fallbackMinifier']
: array($this, '_fallback');
if (isset($options[self::OPTION_FALLBACK_FUNCTION])) {
$this->fallbackMinifier = $options[self::OPTION_FALLBACK_FUNCTION];
}
if (isset($options[self::OPTION_COMPILER_URL])) {
$this->serviceUrl = $options[self::OPTION_COMPILER_URL];
}
if (isset($options[self::OPTION_ADDITIONAL_OPTIONS]) && is_array($options[self::OPTION_ADDITIONAL_OPTIONS])) {
$this->additionalOptions = $options[self::OPTION_ADDITIONAL_OPTIONS];
}
if (isset($options[self::OPTION_MAX_BYTES])) {
$this->maxBytes = (int) $options[self::OPTION_MAX_BYTES];
}
}
/**
* Call the service to perform the minification
*
* @param string $js JavaScript code
* @return string
* @throws Minify_JS_ClosureCompiler_Exception
*/
public function min($js)
{
$postBody = $this->_buildPostBody($js);
$bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
? mb_strlen($postBody, '8bit')
: strlen($postBody);
if ($bytes > 200000) {
throw new Minify_JS_ClosureCompiler_Exception(
'POST content larger than 200000 bytes'
);
$postBody = $this->buildPostBody($js);
if ($this->maxBytes > 0) {
$bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
? mb_strlen($postBody, '8bit')
: strlen($postBody);
if ($bytes > $this->maxBytes) {
throw new Minify_JS_ClosureCompiler_Exception(
'POST content larger than ' . $this->maxBytes . ' bytes'
);
}
}
$response = $this->_getResponse($postBody);
$response = $this->getResponse($postBody);
if (preg_match('/^Error\(\d\d?\):/', $response)) {
if (is_callable($this->_fallbackFunc)) {
if (is_callable($this->fallbackMinifier)) {
// use fallback
$response = "/* Received errors from Closure Compiler API:\n$response"
. "\n(Using fallback minifier)\n*/\n";
$response .= call_user_func($this->_fallbackFunc, $js);
$response .= call_user_func($this->fallbackMinifier, $js);
} else {
throw new Minify_JS_ClosureCompiler_Exception($response);
}
}
if ($response === '') {
$errors = $this->_getResponse($this->_buildPostBody($js, true));
$errors = $this->getResponse($this->buildPostBody($js, true));
throw new Minify_JS_ClosureCompiler_Exception($errors);
}
return $response;
}
protected $_fallbackFunc = null;
protected function _getResponse($postBody)
/**
* Get the response for a given POST body
*
* @param string $postBody
* @return string
* @throws Minify_JS_ClosureCompiler_Exception
*/
protected function getResponse($postBody)
{
$allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
if ($allowUrlFopen) {
$contents = file_get_contents(self::URL, false, stream_context_create(array(
$contents = file_get_contents($this->serviceUrl, false, stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
'content' => $postBody,
'max_redirects' => 0,
'timeout' => 15,
)
)));
} elseif (defined('CURLOPT_POST')) {
$ch = curl_init(self::URL);
$ch = curl_init($this->serviceUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
@@ -100,33 +193,37 @@ class Minify_JS_ClosureCompiler {
"Could not make HTTP request: allow_url_open is false and cURL not available"
);
}
if (false === $contents) {
throw new Minify_JS_ClosureCompiler_Exception(
"No HTTP response from server"
);
}
return trim($contents);
}
protected function _buildPostBody($js, $returnErrors = false)
{
return http_build_query(array(
'js_code' => $js,
'output_info' => ($returnErrors ? 'errors' : 'compiled_code'),
'output_format' => 'text',
'compilation_level' => 'SIMPLE_OPTIMIZATIONS'
), null, '&');
}
/**
* Default fallback function if CC API fails
* @param string $js
* Build a POST request body
*
* @param string $js JavaScript code
* @param bool $returnErrors
* @return string
*/
protected function _fallback($js)
protected function buildPostBody($js, $returnErrors = false)
{
require_once 'JSMin.php';
return JSMin::minify($js);
return http_build_query(
array_merge(
self::$DEFAULT_OPTIONS,
$this->additionalOptions,
array(
'js_code' => $js,
'output_info' => ($returnErrors ? 'errors' : 'compiled_code')
)
),
null,
'&'
);
}
}

View File

@@ -55,7 +55,11 @@ class Minify_Lines {
$newLines = array();
while (null !== ($line = array_shift($lines))) {
if (('' !== $id) && (0 == $i % 50)) {
array_push($newLines, '', "/* {$id} */", '');
if ($inComment) {
array_push($newLines, '', "/* {$id} *|", '');
} else {
array_push($newLines, '', "/* {$id} */", '');
}
}
++$i;
$newLines[] = self::_addNote($line, $i, $inComment, $padTo);
@@ -65,7 +69,6 @@ class Minify_Lines {
// check for desired URI rewriting
if (isset($options['currentDir'])) {
require_once 'Minify/CSS/UriRewriter.php';
Minify_CSS_UriRewriter::$debugText = '';
$content = Minify_CSS_UriRewriter::rewrite(
$content
@@ -93,6 +96,9 @@ class Minify_Lines {
*/
private static function _eolInComment($line, $inComment)
{
// crude way to avoid things like // */
$line = preg_replace('~//.*?(\\*/|/\\*).*~', '', $line);
while (strlen($line)) {
$search = $inComment
? '*/'

33
min/lib/Minify/Loader.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
/**
* Class Minify_Loader
* @package Minify
*/
/**
* Class autoloader
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*
* @deprecated 2.3 This will be removed in Minify 3.0
*/
class Minify_Loader {
public function loadClass($class)
{
$file = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR;
$file .= strtr($class, "\\_", DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR) . '.php';
if (is_readable($file)) {
require $file;
}
}
/**
* @deprecated 2.3 This will be removed in Minify 3.0
*/
static public function register()
{
$inst = new self();
spl_autoload_register(array($inst, 'loadClass'));
}
}

View File

@@ -10,7 +10,7 @@
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*
* @todo lose this singleton! pass log object in Minify::serve and distribute to others
* @deprecated 2.3 This will be removed in Minify 3.0
*/
class Minify_Logger {

View File

@@ -13,14 +13,17 @@
* Java environment.
*
* <code>
* Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.3.5.jar';
* Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.4.6.jar';
* Minify_YUICompressor::$tempDir = '/tmp';
* $code = Minify_YUICompressor::minifyJs(
* $code
* ,array('nomunge' => true, 'line-break' => 1000)
* );
* </code>
*
*
* Note: In case you run out stack (default is 512k), you may increase stack size in $options:
* array('stack-size' => '2048k')
*
* @todo unit tests, $options docs
*
* @package Minify
@@ -87,7 +90,7 @@ class Minify_YUICompressor {
{
self::_prepare();
if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) {
throw new Exception('Minify_YUICompressor : could not create temp file.');
throw new Exception('Minify_YUICompressor : could not create temp file in "'.self::$tempDir.'".');
}
file_put_contents($tmpFile, $content);
exec(self::_getCmd($options, $type, $tmpFile), $output, $result_code);
@@ -108,10 +111,15 @@ class Minify_YUICompressor {
,'nomunge' => false
,'preserve-semi' => false
,'disable-optimizations' => false
,'stack-size' => ''
)
,$userOptions
);
$cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile)
$cmd = self::$javaExecutable
. (!empty($o['stack-size'])
? ' -Xss' . $o['stack-size']
: '')
. ' -jar ' . escapeshellarg(self::$jarFile)
. " --type {$type}"
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
? " --charset {$o['charset']}"
@@ -134,8 +142,8 @@ class Minify_YUICompressor {
if (! is_file(self::$jarFile)) {
throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.');
}
if (! is_executable(self::$jarFile)) {
throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not executable.');
if (! is_readable(self::$jarFile)) {
throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not readable.');
}
if (! is_dir(self::$tempDir)) {
throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.');

View File

@@ -2,6 +2,9 @@
namespace MrClay;
use MrClay\Cli\Arg;
use InvalidArgumentException;
/**
* Forms a front controller for a console app, handling and validating arguments (options)
*
@@ -51,7 +54,7 @@ class Cli {
public $isHelpRequest = false;
/**
* @var array of Cli\Arg
* @var Arg[]
*/
protected $_args = array();
@@ -80,8 +83,8 @@ class Cli {
}
/**
* @param Cli\Arg|string $letter
* @return Cli\Arg
* @param Arg|string $letter
* @return Arg
*/
public function addOptionalArg($letter)
{
@@ -89,8 +92,8 @@ class Cli {
}
/**
* @param Cli\Arg|string $letter
* @return Cli\Arg
* @param Arg|string $letter
* @return Arg
*/
public function addRequiredArg($letter)
{
@@ -100,17 +103,17 @@ class Cli {
/**
* @param string $letter
* @param bool $required
* @param Cli\Arg|null $arg
* @return Cli\Arg
* @throws \InvalidArgumentException
* @param Arg|null $arg
* @return Arg
* @throws InvalidArgumentException
*/
public function addArgument($letter, $required, Cli\Arg $arg = null)
public function addArgument($letter, $required, Arg $arg = null)
{
if (! preg_match('/^[a-zA-Z]$/', $letter)) {
throw new \InvalidArgumentException('$letter must be in [a-zA-z]');
throw new InvalidArgumentException('$letter must be in [a-zA-Z]');
}
if (! $arg) {
$arg = new Cli\Arg($required);
$arg = new Arg($required);
}
$this->_args[$letter] = $arg;
return $arg;
@@ -118,7 +121,7 @@ class Cli {
/**
* @param string $letter
* @return Cli\Arg|null
* @return Arg|null
*/
public function getArgument($letter)
{
@@ -143,7 +146,7 @@ class Cli {
$lettersUsed = '';
foreach ($this->_args as $letter => $arg) {
/* @var Cli\Arg $arg */
/* @var Arg $arg */
$options .= $letter;
$lettersUsed .= $letter;
@@ -159,7 +162,7 @@ class Cli {
$this->debug['getopt_return'] = $o;
foreach ($this->_args as $letter => $arg) {
/* @var Cli\Arg $arg */
/* @var Arg $arg */
$this->values[$letter] = false;
if (isset($o[$letter])) {
if (is_bool($o[$letter])) {
@@ -295,7 +298,7 @@ class Cli {
{
$r = "\n";
foreach ($this->_args as $letter => $arg) {
/* @var Cli\Arg $arg */
/* @var Arg $arg */
$desc = $arg->getDescription();
$flag = " -$letter ";
if ($arg->mayHaveValue) {

View File

@@ -2,6 +2,8 @@
namespace MrClay\Cli;
use BadMethodCallException;
/**
* An argument for a CLI app. This specifies the argument, what values it expects and
* how it's treated during validation.
@@ -150,7 +152,7 @@ class Arg {
* @param string $name
* @param array $args
* @return Arg
* @throws \BadMethodCallException
* @throws BadMethodCallException
*/
public function __call($name, array $args = array())
{
@@ -160,7 +162,7 @@ class Arg {
$this->spec['mustHaveValue'] = true;
}
} else {
throw new \BadMethodCallException('Method does not exist');
throw new BadMethodCallException('Method does not exist');
}
return $this;
}

30
min/quick-test.css Normal file
View File

@@ -0,0 +1,30 @@
/*! This file exists only for testing a Minify installation. It's content is not used.
*
* http://example.org/min/f=min/quick-test.css
*/
@import url( more.css );
body, td, th {
font-family: Verdana , "Bitstream Vera Sans" , Arial Narrow, sans-serif ;
font-size : 12px;
}
.nav {
margin-left: 20%;
}
#main-nav {
background-color: red;
border: 1px solid #00ff77;
}
div#content
h1 + p {
padding-top: 0;
margin-top: 0;
}
@media all and (min-width: 640px) {
#media-queries-1 { background-color: #0f0; }
}

74
min/quick-test.js Normal file
View File

@@ -0,0 +1,74 @@
/*! This file exists only for testing a Minify installation. It's content is not used.
*
* http://example.org/min/f=min/quick-test.js
*/
/* Finds the lowest common multiple of two numbers */
function LCMCalculator(x, y) { // constructor function
var checkInt = function (x) { // inner function
if (x % 1 !== 0) {
throw new TypeError(x + " is not an integer"); // throw an exception
}
return x;
};
this.a = checkInt(x);
// ^ semicolons are optional
this.b = checkInt(y);
}
// The prototype of object instances created by a constructor is
// that constructor's "prototype" property.
LCMCalculator.prototype = { // object literal
constructor: LCMCalculator, // when reassigning a prototype, set the constructor property appropriately
gcd: function () { // method that calculates the greatest common divisor
// Euclidean algorithm:
var a = Math.abs(this.a), b = Math.abs(this.b), t;
if (a < b) {
// swap variables
t = b;
b = a;
a = t;
}
while (b !== 0) {
t = b;
b = a % b;
a = t;
}
// Only need to calculate GCD once, so "redefine" this method.
// (Actually not redefinition - it's defined on the instance itself,
// so that this.gcd refers to this "redefinition" instead of LCMCalculator.prototype.gcd.)
// Also, 'gcd' === "gcd", this['gcd'] === this.gcd
this['gcd'] = function () {
return a;
};
return a;
},
// Object property names can be specified by strings delimited by double (") or single (') quotes.
"lcm" : function () {
// Variable names don't collide with object properties, e.g. |lcm| is not |this.lcm|.
// not using |this.a * this.b| to avoid FP precision issues
var lcm = this.a / this.gcd() * this.b;
// Only need to calculate lcm once, so "redefine" this method.
this.lcm = function () {
return lcm;
};
return lcm;
},
toString: function () {
return "LCMCalculator: a = " + this.a + ", b = " + this.b;
}
};
//define generic output function; this implementation only works for web browsers
function output(x) {
document.write(x + "<br>");
}
// Note: Array's map() and forEach() are defined in JavaScript 1.6.
// They are used here to demonstrate JavaScript's inherent functional nature.
[[25, 55], [21, 56], [22, 58], [28, 56]].map(function (pair) { // array literal + mapping function
return new LCMCalculator(pair[0], pair[1]);
}).sort(function (a, b) { // sort with this comparative function
return a.lcm() - b.lcm();
}).forEach(function (obj) {
output(obj + ", gcd = " + obj.gcd() + ", lcm = " + obj.lcm());
});

25
min/server-info.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
/**
* Reports server info useful in configuring the options $min_documentRoot, $min_symlinks,
* and $min_serveOptions['minApp']['allowDirs'].
*
* Change to true to expose this info.
*/
$enabled = false;
///////////////////////
if (!$enabled) {
die('Set $enabled to true to see server info.');
}
header('Content-Type: text/plain');
$file = __FILE__;
echo <<<EOD
__FILE__ : $file
SCRIPT_FILENAME : {$_SERVER['SCRIPT_FILENAME']}
DOCUMENT_ROOT : {$_SERVER['DOCUMENT_ROOT']}
SCRIPT_NAME : {$_SERVER['SCRIPT_NAME']}
REQUEST_URI : {$_SERVER['REQUEST_URI']}
EOD;

View File

@@ -2,11 +2,18 @@
/**
* Utility functions for generating URIs in HTML files
*
* @warning These functions execute min/groupsConfig.php, sometimes multiple times.
* You must make sure that functions are not redefined, and if your use custom sources,
* you must require_once dirname(__FILE__) . '/lib/Minify/Source.php' so that
* class is available.
*
* @package Minify
*/
require_once dirname(__FILE__) . '/lib/Minify/HTML/Helper.php';
if (! class_exists('Minify_Loader', false)) {
require dirname(__FILE__) . '/lib/Minify/Loader.php';
Minify_Loader::register();
}
/*
* Get an HTML-escaped Minify URI for a group or set of files. By default, URIs

View File

@@ -1,16 +1,10 @@
#!/usr/bin/php
#!/usr/bin/env php
<?php
$pathToLib = dirname(dirname(__DIR__)) . '/min/lib';
// needed because of dumb require statements in class files :(
set_include_path($pathToLib . PATH_SEPARATOR . get_include_path());
// barebones autoloader
spl_autoload_register(function ($class) use ($pathToLib) {
$file = $pathToLib . '/' . str_replace(array('_', '\\'), DIRECTORY_SEPARATOR, $class) . '.php';
return is_file($file) ? ((require $file) || true) : false;
});
require "$pathToLib/Minify/Loader.php";
Minify_Loader::register();
$cli = new MrClay\Cli;

View File

@@ -3,14 +3,8 @@
$pathToLib = dirname(dirname(__DIR__)) . '/min/lib';
// needed because of dumb require statements in class files :(
set_include_path($pathToLib . PATH_SEPARATOR . get_include_path());
// barebones autoloader
spl_autoload_register(function ($class) use ($pathToLib) {
$file = $pathToLib . '/' . str_replace(array('_', '\\'), DIRECTORY_SEPARATOR, $class) . '.php';
return is_file($file) ? ((require $file) || true) : false;
});
require "$min_libPath/Minify/Loader.php";
Minify_Loader::register();
$cli = new MrClay\Cli;

View File

@@ -4,13 +4,9 @@
require dirname(__FILE__) . '/../min/config.php';
set_include_path($min_libPath . PATH_SEPARATOR . get_include_path());
require "$min_libPath/Minify/Loader.php";
Minify_Loader::register();
$minifyCachePath = isset($min_cachePath)
? $min_cachePath
: '';
function min_autoload($name) {
require str_replace(array('_', '\\'), DIRECTORY_SEPARATOR, $name) . '.php';
}
spl_autoload_register('min_autoload');

View File

@@ -6,6 +6,10 @@ function getPost($key) {
: $_POST[$key];
}
function h($txt) {
return htmlspecialchars($txt, ENT_QUOTES, 'UTF-8');
}
if (isset($_POST['textIn'])) {
require '../config.php';
$textIn = str_replace("\r\n", "\n", getPost('textIn'));
@@ -17,7 +21,7 @@ if (isset($_POST['method']) && $_POST['method'] === 'Minify and serve') {
if ($base) {
$textIn = preg_replace(
'@(<head\\b[^>]*>)@i'
,'$1<base href="' . htmlentities($base) . '" />'
,'$1<base href="' . h($base) . '" />'
,$textIn
);
}
@@ -38,14 +42,15 @@ if (isset($_POST['method']) && $_POST['method'] === 'Minify and serve') {
,'contentType' => Minify::TYPE_HTML
));
} catch (Exception $e) {
echo htmlspecialchars($e->getMessage());
echo h($e->getMessage());
}
exit();
exit;
}
$classes = array('Minify_HTML', 'Minify_CSS', 'JSMin', 'JSMinPlus');
$tpl = array();
$tpl['classes'] = array('Minify_HTML', 'JSMin', 'Minify_CSS', 'Minify_CSSmin', 'JSMinPlus');
if (isset($_POST['method']) && in_array($_POST['method'], $classes)) {
if (isset($_POST['method']) && in_array($_POST['method'], $tpl['classes'])) {
$args = array($textIn);
if ($_POST['method'] === 'Minify_HTML') {
@@ -55,37 +60,78 @@ if (isset($_POST['method']) && in_array($_POST['method'], $classes)) {
);
}
$func = array($_POST['method'], 'minify');
$inOutBytes[0] = strlen($textIn);
$tpl['inBytes'] = strlen($textIn);
$startTime = microtime(true);
try {
$textOut = call_user_func_array($func, $args);
$tpl['output'] = call_user_func_array($func, $args);
} catch (Exception $e) {
echo htmlspecialchars($e->getMessage());
exit;
$tpl['exceptionMsg'] = getExceptionMsg($e, $textIn);
$tpl['output'] = $textIn;
sendPage($tpl);
}
$inOutBytes[1] = strlen($textOut);
$tpl['time'] = microtime(true) - $startTime;
$tpl['outBytes'] = strlen($tpl['output']);
}
header('Content-Type: text/html; charset=utf-8');
sendPage($tpl);
/**
* @param Exception $e
* @param string $input
* @return string HTML
*/
function getExceptionMsg(Exception $e, $input) {
$msg = "<p>" . h($e->getMessage()) . "</p>";
if (0 === strpos(get_class($e), 'JSMin_Unterminated')
&& preg_match('~byte (\d+)~', $e->getMessage(), $m)) {
$msg .= "<pre>";
if ($m[1] > 200) {
$msg .= h(substr($input, ($m[1] - 200), 200));
} else {
$msg .= h(substr($input, 0, $m[1]));
}
$highlighted = isset($input[$m[1]]) ? h($input[$m[1]]) : '&#9220;';
if ($highlighted === "\n") {
$highlighted = "&#9166;\n";
}
$msg .= "<span style='background:#c00;color:#fff'>$highlighted</span>";
$msg .= h(substr($input, $m[1] + 1, 200)) . "</span></pre>";
}
return $msg;
}
/**
* Draw page
*
* @param array $vars
*/
function sendPage($vars) {
header('Content-Type: text/html; charset=utf-8');
?>
<!DOCTYPE html><head><title>minifyTextarea</title></head>
<?php
if (isset($inOutBytes)) {
if (isset($vars['exceptionMsg'])) {
echo $vars['exceptionMsg'];
}
if (isset($vars['time'])) {
echo "
<table>
<tr><th>Bytes in</th><td>{$inOutBytes[0]} (after line endings normalized to <code>\\n</code>)</td></tr>
<tr><th>Bytes out</th><td>{$inOutBytes[1]} (reduced " . round(100 - (100 * $inOutBytes[1] / $inOutBytes[0])) . "%)</td></tr>
<tr><th>Bytes in</th><td>{$vars['inBytes']} (after line endings normalized to <code>\\n</code>)</td></tr>
<tr><th>Bytes out</th><td>{$vars['outBytes']} (reduced " . round(100 - (100 * $vars['outBytes'] / $vars['inBytes'])) . "%)</td></tr>
<tr><th>Time (s)</th><td>" . round($vars['time'], 5) . "</td></tr>
</table>
";
}
?>
<form action="?2" method="post">
<p><label>Content<br><textarea name="textIn" cols="80" rows="35" style="width:99%"><?php
if (isset($textOut)) {
echo htmlspecialchars($textOut);
if (isset($vars['output'])) {
echo h($vars['output']);
}
?></textarea></label></p>
<p>Minify with:
<?php foreach ($classes as $minClass): ?>
<?php foreach ($vars['classes'] as $minClass): ?>
<input type="submit" name="method" value="<?php echo $minClass; ?>">
<?php endForEach; ?>
</p>
@@ -95,3 +141,35 @@ if (isset($textOut)) {
<label>Insert BASE element w/ href: <input type="text" name="base" size="20"></label>
</p>
</form>
<?php if (isset($vars['selectByte'])) { ?>
<script>
function selectText(el, begin, end) {
var len = el.value.length;
end = end || len;
if (begin == null) {
el.select();
} else {
if (el.setSelectionRange) {
el.setSelectionRange(begin, end);
} else {
if (el.createTextRange) {
var tr = el.createTextRange()
,c = "character";
tr.moveStart(c, begin);
tr.moveEnd(c, end - len);
tr.select();
} else {
el.select();
}
}
}
el.focus();
}
window.onload = function () {
var ta = document.querySelector('textarea[name="textIn"]');
selectText(ta, <?= $vars['selectByte'] ?>, <?= ($vars['selectByte'] + 1) ?>);
};
</script>
<?php }
exit;
}

View File

@@ -84,7 +84,7 @@ if (isset($_POST['url'])) {
&& ! preg_match('@<base\\b@i', $content)) {
$content = preg_replace(
'@(<head\\b[^>]*>)@i'
,'$1<base href="' . htmlentities($url) . '" />'
,'$1<base href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '" />'
,$content
);
}
@@ -119,7 +119,7 @@ if (isset($_POST['url'])) {
));
} catch (Exception $e) {
header('Content-Type: text/html;charset=utf-8');
echo htmlspecialchars($e->getMessage());
echo htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
}
exit();
}
@@ -156,7 +156,7 @@ The fetched resource Content-Type will determine the minifier used.</p>
<fieldset><legend>Retreival options</legend>
<ul>
<li><label>User-Agent: <input type="text" name="ua" size="60" value="<?php echo htmlspecialchars($ua); ?>"></label>
<li><label>User-Agent: <input type="text" name="ua" size="60" value="<?php echo htmlspecialchars($ua, ENT_QUOTES, 'UTF-8'); ?>"></label>
<li><label>Cookie: <input type="text" name="cook" size="60"></label>
</ul>
</fieldset>

View File

@@ -2,7 +2,8 @@
require dirname(__FILE__) . '/../min/config.php';
set_include_path($min_libPath . PATH_SEPARATOR . get_include_path());
require "$min_libPath/Minify/Loader.php";
Minify_Loader::register();
// set cache path and doc root if configured
$minifyCachePath = isset($min_cachePath)
@@ -13,11 +14,9 @@ if ($min_documentRoot) {
}
// default log to FirePHP
require_once 'Minify/Logger.php';
if ($min_errorLogger && true !== $min_errorLogger) { // custom logger
Minify_Logger::setLogger($min_errorLogger);
} else {
require_once 'FirePHP.php';
Minify_Logger::setLogger(FirePHP::getInstance(true));
}

View File

@@ -3,12 +3,15 @@
@import '/_test_files/bar/foo.css' print;
@import '/foo.css' print;
@import '/css/foo.css'; /* abs, should not alter */
@import 'http://foo.com/css/foo.css'; /* abs, should not alter */
@import 'http://foo.com/css/foo.css'; /* scheme, should not alter */
@import url(/_test_files/foo.css) tv, projection;
@import url("/css/foo.css"); /* abs, should not alter */
@import url(/css2/foo.css); /* abs, should not alter */
@import url(data:image/gif;base64,AAAA); /* data, should not alter */
@import url(foo:bar); /* scheme, should not alter */
foo {background:url('/_test_files/css_uriRewriter/bar/foo.png')}
foo {background:url('http://foo.com/css/foo.css');} /* abs, should not alter */
foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter */
foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */
foo {background:url(data:image/gif;base64,AAAA);} /* data, should not alter */
foo {background:url(foo:bar);} /* scheme, should not alter */
foo {background:url("/_test_files/css_uriRewriter/foo bar.jpg");}
@import url('/_test_files/css_uriRewriter/foo bar.css');
@import "/_test_files/css_uriRewriter/foo bar.css";

View File

@@ -3,12 +3,15 @@
@import 'http://cnd.com/A/bar/foo.css' print;
@import 'http://cnd.com/foo.css' print;
@import '/css/foo.css'; /* abs, should not alter */
@import 'http://foo.com/css/foo.css'; /* abs, should not alter */
@import 'http://foo.com/css/foo.css'; /* scheme, should not alter */
@import url(http://cnd.com/A/foo.css) tv, projection;
@import url("/css/foo.css"); /* abs, should not alter */
@import url(/css2/foo.css); /* abs, should not alter */
@import url(data:image/gif;base64,AAAA); /* data, should not alter */
@import url(foo:bar); /* scheme, should not alter */
foo {background:url('http://cnd.com/A/B/bar/foo.png')}
foo {background:url('http://foo.com/css/foo.css');} /* abs, should not alter */
foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter */
foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */
foo {background:url(data:image/gif;base64,AAAA);} /* data, should not alter */
foo {background:url(foo:bar);} /* scheme, should not alter */
foo {background:url("http://cnd.com/A/B/foo bar.jpg");}
@import url('http://cnd.com/A/B/foo bar.css');
@import "http://cnd.com/A/B/foo bar.css";

View File

@@ -3,12 +3,15 @@
@import '//cnd.com/A/bar/foo.css' print;
@import '//cnd.com/foo.css' print;
@import '/css/foo.css'; /* abs, should not alter */
@import 'http://foo.com/css/foo.css'; /* abs, should not alter */
@import 'http://foo.com/css/foo.css'; /* scheme, should not alter */
@import url(//cnd.com/A/foo.css) tv, projection;
@import url("/css/foo.css"); /* abs, should not alter */
@import url(/css2/foo.css); /* abs, should not alter */
@import url(data:image/gif;base64,AAAA); /* data, should not alter */
@import url(foo:bar); /* scheme, should not alter */
foo {background:url('//cnd.com/A/B/bar/foo.png')}
foo {background:url('http://foo.com/css/foo.css');} /* abs, should not alter */
foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter */
foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */
foo {background:url(data:image/gif;base64,AAAA);} /* data, should not alter */
foo {background:url(foo:bar);} /* scheme, should not alter */
foo {background:url("//cnd.com/A/B/foo bar.jpg");}
@import url('//cnd.com/A/B/foo bar.css');
@import "//cnd.com/A/B/foo bar.css";

View File

@@ -3,12 +3,15 @@
@import '../bar/foo.css' print;
@import '../../foo.css' print;
@import '/css/foo.css'; /* abs, should not alter */
@import 'http://foo.com/css/foo.css'; /* abs, should not alter */
@import 'http://foo.com/css/foo.css'; /* scheme, should not alter */
@import url(../foo.css) tv, projection;
@import url("/css/foo.css"); /* abs, should not alter */
@import url(/css2/foo.css); /* abs, should not alter */
@import url(data:image/gif;base64,AAAA); /* data, should not alter */
@import url(foo:bar); /* scheme, should not alter */
foo {background:url('bar/foo.png')}
foo {background:url('http://foo.com/css/foo.css');} /* abs, should not alter */
foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter */
foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */
foo {background:url(data:image/gif;base64,AAAA);} /* data, should not alter */
foo {background:url(foo:bar);} /* scheme, should not alter */
foo {background:url("foo bar.jpg");}
@import url('foo bar.css');
@import "foo bar.css";

View File

@@ -55,3 +55,12 @@ if ((is.ie && is.win) == recognizesCondComm)
document.write("PASS: IE/win honored single-line conditional comment.<br>");
else
document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.<br>");
// hello
//@cc_on/*
// world
//@cc_on*/
//@cc_on/*
'hello';
/*!* preserved */
/*!* preserved */

View File

@@ -19,4 +19,9 @@ document.write("PASS: Non-IE/win browser ignores multi-line conditional comment.
recognizesCondComm=false;//@cc_on*/
if((is.ie&&is.win)==recognizesCondComm)
document.write("PASS: IE/win honored single-line conditional comment.<br>");else
document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.<br>");
document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.<br>");//@cc_on/*
//@cc_on*/
//@cc_on/*
'hello';
/*!* preserved */
/*!* preserved */

View File

@@ -0,0 +1,3 @@
!function(){}(window)
!function(){}(window)

View File

@@ -0,0 +1,2 @@
!function(){}(window)
!function(){}(window)

View File

@@ -1,4 +0,0 @@
function testIssue74() {
return /'/;
}

View File

@@ -1 +0,0 @@
function testIssue74(){return /'/;}

View File

@@ -1 +0,0 @@
function testIssue74(){return /'/}

View File

@@ -0,0 +1,24 @@
function testIssue74() {
return /'/;
}
!function(s) {
return /^[£$€?.]/.test(s);
}();
typeof
/ ' /;
x = / [/] /;
1
/ foo;
(2)
/ foo;
function(){return/foo/};
function(){return typeof/foo/};

View File

@@ -0,0 +1,3 @@
function testIssue74(){return /'/;}
!function(s){return /^[£$€?.]/.test(s);}();typeof
/ ' /;x=/ [/] /;1/foo;(2)/foo;function(){return/foo/};function(){return typeof/foo/};

View File

@@ -1,227 +1,4 @@
/* email.js */
/* 1 */ // http://mrclay.org/
/* 2 */ (function(){
/* 3 */ var
/* 4 */ reMailto = /^mailto:my_name_is_(\S+)_and_the_domain_is_(\S+)$/,
/* 5 */ reRemoveTitleIf = /^my name is/,
/* 6 */ oo = window.onload,
/* 7 */ fixHrefs = function() {
/* 8 */ var i = 0, l, m;
/* 9 */ while (l = document.links[i++]) {
/* 10 */ // require phrase in href property
/* 11 */ if (m = l.href.match(reMailto)) {
/* 12 */ l.href = 'mailto:' + m[1] + '@' + m[2];
/* 13 */ if (reRemoveTitleIf.test(l.title)) {
/* 14 */ l.title = '';
/* 15 */ }
/* 16 */ }
/* 17 */ }
/* 18 */ };
/* 19 */ // end var
/* 20 */ window.onload = function() {
/* 21 */ oo && oo();
/* 22 */ fixHrefs();
/* 23 */ };
/* 24 */ })();
;
/* lines_bugs.js */
/* 1 */ // sections from Prototype 1.6.1
/* 2 */ var xpath = ".//"+"*[local-name()='ul' or local-name()='UL']" +
/* 3 */ "//"+"*[local-name()='li' or local-name()='LI']";
/* 4 */ this.matcher = ['.//'+'*'];
/* 5 */ xpath = {
/* 6 */ descendant: "//"+"*",
/* 7 */ child: "/"+"*",
/* 8 */ f: 0
/* 9 */ };
/* 10 */ document._getElementsByXPath('.//'+'*' + cond, element);
;
/* QueryString.js */
/* 1 */ var MrClay = window.MrClay || {};
/* 2 */
/* 3 */ /**
/* 4 *| * Simplified access to/manipulation of the query string
/* 5 *| *
/* 6 *| * Based on: http://adamv.com/dev/javascript/files/querystring.js
/* 7 *| * Design pattern: http://www.litotes.demon.co.uk/js_info/private_static.html#wConst
/* 8 *| */
/* 9 */ MrClay.QueryString = function(){
/* 10 */ /**
/* 11 *| * @static
/* 12 *| * @private
/* 13 *| */
/* 14 */ var parse = function(str) {
/* 15 */ var assignments = str.split('&')
/* 16 */ ,obj = {}
/* 17 */ ,propValue;
/* 18 */ for (var i = 0, l = assignments.length; i < l; ++i) {
/* 19 */ propValue = assignments[i].split('=');
/* 20 */ if (propValue.length > 2
/* 21 */ || -1 != propValue[0].indexOf('+')
/* 22 */ || propValue[0] == ''
/* 23 */ ) {
/* 24 */ continue;
/* 25 */ }
/* 26 */ if (propValue.length == 1) {
/* 27 */ propValue[1] = propValue[0];
/* 28 */ }
/* 29 */ obj[unescape(propValue[0])] = unescape(propValue[1].replace(/\+/g, ' '));
/* 30 */ }
/* 31 */ return obj;
/* 32 */ };
/* 33 */
/* 34 */ /**
/* 35 *| * Constructor (MrClay.QueryString becomes this)
/* 36 *| *
/* 37 *| * @param mixed A window object, a query string, or empty (default current window)
/* 38 *| */
/* 39 */ function construct_(spec) {
/* 40 */ spec = spec || window;
/* 41 */ if (typeof spec == 'object') {
/* 42 */ // get querystring from window
/* 43 */ this.window = spec;
/* 44 */ spec = spec.location.search.substr(1);
/* 45 */ } else {
/* 46 */ this.window = window;
/* 47 */ }
/* 48 */ this.vars = parse(spec);
/* 49 */ }
/* 50 */
/* QueryString.js */
/* 51 */ /**
/* 52 *| * Reload the window
/* 53 *| *
/* 54 *| * @static
/* 55 *| * @public
/* 56 *| * @param object vars Specify querystring vars only if you wish to replace them
/* 57 *| * @param object window_ window to be reloaded (current window by default)
/* 58 *| */
/* 59 */ construct_.reload = function(vars, window_) {
/* 60 */ window_ = window_ || window;
/* 61 */ vars = vars || (new MrClay.QueryString(window_)).vars;
/* 62 */ var l = window_.location
/* 63 */ ,currUrl = l.href
/* 64 */ ,s = MrClay.QueryString.toString(vars)
/* 65 */ ,newUrl = l.protocol + '//' + l.hostname + l.pathname
/* 66 */ + (s ? '?' + s : '') + l.hash;
/* 67 */ if (currUrl == newUrl) {
/* 68 */ l.reload();
/* 69 */ } else {
/* 70 */ l.assign(newUrl);
/* 71 */ }
/* 72 */ };
/* 73 */
/* 74 */ /**
/* 75 *| * Get the value of a querystring var
/* 76 *| *
/* 77 *| * @static
/* 78 *| * @public
/* 79 *| * @param string key
/* 80 *| * @param mixed default_ value to return if key not found
/* 81 *| * @param object window_ window to check (current window by default)
/* 82 *| * @return mixed
/* 83 *| */
/* 84 */ construct_.get = function(key, default_, window_) {
/* 85 */ window_ = window_ || window;
/* 86 */ return (new MrClay.QueryString(window_)).get(key, default_);
/* 87 */ };
/* 88 */
/* 89 */ /**
/* 90 *| * Reload the page setting one or multiple querystring vars
/* 91 *| *
/* 92 *| * @static
/* 93 *| * @public
/* 94 *| * @param mixed key object of query vars/values, or a string key for a single
/* 95 *| * assignment
/* 96 *| * @param mixed null for multiple settings, the value to assign for single
/* 97 *| * @param object window_ window to reload (current window by default)
/* 98 *| */
/* 99 */ construct_.set = function(key, value, window_) {
/* 100 */ window_ = window_ || window;
/* QueryString.js */
/* 101 */ (new MrClay.QueryString(window_)).set(key, value).reload();
/* 102 */ };
/* 103 */
/* 104 */ /**
/* 105 *| * Convert an object of query vars/values to a querystring
/* 106 *| *
/* 107 *| * @static
/* 108 *| * @public
/* 109 *| * @param object query vars/values
/* 110 *| * @return string
/* 111 *| */
/* 112 */ construct_.toString = function(vars) {
/* 113 */ var pieces = [];
/* 114 */ for (var prop in vars) {
/* 115 */ pieces.push(escape(prop) + '=' + escape(vars[prop]));
/* 116 */ }
/* 117 */ return pieces.join('&');
/* 118 */ };
/* 119 */
/* 120 */ /**
/* 121 *| * @public
/* 122 *| */
/* 123 */ construct_.prototype.reload = function() {
/* 124 */ MrClay.QueryString.reload(this.vars, this.window);
/* 125 */ return this;
/* 126 */ };
/* 127 */
/* 128 */ /**
/* 129 *| * @public
/* 130 *| */
/* 131 */ construct_.prototype.get = function(key, default_) {
/* 132 */ if (typeof default_ == 'undefined') {
/* 133 */ default_ = null;
/* 134 */ }
/* 135 */ return (this.vars[key] == null)
/* 136 */ ? default_
/* 137 */ : this.vars[key];
/* 138 */ };
/* 139 */
/* 140 */ /**
/* 141 *| * @public
/* 142 *| */
/* 143 */ construct_.prototype.set = function(key, value) {
/* 144 */ var obj = {};
/* 145 */ if (typeof key == 'string') {
/* 146 */ obj[key] = value;
/* 147 */ } else {
/* 148 */ obj = key;
/* 149 */ }
/* 150 */ for (var prop in obj) {
/* QueryString.js */
/* 151 */ if (obj[prop] == null) {
/* 152 */ delete this.vars[prop];
/* 153 */ } else {
/* 154 */ this.vars[prop] = obj[prop];
/* 155 */ }
/* 156 */ }
/* 157 */ return this;
/* 158 */ };
/* 159 */
/* 160 */ /**
/* 161 *| * @public
/* 162 *| */
/* 163 */ construct_.prototype.toString = function() {
/* 164 */ return QueryString.toString(this.vars);
/* 165 */ };
/* 166 */
/* 167 */ return construct_;
/* 168 */ }(); // define and execute
;
/* before.js */
/* 1 */ /*! is.js
@@ -277,11 +54,19 @@
/* before.js */
/* 51 *| recognizesCondComm = false;
/* 52 *| //@cc_on*/
/* 51 */ recognizesCondComm = false;
/* 52 */ //@cc_on*/
/* 53 */
/* 54 */ if ((is.ie && is.win) == recognizesCondComm)
/* 55 */ document.write("PASS: IE/win honored single-line conditional comment.<br>");
/* 56 */ else
/* 57 */ document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.<br>");
/* 58 */
/* 59 */ // hello
/* 60 */ //@cc_on/*
/* 61 */ // world
/* 62 */ //@cc_on*/
/* 63 */ //@cc_on/*
/* 64 */ 'hello';
/* 65 */ /*!* preserved */
/* 66 */ /*!* preserved */

View File

@@ -1,6 +0,0 @@
To add a test:
1. Create a "blah.css" or "blah.js" file.
2. Create a "blah.css.min" or "blah.js.min" file, containing the expected minified output.
That's all!

View File

@@ -1,2 +0,0 @@
a {background-position: 0 0 0 0;}
b {BACKGROUND-POSITION: 0 0;}

View File

@@ -1 +0,0 @@
a{background-position:0 0}b{background-position:0 0}

View File

@@ -1,5 +0,0 @@
a {
border: none;
}
b {BACKGROUND:none}
s {border-top: none;}

View File

@@ -1 +0,0 @@
a{border:0}b{background:0}s{border-top:0}

View File

@@ -1,9 +0,0 @@
#elem {
width: 100px;
voice-family: "\"}\"";
voice-family:inherit;
width: 200px;
}
html>body #elem {
width: 200px;
}

View File

@@ -1 +0,0 @@
#elem{width:100px;voice-family:"\"}\"";voice-family:inherit;width:200px}html>body #elem{width:200px}

View File

@@ -1,10 +0,0 @@
/* this file contains no css, it exists purely to put the revision number into the
combined css before uploading it to SiteManager. The exclaimation at the start
of the comment informs yuicompressor not to strip the comment out */
/*! $LastChangedRevision: 81 $ $LastChangedDate: 2009-05-27 17:41:02 +0100 (Wed, 27 May 2009) $ */
body {
yo: cats;
}
ul[id$=foo] label:hover {yo: yo;}

View File

@@ -1 +0,0 @@
/*! $LastChangedRevision: 81 $ $LastChangedDate: 2009-05-27 17:41:02 +0100 (Wed, 27 May 2009) $ */body{yo:cats}ul[id$=foo] label:hover{yo:yo}

View File

@@ -1,19 +0,0 @@
@media screen and/*!YUI-Compresser */(-webkit-min-device-pixel-ratio:0) {
a{
b: 1;
}
}
@media screen and/*! */ /*! */(-webkit-min-device-pixel-ratio:0) {
a{
b: 1;
}
}
@media -webkit-min-device-pixel-ratio:0 {
a{
b: 1;
}
}

View File

@@ -1 +0,0 @@
@media screen and/*!YUI-Compresser */(-webkit-min-device-pixel-ratio:0){a{b:1}}@media screen and/*! *//*! */(-webkit-min-device-pixel-ratio:0){a{b:1}}@media -webkit-min-device-pixel-ratio:0{a{b:1}}

View File

@@ -1,4 +0,0 @@
/*! special */
body {
}

Some files were not shown because too many files have changed in this diff Show More