Version Description
-
New Features
- Added support for Friendly Minify strings, e.g.
http://example.com/path/to/script.js
(best used with CDN). This feature should work well on nginx server. - Provides a much better method to capture and print JS, CSS files.
- Dependencies are more intelligently handled. This should fix many incompatibility issues with other plugins. Note: users of Leaflet Map Markers plugin will still need to add
leafletmapsmarker
handle toScript to be ignored (not minified)
. - Provides full support for conditional and alternate CSS files.
- Auto-detects CSS, JS files used on site. Added a new admin page to easily add CSS, JS files to desired positions.
- Added a new position called
oblivion
, admin can put CSS, JS files to this position to remove them completely from the site, this is useful when you want to remove duplicate files. - You can now control Minify Library's settings via
wp-admin
setting page. - Basic CDN support with SSL.
- Added support for Friendly Minify strings, e.g.
-
Bugs fixed
- Fixed issues with certain installations where WordPress is installed into a sub-directory or
wp-content
folder is moved. - Fixed incompatibility issues with protocol-relative URLs.
- Fixed possible incompatibility issues with forced-SSL URLs.
- Other minor fixes.
- Fixed issues with certain installations where WordPress is installed into a sub-directory or
-
Enhancements
- BWP Minify is WordPress 3.9 compatible.
- BWP Minify should now be able to handle very late JS, CSS files that are queued/printed directly using
wp_print_scripts
andwp_print_styles
. - Changed Minify URL setting to contain a relative URL (from site root) instead of an absolute URL. This should be useful when switching between staging and live site, or between mirror sites.
- Changed Minify strings' default length to
10
to avoid errors on certain servers. Users are encouraged to increase/decrease the length when needed, or enable Pretty Minify String instead. - Disable
Minify bloginfo()
setting by default. Modern themes should always use the enqueue system for any stylesheet. -
admin-bar
,jquery-core
, andjquery-migrate
are now ignored by default. -
admin-bar
anddashicon
are now ignored by default - Changed default cache age to 1 day instead of 2 hours.
- Users can now use
BWP_MINIFY_CACHE_DIR
andBWP_MINIFY_MIN_PATH
constants to override the Cache Directory and Min Path setting in admin. This can become useful when mirroring a site. - Other minor enhancements.
-
Misc
- Added a Serbo-Croatian translation - Thanks to Borisa Djuraskovic!
- Added an Indonesian translation - Thanks to Nasrulhaq Muiz!
- Added a Russian translation - Thanks to !
Migration from 1.2.x
- Minify URL setting has been replaced with Minify Path setting and you will have to manually update this setting if you're using a non-default one. Note to Developers: the setting's key has been changed from
input_minurl
toinput_minpath
. The hookbwp_minify_min_dir
is still available but deprecated in favor ofbwp_minify_min_path
- Minify Path setting and Cache Directory setting are now default to empty value, which means they're automatically detected.
Enjoy this release, and please don't forget to rate BWP Minify 5 shining stars if you like it, thanks!
Bonus: Check out these Minifying Best Practices to apply to your site today!
Download this release
Release Info
Developer | OddOneOut |
Plugin | Better WordPress Minify |
Version | 1.3.0 |
Comparing to | |
See all releases |
Version 1.3.0
- bwp-minify-ms.php +2 -0
- bwp-minify.php +20 -0
- bwp-minify.pot +808 -0
- cache/.htaccess +4 -0
- css/bwp-minify.css +70 -0
- images/icon_menu.png +0 -0
- images/icon_menu_32.png +0 -0
- includes/bwp-option-page/bwp-option-page.php +3 -0
- includes/bwp-option-page/css/bwp-option-page.css +176 -0
- includes/bwp-option-page/images/ad_250x250.png +0 -0
- includes/bwp-option-page/images/icon-paypal.gif +0 -0
- includes/bwp-option-page/images/icon-rss.png +0 -0
- includes/bwp-option-page/images/icon-twitter.png +0 -0
- includes/bwp-option-page/includes/class-bwp-option-page.php +435 -0
- includes/bwp-option-page/includes/index.php +3 -0
- includes/bwp-option-page/index.php +3 -0
- includes/bwp-option-page/js/paypal.js +11 -0
- includes/class-bwp-enqueued-detector.php +522 -0
- includes/class-bwp-framework-improved.php +727 -0
- includes/class-bwp-minify-cdn.php +85 -0
- includes/class-bwp-minify-fetcher.php +319 -0
- includes/class-bwp-minify.php +3504 -0
- includes/common-functions.php +16 -0
- includes/rewriter/apache.php +194 -0
- includes/rewriter/index.php +0 -0
- includes/rewriter/nginx.php +156 -0
- includes/rewriter/rewriter.php +153 -0
- js/bwp-minify.js +56 -0
- languages/bwp-minify-de_DE.mo +0 -0
- languages/bwp-minify-de_DE.po +341 -0
- languages/bwp-minify-es_ES.mo +0 -0
- languages/bwp-minify-es_ES.po +344 -0
- languages/bwp-minify-fr_FR.mo +0 -0
- languages/bwp-minify-fr_FR.po +285 -0
- languages/bwp-minify-id_ID.mo +0 -0
- languages/bwp-minify-id_ID.po +341 -0
- languages/bwp-minify-it_IT.mo +0 -0
- languages/bwp-minify-it_IT.po +285 -0
- languages/bwp-minify-nl_NL.mo +0 -0
- languages/bwp-minify-nl_NL.po +337 -0
- languages/bwp-minify-ro_RO.mo +0 -0
- languages/bwp-minify-ro_RO.po +286 -0
- languages/bwp-minify-ru_RU.mo +0 -0
- languages/bwp-minify-ru_RU.po +377 -0
- languages/bwp-minify-sr_RS.mo +0 -0
- languages/bwp-minify-sr_RS.po +334 -0
- languages/bwp-minify-tr_TR.mo +0 -0
- languages/bwp-minify-tr_TR.po +275 -0
- min/.htaccess +13 -0
- min/README.txt +145 -0
- min/builder/_index.js +253 -0
- min/builder/bm.js +36 -0
- min/builder/bm2.js +15 -0
- min/builder/index.php +251 -0
- min/builder/ocCheck.php +36 -0
- min/builder/rewriteTest.js +1 -0
- min/builder/test.php +43 -0
- min/config.doc.php +185 -0
- min/config.php +16 -0
- min/groupsConfig.php +17 -0
- min/index.php +72 -0
- min/lib/CSSmin.php +758 -0
- min/lib/DooDigestAuth.php +121 -0
- min/lib/FirePHP.php +1370 -0
- min/lib/HTTP/ConditionalGet.php +368 -0
- min/lib/HTTP/Encoder.php +335 -0
- min/lib/JSMin.php +437 -0
- min/lib/JSMinPlus.php +2086 -0
- min/lib/Minify.php +641 -0
- min/lib/Minify/Build.php +101 -0
- min/lib/Minify/CSS.php +99 -0
- min/lib/Minify/CSS/Compressor.php +249 -0
- min/lib/Minify/CSS/UriRewriter.php +310 -0
- min/lib/Minify/Cache/APC.php +133 -0
- min/lib/Minify/Cache/File.php +194 -0
- min/lib/Minify/Cache/Memcache.php +140 -0
- min/lib/Minify/Cache/XCache.php +126 -0
- min/lib/Minify/Cache/ZendPlatform.php +142 -0
- min/lib/Minify/ClosureCompiler.php +123 -0
- min/lib/Minify/CommentPreserver.php +89 -0
- min/lib/Minify/Controller/Base.php +222 -0
- min/lib/Minify/Controller/Files.php +76 -0
- min/lib/Minify/Controller/Groups.php +91 -0
- min/lib/Minify/Controller/MinApp.php +238 -0
- min/lib/Minify/Controller/Page.php +68 -0
- min/lib/Minify/Controller/Version1.php +119 -0
- min/lib/Minify/DebugDetector.php +26 -0
- min/lib/Minify/HTML.php +257 -0
- min/lib/Minify/HTML/Helper.php +225 -0
- min/lib/Minify/ImportProcessor.php +216 -0
- min/lib/Minify/JS/ClosureCompiler.php +132 -0
- min/lib/Minify/Lines.php +143 -0
- min/lib/Minify/Loader.php +28 -0
- min/lib/Minify/Logger.php +47 -0
- min/lib/Minify/Packer.php +37 -0
- min/lib/Minify/Source.php +187 -0
- min/lib/Minify/YUI/CssCompressor.java +382 -0
- min/lib/Minify/YUI/CssCompressor.php +171 -0
- min/lib/Minify/YUICompressor.php +156 -0
- min/lib/MrClay/Cli.php +384 -0
- min/lib/MrClay/Cli/Arg.php +183 -0
- min/lib/Solar/Dir.php +199 -0
- min/utils.php +81 -0
- readme.txt +235 -0
- screenshot-1.png +0 -0
- screenshot-2.png +0 -0
- screenshot-3.png +0 -0
- screenshot-4.png +0 -0
bwp-minify-ms.php
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
<?php
|
2 |
+
include_once dirname(__FILE__) . '/bwp-minify/bwp-minify.php';
|
bwp-minify.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/*
|
3 |
+
Plugin Name: Better WordPress Minify
|
4 |
+
Plugin URI: http://betterwp.net/wordpress-plugins/bwp-minify/
|
5 |
+
Description: Allows you to minify your CSS and JS files for faster page loading for visitors. This plugin uses the PHP library <a href="http://code.google.com/p/minify/">Minify</a> and relies on WordPress's enqueueing system rather than the output buffer (will not break your website in most cases). This plugin is very customizable and easy to use.
|
6 |
+
Version: 1.3.0
|
7 |
+
Text Domain: bwp-minify
|
8 |
+
Domain Path: /languages/
|
9 |
+
Author: Khang Minh
|
10 |
+
Author URI: http://betterwp.net
|
11 |
+
License: GPLv3 or later
|
12 |
+
*/
|
13 |
+
|
14 |
+
// In case someone integrates this plugin in a theme or calling this directly
|
15 |
+
if (class_exists('BWP_MINIFY') || !defined('ABSPATH'))
|
16 |
+
return;
|
17 |
+
|
18 |
+
// Init the plugin
|
19 |
+
require_once dirname(__FILE__) . '/includes/class-bwp-minify.php';
|
20 |
+
$bwp_minify = new BWP_MINIFY();
|
bwp-minify.pot
ADDED
@@ -0,0 +1,808 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: BWP Minify\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2014-07-15 15:23+0700\n"
|
6 |
+
"PO-Revision-Date: 2014-07-15 15:24+0700\n"
|
7 |
+
"Last-Translator: Khang Minh <contact@betterwp.net>\n"
|
8 |
+
"Language-Team: \n"
|
9 |
+
"Language: en_US\n"
|
10 |
+
"MIME-Version: 1.0\n"
|
11 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
12 |
+
"Content-Transfer-Encoding: 8bit\n"
|
13 |
+
"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n"
|
14 |
+
"X-Poedit-Basepath: .\n"
|
15 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
16 |
+
"X-Generator: Poedit 1.6.5\n"
|
17 |
+
"X-Poedit-SearchPath-0: .\n"
|
18 |
+
|
19 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:81
|
20 |
+
msgid "Plugin Configurations"
|
21 |
+
msgstr ""
|
22 |
+
|
23 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:428
|
24 |
+
msgid "Save Changes"
|
25 |
+
msgstr ""
|
26 |
+
|
27 |
+
#: includes/class-bwp-enqueued-detector.php:221
|
28 |
+
#: includes/class-bwp-enqueued-detector.php:234
|
29 |
+
msgid "Handle"
|
30 |
+
msgstr ""
|
31 |
+
|
32 |
+
#: includes/class-bwp-enqueued-detector.php:222
|
33 |
+
#: includes/class-bwp-enqueued-detector.php:235
|
34 |
+
msgid "Position"
|
35 |
+
msgstr ""
|
36 |
+
|
37 |
+
#: includes/class-bwp-enqueued-detector.php:223
|
38 |
+
msgid "Script src"
|
39 |
+
msgstr ""
|
40 |
+
|
41 |
+
#: includes/class-bwp-enqueued-detector.php:224
|
42 |
+
#: includes/class-bwp-enqueued-detector.php:238
|
43 |
+
msgid "Actions"
|
44 |
+
msgstr ""
|
45 |
+
|
46 |
+
#: includes/class-bwp-enqueued-detector.php:236
|
47 |
+
msgid "Media"
|
48 |
+
msgstr ""
|
49 |
+
|
50 |
+
#: includes/class-bwp-enqueued-detector.php:237
|
51 |
+
msgid "Style src"
|
52 |
+
msgstr ""
|
53 |
+
|
54 |
+
#: includes/class-bwp-enqueued-detector.php:348
|
55 |
+
msgid "move to header"
|
56 |
+
msgstr ""
|
57 |
+
|
58 |
+
#: includes/class-bwp-enqueued-detector.php:349
|
59 |
+
msgid "move to footer"
|
60 |
+
msgstr ""
|
61 |
+
|
62 |
+
#: includes/class-bwp-enqueued-detector.php:350
|
63 |
+
#: includes/class-bwp-enqueued-detector.php:353
|
64 |
+
msgid "print separately"
|
65 |
+
msgstr ""
|
66 |
+
|
67 |
+
#: includes/class-bwp-enqueued-detector.php:351
|
68 |
+
#: includes/class-bwp-enqueued-detector.php:354
|
69 |
+
msgid "ignore"
|
70 |
+
msgstr ""
|
71 |
+
|
72 |
+
#: includes/class-bwp-enqueued-detector.php:352
|
73 |
+
#: includes/class-bwp-enqueued-detector.php:355
|
74 |
+
msgid "forget"
|
75 |
+
msgstr ""
|
76 |
+
|
77 |
+
#: includes/class-bwp-enqueued-detector.php:380
|
78 |
+
msgid "select"
|
79 |
+
msgstr ""
|
80 |
+
|
81 |
+
#: includes/class-bwp-enqueued-detector.php:400
|
82 |
+
msgid ""
|
83 |
+
"No enqueued files detected.<br /><br />Please try visiting a few pages on "
|
84 |
+
"your site and then refresh this page.<br /><br />You should clear this list "
|
85 |
+
"once in a while to get rid of files that are no longer being used as this is "
|
86 |
+
"not done automatically."
|
87 |
+
msgstr ""
|
88 |
+
|
89 |
+
#: includes/class-bwp-enqueued-detector.php:435
|
90 |
+
msgid "header"
|
91 |
+
msgstr ""
|
92 |
+
|
93 |
+
#: includes/class-bwp-enqueued-detector.php:436
|
94 |
+
msgid "footer"
|
95 |
+
msgstr ""
|
96 |
+
|
97 |
+
#: includes/class-bwp-enqueued-detector.php:446
|
98 |
+
#, php-format
|
99 |
+
msgid "separated in %s"
|
100 |
+
msgstr ""
|
101 |
+
|
102 |
+
#: includes/class-bwp-enqueued-detector.php:447
|
103 |
+
#, php-format
|
104 |
+
msgid "minified in %s"
|
105 |
+
msgstr ""
|
106 |
+
|
107 |
+
#: includes/class-bwp-enqueued-detector.php:453
|
108 |
+
msgid "forgotten"
|
109 |
+
msgstr ""
|
110 |
+
|
111 |
+
#: includes/class-bwp-enqueued-detector.php:454
|
112 |
+
#, php-format
|
113 |
+
msgid "ignored in %s"
|
114 |
+
msgstr ""
|
115 |
+
|
116 |
+
#: includes/class-bwp-framework-improved.php:211
|
117 |
+
#, php-format
|
118 |
+
msgid ""
|
119 |
+
"%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</"
|
120 |
+
"strong> or higher. The plugin will not protected function until you update "
|
121 |
+
"your software. Please deactivate this plugin."
|
122 |
+
msgstr ""
|
123 |
+
|
124 |
+
#: includes/class-bwp-framework-improved.php:228
|
125 |
+
msgid "Development Log"
|
126 |
+
msgstr ""
|
127 |
+
|
128 |
+
#: includes/class-bwp-framework-improved.php:228
|
129 |
+
msgid "Frequently Asked Questions"
|
130 |
+
msgstr ""
|
131 |
+
|
132 |
+
#: includes/class-bwp-framework-improved.php:228
|
133 |
+
msgid "FAQ"
|
134 |
+
msgstr ""
|
135 |
+
|
136 |
+
#: includes/class-bwp-framework-improved.php:228
|
137 |
+
msgid "Got a problem? Send me a feedback!"
|
138 |
+
msgstr ""
|
139 |
+
|
140 |
+
#: includes/class-bwp-framework-improved.php:228
|
141 |
+
msgid "Contact"
|
142 |
+
msgstr ""
|
143 |
+
|
144 |
+
#: includes/class-bwp-framework-improved.php:235
|
145 |
+
msgid ""
|
146 |
+
"You can buy me some special coffees if you appreciate my work, thank you!"
|
147 |
+
msgstr ""
|
148 |
+
|
149 |
+
#: includes/class-bwp-framework-improved.php:249
|
150 |
+
#, php-format
|
151 |
+
msgid "Donate to %s"
|
152 |
+
msgstr ""
|
153 |
+
|
154 |
+
#: includes/class-bwp-framework-improved.php:251
|
155 |
+
msgid "One cup $5.00"
|
156 |
+
msgstr ""
|
157 |
+
|
158 |
+
#: includes/class-bwp-framework-improved.php:252
|
159 |
+
msgid "Two cups $10.00"
|
160 |
+
msgstr ""
|
161 |
+
|
162 |
+
#: includes/class-bwp-framework-improved.php:253
|
163 |
+
msgid "Five cups! $25.00"
|
164 |
+
msgstr ""
|
165 |
+
|
166 |
+
#: includes/class-bwp-framework-improved.php:254
|
167 |
+
msgid "One LL-cup!!! $50.00"
|
168 |
+
msgstr ""
|
169 |
+
|
170 |
+
#: includes/class-bwp-framework-improved.php:255
|
171 |
+
msgid "... or any amount!"
|
172 |
+
msgstr ""
|
173 |
+
|
174 |
+
#: includes/class-bwp-framework-improved.php:270
|
175 |
+
msgid "Latest updates from BetterWP.net!"
|
176 |
+
msgstr ""
|
177 |
+
|
178 |
+
#: includes/class-bwp-framework-improved.php:271
|
179 |
+
msgid "Follow me on Twitter!"
|
180 |
+
msgstr ""
|
181 |
+
|
182 |
+
#: includes/class-bwp-framework-improved.php:281
|
183 |
+
msgid "This Plugin is Proudly Sponsored By"
|
184 |
+
msgstr ""
|
185 |
+
|
186 |
+
#: includes/class-bwp-framework-improved.php:300
|
187 |
+
#, php-format
|
188 |
+
msgid "You are using version %s!"
|
189 |
+
msgstr ""
|
190 |
+
|
191 |
+
#: includes/class-bwp-framework-improved.php:541
|
192 |
+
msgid "Settings"
|
193 |
+
msgstr ""
|
194 |
+
|
195 |
+
#: includes/class-bwp-minify-fetcher.php:108
|
196 |
+
#, php-format
|
197 |
+
msgid "Minify group %s not found."
|
198 |
+
msgstr ""
|
199 |
+
|
200 |
+
#: includes/class-bwp-minify-fetcher.php:121
|
201 |
+
msgid "Invalid Minify directory, please check Minify settings."
|
202 |
+
msgstr ""
|
203 |
+
|
204 |
+
#: includes/class-bwp-minify.php:211 includes/class-bwp-minify.php:939
|
205 |
+
#: includes/class-bwp-minify.php:940
|
206 |
+
msgid "General Options"
|
207 |
+
msgstr ""
|
208 |
+
|
209 |
+
#: includes/class-bwp-minify.php:214 includes/class-bwp-minify.php:947
|
210 |
+
#: includes/class-bwp-minify.php:948
|
211 |
+
msgid "Advanced Options"
|
212 |
+
msgstr ""
|
213 |
+
|
214 |
+
#: includes/class-bwp-minify.php:217 includes/class-bwp-minify.php:955
|
215 |
+
msgid "Manage enqueued Files"
|
216 |
+
msgstr ""
|
217 |
+
|
218 |
+
#: includes/class-bwp-minify.php:929
|
219 |
+
msgid "Better WordPress Minify"
|
220 |
+
msgstr ""
|
221 |
+
|
222 |
+
#: includes/class-bwp-minify.php:956
|
223 |
+
msgid "Enqueued Files"
|
224 |
+
msgstr ""
|
225 |
+
|
226 |
+
#: includes/class-bwp-minify.php:971
|
227 |
+
msgid "You do not have sufficient permissions to access this page."
|
228 |
+
msgstr ""
|
229 |
+
|
230 |
+
#: includes/class-bwp-minify.php:1061
|
231 |
+
msgid "Plugin Functionality"
|
232 |
+
msgstr ""
|
233 |
+
|
234 |
+
#: includes/class-bwp-minify.php:1062
|
235 |
+
msgid "Minify JS files automatically?"
|
236 |
+
msgstr ""
|
237 |
+
|
238 |
+
#: includes/class-bwp-minify.php:1063
|
239 |
+
msgid "Minify CSS files automatically?"
|
240 |
+
msgstr ""
|
241 |
+
|
242 |
+
#: includes/class-bwp-minify.php:1064
|
243 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
244 |
+
msgstr ""
|
245 |
+
|
246 |
+
#: includes/class-bwp-minify.php:1065
|
247 |
+
msgid "URL path to Minify library (relative to domain root)"
|
248 |
+
msgstr ""
|
249 |
+
|
250 |
+
#: includes/class-bwp-minify.php:1066
|
251 |
+
msgid "One minify string will contain"
|
252 |
+
msgstr ""
|
253 |
+
|
254 |
+
#: includes/class-bwp-minify.php:1067
|
255 |
+
msgid "For cache buster, use"
|
256 |
+
msgstr ""
|
257 |
+
|
258 |
+
#: includes/class-bwp-minify.php:1068
|
259 |
+
msgid "Minify Library Settings"
|
260 |
+
msgstr ""
|
261 |
+
|
262 |
+
#: includes/class-bwp-minify.php:1069
|
263 |
+
msgid "WordPress document root"
|
264 |
+
msgstr ""
|
265 |
+
|
266 |
+
#: includes/class-bwp-minify.php:1070
|
267 |
+
msgid "Cache directory"
|
268 |
+
msgstr ""
|
269 |
+
|
270 |
+
#: includes/class-bwp-minify.php:1071
|
271 |
+
msgid "Cache age"
|
272 |
+
msgstr ""
|
273 |
+
|
274 |
+
#: includes/class-bwp-minify.php:1072
|
275 |
+
msgid "Enable bubble CSS import?"
|
276 |
+
msgstr ""
|
277 |
+
|
278 |
+
#: includes/class-bwp-minify.php:1073
|
279 |
+
msgid "Enable cache file locking?"
|
280 |
+
msgstr ""
|
281 |
+
|
282 |
+
#: includes/class-bwp-minify.php:1074
|
283 |
+
msgid "Enable debugging?"
|
284 |
+
msgstr ""
|
285 |
+
|
286 |
+
#: includes/class-bwp-minify.php:1095
|
287 |
+
#, php-format
|
288 |
+
msgid ""
|
289 |
+
"<em>These options will let you control how the actual <a href=\"%s\" target="
|
290 |
+
"\"_blank\">Minify</a> library works.</em>"
|
291 |
+
msgstr ""
|
292 |
+
|
293 |
+
#: includes/class-bwp-minify.php:1102
|
294 |
+
msgid "second(s)"
|
295 |
+
msgstr ""
|
296 |
+
|
297 |
+
#: includes/class-bwp-minify.php:1103
|
298 |
+
msgid "minute(s)"
|
299 |
+
msgstr ""
|
300 |
+
|
301 |
+
#: includes/class-bwp-minify.php:1104
|
302 |
+
msgid "hour(s)"
|
303 |
+
msgstr ""
|
304 |
+
|
305 |
+
#: includes/class-bwp-minify.php:1105
|
306 |
+
msgid "day(s)"
|
307 |
+
msgstr ""
|
308 |
+
|
309 |
+
#: includes/class-bwp-minify.php:1108
|
310 |
+
msgid "No cache buster"
|
311 |
+
msgstr ""
|
312 |
+
|
313 |
+
#: includes/class-bwp-minify.php:1109
|
314 |
+
msgid "Cache folder’s last modified time"
|
315 |
+
msgstr ""
|
316 |
+
|
317 |
+
#: includes/class-bwp-minify.php:1110
|
318 |
+
msgid "Your WordPress’s current version"
|
319 |
+
msgstr ""
|
320 |
+
|
321 |
+
#: includes/class-bwp-minify.php:1111
|
322 |
+
msgid "Your theme’s current version"
|
323 |
+
msgstr ""
|
324 |
+
|
325 |
+
#: includes/class-bwp-minify.php:1112
|
326 |
+
msgid "A custom number"
|
327 |
+
msgstr ""
|
328 |
+
|
329 |
+
#: includes/class-bwp-minify.php:1116 includes/class-bwp-minify.php:1117
|
330 |
+
msgid ""
|
331 |
+
"you can still use <code>bwp_minify()</code> helper function if you disable "
|
332 |
+
"this."
|
333 |
+
msgstr ""
|
334 |
+
|
335 |
+
#: includes/class-bwp-minify.php:1118
|
336 |
+
msgid ""
|
337 |
+
"enable this for themes that use <code>bloginfo()</code> to print the main "
|
338 |
+
"stylesheet (i.e. <code>style.css</code>). If you want to minify <code>style."
|
339 |
+
"css</code> with the rest of your css files, you must enqueue it."
|
340 |
+
msgstr ""
|
341 |
+
|
342 |
+
#: includes/class-bwp-minify.php:1119
|
343 |
+
#, php-format
|
344 |
+
msgid ""
|
345 |
+
"move all <code>@import</code> rules in CSS files to the top. More info <a "
|
346 |
+
"href=\"%s\" target=\"_blank\">here</a>."
|
347 |
+
msgstr ""
|
348 |
+
|
349 |
+
#: includes/class-bwp-minify.php:1120
|
350 |
+
msgid "disable this if filesystem is NFS."
|
351 |
+
msgstr ""
|
352 |
+
|
353 |
+
#: includes/class-bwp-minify.php:1121
|
354 |
+
#, php-format
|
355 |
+
msgid ""
|
356 |
+
"only enable this when minification does not work as expected. More info <a "
|
357 |
+
"href=\"%s\" target=\"_blank\">here</a>."
|
358 |
+
msgstr ""
|
359 |
+
|
360 |
+
#: includes/class-bwp-minify.php:1127
|
361 |
+
#, php-format
|
362 |
+
msgid ""
|
363 |
+
"Leave empty to use default value, which is <code>%s</code>.<br />Please read "
|
364 |
+
"<a href=\"%s#advanced_customization\" target=\"_blank\">here</a> to know how "
|
365 |
+
"to properly modify this."
|
366 |
+
msgstr ""
|
367 |
+
|
368 |
+
#: includes/class-bwp-minify.php:1139
|
369 |
+
#, php-format
|
370 |
+
msgid ""
|
371 |
+
"Expect a full path to a publicly accessible directory (i.e. can be served "
|
372 |
+
"under your document root). <br />Leave empty to use default value, which is "
|
373 |
+
"<code>%s</code>.<br />Cache directory must be writable (i.e. CHMOD to 755 or "
|
374 |
+
"777). More details can be found <a href=\"%s#minify_cache_directory\" target="
|
375 |
+
"\"_blank\">here</a>. "
|
376 |
+
msgstr ""
|
377 |
+
|
378 |
+
#: includes/class-bwp-minify.php:1150
|
379 |
+
msgid "file(s) at most."
|
380 |
+
msgstr ""
|
381 |
+
|
382 |
+
#: includes/class-bwp-minify.php:1154
|
383 |
+
msgid "—"
|
384 |
+
msgstr ""
|
385 |
+
|
386 |
+
#: includes/class-bwp-minify.php:1170
|
387 |
+
msgid "Note"
|
388 |
+
msgstr ""
|
389 |
+
|
390 |
+
#: includes/class-bwp-minify.php:1171
|
391 |
+
msgid ""
|
392 |
+
"Whenever a new cache buster is used, you are telling browsers to refresh "
|
393 |
+
"cached JS and CSS files. This is particularly useful when source files have "
|
394 |
+
"been changed, use this feature wisely."
|
395 |
+
msgstr ""
|
396 |
+
|
397 |
+
#: includes/class-bwp-minify.php:1214 includes/class-bwp-minify.php:1223
|
398 |
+
#: includes/class-bwp-minify.php:1564 includes/class-bwp-minify.php:1786
|
399 |
+
#: includes/class-bwp-minify.php:1884
|
400 |
+
msgid "Notice"
|
401 |
+
msgstr ""
|
402 |
+
|
403 |
+
#: includes/class-bwp-minify.php:1216
|
404 |
+
#, php-format
|
405 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
406 |
+
msgstr ""
|
407 |
+
|
408 |
+
#: includes/class-bwp-minify.php:1224
|
409 |
+
msgid ""
|
410 |
+
"Could not delete any cached files. Please manually flush the cache directory."
|
411 |
+
msgstr ""
|
412 |
+
|
413 |
+
#: includes/class-bwp-minify.php:1251
|
414 |
+
msgid "Friendly Minify Urls"
|
415 |
+
msgstr ""
|
416 |
+
|
417 |
+
#: includes/class-bwp-minify.php:1252
|
418 |
+
msgid "Enable friendly Minify urls"
|
419 |
+
msgstr ""
|
420 |
+
|
421 |
+
#: includes/class-bwp-minify.php:1254
|
422 |
+
#, php-format
|
423 |
+
msgid ""
|
424 |
+
"Friendly Minify url path (relative to your Site/Network Address). More info "
|
425 |
+
"<a href=\"%s#friendly_minify_urls\" target=\"_blank\">here</a>."
|
426 |
+
msgstr ""
|
427 |
+
|
428 |
+
#: includes/class-bwp-minify.php:1259
|
429 |
+
msgid "Path to Nginx config file"
|
430 |
+
msgstr ""
|
431 |
+
|
432 |
+
#: includes/class-bwp-minify.php:1260
|
433 |
+
msgid "Content Delivery Network (CDN)"
|
434 |
+
msgstr ""
|
435 |
+
|
436 |
+
#: includes/class-bwp-minify.php:1261
|
437 |
+
msgid "Enable CDN support"
|
438 |
+
msgstr ""
|
439 |
+
|
440 |
+
#: includes/class-bwp-minify.php:1262
|
441 |
+
msgid "SSL support for CDN"
|
442 |
+
msgstr ""
|
443 |
+
|
444 |
+
#: includes/class-bwp-minify.php:1263
|
445 |
+
msgid "CDN hostname (primary)"
|
446 |
+
msgstr ""
|
447 |
+
|
448 |
+
#: includes/class-bwp-minify.php:1264
|
449 |
+
msgid "CDN hostname for JS files"
|
450 |
+
msgstr ""
|
451 |
+
|
452 |
+
#: includes/class-bwp-minify.php:1265
|
453 |
+
msgid "CDN hostname for CSS files"
|
454 |
+
msgstr ""
|
455 |
+
|
456 |
+
#: includes/class-bwp-minify.php:1266
|
457 |
+
msgid "Additional HTTP headers used with CDN"
|
458 |
+
msgstr ""
|
459 |
+
|
460 |
+
#: includes/class-bwp-minify.php:1282
|
461 |
+
#, php-format
|
462 |
+
msgid ""
|
463 |
+
"Turn long and ugly Minify urls with query variables (such as <code>%s</"
|
464 |
+
"code>), into more friendly ones (e.g. <code>%s</code>). "
|
465 |
+
msgstr ""
|
466 |
+
|
467 |
+
#: includes/class-bwp-minify.php:1291
|
468 |
+
#, php-format
|
469 |
+
msgid ""
|
470 |
+
"Serve minified contents using a <a href=\"%s\" target=\"_blank\">CDN</a> to "
|
471 |
+
"improve performance."
|
472 |
+
msgstr ""
|
473 |
+
|
474 |
+
#: includes/class-bwp-minify.php:1298
|
475 |
+
msgid "WordPress"
|
476 |
+
msgstr ""
|
477 |
+
|
478 |
+
#: includes/class-bwp-minify.php:1299
|
479 |
+
msgid "Server rewrite rules"
|
480 |
+
msgstr ""
|
481 |
+
|
482 |
+
#: includes/class-bwp-minify.php:1302
|
483 |
+
msgid "Do not use SSL for CDN"
|
484 |
+
msgstr ""
|
485 |
+
|
486 |
+
#: includes/class-bwp-minify.php:1303
|
487 |
+
msgid "Use SSL when suitable"
|
488 |
+
msgstr ""
|
489 |
+
|
490 |
+
#: includes/class-bwp-minify.php:1304
|
491 |
+
msgid "Use protocol-relative URL"
|
492 |
+
msgstr ""
|
493 |
+
|
494 |
+
#: includes/class-bwp-minify.php:1309
|
495 |
+
msgid "for CDN support it is highly recommended that you enable this feature."
|
496 |
+
msgstr ""
|
497 |
+
|
498 |
+
#: includes/class-bwp-minify.php:1312
|
499 |
+
msgid ""
|
500 |
+
"please make sure that your CDN is property setup before enabling this "
|
501 |
+
"feature."
|
502 |
+
msgstr ""
|
503 |
+
|
504 |
+
#: includes/class-bwp-minify.php:1323
|
505 |
+
#, php-format
|
506 |
+
msgid ""
|
507 |
+
"Expect a full path to your Nginx configuration file (e.g. <code>%s</code>). "
|
508 |
+
"If none specified or the config file is not writable, you will have to "
|
509 |
+
"manually update it. Alternatively, if you have a config file at <code>%s</"
|
510 |
+
"code>, which is used by W3 Total Cache plugin, you can leave this field "
|
511 |
+
"blank and BWP Minify will use that config file."
|
512 |
+
msgstr ""
|
513 |
+
|
514 |
+
#: includes/class-bwp-minify.php:1337
|
515 |
+
#, php-format
|
516 |
+
msgid ""
|
517 |
+
"Use either hostnames provided by your CDN or custom ones. Please do NOT "
|
518 |
+
"include the scheme (i.e. <code>http://</code> or <code>https://</code>). "
|
519 |
+
"Good examples are: <code>%s</code>, <code>%s</code>, etc."
|
520 |
+
msgstr ""
|
521 |
+
|
522 |
+
#: includes/class-bwp-minify.php:1346 includes/class-bwp-minify.php:1350
|
523 |
+
msgid "used when not empty."
|
524 |
+
msgstr ""
|
525 |
+
|
526 |
+
#: includes/class-bwp-minify.php:1360
|
527 |
+
msgid ""
|
528 |
+
"If you're looking for a CDN, I highly recommend <strong>MaxCDN</strong>. "
|
529 |
+
"They provide the most reliable CDN service for WordPress sites. Support is "
|
530 |
+
"exceptional and you got a 30-day Money Back Guarantee, too."
|
531 |
+
msgstr ""
|
532 |
+
|
533 |
+
#: includes/class-bwp-minify.php:1367
|
534 |
+
msgid "Create a MaxCDN account today and save 25%</a>"
|
535 |
+
msgstr ""
|
536 |
+
|
537 |
+
#: includes/class-bwp-minify.php:1391
|
538 |
+
msgid ""
|
539 |
+
"Enqueued file lists have been cleared successfully. Try refreshing this page "
|
540 |
+
"to see updated file lists."
|
541 |
+
msgstr ""
|
542 |
+
|
543 |
+
#: includes/class-bwp-minify.php:1402
|
544 |
+
msgid "Move enqueued JS files to appropriate positions"
|
545 |
+
msgstr ""
|
546 |
+
|
547 |
+
#: includes/class-bwp-minify.php:1403
|
548 |
+
msgid "Move enqueued CSS files to appropriate positions"
|
549 |
+
msgstr ""
|
550 |
+
|
551 |
+
#: includes/class-bwp-minify.php:1411
|
552 |
+
#, php-format
|
553 |
+
msgid ""
|
554 |
+
"Below you can find a list of enqueued JS files detected by this plugin. "
|
555 |
+
"Press <strong>select</strong> and then choose an appropriate position for "
|
556 |
+
"selected JS file. You can also directly type in one script handle "
|
557 |
+
"(<strong>NOT filename/script src</strong>) per line in the input field if "
|
558 |
+
"you want. More info <a href=\"%s#positioning_your_files\" target=\"_blank"
|
559 |
+
"\">here</a>."
|
560 |
+
msgstr ""
|
561 |
+
|
562 |
+
#: includes/class-bwp-minify.php:1420
|
563 |
+
#, php-format
|
564 |
+
msgid ""
|
565 |
+
"Below you can find a list of enqueued CSS files detected by this plugin. "
|
566 |
+
"Press <strong>select</strong> and then choose an appropriate position for "
|
567 |
+
"selected CSS file. You can also directly type in one style handle "
|
568 |
+
"(<strong>NOT filename/style src</strong>) per line in the input field if you "
|
569 |
+
"want. More info <a href=\"%s#positioning_your_files\" target=\"_blank"
|
570 |
+
"\">here</a>."
|
571 |
+
msgstr ""
|
572 |
+
|
573 |
+
#: includes/class-bwp-minify.php:1514
|
574 |
+
msgid ""
|
575 |
+
"All positions have been saved. Try refreshing this page for updated file "
|
576 |
+
"lists."
|
577 |
+
msgstr ""
|
578 |
+
|
579 |
+
#: includes/class-bwp-minify.php:1518
|
580 |
+
msgid "All options have been saved."
|
581 |
+
msgstr ""
|
582 |
+
|
583 |
+
#: includes/class-bwp-minify.php:1530
|
584 |
+
#, php-format
|
585 |
+
msgid "Minify config file <code>%s</code> has been updated successfully."
|
586 |
+
msgstr ""
|
587 |
+
|
588 |
+
#: includes/class-bwp-minify.php:1539 includes/class-bwp-minify.php:1553
|
589 |
+
#: includes/class-bwp-minify.php:1978 includes/class-bwp-minify.php:1984
|
590 |
+
#: includes/class-bwp-minify.php:1994 includes/class-bwp-minify.php:2006
|
591 |
+
msgid "Error"
|
592 |
+
msgstr ""
|
593 |
+
|
594 |
+
#: includes/class-bwp-minify.php:1541
|
595 |
+
#, php-format
|
596 |
+
msgid ""
|
597 |
+
"Minify config file <code>config.php</code> could not be found. The auto-"
|
598 |
+
"detected directory to look for the config file is <code>%s</code>. Please "
|
599 |
+
"manually check if that directory actually exists and contains the config "
|
600 |
+
"file."
|
601 |
+
msgstr ""
|
602 |
+
|
603 |
+
#: includes/class-bwp-minify.php:1554
|
604 |
+
#, php-format
|
605 |
+
msgid ""
|
606 |
+
"There was an error writing to Minify config file <code>%s</code>. Please try "
|
607 |
+
"again."
|
608 |
+
msgstr ""
|
609 |
+
|
610 |
+
#: includes/class-bwp-minify.php:1565
|
611 |
+
#, php-format
|
612 |
+
msgid ""
|
613 |
+
"Minify config file <code>%s</code> is not writable. See <a href=\"#minify."
|
614 |
+
"config.php\">below</a> for details."
|
615 |
+
msgstr ""
|
616 |
+
|
617 |
+
#: includes/class-bwp-minify.php:1608 includes/class-bwp-minify.php:1620
|
618 |
+
msgid "Warning"
|
619 |
+
msgstr ""
|
620 |
+
|
621 |
+
#: includes/class-bwp-minify.php:1610
|
622 |
+
#, php-format
|
623 |
+
msgid ""
|
624 |
+
"Cache directory <code>%s</code> does not exist and can not be created "
|
625 |
+
"automatically. Please manually create the cache folder and make sure that it "
|
626 |
+
"is writable for Minify to perform more efficiently."
|
627 |
+
msgstr ""
|
628 |
+
|
629 |
+
#: includes/class-bwp-minify.php:1622
|
630 |
+
#, php-format
|
631 |
+
msgid ""
|
632 |
+
"Cache directory <code>%s</code> is not writable. Please try CHMOD your cache "
|
633 |
+
"directory to 755. If you still see this warning, try CHMOD to 777."
|
634 |
+
msgstr ""
|
635 |
+
|
636 |
+
#: includes/class-bwp-minify.php:1660
|
637 |
+
msgid "Styles to be ignored (not minified)"
|
638 |
+
msgstr ""
|
639 |
+
|
640 |
+
#: includes/class-bwp-minify.php:1661
|
641 |
+
msgid "Styles to be minified and then printed separately"
|
642 |
+
msgstr ""
|
643 |
+
|
644 |
+
#: includes/class-bwp-minify.php:1662
|
645 |
+
msgid "Styles to be forgotten (to remove duplicate styles)"
|
646 |
+
msgstr ""
|
647 |
+
|
648 |
+
#: includes/class-bwp-minify.php:1671
|
649 |
+
msgid "Scripts to be ignored (not minified)"
|
650 |
+
msgstr ""
|
651 |
+
|
652 |
+
#: includes/class-bwp-minify.php:1672
|
653 |
+
msgid "Scripts to be minified and then printed separately"
|
654 |
+
msgstr ""
|
655 |
+
|
656 |
+
#: includes/class-bwp-minify.php:1673
|
657 |
+
msgid "Scripts to be minified in header"
|
658 |
+
msgstr ""
|
659 |
+
|
660 |
+
#: includes/class-bwp-minify.php:1674
|
661 |
+
msgid "Scripts to be minified in footer"
|
662 |
+
msgstr ""
|
663 |
+
|
664 |
+
#: includes/class-bwp-minify.php:1675
|
665 |
+
msgid "Scripts to be forgotten (to remove duplicate scripts)"
|
666 |
+
msgstr ""
|
667 |
+
|
668 |
+
#: includes/class-bwp-minify.php:1733
|
669 |
+
msgid "Save Changes and Flush Cache"
|
670 |
+
msgstr ""
|
671 |
+
|
672 |
+
#: includes/class-bwp-minify.php:1736
|
673 |
+
msgid "Flush Cache"
|
674 |
+
msgstr ""
|
675 |
+
|
676 |
+
#: includes/class-bwp-minify.php:1748
|
677 |
+
msgid "Clear File Lists"
|
678 |
+
msgstr ""
|
679 |
+
|
680 |
+
#: includes/class-bwp-minify.php:1762
|
681 |
+
#, php-format
|
682 |
+
msgid ""
|
683 |
+
"BWP Minify was not able to auto-detect friendly url based on Minify's "
|
684 |
+
"current cache directory (<code>%s</code>)."
|
685 |
+
msgstr ""
|
686 |
+
|
687 |
+
#: includes/class-bwp-minify.php:1768
|
688 |
+
msgid ""
|
689 |
+
"You must manually set this setting for this feature to work.<br />Please "
|
690 |
+
"make sure that the entered URL path correctly links to Minify's current "
|
691 |
+
"cache directory."
|
692 |
+
msgstr ""
|
693 |
+
|
694 |
+
#: includes/class-bwp-minify.php:1776
|
695 |
+
#, php-format
|
696 |
+
msgid ""
|
697 |
+
"Leave empty to use <code>%s</code>, which is auto-detected based on Minify's "
|
698 |
+
"current cache directory (<code>%s</code>). The URL path (either manually "
|
699 |
+
"entered or auto-detected) must correctly link to Minify's current cache "
|
700 |
+
"directory."
|
701 |
+
msgstr ""
|
702 |
+
|
703 |
+
#: includes/class-bwp-minify.php:1787
|
704 |
+
#, php-format
|
705 |
+
msgid ""
|
706 |
+
"In a sub-directory multisite environment, a blog path will be added before "
|
707 |
+
"the URL path (e.g. <code>%s</code>) when friendly Minify urls are served."
|
708 |
+
msgstr ""
|
709 |
+
|
710 |
+
#: includes/class-bwp-minify.php:1807
|
711 |
+
#, php-format
|
712 |
+
msgid ""
|
713 |
+
"Leave empty to use parent directory of WordPress's <code>index.php</code>, "
|
714 |
+
"which is <code>%s</code>. If you want to include JS and CSS files outside of "
|
715 |
+
"this root, or you happen to move <code>wp-content</code> directory somewhere "
|
716 |
+
"else, make sure you set this to the correct directory with NO trailing "
|
717 |
+
"slash. This setting is very important as it makes sure that Minify library "
|
718 |
+
"can correctly locate your JS, CSS files. More info can be found <a href="
|
719 |
+
"\"%s#minify_document_root\" target=\"_blank\">here</a>."
|
720 |
+
msgstr ""
|
721 |
+
|
722 |
+
#: includes/class-bwp-minify.php:1824
|
723 |
+
#, php-format
|
724 |
+
msgid ""
|
725 |
+
"Leave empty to use <code>$_SERVER['DOCUMENT_ROOT']</code>, which is <code>"
|
726 |
+
"%s</code>. This is only used when it is not empty and your WordPress does "
|
727 |
+
"live under it.<br />If you believe the auto-detected document root is wrong, "
|
728 |
+
"make sure you set it to the correct directory with NO trailing slash. <br /"
|
729 |
+
">This setting is very important as it makes sure that Minify can correctly "
|
730 |
+
"locate your JS, CSS files. More info can be found <a href="
|
731 |
+
"\"%s#minify_document_root\" target=\"_blank\">here</a>."
|
732 |
+
msgstr ""
|
733 |
+
|
734 |
+
#: includes/class-bwp-minify.php:1885
|
735 |
+
msgid ""
|
736 |
+
"Friendly minify url feature has been turned off automatically to prevent "
|
737 |
+
"your site from breaking. "
|
738 |
+
msgstr ""
|
739 |
+
|
740 |
+
#: includes/class-bwp-minify.php:1904
|
741 |
+
#, php-format
|
742 |
+
msgid ""
|
743 |
+
"Could not write Minify library settings to <code>%s</code>. Please update "
|
744 |
+
"the config file manually by <em>replacing</em> its current contents with "
|
745 |
+
"auto-generated contents as shown below:"
|
746 |
+
msgstr ""
|
747 |
+
|
748 |
+
#: includes/class-bwp-minify.php:1922
|
749 |
+
#, php-format
|
750 |
+
msgid ""
|
751 |
+
"Please update the Nginx config file manually using auto-generated contents "
|
752 |
+
"as shown below. It is highly recommended that you create a separate config "
|
753 |
+
"file for BWP Minify (e.g. <code>bwp-minify.conf</code>) and include it "
|
754 |
+
"directly above where you include your cache plugins' config files, as shown "
|
755 |
+
"<a href=\"%s\" target=\"_blank\">here</a>. "
|
756 |
+
msgstr ""
|
757 |
+
|
758 |
+
#: includes/class-bwp-minify.php:1935
|
759 |
+
#, php-format
|
760 |
+
msgid ""
|
761 |
+
"Please update the server config file <code>%s</code> manually using auto-"
|
762 |
+
"generated contents as shown below. It is highly recommended that you paste "
|
763 |
+
"the contents at the top of the server config file. If config file does not "
|
764 |
+
"exist, you must first create it."
|
765 |
+
msgstr ""
|
766 |
+
|
767 |
+
#: includes/class-bwp-minify.php:1954
|
768 |
+
#, php-format
|
769 |
+
msgid ""
|
770 |
+
"Please update the cache directory config file <code>%s</code> manually using "
|
771 |
+
"auto-generated contents as shown below. It is highly recommended that you "
|
772 |
+
"paste the contents at the top of the server config file. If config file does "
|
773 |
+
"not exist, you must first create it."
|
774 |
+
msgstr ""
|
775 |
+
|
776 |
+
#: includes/class-bwp-minify.php:1979
|
777 |
+
#, php-format
|
778 |
+
msgid ""
|
779 |
+
"There was an error writing to server config file <code>%s</code>. Please "
|
780 |
+
"make sure that it is writable and try again, or you can manually update the "
|
781 |
+
"config file using auto-generated contents as shown below."
|
782 |
+
msgstr ""
|
783 |
+
|
784 |
+
#: includes/class-bwp-minify.php:1985
|
785 |
+
msgid ""
|
786 |
+
"There was an error writing to your Nginx config file. Please make sure that "
|
787 |
+
"it is writable and try again, or you can manually update the config file "
|
788 |
+
"using auto-generated contents as shown below."
|
789 |
+
msgstr ""
|
790 |
+
|
791 |
+
#: includes/class-bwp-minify.php:1995
|
792 |
+
#, php-format
|
793 |
+
msgid ""
|
794 |
+
"The server config file <code>%s</code> is not writable, please manually "
|
795 |
+
"update it using auto-generated contents as shown below."
|
796 |
+
msgstr ""
|
797 |
+
|
798 |
+
#: includes/class-bwp-minify.php:2007
|
799 |
+
#, php-format
|
800 |
+
msgid ""
|
801 |
+
"The server config file <code>%s</code> does not exist and could not be "
|
802 |
+
"automatically created, please manually create it using auto-generated "
|
803 |
+
"contents as shown below."
|
804 |
+
msgstr ""
|
805 |
+
|
806 |
+
#: includes/class-bwp-minify.php:2313
|
807 |
+
msgid "empty"
|
808 |
+
msgstr ""
|
cache/.htaccess
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<Limit POST PUT DELETE>
|
2 |
+
Order Allow,Deny
|
3 |
+
Deny from All
|
4 |
+
</Limit>
|
css/bwp-minify.css
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.bwp-minify-table-wrapper {
|
2 |
+
position: relative;
|
3 |
+
float: left;
|
4 |
+
width: 65%;
|
5 |
+
background-color: #ffffff;
|
6 |
+
border: 1px solid #cccccc;
|
7 |
+
/* padding: 5px; */
|
8 |
+
margin-right: 10px;
|
9 |
+
overflow-x: hidden;
|
10 |
+
}
|
11 |
+
.bwp-minify-table-scroller {
|
12 |
+
height: 300px;
|
13 |
+
overflow: auto;
|
14 |
+
margin-top: 30px;
|
15 |
+
border-top: 1px solid #e5e5e5;
|
16 |
+
padding: 0 5px;
|
17 |
+
}
|
18 |
+
.bwp-minify-detector-table th {
|
19 |
+
min-width: 50px;
|
20 |
+
}
|
21 |
+
.bwp-minify-detector-table th .bwp-text {
|
22 |
+
position: absolute;
|
23 |
+
height: 30px;
|
24 |
+
top: 7px;
|
25 |
+
z-index: 2;
|
26 |
+
}
|
27 |
+
.bwp-minify-detector-table tr {
|
28 |
+
/* position: relative; */
|
29 |
+
}
|
30 |
+
.bwp-minify-detector-table td, .bwp-minify-detector-table th {
|
31 |
+
padding: 2px 5px;
|
32 |
+
text-align: left;
|
33 |
+
}
|
34 |
+
.bwp-minify-detector-table td {
|
35 |
+
padding: 4px 5px;
|
36 |
+
}
|
37 |
+
.bwp-minify-detector-table td input, .bwp-minify-detector-table td input:focus {
|
38 |
+
width: 300px;
|
39 |
+
border: none;
|
40 |
+
background: none;
|
41 |
+
box-shadow: none;
|
42 |
+
padding: 0;
|
43 |
+
margin: 0;
|
44 |
+
font-size: 13px;
|
45 |
+
}
|
46 |
+
.bwp-minify-detector-table .action-handles, .bwp-minify-detector-table .data-handle {
|
47 |
+
display: none;
|
48 |
+
}
|
49 |
+
.bwp-minify-detector-table .action-handles {
|
50 |
+
position: absolute;
|
51 |
+
background-color: #ffffff;
|
52 |
+
padding: 5px 10px;
|
53 |
+
/* border: 1px dashed #cccccc; */
|
54 |
+
right: 0px;
|
55 |
+
top: 0px;
|
56 |
+
z-index: 3;
|
57 |
+
}
|
58 |
+
.bwp-sidebar {
|
59 |
+
width: auto;
|
60 |
+
overflow: auto;
|
61 |
+
}
|
62 |
+
.bwp-sidebar textarea {
|
63 |
+
width: 90%;
|
64 |
+
margin-top: 10px;
|
65 |
+
display: none;
|
66 |
+
}
|
67 |
+
.bwp-sidebar .bwp-sign {
|
68 |
+
font-weight: bold;
|
69 |
+
font-size: 1.1em;
|
70 |
+
}
|
images/icon_menu.png
ADDED
Binary file
|
images/icon_menu_32.png
ADDED
Binary file
|
includes/bwp-option-page/bwp-option-page.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if (!is_admin()) return;
|
3 |
+
require_once(dirname(__FILE__) . '/includes/class-bwp-option-page.php');
|
includes/bwp-option-page/css/bwp-option-page.css
ADDED
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* For WordPress of older versions */
|
2 |
+
.nav-tab{
|
3 |
+
border-style:solid;
|
4 |
+
border-color:#ccc #ccc #f9f9f9;
|
5 |
+
border-width:1px 1px 0;
|
6 |
+
color:#c1c1c1;
|
7 |
+
text-shadow:rgba(255,255,255,1) 0 1px 0;
|
8 |
+
font-size:12px;
|
9 |
+
line-height:16px;
|
10 |
+
display:inline-block;
|
11 |
+
padding:4px 14px 6px;
|
12 |
+
text-decoration:none;
|
13 |
+
margin:0 3px -1px 0;
|
14 |
+
/* -moz-border-radius:5px 5px 0 0; */
|
15 |
+
/* -webkit-border-top-left-radius:5px; */
|
16 |
+
/* -webkit-border-top-right-radius:5px; */
|
17 |
+
/* -khtml-border-top-left-radius:5px; */
|
18 |
+
/* -khtml-border-top-right-radius:5px; */
|
19 |
+
/* border-top-left-radius:5px; */
|
20 |
+
/* border-top-right-radius:5px; */
|
21 |
+
}
|
22 |
+
.nav-tab-active{
|
23 |
+
border-width:1px;
|
24 |
+
border-bottom-color: transparent;
|
25 |
+
color: #000000;
|
26 |
+
}
|
27 |
+
h2.nav-tab-wrapper, h3.nav-tab-wrapper{
|
28 |
+
border-bottom:1px solid #ccc;
|
29 |
+
padding-bottom:0;
|
30 |
+
}
|
31 |
+
h2 .nav-tab{padding:6px 15px 6px;}
|
32 |
+
|
33 |
+
.bwp-option-page {
|
34 |
+
margin-top: 1em;
|
35 |
+
}
|
36 |
+
|
37 |
+
.bwp-option-page p {
|
38 |
+
margin-top: 0px;
|
39 |
+
margin-bottom: 0px;
|
40 |
+
zoom: 1;
|
41 |
+
}
|
42 |
+
|
43 |
+
.bwp-option-page li.bwp-clear, .bwp-option-page div.bwp-clear {
|
44 |
+
clear: left;
|
45 |
+
display: block;
|
46 |
+
margin: 1em 0 0 0;
|
47 |
+
zoom: 1;
|
48 |
+
}
|
49 |
+
|
50 |
+
.bwp-option-page p.submit {
|
51 |
+
clear: both;
|
52 |
+
padding-top: 1.5em;
|
53 |
+
}
|
54 |
+
|
55 |
+
.wrap .bwp-option-page-tabs {
|
56 |
+
border-bottom: 1px solid #CCCCCC;
|
57 |
+
padding-bottom: 0px;
|
58 |
+
margin-bottom: 20px;
|
59 |
+
}
|
60 |
+
|
61 |
+
.bwp-option-page-tabs a {
|
62 |
+
font-size: 18px;
|
63 |
+
line-height: 24px;
|
64 |
+
font-style: italic;
|
65 |
+
}
|
66 |
+
|
67 |
+
.bwp-option-page-tabs a.version {
|
68 |
+
font-size: 14px;
|
69 |
+
line-height: 14px;
|
70 |
+
}
|
71 |
+
|
72 |
+
.bwp-opton-page-label {
|
73 |
+
display: block;
|
74 |
+
float: left;
|
75 |
+
width: 200px;
|
76 |
+
margin: 3px 0 1em 0;
|
77 |
+
}
|
78 |
+
|
79 |
+
.type-checkbox {
|
80 |
+
margin-top: 0px;
|
81 |
+
}
|
82 |
+
|
83 |
+
p.bwp-option-page-inputs {
|
84 |
+
margin: 0 0 0 220px;
|
85 |
+
padding: 0;
|
86 |
+
}
|
87 |
+
|
88 |
+
.bwp-option-page-inputs input {
|
89 |
+
margin: 0 0 5px 0;
|
90 |
+
}
|
91 |
+
|
92 |
+
.bwp-option-page-inputs label {
|
93 |
+
margin: 0;
|
94 |
+
}
|
95 |
+
|
96 |
+
.bwp-option-page-inputs select {
|
97 |
+
margin: 1px 0 0 0;
|
98 |
+
}
|
99 |
+
|
100 |
+
.bwp-option-page-inputs input[type="checkbox"] {
|
101 |
+
margin-left: 2px;
|
102 |
+
}
|
103 |
+
|
104 |
+
#icon-betterwp-net {
|
105 |
+
background: url("betterwp.jpg") no-repeat transparent;
|
106 |
+
border: 1px solid #E3E3E3;
|
107 |
+
height: 32px;
|
108 |
+
width: 32px;
|
109 |
+
}
|
110 |
+
|
111 |
+
#icon-bwp-plugin {
|
112 |
+
background-repeat: no-repeat;
|
113 |
+
background-position: left center;
|
114 |
+
margin-top: 21px; margin-left: 5px;
|
115 |
+
width: 36px;
|
116 |
+
}
|
117 |
+
|
118 |
+
#bwp-info-place {
|
119 |
+
float: right;
|
120 |
+
margin: 0 0 10px 10px;
|
121 |
+
}
|
122 |
+
|
123 |
+
#bwp-donation, #bwp-contact, #bwp-ads {
|
124 |
+
width: 255px;
|
125 |
+
padding: 7px; padding-bottom: 0px;
|
126 |
+
background-color: #ffffff;
|
127 |
+
border: 1px solid #cccccc;
|
128 |
+
text-align: center;
|
129 |
+
}
|
130 |
+
|
131 |
+
#bwp-donation p, #bwp-ads p {
|
132 |
+
margin: 0.5em 0 1em 0;
|
133 |
+
}
|
134 |
+
|
135 |
+
#bwp-ads {
|
136 |
+
padding-bottom: 5px;
|
137 |
+
}
|
138 |
+
|
139 |
+
#bwp-ads p {
|
140 |
+
text-decoration: underline;
|
141 |
+
}
|
142 |
+
|
143 |
+
#bwp-info-place small {
|
144 |
+
font-size: 0.8em;
|
145 |
+
}
|
146 |
+
|
147 |
+
#bwp-donation .paypal-submit {
|
148 |
+
vertical-align: bottom;
|
149 |
+
display: inline-block;
|
150 |
+
margin: 0 0 1px 2px;
|
151 |
+
padding: 0px;
|
152 |
+
}
|
153 |
+
|
154 |
+
.bwp-separator {
|
155 |
+
width: 271px;
|
156 |
+
}
|
157 |
+
|
158 |
+
#bwp-contact {
|
159 |
+
text-align: left;
|
160 |
+
padding: 7px;
|
161 |
+
}
|
162 |
+
|
163 |
+
#bwp-contact a {
|
164 |
+
display: block;
|
165 |
+
height: 20px;
|
166 |
+
font-size: 0.8em;
|
167 |
+
padding-left: 25px;
|
168 |
+
}
|
169 |
+
|
170 |
+
#bwp-contact .bwp-rss {
|
171 |
+
background: transparent url("../images/icon-rss.png") no-repeat left center;
|
172 |
+
}
|
173 |
+
|
174 |
+
#bwp-contact .bwp-twitter {
|
175 |
+
background: transparent url("../images/icon-twitter.png") no-repeat left center;
|
176 |
+
}
|
includes/bwp-option-page/images/ad_250x250.png
ADDED
Binary file
|
includes/bwp-option-page/images/icon-paypal.gif
ADDED
Binary file
|
includes/bwp-option-page/images/icon-rss.png
ADDED
Binary file
|
includes/bwp-option-page/images/icon-twitter.png
ADDED
Binary file
|
includes/bwp-option-page/includes/class-bwp-option-page.php
ADDED
@@ -0,0 +1,435 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) 2014 Khang Minh <betterwp.net>
|
4 |
+
* @license http://www.gnu.org/licenses/gpl.html GNU GENERAL PUBLIC LICENSE VERSION 3.0 OR LATER
|
5 |
+
*/
|
6 |
+
|
7 |
+
class BWP_OPTION_PAGE {
|
8 |
+
|
9 |
+
/**
|
10 |
+
* The form
|
11 |
+
*/
|
12 |
+
var $form;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* The form name
|
16 |
+
*/
|
17 |
+
var $form_name;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Tabs to build
|
21 |
+
*/
|
22 |
+
var $form_tabs;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Current tab
|
26 |
+
*/
|
27 |
+
var $current_tab;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* This holds the form items, determining the position
|
31 |
+
*/
|
32 |
+
var $form_items = array();
|
33 |
+
|
34 |
+
/**
|
35 |
+
* This holds the name for each items (an item can have more than one fields)
|
36 |
+
*/
|
37 |
+
var $form_item_names = array();
|
38 |
+
|
39 |
+
/**
|
40 |
+
* This holds the form label
|
41 |
+
*/
|
42 |
+
var $form_item_labels = array();
|
43 |
+
|
44 |
+
/**
|
45 |
+
* This holds the form option aka data
|
46 |
+
*/
|
47 |
+
var $form_options = array(), $site_options = array();
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Other things
|
51 |
+
*/
|
52 |
+
var $domain;
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Constructor
|
56 |
+
*/
|
57 |
+
function __construct($form_name = 'bwp_option_page', $site_options = array(), $domain = '')
|
58 |
+
{
|
59 |
+
$this->form_name = $form_name;
|
60 |
+
$this->site_options = $site_options;
|
61 |
+
$this->domain = $domain;
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Init the class
|
66 |
+
*
|
67 |
+
* @param array $form The form array that contains everything we need to build the form
|
68 |
+
* @param array $options The data array that contains all data fetched from db or by default
|
69 |
+
* @param string $form_name The name of the form, change this if you have more than one forms on a page
|
70 |
+
*/
|
71 |
+
function init($form = array(), $options = array(), $form_tabs = array())
|
72 |
+
{
|
73 |
+
$this->form_items = $form['items'];
|
74 |
+
$this->form_item_names = $form['item_names'];
|
75 |
+
$this->form_item_labels = $form['item_labels'];
|
76 |
+
$this->form = $form;
|
77 |
+
$this->form_options = $options;
|
78 |
+
$this->form_tabs = $form_tabs;
|
79 |
+
|
80 |
+
if (sizeof($this->form_tabs) == 0)
|
81 |
+
$this->form_tabs = array(__('Plugin Configurations', 'bwp-option-page'));
|
82 |
+
}
|
83 |
+
|
84 |
+
function get_form_name()
|
85 |
+
{
|
86 |
+
return $this->form_name;
|
87 |
+
}
|
88 |
+
|
89 |
+
function set_current_tab($current_tab = 0)
|
90 |
+
{
|
91 |
+
$this->current_tab = $current_tab;
|
92 |
+
}
|
93 |
+
|
94 |
+
function get_options($options = array(), $options_default = array())
|
95 |
+
{
|
96 |
+
foreach ($options_default as $key => $option)
|
97 |
+
{
|
98 |
+
if (!in_array($key, $options))
|
99 |
+
unset($options_default[$key]);
|
100 |
+
}
|
101 |
+
return $options_default;
|
102 |
+
}
|
103 |
+
|
104 |
+
function get_db_options($name = '', $options = array())
|
105 |
+
{
|
106 |
+
$db_options = get_option($name);
|
107 |
+
if (!$db_options)
|
108 |
+
update_option($name, $options);
|
109 |
+
else if (array_keys($options) != array_keys($db_options))
|
110 |
+
{
|
111 |
+
foreach ($db_options as $key => $data)
|
112 |
+
if (isset($options[$key]) && !in_array($key, $this->site_options))
|
113 |
+
$options[$key] = $data;
|
114 |
+
update_option($name, $options);
|
115 |
+
}
|
116 |
+
else
|
117 |
+
{
|
118 |
+
foreach ($db_options as $key => $data)
|
119 |
+
if (!in_array($key, $this->site_options))
|
120 |
+
$options[$key] = $data;
|
121 |
+
}
|
122 |
+
|
123 |
+
return $options;
|
124 |
+
}
|
125 |
+
|
126 |
+
function format_field($key, $option_formats)
|
127 |
+
{
|
128 |
+
if (!empty($option_formats[$key]))
|
129 |
+
{
|
130 |
+
if ('int' == $option_formats[$key])
|
131 |
+
$_POST[$key] = (int) $_POST[$key];
|
132 |
+
else if ('float' == $option_formats[$key])
|
133 |
+
$_POST[$key] = (float) $_POST[$key];
|
134 |
+
else if ('html' == $option_formats[$key])
|
135 |
+
$_POST[$key] = wp_filter_post_kses($_POST[$key]);
|
136 |
+
}
|
137 |
+
else
|
138 |
+
$_POST[$key] = strip_tags($_POST[$key]);
|
139 |
+
}
|
140 |
+
|
141 |
+
function kill_html_fields(&$form = array(), $ids)
|
142 |
+
{
|
143 |
+
$ids = (array) $ids;
|
144 |
+
$in_keys = array('items', 'item_labels', 'item_names');
|
145 |
+
foreach ($ids as $id)
|
146 |
+
{
|
147 |
+
foreach ($in_keys as $key)
|
148 |
+
unset($form[$key][$id]);
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Generate HTML field
|
154 |
+
*
|
155 |
+
* @params they explain themselves
|
156 |
+
*/
|
157 |
+
function generate_html_field($type = '', $data = array(), $name = '', $in_section = false)
|
158 |
+
{
|
159 |
+
$pre_html_field = '';
|
160 |
+
$post_html_field = '';
|
161 |
+
$checked = 'checked="checked" ';
|
162 |
+
$selected = 'selected="selected" ';
|
163 |
+
|
164 |
+
$value = isset($this->form_options[$name])
|
165 |
+
? $this->form_options[$name]
|
166 |
+
: '';
|
167 |
+
|
168 |
+
$value = !empty($this->domain)
|
169 |
+
&& ('textarea' == $type || 'input' == $type)
|
170 |
+
? __($value, $this->domain)
|
171 |
+
: $value;
|
172 |
+
|
173 |
+
$value = 'textarea' == $type
|
174 |
+
? esc_html($value)
|
175 |
+
: esc_attr($value);
|
176 |
+
|
177 |
+
$array_replace = array();
|
178 |
+
$array_search = array('size', 'name', 'value', 'cols',
|
179 |
+
'rows', 'label', 'disabled', 'pre', 'post');
|
180 |
+
$return_html = '';
|
181 |
+
|
182 |
+
$br = isset($this->form['inline_fields'][$name])
|
183 |
+
&& is_array($this->form['inline_fields'][$name])
|
184 |
+
? ''
|
185 |
+
: "<br />\n";
|
186 |
+
|
187 |
+
$pre = !empty($data['pre']) ? $data['pre'] : '';
|
188 |
+
$post = !empty($data['post']) ? $data['post'] : '';
|
189 |
+
|
190 |
+
switch ($type)
|
191 |
+
{
|
192 |
+
case 'heading':
|
193 |
+
$html_field = '%s';
|
194 |
+
break;
|
195 |
+
|
196 |
+
case 'input':
|
197 |
+
$html_field = (!$in_section) ? '%pre%<input%disabled% size="%size%" type="text" id="' . $name . '" name="' . $name . '" value="' . $value . '" /> <em>%label%</em>' : '<label for="' . $name . '">%pre%<input%disabled% size="%size%" type="text" id="' . $name . '" name="' . $name . '" value="' . $value . '" /> <em>%label%</em></label>';
|
198 |
+
break;
|
199 |
+
|
200 |
+
case 'select':
|
201 |
+
$pre_html_field = '%pre%<select id="' . $name . '" name="' . $name . '">' . "\n";
|
202 |
+
$html_field = '<option %selected%value="%value%" />%option%</option>';
|
203 |
+
$post_html_field = '</select>%post%' . $br;
|
204 |
+
break;
|
205 |
+
|
206 |
+
case 'checkbox':
|
207 |
+
$html_field = '<label for="%name%">' . '<input %checked%type="checkbox" id="%name%" name="%name%" value="yes" /> %label%</label>';
|
208 |
+
break;
|
209 |
+
|
210 |
+
case 'radio':
|
211 |
+
$html_field = '<label>' . '<input %checked%type="radio" name="' . $name . '" value="%value%" /> %label%</label>';
|
212 |
+
break;
|
213 |
+
|
214 |
+
case 'textarea':
|
215 |
+
$html_field = '%pre%<textarea%disabled% id="' . $name . '" name="' . $name . '" cols="%cols%" rows="%rows%">' . $value . '</textarea>%post%';
|
216 |
+
break;
|
217 |
+
}
|
218 |
+
|
219 |
+
if (!isset($data))
|
220 |
+
return;
|
221 |
+
|
222 |
+
if ($type == 'heading' && !is_array($data))
|
223 |
+
{
|
224 |
+
$return_html .= sprintf($html_field, $data) . $br;
|
225 |
+
}
|
226 |
+
else if ($type == 'radio' || $type == 'checkbox' || $type == 'select')
|
227 |
+
{
|
228 |
+
foreach ($data as $key => $value)
|
229 |
+
{
|
230 |
+
// handle checkbox a little bit differently
|
231 |
+
if ($type == 'checkbox')
|
232 |
+
{
|
233 |
+
if ($this->form_options[$value] == 'yes')
|
234 |
+
$return_html .= str_replace(array('%value%', '%name%', '%label%', '%checked%'), array($value, $value, $key, $checked), $html_field) . $br;
|
235 |
+
else
|
236 |
+
$return_html .= str_replace(array('%value%', '%name%', '%label%', '%checked%'), array($value, $value, $key, ''), $html_field) . $br;
|
237 |
+
}
|
238 |
+
else if (isset($this->form_options[$name]) && $this->form_options[$name] == $value)
|
239 |
+
$return_html .= str_replace(array('%value%', '%name%', '%label%', '%option%', '%checked%', '%selected%', '%pre%', '%post%'), array($value, $value, $key, $key, $checked, $selected, $pre, $post), $html_field) . $br;
|
240 |
+
else
|
241 |
+
$return_html .= str_replace(array('%value%', '%name%', '%label%', '%option%', '%checked%', '%selected%', '%pre%', '%post%'), array($value, $value, $key, $key, '', '', $pre, $post), $html_field) . $br;
|
242 |
+
}
|
243 |
+
}
|
244 |
+
else
|
245 |
+
{
|
246 |
+
foreach ($array_search as &$keyword)
|
247 |
+
{
|
248 |
+
$array_replace[$keyword] = '';
|
249 |
+
if (!empty($data[$keyword]))
|
250 |
+
{
|
251 |
+
$array_replace[$keyword] = $data[$keyword];
|
252 |
+
}
|
253 |
+
$keyword = '%' . $keyword . '%';
|
254 |
+
}
|
255 |
+
$return_html = str_replace($array_search, $array_replace, $html_field) . $br;
|
256 |
+
}
|
257 |
+
|
258 |
+
// inline fields
|
259 |
+
$inline_html = '';
|
260 |
+
if (isset($this->form['inline_fields'][$name]) && is_array($this->form['inline_fields'][$name]))
|
261 |
+
{
|
262 |
+
foreach ($this->form['inline_fields'][$name] as $field => $field_type)
|
263 |
+
{
|
264 |
+
if (isset($this->form[$field_type][$field]))
|
265 |
+
$inline_html = ' ' . $this->generate_html_field($field_type, $this->form[$field_type][$field], $field, $in_section);
|
266 |
+
}
|
267 |
+
}
|
268 |
+
|
269 |
+
// Post
|
270 |
+
$post = (!empty($this->form['post'][$name])) ? ' ' . $this->form['post'][$name] : $post;
|
271 |
+
|
272 |
+
return str_replace('%pre%', $pre, $pre_html_field) . $return_html . str_replace('%post%', $post, $post_html_field) . $inline_html;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Generate HTML fields
|
277 |
+
*
|
278 |
+
* @params they explain themselves
|
279 |
+
*/
|
280 |
+
function generate_html_fields($type, $name)
|
281 |
+
{
|
282 |
+
$item_label = '';
|
283 |
+
$return_html = '';
|
284 |
+
|
285 |
+
$item_key = array_keys($this->form_item_names, $name);
|
286 |
+
|
287 |
+
$input_class = $type == 'heading'
|
288 |
+
? 'bwp-option-page-heading-desc'
|
289 |
+
: 'bwp-option-page-inputs';
|
290 |
+
|
291 |
+
// An inline item can hold any HTML markup
|
292 |
+
// An example is to display some kinds of button right be low the label
|
293 |
+
$inline = '';
|
294 |
+
if (isset($this->form['inline']) && is_array($this->form['inline'])
|
295 |
+
&& array_key_exists($name, $this->form['inline'])
|
296 |
+
) {
|
297 |
+
$inline = (empty($this->form['inline'][$name])) ? '' : $this->form['inline'][$name];
|
298 |
+
}
|
299 |
+
$inline .= "\n";
|
300 |
+
|
301 |
+
switch ($type)
|
302 |
+
{
|
303 |
+
case 'section':
|
304 |
+
|
305 |
+
if (!isset($this->form[$name]) || !is_array($this->form[$name]))
|
306 |
+
return;
|
307 |
+
|
308 |
+
$item_label = '<span class="bwp-opton-page-label">' . $this->form_item_labels[$item_key[0]] . $inline . '</span>';
|
309 |
+
|
310 |
+
foreach ($this->form[$name] as $section_field)
|
311 |
+
{
|
312 |
+
$type = $section_field[0];
|
313 |
+
$name = $section_field['name'];
|
314 |
+
|
315 |
+
if (isset($this->form[$section_field[0]]))
|
316 |
+
{
|
317 |
+
$return_html .= $this->generate_html_field($section_field[0], $this->form[$type][$name], $name, true);
|
318 |
+
}
|
319 |
+
}
|
320 |
+
break;
|
321 |
+
|
322 |
+
default:
|
323 |
+
|
324 |
+
if (!isset($this->form[$type][$name])
|
325 |
+
|| ($type != 'heading' && !is_array($this->form[$type][$name]))
|
326 |
+
) {
|
327 |
+
return;
|
328 |
+
}
|
329 |
+
|
330 |
+
/*$item_label = (empty($this->form[$type][$name]['label'])) ? '<label class="bwp-opton-page-label" for="' . $name . '">' . $this->form_item_labels[$item_key[0]] . '</label>' : '<span class="bwp-opton-page-label">' . $this->form_item_labels[$item_key[0]] . '</span>';*/
|
331 |
+
$item_label = $type != 'checkbox' && $type != 'radio'
|
332 |
+
? '<label class="bwp-opton-page-label" for="' . $name . '">' . $this->form_item_labels[$item_key[0]] . $inline . '</label>'
|
333 |
+
: '<span class="bwp-opton-page-label type-' . $type . '">' . $this->form_item_labels[$item_key[0]] . $inline . '</span>';
|
334 |
+
|
335 |
+
$item_label = $type == 'heading'
|
336 |
+
? '<h3>' . $this->form_item_labels[$item_key[0]] . '</h3>' . $inline
|
337 |
+
: $item_label;
|
338 |
+
|
339 |
+
if (isset($this->form[$type]))
|
340 |
+
$return_html = $this->generate_html_field($type, $this->form[$type][$name], $name);
|
341 |
+
break;
|
342 |
+
}
|
343 |
+
|
344 |
+
// A container can hold some result executed by customized script,
|
345 |
+
// such as displaying something when user press the submit button
|
346 |
+
$containers = '';
|
347 |
+
if (isset($this->form['container']) && is_array($this->form['container'])
|
348 |
+
&& array_key_exists($name, $this->form['container'])
|
349 |
+
) {
|
350 |
+
$container_array = (array) $this->form['container'][$name];
|
351 |
+
foreach ($container_array as $container)
|
352 |
+
{
|
353 |
+
$containers .= empty($container)
|
354 |
+
? '<div style="display: none;"><!-- --></div>'
|
355 |
+
: '<div class="bwp-clear">' . $container . '</div>' . "\n";
|
356 |
+
}
|
357 |
+
}
|
358 |
+
|
359 |
+
$pure_return = trim(strip_tags($return_html));
|
360 |
+
if (empty($pure_return) && $type == 'heading')
|
361 |
+
return $item_label . $containers;
|
362 |
+
else
|
363 |
+
return $item_label . '<p class="' . $input_class . '">' . $return_html . '</p>' . $containers;
|
364 |
+
}
|
365 |
+
|
366 |
+
/**
|
367 |
+
* Generate HTML form
|
368 |
+
*
|
369 |
+
* @see Constructor
|
370 |
+
*/
|
371 |
+
function generate_html_form()
|
372 |
+
{
|
373 |
+
$return_str = '<div class="wrap" style="padding-bottom: 20px;">' . "\n";
|
374 |
+
if (sizeof($this->form_tabs) >= 2)
|
375 |
+
$return_str .= apply_filters('bwp-admin-form-icon', '<div class="icon32" id="icon-options-general"><br></div>' . "\n");
|
376 |
+
else
|
377 |
+
$return_str .= '<div class="icon32" id="icon-options-general"><br></div>';
|
378 |
+
|
379 |
+
if (sizeof($this->form_tabs) >= 2)
|
380 |
+
{
|
381 |
+
$count = 0;
|
382 |
+
$return_str .= '<h2 class="bwp-option-page-tabs">' . "\n";
|
383 |
+
$return_str .= apply_filters('bwp-admin-plugin-version', '') . "\n";
|
384 |
+
foreach ($this->form_tabs as $title => $link)
|
385 |
+
{
|
386 |
+
$count++;
|
387 |
+
$active = ($count == $this->current_tab) ? ' nav-tab-active' : '';
|
388 |
+
$return_str .= '<a class="nav-tab' . $active . '" href="' . $link . '">' . $title . '</a>' . "\n";
|
389 |
+
}
|
390 |
+
$return_str .= '</h2>' . "\n";
|
391 |
+
}
|
392 |
+
else if (!isset($this->form_tabs[0]))
|
393 |
+
{
|
394 |
+
$title = array_keys($this->form_tabs);
|
395 |
+
$return_str .= '<h2>' . $title[0] . '</h2>' . "\n";
|
396 |
+
}
|
397 |
+
else
|
398 |
+
$return_str .= '<h2>' . $this->form_tabs[0] . '</h2>' . "\n";
|
399 |
+
|
400 |
+
$return_str .= apply_filters('bwp_option_before_form', '');
|
401 |
+
echo $return_str;
|
402 |
+
do_action('bwp_option_action_before_form');
|
403 |
+
$return_str = '';
|
404 |
+
$return_str .= '<form class="bwp-option-page" name="' . $this->form_name . '" method="post" action="">' . "\n";
|
405 |
+
if (function_exists('wp_nonce_field'))
|
406 |
+
{
|
407 |
+
echo $return_str;
|
408 |
+
wp_nonce_field($this->form_name);
|
409 |
+
$return_str = '';
|
410 |
+
}
|
411 |
+
$return_str .= '<ul>' . "\n";
|
412 |
+
|
413 |
+
// generate filled form
|
414 |
+
if (isset($this->form_items) && is_array($this->form_items))
|
415 |
+
foreach ($this->form_items as $key => $type)
|
416 |
+
{
|
417 |
+
if (!empty($this->form_item_names[$key]) && !empty($this->form_item_labels[$key]))
|
418 |
+
{
|
419 |
+
$return_str .= '<li class="bwp-clear">' . $this->generate_html_fields($type, $this->form_item_names[$key]) . '</li>' . "\n";
|
420 |
+
}
|
421 |
+
}
|
422 |
+
|
423 |
+
$return_str .= '</ul>' . "\n";
|
424 |
+
$return_str .= apply_filters('bwp_option_before_submit_button', '');
|
425 |
+
echo $return_str;
|
426 |
+
do_action('bwp_option_action_before_submit_button');
|
427 |
+
$return_str = '';
|
428 |
+
$return_str .= apply_filters('bwp_option_submit_button', '<p class="submit"><input type="submit" class="button-primary" name="submit_' . $this->form_name . '" value="' . __('Save Changes') . '" /></p>') . "\n";
|
429 |
+
$return_str .= '</form>' . "\n";
|
430 |
+
$return_str .= '</div>' . "\n";
|
431 |
+
|
432 |
+
echo $return_str;
|
433 |
+
}
|
434 |
+
|
435 |
+
}
|
includes/bwp-option-page/includes/index.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
// White page
|
3 |
+
?>
|
includes/bwp-option-page/index.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
// White page
|
3 |
+
?>
|
includes/bwp-option-page/js/paypal.js
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(document).ready(function(){
|
2 |
+
/* Paypal form */
|
3 |
+
jQuery('.paypal-form select[name="amount"]').change(function() {
|
4 |
+
if (jQuery(this).val() == '100.00')
|
5 |
+
{
|
6 |
+
jQuery(this).hide();
|
7 |
+
jQuery('.paypal-alternate-input').append('<input type="text" style="padding: 3px; width: 70px; text-align: right; line-height: 1;" name="amount" value="15.00" /> <code>$</code>');
|
8 |
+
jQuery('.paypal-alternate-input').show();
|
9 |
+
}
|
10 |
+
});
|
11 |
+
});
|
includes/class-bwp-enqueued-detector.php
ADDED
@@ -0,0 +1,522 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) 2014 Khang Minh <http://betterwp.net>
|
4 |
+
* @license http://www.gnu.org/licenses/gpl.html GNU GENERAL PUBLIC LICENSE VERSION 3.0 OR LATER
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class BWP_Enqueued_Detector
|
9 |
+
* @author Khang Minh <contact@betterwp.net>
|
10 |
+
* @since BWP Minify 1.3.0
|
11 |
+
* @package BWP Minify
|
12 |
+
*/
|
13 |
+
class BWP_Enqueued_Detector
|
14 |
+
{
|
15 |
+
private $_domain = '';
|
16 |
+
|
17 |
+
private $_options = array();
|
18 |
+
|
19 |
+
private $_log_key = '';
|
20 |
+
|
21 |
+
private $_logs = array();
|
22 |
+
|
23 |
+
private $_db_logs = array();
|
24 |
+
|
25 |
+
private $_scripts = array();
|
26 |
+
|
27 |
+
private $_styles = array();
|
28 |
+
|
29 |
+
private $_groups = array();
|
30 |
+
|
31 |
+
private $_script_count = 0;
|
32 |
+
|
33 |
+
private $_style_count = 0;
|
34 |
+
|
35 |
+
private $_group_count = 0;
|
36 |
+
|
37 |
+
private $_version = '1.0.0';
|
38 |
+
|
39 |
+
public function __construct($options, $domain)
|
40 |
+
{
|
41 |
+
$this->_options = $options;
|
42 |
+
$this->_domain = $domain;
|
43 |
+
|
44 |
+
$this->_init();
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Auto detects enqueued files when needed
|
49 |
+
*
|
50 |
+
* Attempts to make some automatic requests to some regular pages to
|
51 |
+
* detect enqueued files.
|
52 |
+
*
|
53 |
+
* @uses wp_remote_get
|
54 |
+
* @return void
|
55 |
+
*/
|
56 |
+
public function auto_detect()
|
57 |
+
{
|
58 |
+
$urls_to_request = array(
|
59 |
+
home_url(), // homepage
|
60 |
+
get_permalink(1), // first post, might not work
|
61 |
+
get_permalink(2) // first page, might not work
|
62 |
+
);
|
63 |
+
|
64 |
+
foreach ($urls_to_request as $url) {
|
65 |
+
$request = array(
|
66 |
+
'url' => $url,
|
67 |
+
'args' => array(
|
68 |
+
'timeout' => 0.01,
|
69 |
+
'blocking' => false,
|
70 |
+
'sslverify' => apply_filters('https_local_ssl_verify', true)
|
71 |
+
)
|
72 |
+
);
|
73 |
+
wp_remote_get($request['url'], $request['args']);
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Detects an enqueued script
|
79 |
+
*
|
80 |
+
* This function is called whenever a script is being added to a Minify
|
81 |
+
* Group and it will get necessary data to store in the detector's log.
|
82 |
+
* Stored data include: `handle`, `url` (processed src), `min` (minify or
|
83 |
+
* ignore), `wp` (let WordPress handles or not), `dependencies`,
|
84 |
+
* `position`, `group`
|
85 |
+
*
|
86 |
+
* @param $handle string handle of the script being detected
|
87 |
+
* @param $item array data of the script being detected
|
88 |
+
* @param $groups array the current list of Minify Groups. This is the
|
89 |
+
* list at the time of detection.
|
90 |
+
* @return void
|
91 |
+
*/
|
92 |
+
public function detect_script($handle, $item)
|
93 |
+
{
|
94 |
+
$group_handle = $item['group'];
|
95 |
+
|
96 |
+
$log_data = array(
|
97 |
+
'handle' => $handle,
|
98 |
+
'src' => $item['src'],
|
99 |
+
'min' => $item['min'],
|
100 |
+
'wp' => $item['wp'],
|
101 |
+
'depend' => $item['depend'],
|
102 |
+
'position' => $this->_get_position($item),
|
103 |
+
'order' => $this->_get_position_order($item),
|
104 |
+
'group' => $item['group']
|
105 |
+
);
|
106 |
+
|
107 |
+
$this->_script_count++;
|
108 |
+
$this->_log('script', $log_data);
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Detects an enqueued style
|
113 |
+
*
|
114 |
+
* This function is called whenever a style is being added to a Minify
|
115 |
+
* Group and it will get necessary data to store in the detector's log.
|
116 |
+
* Stored data include: `handle`, `url (processed src)`, `min` (minify or
|
117 |
+
* ignore), `wp` (let WordPress handles or not), `dependencies`,
|
118 |
+
* `position`, `media type`, `group`
|
119 |
+
*
|
120 |
+
* @param $handle string handle of the style being detected
|
121 |
+
* @param $item array data of the style being detected
|
122 |
+
* @param $groups array the current list of Minify Groups. This is the
|
123 |
+
* list at the time of detection.
|
124 |
+
* @return void
|
125 |
+
*/
|
126 |
+
public function detect_style($handle, $item)
|
127 |
+
{
|
128 |
+
$group_handle = $item['group'];
|
129 |
+
|
130 |
+
$log_data = array(
|
131 |
+
'handle' => $handle,
|
132 |
+
'src' => $item['src'],
|
133 |
+
'min' => $item['min'],
|
134 |
+
'wp' => $item['wp'],
|
135 |
+
'depend' => $item['depend'],
|
136 |
+
'position' => $this->_get_position($item),
|
137 |
+
'order' => $this->_get_position_order($item),
|
138 |
+
'group' => $item['group'],
|
139 |
+
'media' => $item['media']
|
140 |
+
);
|
141 |
+
|
142 |
+
$this->_style_count++;
|
143 |
+
$this->_log('style', $log_data);
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Detects a group that is going to be served
|
148 |
+
*
|
149 |
+
* @param $group_handle string handle of the group being detected
|
150 |
+
* @param $string string the original string that contains paths to
|
151 |
+
* files as a comma-separated list
|
152 |
+
* @param $group_type string either 'script' or 'style'
|
153 |
+
* @return void
|
154 |
+
*/
|
155 |
+
public function detect_group($group_handle, $string, $group_type)
|
156 |
+
{
|
157 |
+
$hash = md5($string . $this->_version);
|
158 |
+
|
159 |
+
$this->_log('group', array(
|
160 |
+
'handle' => $group_handle . '-' . $hash,
|
161 |
+
'string' => $string,
|
162 |
+
'type' => $group_type,
|
163 |
+
'order' => $this->_group_count,
|
164 |
+
'hash' => $hash
|
165 |
+
));
|
166 |
+
|
167 |
+
$this->_group_count++;
|
168 |
+
}
|
169 |
+
|
170 |
+
public function get_group($group_handle, $from_db = false)
|
171 |
+
{
|
172 |
+
$groups = $this->_groups;
|
173 |
+
|
174 |
+
foreach ($groups as $handle => $group)
|
175 |
+
{
|
176 |
+
if ($group_handle == $handle)
|
177 |
+
return $group;
|
178 |
+
}
|
179 |
+
|
180 |
+
return false;
|
181 |
+
}
|
182 |
+
|
183 |
+
public function get_detected_scripts($from_db = false)
|
184 |
+
{
|
185 |
+
if ($from_db)
|
186 |
+
return isset($this->_db_logs['scripts'])
|
187 |
+
? $this->_db_logs['scripts']
|
188 |
+
: array();
|
189 |
+
else
|
190 |
+
return $this->_scripts;
|
191 |
+
}
|
192 |
+
|
193 |
+
public function get_detected_styles($from_db = false)
|
194 |
+
{
|
195 |
+
if ($from_db)
|
196 |
+
return isset($this->_db_logs['styles'])
|
197 |
+
? $this->_db_logs['styles']
|
198 |
+
: array();
|
199 |
+
else
|
200 |
+
return $this->_styles;
|
201 |
+
}
|
202 |
+
|
203 |
+
public function get_detected_groups($from_db = false)
|
204 |
+
{
|
205 |
+
if ($from_db)
|
206 |
+
return isset($this->_db_logs['groups'])
|
207 |
+
? $this->_db_logs['groups']
|
208 |
+
: array();
|
209 |
+
else
|
210 |
+
return $this->_groups;
|
211 |
+
}
|
212 |
+
|
213 |
+
public function get_version()
|
214 |
+
{
|
215 |
+
return $this->_version;
|
216 |
+
}
|
217 |
+
|
218 |
+
public function show_detected_scripts()
|
219 |
+
{
|
220 |
+
$headings = array(
|
221 |
+
'handle' => __('Handle', $this->_domain),
|
222 |
+
'position' => __('Position', $this->_domain),
|
223 |
+
'src' => __('Script src', $this->_domain),
|
224 |
+
'action' => __('Actions', $this->_domain)
|
225 |
+
);
|
226 |
+
|
227 |
+
uasort($this->_scripts, array($this, 'sort_item'));
|
228 |
+
$this->_show_log('script', $headings);
|
229 |
+
}
|
230 |
+
|
231 |
+
public function show_detected_styles()
|
232 |
+
{
|
233 |
+
$headings = array(
|
234 |
+
'handle' => __('Handle', $this->_domain),
|
235 |
+
'position' => __('Position', $this->_domain),
|
236 |
+
'media' => __('Media', $this->_domain),
|
237 |
+
'src' => __('Style src', $this->_domain),
|
238 |
+
'action' => __('Actions', $this->_domain)
|
239 |
+
);
|
240 |
+
|
241 |
+
uasort($this->_styles, array($this, 'sort_item'));
|
242 |
+
$this->_show_log('style', $headings);
|
243 |
+
}
|
244 |
+
|
245 |
+
public function sort_item($a, $b)
|
246 |
+
{
|
247 |
+
if (!isset($a['order']) || !isset($b['order']))
|
248 |
+
return 0;
|
249 |
+
|
250 |
+
if ($a['order'] == $b['order'])
|
251 |
+
return 0;
|
252 |
+
|
253 |
+
return $a['order'] < $b['order'] ? -1 : 1;
|
254 |
+
}
|
255 |
+
|
256 |
+
public function clear_logs($type = 'enqueue')
|
257 |
+
{
|
258 |
+
if ('enqueue' === $type || 'all' == $type)
|
259 |
+
{
|
260 |
+
$this->_scripts = array();
|
261 |
+
$this->_styles = array();
|
262 |
+
}
|
263 |
+
|
264 |
+
if ('group' == $type || 'all' == $type)
|
265 |
+
{
|
266 |
+
$this->_groups = array();
|
267 |
+
}
|
268 |
+
|
269 |
+
$this->commit_logs();
|
270 |
+
$this->auto_detect();
|
271 |
+
}
|
272 |
+
|
273 |
+
/**
|
274 |
+
* Updates display text of a file's position in the log
|
275 |
+
*
|
276 |
+
* When a group is moved to a different position all files contained in
|
277 |
+
* that group are moved too so we have to update their position in the log
|
278 |
+
* accordingly.
|
279 |
+
*
|
280 |
+
* @param $type string type of group either 'script' or 'style'
|
281 |
+
* @param $group_handle string the moved group's handle
|
282 |
+
* @param $new_position string new position of the moved group
|
283 |
+
* @return void
|
284 |
+
*/
|
285 |
+
public function update_position($type, $group_handle, $new_position)
|
286 |
+
{
|
287 |
+
if ('script' == $type)
|
288 |
+
$log = &$this->_scripts;
|
289 |
+
else
|
290 |
+
$log = &$this->_styles;
|
291 |
+
|
292 |
+
foreach ($log as &$item)
|
293 |
+
{
|
294 |
+
if ($group_handle == $item['group'])
|
295 |
+
{
|
296 |
+
$item['position'] = $new_position;
|
297 |
+
$item['position'] = $this->_get_position($item);
|
298 |
+
}
|
299 |
+
}
|
300 |
+
}
|
301 |
+
|
302 |
+
public function set_log($key)
|
303 |
+
{
|
304 |
+
$logs = get_option($key);
|
305 |
+
|
306 |
+
$this->_log_key = $key;
|
307 |
+
$this->_logs = !empty($logs) ? (array) $logs : array();
|
308 |
+
$this->_db_logs = $this->_logs;
|
309 |
+
|
310 |
+
$this->_prepare_logs();
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Combines all logs and overwrites logs in db
|
315 |
+
*
|
316 |
+
* @return void
|
317 |
+
*/
|
318 |
+
public function commit_logs()
|
319 |
+
{
|
320 |
+
$this->_logs = array(
|
321 |
+
'scripts' => $this->_scripts,
|
322 |
+
'styles' => $this->_styles,
|
323 |
+
'groups' => $this->_groups
|
324 |
+
);
|
325 |
+
|
326 |
+
update_option($this->_log_key, $this->_logs);
|
327 |
+
}
|
328 |
+
|
329 |
+
private function _show_log($type, $headings)
|
330 |
+
{
|
331 |
+
?>
|
332 |
+
<thead>
|
333 |
+
<tr>
|
334 |
+
<?php
|
335 |
+
foreach ($headings as $heading) :
|
336 |
+
?>
|
337 |
+
<th><span class="bwp-text"><?php echo $heading; ?></span></th>
|
338 |
+
<?php
|
339 |
+
endforeach;
|
340 |
+
?>
|
341 |
+
</tr>
|
342 |
+
</thead>
|
343 |
+
<tbody>
|
344 |
+
<?php
|
345 |
+
$log = 'script' == $type ? $this->_scripts : $this->_styles;
|
346 |
+
|
347 |
+
$actions = 'script' == $type
|
348 |
+
? array('header' => __('move to header', $this->_domain),
|
349 |
+
'footer' => __('move to footer', $this->_domain),
|
350 |
+
'direct' => __('print separately', $this->_domain),
|
351 |
+
'ignore' => __('ignore', $this->_domain),
|
352 |
+
'oblivion' => __('forget', $this->_domain))
|
353 |
+
: array('style_direct' => __('print separately', $this->_domain),
|
354 |
+
'style_ignore' => __('ignore', $this->_domain),
|
355 |
+
'style_oblivion' => __('forget', $this->_domain));
|
356 |
+
|
357 |
+
if (0 < sizeof($log)) :
|
358 |
+
foreach ($log as $item) :
|
359 |
+
?>
|
360 |
+
<tr>
|
361 |
+
<?php
|
362 |
+
foreach ($headings as $key => $heading) :
|
363 |
+
if ('action' == $key)
|
364 |
+
continue;
|
365 |
+
|
366 |
+
if ('src' == $key)
|
367 |
+
$value = '<input readonly="readonly" type="text" '
|
368 |
+
. 'value="' . esc_attr($item[$key]) . '" />';
|
369 |
+
else if ('handle' == $key)
|
370 |
+
$value = '<input readonly="readonly" type="text" style="width: 130px;" '
|
371 |
+
. 'value="' . esc_attr($item[$key]) . '" />';
|
372 |
+
else
|
373 |
+
$value = esc_html($item[$key]);
|
374 |
+
?>
|
375 |
+
<td><?php echo $value ?></td>
|
376 |
+
<?php
|
377 |
+
endforeach;
|
378 |
+
?>
|
379 |
+
<td style="width: 50px">
|
380 |
+
<a href="#" class="action-toggle-handle"><?php _e('select', $this->_domain); ?></a>
|
381 |
+
<div class="action-handles">
|
382 |
+
<span class="data-handle"><?php esc_html_e($item['handle']); ?></span>
|
383 |
+
<?php
|
384 |
+
foreach ($actions as $position => $action) :
|
385 |
+
?>
|
386 |
+
| <a href="#" class="action-handle"
|
387 |
+
data-position="<?php echo $position; ?>"><?php echo $action; ?></a>
|
388 |
+
<?php
|
389 |
+
endforeach;
|
390 |
+
?>
|
391 |
+
</div>
|
392 |
+
</td>
|
393 |
+
</tr>
|
394 |
+
<?php
|
395 |
+
endforeach;
|
396 |
+
else :
|
397 |
+
?>
|
398 |
+
<tr>
|
399 |
+
<td colspan="5">
|
400 |
+
<?php _e('No enqueued files detected.<br /><br />'
|
401 |
+
. 'Please try visiting a few pages on your site '
|
402 |
+
. 'and then refresh this page.<br /><br />'
|
403 |
+
. 'You should clear this list once in a while '
|
404 |
+
. 'to get rid of files that are no longer being used '
|
405 |
+
. 'as this is not done automatically.', $this->_domain); ?>
|
406 |
+
</td>
|
407 |
+
</tr>
|
408 |
+
<?php
|
409 |
+
endif;
|
410 |
+
?>
|
411 |
+
</tbody>
|
412 |
+
<?php
|
413 |
+
}
|
414 |
+
|
415 |
+
/**
|
416 |
+
* Gets enqueued file's user-friendly position text
|
417 |
+
*
|
418 |
+
* Most of the time this function will rely on the expected position of a
|
419 |
+
* file (registered via `wp_enqueue_` functions or via custom positioning),
|
420 |
+
* but it might as well return a dynamic position due to a forceful
|
421 |
+
* positioning of one of its dependencies.
|
422 |
+
*
|
423 |
+
* @see $this->_update_position()
|
424 |
+
*
|
425 |
+
* @access private
|
426 |
+
* @param $item array data of the enqueued file
|
427 |
+
* @return string user-friendly position text
|
428 |
+
*/
|
429 |
+
private function _get_position($item)
|
430 |
+
{
|
431 |
+
$position = $item['position'];
|
432 |
+
if ('oblivion' != $position)
|
433 |
+
{
|
434 |
+
$position = 'header' == $position // header or footer or footer{n}
|
435 |
+
? __('header', $this->_domain)
|
436 |
+
: __('footer', $this->_domain);
|
437 |
+
}
|
438 |
+
|
439 |
+
$min = $item['min']; // minify needed
|
440 |
+
$wp = $item['wp']; // minify but print separately
|
441 |
+
|
442 |
+
if ($min)
|
443 |
+
{
|
444 |
+
// separated or combined and minified
|
445 |
+
return $wp
|
446 |
+
? sprintf(__('separated in %s', $this->_domain), $position)
|
447 |
+
: sprintf(__('minified in %s', $this->_domain), $position);
|
448 |
+
}
|
449 |
+
else
|
450 |
+
{
|
451 |
+
// ignored or forgotten
|
452 |
+
return 'oblivion' == $position
|
453 |
+
? __('forgotten', $this->_domain)
|
454 |
+
: sprintf(__('ignored in %s', $this->_domain), $position);
|
455 |
+
}
|
456 |
+
}
|
457 |
+
|
458 |
+
private function _get_position_order($item)
|
459 |
+
{
|
460 |
+
// order of positions: header, separate in header, ignore in header,
|
461 |
+
// footer{n}, separate in footer{n}, ignore in footer{n}, oblivion
|
462 |
+
$orders = array(
|
463 |
+
'1_0_header' => 0,
|
464 |
+
'1_1_header' => 1,
|
465 |
+
'0_0_header' => 2,
|
466 |
+
'0_1_header' => 2,
|
467 |
+
'1_0_footer' => 3,
|
468 |
+
'1_1_footer' => 4,
|
469 |
+
'0_0_footer' => 5,
|
470 |
+
'0_1_footer' => 5,
|
471 |
+
'1_1_oblivion' => 6,
|
472 |
+
'1_0_oblivion' => 6,
|
473 |
+
'0_1_oblivion' => 6,
|
474 |
+
'0_0_oblivion' => 6
|
475 |
+
);
|
476 |
+
$order = 0;
|
477 |
+
|
478 |
+
$order = (int) $item['min'] . '_' . (int) $item['wp'] . '_' . $item['position'];
|
479 |
+
$order = $orders[$order];
|
480 |
+
|
481 |
+
return $order;
|
482 |
+
}
|
483 |
+
|
484 |
+
private function _log($type, $data)
|
485 |
+
{
|
486 |
+
if ('script' == $type)
|
487 |
+
$this->_scripts[$data['handle']] = $data;
|
488 |
+
else if ('style' == $type)
|
489 |
+
$this->_styles[$data['handle']] = $data;
|
490 |
+
else if ('group' == $type)
|
491 |
+
$this->_groups[$data['handle']] = $data;
|
492 |
+
}
|
493 |
+
|
494 |
+
private function _prepare_logs()
|
495 |
+
{
|
496 |
+
$logs = $this->_logs;
|
497 |
+
|
498 |
+
$this->_scripts = isset($logs['scripts']) && is_array($logs['scripts'])
|
499 |
+
? $logs['scripts']
|
500 |
+
: array();
|
501 |
+
|
502 |
+
$this->_styles = isset($logs['styles']) && is_array($logs['styles'])
|
503 |
+
? $logs['styles']
|
504 |
+
: array();
|
505 |
+
|
506 |
+
$this->_groups = isset($logs['groups']) && is_array($logs['groups'])
|
507 |
+
? $logs['groups']
|
508 |
+
: array();
|
509 |
+
}
|
510 |
+
|
511 |
+
private function _register_hooks()
|
512 |
+
{
|
513 |
+
add_action('bwp_minify_moved_group', array($this, 'update_position'), 10, 3);
|
514 |
+
add_action('bwp_minify_processed_style', array($this, 'detect_style'), 10, 2);
|
515 |
+
add_action('bwp_minify_processed_script', array($this, 'detect_script'), 10, 2);
|
516 |
+
}
|
517 |
+
|
518 |
+
private function _init()
|
519 |
+
{
|
520 |
+
$this->_register_hooks();
|
521 |
+
}
|
522 |
+
}
|
includes/class-bwp-framework-improved.php
ADDED
@@ -0,0 +1,727 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) 2014 Khang Minh <betterwp.net>
|
4 |
+
* @license http://www.gnu.org/licenses/gpl.html GNU GENERAL PUBLIC LICENSE VERSION 3.0 OR LATER
|
5 |
+
*/
|
6 |
+
|
7 |
+
class BWP_FRAMEWORK_IMPROVED {
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Database related data
|
11 |
+
*/
|
12 |
+
var $options = array();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Default data
|
16 |
+
*/
|
17 |
+
var $options_default = array(), $site_options = array();
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Hold db option keys
|
21 |
+
*/
|
22 |
+
var $option_keys = array();
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Hold extra option keys
|
26 |
+
*/
|
27 |
+
var $extra_option_keys = array();
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Hold old option pages
|
31 |
+
*/
|
32 |
+
var $option_pages = array();
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Key to identify plugin
|
36 |
+
*/
|
37 |
+
var $plugin_key;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Constant Key to identify plugin
|
41 |
+
*/
|
42 |
+
var $plugin_ckey;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Domain Key to identify plugin
|
46 |
+
*/
|
47 |
+
var $plugin_dkey;
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Title of the plugin
|
51 |
+
*/
|
52 |
+
var $plugin_title;
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Homepage of the plugin
|
56 |
+
*/
|
57 |
+
var $plugin_url;
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Urls to various parts of homepage or other places
|
61 |
+
*
|
62 |
+
* Expect to have a format of array('relative' => bool, 'url' => url)
|
63 |
+
*/
|
64 |
+
var $urls = array();
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Plugin file
|
68 |
+
*/
|
69 |
+
var $plugin_file;
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Plugin folder
|
73 |
+
*/
|
74 |
+
var $plugin_folder;
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Plugin WP url
|
78 |
+
*/
|
79 |
+
var $plugin_wp_url;
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Version of the plugin
|
83 |
+
*/
|
84 |
+
var $plugin_ver = '';
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Message shown to user (Warning, Notes, etc.)
|
88 |
+
*/
|
89 |
+
var $notices = array(), $notice_shown = false;
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Error shown to user
|
93 |
+
*/
|
94 |
+
var $errors = array(), $error_shown = false;
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Capabilities to manage this plugin
|
98 |
+
*/
|
99 |
+
var $plugin_cap = 'manage_options';
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Whether or not to create filter for media paths
|
103 |
+
*/
|
104 |
+
var $need_media_filters;
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Form tabs to build
|
108 |
+
*/
|
109 |
+
var $form_tabs = array();
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Other things
|
113 |
+
*/
|
114 |
+
var $wp_ver = '3.0';
|
115 |
+
var $php_ver = '5';
|
116 |
+
var $domain = '';
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Other special variables
|
120 |
+
*/
|
121 |
+
protected $_menu_under_settings = false;
|
122 |
+
protected $_simple_menu = false;
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Build base properties
|
126 |
+
*/
|
127 |
+
protected function build_properties($key, $dkey, $options, $plugin_title = '',
|
128 |
+
$plugin_file = '', $plugin_url = '', $need_media_filters = true)
|
129 |
+
{
|
130 |
+
$this->plugin_key = strtolower($key);
|
131 |
+
$this->plugin_ckey = strtoupper($key);
|
132 |
+
$this->plugin_dkey = $dkey;
|
133 |
+
$this->plugin_title = $plugin_title;
|
134 |
+
$this->plugin_url = $plugin_url;
|
135 |
+
|
136 |
+
$this->options_default = $options;
|
137 |
+
$this->need_media_filters = (boolean) $need_media_filters;
|
138 |
+
|
139 |
+
$this->plugin_file = $plugin_file;
|
140 |
+
$this->plugin_folder = basename(dirname($plugin_file));
|
141 |
+
|
142 |
+
$this->pre_init_actions();
|
143 |
+
$this->init_actions();
|
144 |
+
|
145 |
+
// Load locale
|
146 |
+
load_plugin_textdomain($dkey, false, $this->plugin_folder . '/languages');
|
147 |
+
}
|
148 |
+
|
149 |
+
protected function add_option_key($key, $option, $title)
|
150 |
+
{
|
151 |
+
$this->option_keys[$key] = $option;
|
152 |
+
$this->option_pages[$key] = $title;
|
153 |
+
}
|
154 |
+
|
155 |
+
protected function add_extra_option_key($key, $option, $title)
|
156 |
+
{
|
157 |
+
$this->extra_option_keys[$key] = $option;
|
158 |
+
$this->option_pages[$key] = $title;
|
159 |
+
}
|
160 |
+
|
161 |
+
public function add_icon()
|
162 |
+
{
|
163 |
+
return '<div class="icon32" id="icon-bwp-plugin" '
|
164 |
+
. 'style=\'background-image: url("'
|
165 |
+
. constant($this->plugin_ckey . '_IMAGES')
|
166 |
+
. '/icon_menu_32.png");\'><br></div>' . "\n";
|
167 |
+
}
|
168 |
+
|
169 |
+
protected function set_version($ver = '', $type = '')
|
170 |
+
{
|
171 |
+
switch ($type)
|
172 |
+
{
|
173 |
+
case '': $this->plugin_ver = $ver;
|
174 |
+
break;
|
175 |
+
case 'php': $this->php_ver = $ver;
|
176 |
+
break;
|
177 |
+
case 'wp': $this->wp_ver = $ver;
|
178 |
+
break;
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
protected function get_version($type = '')
|
183 |
+
{
|
184 |
+
switch ($type)
|
185 |
+
{
|
186 |
+
case '': return $this->plugin_ver;
|
187 |
+
break;
|
188 |
+
case 'php': return $this->php_ver;
|
189 |
+
break;
|
190 |
+
case 'wp': return $this->wp_ver;
|
191 |
+
break;
|
192 |
+
}
|
193 |
+
}
|
194 |
+
|
195 |
+
protected function check_required_versions()
|
196 |
+
{
|
197 |
+
if (version_compare(PHP_VERSION, $this->php_ver, '<')
|
198 |
+
|| version_compare(get_bloginfo('version'), $this->wp_ver, '<')
|
199 |
+
) {
|
200 |
+
add_action('admin_notices', array($this, 'warn_required_versions'));
|
201 |
+
add_action('network_admin_notices', array($this, 'warn_required_versions'));
|
202 |
+
return false;
|
203 |
+
}
|
204 |
+
else
|
205 |
+
return true;
|
206 |
+
}
|
207 |
+
|
208 |
+
public function warn_required_versions()
|
209 |
+
{
|
210 |
+
echo '<div class="error"><p>' . sprintf(
|
211 |
+
__('%s requires WordPress <strong>%s</strong> or higher '
|
212 |
+
. 'and PHP <strong>%s</strong> or higher. '
|
213 |
+
. 'The plugin will not protected function until you update your software. '
|
214 |
+
. 'Please deactivate this plugin.', $this->plugin_dkey),
|
215 |
+
$this->plugin_title, $this->wp_ver, $this->php_ver)
|
216 |
+
. '</p></div>';
|
217 |
+
}
|
218 |
+
|
219 |
+
public function show_donation()
|
220 |
+
{
|
221 |
+
$showable = apply_filters('bwp_donation_showable', true);
|
222 |
+
$ad_showable = apply_filters('bwp_ad_showable', true);
|
223 |
+
?>
|
224 |
+
<div id="bwp-info-place">
|
225 |
+
<div id="bwp-donation" style="margin-bottom: 0px;">
|
226 |
+
<a href="<?php echo $this->plugin_url; ?>"><?php echo $this->plugin_title; ?></a> <small>v<?php echo $this->plugin_ver; ?></small><br />
|
227 |
+
<small>
|
228 |
+
<a href="<?php echo str_replace('/wordpress-plugins/', '/topic/', $this->plugin_url); ?>"><?php _e('Development Log', $this->plugin_dkey); ?></a> – <a href="<?php echo $this->plugin_url . 'faq/'; ?>" title="<?php _e('Frequently Asked Questions', $this->plugin_dkey) ?>"><?php _e('FAQ', $this->plugin_dkey); ?></a> – <a href="http://betterwp.net/contact/" title="<?php _e('Got a problem? Send me a feedback!', $this->plugin_dkey) ?>"><?php _e('Contact', $this->plugin_dkey); ?></a>
|
229 |
+
</small>
|
230 |
+
<br />
|
231 |
+
<?php
|
232 |
+
if (true == $showable || ($this->is_multisite() && is_super_admin()))
|
233 |
+
{
|
234 |
+
?>
|
235 |
+
<small><?php _e('You can buy me some special coffees if you appreciate my work, thank you!', $this->plugin_dkey); ?></small>
|
236 |
+
<form class="paypal-form" action="https://www.paypal.com/cgi-bin/webscr" method="post">
|
237 |
+
<p>
|
238 |
+
<input type="hidden" name="cmd" value="_xclick">
|
239 |
+
<input type="hidden" name="business" value="NWBB8JUDW5VSY">
|
240 |
+
<input type="hidden" name="lc" value="VN">
|
241 |
+
<input type="hidden" name="button_subtype" value="services">
|
242 |
+
<input type="hidden" name="no_note" value="0">
|
243 |
+
<input type="hidden" name="cn" value="Would you like to say anything to me?">
|
244 |
+
<input type="hidden" name="no_shipping" value="1">
|
245 |
+
<input type="hidden" name="rm" value="1">
|
246 |
+
<input type="hidden" name="return" value="http://betterwp.net">
|
247 |
+
<input type="hidden" name="currency_code" value="USD">
|
248 |
+
<input type="hidden" name="bn" value="PP-BuyNowBF:icon-paypal.gif:NonHosted">
|
249 |
+
<input type="hidden" name="item_name" value="<?php printf(__('Donate to %s', $this->plugin_dkey), $this->plugin_title); ?>" />
|
250 |
+
<select name="amount">
|
251 |
+
<option value="5.00"><?php _e('One cup $5.00', $this->plugin_dkey); ?></option>
|
252 |
+
<option value="10.00"><?php _e('Two cups $10.00', $this->plugin_dkey); ?></option>
|
253 |
+
<option value="25.00"><?php _e('Five cups! $25.00', $this->plugin_dkey); ?></option>
|
254 |
+
<option value="50.00"><?php _e('One LL-cup!!! $50.00', $this->plugin_dkey); ?></option>
|
255 |
+
<option value="100.00"><?php _e('... or any amount!', $this->plugin_dkey); ?></option>
|
256 |
+
</select>
|
257 |
+
<span class="paypal-alternate-input" style="display: none;"><!-- --></span>
|
258 |
+
<input class="paypal-submit" type="image" src="<?php echo $this->plugin_wp_url . 'includes/bwp-option-page/images/icon-paypal.gif'; ?>" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" />
|
259 |
+
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
260 |
+
</p>
|
261 |
+
</form>
|
262 |
+
<?php
|
263 |
+
}
|
264 |
+
?>
|
265 |
+
</div>
|
266 |
+
<div class="bwp-separator">
|
267 |
+
<div style="height: 10px; width: 5px; background-color: #cccccc; margin: 0 auto;"><!-- --></div>
|
268 |
+
</div>
|
269 |
+
<div id="bwp-contact">
|
270 |
+
<a class="bwp-rss" href="http://feeds.feedburner.com/BetterWPnet"><?php _e('Latest updates from BetterWP.net!', $this->plugin_dkey); ?></a>
|
271 |
+
<a class="bwp-twitter" href="http://twitter.com/0dd0ne0ut"><?php _e('Follow me on Twitter!', $this->plugin_dkey); ?></a>
|
272 |
+
</div>
|
273 |
+
<?php
|
274 |
+
if (true == $ad_showable)
|
275 |
+
{
|
276 |
+
?>
|
277 |
+
<div class="bwp-separator">
|
278 |
+
<div style="height: 10px; width: 5px; background-color: #cccccc; margin: 0 auto;"><!-- --></div>
|
279 |
+
</div>
|
280 |
+
<div id="bwp-ads">
|
281 |
+
<p><strong><?php _e('This Plugin is Proudly Sponsored By', $this->plugin_dkey); ?></strong></p>
|
282 |
+
<div style="width: 250px; margin: 0 auto;">
|
283 |
+
<a href="http://bit.ly/<?php echo $this->plugin_dkey; ?>-mwp" target="_blank">
|
284 |
+
<img src="<?php echo $this->plugin_wp_url . 'includes/bwp-option-page/images/ad_250x250.png'; ?>" />
|
285 |
+
</a>
|
286 |
+
</div>
|
287 |
+
</div>
|
288 |
+
<?php
|
289 |
+
}
|
290 |
+
?>
|
291 |
+
</div>
|
292 |
+
<?php
|
293 |
+
}
|
294 |
+
|
295 |
+
public function show_version()
|
296 |
+
{
|
297 |
+
if (empty($this->plugin_ver)) return '';
|
298 |
+
|
299 |
+
return '<a class="nav-tab version" title="'
|
300 |
+
. sprintf(esc_attr(__('You are using version %s!', $this->plugin_dkey)), $this->plugin_ver)
|
301 |
+
. '">' . $this->plugin_ver . '</a>';
|
302 |
+
}
|
303 |
+
|
304 |
+
protected function pre_init_actions()
|
305 |
+
{
|
306 |
+
$this->pre_init_build_constants();
|
307 |
+
$this->pre_init_build_options();
|
308 |
+
$this->pre_init_properties();
|
309 |
+
$this->load_libraries();
|
310 |
+
$this->pre_init_hooks();
|
311 |
+
$this->pre_init_update_plugin();
|
312 |
+
|
313 |
+
// Support installation and uninstallation
|
314 |
+
register_activation_hook($this->plugin_file, array($this, 'install'));
|
315 |
+
register_deactivation_hook($this->plugin_file, array($this, 'uninstall'));
|
316 |
+
}
|
317 |
+
|
318 |
+
protected function init_actions()
|
319 |
+
{
|
320 |
+
add_action('init', array($this, 'build_wp_properties'));
|
321 |
+
add_action('init', array($this, 'init'));
|
322 |
+
|
323 |
+
// register backend hooks
|
324 |
+
add_action('admin_menu', array($this, 'init_admin'), 1);
|
325 |
+
}
|
326 |
+
|
327 |
+
public function init()
|
328 |
+
{
|
329 |
+
do_action($this->plugin_key . '_pre_init');
|
330 |
+
|
331 |
+
$this->init_update_plugin();
|
332 |
+
$this->build_constants();
|
333 |
+
$this->build_options();
|
334 |
+
$this->init_properties();
|
335 |
+
$this->init_hooks();
|
336 |
+
$this->enqueue_media();
|
337 |
+
|
338 |
+
do_action($this->plugin_key . '_loaded');
|
339 |
+
|
340 |
+
// icon 32px
|
341 |
+
if ($this->is_admin_page())
|
342 |
+
{
|
343 |
+
add_filter('bwp-admin-form-icon', array($this, 'add_icon'));
|
344 |
+
add_filter('bwp-admin-plugin-version', array($this, 'show_version'));
|
345 |
+
add_action('bwp_option_action_before_form', array($this, 'show_donation'), 12);
|
346 |
+
}
|
347 |
+
}
|
348 |
+
|
349 |
+
protected function add_cap($cap)
|
350 |
+
{
|
351 |
+
$this->plugin_cap = $cap;
|
352 |
+
}
|
353 |
+
|
354 |
+
public function build_wp_properties()
|
355 |
+
{
|
356 |
+
// set the plugin WP url here so it can be filtered
|
357 |
+
if (defined('BWP_USE_SYMLINKS'))
|
358 |
+
// make use of symlinks on development environment
|
359 |
+
$this->plugin_wp_url = trailingslashit(plugins_url($this->plugin_folder));
|
360 |
+
else
|
361 |
+
// this should allow other package to include BWP plugins while
|
362 |
+
// retaining correct URLs pointing to assets
|
363 |
+
$this->plugin_wp_url = trailingslashit(plugin_dir_url($this->plugin_file));
|
364 |
+
}
|
365 |
+
|
366 |
+
protected function pre_init_build_constants()
|
367 |
+
{
|
368 |
+
// url to plugin bwp website
|
369 |
+
define($this->plugin_ckey . '_PLUGIN_URL', $this->plugin_url);
|
370 |
+
// the capability needed to configure this plugin
|
371 |
+
define($this->plugin_ckey . '_CAPABILITY', $this->plugin_cap);
|
372 |
+
|
373 |
+
// define registered option keys, to be used when building option pages
|
374 |
+
// and build options
|
375 |
+
foreach ($this->option_keys as $key => $option)
|
376 |
+
{
|
377 |
+
define(strtoupper($key), $option);
|
378 |
+
}
|
379 |
+
foreach ($this->extra_option_keys as $key => $option)
|
380 |
+
{
|
381 |
+
define(strtoupper($key), $option);
|
382 |
+
}
|
383 |
+
}
|
384 |
+
|
385 |
+
protected function build_constants()
|
386 |
+
{
|
387 |
+
// these constants are only available once plugin_wp_url is available
|
388 |
+
if (true == $this->need_media_filters)
|
389 |
+
{
|
390 |
+
define($this->plugin_ckey . '_IMAGES',
|
391 |
+
apply_filters($this->plugin_key . '_image_path',
|
392 |
+
$this->plugin_wp_url . 'images'));
|
393 |
+
define($this->plugin_ckey . '_CSS',
|
394 |
+
apply_filters($this->plugin_key . '_css_path',
|
395 |
+
$this->plugin_wp_url . 'css'));
|
396 |
+
define($this->plugin_ckey . '_JS',
|
397 |
+
apply_filters($this->plugin_key . '_js_path',
|
398 |
+
$this->plugin_wp_url . 'js'));
|
399 |
+
}
|
400 |
+
else
|
401 |
+
{
|
402 |
+
define($this->plugin_ckey . '_IMAGES', $this->plugin_wp_url . 'images');
|
403 |
+
define($this->plugin_ckey . '_CSS', $this->plugin_wp_url . 'css');
|
404 |
+
define($this->plugin_ckey . '_JS', $this->plugin_wp_url . 'js');
|
405 |
+
}
|
406 |
+
}
|
407 |
+
|
408 |
+
protected function pre_init_build_options()
|
409 |
+
{
|
410 |
+
$this->build_options();
|
411 |
+
}
|
412 |
+
|
413 |
+
protected function build_options()
|
414 |
+
{
|
415 |
+
// Get all options and merge them
|
416 |
+
$options = $this->options_default;
|
417 |
+
foreach ($this->option_keys as $option)
|
418 |
+
{
|
419 |
+
$db_option = get_option($option);
|
420 |
+
if ($db_option && is_array($db_option))
|
421 |
+
$options = array_merge($options, $db_option);
|
422 |
+
unset($db_option);
|
423 |
+
// Also check for global options if in Multi-site
|
424 |
+
if ($this->is_multisite())
|
425 |
+
{
|
426 |
+
$db_option = get_site_option($option);
|
427 |
+
if ($db_option && is_array($db_option))
|
428 |
+
{
|
429 |
+
$temp = array();
|
430 |
+
foreach ($db_option as $k => $o)
|
431 |
+
{
|
432 |
+
if (in_array($k, $this->site_options))
|
433 |
+
$temp[$k] = $o;
|
434 |
+
}
|
435 |
+
$options = array_merge($options, $temp);
|
436 |
+
}
|
437 |
+
}
|
438 |
+
}
|
439 |
+
$this->options = $options;
|
440 |
+
}
|
441 |
+
|
442 |
+
protected function pre_init_properties()
|
443 |
+
{
|
444 |
+
/* intentionally left blank */
|
445 |
+
}
|
446 |
+
|
447 |
+
protected function init_properties()
|
448 |
+
{
|
449 |
+
/* intentionally left blank */
|
450 |
+
}
|
451 |
+
|
452 |
+
protected function load_libraries()
|
453 |
+
{
|
454 |
+
/* intentionally left blank */
|
455 |
+
}
|
456 |
+
|
457 |
+
protected function update_plugin($when = '')
|
458 |
+
{
|
459 |
+
if (!is_admin())
|
460 |
+
return;
|
461 |
+
|
462 |
+
$current_version = $this->plugin_ver;
|
463 |
+
$db_version = get_option($this->plugin_key . '_version');
|
464 |
+
|
465 |
+
$action_hook = 'pre_init' == $when
|
466 |
+
? $this->plugin_key . '_upgrade'
|
467 |
+
: $this->plugin_key . '_init_upgrade';
|
468 |
+
|
469 |
+
if (!$db_version || version_compare($db_version, $current_version, '<'))
|
470 |
+
{
|
471 |
+
do_action($action_hook, $db_version, $current_version);
|
472 |
+
// only mark as upgraded when this is init update
|
473 |
+
if ('init' == $when)
|
474 |
+
update_option($this->plugin_key . '_version', $current_version);
|
475 |
+
}
|
476 |
+
}
|
477 |
+
|
478 |
+
protected function pre_init_update_plugin()
|
479 |
+
{
|
480 |
+
$this->update_plugin('pre_init');
|
481 |
+
}
|
482 |
+
|
483 |
+
protected function init_update_plugin()
|
484 |
+
{
|
485 |
+
$this->update_plugin('init');
|
486 |
+
}
|
487 |
+
|
488 |
+
protected function pre_init_hooks()
|
489 |
+
{
|
490 |
+
/* intentionally left blank */
|
491 |
+
}
|
492 |
+
|
493 |
+
protected function init_hooks()
|
494 |
+
{
|
495 |
+
/* intentionally left blank */
|
496 |
+
}
|
497 |
+
|
498 |
+
protected function enqueue_media()
|
499 |
+
{
|
500 |
+
/* intentionally left blank */
|
501 |
+
}
|
502 |
+
|
503 |
+
public function install()
|
504 |
+
{
|
505 |
+
/* intentionally left blank */
|
506 |
+
}
|
507 |
+
|
508 |
+
public function uninstall()
|
509 |
+
{
|
510 |
+
/* intentionally left blank */
|
511 |
+
}
|
512 |
+
|
513 |
+
protected function is_admin_page($page = '')
|
514 |
+
{
|
515 |
+
if (is_admin() && !empty($_GET['page'])
|
516 |
+
&& (in_array($_GET['page'], $this->option_keys)
|
517 |
+
|| in_array($_GET['page'], $this->extra_option_keys))
|
518 |
+
&& (empty($page)
|
519 |
+
|| (!empty($page) && $page == $_GET['page']))
|
520 |
+
) {
|
521 |
+
return true;
|
522 |
+
}
|
523 |
+
}
|
524 |
+
|
525 |
+
public function get_admin_page($page)
|
526 |
+
{
|
527 |
+
$option_script = !$this->_menu_under_settings && !$this->_simple_menu
|
528 |
+
? 'admin.php'
|
529 |
+
: 'options-general.php';
|
530 |
+
|
531 |
+
return add_query_arg(array('page' => $page), admin_url($option_script));
|
532 |
+
}
|
533 |
+
|
534 |
+
public function plugin_action_links($links, $file)
|
535 |
+
{
|
536 |
+
$option_keys = array_values($this->option_keys);
|
537 |
+
|
538 |
+
if (false !== strpos(plugin_basename($this->plugin_file), $file))
|
539 |
+
{
|
540 |
+
$links[] = '<a href="' . $this->get_admin_page($option_keys[0]) . '">'
|
541 |
+
. __('Settings') . '</a>';
|
542 |
+
}
|
543 |
+
|
544 |
+
return $links;
|
545 |
+
}
|
546 |
+
|
547 |
+
public function init_admin()
|
548 |
+
{
|
549 |
+
$this->_menu_under_settings = apply_filters('bwp_menus_under_settings', false);
|
550 |
+
|
551 |
+
add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
|
552 |
+
|
553 |
+
if ($this->is_admin_page())
|
554 |
+
{
|
555 |
+
// Build tabs
|
556 |
+
$this->build_tabs();
|
557 |
+
|
558 |
+
// Load option page builder
|
559 |
+
if (!class_exists('BWP_OPTION_PAGE'))
|
560 |
+
require_once dirname(__FILE__) . '/bwp-option-page/bwp-option-page.php';
|
561 |
+
|
562 |
+
// Enqueue style sheets and scripts for the option page
|
563 |
+
wp_enqueue_style(
|
564 |
+
'bwp-option-page',
|
565 |
+
$this->plugin_wp_url . 'includes/bwp-option-page/css/bwp-option-page.css',
|
566 |
+
self::is_multisite() || class_exists('JCP_UseGoogleLibraries') ? array('wp-admin') : array(),
|
567 |
+
'1.1.0'
|
568 |
+
);
|
569 |
+
|
570 |
+
wp_enqueue_script(
|
571 |
+
'bwp-paypal-js',
|
572 |
+
$this->plugin_wp_url . 'includes/bwp-option-page/js/paypal.js',
|
573 |
+
array('jquery')
|
574 |
+
);
|
575 |
+
}
|
576 |
+
|
577 |
+
$this->build_menus();
|
578 |
+
}
|
579 |
+
|
580 |
+
/**
|
581 |
+
* Build the Menus
|
582 |
+
*/
|
583 |
+
protected function build_menus()
|
584 |
+
{
|
585 |
+
/* intentionally left blank */
|
586 |
+
}
|
587 |
+
|
588 |
+
protected function build_tabs()
|
589 |
+
{
|
590 |
+
$option_script = !$this->_menu_under_settings
|
591 |
+
? 'admin.php'
|
592 |
+
: 'options-general.php';
|
593 |
+
|
594 |
+
foreach ($this->option_pages as $key => $page)
|
595 |
+
{
|
596 |
+
$pagelink = !empty($this->option_keys[$key])
|
597 |
+
? $this->option_keys[$key]
|
598 |
+
: $this->extra_option_keys[$key];
|
599 |
+
|
600 |
+
$this->form_tabs[$page] = admin_url($option_script)
|
601 |
+
. '?page=' . $pagelink;
|
602 |
+
}
|
603 |
+
}
|
604 |
+
|
605 |
+
/**
|
606 |
+
* Build the option pages
|
607 |
+
*
|
608 |
+
* Utilizes BWP Option Page Builder (@see BWP_OPTION_PAGE)
|
609 |
+
*/
|
610 |
+
protected function build_option_pages()
|
611 |
+
{
|
612 |
+
/* intentionally left blank */
|
613 |
+
}
|
614 |
+
|
615 |
+
protected function add_notice($notice)
|
616 |
+
{
|
617 |
+
if (!in_array($notice, $this->notices))
|
618 |
+
{
|
619 |
+
$this->notices[] = $notice;
|
620 |
+
add_action('bwp_option_action_before_form', array($this, 'show_notices'));
|
621 |
+
}
|
622 |
+
}
|
623 |
+
|
624 |
+
public function show_notices()
|
625 |
+
{
|
626 |
+
if (false == $this->notice_shown)
|
627 |
+
{
|
628 |
+
foreach ($this->notices as $notice)
|
629 |
+
{
|
630 |
+
echo '<div class="updated fade"><p>' . $notice . '</p></div>';
|
631 |
+
}
|
632 |
+
$this->notice_shown = true;
|
633 |
+
}
|
634 |
+
}
|
635 |
+
|
636 |
+
protected function add_error($error)
|
637 |
+
{
|
638 |
+
if (!in_array($error, $this->errors))
|
639 |
+
{
|
640 |
+
$this->errors[] = $error;
|
641 |
+
add_action('bwp_option_action_before_form', array($this, 'show_errors'));
|
642 |
+
}
|
643 |
+
}
|
644 |
+
|
645 |
+
public function show_errors()
|
646 |
+
{
|
647 |
+
if (false == $this->error_shown)
|
648 |
+
{
|
649 |
+
foreach ($this->errors as $error)
|
650 |
+
{
|
651 |
+
echo '<div class="error"><p>' . $error . '</p></div>';
|
652 |
+
}
|
653 |
+
$this->error_shown = true;
|
654 |
+
}
|
655 |
+
}
|
656 |
+
|
657 |
+
public function add_url($key, $url, $relative = true)
|
658 |
+
{
|
659 |
+
$this->urls[$key] = array(
|
660 |
+
'relative' => $relative,
|
661 |
+
'url' => $url
|
662 |
+
);
|
663 |
+
}
|
664 |
+
|
665 |
+
public function get_url($key)
|
666 |
+
{
|
667 |
+
if (isset($this->urls[$key]))
|
668 |
+
{
|
669 |
+
$url = $this->urls[$key];
|
670 |
+
if ($url['relative'])
|
671 |
+
return trailingslashit($this->plugin_url) . $url['url'];
|
672 |
+
|
673 |
+
return $url['url'];
|
674 |
+
}
|
675 |
+
|
676 |
+
return '';
|
677 |
+
}
|
678 |
+
|
679 |
+
public static function is_multisite()
|
680 |
+
{
|
681 |
+
if (function_exists('is_multisite') && is_multisite())
|
682 |
+
return true;
|
683 |
+
|
684 |
+
if (defined('MULTISITE'))
|
685 |
+
return MULTISITE;
|
686 |
+
|
687 |
+
if (defined('SUBDOMAIN_INSTALL') || defined('VHOST') || defined('SUNRISE'))
|
688 |
+
return true;
|
689 |
+
|
690 |
+
return false;
|
691 |
+
}
|
692 |
+
|
693 |
+
public static function is_subdomain_install()
|
694 |
+
{
|
695 |
+
if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL)
|
696 |
+
return true;
|
697 |
+
|
698 |
+
return false;
|
699 |
+
}
|
700 |
+
|
701 |
+
protected static function is_normal_admin()
|
702 |
+
{
|
703 |
+
if (self::is_multisite() && !is_super_admin())
|
704 |
+
return true;
|
705 |
+
return false;
|
706 |
+
}
|
707 |
+
|
708 |
+
protected static function is_apache()
|
709 |
+
{
|
710 |
+
if (isset($_SERVER['SERVER_SOFTWARE'])
|
711 |
+
&& false !== stripos($_SERVER['SERVER_SOFTWARE'], 'apache')
|
712 |
+
) {
|
713 |
+
return true;
|
714 |
+
}
|
715 |
+
return false;
|
716 |
+
}
|
717 |
+
|
718 |
+
protected static function is_nginx()
|
719 |
+
{
|
720 |
+
if (isset($_SERVER['SERVER_SOFTWARE'])
|
721 |
+
&& false !== stripos($_SERVER['SERVER_SOFTWARE'], 'nginx')
|
722 |
+
) {
|
723 |
+
return true;
|
724 |
+
}
|
725 |
+
return false;
|
726 |
+
}
|
727 |
+
}
|
includes/class-bwp-minify-cdn.php
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) 2014 Khang Minh <http://betterwp.net>
|
4 |
+
* @license http://www.gnu.org/licenses/gpl.html GNU GENERAL PUBLIC LICENSE VERSION 3.0 OR LATER
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class BWP_Minify_CDN
|
9 |
+
* @author Khang Minh
|
10 |
+
* @since BWP Minify 1.3.0
|
11 |
+
* @package BWP Minify
|
12 |
+
*/
|
13 |
+
class BWP_Minify_CDN
|
14 |
+
{
|
15 |
+
private $_options = array();
|
16 |
+
|
17 |
+
private $_domain = '';
|
18 |
+
|
19 |
+
public function __construct($options, $domain)
|
20 |
+
{
|
21 |
+
$this->_options = $options;
|
22 |
+
$this->_domain = $domain;
|
23 |
+
|
24 |
+
$this->_init();
|
25 |
+
}
|
26 |
+
|
27 |
+
public function replace_host($string, $original_string)
|
28 |
+
{
|
29 |
+
$ext = preg_match('/\.([^\.]+)$/ui', $original_string, $matches)
|
30 |
+
? $matches[1] : '';
|
31 |
+
|
32 |
+
if (empty($ext))
|
33 |
+
return $string;
|
34 |
+
|
35 |
+
$cdn_host = $this->_get_cdn_host($ext);
|
36 |
+
if (empty($cdn_host))
|
37 |
+
return $string;
|
38 |
+
|
39 |
+
|
40 |
+
// force SSL when WordPress is on SSL, or use scheme-less URL
|
41 |
+
$ssl_type = $this->_options['select_cdn_ssl_type'];
|
42 |
+
$scheme = is_ssl() ? 'https://' : 'http://';
|
43 |
+
$scheme = 'less' == $ssl_type ? '//' : $scheme;
|
44 |
+
$scheme = 'off' == $ssl_type ? 'http://' : $scheme;
|
45 |
+
$string = preg_replace('#https?://[^/]+#ui',
|
46 |
+
$scheme . $cdn_host,
|
47 |
+
$string
|
48 |
+
);
|
49 |
+
|
50 |
+
return $string;
|
51 |
+
}
|
52 |
+
|
53 |
+
private static function _sanitize_cdn_host($host)
|
54 |
+
{
|
55 |
+
$host = untrailingslashit($host);
|
56 |
+
return str_replace(array('http://', 'https://'), '', $host);
|
57 |
+
}
|
58 |
+
|
59 |
+
private function _get_cdn_host($ext)
|
60 |
+
{
|
61 |
+
$cdn_host = self::_sanitize_cdn_host($this->_options['input_cdn_host']);
|
62 |
+
|
63 |
+
// use file-type specific CDN host if needed
|
64 |
+
$js_cdn = !empty($this->_options['input_cdn_host_js'])
|
65 |
+
? self::_sanitize_cdn_host($this->_options['input_cdn_host_js'])
|
66 |
+
: $cdn_host;
|
67 |
+
$css_cdn = !empty($this->_options['input_cdn_host_css'])
|
68 |
+
? self::_sanitize_cdn_host($this->_options['input_cdn_host_css'])
|
69 |
+
: $cdn_host;
|
70 |
+
|
71 |
+
return 'js' == $ext ? $js_cdn : $css_cdn;
|
72 |
+
}
|
73 |
+
|
74 |
+
private function _register_hooks()
|
75 |
+
{
|
76 |
+
// priority 11 to make sure that this filter is applied after the
|
77 |
+
// fetcher class has finished friendlifying the Minify string
|
78 |
+
add_filter('bwp_get_minify_src', array($this, 'replace_host'), 11, 2);
|
79 |
+
}
|
80 |
+
|
81 |
+
private function _init()
|
82 |
+
{
|
83 |
+
$this->_register_hooks();
|
84 |
+
}
|
85 |
+
}
|
includes/class-bwp-minify-fetcher.php
ADDED
@@ -0,0 +1,319 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) 2014 Khang Minh <http://betterwp.net>
|
4 |
+
* @license http://www.gnu.org/licenses/gpl.html GNU GENERAL PUBLIC LICENSE VERSION 3.0 OR LATER
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class BWP_Minify_Fetcher
|
9 |
+
* @author Khang Minh <contact@betterwp.net>
|
10 |
+
* @since BWP Minify 1.3.0
|
11 |
+
* @package BWP Minify
|
12 |
+
*/
|
13 |
+
class BWP_Minify_Fetcher
|
14 |
+
{
|
15 |
+
private $_domain = '';
|
16 |
+
|
17 |
+
private $_options = array();
|
18 |
+
|
19 |
+
private $_main;
|
20 |
+
|
21 |
+
private $_detector;
|
22 |
+
|
23 |
+
private $_min_url = '';
|
24 |
+
|
25 |
+
private $_min_fly_url = '';
|
26 |
+
|
27 |
+
private $_rewrite_pattern = '';
|
28 |
+
|
29 |
+
private $_rewrite_rules_ready = false;
|
30 |
+
|
31 |
+
private $_cache_dir = '';
|
32 |
+
|
33 |
+
private $_group_cache = array();
|
34 |
+
|
35 |
+
private $_minify_version = '2.1.7';
|
36 |
+
|
37 |
+
public function __construct($options, $domain)
|
38 |
+
{
|
39 |
+
$this->_options = $options;
|
40 |
+
$this->_domain = $domain;
|
41 |
+
|
42 |
+
$this->_init();
|
43 |
+
}
|
44 |
+
|
45 |
+
public function set_main($main)
|
46 |
+
{
|
47 |
+
$this->_main = $main;
|
48 |
+
}
|
49 |
+
|
50 |
+
public function set_detector($detector)
|
51 |
+
{
|
52 |
+
$this->_detector = $detector;
|
53 |
+
}
|
54 |
+
|
55 |
+
public function set_min_url($min_url)
|
56 |
+
{
|
57 |
+
$this->_min_url = $min_url;
|
58 |
+
}
|
59 |
+
|
60 |
+
public function set_min_fly_url($min_fly_url)
|
61 |
+
{
|
62 |
+
$this->_min_fly_url = $min_fly_url;
|
63 |
+
}
|
64 |
+
|
65 |
+
public function set_rewrite_pattern($pattern)
|
66 |
+
{
|
67 |
+
$this->_rewrite_pattern = $pattern;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Serves friendly minify url with minified contents
|
72 |
+
*
|
73 |
+
* This actually does a remote get from Minify's dir with appropriate `?f`
|
74 |
+
* query variable built from persistently stored detected enqueued files.
|
75 |
+
* All minified contents as well as headers should be preserved.
|
76 |
+
*
|
77 |
+
* Serving friendly minify urls like this cause a lot of overheads, but is
|
78 |
+
* safer to use than server rewrite rules.
|
79 |
+
*
|
80 |
+
* @uses wp_remote_get()
|
81 |
+
* @return void
|
82 |
+
*/
|
83 |
+
public function serve($query)
|
84 |
+
{
|
85 |
+
$group_handle = isset($_GET['min_group'])
|
86 |
+
? $this->_sanitize_request($_GET['min_group'])
|
87 |
+
: '';
|
88 |
+
$type = isset($_GET['min_type'])
|
89 |
+
&& in_array($_GET['min_type'], array('js', 'css'))
|
90 |
+
? $_GET['min_type']
|
91 |
+
: '';
|
92 |
+
$bid = isset($_GET['blog']) ? (int) $_GET['blog'] : 0;
|
93 |
+
|
94 |
+
if (empty($group_handle) || empty($type) || empty($bid))
|
95 |
+
// if missing any important data, do not proceed
|
96 |
+
return;
|
97 |
+
|
98 |
+
global $blog_id;
|
99 |
+
if ($bid != $blog_id)
|
100 |
+
// if not on the correct blog, do not proceed
|
101 |
+
return;
|
102 |
+
|
103 |
+
$group = $this->_detector->get_group($group_handle);
|
104 |
+
if (false == $group)
|
105 |
+
{
|
106 |
+
// this should not happen, but just in case it does
|
107 |
+
echo sprintf(
|
108 |
+
__('Minify group %s not found.', $this->_domain),
|
109 |
+
$group_handle
|
110 |
+
);
|
111 |
+
exit;
|
112 |
+
}
|
113 |
+
|
114 |
+
// load Minify class based on default or custom Minify directory
|
115 |
+
$min_dir = $this->_main->get_min_dir();
|
116 |
+
|
117 |
+
if (empty($min_dir) || !file_exists($min_dir . 'config.php'))
|
118 |
+
{
|
119 |
+
// if Minify directory is not valid or Minify library is not found
|
120 |
+
// we can not continue
|
121 |
+
_e('Invalid Minify directory, please check Minify settings.', $this->_domain);
|
122 |
+
exit;
|
123 |
+
}
|
124 |
+
|
125 |
+
// prepare input for Minify to handle
|
126 |
+
$_GET['f'] = $group['string'];
|
127 |
+
|
128 |
+
if ($this->_options['enable_debug'] == 'yes')
|
129 |
+
{
|
130 |
+
// add debug flag if needed
|
131 |
+
$_GET['debug'] = 1;
|
132 |
+
}
|
133 |
+
else
|
134 |
+
{
|
135 |
+
// minified contents are often served compressed so it's best to turn off
|
136 |
+
// error reporting to avoid content encoding error, unless debug is
|
137 |
+
// enabled. This is useful when Minify doesn't catch all the notices
|
138 |
+
// (for example when the cache directory is not writable).
|
139 |
+
error_reporting(0);
|
140 |
+
}
|
141 |
+
|
142 |
+
// load Minify classes
|
143 |
+
define('MINIFY_MIN_DIR', $min_dir);
|
144 |
+
require_once MINIFY_MIN_DIR . '/config.php';
|
145 |
+
require_once $min_libPath . '/Minify/Loader.php';
|
146 |
+
Minify_Loader::register();
|
147 |
+
|
148 |
+
// set some optional for the Minify application based on current settings
|
149 |
+
Minify::$uploaderHoursBehind = $min_uploaderHoursBehind;
|
150 |
+
|
151 |
+
// set cache file name (id) and cache path if needed
|
152 |
+
Minify::setCacheId('minify-b' . $blog_id . '-' . $group_handle . '.' . $type);
|
153 |
+
Minify::setCache(
|
154 |
+
isset($min_cachePath) ? $min_cachePath : '',
|
155 |
+
$min_cacheFileLocking
|
156 |
+
);
|
157 |
+
|
158 |
+
if ($min_documentRoot)
|
159 |
+
{
|
160 |
+
$_SERVER['DOCUMENT_ROOT'] = $min_documentRoot;
|
161 |
+
Minify::$isDocRootSet = true;
|
162 |
+
}
|
163 |
+
|
164 |
+
// set serve options for each file type if needed
|
165 |
+
$min_serveOptions['minifierOptions']['text/css']['symlinks'] = $min_symlinks;
|
166 |
+
foreach ($min_symlinks as $uri => $target)
|
167 |
+
$min_serveOptions['minApp']['allowDirs'][] = $target;
|
168 |
+
|
169 |
+
if ($min_allowDebugFlag)
|
170 |
+
{
|
171 |
+
// allow debugging Minify application
|
172 |
+
$min_serveOptions['debug'] = Minify_DebugDetector::shouldDebugRequest(
|
173 |
+
$_COOKIE,
|
174 |
+
$_GET,
|
175 |
+
$_SERVER['REQUEST_URI']
|
176 |
+
);
|
177 |
+
}
|
178 |
+
|
179 |
+
if ($min_errorLogger)
|
180 |
+
{
|
181 |
+
// log Minify error if allowed
|
182 |
+
if (true === $min_errorLogger)
|
183 |
+
$min_errorLogger = FirePHP::getInstance(true);
|
184 |
+
|
185 |
+
Minify_Logger::setLogger($min_errorLogger);
|
186 |
+
}
|
187 |
+
|
188 |
+
// serve minified contents, on the fly or from cache
|
189 |
+
$min_serveController = new Minify_Controller_MinApp();
|
190 |
+
Minify::serve($min_serveController, $min_serveOptions);
|
191 |
+
|
192 |
+
// for a proper response headers
|
193 |
+
exit;
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Converts a regular Minify url to a more friendly one
|
198 |
+
*
|
199 |
+
* A regular Minify url is only converted when all enqueued files
|
200 |
+
* contained in it have already been detected (logged and saved in
|
201 |
+
* database). This is to make sure that when a friendly url is requested
|
202 |
+
* the fetcher knows what files to serve based on group handle.
|
203 |
+
*
|
204 |
+
* @param $string string the Minify url with min path prepended
|
205 |
+
* @param $original_string string the Minify url without min path
|
206 |
+
* @return string
|
207 |
+
*/
|
208 |
+
public function friendlify_src($string, $original_string, $group_handle, $buster)
|
209 |
+
{
|
210 |
+
// need to check whether a valid minify urls is set, if not we should
|
211 |
+
// return the regular Minify string
|
212 |
+
if (false === $this->_min_fly_url)
|
213 |
+
return $string;
|
214 |
+
|
215 |
+
// get extension from minify string, this determines the type of source
|
216 |
+
// we're dealing with
|
217 |
+
$ext = preg_match('/\.([^\.]+)$/ui', $original_string, $matches)
|
218 |
+
? $matches[1] : '';
|
219 |
+
|
220 |
+
if (empty($ext))
|
221 |
+
return $string;
|
222 |
+
|
223 |
+
// if this group has not been detected we serve the regular Minify url,
|
224 |
+
// and detect this group as well so it can be served using friendly url
|
225 |
+
// next time
|
226 |
+
$detector_version = $this->_detector->get_version();
|
227 |
+
$group_hash = md5($original_string . $detector_version);
|
228 |
+
|
229 |
+
if (false == $this->_is_group_detected($group_hash))
|
230 |
+
{
|
231 |
+
$group_type = 'js' == $ext ? 'script' : 'style';
|
232 |
+
|
233 |
+
$this->_detector->detect_group(
|
234 |
+
$group_handle,
|
235 |
+
$original_string,
|
236 |
+
$group_type
|
237 |
+
);
|
238 |
+
|
239 |
+
return $string;
|
240 |
+
}
|
241 |
+
|
242 |
+
// build the friendly url for this minify url
|
243 |
+
global $blog_id;
|
244 |
+
$fly_url = $this->_min_fly_url . 'minify-'
|
245 |
+
. 'b' . $blog_id . '-' . $group_handle
|
246 |
+
. '-' . $group_hash
|
247 |
+
. '.' . $ext;
|
248 |
+
|
249 |
+
// add a cache buster if needed
|
250 |
+
$fly_url .= !empty($buster) ? '?ver=' . $buster : '';
|
251 |
+
|
252 |
+
return $fly_url;
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Checks whether all expected groups are already detected
|
257 |
+
*
|
258 |
+
* @param $group_hash string the handle of the group being checked
|
259 |
+
* @return bool
|
260 |
+
*/
|
261 |
+
private function _is_group_detected($group_hash)
|
262 |
+
{
|
263 |
+
// get detected groups from db to make sure that they are persistently stored
|
264 |
+
$detected = $this->_detector->get_detected_groups(true);
|
265 |
+
|
266 |
+
if (0 == sizeof($detected))
|
267 |
+
return false;
|
268 |
+
|
269 |
+
foreach ($detected as $key => $group)
|
270 |
+
{
|
271 |
+
if ($group_hash == $group['hash'])
|
272 |
+
return true;
|
273 |
+
}
|
274 |
+
|
275 |
+
return false;
|
276 |
+
}
|
277 |
+
|
278 |
+
private function _sanitize_request($request)
|
279 |
+
{
|
280 |
+
return trim(stripslashes(strip_tags($request)));
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Gets the original request headers
|
285 |
+
*
|
286 |
+
* @return array
|
287 |
+
*/
|
288 |
+
private function _get_request_headers()
|
289 |
+
{
|
290 |
+
$headers = array();
|
291 |
+
|
292 |
+
foreach ($_SERVER as $k => $v)
|
293 |
+
{
|
294 |
+
if (substr($k, 0, 5) == 'HTTP_')
|
295 |
+
{
|
296 |
+
$k = str_replace(' ', '-', ucwords(
|
297 |
+
strtolower(str_replace('_', ' ', substr($k, 5)))
|
298 |
+
));
|
299 |
+
$headers[$k] = $v;
|
300 |
+
}
|
301 |
+
}
|
302 |
+
|
303 |
+
return $headers;
|
304 |
+
}
|
305 |
+
|
306 |
+
private function _register_hooks()
|
307 |
+
{
|
308 |
+
add_action('parse_request', array($this, 'serve'), 1);
|
309 |
+
add_filter('bwp_minify_get_src', array($this, 'friendlify_src'), 10, 4);
|
310 |
+
}
|
311 |
+
|
312 |
+
private function _init_properties() {}
|
313 |
+
|
314 |
+
private function _init()
|
315 |
+
{
|
316 |
+
$this->_init_properties();
|
317 |
+
$this->_register_hooks();
|
318 |
+
}
|
319 |
+
}
|
includes/class-bwp-minify.php
ADDED
@@ -0,0 +1,3504 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Main BWP Minify class that provides all logics.
|
4 |
+
*
|
5 |
+
* Copyright (c) 2014 Khang Minh <betterwp.net>
|
6 |
+
*
|
7 |
+
* This program is free software: you can redistribute it and/or modify
|
8 |
+
* it under the terms of the GNU General Public License as published by
|
9 |
+
* the Free Software Foundation, either version 3 of the License, or
|
10 |
+
* (at your option) any later version.
|
11 |
+
*
|
12 |
+
* This program is distributed in the hope that it will be useful,
|
13 |
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 |
+
* GNU General Public License for more details.
|
16 |
+
*
|
17 |
+
* You should have received a copy of the GNU General Public License
|
18 |
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19 |
+
*
|
20 |
+
* @author Khang Minh <contact@betterwp.net>
|
21 |
+
* @link http://betterwp.net/wordpress-plugins/bwp-minify/
|
22 |
+
* @link https://github.com/OddOneOut/Better-WordPress-Minify/
|
23 |
+
*/
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Convert a normal source to a minified source.
|
27 |
+
*
|
28 |
+
* Please do not use this function before WordPress has been
|
29 |
+
* initialized. Otherwise, you might get a fatal error.
|
30 |
+
*
|
31 |
+
* @param string $src The source file you would like to convert
|
32 |
+
*/
|
33 |
+
function bwp_minify($src)
|
34 |
+
{
|
35 |
+
global $bwp_minify;
|
36 |
+
|
37 |
+
return $bwp_minify->minify_item($src);
|
38 |
+
}
|
39 |
+
|
40 |
+
if (!class_exists('BWP_FRAMEWORK_IMPROVED'))
|
41 |
+
require_once dirname(__FILE__) . '/class-bwp-framework-improved.php';
|
42 |
+
|
43 |
+
class BWP_MINIFY extends BWP_FRAMEWORK_IMPROVED
|
44 |
+
{
|
45 |
+
/**
|
46 |
+
* Positions to put scripts/styles in
|
47 |
+
*/
|
48 |
+
var $print_positions = array();
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Internal _Todo_ lists
|
52 |
+
*
|
53 |
+
* @since 1.3.0
|
54 |
+
*/
|
55 |
+
var $todo_scripts = array(), $todo_styles = array(), $todo_l10n = array(), $todo_inline_styles = array();
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Minify groups
|
59 |
+
*
|
60 |
+
* @since 1.3.0
|
61 |
+
*/
|
62 |
+
var $min_scripts = array(), $min_styles = array();
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Special handling for unusually printed scripts/styles
|
66 |
+
*
|
67 |
+
* @since 1.3.0
|
68 |
+
*/
|
69 |
+
var $late_script_order = 1, $todo_late_scripts = '';
|
70 |
+
var $late_style_order = 1, $todo_late_styles = '';
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Other options
|
74 |
+
*/
|
75 |
+
var $wp_ver = '', $base = '', $remove_from_base = '', $buster = '', $cache_age = 86400;
|
76 |
+
var $min_url = '', $min_dir = '', $min_path = '', $min_cache_dir = '';
|
77 |
+
var $fly_min_path = '', $fly_min_url = '';
|
78 |
+
var $doc_root = '', $blog_path = false;
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Holds the extracted HTTP Host
|
82 |
+
*
|
83 |
+
* @since 1.3.0
|
84 |
+
*/
|
85 |
+
var $http_host = '';
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Default properties used throughout the plugin
|
89 |
+
*
|
90 |
+
* @since 1.3.0
|
91 |
+
*/
|
92 |
+
var $default_min_path = '';
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Rewrite rules used for friendly minify url
|
96 |
+
*
|
97 |
+
* @since 1.3.0
|
98 |
+
* @var array
|
99 |
+
*/
|
100 |
+
var $rewrite_rules;
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Holds an instance of BWP Enqueued Detector class
|
104 |
+
*
|
105 |
+
* Responsible for detecting and logging enqueued JS, CSS files. Also used
|
106 |
+
* to display a friendly page to move JS, CSS files to appropriate
|
107 |
+
* positions.
|
108 |
+
*
|
109 |
+
* @since 1.3.0
|
110 |
+
* @var BWP_Enqueued_Detector
|
111 |
+
*/
|
112 |
+
var $detector;
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Holds an instance of BWP Minify Fetcher class
|
116 |
+
*
|
117 |
+
* Responsible for fetching minified contents served by Minify library and
|
118 |
+
* allow BWP Minify to link to them from almost anywhere. This makes it
|
119 |
+
* possible to have pretty Minify urls like
|
120 |
+
* `http://www.example.com/assets/arandomstring.js`
|
121 |
+
*
|
122 |
+
* @since 1.3.0
|
123 |
+
* @var BWP_Minify_Fetcher
|
124 |
+
*/
|
125 |
+
var $fetcher;
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Holds an instance of BWP Minify CDN class
|
129 |
+
*
|
130 |
+
* @since 1.3.0
|
131 |
+
* @var BWP_Minify_CDN
|
132 |
+
*/
|
133 |
+
var $cdn;
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Holds an instance of BWP Minify Rewriter class
|
137 |
+
*
|
138 |
+
* @since 1.3.0
|
139 |
+
* @var BWP_Minify_AbstractRewriter
|
140 |
+
*/
|
141 |
+
var $rewriter;
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Constructor
|
145 |
+
*/
|
146 |
+
function __construct($version = '1.3.0')
|
147 |
+
{
|
148 |
+
// Plugin's title
|
149 |
+
$this->plugin_title = 'Better WordPress Minify';
|
150 |
+
// Plugin's version
|
151 |
+
$this->set_version($version);
|
152 |
+
$this->set_version('5.1.6', 'php');
|
153 |
+
// Plugin's language domain
|
154 |
+
$this->domain = 'bwp-minify';
|
155 |
+
// Basic version checking
|
156 |
+
if (!$this->check_required_versions())
|
157 |
+
return;
|
158 |
+
|
159 |
+
// The default options
|
160 |
+
$options = array(
|
161 |
+
'input_minurl' => '', // @deprecated 1.3.0 replaced by input_minpath
|
162 |
+
'input_minpath' => '',
|
163 |
+
'input_cache_dir' => '', // super admin, Minify
|
164 |
+
'input_doc_root' => '', // @since 1.3.0 super admin, Minify
|
165 |
+
'input_fly_minpath' => '', // @since 1.3.0 super admin, Minify
|
166 |
+
'input_nginx_config_file' => '', // @since 1.3.0, super admin
|
167 |
+
'input_maxfiles' => 10,
|
168 |
+
'input_maxage' => 1, // super admin, Minify
|
169 |
+
'input_symlinks' => '', // @since 1.3.0, super admin
|
170 |
+
'input_ignore' => 'admin-bar' . "\r\n" . 'jquery-core' . "\r\n" . 'jquery-migrate',
|
171 |
+
'input_header' => '',
|
172 |
+
'input_direct' => '',
|
173 |
+
'input_footer' => '',
|
174 |
+
'input_oblivion' => '',
|
175 |
+
'input_style_ignore' => 'admin-bar' . "\r\n" . 'dashicons',
|
176 |
+
'input_style_direct' => '',
|
177 |
+
'input_style_oblivion' => '',
|
178 |
+
'input_custom_buster' => '',
|
179 |
+
'input_cdn_host' => '', // @since 1.3.0
|
180 |
+
'input_cdn_host_js' => '', // @since 1.3.0
|
181 |
+
'input_cdn_host_css' => '', // @since 1.3.0
|
182 |
+
'enable_min_js' => 'yes',
|
183 |
+
'enable_min_css' => 'yes',
|
184 |
+
'enable_bloginfo' => '',
|
185 |
+
'enable_css_bubble' => 'yes', // @since 1.3.0 super admin, Minify
|
186 |
+
'enable_cache_file_lock' => 'yes', // @since 1.3.0 super admin, Minify
|
187 |
+
'enable_debug' => '', // @since 1.3.0 super admin
|
188 |
+
'enable_fly_min' => '', // @since 1.3.0
|
189 |
+
'enable_cdn' => '', // @since 1.3.0
|
190 |
+
'select_buster_type' => 'none',
|
191 |
+
'select_time_type' => 86400, // super admin, Minify
|
192 |
+
'select_fly_serve_method' => 'wp', // @since 1.3.0
|
193 |
+
'select_cdn_ssl_type' => 'off' // @since 1.3.0
|
194 |
+
);
|
195 |
+
|
196 |
+
// super admin only options
|
197 |
+
$this->site_options = array(
|
198 |
+
'input_cache_dir',
|
199 |
+
'input_doc_root',
|
200 |
+
'input_fly_minpath',
|
201 |
+
'input_nginx_config_file',
|
202 |
+
'input_maxage',
|
203 |
+
'input_symlinks',
|
204 |
+
'enable_debug',
|
205 |
+
'enable_css_bubble',
|
206 |
+
'enable_cache_file_lock',
|
207 |
+
'select_time_type'
|
208 |
+
);
|
209 |
+
|
210 |
+
$this->add_option_key('BWP_MINIFY_OPTION_GENERAL', 'bwp_minify_general',
|
211 |
+
__('General Options', $this->domain)
|
212 |
+
);
|
213 |
+
$this->add_option_key('BWP_MINIFY_OPTION_ADVANCED', 'bwp_minify_advanced',
|
214 |
+
__('Advanced Options', $this->domain)
|
215 |
+
);
|
216 |
+
$this->add_extra_option_key('BWP_MINIFY_MANAGE', 'bwp_minify_manage',
|
217 |
+
__('Manage enqueued Files', $this->domain)
|
218 |
+
);
|
219 |
+
|
220 |
+
// define hidden option keys
|
221 |
+
define('BWP_MINIFY_DETECTOR_LOG', 'bwp_minify_detector_log');
|
222 |
+
|
223 |
+
$this->build_properties('BWP_MINIFY', $this->domain, $options,
|
224 |
+
'Better WordPress Minify', dirname(dirname(__FILE__)) . '/bwp-minify.php',
|
225 |
+
'http://betterwp.net/wordpress-plugins/bwp-minify/', false
|
226 |
+
);
|
227 |
+
}
|
228 |
+
|
229 |
+
function install()
|
230 |
+
{
|
231 |
+
// if friendly minify url is enabled we need to flush rewrite rules
|
232 |
+
if ('yes' == $this->options['enable_fly_min'])
|
233 |
+
$this->_add_rewrite_rules();
|
234 |
+
}
|
235 |
+
|
236 |
+
function uninstall()
|
237 |
+
{
|
238 |
+
$this->_remove_rewrite_rules();
|
239 |
+
}
|
240 |
+
|
241 |
+
function upgrade_plugin($from, $to)
|
242 |
+
{
|
243 |
+
if ($to == '1.3.0')
|
244 |
+
{
|
245 |
+
// @since 1.3.0 default values of min path and cache dir is empty,
|
246 |
+
// also look for 'Better-WordPress-Minify-1.3.0' string for users
|
247 |
+
// that tested 1.3.0-beta
|
248 |
+
$options = get_option(BWP_MINIFY_OPTION_GENERAL);
|
249 |
+
$this->_reset_min_path($options);
|
250 |
+
$this->_reset_cache_dir($options);
|
251 |
+
update_option(BWP_MINIFY_OPTION_GENERAL, $options);
|
252 |
+
}
|
253 |
+
}
|
254 |
+
|
255 |
+
protected function pre_init_properties()
|
256 |
+
{
|
257 |
+
$this->parse_positions();
|
258 |
+
|
259 |
+
$this->wp_ver = get_bloginfo('version');
|
260 |
+
|
261 |
+
// define a few urls that are used throughout the plugin
|
262 |
+
$this->add_url('min_debug',
|
263 |
+
'https://code.google.com/p/minify/wiki/Debugging',
|
264 |
+
false
|
265 |
+
);
|
266 |
+
$this->add_url('min_css_bubble',
|
267 |
+
'http://code.google.com/p/minify/wiki/CommonProblems#@imports_can_appear_in_invalid_locations_in_combined_CSS_files',
|
268 |
+
false
|
269 |
+
);
|
270 |
+
$this->add_url('wp_codex_permalink',
|
271 |
+
'http://codex.wordpress.org/Using_Permalinks#mod_rewrite:_.22Pretty_Permalinks.22',
|
272 |
+
false
|
273 |
+
);
|
274 |
+
$this->add_url('wp_codex_nginx_rewrite_rules',
|
275 |
+
'http://codex.wordpress.org/Nginx#General_WordPress_rules',
|
276 |
+
false
|
277 |
+
);
|
278 |
+
$this->add_url('wikipedia_cdn',
|
279 |
+
'http://en.wikipedia.org/wiki/Content_delivery_network',
|
280 |
+
false
|
281 |
+
);
|
282 |
+
$this->add_url('aff_maxcdn', 'http://bit.ly/bwp-minify-plugin-maxcdn', false);
|
283 |
+
|
284 |
+
// rewrite rules and headers start/end markers
|
285 |
+
define('BWP_MINIFY_RULES_BEGIN', '# BEGIN BWP Minify Rules');
|
286 |
+
define('BWP_MINIFY_RULES_END', '# END BWP Minify Rules');
|
287 |
+
define('BWP_MINIFY_HEADERS_BEGIN', '# BEGIN BWP Minify Headers');
|
288 |
+
define('BWP_MINIFY_HEADERS_END', '# END BWP Minify Headers');
|
289 |
+
define('BWP_MINIFY_WP_RULES_BEGIN', '# BEGIN BWP Minify WP Rules');
|
290 |
+
define('BWP_MINIFY_WP_RULES_END', '# END BWP Minify WP Rules');
|
291 |
+
}
|
292 |
+
|
293 |
+
private function _load_rewriter_class()
|
294 |
+
{
|
295 |
+
// load and init appropriate rewriter class based on server type
|
296 |
+
require_once dirname(__FILE__) . '/rewriter/rewriter.php';
|
297 |
+
if (self::is_nginx())
|
298 |
+
{
|
299 |
+
require_once dirname(__FILE__) . '/rewriter/nginx.php';
|
300 |
+
$this->rewriter = new BWP_Minify_Rewriter_Nginx($this);
|
301 |
+
}
|
302 |
+
else
|
303 |
+
{
|
304 |
+
// if not nginx, assume it's apache
|
305 |
+
require_once dirname(__FILE__) . '/rewriter/apache.php';
|
306 |
+
$this->rewriter = new BWP_Minify_Rewriter_Apache($this);
|
307 |
+
}
|
308 |
+
}
|
309 |
+
|
310 |
+
protected function load_libraries()
|
311 |
+
{
|
312 |
+
require_once dirname(__FILE__) . '/common-functions.php';
|
313 |
+
require_once dirname(__FILE__) . '/class-bwp-enqueued-detector.php';
|
314 |
+
require_once dirname(__FILE__) . '/class-bwp-minify-fetcher.php';
|
315 |
+
require_once dirname(__FILE__) . '/class-bwp-minify-cdn.php';
|
316 |
+
|
317 |
+
$this->_load_rewriter_class();
|
318 |
+
}
|
319 |
+
|
320 |
+
private function _reset_min_path(&$options = false)
|
321 |
+
{
|
322 |
+
$options = false == $options ? $this->options : $options;
|
323 |
+
if ($options['input_minpath'] == $this->get_default_min_path()
|
324 |
+
|| false !== stripos($options['input_minpath'], 'Better-WordPress-Minify-1.3.0')
|
325 |
+
) {
|
326 |
+
$options['input_minpath'] = '';
|
327 |
+
}
|
328 |
+
}
|
329 |
+
|
330 |
+
private function _reset_cache_dir(&$options = false)
|
331 |
+
{
|
332 |
+
$options = false == $options ? $this->options : $options;
|
333 |
+
if ($options['input_cache_dir'] == $this->get_default_cache_dir()
|
334 |
+
|| false !== stripos($options['input_cache_dir'], 'Better-WordPress-Minify-1.3.0')
|
335 |
+
) {
|
336 |
+
$options['input_cache_dir'] = '';
|
337 |
+
}
|
338 |
+
}
|
339 |
+
|
340 |
+
/**
|
341 |
+
* Makes sure handle created by this plugin is in expected format
|
342 |
+
*
|
343 |
+
* @return string
|
344 |
+
*/
|
345 |
+
private function _sanitize_handle($handle)
|
346 |
+
{
|
347 |
+
return preg_replace('/[^a-z0-9-_.]+/ui', '', $handle);
|
348 |
+
}
|
349 |
+
|
350 |
+
private function _sanitize_min_path($left_trim = false, $min_path = false)
|
351 |
+
{
|
352 |
+
$min_path = !$min_path ? $this->options['input_minpath'] : $min_path;
|
353 |
+
$min_path = trailingslashit($min_path);
|
354 |
+
$min_path = str_replace('\\', '/', $min_path);
|
355 |
+
$min_path = $left_trim ? ltrim($min_path, '/') : $min_path;
|
356 |
+
|
357 |
+
return $min_path;
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* Gets blog path for the current blog in a multisite installation
|
362 |
+
*
|
363 |
+
* @return string
|
364 |
+
*/
|
365 |
+
private function _get_blog_path()
|
366 |
+
{
|
367 |
+
if (!self::is_multisite())
|
368 |
+
return '';
|
369 |
+
|
370 |
+
if ($this->blog_path)
|
371 |
+
return $this->blog_path;
|
372 |
+
|
373 |
+
global $current_blog;
|
374 |
+
$blog_path = isset($current_blog->path) && '/' != $current_blog->path
|
375 |
+
? $current_blog->path
|
376 |
+
: '';
|
377 |
+
|
378 |
+
// because `$blog_path` also contains the base, we need to remove the
|
379 |
+
// base from it so we can process media source correctly later on
|
380 |
+
if (!empty($this->base) && 0 === strpos($blog_path, '/' . $this->base . '/'))
|
381 |
+
$blog_path = preg_replace('#/[^/]+#ui', '', $blog_path, 1);
|
382 |
+
|
383 |
+
$this->blog_path = $blog_path;
|
384 |
+
return $blog_path;
|
385 |
+
}
|
386 |
+
|
387 |
+
private function _get_http_host()
|
388 |
+
{
|
389 |
+
$home_url = home_url();
|
390 |
+
$http_host = '';
|
391 |
+
$matches = array();
|
392 |
+
|
393 |
+
// Gets the http host part of the wp url
|
394 |
+
if (false !== preg_match('#^https?://[^/]+#ui', $home_url, $matches))
|
395 |
+
{
|
396 |
+
$http_host = $matches[0];
|
397 |
+
}
|
398 |
+
else
|
399 |
+
{
|
400 |
+
$url = @parse_url($home_url);
|
401 |
+
$http_host = $url['scheme'] . '://' . $url['host'];
|
402 |
+
$http_host = !empty($url['port'])
|
403 |
+
? $http_host . ':' . $url['port']
|
404 |
+
: $http_host;
|
405 |
+
}
|
406 |
+
|
407 |
+
return $http_host;
|
408 |
+
}
|
409 |
+
|
410 |
+
private function _guess_min_dir()
|
411 |
+
{
|
412 |
+
$min_path = $this->get_min_path();
|
413 |
+
$min_path = trim($min_path, '/');
|
414 |
+
$doc_root = trailingslashit($this->get_doc_root());
|
415 |
+
|
416 |
+
// min directory is guessed using current document root
|
417 |
+
$min_dir = $doc_root . $min_path;
|
418 |
+
$min_dir = trailingslashit($min_dir);
|
419 |
+
|
420 |
+
// if min directory does not exist, and a tilde (`~`) is found in
|
421 |
+
// base, it is very possible that mod user_dir is being used, try
|
422 |
+
// to remove the ~username dir from min_path and see if that works
|
423 |
+
if (!file_exists($min_dir))
|
424 |
+
{
|
425 |
+
$min_dir = preg_match('#^~[^/]+/#u', $min_path, $matches)
|
426 |
+
? preg_replace('#^~[^/]+/#u', '', $min_path, 1)
|
427 |
+
: false;
|
428 |
+
|
429 |
+
if (false == $min_dir)
|
430 |
+
return false;
|
431 |
+
|
432 |
+
$min_dir = trailingslashit($doc_root . $min_dir);
|
433 |
+
if (!file_exists($min_dir))
|
434 |
+
return false;
|
435 |
+
|
436 |
+
$this->remove_from_base = trim($matches[0], '/');
|
437 |
+
}
|
438 |
+
|
439 |
+
return $min_dir;
|
440 |
+
}
|
441 |
+
|
442 |
+
/**
|
443 |
+
* Detects the default path to Minify library
|
444 |
+
*
|
445 |
+
* @since 1.3.0
|
446 |
+
* @return string
|
447 |
+
*/
|
448 |
+
function get_default_min_path()
|
449 |
+
{
|
450 |
+
if (!empty($this->default_min_path))
|
451 |
+
return $this->default_min_path;
|
452 |
+
|
453 |
+
// @since 1.3.0 we get a path relative to root for Minify instead of an
|
454 |
+
// absolute URL to add compatibility to staging or mirror site.
|
455 |
+
$min_path = preg_replace('#https?://[^/]+#ui', '', $this->plugin_wp_url);
|
456 |
+
$min_path = $min_path . 'min/';
|
457 |
+
|
458 |
+
$this->default_min_path = $min_path;
|
459 |
+
return $min_path;
|
460 |
+
}
|
461 |
+
|
462 |
+
/**
|
463 |
+
* Gets path to Minify library, either default or from admin input
|
464 |
+
*
|
465 |
+
* @since 1.3.0
|
466 |
+
* @return string
|
467 |
+
*/
|
468 |
+
function get_min_path()
|
469 |
+
{
|
470 |
+
$min_path = empty($this->options['input_minpath'])
|
471 |
+
? $this->get_default_min_path()
|
472 |
+
: $this->options['input_minpath'];
|
473 |
+
|
474 |
+
$min_path = apply_filters('bwp_minify_min_dir', $min_path);
|
475 |
+
$min_path = apply_filters('bwp_minify_min_path', $min_path);
|
476 |
+
|
477 |
+
// allow overidden of the generated min path via constants
|
478 |
+
$min_path = defined('BWP_MINIFY_MIN_PATH') && '' != BWP_MINIFY_MIN_PATH
|
479 |
+
? BWP_MINIFY_MIN_PATH : $min_path;
|
480 |
+
|
481 |
+
return $min_path;
|
482 |
+
}
|
483 |
+
|
484 |
+
/**
|
485 |
+
* Gets the default directory where Minify library lives
|
486 |
+
*
|
487 |
+
* @since 1.3.0
|
488 |
+
* @return string
|
489 |
+
*/
|
490 |
+
function get_default_min_dir()
|
491 |
+
{
|
492 |
+
$plugin_wp_dir = trailingslashit(plugin_dir_path($this->plugin_file));
|
493 |
+
return $plugin_wp_dir . 'min/';
|
494 |
+
}
|
495 |
+
|
496 |
+
/**
|
497 |
+
* Gets the directory where Minify library lives
|
498 |
+
*
|
499 |
+
* @since 1.3.0
|
500 |
+
* @return string|bool false if no directory can be found
|
501 |
+
*/
|
502 |
+
function get_min_dir()
|
503 |
+
{
|
504 |
+
if (!empty($this->min_dir))
|
505 |
+
return $this->min_dir;
|
506 |
+
|
507 |
+
// if min path is the default one, the task is easy
|
508 |
+
if (empty($this->options['input_minpath'])
|
509 |
+
|| $this->options['input_minpath'] == $this->get_default_min_path()
|
510 |
+
) {
|
511 |
+
$min_dir = $this->get_default_min_dir();
|
512 |
+
}
|
513 |
+
else
|
514 |
+
{
|
515 |
+
$min_dir = $this->_guess_min_dir();
|
516 |
+
// guessing failed
|
517 |
+
if (false == $min_dir)
|
518 |
+
return false;
|
519 |
+
}
|
520 |
+
|
521 |
+
if (false == self::is_multisite())
|
522 |
+
{
|
523 |
+
// if this is NOT a multisite environment, and a `config.php` file
|
524 |
+
// exists, this is the Minify directory we're looking for
|
525 |
+
if (file_exists($min_dir . 'config.php'))
|
526 |
+
{
|
527 |
+
$this->min_dir = $min_dir;
|
528 |
+
return $min_dir;
|
529 |
+
}
|
530 |
+
else
|
531 |
+
return false;
|
532 |
+
}
|
533 |
+
else
|
534 |
+
{
|
535 |
+
// if we're in a multi-site environment, we look for Minify's
|
536 |
+
// directory in current blog first, and then main blog, and use
|
537 |
+
// the first directory that is found
|
538 |
+
if (!file_exists($min_dir . 'config.php'))
|
539 |
+
{
|
540 |
+
// current blog's Minify directory doesn't seem to exist,
|
541 |
+
// remove the current blog's path from the directory
|
542 |
+
// being checked and try again
|
543 |
+
$blog_path = $this->_get_blog_path();
|
544 |
+
$min_dir = !empty($blog_path)
|
545 |
+
? preg_replace('#' . $blog_path . '#ui', '/', $min_dir, 1)
|
546 |
+
: $min_dir;
|
547 |
+
|
548 |
+
if (file_exists($min_dir . 'config.php'))
|
549 |
+
{
|
550 |
+
$this->min_dir = $min_dir;
|
551 |
+
return $min_dir;
|
552 |
+
}
|
553 |
+
|
554 |
+
// fail with no fallback, admin must set it manually
|
555 |
+
return false;
|
556 |
+
}
|
557 |
+
else
|
558 |
+
{
|
559 |
+
$this->min_dir = $min_dir;
|
560 |
+
return $min_dir;
|
561 |
+
}
|
562 |
+
}
|
563 |
+
}
|
564 |
+
|
565 |
+
function get_wp_doc_root()
|
566 |
+
{
|
567 |
+
$wp_doc_root = !empty($_SERVER['SCRIPT_FILENAME'])
|
568 |
+
&& $_SERVER['SCRIPT_FILENAME'] != $_SERVER['PHP_SELF']
|
569 |
+
? dirname(str_replace('\\', '/', $_SERVER['SCRIPT_FILENAME']))
|
570 |
+
: ABSPATH;
|
571 |
+
|
572 |
+
// script filename contains `wp-admin` when in admin so we need to remove it
|
573 |
+
return is_admin() ? str_replace('/wp-admin', '', $wp_doc_root) : $wp_doc_root;
|
574 |
+
}
|
575 |
+
|
576 |
+
/**
|
577 |
+
* Gets WordPress document root or document root from admin input
|
578 |
+
*
|
579 |
+
* @since 1.3.0
|
580 |
+
* @return string
|
581 |
+
*/
|
582 |
+
function get_doc_root($path = '')
|
583 |
+
{
|
584 |
+
$server_doc_root = str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT']);
|
585 |
+
$wp_doc_root = $this->get_wp_doc_root();
|
586 |
+
|
587 |
+
$doc_root = empty($server_doc_root) || 0 !== strpos($wp_doc_root, $server_doc_root)
|
588 |
+
? $wp_doc_root : $server_doc_root;
|
589 |
+
|
590 |
+
$doc_root = !empty($this->options['input_doc_root'])
|
591 |
+
? $this->options['input_doc_root']
|
592 |
+
: $doc_root;
|
593 |
+
|
594 |
+
return !empty($path)
|
595 |
+
? trailingslashit($doc_root) . ltrim($path, '/')
|
596 |
+
: $doc_root;
|
597 |
+
}
|
598 |
+
|
599 |
+
/**
|
600 |
+
* Gets default cache dir based on this plugin's dir
|
601 |
+
*
|
602 |
+
* @since 1.3.0
|
603 |
+
* @return string
|
604 |
+
*/
|
605 |
+
function get_default_cache_dir()
|
606 |
+
{
|
607 |
+
$plugin_wp_dir = plugin_dir_path($this->plugin_file);
|
608 |
+
$cache_dir = trailingslashit($plugin_wp_dir) . 'cache/';
|
609 |
+
$cache_dir = str_replace('\\', '/', $cache_dir);
|
610 |
+
|
611 |
+
return $cache_dir;
|
612 |
+
}
|
613 |
+
|
614 |
+
/**
|
615 |
+
* Gets the current cache directory based on min path or admin input
|
616 |
+
*
|
617 |
+
* The default cache directory is `/min/cache/` which can be changed inside
|
618 |
+
* admin area if `/min/config.php` file is writable (@since 1.3.0).
|
619 |
+
*/
|
620 |
+
function get_cache_dir()
|
621 |
+
{
|
622 |
+
// return the cache dir as entered by admin
|
623 |
+
// or if cache dir is empty, get cache directory from plugin
|
624 |
+
// directory, i.e. `bwp-minify/cache`, this is the default
|
625 |
+
$cache_dir = !empty($this->options['input_cache_dir'])
|
626 |
+
? $this->options['input_cache_dir']
|
627 |
+
: $this->get_default_cache_dir();
|
628 |
+
|
629 |
+
// allow overidden of the generated cache dir via constants
|
630 |
+
$cache_dir = defined('BWP_MINIFY_CACHE_DIR') && '' != BWP_MINIFY_CACHE_DIR
|
631 |
+
? BWP_MINIFY_CACHE_DIR : $cache_dir;
|
632 |
+
|
633 |
+
return apply_filters('bwp_minify_cache_dir', $cache_dir);
|
634 |
+
}
|
635 |
+
|
636 |
+
private function _get_default_fly_min_path()
|
637 |
+
{
|
638 |
+
return trailingslashit($this->plugin_wp_url) . 'cache/';
|
639 |
+
}
|
640 |
+
|
641 |
+
/**
|
642 |
+
* Gets friendly minify url path based on user input
|
643 |
+
*
|
644 |
+
* @since 1.3.0
|
645 |
+
* @param $need_host bool whether or not to prepend a http host to the
|
646 |
+
* url path to form a friendly url.
|
647 |
+
* $from_cache_dir bool whether or not to get the min path based on
|
648 |
+
* Minify's cache directory or from user defined setting
|
649 |
+
* @return string when `$need_host` is true this should return a fully
|
650 |
+
* qualify URL to minified contents, when false the url path will
|
651 |
+
* be returned for further use (such as writing rewrite rules)
|
652 |
+
*/
|
653 |
+
function get_fly_min_path($need_host = false, $from_cache_dir = false)
|
654 |
+
{
|
655 |
+
// blog path needs to be removed from the fly min path when shown in
|
656 |
+
// setting page but needs to be added when used to contruct the fly url
|
657 |
+
// (i.e. when `$need_host` is true)
|
658 |
+
$blog_path = $this->_get_blog_path();
|
659 |
+
|
660 |
+
if (empty($this->options['input_cache_dir'])
|
661 |
+
|| $this->options['input_cache_dir'] == $this->get_default_cache_dir()
|
662 |
+
) {
|
663 |
+
// use default fly min path
|
664 |
+
$fly_min_path = $this->_get_default_fly_min_path();
|
665 |
+
$fly_min_path = !empty($blog_path) && false == $need_host
|
666 |
+
? preg_replace('#' . $blog_path . '#ui', '/', $fly_min_path, 1)
|
667 |
+
: $fly_min_path;
|
668 |
+
}
|
669 |
+
elseif (empty($this->options['input_fly_minpath']) || $from_cache_dir)
|
670 |
+
{
|
671 |
+
// no fly min path is specified by user
|
672 |
+
$cache_dir = $this->get_cache_dir();
|
673 |
+
$doc_root = $this->get_doc_root();
|
674 |
+
if (0 !== strpos($cache_dir, $doc_root)
|
675 |
+
|| empty($doc_root) || '/' == $doc_root
|
676 |
+
) {
|
677 |
+
// cache directory doesn't seem to live under document root,
|
678 |
+
// return false to not use friendly minify urls
|
679 |
+
return false;
|
680 |
+
}
|
681 |
+
|
682 |
+
// guessing the min path by removing document root from cache dir
|
683 |
+
$fly_min_path = str_replace($doc_root, '', $cache_dir);
|
684 |
+
$fly_min_path = ltrim($fly_min_path, '/');
|
685 |
+
|
686 |
+
if (!empty($blog_path) && $need_host)
|
687 |
+
{
|
688 |
+
// add blog path to fly min path if blog path is not empty, this is
|
689 |
+
// for correct urls served to visitors
|
690 |
+
$fly_min_path = !empty($this->base)
|
691 |
+
? preg_replace('#^' . $this->base . '/#ui',
|
692 |
+
$this->base . $blog_path,
|
693 |
+
$fly_min_path, 1)
|
694 |
+
: ltrim($blog_path . $fly_min_path, '/');
|
695 |
+
}
|
696 |
+
|
697 |
+
if (!empty($this->base) && !self::is_nginx() && false == $need_host)
|
698 |
+
{
|
699 |
+
// remove base from fly min path if we don't need host, and this
|
700 |
+
// server is not nginx, since apache doesn't need the base but
|
701 |
+
// nginx does
|
702 |
+
$fly_min_path = preg_replace('#^' . $this->base . '/#ui',
|
703 |
+
'', $fly_min_path, 1);
|
704 |
+
}
|
705 |
+
|
706 |
+
$fly_min_path = trailingslashit($this->http_host) . trailingslashit($fly_min_path);
|
707 |
+
}
|
708 |
+
else
|
709 |
+
{
|
710 |
+
$fly_min_path = trim($this->options['input_fly_minpath'], '/');
|
711 |
+
$fly_min_path = trailingslashit($this->http_host) . trailingslashit($fly_min_path);
|
712 |
+
}
|
713 |
+
|
714 |
+
if (false == $need_host)
|
715 |
+
$fly_min_path = preg_replace('#https?://[^/]+#ui', '', $fly_min_path);
|
716 |
+
|
717 |
+
return $fly_min_path;
|
718 |
+
}
|
719 |
+
|
720 |
+
function get_fly_min_url()
|
721 |
+
{
|
722 |
+
if (!empty($this->fly_min_url))
|
723 |
+
return $this->fly_min_url;
|
724 |
+
|
725 |
+
return $this->get_fly_min_path(true);
|
726 |
+
}
|
727 |
+
|
728 |
+
function init_properties()
|
729 |
+
{
|
730 |
+
$this->get_base();
|
731 |
+
|
732 |
+
$this->http_host = $this->_get_http_host();
|
733 |
+
$this->buster = $this->get_buster($this->options['select_buster_type']);
|
734 |
+
|
735 |
+
$this->min_path = $this->get_min_path();
|
736 |
+
$this->min_cache_dir = $this->get_cache_dir();
|
737 |
+
$this->min_url = trailingslashit($this->http_host) . ltrim($this->min_path, '/');
|
738 |
+
|
739 |
+
$this->doc_root = $this->get_doc_root();
|
740 |
+
$this->fly_min_path = $this->get_fly_min_path();
|
741 |
+
$this->fly_min_url = $this->get_fly_min_url();
|
742 |
+
|
743 |
+
// init the detector class, responsible for detecting and logging
|
744 |
+
// enqueued files
|
745 |
+
$this->detector = new BWP_Enqueued_Detector($this->options, $this->domain);
|
746 |
+
$this->detector->set_log(BWP_MINIFY_DETECTOR_LOG);
|
747 |
+
|
748 |
+
// init fetcher class if needed, responsible for serving friendly minify urls
|
749 |
+
if ('yes' == $this->options['enable_fly_min'])
|
750 |
+
{
|
751 |
+
$this->fetcher = new BWP_Minify_Fetcher($this->options, $this->domain);
|
752 |
+
$this->fetcher->set_main($this);
|
753 |
+
$this->fetcher->set_detector($this->detector);
|
754 |
+
$this->fetcher->set_min_url($this->min_url);
|
755 |
+
$this->fetcher->set_min_fly_url($this->fly_min_url);
|
756 |
+
}
|
757 |
+
|
758 |
+
// init the CDN class if needed
|
759 |
+
if ('yes' == $this->options['enable_cdn'])
|
760 |
+
$this->cdn = new BWP_Minify_CDN($this->options, $this->domain);
|
761 |
+
}
|
762 |
+
|
763 |
+
/**
|
764 |
+
* Adds rewrite rules to server config file
|
765 |
+
*
|
766 |
+
* @since 1.3.0
|
767 |
+
* @return bool|string
|
768 |
+
*/
|
769 |
+
private function _add_rewrite_rules($suppress = true)
|
770 |
+
{
|
771 |
+
return $this->rewriter->add_rewrite_rules($suppress);
|
772 |
+
}
|
773 |
+
|
774 |
+
private function _remove_rewrite_rules()
|
775 |
+
{
|
776 |
+
// remove rewrite rules in WP root directory
|
777 |
+
$this->rewriter->remove_wp_rewrite_rules();
|
778 |
+
// remove rewrite rules in cache directory
|
779 |
+
$this->rewriter->remove_cache_rewrite_rules();
|
780 |
+
}
|
781 |
+
|
782 |
+
private static function _flush_rewrite_rules($hard = false)
|
783 |
+
{
|
784 |
+
global $wp_rewrite;
|
785 |
+
|
786 |
+
$wp_rewrite->flush_rules($hard);
|
787 |
+
}
|
788 |
+
|
789 |
+
private static function _is_loadable()
|
790 |
+
{
|
791 |
+
// allow other developers to use BWP Minify inside wp-admin, be very careful :-)
|
792 |
+
$allowed_in_admin = apply_filters('bwp_minify_allowed_in_admin', false);
|
793 |
+
|
794 |
+
if (is_admin() && !$allowed_in_admin)
|
795 |
+
return false;
|
796 |
+
|
797 |
+
if (!did_action('template_redirect'))
|
798 |
+
return true;
|
799 |
+
|
800 |
+
// ignore Geomashup
|
801 |
+
if (!empty($_GET['geo_mashup_content'])
|
802 |
+
&& 'render-map' == $_GET['geo_mashup_content'])
|
803 |
+
return false;
|
804 |
+
|
805 |
+
// ignore AEC (Ajax Edit Comment)
|
806 |
+
if (!empty($_GET['aec_page']))
|
807 |
+
return false;
|
808 |
+
|
809 |
+
// ignore Simple:Press forum plugin
|
810 |
+
if (defined('SPVERSION') && function_exists('sp_get_option'))
|
811 |
+
{
|
812 |
+
$sp_page = sp_get_option('sfpage');
|
813 |
+
if (is_page($sp_page))
|
814 |
+
return false;
|
815 |
+
}
|
816 |
+
|
817 |
+
return true;
|
818 |
+
}
|
819 |
+
|
820 |
+
function add_conditional_hooks()
|
821 |
+
{
|
822 |
+
// Certain plugins use a single file to show contents, which doesn't
|
823 |
+
// make use of wp_head and wp_footer action and certain plugins should
|
824 |
+
// just be excluded :-)
|
825 |
+
if (false == apply_filters('bwp_minify_is_loadable', self::_is_loadable()))
|
826 |
+
return;
|
827 |
+
|
828 |
+
// minify styles if needed
|
829 |
+
if ('yes' == $this->options['enable_min_css'])
|
830 |
+
{
|
831 |
+
// build a list of style groups to print
|
832 |
+
add_filter('print_styles_array', array($this, 'add_styles'), 999);
|
833 |
+
// build a list of very late styles
|
834 |
+
add_action('wp_print_styles', array($this, 'add_late_styles'), 999);
|
835 |
+
}
|
836 |
+
|
837 |
+
// minify scripts if needed
|
838 |
+
if ('yes' == $this->options['enable_min_js'])
|
839 |
+
{
|
840 |
+
// build a list of script groups to print
|
841 |
+
add_filter('print_scripts_array', array($this, 'add_scripts'), 999);
|
842 |
+
// build a list of very late scripts
|
843 |
+
add_action('wp_print_scripts', array($this, 'add_late_scripts'), 999);
|
844 |
+
}
|
845 |
+
|
846 |
+
if ('yes' == $this->options['enable_bloginfo'])
|
847 |
+
{
|
848 |
+
add_filter('bloginfo_url', array($this, 'minify_stylesheet'), 10, 2);
|
849 |
+
}
|
850 |
+
}
|
851 |
+
|
852 |
+
function pre_init_hooks()
|
853 |
+
{
|
854 |
+
// check and update Minify config file whenever this plugin is updated
|
855 |
+
add_action('upgrader_process_complete', array($this, 'check_config_file'), 10, 2);
|
856 |
+
|
857 |
+
// check and update plugin db if needed, this is fired after init
|
858 |
+
add_action('bwp_minify_init_upgrade', array($this, 'upgrade_plugin'), 10, 2);
|
859 |
+
|
860 |
+
/**
|
861 |
+
* Priorities of these hooks below greatly affects compatibility
|
862 |
+
* with other plugins, especially plugins that output additional
|
863 |
+
* scripts directly via actions attached to `wp_footer` or
|
864 |
+
* `wp_print_*` hooks. Those plugins will likely register their
|
865 |
+
* output functions at a rather low priority (WordPress's WYSIWYG
|
866 |
+
* editor uses `50` to output TinyMCE's inline and linked JS, for
|
867 |
+
* e.g.). BWP Minify registers its output functions at the same
|
868 |
+
* priority as `wp_print_head_scripts` (i.e. 9) for header scripts
|
869 |
+
* and same priority as `_wp_footer_scripts` for footer
|
870 |
+
* scripts (i.e. 10), to make sure (well not that sure) additional
|
871 |
+
* scripts are printed after their main JS files are printed.
|
872 |
+
*/
|
873 |
+
|
874 |
+
$is_loadable = apply_filters('bwp_minify_is_loadable', self::_is_loadable());
|
875 |
+
|
876 |
+
if ($is_loadable && 'yes' == $this->options['enable_min_css'])
|
877 |
+
{
|
878 |
+
// hooks to common head and footer actions to print combined and
|
879 |
+
// minified css, as late as possible
|
880 |
+
add_action('wp_head', array($this, 'print_header_styles'), 8);
|
881 |
+
add_action('login_head', array($this, 'print_header_styles'), 8);
|
882 |
+
add_action('wp_print_footer_scripts', array($this, 'print_footer_styles'), 10);
|
883 |
+
add_action('admin_print_styles', array($this, 'print_header_styles'), 8);
|
884 |
+
}
|
885 |
+
|
886 |
+
if ($is_loadable && 'yes' == $this->options['enable_min_js'])
|
887 |
+
{
|
888 |
+
// hooks to common head and footer actions to print combined and
|
889 |
+
// minified js, as late as possible
|
890 |
+
add_action('wp_head', array($this, 'print_header_scripts'), 9);
|
891 |
+
add_action('login_head', array($this, 'print_header_scripts'), 9);
|
892 |
+
add_action('wp_print_footer_scripts', array($this, 'print_footer_scripts'), 10);
|
893 |
+
add_action('admin_print_scripts', array($this, 'print_header_scripts'), 9);
|
894 |
+
add_action('admin_print_footer_scripts', array($this, 'print_footer_scripts'), 10);
|
895 |
+
}
|
896 |
+
|
897 |
+
// collect js and css files to be combined and minified
|
898 |
+
if (false === strpos($_SERVER['REQUEST_URI'], 'wp-login.php')
|
899 |
+
&& false === strpos($_SERVER['REQUEST_URI'], 'wp-signup.php'))
|
900 |
+
// a priority of 1 makes BWP Minify compatible with plugins that
|
901 |
+
// use `template_redirect` hook to inject their own templates, such
|
902 |
+
// as BuddyPress.
|
903 |
+
add_action('template_redirect', array($this, 'add_conditional_hooks'), 8);
|
904 |
+
else
|
905 |
+
$this->add_conditional_hooks();
|
906 |
+
}
|
907 |
+
|
908 |
+
function enqueue_media()
|
909 |
+
{
|
910 |
+
if ($this->is_admin_page(BWP_MINIFY_MANAGE))
|
911 |
+
{
|
912 |
+
wp_enqueue_script('bwp-minify', BWP_MINIFY_JS . '/bwp-minify.js',
|
913 |
+
array('jquery'), false, true);
|
914 |
+
wp_enqueue_style('bwp-minify', BWP_MINIFY_CSS . '/bwp-minify.css');
|
915 |
+
}
|
916 |
+
}
|
917 |
+
|
918 |
+
/**
|
919 |
+
* Build the Menus
|
920 |
+
*/
|
921 |
+
function build_menus()
|
922 |
+
{
|
923 |
+
// use fancy dashicons if WP version is 3.8+
|
924 |
+
$menu_icon = version_compare($this->wp_ver, '3.8.0', '>=')
|
925 |
+
? 'dashicons-performance'
|
926 |
+
: BWP_MINIFY_IMAGES . '/icon_menu.png';
|
927 |
+
|
928 |
+
add_menu_page(
|
929 |
+
__('Better WordPress Minify', $this->domain),
|
930 |
+
'BWP Minify',
|
931 |
+
BWP_MINIFY_CAPABILITY,
|
932 |
+
BWP_MINIFY_OPTION_GENERAL,
|
933 |
+
array($this, 'build_option_pages'),
|
934 |
+
$menu_icon
|
935 |
+
);
|
936 |
+
// Sub menus
|
937 |
+
add_submenu_page(
|
938 |
+
BWP_MINIFY_OPTION_GENERAL,
|
939 |
+
__('General Options', $this->domain),
|
940 |
+
__('General Options', $this->domain),
|
941 |
+
BWP_MINIFY_CAPABILITY,
|
942 |
+
BWP_MINIFY_OPTION_GENERAL,
|
943 |
+
array($this, 'build_option_pages')
|
944 |
+
);
|
945 |
+
add_submenu_page(
|
946 |
+
BWP_MINIFY_OPTION_GENERAL,
|
947 |
+
__('Advanced Options', $this->domain),
|
948 |
+
__('Advanced Options', $this->domain),
|
949 |
+
BWP_MINIFY_CAPABILITY,
|
950 |
+
BWP_MINIFY_OPTION_ADVANCED,
|
951 |
+
array($this, 'build_option_pages')
|
952 |
+
);
|
953 |
+
add_submenu_page(
|
954 |
+
BWP_MINIFY_OPTION_GENERAL,
|
955 |
+
__('Manage enqueued Files', $this->domain),
|
956 |
+
__('Enqueued Files', $this->domain),
|
957 |
+
BWP_MINIFY_CAPABILITY,
|
958 |
+
BWP_MINIFY_MANAGE,
|
959 |
+
array($this, 'build_option_pages')
|
960 |
+
);
|
961 |
+
}
|
962 |
+
|
963 |
+
/**
|
964 |
+
* Build the option pages
|
965 |
+
*
|
966 |
+
* Utilizes BWP Option Page Builder (@see BWP_OPTION_PAGE)
|
967 |
+
*/
|
968 |
+
function build_option_pages()
|
969 |
+
{
|
970 |
+
if (!current_user_can(BWP_MINIFY_CAPABILITY))
|
971 |
+
wp_die(__('You do not have sufficient permissions to access this page.'));
|
972 |
+
|
973 |
+
$page = $_GET['page'];
|
974 |
+
$active_page = '';
|
975 |
+
$original_options = $this->options;
|
976 |
+
$bwp_option_page = new BWP_OPTION_PAGE($page, $this->site_options);
|
977 |
+
|
978 |
+
// Get option from the database, general options are used for both
|
979 |
+
// 'General Options' page and 'Manage enqueued' page
|
980 |
+
if (BWP_MINIFY_OPTION_GENERAL == $page || BWP_MINIFY_MANAGE == $page)
|
981 |
+
{
|
982 |
+
$active_page = BWP_MINIFY_OPTION_GENERAL;
|
983 |
+
$options = $bwp_option_page->get_db_options(
|
984 |
+
$active_page,
|
985 |
+
$bwp_option_page->get_options(array(
|
986 |
+
'input_minurl',
|
987 |
+
'input_minpath',
|
988 |
+
'input_cache_dir',
|
989 |
+
'input_doc_root',
|
990 |
+
'input_maxfiles',
|
991 |
+
'input_maxage',
|
992 |
+
'input_custom_buster',
|
993 |
+
'enable_min_js',
|
994 |
+
'enable_min_css',
|
995 |
+
'enable_bloginfo',
|
996 |
+
'enable_css_bubble',
|
997 |
+
'enable_cache_file_lock',
|
998 |
+
'enable_debug',
|
999 |
+
'select_buster_type',
|
1000 |
+
'select_time_type',
|
1001 |
+
'input_ignore',
|
1002 |
+
'input_header',
|
1003 |
+
'input_direct',
|
1004 |
+
'input_footer',
|
1005 |
+
'input_oblivion',
|
1006 |
+
'input_style_ignore',
|
1007 |
+
'input_style_direct',
|
1008 |
+
'input_style_oblivion',
|
1009 |
+
), $this->options)
|
1010 |
+
);
|
1011 |
+
}
|
1012 |
+
else
|
1013 |
+
{
|
1014 |
+
$active_page = BWP_MINIFY_OPTION_ADVANCED;
|
1015 |
+
$options = $bwp_option_page->get_db_options(
|
1016 |
+
$active_page,
|
1017 |
+
$bwp_option_page->get_options(array(
|
1018 |
+
'enable_fly_min',
|
1019 |
+
'input_fly_minpath',
|
1020 |
+
'input_nginx_config_file',
|
1021 |
+
'enable_cdn',
|
1022 |
+
'input_cdn_host',
|
1023 |
+
'input_cdn_host_js',
|
1024 |
+
'input_cdn_host_css',
|
1025 |
+
'select_cdn_ssl_type'
|
1026 |
+
), $this->options)
|
1027 |
+
);
|
1028 |
+
}
|
1029 |
+
|
1030 |
+
if (!empty($page))
|
1031 |
+
{
|
1032 |
+
if ($page == BWP_MINIFY_OPTION_GENERAL)
|
1033 |
+
{
|
1034 |
+
$bwp_option_page->set_current_tab(1);
|
1035 |
+
|
1036 |
+
// add two buttons to save and flush cache or to flush cache
|
1037 |
+
// right now we only support flushing by super admin, to be
|
1038 |
+
// improved in 1.3.x
|
1039 |
+
if (!self::is_normal_admin())
|
1040 |
+
add_filter('bwp_option_submit_button', array($this, 'add_flush_cache_buttons'));
|
1041 |
+
|
1042 |
+
$form = array(
|
1043 |
+
'items' => array(
|
1044 |
+
'heading',
|
1045 |
+
'checkbox',
|
1046 |
+
'checkbox',
|
1047 |
+
'checkbox',
|
1048 |
+
'input',
|
1049 |
+
'input',
|
1050 |
+
'select',
|
1051 |
+
'heading',
|
1052 |
+
'input',
|
1053 |
+
'input',
|
1054 |
+
'input',
|
1055 |
+
'checkbox',
|
1056 |
+
'checkbox',
|
1057 |
+
'checkbox'
|
1058 |
+
),
|
1059 |
+
'item_labels' => array
|
1060 |
+
(
|
1061 |
+
__('Plugin Functionality', $this->domain),
|
1062 |
+
__('Minify JS files automatically?', $this->domain),
|
1063 |
+
__('Minify CSS files automatically?', $this->domain),
|
1064 |
+
__('Minify <code>bloginfo()</code> stylesheets?', $this->domain),
|
1065 |
+
__('URL path to Minify library (relative to domain root)', $this->domain),
|
1066 |
+
__('One minify string will contain', $this->domain),
|
1067 |
+
__('For cache buster, use', $this->domain),
|
1068 |
+
__('Minify Library Settings', $this->domain),
|
1069 |
+
__('WordPress document root', $this->domain),
|
1070 |
+
__('Cache directory', $this->domain),
|
1071 |
+
__('Cache age', $this->domain),
|
1072 |
+
__('Enable bubble CSS import?', $this->domain),
|
1073 |
+
__('Enable cache file locking?', $this->domain),
|
1074 |
+
__('Enable debugging?', $this->domain)
|
1075 |
+
),
|
1076 |
+
'item_names' => array(
|
1077 |
+
'h1',
|
1078 |
+
'cb1',
|
1079 |
+
'cb3',
|
1080 |
+
'cb2',
|
1081 |
+
'input_minpath',
|
1082 |
+
'input_maxfiles',
|
1083 |
+
'select_buster_type',
|
1084 |
+
'h2',
|
1085 |
+
'input_doc_root',
|
1086 |
+
'input_cache_dir',
|
1087 |
+
'input_maxage',
|
1088 |
+
'cb4',
|
1089 |
+
'cb5',
|
1090 |
+
'cb6'
|
1091 |
+
),
|
1092 |
+
'heading' => array(
|
1093 |
+
'h1' => '',
|
1094 |
+
'h2' => '<a name="minify.config.php"></a>' . sprintf(
|
1095 |
+
__('<em>These options will let you control how the actual '
|
1096 |
+
. '<a href="%s" target="_blank">Minify</a> library works.</em>', $this->domain),
|
1097 |
+
'https://code.google.com/p/minify/'
|
1098 |
+
)
|
1099 |
+
),
|
1100 |
+
'select' => array(
|
1101 |
+
'select_time_type' => array(
|
1102 |
+
__('second(s)', $this->domain) => 1,
|
1103 |
+
__('minute(s)', $this->domain) => 60,
|
1104 |
+
__('hour(s)', $this->domain) => 3600,
|
1105 |
+
__('day(s)', $this->domain) => 86400
|
1106 |
+
),
|
1107 |
+
'select_buster_type' => array(
|
1108 |
+
__('No cache buster', $this->domain) => 'none',
|
1109 |
+
__('Cache folder’s last modified time', $this->domain) => 'mtime',
|
1110 |
+
__('Your WordPress’s current version', $this->domain) => 'wpver',
|
1111 |
+
__('Your theme’s current version', $this->domain) => 'tver',
|
1112 |
+
__('A custom number', $this->domain) => 'custom'
|
1113 |
+
)
|
1114 |
+
),
|
1115 |
+
'checkbox' => array(
|
1116 |
+
'cb1' => array(__('you can still use <code>bwp_minify()</code> helper function if you disable this.', $this->domain) => 'enable_min_js'),
|
1117 |
+
'cb3' => array(__('you can still use <code>bwp_minify()</code> helper function if you disable this.', $this->domain) => 'enable_min_css'),
|
1118 |
+
'cb2' => array(__('enable this for themes that use <code>bloginfo()</code> to print the main stylesheet (i.e. <code>style.css</code>). If you want to minify <code>style.css</code> with the rest of your css files, you must enqueue it.', $this->domain) => 'enable_bloginfo'),
|
1119 |
+
'cb4' => array(sprintf(__('move all <code>@import</code> rules in CSS files to the top. More info <a href="%s" target="_blank">here</a>.', $this->domain), $this->get_url('min_css_bubble')) => 'enable_css_bubble'),
|
1120 |
+
'cb5' => array(__('disable this if filesystem is NFS.', $this->domain) => 'enable_cache_file_lock'),
|
1121 |
+
'cb6' => array(sprintf(__('only enable this when minification does not work as expected. More info <a href="%s" target="_blank">here</a>.', $this->domain), $this->get_url('min_debug')) => 'enable_debug')
|
1122 |
+
),
|
1123 |
+
'input' => array(
|
1124 |
+
'input_minpath' => array(
|
1125 |
+
'size' => 55,
|
1126 |
+
'label' => '<br />' . sprintf(
|
1127 |
+
__('Leave empty to use default value, which is <code>%s</code>.<br />'
|
1128 |
+
. 'Please read <a href="%s#advanced_customization" target="_blank">here</a> '
|
1129 |
+
. 'to know how to properly modify this.', $this->domain),
|
1130 |
+
$this->get_default_min_path(), $this->plugin_url)
|
1131 |
+
),
|
1132 |
+
'input_doc_root' => array(
|
1133 |
+
'size' => 55,
|
1134 |
+
'label' => '<br />' . $this->_get_input_doc_root_label()
|
1135 |
+
),
|
1136 |
+
'input_cache_dir' => array(
|
1137 |
+
'size' => 55,
|
1138 |
+
'label' => '<br />' . sprintf(
|
1139 |
+
__('Expect a full path to a publicly accessible directory '
|
1140 |
+
. '(i.e. can be served under your document root). <br />'
|
1141 |
+
. 'Leave empty to use default value, which is <code>%s</code>.<br />'
|
1142 |
+
. 'Cache directory must be writable '
|
1143 |
+
. '(i.e. CHMOD to 755 or 777). '
|
1144 |
+
. 'More details can be found '
|
1145 |
+
. '<a href="%s#minify_cache_directory" target="_blank">here</a>. ', $this->domain),
|
1146 |
+
$this->get_default_cache_dir(), $this->plugin_url)
|
1147 |
+
),
|
1148 |
+
'input_maxfiles' => array(
|
1149 |
+
'size' => 3,
|
1150 |
+
'label' => __('file(s) at most.', $this->domain)
|
1151 |
+
),
|
1152 |
+
'input_maxage' => array(
|
1153 |
+
'size' => 5,
|
1154 |
+
'label' => __('—', $this->domain)
|
1155 |
+
),
|
1156 |
+
'input_custom_buster' => array(
|
1157 |
+
'pre' => '<em>→ /min/?f=file.js&ver=</em> ',
|
1158 |
+
'size' => 12,
|
1159 |
+
'label' => '.',
|
1160 |
+
'disabled' => ' disabled="disabled"'
|
1161 |
+
)
|
1162 |
+
),
|
1163 |
+
'textarea' => array(
|
1164 |
+
'input_symlinks' => array(
|
1165 |
+
'cols' => 90,
|
1166 |
+
'rows' => 5
|
1167 |
+
)
|
1168 |
+
),
|
1169 |
+
'container' => array(
|
1170 |
+
'select_buster_type' => '<em><strong>' . __('Note', $this->domain) . ':</strong> '
|
1171 |
+
. __('Whenever a new cache buster is used, '
|
1172 |
+
. 'you are telling browsers to refresh cached JS and CSS files. '
|
1173 |
+
. 'This is particularly useful when source files have been changed, '
|
1174 |
+
. 'use this feature wisely.', $this->domain) . '</em>'
|
1175 |
+
),
|
1176 |
+
'inline_fields' => array(
|
1177 |
+
'input_maxage' => array(
|
1178 |
+
'select_time_type' => 'select'
|
1179 |
+
),
|
1180 |
+
'select_buster_type' => array(
|
1181 |
+
'input_custom_buster' => 'input'
|
1182 |
+
)
|
1183 |
+
)
|
1184 |
+
);
|
1185 |
+
|
1186 |
+
// Get a subset of the option array for this form
|
1187 |
+
$form_options = array(
|
1188 |
+
'input_minurl',
|
1189 |
+
'input_minpath',
|
1190 |
+
'input_cache_dir',
|
1191 |
+
'input_doc_root',
|
1192 |
+
'input_maxfiles',
|
1193 |
+
'input_maxage',
|
1194 |
+
'input_custom_buster',
|
1195 |
+
'enable_min_js',
|
1196 |
+
'enable_min_css',
|
1197 |
+
'enable_bloginfo',
|
1198 |
+
'enable_css_bubble',
|
1199 |
+
'enable_cache_file_lock',
|
1200 |
+
'enable_debug',
|
1201 |
+
'select_buster_type',
|
1202 |
+
'select_time_type',
|
1203 |
+
);
|
1204 |
+
|
1205 |
+
// Flush the cache
|
1206 |
+
if ((isset($_POST['flush_cache']) || isset($_POST['save_flush']))
|
1207 |
+
&& !self::is_normal_admin()
|
1208 |
+
) {
|
1209 |
+
check_admin_referer($page);
|
1210 |
+
|
1211 |
+
$deleted = $this->_flush_cache();
|
1212 |
+
if (0 < $deleted)
|
1213 |
+
$this->add_notice(
|
1214 |
+
'<strong>' . __('Notice', $this->domain) . ':</strong> '
|
1215 |
+
. sprintf(
|
1216 |
+
__("<strong>%d</strong> cached files "
|
1217 |
+
. "have been deleted successfully!", $this->domain),
|
1218 |
+
$deleted
|
1219 |
+
)
|
1220 |
+
);
|
1221 |
+
else
|
1222 |
+
$this->add_notice(
|
1223 |
+
'<strong>' . __('Notice', $this->domain) . ':</strong> '
|
1224 |
+
. __("Could not delete any cached files. "
|
1225 |
+
. "Please manually flush the cache directory.", $this->domain)
|
1226 |
+
);
|
1227 |
+
|
1228 |
+
// this should also clear all saved Minify groups
|
1229 |
+
$this->detector->clear_logs('group');
|
1230 |
+
}
|
1231 |
+
}
|
1232 |
+
else if ($page == BWP_MINIFY_OPTION_ADVANCED)
|
1233 |
+
{
|
1234 |
+
$bwp_option_page->set_current_tab(2);
|
1235 |
+
|
1236 |
+
$form = array(
|
1237 |
+
'items' => array(
|
1238 |
+
'heading',
|
1239 |
+
'checkbox',
|
1240 |
+
'input',
|
1241 |
+
'input',
|
1242 |
+
'heading',
|
1243 |
+
'checkbox',
|
1244 |
+
'select',
|
1245 |
+
'input',
|
1246 |
+
'input',
|
1247 |
+
'input',
|
1248 |
+
'textarea'
|
1249 |
+
),
|
1250 |
+
'item_labels' => array(
|
1251 |
+
__('Friendly Minify Urls', $this->domain),
|
1252 |
+
__('Enable friendly Minify urls', $this->domain),
|
1253 |
+
sprintf(
|
1254 |
+
__('Friendly Minify url path (relative to '
|
1255 |
+
. 'your Site/Network Address). More info <a href="%s#friendly_minify_urls" '
|
1256 |
+
. 'target="_blank">here</a>.', $this->domain),
|
1257 |
+
$this->plugin_url
|
1258 |
+
),
|
1259 |
+
__('Path to Nginx config file', $this->domain),
|
1260 |
+
__('Content Delivery Network (CDN)', $this->domain),
|
1261 |
+
__('Enable CDN support', $this->domain),
|
1262 |
+
__('SSL support for CDN', $this->domain),
|
1263 |
+
__('CDN hostname (primary)', $this->domain),
|
1264 |
+
__('CDN hostname for JS files', $this->domain),
|
1265 |
+
__('CDN hostname for CSS files', $this->domain),
|
1266 |
+
__('Additional HTTP headers used with CDN', $this->domain)
|
1267 |
+
),
|
1268 |
+
'item_names' => array(
|
1269 |
+
'h1',
|
1270 |
+
'cb1',
|
1271 |
+
'input_fly_minpath',
|
1272 |
+
'input_nginx_config_file',
|
1273 |
+
'h2',
|
1274 |
+
'cb2',
|
1275 |
+
'select_cdn_ssl_type',
|
1276 |
+
'input_cdn_host',
|
1277 |
+
'input_cdn_host_js',
|
1278 |
+
'input_cdn_host_css'
|
1279 |
+
),
|
1280 |
+
'heading' => array(
|
1281 |
+
'h1' => '<em>' . sprintf(
|
1282 |
+
__('Turn long and ugly Minify urls with '
|
1283 |
+
. 'query variables (such as <code>%s</code>), '
|
1284 |
+
. 'into more friendly ones (e.g. <code>%s</code>). '
|
1285 |
+
. '', $this->domain),
|
1286 |
+
trailingslashit($this->min_url)
|
1287 |
+
. '?f=path/to/script1.js,path/to/script2.js',
|
1288 |
+
home_url('path/to/cache/somestring.js')
|
1289 |
+
) . '</em>',
|
1290 |
+
'h2' => '<em>' . sprintf(
|
1291 |
+
__('Serve minified contents '
|
1292 |
+
. 'using a <a href="%s" target="_blank">CDN</a> to improve performance.', $this->domain),
|
1293 |
+
$this->get_url('wikipedia_cdn'))
|
1294 |
+
. '</em>'
|
1295 |
+
),
|
1296 |
+
'select' => array(
|
1297 |
+
'select_fly_serve_method' => array(
|
1298 |
+
__('WordPress', $this->domain) => 'wp',
|
1299 |
+
__('Server rewrite rules', $this->domain) => 'server'
|
1300 |
+
),
|
1301 |
+
'select_cdn_ssl_type' => array(
|
1302 |
+
__('Do not use SSL for CDN', $this->domain) => 'off',
|
1303 |
+
__('Use SSL when suitable', $this->domain) => 'on',
|
1304 |
+
__('Use protocol-relative URL', $this->domain) => 'less'
|
1305 |
+
)
|
1306 |
+
),
|
1307 |
+
'checkbox' => array(
|
1308 |
+
'cb1' => array(
|
1309 |
+
__('for CDN support it is highly recommended that you enable this feature.', $this->domain) => 'enable_fly_min'
|
1310 |
+
),
|
1311 |
+
'cb2' => array(
|
1312 |
+
__('please make sure that your CDN is property setup before enabling this feature.', $this->domain) => 'enable_cdn'
|
1313 |
+
)
|
1314 |
+
),
|
1315 |
+
'input' => array(
|
1316 |
+
'input_fly_minpath' => array(
|
1317 |
+
'size' => 55,
|
1318 |
+
'label' => $this->_get_input_fly_min_path_label()
|
1319 |
+
),
|
1320 |
+
'input_nginx_config_file' => array(
|
1321 |
+
'size' => 55,
|
1322 |
+
'label' => '<br />' . sprintf(
|
1323 |
+
__('Expect a full path to your Nginx '
|
1324 |
+
. 'configuration file (e.g. <code>%s</code>). '
|
1325 |
+
. 'If none specified or the config file '
|
1326 |
+
. 'is not writable, you will have to manually update it. '
|
1327 |
+
. 'Alternatively, if you have a config file '
|
1328 |
+
. 'at <code>%s</code>, which is used by W3 Total Cache '
|
1329 |
+
. 'plugin, you can leave this field blank and '
|
1330 |
+
. 'BWP Minify will use that config file.', $this->domain),
|
1331 |
+
'/path/to/nginx.conf', $this->get_doc_root('nginx.conf')
|
1332 |
+
)
|
1333 |
+
),
|
1334 |
+
'input_cdn_host' => array(
|
1335 |
+
'size' => 40,
|
1336 |
+
'label' => '<br />' . sprintf(
|
1337 |
+
__('Use either hostnames provided by your '
|
1338 |
+
. 'CDN or custom ones. Please do NOT include '
|
1339 |
+
. 'the scheme (i.e. <code>http://</code> or <code>https://</code>). '
|
1340 |
+
. 'Good examples are: <code>%s</code>, <code>%s</code>, etc.', $this->domain),
|
1341 |
+
'yourzone.yourcdn.com', 'cdn.yourdomain.com'
|
1342 |
+
)
|
1343 |
+
),
|
1344 |
+
'input_cdn_host_js' => array(
|
1345 |
+
'size' => 40,
|
1346 |
+
'label' => ' ' . __('used when not empty.', $this->domain)
|
1347 |
+
),
|
1348 |
+
'input_cdn_host_css' => array(
|
1349 |
+
'size' => 40,
|
1350 |
+
'label' => ' ' . __('used when not empty.', $this->domain)
|
1351 |
+
)
|
1352 |
+
),
|
1353 |
+
'textarea' => array(
|
1354 |
+
'input_cdn_headers' => array(
|
1355 |
+
''
|
1356 |
+
)
|
1357 |
+
),
|
1358 |
+
'container' => array(
|
1359 |
+
'cb2' => '<p style="margin-left:220px;"><em>'
|
1360 |
+
. __('If you\'re looking for a CDN, I highly recommend '
|
1361 |
+
. '<strong>MaxCDN</strong>. They provide the most reliable CDN service '
|
1362 |
+
. 'for WordPress sites. Support is exceptional and '
|
1363 |
+
. 'you got a 30-day Money Back Guarantee, too.', $this->domain)
|
1364 |
+
. '</em><br />'
|
1365 |
+
. '<a href="#" target="_blank" class="button-secondary" style="margin-top:10px;" '
|
1366 |
+
. 'onclick="this.href=\'' . $this->get_url('aff_maxcdn') . '\';">'
|
1367 |
+
. __('Create a MaxCDN account today and save 25%</a>', $this->domain)
|
1368 |
+
. '</p>'
|
1369 |
+
),
|
1370 |
+
'post' => array(
|
1371 |
+
)
|
1372 |
+
);
|
1373 |
+
|
1374 |
+
$form_options = array();
|
1375 |
+
}
|
1376 |
+
else if ($page == BWP_MINIFY_MANAGE)
|
1377 |
+
{
|
1378 |
+
$bwp_option_page->set_current_tab(3);
|
1379 |
+
|
1380 |
+
remove_action('bwp_option_action_before_form', array($this, 'show_donation'), 12);
|
1381 |
+
|
1382 |
+
// add a secondary button to clear enqueued file lists (both)
|
1383 |
+
add_filter('bwp_option_submit_button', array($this, 'add_clear_enqueue_button'));
|
1384 |
+
|
1385 |
+
if (isset($_POST['clear_enqueue']))
|
1386 |
+
{
|
1387 |
+
check_admin_referer($page);
|
1388 |
+
|
1389 |
+
$this->detector->clear_logs('enqueue');
|
1390 |
+
$this->add_notice(
|
1391 |
+
__('Enqueued file lists have been cleared successfully. '
|
1392 |
+
. 'Try refreshing this page to see updated file lists.', $this->domain)
|
1393 |
+
);
|
1394 |
+
}
|
1395 |
+
|
1396 |
+
$form = array(
|
1397 |
+
'items' => array(
|
1398 |
+
'heading',
|
1399 |
+
'heading'
|
1400 |
+
),
|
1401 |
+
'item_labels' => array (
|
1402 |
+
__('Move enqueued JS files to appropriate positions', $this->domain),
|
1403 |
+
__('Move enqueued CSS files to appropriate positions', $this->domain)
|
1404 |
+
),
|
1405 |
+
'item_names' => array(
|
1406 |
+
'h1',
|
1407 |
+
'h2'
|
1408 |
+
),
|
1409 |
+
'heading' => array(
|
1410 |
+
'h1' => '<em>' . sprintf(
|
1411 |
+
__('Below you can find a list of enqueued JS files '
|
1412 |
+
. 'detected by this plugin. Press <strong>select</strong> and '
|
1413 |
+
. 'then choose an appropriate position for selected JS file. '
|
1414 |
+
. 'You can also directly type in one script handle (<strong>NOT '
|
1415 |
+
. 'filename/script src</strong>) per line in the input field if '
|
1416 |
+
. 'you want. More info <a href="%s#positioning_your_files" target="_blank">here</a>.', $this->domain),
|
1417 |
+
$this->plugin_url
|
1418 |
+
) . '</em>',
|
1419 |
+
'h2' => '<em>' . sprintf(
|
1420 |
+
__('Below you can find a list of enqueued CSS files '
|
1421 |
+
. 'detected by this plugin. Press <strong>select</strong> and '
|
1422 |
+
. 'then choose an appropriate position for selected CSS file. '
|
1423 |
+
. 'You can also directly type in one style handle (<strong>NOT '
|
1424 |
+
. 'filename/style src</strong>) per line in the input field if '
|
1425 |
+
. 'you want. More info <a href="%s#positioning_your_files" target="_blank">here</a>.', $this->domain),
|
1426 |
+
$this->plugin_url
|
1427 |
+
) . '</em>'
|
1428 |
+
),
|
1429 |
+
'container' => array(
|
1430 |
+
'h1' => $this->_show_enqueued_scripts(),
|
1431 |
+
'h2' => $this->_show_enqueued_styles(),
|
1432 |
+
)
|
1433 |
+
);
|
1434 |
+
|
1435 |
+
// Get a subset of the option array for this form
|
1436 |
+
$form_options = array(
|
1437 |
+
'input_ignore',
|
1438 |
+
'input_header',
|
1439 |
+
'input_direct',
|
1440 |
+
'input_footer',
|
1441 |
+
'input_oblivion',
|
1442 |
+
'input_style_ignore',
|
1443 |
+
'input_style_direct',
|
1444 |
+
'input_style_oblivion',
|
1445 |
+
);
|
1446 |
+
}
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
$option_formats = array(
|
1450 |
+
'input_maxfiles' => 'int',
|
1451 |
+
'input_maxage' => 'int',
|
1452 |
+
'select_time_type' => 'int'
|
1453 |
+
);
|
1454 |
+
|
1455 |
+
$option_super_admin = $this->site_options;
|
1456 |
+
|
1457 |
+
// Get option from user input
|
1458 |
+
if ((isset($_POST['submit_' . $bwp_option_page->get_form_name()])
|
1459 |
+
|| (isset($_POST['save_flush']) && $page == BWP_MINIFY_OPTION_GENERAL
|
1460 |
+
&& !self::is_normal_admin()))
|
1461 |
+
&& isset($options) && is_array($options)
|
1462 |
+
) {
|
1463 |
+
// basic security check
|
1464 |
+
check_admin_referer($page);
|
1465 |
+
|
1466 |
+
foreach ($options as $key => &$option)
|
1467 |
+
{
|
1468 |
+
if ((0 < sizeof($form_options) && !in_array($key, $form_options))
|
1469 |
+
|| (self::is_normal_admin() && in_array($key, $option_super_admin))
|
1470 |
+
) {
|
1471 |
+
// field not found in options assigned to current form
|
1472 |
+
// OR not a super admin, and this is a super-admin only setting
|
1473 |
+
continue;
|
1474 |
+
}
|
1475 |
+
else
|
1476 |
+
{
|
1477 |
+
if (isset($_POST[$key]))
|
1478 |
+
{
|
1479 |
+
$bwp_option_page->format_field($key, $option_formats);
|
1480 |
+
$option = trim(stripslashes($_POST[$key]));
|
1481 |
+
}
|
1482 |
+
|
1483 |
+
if (!isset($_POST[$key])
|
1484 |
+
&& !isset($form['input'][$key]['disabled'])
|
1485 |
+
) {
|
1486 |
+
// checkbox, exclude disabled input
|
1487 |
+
$option = '';
|
1488 |
+
}
|
1489 |
+
else if (isset($option_formats[$key])
|
1490 |
+
&& 'int' == $option_formats[$key]
|
1491 |
+
&& ('' === $_POST[$key] || 0 > $_POST[$key])
|
1492 |
+
) {
|
1493 |
+
// expect integer but received empty string or negative integer
|
1494 |
+
$option = $this->options_default[$key];
|
1495 |
+
}
|
1496 |
+
}
|
1497 |
+
}
|
1498 |
+
|
1499 |
+
// update per-blog options
|
1500 |
+
update_option($active_page, $options);
|
1501 |
+
|
1502 |
+
// if current user is super admin, allow him to update site-only
|
1503 |
+
// options - this is WPMS compatible
|
1504 |
+
if (!self::is_normal_admin())
|
1505 |
+
update_site_option($active_page, $options);
|
1506 |
+
|
1507 |
+
// refresh the options property to include updated options
|
1508 |
+
$this->options = array_merge($this->options, $options);
|
1509 |
+
|
1510 |
+
// success messages when settings are saved
|
1511 |
+
if ($page == BWP_MINIFY_MANAGE) {
|
1512 |
+
$this->detector->auto_detect();
|
1513 |
+
$this->add_notice(
|
1514 |
+
__('All positions have been saved. '
|
1515 |
+
. 'Try refreshing this page for updated file lists.', $this->domain)
|
1516 |
+
);
|
1517 |
+
} else {
|
1518 |
+
$this->add_notice(__('All options have been saved.', $this->domain));
|
1519 |
+
}
|
1520 |
+
|
1521 |
+
// take care of some custom POST actions when form is submitted
|
1522 |
+
if ($page == BWP_MINIFY_OPTION_GENERAL && !self::is_normal_admin())
|
1523 |
+
{
|
1524 |
+
// try to save the Minify config file
|
1525 |
+
$result = $this->create_minify_config_file();
|
1526 |
+
if (true === $result)
|
1527 |
+
{
|
1528 |
+
// config file was successfully written
|
1529 |
+
$this->add_notice(sprintf(
|
1530 |
+
__('Minify config file <code>%s</code> '
|
1531 |
+
. 'has been updated successfully.', $this->domain),
|
1532 |
+
$this->min_dir . 'config.php'
|
1533 |
+
));
|
1534 |
+
}
|
1535 |
+
else if ('config' === $result)
|
1536 |
+
{
|
1537 |
+
// config file is missing
|
1538 |
+
$this->add_error(
|
1539 |
+
'<strong style="color:red">' . __('Error') . ':</strong> '
|
1540 |
+
. sprintf(
|
1541 |
+
__('Minify config file <code>config.php</code> could not be found. '
|
1542 |
+
. 'The auto-detected directory to look for the config file is <code>%s</code>. '
|
1543 |
+
. 'Please manually check if that directory actually exists '
|
1544 |
+
. 'and contains the config file.', $this->domain),
|
1545 |
+
$this->get_doc_root($this->get_min_path())
|
1546 |
+
)
|
1547 |
+
);
|
1548 |
+
}
|
1549 |
+
else if ('put' === $result)
|
1550 |
+
{
|
1551 |
+
// the write process failed for some reasons
|
1552 |
+
$this->add_error(sprintf(
|
1553 |
+
'<strong style="color:red">' . __('Error') . ':</strong> '
|
1554 |
+
. __('There was an error writing to Minify config file <code>%s</code>. '
|
1555 |
+
. 'Please try again.', $this->domain),
|
1556 |
+
$this->min_dir . 'config.php'
|
1557 |
+
));
|
1558 |
+
}
|
1559 |
+
else
|
1560 |
+
{
|
1561 |
+
// config file is not writable, show the auto-generated
|
1562 |
+
// contents to admin for manual update
|
1563 |
+
$this->add_notice(sprintf(
|
1564 |
+
'<strong>' . __('Notice', $this->domain) . ':</strong> '
|
1565 |
+
. __('Minify config file <code>%s</code> '
|
1566 |
+
. 'is not writable. See '
|
1567 |
+
. '<a href="#minify.config.php">below</a> '
|
1568 |
+
. 'for details.', $this->domain),
|
1569 |
+
$this->min_dir . 'config.php'
|
1570 |
+
));
|
1571 |
+
$form['container']['h2'] = $this->_show_generated_config($result);
|
1572 |
+
}
|
1573 |
+
}
|
1574 |
+
else if ($page == BWP_MINIFY_OPTION_ADVANCED)
|
1575 |
+
{
|
1576 |
+
if ('yes' != $original_options['enable_fly_min']
|
1577 |
+
&& 'yes' == $this->options['enable_fly_min']
|
1578 |
+
) {
|
1579 |
+
// friendly minify url is turned on, add rewrite rules to
|
1580 |
+
// server directory config file (e.g. `.htaccess`). This should
|
1581 |
+
// not require WordPress's pretty permalink to be turned on
|
1582 |
+
$this->_toggle_rewrite_rules($options, $form);
|
1583 |
+
} else if ('yes' == $original_options['enable_fly_min']
|
1584 |
+
&& 'yes' != $this->options['enable_fly_min']
|
1585 |
+
) {
|
1586 |
+
// remove the rules and flush cache if this setting is
|
1587 |
+
// turned off, ingore all error messages for now
|
1588 |
+
$this->_remove_rewrite_rules();
|
1589 |
+
$this->_flush_cache();
|
1590 |
+
}
|
1591 |
+
}
|
1592 |
+
}
|
1593 |
+
|
1594 |
+
if ($page == BWP_MINIFY_OPTION_GENERAL)
|
1595 |
+
{
|
1596 |
+
// take care of non-POST actions when we're on General Options page
|
1597 |
+
// re-generate the buster string preview whenever buster type change
|
1598 |
+
$options['input_custom_buster'] = $this->get_buster($options['select_buster_type']);
|
1599 |
+
if ('custom' == $options['select_buster_type'])
|
1600 |
+
unset($form['input']['input_custom_buster']['disabled']);
|
1601 |
+
|
1602 |
+
// warns admin that the cache directory does not exist or is not writable
|
1603 |
+
if (!self::is_normal_admin())
|
1604 |
+
{
|
1605 |
+
$cache_dir = $this->get_cache_dir();
|
1606 |
+
if (!file_exists($cache_dir))
|
1607 |
+
$this->add_notice(
|
1608 |
+
'<strong>' . __('Warning') . ':</strong> '
|
1609 |
+
. sprintf(
|
1610 |
+
__("Cache directory <code>%s</code> does not exist "
|
1611 |
+
. "and can not be created automatically. "
|
1612 |
+
. "Please manually create the cache folder "
|
1613 |
+
. "and make sure that it is writable "
|
1614 |
+
. "for Minify to perform more efficiently.", $this->domain),
|
1615 |
+
$cache_dir
|
1616 |
+
)
|
1617 |
+
);
|
1618 |
+
else if (!is_writable($cache_dir))
|
1619 |
+
$this->add_notice(
|
1620 |
+
'<strong>' . __('Warning') . ':</strong> '
|
1621 |
+
. sprintf(
|
1622 |
+
__("Cache directory <code>%s</code> is not writable. "
|
1623 |
+
. "Please try CHMOD your cache directory to 755. "
|
1624 |
+
. "If you still see this warning, try CHMOD to 777.", $this->domain),
|
1625 |
+
$cache_dir
|
1626 |
+
)
|
1627 |
+
);
|
1628 |
+
}
|
1629 |
+
|
1630 |
+
// Remove all Minify library-related settings if we're in multisite
|
1631 |
+
// and this is not super-admin
|
1632 |
+
if (self::is_normal_admin())
|
1633 |
+
$bwp_option_page->kill_html_fields($form, array(7,8,9,10,11,12,13));
|
1634 |
+
}
|
1635 |
+
else if ($page == BWP_MINIFY_OPTION_ADVANCED)
|
1636 |
+
{
|
1637 |
+
// Remove all super admin only settings if we're in multisite
|
1638 |
+
// and this is not super-admin, also remove nginx config path if
|
1639 |
+
// not on nginx server
|
1640 |
+
if (self::is_normal_admin())
|
1641 |
+
$bwp_option_page->kill_html_fields($form, array(2));
|
1642 |
+
if (!self::is_nginx() || self::is_normal_admin())
|
1643 |
+
$bwp_option_page->kill_html_fields($form, array(3));
|
1644 |
+
}
|
1645 |
+
|
1646 |
+
// check for rewrite rules if needed, suppress error
|
1647 |
+
if ('yes' == $this->options['enable_fly_min'])
|
1648 |
+
$this->_add_rewrite_rules();
|
1649 |
+
|
1650 |
+
// Assign the form and option array
|
1651 |
+
$bwp_option_page->init($form, $options, $this->form_tabs);
|
1652 |
+
|
1653 |
+
// Build the option page
|
1654 |
+
echo $bwp_option_page->generate_html_form();
|
1655 |
+
}
|
1656 |
+
|
1657 |
+
private function _show_enqueued_styles()
|
1658 |
+
{
|
1659 |
+
$fields = array(
|
1660 |
+
'input_style_ignore' => __('Styles to be ignored (not minified)', $this->domain),
|
1661 |
+
'input_style_direct' => __('Styles to be minified and then printed separately', $this->domain),
|
1662 |
+
'input_style_oblivion' => __('Styles to be forgotten (to remove duplicate styles)', $this->domain)
|
1663 |
+
);
|
1664 |
+
|
1665 |
+
return $this->_show_enqueued('style', $fields);
|
1666 |
+
}
|
1667 |
+
|
1668 |
+
private function _show_enqueued_scripts()
|
1669 |
+
{
|
1670 |
+
$fields = array(
|
1671 |
+
'input_ignore' => __('Scripts to be ignored (not minified)', $this->domain),
|
1672 |
+
'input_direct' => __('Scripts to be minified and then printed separately', $this->domain),
|
1673 |
+
'input_header' => __('Scripts to be minified in header', $this->domain),
|
1674 |
+
'input_footer' => __('Scripts to be minified in footer', $this->domain),
|
1675 |
+
'input_oblivion' => __('Scripts to be forgotten (to remove duplicate scripts)', $this->domain)
|
1676 |
+
);
|
1677 |
+
|
1678 |
+
return $this->_show_enqueued('script', $fields);
|
1679 |
+
}
|
1680 |
+
|
1681 |
+
private function _show_enqueued($type, $fields)
|
1682 |
+
{
|
1683 |
+
ob_start();
|
1684 |
+
?>
|
1685 |
+
<div class="bwp-minify-table-wrapper">
|
1686 |
+
<div class="bwp-minify-table-scroller">
|
1687 |
+
<table class="bwp-minify-detector-table" cellpadding="0"
|
1688 |
+
cellspacing="0" border="0">
|
1689 |
+
<?php
|
1690 |
+
if ('script' == $type)
|
1691 |
+
$this->detector->show_detected_scripts();
|
1692 |
+
else
|
1693 |
+
$this->detector->show_detected_styles();
|
1694 |
+
?>
|
1695 |
+
</table>
|
1696 |
+
</div>
|
1697 |
+
</div>
|
1698 |
+
|
1699 |
+
<div class="bwp-sidebar">
|
1700 |
+
<ul>
|
1701 |
+
<?php
|
1702 |
+
foreach ($fields as $field => $label) :
|
1703 |
+
$value = isset($_POST[$field])
|
1704 |
+
? trim(strip_tags($_POST[$field]))
|
1705 |
+
: $this->options[$field];
|
1706 |
+
?>
|
1707 |
+
<li>
|
1708 |
+
<a class="position-handle" data-position="<?php echo $field; ?>"
|
1709 |
+
href="#"><span class="bwp-sign">+</span> <?php echo $label ?></a>
|
1710 |
+
<textarea name="<?php echo $field ?>"
|
1711 |
+
cols="20" rows="5"><?php esc_html_e($value); ?></textarea>
|
1712 |
+
</li>
|
1713 |
+
<?php
|
1714 |
+
endforeach;
|
1715 |
+
?>
|
1716 |
+
</ul>
|
1717 |
+
</div>
|
1718 |
+
<div style="clear: both"></div>
|
1719 |
+
<?php
|
1720 |
+
|
1721 |
+
$output = ob_get_contents();
|
1722 |
+
ob_end_clean();
|
1723 |
+
|
1724 |
+
return $output;
|
1725 |
+
}
|
1726 |
+
|
1727 |
+
function add_flush_cache_buttons($button)
|
1728 |
+
{
|
1729 |
+
$button = str_replace(
|
1730 |
+
'</p>',
|
1731 |
+
' <input type="submit" class="button-secondary action" '
|
1732 |
+
. 'name="save_flush" value="'
|
1733 |
+
. __('Save Changes and Flush Cache', $this->domain) . '" />'
|
1734 |
+
. ' <input type="submit" class="button-secondary action" '
|
1735 |
+
. 'name="flush_cache" value="'
|
1736 |
+
. __('Flush Cache', $this->domain) . '" /></p>',
|
1737 |
+
$button);
|
1738 |
+
|
1739 |
+
return $button;
|
1740 |
+
}
|
1741 |
+
|
1742 |
+
function add_clear_enqueue_button($button)
|
1743 |
+
{
|
1744 |
+
$button = str_replace(
|
1745 |
+
'</p>',
|
1746 |
+
' <input type="submit" class="button-secondary action" '
|
1747 |
+
. 'name="clear_enqueue" value="'
|
1748 |
+
. __('Clear File Lists', $this->domain) . '" /></p>',
|
1749 |
+
$button);
|
1750 |
+
|
1751 |
+
return $button;
|
1752 |
+
}
|
1753 |
+
|
1754 |
+
private function _get_input_fly_min_path_label()
|
1755 |
+
{
|
1756 |
+
$fly_min_path = $this->get_fly_min_path(false, true);
|
1757 |
+
$label = '';
|
1758 |
+
|
1759 |
+
if (false == $fly_min_path)
|
1760 |
+
{
|
1761 |
+
$warning = sprintf(
|
1762 |
+
__('BWP Minify was not able to auto-detect friendly url based on '
|
1763 |
+
. 'Minify\'s current cache directory (<code>%s</code>).', $this->domain),
|
1764 |
+
$this->get_cache_dir()
|
1765 |
+
);
|
1766 |
+
$this->add_error($warning);
|
1767 |
+
|
1768 |
+
$label = '<br />' . __('You must manually set this setting for '
|
1769 |
+
. 'this feature to work.<br />'
|
1770 |
+
. 'Please make sure that the entered URL path correctly links to '
|
1771 |
+
. 'Minify\'s current cache directory.', $this->domain);
|
1772 |
+
}
|
1773 |
+
else
|
1774 |
+
{
|
1775 |
+
$label = '<br />' . sprintf(
|
1776 |
+
__('Leave empty to use <code>%s</code>, which is auto-detected '
|
1777 |
+
. 'based on Minify\'s current cache directory (<code>%s</code>). '
|
1778 |
+
. 'The URL path (either manually entered or auto-detected) '
|
1779 |
+
. 'must correctly link to Minify\'s current cache directory.', $this->domain),
|
1780 |
+
$fly_min_path, $this->get_cache_dir()
|
1781 |
+
);
|
1782 |
+
}
|
1783 |
+
|
1784 |
+
if (self::is_multisite() && !self::is_subdomain_install())
|
1785 |
+
{
|
1786 |
+
$label .= '<br /><strong>' . __('Notice', $this->domain) . ':</strong> ' . sprintf(
|
1787 |
+
__('In a sub-directory multisite environment, '
|
1788 |
+
. 'a blog path will be added before the URL path (e.g. <code>%s</code>) '
|
1789 |
+
. 'when friendly Minify urls are served.', $this->domain),
|
1790 |
+
'/blog1' . $fly_min_path
|
1791 |
+
);
|
1792 |
+
}
|
1793 |
+
|
1794 |
+
return $label;
|
1795 |
+
}
|
1796 |
+
|
1797 |
+
private function _get_input_doc_root_label()
|
1798 |
+
{
|
1799 |
+
$doc_root = $_SERVER['DOCUMENT_ROOT'];
|
1800 |
+
$wp_doc_root = $this->get_wp_doc_root();
|
1801 |
+
|
1802 |
+
// if server document root is empty, or is invalid (i.e. WordPress
|
1803 |
+
// does NOT live under it), we use WordPress document root
|
1804 |
+
if (empty($doc_root) || 0 !== strpos($wp_doc_root, $doc_root))
|
1805 |
+
{
|
1806 |
+
return sprintf(
|
1807 |
+
__('Leave empty to use parent directory of WordPress\'s <code>index.php</code>, '
|
1808 |
+
. 'which is <code>%s</code>. '
|
1809 |
+
. 'If you want to include JS and CSS files '
|
1810 |
+
. 'outside of this root, or you happen to move '
|
1811 |
+
. '<code>wp-content</code> directory somewhere else, '
|
1812 |
+
. 'make sure you set this to the correct directory '
|
1813 |
+
. 'with NO trailing slash. '
|
1814 |
+
. 'This setting is very important as it makes sure that Minify library '
|
1815 |
+
. 'can correctly locate your JS, CSS files. '
|
1816 |
+
. 'More info can be found <a href="%s#minify_document_root" '
|
1817 |
+
. 'target="_blank">here</a>.', $this->domain),
|
1818 |
+
$wp_doc_root, $this->plugin_url
|
1819 |
+
);
|
1820 |
+
}
|
1821 |
+
else
|
1822 |
+
{
|
1823 |
+
return sprintf(
|
1824 |
+
__('Leave empty to use <code>$_SERVER[\'DOCUMENT_ROOT\']</code>, '
|
1825 |
+
. 'which is <code>%s</code>. This is only used when '
|
1826 |
+
. 'it is not empty and your WordPress does live under it.<br />'
|
1827 |
+
. 'If you believe the auto-detected document root is wrong, '
|
1828 |
+
. 'make sure you set it to the correct directory '
|
1829 |
+
. 'with NO trailing slash. <br />'
|
1830 |
+
. 'This setting is very important as it makes sure that Minify '
|
1831 |
+
. 'can correctly locate your JS, CSS files. '
|
1832 |
+
. 'More info can be found <a href="%s#minify_document_root" '
|
1833 |
+
. 'target="_blank">here</a>.', $this->domain),
|
1834 |
+
$doc_root, $this->plugin_url
|
1835 |
+
);
|
1836 |
+
}
|
1837 |
+
}
|
1838 |
+
|
1839 |
+
/**
|
1840 |
+
* Writes rewrite rules to server config file when needed
|
1841 |
+
*
|
1842 |
+
* This function is called whenever friendly minify url feature is enabled,
|
1843 |
+
* and based on current server certain actions are taken accordingly.
|
1844 |
+
*
|
1845 |
+
* @since 1.3.0
|
1846 |
+
* @return void
|
1847 |
+
*/
|
1848 |
+
private function _toggle_rewrite_rules(&$options, &$form)
|
1849 |
+
{
|
1850 |
+
// add rewrite rules to WP root, used by all servers
|
1851 |
+
$form['container']['h1'] = array();
|
1852 |
+
$this->rewriter->no_suppress();
|
1853 |
+
$result = $this->rewriter->add_wp_rewrite_rules();
|
1854 |
+
|
1855 |
+
if (true !== $result && 'written' !== $result)
|
1856 |
+
{
|
1857 |
+
// get appropriate error messages and show it to admin
|
1858 |
+
$config_file = $this->rewriter->get_wp_config_file();
|
1859 |
+
$error = $this->_get_rewrite_rules_error($result, $config_file);
|
1860 |
+
$this->add_error($error);
|
1861 |
+
$form['container']['h1'][] = $this->_show_generated_wp_rewrite_rules();
|
1862 |
+
}
|
1863 |
+
|
1864 |
+
if (false == self::is_nginx())
|
1865 |
+
{
|
1866 |
+
// asumming apache, need to add rules to cache directory too
|
1867 |
+
$result = $this->rewriter->add_cache_rewrite_rules();
|
1868 |
+
$config_file = $this->rewriter->get_cache_config_file();
|
1869 |
+
|
1870 |
+
if (true !== $result && 'written' !== $result)
|
1871 |
+
{
|
1872 |
+
// get appropriate error messages and show it to admin
|
1873 |
+
$error = $this->_get_rewrite_rules_error($result, $config_file);
|
1874 |
+
$this->add_error($error);
|
1875 |
+
|
1876 |
+
// in any case rewrite rules were NOT successfully written,
|
1877 |
+
// except for when rewrite rules are already found,
|
1878 |
+
// turn off this setting to prevent site from breaking,
|
1879 |
+
// and show the auto-generated rules to admin
|
1880 |
+
$options['enable_fly_min'] = '';
|
1881 |
+
update_option(BWP_MINIFY_OPTION_ADVANCED, $options);
|
1882 |
+
|
1883 |
+
$this->add_notice(
|
1884 |
+
'<strong>' . __('Notice') . ':</strong> '
|
1885 |
+
. __('Friendly minify url feature has been turned off '
|
1886 |
+
. 'automatically to prevent your site from breaking. ', $this->domain)
|
1887 |
+
);
|
1888 |
+
|
1889 |
+
$form['container']['h1'][] = $this->_show_generated_cache_rewrite_rules();
|
1890 |
+
}
|
1891 |
+
else if (true === $result)
|
1892 |
+
{
|
1893 |
+
// successfully enable friendly minify url feature,
|
1894 |
+
// flush all cached files and auto-detect groups
|
1895 |
+
$this->_flush_cache();
|
1896 |
+
$this->detector->auto_detect();
|
1897 |
+
}
|
1898 |
+
}
|
1899 |
+
}
|
1900 |
+
|
1901 |
+
private function _show_generated_config($contents)
|
1902 |
+
{
|
1903 |
+
$output = '<strong>'
|
1904 |
+
. __('Could not write Minify library settings to <code>%s</code>. '
|
1905 |
+
. 'Please update the config file manually by <em>replacing</em> its current contents '
|
1906 |
+
. 'with auto-generated contents as shown below:', $this->domain)
|
1907 |
+
. '</strong>';
|
1908 |
+
$output = sprintf($output, $this->min_dir . 'config.php');
|
1909 |
+
$output .= '<br /><br />';
|
1910 |
+
$output .= '<textarea class="code" rows="16" cols="90" readonly="readonly">'
|
1911 |
+
. $contents . '</textarea>';
|
1912 |
+
|
1913 |
+
return $output;
|
1914 |
+
}
|
1915 |
+
|
1916 |
+
private function _show_generated_wp_rewrite_rules()
|
1917 |
+
{
|
1918 |
+
if (self::is_nginx())
|
1919 |
+
{
|
1920 |
+
// nginx server only needs rewrite rules in a central config file
|
1921 |
+
$rules = $this->rewriter->get_generated_wp_rewrite_rules();
|
1922 |
+
$output = '<strong>' . __('Please update the Nginx config file '
|
1923 |
+
. 'manually using auto-generated contents as shown below. '
|
1924 |
+
. 'It is highly recommended that you create a separate config file '
|
1925 |
+
. 'for BWP Minify (e.g. <code>bwp-minify.conf</code>) and include it '
|
1926 |
+
. 'directly above where you include your cache plugins\' config files, '
|
1927 |
+
. 'as shown <a href="%s" target="_blank">here</a>. ', $this->domain) . '</strong>';
|
1928 |
+
$output = sprintf($output, $this->get_url('wp_codex_nginx_rewrite_rules'));
|
1929 |
+
}
|
1930 |
+
else
|
1931 |
+
{
|
1932 |
+
// Apache server needs rewrite rules for root WP rewrite rules when
|
1933 |
+
// sub-directory multisite is set up
|
1934 |
+
$rules = $this->rewriter->get_generated_wp_rewrite_rules();
|
1935 |
+
$output = '<strong>' . __('Please update the server config file <code>%s</code> '
|
1936 |
+
. 'manually using auto-generated contents as shown below. '
|
1937 |
+
. 'It is highly recommended that you paste the contents '
|
1938 |
+
. 'at the top of the server config file. '
|
1939 |
+
. 'If config file does not exist, you must first create it.', $this->domain) . '</strong>';
|
1940 |
+
$output = sprintf($output, $this->rewriter->get_wp_config_file());
|
1941 |
+
}
|
1942 |
+
|
1943 |
+
$output .= '<br /><br />';
|
1944 |
+
$output .= '<textarea class="code" rows="8" cols="90" readonly="readonly">'
|
1945 |
+
. $rules . '</textarea>';
|
1946 |
+
|
1947 |
+
return $output;
|
1948 |
+
}
|
1949 |
+
|
1950 |
+
private function _show_generated_cache_rewrite_rules()
|
1951 |
+
{
|
1952 |
+
$rules = $this->rewriter->get_generated_cache_rewrite_rules();
|
1953 |
+
$output = '<strong>'
|
1954 |
+
. __('Please update the cache directory config file <code>%s</code> '
|
1955 |
+
. 'manually using auto-generated contents as shown below. '
|
1956 |
+
. 'It is highly recommended that you paste the contents '
|
1957 |
+
. 'at the top of the server config file. '
|
1958 |
+
. 'If config file does not exist, you must first create it.', $this->domain)
|
1959 |
+
. '</strong>';
|
1960 |
+
$output = sprintf($output, $this->rewriter->get_cache_config_file());
|
1961 |
+
|
1962 |
+
$output .= '<br /><br />';
|
1963 |
+
$output .= '<textarea class="code" rows="8" cols="90" readonly="readonly">'
|
1964 |
+
. $rules . '</textarea>';
|
1965 |
+
|
1966 |
+
return $output;
|
1967 |
+
}
|
1968 |
+
|
1969 |
+
private function _get_rewrite_rules_error($result, $config_file)
|
1970 |
+
{
|
1971 |
+
$error = '';
|
1972 |
+
|
1973 |
+
if ('put' === $result || false === $result)
|
1974 |
+
{
|
1975 |
+
// the write process failed for some reasons
|
1976 |
+
$error = false == self::is_nginx()
|
1977 |
+
? sprintf(
|
1978 |
+
'<strong style="color:red">' . __('Error') . ':</strong> '
|
1979 |
+
. __('There was an error writing to server config file <code>%s</code>. '
|
1980 |
+
. 'Please make sure that it is writable and try again, '
|
1981 |
+
. 'or you can manually update the config file '
|
1982 |
+
. 'using auto-generated contents as shown below.', $this->domain),
|
1983 |
+
$config_file)
|
1984 |
+
: '<strong style="color:red">' . __('Error') . ':</strong> '
|
1985 |
+
. __('There was an error writing to your Nginx config file. '
|
1986 |
+
. 'Please make sure that it is writable and try again, '
|
1987 |
+
. 'or you can manually update the config file '
|
1988 |
+
. 'using auto-generated contents as shown below.', $this->domain);
|
1989 |
+
}
|
1990 |
+
else if ('write_file' === $result)
|
1991 |
+
{
|
1992 |
+
// config file is found, but could not be written to
|
1993 |
+
$error = sprintf(
|
1994 |
+
'<strong style="color:red">' . __('Error') . ':</strong> '
|
1995 |
+
. __('The server config file <code>%s</code> '
|
1996 |
+
. 'is not writable, please manually update it '
|
1997 |
+
. 'using auto-generated contents as shown below.', $this->domain),
|
1998 |
+
$config_file
|
1999 |
+
);
|
2000 |
+
}
|
2001 |
+
else if ('exists_dir' === $result || 'write_dir' === $result)
|
2002 |
+
{
|
2003 |
+
// config file is not found, and cache dir does not
|
2004 |
+
// exist or is not writable
|
2005 |
+
$error = sprintf(
|
2006 |
+
'<strong style="color:red">' . __('Error') . ':</strong> '
|
2007 |
+
. __('The server config file <code>%s</code> '
|
2008 |
+
. 'does not exist and could not be automatically created, '
|
2009 |
+
. 'please manually create it using auto-generated '
|
2010 |
+
. 'contents as shown below.', $this->domain),
|
2011 |
+
$config_file
|
2012 |
+
);
|
2013 |
+
}
|
2014 |
+
|
2015 |
+
return $error;
|
2016 |
+
}
|
2017 |
+
|
2018 |
+
/**
|
2019 |
+
* Creates config file for Minify library based on saved settings
|
2020 |
+
*
|
2021 |
+
* The plugin will try to create/update the `config.php` file that Minify
|
2022 |
+
* library uses using saved settings in BWP Minify's admin page. If the
|
2023 |
+
* config file is not found or not writable, admin will be given the option
|
2024 |
+
* to copy the file's contents and create/update the file manually.
|
2025 |
+
*
|
2026 |
+
* @since 1.3.0
|
2027 |
+
* @return bool|string true if config file is writable or the contents to
|
2028 |
+
* be written to the config file if not writable. Returns 'config'
|
2029 |
+
* if `config.php` file is missing or 'write' if the write process
|
2030 |
+
* fails.
|
2031 |
+
*/
|
2032 |
+
function create_minify_config_file()
|
2033 |
+
{
|
2034 |
+
$options = $this->options;
|
2035 |
+
|
2036 |
+
// prepare config entry that can be changed
|
2037 |
+
$min_cachePath = empty($options['input_minpath'])
|
2038 |
+
&& empty($options['input_cache_dir'])
|
2039 |
+
? 'dirname(dirname(__FILE__)) . \'/cache\''
|
2040 |
+
: "'" . untrailingslashit($this->get_cache_dir()) . "'";
|
2041 |
+
|
2042 |
+
$min_cacheFileLocking = 'yes' == $options['enable_cache_file_lock']
|
2043 |
+
? 'true' : 'false';
|
2044 |
+
$min_bubbleCssImports = 'yes' == $options['enable_css_bubble']
|
2045 |
+
? 'true' : 'false';
|
2046 |
+
$min_allowDebugFlag = 'yes' == $options['enable_debug']
|
2047 |
+
? 'true' : 'false';
|
2048 |
+
$min_errorLogger = 'yes' == $options['enable_debug']
|
2049 |
+
? 'true' : 'false';
|
2050 |
+
$min_maxAge = (int) $options['input_maxage'] * (int) $options['select_time_type'];
|
2051 |
+
|
2052 |
+
$doc_root = $this->get_doc_root();
|
2053 |
+
$min_documentRoot = $_SERVER['DOCUMENT_ROOT'] != $doc_root
|
2054 |
+
? "'" . untrailingslashit($this->get_doc_root()) . "'"
|
2055 |
+
: "''";
|
2056 |
+
|
2057 |
+
$configs = array(
|
2058 |
+
'min_enableBuilder' => 'false',
|
2059 |
+
'min_builderPassword' => "'admin'",
|
2060 |
+
'min_errorLogger' => $min_errorLogger,
|
2061 |
+
'min_allowDebugFlag' => $min_allowDebugFlag,
|
2062 |
+
'min_cachePath' => $min_cachePath,
|
2063 |
+
'min_documentRoot' => $min_documentRoot,
|
2064 |
+
'min_cacheFileLocking' => $min_cacheFileLocking,
|
2065 |
+
'min_serveOptions[\'bubbleCssImports\']' => $min_bubbleCssImports,
|
2066 |
+
'min_serveOptions[\'maxAge\']' => $min_maxAge,
|
2067 |
+
'min_serveOptions[\'minApp\'][\'groupsOnly\']' => 'false',
|
2068 |
+
'min_symlinks' => 'array()',
|
2069 |
+
'min_uploaderHoursBehind' => '0',
|
2070 |
+
'min_libPath' => 'dirname(__FILE__) . \'/lib\'',
|
2071 |
+
'ini_set(\'zlib.output_compression\', \'0\')' => ''
|
2072 |
+
);
|
2073 |
+
|
2074 |
+
$config_lines = array();
|
2075 |
+
foreach ($configs as $config_key => $config_value)
|
2076 |
+
{
|
2077 |
+
if (false === strpos($config_key, 'ini_set'))
|
2078 |
+
$config_lines[] = '$' . $config_key . ' = ' . $config_value . ';';
|
2079 |
+
else
|
2080 |
+
$config_lines[] = $config_key . ';';
|
2081 |
+
}
|
2082 |
+
|
2083 |
+
$lines = implode("\n", $config_lines) . "\n";
|
2084 |
+
$lines .= '// auto-generated on ' . current_time('mysql') . "\n";
|
2085 |
+
|
2086 |
+
$min_dir = $this->get_min_dir();
|
2087 |
+
if (false == $min_dir)
|
2088 |
+
// Minify directory can not be found, warn admin
|
2089 |
+
return 'config';
|
2090 |
+
|
2091 |
+
$config_file = $min_dir . 'config.php';
|
2092 |
+
$current_lines = @file($config_file);
|
2093 |
+
|
2094 |
+
if (is_writable($config_file) && false !== $current_lines)
|
2095 |
+
{
|
2096 |
+
// write Minify config variable to `config.php` file, but try not
|
2097 |
+
// to overwrite current config variables (put at the end of the
|
2098 |
+
// file if needed)
|
2099 |
+
$last_line = '';
|
2100 |
+
$begin = $end = false;
|
2101 |
+
foreach ($current_lines as $line_number => $line)
|
2102 |
+
{
|
2103 |
+
if (false !== strpos($line, '$min_enableBuilder'))
|
2104 |
+
$begin = $line_number;
|
2105 |
+
|
2106 |
+
if (false !== strpos($line, '// auto-generated on'))
|
2107 |
+
{
|
2108 |
+
$end = $line_number;
|
2109 |
+
$last_line = $line;
|
2110 |
+
}
|
2111 |
+
}
|
2112 |
+
|
2113 |
+
$current_lines = implode('', $current_lines);
|
2114 |
+
if (false === $begin || false === $end)
|
2115 |
+
{
|
2116 |
+
// if we could not find the begin OR end, add config variables to
|
2117 |
+
// the end of the config file
|
2118 |
+
$current_lines .= "\n";
|
2119 |
+
$current_lines .= $lines;
|
2120 |
+
}
|
2121 |
+
else
|
2122 |
+
{
|
2123 |
+
$begin = strpos($current_lines, '$min_enableBuilder');
|
2124 |
+
$end = strpos($current_lines, '// auto-generated on');
|
2125 |
+
$length = $end > $begin ? $end - $begin + strlen($last_line) : 0;
|
2126 |
+
|
2127 |
+
$current_lines = substr_replace($current_lines, $lines, $begin, $length);
|
2128 |
+
}
|
2129 |
+
|
2130 |
+
if (false === @file_put_contents($config_file, $current_lines))
|
2131 |
+
return 'put';
|
2132 |
+
else
|
2133 |
+
return true;
|
2134 |
+
}
|
2135 |
+
else
|
2136 |
+
{
|
2137 |
+
// return the contents of the config file for manual update
|
2138 |
+
return $lines;
|
2139 |
+
}
|
2140 |
+
}
|
2141 |
+
|
2142 |
+
/**
|
2143 |
+
* Checks whether config files needs to be rewritten
|
2144 |
+
*
|
2145 |
+
* This action should be called by `upgrader_process_complete` action hook
|
2146 |
+
* if this plugin is updated individually or in bulk.
|
2147 |
+
*
|
2148 |
+
* @since 1.3.0
|
2149 |
+
*/
|
2150 |
+
function check_config_file($upgrader, $data)
|
2151 |
+
{
|
2152 |
+
if (!isset($data['type']) || !isset($data['action'])
|
2153 |
+
|| 'plugin' != $data['type'] || 'update' != $data['action']
|
2154 |
+
) {
|
2155 |
+
// if this is not a plugin update
|
2156 |
+
return;
|
2157 |
+
}
|
2158 |
+
|
2159 |
+
if ((isset($data['plugin']) && false === strpos($data['plugin'], 'bwp-minify/'))
|
2160 |
+
|| (isset($data['plugins']) && !in_array('bwp-minify/bwp-minify.php', $data['plugins']))
|
2161 |
+
) {
|
2162 |
+
// if BWP Minify is not being updated, individually or in bulk
|
2163 |
+
return;
|
2164 |
+
}
|
2165 |
+
|
2166 |
+
// if user is updating this plugin, try to re-generate `config.php`
|
2167 |
+
// file silently, we do not handle any error at this stage
|
2168 |
+
$result = $this->create_minify_config_file();
|
2169 |
+
|
2170 |
+
// if friendly minify url is enabled, add rewrite rules to server
|
2171 |
+
// config file if needed, suppress any error message
|
2172 |
+
if ('yes' == $this->options['enable_fly_min'])
|
2173 |
+
$this->_add_rewrite_rules();
|
2174 |
+
}
|
2175 |
+
|
2176 |
+
private function _flush_cache($cache_dir = '')
|
2177 |
+
{
|
2178 |
+
$deleted = 0;
|
2179 |
+
$cache_dir = !empty($cache_dir) ? $cache_dir : $this->get_cache_dir();
|
2180 |
+
$cache_dir = trailingslashit($cache_dir);
|
2181 |
+
|
2182 |
+
if (is_dir($cache_dir))
|
2183 |
+
{
|
2184 |
+
if ($dh = opendir($cache_dir))
|
2185 |
+
{
|
2186 |
+
while (($file = readdir($dh)) !== false)
|
2187 |
+
{
|
2188 |
+
if (preg_match('/^minify_[a-z0-9\\.=_,]+(\.gz)?$/ui', $file)
|
2189 |
+
|| preg_match('/^minify-b\d+-[a-z0-9-_.]+(\.gz)?$/ui', $file)
|
2190 |
+
) {
|
2191 |
+
$deleted += true === @unlink($cache_dir . $file)
|
2192 |
+
? 1 : 0;
|
2193 |
+
}
|
2194 |
+
}
|
2195 |
+
closedir($dh);
|
2196 |
+
}
|
2197 |
+
}
|
2198 |
+
|
2199 |
+
return $deleted;
|
2200 |
+
}
|
2201 |
+
|
2202 |
+
function parse_positions()
|
2203 |
+
{
|
2204 |
+
$positions = array(
|
2205 |
+
'header' => $this->options['input_header'],
|
2206 |
+
'footer' => $this->options['input_footer'],
|
2207 |
+
'direct' => $this->options['input_direct'],
|
2208 |
+
'ignore' => $this->options['input_ignore'],
|
2209 |
+
'oblivion' => $this->options['input_oblivion'],
|
2210 |
+
'style_direct' => $this->options['input_style_direct'],
|
2211 |
+
'style_ignore' => $this->options['input_style_ignore'],
|
2212 |
+
'style_oblivion' => $this->options['input_style_oblivion']
|
2213 |
+
);
|
2214 |
+
|
2215 |
+
foreach ($positions as $key => &$position)
|
2216 |
+
{
|
2217 |
+
if (!empty($position))
|
2218 |
+
{
|
2219 |
+
$position = preg_split('/\r\n|[\r\n]/', $position);
|
2220 |
+
$position = array_map('trim', $position);
|
2221 |
+
}
|
2222 |
+
$filter = false === strpos($key, 'style_') ? '_script_' : '_';
|
2223 |
+
$position = apply_filters('bwp_minify' . $filter . $key, $position);
|
2224 |
+
}
|
2225 |
+
|
2226 |
+
$this->print_positions = $positions;
|
2227 |
+
}
|
2228 |
+
|
2229 |
+
function get_wp_base()
|
2230 |
+
{
|
2231 |
+
$blog_path = $this->_get_blog_path();
|
2232 |
+
$base = parse_url(home_url());
|
2233 |
+
$base = isset($base['path']) ? trailingslashit($base['path']) : '/';
|
2234 |
+
|
2235 |
+
return !empty($blog_path)
|
2236 |
+
? preg_replace('#' . $blog_path . '#ui', '/', $base, 1)
|
2237 |
+
: $base;
|
2238 |
+
}
|
2239 |
+
|
2240 |
+
/**
|
2241 |
+
* Gets a base to prepend relative media sources
|
2242 |
+
*
|
2243 |
+
* The base is deduced from siteurl (the folder where actual WordPress
|
2244 |
+
* files are located.), so if WP files are located in /blog instead of
|
2245 |
+
* root, the base would be `blog`. This is different from what WordPress
|
2246 |
+
* calls base as it is the URL path coming from the http host and not
|
2247 |
+
* the path between home_url and site_url.
|
2248 |
+
*
|
2249 |
+
* @uses get_site_option to add support for Multisite
|
2250 |
+
* @return void
|
2251 |
+
*/
|
2252 |
+
function get_base()
|
2253 |
+
{
|
2254 |
+
$site_url = get_site_option('siteurl');
|
2255 |
+
$base = trim(preg_replace('#https?://[^/]+#ui', '', $site_url), '/');
|
2256 |
+
|
2257 |
+
// @since 1.3.0 - guess min dir to check for any dir that we have to
|
2258 |
+
// remove from the base
|
2259 |
+
$this->_guess_min_dir();
|
2260 |
+
|
2261 |
+
$this->base = !empty($this->remove_from_base)
|
2262 |
+
? preg_replace('#^' . $this->remove_from_base . '/?#ui', '', $base, 1)
|
2263 |
+
: $base;
|
2264 |
+
}
|
2265 |
+
|
2266 |
+
/**
|
2267 |
+
* Gets the buster used to invalidate a cached Minify string
|
2268 |
+
*
|
2269 |
+
* @return string
|
2270 |
+
*/
|
2271 |
+
function get_buster($type)
|
2272 |
+
{
|
2273 |
+
$buster = '';
|
2274 |
+
|
2275 |
+
switch ($type)
|
2276 |
+
{
|
2277 |
+
case 'mtime':
|
2278 |
+
$cache_dir = trailingslashit($this->get_cache_dir());
|
2279 |
+
if (file_exists($cache_dir))
|
2280 |
+
$buster = filemtime($cache_dir);
|
2281 |
+
break;
|
2282 |
+
|
2283 |
+
case 'wpver':
|
2284 |
+
$buster = $this->wp_ver;
|
2285 |
+
break;
|
2286 |
+
|
2287 |
+
case 'tver':
|
2288 |
+
if (function_exists('wp_get_theme'))
|
2289 |
+
{
|
2290 |
+
$theme = wp_get_theme();
|
2291 |
+
if ($theme && $theme instanceof WP_Theme)
|
2292 |
+
{
|
2293 |
+
$version = $theme->get('Version');
|
2294 |
+
if (!empty($version))
|
2295 |
+
$buster = $version;
|
2296 |
+
}
|
2297 |
+
}
|
2298 |
+
else
|
2299 |
+
{
|
2300 |
+
$theme = get_theme_data(STYLESHEETPATH . '/style.css');
|
2301 |
+
if (!empty($theme['Version']))
|
2302 |
+
$buster = $theme['Version'];
|
2303 |
+
}
|
2304 |
+
break;
|
2305 |
+
|
2306 |
+
case 'custom':
|
2307 |
+
$buster = $this->options['input_custom_buster'];
|
2308 |
+
break;
|
2309 |
+
|
2310 |
+
case 'none':
|
2311 |
+
default:
|
2312 |
+
if (is_admin())
|
2313 |
+
$buster = __('empty', $this->domain);
|
2314 |
+
break;
|
2315 |
+
}
|
2316 |
+
|
2317 |
+
return apply_filters('bwp_minify_get_buster', $buster);
|
2318 |
+
}
|
2319 |
+
|
2320 |
+
/**
|
2321 |
+
* Checks whether the media files have been included in Minify string or not
|
2322 |
+
*
|
2323 |
+
* @return bool
|
2324 |
+
*/
|
2325 |
+
function is_in($handle, $position = 'header')
|
2326 |
+
{
|
2327 |
+
if (!isset($this->print_positions[$position])
|
2328 |
+
|| !is_array($this->print_positions[$position])
|
2329 |
+
) {
|
2330 |
+
return false;
|
2331 |
+
}
|
2332 |
+
|
2333 |
+
if (in_array($handle, $this->print_positions[$position]))
|
2334 |
+
return true;
|
2335 |
+
}
|
2336 |
+
|
2337 |
+
/**
|
2338 |
+
* Checks if a style has inline styles to print
|
2339 |
+
*
|
2340 |
+
* @return bool
|
2341 |
+
* @see wp-includes/functions.wp-styles.php
|
2342 |
+
*/
|
2343 |
+
private static function has_inline($handle)
|
2344 |
+
{
|
2345 |
+
global $wp_styles;
|
2346 |
+
|
2347 |
+
if (isset($wp_styles->registered[$handle]->extra['after']))
|
2348 |
+
return true;
|
2349 |
+
|
2350 |
+
return false;
|
2351 |
+
}
|
2352 |
+
|
2353 |
+
/**
|
2354 |
+
* Check if a sciprt has been localized using wp_localize_script()
|
2355 |
+
*
|
2356 |
+
* @return bool
|
2357 |
+
* @see wp-includes/functions.wp-scripts.php
|
2358 |
+
*/
|
2359 |
+
private static function is_l10n($handle)
|
2360 |
+
{
|
2361 |
+
global $wp_scripts;
|
2362 |
+
|
2363 |
+
// Since WordPress 3.3, 'l10n' has been changed into 'data'
|
2364 |
+
if (isset($wp_scripts->registered[$handle]->extra['l10n'])
|
2365 |
+
|| isset($wp_scripts->registered[$handle]->extra['data']))
|
2366 |
+
return true;
|
2367 |
+
|
2368 |
+
return false;
|
2369 |
+
}
|
2370 |
+
|
2371 |
+
/**
|
2372 |
+
* Checks if media source is local
|
2373 |
+
*
|
2374 |
+
* @return bool
|
2375 |
+
*/
|
2376 |
+
function is_local($src)
|
2377 |
+
{
|
2378 |
+
// prepend scheme to scheme-less URLs, to make parse_url work
|
2379 |
+
if (0 === strpos($src, '//')) {
|
2380 |
+
$src = 'http:' . $src;
|
2381 |
+
}
|
2382 |
+
|
2383 |
+
$url = @parse_url($src);
|
2384 |
+
$blog_url = @parse_url(home_url());
|
2385 |
+
if (false === $url)
|
2386 |
+
return false;
|
2387 |
+
|
2388 |
+
if (isset($url['scheme']))
|
2389 |
+
{
|
2390 |
+
// this should be an absolute URL
|
2391 |
+
// @since 1.3.0 consider sub-domain external for now
|
2392 |
+
if (0 <> strcmp($url['host'], $blog_url['host']))
|
2393 |
+
return false;
|
2394 |
+
|
2395 |
+
return true;
|
2396 |
+
}
|
2397 |
+
else // Probably a relative link
|
2398 |
+
return true;
|
2399 |
+
}
|
2400 |
+
|
2401 |
+
/**
|
2402 |
+
* Make sure the source is valid
|
2403 |
+
*
|
2404 |
+
* @since 1.0.3
|
2405 |
+
* @return bool
|
2406 |
+
*/
|
2407 |
+
function is_source_static($src)
|
2408 |
+
{
|
2409 |
+
// @since 1.3.0 check for query string in `$src`
|
2410 |
+
if (false !== strpos($src, '?'))
|
2411 |
+
{
|
2412 |
+
$src = explode('?', $src);
|
2413 |
+
$src = $src[0];
|
2414 |
+
}
|
2415 |
+
|
2416 |
+
// Source that doesn't have .css or .js extesion is dynamic
|
2417 |
+
if (!preg_match('#.*\.(css|js)$#ui', $src))
|
2418 |
+
return false;
|
2419 |
+
|
2420 |
+
// Source that contains =, & is dynamic
|
2421 |
+
// @since 1.3.0 ? is considered static
|
2422 |
+
if (strpos($src, '=') === false && strpos($src, '&') === false)
|
2423 |
+
return true;
|
2424 |
+
|
2425 |
+
return false;
|
2426 |
+
}
|
2427 |
+
|
2428 |
+
/**
|
2429 |
+
* Formats media source before adding to Minify string
|
2430 |
+
*
|
2431 |
+
* @return string
|
2432 |
+
*/
|
2433 |
+
function process_media_source($src = '')
|
2434 |
+
{
|
2435 |
+
$src = trim($src);
|
2436 |
+
|
2437 |
+
// handle possible scheme-less urls
|
2438 |
+
if ('//' === substr($src, 0, 2))
|
2439 |
+
$src = 'http:' . $src;
|
2440 |
+
|
2441 |
+
// Absolute url
|
2442 |
+
if (0 === strpos($src, 'http'))
|
2443 |
+
{
|
2444 |
+
// handle both http and https, can't assume `$src` is properly setup
|
2445 |
+
// with appropriate scheme
|
2446 |
+
$src = preg_replace('#https?://[^/]+#ui', '', $src);
|
2447 |
+
|
2448 |
+
// we need to remove blog path from `$src`, this is for
|
2449 |
+
// compatibility with sub-directory multisite installation
|
2450 |
+
$blog_path = $this->_get_blog_path();
|
2451 |
+
$src = !empty($blog_path)
|
2452 |
+
? preg_replace('#' . $blog_path . '#ui', '/', $src, 1)
|
2453 |
+
: $src;
|
2454 |
+
|
2455 |
+
// need to remove anything that needs removing from the base
|
2456 |
+
// because the current url also contains the base
|
2457 |
+
$src = !empty($this->remove_from_base)
|
2458 |
+
? preg_replace('#^/' . $this->remove_from_base . '/#ui', '/', $src, 1)
|
2459 |
+
: $src;
|
2460 |
+
}
|
2461 |
+
else if ('/' === substr($src, 0, 1) && !empty($this->base))
|
2462 |
+
{
|
2463 |
+
// root relative url
|
2464 |
+
if (false !== strpos($src, 'wp-includes')
|
2465 |
+
|| false !== strpos($src, 'wp-admin')
|
2466 |
+
|| (0 === strpos($src, '/wp-content')
|
2467 |
+
&& false !== strpos(content_url(), '/' . $this->base . '/'))
|
2468 |
+
) {
|
2469 |
+
// Add base for relative media source. Because `wp_content`
|
2470 |
+
// folder can be moved away from where WordPress's files are
|
2471 |
+
// located, only add a base before `wp-content` if it is
|
2472 |
+
// needed.
|
2473 |
+
$src = $this->base . $src;
|
2474 |
+
}
|
2475 |
+
}
|
2476 |
+
|
2477 |
+
// @since 1.3.0 strip query string from `$src` if any
|
2478 |
+
if (false !== strpos($src, '?'))
|
2479 |
+
{
|
2480 |
+
$src = explode('?', $src);
|
2481 |
+
$src = $src[0];
|
2482 |
+
}
|
2483 |
+
|
2484 |
+
$src = str_replace('./', '/', $src);
|
2485 |
+
$src = str_replace('\\', '/', $src);
|
2486 |
+
$src = preg_replace('#[/]+#iu', '/', $src);
|
2487 |
+
$src = ltrim($src, '/');
|
2488 |
+
|
2489 |
+
return $src;
|
2490 |
+
}
|
2491 |
+
|
2492 |
+
/**
|
2493 |
+
* Gets the Minify string to be printed
|
2494 |
+
*
|
2495 |
+
* @return string
|
2496 |
+
*/
|
2497 |
+
function get_minify_src($string, $group_handle = '')
|
2498 |
+
{
|
2499 |
+
if (empty($string))
|
2500 |
+
return '';
|
2501 |
+
|
2502 |
+
$buster = !empty($this->buster) ? '&ver=' . $this->buster : '';
|
2503 |
+
$debug_flag = 'yes' == $this->options['enable_debug']
|
2504 |
+
? '&debug' : '';
|
2505 |
+
|
2506 |
+
$min_url = $this->min_url;
|
2507 |
+
|
2508 |
+
return apply_filters('bwp_minify_get_src',
|
2509 |
+
trailingslashit($min_url) . '?f=' . $string . $buster . $debug_flag,
|
2510 |
+
$string, $group_handle, $this->buster, $min_url
|
2511 |
+
);
|
2512 |
+
}
|
2513 |
+
|
2514 |
+
function get_minify_tag($type, $group, $group_handle)
|
2515 |
+
{
|
2516 |
+
if (!isset($group['string']))
|
2517 |
+
return '';
|
2518 |
+
|
2519 |
+
$original_string = implode(',', $group['string']);
|
2520 |
+
$string = $this->get_minify_src($original_string, $group_handle);
|
2521 |
+
|
2522 |
+
$media = isset($group['media']) ? $group['media'] : false;
|
2523 |
+
$title = isset($group['alt']) ? $group['alt'] : false;
|
2524 |
+
$if = isset($group['if']) ? $group['if'] : false;
|
2525 |
+
|
2526 |
+
switch ($type)
|
2527 |
+
{
|
2528 |
+
case 'script':
|
2529 |
+
$return = "<script type='text/javascript' src='"
|
2530 |
+
. esc_url($string)
|
2531 |
+
. "'></script>\r\n";
|
2532 |
+
break;
|
2533 |
+
|
2534 |
+
case 'style':
|
2535 |
+
$return = "<link rel='stylesheet' id='"
|
2536 |
+
. esc_attr($group_handle) . "-group-css' href='"
|
2537 |
+
. esc_url($string) . "' type='text/css' media='"
|
2538 |
+
. esc_attr($media) . "' />\r\n";
|
2539 |
+
|
2540 |
+
if ($title)
|
2541 |
+
{
|
2542 |
+
$return = "<link rel='alternate stylesheet' id='"
|
2543 |
+
. esc_attr($group_handle) . "-group-css' title='"
|
2544 |
+
. esc_attr($title) . "' href='"
|
2545 |
+
. esc_url($string) . "' type='text/css' media='"
|
2546 |
+
. esc_attr($media) . "' />\r\n";
|
2547 |
+
}
|
2548 |
+
break;
|
2549 |
+
}
|
2550 |
+
|
2551 |
+
if ($if)
|
2552 |
+
$return = "<!--[if " . esc_html($if) . "]>\r\n" . $return . "<![endif]-->\r\n";
|
2553 |
+
|
2554 |
+
return apply_filters('bwp_minify_get_tag', $return, $string, $type, $group);
|
2555 |
+
}
|
2556 |
+
|
2557 |
+
function minify_item($src)
|
2558 |
+
{
|
2559 |
+
return $this->get_minify_src($this->process_media_source($src));
|
2560 |
+
}
|
2561 |
+
|
2562 |
+
function minify_stylesheet($src, $context)
|
2563 |
+
{
|
2564 |
+
if ($context == 'stylesheet_url')
|
2565 |
+
return $this->minify_item($src);
|
2566 |
+
|
2567 |
+
return $src;
|
2568 |
+
}
|
2569 |
+
|
2570 |
+
function init_todo_item($handle, $type = 'script')
|
2571 |
+
{
|
2572 |
+
global $wp_scripts, $wp_styles;
|
2573 |
+
|
2574 |
+
$media = 'script' == $type ? $wp_scripts : $wp_styles;
|
2575 |
+
$prefix = 'script' == $type ? '' : 'style_';
|
2576 |
+
$item = $media->registered[$handle];
|
2577 |
+
|
2578 |
+
// if $src contains minify url and/or the buster, we need to strip them
|
2579 |
+
$item->src = str_replace($this->min_url . '?f=', '', $item->src);
|
2580 |
+
$item->src = str_replace('&ver=' . $this->buster, '', $item->src);
|
2581 |
+
$src = trim($item->src);
|
2582 |
+
|
2583 |
+
$todo_item = array(
|
2584 |
+
'position' => 'dummy', // 'dummy', 'header', 'footer', or 'footer{n}'
|
2585 |
+
'min' => true, // expect to be minified
|
2586 |
+
'wp' => false, // expect to be handled by WP
|
2587 |
+
'forget' => false, // put into oblivion or not
|
2588 |
+
'depend' => false,
|
2589 |
+
'group' => false
|
2590 |
+
);
|
2591 |
+
|
2592 |
+
// look for dependencies of this item
|
2593 |
+
$deps = $item->deps;
|
2594 |
+
if ($deps && is_array($deps) && 0 < sizeof($deps)) {
|
2595 |
+
$todo_item['depend'] = $deps;
|
2596 |
+
}
|
2597 |
+
|
2598 |
+
if (empty($src))
|
2599 |
+
return $todo_item;
|
2600 |
+
|
2601 |
+
if ($this->is_in($handle, $prefix . 'oblivion'))
|
2602 |
+
{
|
2603 |
+
// this item is put into oblivion (forget)
|
2604 |
+
$todo_item['min'] = false;
|
2605 |
+
$todo_item['forget'] = true;
|
2606 |
+
$todo_item['position'] = 'oblivion';
|
2607 |
+
$todo_item['src'] = $src;
|
2608 |
+
}
|
2609 |
+
else if (('all' != $this->print_positions[$prefix . 'allowed']
|
2610 |
+
&& !$this->is_in($handle, $prefix . 'allowed'))
|
2611 |
+
|| !$this->is_source_static($src) || !$this->is_local($src)
|
2612 |
+
) {
|
2613 |
+
// If this item is specifically disallowed to be minified
|
2614 |
+
// OR if this item is dynamic or external, no minify
|
2615 |
+
$todo_item['min'] = false;
|
2616 |
+
}
|
2617 |
+
else if ($this->is_in($handle, $prefix . 'ignore')
|
2618 |
+
|| $this->is_in($handle, $prefix . 'direct')
|
2619 |
+
) {
|
2620 |
+
// if this item belongs to 'ignore', no minify is needed
|
2621 |
+
if ($this->is_in($handle, $prefix . 'ignore'))
|
2622 |
+
$todo_item['min'] = false;
|
2623 |
+
|
2624 |
+
// let WordPress handle the output
|
2625 |
+
$todo_item['wp'] = true;
|
2626 |
+
}
|
2627 |
+
|
2628 |
+
return $todo_item;
|
2629 |
+
}
|
2630 |
+
|
2631 |
+
/**
|
2632 |
+
* Checks if a group is a dependency of some groups
|
2633 |
+
*
|
2634 |
+
* @param $group string the group handle that needs checking
|
2635 |
+
* @param $groups array a list of group handles to check against
|
2636 |
+
* @param $type string either 'script' or 'style'
|
2637 |
+
* @return bool
|
2638 |
+
*/
|
2639 |
+
function is_a_dependency($group, $groups, $type)
|
2640 |
+
{
|
2641 |
+
if (0 == sizeof($groups))
|
2642 |
+
return false;
|
2643 |
+
|
2644 |
+
$result = false;
|
2645 |
+
$min_groups = 'script' == $type ? $this->min_scripts : $this->min_styles;
|
2646 |
+
|
2647 |
+
foreach ($groups as $_group => $active)
|
2648 |
+
{
|
2649 |
+
// if it's the same group handle no need to check
|
2650 |
+
if ($group == $_group)
|
2651 |
+
continue;
|
2652 |
+
|
2653 |
+
$group_deps = $min_groups[$_group]['depend'];
|
2654 |
+
if (array_key_exists($group, $group_deps))
|
2655 |
+
return true;
|
2656 |
+
else
|
2657 |
+
$result = $this->is_a_dependency($group, $group_deps, $type);
|
2658 |
+
|
2659 |
+
if ($result)
|
2660 |
+
return true;
|
2661 |
+
}
|
2662 |
+
|
2663 |
+
return $result;
|
2664 |
+
}
|
2665 |
+
|
2666 |
+
function get_dependencies($item, $type)
|
2667 |
+
{
|
2668 |
+
$deps = array();
|
2669 |
+
$todo = 'script' == $type ? $this->todo_scripts : $this->todo_styles;
|
2670 |
+
|
2671 |
+
foreach ($item['depend'] as $dep)
|
2672 |
+
{
|
2673 |
+
if (isset($todo[$dep]))
|
2674 |
+
$deps[$dep] = $todo[$dep];
|
2675 |
+
}
|
2676 |
+
|
2677 |
+
return $deps;
|
2678 |
+
}
|
2679 |
+
|
2680 |
+
/**
|
2681 |
+
* Builds a list of printable groups from an internal _todo_ list
|
2682 |
+
*
|
2683 |
+
* A group can be one of the following types:
|
2684 |
+
* 1. A Minify group: files are minified and combined, printed together
|
2685 |
+
* 2. A WP Minify group: files are minified but separately printed
|
2686 |
+
* 3. A WP group: files are NOT minified and separately printed
|
2687 |
+
*
|
2688 |
+
* Apply to a Minify group: If the number of files per group reaches a
|
2689 |
+
* limit (by default the limit is 10) this plugin will split the minify
|
2690 |
+
* string into an appropriate number of <link> tags. On some server if the
|
2691 |
+
* minify string is too long it could trigger a 500 Internal Server error.
|
2692 |
+
*
|
2693 |
+
* @since 1.3.0
|
2694 |
+
*/
|
2695 |
+
function minify($todo, $type, $recursive = false)
|
2696 |
+
{
|
2697 |
+
$group_deps = array();
|
2698 |
+
|
2699 |
+
foreach ($todo as $handle => $item)
|
2700 |
+
{
|
2701 |
+
$group_handle = $this->_sanitize_handle($handle);
|
2702 |
+
|
2703 |
+
// only process item that is not in any group yet
|
2704 |
+
if ($item['group'])
|
2705 |
+
{
|
2706 |
+
if ($recursive)
|
2707 |
+
{
|
2708 |
+
// if we are looking for group dependencies, save the
|
2709 |
+
// current item's group
|
2710 |
+
$group_deps[$item['group']] = 1;
|
2711 |
+
}
|
2712 |
+
continue;
|
2713 |
+
}
|
2714 |
+
|
2715 |
+
// when not in recrusive mode, only process actual items
|
2716 |
+
if ('dummy' == $item['position'] && !$recursive)
|
2717 |
+
continue;
|
2718 |
+
|
2719 |
+
// if this item has dependencies, and we're not resolving
|
2720 |
+
// dependencies already OR this is a dummy item, we create group
|
2721 |
+
// for them first
|
2722 |
+
if ((!$recursive || 'dummy' == $item['position'])
|
2723 |
+
&& $item['depend'] && 0 < sizeof($item['depend'])
|
2724 |
+
) {
|
2725 |
+
// recursively resolve dependencies
|
2726 |
+
$deps = $this->get_dependencies($item, $type);
|
2727 |
+
$group_deps = array_merge(
|
2728 |
+
$group_deps,
|
2729 |
+
$this->minify($deps, $type, true)
|
2730 |
+
);
|
2731 |
+
}
|
2732 |
+
|
2733 |
+
// if this is a dummy item, no processing
|
2734 |
+
if ('dummy' == $item['position'])
|
2735 |
+
continue;
|
2736 |
+
|
2737 |
+
if ('script' == $type)
|
2738 |
+
$this->minify_script($handle, $item, $group_handle, $group_deps);
|
2739 |
+
else
|
2740 |
+
$this->minify_style($handle, $item, $group_handle, $group_deps);
|
2741 |
+
|
2742 |
+
if (!$recursive)
|
2743 |
+
$group_deps = array();
|
2744 |
+
}
|
2745 |
+
|
2746 |
+
return $group_deps;
|
2747 |
+
}
|
2748 |
+
|
2749 |
+
/**
|
2750 |
+
* Builds a list of internal $todo_styles from WP's $todo
|
2751 |
+
*
|
2752 |
+
* @since 1.3.0
|
2753 |
+
*/
|
2754 |
+
function add_styles($todo)
|
2755 |
+
{
|
2756 |
+
global $wp_styles;
|
2757 |
+
|
2758 |
+
// @since 1.3.0 add 'dashicons' to default ignore list
|
2759 |
+
$this->print_positions['style_allowed'] = apply_filters('bwp_minify_allowed_styles', 'all');
|
2760 |
+
|
2761 |
+
foreach ($todo as $handle)
|
2762 |
+
{
|
2763 |
+
$style = $wp_styles->registered[$handle];
|
2764 |
+
$todo_style = $this->init_todo_item($handle, 'style');
|
2765 |
+
|
2766 |
+
if (empty($style->src))
|
2767 |
+
{
|
2768 |
+
if ($todo_styles['depend'])
|
2769 |
+
{
|
2770 |
+
$this->todo_styles[$handle] = $todo_style;
|
2771 |
+
$wp_styles->done[] = $handle;
|
2772 |
+
}
|
2773 |
+
continue;
|
2774 |
+
}
|
2775 |
+
|
2776 |
+
$todo_style['media'] = 'all'; // can be 'all', 'print', etc.
|
2777 |
+
$todo_style['if'] = '';
|
2778 |
+
$todo_style['alt'] = '';
|
2779 |
+
|
2780 |
+
// if this style has different media type, set it
|
2781 |
+
if (!empty($style->args) && 'all' != $style->args)
|
2782 |
+
$todo_style['media'] = trim($style->args);
|
2783 |
+
|
2784 |
+
// if this style needs conditional statement (e.g. IE-specific
|
2785 |
+
// stylesheets)
|
2786 |
+
if (!empty($style->extra['conditional']))
|
2787 |
+
$todo_style['if'] = trim($style->extra['conditional']);
|
2788 |
+
|
2789 |
+
// if this style is an alternate stylesheet (@link
|
2790 |
+
// http://www.w3.org/TR/REC-html40/present/styles.html#h-14.3.1)
|
2791 |
+
if (!empty($style->extra['alt']))
|
2792 |
+
{
|
2793 |
+
$todo_style['alt'] = isset($style->extra['title'])
|
2794 |
+
? trim($style->extra['title'])
|
2795 |
+
: '';
|
2796 |
+
}
|
2797 |
+
|
2798 |
+
// if this style has a RTL version, and the current text direction
|
2799 |
+
// setting is RTL
|
2800 |
+
if ('rtl' === $wp_styles->text_direction
|
2801 |
+
&& !empty($style->extra['rtl'])
|
2802 |
+
) {
|
2803 |
+
if (is_bool($style->extra['rtl'])
|
2804 |
+
|| 'replace' === $style->extra['rtl']
|
2805 |
+
) {
|
2806 |
+
// replace the style's original src with RTL version
|
2807 |
+
$suffix = isset($style->extra['suffix'])
|
2808 |
+
? $style->extra['suffix']
|
2809 |
+
: '';
|
2810 |
+
$rtl_src = str_replace("{$suffix}.css", "-rtl{$suffix}.css", $style->src);
|
2811 |
+
$todo_style['src'] = $rtl_src;
|
2812 |
+
}
|
2813 |
+
else
|
2814 |
+
{
|
2815 |
+
// add a new todo_rtl as a clone of current todo_style and
|
2816 |
+
// make todo_style its dependency
|
2817 |
+
$rtl_src = trim($style->extra['rtl']);
|
2818 |
+
$todo_rtl = $todo_style;
|
2819 |
+
$todo_rtl['src'] = $rtl_src;
|
2820 |
+
$todo_rtl['depend'] = array($handle);
|
2821 |
+
$todo_rtl['min'] = $this->is_source_static($rtl_src) && $this->is_local($rtl_src);
|
2822 |
+
}
|
2823 |
+
}
|
2824 |
+
|
2825 |
+
if (true === $todo_style['forget'])
|
2826 |
+
{
|
2827 |
+
// this style is forgotten, we don't process it, and tell
|
2828 |
+
// WordPress to forget it as well. We also need to detect this
|
2829 |
+
// style as forgotten, the same goes for script
|
2830 |
+
do_action('bwp_minify_processed_style', $handle, $todo_style);
|
2831 |
+
$wp_styles->done[] = $handle;
|
2832 |
+
continue;
|
2833 |
+
}
|
2834 |
+
|
2835 |
+
if (did_action('bwp_minify_after_header_styles'))
|
2836 |
+
{
|
2837 |
+
// if this style is registered after the styles are printed, it is
|
2838 |
+
// expected in footer
|
2839 |
+
$todo_style['position'] = did_action('bwp_minify_after_footer_styles')
|
2840 |
+
? 'footer' . $this->late_style_order
|
2841 |
+
: 'footer';
|
2842 |
+
}
|
2843 |
+
else
|
2844 |
+
{
|
2845 |
+
// this style belongs to header
|
2846 |
+
$todo_style['position'] = 'header';
|
2847 |
+
}
|
2848 |
+
|
2849 |
+
$this->todo_styles[$handle] = $todo_style;
|
2850 |
+
|
2851 |
+
if (!empty($todo_rtl['src']))
|
2852 |
+
{
|
2853 |
+
// this style needs a separate RTL stylesheet
|
2854 |
+
$this->todo_styles[$handle . '_rtl'] = $todo_rtl;
|
2855 |
+
$todo_rtl = false;
|
2856 |
+
}
|
2857 |
+
|
2858 |
+
$wp_styles->done[] = $handle;
|
2859 |
+
}
|
2860 |
+
|
2861 |
+
// start minifying
|
2862 |
+
$this->minify($this->todo_styles, 'style');
|
2863 |
+
|
2864 |
+
// if late styles are found, print them now
|
2865 |
+
if (!empty($this->todo_late_styles))
|
2866 |
+
{
|
2867 |
+
$this->print_styles($this->todo_late_styles);
|
2868 |
+
$this->todo_late_styles = '';
|
2869 |
+
$this->late_style_order++;
|
2870 |
+
}
|
2871 |
+
|
2872 |
+
// no more $todo for WordPress because we have done it all
|
2873 |
+
return array();
|
2874 |
+
}
|
2875 |
+
|
2876 |
+
/**
|
2877 |
+
* Captures very late styles to print later
|
2878 |
+
*
|
2879 |
+
* @since 1.3.0
|
2880 |
+
*/
|
2881 |
+
function add_late_styles()
|
2882 |
+
{
|
2883 |
+
// only procceed if we have finished printing footer styles
|
2884 |
+
if (did_action('bwp_minify_after_footer_styles'))
|
2885 |
+
$this->todo_late_styles = 'footer' . $this->late_style_order;
|
2886 |
+
}
|
2887 |
+
|
2888 |
+
function minify_style($handle, $item, $group_handle, $group_deps)
|
2889 |
+
{
|
2890 |
+
global $wp_styles;
|
2891 |
+
|
2892 |
+
$style = $wp_styles->registered[$handle];
|
2893 |
+
$src = !empty($item['src']) ? $item['src'] : $style->src;
|
2894 |
+
|
2895 |
+
if ($item['min'])
|
2896 |
+
{
|
2897 |
+
// minify is needed
|
2898 |
+
$src = $this->process_media_source($src);
|
2899 |
+
|
2900 |
+
// if WordPress handles this item make sure the $src is
|
2901 |
+
// processed and version is not set
|
2902 |
+
if ($item['wp'])
|
2903 |
+
{
|
2904 |
+
// because WordPress needs original $src, we process the
|
2905 |
+
// original $style->src, not the pre-processed $src.
|
2906 |
+
$style->src = $this->get_minify_src(
|
2907 |
+
$this->process_media_source($style->src),
|
2908 |
+
$group_handle
|
2909 |
+
);
|
2910 |
+
$style->ver = NULL;
|
2911 |
+
// add a new group to hold this item
|
2912 |
+
$this->min_styles[$group_handle] = array(
|
2913 |
+
'depend' => $group_deps,
|
2914 |
+
'handle' => $handle,
|
2915 |
+
'position' => $item['position']
|
2916 |
+
);
|
2917 |
+
$item['group'] = $group_handle;
|
2918 |
+
}
|
2919 |
+
else
|
2920 |
+
{
|
2921 |
+
$group_handle = false;
|
2922 |
+
foreach ($this->min_styles as $_group_handle => $_group)
|
2923 |
+
{
|
2924 |
+
// pick the first available group that is:
|
2925 |
+
// 1. in the same position (header or footer)
|
2926 |
+
// 2. has the same media type
|
2927 |
+
// 3. same conditional type, if is a condiional style
|
2928 |
+
// 4. same alternate title, if is an alternate style
|
2929 |
+
// 5. still has room for more scripts
|
2930 |
+
// but do not take into account group that is
|
2931 |
+
// dependency of this item's own dependencies.
|
2932 |
+
if (isset($_group['string']) && $_group['position'] == $item['position']
|
2933 |
+
&& $this->options['input_maxfiles'] > sizeof($_group['string'])
|
2934 |
+
&& !$this->is_a_dependency($_group_handle, $group_deps, 'style')
|
2935 |
+
) {
|
2936 |
+
$is_same_media = $_group['media'] == $item['media'];
|
2937 |
+
$is_same_if = $_group['if'] == $item['if'];
|
2938 |
+
$is_same_alt = $_group['alt'] == $item['alt'];
|
2939 |
+
if ($is_same_media && $is_same_if && $is_same_alt)
|
2940 |
+
{
|
2941 |
+
$group_handle = $_group_handle;
|
2942 |
+
break;
|
2943 |
+
}
|
2944 |
+
}
|
2945 |
+
}
|
2946 |
+
|
2947 |
+
if ($group_handle)
|
2948 |
+
{
|
2949 |
+
// append to selected group's minify string, only if
|
2950 |
+
// this $src has not been added before
|
2951 |
+
$group = &$this->min_styles[$group_handle];
|
2952 |
+
if (!in_array($src, $group['string']))
|
2953 |
+
{
|
2954 |
+
$group['string'][] = $src;
|
2955 |
+
|
2956 |
+
// make sure we don't self-referencing the
|
2957 |
+
// group_handle within this item's dependencies
|
2958 |
+
if (isset($group_deps[$group_handle]))
|
2959 |
+
unset($group_deps[$group_handle]);
|
2960 |
+
|
2961 |
+
// merge this item's dependencies with the selected
|
2962 |
+
// group's deps.
|
2963 |
+
$group['depend'] = array_merge($group['depend'], $group_deps);
|
2964 |
+
$item['group'] = $group_handle;
|
2965 |
+
}
|
2966 |
+
}
|
2967 |
+
else
|
2968 |
+
{
|
2969 |
+
// otherwise make a new group
|
2970 |
+
$group_handle = $this->_sanitize_handle($handle);
|
2971 |
+
$this->min_styles[$group_handle] = array(
|
2972 |
+
'depend' => $group_deps,
|
2973 |
+
'string' => array($src),
|
2974 |
+
'position' => $item['position'],
|
2975 |
+
'media' => $item['media'],
|
2976 |
+
'alt' => $item['alt'],
|
2977 |
+
'if' => $item['if']
|
2978 |
+
);
|
2979 |
+
$item['group'] = $group_handle;
|
2980 |
+
}
|
2981 |
+
}
|
2982 |
+
}
|
2983 |
+
else
|
2984 |
+
{
|
2985 |
+
// no minify is needed, add new group to hold this item
|
2986 |
+
$this->min_styles[$group_handle] = array(
|
2987 |
+
'depend' => $group_deps,
|
2988 |
+
'handle' => $handle,
|
2989 |
+
'position' => $item['position']
|
2990 |
+
);
|
2991 |
+
$item['group'] = $group_handle;
|
2992 |
+
}
|
2993 |
+
|
2994 |
+
// if this item has inline styles, mark it to process later
|
2995 |
+
if (self::has_inline($handle))
|
2996 |
+
$this->todo_inline_styles[$group_handle][] = $handle;
|
2997 |
+
|
2998 |
+
// update the internal _Todo_ list
|
2999 |
+
$item['src'] = $src;
|
3000 |
+
$this->todo_styles[$handle] = $item;
|
3001 |
+
|
3002 |
+
do_action('bwp_minify_processed_style', $handle, $item);
|
3003 |
+
}
|
3004 |
+
|
3005 |
+
/**
|
3006 |
+
* Prints Minify string for CSS files recursively
|
3007 |
+
*
|
3008 |
+
* This function will traverse all style groups recursively to print styles
|
3009 |
+
* while retaining dependencies. Use actions provided to add other things
|
3010 |
+
* before or after the output.
|
3011 |
+
*
|
3012 |
+
* @since 1.3.0
|
3013 |
+
* @uses $wp_styles
|
3014 |
+
* @return void
|
3015 |
+
*/
|
3016 |
+
function print_styles($position = 'header', $groups = false, $recursive = false)
|
3017 |
+
{
|
3018 |
+
global $wp_styles;
|
3019 |
+
|
3020 |
+
if (!$recursive)
|
3021 |
+
do_action('bwp_minify_before_' . $position . '_styles');
|
3022 |
+
|
3023 |
+
$groups = !$groups ? $this->min_styles : $groups;
|
3024 |
+
$group_position = $position;
|
3025 |
+
|
3026 |
+
foreach ($groups as $group_handle => $group)
|
3027 |
+
{
|
3028 |
+
// if group is already done, do not procceed
|
3029 |
+
if (!empty($this->min_styles[$group_handle]['done']))
|
3030 |
+
continue;
|
3031 |
+
|
3032 |
+
// if this is not the correct position for the group, halt the
|
3033 |
+
// entire loop but return the correct position so the offending group
|
3034 |
+
// can update itself
|
3035 |
+
if ($group['position'] != $position)
|
3036 |
+
if ($recursive)
|
3037 |
+
return $group['position'];
|
3038 |
+
else
|
3039 |
+
continue;
|
3040 |
+
|
3041 |
+
// print dependencies first
|
3042 |
+
$deps = array();
|
3043 |
+
if (0 < sizeof($group['depend']))
|
3044 |
+
{
|
3045 |
+
foreach ($group['depend'] as $dep => $active)
|
3046 |
+
{
|
3047 |
+
if (isset($this->min_styles[$dep]))
|
3048 |
+
$deps[$dep] = $this->min_styles[$dep];
|
3049 |
+
}
|
3050 |
+
$group_position = $this->print_styles($position, $deps, true);
|
3051 |
+
}
|
3052 |
+
|
3053 |
+
// update this group's position if dependencies can not be resolved
|
3054 |
+
// and ignore this group for now
|
3055 |
+
if ($group_position != $position)
|
3056 |
+
{
|
3057 |
+
// this group needs to move to a new position, trigger events
|
3058 |
+
do_action('bwp_minify_moved_group', 'style',
|
3059 |
+
$group_handle,
|
3060 |
+
$group_position
|
3061 |
+
);
|
3062 |
+
$this->min_styles[$group_handle]['position'] = $group_position;
|
3063 |
+
continue;
|
3064 |
+
}
|
3065 |
+
|
3066 |
+
if (isset($group['string']) && 0 < sizeof($group['string']))
|
3067 |
+
{
|
3068 |
+
// if this is a minify string
|
3069 |
+
echo $this->get_minify_tag('style', $group, $group_handle);
|
3070 |
+
// print inline style after if this group has any
|
3071 |
+
if (isset($this->todo_inline_styles[$group_handle]))
|
3072 |
+
$this->print_inline_styles($this->todo_inline_styles[$group_handle]);
|
3073 |
+
}
|
3074 |
+
else if (!empty($group['handle']))
|
3075 |
+
{
|
3076 |
+
// if this should be handled by WordPress
|
3077 |
+
$wp_styles->do_item($group['handle']);
|
3078 |
+
}
|
3079 |
+
|
3080 |
+
$this->min_styles[$group_handle]['done'] = true;
|
3081 |
+
}
|
3082 |
+
|
3083 |
+
if (!$recursive)
|
3084 |
+
{
|
3085 |
+
do_action('bwp_minify_after_' . $position . '_styles');
|
3086 |
+
// save detector's log whenever we finish printing a footer{n} batch
|
3087 |
+
if (false !== strpos($position, 'footer' . $this->late_style_order))
|
3088 |
+
$this->detector->commit_logs();
|
3089 |
+
}
|
3090 |
+
|
3091 |
+
return $group_position;
|
3092 |
+
}
|
3093 |
+
|
3094 |
+
function print_header_styles()
|
3095 |
+
{
|
3096 |
+
$this->print_styles();
|
3097 |
+
|
3098 |
+
// fire an action after header styles have been printed
|
3099 |
+
do_action('bwp_minify_printed_header_styles');
|
3100 |
+
}
|
3101 |
+
|
3102 |
+
function print_footer_styles()
|
3103 |
+
{
|
3104 |
+
$this->print_styles('footer');
|
3105 |
+
|
3106 |
+
// fire an action after footer styles have been print
|
3107 |
+
do_action('bwp_minify_printed_footer_styles');
|
3108 |
+
}
|
3109 |
+
|
3110 |
+
function print_inline_styles($handles)
|
3111 |
+
{
|
3112 |
+
global $wp_styles;
|
3113 |
+
|
3114 |
+
// this feature is only available on WP 3.3 or higher
|
3115 |
+
if (version_compare($this->wp_ver, '3.3', '<'))
|
3116 |
+
return;
|
3117 |
+
|
3118 |
+
foreach ($handles as $handle)
|
3119 |
+
$wp_styles->print_inline_style($handle);
|
3120 |
+
}
|
3121 |
+
|
3122 |
+
/**
|
3123 |
+
* @deprecated 1.3.0
|
3124 |
+
*/
|
3125 |
+
function print_media_styles()
|
3126 |
+
{
|
3127 |
+
_deprecated_function(__FUNCTION__, '1.3.0 (BWP Minify)');
|
3128 |
+
}
|
3129 |
+
|
3130 |
+
/**
|
3131 |
+
* @deprecated 1.3.0
|
3132 |
+
*/
|
3133 |
+
function print_dynamic_media_styles()
|
3134 |
+
{
|
3135 |
+
_deprecated_function(__FUNCTION__, '1.3.0 (BWP Minify)');
|
3136 |
+
}
|
3137 |
+
|
3138 |
+
/**
|
3139 |
+
* @deprecated 1.3.0
|
3140 |
+
*/
|
3141 |
+
function print_dynamic_styles()
|
3142 |
+
{
|
3143 |
+
_deprecated_function(__FUNCTION__, '1.3.0 (BWP Minify)');
|
3144 |
+
}
|
3145 |
+
|
3146 |
+
/**
|
3147 |
+
* Builds a list of internal $todo_scripts from WP's $todo
|
3148 |
+
*
|
3149 |
+
* @since 1.3.0
|
3150 |
+
*/
|
3151 |
+
function add_scripts($todo)
|
3152 |
+
{
|
3153 |
+
global $wp_scripts;
|
3154 |
+
|
3155 |
+
// Avoid conflict with WordPress 3.1
|
3156 |
+
if (1 == sizeof($todo) && isset($todo[0]) && 'l10n' == $todo[0])
|
3157 |
+
return array();
|
3158 |
+
|
3159 |
+
// @since 1.0.5 - 1.0.6
|
3160 |
+
$this->print_positions['allowed'] = apply_filters('bwp_minify_allowed_scripts', 'all');
|
3161 |
+
|
3162 |
+
foreach ($todo as $handle)
|
3163 |
+
{
|
3164 |
+
$script = $wp_scripts->registered[$handle];
|
3165 |
+
$todo_script = $this->init_todo_item($handle);
|
3166 |
+
|
3167 |
+
if (empty($script->src))
|
3168 |
+
{
|
3169 |
+
if ($todo_script['depend'])
|
3170 |
+
{
|
3171 |
+
// script's src is empty/invalid but it has dependencies so
|
3172 |
+
// it's probably a dummy script used to load other scripts
|
3173 |
+
$this->todo_scripts[$handle] = $todo_script;
|
3174 |
+
$wp_scripts->done[] = $handle;
|
3175 |
+
}
|
3176 |
+
continue;
|
3177 |
+
}
|
3178 |
+
|
3179 |
+
if ('jquery-migrate' == $handle) {
|
3180 |
+
// jquery-migrate and jquery-core might get separated so we
|
3181 |
+
// force jquery-migrate to have jquery-core as its dependecy
|
3182 |
+
$todo_script['depend'] = false == $todo_script['depend']
|
3183 |
+
? array() : $todo_script['depend'];
|
3184 |
+
$todo_script['depend'][] = 'jquery-core';
|
3185 |
+
}
|
3186 |
+
|
3187 |
+
if (true === $todo_script['forget'])
|
3188 |
+
{
|
3189 |
+
do_action('bwp_minify_processed_script', $handle, $todo_script);
|
3190 |
+
$wp_scripts->done[] = $handle;
|
3191 |
+
continue;
|
3192 |
+
}
|
3193 |
+
|
3194 |
+
// if this script is registered in footer, or it is registered
|
3195 |
+
// after the header scripts are printed, it is expected in footer
|
3196 |
+
$expected_in_footer = isset($wp_scripts->groups[$handle])
|
3197 |
+
&& 0 < $wp_scripts->groups[$handle]
|
3198 |
+
|| did_action('bwp_minify_after_header_scripts')
|
3199 |
+
? true : false;
|
3200 |
+
|
3201 |
+
// determine the position of the script
|
3202 |
+
if (!$this->is_in($handle, 'header')
|
3203 |
+
&& ($this->is_in($handle, 'footer') || $expected_in_footer)
|
3204 |
+
) {
|
3205 |
+
// if this script belongs to footer (logically or
|
3206 |
+
// 'intentionally') and is not 'forced' to be in header
|
3207 |
+
$todo_script['position'] = did_action('bwp_minify_after_footer_scripts')
|
3208 |
+
? 'footer' . $this->late_script_order
|
3209 |
+
: 'footer';
|
3210 |
+
}
|
3211 |
+
else
|
3212 |
+
{
|
3213 |
+
// this script belongs to header
|
3214 |
+
$todo_script['position'] = 'header';
|
3215 |
+
}
|
3216 |
+
|
3217 |
+
$this->todo_scripts[$handle] = $todo_script;
|
3218 |
+
$wp_scripts->done[] = $handle;
|
3219 |
+
}
|
3220 |
+
|
3221 |
+
// start minifying
|
3222 |
+
$this->minify($this->todo_scripts, 'script');
|
3223 |
+
|
3224 |
+
// if late scripts are found, print them now
|
3225 |
+
if (!empty($this->todo_late_scripts))
|
3226 |
+
{
|
3227 |
+
$this->print_scripts($this->todo_late_scripts);
|
3228 |
+
$this->todo_late_scripts = '';
|
3229 |
+
$this->late_script_order++;
|
3230 |
+
}
|
3231 |
+
|
3232 |
+
// no more $todo for WordPress because we have done it all
|
3233 |
+
return array();
|
3234 |
+
}
|
3235 |
+
|
3236 |
+
/**
|
3237 |
+
* Captures very late scripts to print later
|
3238 |
+
*
|
3239 |
+
* @since 1.3.0
|
3240 |
+
*/
|
3241 |
+
function add_late_scripts()
|
3242 |
+
{
|
3243 |
+
// only procceed if we have finished printing footer scripts
|
3244 |
+
if (did_action('bwp_minify_after_footer_scripts'))
|
3245 |
+
$this->todo_late_scripts = 'footer' . $this->late_script_order;
|
3246 |
+
}
|
3247 |
+
|
3248 |
+
function minify_script($handle, $item, $group_handle, $group_deps)
|
3249 |
+
{
|
3250 |
+
global $wp_scripts;
|
3251 |
+
|
3252 |
+
$script = $wp_scripts->registered[$handle];
|
3253 |
+
$src = $script->src;
|
3254 |
+
|
3255 |
+
if ($item['min'])
|
3256 |
+
{
|
3257 |
+
// minify is needed
|
3258 |
+
$src = $this->process_media_source($src);
|
3259 |
+
|
3260 |
+
if ($item['wp'])
|
3261 |
+
{
|
3262 |
+
// if WordPress handles this item make sure the $src is
|
3263 |
+
// processed and version is not set
|
3264 |
+
$script->src = $this->get_minify_src($src, $group_handle);
|
3265 |
+
$script->ver = NULL;
|
3266 |
+
// add a new group to hold this item
|
3267 |
+
$this->min_scripts[$group_handle] = array(
|
3268 |
+
'depend' => $group_deps,
|
3269 |
+
'handle' => $handle,
|
3270 |
+
'position' => $item['position']
|
3271 |
+
);
|
3272 |
+
$item['group'] = $group_handle;
|
3273 |
+
}
|
3274 |
+
else
|
3275 |
+
{
|
3276 |
+
$group_handle = false;
|
3277 |
+
foreach ($this->min_scripts as $_group_handle => $_group)
|
3278 |
+
{
|
3279 |
+
// pick the first available group in the same position
|
3280 |
+
// (header or footer) that still has room for more scripts,
|
3281 |
+
// but do not take into account group that is a
|
3282 |
+
// dependency of this item's own dependencies.
|
3283 |
+
if (isset($_group['string']) && $_group['position'] == $item['position']
|
3284 |
+
&& $this->options['input_maxfiles'] > sizeof($_group['string'])
|
3285 |
+
&& !$this->is_a_dependency($_group_handle, $group_deps, 'script')
|
3286 |
+
) {
|
3287 |
+
$group_handle = $_group_handle;
|
3288 |
+
break;
|
3289 |
+
}
|
3290 |
+
}
|
3291 |
+
|
3292 |
+
if ($group_handle)
|
3293 |
+
{
|
3294 |
+
// append to selected group's minify string, only if
|
3295 |
+
// this $src has not been added before
|
3296 |
+
$group = &$this->min_scripts[$group_handle];
|
3297 |
+
if (!in_array($src, $group['string']))
|
3298 |
+
{
|
3299 |
+
$group['string'][] = $src;
|
3300 |
+
|
3301 |
+
// make sure we don't self-referencing the
|
3302 |
+
// group_handle within this item's dependencies
|
3303 |
+
if (isset($group_deps[$group_handle]))
|
3304 |
+
unset($group_deps[$group_handle]);
|
3305 |
+
|
3306 |
+
// merge this item's dependencies with the selected
|
3307 |
+
// group's deps.
|
3308 |
+
$group['depend'] = array_merge($group['depend'], $group_deps);
|
3309 |
+
$item['group'] = $group_handle;
|
3310 |
+
}
|
3311 |
+
}
|
3312 |
+
else
|
3313 |
+
{
|
3314 |
+
// otherwise make a new group
|
3315 |
+
$group_handle = $this->_sanitize_handle($handle);
|
3316 |
+
$this->min_scripts[$group_handle] = array(
|
3317 |
+
'depend' => $group_deps,
|
3318 |
+
'string' => array($src),
|
3319 |
+
'position' => $item['position']
|
3320 |
+
);
|
3321 |
+
$item['group'] = $group_handle;
|
3322 |
+
}
|
3323 |
+
}
|
3324 |
+
}
|
3325 |
+
else
|
3326 |
+
{
|
3327 |
+
// no minify is needed, add new group to hold this item
|
3328 |
+
$this->min_scripts[$group_handle] = array(
|
3329 |
+
'depend' => $group_deps,
|
3330 |
+
'handle' => $handle,
|
3331 |
+
'position' => $item['position']
|
3332 |
+
);
|
3333 |
+
$item['group'] = $group_handle;
|
3334 |
+
}
|
3335 |
+
|
3336 |
+
// if this item has l10n data, mark it to process later
|
3337 |
+
if (self::is_l10n($handle))
|
3338 |
+
$this->todo_l10n[$group_handle][] = $handle;
|
3339 |
+
|
3340 |
+
// update the internal _Todo_ list
|
3341 |
+
$item['src'] = $src;
|
3342 |
+
$this->todo_scripts[$handle] = $item;
|
3343 |
+
|
3344 |
+
do_action('bwp_minify_processed_script', $handle, $item);
|
3345 |
+
}
|
3346 |
+
|
3347 |
+
/**
|
3348 |
+
* Prints Minify string for JS files recursively
|
3349 |
+
*
|
3350 |
+
* This function will traverse all script groups recursively to print scripts
|
3351 |
+
* while retaining dependencies. Use actions provided to add other things
|
3352 |
+
* before or after the output.
|
3353 |
+
*
|
3354 |
+
* @since 1.3.0
|
3355 |
+
* @uses $wp_scripts
|
3356 |
+
* @return void
|
3357 |
+
*/
|
3358 |
+
function print_scripts($position = 'header', $groups = false, $recursive = false)
|
3359 |
+
{
|
3360 |
+
global $wp_scripts;
|
3361 |
+
|
3362 |
+
if (!$recursive)
|
3363 |
+
do_action('bwp_minify_before_' . $position . '_scripts');
|
3364 |
+
|
3365 |
+
$groups = !$groups ? $this->min_scripts : $groups;
|
3366 |
+
$group_position = $position;
|
3367 |
+
|
3368 |
+
foreach ($groups as $group_handle => $group)
|
3369 |
+
{
|
3370 |
+
// if group is already done, no need to process anything
|
3371 |
+
if (!empty($this->min_scripts[$group_handle]['done']))
|
3372 |
+
continue;
|
3373 |
+
|
3374 |
+
// if this is not the correct position for the group
|
3375 |
+
if ($group['position'] != $position)
|
3376 |
+
if ($recursive)
|
3377 |
+
return $group['position'];
|
3378 |
+
else
|
3379 |
+
continue;
|
3380 |
+
|
3381 |
+
// print dependencies first
|
3382 |
+
$deps = array();
|
3383 |
+
if (0 < sizeof($group['depend']))
|
3384 |
+
{
|
3385 |
+
foreach ($group['depend'] as $dep => $active)
|
3386 |
+
{
|
3387 |
+
if (isset($this->min_scripts[$dep]))
|
3388 |
+
$deps[$dep] = $this->min_scripts[$dep];
|
3389 |
+
}
|
3390 |
+
$group_position = $this->print_scripts($position, $deps, true);
|
3391 |
+
}
|
3392 |
+
|
3393 |
+
// resolve dependencies failed, ignore this group for now
|
3394 |
+
if ($group_position != $position)
|
3395 |
+
{
|
3396 |
+
// this group needs to move to a new position, trigger events
|
3397 |
+
do_action('bwp_minify_moved_group', 'script',
|
3398 |
+
$group_handle,
|
3399 |
+
$group_position
|
3400 |
+
);
|
3401 |
+
$this->min_scripts[$group_handle]['position'] = $group_position;
|
3402 |
+
continue;
|
3403 |
+
}
|
3404 |
+
|
3405 |
+
// print this group using minify tag or $wp_scripts->do_item
|
3406 |
+
if (isset($group['string']) && 0 < sizeof($group['string']))
|
3407 |
+
{
|
3408 |
+
// print l10n data first if this group has any
|
3409 |
+
if (isset($this->todo_l10n[$group_handle]))
|
3410 |
+
$this->print_scripts_l10n($this->todo_l10n[$group_handle]);
|
3411 |
+
// if this is a minify string
|
3412 |
+
echo $this->get_minify_tag('script', $group, $group_handle);
|
3413 |
+
}
|
3414 |
+
else if (!empty($group['handle']))
|
3415 |
+
{
|
3416 |
+
// if this should be handled by WordPress
|
3417 |
+
$wp_scripts->do_item($group['handle']);
|
3418 |
+
}
|
3419 |
+
|
3420 |
+
$this->min_scripts[$group_handle]['done'] = true;
|
3421 |
+
}
|
3422 |
+
|
3423 |
+
if (!$recursive)
|
3424 |
+
{
|
3425 |
+
do_action('bwp_minify_after_' . $position . '_scripts');
|
3426 |
+
// save detector's log whenever we finish printing a footer batch
|
3427 |
+
if (false !== strpos($position, 'footer'))
|
3428 |
+
$this->detector->commit_logs();
|
3429 |
+
}
|
3430 |
+
|
3431 |
+
return $group_position;
|
3432 |
+
}
|
3433 |
+
|
3434 |
+
function print_header_scripts()
|
3435 |
+
{
|
3436 |
+
$this->print_scripts();
|
3437 |
+
|
3438 |
+
// fire an action after header scripts have been printed
|
3439 |
+
do_action('bwp_minify_printed_header_scripts');
|
3440 |
+
}
|
3441 |
+
|
3442 |
+
function print_footer_scripts()
|
3443 |
+
{
|
3444 |
+
$this->print_scripts('footer');
|
3445 |
+
|
3446 |
+
// fire an action after footer scripts have been print
|
3447 |
+
do_action('bwp_minify_printed_footer_scripts');
|
3448 |
+
}
|
3449 |
+
|
3450 |
+
function print_scripts_l10n($handles)
|
3451 |
+
{
|
3452 |
+
global $wp_scripts;
|
3453 |
+
|
3454 |
+
foreach ($handles as $handle)
|
3455 |
+
{
|
3456 |
+
// since WordPress version 3.3: uses
|
3457 |
+
// $wp_scripts->print_extra_script instead to print l10n info
|
3458 |
+
if (version_compare($this->wp_ver, '3.3', '>='))
|
3459 |
+
$wp_scripts->print_extra_script($handle);
|
3460 |
+
else
|
3461 |
+
$wp_scripts->print_scripts_l10n($handle);
|
3462 |
+
}
|
3463 |
+
}
|
3464 |
+
|
3465 |
+
/**
|
3466 |
+
* @deprecated 1.3.0
|
3467 |
+
*/
|
3468 |
+
function print_dynamic_scripts($type = 'header')
|
3469 |
+
{
|
3470 |
+
_deprecated_function(__FUNCTION__, '1.3.0 (BWP Minify)');
|
3471 |
+
}
|
3472 |
+
|
3473 |
+
/**
|
3474 |
+
* @deprecated 1.3.0
|
3475 |
+
*/
|
3476 |
+
function print_dynamic_header_scripts()
|
3477 |
+
{
|
3478 |
+
_deprecated_function(__FUNCTION__, '1.3.0 (BWP Minify)');
|
3479 |
+
}
|
3480 |
+
|
3481 |
+
/**
|
3482 |
+
* @deprecated 1.3.0
|
3483 |
+
*/
|
3484 |
+
function print_dynamic_footer_scripts()
|
3485 |
+
{
|
3486 |
+
_deprecated_function(__FUNCTION__, '1.3.0 (BWP Minify)');
|
3487 |
+
}
|
3488 |
+
|
3489 |
+
/**
|
3490 |
+
* @deprecated 1.3.0
|
3491 |
+
*/
|
3492 |
+
function print_header_scripts_l10n()
|
3493 |
+
{
|
3494 |
+
_deprecated_function(__FUNCTION__, '1.3.0 (BWP Minify)');
|
3495 |
+
}
|
3496 |
+
|
3497 |
+
/**
|
3498 |
+
* @deprecated 1.3.0
|
3499 |
+
*/
|
3500 |
+
function print_footer_scripts_l10n()
|
3501 |
+
{
|
3502 |
+
_deprecated_function(__FUNCTION__, '1.3.0 (BWP Minify)');
|
3503 |
+
}
|
3504 |
+
}
|
includes/common-functions.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Get the absolute filesystem path to the root of the WordPress installation
|
4 |
+
*
|
5 |
+
* @since 1.3.0
|
6 |
+
* @uses get_home_path wordpress/wp-admin/includes/files.php:81
|
7 |
+
* @return string Full filesystem path to the root of the WordPress installation
|
8 |
+
*/
|
9 |
+
if (!function_exists('bwp_get_home_path')) :
|
10 |
+
|
11 |
+
function bwp_get_home_path() {
|
12 |
+
require_once ABSPATH . 'wp-admin/includes/file.php';
|
13 |
+
return get_home_path();
|
14 |
+
}
|
15 |
+
|
16 |
+
endif;
|
includes/rewriter/apache.php
ADDED
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) 2014 Khang Minh <http://betterwp.net>
|
4 |
+
* @license http://www.gnu.org/licenses/gpl.html GNU GENERAL PUBLIC LICENSE VERSION 3.0 OR LATER
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class BWP_Minify_Rewriter_Apache
|
9 |
+
* @author Khang Minh <contact@betterwp.net>
|
10 |
+
* @since BWP Minify 1.3.0
|
11 |
+
* @package BWP Minify
|
12 |
+
*/
|
13 |
+
class BWP_Minify_Rewriter_Apache extends BWP_Minify_AbstractRewriter
|
14 |
+
{
|
15 |
+
public function add_rewrite_rules($suppress = true)
|
16 |
+
{
|
17 |
+
$this->suppress = $suppress;
|
18 |
+
$result = true;
|
19 |
+
|
20 |
+
switch ($this->rule_set)
|
21 |
+
{
|
22 |
+
default:
|
23 |
+
case '':
|
24 |
+
$this->add_wp_rewrite_rules();
|
25 |
+
$result = $this->add_cache_rewrite_rules();
|
26 |
+
break;
|
27 |
+
|
28 |
+
case 'wp':
|
29 |
+
$this->add_wp_rewrite_rules();
|
30 |
+
break;
|
31 |
+
|
32 |
+
case 'cache':
|
33 |
+
$result = $this->add_cache_rewrite_rules();
|
34 |
+
break;
|
35 |
+
}
|
36 |
+
|
37 |
+
return $result;
|
38 |
+
}
|
39 |
+
|
40 |
+
public function get_wp_config_file()
|
41 |
+
{
|
42 |
+
$config_dir = $this->get_wp_config_dir();
|
43 |
+
return trailingslashit($config_dir) . '.htaccess';
|
44 |
+
}
|
45 |
+
|
46 |
+
public function get_wp_config_dir()
|
47 |
+
{
|
48 |
+
return $this->main->get_wp_doc_root();
|
49 |
+
}
|
50 |
+
|
51 |
+
public function get_cache_config_file()
|
52 |
+
{
|
53 |
+
$config_dir = $this->get_cache_config_dir();
|
54 |
+
return trailingslashit($config_dir) . '.htaccess';
|
55 |
+
}
|
56 |
+
|
57 |
+
public function get_cache_config_dir()
|
58 |
+
{
|
59 |
+
return $this->main->get_cache_dir();
|
60 |
+
}
|
61 |
+
|
62 |
+
public function get_wp_rewrite_rules()
|
63 |
+
{
|
64 |
+
// get fly min path and remove the base
|
65 |
+
$fly_min_path = ltrim($this->main->get_fly_min_path(), '/');
|
66 |
+
$rules = array();
|
67 |
+
|
68 |
+
$rules[] = '<IfModule mod_rewrite.c>';
|
69 |
+
$rules[] = 'RewriteEngine On';
|
70 |
+
$rules[] = 'RewriteCond %{REQUEST_FILENAME} !-f';
|
71 |
+
$rules[] = 'RewriteRule ^([_0-9a-zA-Z-]+/)?'
|
72 |
+
. '(' . $fly_min_path . 'minify-.*\.(js|css))$ $2 [L]';
|
73 |
+
$rules[] = '</IfModule>' . "\n";
|
74 |
+
|
75 |
+
$rules = implode("\n", $rules);
|
76 |
+
return $rules;
|
77 |
+
}
|
78 |
+
|
79 |
+
public function get_cache_rewrite_rules()
|
80 |
+
{
|
81 |
+
// make use of WordPress's base, with blog path removed if any
|
82 |
+
$base = $this->main->get_wp_base();
|
83 |
+
$rules = array();
|
84 |
+
|
85 |
+
$rules[] = '<IfModule mod_rewrite.c>';
|
86 |
+
$rules[] = 'RewriteEngine On';
|
87 |
+
$rules[] = 'RewriteCond %{HTTP:Accept-Encoding} gzip';
|
88 |
+
$rules[] = 'RewriteRule .* - [E=ZIP_EXT:.gz]';
|
89 |
+
$rules[] = 'RewriteCond %{HTTP:Cache-Control} !no-cache';
|
90 |
+
$rules[] = 'RewriteCond %{HTTP:If-Modified-Since} !no-cache';
|
91 |
+
$rules[] = 'RewriteCond %{REQUEST_FILENAME}%{ENV:ZIP_EXT} -f';
|
92 |
+
$rules[] = 'RewriteRule (.*) $1%{ENV:ZIP_EXT} [L]';
|
93 |
+
$rules[] = 'RewriteRule ^minify-b(\d+)-([a-zA-Z0-9-_.]+)\.(css|js)$ '
|
94 |
+
. $base . 'index.php?blog=$1&min_group=$2&min_type=$3 [L]';
|
95 |
+
$rules[] = '</IfModule>' . "\n";
|
96 |
+
|
97 |
+
$rules = $this->get_cache_response_headers() . implode("\n", $rules);
|
98 |
+
return $rules;
|
99 |
+
}
|
100 |
+
|
101 |
+
public function remove_wp_rewrite_rules()
|
102 |
+
{
|
103 |
+
$this->prepare_wp_rewrite_rules();
|
104 |
+
$this->remove_rewrite_rules();
|
105 |
+
}
|
106 |
+
|
107 |
+
public function remove_cache_rewrite_rules()
|
108 |
+
{
|
109 |
+
$this->prepare_cache_rewrite_rules();
|
110 |
+
$this->remove_rewrite_rules();
|
111 |
+
}
|
112 |
+
|
113 |
+
public function add_wp_rewrite_rules()
|
114 |
+
{
|
115 |
+
if (!BWP_MINIFY::is_multisite() || BWP_MINIFY::is_subdomain_install())
|
116 |
+
return true;
|
117 |
+
|
118 |
+
$this->prepare_wp_rewrite_rules();
|
119 |
+
|
120 |
+
// only add these rules if `wp-content` is not already in fly min path
|
121 |
+
$fly_min_path = $this->main->get_fly_min_path();
|
122 |
+
if (false === strpos($fly_min_path, 'wp-content')
|
123 |
+
&& file_exists($this->config_file)
|
124 |
+
) {
|
125 |
+
return $this->write_rewrite_rules($this->rules);
|
126 |
+
}
|
127 |
+
}
|
128 |
+
|
129 |
+
public function add_cache_rewrite_rules()
|
130 |
+
{
|
131 |
+
$this->prepare_cache_rewrite_rules();
|
132 |
+
|
133 |
+
if (file_exists($this->config_file) || is_writable($this->config_dir))
|
134 |
+
{
|
135 |
+
// server config file exists, OR doesn't exist but
|
136 |
+
// directory is writable, attempt to create a new file, and
|
137 |
+
// write rewrite rules to it
|
138 |
+
return $this->write_rewrite_rules($this->rules);
|
139 |
+
}
|
140 |
+
|
141 |
+
// no need to check and return any error
|
142 |
+
if ($this->suppress)
|
143 |
+
return false;
|
144 |
+
|
145 |
+
// if we reach here that mean we COULD NOT write rewrite rules
|
146 |
+
// automatically. These check below tell the plugin to show the
|
147 |
+
// contents to be written to the server config file to admin to manually
|
148 |
+
// update the rewrite rules, but provide different error messages
|
149 |
+
if (!file_exists($this->config_dir))
|
150 |
+
return 'exists_dir';
|
151 |
+
if (!file_exists($this->config_file) && !is_writable($this->config_dir))
|
152 |
+
return 'write_dir';
|
153 |
+
|
154 |
+
// if we reach here, nothing works
|
155 |
+
return false;
|
156 |
+
}
|
157 |
+
|
158 |
+
protected function get_cache_response_headers()
|
159 |
+
{
|
160 |
+
$header_begin = BWP_MINIFY_HEADERS_BEGIN . "\n";
|
161 |
+
$header_end = BWP_MINIFY_HEADERS_END . "\n";
|
162 |
+
$headers = array();
|
163 |
+
|
164 |
+
// file type and encoding handling
|
165 |
+
$headers[] = '<Files "*.js.gz">';
|
166 |
+
$headers[] = 'ForceType application/x-javascript';
|
167 |
+
$headers[] = '</Files>';
|
168 |
+
$headers[] = '<Files "*.css.gz">';
|
169 |
+
$headers[] = 'ForceType text/css';
|
170 |
+
$headers[] = '</Files>';
|
171 |
+
$headers[] = '<IfModule mod_mime.c>';
|
172 |
+
$headers[] = 'AddEncoding gzip .gz';
|
173 |
+
$headers[] = 'AddCharset utf-8 .js .css';
|
174 |
+
$headers[] = '</IfModule>';
|
175 |
+
|
176 |
+
// compression handling
|
177 |
+
$headers[] = '<IfModule mod_deflate.c>';
|
178 |
+
$headers[] = ' <IfModule mod_setenvif.c>';
|
179 |
+
$headers[] = ' SetEnvIfNoCase Request_URI "\.gz$" no-gzip';
|
180 |
+
$headers[] = ' </IfModule>';
|
181 |
+
$headers[] = '</IfModule>';
|
182 |
+
|
183 |
+
// caching headers
|
184 |
+
$cache_age = (int) $this->options['input_maxage'] * (int) $this->options['select_time_type'];
|
185 |
+
$headers[] = '<IfModule mod_headers.c>';
|
186 |
+
$headers[] = 'Header set Cache-Control "public, max-age=' . $cache_age . '"';
|
187 |
+
$headers[] = 'Header set Vary "Accept-Encoding"';
|
188 |
+
$headers[] = 'Header unset ETag';
|
189 |
+
$headers[] = '</IfModule>' . "\n";
|
190 |
+
|
191 |
+
$headers = implode("\n", $headers);
|
192 |
+
return $header_begin . $headers . $header_end;
|
193 |
+
}
|
194 |
+
}
|
includes/rewriter/index.php
ADDED
File without changes
|
includes/rewriter/nginx.php
ADDED
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) 2014 Khang Minh <http://betterwp.net>
|
4 |
+
* @license http://www.gnu.org/licenses/gpl.html GNU GENERAL PUBLIC LICENSE VERSION 3.0 OR LATER
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class BWP_Minify_Rewriter_Nginx
|
9 |
+
* @author Khang Minh <contact@betterwp.net>
|
10 |
+
* @since BWP Minify 1.3.0
|
11 |
+
* @package BWP Minify
|
12 |
+
*/
|
13 |
+
class BWP_Minify_Rewriter_Nginx extends BWP_Minify_AbstractRewriter
|
14 |
+
{
|
15 |
+
public function add_rewrite_rules($suppress = true)
|
16 |
+
{
|
17 |
+
$this->suppress = $suppress;
|
18 |
+
return $this->add_wp_rewrite_rules();
|
19 |
+
}
|
20 |
+
|
21 |
+
public function get_wp_config_file()
|
22 |
+
{
|
23 |
+
$config_file = !empty($this->options['input_nginx_config_file'])
|
24 |
+
? $this->options['input_nginx_config_file']
|
25 |
+
: $this->main->get_doc_root('nginx.conf');
|
26 |
+
|
27 |
+
return $config_file;
|
28 |
+
}
|
29 |
+
|
30 |
+
public function get_wp_config_dir()
|
31 |
+
{
|
32 |
+
$config_file = $this->get_wp_config_file();
|
33 |
+
return dirname($config_file);
|
34 |
+
}
|
35 |
+
|
36 |
+
public function get_cache_config_file()
|
37 |
+
{
|
38 |
+
return $this->get_wp_config_file();
|
39 |
+
}
|
40 |
+
|
41 |
+
public function get_cache_config_dir()
|
42 |
+
{
|
43 |
+
return $this->get_wp_config_dir();
|
44 |
+
}
|
45 |
+
|
46 |
+
public function get_wp_rewrite_rules()
|
47 |
+
{
|
48 |
+
// make use of WordPress's base, with blog path removed if any
|
49 |
+
$base = $this->main->get_wp_base();
|
50 |
+
$fly_min_path = $this->main->get_fly_min_path();
|
51 |
+
$rules = array();
|
52 |
+
|
53 |
+
$rules[] = 'set $zip_ext "";';
|
54 |
+
$rules[] = 'if ($http_accept_encoding ~* gzip) {';
|
55 |
+
$rules[] = ' set $zip_ext ".gz";';
|
56 |
+
$rules[] = '}';
|
57 |
+
$rules[] = 'set $minify_static "";';
|
58 |
+
$rules[] = 'if ($http_cache_control = false) {';
|
59 |
+
$rules[] = ' set $minify_static "C";';
|
60 |
+
$rules[] = ' set $http_cache_control "";';
|
61 |
+
$rules[] = '}';
|
62 |
+
$rules[] = 'if ($http_cache_control !~* no-cache) {';
|
63 |
+
$rules[] = ' set $minify_static "C";';
|
64 |
+
$rules[] = '}';
|
65 |
+
$rules[] = 'if ($http_if_modified_since = false) {';
|
66 |
+
$rules[] = ' set $minify_static "${minify_static}M";';
|
67 |
+
$rules[] = '}';
|
68 |
+
$rules[] = 'if (-f $request_filename$zip_ext) {';
|
69 |
+
$rules[] = ' set $minify_static "${minify_static}E";';
|
70 |
+
$rules[] = '}';
|
71 |
+
$rules[] = 'if ($minify_static = CME) {';
|
72 |
+
$rules[] = ' rewrite (.*) $1$zip_ext break;';
|
73 |
+
$rules[] = '}';
|
74 |
+
|
75 |
+
// nginx rewrite rules and location directive do not match query
|
76 |
+
// variable so `/path/to/file.js` is the same as `/path/to/file.js?ver=1`
|
77 |
+
|
78 |
+
if (BWP_MINIFY::is_multisite() && !BWP_MINIFY::is_subdomain_install())
|
79 |
+
{
|
80 |
+
// special rewrite rules for sub-directory multisite environment
|
81 |
+
$blog_regex = '/[_0-9a-zA-Z-]+';
|
82 |
+
$rules[] = 'rewrite ^' . $blog_regex . '(' . $fly_min_path
|
83 |
+
. 'minify-b\d+-[a-zA-Z0-9-_.]+\.(css|js))$ $1;';
|
84 |
+
}
|
85 |
+
|
86 |
+
$rules[] = 'rewrite ^' . $fly_min_path
|
87 |
+
. 'minify-b(\d+)-([a-zA-Z0-9-_.]+)\.(css|js)$ '
|
88 |
+
. $base . 'index.php?blog=$1&min_group=$2&min_type=$3 last;';
|
89 |
+
$rules[] = "\n";
|
90 |
+
|
91 |
+
$rules = $this->get_cache_response_headers() . implode("\n", $rules);
|
92 |
+
return $rules;
|
93 |
+
}
|
94 |
+
|
95 |
+
public function get_cache_rewrite_rules()
|
96 |
+
{
|
97 |
+
return $this->get_wp_rewrite_rules();
|
98 |
+
}
|
99 |
+
|
100 |
+
public function remove_wp_rewrite_rules()
|
101 |
+
{
|
102 |
+
$this->prepare_wp_rewrite_rules();
|
103 |
+
$this->remove_rewrite_rules();
|
104 |
+
}
|
105 |
+
|
106 |
+
public function remove_cache_rewrite_rules() {}
|
107 |
+
|
108 |
+
public function add_wp_rewrite_rules()
|
109 |
+
{
|
110 |
+
$this->prepare_wp_rewrite_rules();
|
111 |
+
|
112 |
+
if (file_exists($this->config_file))
|
113 |
+
return $this->write_rewrite_rules($this->rules);
|
114 |
+
|
115 |
+
// if we reach here, nothing works
|
116 |
+
return false;
|
117 |
+
}
|
118 |
+
|
119 |
+
public function add_cache_rewrite_rules() {}
|
120 |
+
|
121 |
+
protected function get_cache_response_headers()
|
122 |
+
{
|
123 |
+
$fly_min_path = $this->main->get_fly_min_path();
|
124 |
+
$header_begin = BWP_MINIFY_HEADERS_BEGIN . "\n";
|
125 |
+
$header_end = BWP_MINIFY_HEADERS_END . "\n";
|
126 |
+
$headers = array();
|
127 |
+
|
128 |
+
$cache_age = (int) $this->options['input_maxage'] * (int) $this->options['select_time_type'];
|
129 |
+
$headers[] = 'location ~ ' . $fly_min_path . '.*\.(js|css)$ {';
|
130 |
+
$headers[] = ' add_header Cache-Control "public, max-age=' . $cache_age . '";';
|
131 |
+
$headers[] = ' add_header Vary "Accept-Encoding";';
|
132 |
+
$headers[] = ' etag off;';
|
133 |
+
$headers[] = '}';
|
134 |
+
$headers[] = 'location ~ ' . $fly_min_path . '.*\.js\.gz$ {';
|
135 |
+
$headers[] = ' gzip off;';
|
136 |
+
$headers[] = ' types {}';
|
137 |
+
$headers[] = ' default_type application/x-javascript;';
|
138 |
+
$headers[] = ' add_header Cache-Control "public, max-age=' . $cache_age . '";';
|
139 |
+
$headers[] = ' add_header Content-Encoding gzip;';
|
140 |
+
$headers[] = ' add_header Vary "Accept-Encoding";';
|
141 |
+
$headers[] = ' etag off;';
|
142 |
+
$headers[] = '}';
|
143 |
+
$headers[] = 'location ~ ' . $fly_min_path . '.*\.css\.gz$ {';
|
144 |
+
$headers[] = ' gzip off;';
|
145 |
+
$headers[] = ' types {}';
|
146 |
+
$headers[] = ' default_type text/css;';
|
147 |
+
$headers[] = ' add_header Cache-Control "public, max-age=' . $cache_age . '";';
|
148 |
+
$headers[] = ' add_header Content-Encoding gzip;';
|
149 |
+
$headers[] = ' add_header Vary "Accept-Encoding";';
|
150 |
+
$headers[] = ' etag off;';
|
151 |
+
$headers[] = '}' . "\n";
|
152 |
+
|
153 |
+
$headers = implode("\n", $headers);
|
154 |
+
return $header_begin . $headers . $header_end;
|
155 |
+
}
|
156 |
+
}
|
includes/rewriter/rewriter.php
ADDED
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) 2014 Khang Minh <http://betterwp.net>
|
4 |
+
* @license http://www.gnu.org/licenses/gpl.html GNU GENERAL PUBLIC LICENSE VERSION 3.0 OR LATER
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Abstract class BWP_Minify_AbstractRewriter
|
9 |
+
* @author Khang Minh <contact@betterwp.net>
|
10 |
+
* @since BWP Minify 1.3.0
|
11 |
+
* @package BWP Minify
|
12 |
+
*/
|
13 |
+
abstract class BWP_Minify_AbstractRewriter
|
14 |
+
{
|
15 |
+
protected $config_file = '';
|
16 |
+
|
17 |
+
protected $config_dir = '';
|
18 |
+
|
19 |
+
protected $start_marker = '';
|
20 |
+
|
21 |
+
protected $end_marker = '';
|
22 |
+
|
23 |
+
protected $rules = '';
|
24 |
+
|
25 |
+
protected $rules_clean = '';
|
26 |
+
|
27 |
+
protected $rule_set = '';
|
28 |
+
|
29 |
+
protected $suppress = true;
|
30 |
+
|
31 |
+
protected $options = '';
|
32 |
+
|
33 |
+
protected $domain = '';
|
34 |
+
|
35 |
+
protected $main = false; // BWP Minify class instance
|
36 |
+
|
37 |
+
public function __construct($main)
|
38 |
+
{
|
39 |
+
$this->main = $main;
|
40 |
+
$this->options = &$main->options;
|
41 |
+
$this->domain = $main->domain;
|
42 |
+
$this->init();
|
43 |
+
}
|
44 |
+
|
45 |
+
public function get_generated_wp_rewrite_rules()
|
46 |
+
{
|
47 |
+
$this->prepare_wp_rewrite_rules();
|
48 |
+
return $this->start_marker . "\n" . $this->rules . $this->end_marker . "\n";
|
49 |
+
}
|
50 |
+
|
51 |
+
public function get_generated_cache_rewrite_rules()
|
52 |
+
{
|
53 |
+
$this->prepare_cache_rewrite_rules();
|
54 |
+
return $this->start_marker . "\n" . $this->rules . $this->end_marker . "\n";
|
55 |
+
}
|
56 |
+
|
57 |
+
public function no_suppress()
|
58 |
+
{
|
59 |
+
$this->suppress = false;
|
60 |
+
return $this;
|
61 |
+
}
|
62 |
+
|
63 |
+
abstract protected function add_rewrite_rules($suppress = true);
|
64 |
+
abstract protected function add_wp_rewrite_rules();
|
65 |
+
abstract protected function add_cache_rewrite_rules();
|
66 |
+
abstract protected function remove_wp_rewrite_rules();
|
67 |
+
abstract protected function remove_cache_rewrite_rules();
|
68 |
+
abstract protected function get_wp_config_file();
|
69 |
+
abstract protected function get_cache_config_file();
|
70 |
+
abstract protected function get_wp_config_dir();
|
71 |
+
abstract protected function get_cache_config_dir();
|
72 |
+
abstract protected function get_wp_rewrite_rules();
|
73 |
+
abstract protected function get_cache_rewrite_rules();
|
74 |
+
abstract protected function get_cache_response_headers();
|
75 |
+
|
76 |
+
protected function init()
|
77 |
+
{
|
78 |
+
// intentionally left blank
|
79 |
+
}
|
80 |
+
|
81 |
+
protected function prepare_wp_rewrite_rules()
|
82 |
+
{
|
83 |
+
$this->config_dir = $this->get_wp_config_dir();
|
84 |
+
$this->config_file = $this->get_wp_config_file();
|
85 |
+
$this->rules = $this->get_wp_rewrite_rules();
|
86 |
+
$this->start_marker = BWP_MINIFY_WP_RULES_BEGIN;
|
87 |
+
$this->end_marker = BWP_MINIFY_WP_RULES_END;
|
88 |
+
}
|
89 |
+
|
90 |
+
protected function prepare_cache_rewrite_rules()
|
91 |
+
{
|
92 |
+
$this->config_dir = $this->get_cache_config_dir();
|
93 |
+
$this->config_file = $this->get_cache_config_file();
|
94 |
+
$this->rules = $this->get_cache_rewrite_rules();
|
95 |
+
$this->start_marker = BWP_MINIFY_RULES_BEGIN;
|
96 |
+
$this->end_marker = BWP_MINIFY_RULES_END;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Writes rewrite rules to config file
|
101 |
+
*
|
102 |
+
* @return bool|string true if write succeeds, 'put' if write fails, and
|
103 |
+
* 'written' if rewrite rules are there but file is not writable
|
104 |
+
* (probably manual update). should never return false
|
105 |
+
*/
|
106 |
+
protected function write_rewrite_rules($rules)
|
107 |
+
{
|
108 |
+
$rule_begin = $this->start_marker . "\n";
|
109 |
+
$rule_end = $this->end_marker . "\n";
|
110 |
+
$rules = !empty($rules) ? $rule_begin . $rules . $rule_end : '';
|
111 |
+
$rules_clean = !empty($rules) ? $rules : $rule_begin . $this->rules . $rule_end;
|
112 |
+
|
113 |
+
$current_rules = @file_get_contents($this->config_file);
|
114 |
+
$current_rules = false === $current_rules ? '' : $current_rules;
|
115 |
+
$begin = strpos($current_rules, $rule_begin);
|
116 |
+
$end = strpos($current_rules, $rule_end);
|
117 |
+
|
118 |
+
$current_rules_clean = preg_replace('/[\r\n]+/', "\n", $current_rules);
|
119 |
+
if (!empty($rules) && false !== strpos($current_rules_clean, $rules_clean))
|
120 |
+
{
|
121 |
+
// if needed rewrite rules are there, we don't have to do anything,
|
122 |
+
// but if $file is not writable we should return appropriate message
|
123 |
+
if (!is_writable($this->config_file))
|
124 |
+
return 'written';
|
125 |
+
|
126 |
+
return true;
|
127 |
+
}
|
128 |
+
else
|
129 |
+
{
|
130 |
+
// otherwise we add/update rewrite rules in the file
|
131 |
+
$end = false === $end ? 0 : $end;
|
132 |
+
$length = false !== $begin && $end > $begin
|
133 |
+
? $end - $begin + strlen($rule_end) : 0;
|
134 |
+
|
135 |
+
$begin = false === $begin ? 0 : $begin;
|
136 |
+
$current_rules = empty($length) && empty($rules)
|
137 |
+
? $current_rules
|
138 |
+
: substr_replace($current_rules, $rules, $begin, $length);
|
139 |
+
}
|
140 |
+
|
141 |
+
if (false === @file_put_contents($this->config_file, $current_rules))
|
142 |
+
// write to server config file failed for some reasons
|
143 |
+
return 'put';
|
144 |
+
else
|
145 |
+
return true;
|
146 |
+
}
|
147 |
+
|
148 |
+
protected function remove_rewrite_rules()
|
149 |
+
{
|
150 |
+
if (file_exists($this->config_file))
|
151 |
+
$this->write_rewrite_rules('');
|
152 |
+
}
|
153 |
+
}
|
js/bwp-minify.js
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(function($) {
|
2 |
+
function toggle_input(t, toggle) {
|
3 |
+
var $t = $(t);
|
4 |
+
var position = $t.data('position');
|
5 |
+
var $w = $t.parents('.bwp-sidebar');
|
6 |
+
var $s = $t.find('.bwp-sign');
|
7 |
+
toggle = typeof toggle !== 'undefined' ? toggle : true;
|
8 |
+
|
9 |
+
$w.find('.position-handle').not(t).find('.bwp-sign').html('+');
|
10 |
+
$w.find(':input[name!="' + position + '"]').hide();
|
11 |
+
|
12 |
+
if (false === toggle && '+' != $s.html()) {
|
13 |
+
return false;
|
14 |
+
}
|
15 |
+
|
16 |
+
$w.find(':input[name="' + position + '"]').toggle();
|
17 |
+
if ('+' == $s.html()) {
|
18 |
+
$s.html('>>');
|
19 |
+
} else {
|
20 |
+
$s.html('+');
|
21 |
+
}
|
22 |
+
}
|
23 |
+
|
24 |
+
$('.bwp-sidebar').on('click', '.position-handle', function(e) {
|
25 |
+
e.preventDefault();
|
26 |
+
toggle_input(this);
|
27 |
+
});
|
28 |
+
|
29 |
+
$('.bwp-minify-detector-table').on('click', '.action-toggle-handle', function(e) {
|
30 |
+
e.preventDefault();
|
31 |
+
|
32 |
+
var $t = $(this);
|
33 |
+
var $i = $t.parent('td');
|
34 |
+
var c = $i.find('.action-handles')[0];
|
35 |
+
var $w = $t.parents('.bwp-minify-detector-table');
|
36 |
+
|
37 |
+
$w.find('.action-handles').not(c).hide();
|
38 |
+
$(c).toggle();
|
39 |
+
})
|
40 |
+
|
41 |
+
$('.bwp-minify-detector-table').on('click', '.action-handle', function(e) {
|
42 |
+
e.preventDefault();
|
43 |
+
|
44 |
+
var $t = $(this);
|
45 |
+
var position = $t.data('position');
|
46 |
+
var handle = $t.parent().find('.data-handle').text();
|
47 |
+
var input = 'input_' + position;
|
48 |
+
var $i = $('.bwp-sidebar :input[name="' + input + '"]');
|
49 |
+
var input_handle = $i.parent().find('.position-handle')[0];
|
50 |
+
var input_val = '' == $i.val() ? '' : $i.val() + "\r\n";
|
51 |
+
|
52 |
+
$i.val(input_val + handle);
|
53 |
+
$t.parent().hide();
|
54 |
+
toggle_input(input_handle, false);
|
55 |
+
})
|
56 |
+
});
|
languages/bwp-minify-de_DE.mo
ADDED
Binary file
|
languages/bwp-minify-de_DE.po
ADDED
@@ -0,0 +1,341 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: BWP Minify\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2012-10-07 22:54+0700\n"
|
6 |
+
"PO-Revision-Date: 2013-02-16 13:16+0100\n"
|
7 |
+
"Last-Translator: Matthias Wehrlein <post@supermonster.de>\n"
|
8 |
+
"Language-Team: \n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n"
|
13 |
+
"X-Poedit-Basepath: .\n"
|
14 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
15 |
+
"X-Generator: Poedit 1.5.5\n"
|
16 |
+
"X-Poedit-SearchPath-0: .\n"
|
17 |
+
|
18 |
+
#: includes/class-bwp-framework.php:178
|
19 |
+
#, php-format
|
20 |
+
msgid ""
|
21 |
+
"%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</"
|
22 |
+
"strong> or higher. The plugin will not function until you update your "
|
23 |
+
"software. Please deactivate this plugin."
|
24 |
+
msgstr ""
|
25 |
+
"%s benötigt WordPress <strong>%s</strong> oder höher und PHP <strong>%s</"
|
26 |
+
"strong> oder höher. Dieses Plug-in wird nicht funktionieren bis du deine "
|
27 |
+
"Software aktualisiert hast. Bitte aktiviere dieses Plug-in."
|
28 |
+
|
29 |
+
#: includes/class-bwp-framework.php:189
|
30 |
+
msgid "Development Log"
|
31 |
+
msgstr "Entwicklungs-Log"
|
32 |
+
|
33 |
+
#: includes/class-bwp-framework.php:189
|
34 |
+
msgid "Frequently Asked Questions"
|
35 |
+
msgstr "Häufig gestellte Fragen"
|
36 |
+
|
37 |
+
#: includes/class-bwp-framework.php:189
|
38 |
+
msgid "FAQ"
|
39 |
+
msgstr "FAQ"
|
40 |
+
|
41 |
+
#: includes/class-bwp-framework.php:189
|
42 |
+
msgid "Got a problem? Send me a feedback!"
|
43 |
+
msgstr "Auf ein Problem gestoßen? Schick mir Feedback!"
|
44 |
+
|
45 |
+
#: includes/class-bwp-framework.php:189
|
46 |
+
msgid "Contact"
|
47 |
+
msgstr "Kontakt"
|
48 |
+
|
49 |
+
#: includes/class-bwp-framework.php:196
|
50 |
+
msgid ""
|
51 |
+
"You can buy me some special coffees if you appreciate my work, thank you!"
|
52 |
+
msgstr ""
|
53 |
+
"Du kannst mir ein paar besondere Kaffee kaufen, wenn du meine Arbeit zu "
|
54 |
+
"schätzen weißt. Danke!"
|
55 |
+
|
56 |
+
#: includes/class-bwp-framework.php:210
|
57 |
+
msgid "Donate to "
|
58 |
+
msgstr "Spenden an"
|
59 |
+
|
60 |
+
#: includes/class-bwp-framework.php:212
|
61 |
+
msgid "One cup $5.00"
|
62 |
+
msgstr "Ein Becher 5,00 $"
|
63 |
+
|
64 |
+
#: includes/class-bwp-framework.php:213
|
65 |
+
msgid "Two cups $10.00"
|
66 |
+
msgstr "Zwei Becher 10,00 $"
|
67 |
+
|
68 |
+
#: includes/class-bwp-framework.php:214
|
69 |
+
msgid "Five cups! $25.00"
|
70 |
+
msgstr "Fünf Becher! 25,00 $"
|
71 |
+
|
72 |
+
#: includes/class-bwp-framework.php:215
|
73 |
+
msgid "One LL-cup!!! $50.00"
|
74 |
+
msgstr "Ein riesiger Becher!!! 50,00 $"
|
75 |
+
|
76 |
+
#: includes/class-bwp-framework.php:216
|
77 |
+
msgid "... or any amount!"
|
78 |
+
msgstr "… oder jeder andere Betrag!"
|
79 |
+
|
80 |
+
#: includes/class-bwp-framework.php:231
|
81 |
+
msgid "Latest updates from BetterWP.net!"
|
82 |
+
msgstr "Das Neueste von BetterWP.net!"
|
83 |
+
|
84 |
+
#: includes/class-bwp-framework.php:232
|
85 |
+
msgid "Follow me on Twitter!"
|
86 |
+
msgstr "Folge mir auf Twitter!"
|
87 |
+
|
88 |
+
#: includes/class-bwp-framework.php:241
|
89 |
+
#, php-format
|
90 |
+
msgid "You are using version %s!"
|
91 |
+
msgstr "Du benutzt Version %s!"
|
92 |
+
|
93 |
+
#: includes/class-bwp-framework.php:377
|
94 |
+
msgid "Settings"
|
95 |
+
msgstr "Einstellungen"
|
96 |
+
|
97 |
+
#: includes/class-bwp-minify.php:110
|
98 |
+
msgid "Better WordPress Minify Settings"
|
99 |
+
msgstr "Better-WordPress-Minify-Einstellungen"
|
100 |
+
|
101 |
+
#: includes/class-bwp-minify.php:206
|
102 |
+
msgid "Better WordPress Minify"
|
103 |
+
msgstr "Better Wordpress Minify"
|
104 |
+
|
105 |
+
#: includes/class-bwp-minify.php:217
|
106 |
+
msgid "You do not have sufficient permissions to access this page."
|
107 |
+
msgstr ""
|
108 |
+
"Du verfügst nicht über die nötigen Rechte, um auf diese Seite zugreifen zu "
|
109 |
+
"können."
|
110 |
+
|
111 |
+
#: includes/class-bwp-minify.php:233
|
112 |
+
msgid "General Options"
|
113 |
+
msgstr "Allgemeine Einstellungen"
|
114 |
+
|
115 |
+
#: includes/class-bwp-minify.php:234
|
116 |
+
msgid "Minify JS files automatically?"
|
117 |
+
msgstr "JavaScript-Dateien automatisch komprimieren?"
|
118 |
+
|
119 |
+
#: includes/class-bwp-minify.php:235
|
120 |
+
msgid "Minify CSS files automatically?"
|
121 |
+
msgstr "CSS-Dateien automatisch komprimieren?"
|
122 |
+
|
123 |
+
#: includes/class-bwp-minify.php:236
|
124 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
125 |
+
msgstr "CSS von <code>bloginfo()</code> komprimieren?"
|
126 |
+
|
127 |
+
#: includes/class-bwp-minify.php:237
|
128 |
+
msgid "Minifying Options"
|
129 |
+
msgstr "Komprimierungs-Einstellungen"
|
130 |
+
|
131 |
+
#: includes/class-bwp-minify.php:238
|
132 |
+
msgid "Minify URL (double-click to edit)"
|
133 |
+
msgstr "Komprimierungs-URL (doppeklicken um zu bearbeiten)"
|
134 |
+
|
135 |
+
#: includes/class-bwp-minify.php:239
|
136 |
+
msgid "Cache directory (double-click to edit)"
|
137 |
+
msgstr "Cache-Verzeichnis (doppeklicken um zu bearbeiten)"
|
138 |
+
|
139 |
+
#: includes/class-bwp-minify.php:240
|
140 |
+
msgid "One minify string will contain"
|
141 |
+
msgstr "Eine Komprimierungs-Zeichenkette enthält"
|
142 |
+
|
143 |
+
#: includes/class-bwp-minify.php:241
|
144 |
+
msgid "Append the minify string with"
|
145 |
+
msgstr "Dies an die Komprimierungs-Zeichenkette anhängen"
|
146 |
+
|
147 |
+
#: includes/class-bwp-minify.php:242
|
148 |
+
msgid "Minifying Scripts Options"
|
149 |
+
msgstr "Komprimierungs-Einstellungen für Skripte"
|
150 |
+
|
151 |
+
#: includes/class-bwp-minify.php:243
|
152 |
+
msgid "Scripts to be minified in header"
|
153 |
+
msgstr "JavaScripts, die im Seitenkopf komprimiert werden sollen"
|
154 |
+
|
155 |
+
#: includes/class-bwp-minify.php:244
|
156 |
+
msgid "Scripts to be minified in footer"
|
157 |
+
msgstr "JavaScripts, die im Seitenfuß komprimiert werden sollen"
|
158 |
+
|
159 |
+
#: includes/class-bwp-minify.php:245
|
160 |
+
msgid "Scripts to be minified and then printed separately"
|
161 |
+
msgstr "JavaScripts, die komprimiert aber separat ausgegeben werden sollen"
|
162 |
+
|
163 |
+
#: includes/class-bwp-minify.php:246
|
164 |
+
msgid "Scripts to be ignored (not minified)"
|
165 |
+
msgstr "JavaScripts, die nicht komprimiert werden sollen"
|
166 |
+
|
167 |
+
#: includes/class-bwp-minify.php:251
|
168 |
+
msgid "<em>Options that affect both your stylesheets and scripts.</em>"
|
169 |
+
msgstr ""
|
170 |
+
"<em>Einstellungen, die Stylesheets und auch JavaScripts beeinflussen.</em>"
|
171 |
+
|
172 |
+
#: includes/class-bwp-minify.php:252
|
173 |
+
#, php-format
|
174 |
+
msgid ""
|
175 |
+
"<em>You can force the position of each script using those inputs below (e.g. "
|
176 |
+
"you have a script registered in the header but you want to minify it in the "
|
177 |
+
"footer instead). If you are still unsure, please read more <a href="
|
178 |
+
"\"%s#positioning-your-scripts\">here</a>. Type in one script handle "
|
179 |
+
"(<strong>NOT filename</strong>) per line.</em>"
|
180 |
+
msgstr ""
|
181 |
+
"<em>Du kannst die Position jedes einzelnen JavaScripts über die Eingaben "
|
182 |
+
"unten beeinflussen (z.B. hast du ein JavaScript im Kopf registriert, du "
|
183 |
+
"willst es aber komprimiert im Seitenfuß ausgeben). Wenn du unsicher bist, "
|
184 |
+
"kannst du <a href=\"%s#positioning-your-scripts\">hier mehr erfahren</a>. "
|
185 |
+
"Gib einen JavaScript-Alias (<strong>NICHT den Dateinamen</strong>) pro Zeile "
|
186 |
+
"ein.</em>"
|
187 |
+
|
188 |
+
#: includes/class-bwp-minify.php:256
|
189 |
+
msgid "second(s)"
|
190 |
+
msgstr "Sekunde(n)"
|
191 |
+
|
192 |
+
#: includes/class-bwp-minify.php:257
|
193 |
+
msgid "minute(s)"
|
194 |
+
msgstr "Minute(n)"
|
195 |
+
|
196 |
+
#: includes/class-bwp-minify.php:258
|
197 |
+
msgid "hour(s)"
|
198 |
+
msgstr "Stunde(n)"
|
199 |
+
|
200 |
+
#: includes/class-bwp-minify.php:259
|
201 |
+
msgid "day(s)"
|
202 |
+
msgstr "Tag(e)"
|
203 |
+
|
204 |
+
#: includes/class-bwp-minify.php:262
|
205 |
+
msgid "Do not append anything"
|
206 |
+
msgstr "Nichts anhängen"
|
207 |
+
|
208 |
+
#: includes/class-bwp-minify.php:263
|
209 |
+
msgid "Cache folder’s last modified time"
|
210 |
+
msgstr "Last-Modified-Zeit für Cache-Verzeichnis"
|
211 |
+
|
212 |
+
#: includes/class-bwp-minify.php:264
|
213 |
+
msgid "Your WordPress’s current version"
|
214 |
+
msgstr "Version deiner WordPress-Installation"
|
215 |
+
|
216 |
+
#: includes/class-bwp-minify.php:265
|
217 |
+
msgid "Your theme’s current version"
|
218 |
+
msgstr "Version des von dir verwendeten Themes"
|
219 |
+
|
220 |
+
#: includes/class-bwp-minify.php:266
|
221 |
+
msgid "A custom number"
|
222 |
+
msgstr "Spezifische Zahl"
|
223 |
+
|
224 |
+
#: includes/class-bwp-minify.php:270 includes/class-bwp-minify.php:271
|
225 |
+
msgid ""
|
226 |
+
"you can still use <code>bwp_minify()</code> helper function if you disable "
|
227 |
+
"this."
|
228 |
+
msgstr ""
|
229 |
+
"Du kannst weiterhin die <code>bwp_minify()</code>-Hilfsfunktion benutzen, "
|
230 |
+
"wenn du dies deaktivierst."
|
231 |
+
|
232 |
+
#: includes/class-bwp-minify.php:272
|
233 |
+
msgid ""
|
234 |
+
"most themes (e.g. Twenty Ten) use <code>bloginfo()</code> to print the main "
|
235 |
+
"stylesheet (i.e. <code>style.css</code>) and BWP Minify will not be able to "
|
236 |
+
"add it to the main minify string. If you want to minify <code>style.css</"
|
237 |
+
"code> with the rest of your css files, you must enqueue it."
|
238 |
+
msgstr ""
|
239 |
+
"Die meisten Themes (z.B. Twenty Ten) benutzen <code>bloginfo()</code>, um "
|
240 |
+
"das Haupt-Stylesheet auszugeben (z.B. <code>style.css</code>). BWP Minify "
|
241 |
+
"kann dies nicht der Haupt-Komprimierung hinzufügen. Wenn du möchtest, dass "
|
242 |
+
"<code>style.css</code> mit dem Rest deiner CSS-Dateien komprimiert werden "
|
243 |
+
"soll, musst du es über <code>wp_enqueue_style()</code> integrieren."
|
244 |
+
|
245 |
+
#: includes/class-bwp-minify.php:275
|
246 |
+
#, php-format
|
247 |
+
msgid ""
|
248 |
+
"This should be set automatically. If you think the URL is too long, please "
|
249 |
+
"read <a href=\"%s#customization\">here</a> to know how to properly modify "
|
250 |
+
"this."
|
251 |
+
msgstr ""
|
252 |
+
"Dies sollte automatisch gesetzt werden. Wenn du denkst, dass die URL zu lang "
|
253 |
+
"ist, lies bitte <a href=\"%s#customization\">hier</a> nach, wie du es "
|
254 |
+
"richtig anpassen kannst."
|
255 |
+
|
256 |
+
#: includes/class-bwp-minify.php:276
|
257 |
+
#, php-format
|
258 |
+
msgid ""
|
259 |
+
"<strong>Important</strong>: Changing cache directory is a two-step process, "
|
260 |
+
"which is described in details <a href=\"%s#advanced_customization\" target="
|
261 |
+
"\"_blank\">here</a>. Please note that cache directory must be writable (i.e. "
|
262 |
+
"CHMOD to 755 or 777)."
|
263 |
+
msgstr ""
|
264 |
+
"<strong>Wichtig:</strong> Das Cache-Verzeichnis zu ändern benötigt zwei "
|
265 |
+
"Schritte, welche im Detail <a href=\"%s#advanced_customization\" target="
|
266 |
+
"\"_blank\">hier</a> beschrieben sind. Beachte dabei, dass das Cache-"
|
267 |
+
"Verzeichnis beschreibbar sein muss (CHMOD 755 oder 777)."
|
268 |
+
|
269 |
+
#: includes/class-bwp-minify.php:277
|
270 |
+
msgid "file(s) at most."
|
271 |
+
msgstr "Datei(en) maximal."
|
272 |
+
|
273 |
+
#: includes/class-bwp-minify.php:278
|
274 |
+
msgid "—"
|
275 |
+
msgstr "—"
|
276 |
+
|
277 |
+
#: includes/class-bwp-minify.php:279
|
278 |
+
msgid "<em>→ /min/?f=file.js&ver=</em> "
|
279 |
+
msgstr "<em>→ /min/?f=file.js&ver=</em> "
|
280 |
+
|
281 |
+
#: includes/class-bwp-minify.php:289
|
282 |
+
msgid ""
|
283 |
+
"<em><strong>Note:</strong> When you append one of the things above you are "
|
284 |
+
"basically telling browsers to clear their cached version of your CSS and JS "
|
285 |
+
"files, which is very useful when you change source files. Use this feature "
|
286 |
+
"wisely :).</em>"
|
287 |
+
msgstr ""
|
288 |
+
"<em><strong>Hinweis:</strong> Wenn du eines der obrigen Dinge anhängst, "
|
289 |
+
"gibst du den Browsern praktisch den Befehl, dass sie ihre gecachten "
|
290 |
+
"Versionen deiner CSS- und JavaScript-Dateien löschen sollen. Das ist sehr "
|
291 |
+
"hilfreich, wenn du an deinen Dateien etwas geändert hast. Benutze diese "
|
292 |
+
"Funktion weise. :-)</em>"
|
293 |
+
|
294 |
+
#: includes/class-bwp-minify.php:296
|
295 |
+
msgid "Flush the cache"
|
296 |
+
msgstr "Cache löschen"
|
297 |
+
|
298 |
+
#: includes/class-bwp-minify.php:316 includes/class-bwp-minify.php:318
|
299 |
+
msgid "Notice"
|
300 |
+
msgstr "Hinweis"
|
301 |
+
|
302 |
+
#: includes/class-bwp-minify.php:316
|
303 |
+
#, php-format
|
304 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
305 |
+
msgstr "<strong>%d</strong> gecachte Dateien wurden erfolgreich gelöscht!"
|
306 |
+
|
307 |
+
#: includes/class-bwp-minify.php:318
|
308 |
+
msgid ""
|
309 |
+
"Could not delete any cached files. Please manually check the cache directory."
|
310 |
+
msgstr ""
|
311 |
+
"Konnte die gecachten Dateien nicht löschen. Bitte prüfe das Verzeichnis "
|
312 |
+
"selbst. "
|
313 |
+
|
314 |
+
#: includes/class-bwp-minify.php:352
|
315 |
+
msgid "All options have been saved."
|
316 |
+
msgstr "Alle Einstellungen wurden gespeichert."
|
317 |
+
|
318 |
+
#: includes/class-bwp-minify.php:368
|
319 |
+
msgid "Warning"
|
320 |
+
msgstr "Warnung"
|
321 |
+
|
322 |
+
#: includes/class-bwp-minify.php:368
|
323 |
+
msgid ""
|
324 |
+
"Cache directory does not exist or is not writable. Please try CHMOD your "
|
325 |
+
"cache directory to 755. If you still see this warning, CHMOD to 777."
|
326 |
+
msgstr ""
|
327 |
+
"Das Cache-Verzeichnis existiert nicht oder ist nicht beschreibbar. Bitte "
|
328 |
+
"versuche CHMOD 755 auf das Verzeichnis. Wenn du danach immer noch diese "
|
329 |
+
"Warnung siehst, probiere es mit CHMOD 777."
|
330 |
+
|
331 |
+
#: includes/class-bwp-minify.php:490
|
332 |
+
msgid "empty"
|
333 |
+
msgstr "leer"
|
334 |
+
|
335 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:80
|
336 |
+
msgid "Plugin Configurations"
|
337 |
+
msgstr "Plug-in-Einstellungen"
|
338 |
+
|
339 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:398
|
340 |
+
msgid "Save Changes"
|
341 |
+
msgstr "Änderungen speichern"
|
languages/bwp-minify-es_ES.mo
ADDED
Binary file
|
languages/bwp-minify-es_ES.po
ADDED
@@ -0,0 +1,344 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: BWP Minify\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2012-10-07 22:54+0700\n"
|
6 |
+
"PO-Revision-Date: 2013-10-24 01:21+0100\n"
|
7 |
+
"Last-Translator: ruben <info@usitility.com>\n"
|
8 |
+
"Language-Team: \n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n"
|
13 |
+
"X-Poedit-Basepath: .\n"
|
14 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
15 |
+
"X-Generator: Poedit 1.5.4\n"
|
16 |
+
"Language: ES_es\n"
|
17 |
+
"X-Poedit-SearchPath-0: .\n"
|
18 |
+
|
19 |
+
#: includes/class-bwp-framework.php:178
|
20 |
+
#, php-format
|
21 |
+
msgid ""
|
22 |
+
"%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</"
|
23 |
+
"strong> or higher. The plugin will not function until you update your "
|
24 |
+
"software. Please deactivate this plugin."
|
25 |
+
msgstr ""
|
26 |
+
"%s requiere WordPress <strong>%s</strong> o superior y PHP <strong>%s</"
|
27 |
+
"strong> o superior. El plugin no funcionará hasta que no actualices tu "
|
28 |
+
"software. Por favor, desactiva el plugin."
|
29 |
+
|
30 |
+
#: includes/class-bwp-framework.php:189
|
31 |
+
msgid "Development Log"
|
32 |
+
msgstr "Log de Desarrollo"
|
33 |
+
|
34 |
+
#: includes/class-bwp-framework.php:189
|
35 |
+
msgid "Frequently Asked Questions"
|
36 |
+
msgstr "Preguntas Más Frecuentes"
|
37 |
+
|
38 |
+
#: includes/class-bwp-framework.php:189
|
39 |
+
msgid "FAQ"
|
40 |
+
msgstr "FAQ"
|
41 |
+
|
42 |
+
#: includes/class-bwp-framework.php:189
|
43 |
+
msgid "Got a problem? Send me a feedback!"
|
44 |
+
msgstr "¿Tienes algún problema? !Envíame un comentario!"
|
45 |
+
|
46 |
+
#: includes/class-bwp-framework.php:189
|
47 |
+
#, fuzzy
|
48 |
+
msgid "Contact"
|
49 |
+
msgstr "Contacto"
|
50 |
+
|
51 |
+
#: includes/class-bwp-framework.php:196
|
52 |
+
msgid ""
|
53 |
+
"You can buy me some special coffees if you appreciate my work, thank you!"
|
54 |
+
msgstr "Puedes comprarme un café especial si aprecias mi trabajo, !Gracias!"
|
55 |
+
|
56 |
+
#: includes/class-bwp-framework.php:210
|
57 |
+
msgid "Donate to "
|
58 |
+
msgstr "Donar a"
|
59 |
+
|
60 |
+
#: includes/class-bwp-framework.php:212
|
61 |
+
msgid "One cup $5.00"
|
62 |
+
msgstr "Una taza $5.00"
|
63 |
+
|
64 |
+
#: includes/class-bwp-framework.php:213
|
65 |
+
msgid "Two cups $10.00"
|
66 |
+
msgstr "Dos tazas $10.00"
|
67 |
+
|
68 |
+
#: includes/class-bwp-framework.php:214
|
69 |
+
msgid "Five cups! $25.00"
|
70 |
+
msgstr "Cinco tazas! $25.00"
|
71 |
+
|
72 |
+
#: includes/class-bwp-framework.php:215
|
73 |
+
msgid "One LL-cup!!! $50.00"
|
74 |
+
msgstr "Una Taza ExtraGrande!!! $50.00"
|
75 |
+
|
76 |
+
#: includes/class-bwp-framework.php:216
|
77 |
+
msgid "... or any amount!"
|
78 |
+
msgstr "... o cualquier cantidad!"
|
79 |
+
|
80 |
+
#: includes/class-bwp-framework.php:231
|
81 |
+
msgid "Latest updates from BetterWP.net!"
|
82 |
+
msgstr "¡Ultimas actualizaciones de BetterWP.net!"
|
83 |
+
|
84 |
+
#: includes/class-bwp-framework.php:232
|
85 |
+
msgid "Follow me on Twitter!"
|
86 |
+
msgstr "¡Sígueme en Twitter!"
|
87 |
+
|
88 |
+
#: includes/class-bwp-framework.php:241
|
89 |
+
#, php-format
|
90 |
+
msgid "You are using version %s!"
|
91 |
+
msgstr "Estás usando la versión %s!"
|
92 |
+
|
93 |
+
#: includes/class-bwp-framework.php:377
|
94 |
+
#, fuzzy
|
95 |
+
msgid "Settings"
|
96 |
+
msgstr "Configuración"
|
97 |
+
|
98 |
+
#: includes/class-bwp-minify.php:110
|
99 |
+
msgid "Better WordPress Minify Settings"
|
100 |
+
msgstr "Configuraciones de Better WordPress Minify "
|
101 |
+
|
102 |
+
#: includes/class-bwp-minify.php:206
|
103 |
+
msgid "Better WordPress Minify"
|
104 |
+
msgstr "Better WordPress Minify"
|
105 |
+
|
106 |
+
#: includes/class-bwp-minify.php:217
|
107 |
+
msgid "You do not have sufficient permissions to access this page."
|
108 |
+
msgstr "No tienes permisos suficientes para acceder a esta página."
|
109 |
+
|
110 |
+
#: includes/class-bwp-minify.php:233
|
111 |
+
msgid "General Options"
|
112 |
+
msgstr "Opciones Generales"
|
113 |
+
|
114 |
+
#: includes/class-bwp-minify.php:234
|
115 |
+
msgid "Minify JS files automatically?"
|
116 |
+
msgstr "¿Minificar los ficheros JS automáticamente?"
|
117 |
+
|
118 |
+
#: includes/class-bwp-minify.php:235
|
119 |
+
msgid "Minify CSS files automatically?"
|
120 |
+
msgstr "¿Minificar los ficheros CSS automáticamente?"
|
121 |
+
|
122 |
+
#: includes/class-bwp-minify.php:236
|
123 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
124 |
+
msgstr "¿Minificar las hojas de estilo <code>bloginfo()</code>?"
|
125 |
+
|
126 |
+
#: includes/class-bwp-minify.php:237
|
127 |
+
msgid "Minifying Options"
|
128 |
+
msgstr "Opciones de Minificado"
|
129 |
+
|
130 |
+
#: includes/class-bwp-minify.php:238
|
131 |
+
msgid "Minify URL (double-click to edit)"
|
132 |
+
msgstr "URL de Minificado (doble click para editar)"
|
133 |
+
|
134 |
+
#: includes/class-bwp-minify.php:239
|
135 |
+
msgid "Cache directory (double-click to edit)"
|
136 |
+
msgstr "Directorio caché (doble click para editar)"
|
137 |
+
|
138 |
+
#: includes/class-bwp-minify.php:240
|
139 |
+
msgid "One minify string will contain"
|
140 |
+
msgstr "Una cadena de Minificado contendrá"
|
141 |
+
|
142 |
+
#: includes/class-bwp-minify.php:241
|
143 |
+
msgid "Append the minify string with"
|
144 |
+
msgstr "Agregar la cadena de minificado con "
|
145 |
+
|
146 |
+
#: includes/class-bwp-minify.php:242
|
147 |
+
msgid "Minifying Scripts Options"
|
148 |
+
msgstr "Opciones de Minificado para Scripts"
|
149 |
+
|
150 |
+
#: includes/class-bwp-minify.php:243
|
151 |
+
msgid "Scripts to be minified in header"
|
152 |
+
msgstr "Scripts para ser minificados en la cabecera"
|
153 |
+
|
154 |
+
#: includes/class-bwp-minify.php:244
|
155 |
+
msgid "Scripts to be minified in footer"
|
156 |
+
msgstr "Scripts para ser minificados en el pie"
|
157 |
+
|
158 |
+
#: includes/class-bwp-minify.php:245
|
159 |
+
msgid "Scripts to be minified and then printed separately"
|
160 |
+
msgstr "Scripts para ser minificados y mostrados por separado"
|
161 |
+
|
162 |
+
#: includes/class-bwp-minify.php:246
|
163 |
+
msgid "Scripts to be ignored (not minified)"
|
164 |
+
msgstr "Scripts para ser ignorados (no se minificarán)"
|
165 |
+
|
166 |
+
#: includes/class-bwp-minify.php:251
|
167 |
+
msgid "<em>Options that affect both your stylesheets and scripts.</em>"
|
168 |
+
msgstr ""
|
169 |
+
"<em>Opciones que afectan tanto a tus hojas de estilo como a tus scripts.</em>"
|
170 |
+
|
171 |
+
#: includes/class-bwp-minify.php:252
|
172 |
+
#, php-format
|
173 |
+
msgid ""
|
174 |
+
"<em>You can force the position of each script using those inputs below (e.g. "
|
175 |
+
"you have a script registered in the header but you want to minify it in the "
|
176 |
+
"footer instead). If you are still unsure, please read more <a href="
|
177 |
+
"\"%s#positioning-your-scripts\">here</a>. Type in one script handle "
|
178 |
+
"(<strong>NOT filename</strong>) per line.</em>"
|
179 |
+
msgstr ""
|
180 |
+
"<em>Puedes forzar la posición de cada script usando las siguientes "
|
181 |
+
"entradas(p.ej. tienes un script registrado en la cabecera pero quieres que "
|
182 |
+
"aparezca minificado en el pie de página). Si no estás seguro, lee más <a "
|
183 |
+
"href=\"%s#positioning-your-scripts\">aquí</a>. Escribe un \"script handle"
|
184 |
+
"\" (<strong>NO el nombre del fichero</strong>) por línea.</em>"
|
185 |
+
|
186 |
+
#: includes/class-bwp-minify.php:256
|
187 |
+
msgid "second(s)"
|
188 |
+
msgstr "segundo(s)"
|
189 |
+
|
190 |
+
#: includes/class-bwp-minify.php:257
|
191 |
+
msgid "minute(s)"
|
192 |
+
msgstr "minuto(s)"
|
193 |
+
|
194 |
+
#: includes/class-bwp-minify.php:258
|
195 |
+
msgid "hour(s)"
|
196 |
+
msgstr "hora(s)"
|
197 |
+
|
198 |
+
#: includes/class-bwp-minify.php:259
|
199 |
+
msgid "day(s)"
|
200 |
+
msgstr "día(s)"
|
201 |
+
|
202 |
+
#: includes/class-bwp-minify.php:262
|
203 |
+
msgid "Do not append anything"
|
204 |
+
msgstr "No agregar nada"
|
205 |
+
|
206 |
+
#: includes/class-bwp-minify.php:263
|
207 |
+
msgid "Cache folder’s last modified time"
|
208 |
+
msgstr "Última fecha de modificación del directorio Cache"
|
209 |
+
|
210 |
+
#: includes/class-bwp-minify.php:264
|
211 |
+
msgid "Your WordPress’s current version"
|
212 |
+
msgstr "Tu versión actual de WordPress"
|
213 |
+
|
214 |
+
#: includes/class-bwp-minify.php:265
|
215 |
+
msgid "Your theme’s current version"
|
216 |
+
msgstr "Tu versión actual del Theme"
|
217 |
+
|
218 |
+
#: includes/class-bwp-minify.php:266
|
219 |
+
msgid "A custom number"
|
220 |
+
msgstr "Un número concreto"
|
221 |
+
|
222 |
+
#: includes/class-bwp-minify.php:270 includes/class-bwp-minify.php:271
|
223 |
+
msgid ""
|
224 |
+
"you can still use <code>bwp_minify()</code> helper function if you disable "
|
225 |
+
"this."
|
226 |
+
msgstr ""
|
227 |
+
"podrás continuar usando la función <code>bwp_minify()</code> en caso de que "
|
228 |
+
"desactives esta opción."
|
229 |
+
|
230 |
+
#: includes/class-bwp-minify.php:272
|
231 |
+
msgid ""
|
232 |
+
"most themes (e.g. Twenty Ten) use <code>bloginfo()</code> to print the main "
|
233 |
+
"stylesheet (i.e. <code>style.css</code>) and BWP Minify will not be able to "
|
234 |
+
"add it to the main minify string. If you want to minify <code>style.css</"
|
235 |
+
"code> with the rest of your css files, you must enqueue it."
|
236 |
+
msgstr ""
|
237 |
+
"la mayoría de Themes (ej. Twenty Ten) usan <code>bloginfo()</code> para "
|
238 |
+
"imprimir la hoja de estilos principal (ej. <code>style.css</code>) y BWP "
|
239 |
+
"Minify no será capaz de agregar esta a la cadena principal de minificado. Si "
|
240 |
+
"quieres minificar <code>style.css</code> junto al resto de tus ficheros css, "
|
241 |
+
"deberás agregarla a la cola."
|
242 |
+
|
243 |
+
#: includes/class-bwp-minify.php:275
|
244 |
+
#, php-format
|
245 |
+
msgid ""
|
246 |
+
"This should be set automatically. If you think the URL is too long, please "
|
247 |
+
"read <a href=\"%s#customization\">here</a> to know how to properly modify "
|
248 |
+
"this."
|
249 |
+
msgstr ""
|
250 |
+
"Esto debería ponerse automáticamente. Si crees que la URL es demasiado "
|
251 |
+
"larga, por favor lee <a href=\"%s#customization\">esto</a> para saber como "
|
252 |
+
"modificar esto apropiadamente."
|
253 |
+
|
254 |
+
#: includes/class-bwp-minify.php:276
|
255 |
+
#, php-format
|
256 |
+
msgid ""
|
257 |
+
"<strong>Important</strong>: Changing cache directory is a two-step process, "
|
258 |
+
"which is described in details <a href=\"%s#advanced_customization\" target="
|
259 |
+
"\"_blank\">here</a>. Please note that cache directory must be writable (i.e. "
|
260 |
+
"CHMOD to 755 or 777)."
|
261 |
+
msgstr ""
|
262 |
+
"<strong>Importante</strong>: Cambiar el directorio de caché es un proceso "
|
263 |
+
"que requiere dos pasos, que están explicados en detalle <a href="
|
264 |
+
"\"%s#advanced_customization\" target=\"_blank\">aquí</a>. Por favor "
|
265 |
+
"comprueba que el directorio de la caché tiene permisos de escritura (ej "
|
266 |
+
"CHMOD 755 ó 777)."
|
267 |
+
|
268 |
+
#: includes/class-bwp-minify.php:277
|
269 |
+
msgid "file(s) at most."
|
270 |
+
msgstr "fichero(s) como máximo"
|
271 |
+
|
272 |
+
#: includes/class-bwp-minify.php:278
|
273 |
+
msgid "—"
|
274 |
+
msgstr "—"
|
275 |
+
|
276 |
+
#: includes/class-bwp-minify.php:279
|
277 |
+
msgid "<em>→ /min/?f=file.js&ver=</em> "
|
278 |
+
msgstr "<em>→ /min/?f=file.js&ver=</em> "
|
279 |
+
|
280 |
+
#: includes/class-bwp-minify.php:289
|
281 |
+
msgid ""
|
282 |
+
"<em><strong>Note:</strong> When you append one of the things above you are "
|
283 |
+
"basically telling browsers to clear their cached version of your CSS and JS "
|
284 |
+
"files, which is very useful when you change source files. Use this feature "
|
285 |
+
"wisely :).</em>"
|
286 |
+
msgstr ""
|
287 |
+
"<em><strong>Nota:</strong> Cuando agregas un texto arriba, básicamente le "
|
288 |
+
"estás diciendo a los buscadores que limpien la versión cacheada de tus "
|
289 |
+
"ficheros CSS y JS, lo cual es muy útil cuando modificas esos ficheros. "
|
290 |
+
"Utiliza esta característica de forma inteligente ).</em>"
|
291 |
+
|
292 |
+
#: includes/class-bwp-minify.php:296
|
293 |
+
msgid "Flush the cache"
|
294 |
+
msgstr "Vaciar la caché"
|
295 |
+
|
296 |
+
#: includes/class-bwp-minify.php:316 includes/class-bwp-minify.php:318
|
297 |
+
#, fuzzy
|
298 |
+
msgid "Notice"
|
299 |
+
msgstr "Noticia"
|
300 |
+
|
301 |
+
#: includes/class-bwp-minify.php:316
|
302 |
+
#, php-format
|
303 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
304 |
+
msgstr ""
|
305 |
+
"<strong>%d</strong> los ficheros cacheados han sido borrados correctamente!"
|
306 |
+
|
307 |
+
#: includes/class-bwp-minify.php:318
|
308 |
+
msgid ""
|
309 |
+
"Could not delete any cached files. Please manually check the cache directory."
|
310 |
+
msgstr ""
|
311 |
+
"No se ha podido borrar ningún fichero cacheado. Por favor, comprueba "
|
312 |
+
"manualmente el directorio caché."
|
313 |
+
|
314 |
+
#: includes/class-bwp-minify.php:352
|
315 |
+
msgid "All options have been saved."
|
316 |
+
msgstr "Todas las opciones han sido guardadas."
|
317 |
+
|
318 |
+
#: includes/class-bwp-minify.php:368
|
319 |
+
#, fuzzy
|
320 |
+
msgid "Warning"
|
321 |
+
msgstr "Aviso"
|
322 |
+
|
323 |
+
#: includes/class-bwp-minify.php:368
|
324 |
+
msgid ""
|
325 |
+
"Cache directory does not exist or is not writable. Please try CHMOD your "
|
326 |
+
"cache directory to 755. If you still see this warning, CHMOD to 777."
|
327 |
+
msgstr ""
|
328 |
+
"El directorio de Caché no existe o no tiene permisos de escritura. Por favor "
|
329 |
+
"haz un CHMOD 755 en tu directorio de caché. Si sigues viendo este aviso, "
|
330 |
+
"ejecuta un CHMOD 777."
|
331 |
+
|
332 |
+
#: includes/class-bwp-minify.php:490
|
333 |
+
#, fuzzy
|
334 |
+
msgid "empty"
|
335 |
+
msgstr "vacío"
|
336 |
+
|
337 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:80
|
338 |
+
msgid "Plugin Configurations"
|
339 |
+
msgstr "Configuraciones de Plugin"
|
340 |
+
|
341 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:398
|
342 |
+
#, fuzzy
|
343 |
+
msgid "Save Changes"
|
344 |
+
msgstr "Guardar cambios"
|
languages/bwp-minify-fr_FR.mo
ADDED
Binary file
|
languages/bwp-minify-fr_FR.po
ADDED
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: BWP Minify\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2012-03-20 21:51+0700\n"
|
6 |
+
"PO-Revision-Date: 2012-03-20 22:01+0700\n"
|
7 |
+
"Last-Translator: \n"
|
8 |
+
"Language-Team: \n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n"
|
13 |
+
"X-Poedit-Basepath: .\n"
|
14 |
+
"X-Poedit-SourceCharset: utf-8\n"
|
15 |
+
"X-Poedit-SearchPath-0: .\n"
|
16 |
+
|
17 |
+
#: includes/class-bwp-framework.php:177
|
18 |
+
#, php-format
|
19 |
+
msgid "%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</strong> or higher. The plugin will not function until you update your software. Please deactivate this plugin."
|
20 |
+
msgstr "%s necesita versiunea de Wordpress <strong>%s</strong> sau mai mare si PHP <strong>%s</strong> sau mai mare. Plugin-ul nu va functiona pana nu actualizati programele. Va rugam dezactivati plugin-ul."
|
21 |
+
|
22 |
+
#: includes/class-bwp-framework.php:188
|
23 |
+
msgid "Development Log"
|
24 |
+
msgstr "Journal de développement"
|
25 |
+
|
26 |
+
#: includes/class-bwp-framework.php:188
|
27 |
+
msgid "Frequently Asked Questions"
|
28 |
+
msgstr "Questions fréquentes"
|
29 |
+
|
30 |
+
#: includes/class-bwp-framework.php:188
|
31 |
+
msgid "FAQ"
|
32 |
+
msgstr "FAQ"
|
33 |
+
|
34 |
+
#: includes/class-bwp-framework.php:188
|
35 |
+
msgid "Got a problem? Send me a feedback!"
|
36 |
+
msgstr "Vous avez un problème ? Envoyez moi un message !"
|
37 |
+
|
38 |
+
#: includes/class-bwp-framework.php:188
|
39 |
+
msgid "Contact"
|
40 |
+
msgstr "Contact"
|
41 |
+
|
42 |
+
#: includes/class-bwp-framework.php:195
|
43 |
+
msgid "You can buy me some special coffees if you appreciate my work, thank you!"
|
44 |
+
msgstr "Vous pouvez m'acheter du café si vous appréciez mon travail, merci :"
|
45 |
+
|
46 |
+
#: includes/class-bwp-framework.php:209
|
47 |
+
msgid "Donate to "
|
48 |
+
msgstr "Donner à"
|
49 |
+
|
50 |
+
#: includes/class-bwp-framework.php:211
|
51 |
+
msgid "One cup $5.00"
|
52 |
+
msgstr "Une tasse 5.00$"
|
53 |
+
|
54 |
+
#: includes/class-bwp-framework.php:212
|
55 |
+
msgid "Two cups $10.00"
|
56 |
+
msgstr "Deux tasses 10.00$"
|
57 |
+
|
58 |
+
#: includes/class-bwp-framework.php:213
|
59 |
+
msgid "Five cups! $25.00"
|
60 |
+
msgstr "Cinq tasses ! 25.00$"
|
61 |
+
|
62 |
+
#: includes/class-bwp-framework.php:214
|
63 |
+
msgid "One LL-cup!!! $50.00"
|
64 |
+
msgstr "Un tonneau de café !!! 50.00 $"
|
65 |
+
|
66 |
+
#: includes/class-bwp-framework.php:215
|
67 |
+
msgid "... or any amount!"
|
68 |
+
msgstr "...ou n'importe quel montant"
|
69 |
+
|
70 |
+
#: includes/class-bwp-framework.php:230
|
71 |
+
msgid "Latest updates from BetterWP.net!"
|
72 |
+
msgstr "Dernières mise à jour de BetterWP.net"
|
73 |
+
|
74 |
+
#: includes/class-bwp-framework.php:231
|
75 |
+
msgid "Follow me on Twitter!"
|
76 |
+
msgstr "Suivez-moi sur Twitter !"
|
77 |
+
|
78 |
+
#: includes/class-bwp-framework.php:240
|
79 |
+
#, php-format
|
80 |
+
msgid "You are using version %s!"
|
81 |
+
msgstr "Vous utilisez la version %s !"
|
82 |
+
|
83 |
+
#: includes/class-bwp-framework.php:376
|
84 |
+
msgid "Settings"
|
85 |
+
msgstr "Paramètres"
|
86 |
+
|
87 |
+
#: includes/class-bwp-minify.php:110
|
88 |
+
msgid "Better WordPress Minify Settings"
|
89 |
+
msgstr "Paramètres Better WordPress Minify "
|
90 |
+
|
91 |
+
#: includes/class-bwp-minify.php:196
|
92 |
+
msgid "Better WordPress Minify"
|
93 |
+
msgstr "Better WordPress Minify"
|
94 |
+
|
95 |
+
#: includes/class-bwp-minify.php:207
|
96 |
+
msgid "You do not have sufficient permissions to access this page."
|
97 |
+
msgstr "Vous n'avez la permission d'accéder à cette page"
|
98 |
+
|
99 |
+
#: includes/class-bwp-minify.php:223
|
100 |
+
msgid "General Options"
|
101 |
+
msgstr "Options Générales"
|
102 |
+
|
103 |
+
#: includes/class-bwp-minify.php:224
|
104 |
+
#, fuzzy
|
105 |
+
msgid "Minify JS files automatically?"
|
106 |
+
msgstr "Minifier les fichiers CSS et JS automatiquement ?"
|
107 |
+
|
108 |
+
#: includes/class-bwp-minify.php:225
|
109 |
+
#, fuzzy
|
110 |
+
msgid "Minify CSS files automatically?"
|
111 |
+
msgstr "Minifier les fichiers CSS et JS automatiquement ?"
|
112 |
+
|
113 |
+
#: includes/class-bwp-minify.php:226
|
114 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
115 |
+
msgstr "Minifier les feuilles de style <code>bloginfo()</code> ?"
|
116 |
+
|
117 |
+
#: includes/class-bwp-minify.php:227
|
118 |
+
msgid "Minifying Options"
|
119 |
+
msgstr "Options de Minification"
|
120 |
+
|
121 |
+
#: includes/class-bwp-minify.php:228
|
122 |
+
msgid "Minify URL (double-click to edit)"
|
123 |
+
msgstr "URL de Minification (double-cliquer pour la modifier)"
|
124 |
+
|
125 |
+
#: includes/class-bwp-minify.php:229
|
126 |
+
#, fuzzy
|
127 |
+
msgid "Cache directory (double-click to edit)"
|
128 |
+
msgstr "URL de Minification (double-cliquer pour la modifier)"
|
129 |
+
|
130 |
+
#: includes/class-bwp-minify.php:230
|
131 |
+
msgid "One minify string will contain"
|
132 |
+
msgstr "Une chaîne de minification contiendra"
|
133 |
+
|
134 |
+
#: includes/class-bwp-minify.php:231
|
135 |
+
msgid "Append the minify string with"
|
136 |
+
msgstr "Ajouter à la fin de la chaîne de minification"
|
137 |
+
|
138 |
+
#: includes/class-bwp-minify.php:232
|
139 |
+
msgid "Minifying Scripts Options"
|
140 |
+
msgstr "Options de Minification des Scripts"
|
141 |
+
|
142 |
+
#: includes/class-bwp-minify.php:233
|
143 |
+
msgid "Scripts to be minified in header"
|
144 |
+
msgstr "Scripts à minifier dans l'entête de la page"
|
145 |
+
|
146 |
+
#: includes/class-bwp-minify.php:234
|
147 |
+
msgid "Scripts to be minified in footer"
|
148 |
+
msgstr "Script à minifier dans le pied de la page"
|
149 |
+
|
150 |
+
#: includes/class-bwp-minify.php:235
|
151 |
+
msgid "Scripts to be minified and then printed separately"
|
152 |
+
msgstr "Scripts à minifier et à imprimer séparemment"
|
153 |
+
|
154 |
+
#: includes/class-bwp-minify.php:236
|
155 |
+
msgid "Scripts to be ignored (not minified)"
|
156 |
+
msgstr "Scripts à ignorer (ne seront pas minifiés)"
|
157 |
+
|
158 |
+
#: includes/class-bwp-minify.php:241
|
159 |
+
msgid "<em>Options that affect both your stylesheets and scripts.</em>"
|
160 |
+
msgstr "<em>Options s'appliquant aux feuilles de styles et aux scripts</em>"
|
161 |
+
|
162 |
+
#: includes/class-bwp-minify.php:242
|
163 |
+
#, php-format
|
164 |
+
msgid "<em>You can force the position of each script using those inputs below (e.g. you have a script registered in the header but you want to minify it in the footer instead). If you are still unsure, please read more <a href=\"%s#positioning-your-scripts\">here</a>. Type in one script handle (<strong>NOT filename</strong>) per line.</em>"
|
165 |
+
msgstr "<em>Vous pouvez forcer la position de chaque script en utilisant les paramètres ci-dessous (ex: vous avez un script enregistré dans l'entête mais vous voulez le voir minifié dans le pied à la place). Si vous n'êtes pas sûr de ce que vous faites, veuillez lire <a href=\"%s#positioning-your-scripts\">ceci</a>. Saisissez un handle de script (<strong>PAS un nom de fichier</strong>) par ligne.</em>"
|
166 |
+
|
167 |
+
#: includes/class-bwp-minify.php:246
|
168 |
+
msgid "second(s)"
|
169 |
+
msgstr "seconde(s)"
|
170 |
+
|
171 |
+
#: includes/class-bwp-minify.php:247
|
172 |
+
msgid "minute(s)"
|
173 |
+
msgstr "minute(s)"
|
174 |
+
|
175 |
+
#: includes/class-bwp-minify.php:248
|
176 |
+
msgid "hour(s)"
|
177 |
+
msgstr "heure(s)"
|
178 |
+
|
179 |
+
#: includes/class-bwp-minify.php:249
|
180 |
+
msgid "day(s)"
|
181 |
+
msgstr "jour(s)"
|
182 |
+
|
183 |
+
#: includes/class-bwp-minify.php:252
|
184 |
+
msgid "Do not append anything"
|
185 |
+
msgstr "Ne rien ajouter"
|
186 |
+
|
187 |
+
#: includes/class-bwp-minify.php:253
|
188 |
+
msgid "Cache folder’s last modified time"
|
189 |
+
msgstr "La date de dernière modification du cache"
|
190 |
+
|
191 |
+
#: includes/class-bwp-minify.php:254
|
192 |
+
msgid "Your WordPress’s current version"
|
193 |
+
msgstr "Votre version actuelle de Wordpress"
|
194 |
+
|
195 |
+
#: includes/class-bwp-minify.php:255
|
196 |
+
msgid "A custom number"
|
197 |
+
msgstr "Un numéro personnalisé"
|
198 |
+
|
199 |
+
#: includes/class-bwp-minify.php:259
|
200 |
+
#: includes/class-bwp-minify.php:260
|
201 |
+
#, fuzzy
|
202 |
+
msgid "you can still use <code>bwp_minify()</code> helper function if you disable this."
|
203 |
+
msgstr "vous pourrez toujours utiliser la fonction <code>bwp_minify()</code> si vous désactiver ceci."
|
204 |
+
|
205 |
+
#: includes/class-bwp-minify.php:261
|
206 |
+
msgid "most themes (e.g. Twenty Ten) use <code>bloginfo()</code> to print the main stylesheet (i.e. <code>style.css</code>) and BWP Minify will not be able to add it to the main minify string. If you want to minify <code>style.css</code> with the rest of your css files, you must enqueue it."
|
207 |
+
msgstr "la plupart des thèmes (ex: Twenty Ten) utilisent <code>bloginfo()</code> pour imprimer la feuille de style principale (ex: <code>style.css</code>) et BWP Minify ne sera pas capable de l'ajouter à la chaîne de minification principale. Si vous voulez minifier <code>style.css</code> avec le reste de vos fichiers CSS, vous devez utiliser la fonction <code>enqueue_style()</code> de Wordpress."
|
208 |
+
|
209 |
+
#: includes/class-bwp-minify.php:264
|
210 |
+
#, php-format
|
211 |
+
msgid "This should be set automatically. If you think the URL is too long, please read <a href=\"%s#customization\">here</a> to know how to properly modify this."
|
212 |
+
msgstr "Ce devrait être configuré automatiquement. Si vous pensez que cette URL est trop longue, veuillez lire <a href=\"%s#customization\">ceci</a> pour apprendre à modifier ce paramètre correctement."
|
213 |
+
|
214 |
+
#: includes/class-bwp-minify.php:265
|
215 |
+
#, php-format
|
216 |
+
msgid "<strong>Important</strong>: Changing cache directory is a two-step process, which is described in details <a href=\"%s#config\" target=\"_blank\">here</a>. Please note that cache directory must be writable (i.e. CHMOD to 755 or 777)."
|
217 |
+
msgstr ""
|
218 |
+
|
219 |
+
#: includes/class-bwp-minify.php:266
|
220 |
+
msgid "file(s) at most."
|
221 |
+
msgstr "fichier(s) maximum."
|
222 |
+
|
223 |
+
#: includes/class-bwp-minify.php:267
|
224 |
+
msgid "—"
|
225 |
+
msgstr "—"
|
226 |
+
|
227 |
+
#: includes/class-bwp-minify.php:268
|
228 |
+
msgid "<em>→ /min/?f=file.js&ver=</em> "
|
229 |
+
msgstr "<em>→ /min/?f=file.js&ver=</em> "
|
230 |
+
|
231 |
+
#: includes/class-bwp-minify.php:278
|
232 |
+
msgid "<em><strong>Note:</strong> When you append one of the things above you are basically telling browsers to clear their cached version of your CSS and JS files, which is very useful when you change source files. Use this feature wisely :).</em>"
|
233 |
+
msgstr "<em><strong>Note: </strong> Quand vous ajouter l'un des paramètres ci-dessus à la chaîne de minification, cela signifie que vous dites aux navigateurs de mettre à jour leur propre cache de vos fichiers CSS et JS. Cela peut-être utile lorsque vous modifiez vos fichiers sources. Utilisez cette fonction avec parcimonie :).</em>"
|
234 |
+
|
235 |
+
#: includes/class-bwp-minify.php:285
|
236 |
+
msgid "Flush the cache"
|
237 |
+
msgstr ""
|
238 |
+
|
239 |
+
#: includes/class-bwp-minify.php:305
|
240 |
+
#: includes/class-bwp-minify.php:307
|
241 |
+
msgid "Notice"
|
242 |
+
msgstr ""
|
243 |
+
|
244 |
+
#: includes/class-bwp-minify.php:305
|
245 |
+
#, php-format
|
246 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
247 |
+
msgstr ""
|
248 |
+
|
249 |
+
#: includes/class-bwp-minify.php:307
|
250 |
+
msgid "Could not delete any cached files. Please manually check the cache directory."
|
251 |
+
msgstr ""
|
252 |
+
|
253 |
+
#: includes/class-bwp-minify.php:341
|
254 |
+
msgid "All options have been saved."
|
255 |
+
msgstr ""
|
256 |
+
|
257 |
+
#: includes/class-bwp-minify.php:357
|
258 |
+
msgid "Warning"
|
259 |
+
msgstr "Avertissement"
|
260 |
+
|
261 |
+
#: includes/class-bwp-minify.php:357
|
262 |
+
msgid "Cache directory does not exist or is not writable. Please try CHMOD your cache directory to 755. If you still see this warning, CHMOD to 777."
|
263 |
+
msgstr "Le dossier de cache n'existe pas ou n'est pas inscriptible. Essayez de CHMOD votre dossier de cache à 755. Si vous voyez encore cet avertissement, CHMOD à 777."
|
264 |
+
|
265 |
+
#: includes/class-bwp-minify.php:471
|
266 |
+
msgid "empty"
|
267 |
+
msgstr "vide"
|
268 |
+
|
269 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:80
|
270 |
+
msgid "Plugin Configurations"
|
271 |
+
msgstr "Configuration du Plugin"
|
272 |
+
|
273 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:398
|
274 |
+
msgid "Save Changes"
|
275 |
+
msgstr "Enregistrer les modifications"
|
276 |
+
|
277 |
+
#~ msgid "Cache will be stored in (by default)"
|
278 |
+
#~ msgstr "Le cache sera stocké dans (par défaut)"
|
279 |
+
|
280 |
+
#~ msgid "The cache directory must be writable (i.e. CHMOD to 755 or 777)."
|
281 |
+
#~ msgstr ""
|
282 |
+
#~ "Le dossier de cache doit être inscriptible (ex: CHMOD en 755 ou 777)."
|
283 |
+
|
284 |
+
#~ msgid "log"
|
285 |
+
#~ msgstr "log"
|
languages/bwp-minify-id_ID.mo
ADDED
Binary file
|
languages/bwp-minify-id_ID.po
ADDED
@@ -0,0 +1,341 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: BWP Minify\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2013-10-27 14:39+0700\n"
|
6 |
+
"PO-Revision-Date: 2014-06-02 17:51+0700\n"
|
7 |
+
"Last-Translator: Khang Minh <contact@betterwp.net>\n"
|
8 |
+
"Language-Team: Nasrulhaq Muiz <nasroel@al-badar.net>\n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n"
|
13 |
+
"X-Poedit-Basepath: .\n"
|
14 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
15 |
+
"X-Generator: Poedit 1.6.4\n"
|
16 |
+
"Language: id_ID\n"
|
17 |
+
"X-Poedit-SearchPath-0: .\n"
|
18 |
+
|
19 |
+
#: includes/class-bwp-framework.php:178
|
20 |
+
#, php-format
|
21 |
+
msgid ""
|
22 |
+
"%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</"
|
23 |
+
"strong> or higher. The plugin will not function until you update your "
|
24 |
+
"software. Please deactivate this plugin."
|
25 |
+
msgstr ""
|
26 |
+
"%s membutuhkan WordPress <strong>%s</strong> atau lebih tinggi dan PHP "
|
27 |
+
"<strong>%s</strong> atau lebih tinggi. Plugin tidak akan berfungsi hingga "
|
28 |
+
"anda memperbarui perangkat lunak. Harap Non Aktifkan plugin ini."
|
29 |
+
|
30 |
+
#: includes/class-bwp-framework.php:190
|
31 |
+
msgid "Development Log"
|
32 |
+
msgstr "Log Pengembangan"
|
33 |
+
|
34 |
+
#: includes/class-bwp-framework.php:190
|
35 |
+
msgid "Frequently Asked Questions"
|
36 |
+
msgstr "Pertanyaan Yang Sering Diajukan"
|
37 |
+
|
38 |
+
#: includes/class-bwp-framework.php:190
|
39 |
+
msgid "FAQ"
|
40 |
+
msgstr "FAQ"
|
41 |
+
|
42 |
+
#: includes/class-bwp-framework.php:190
|
43 |
+
msgid "Got a problem? Send me a feedback!"
|
44 |
+
msgstr "Mendapat masalah? Kirimkan umpan balik!"
|
45 |
+
|
46 |
+
#: includes/class-bwp-framework.php:190
|
47 |
+
msgid "Contact"
|
48 |
+
msgstr "Kontak"
|
49 |
+
|
50 |
+
#: includes/class-bwp-framework.php:197
|
51 |
+
msgid ""
|
52 |
+
"You can buy me some special coffees if you appreciate my work, thank you!"
|
53 |
+
msgstr ""
|
54 |
+
"Anda dapat membelikan saya kopi jika anda mengapresiasi pekerjaanku, "
|
55 |
+
"terimakasih!"
|
56 |
+
|
57 |
+
#: includes/class-bwp-framework.php:211
|
58 |
+
msgid "Donate to "
|
59 |
+
msgstr "Donasi ke"
|
60 |
+
|
61 |
+
#: includes/class-bwp-framework.php:213
|
62 |
+
msgid "One cup $5.00"
|
63 |
+
msgstr "Satu gelas $5.00"
|
64 |
+
|
65 |
+
#: includes/class-bwp-framework.php:214
|
66 |
+
msgid "Two cups $10.00"
|
67 |
+
msgstr "Dua gelas $10.00"
|
68 |
+
|
69 |
+
#: includes/class-bwp-framework.php:215
|
70 |
+
msgid "Five cups! $25.00"
|
71 |
+
msgstr "Lima gelas! $25.00"
|
72 |
+
|
73 |
+
#: includes/class-bwp-framework.php:216
|
74 |
+
msgid "One LL-cup!!! $50.00"
|
75 |
+
msgstr "1 Dobel L gelas!$50.00"
|
76 |
+
|
77 |
+
#: includes/class-bwp-framework.php:217
|
78 |
+
msgid "... or any amount!"
|
79 |
+
msgstr "... atau berapapun!"
|
80 |
+
|
81 |
+
#: includes/class-bwp-framework.php:232
|
82 |
+
msgid "Latest updates from BetterWP.net!"
|
83 |
+
msgstr "Pembaruan terakhir dari BetterWP.net!"
|
84 |
+
|
85 |
+
#: includes/class-bwp-framework.php:233
|
86 |
+
msgid "Follow me on Twitter!"
|
87 |
+
msgstr "Follow saya di Twitter!"
|
88 |
+
|
89 |
+
#: includes/class-bwp-framework.php:243
|
90 |
+
msgid "This Plugin is Proudly Sponsored By"
|
91 |
+
msgstr "Plugin ini dengan Bangga Didukung Oleh"
|
92 |
+
|
93 |
+
#: includes/class-bwp-framework.php:260
|
94 |
+
#, php-format
|
95 |
+
msgid "You are using version %s!"
|
96 |
+
msgstr "Anda menggunakan versi %s!"
|
97 |
+
|
98 |
+
#: includes/class-bwp-framework.php:396
|
99 |
+
msgid "Settings"
|
100 |
+
msgstr "Pengaturan"
|
101 |
+
|
102 |
+
#: includes/class-bwp-minify.php:109
|
103 |
+
msgid "Better WordPress Minify Settings"
|
104 |
+
msgstr "Pengaturan Better WordPress Minify"
|
105 |
+
|
106 |
+
#: includes/class-bwp-minify.php:218
|
107 |
+
msgid "Better WordPress Minify"
|
108 |
+
msgstr "Better WordPress Minify"
|
109 |
+
|
110 |
+
#: includes/class-bwp-minify.php:229
|
111 |
+
msgid "You do not have sufficient permissions to access this page."
|
112 |
+
msgstr "Anda tidak memiliki izin memadai mengakses halaman ini."
|
113 |
+
|
114 |
+
#: includes/class-bwp-minify.php:245
|
115 |
+
msgid "General Options"
|
116 |
+
msgstr "Pilihan Umum"
|
117 |
+
|
118 |
+
#: includes/class-bwp-minify.php:246
|
119 |
+
msgid "Minify JS files automatically?"
|
120 |
+
msgstr "Minify otomatis data JS ?"
|
121 |
+
|
122 |
+
#: includes/class-bwp-minify.php:247
|
123 |
+
msgid "Minify CSS files automatically?"
|
124 |
+
msgstr "Minify otomatis data CSS ?"
|
125 |
+
|
126 |
+
#: includes/class-bwp-minify.php:248
|
127 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
128 |
+
msgstr "Kecilkan <code>bloginfo()</code> stylesheets?"
|
129 |
+
|
130 |
+
#: includes/class-bwp-minify.php:249
|
131 |
+
msgid "Minifying Options"
|
132 |
+
msgstr "Pilihan Minify"
|
133 |
+
|
134 |
+
#: includes/class-bwp-minify.php:250
|
135 |
+
msgid "Minify URL (double-click to edit)"
|
136 |
+
msgstr "Mengecilkan URL (double-klik untuk mengedit)"
|
137 |
+
|
138 |
+
#: includes/class-bwp-minify.php:251
|
139 |
+
msgid "Cache directory (double-click to edit)"
|
140 |
+
msgstr "Direktori cache (double-klik untuk mengedit)"
|
141 |
+
|
142 |
+
#: includes/class-bwp-minify.php:252
|
143 |
+
msgid "One minify string will contain"
|
144 |
+
msgstr "Satu string pengecilan akan berisi"
|
145 |
+
|
146 |
+
#: includes/class-bwp-minify.php:253
|
147 |
+
msgid "Append the minify string with"
|
148 |
+
msgstr "Menambahkan string pengecilan dengan"
|
149 |
+
|
150 |
+
#: includes/class-bwp-minify.php:254
|
151 |
+
msgid "Minifying Scripts Options"
|
152 |
+
msgstr "Pilihan Skrip Pengecilan"
|
153 |
+
|
154 |
+
#: includes/class-bwp-minify.php:255
|
155 |
+
msgid "Scripts to be minified in header"
|
156 |
+
msgstr "Script yang akan minified dalam header"
|
157 |
+
|
158 |
+
#: includes/class-bwp-minify.php:256
|
159 |
+
msgid "Scripts to be minified in footer"
|
160 |
+
msgstr "Skrip yang akan di minified di footer"
|
161 |
+
|
162 |
+
#: includes/class-bwp-minify.php:257
|
163 |
+
msgid "Scripts to be minified and then printed separately"
|
164 |
+
msgstr "Skrip yang akan minified dan kemudian dicetak secara terpisah"
|
165 |
+
|
166 |
+
#: includes/class-bwp-minify.php:258
|
167 |
+
msgid "Scripts to be ignored (not minified)"
|
168 |
+
msgstr "Skrip untuk diabaikan (tidak minified)"
|
169 |
+
|
170 |
+
#: includes/class-bwp-minify.php:263
|
171 |
+
msgid "<em>Options that affect both your stylesheets and scripts.</em>"
|
172 |
+
msgstr "<em>Opsi yang mempengaruhi stylesheet dan skrip anda</em>"
|
173 |
+
|
174 |
+
#: includes/class-bwp-minify.php:264
|
175 |
+
#, php-format
|
176 |
+
msgid ""
|
177 |
+
"<em>You can force the position of each script using those inputs below (e.g. "
|
178 |
+
"you have a script registered in the header but you want to minify it in the "
|
179 |
+
"footer instead). If you are still unsure, please read more <a href="
|
180 |
+
"\"%s#positioning-your-scripts\">here</a>. Type in one script handle "
|
181 |
+
"(<strong>NOT filename</strong>) per line.</em>"
|
182 |
+
msgstr ""
|
183 |
+
"<em>Anda dapat memaksa posisi setiap script menggunakan input tersebut di "
|
184 |
+
"bawah ini (misalnya Anda memiliki script yang terdaftar di header tetapi "
|
185 |
+
"Anda ingin mengecilkan di footer sebagai gantinya). Jika Anda masih ragu, "
|
186 |
+
"silakan baca lebih lanjut <a href=\"%s#positioning-your-scripts\">di sini</ "
|
187 |
+
"a>. Ketik satu pegangan naskah (<strong>TIDAK nama file</strong>) per baris."
|
188 |
+
"</Em>"
|
189 |
+
|
190 |
+
#: includes/class-bwp-minify.php:268
|
191 |
+
msgid "second(s)"
|
192 |
+
msgstr "detik"
|
193 |
+
|
194 |
+
#: includes/class-bwp-minify.php:269
|
195 |
+
msgid "minute(s)"
|
196 |
+
msgstr "menit"
|
197 |
+
|
198 |
+
#: includes/class-bwp-minify.php:270
|
199 |
+
msgid "hour(s)"
|
200 |
+
msgstr "jam"
|
201 |
+
|
202 |
+
#: includes/class-bwp-minify.php:271
|
203 |
+
msgid "day(s)"
|
204 |
+
msgstr "hari"
|
205 |
+
|
206 |
+
#: includes/class-bwp-minify.php:274
|
207 |
+
msgid "Do not append anything"
|
208 |
+
msgstr "Jangan menambahkan apa-apa"
|
209 |
+
|
210 |
+
#: includes/class-bwp-minify.php:275
|
211 |
+
msgid "Cache folder’s last modified time"
|
212 |
+
msgstr "Folder cache’s waktu terakhir dimodifikasi"
|
213 |
+
|
214 |
+
#: includes/class-bwp-minify.php:276
|
215 |
+
msgid "Your WordPress’s current version"
|
216 |
+
msgstr "Versi WordPress’s anda saat ini"
|
217 |
+
|
218 |
+
#: includes/class-bwp-minify.php:277
|
219 |
+
msgid "Your theme’s current version"
|
220 |
+
msgstr "Versi theme’s anda saat ini"
|
221 |
+
|
222 |
+
#: includes/class-bwp-minify.php:278
|
223 |
+
msgid "A custom number"
|
224 |
+
msgstr "Sejumlah kustom"
|
225 |
+
|
226 |
+
#: includes/class-bwp-minify.php:282 includes/class-bwp-minify.php:283
|
227 |
+
msgid ""
|
228 |
+
"you can still use <code>bwp_minify()</code> helper function if you disable "
|
229 |
+
"this."
|
230 |
+
msgstr ""
|
231 |
+
"Anda masih dapat menggunakan <code>bwp_minify()</code> fungsi pembantu jika "
|
232 |
+
"Anda menonaktifkan ini."
|
233 |
+
|
234 |
+
#: includes/class-bwp-minify.php:284
|
235 |
+
msgid ""
|
236 |
+
"most themes (e.g. Twenty Ten) use <code>bloginfo()</code> to print the main "
|
237 |
+
"stylesheet (i.e. <code>style.css</code>) and BWP Minify will not be able to "
|
238 |
+
"add it to the main minify string. If you want to minify <code>style.css</"
|
239 |
+
"code> with the rest of your css files, you must enqueue it."
|
240 |
+
msgstr ""
|
241 |
+
"kebanyakan tema (misalnya Twenty Ten) menggunakan <code>bloginfo()</code> "
|
242 |
+
"untuk mencetak stylesheet utama (yaitu <code>style.css</code>) dan BWP "
|
243 |
+
"Minify tidak akan dapat menambahkannya ke String pengecilan utama . Jika "
|
244 |
+
"Anda ingin mengecilkan <code>style.css </code> dengan sisa file css Anda, "
|
245 |
+
"Anda harus enqueue itu."
|
246 |
+
|
247 |
+
#: includes/class-bwp-minify.php:287
|
248 |
+
#, php-format
|
249 |
+
msgid ""
|
250 |
+
"This should be set automatically. If you think the URL is too long, please "
|
251 |
+
"read <a href=\"%s#customization\">here</a> to know how to properly modify "
|
252 |
+
"this."
|
253 |
+
msgstr ""
|
254 |
+
"Seharusnya diset otomatis, Jika anda berpikir URL terlalu panjang, harap "
|
255 |
+
"baca <a href=\"%s#customization\">disini</a>untuk mengetahui bagaimana "
|
256 |
+
"memodifikasi dengan baik."
|
257 |
+
|
258 |
+
#: includes/class-bwp-minify.php:288
|
259 |
+
#, php-format
|
260 |
+
msgid ""
|
261 |
+
"<strong>Important</strong>: Changing cache directory is a two-step process, "
|
262 |
+
"which is described in details <a href=\"%s#advanced_customization\" target="
|
263 |
+
"\"_blank\">here</a>. Please note that cache directory must be writable (i.e. "
|
264 |
+
"CHMOD to 755 or 777)."
|
265 |
+
msgstr ""
|
266 |
+
"<strong>Penting</strong>: Merubah direktori cache itu dalam dua tahap, yang "
|
267 |
+
"telah dijelaskan secara rinci <a href=\"%s#advanced_customization\" target="
|
268 |
+
"\"_blank\">disini</a>. Harap dicatat bahwa direktori cache harus dapat "
|
269 |
+
"ditulis (mis CHMOD ke 755 atau 777)."
|
270 |
+
|
271 |
+
#: includes/class-bwp-minify.php:289
|
272 |
+
msgid "file(s) at most."
|
273 |
+
msgstr "file paling banyak."
|
274 |
+
|
275 |
+
#: includes/class-bwp-minify.php:290
|
276 |
+
msgid "—"
|
277 |
+
msgstr "—"
|
278 |
+
|
279 |
+
#: includes/class-bwp-minify.php:291
|
280 |
+
msgid "<em>→ /min/?f=file.js&ver=</em> "
|
281 |
+
msgstr "<em>→ /min/?f=file.js&ver=</em> "
|
282 |
+
|
283 |
+
#: includes/class-bwp-minify.php:301
|
284 |
+
msgid ""
|
285 |
+
"<em><strong>Note:</strong> When you append one of the things above you are "
|
286 |
+
"basically telling browsers to clear their cached version of your CSS and JS "
|
287 |
+
"files, which is very useful when you change source files. Use this feature "
|
288 |
+
"wisely :).</em>"
|
289 |
+
msgstr ""
|
290 |
+
"<em><strong>Catatan:</strong> Ketika Anda menambahkan salah satu hal di atas "
|
291 |
+
"pada dasarnya Anda memberitahu browser untuk menghapus versi cache CSS dan "
|
292 |
+
"file JS anda, yang sangat berguna ketika Anda mengubah file sumber. Gunakan "
|
293 |
+
"fitur ini dengan bijak :).</em>"
|
294 |
+
|
295 |
+
#: includes/class-bwp-minify.php:308
|
296 |
+
msgid "Flush the cache"
|
297 |
+
msgstr "Sirap cache"
|
298 |
+
|
299 |
+
#: includes/class-bwp-minify.php:328 includes/class-bwp-minify.php:330
|
300 |
+
msgid "Notice"
|
301 |
+
msgstr "Maklumat"
|
302 |
+
|
303 |
+
#: includes/class-bwp-minify.php:328
|
304 |
+
#, php-format
|
305 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
306 |
+
msgstr "<strong>%d</strong> file cache berhasil dihapus!"
|
307 |
+
|
308 |
+
#: includes/class-bwp-minify.php:330
|
309 |
+
msgid ""
|
310 |
+
"Could not delete any cached files. Please manually check the cache directory."
|
311 |
+
msgstr ""
|
312 |
+
"Tidak dapat menghapus data cache apapun. Harap periksa secara manual "
|
313 |
+
"didirektori cache."
|
314 |
+
|
315 |
+
#: includes/class-bwp-minify.php:364
|
316 |
+
msgid "All options have been saved."
|
317 |
+
msgstr "Semua pilihan telah disimpan"
|
318 |
+
|
319 |
+
#: includes/class-bwp-minify.php:380
|
320 |
+
msgid "Warning"
|
321 |
+
msgstr "Peringatan"
|
322 |
+
|
323 |
+
#: includes/class-bwp-minify.php:380
|
324 |
+
msgid ""
|
325 |
+
"Cache directory does not exist or is not writable. Please try CHMOD your "
|
326 |
+
"cache directory to 755. If you still see this warning, CHMOD to 777."
|
327 |
+
msgstr ""
|
328 |
+
"Direktori cache tidak ada atau tidak dapat ditulis. Coba CHMOD direktori "
|
329 |
+
"cache anda ke 755. Jika masih melihat peringatan ini, CHMOD ke 777."
|
330 |
+
|
331 |
+
#: includes/class-bwp-minify.php:502
|
332 |
+
msgid "empty"
|
333 |
+
msgstr "kosong"
|
334 |
+
|
335 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:80
|
336 |
+
msgid "Plugin Configurations"
|
337 |
+
msgstr "Konfigurasi Plugin"
|
338 |
+
|
339 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:398
|
340 |
+
msgid "Save Changes"
|
341 |
+
msgstr "Simpan Perubahan"
|
languages/bwp-minify-it_IT.mo
ADDED
Binary file
|
languages/bwp-minify-it_IT.po
ADDED
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: BWP Minify\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2012-03-20 21:51+0700\n"
|
6 |
+
"PO-Revision-Date: 2012-03-20 22:03+0700\n"
|
7 |
+
"Last-Translator: \n"
|
8 |
+
"Language-Team: Cook Spot <info@cookspot.it>\n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n"
|
13 |
+
"X-Poedit-Basepath: .\n"
|
14 |
+
"X-Poedit-SourceCharset: utf-8\n"
|
15 |
+
"X-Poedit-Language: Italian\n"
|
16 |
+
"X-Poedit-Country: ITALY\n"
|
17 |
+
"X-Poedit-SearchPath-0: .\n"
|
18 |
+
|
19 |
+
#: includes/class-bwp-framework.php:177
|
20 |
+
#, php-format
|
21 |
+
msgid "%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</strong> or higher. The plugin will not function until you update your software. Please deactivate this plugin."
|
22 |
+
msgstr "%s richiede WordPress <strong>%s</strong> o più recente e PHP <strong>%s</strong> o più recente. Il plugin non funzionerà finché non aggiornerai il to software. Per favore disattiva il plugin."
|
23 |
+
|
24 |
+
#: includes/class-bwp-framework.php:188
|
25 |
+
msgid "Development Log"
|
26 |
+
msgstr "Log di sviluppo"
|
27 |
+
|
28 |
+
#: includes/class-bwp-framework.php:188
|
29 |
+
msgid "Frequently Asked Questions"
|
30 |
+
msgstr "Domande più frequenti"
|
31 |
+
|
32 |
+
#: includes/class-bwp-framework.php:188
|
33 |
+
msgid "FAQ"
|
34 |
+
msgstr "FAQ"
|
35 |
+
|
36 |
+
#: includes/class-bwp-framework.php:188
|
37 |
+
msgid "Got a problem? Send me a feedback!"
|
38 |
+
msgstr "Hai un problema? Inviami un feedback!"
|
39 |
+
|
40 |
+
#: includes/class-bwp-framework.php:188
|
41 |
+
msgid "Contact"
|
42 |
+
msgstr "Contatti"
|
43 |
+
|
44 |
+
#: includes/class-bwp-framework.php:195
|
45 |
+
msgid "You can buy me some special coffees if you appreciate my work, thank you!"
|
46 |
+
msgstr "Puoi acquistare per me qualche caffè speciale, se hai apprezzato il mio lavoro, grazie mille!"
|
47 |
+
|
48 |
+
#: includes/class-bwp-framework.php:209
|
49 |
+
msgid "Donate to "
|
50 |
+
msgstr "Dona"
|
51 |
+
|
52 |
+
#: includes/class-bwp-framework.php:211
|
53 |
+
msgid "One cup $5.00"
|
54 |
+
msgstr "Una tazza $5.00"
|
55 |
+
|
56 |
+
#: includes/class-bwp-framework.php:212
|
57 |
+
msgid "Two cups $10.00"
|
58 |
+
msgstr "Due tazze $10.00"
|
59 |
+
|
60 |
+
#: includes/class-bwp-framework.php:213
|
61 |
+
msgid "Five cups! $25.00"
|
62 |
+
msgstr "Cinque tazze! $25.00"
|
63 |
+
|
64 |
+
#: includes/class-bwp-framework.php:214
|
65 |
+
msgid "One LL-cup!!! $50.00"
|
66 |
+
msgstr "Una tazza extra large!!! $50.00"
|
67 |
+
|
68 |
+
#: includes/class-bwp-framework.php:215
|
69 |
+
msgid "... or any amount!"
|
70 |
+
msgstr "...o la somma che preferisci!"
|
71 |
+
|
72 |
+
#: includes/class-bwp-framework.php:230
|
73 |
+
msgid "Latest updates from BetterWP.net!"
|
74 |
+
msgstr "Ultimi aggiornamenti da BetterWP.net!"
|
75 |
+
|
76 |
+
#: includes/class-bwp-framework.php:231
|
77 |
+
msgid "Follow me on Twitter!"
|
78 |
+
msgstr "Seguimi su Twitter!"
|
79 |
+
|
80 |
+
#: includes/class-bwp-framework.php:240
|
81 |
+
#, php-format
|
82 |
+
msgid "You are using version %s!"
|
83 |
+
msgstr "Stai utilizzando la versione %s!"
|
84 |
+
|
85 |
+
#: includes/class-bwp-framework.php:376
|
86 |
+
msgid "Settings"
|
87 |
+
msgstr "Opzioni"
|
88 |
+
|
89 |
+
#: includes/class-bwp-minify.php:110
|
90 |
+
msgid "Better WordPress Minify Settings"
|
91 |
+
msgstr "Opzioni Better Wordpress Minify"
|
92 |
+
|
93 |
+
#: includes/class-bwp-minify.php:196
|
94 |
+
msgid "Better WordPress Minify"
|
95 |
+
msgstr "Better Wordpress Minify"
|
96 |
+
|
97 |
+
#: includes/class-bwp-minify.php:207
|
98 |
+
msgid "You do not have sufficient permissions to access this page."
|
99 |
+
msgstr "Non hai sufficienti permessi per accedere a questa pagina."
|
100 |
+
|
101 |
+
#: includes/class-bwp-minify.php:223
|
102 |
+
msgid "General Options"
|
103 |
+
msgstr "Opzioni generali"
|
104 |
+
|
105 |
+
#: includes/class-bwp-minify.php:224
|
106 |
+
#, fuzzy
|
107 |
+
msgid "Minify JS files automatically?"
|
108 |
+
msgstr "Minimizza CSS e JS automaticamente?"
|
109 |
+
|
110 |
+
#: includes/class-bwp-minify.php:225
|
111 |
+
#, fuzzy
|
112 |
+
msgid "Minify CSS files automatically?"
|
113 |
+
msgstr "Minimizza CSS e JS automaticamente?"
|
114 |
+
|
115 |
+
#: includes/class-bwp-minify.php:226
|
116 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
117 |
+
msgstr "Minimizza fogli di stile <code>bloginfo()</code>?"
|
118 |
+
|
119 |
+
#: includes/class-bwp-minify.php:227
|
120 |
+
msgid "Minifying Options"
|
121 |
+
msgstr "Opzioni di minimizzazione"
|
122 |
+
|
123 |
+
#: includes/class-bwp-minify.php:228
|
124 |
+
msgid "Minify URL (double-click to edit)"
|
125 |
+
msgstr "URL di minimizzazione (doppio-click per modificare)"
|
126 |
+
|
127 |
+
#: includes/class-bwp-minify.php:229
|
128 |
+
#, fuzzy
|
129 |
+
msgid "Cache directory (double-click to edit)"
|
130 |
+
msgstr "URL di minimizzazione (doppio-click per modificare)"
|
131 |
+
|
132 |
+
#: includes/class-bwp-minify.php:230
|
133 |
+
msgid "One minify string will contain"
|
134 |
+
msgstr "Una stringa di minimizzazione conterrà"
|
135 |
+
|
136 |
+
#: includes/class-bwp-minify.php:231
|
137 |
+
msgid "Append the minify string with"
|
138 |
+
msgstr "Accoda alla stringa di minimizzazione"
|
139 |
+
|
140 |
+
#: includes/class-bwp-minify.php:232
|
141 |
+
msgid "Minifying Scripts Options"
|
142 |
+
msgstr "Opzioni di minimizzazione degli script"
|
143 |
+
|
144 |
+
#: includes/class-bwp-minify.php:233
|
145 |
+
msgid "Scripts to be minified in header"
|
146 |
+
msgstr "Script da minimizzare nell'header"
|
147 |
+
|
148 |
+
#: includes/class-bwp-minify.php:234
|
149 |
+
msgid "Scripts to be minified in footer"
|
150 |
+
msgstr "Script da minimizzare nel footer"
|
151 |
+
|
152 |
+
#: includes/class-bwp-minify.php:235
|
153 |
+
msgid "Scripts to be minified and then printed separately"
|
154 |
+
msgstr "Script da minimizzare e da tenere separati"
|
155 |
+
|
156 |
+
#: includes/class-bwp-minify.php:236
|
157 |
+
msgid "Scripts to be ignored (not minified)"
|
158 |
+
msgstr "Script da ignorare (non minimizzare)"
|
159 |
+
|
160 |
+
#: includes/class-bwp-minify.php:241
|
161 |
+
msgid "<em>Options that affect both your stylesheets and scripts.</em>"
|
162 |
+
msgstr "<em>Opzioni che hanno effetto sia sui fogli di stile che sugli script.</em>"
|
163 |
+
|
164 |
+
#: includes/class-bwp-minify.php:242
|
165 |
+
#, php-format
|
166 |
+
msgid "<em>You can force the position of each script using those inputs below (e.g. you have a script registered in the header but you want to minify it in the footer instead). If you are still unsure, please read more <a href=\"%s#positioning-your-scripts\">here</a>. Type in one script handle (<strong>NOT filename</strong>) per line.</em>"
|
167 |
+
msgstr "<em>Puoi forzare la posizione di ogni script utilizzando questi campi in basso (es. hai uno script registrato nell'header ma vuoi che sia minimizzato nel footer). Se non sei sicuro, approfondisci <a href=\"%s#positioning-your-scripts\">qui</a>. Inserisci l'handle dello script(<strong>NON il nome del file</strong>), uno per riga.</em>"
|
168 |
+
|
169 |
+
#: includes/class-bwp-minify.php:246
|
170 |
+
msgid "second(s)"
|
171 |
+
msgstr "secondo/i"
|
172 |
+
|
173 |
+
#: includes/class-bwp-minify.php:247
|
174 |
+
msgid "minute(s)"
|
175 |
+
msgstr "minuto/i"
|
176 |
+
|
177 |
+
#: includes/class-bwp-minify.php:248
|
178 |
+
msgid "hour(s)"
|
179 |
+
msgstr "ora/e"
|
180 |
+
|
181 |
+
#: includes/class-bwp-minify.php:249
|
182 |
+
msgid "day(s)"
|
183 |
+
msgstr "giorno/i"
|
184 |
+
|
185 |
+
#: includes/class-bwp-minify.php:252
|
186 |
+
msgid "Do not append anything"
|
187 |
+
msgstr "Non accodare nessun suffisso"
|
188 |
+
|
189 |
+
#: includes/class-bwp-minify.php:253
|
190 |
+
msgid "Cache folder’s last modified time"
|
191 |
+
msgstr "Data ed ora dell’ultima modifica della cartella della cache"
|
192 |
+
|
193 |
+
#: includes/class-bwp-minify.php:254
|
194 |
+
msgid "Your WordPress’s current version"
|
195 |
+
msgstr "La versione corrente di questo Wordpress"
|
196 |
+
|
197 |
+
#: includes/class-bwp-minify.php:255
|
198 |
+
msgid "A custom number"
|
199 |
+
msgstr "Un numero persomalizzato"
|
200 |
+
|
201 |
+
#: includes/class-bwp-minify.php:259
|
202 |
+
#: includes/class-bwp-minify.php:260
|
203 |
+
#, fuzzy
|
204 |
+
msgid "you can still use <code>bwp_minify()</code> helper function if you disable this."
|
205 |
+
msgstr "puoi comunque ancora utilizzare la funzione di template <code>bwp_minify()</code> se disattivi questa opzione."
|
206 |
+
|
207 |
+
#: includes/class-bwp-minify.php:261
|
208 |
+
msgid "most themes (e.g. Twenty Ten) use <code>bloginfo()</code> to print the main stylesheet (i.e. <code>style.css</code>) and BWP Minify will not be able to add it to the main minify string. If you want to minify <code>style.css</code> with the rest of your css files, you must enqueue it."
|
209 |
+
msgstr "molti temi (es. Twenty Ten) utilizzano <code>bloginfo()</code> per produrre l'output del foglio di stile principale (es. <code>style.css</code>) e BWP non sarà in grado di aggiungerlo alla stringa di minimizzazione principale. Se vuoi minimizzare <code>style.css</code> con gli altri, seleziona questa opzione."
|
210 |
+
|
211 |
+
#: includes/class-bwp-minify.php:264
|
212 |
+
#, php-format
|
213 |
+
msgid "This should be set automatically. If you think the URL is too long, please read <a href=\"%s#customization\">here</a> to know how to properly modify this."
|
214 |
+
msgstr "Dovrebbe essere impostato automaticamente. Se ritieni che questo URL sia troppo lungo, fai riferimento <a href=\"%s#customization\">qui</a> per le informazioni su come modificarlo."
|
215 |
+
|
216 |
+
#: includes/class-bwp-minify.php:265
|
217 |
+
#, php-format
|
218 |
+
msgid "<strong>Important</strong>: Changing cache directory is a two-step process, which is described in details <a href=\"%s#config\" target=\"_blank\">here</a>. Please note that cache directory must be writable (i.e. CHMOD to 755 or 777)."
|
219 |
+
msgstr ""
|
220 |
+
|
221 |
+
#: includes/class-bwp-minify.php:266
|
222 |
+
msgid "file(s) at most."
|
223 |
+
msgstr "file (massimo)."
|
224 |
+
|
225 |
+
#: includes/class-bwp-minify.php:267
|
226 |
+
msgid "—"
|
227 |
+
msgstr "—"
|
228 |
+
|
229 |
+
#: includes/class-bwp-minify.php:268
|
230 |
+
msgid "<em>→ /min/?f=file.js&ver=</em> "
|
231 |
+
msgstr "<em>→ /min/?f=file.js&ver=</em> "
|
232 |
+
|
233 |
+
#: includes/class-bwp-minify.php:278
|
234 |
+
msgid "<em><strong>Note:</strong> When you append one of the things above you are basically telling browsers to clear their cached version of your CSS and JS files, which is very useful when you change source files. Use this feature wisely :).</em>"
|
235 |
+
msgstr "<em><strong>Nota:</strong> quando apponi uno di questi suffissi, praticamente stai dicendo al browser di eliminare la versione in cache dei tuoi CSS e JS. Ciò è molto utile quando effettui delle modifiche ai tuoi sorgenti. Utilizza questa opzione con saggezza :).</em>"
|
236 |
+
|
237 |
+
#: includes/class-bwp-minify.php:285
|
238 |
+
msgid "Flush the cache"
|
239 |
+
msgstr ""
|
240 |
+
|
241 |
+
#: includes/class-bwp-minify.php:305
|
242 |
+
#: includes/class-bwp-minify.php:307
|
243 |
+
msgid "Notice"
|
244 |
+
msgstr ""
|
245 |
+
|
246 |
+
#: includes/class-bwp-minify.php:305
|
247 |
+
#, php-format
|
248 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
249 |
+
msgstr ""
|
250 |
+
|
251 |
+
#: includes/class-bwp-minify.php:307
|
252 |
+
msgid "Could not delete any cached files. Please manually check the cache directory."
|
253 |
+
msgstr ""
|
254 |
+
|
255 |
+
#: includes/class-bwp-minify.php:341
|
256 |
+
msgid "All options have been saved."
|
257 |
+
msgstr ""
|
258 |
+
|
259 |
+
#: includes/class-bwp-minify.php:357
|
260 |
+
msgid "Warning"
|
261 |
+
msgstr "Attenzione"
|
262 |
+
|
263 |
+
#: includes/class-bwp-minify.php:357
|
264 |
+
msgid "Cache directory does not exist or is not writable. Please try CHMOD your cache directory to 755. If you still see this warning, CHMOD to 777."
|
265 |
+
msgstr "La directory della cache non esiste o non è accessibile in scrittura. Per favore, prova ad effettuare un CHMOD sulla directory a 755. Se continui a vedere questo messaggio, CHMOD a 777."
|
266 |
+
|
267 |
+
#: includes/class-bwp-minify.php:471
|
268 |
+
msgid "empty"
|
269 |
+
msgstr "vuoto"
|
270 |
+
|
271 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:80
|
272 |
+
msgid "Plugin Configurations"
|
273 |
+
msgstr "Configurazione del plugin"
|
274 |
+
|
275 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:398
|
276 |
+
msgid "Save Changes"
|
277 |
+
msgstr "Salva le modifiche"
|
278 |
+
|
279 |
+
#~ msgid "Cache will be stored in (by default)"
|
280 |
+
#~ msgstr "La cache sarà memorizzata in (per default)"
|
281 |
+
|
282 |
+
#~ msgid "The cache directory must be writable (i.e. CHMOD to 755 or 777)."
|
283 |
+
#~ msgstr ""
|
284 |
+
#~ "La directory della cache deve essere accessibile in scrittura (es. CHMOD "
|
285 |
+
#~ "a 755 o 777)."
|
languages/bwp-minify-nl_NL.mo
ADDED
Binary file
|
languages/bwp-minify-nl_NL.po
ADDED
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: BWP Minify\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2012-10-07 22:54+0700\n"
|
6 |
+
"PO-Revision-Date: 2013-08-21 09:43+0100\n"
|
7 |
+
"Last-Translator: Martijn van Egmond <martijn@tensheep.nl>\n"
|
8 |
+
"Language-Team: \n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n"
|
13 |
+
"X-Poedit-Basepath: .\n"
|
14 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
15 |
+
"X-Generator: Poedit 1.5.7\n"
|
16 |
+
"X-Poedit-SearchPath-0: .\n"
|
17 |
+
|
18 |
+
#: includes/class-bwp-framework.php:178
|
19 |
+
#, php-format
|
20 |
+
msgid ""
|
21 |
+
"%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</"
|
22 |
+
"strong> or higher. The plugin will not function until you update your "
|
23 |
+
"software. Please deactivate this plugin."
|
24 |
+
msgstr ""
|
25 |
+
"%s vereist WordPress <strong>%s </strong> of hoger en PHP <strong>%s </"
|
26 |
+
"strong> of hoger. De plugin zal niet werken totdat de software geupdate is. "
|
27 |
+
"Deactiveer de plugin."
|
28 |
+
|
29 |
+
#: includes/class-bwp-framework.php:189
|
30 |
+
msgid "Development Log"
|
31 |
+
msgstr "Development Log"
|
32 |
+
|
33 |
+
#: includes/class-bwp-framework.php:189
|
34 |
+
msgid "Frequently Asked Questions"
|
35 |
+
msgstr "Veel gestelde vragen"
|
36 |
+
|
37 |
+
#: includes/class-bwp-framework.php:189
|
38 |
+
msgid "FAQ"
|
39 |
+
msgstr "FAQ"
|
40 |
+
|
41 |
+
#: includes/class-bwp-framework.php:189
|
42 |
+
msgid "Got a problem? Send me a feedback!"
|
43 |
+
msgstr "Heb je een probleem? Stuur mij feedback!"
|
44 |
+
|
45 |
+
#: includes/class-bwp-framework.php:189
|
46 |
+
msgid "Contact"
|
47 |
+
msgstr "Contact"
|
48 |
+
|
49 |
+
#: includes/class-bwp-framework.php:196
|
50 |
+
msgid ""
|
51 |
+
"You can buy me some special coffees if you appreciate my work, thank you!"
|
52 |
+
msgstr ""
|
53 |
+
"Je kunt voor mijeen aantal speciale koffies kopen wanneer je mijn werk "
|
54 |
+
"waardeert, bedankt!"
|
55 |
+
|
56 |
+
#: includes/class-bwp-framework.php:210
|
57 |
+
msgid "Donate to "
|
58 |
+
msgstr "Doneer"
|
59 |
+
|
60 |
+
#: includes/class-bwp-framework.php:212
|
61 |
+
msgid "One cup $5.00"
|
62 |
+
msgstr "Een kopje $5,00"
|
63 |
+
|
64 |
+
#: includes/class-bwp-framework.php:213
|
65 |
+
msgid "Two cups $10.00"
|
66 |
+
msgstr "Twee kopjes $10,00"
|
67 |
+
|
68 |
+
#: includes/class-bwp-framework.php:214
|
69 |
+
msgid "Five cups! $25.00"
|
70 |
+
msgstr "Vijf kopjes! $25,00"
|
71 |
+
|
72 |
+
#: includes/class-bwp-framework.php:215
|
73 |
+
msgid "One LL-cup!!! $50.00"
|
74 |
+
msgstr "Eén LL-kopje!!! $50,00"
|
75 |
+
|
76 |
+
#: includes/class-bwp-framework.php:216
|
77 |
+
msgid "... or any amount!"
|
78 |
+
msgstr "… of een ander bedrag!"
|
79 |
+
|
80 |
+
#: includes/class-bwp-framework.php:231
|
81 |
+
msgid "Latest updates from BetterWP.net!"
|
82 |
+
msgstr "Meest recente updates van BetterWP.net!"
|
83 |
+
|
84 |
+
#: includes/class-bwp-framework.php:232
|
85 |
+
msgid "Follow me on Twitter!"
|
86 |
+
msgstr "Volg mij op Twitter!"
|
87 |
+
|
88 |
+
#: includes/class-bwp-framework.php:241
|
89 |
+
#, php-format
|
90 |
+
msgid "You are using version %s!"
|
91 |
+
msgstr "Je maakt gebruikt van versie %s!"
|
92 |
+
|
93 |
+
#: includes/class-bwp-framework.php:377
|
94 |
+
msgid "Settings"
|
95 |
+
msgstr "Instellingen"
|
96 |
+
|
97 |
+
#: includes/class-bwp-minify.php:110
|
98 |
+
msgid "Better WordPress Minify Settings"
|
99 |
+
msgstr "Better WordPress Minify instellingen"
|
100 |
+
|
101 |
+
#: includes/class-bwp-minify.php:206
|
102 |
+
msgid "Better WordPress Minify"
|
103 |
+
msgstr "Better WordPress Minify"
|
104 |
+
|
105 |
+
#: includes/class-bwp-minify.php:217
|
106 |
+
msgid "You do not have sufficient permissions to access this page."
|
107 |
+
msgstr "Je hebt niet de juiste rechten om deze pagina te bezoeken."
|
108 |
+
|
109 |
+
#: includes/class-bwp-minify.php:233
|
110 |
+
msgid "General Options"
|
111 |
+
msgstr "Algemene opties"
|
112 |
+
|
113 |
+
#: includes/class-bwp-minify.php:234
|
114 |
+
msgid "Minify JS files automatically?"
|
115 |
+
msgstr "Minify JS bestanden automatisch?"
|
116 |
+
|
117 |
+
#: includes/class-bwp-minify.php:235
|
118 |
+
msgid "Minify CSS files automatically?"
|
119 |
+
msgstr "Minify CSS bestanden automatisch?"
|
120 |
+
|
121 |
+
#: includes/class-bwp-minify.php:236
|
122 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
123 |
+
msgstr "Minify <code>bloginfo()</code> stylesheets?"
|
124 |
+
|
125 |
+
#: includes/class-bwp-minify.php:237
|
126 |
+
msgid "Minifying Options"
|
127 |
+
msgstr "Minifying opties"
|
128 |
+
|
129 |
+
#: includes/class-bwp-minify.php:238
|
130 |
+
msgid "Minify URL (double-click to edit)"
|
131 |
+
msgstr "Minify URL (dubbel-click om te bewerken)"
|
132 |
+
|
133 |
+
#: includes/class-bwp-minify.php:239
|
134 |
+
msgid "Cache directory (double-click to edit)"
|
135 |
+
msgstr "Cache directory (dubbel-click om te bewerken)"
|
136 |
+
|
137 |
+
#: includes/class-bwp-minify.php:240
|
138 |
+
msgid "One minify string will contain"
|
139 |
+
msgstr "Eén minify string zal dit aantal bevatten"
|
140 |
+
|
141 |
+
#: includes/class-bwp-minify.php:241
|
142 |
+
msgid "Append the minify string with"
|
143 |
+
msgstr "Voeg dit aan de minify string toe"
|
144 |
+
|
145 |
+
#: includes/class-bwp-minify.php:242
|
146 |
+
msgid "Minifying Scripts Options"
|
147 |
+
msgstr "Minifying scripts opties"
|
148 |
+
|
149 |
+
#: includes/class-bwp-minify.php:243
|
150 |
+
msgid "Scripts to be minified in header"
|
151 |
+
msgstr "Scripts die in de header geminified worden"
|
152 |
+
|
153 |
+
#: includes/class-bwp-minify.php:244
|
154 |
+
msgid "Scripts to be minified in footer"
|
155 |
+
msgstr "Scripts die in de footer geminified worden"
|
156 |
+
|
157 |
+
#: includes/class-bwp-minify.php:245
|
158 |
+
msgid "Scripts to be minified and then printed separately"
|
159 |
+
msgstr ""
|
160 |
+
"Scripts die geminified worden en dan in losse bestanden worden geplaatst"
|
161 |
+
|
162 |
+
#: includes/class-bwp-minify.php:246
|
163 |
+
msgid "Scripts to be ignored (not minified)"
|
164 |
+
msgstr "Scripts die worden genegeerd (niet geminified)"
|
165 |
+
|
166 |
+
#: includes/class-bwp-minify.php:251
|
167 |
+
msgid "<em>Options that affect both your stylesheets and scripts.</em>"
|
168 |
+
msgstr ""
|
169 |
+
"<em>Opties die effect hebben op zowel de aanwezige stylesheets als scripts.</"
|
170 |
+
"em>"
|
171 |
+
|
172 |
+
#: includes/class-bwp-minify.php:252
|
173 |
+
#, php-format
|
174 |
+
msgid ""
|
175 |
+
"<em>You can force the position of each script using those inputs below (e.g. "
|
176 |
+
"you have a script registered in the header but you want to minify it in the "
|
177 |
+
"footer instead). If you are still unsure, please read more <a href="
|
178 |
+
"\"%s#positioning-your-scripts\">here</a>. Type in one script handle "
|
179 |
+
"(<strong>NOT filename</strong>) per line.</em>"
|
180 |
+
msgstr ""
|
181 |
+
"<em>Je kan de positie van elk script bepalen met de onderstaande "
|
182 |
+
"instellingen (bijv. er is een script in de header, maar de nieuwe versie wil "
|
183 |
+
"je in de footer hebben). Als je niet zeker weet hoe het werk, lees <a href="
|
184 |
+
"\"%s#positioning-your-scripts\">hier</a> meer. Type een script handle "
|
185 |
+
"(<strong> GEEN bestandsnaam</strong>) per regel.</em>"
|
186 |
+
|
187 |
+
#: includes/class-bwp-minify.php:256
|
188 |
+
msgid "second(s)"
|
189 |
+
msgstr "seconde(n)"
|
190 |
+
|
191 |
+
#: includes/class-bwp-minify.php:257
|
192 |
+
msgid "minute(s)"
|
193 |
+
msgstr "minuut(/minuten)"
|
194 |
+
|
195 |
+
#: includes/class-bwp-minify.php:258
|
196 |
+
msgid "hour(s)"
|
197 |
+
msgstr "uur(/uren)"
|
198 |
+
|
199 |
+
#: includes/class-bwp-minify.php:259
|
200 |
+
msgid "day(s)"
|
201 |
+
msgstr "dag(en)"
|
202 |
+
|
203 |
+
#: includes/class-bwp-minify.php:262
|
204 |
+
msgid "Do not append anything"
|
205 |
+
msgstr "Voeg niets toe"
|
206 |
+
|
207 |
+
#: includes/class-bwp-minify.php:263
|
208 |
+
msgid "Cache folder’s last modified time"
|
209 |
+
msgstr "Cache folder’s laatst gewijzigde tijd"
|
210 |
+
|
211 |
+
#: includes/class-bwp-minify.php:264
|
212 |
+
msgid "Your WordPress’s current version"
|
213 |
+
msgstr "Jouw WordPress’s huidige versie"
|
214 |
+
|
215 |
+
#: includes/class-bwp-minify.php:265
|
216 |
+
msgid "Your theme’s current version"
|
217 |
+
msgstr "Jouw thema’s huidige versie"
|
218 |
+
|
219 |
+
#: includes/class-bwp-minify.php:266
|
220 |
+
msgid "A custom number"
|
221 |
+
msgstr "Een aangepast getal"
|
222 |
+
|
223 |
+
#: includes/class-bwp-minify.php:270 includes/class-bwp-minify.php:271
|
224 |
+
msgid ""
|
225 |
+
"you can still use <code>bwp_minify()</code> helper function if you disable "
|
226 |
+
"this."
|
227 |
+
msgstr ""
|
228 |
+
"u kunt nog steeds gebruik maken van <code>bwp_minify ()</code>helper functie "
|
229 |
+
"als je deze uitschakelt."
|
230 |
+
|
231 |
+
#: includes/class-bwp-minify.php:272
|
232 |
+
msgid ""
|
233 |
+
"most themes (e.g. Twenty Ten) use <code>bloginfo()</code> to print the main "
|
234 |
+
"stylesheet (i.e. <code>style.css</code>) and BWP Minify will not be able to "
|
235 |
+
"add it to the main minify string. If you want to minify <code>style.css</"
|
236 |
+
"code> with the rest of your css files, you must enqueue it."
|
237 |
+
msgstr ""
|
238 |
+
"de meeste thema's (bijv. Twenty Ten) maken gebruik van <code>bloginfo()</"
|
239 |
+
"code> om de stylesheet te plaatsen (d.w.z. <code>style.css</code> "
|
240 |
+
|
241 |
+
#: includes/class-bwp-minify.php:275
|
242 |
+
#, php-format
|
243 |
+
msgid ""
|
244 |
+
"This should be set automatically. If you think the URL is too long, please "
|
245 |
+
"read <a href=\"%s#customization\">here</a> to know how to properly modify "
|
246 |
+
"this."
|
247 |
+
msgstr ""
|
248 |
+
"Deze moet automatisch worden ingesteld. Als je denkt dat de URL te lang is, "
|
249 |
+
"lees dan <a href=\"%s#customization\">hier</a> hoe je dit zelf op de juiste "
|
250 |
+
"manier kan aanpassen."
|
251 |
+
|
252 |
+
#: includes/class-bwp-minify.php:276
|
253 |
+
#, php-format
|
254 |
+
msgid ""
|
255 |
+
"<strong>Important</strong>: Changing cache directory is a two-step process, "
|
256 |
+
"which is described in details <a href=\"%s#advanced_customization\" target="
|
257 |
+
"\"_blank\">here</a>. Please note that cache directory must be writable (i.e. "
|
258 |
+
"CHMOD to 755 or 777)."
|
259 |
+
msgstr ""
|
260 |
+
"<strong>Belangrijk</strong>: Het wijzigen van de cache directory is een "
|
261 |
+
"proces van twee stappen, die <a href=\"%s#advanced_customization\" target="
|
262 |
+
"\"_blank\">hier</a> beschreven staan. Houd er rekening mee dat de cache "
|
263 |
+
"directory schrijfbaar moet zijn (d.w.z. CHMOD 755 of 777)."
|
264 |
+
|
265 |
+
#: includes/class-bwp-minify.php:277
|
266 |
+
msgid "file(s) at most."
|
267 |
+
msgstr "maximaal aantal bestanden."
|
268 |
+
|
269 |
+
#: includes/class-bwp-minify.php:278
|
270 |
+
msgid "—"
|
271 |
+
msgstr "—"
|
272 |
+
|
273 |
+
#: includes/class-bwp-minify.php:279
|
274 |
+
msgid "<em>→ /min/?f=file.js&ver=</em> "
|
275 |
+
msgstr "<em>→ /min/?f=file.js&ver=</em> "
|
276 |
+
|
277 |
+
#: includes/class-bwp-minify.php:289
|
278 |
+
msgid ""
|
279 |
+
"<em><strong>Note:</strong> When you append one of the things above you are "
|
280 |
+
"basically telling browsers to clear their cached version of your CSS and JS "
|
281 |
+
"files, which is very useful when you change source files. Use this feature "
|
282 |
+
"wisely :).</em>"
|
283 |
+
msgstr ""
|
284 |
+
"<em><strong>Opmerking:</strong> Wanneer je één van de bovenstaande toevoegd "
|
285 |
+
"wordt er aan de browser doorgegeven om de cache bestanden van jou CSS and JS "
|
286 |
+
"bestanden te verwijderen, dit is zeer nuttig wanneer je de bronbestanden "
|
287 |
+
"wijzigt. Gebruik deze functie verstandig :).</em>"
|
288 |
+
|
289 |
+
#: includes/class-bwp-minify.php:296
|
290 |
+
msgid "Flush the cache"
|
291 |
+
msgstr "Verwijder de cache"
|
292 |
+
|
293 |
+
#: includes/class-bwp-minify.php:316 includes/class-bwp-minify.php:318
|
294 |
+
msgid "Notice"
|
295 |
+
msgstr "Opmerking"
|
296 |
+
|
297 |
+
#: includes/class-bwp-minify.php:316
|
298 |
+
#, php-format
|
299 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
300 |
+
msgstr ""
|
301 |
+
"<strong>%d</strong> alle bestanden in de cache zijn succesvol verwijderd!"
|
302 |
+
|
303 |
+
#: includes/class-bwp-minify.php:318
|
304 |
+
msgid ""
|
305 |
+
"Could not delete any cached files. Please manually check the cache directory."
|
306 |
+
msgstr ""
|
307 |
+
"Kan geen enkel bestand in de cache verwijderen. Check de cache directory "
|
308 |
+
"handmatig."
|
309 |
+
|
310 |
+
#: includes/class-bwp-minify.php:352
|
311 |
+
msgid "All options have been saved."
|
312 |
+
msgstr "Alle opties zijn opgeslagen"
|
313 |
+
|
314 |
+
#: includes/class-bwp-minify.php:368
|
315 |
+
msgid "Warning"
|
316 |
+
msgstr "Waarschuwing"
|
317 |
+
|
318 |
+
#: includes/class-bwp-minify.php:368
|
319 |
+
msgid ""
|
320 |
+
"Cache directory does not exist or is not writable. Please try CHMOD your "
|
321 |
+
"cache directory to 755. If you still see this warning, CHMOD to 777."
|
322 |
+
msgstr ""
|
323 |
+
"Cache directory bestand niet of is niet schrijfbaar. Probeer de "
|
324 |
+
"schrijfrechten van de cache directory te wijzigen naar CHMOD 755. Als je nog "
|
325 |
+
"steeds deze waarschuwing ziet, wijzig dan naar CHMOD 777."
|
326 |
+
|
327 |
+
#: includes/class-bwp-minify.php:490
|
328 |
+
msgid "empty"
|
329 |
+
msgstr "leeg"
|
330 |
+
|
331 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:80
|
332 |
+
msgid "Plugin Configurations"
|
333 |
+
msgstr "Plugin configuratie"
|
334 |
+
|
335 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:398
|
336 |
+
msgid "Save Changes"
|
337 |
+
msgstr "Wijzigingen opslaan"
|
languages/bwp-minify-ro_RO.mo
ADDED
Binary file
|
languages/bwp-minify-ro_RO.po
ADDED
@@ -0,0 +1,286 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: BWP Minify\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2012-03-20 21:51+0700\n"
|
6 |
+
"PO-Revision-Date: 2012-03-20 21:51+0700\n"
|
7 |
+
"Last-Translator: \n"
|
8 |
+
"Language-Team: \n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n"
|
13 |
+
"X-Poedit-Basepath: .\n"
|
14 |
+
"X-Poedit-SourceCharset: utf-8\n"
|
15 |
+
"X-Poedit-SearchPath-0: .\n"
|
16 |
+
|
17 |
+
#: includes/class-bwp-framework.php:177
|
18 |
+
#, php-format
|
19 |
+
msgid "%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</strong> or higher. The plugin will not function until you update your software. Please deactivate this plugin."
|
20 |
+
msgstr "%s necesita versiunea de Wordpress <strong>%s</strong> sau mai mare si PHP <strong>%s</strong> sau mai mare. Plugin-ul nu va functiona pana nu actualizati programele. Va rugam dezactivati plugin-ul."
|
21 |
+
|
22 |
+
#: includes/class-bwp-framework.php:188
|
23 |
+
msgid "Development Log"
|
24 |
+
msgstr "Log dezvoltare"
|
25 |
+
|
26 |
+
#: includes/class-bwp-framework.php:188
|
27 |
+
msgid "Frequently Asked Questions"
|
28 |
+
msgstr "Intrebari frecvente"
|
29 |
+
|
30 |
+
#: includes/class-bwp-framework.php:188
|
31 |
+
msgid "FAQ"
|
32 |
+
msgstr "FAQ"
|
33 |
+
|
34 |
+
#: includes/class-bwp-framework.php:188
|
35 |
+
msgid "Got a problem? Send me a feedback!"
|
36 |
+
msgstr "Ai o problema? Trimite-mi feedback!"
|
37 |
+
|
38 |
+
#: includes/class-bwp-framework.php:188
|
39 |
+
msgid "Contact"
|
40 |
+
msgstr "Contact"
|
41 |
+
|
42 |
+
#: includes/class-bwp-framework.php:195
|
43 |
+
#, fuzzy
|
44 |
+
msgid "You can buy me some special coffees if you appreciate my work, thank you!"
|
45 |
+
msgstr "Imi poti face cinste cu o cafea, daca apreciezi munca mea!"
|
46 |
+
|
47 |
+
#: includes/class-bwp-framework.php:209
|
48 |
+
msgid "Donate to "
|
49 |
+
msgstr "Doneaza"
|
50 |
+
|
51 |
+
#: includes/class-bwp-framework.php:211
|
52 |
+
msgid "One cup $5.00"
|
53 |
+
msgstr "O cana $5.00"
|
54 |
+
|
55 |
+
#: includes/class-bwp-framework.php:212
|
56 |
+
msgid "Two cups $10.00"
|
57 |
+
msgstr "2 cani $10.00"
|
58 |
+
|
59 |
+
#: includes/class-bwp-framework.php:213
|
60 |
+
msgid "Five cups! $25.00"
|
61 |
+
msgstr "5 cani $25.00"
|
62 |
+
|
63 |
+
#: includes/class-bwp-framework.php:214
|
64 |
+
msgid "One LL-cup!!! $50.00"
|
65 |
+
msgstr "O cana extra-large $50.00"
|
66 |
+
|
67 |
+
#: includes/class-bwp-framework.php:215
|
68 |
+
msgid "... or any amount!"
|
69 |
+
msgstr "... sau orice suma!"
|
70 |
+
|
71 |
+
#: includes/class-bwp-framework.php:230
|
72 |
+
msgid "Latest updates from BetterWP.net!"
|
73 |
+
msgstr "Cele mai recent update-uri de la BetterWP.net!"
|
74 |
+
|
75 |
+
#: includes/class-bwp-framework.php:231
|
76 |
+
msgid "Follow me on Twitter!"
|
77 |
+
msgstr "Urmareste-ma pe Twitter!"
|
78 |
+
|
79 |
+
#: includes/class-bwp-framework.php:240
|
80 |
+
#, php-format
|
81 |
+
msgid "You are using version %s!"
|
82 |
+
msgstr "Folosesti versiunea %s!"
|
83 |
+
|
84 |
+
#: includes/class-bwp-framework.php:376
|
85 |
+
msgid "Settings"
|
86 |
+
msgstr "Setari"
|
87 |
+
|
88 |
+
#: includes/class-bwp-minify.php:110
|
89 |
+
msgid "Better WordPress Minify Settings"
|
90 |
+
msgstr "Setari Better WordPress Minify "
|
91 |
+
|
92 |
+
#: includes/class-bwp-minify.php:196
|
93 |
+
msgid "Better WordPress Minify"
|
94 |
+
msgstr "Better WordPress Minify"
|
95 |
+
|
96 |
+
#: includes/class-bwp-minify.php:207
|
97 |
+
msgid "You do not have sufficient permissions to access this page."
|
98 |
+
msgstr "Nu aveti permisiuni suficiente pentru a accesa aceasta pagina!"
|
99 |
+
|
100 |
+
#: includes/class-bwp-minify.php:223
|
101 |
+
msgid "General Options"
|
102 |
+
msgstr "Optiuni generale"
|
103 |
+
|
104 |
+
#: includes/class-bwp-minify.php:224
|
105 |
+
#, fuzzy
|
106 |
+
msgid "Minify JS files automatically?"
|
107 |
+
msgstr "Fa compresie pentru fisierele CSS, JS automat?"
|
108 |
+
|
109 |
+
#: includes/class-bwp-minify.php:225
|
110 |
+
#, fuzzy
|
111 |
+
msgid "Minify CSS files automatically?"
|
112 |
+
msgstr "Fa compresie pentru fisierele CSS, JS automat?"
|
113 |
+
|
114 |
+
#: includes/class-bwp-minify.php:226
|
115 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
116 |
+
msgstr "Fa compresie la stylesheet-urile <code>bloginfo()</code>?"
|
117 |
+
|
118 |
+
#: includes/class-bwp-minify.php:227
|
119 |
+
msgid "Minifying Options"
|
120 |
+
msgstr "Optiuni de compresie"
|
121 |
+
|
122 |
+
#: includes/class-bwp-minify.php:228
|
123 |
+
msgid "Minify URL (double-click to edit)"
|
124 |
+
msgstr "Compresie URL (faceti dublu clic pentru a edita)"
|
125 |
+
|
126 |
+
#: includes/class-bwp-minify.php:229
|
127 |
+
#, fuzzy
|
128 |
+
msgid "Cache directory (double-click to edit)"
|
129 |
+
msgstr "Compresie URL (faceti dublu clic pentru a edita)"
|
130 |
+
|
131 |
+
#: includes/class-bwp-minify.php:230
|
132 |
+
msgid "One minify string will contain"
|
133 |
+
msgstr "Un string minify (de compresie) va contine"
|
134 |
+
|
135 |
+
#: includes/class-bwp-minify.php:231
|
136 |
+
msgid "Append the minify string with"
|
137 |
+
msgstr "Adauga string-ului"
|
138 |
+
|
139 |
+
#: includes/class-bwp-minify.php:232
|
140 |
+
msgid "Minifying Scripts Options"
|
141 |
+
msgstr "Optiuni de script Minify"
|
142 |
+
|
143 |
+
#: includes/class-bwp-minify.php:233
|
144 |
+
msgid "Scripts to be minified in header"
|
145 |
+
msgstr "Scripturi de comprimat in header"
|
146 |
+
|
147 |
+
#: includes/class-bwp-minify.php:234
|
148 |
+
msgid "Scripts to be minified in footer"
|
149 |
+
msgstr "Scripturi de comprimat in footer"
|
150 |
+
|
151 |
+
#: includes/class-bwp-minify.php:235
|
152 |
+
msgid "Scripts to be minified and then printed separately"
|
153 |
+
msgstr "Scripturi de comprimat si imprimate separate"
|
154 |
+
|
155 |
+
#: includes/class-bwp-minify.php:236
|
156 |
+
msgid "Scripts to be ignored (not minified)"
|
157 |
+
msgstr "Scripturi de ignorat (nu vor fi comprimate)"
|
158 |
+
|
159 |
+
#: includes/class-bwp-minify.php:241
|
160 |
+
msgid "<em>Options that affect both your stylesheets and scripts.</em>"
|
161 |
+
msgstr "<em>Optiuni care afecteaza scripturile si stylesheet-urile.</em>"
|
162 |
+
|
163 |
+
#: includes/class-bwp-minify.php:242
|
164 |
+
#, php-format
|
165 |
+
msgid "<em>You can force the position of each script using those inputs below (e.g. you have a script registered in the header but you want to minify it in the footer instead). If you are still unsure, please read more <a href=\"%s#positioning-your-scripts\">here</a>. Type in one script handle (<strong>NOT filename</strong>) per line.</em>"
|
166 |
+
msgstr "<em>Puteti forta pozitia fiecarui script folosind datele de mai jos (ex. aveti un script in header dar vreti sa il comprimati in footer). Pentru mai multe informatii accesati pagina de <a href=\"%s#positioning-your-scripts\">aici</a>. Introduceti o comanda (<strong>NU nume de fisier</strong>) pe linie.</em>"
|
167 |
+
|
168 |
+
#: includes/class-bwp-minify.php:246
|
169 |
+
msgid "second(s)"
|
170 |
+
msgstr "secunda(-e)"
|
171 |
+
|
172 |
+
#: includes/class-bwp-minify.php:247
|
173 |
+
msgid "minute(s)"
|
174 |
+
msgstr "minut(-e)"
|
175 |
+
|
176 |
+
#: includes/class-bwp-minify.php:248
|
177 |
+
msgid "hour(s)"
|
178 |
+
msgstr "ora(-e)"
|
179 |
+
|
180 |
+
#: includes/class-bwp-minify.php:249
|
181 |
+
msgid "day(s)"
|
182 |
+
msgstr "zi(le)"
|
183 |
+
|
184 |
+
#: includes/class-bwp-minify.php:252
|
185 |
+
msgid "Do not append anything"
|
186 |
+
msgstr "Nu adauga nimic"
|
187 |
+
|
188 |
+
#: includes/class-bwp-minify.php:253
|
189 |
+
msgid "Cache folder’s last modified time"
|
190 |
+
msgstr "Folder deCache ’s modificat ultima data"
|
191 |
+
|
192 |
+
#: includes/class-bwp-minify.php:254
|
193 |
+
msgid "Your WordPress’s current version"
|
194 |
+
msgstr "Versiunea curenta de WordPress’s"
|
195 |
+
|
196 |
+
#: includes/class-bwp-minify.php:255
|
197 |
+
msgid "A custom number"
|
198 |
+
msgstr "Un numar personalizat"
|
199 |
+
|
200 |
+
#: includes/class-bwp-minify.php:259
|
201 |
+
#: includes/class-bwp-minify.php:260
|
202 |
+
#, fuzzy
|
203 |
+
msgid "you can still use <code>bwp_minify()</code> helper function if you disable this."
|
204 |
+
msgstr "puteti folosi in continuare functia de template function <code>bwp_minify()</code> daca dezactivati."
|
205 |
+
|
206 |
+
#: includes/class-bwp-minify.php:261
|
207 |
+
msgid "most themes (e.g. Twenty Ten) use <code>bloginfo()</code> to print the main stylesheet (i.e. <code>style.css</code>) and BWP Minify will not be able to add it to the main minify string. If you want to minify <code>style.css</code> with the rest of your css files, you must enqueue it."
|
208 |
+
msgstr "majoritatea temelor (ex. Twenty Ten) folosesc <code>bloginfo()</code> pentru a genera stylesheet-ul principal (i.e. <code>style.css</code>) iar BWP Minify nu va putea sa il adauge la string-ul primar. Daca doriti sa comprimati <code>style.css</code> cu celelalte fisiere css, trebuie sa il adaugati in sir."
|
209 |
+
|
210 |
+
#: includes/class-bwp-minify.php:264
|
211 |
+
#, php-format
|
212 |
+
msgid "This should be set automatically. If you think the URL is too long, please read <a href=\"%s#customization\">here</a> to know how to properly modify this."
|
213 |
+
msgstr "Ar trebui setat automat. Daca credeti ca URL-ul este prea lung, va rugam cititi <a href=\"%s#customization\">aici</a> pentru a afla cum puteti modifica aceasta functie"
|
214 |
+
|
215 |
+
#: includes/class-bwp-minify.php:265
|
216 |
+
#, php-format
|
217 |
+
msgid "<strong>Important</strong>: Changing cache directory is a two-step process, which is described in details <a href=\"%s#config\" target=\"_blank\">here</a>. Please note that cache directory must be writable (i.e. CHMOD to 755 or 777)."
|
218 |
+
msgstr ""
|
219 |
+
|
220 |
+
#: includes/class-bwp-minify.php:266
|
221 |
+
msgid "file(s) at most."
|
222 |
+
msgstr "fisier(e) maxim."
|
223 |
+
|
224 |
+
#: includes/class-bwp-minify.php:267
|
225 |
+
msgid "—"
|
226 |
+
msgstr "—"
|
227 |
+
|
228 |
+
#: includes/class-bwp-minify.php:268
|
229 |
+
msgid "<em>→ /min/?f=file.js&ver=</em> "
|
230 |
+
msgstr "<em>→ /min/?f=file.js&ver=</em> "
|
231 |
+
|
232 |
+
#: includes/class-bwp-minify.php:278
|
233 |
+
msgid "<em><strong>Note:</strong> When you append one of the things above you are basically telling browsers to clear their cached version of your CSS and JS files, which is very useful when you change source files. Use this feature wisely :).</em>"
|
234 |
+
msgstr "<em><strong>Nota:</strong> Cand adaugati unul dintre lucrurile de mai sus, le spuneti browserelor sa curete versiunea din cache a fisierelor CSS si JS, un lucru util pentru atunci cand schimbati sursa fisierelor. Folositi functia cu atentie :).</em>"
|
235 |
+
|
236 |
+
#: includes/class-bwp-minify.php:285
|
237 |
+
msgid "Flush the cache"
|
238 |
+
msgstr ""
|
239 |
+
|
240 |
+
#: includes/class-bwp-minify.php:305
|
241 |
+
#: includes/class-bwp-minify.php:307
|
242 |
+
msgid "Notice"
|
243 |
+
msgstr ""
|
244 |
+
|
245 |
+
#: includes/class-bwp-minify.php:305
|
246 |
+
#, php-format
|
247 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
248 |
+
msgstr ""
|
249 |
+
|
250 |
+
#: includes/class-bwp-minify.php:307
|
251 |
+
msgid "Could not delete any cached files. Please manually check the cache directory."
|
252 |
+
msgstr ""
|
253 |
+
|
254 |
+
#: includes/class-bwp-minify.php:341
|
255 |
+
msgid "All options have been saved."
|
256 |
+
msgstr ""
|
257 |
+
|
258 |
+
#: includes/class-bwp-minify.php:357
|
259 |
+
msgid "Warning"
|
260 |
+
msgstr "Avertisment"
|
261 |
+
|
262 |
+
#: includes/class-bwp-minify.php:357
|
263 |
+
msgid "Cache directory does not exist or is not writable. Please try CHMOD your cache directory to 755. If you still see this warning, CHMOD to 777."
|
264 |
+
msgstr "Directorul de cache nu exista sau nu este writable. Va rugam schimbati CHMOD pentru acest director la 755. Daca eroarea persista, schimbati la 777."
|
265 |
+
|
266 |
+
#: includes/class-bwp-minify.php:471
|
267 |
+
msgid "empty"
|
268 |
+
msgstr "gol"
|
269 |
+
|
270 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:80
|
271 |
+
msgid "Plugin Configurations"
|
272 |
+
msgstr "Configurarea plugin-ului"
|
273 |
+
|
274 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:398
|
275 |
+
msgid "Save Changes"
|
276 |
+
msgstr "Salveaza schimbarile"
|
277 |
+
|
278 |
+
#~ msgid "Cache will be stored in (by default)"
|
279 |
+
#~ msgstr "Cache-ul va fi stocat in"
|
280 |
+
|
281 |
+
#~ msgid "The cache directory must be writable (i.e. CHMOD to 755 or 777)."
|
282 |
+
#~ msgstr ""
|
283 |
+
#~ "Directorul de cache trebuie sa fie writable (CHMOD setat la 755 sau 777)."
|
284 |
+
|
285 |
+
#~ msgid "log"
|
286 |
+
#~ msgstr "log"
|
languages/bwp-minify-ru_RU.mo
ADDED
Binary file
|
languages/bwp-minify-ru_RU.po
ADDED
@@ -0,0 +1,377 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: Better WordPress Minify v1.2.3\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2012-03-20 21:51+0700\n"
|
6 |
+
"PO-Revision-Date: 2014-03-25 15:39:59+0000\n"
|
7 |
+
"Last-Translator: adward <adward@chelny.com>\n"
|
8 |
+
"Language-Team: adward <adward@chelny.com>\n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"Plural-Forms: nplurals=3; plural=n%100/10==1 ? 2 : n%10==1 ? 0 : (n+9)%10>3 ? 2 : 1;\n"
|
13 |
+
"X-Generator: CSL v1.x\n"
|
14 |
+
"X-Poedit-Language: Russian\n"
|
15 |
+
"X-Poedit-Country: RUSSIA\n"
|
16 |
+
"X-Poedit-SourceCharset: utf-8\n"
|
17 |
+
"X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;\n"
|
18 |
+
"X-Poedit-Basepath: .\n"
|
19 |
+
"X-Poedit-Bookmarks: \n"
|
20 |
+
"X-Poedit-SearchPath-0: .\n"
|
21 |
+
"X-Textdomain-Support: yes"
|
22 |
+
|
23 |
+
#: includes/class-bwp-framework.php:178
|
24 |
+
#, php-format
|
25 |
+
#@ bwp-minify
|
26 |
+
msgid "%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</strong> or higher. The plugin will not function until you update your software. Please deactivate this plugin."
|
27 |
+
msgstr "%s требуется WordPress версии <strong>%s</strong> или выше и PHP <strong>%s</strong> или выше. Плагин не будет работать, пока вы не обновите программное обеспечение. Настаиваем на деактивации этого плагина."
|
28 |
+
|
29 |
+
#: includes/class-bwp-framework.php:190
|
30 |
+
#@ bwp-minify
|
31 |
+
msgid "Development Log"
|
32 |
+
msgstr "Дневник разработки"
|
33 |
+
|
34 |
+
#: includes/class-bwp-framework.php:190
|
35 |
+
#@ bwp-minify
|
36 |
+
msgid "Frequently Asked Questions"
|
37 |
+
msgstr "Часто задаваемые вопросы"
|
38 |
+
|
39 |
+
#: includes/class-bwp-framework.php:190
|
40 |
+
#@ bwp-minify
|
41 |
+
msgid "FAQ"
|
42 |
+
msgstr ""
|
43 |
+
|
44 |
+
#: includes/class-bwp-framework.php:190
|
45 |
+
#@ bwp-minify
|
46 |
+
msgid "Got a problem? Send me a feedback!"
|
47 |
+
msgstr "Столкнулись с проблемой? Отправьте мне запрос!"
|
48 |
+
|
49 |
+
#: includes/class-bwp-framework.php:190
|
50 |
+
#@ bwp-minify
|
51 |
+
msgid "Contact"
|
52 |
+
msgstr "Контакт"
|
53 |
+
|
54 |
+
#: includes/class-bwp-framework.php:197
|
55 |
+
#@ bwp-minify
|
56 |
+
msgid "You can buy me some special coffees if you appreciate my work, thank you!"
|
57 |
+
msgstr "Пожертвуйте на чай, если Вам понравилась моя работа ;) "
|
58 |
+
|
59 |
+
#: includes/class-bwp-framework.php:213
|
60 |
+
#@ bwp-minify
|
61 |
+
msgid "One cup $5.00"
|
62 |
+
msgstr "Чашка чая за 5 баксов"
|
63 |
+
|
64 |
+
#: includes/class-bwp-framework.php:214
|
65 |
+
#@ bwp-minify
|
66 |
+
msgid "Two cups $10.00"
|
67 |
+
msgstr "Кружку чая за 10 баксов"
|
68 |
+
|
69 |
+
#: includes/class-bwp-framework.php:215
|
70 |
+
#@ bwp-minify
|
71 |
+
msgid "Five cups! $25.00"
|
72 |
+
msgstr "Литровую кружку за 25 баксов"
|
73 |
+
|
74 |
+
#: includes/class-bwp-framework.php:216
|
75 |
+
#@ bwp-minify
|
76 |
+
msgid "One LL-cup!!! $50.00"
|
77 |
+
msgstr "Бидон чая за 50 баксов"
|
78 |
+
|
79 |
+
#: includes/class-bwp-framework.php:217
|
80 |
+
#@ bwp-minify
|
81 |
+
msgid "... or any amount!"
|
82 |
+
msgstr "...или сколько не жалко!"
|
83 |
+
|
84 |
+
#: includes/class-bwp-framework.php:232
|
85 |
+
#@ bwp-minify
|
86 |
+
msgid "Latest updates from BetterWP.net!"
|
87 |
+
msgstr "Свежие обновления на сайте BetterWP.net!"
|
88 |
+
|
89 |
+
#: includes/class-bwp-framework.php:233
|
90 |
+
#@ bwp-minify
|
91 |
+
msgid "Follow me on Twitter!"
|
92 |
+
msgstr "Фоловим в Твиттере!"
|
93 |
+
|
94 |
+
#: includes/class-bwp-framework.php:260
|
95 |
+
#, php-format
|
96 |
+
#@ bwp-minify
|
97 |
+
msgid "You are using version %s!"
|
98 |
+
msgstr "Вы юзаете версию %s!"
|
99 |
+
|
100 |
+
#: includes/class-bwp-framework.php:396
|
101 |
+
#@ default
|
102 |
+
msgid "Settings"
|
103 |
+
msgstr "Настройки"
|
104 |
+
|
105 |
+
#: includes/class-bwp-minify.php:109
|
106 |
+
#@ bwp-minify
|
107 |
+
msgid "Better WordPress Minify Settings"
|
108 |
+
msgstr "Настройки плагина Better Minify"
|
109 |
+
|
110 |
+
#. translators: plugin header field 'Name'
|
111 |
+
#: bwp-minify.php:0
|
112 |
+
#: includes/class-bwp-minify.php:219
|
113 |
+
#@ bwp-minify
|
114 |
+
msgid "Better WordPress Minify"
|
115 |
+
msgstr "Better WordPress Minify"
|
116 |
+
|
117 |
+
#: includes/class-bwp-minify.php:230
|
118 |
+
#@ default
|
119 |
+
msgid "You do not have sufficient permissions to access this page."
|
120 |
+
msgstr "Вы не имеете достаточно прав для доступа к этой странице."
|
121 |
+
|
122 |
+
#: includes/class-bwp-minify.php:246
|
123 |
+
#@ bwp-minify
|
124 |
+
msgid "General Options"
|
125 |
+
msgstr "Главные настройки"
|
126 |
+
|
127 |
+
#: includes/class-bwp-minify.php:247
|
128 |
+
#@ bwp-minify
|
129 |
+
msgid "Minify JS files automatically?"
|
130 |
+
msgstr "Минимизировать JS-файлы скриптов автоматически?"
|
131 |
+
|
132 |
+
#: includes/class-bwp-minify.php:248
|
133 |
+
#@ bwp-minify
|
134 |
+
msgid "Minify CSS files automatically?"
|
135 |
+
msgstr "Минимизировать CSS-файлы стилей автоматически? "
|
136 |
+
|
137 |
+
#: includes/class-bwp-minify.php:249
|
138 |
+
#@ bwp-minify
|
139 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
140 |
+
msgstr "Минимизировать <code>bloginfo()</code> стили?"
|
141 |
+
|
142 |
+
#: includes/class-bwp-minify.php:250
|
143 |
+
#@ bwp-minify
|
144 |
+
msgid "Minifying Options"
|
145 |
+
msgstr "Опции Minifying "
|
146 |
+
|
147 |
+
#: includes/class-bwp-minify.php:251
|
148 |
+
#@ bwp-minify
|
149 |
+
msgid "Minify URL (double-click to edit)"
|
150 |
+
msgstr "Путь до минимизированных файлов (дваждыклик для редактирования)"
|
151 |
+
|
152 |
+
#: includes/class-bwp-minify.php:252
|
153 |
+
#@ bwp-minify
|
154 |
+
msgid "Cache directory (double-click to edit)"
|
155 |
+
msgstr "Папка кэша <br>(дваждыклик для редактирования)"
|
156 |
+
|
157 |
+
#: includes/class-bwp-minify.php:253
|
158 |
+
#@ bwp-minify
|
159 |
+
msgid "One minify string will contain"
|
160 |
+
msgstr "Одна минимизированная строка будет содержать"
|
161 |
+
|
162 |
+
#: includes/class-bwp-minify.php:254
|
163 |
+
#@ bwp-minify
|
164 |
+
msgid "Append the minify string with"
|
165 |
+
msgstr "Что добавить в минимизированную строку?"
|
166 |
+
|
167 |
+
#: includes/class-bwp-minify.php:255
|
168 |
+
#@ bwp-minify
|
169 |
+
msgid "Minifying Scripts Options"
|
170 |
+
msgstr "Настройки сжатия скриптов"
|
171 |
+
|
172 |
+
#: includes/class-bwp-minify.php:256
|
173 |
+
#@ bwp-minify
|
174 |
+
msgid "Scripts to be minified in header"
|
175 |
+
msgstr "Скрипты должны быть сжаты в заголовок"
|
176 |
+
|
177 |
+
#: includes/class-bwp-minify.php:257
|
178 |
+
#@ bwp-minify
|
179 |
+
msgid "Scripts to be minified in footer"
|
180 |
+
msgstr "Скрипты должны быть сжаты в подвал"
|
181 |
+
|
182 |
+
#: includes/class-bwp-minify.php:258
|
183 |
+
#@ bwp-minify
|
184 |
+
msgid "Scripts to be minified and then printed separately"
|
185 |
+
msgstr "Скрипты, которые должны быть сжаты, а следом выведены по отдельности"
|
186 |
+
|
187 |
+
#: includes/class-bwp-minify.php:259
|
188 |
+
#@ bwp-minify
|
189 |
+
msgid "Scripts to be ignored (not minified)"
|
190 |
+
msgstr "Игнорируемые скрипты (не будут модифицироваться)"
|
191 |
+
|
192 |
+
#: includes/class-bwp-minify.php:264
|
193 |
+
#@ bwp-minify
|
194 |
+
msgid "<em>Options that affect both your stylesheets and scripts.</em>"
|
195 |
+
msgstr "<em>Параментры, влияющие на каскадные стили и скрипты</em>"
|
196 |
+
|
197 |
+
#: includes/class-bwp-minify.php:265
|
198 |
+
#, php-format
|
199 |
+
#@ bwp-minify
|
200 |
+
msgid "<em>You can force the position of each script using those inputs below (e.g. you have a script registered in the header but you want to minify it in the footer instead). If you are still unsure, please read more <a href=\"%s#positioning-your-scripts\">here</a>. Type in one script handle (<strong>NOT filename</strong>) per line.</em>"
|
201 |
+
msgstr "<em>Настраиваемые состояния для каждого отдельного сценария (например, у Вас есть скрипт, зарегистрированный в заголовке, а вы хотите выводить его в подвале). Если нет уверенности, ознакомьтесь с деталями <a href=\"%s#positioning-your-scripts\">вот тут</a>. По одному сценарию (<strong>НЕ имя файла</strong>) на строку.</em>"
|
202 |
+
|
203 |
+
#: includes/class-bwp-minify.php:269
|
204 |
+
#@ bwp-minify
|
205 |
+
msgid "second(s)"
|
206 |
+
msgstr "секунд"
|
207 |
+
|
208 |
+
#: includes/class-bwp-minify.php:270
|
209 |
+
#@ bwp-minify
|
210 |
+
msgid "minute(s)"
|
211 |
+
msgstr "минут"
|
212 |
+
|
213 |
+
#: includes/class-bwp-minify.php:271
|
214 |
+
#@ bwp-minify
|
215 |
+
msgid "hour(s)"
|
216 |
+
msgstr "часов"
|
217 |
+
|
218 |
+
#: includes/class-bwp-minify.php:272
|
219 |
+
#@ bwp-minify
|
220 |
+
msgid "day(s)"
|
221 |
+
msgstr "дней"
|
222 |
+
|
223 |
+
#: includes/class-bwp-minify.php:275
|
224 |
+
#@ bwp-minify
|
225 |
+
msgid "Do not append anything"
|
226 |
+
msgstr "Ничего"
|
227 |
+
|
228 |
+
#: includes/class-bwp-minify.php:276
|
229 |
+
#@ bwp-minify
|
230 |
+
msgid "Cache folder’s last modified time"
|
231 |
+
msgstr "Последнее изменение папки кэша"
|
232 |
+
|
233 |
+
#: includes/class-bwp-minify.php:277
|
234 |
+
#@ bwp-minify
|
235 |
+
msgid "Your WordPress’s current version"
|
236 |
+
msgstr "Текущую версию WordPress"
|
237 |
+
|
238 |
+
#: includes/class-bwp-minify.php:279
|
239 |
+
#@ bwp-minify
|
240 |
+
msgid "A custom number"
|
241 |
+
msgstr "Свою цифру"
|
242 |
+
|
243 |
+
#: includes/class-bwp-minify.php:283
|
244 |
+
#: includes/class-bwp-minify.php:284
|
245 |
+
#@ bwp-minify
|
246 |
+
msgid "you can still use <code>bwp_minify()</code> helper function if you disable this."
|
247 |
+
msgstr "<i>можно продолжать использовать функцию <code>bwp_minify()</code> даже при отключении этого параметра.</i>"
|
248 |
+
|
249 |
+
#: includes/class-bwp-minify.php:285
|
250 |
+
#@ bwp-minify
|
251 |
+
msgid "most themes (e.g. Twenty Ten) use <code>bloginfo()</code> to print the main stylesheet (i.e. <code>style.css</code>) and BWP Minify will not be able to add it to the main minify string. If you want to minify <code>style.css</code> with the rest of your css files, you must enqueue it."
|
252 |
+
msgstr "Большинство тем оформления (к примеру Twenty Ten) используют <code>bloginfo()</code> для вывода основных файлов стилей(например <code>style.css</code>) и BWP Minify не в состоянии будет добавить такой стиль в сжатую строку. Если захотите уменьшить такой <code>style.css</code> - необходимо будет поставить его в очередь."
|
253 |
+
|
254 |
+
#: includes/class-bwp-minify.php:288
|
255 |
+
#, php-format
|
256 |
+
#@ bwp-minify
|
257 |
+
msgid "This should be set automatically. If you think the URL is too long, please read <a href=\"%s#customization\">here</a> to know how to properly modify this."
|
258 |
+
msgstr "<br>Этот параметр устанавливается автоматически. Если Вы считаете, что URL чересчур длинный, почитайте <a href=\"%s#customization\">вот здесь</a> чтобы узнать, как правильно изменить это значение."
|
259 |
+
|
260 |
+
#: includes/class-bwp-minify.php:290
|
261 |
+
#@ bwp-minify
|
262 |
+
msgid "file(s) at most."
|
263 |
+
msgstr "файлов"
|
264 |
+
|
265 |
+
#: includes/class-bwp-minify.php:291
|
266 |
+
#@ bwp-minify
|
267 |
+
msgid "—"
|
268 |
+
msgstr "—"
|
269 |
+
|
270 |
+
#: includes/class-bwp-minify.php:292
|
271 |
+
#@ bwp-minify
|
272 |
+
msgid "<em>→ /min/?f=file.js&ver=</em> "
|
273 |
+
msgstr ""
|
274 |
+
|
275 |
+
#: includes/class-bwp-minify.php:302
|
276 |
+
#@ bwp-minify
|
277 |
+
msgid "<em><strong>Note:</strong> When you append one of the things above you are basically telling browsers to clear their cached version of your CSS and JS files, which is very useful when you change source files. Use this feature wisely :).</em>"
|
278 |
+
msgstr "<em><strong>Имейте ввиду:</strong> Изменяя любой из вышеперечисленных параметров, тем самым, Вы так же сообщаете браузеру очистить закэшированные скрипты и стили, которые становятся к моменту сохранения бесполезными. Используйте эту фичу мудро ;) </em>"
|
279 |
+
|
280 |
+
#: includes/class-bwp-minify.php:309
|
281 |
+
#@ bwp-minify
|
282 |
+
msgid "Flush the cache"
|
283 |
+
msgstr "Очистить кэш"
|
284 |
+
|
285 |
+
#: includes/class-bwp-minify.php:329
|
286 |
+
#: includes/class-bwp-minify.php:331
|
287 |
+
#@ bwp-minify
|
288 |
+
msgid "Notice"
|
289 |
+
msgstr "Уведомление"
|
290 |
+
|
291 |
+
#: includes/class-bwp-minify.php:329
|
292 |
+
#, php-format
|
293 |
+
#@ bwp-minify
|
294 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
295 |
+
msgstr "<strong>%d</strong> успешно удалены кэшированные файлы!"
|
296 |
+
|
297 |
+
#: includes/class-bwp-minify.php:331
|
298 |
+
#@ bwp-minify
|
299 |
+
msgid "Could not delete any cached files. Please manually check the cache directory."
|
300 |
+
msgstr "Не удалось оистить все файлы из кэша. Пожалуйста, почистите файлы в папке кэша ручками."
|
301 |
+
|
302 |
+
#: includes/class-bwp-minify.php:365
|
303 |
+
#@ bwp-minify
|
304 |
+
msgid "All options have been saved."
|
305 |
+
msgstr "Все изменения успешно сохранены."
|
306 |
+
|
307 |
+
#: includes/class-bwp-minify.php:381
|
308 |
+
#@ default
|
309 |
+
msgid "Warning"
|
310 |
+
msgstr "Предупреждение"
|
311 |
+
|
312 |
+
#: includes/class-bwp-minify.php:381
|
313 |
+
#@ bwp-minify
|
314 |
+
msgid "Cache directory does not exist or is not writable. Please try CHMOD your cache directory to 755. If you still see this warning, CHMOD to 777."
|
315 |
+
msgstr "Папка кэша не доступна для записи. Пожалуйста, установите CMOD разрешения на эту папку в 755. если это сообщение повторится - то в 777."
|
316 |
+
|
317 |
+
#: includes/class-bwp-minify.php:503
|
318 |
+
#@ bwp-minify
|
319 |
+
msgid "empty"
|
320 |
+
msgstr "пусто"
|
321 |
+
|
322 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:80
|
323 |
+
#@ bwp-option-page
|
324 |
+
msgid "Plugin Configurations"
|
325 |
+
msgstr "Конфигурация плагина"
|
326 |
+
|
327 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:398
|
328 |
+
#@ default
|
329 |
+
msgid "Save Changes"
|
330 |
+
msgstr "Сохранить изменения"
|
331 |
+
|
332 |
+
#. translators: plugin header field 'PluginURI'
|
333 |
+
#: bwp-minify.php:0
|
334 |
+
#@ bwp-minify
|
335 |
+
msgid "http://betterwp.net/wordpress-plugins/bwp-minify/"
|
336 |
+
msgstr ""
|
337 |
+
|
338 |
+
#. translators: plugin header field 'Description'
|
339 |
+
#: bwp-minify.php:0
|
340 |
+
#@ bwp-minify
|
341 |
+
msgid "Allows you to minify your CSS and JS files for faster page loading for visitors. This plugin uses the PHP library <a href=\"http://code.google.com/p/minify/\">Minify</a> and relies on WordPress's enqueueing system rather than the output buffer (will not break your website in most cases). This plugin is very customizable and easy to use."
|
342 |
+
msgstr "Позволяет уменьшать файлы стилей и скриптов (CSS и JS) файлы для уменьшения скорости загрузки страниц сайта. Плагин использует библиотеку PHP <a href=\"http://code.google.com/p/minify/\">Minify</ A> и полагается на стандартный механизм добавления скриптов (enqueue script Wordpress), а не на выходной буфер (Ваш сайт не сломается в большинстве случаев). Решение весьма настраиваемое и достаточно просто в использовании."
|
343 |
+
|
344 |
+
#. translators: plugin header field 'Author'
|
345 |
+
#: bwp-minify.php:0
|
346 |
+
#@ bwp-minify
|
347 |
+
msgid "Khang Minh"
|
348 |
+
msgstr ""
|
349 |
+
|
350 |
+
#. translators: plugin header field 'AuthorURI'
|
351 |
+
#: bwp-minify.php:0
|
352 |
+
#@ bwp-minify
|
353 |
+
msgid "http://betterwp.net"
|
354 |
+
msgstr ""
|
355 |
+
|
356 |
+
#. translators: plugin header field 'Version'
|
357 |
+
#: bwp-minify.php:0
|
358 |
+
#@ bwp-minify
|
359 |
+
msgid "1.2.3"
|
360 |
+
msgstr ""
|
361 |
+
|
362 |
+
#: includes/class-bwp-framework.php:243
|
363 |
+
#@ bwp-minify
|
364 |
+
msgid "This Plugin is Proudly Sponsored By"
|
365 |
+
msgstr "Спонсоры"
|
366 |
+
|
367 |
+
#: includes/class-bwp-minify.php:278
|
368 |
+
#@ bwp-minify
|
369 |
+
msgid "Your theme’s current version"
|
370 |
+
msgstr "Текущую версию выбранной темы оформления"
|
371 |
+
|
372 |
+
#: includes/class-bwp-minify.php:289
|
373 |
+
#, php-format
|
374 |
+
#@ bwp-minify
|
375 |
+
msgid "<strong>Important</strong>: Changing cache directory is a two-step process, which is described in details <a href=\"%s#advanced_customization\" target=\"_blank\">here</a>. Please note that cache directory must be writable (i.e. CHMOD to 755 or 777)."
|
376 |
+
msgstr "<strong>Важно:</strong> Изменение папки хранения кэша - процесс двуэтапный и подробности в деталях изложены <a href=\"%s#advanced_customization\" target=\"_blank\">здесь</a>. Обратите внимание на то, что каталог кэша должен быть доступен для записи (т.е. CHMOD 755 или 777)."
|
377 |
+
|
languages/bwp-minify-sr_RS.mo
ADDED
Binary file
|
languages/bwp-minify-sr_RS.po
ADDED
@@ -0,0 +1,334 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: BWP Minify\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2012-10-07 22:54+0700\n"
|
6 |
+
"PO-Revision-Date: 2013-12-02 09:52+0100\n"
|
7 |
+
"Last-Translator: Borisa Djuraskovic <borisad@webhostinghub.com>\n"
|
8 |
+
"Language-Team: \n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n"
|
13 |
+
"X-Poedit-Basepath: .\n"
|
14 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
15 |
+
"X-Generator: Poedit 1.5.7\n"
|
16 |
+
"X-Poedit-SearchPath-0: .\n"
|
17 |
+
|
18 |
+
#: includes/class-bwp-framework.php:178
|
19 |
+
#, php-format
|
20 |
+
msgid ""
|
21 |
+
"%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</"
|
22 |
+
"strong> or higher. The plugin will not function until you update your "
|
23 |
+
"software. Please deactivate this plugin."
|
24 |
+
msgstr ""
|
25 |
+
"%s zahteva WordPress<strong>%s</strong> ili viši i PHP<strong>%s</strong> "
|
26 |
+
"ili viši. Ovaj plugin neće funkcionisati sve dok ne ažurirate svoj softver. "
|
27 |
+
"Molimo da deaktivirate ovaj plugin. "
|
28 |
+
|
29 |
+
#: includes/class-bwp-framework.php:189
|
30 |
+
msgid "Development Log"
|
31 |
+
msgstr "Dnevnik razvoja"
|
32 |
+
|
33 |
+
#: includes/class-bwp-framework.php:189
|
34 |
+
msgid "Frequently Asked Questions"
|
35 |
+
msgstr "Česta pitanja"
|
36 |
+
|
37 |
+
#: includes/class-bwp-framework.php:189
|
38 |
+
msgid "FAQ"
|
39 |
+
msgstr "FAQ"
|
40 |
+
|
41 |
+
#: includes/class-bwp-framework.php:189
|
42 |
+
msgid "Got a problem? Send me a feedback!"
|
43 |
+
msgstr "Imate neki problem? Pošaljite mi povratnu informaciju! "
|
44 |
+
|
45 |
+
#: includes/class-bwp-framework.php:189
|
46 |
+
msgid "Contact"
|
47 |
+
msgstr "Kontakt"
|
48 |
+
|
49 |
+
#: includes/class-bwp-framework.php:196
|
50 |
+
msgid ""
|
51 |
+
"You can buy me some special coffees if you appreciate my work, thank you!"
|
52 |
+
msgstr ""
|
53 |
+
"Možete da me častite posebnim kaficama ako vam se dopada moj rad, hvala vam! "
|
54 |
+
|
55 |
+
#: includes/class-bwp-framework.php:210
|
56 |
+
msgid "Donate to "
|
57 |
+
msgstr "Pošaljite donaciju"
|
58 |
+
|
59 |
+
#: includes/class-bwp-framework.php:212
|
60 |
+
msgid "One cup $5.00"
|
61 |
+
msgstr "Jedna šolja $5.00"
|
62 |
+
|
63 |
+
#: includes/class-bwp-framework.php:213
|
64 |
+
msgid "Two cups $10.00"
|
65 |
+
msgstr "Dve šolje $10.00"
|
66 |
+
|
67 |
+
#: includes/class-bwp-framework.php:214
|
68 |
+
msgid "Five cups! $25.00"
|
69 |
+
msgstr "Pet šolja! $25.00"
|
70 |
+
|
71 |
+
#: includes/class-bwp-framework.php:215
|
72 |
+
msgid "One LL-cup!!! $50.00"
|
73 |
+
msgstr "Jedna LL-šolja!!! $50.00"
|
74 |
+
|
75 |
+
#: includes/class-bwp-framework.php:216
|
76 |
+
msgid "... or any amount!"
|
77 |
+
msgstr "...ili bilo koja količina!"
|
78 |
+
|
79 |
+
#: includes/class-bwp-framework.php:231
|
80 |
+
msgid "Latest updates from BetterWP.net!"
|
81 |
+
msgstr "Najnovija ažuriranja sa BetterWP.net!"
|
82 |
+
|
83 |
+
#: includes/class-bwp-framework.php:232
|
84 |
+
msgid "Follow me on Twitter!"
|
85 |
+
msgstr "Pratite me na Twitter-u!"
|
86 |
+
|
87 |
+
#: includes/class-bwp-framework.php:241
|
88 |
+
#, php-format
|
89 |
+
msgid "You are using version %s!"
|
90 |
+
msgstr "Koristite verziju %s!"
|
91 |
+
|
92 |
+
#: includes/class-bwp-framework.php:377
|
93 |
+
msgid "Settings"
|
94 |
+
msgstr "Podešavanja"
|
95 |
+
|
96 |
+
#: includes/class-bwp-minify.php:110
|
97 |
+
msgid "Better WordPress Minify Settings"
|
98 |
+
msgstr "Bolja podešavanja WordPress smanjivanja "
|
99 |
+
|
100 |
+
#: includes/class-bwp-minify.php:206
|
101 |
+
msgid "Better WordPress Minify"
|
102 |
+
msgstr "Bolje WordPress smanjivanje"
|
103 |
+
|
104 |
+
#: includes/class-bwp-minify.php:217
|
105 |
+
msgid "You do not have sufficient permissions to access this page."
|
106 |
+
msgstr "Nemate potrebne dozvole da biste pristupili ovoj stranici. "
|
107 |
+
|
108 |
+
#: includes/class-bwp-minify.php:233
|
109 |
+
msgid "General Options"
|
110 |
+
msgstr "Opšte opcije "
|
111 |
+
|
112 |
+
#: includes/class-bwp-minify.php:234
|
113 |
+
msgid "Minify JS files automatically?"
|
114 |
+
msgstr "Smanjite JS fajlove automatski?"
|
115 |
+
|
116 |
+
#: includes/class-bwp-minify.php:235
|
117 |
+
msgid "Minify CSS files automatically?"
|
118 |
+
msgstr "Smanjite CSS fajlove automatski?"
|
119 |
+
|
120 |
+
#: includes/class-bwp-minify.php:236
|
121 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
122 |
+
msgstr "Smanjite <code> bloginfo()</code> šeme stilova?"
|
123 |
+
|
124 |
+
#: includes/class-bwp-minify.php:237
|
125 |
+
msgid "Minifying Options"
|
126 |
+
msgstr "Opcije smanjivanja"
|
127 |
+
|
128 |
+
#: includes/class-bwp-minify.php:238
|
129 |
+
msgid "Minify URL (double-click to edit)"
|
130 |
+
msgstr "Smanjite URL (dupli klik za uređivanje)"
|
131 |
+
|
132 |
+
#: includes/class-bwp-minify.php:239
|
133 |
+
msgid "Cache directory (double-click to edit)"
|
134 |
+
msgstr "Keš direktorijum (dupli klik za uređivanje)"
|
135 |
+
|
136 |
+
#: includes/class-bwp-minify.php:240
|
137 |
+
msgid "One minify string will contain"
|
138 |
+
msgstr "Jedan string smanjivanja sadržaće"
|
139 |
+
|
140 |
+
#: includes/class-bwp-minify.php:241
|
141 |
+
msgid "Append the minify string with"
|
142 |
+
msgstr "Dopunite string smanjivanja sa"
|
143 |
+
|
144 |
+
#: includes/class-bwp-minify.php:242
|
145 |
+
msgid "Minifying Scripts Options"
|
146 |
+
msgstr "Skriptovi smanjivanja, opcije"
|
147 |
+
|
148 |
+
#: includes/class-bwp-minify.php:243
|
149 |
+
msgid "Scripts to be minified in header"
|
150 |
+
msgstr "Skriptovi koji će se smanjiti u hederu "
|
151 |
+
|
152 |
+
#: includes/class-bwp-minify.php:244
|
153 |
+
msgid "Scripts to be minified in footer"
|
154 |
+
msgstr "Skriptovi koji će se smanjiti u futeru "
|
155 |
+
|
156 |
+
#: includes/class-bwp-minify.php:245
|
157 |
+
msgid "Scripts to be minified and then printed separately"
|
158 |
+
msgstr "Skriptovi koji će se smanjiti i onda odvojeno štampati"
|
159 |
+
|
160 |
+
#: includes/class-bwp-minify.php:246
|
161 |
+
msgid "Scripts to be ignored (not minified)"
|
162 |
+
msgstr "Skriptovi koje treba ignorisati (ne smanjiti) "
|
163 |
+
|
164 |
+
#: includes/class-bwp-minify.php:251
|
165 |
+
msgid "<em>Options that affect both your stylesheets and scripts.</em>"
|
166 |
+
msgstr "<em>Opcije koje utiču i na vaše šeme stilova i na skriptove.</em>"
|
167 |
+
|
168 |
+
#: includes/class-bwp-minify.php:252
|
169 |
+
#, php-format
|
170 |
+
msgid ""
|
171 |
+
"<em>You can force the position of each script using those inputs below (e.g. "
|
172 |
+
"you have a script registered in the header but you want to minify it in the "
|
173 |
+
"footer instead). If you are still unsure, please read more <a href="
|
174 |
+
"\"%s#positioning-your-scripts\">here</a>. Type in one script handle "
|
175 |
+
"(<strong>NOT filename</strong>) per line.</em>"
|
176 |
+
msgstr ""
|
177 |
+
"<em>Možete nasilno da podesite položaj svakog skripta koristeći dolenavedena "
|
178 |
+
"uputstva (npr. ako imate skript registrovan u hederu ali želite da ga "
|
179 |
+
"smanjite u futeru). Ako i dalje niste sigurni, molim vas pročitajte više<a "
|
180 |
+
"href=\"%s#positioning-your-scripts\"> ovde</a>. Ukucajte jednu skript handle "
|
181 |
+
"(<strong>NE ime fajla</strong>) po liniji.</em>"
|
182 |
+
|
183 |
+
#: includes/class-bwp-minify.php:256
|
184 |
+
msgid "second(s)"
|
185 |
+
msgstr "sekund(e)"
|
186 |
+
|
187 |
+
#: includes/class-bwp-minify.php:257
|
188 |
+
msgid "minute(s)"
|
189 |
+
msgstr "minut(i)"
|
190 |
+
|
191 |
+
#: includes/class-bwp-minify.php:258
|
192 |
+
msgid "hour(s)"
|
193 |
+
msgstr "sat(i)"
|
194 |
+
|
195 |
+
#: includes/class-bwp-minify.php:259
|
196 |
+
msgid "day(s)"
|
197 |
+
msgstr "dan(i)"
|
198 |
+
|
199 |
+
#: includes/class-bwp-minify.php:262
|
200 |
+
msgid "Do not append anything"
|
201 |
+
msgstr "Ništa ne dopunjujte"
|
202 |
+
|
203 |
+
#: includes/class-bwp-minify.php:263
|
204 |
+
msgid "Cache folder’s last modified time"
|
205 |
+
msgstr "Vreme poslednje modifikacije keširanog foldera’s "
|
206 |
+
|
207 |
+
#: includes/class-bwp-minify.php:264
|
208 |
+
msgid "Your WordPress’s current version"
|
209 |
+
msgstr "Vaša tekuća verzija WordPress-a’s "
|
210 |
+
|
211 |
+
#: includes/class-bwp-minify.php:265
|
212 |
+
msgid "Your theme’s current version"
|
213 |
+
msgstr "Tekuća verzija vaše teme’s"
|
214 |
+
|
215 |
+
#: includes/class-bwp-minify.php:266
|
216 |
+
msgid "A custom number"
|
217 |
+
msgstr "Kustomizirani broj"
|
218 |
+
|
219 |
+
#: includes/class-bwp-minify.php:270 includes/class-bwp-minify.php:271
|
220 |
+
msgid ""
|
221 |
+
"you can still use <code>bwp_minify()</code> helper function if you disable "
|
222 |
+
"this."
|
223 |
+
msgstr ""
|
224 |
+
"i dalje možete da koristite<code>bwp_minify()</code> funkciju helper i kada "
|
225 |
+
"ovo onesposobite "
|
226 |
+
|
227 |
+
#: includes/class-bwp-minify.php:272
|
228 |
+
msgid ""
|
229 |
+
"most themes (e.g. Twenty Ten) use <code>bloginfo()</code> to print the main "
|
230 |
+
"stylesheet (i.e. <code>style.css</code>) and BWP Minify will not be able to "
|
231 |
+
"add it to the main minify string. If you want to minify <code>style.css</"
|
232 |
+
"code> with the rest of your css files, you must enqueue it."
|
233 |
+
msgstr ""
|
234 |
+
"većina tema (npr. Twenty Ten) koriste<code>bloginfo()</code> da štampaju "
|
235 |
+
"glavnu šemu stila (npr.<code>style.css</code> ) i BWP Minify neće moći da "
|
236 |
+
"ga doda glavnom nizu za minifikaciju. Ako želite da minifikujete<code>style."
|
237 |
+
"css</code> sa svojim ostalim css fajlovima, morate da ga stavite u red. "
|
238 |
+
|
239 |
+
#: includes/class-bwp-minify.php:275
|
240 |
+
#, php-format
|
241 |
+
msgid ""
|
242 |
+
"This should be set automatically. If you think the URL is too long, please "
|
243 |
+
"read <a href=\"%s#customization\">here</a> to know how to properly modify "
|
244 |
+
"this."
|
245 |
+
msgstr ""
|
246 |
+
"Ovo treba automatski da se podesi. Ako mislite da je URL predug, "
|
247 |
+
"pročitajte<a href=\"%s#customization\"> ovo </a>da biste znali kako da to "
|
248 |
+
"ispravno izmenite. "
|
249 |
+
|
250 |
+
#: includes/class-bwp-minify.php:276
|
251 |
+
#, php-format
|
252 |
+
msgid ""
|
253 |
+
"<strong>Important</strong>: Changing cache directory is a two-step process, "
|
254 |
+
"which is described in details <a href=\"%s#advanced_customization\" target="
|
255 |
+
"\"_blank\">here</a>. Please note that cache directory must be writable (i.e. "
|
256 |
+
"CHMOD to 755 or 777)."
|
257 |
+
msgstr ""
|
258 |
+
"<strong>Važno</strong>: Menjanje keš direktorijuma je proces u dva koraka, "
|
259 |
+
"koji je<a href=\"%s#advanced_customization\" target=\"_blank\"> ovde</a> "
|
260 |
+
"detaljno opisan. Imajte u vidu da keš direktorijum mora biti takav da se u "
|
261 |
+
"njemu može pisati (npr. CHMOD na 755 ili 777)."
|
262 |
+
|
263 |
+
#: includes/class-bwp-minify.php:277
|
264 |
+
msgid "file(s) at most."
|
265 |
+
msgstr "maksimum fajl(ova)."
|
266 |
+
|
267 |
+
#: includes/class-bwp-minify.php:278
|
268 |
+
msgid "—"
|
269 |
+
msgstr "—"
|
270 |
+
|
271 |
+
#: includes/class-bwp-minify.php:279
|
272 |
+
msgid "<em>→ /min/?f=file.js&ver=</em> "
|
273 |
+
msgstr "<em>→ /min/?f=file.js&ver=</em> "
|
274 |
+
|
275 |
+
#: includes/class-bwp-minify.php:289
|
276 |
+
msgid ""
|
277 |
+
"<em><strong>Note:</strong> When you append one of the things above you are "
|
278 |
+
"basically telling browsers to clear their cached version of your CSS and JS "
|
279 |
+
"files, which is very useful when you change source files. Use this feature "
|
280 |
+
"wisely :).</em>"
|
281 |
+
msgstr ""
|
282 |
+
"<em><strong> Napomena:</strong> Kada dopunite neki od gorenavedenih "
|
283 |
+
"stringova, u suštini poručujete pretraživačima da očiste svoju keširanu "
|
284 |
+
"verziju vaših CSS i JS fajlova, što je veoma korisno kada menjate izvorne "
|
285 |
+
"fajlove. Mudro koristite ovu karakteristiku :).</em>"
|
286 |
+
|
287 |
+
#: includes/class-bwp-minify.php:296
|
288 |
+
msgid "Flush the cache"
|
289 |
+
msgstr "Očistite keš"
|
290 |
+
|
291 |
+
#: includes/class-bwp-minify.php:316 includes/class-bwp-minify.php:318
|
292 |
+
msgid "Notice"
|
293 |
+
msgstr "Napomena"
|
294 |
+
|
295 |
+
#: includes/class-bwp-minify.php:316
|
296 |
+
#, php-format
|
297 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
298 |
+
msgstr "<strong>%d</strong> keširani fajlovi uspešno su obrisani! "
|
299 |
+
|
300 |
+
#: includes/class-bwp-minify.php:318
|
301 |
+
msgid ""
|
302 |
+
"Could not delete any cached files. Please manually check the cache directory."
|
303 |
+
msgstr ""
|
304 |
+
"Ne mogu da se obrišu keširani fajlovi. Molimo vas, ručno proverite keš "
|
305 |
+
"direktorijum. "
|
306 |
+
|
307 |
+
#: includes/class-bwp-minify.php:352
|
308 |
+
msgid "All options have been saved."
|
309 |
+
msgstr "Sve opcije su sačuvane"
|
310 |
+
|
311 |
+
#: includes/class-bwp-minify.php:368
|
312 |
+
msgid "Warning"
|
313 |
+
msgstr "Upozorenje"
|
314 |
+
|
315 |
+
#: includes/class-bwp-minify.php:368
|
316 |
+
msgid ""
|
317 |
+
"Cache directory does not exist or is not writable. Please try CHMOD your "
|
318 |
+
"cache directory to 755. If you still see this warning, CHMOD to 777."
|
319 |
+
msgstr ""
|
320 |
+
"Keš directorijum ne postoji ili se u njemu ne može pisati. Molim vas, "
|
321 |
+
"pokušajte da CHMOD svoj keš directorijum na 755. Ako i dalje vidite ovo "
|
322 |
+
"upozorenje, CHMOD na 777."
|
323 |
+
|
324 |
+
#: includes/class-bwp-minify.php:490
|
325 |
+
msgid "empty"
|
326 |
+
msgstr "prazno"
|
327 |
+
|
328 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:80
|
329 |
+
msgid "Plugin Configurations"
|
330 |
+
msgstr "Plugin konfiguracije"
|
331 |
+
|
332 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:398
|
333 |
+
msgid "Save Changes"
|
334 |
+
msgstr "Sačuvajte izmene"
|
languages/bwp-minify-tr_TR.mo
ADDED
Binary file
|
languages/bwp-minify-tr_TR.po
ADDED
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: BWP Minify\n"
|
4 |
+
"Report-Msgid-Bugs-To: \n"
|
5 |
+
"POT-Creation-Date: 2012-03-20 21:51+0700\n"
|
6 |
+
"PO-Revision-Date: 2012-03-29 10:45+0200\n"
|
7 |
+
"Last-Translator: kazancexpert <kazancexpert@gmail.com>\n"
|
8 |
+
"Language-Team: kazancexpert <kazancexpert@gmail.com>\n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n"
|
13 |
+
"X-Poedit-Basepath: .\n"
|
14 |
+
"X-Poedit-SourceCharset: utf-8\n"
|
15 |
+
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
16 |
+
"X-Poedit-Language: Turkish\n"
|
17 |
+
"X-Poedit-Country: TURKEY\n"
|
18 |
+
"X-Poedit-SearchPath-0: .\n"
|
19 |
+
|
20 |
+
#: includes/class-bwp-framework.php:177
|
21 |
+
#, php-format
|
22 |
+
msgid "%s requires WordPress <strong>%s</strong> or higher and PHP <strong>%s</strong> or higher. The plugin will not function until you update your software. Please deactivate this plugin."
|
23 |
+
msgstr "%s WordPress <strong>%s</strong> veya üzeri ve PHP <strong>%s</strong> veya üzeri gerektirir. Yazılımınızı güncelleştirine kadar eklenti çalışmaz. Lütfen bu eklentiyi etkisizleştirin."
|
24 |
+
|
25 |
+
#: includes/class-bwp-framework.php:188
|
26 |
+
msgid "Development Log"
|
27 |
+
msgstr "Geliştirme Günlüğü"
|
28 |
+
|
29 |
+
#: includes/class-bwp-framework.php:188
|
30 |
+
msgid "Frequently Asked Questions"
|
31 |
+
msgstr "Sıkça Sorulan Sorular"
|
32 |
+
|
33 |
+
#: includes/class-bwp-framework.php:188
|
34 |
+
msgid "FAQ"
|
35 |
+
msgstr "SSS"
|
36 |
+
|
37 |
+
#: includes/class-bwp-framework.php:188
|
38 |
+
msgid "Got a problem? Send me a feedback!"
|
39 |
+
msgstr "Bir sorun mu var? Bana bir geri bildirim gönderin!"
|
40 |
+
|
41 |
+
#: includes/class-bwp-framework.php:188
|
42 |
+
msgid "Contact"
|
43 |
+
msgstr "İletişim"
|
44 |
+
|
45 |
+
#: includes/class-bwp-framework.php:195
|
46 |
+
msgid "You can buy me some special coffees if you appreciate my work, thank you!"
|
47 |
+
msgstr "Eğer çalışmamı takdir ettiyseniz bana özel bir kahve alabilirsiniz, teşekkürler!"
|
48 |
+
|
49 |
+
#: includes/class-bwp-framework.php:209
|
50 |
+
msgid "Donate to "
|
51 |
+
msgstr "Bağış"
|
52 |
+
|
53 |
+
#: includes/class-bwp-framework.php:211
|
54 |
+
msgid "One cup $5.00"
|
55 |
+
msgstr "Bir fincan $5.00"
|
56 |
+
|
57 |
+
#: includes/class-bwp-framework.php:212
|
58 |
+
msgid "Two cups $10.00"
|
59 |
+
msgstr "İki fincan $10.00"
|
60 |
+
|
61 |
+
#: includes/class-bwp-framework.php:213
|
62 |
+
msgid "Five cups! $25.00"
|
63 |
+
msgstr "Beş fincan $25.00"
|
64 |
+
|
65 |
+
#: includes/class-bwp-framework.php:214
|
66 |
+
msgid "One LL-cup!!! $50.00"
|
67 |
+
msgstr "Bir LL -fincan!!! $50.00"
|
68 |
+
|
69 |
+
#: includes/class-bwp-framework.php:215
|
70 |
+
msgid "... or any amount!"
|
71 |
+
msgstr "... veya herhangi bir miktar!"
|
72 |
+
|
73 |
+
#: includes/class-bwp-framework.php:230
|
74 |
+
msgid "Latest updates from BetterWP.net!"
|
75 |
+
msgstr "BetterWP.net en son güncellemeleri!"
|
76 |
+
|
77 |
+
#: includes/class-bwp-framework.php:231
|
78 |
+
msgid "Follow me on Twitter!"
|
79 |
+
msgstr "Beni Twitter'da takip et!"
|
80 |
+
|
81 |
+
#: includes/class-bwp-framework.php:240
|
82 |
+
#, php-format
|
83 |
+
msgid "You are using version %s!"
|
84 |
+
msgstr "Sürüm %s kullanıyorsunuz!"
|
85 |
+
|
86 |
+
#: includes/class-bwp-framework.php:376
|
87 |
+
msgid "Settings"
|
88 |
+
msgstr "Ayarlar"
|
89 |
+
|
90 |
+
#: includes/class-bwp-minify.php:110
|
91 |
+
msgid "Better WordPress Minify Settings"
|
92 |
+
msgstr "Better WordPress Minify Ayarları"
|
93 |
+
|
94 |
+
#: includes/class-bwp-minify.php:196
|
95 |
+
msgid "Better WordPress Minify"
|
96 |
+
msgstr "Better WordPress Minify"
|
97 |
+
|
98 |
+
#: includes/class-bwp-minify.php:207
|
99 |
+
msgid "You do not have sufficient permissions to access this page."
|
100 |
+
msgstr "Bu sayfaya erişmek için yeterli izniniz yok."
|
101 |
+
|
102 |
+
#: includes/class-bwp-minify.php:223
|
103 |
+
msgid "General Options"
|
104 |
+
msgstr "Genel Seçenekler"
|
105 |
+
|
106 |
+
#: includes/class-bwp-minify.php:224
|
107 |
+
msgid "Minify JS files automatically?"
|
108 |
+
msgstr "JS dosyalarını otomatik küçült (Minify)?"
|
109 |
+
|
110 |
+
#: includes/class-bwp-minify.php:225
|
111 |
+
msgid "Minify CSS files automatically?"
|
112 |
+
msgstr "CSS dosyalarını otomatik küçült (Minify)?"
|
113 |
+
|
114 |
+
#: includes/class-bwp-minify.php:226
|
115 |
+
msgid "Minify <code>bloginfo()</code> stylesheets?"
|
116 |
+
msgstr "<code>bloginfo()</code> stil sayfalarını küçült?"
|
117 |
+
|
118 |
+
#: includes/class-bwp-minify.php:227
|
119 |
+
msgid "Minifying Options"
|
120 |
+
msgstr "Küçültme Seçenekleri"
|
121 |
+
|
122 |
+
#: includes/class-bwp-minify.php:228
|
123 |
+
msgid "Minify URL (double-click to edit)"
|
124 |
+
msgstr "Minify URL (düzenlemek için çift-tık)"
|
125 |
+
|
126 |
+
#: includes/class-bwp-minify.php:229
|
127 |
+
msgid "Cache directory (double-click to edit)"
|
128 |
+
msgstr "Önbellek dizini (düzenlemek için çift-tık)"
|
129 |
+
|
130 |
+
#: includes/class-bwp-minify.php:230
|
131 |
+
msgid "One minify string will contain"
|
132 |
+
msgstr "Bir minify dizesi en fazla"
|
133 |
+
|
134 |
+
#: includes/class-bwp-minify.php:231
|
135 |
+
msgid "Append the minify string with"
|
136 |
+
msgstr "Minify dizesine eklenecek"
|
137 |
+
|
138 |
+
#: includes/class-bwp-minify.php:232
|
139 |
+
msgid "Minifying Scripts Options"
|
140 |
+
msgstr "Küçültme Betik Seçenekleri"
|
141 |
+
|
142 |
+
#: includes/class-bwp-minify.php:233
|
143 |
+
msgid "Scripts to be minified in header"
|
144 |
+
msgstr "Header içine küçültülecek betikler(scripts)"
|
145 |
+
|
146 |
+
#: includes/class-bwp-minify.php:234
|
147 |
+
msgid "Scripts to be minified in footer"
|
148 |
+
msgstr "Footer içine küçültülecek betikler"
|
149 |
+
|
150 |
+
#: includes/class-bwp-minify.php:235
|
151 |
+
msgid "Scripts to be minified and then printed separately"
|
152 |
+
msgstr "Küçültülecek ve daha sonra ayrı yazdırılacak betikler"
|
153 |
+
|
154 |
+
#: includes/class-bwp-minify.php:236
|
155 |
+
msgid "Scripts to be ignored (not minified)"
|
156 |
+
msgstr "Göz ardı edilecek betikler (küçültülmeyecek)"
|
157 |
+
|
158 |
+
#: includes/class-bwp-minify.php:241
|
159 |
+
msgid "<em>Options that affect both your stylesheets and scripts.</em>"
|
160 |
+
msgstr "<em>Hem stil hem de komut dosyalarını etkileyen seçenekler.</em>"
|
161 |
+
|
162 |
+
#: includes/class-bwp-minify.php:242
|
163 |
+
#, php-format
|
164 |
+
msgid "<em>You can force the position of each script using those inputs below (e.g. you have a script registered in the header but you want to minify it in the footer instead). If you are still unsure, please read more <a href=\"%s#positioning-your-scripts\">here</a>. Type in one script handle (<strong>NOT filename</strong>) per line.</em>"
|
165 |
+
msgstr "<em>Bu girişi kullanarak her betik için konumunu zorlayabilirsiniz (örn. header içnde bir betik var fakat siz onun yerine footer içinde minify yapmak istiyorsunuz). Eğer hala emin değilseniz, lütfen daha fazla bilgi için <a href=\"%s#positioning-your-scripts\">buraya</a> bakın. Her satır için bir betik yolu (<strong>dosya adı DEĞİL</strong>) yazın.</em>"
|
166 |
+
|
167 |
+
#: includes/class-bwp-minify.php:246
|
168 |
+
msgid "second(s)"
|
169 |
+
msgstr "saniye(ler)"
|
170 |
+
|
171 |
+
#: includes/class-bwp-minify.php:247
|
172 |
+
msgid "minute(s)"
|
173 |
+
msgstr "dakika(lar)"
|
174 |
+
|
175 |
+
#: includes/class-bwp-minify.php:248
|
176 |
+
msgid "hour(s)"
|
177 |
+
msgstr "saat(ler)"
|
178 |
+
|
179 |
+
#: includes/class-bwp-minify.php:249
|
180 |
+
msgid "day(s)"
|
181 |
+
msgstr "gün(ler)"
|
182 |
+
|
183 |
+
#: includes/class-bwp-minify.php:252
|
184 |
+
msgid "Do not append anything"
|
185 |
+
msgstr "Bir şey ekleme"
|
186 |
+
|
187 |
+
#: includes/class-bwp-minify.php:253
|
188 |
+
msgid "Cache folder’s last modified time"
|
189 |
+
msgstr "Önbellek klasörü son değişiklik zamanı"
|
190 |
+
|
191 |
+
#: includes/class-bwp-minify.php:254
|
192 |
+
msgid "Your WordPress’s current version"
|
193 |
+
msgstr "Wordpress geçerli sürümünüz"
|
194 |
+
|
195 |
+
#: includes/class-bwp-minify.php:255
|
196 |
+
msgid "A custom number"
|
197 |
+
msgstr "Kişisel bir numara"
|
198 |
+
|
199 |
+
#: includes/class-bwp-minify.php:259
|
200 |
+
#: includes/class-bwp-minify.php:260
|
201 |
+
msgid "you can still use <code>bwp_minify()</code> helper function if you disable this."
|
202 |
+
msgstr "Bunu devre dışı bıraksanız da hala şablon fonksiyonu <code>bwp_minify()</code> kullanabilirsiniz."
|
203 |
+
|
204 |
+
#: includes/class-bwp-minify.php:261
|
205 |
+
msgid "most themes (e.g. Twenty Ten) use <code>bloginfo()</code> to print the main stylesheet (i.e. <code>style.css</code>) and BWP Minify will not be able to add it to the main minify string. If you want to minify <code>style.css</code> with the rest of your css files, you must enqueue it."
|
206 |
+
msgstr "Pek çok tema (örn. Twenty Ten) ana stil dosyasını (örn. <code>style.css</code>) print için <code>bloginfo()</code> kullanır ve BWP Minify onu ana minify dizesine ekleyemez. Eğer <code>style.css</code> dosyasını geri kalan css dosyaları ile birlikte küçültmek istiyorsanız, kuyruğa eklemeniz gerekir."
|
207 |
+
|
208 |
+
#: includes/class-bwp-minify.php:264
|
209 |
+
#, php-format
|
210 |
+
msgid "This should be set automatically. If you think the URL is too long, please read <a href=\"%s#customization\">here</a> to know how to properly modify this."
|
211 |
+
msgstr "Bunun otomatik olarak ayarlanması gerekir. Eğer URL çok uzunsa , doğru bir şekilde nasıl düzenlendiğini öğrenmek için <a href=\"%s#customization\">burayı</a> okuyun."
|
212 |
+
|
213 |
+
#: includes/class-bwp-minify.php:265
|
214 |
+
#, php-format
|
215 |
+
msgid "<strong>Important</strong>: Changing cache directory is a two-step process, which is described in details <a href=\"%s#config\" target=\"_blank\">here</a>. Please note that cache directory must be writable (i.e. CHMOD to 755 or 777)."
|
216 |
+
msgstr "<strong>Önemli</strong>: Önbellek dizinini değiştirmek iki adımlı bir işlemdir, <a href=\"%s#config\" target=\"_blank\">burada</a> ayrıntılılarıyla tarif edilmiştir. Önbellek dizininin yazılabilir olması gerektiğini unutmayın (örn. CHMOD 755 ya da 777)."
|
217 |
+
|
218 |
+
#: includes/class-bwp-minify.php:266
|
219 |
+
msgid "file(s) at most."
|
220 |
+
msgstr "dosya içerecektir."
|
221 |
+
|
222 |
+
#: includes/class-bwp-minify.php:267
|
223 |
+
msgid "—"
|
224 |
+
msgstr "—"
|
225 |
+
|
226 |
+
#: includes/class-bwp-minify.php:268
|
227 |
+
msgid "<em>→ /min/?f=file.js&ver=</em> "
|
228 |
+
msgstr "<em>→ /min/?f=file.js&ver=</em>"
|
229 |
+
|
230 |
+
#: includes/class-bwp-minify.php:278
|
231 |
+
msgid "<em><strong>Note:</strong> When you append one of the things above you are basically telling browsers to clear their cached version of your CSS and JS files, which is very useful when you change source files. Use this feature wisely :).</em>"
|
232 |
+
msgstr "<em><strong>Not:</strong> Yukarıdakilerden birini eklediğinizde tarayıcıya kısaca CSS ve JS dosyalarının önbellekli versiyonlarını temizlemesini söylersiniz, kaynak dosyası değiştiğinizde çok faydalı olur. Bu özelliği akıllıca kullanın :).</em>"
|
233 |
+
|
234 |
+
#: includes/class-bwp-minify.php:285
|
235 |
+
msgid "Flush the cache"
|
236 |
+
msgstr "Önbelleği boşalt"
|
237 |
+
|
238 |
+
#: includes/class-bwp-minify.php:305
|
239 |
+
#: includes/class-bwp-minify.php:307
|
240 |
+
msgid "Notice"
|
241 |
+
msgstr "Duyuru"
|
242 |
+
|
243 |
+
#: includes/class-bwp-minify.php:305
|
244 |
+
#, php-format
|
245 |
+
msgid "<strong>%d</strong> cached files have been deleted successfully!"
|
246 |
+
msgstr "Önbelleklenmiş <strong>%d</strong> dosya başarıyla silindi!"
|
247 |
+
|
248 |
+
#: includes/class-bwp-minify.php:307
|
249 |
+
msgid "Could not delete any cached files. Please manually check the cache directory."
|
250 |
+
msgstr "Önbelleğe alınmış dosyalar silinemedi. Lütfen önbellek dizinini el ile kontrol edin."
|
251 |
+
|
252 |
+
#: includes/class-bwp-minify.php:341
|
253 |
+
msgid "All options have been saved."
|
254 |
+
msgstr "Tüm seçenekler kaydedildi."
|
255 |
+
|
256 |
+
#: includes/class-bwp-minify.php:357
|
257 |
+
msgid "Warning"
|
258 |
+
msgstr "Uyarı"
|
259 |
+
|
260 |
+
#: includes/class-bwp-minify.php:357
|
261 |
+
msgid "Cache directory does not exist or is not writable. Please try CHMOD your cache directory to 755. If you still see this warning, CHMOD to 777."
|
262 |
+
msgstr "Önbellek dizini yok veya yazılabilir değil. Önbellek dizinini için CHMOD 755 izinini deneyin. Eğer halen uyarı varsa CHMOD 777 izinini deneyin."
|
263 |
+
|
264 |
+
#: includes/class-bwp-minify.php:471
|
265 |
+
msgid "empty"
|
266 |
+
msgstr "empty"
|
267 |
+
|
268 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:80
|
269 |
+
msgid "Plugin Configurations"
|
270 |
+
msgstr "Eklenti Konfigürasyonu"
|
271 |
+
|
272 |
+
#: includes/bwp-option-page/includes/class-bwp-option-page.php:398
|
273 |
+
msgid "Save Changes"
|
274 |
+
msgstr "Değişiklikleri Kaydet"
|
275 |
+
|
min/.htaccess
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<IfModule mod_rewrite.c>
|
2 |
+
RewriteEngine on
|
3 |
+
|
4 |
+
# You may need RewriteBase on some servers
|
5 |
+
#RewriteBase /min
|
6 |
+
|
7 |
+
# rewrite URLs like "/min/f=..." to "/min/?f=..."
|
8 |
+
RewriteRule ^([bfg]=.*) index.php?$1 [L,NE]
|
9 |
+
</IfModule>
|
10 |
+
<IfModule mod_env.c>
|
11 |
+
# In case AddOutputFilterByType has been added
|
12 |
+
SetEnv no-gzip
|
13 |
+
</IfModule>
|
min/README.txt
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
The files in this directory represent the default Minify setup designed to ease
|
2 |
+
integration with your site. This app will combine and minify your Javascript or
|
3 |
+
CSS files and serve them with HTTP compression and cache headers.
|
4 |
+
|
5 |
+
|
6 |
+
RECOMMENDED
|
7 |
+
|
8 |
+
It's recommended to edit config.php to set $min_cachePath to a writeable
|
9 |
+
(by PHP) directory on your system. This will improve performance.
|
10 |
+
|
11 |
+
|
12 |
+
GETTING STARTED
|
13 |
+
|
14 |
+
The quickest way to get started is to use the Minify URI Builder application
|
15 |
+
on your website: http://example.com/min/builder/
|
16 |
+
|
17 |
+
|
18 |
+
MINIFYING A SINGLE FILE
|
19 |
+
|
20 |
+
Let's say you want to serve this file:
|
21 |
+
http://example.com/wp-content/themes/default/default.css
|
22 |
+
|
23 |
+
Here's the "Minify URL" for this file:
|
24 |
+
http://example.com/min/?f=wp-content/themes/default/default.css
|
25 |
+
|
26 |
+
In other words, the "f" argument is set to the file path from root without the
|
27 |
+
initial "/". As CSS files may contain relative URIs, Minify will automatically
|
28 |
+
"fix" these by rewriting them as root relative.
|
29 |
+
|
30 |
+
|
31 |
+
COMBINING MULTIPLE FILES IN ONE DOWNLOAD
|
32 |
+
|
33 |
+
Separate the paths given to "f" with commas.
|
34 |
+
|
35 |
+
Let's say you have CSS files at these URLs:
|
36 |
+
http://example.com/scripts/jquery-1.2.6.js
|
37 |
+
http://example.com/scripts/site.js
|
38 |
+
|
39 |
+
You can combine these files through Minify by requesting this URL:
|
40 |
+
http://example.com/min/?f=scripts/jquery-1.2.6.js,scripts/site.js
|
41 |
+
|
42 |
+
|
43 |
+
SIMPLIFYING URLS WITH A BASE PATH
|
44 |
+
|
45 |
+
If you're combining files that share the same ancestor directory, you can use
|
46 |
+
the "b" argument to set the base directory for the "f" argument. Do not include
|
47 |
+
the leading or trailing "/" characters.
|
48 |
+
|
49 |
+
E.g., the following URLs will serve the exact same content:
|
50 |
+
http://example.com/min/?f=scripts/jquery-1.2.6.js,scripts/site.js,scripts/home.js
|
51 |
+
http://example.com/min/?b=scripts&f=jquery-1.2.6.js,site.js,home.js
|
52 |
+
|
53 |
+
|
54 |
+
MINIFY URLS IN HTML
|
55 |
+
|
56 |
+
In (X)HTML files, don't forget to replace any "&" characters with "&".
|
57 |
+
|
58 |
+
|
59 |
+
SPECIFYING ALLOWED DIRECTORIES
|
60 |
+
|
61 |
+
By default, Minify will serve any *.css/*.js files within the DOCUMENT_ROOT. If
|
62 |
+
you'd prefer to limit Minify's access to certain directories, set the
|
63 |
+
$min_serveOptions['minApp']['allowDirs'] array in config.php. E.g. to limit
|
64 |
+
to the /js and /themes/default directories, use:
|
65 |
+
|
66 |
+
$min_serveOptions['minApp']['allowDirs'] = array('//js', '//themes/default');
|
67 |
+
|
68 |
+
|
69 |
+
GROUPS: NICER URLS
|
70 |
+
|
71 |
+
For nicer URLs, edit groupsConfig.php to pre-specify groups of files
|
72 |
+
to be combined under preset keys. E.g., here's an example configuration in
|
73 |
+
groupsConfig.php:
|
74 |
+
|
75 |
+
return array(
|
76 |
+
'js' => array('//js/Class.js', '//js/email.js')
|
77 |
+
);
|
78 |
+
|
79 |
+
This pre-selects the following files to be combined under the key "js":
|
80 |
+
http://example.com/js/Class.js
|
81 |
+
http://example.com/js/email.js
|
82 |
+
|
83 |
+
You can now serve these files with this simple URL:
|
84 |
+
http://example.com/min/?g=js
|
85 |
+
|
86 |
+
|
87 |
+
GROUPS: SPECIFYING FILES OUTSIDE THE DOC_ROOT
|
88 |
+
|
89 |
+
In the groupsConfig.php array, the "//" in the file paths is a shortcut for
|
90 |
+
the DOCUMENT_ROOT, but you can also specify paths from the root of the filesystem
|
91 |
+
or relative to the DOC_ROOT:
|
92 |
+
|
93 |
+
return array(
|
94 |
+
'js' => array(
|
95 |
+
'//js/file.js' // file within DOC_ROOT
|
96 |
+
,'//../file.js' // file in parent directory of DOC_ROOT
|
97 |
+
,'C:/Users/Steve/file.js' // file anywhere on filesystem
|
98 |
+
)
|
99 |
+
);
|
100 |
+
|
101 |
+
|
102 |
+
COMBINE MULTIPLE GROUPS AND FILES IN ONE URL
|
103 |
+
|
104 |
+
E.g.: http://example.com/min/?g=js&f=more/scripts.js
|
105 |
+
|
106 |
+
Separate group keys with commas:
|
107 |
+
http://example.com/min/?g=baseCss,css1&f=moreStyles.css
|
108 |
+
|
109 |
+
|
110 |
+
FAR-FUTURE EXPIRES HEADERS
|
111 |
+
|
112 |
+
Minify can send far-future (one year) Expires headers. To enable this you must
|
113 |
+
add a number to the querystring (e.g. /min/?g=js&1234 or /min/f=file.js&1234)
|
114 |
+
and alter it whenever a source file is changed. If you have a build process you
|
115 |
+
can use a build/source control revision number.
|
116 |
+
|
117 |
+
You can alternately use the utility function Minify_getUri() to get a "versioned"
|
118 |
+
Minify URI for use in your HTML. E.g.:
|
119 |
+
|
120 |
+
<?php
|
121 |
+
require $_SERVER['DOCUMENT_ROOT'] . '/min/utils.php';
|
122 |
+
|
123 |
+
$jsUri = Minify_getUri('js'); // a key in groupsConfig.php
|
124 |
+
echo "<script src='{$jsUri}'></script>";
|
125 |
+
|
126 |
+
$cssUri = Minify_getUri(array(
|
127 |
+
'//css/styles1.css'
|
128 |
+
,'//css/styles2.css'
|
129 |
+
)); // a list of files
|
130 |
+
echo "<link rel=stylesheet href='{$cssUri}'>";
|
131 |
+
|
132 |
+
|
133 |
+
DEBUG MODE
|
134 |
+
|
135 |
+
In debug mode, instead of compressing files, Minify sends combined files with
|
136 |
+
comments prepended to each line to show the line number in the original source
|
137 |
+
file. To enable this, set $min_allowDebugFlag to true in config.php and append
|
138 |
+
"&debug=1" to your URIs. E.g. /min/?f=script1.js,script2.js&debug=1
|
139 |
+
|
140 |
+
Known issue: files with comment-like strings/regexps can cause problems in this mode.
|
141 |
+
|
142 |
+
|
143 |
+
QUESTIONS?
|
144 |
+
|
145 |
+
http://groups.google.com/group/minify
|
min/builder/_index.js
ADDED
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Minify URI Builder
|
3 |
+
*/
|
4 |
+
var MUB = {
|
5 |
+
_uid : 0
|
6 |
+
,_minRoot : '/min/?'
|
7 |
+
,checkRewrite : function () {
|
8 |
+
var testUri = location.pathname.replace(/\/[^\/]*$/, '/rewriteTest.js').substr(1);
|
9 |
+
function fail() {
|
10 |
+
$('#minRewriteFailed')[0].className = 'topNote';
|
11 |
+
};
|
12 |
+
$.ajax({
|
13 |
+
url : '../f=' + testUri + '&' + (new Date()).getTime()
|
14 |
+
,success : function (data) {
|
15 |
+
if (data === '1') {
|
16 |
+
MUB._minRoot = '/min/';
|
17 |
+
$('span.minRoot').html('/min/');
|
18 |
+
} else
|
19 |
+
fail();
|
20 |
+
}
|
21 |
+
,error : fail
|
22 |
+
});
|
23 |
+
}
|
24 |
+
/**
|
25 |
+
* Get markup for new source LI element
|
26 |
+
*/
|
27 |
+
,newLi : function () {
|
28 |
+
return '<li id="li' + MUB._uid + '">http://' + location.host + '/<input type=text size=20>'
|
29 |
+
+ ' <button title="Remove">x</button> <button title="Include Earlier">↑</button>'
|
30 |
+
+ ' <button title="Include Later">↓</button> <span></span></li>';
|
31 |
+
}
|
32 |
+
/**
|
33 |
+
* Add new empty source LI and attach handlers to buttons
|
34 |
+
*/
|
35 |
+
,addLi : function () {
|
36 |
+
$('#sources').append(MUB.newLi());
|
37 |
+
var li = $('#li' + MUB._uid)[0];
|
38 |
+
$('button[title=Remove]', li).click(function () {
|
39 |
+
$('#results').hide();
|
40 |
+
var hadValue = !!$('input', li)[0].value;
|
41 |
+
$(li).remove();
|
42 |
+
});
|
43 |
+
$('button[title$=Earlier]', li).click(function () {
|
44 |
+
$(li).prev('li').find('input').each(function () {
|
45 |
+
$('#results').hide();
|
46 |
+
// this = previous li input
|
47 |
+
var tmp = this.value;
|
48 |
+
this.value = $('input', li).val();
|
49 |
+
$('input', li).val(tmp);
|
50 |
+
MUB.updateAllTestLinks();
|
51 |
+
});
|
52 |
+
});
|
53 |
+
$('button[title$=Later]', li).click(function () {
|
54 |
+
$(li).next('li').find('input').each(function () {
|
55 |
+
$('#results').hide();
|
56 |
+
// this = next li input
|
57 |
+
var tmp = this.value;
|
58 |
+
this.value = $('input', li).val();
|
59 |
+
$('input', li).val(tmp);
|
60 |
+
MUB.updateAllTestLinks();
|
61 |
+
});
|
62 |
+
});
|
63 |
+
++MUB._uid;
|
64 |
+
}
|
65 |
+
/**
|
66 |
+
* In the context of a source LI element, this will analyze the URI in
|
67 |
+
* the INPUT and check the URL on the site.
|
68 |
+
*/
|
69 |
+
,liUpdateTestLink : function () { // call in context of li element
|
70 |
+
if (! $('input', this)[0].value)
|
71 |
+
return;
|
72 |
+
var li = this;
|
73 |
+
$('span', this).html('');
|
74 |
+
var url = 'http://' + location.host + '/'
|
75 |
+
+ $('input', this)[0].value.replace(/^\//, '');
|
76 |
+
$.ajax({
|
77 |
+
url : url
|
78 |
+
,complete : function (xhr, stat) {
|
79 |
+
if ('success' == stat)
|
80 |
+
$('span', li).html('✓');
|
81 |
+
else {
|
82 |
+
$('span', li).html('<button><b>404! </b> recheck</button>')
|
83 |
+
.find('button').click(function () {
|
84 |
+
MUB.liUpdateTestLink.call(li);
|
85 |
+
});
|
86 |
+
}
|
87 |
+
}
|
88 |
+
,dataType : 'text'
|
89 |
+
});
|
90 |
+
}
|
91 |
+
/**
|
92 |
+
* Check all source URLs
|
93 |
+
*/
|
94 |
+
,updateAllTestLinks : function () {
|
95 |
+
$('#sources li').each(MUB.liUpdateTestLink);
|
96 |
+
}
|
97 |
+
/**
|
98 |
+
* In a given array of strings, find the character they all have at
|
99 |
+
* a particular index
|
100 |
+
* @param Array arr array of strings
|
101 |
+
* @param Number pos index to check
|
102 |
+
* @return mixed a common char or '' if any do not match
|
103 |
+
*/
|
104 |
+
,getCommonCharAtPos : function (arr, pos) {
|
105 |
+
var i
|
106 |
+
,l = arr.length
|
107 |
+
,c = arr[0].charAt(pos);
|
108 |
+
if (c === '' || l === 1)
|
109 |
+
return c;
|
110 |
+
for (i = 1; i < l; ++i)
|
111 |
+
if (arr[i].charAt(pos) !== c)
|
112 |
+
return '';
|
113 |
+
return c;
|
114 |
+
}
|
115 |
+
/**
|
116 |
+
* Get the shortest URI to minify the set of source files
|
117 |
+
* @param Array sources URIs
|
118 |
+
*/
|
119 |
+
,getBestUri : function (sources) {
|
120 |
+
var pos = 0
|
121 |
+
,base = ''
|
122 |
+
,c;
|
123 |
+
while (true) {
|
124 |
+
c = MUB.getCommonCharAtPos(sources, pos);
|
125 |
+
if (c === '')
|
126 |
+
break;
|
127 |
+
else
|
128 |
+
base += c;
|
129 |
+
++pos;
|
130 |
+
}
|
131 |
+
base = base.replace(/[^\/]+$/, '');
|
132 |
+
var uri = MUB._minRoot + 'f=' + sources.join(',');
|
133 |
+
if (base.charAt(base.length - 1) === '/') {
|
134 |
+
// we have a base dir!
|
135 |
+
var basedSources = sources
|
136 |
+
,i
|
137 |
+
,l = sources.length;
|
138 |
+
for (i = 0; i < l; ++i) {
|
139 |
+
basedSources[i] = sources[i].substr(base.length);
|
140 |
+
}
|
141 |
+
base = base.substr(0, base.length - 1);
|
142 |
+
var bUri = MUB._minRoot + 'b=' + base + '&f=' + basedSources.join(',');
|
143 |
+
//window.console && console.log([uri, bUri]);
|
144 |
+
uri = uri.length < bUri.length
|
145 |
+
? uri
|
146 |
+
: bUri;
|
147 |
+
}
|
148 |
+
return uri;
|
149 |
+
}
|
150 |
+
/**
|
151 |
+
* Create the Minify URI for the sources
|
152 |
+
*/
|
153 |
+
,update : function () {
|
154 |
+
MUB.updateAllTestLinks();
|
155 |
+
var sources = []
|
156 |
+
,ext = false
|
157 |
+
,fail = false;
|
158 |
+
$('#sources input').each(function () {
|
159 |
+
var m, val;
|
160 |
+
if (! fail && this.value && (m = this.value.match(/\.(css|js)$/))) {
|
161 |
+
var thisExt = m[1];
|
162 |
+
if (ext === false)
|
163 |
+
ext = thisExt;
|
164 |
+
else if (thisExt !== ext) {
|
165 |
+
fail = true;
|
166 |
+
return alert('extensions must match!');
|
167 |
+
}
|
168 |
+
this.value = this.value.replace(/^\//, '');
|
169 |
+
if (-1 != $.inArray(this.value, sources)) {
|
170 |
+
fail = true;
|
171 |
+
return alert('duplicate file!');
|
172 |
+
}
|
173 |
+
sources.push(this.value);
|
174 |
+
}
|
175 |
+
});
|
176 |
+
if (fail || ! sources.length)
|
177 |
+
return;
|
178 |
+
$('#groupConfig').val(" 'keyName' => array('//" + sources.join("', '//") + "'),");
|
179 |
+
var uri = MUB.getBestUri(sources)
|
180 |
+
,uriH = uri.replace(/</, '<').replace(/>/, '>').replace(/&/, '&');
|
181 |
+
$('#uriA').html(uriH)[0].href = uri;
|
182 |
+
$('#uriHtml').val(
|
183 |
+
ext === 'js'
|
184 |
+
? '<script type="text/javascript" src="' + uriH + '"></script>'
|
185 |
+
: '<link type="text/css" rel="stylesheet" href="' + uriH + '" />'
|
186 |
+
);
|
187 |
+
$('#results').show();
|
188 |
+
}
|
189 |
+
/**
|
190 |
+
* Handler for the "Add file +" button
|
191 |
+
*/
|
192 |
+
,addButtonClick : function () {
|
193 |
+
$('#results').hide();
|
194 |
+
MUB.addLi();
|
195 |
+
MUB.updateAllTestLinks();
|
196 |
+
$('#update').show().click(MUB.update);
|
197 |
+
$('#sources li:last input')[0].focus();
|
198 |
+
}
|
199 |
+
/**
|
200 |
+
* Runs on DOMready
|
201 |
+
*/
|
202 |
+
,init : function () {
|
203 |
+
$('#jsDidntLoad').hide();
|
204 |
+
$('#app').show();
|
205 |
+
$('#sources').html('');
|
206 |
+
$('#add button').click(MUB.addButtonClick);
|
207 |
+
// make easier to copy text out of
|
208 |
+
$('#uriHtml, #groupConfig, #symlinkOpt').click(function () {
|
209 |
+
this.select();
|
210 |
+
}).focus(function () {
|
211 |
+
this.select();
|
212 |
+
});
|
213 |
+
$('a.ext').attr({target:'_blank'});
|
214 |
+
if (location.hash) {
|
215 |
+
// make links out of URIs from bookmarklet
|
216 |
+
$('#getBm').hide();
|
217 |
+
$('#bmUris').html('<p><strong>Found by bookmarklet:</strong> /<a href=#>'
|
218 |
+
+ location.hash.substr(1).split(',').join('</a> | /<a href=#>')
|
219 |
+
+ '</a></p>'
|
220 |
+
);
|
221 |
+
$('#bmUris a').click(function () {
|
222 |
+
MUB.addButtonClick();
|
223 |
+
$('#sources li:last input').val(this.innerHTML)
|
224 |
+
MUB.liUpdateTestLink.call($('#sources li:last')[0]);
|
225 |
+
$('#results').hide();
|
226 |
+
return false;
|
227 |
+
}).attr({title:'Add file +'});
|
228 |
+
} else {
|
229 |
+
// setup bookmarklet 1
|
230 |
+
$.ajax({
|
231 |
+
url : '../?f=' + location.pathname.replace(/\/[^\/]*$/, '/bm.js').substr(1)
|
232 |
+
,success : function (code) {
|
233 |
+
$('#bm')[0].href = code
|
234 |
+
.replace('%BUILDER_URL%', location.href)
|
235 |
+
.replace(/\n/g, ' ');
|
236 |
+
}
|
237 |
+
,dataType : 'text'
|
238 |
+
});
|
239 |
+
$.browser.msie && $('#getBm p:last').append(' Sorry, not supported in MSIE!');
|
240 |
+
MUB.addButtonClick();
|
241 |
+
}
|
242 |
+
// setup bookmarklet 2
|
243 |
+
$.ajax({
|
244 |
+
url : '../?f=' + location.pathname.replace(/\/[^\/]*$/, '/bm2.js').substr(1)
|
245 |
+
,success : function (code) {
|
246 |
+
$('#bm2')[0].href = code.replace(/\n/g, ' ');
|
247 |
+
}
|
248 |
+
,dataType : 'text'
|
249 |
+
});
|
250 |
+
MUB.checkRewrite();
|
251 |
+
}
|
252 |
+
};
|
253 |
+
$(MUB.init);
|
min/builder/bm.js
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
javascript:(function() {
|
2 |
+
var d = document
|
3 |
+
,uris = []
|
4 |
+
,i = 0
|
5 |
+
,o
|
6 |
+
,home = (location + '').split('/').splice(0, 3).join('/') + '/';
|
7 |
+
function add(uri) {
|
8 |
+
return (0 === uri.indexOf(home))
|
9 |
+
&& (!/[\?&]/.test(uri))
|
10 |
+
&& uris.push(escape(uri.substr(home.length)));
|
11 |
+
};
|
12 |
+
function sheet(ss) {
|
13 |
+
// we must check the domain with add() before accessing ss.cssRules
|
14 |
+
// otherwise a security exception will be thrown
|
15 |
+
if (ss.href && add(ss.href) && ss.cssRules) {
|
16 |
+
var i = 0, r;
|
17 |
+
while (r = ss.cssRules[i++])
|
18 |
+
r.styleSheet && sheet(r.styleSheet);
|
19 |
+
}
|
20 |
+
};
|
21 |
+
while (o = d.getElementsByTagName('script')[i++])
|
22 |
+
o.src && !(o.type && /vbs/i.test(o.type)) && add(o.src);
|
23 |
+
i = 0;
|
24 |
+
while (o = d.styleSheets[i++])
|
25 |
+
/* http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-DocumentStyle-styleSheets
|
26 |
+
document.styleSheet is a list property where [0] accesses the 1st element and
|
27 |
+
[outOfRange] returns null. In IE, styleSheets is a function, and also throws an
|
28 |
+
exception when you check the out of bounds index. (sigh) */
|
29 |
+
sheet(o);
|
30 |
+
if (uris.length)
|
31 |
+
window.open('%BUILDER_URL%#' + uris.join(','));
|
32 |
+
else
|
33 |
+
alert('No js/css files found with URLs within "'
|
34 |
+
+ home.split('/')[2]
|
35 |
+
+ '".\n(This tool is limited to URLs with the same domain.)');
|
36 |
+
})();
|
min/builder/bm2.js
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
javascript:(function(){
|
2 |
+
var d = document
|
3 |
+
,c = d.cookie
|
4 |
+
,m = c.match(/\bminDebug=([^; ]+)/)
|
5 |
+
,v = m ? decodeURIComponent(m[1]) : ''
|
6 |
+
,p = prompt('Debug Minify URIs on ' + location.hostname + ' which contain:'
|
7 |
+
+ '\n(empty for none, space = OR)', v)
|
8 |
+
;
|
9 |
+
if (p === null) return;
|
10 |
+
p = p.replace(/^\s+|\s+$/, '');
|
11 |
+
v = (p === '')
|
12 |
+
? 'minDebug=; expires=Fri, 27 Jul 2001 02:47:11 UTC; path=/'
|
13 |
+
: 'minDebug=' + encodeURIComponent(p) + '; path=/';
|
14 |
+
d.cookie = v;
|
15 |
+
})();
|
min/builder/index.php
ADDED
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if (phpversion() < 5) {
|
4 |
+
exit('Minify requires PHP5 or greater.');
|
5 |
+
}
|
6 |
+
|
7 |
+
// check for auto-encoding
|
8 |
+
$encodeOutput = (function_exists('gzdeflate')
|
9 |
+
&& !ini_get('zlib.output_compression'));
|
10 |
+
|
11 |
+
// recommend $min_symlinks setting for Apache UserDir
|
12 |
+
$symlinkOption = '';
|
13 |
+
if (0 === strpos($_SERVER["SERVER_SOFTWARE"], 'Apache/')
|
14 |
+
&& preg_match('@^/\\~(\\w+)/@', $_SERVER['REQUEST_URI'], $m)
|
15 |
+
) {
|
16 |
+
$userDir = DIRECTORY_SEPARATOR . $m[1] . DIRECTORY_SEPARATOR;
|
17 |
+
if (false !== strpos(__FILE__, $userDir)) {
|
18 |
+
$sm = array();
|
19 |
+
$sm["//~{$m[1]}"] = dirname(dirname(__FILE__));
|
20 |
+
$array = str_replace('array (', 'array(', var_export($sm, 1));
|
21 |
+
$symlinkOption = "\$min_symlinks = $array;";
|
22 |
+
}
|
23 |
+
}
|
24 |
+
|
25 |
+
require dirname(__FILE__) . '/../config.php';
|
26 |
+
|
27 |
+
if (! $min_enableBuilder) {
|
28 |
+
header('Location: /');
|
29 |
+
exit();
|
30 |
+
}
|
31 |
+
|
32 |
+
$setIncludeSuccess = set_include_path(dirname(__FILE__) . '/../lib' . PATH_SEPARATOR . get_include_path());
|
33 |
+
// we do it this way because we want the builder to work after the user corrects
|
34 |
+
// include_path. (set_include_path returning FALSE is OK).
|
35 |
+
try {
|
36 |
+
require_once 'Solar/Dir.php';
|
37 |
+
} catch (Exception $e) {
|
38 |
+
if (! $setIncludeSuccess) {
|
39 |
+
echo "Minify: set_include_path() failed. You may need to set your include_path "
|
40 |
+
."outside of PHP code, e.g., in php.ini.";
|
41 |
+
} else {
|
42 |
+
echo $e->getMessage();
|
43 |
+
}
|
44 |
+
exit();
|
45 |
+
}
|
46 |
+
require 'Minify.php';
|
47 |
+
|
48 |
+
$cachePathCode = '';
|
49 |
+
if (! isset($min_cachePath)) {
|
50 |
+
$detectedTmp = rtrim(Solar_Dir::tmp(), DIRECTORY_SEPARATOR);
|
51 |
+
$cachePathCode = "\$min_cachePath = " . var_export($detectedTmp, 1) . ';';
|
52 |
+
}
|
53 |
+
|
54 |
+
ob_start();
|
55 |
+
?>
|
56 |
+
<!DOCTYPE HTML>
|
57 |
+
<title>Minify URI Builder</title>
|
58 |
+
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
|
59 |
+
<style>
|
60 |
+
body {margin:1em 60px;}
|
61 |
+
h1, h2, h3 {margin-left:-25px; position:relative;}
|
62 |
+
h1 {margin-top:0;}
|
63 |
+
#sources {margin:0; padding:0;}
|
64 |
+
#sources li {margin:0 0 0 40px}
|
65 |
+
#sources li input {margin-left:2px}
|
66 |
+
#add {margin:5px 0 1em 40px}
|
67 |
+
.hide {display:none}
|
68 |
+
#uriTable {border-collapse:collapse;}
|
69 |
+
#uriTable td, #uriTable th {padding-top:10px;}
|
70 |
+
#uriTable th {padding-right:10px;}
|
71 |
+
#groupConfig {font-family:monospace;}
|
72 |
+
b {color:#c00}
|
73 |
+
.topNote {background: #ff9; display:inline-block; padding:.5em .6em; margin:0 0 1em;}
|
74 |
+
.topWarning {background:#c00; color:#fff; padding:.5em .6em; margin:0 0 1em;}
|
75 |
+
.topWarning a {color:#fff;}
|
76 |
+
</style>
|
77 |
+
<body>
|
78 |
+
<?php if ($symlinkOption): ?>
|
79 |
+
<div class=topNote><strong>Note:</strong> It looks like you're running Minify in a user
|
80 |
+
directory. You may need the following option in /min/config.php to have URIs
|
81 |
+
correctly rewritten in CSS output:
|
82 |
+
<br><textarea id=symlinkOpt rows=3 cols=80 readonly><?php echo htmlspecialchars($symlinkOption); ?></textarea>
|
83 |
+
</div>
|
84 |
+
<?php endif; ?>
|
85 |
+
|
86 |
+
<p class=topWarning id=jsDidntLoad><strong>Uh Oh.</strong> Minify was unable to
|
87 |
+
serve Javascript for this app. To troubleshoot this,
|
88 |
+
<a href="http://code.google.com/p/minify/wiki/Debugging">enable FirePHP debugging</a>
|
89 |
+
and request the <a id=builderScriptSrc href=#>Minify URL</a> directly. Hopefully the
|
90 |
+
FirePHP console will report the cause of the error.
|
91 |
+
</p>
|
92 |
+
|
93 |
+
<?php if ($cachePathCode): ?>
|
94 |
+
<p class=topNote><strong>Note:</strong> <code><?php echo
|
95 |
+
htmlspecialchars($detectedTmp); ?></code> was discovered as a usable temp directory.<br>To
|
96 |
+
slightly improve performance you can hardcode this in /min/config.php:
|
97 |
+
<code><?php echo htmlspecialchars($cachePathCode); ?></code></p>
|
98 |
+
<?php endIf; ?>
|
99 |
+
|
100 |
+
<p id=minRewriteFailed class="hide"><strong>Note:</strong> Your webserver does not seem to
|
101 |
+
support mod_rewrite (used in /min/.htaccess). Your Minify URIs will contain "?", which
|
102 |
+
<a href="http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/"
|
103 |
+
>may reduce the benefit of proxy cache servers</a>.</p>
|
104 |
+
|
105 |
+
<h1>Minify URI Builder</h1>
|
106 |
+
|
107 |
+
<noscript><p class="topNote">Javascript and a browser supported by jQuery 1.2.6 is required
|
108 |
+
for this application.</p></noscript>
|
109 |
+
|
110 |
+
<div id=app class=hide>
|
111 |
+
|
112 |
+
<p>Create a list of Javascript or CSS files (or 1 is fine) you'd like to combine
|
113 |
+
and click [Update].</p>
|
114 |
+
|
115 |
+
<ol id=sources><li></li></ol>
|
116 |
+
<div id=add><button>Add file +</button></div>
|
117 |
+
|
118 |
+
<div id=bmUris></div>
|
119 |
+
|
120 |
+
<p><button id=update class=hide>Update</button></p>
|
121 |
+
|
122 |
+
<div id=results class=hide>
|
123 |
+
|
124 |
+
<h2>Minify URI</h2>
|
125 |
+
<p>Place this URI in your HTML to serve the files above combined, minified, compressed and
|
126 |
+
with cache headers.</p>
|
127 |
+
<table id=uriTable>
|
128 |
+
<tr><th>URI</th><td><a id=uriA class=ext>/min</a> <small>(opens in new window)</small></td></tr>
|
129 |
+
<tr><th>HTML</th><td><input id=uriHtml type=text size=100 readonly></td></tr>
|
130 |
+
</table>
|
131 |
+
|
132 |
+
<h2>How to serve these files as a group</h2>
|
133 |
+
<p>For the best performance you can serve these files as a pre-defined group with a URI
|
134 |
+
like: <code><span class=minRoot>/min/?</span>g=keyName</code></p>
|
135 |
+
<p>To do this, add a line like this to /min/groupsConfig.php:</p>
|
136 |
+
|
137 |
+
<pre><code>return array(
|
138 |
+
<span style="color:#666">... your existing groups here ...</span>
|
139 |
+
<input id=groupConfig size=100 type=text readonly>
|
140 |
+
);</code></pre>
|
141 |
+
|
142 |
+
<p><em>Make sure to replace <code>keyName</code> with a unique key for this group.</em></p>
|
143 |
+
</div>
|
144 |
+
|
145 |
+
<div id=getBm>
|
146 |
+
<h3>Find URIs on a Page</h3>
|
147 |
+
<p>You can use the bookmarklet below to fetch all CSS & Javascript URIs from a page
|
148 |
+
on your site. When you active it, this page will open in a new window with a list of
|
149 |
+
available URIs to add.</p>
|
150 |
+
|
151 |
+
<p><a id=bm>Create Minify URIs</a> <small>(right-click, add to bookmarks)</small></p>
|
152 |
+
</div>
|
153 |
+
|
154 |
+
<h3>Combining CSS files that contain <code>@import</code></h3>
|
155 |
+
<p>If your CSS files contain <code>@import</code> declarations, Minify will not
|
156 |
+
remove them. Therefore, you will want to remove those that point to files already
|
157 |
+
in your list, and move any others to the top of the first file in your list
|
158 |
+
(imports below any styles will be ignored by browsers as invalid).</p>
|
159 |
+
<p>If you desire, you can use Minify URIs in imports and they will not be touched
|
160 |
+
by Minify. E.g. <code>@import "<span class=minRoot>/min/?</span>g=css2";</code></p>
|
161 |
+
|
162 |
+
<h3>Debug Mode</h3>
|
163 |
+
<p>When /min/config.php has <code>$min_allowDebugFlag = <strong>true</strong>;</code>
|
164 |
+
you can get debug output by appending <code>&debug</code> to a Minify URL, or
|
165 |
+
by sending the cookie <code>minDebug=<match></code>, where <code><match></code>
|
166 |
+
should be a string in the Minify URIs you'd like to debug. This bookmarklet will allow you to
|
167 |
+
set this cookie.</p>
|
168 |
+
<p><a id=bm2>Minify Debug</a> <small>(right-click, add to bookmarks)</small></p>
|
169 |
+
|
170 |
+
</div><!-- #app -->
|
171 |
+
|
172 |
+
<hr>
|
173 |
+
<p>Need help? Check the <a href="http://code.google.com/p/minify/w/list?can=3">wiki</a>,
|
174 |
+
or post to the <a class=ext href="http://groups.google.com/group/minify">discussion
|
175 |
+
list</a>.</p>
|
176 |
+
<p><small>Powered by Minify <?php echo Minify::VERSION; ?></small></p>
|
177 |
+
|
178 |
+
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
|
179 |
+
<script>
|
180 |
+
$(function () {
|
181 |
+
// give Minify a few seconds to serve _index.js before showing scary red warning
|
182 |
+
$('#jsDidntLoad').hide();
|
183 |
+
setTimeout(function () {
|
184 |
+
if (! window.MUB) {
|
185 |
+
// Minify didn't load
|
186 |
+
$('#jsDidntLoad').show();
|
187 |
+
}
|
188 |
+
}, 3000);
|
189 |
+
|
190 |
+
// detection of double output encoding
|
191 |
+
var msg = '<\p class=topWarning><\strong>Warning:<\/strong> ';
|
192 |
+
var url = 'ocCheck.php?' + (new Date()).getTime();
|
193 |
+
$.get(url, function (ocStatus) {
|
194 |
+
$.get(url + '&hello=1', function (ocHello) {
|
195 |
+
if (ocHello != 'World!') {
|
196 |
+
msg += 'It appears output is being automatically compressed, interfering '
|
197 |
+
+ ' with Minify\'s own compression. ';
|
198 |
+
if (ocStatus == '1')
|
199 |
+
msg += 'The option "zlib.output_compression" is enabled in your PHP configuration. '
|
200 |
+
+ 'Minify set this to "0", but it had no effect. This option must be disabled '
|
201 |
+
+ 'in php.ini or .htaccess.';
|
202 |
+
else
|
203 |
+
msg += 'The option "zlib.output_compression" is disabled in your PHP configuration '
|
204 |
+
+ 'so this behavior is likely due to a server option.';
|
205 |
+
$(document.body).prepend(msg + '<\/p>');
|
206 |
+
} else
|
207 |
+
if (ocStatus == '1')
|
208 |
+
$(document.body).prepend('<\p class=topNote><\strong>Note:</\strong> The option '
|
209 |
+
+ '"zlib.output_compression" is enabled in your PHP configuration, but has been '
|
210 |
+
+ 'successfully disabled via ini_set(). If you experience mangled output you '
|
211 |
+
+ 'may want to consider disabling this option in your PHP configuration.<\/p>'
|
212 |
+
);
|
213 |
+
});
|
214 |
+
});
|
215 |
+
});
|
216 |
+
</script>
|
217 |
+
<script>
|
218 |
+
// workaround required to test when /min isn't child of web root
|
219 |
+
var src = location.pathname.replace(/\/[^\/]*$/, '/_index.js').substr(1);
|
220 |
+
src = "../?f=" + src;
|
221 |
+
document.write('<\script type="text/javascript" src="' + src + '"><\/script>');
|
222 |
+
$(function () {
|
223 |
+
$('#builderScriptSrc')[0].href = src;
|
224 |
+
});
|
225 |
+
</script>
|
226 |
+
</body>
|
227 |
+
<?php
|
228 |
+
$content = ob_get_clean();
|
229 |
+
|
230 |
+
// setup Minify
|
231 |
+
if (0 === stripos(PHP_OS, 'win')) {
|
232 |
+
Minify::setDocRoot(); // we may be on IIS
|
233 |
+
}
|
234 |
+
Minify::setCache(
|
235 |
+
isset($min_cachePath) ? $min_cachePath : ''
|
236 |
+
,$min_cacheFileLocking
|
237 |
+
);
|
238 |
+
Minify::$uploaderHoursBehind = $min_uploaderHoursBehind;
|
239 |
+
|
240 |
+
Minify::serve('Page', array(
|
241 |
+
'content' => $content
|
242 |
+
,'id' => __FILE__
|
243 |
+
,'lastModifiedTime' => max(
|
244 |
+
// regenerate cache if any of these change
|
245 |
+
filemtime(__FILE__)
|
246 |
+
,filemtime(dirname(__FILE__) . '/../config.php')
|
247 |
+
,filemtime(dirname(__FILE__) . '/../lib/Minify.php')
|
248 |
+
)
|
249 |
+
,'minifyAll' => true
|
250 |
+
,'encodeOutput' => $encodeOutput
|
251 |
+
));
|
min/builder/ocCheck.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* AJAX checks for zlib.output_compression
|
4 |
+
*
|
5 |
+
* @package Minify
|
6 |
+
*/
|
7 |
+
|
8 |
+
$_oc = ini_get('zlib.output_compression');
|
9 |
+
|
10 |
+
// allow access only if builder is enabled
|
11 |
+
require dirname(__FILE__) . '/../config.php';
|
12 |
+
if (! $min_enableBuilder) {
|
13 |
+
header('Location: /');
|
14 |
+
exit();
|
15 |
+
}
|
16 |
+
|
17 |
+
if (isset($_GET['hello'])) {
|
18 |
+
// echo 'World!'
|
19 |
+
|
20 |
+
// try to prevent double encoding (may not have an effect)
|
21 |
+
ini_set('zlib.output_compression', '0');
|
22 |
+
|
23 |
+
require $min_libPath . '/HTTP/Encoder.php';
|
24 |
+
HTTP_Encoder::$encodeToIe6 = true; // just in case
|
25 |
+
$he = new HTTP_Encoder(array(
|
26 |
+
'content' => 'World!'
|
27 |
+
,'method' => 'deflate'
|
28 |
+
));
|
29 |
+
$he->encode();
|
30 |
+
$he->sendAll();
|
31 |
+
|
32 |
+
} else {
|
33 |
+
// echo status "0" or "1"
|
34 |
+
header('Content-Type: text/plain');
|
35 |
+
echo (int)$_oc;
|
36 |
+
}
|
min/builder/rewriteTest.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
1
|
min/builder/test.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
exit();
|
3 |
+
/* currently unused.
|
4 |
+
|
5 |
+
// capture PHP's default setting (may get overridden in config
|
6 |
+
$_oc = ini_get('zlib.output_compression');
|
7 |
+
|
8 |
+
// allow access only if builder is enabled
|
9 |
+
require dirname(__FILE__) . '/../config.php';
|
10 |
+
if (! $min_enableBuilder) {
|
11 |
+
exit();
|
12 |
+
}
|
13 |
+
|
14 |
+
if (isset($_GET['oc'])) {
|
15 |
+
header('Content-Type: text/plain');
|
16 |
+
echo (int)$_oc;
|
17 |
+
|
18 |
+
} elseif (isset($_GET['text']) && in_array($_GET['text'], array('js', 'css', 'fake'))) {
|
19 |
+
ini_set('zlib.output_compression', '0');
|
20 |
+
$type = ($_GET['text'] == 'js')
|
21 |
+
? 'application/x-javascript'
|
22 |
+
: "text/{$_GET['text']}";
|
23 |
+
header("Content-Type: {$type}");
|
24 |
+
echo 'Hello';
|
25 |
+
|
26 |
+
} elseif (isset($_GET['docroot'])) {
|
27 |
+
if (false === realpath($_SERVER['DOCUMENT_ROOT'])) {
|
28 |
+
echo "<p class=topWarning><strong>realpath(DOCUMENT_ROOT) failed.</strong> You may need "
|
29 |
+
. "to set \$min_documentRoot manually (hopefully realpath() is not "
|
30 |
+
. "broken in your environment).</p>";
|
31 |
+
}
|
32 |
+
if (0 !== strpos(realpath(__FILE__), realpath($_SERVER['DOCUMENT_ROOT']))) {
|
33 |
+
echo "<p class=topWarning><strong>DOCUMENT_ROOT doesn't contain this file.</strong> You may "
|
34 |
+
. " need to set \$min_documentRoot manually</p>";
|
35 |
+
}
|
36 |
+
if (isset($_SERVER['SUBDOMAIN_DOCUMENT_ROOT'])) {
|
37 |
+
echo "<p class=topNote><strong>\$_SERVER['SUBDOMAIN_DOCUMENT_ROOT'] is set.</strong> "
|
38 |
+
. "You may need to set \$min_documentRoot to this in config.php</p>";
|
39 |
+
}
|
40 |
+
|
41 |
+
}
|
42 |
+
|
43 |
+
//*/
|
min/config.doc.php
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Configuration for "min", the default application built with the Minify
|
4 |
+
* library
|
5 |
+
*
|
6 |
+
* @package Minify
|
7 |
+
*/
|
8 |
+
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Allow use of the Minify URI Builder app. Only set this to true while you need it.
|
12 |
+
*/
|
13 |
+
$min_enableBuilder = false;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* If non-empty, the Builder will be protected with HTTP Digest auth.
|
17 |
+
* The username is "admin".
|
18 |
+
*/
|
19 |
+
$min_builderPassword = 'admin';
|
20 |
+
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Set to true to log messages to FirePHP (Firefox Firebug addon).
|
24 |
+
* Set to false for no error logging (Minify may be slightly faster).
|
25 |
+
* @link http://www.firephp.org/
|
26 |
+
*
|
27 |
+
* If you want to use a custom error logger, set this to your logger
|
28 |
+
* instance. Your object should have a method log(string $message).
|
29 |
+
*/
|
30 |
+
$min_errorLogger = false;
|
31 |
+
|
32 |
+
|
33 |
+
/**
|
34 |
+
* To allow debug mode output, you must set this option to true.
|
35 |
+
*
|
36 |
+
* Once true, you can send the cookie minDebug to request debug mode output. The
|
37 |
+
* cookie value should match the URIs you'd like to debug. E.g. to debug
|
38 |
+
* /min/f=file1.js send the cookie minDebug=file1.js
|
39 |
+
* You can manually enable debugging by appending "&debug" to a URI.
|
40 |
+
* E.g. /min/?f=script1.js,script2.js&debug
|
41 |
+
*
|
42 |
+
* In 'debug' mode, Minify combines files with no minification and adds comments
|
43 |
+
* to indicate line #s of the original files.
|
44 |
+
*/
|
45 |
+
$min_allowDebugFlag = false;
|
46 |
+
|
47 |
+
|
48 |
+
/**
|
49 |
+
* For best performance, specify your temp directory here. Otherwise Minify
|
50 |
+
* will have to load extra code to guess. Some examples below:
|
51 |
+
*/
|
52 |
+
$min_cachePath = dirname(dirname(__FILE__)) . '/cache';
|
53 |
+
//$min_cachePath = 'c:\\WINDOWS\\Temp';
|
54 |
+
//$min_cachePath = '/tmp';
|
55 |
+
//$min_cachePath = preg_replace('/^\\d+;/', '', session_save_path());
|
56 |
+
/**
|
57 |
+
* To use APC/Memcache/ZendPlatform for cache storage, require the class and
|
58 |
+
* set $min_cachePath to an instance. Example below:
|
59 |
+
*/
|
60 |
+
//require dirname(__FILE__) . '/lib/Minify/Cache/APC.php';
|
61 |
+
//$min_cachePath = new Minify_Cache_APC();
|
62 |
+
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Leave an empty string to use PHP's $_SERVER['DOCUMENT_ROOT'].
|
66 |
+
*
|
67 |
+
* On some servers, this value may be misconfigured or missing. If so, set this
|
68 |
+
* to your full document root path with no trailing slash.
|
69 |
+
* E.g. '/home/accountname/public_html' or 'c:\\xampp\\htdocs'
|
70 |
+
*
|
71 |
+
* If /min/ is directly inside your document root, just uncomment the
|
72 |
+
* second line. The third line might work on some Apache servers.
|
73 |
+
*/
|
74 |
+
$min_documentRoot = '';
|
75 |
+
//$min_documentRoot = substr(__FILE__, 0, -15);
|
76 |
+
//$min_documentRoot = $_SERVER['SUBDOMAIN_DOCUMENT_ROOT'];
|
77 |
+
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Cache file locking. Set to false if filesystem is NFS. On at least one
|
81 |
+
* NFS system flock-ing attempts stalled PHP for 30 seconds!
|
82 |
+
*/
|
83 |
+
$min_cacheFileLocking = true;
|
84 |
+
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Combining multiple CSS files can place @import declarations after rules, which
|
88 |
+
* is invalid. Minify will attempt to detect when this happens and place a
|
89 |
+
* warning comment at the top of the CSS output. To resolve this you can either
|
90 |
+
* move the @imports within your CSS files, or enable this option, which will
|
91 |
+
* move all @imports to the top of the output. Note that moving @imports could
|
92 |
+
* affect CSS values (which is why this option is disabled by default).
|
93 |
+
*/
|
94 |
+
$min_serveOptions['bubbleCssImports'] = true;
|
95 |
+
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Cache-Control: max-age value sent to browser (in seconds). After this period,
|
99 |
+
* the browser will send another conditional GET. Use a longer period for lower
|
100 |
+
* traffic but you may want to shorten this before making changes if it's crucial
|
101 |
+
* those changes are seen immediately.
|
102 |
+
*
|
103 |
+
* Note: Despite this setting, if you include a number at the end of the
|
104 |
+
* querystring, maxAge will be set to one year. E.g. /min/f=hello.css&123456
|
105 |
+
*/
|
106 |
+
$min_serveOptions['maxAge'] = 7200;
|
107 |
+
|
108 |
+
|
109 |
+
/**
|
110 |
+
* To use Google's Closure Compiler API to minify Javascript (falling back to JSMin
|
111 |
+
* on failure), uncomment the following line:
|
112 |
+
*/
|
113 |
+
//$min_serveOptions['minifiers']['application/x-javascript'] = array('Minify_JS_ClosureCompiler', 'minify');
|
114 |
+
|
115 |
+
|
116 |
+
/**
|
117 |
+
* If you'd like to restrict the "f" option to files within/below
|
118 |
+
* particular directories below DOCUMENT_ROOT, set this here.
|
119 |
+
* You will still need to include the directory in the
|
120 |
+
* f or b GET parameters.
|
121 |
+
*
|
122 |
+
* // = shortcut for DOCUMENT_ROOT
|
123 |
+
*/
|
124 |
+
//$min_serveOptions['minApp']['allowDirs'] = array('//js', '//css');
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Set to true to disable the "f" GET parameter for specifying files.
|
128 |
+
* Only the "g" parameter will be considered.
|
129 |
+
*/
|
130 |
+
$min_serveOptions['minApp']['groupsOnly'] = false;
|
131 |
+
|
132 |
+
|
133 |
+
/**
|
134 |
+
* By default, Minify will not minify files with names containing .min or -min
|
135 |
+
* before the extension. E.g. myFile.min.js will not be processed by JSMin
|
136 |
+
*
|
137 |
+
* To minify all files, set this option to null. You could also specify your
|
138 |
+
* own pattern that is matched against the filename.
|
139 |
+
*/
|
140 |
+
//$min_serveOptions['minApp']['noMinPattern'] = '@[-\\.]min\\.(?:js|css)$@i';
|
141 |
+
|
142 |
+
|
143 |
+
/**
|
144 |
+
* If you minify CSS files stored in symlink-ed directories, the URI rewriting
|
145 |
+
* algorithm can fail. To prevent this, provide an array of link paths to
|
146 |
+
* target paths, where the link paths are within the document root.
|
147 |
+
*
|
148 |
+
* Because paths need to be normalized for this to work, use "//" to substitute
|
149 |
+
* the doc root in the link paths (the array keys). E.g.:
|
150 |
+
* <code>
|
151 |
+
* array('//symlink' => '/real/target/path') // unix
|
152 |
+
* array('//static' => 'D:\\staticStorage') // Windows
|
153 |
+
* </code>
|
154 |
+
*/
|
155 |
+
$min_symlinks = array();
|
156 |
+
|
157 |
+
|
158 |
+
/**
|
159 |
+
* If you upload files from Windows to a non-Windows server, Windows may report
|
160 |
+
* incorrect mtimes for the files. This may cause Minify to keep serving stale
|
161 |
+
* cache files when source file changes are made too frequently (e.g. more than
|
162 |
+
* once an hour).
|
163 |
+
*
|
164 |
+
* Immediately after modifying and uploading a file, use the touch command to
|
165 |
+
* update the mtime on the server. If the mtime jumps ahead by a number of hours,
|
166 |
+
* set this variable to that number. If the mtime moves back, this should not be
|
167 |
+
* needed.
|
168 |
+
*
|
169 |
+
* In the Windows SFTP client WinSCP, there's an option that may fix this
|
170 |
+
* issue without changing the variable below. Under login > environment,
|
171 |
+
* select the option "Adjust remote timestamp with DST".
|
172 |
+
* @link http://winscp.net/eng/docs/ui_login_environment#daylight_saving_time
|
173 |
+
*/
|
174 |
+
$min_uploaderHoursBehind = 0;
|
175 |
+
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Path to Minify's lib folder. If you happen to move it, change
|
179 |
+
* this accordingly.
|
180 |
+
*/
|
181 |
+
$min_libPath = dirname(__FILE__) . '/lib';
|
182 |
+
|
183 |
+
|
184 |
+
// try to disable output_compression (may not have an effect)
|
185 |
+
ini_set('zlib.output_compression', '0');
|
min/config.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
$min_enableBuilder = false;
|
3 |
+
$min_builderPassword = 'admin';
|
4 |
+
$min_errorLogger = false;
|
5 |
+
$min_allowDebugFlag = false;
|
6 |
+
$min_cachePath = dirname(dirname(__FILE__)) . '/cache';
|
7 |
+
$min_documentRoot = '';
|
8 |
+
$min_cacheFileLocking = true;
|
9 |
+
$min_serveOptions['bubbleCssImports'] = true;
|
10 |
+
$min_serveOptions['maxAge'] = 86400;
|
11 |
+
$min_serveOptions['minApp']['groupsOnly'] = false;
|
12 |
+
$min_symlinks = array();
|
13 |
+
$min_uploaderHoursBehind = 0;
|
14 |
+
$min_libPath = dirname(__FILE__) . '/lib';
|
15 |
+
ini_set('zlib.output_compression', '0');
|
16 |
+
// auto-generated on 2014-07-15 08:00:19
|
min/groupsConfig.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Groups configuration for default Minify implementation
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* You may wish to use the Minify URI Builder app to suggest
|
9 |
+
* changes. http://yourdomain/min/builder/
|
10 |
+
*
|
11 |
+
* See http://code.google.com/p/minify/wiki/CustomSource for other ideas
|
12 |
+
**/
|
13 |
+
|
14 |
+
return array(
|
15 |
+
// 'js' => array('//js/file1.js', '//js/file2.js'),
|
16 |
+
// 'css' => array('//css/file1.css', '//css/file2.css'),
|
17 |
+
);
|
min/index.php
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Front controller for default Minify implementation
|
4 |
+
*
|
5 |
+
* DO NOT EDIT! Configure this utility via config.php and groupsConfig.php
|
6 |
+
*
|
7 |
+
* @package Minify
|
8 |
+
*/
|
9 |
+
|
10 |
+
define('MINIFY_MIN_DIR', dirname(__FILE__));
|
11 |
+
|
12 |
+
// load config
|
13 |
+
require MINIFY_MIN_DIR . '/config.php';
|
14 |
+
|
15 |
+
if (isset($_GET['test'])) {
|
16 |
+
include MINIFY_MIN_DIR . '/config-test.php';
|
17 |
+
}
|
18 |
+
|
19 |
+
require "$min_libPath/Minify/Loader.php";
|
20 |
+
Minify_Loader::register();
|
21 |
+
|
22 |
+
Minify::$uploaderHoursBehind = $min_uploaderHoursBehind;
|
23 |
+
Minify::setCache(
|
24 |
+
isset($min_cachePath) ? $min_cachePath : ''
|
25 |
+
,$min_cacheFileLocking
|
26 |
+
);
|
27 |
+
|
28 |
+
if ($min_documentRoot) {
|
29 |
+
$_SERVER['DOCUMENT_ROOT'] = $min_documentRoot;
|
30 |
+
Minify::$isDocRootSet = true;
|
31 |
+
}
|
32 |
+
|
33 |
+
$min_serveOptions['minifierOptions']['text/css']['symlinks'] = $min_symlinks;
|
34 |
+
// auto-add targets to allowDirs
|
35 |
+
foreach ($min_symlinks as $uri => $target) {
|
36 |
+
$min_serveOptions['minApp']['allowDirs'][] = $target;
|
37 |
+
}
|
38 |
+
|
39 |
+
if ($min_allowDebugFlag) {
|
40 |
+
$min_serveOptions['debug'] = Minify_DebugDetector::shouldDebugRequest($_COOKIE, $_GET, $_SERVER['REQUEST_URI']);
|
41 |
+
}
|
42 |
+
|
43 |
+
if ($min_errorLogger) {
|
44 |
+
if (true === $min_errorLogger) {
|
45 |
+
$min_errorLogger = FirePHP::getInstance(true);
|
46 |
+
}
|
47 |
+
Minify_Logger::setLogger($min_errorLogger);
|
48 |
+
}
|
49 |
+
|
50 |
+
// check for URI versioning
|
51 |
+
if (preg_match('/&\\d/', $_SERVER['QUERY_STRING'])) {
|
52 |
+
$min_serveOptions['maxAge'] = 31536000;
|
53 |
+
}
|
54 |
+
if (isset($_GET['g'])) {
|
55 |
+
// well need groups config
|
56 |
+
$min_serveOptions['minApp']['groups'] = (require MINIFY_MIN_DIR . '/groupsConfig.php');
|
57 |
+
}
|
58 |
+
if (isset($_GET['f']) || isset($_GET['g'])) {
|
59 |
+
// serve!
|
60 |
+
|
61 |
+
if (! isset($min_serveController)) {
|
62 |
+
$min_serveController = new Minify_Controller_MinApp();
|
63 |
+
}
|
64 |
+
Minify::serve($min_serveController, $min_serveOptions);
|
65 |
+
|
66 |
+
} elseif ($min_enableBuilder) {
|
67 |
+
header('Location: builder/');
|
68 |
+
exit();
|
69 |
+
} else {
|
70 |
+
header("Location: /");
|
71 |
+
exit();
|
72 |
+
}
|
min/lib/CSSmin.php
ADDED
@@ -0,0 +1,758 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*!
|
4 |
+
* cssmin.php rev ebaf67b 12/06/2013
|
5 |
+
* Author: Tubal Martin - http://tubalmartin.me/
|
6 |
+
* Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
|
7 |
+
*
|
8 |
+
* This is a PHP port of the CSS minification tool distributed with YUICompressor,
|
9 |
+
* itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
|
10 |
+
* Permission is hereby granted to use the PHP version under the same
|
11 |
+
* conditions as the YUICompressor.
|
12 |
+
*/
|
13 |
+
|
14 |
+
/*!
|
15 |
+
* YUI Compressor
|
16 |
+
* http://developer.yahoo.com/yui/compressor/
|
17 |
+
* Author: Julien Lecomte - http://www.julienlecomte.net/
|
18 |
+
* Copyright (c) 2013 Yahoo! Inc. All rights reserved.
|
19 |
+
* The copyrights embodied in the content of this file are licensed
|
20 |
+
* by Yahoo! Inc. under the BSD (revised) open source license.
|
21 |
+
*/
|
22 |
+
|
23 |
+
class CSSmin
|
24 |
+
{
|
25 |
+
const NL = '___YUICSSMIN_PRESERVED_NL___';
|
26 |
+
const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
|
27 |
+
const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
|
28 |
+
const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
|
29 |
+
const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';
|
30 |
+
|
31 |
+
private $comments;
|
32 |
+
private $preserved_tokens;
|
33 |
+
private $memory_limit;
|
34 |
+
private $max_execution_time;
|
35 |
+
private $pcre_backtrack_limit;
|
36 |
+
private $pcre_recursion_limit;
|
37 |
+
private $raise_php_limits;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @param bool|int $raise_php_limits
|
41 |
+
* If true, PHP settings will be raised if needed
|
42 |
+
*/
|
43 |
+
public function __construct($raise_php_limits = TRUE)
|
44 |
+
{
|
45 |
+
// Set suggested PHP limits
|
46 |
+
$this->memory_limit = 128 * 1048576; // 128MB in bytes
|
47 |
+
$this->max_execution_time = 60; // 1 min
|
48 |
+
$this->pcre_backtrack_limit = 1000 * 1000;
|
49 |
+
$this->pcre_recursion_limit = 500 * 1000;
|
50 |
+
|
51 |
+
$this->raise_php_limits = (bool) $raise_php_limits;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Minify a string of CSS
|
56 |
+
* @param string $css
|
57 |
+
* @param int|bool $linebreak_pos
|
58 |
+
* @return string
|
59 |
+
*/
|
60 |
+
public function run($css = '', $linebreak_pos = FALSE)
|
61 |
+
{
|
62 |
+
if (empty($css)) {
|
63 |
+
return '';
|
64 |
+
}
|
65 |
+
|
66 |
+
if ($this->raise_php_limits) {
|
67 |
+
$this->do_raise_php_limits();
|
68 |
+
}
|
69 |
+
|
70 |
+
$this->comments = array();
|
71 |
+
$this->preserved_tokens = array();
|
72 |
+
|
73 |
+
$start_index = 0;
|
74 |
+
$length = strlen($css);
|
75 |
+
|
76 |
+
$css = $this->extract_data_urls($css);
|
77 |
+
|
78 |
+
// collect all comment blocks...
|
79 |
+
while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) {
|
80 |
+
$end_index = $this->index_of($css, '*/', $start_index + 2);
|
81 |
+
if ($end_index < 0) {
|
82 |
+
$end_index = $length;
|
83 |
+
}
|
84 |
+
$comment_found = $this->str_slice($css, $start_index + 2, $end_index);
|
85 |
+
$this->comments[] = $comment_found;
|
86 |
+
$comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
|
87 |
+
$css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index);
|
88 |
+
// Set correct start_index: Fixes issue #2528130
|
89 |
+
$start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
|
90 |
+
}
|
91 |
+
|
92 |
+
// preserve strings so their content doesn't get accidentally minified
|
93 |
+
$css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);
|
94 |
+
|
95 |
+
// Let's divide css code in chunks of 25.000 chars aprox.
|
96 |
+
// Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
|
97 |
+
// of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
|
98 |
+
// long strings and a (sub)pattern matches a number of chars greater than
|
99 |
+
// the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
|
100 |
+
// returning NULL and $css would be empty.
|
101 |
+
$charset = '';
|
102 |
+
$charset_regexp = '/(@charset)( [^;]+;)/i';
|
103 |
+
$css_chunks = array();
|
104 |
+
$css_chunk_length = 25000; // aprox size, not exact
|
105 |
+
$start_index = 0;
|
106 |
+
$i = $css_chunk_length; // save initial iterations
|
107 |
+
$l = strlen($css);
|
108 |
+
|
109 |
+
|
110 |
+
// if the number of characters is 25000 or less, do not chunk
|
111 |
+
if ($l <= $css_chunk_length) {
|
112 |
+
$css_chunks[] = $css;
|
113 |
+
} else {
|
114 |
+
// chunk css code securely
|
115 |
+
while ($i < $l) {
|
116 |
+
$i += 50; // save iterations. 500 checks for a closing curly brace }
|
117 |
+
if ($l - $start_index <= $css_chunk_length || $i >= $l) {
|
118 |
+
$css_chunks[] = $this->str_slice($css, $start_index);
|
119 |
+
break;
|
120 |
+
}
|
121 |
+
if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {
|
122 |
+
// If there are two ending curly braces }} separated or not by spaces,
|
123 |
+
// join them in the same chunk (i.e. @media blocks)
|
124 |
+
$next_chunk = substr($css, $i);
|
125 |
+
if (preg_match('/^\s*\}/', $next_chunk)) {
|
126 |
+
$i = $i + $this->index_of($next_chunk, '}') + 1;
|
127 |
+
}
|
128 |
+
|
129 |
+
$css_chunks[] = $this->str_slice($css, $start_index, $i);
|
130 |
+
$start_index = $i;
|
131 |
+
}
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
// Minify each chunk
|
136 |
+
for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
|
137 |
+
$css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos);
|
138 |
+
// Keep the first @charset at-rule found
|
139 |
+
if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
|
140 |
+
$charset = strtolower($matches[1]) . $matches[2];
|
141 |
+
}
|
142 |
+
// Delete all @charset at-rules
|
143 |
+
$css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
|
144 |
+
}
|
145 |
+
|
146 |
+
// Update the first chunk and push the charset to the top of the file.
|
147 |
+
$css_chunks[0] = $charset . $css_chunks[0];
|
148 |
+
|
149 |
+
return implode('', $css_chunks);
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Sets the memory limit for this script
|
154 |
+
* @param int|string $limit
|
155 |
+
*/
|
156 |
+
public function set_memory_limit($limit)
|
157 |
+
{
|
158 |
+
$this->memory_limit = $this->normalize_int($limit);
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Sets the maximum execution time for this script
|
163 |
+
* @param int|string $seconds
|
164 |
+
*/
|
165 |
+
public function set_max_execution_time($seconds)
|
166 |
+
{
|
167 |
+
$this->max_execution_time = (int) $seconds;
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Sets the PCRE backtrack limit for this script
|
172 |
+
* @param int $limit
|
173 |
+
*/
|
174 |
+
public function set_pcre_backtrack_limit($limit)
|
175 |
+
{
|
176 |
+
$this->pcre_backtrack_limit = (int) $limit;
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Sets the PCRE recursion limit for this script
|
181 |
+
* @param int $limit
|
182 |
+
*/
|
183 |
+
public function set_pcre_recursion_limit($limit)
|
184 |
+
{
|
185 |
+
$this->pcre_recursion_limit = (int) $limit;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Try to configure PHP to use at least the suggested minimum settings
|
190 |
+
*/
|
191 |
+
private function do_raise_php_limits()
|
192 |
+
{
|
193 |
+
$php_limits = array(
|
194 |
+
'memory_limit' => $this->memory_limit,
|
195 |
+
'max_execution_time' => $this->max_execution_time,
|
196 |
+
'pcre.backtrack_limit' => $this->pcre_backtrack_limit,
|
197 |
+
'pcre.recursion_limit' => $this->pcre_recursion_limit
|
198 |
+
);
|
199 |
+
|
200 |
+
// If current settings are higher respect them.
|
201 |
+
foreach ($php_limits as $name => $suggested) {
|
202 |
+
$current = $this->normalize_int(ini_get($name));
|
203 |
+
// memory_limit exception: allow -1 for "no memory limit".
|
204 |
+
if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
|
205 |
+
ini_set($name, $suggested);
|
206 |
+
}
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Does bulk of the minification
|
212 |
+
* @param string $css
|
213 |
+
* @param int|bool $linebreak_pos
|
214 |
+
* @return string
|
215 |
+
*/
|
216 |
+
private function minify($css, $linebreak_pos)
|
217 |
+
{
|
218 |
+
// strings are safe, now wrestle the comments
|
219 |
+
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
|
220 |
+
|
221 |
+
$token = $this->comments[$i];
|
222 |
+
$placeholder = '/' . self::COMMENT . $i . '___/';
|
223 |
+
|
224 |
+
// ! in the first position of the comment means preserve
|
225 |
+
// so push to the preserved tokens keeping the !
|
226 |
+
if (substr($token, 0, 1) === '!') {
|
227 |
+
$this->preserved_tokens[] = $token;
|
228 |
+
$token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
|
229 |
+
$css = preg_replace($placeholder, $token_tring, $css, 1);
|
230 |
+
// Preserve new lines for /*! important comments
|
231 |
+
$css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css);
|
232 |
+
$css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css);
|
233 |
+
continue;
|
234 |
+
}
|
235 |
+
|
236 |
+
// \ in the last position looks like hack for Mac/IE5
|
237 |
+
// shorten that to /*\*/ and the next one to /**/
|
238 |
+
if (substr($token, (strlen($token) - 1), 1) === '\\') {
|
239 |
+
$this->preserved_tokens[] = '\\';
|
240 |
+
$css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
|
241 |
+
$i = $i + 1; // attn: advancing the loop
|
242 |
+
$this->preserved_tokens[] = '';
|
243 |
+
$css = preg_replace('/' . self::COMMENT . $i . '___/', self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
|
244 |
+
continue;
|
245 |
+
}
|
246 |
+
|
247 |
+
// keep empty comments after child selectors (IE7 hack)
|
248 |
+
// e.g. html >/**/ body
|
249 |
+
if (strlen($token) === 0) {
|
250 |
+
$start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1));
|
251 |
+
if ($start_index > 2) {
|
252 |
+
if (substr($css, $start_index - 3, 1) === '>') {
|
253 |
+
$this->preserved_tokens[] = '';
|
254 |
+
$css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
|
255 |
+
}
|
256 |
+
}
|
257 |
+
}
|
258 |
+
|
259 |
+
// in all other cases kill the comment
|
260 |
+
$css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1);
|
261 |
+
}
|
262 |
+
|
263 |
+
|
264 |
+
// Normalize all whitespace strings to single spaces. Easier to work with that way.
|
265 |
+
$css = preg_replace('/\s+/', ' ', $css);
|
266 |
+
|
267 |
+
// Shorten & preserve calculations calc(...) since spaces are important
|
268 |
+
$css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
|
269 |
+
|
270 |
+
// Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
|
271 |
+
// +1.2em to 1.2em, +.8px to .8px, +2% to 2%
|
272 |
+
$css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);
|
273 |
+
|
274 |
+
// Remove leading zeros from integer and float numbers preceded by : or a white-space
|
275 |
+
// 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
|
276 |
+
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);
|
277 |
+
|
278 |
+
// Remove trailing zeros from float numbers preceded by : or a white-space
|
279 |
+
// -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
|
280 |
+
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
|
281 |
+
|
282 |
+
// Remove trailing .0 -> -9.0 to -9
|
283 |
+
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);
|
284 |
+
|
285 |
+
// Replace 0 length numbers with 0
|
286 |
+
$css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);
|
287 |
+
|
288 |
+
// Remove the spaces before the things that should not have spaces before them.
|
289 |
+
// But, be careful not to turn "p :link {...}" into "p:link{...}"
|
290 |
+
// Swap out any pseudo-class colons with the token, and then swap back.
|
291 |
+
$css = preg_replace_callback('/(?:^|\})(?:(?:[^\{\:])+\:)+(?:[^\{]*\{)/', array($this, 'replace_colon'), $css);
|
292 |
+
|
293 |
+
// Remove spaces before the things that should not have spaces before them.
|
294 |
+
$css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
|
295 |
+
|
296 |
+
// Restore spaces for !important
|
297 |
+
$css = preg_replace('/\!important/i', ' !important', $css);
|
298 |
+
|
299 |
+
// bring back the colon
|
300 |
+
$css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);
|
301 |
+
|
302 |
+
// retain space for special IE6 cases
|
303 |
+
$css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);
|
304 |
+
|
305 |
+
// no space after the end of a preserved comment
|
306 |
+
$css = preg_replace('/\*\/ /', '*/', $css);
|
307 |
+
|
308 |
+
// lowercase some popular @directives
|
309 |
+
$css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);
|
310 |
+
|
311 |
+
// lowercase some more common pseudo-elements
|
312 |
+
$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);
|
313 |
+
|
314 |
+
// lowercase some more common functions
|
315 |
+
$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);
|
316 |
+
|
317 |
+
// lower case some common function that can be values
|
318 |
+
// NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
|
319 |
+
$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);
|
320 |
+
|
321 |
+
// Put the space back in some cases, to support stuff like
|
322 |
+
// @media screen and (-webkit-min-device-pixel-ratio:0){
|
323 |
+
$css = preg_replace('/\band\(/i', 'and (', $css);
|
324 |
+
|
325 |
+
// Remove the spaces after the things that should not have spaces after them.
|
326 |
+
$css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
|
327 |
+
|
328 |
+
// remove unnecessary semicolons
|
329 |
+
$css = preg_replace('/;+\}/', '}', $css);
|
330 |
+
|
331 |
+
// Fix for issue: #2528146
|
332 |
+
// Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
|
333 |
+
// to avoid issues on Symbian S60 3.x browsers.
|
334 |
+
$css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
|
335 |
+
|
336 |
+
// Replace 0 length units 0(px,em,%) with 0.
|
337 |
+
$css = preg_replace('/(^|[^0-9])(?:0?\.)?0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%|deg|g?rad|m?s|k?hz)/iS', '${1}0', $css);
|
338 |
+
|
339 |
+
// Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
|
340 |
+
$css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
|
341 |
+
|
342 |
+
// Fix for issue: #2528142
|
343 |
+
// Replace text-shadow:0; with text-shadow:0 0 0;
|
344 |
+
$css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);
|
345 |
+
|
346 |
+
// Replace background-position:0; with background-position:0 0;
|
347 |
+
// same for transform-origin
|
348 |
+
// Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
|
349 |
+
$css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);
|
350 |
+
|
351 |
+
// Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
|
352 |
+
// Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
|
353 |
+
// This makes it more likely that it'll get further compressed in the next step.
|
354 |
+
$css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
|
355 |
+
$css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);
|
356 |
+
|
357 |
+
// Shorten colors from #AABBCC to #ABC or short color name.
|
358 |
+
$css = $this->compress_hex_colors($css);
|
359 |
+
|
360 |
+
// border: none to border:0, outline: none to outline:0
|
361 |
+
$css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);
|
362 |
+
|
363 |
+
// shorter opacity IE filter
|
364 |
+
$css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);
|
365 |
+
|
366 |
+
// Find a fraction that is used for Opera's -o-device-pixel-ratio query
|
367 |
+
// Add token to add the "\" back in later
|
368 |
+
$css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
|
369 |
+
|
370 |
+
// Remove empty rules.
|
371 |
+
$css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);
|
372 |
+
|
373 |
+
// Add "/" back to fix Opera -o-device-pixel-ratio query
|
374 |
+
$css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
|
375 |
+
|
376 |
+
// Some source control tools don't like it when files containing lines longer
|
377 |
+
// than, say 8000 characters, are checked in. The linebreak option is used in
|
378 |
+
// that case to split long lines after a specific column.
|
379 |
+
if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
|
380 |
+
$linebreak_pos = (int) $linebreak_pos;
|
381 |
+
$start_index = $i = 0;
|
382 |
+
while ($i < strlen($css)) {
|
383 |
+
$i++;
|
384 |
+
if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
|
385 |
+
$css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
|
386 |
+
$start_index = $i;
|
387 |
+
}
|
388 |
+
}
|
389 |
+
}
|
390 |
+
|
391 |
+
// Replace multiple semi-colons in a row by a single one
|
392 |
+
// See SF bug #1980989
|
393 |
+
$css = preg_replace('/;;+/', ';', $css);
|
394 |
+
|
395 |
+
// Restore new lines for /*! important comments
|
396 |
+
$css = preg_replace('/'. self::NL .'/', "\n", $css);
|
397 |
+
|
398 |
+
// Lowercase all uppercase properties
|
399 |
+
$css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
|
400 |
+
|
401 |
+
// restore preserved comments and strings
|
402 |
+
for ($i = 0, $max = count($this->preserved_tokens); $i < $max; $i++) {
|
403 |
+
$css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
|
404 |
+
}
|
405 |
+
|
406 |
+
// Trim the final string (for any leading or trailing white spaces)
|
407 |
+
return trim($css);
|
408 |
+
}
|
409 |
+
|
410 |
+
/**
|
411 |
+
* Utility method to replace all data urls with tokens before we start
|
412 |
+
* compressing, to avoid performance issues running some of the subsequent
|
413 |
+
* regexes against large strings chunks.
|
414 |
+
*
|
415 |
+
* @param string $css
|
416 |
+
* @return string
|
417 |
+
*/
|
418 |
+
private function extract_data_urls($css)
|
419 |
+
{
|
420 |
+
// Leave data urls alone to increase parse performance.
|
421 |
+
$max_index = strlen($css) - 1;
|
422 |
+
$append_index = $index = $last_index = $offset = 0;
|
423 |
+
$sb = array();
|
424 |
+
$pattern = '/url\(\s*(["\']?)data\:/i';
|
425 |
+
|
426 |
+
// Since we need to account for non-base64 data urls, we need to handle
|
427 |
+
// ' and ) being part of the data string. Hence switching to indexOf,
|
428 |
+
// to determine whether or not we have matching string terminators and
|
429 |
+
// handling sb appends directly, instead of using matcher.append* methods.
|
430 |
+
|
431 |
+
while (preg_match($pattern, $css, $m, 0, $offset)) {
|
432 |
+
$index = $this->index_of($css, $m[0], $offset);
|
433 |
+
$last_index = $index + strlen($m[0]);
|
434 |
+
$start_index = $index + 4; // "url(".length()
|
435 |
+
$end_index = $last_index - 1;
|
436 |
+
$terminator = $m[1]; // ', " or empty (not quoted)
|
437 |
+
$found_terminator = FALSE;
|
438 |
+
|
439 |
+
if (strlen($terminator) === 0) {
|
440 |
+
$terminator = ')';
|
441 |
+
}
|
442 |
+
|
443 |
+
while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
|
444 |
+
$end_index = $this->index_of($css, $terminator, $end_index + 1);
|
445 |
+
|
446 |
+
// endIndex == 0 doesn't really apply here
|
447 |
+
if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
|
448 |
+
$found_terminator = TRUE;
|
449 |
+
if (')' != $terminator) {
|
450 |
+
$end_index = $this->index_of($css, ')', $end_index);
|
451 |
+
}
|
452 |
+
}
|
453 |
+
}
|
454 |
+
|
455 |
+
// Enough searching, start moving stuff over to the buffer
|
456 |
+
$sb[] = $this->str_slice($css, $append_index, $index);
|
457 |
+
|
458 |
+
if ($found_terminator) {
|
459 |
+
$token = $this->str_slice($css, $start_index, $end_index);
|
460 |
+
$token = preg_replace('/\s+/', '', $token);
|
461 |
+
$this->preserved_tokens[] = $token;
|
462 |
+
|
463 |
+
$preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
|
464 |
+
$sb[] = $preserver;
|
465 |
+
|
466 |
+
$append_index = $end_index + 1;
|
467 |
+
} else {
|
468 |
+
// No end terminator found, re-add the whole match. Should we throw/warn here?
|
469 |
+
$sb[] = $this->str_slice($css, $index, $last_index);
|
470 |
+
$append_index = $last_index;
|
471 |
+
}
|
472 |
+
|
473 |
+
$offset = $last_index;
|
474 |
+
}
|
475 |
+
|
476 |
+
$sb[] = $this->str_slice($css, $append_index);
|
477 |
+
|
478 |
+
return implode('', $sb);
|
479 |
+
}
|
480 |
+
|
481 |
+
/**
|
482 |
+
* Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
|
483 |
+
*
|
484 |
+
* DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
|
485 |
+
* e.g. #AddressForm { ... }
|
486 |
+
*
|
487 |
+
* DOES NOT compress IE filters, which have hex color values (which would break things).
|
488 |
+
* e.g. filter: chroma(color="#FFFFFF");
|
489 |
+
*
|
490 |
+
* DOES NOT compress invalid hex values.
|
491 |
+
* e.g. background-color: #aabbccdd
|
492 |
+
*
|
493 |
+
* @param string $css
|
494 |
+
* @return string
|
495 |
+
*/
|
496 |
+
private function compress_hex_colors($css)
|
497 |
+
{
|
498 |
+
// Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
|
499 |
+
$pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
|
500 |
+
$_index = $index = $last_index = $offset = 0;
|
501 |
+
$sb = array();
|
502 |
+
// See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
|
503 |
+
$short_safe = array(
|
504 |
+
'#808080' => 'gray',
|
505 |
+
'#008000' => 'green',
|
506 |
+
'#800000' => 'maroon',
|
507 |
+
'#000080' => 'navy',
|
508 |
+
'#808000' => 'olive',
|
509 |
+
'#ffa500' => 'orange',
|
510 |
+
'#800080' => 'purple',
|
511 |
+
'#c0c0c0' => 'silver',
|
512 |
+
'#008080' => 'teal',
|
513 |
+
'#f00' => 'red'
|
514 |
+
);
|
515 |
+
|
516 |
+
while (preg_match($pattern, $css, $m, 0, $offset)) {
|
517 |
+
$index = $this->index_of($css, $m[0], $offset);
|
518 |
+
$last_index = $index + strlen($m[0]);
|
519 |
+
$is_filter = $m[1] !== null && $m[1] !== '';
|
520 |
+
|
521 |
+
$sb[] = $this->str_slice($css, $_index, $index);
|
522 |
+
|
523 |
+
if ($is_filter) {
|
524 |
+
// Restore, maintain case, otherwise filter will break
|
525 |
+
$sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
|
526 |
+
} else {
|
527 |
+
if (strtolower($m[2]) == strtolower($m[3]) &&
|
528 |
+
strtolower($m[4]) == strtolower($m[5]) &&
|
529 |
+
strtolower($m[6]) == strtolower($m[7])) {
|
530 |
+
// Compress.
|
531 |
+
$hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
|
532 |
+
} else {
|
533 |
+
// Non compressible color, restore but lower case.
|
534 |
+
$hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
|
535 |
+
}
|
536 |
+
// replace Hex colors to short safe color names
|
537 |
+
$sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
|
538 |
+
}
|
539 |
+
|
540 |
+
$_index = $offset = $last_index - strlen($m[8]);
|
541 |
+
}
|
542 |
+
|
543 |
+
$sb[] = $this->str_slice($css, $_index);
|
544 |
+
|
545 |
+
return implode('', $sb);
|
546 |
+
}
|
547 |
+
|
548 |
+
/* CALLBACKS
|
549 |
+
* ---------------------------------------------------------------------------------------------
|
550 |
+
*/
|
551 |
+
|
552 |
+
private function replace_string($matches)
|
553 |
+
{
|
554 |
+
$match = $matches[0];
|
555 |
+
$quote = substr($match, 0, 1);
|
556 |
+
// Must use addcslashes in PHP to avoid parsing of backslashes
|
557 |
+
$match = addcslashes($this->str_slice($match, 1, -1), '\\');
|
558 |
+
|
559 |
+
// maybe the string contains a comment-like substring?
|
560 |
+
// one, maybe more? put'em back then
|
561 |
+
if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
|
562 |
+
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
|
563 |
+
$match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
|
564 |
+
}
|
565 |
+
}
|
566 |
+
|
567 |
+
// minify alpha opacity in filter strings
|
568 |
+
$match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);
|
569 |
+
|
570 |
+
$this->preserved_tokens[] = $match;
|
571 |
+
return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
|
572 |
+
}
|
573 |
+
|
574 |
+
private function replace_colon($matches)
|
575 |
+
{
|
576 |
+
return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
|
577 |
+
}
|
578 |
+
|
579 |
+
private function replace_calc($matches)
|
580 |
+
{
|
581 |
+
$this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]));
|
582 |
+
return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
|
583 |
+
}
|
584 |
+
|
585 |
+
private function rgb_to_hex($matches)
|
586 |
+
{
|
587 |
+
// Support for percentage values rgb(100%, 0%, 45%);
|
588 |
+
if ($this->index_of($matches[1], '%') >= 0){
|
589 |
+
$rgbcolors = explode(',', str_replace('%', '', $matches[1]));
|
590 |
+
for ($i = 0; $i < count($rgbcolors); $i++) {
|
591 |
+
$rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55);
|
592 |
+
}
|
593 |
+
} else {
|
594 |
+
$rgbcolors = explode(',', $matches[1]);
|
595 |
+
}
|
596 |
+
|
597 |
+
// Values outside the sRGB color space should be clipped (0-255)
|
598 |
+
for ($i = 0; $i < count($rgbcolors); $i++) {
|
599 |
+
$rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255);
|
600 |
+
$rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]);
|
601 |
+
}
|
602 |
+
|
603 |
+
// Fix for issue #2528093
|
604 |
+
if (!preg_match('/[\s\,\);\}]/', $matches[2])){
|
605 |
+
$matches[2] = ' ' . $matches[2];
|
606 |
+
}
|
607 |
+
|
608 |
+
return '#' . implode('', $rgbcolors) . $matches[2];
|
609 |
+
}
|
610 |
+
|
611 |
+
private function hsl_to_hex($matches)
|
612 |
+
{
|
613 |
+
$values = explode(',', str_replace('%', '', $matches[1]));
|
614 |
+
$h = floatval($values[0]);
|
615 |
+
$s = floatval($values[1]);
|
616 |
+
$l = floatval($values[2]);
|
617 |
+
|
618 |
+
// Wrap and clamp, then fraction!
|
619 |
+
$h = ((($h % 360) + 360) % 360) / 360;
|
620 |
+
$s = $this->clamp_number($s, 0, 100) / 100;
|
621 |
+
$l = $this->clamp_number($l, 0, 100) / 100;
|
622 |
+
|
623 |
+
if ($s == 0) {
|
624 |
+
$r = $g = $b = $this->round_number(255 * $l);
|
625 |
+
} else {
|
626 |
+
$v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
|
627 |
+
$v1 = (2 * $l) - $v2;
|
628 |
+
$r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3)));
|
629 |
+
$g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h));
|
630 |
+
$b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3)));
|
631 |
+
}
|
632 |
+
|
633 |
+
return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2]));
|
634 |
+
}
|
635 |
+
|
636 |
+
private function lowercase_pseudo_first($matches)
|
637 |
+
{
|
638 |
+
return ':first-'. strtolower($matches[1]) .' '. $matches[2];
|
639 |
+
}
|
640 |
+
|
641 |
+
private function lowercase_directives($matches)
|
642 |
+
{
|
643 |
+
return '@'. strtolower($matches[1]);
|
644 |
+
}
|
645 |
+
|
646 |
+
private function lowercase_pseudo_elements($matches)
|
647 |
+
{
|
648 |
+
return ':'. strtolower($matches[1]);
|
649 |
+
}
|
650 |
+
|
651 |
+
private function lowercase_common_functions($matches)
|
652 |
+
{
|
653 |
+
return ':'. strtolower($matches[1]) .'(';
|
654 |
+
}
|
655 |
+
|
656 |
+
private function lowercase_common_functions_values($matches)
|
657 |
+
{
|
658 |
+
return $matches[1] . strtolower($matches[2]);
|
659 |
+
}
|
660 |
+
|
661 |
+
private function lowercase_properties($matches)
|
662 |
+
{
|
663 |
+
return $matches[1].strtolower($matches[2]).$matches[3];
|
664 |
+
}
|
665 |
+
|
666 |
+
/* HELPERS
|
667 |
+
* ---------------------------------------------------------------------------------------------
|
668 |
+
*/
|
669 |
+
|
670 |
+
private function hue_to_rgb($v1, $v2, $vh)
|
671 |
+
{
|
672 |
+
$vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
|
673 |
+
if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh;
|
674 |
+
if ($vh * 2 < 1) return $v2;
|
675 |
+
if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6;
|
676 |
+
return $v1;
|
677 |
+
}
|
678 |
+
|
679 |
+
private function round_number($n)
|
680 |
+
{
|
681 |
+
return intval(floor(floatval($n) + 0.5), 10);
|
682 |
+
}
|
683 |
+
|
684 |
+
private function clamp_number($n, $min, $max)
|
685 |
+
{
|
686 |
+
return min(max($n, $min), $max);
|
687 |
+
}
|
688 |
+
|
689 |
+
/**
|
690 |
+
* PHP port of Javascript's "indexOf" function for strings only
|
691 |
+
* Author: Tubal Martin http://blog.margenn.com
|
692 |
+
*
|
693 |
+
* @param string $haystack
|
694 |
+
* @param string $needle
|
695 |
+
* @param int $offset index (optional)
|
696 |
+
* @return int
|
697 |
+
*/
|
698 |
+
private function index_of($haystack, $needle, $offset = 0)
|
699 |
+
{
|
700 |
+
$index = strpos($haystack, $needle, $offset);
|
701 |
+
|
702 |
+
return ($index !== FALSE) ? $index : -1;
|
703 |
+
}
|
704 |
+
|
705 |
+
/**
|
706 |
+
* PHP port of Javascript's "slice" function for strings only
|
707 |
+
* Author: Tubal Martin http://blog.margenn.com
|
708 |
+
* Tests: http://margenn.com/tubal/str_slice/
|
709 |
+
*
|
710 |
+
* @param string $str
|
711 |
+
* @param int $start index
|
712 |
+
* @param int|bool $end index (optional)
|
713 |
+
* @return string
|
714 |
+
*/
|
715 |
+
private function str_slice($str, $start = 0, $end = FALSE)
|
716 |
+
{
|
717 |
+
if ($end !== FALSE && ($start < 0 || $end <= 0)) {
|
718 |
+
$max = strlen($str);
|
719 |
+
|
720 |
+
if ($start < 0) {
|
721 |
+
if (($start = $max + $start) < 0) {
|
722 |
+
return '';
|
723 |
+
}
|
724 |
+
}
|
725 |
+
|
726 |
+
if ($end < 0) {
|
727 |
+
if (($end = $max + $end) < 0) {
|
728 |
+
return '';
|
729 |
+
}
|
730 |
+
}
|
731 |
+
|
732 |
+
if ($end <= $start) {
|
733 |
+
return '';
|
734 |
+
}
|
735 |
+
}
|
736 |
+
|
737 |
+
$slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start);
|
738 |
+
return ($slice === FALSE) ? '' : $slice;
|
739 |
+
}
|
740 |
+
|
741 |
+
/**
|
742 |
+
* Convert strings like "64M" or "30" to int values
|
743 |
+
* @param mixed $size
|
744 |
+
* @return int
|
745 |
+
*/
|
746 |
+
private function normalize_int($size)
|
747 |
+
{
|
748 |
+
if (is_string($size)) {
|
749 |
+
switch (substr($size, -1)) {
|
750 |
+
case 'M': case 'm': return $size * 1048576;
|
751 |
+
case 'K': case 'k': return $size * 1024;
|
752 |
+
case 'G': case 'g': return $size * 1073741824;
|
753 |
+
}
|
754 |
+
}
|
755 |
+
|
756 |
+
return (int) $size;
|
757 |
+
}
|
758 |
+
}
|
min/lib/DooDigestAuth.php
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* DooDigestAuth class file.
|
4 |
+
*
|
5 |
+
* @author Leng Sheng Hong <darkredz@gmail.com>
|
6 |
+
* @link http://www.doophp.com/
|
7 |
+
* @copyright Copyright © 2009 Leng Sheng Hong
|
8 |
+
* @license http://www.doophp.com/license
|
9 |
+
*/
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Handles HTTP digest authentication
|
13 |
+
*
|
14 |
+
* <p>HTTP digest authentication can be used with the URI router.
|
15 |
+
* HTTP digest is much more recommended over the use of HTTP Basic auth which doesn't provide any encryption.
|
16 |
+
* If you are running PHP on Apache in CGI/FastCGI mode, you would need to
|
17 |
+
* add the following line to your .htaccess for digest auth to work correctly.</p>
|
18 |
+
* <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code>
|
19 |
+
*
|
20 |
+
* <p>This class is tested under Apache 2.2 and Cherokee web server. It should work in both mod_php and cgi mode.</p>
|
21 |
+
*
|
22 |
+
* @author Leng Sheng Hong <darkredz@gmail.com>
|
23 |
+
* @version $Id: DooDigestAuth.php 1000 2009-07-7 18:27:22
|
24 |
+
* @package doo.auth
|
25 |
+
* @since 1.0
|
26 |
+
*/
|
27 |
+
class DooDigestAuth{
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Authenticate against a list of username and passwords.
|
31 |
+
*
|
32 |
+
* <p>HTTP Digest Authentication doesn't work with PHP in CGI mode,
|
33 |
+
* you have to add this into your .htaccess <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code></p>
|
34 |
+
*
|
35 |
+
* @param string $realm Name of the authentication session
|
36 |
+
* @param array $users An assoc array of username and password: array('uname1'=>'pwd1', 'uname2'=>'pwd2')
|
37 |
+
* @param string $fail_msg Message to be displayed if the User cancel the login
|
38 |
+
* @param string $fail_url URL to be redirect if the User cancel the login
|
39 |
+
* @return string The username if login success.
|
40 |
+
*/
|
41 |
+
public static function http_auth($realm, $users, $fail_msg=NULL, $fail_url=NULL){
|
42 |
+
$realm = "Restricted area - $realm";
|
43 |
+
|
44 |
+
//user => password
|
45 |
+
//$users = array('admin' => '1234', 'guest' => 'guest');
|
46 |
+
if(!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && strpos($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 'Digest')===0){
|
47 |
+
$_SERVER['PHP_AUTH_DIGEST'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
|
48 |
+
}
|
49 |
+
|
50 |
+
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
|
51 |
+
header('WWW-Authenticate: Digest realm="'.$realm.
|
52 |
+
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
|
53 |
+
header('HTTP/1.1 401 Unauthorized');
|
54 |
+
if($fail_msg!=NULL)
|
55 |
+
die($fail_msg);
|
56 |
+
if($fail_url!=NULL)
|
57 |
+
die("<script>window.location.href = '$fail_url'</script>");
|
58 |
+
exit;
|
59 |
+
}
|
60 |
+
|
61 |
+
// analyze the PHP_AUTH_DIGEST variable
|
62 |
+
if (!($data = self::http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($users[$data['username']])){
|
63 |
+
header('WWW-Authenticate: Digest realm="'.$realm.
|
64 |
+
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
|
65 |
+
header('HTTP/1.1 401 Unauthorized');
|
66 |
+
if($fail_msg!=NULL)
|
67 |
+
die($fail_msg);
|
68 |
+
if($fail_url!=NULL)
|
69 |
+
die("<script>window.location.href = '$fail_url'</script>");
|
70 |
+
exit;
|
71 |
+
}
|
72 |
+
|
73 |
+
// generate the valid response
|
74 |
+
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
|
75 |
+
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
|
76 |
+
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
|
77 |
+
|
78 |
+
if ($data['response'] != $valid_response){
|
79 |
+
header('HTTP/1.1 401 Unauthorized');
|
80 |
+
header('WWW-Authenticate: Digest realm="'.$realm.
|
81 |
+
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
|
82 |
+
if($fail_msg!=NULL)
|
83 |
+
die($fail_msg);
|
84 |
+
if($fail_url!=NULL)
|
85 |
+
die("<script>window.location.href = '$fail_url'</script>");
|
86 |
+
exit;
|
87 |
+
}
|
88 |
+
|
89 |
+
// ok, valid username & password
|
90 |
+
return $data['username'];
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Method to parse the http auth header, works with IE.
|
95 |
+
*
|
96 |
+
* Internet Explorer returns a qop="xxxxxxxxxxx" in the header instead of qop=xxxxxxxxxxx as most browsers do.
|
97 |
+
*
|
98 |
+
* @param string $txt header string to parse
|
99 |
+
* @return array An assoc array of the digest auth session
|
100 |
+
*/
|
101 |
+
private static function http_digest_parse($txt)
|
102 |
+
{
|
103 |
+
$res = preg_match("/username=\"([^\"]+)\"/i", $txt, $match);
|
104 |
+
$data['username'] = (isset($match[1]))?$match[1]:null;
|
105 |
+
$res = preg_match('/nonce=\"([^\"]+)\"/i', $txt, $match);
|
106 |
+
$data['nonce'] = $match[1];
|
107 |
+
$res = preg_match('/nc=([0-9]+)/i', $txt, $match);
|
108 |
+
$data['nc'] = $match[1];
|
109 |
+
$res = preg_match('/cnonce=\"([^\"]+)\"/i', $txt, $match);
|
110 |
+
$data['cnonce'] = $match[1];
|
111 |
+
$res = preg_match('/qop=([^,]+)/i', $txt, $match);
|
112 |
+
$data['qop'] = str_replace('"','',$match[1]);
|
113 |
+
$res = preg_match('/uri=\"([^\"]+)\"/i', $txt, $match);
|
114 |
+
$data['uri'] = $match[1];
|
115 |
+
$res = preg_match('/response=\"([^\"]+)\"/i', $txt, $match);
|
116 |
+
$data['response'] = $match[1];
|
117 |
+
return $data;
|
118 |
+
}
|
119 |
+
|
120 |
+
|
121 |
+
}
|
min/lib/FirePHP.php
ADDED
@@ -0,0 +1,1370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* *** BEGIN LICENSE BLOCK *****
|
4 |
+
*
|
5 |
+
* This file is part of FirePHP (http://www.firephp.org/).
|
6 |
+
*
|
7 |
+
* Software License Agreement (New BSD License)
|
8 |
+
*
|
9 |
+
* Copyright (c) 2006-2008, Christoph Dorn
|
10 |
+
* All rights reserved.
|
11 |
+
*
|
12 |
+
* Redistribution and use in source and binary forms, with or without modification,
|
13 |
+
* are permitted provided that the following conditions are met:
|
14 |
+
*
|
15 |
+
* * Redistributions of source code must retain the above copyright notice,
|
16 |
+
* this list of conditions and the following disclaimer.
|
17 |
+
*
|
18 |
+
* * Redistributions in binary form must reproduce the above copyright notice,
|
19 |
+
* this list of conditions and the following disclaimer in the documentation
|
20 |
+
* and/or other materials provided with the distribution.
|
21 |
+
*
|
22 |
+
* * Neither the name of Christoph Dorn nor the names of its
|
23 |
+
* contributors may be used to endorse or promote products derived from this
|
24 |
+
* software without specific prior written permission.
|
25 |
+
*
|
26 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
27 |
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
28 |
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
29 |
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
30 |
+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
31 |
+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
32 |
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
33 |
+
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
34 |
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
35 |
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
36 |
+
*
|
37 |
+
* ***** END LICENSE BLOCK *****
|
38 |
+
*
|
39 |
+
* @copyright Copyright (C) 2007-2008 Christoph Dorn
|
40 |
+
* @author Christoph Dorn <christoph@christophdorn.com>
|
41 |
+
* @license http://www.opensource.org/licenses/bsd-license.php
|
42 |
+
* @package FirePHP
|
43 |
+
*/
|
44 |
+
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Sends the given data to the FirePHP Firefox Extension.
|
48 |
+
* The data can be displayed in the Firebug Console or in the
|
49 |
+
* "Server" request tab.
|
50 |
+
*
|
51 |
+
* For more information see: http://www.firephp.org/
|
52 |
+
*
|
53 |
+
* @copyright Copyright (C) 2007-2008 Christoph Dorn
|
54 |
+
* @author Christoph Dorn <christoph@christophdorn.com>
|
55 |
+
* @license http://www.opensource.org/licenses/bsd-license.php
|
56 |
+
* @package FirePHP
|
57 |
+
*/
|
58 |
+
class FirePHP {
|
59 |
+
|
60 |
+
/**
|
61 |
+
* FirePHP version
|
62 |
+
*
|
63 |
+
* @var string
|
64 |
+
*/
|
65 |
+
const VERSION = '0.2.0';
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Firebug LOG level
|
69 |
+
*
|
70 |
+
* Logs a message to firebug console.
|
71 |
+
*
|
72 |
+
* @var string
|
73 |
+
*/
|
74 |
+
const LOG = 'LOG';
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Firebug INFO level
|
78 |
+
*
|
79 |
+
* Logs a message to firebug console and displays an info icon before the message.
|
80 |
+
*
|
81 |
+
* @var string
|
82 |
+
*/
|
83 |
+
const INFO = 'INFO';
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Firebug WARN level
|
87 |
+
*
|
88 |
+
* Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise.
|
89 |
+
*
|
90 |
+
* @var string
|
91 |
+
*/
|
92 |
+
const WARN = 'WARN';
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Firebug ERROR level
|
96 |
+
*
|
97 |
+
* Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
|
98 |
+
*
|
99 |
+
* @var string
|
100 |
+
*/
|
101 |
+
const ERROR = 'ERROR';
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Dumps a variable to firebug's server panel
|
105 |
+
*
|
106 |
+
* @var string
|
107 |
+
*/
|
108 |
+
const DUMP = 'DUMP';
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Displays a stack trace in firebug console
|
112 |
+
*
|
113 |
+
* @var string
|
114 |
+
*/
|
115 |
+
const TRACE = 'TRACE';
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Displays an exception in firebug console
|
119 |
+
*
|
120 |
+
* Increments the firebug error count.
|
121 |
+
*
|
122 |
+
* @var string
|
123 |
+
*/
|
124 |
+
const EXCEPTION = 'EXCEPTION';
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Displays an table in firebug console
|
128 |
+
*
|
129 |
+
* @var string
|
130 |
+
*/
|
131 |
+
const TABLE = 'TABLE';
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Starts a group in firebug console
|
135 |
+
*
|
136 |
+
* @var string
|
137 |
+
*/
|
138 |
+
const GROUP_START = 'GROUP_START';
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Ends a group in firebug console
|
142 |
+
*
|
143 |
+
* @var string
|
144 |
+
*/
|
145 |
+
const GROUP_END = 'GROUP_END';
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Singleton instance of FirePHP
|
149 |
+
*
|
150 |
+
* @var FirePHP
|
151 |
+
*/
|
152 |
+
protected static $instance = null;
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Wildfire protocol message index
|
156 |
+
*
|
157 |
+
* @var int
|
158 |
+
*/
|
159 |
+
protected $messageIndex = 1;
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Options for the library
|
163 |
+
*
|
164 |
+
* @var array
|
165 |
+
*/
|
166 |
+
protected $options = array();
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Filters used to exclude object members when encoding
|
170 |
+
*
|
171 |
+
* @var array
|
172 |
+
*/
|
173 |
+
protected $objectFilters = array();
|
174 |
+
|
175 |
+
/**
|
176 |
+
* A stack of objects used to detect recursion during object encoding
|
177 |
+
*
|
178 |
+
* @var object
|
179 |
+
*/
|
180 |
+
protected $objectStack = array();
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Flag to enable/disable logging
|
184 |
+
*
|
185 |
+
* @var boolean
|
186 |
+
*/
|
187 |
+
protected $enabled = true;
|
188 |
+
|
189 |
+
/**
|
190 |
+
* The object constructor
|
191 |
+
*/
|
192 |
+
function __construct() {
|
193 |
+
$this->options['maxObjectDepth'] = 10;
|
194 |
+
$this->options['maxArrayDepth'] = 20;
|
195 |
+
$this->options['useNativeJsonEncode'] = true;
|
196 |
+
$this->options['includeLineNumbers'] = true;
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* When the object gets serialized only include specific object members.
|
201 |
+
*
|
202 |
+
* @return array
|
203 |
+
*/
|
204 |
+
public function __sleep() {
|
205 |
+
return array('options','objectFilters','enabled');
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* Gets singleton instance of FirePHP
|
210 |
+
*
|
211 |
+
* @param boolean $AutoCreate
|
212 |
+
* @return FirePHP
|
213 |
+
*/
|
214 |
+
public static function getInstance($AutoCreate=false) {
|
215 |
+
if($AutoCreate===true && !self::$instance) {
|
216 |
+
self::init();
|
217 |
+
}
|
218 |
+
return self::$instance;
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Creates FirePHP object and stores it for singleton access
|
223 |
+
*
|
224 |
+
* @return FirePHP
|
225 |
+
*/
|
226 |
+
public static function init() {
|
227 |
+
return self::$instance = new self();
|
228 |
+
}
|
229 |
+
|
230 |
+
/**
|
231 |
+
* Enable and disable logging to Firebug
|
232 |
+
*
|
233 |
+
* @param boolean $Enabled TRUE to enable, FALSE to disable
|
234 |
+
* @return void
|
235 |
+
*/
|
236 |
+
public function setEnabled($Enabled) {
|
237 |
+
$this->enabled = $Enabled;
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Check if logging is enabled
|
242 |
+
*
|
243 |
+
* @return boolean TRUE if enabled
|
244 |
+
*/
|
245 |
+
public function getEnabled() {
|
246 |
+
return $this->enabled;
|
247 |
+
}
|
248 |
+
|
249 |
+
/**
|
250 |
+
* Specify a filter to be used when encoding an object
|
251 |
+
*
|
252 |
+
* Filters are used to exclude object members.
|
253 |
+
*
|
254 |
+
* @param string $Class The class name of the object
|
255 |
+
* @param array $Filter An array or members to exclude
|
256 |
+
* @return void
|
257 |
+
*/
|
258 |
+
public function setObjectFilter($Class, $Filter) {
|
259 |
+
$this->objectFilters[$Class] = $Filter;
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* Set some options for the library
|
264 |
+
*
|
265 |
+
* Options:
|
266 |
+
* - maxObjectDepth: The maximum depth to traverse objects (default: 10)
|
267 |
+
* - maxArrayDepth: The maximum depth to traverse arrays (default: 20)
|
268 |
+
* - useNativeJsonEncode: If true will use json_encode() (default: true)
|
269 |
+
* - includeLineNumbers: If true will include line numbers and filenames (default: true)
|
270 |
+
*
|
271 |
+
* @param array $Options The options to be set
|
272 |
+
* @return void
|
273 |
+
*/
|
274 |
+
public function setOptions($Options) {
|
275 |
+
$this->options = array_merge($this->options,$Options);
|
276 |
+
}
|
277 |
+
|
278 |
+
/**
|
279 |
+
* Register FirePHP as your error handler
|
280 |
+
*
|
281 |
+
* Will throw exceptions for each php error.
|
282 |
+
*/
|
283 |
+
public function registerErrorHandler()
|
284 |
+
{
|
285 |
+
//NOTE: The following errors will not be caught by this error handler:
|
286 |
+
// E_ERROR, E_PARSE, E_CORE_ERROR,
|
287 |
+
// E_CORE_WARNING, E_COMPILE_ERROR,
|
288 |
+
// E_COMPILE_WARNING, E_STRICT
|
289 |
+
|
290 |
+
set_error_handler(array($this,'errorHandler'));
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* FirePHP's error handler
|
295 |
+
*
|
296 |
+
* Throws exception for each php error that will occur.
|
297 |
+
*
|
298 |
+
* @param int $errno
|
299 |
+
* @param string $errstr
|
300 |
+
* @param string $errfile
|
301 |
+
* @param int $errline
|
302 |
+
* @param array $errcontext
|
303 |
+
*/
|
304 |
+
public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
|
305 |
+
{
|
306 |
+
// Don't throw exception if error reporting is switched off
|
307 |
+
if (error_reporting() == 0) {
|
308 |
+
return;
|
309 |
+
}
|
310 |
+
// Only throw exceptions for errors we are asking for
|
311 |
+
if (error_reporting() & $errno) {
|
312 |
+
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
|
313 |
+
}
|
314 |
+
}
|
315 |
+
|
316 |
+
/**
|
317 |
+
* Register FirePHP as your exception handler
|
318 |
+
*/
|
319 |
+
public function registerExceptionHandler()
|
320 |
+
{
|
321 |
+
set_exception_handler(array($this,'exceptionHandler'));
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* FirePHP's exception handler
|
326 |
+
*
|
327 |
+
* Logs all exceptions to your firebug console and then stops the script.
|
328 |
+
*
|
329 |
+
* @param Exception $Exception
|
330 |
+
* @throws Exception
|
331 |
+
*/
|
332 |
+
function exceptionHandler($Exception) {
|
333 |
+
$this->fb($Exception);
|
334 |
+
}
|
335 |
+
|
336 |
+
/**
|
337 |
+
* Set custom processor url for FirePHP
|
338 |
+
*
|
339 |
+
* @param string $URL
|
340 |
+
*/
|
341 |
+
public function setProcessorUrl($URL)
|
342 |
+
{
|
343 |
+
$this->setHeader('X-FirePHP-ProcessorURL', $URL);
|
344 |
+
}
|
345 |
+
|
346 |
+
/**
|
347 |
+
* Set custom renderer url for FirePHP
|
348 |
+
*
|
349 |
+
* @param string $URL
|
350 |
+
*/
|
351 |
+
public function setRendererUrl($URL)
|
352 |
+
{
|
353 |
+
$this->setHeader('X-FirePHP-RendererURL', $URL);
|
354 |
+
}
|
355 |
+
|
356 |
+
/**
|
357 |
+
* Start a group for following messages
|
358 |
+
*
|
359 |
+
* @param string $Name
|
360 |
+
* @return true
|
361 |
+
* @throws Exception
|
362 |
+
*/
|
363 |
+
public function group($Name) {
|
364 |
+
return $this->fb(null, $Name, FirePHP::GROUP_START);
|
365 |
+
}
|
366 |
+
|
367 |
+
/**
|
368 |
+
* Ends a group you have started before
|
369 |
+
*
|
370 |
+
* @return true
|
371 |
+
* @throws Exception
|
372 |
+
*/
|
373 |
+
public function groupEnd() {
|
374 |
+
return $this->fb(null, null, FirePHP::GROUP_END);
|
375 |
+
}
|
376 |
+
|
377 |
+
/**
|
378 |
+
* Log object with label to firebug console
|
379 |
+
*
|
380 |
+
* @see FirePHP::LOG
|
381 |
+
* @param mixes $Object
|
382 |
+
* @param string $Label
|
383 |
+
* @return true
|
384 |
+
* @throws Exception
|
385 |
+
*/
|
386 |
+
public function log($Object, $Label=null) {
|
387 |
+
return $this->fb($Object, $Label, FirePHP::LOG);
|
388 |
+
}
|
389 |
+
|
390 |
+
/**
|
391 |
+
* Log object with label to firebug console
|
392 |
+
*
|
393 |
+
* @see FirePHP::INFO
|
394 |
+
* @param mixes $Object
|
395 |
+
* @param string $Label
|
396 |
+
* @return true
|
397 |
+
* @throws Exception
|
398 |
+
*/
|
399 |
+
public function info($Object, $Label=null) {
|
400 |
+
return $this->fb($Object, $Label, FirePHP::INFO);
|
401 |
+
}
|
402 |
+
|
403 |
+
/**
|
404 |
+
* Log object with label to firebug console
|
405 |
+
*
|
406 |
+
* @see FirePHP::WARN
|
407 |
+
* @param mixes $Object
|
408 |
+
* @param string $Label
|
409 |
+
* @return true
|
410 |
+
* @throws Exception
|
411 |
+
*/
|
412 |
+
public function warn($Object, $Label=null) {
|
413 |
+
return $this->fb($Object, $Label, FirePHP::WARN);
|
414 |
+
}
|
415 |
+
|
416 |
+
/**
|
417 |
+
* Log object with label to firebug console
|
418 |
+
*
|
419 |
+
* @see FirePHP::ERROR
|
420 |
+
* @param mixes $Object
|
421 |
+
* @param string $Label
|
422 |
+
* @return true
|
423 |
+
* @throws Exception
|
424 |
+
*/
|
425 |
+
public function error($Object, $Label=null) {
|
426 |
+
return $this->fb($Object, $Label, FirePHP::ERROR);
|
427 |
+
}
|
428 |
+
|
429 |
+
/**
|
430 |
+
* Dumps key and variable to firebug server panel
|
431 |
+
*
|
432 |
+
* @see FirePHP::DUMP
|
433 |
+
* @param string $Key
|
434 |
+
* @param mixed $Variable
|
435 |
+
* @return true
|
436 |
+
* @throws Exception
|
437 |
+
*/
|
438 |
+
public function dump($Key, $Variable) {
|
439 |
+
return $this->fb($Variable, $Key, FirePHP::DUMP);
|
440 |
+
}
|
441 |
+
|
442 |
+
/**
|
443 |
+
* Log a trace in the firebug console
|
444 |
+
*
|
445 |
+
* @see FirePHP::TRACE
|
446 |
+
* @param string $Label
|
447 |
+
* @return true
|
448 |
+
* @throws Exception
|
449 |
+
*/
|
450 |
+
public function trace($Label) {
|
451 |
+
return $this->fb($Label, FirePHP::TRACE);
|
452 |
+
}
|
453 |
+
|
454 |
+
/**
|
455 |
+
* Log a table in the firebug console
|
456 |
+
*
|
457 |
+
* @see FirePHP::TABLE
|
458 |
+
* @param string $Label
|
459 |
+
* @param string $Table
|
460 |
+
* @return true
|
461 |
+
* @throws Exception
|
462 |
+
*/
|
463 |
+
public function table($Label, $Table) {
|
464 |
+
return $this->fb($Table, $Label, FirePHP::TABLE);
|
465 |
+
}
|
466 |
+
|
467 |
+
/**
|
468 |
+
* Check if FirePHP is installed on client
|
469 |
+
*
|
470 |
+
* @return boolean
|
471 |
+
*/
|
472 |
+
public function detectClientExtension() {
|
473 |
+
/* Check if FirePHP is installed on client */
|
474 |
+
if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) ||
|
475 |
+
!version_compare($m[1][0],'0.0.6','>=')) {
|
476 |
+
return false;
|
477 |
+
}
|
478 |
+
return true;
|
479 |
+
}
|
480 |
+
|
481 |
+
/**
|
482 |
+
* Log varible to Firebug
|
483 |
+
*
|
484 |
+
* @see http://www.firephp.org/Wiki/Reference/Fb
|
485 |
+
* @param mixed $Object The variable to be logged
|
486 |
+
* @return true Return TRUE if message was added to headers, FALSE otherwise
|
487 |
+
* @throws Exception
|
488 |
+
*/
|
489 |
+
public function fb($Object) {
|
490 |
+
|
491 |
+
if(!$this->enabled) {
|
492 |
+
return false;
|
493 |
+
}
|
494 |
+
|
495 |
+
if (headers_sent($filename, $linenum)) {
|
496 |
+
throw $this->newException('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
|
497 |
+
}
|
498 |
+
|
499 |
+
$Type = null;
|
500 |
+
$Label = null;
|
501 |
+
|
502 |
+
if(func_num_args()==1) {
|
503 |
+
} else
|
504 |
+
if(func_num_args()==2) {
|
505 |
+
switch(func_get_arg(1)) {
|
506 |
+
case self::LOG:
|
507 |
+
case self::INFO:
|
508 |
+
case self::WARN:
|
509 |
+
case self::ERROR:
|
510 |
+
case self::DUMP:
|
511 |
+
case self::TRACE:
|
512 |
+
case self::EXCEPTION:
|
513 |
+
case self::TABLE:
|
514 |
+
case self::GROUP_START:
|
515 |
+
case self::GROUP_END:
|
516 |
+
$Type = func_get_arg(1);
|
517 |
+
break;
|
518 |
+
default:
|
519 |
+
$Label = func_get_arg(1);
|
520 |
+
break;
|
521 |
+
}
|
522 |
+
} else
|
523 |
+
if(func_num_args()==3) {
|
524 |
+
$Type = func_get_arg(2);
|
525 |
+
$Label = func_get_arg(1);
|
526 |
+
} else {
|
527 |
+
throw $this->newException('Wrong number of arguments to fb() function!');
|
528 |
+
}
|
529 |
+
|
530 |
+
|
531 |
+
if(!$this->detectClientExtension()) {
|
532 |
+
return false;
|
533 |
+
}
|
534 |
+
|
535 |
+
$meta = array();
|
536 |
+
$skipFinalObjectEncode = false;
|
537 |
+
|
538 |
+
if($Object instanceof Exception) {
|
539 |
+
|
540 |
+
$meta['file'] = $this->_escapeTraceFile($Object->getFile());
|
541 |
+
$meta['line'] = $Object->getLine();
|
542 |
+
|
543 |
+
$trace = $Object->getTrace();
|
544 |
+
if($Object instanceof ErrorException
|
545 |
+
&& isset($trace[0]['function'])
|
546 |
+
&& $trace[0]['function']=='errorHandler'
|
547 |
+
&& isset($trace[0]['class'])
|
548 |
+
&& $trace[0]['class']=='FirePHP') {
|
549 |
+
|
550 |
+
$severity = false;
|
551 |
+
switch($Object->getSeverity()) {
|
552 |
+
case E_WARNING: $severity = 'E_WARNING'; break;
|
553 |
+
case E_NOTICE: $severity = 'E_NOTICE'; break;
|
554 |
+
case E_USER_ERROR: $severity = 'E_USER_ERROR'; break;
|
555 |
+
case E_USER_WARNING: $severity = 'E_USER_WARNING'; break;
|
556 |
+
case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break;
|
557 |
+
case E_STRICT: $severity = 'E_STRICT'; break;
|
558 |
+
case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break;
|
559 |
+
case E_DEPRECATED: $severity = 'E_DEPRECATED'; break;
|
560 |
+
case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break;
|
561 |
+
}
|
562 |
+
|
563 |
+
$Object = array('Class'=>get_class($Object),
|
564 |
+
'Message'=>$severity.': '.$Object->getMessage(),
|
565 |
+
'File'=>$this->_escapeTraceFile($Object->getFile()),
|
566 |
+
'Line'=>$Object->getLine(),
|
567 |
+
'Type'=>'trigger',
|
568 |
+
'Trace'=>$this->_escapeTrace(array_splice($trace,2)));
|
569 |
+
$skipFinalObjectEncode = true;
|
570 |
+
} else {
|
571 |
+
$Object = array('Class'=>get_class($Object),
|
572 |
+
'Message'=>$Object->getMessage(),
|
573 |
+
'File'=>$this->_escapeTraceFile($Object->getFile()),
|
574 |
+
'Line'=>$Object->getLine(),
|
575 |
+
'Type'=>'throw',
|
576 |
+
'Trace'=>$this->_escapeTrace($trace));
|
577 |
+
$skipFinalObjectEncode = true;
|
578 |
+
}
|
579 |
+
$Type = self::EXCEPTION;
|
580 |
+
|
581 |
+
} else
|
582 |
+
if($Type==self::TRACE) {
|
583 |
+
|
584 |
+
$trace = debug_backtrace();
|
585 |
+
if(!$trace) return false;
|
586 |
+
for( $i=0 ; $i<sizeof($trace) ; $i++ ) {
|
587 |
+
|
588 |
+
if(isset($trace[$i]['class'])
|
589 |
+
&& isset($trace[$i]['file'])
|
590 |
+
&& ($trace[$i]['class']=='FirePHP'
|
591 |
+
|| $trace[$i]['class']=='FB')
|
592 |
+
&& (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
|
593 |
+
|| substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
|
594 |
+
/* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
|
595 |
+
} else
|
596 |
+
if(isset($trace[$i]['class'])
|
597 |
+
&& isset($trace[$i+1]['file'])
|
598 |
+
&& $trace[$i]['class']=='FirePHP'
|
599 |
+
&& substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
|
600 |
+
/* Skip fb() */
|
601 |
+
} else
|
602 |
+
if($trace[$i]['function']=='fb'
|
603 |
+
|| $trace[$i]['function']=='trace'
|
604 |
+
|| $trace[$i]['function']=='send') {
|
605 |
+
$Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'',
|
606 |
+
'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'',
|
607 |
+
'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'',
|
608 |
+
'Message'=>$trace[$i]['args'][0],
|
609 |
+
'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'',
|
610 |
+
'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'',
|
611 |
+
'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'',
|
612 |
+
'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1)));
|
613 |
+
|
614 |
+
$skipFinalObjectEncode = true;
|
615 |
+
$meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
|
616 |
+
$meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
|
617 |
+
break;
|
618 |
+
}
|
619 |
+
}
|
620 |
+
|
621 |
+
} else
|
622 |
+
if($Type==self::TABLE) {
|
623 |
+
|
624 |
+
if(isset($Object[0]) && is_string($Object[0])) {
|
625 |
+
$Object[1] = $this->encodeTable($Object[1]);
|
626 |
+
} else {
|
627 |
+
$Object = $this->encodeTable($Object);
|
628 |
+
}
|
629 |
+
|
630 |
+
$skipFinalObjectEncode = true;
|
631 |
+
|
632 |
+
} else {
|
633 |
+
if($Type===null) {
|
634 |
+
$Type = self::LOG;
|
635 |
+
}
|
636 |
+
}
|
637 |
+
|
638 |
+
if($this->options['includeLineNumbers']) {
|
639 |
+
if(!isset($meta['file']) || !isset($meta['line'])) {
|
640 |
+
|
641 |
+
$trace = debug_backtrace();
|
642 |
+
for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) {
|
643 |
+
|
644 |
+
if(isset($trace[$i]['class'])
|
645 |
+
&& isset($trace[$i]['file'])
|
646 |
+
&& ($trace[$i]['class']=='FirePHP'
|
647 |
+
|| $trace[$i]['class']=='FB')
|
648 |
+
&& (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
|
649 |
+
|| substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
|
650 |
+
/* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
|
651 |
+
} else
|
652 |
+
if(isset($trace[$i]['class'])
|
653 |
+
&& isset($trace[$i+1]['file'])
|
654 |
+
&& $trace[$i]['class']=='FirePHP'
|
655 |
+
&& substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
|
656 |
+
/* Skip fb() */
|
657 |
+
} else
|
658 |
+
if(isset($trace[$i]['file'])
|
659 |
+
&& substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') {
|
660 |
+
/* Skip FB::fb() */
|
661 |
+
} else {
|
662 |
+
$meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
|
663 |
+
$meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
|
664 |
+
break;
|
665 |
+
}
|
666 |
+
}
|
667 |
+
|
668 |
+
}
|
669 |
+
} else {
|
670 |
+
unset($meta['file']);
|
671 |
+
unset($meta['line']);
|
672 |
+
}
|
673 |
+
|
674 |
+
$this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
|
675 |
+
$this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION);
|
676 |
+
|
677 |
+
$structure_index = 1;
|
678 |
+
if($Type==self::DUMP) {
|
679 |
+
$structure_index = 2;
|
680 |
+
$this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
|
681 |
+
} else {
|
682 |
+
$this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
|
683 |
+
}
|
684 |
+
|
685 |
+
if($Type==self::DUMP) {
|
686 |
+
$msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}';
|
687 |
+
} else {
|
688 |
+
$msg_meta = array('Type'=>$Type);
|
689 |
+
if($Label!==null) {
|
690 |
+
$msg_meta['Label'] = $Label;
|
691 |
+
}
|
692 |
+
if(isset($meta['file'])) {
|
693 |
+
$msg_meta['File'] = $meta['file'];
|
694 |
+
}
|
695 |
+
if(isset($meta['line'])) {
|
696 |
+
$msg_meta['Line'] = $meta['line'];
|
697 |
+
}
|
698 |
+
$msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']';
|
699 |
+
}
|
700 |
+
|
701 |
+
$parts = explode("\n",chunk_split($msg, 5000, "\n"));
|
702 |
+
|
703 |
+
for( $i=0 ; $i<count($parts) ; $i++) {
|
704 |
+
|
705 |
+
$part = $parts[$i];
|
706 |
+
if ($part) {
|
707 |
+
|
708 |
+
if(count($parts)>2) {
|
709 |
+
// Message needs to be split into multiple parts
|
710 |
+
$this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
|
711 |
+
(($i==0)?strlen($msg):'')
|
712 |
+
. '|' . $part . '|'
|
713 |
+
. (($i<count($parts)-2)?'\\':''));
|
714 |
+
} else {
|
715 |
+
$this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
|
716 |
+
strlen($part) . '|' . $part . '|');
|
717 |
+
}
|
718 |
+
|
719 |
+
$this->messageIndex++;
|
720 |
+
|
721 |
+
if ($this->messageIndex > 99999) {
|
722 |
+
throw new Exception('Maximum number (99,999) of messages reached!');
|
723 |
+
}
|
724 |
+
}
|
725 |
+
}
|
726 |
+
|
727 |
+
$this->setHeader('X-Wf-1-Index',$this->messageIndex-1);
|
728 |
+
|
729 |
+
return true;
|
730 |
+
}
|
731 |
+
|
732 |
+
/**
|
733 |
+
* Standardizes path for windows systems.
|
734 |
+
*
|
735 |
+
* @param string $Path
|
736 |
+
* @return string
|
737 |
+
*/
|
738 |
+
protected function _standardizePath($Path) {
|
739 |
+
return preg_replace('/\\\\+/','/',$Path);
|
740 |
+
}
|
741 |
+
|
742 |
+
/**
|
743 |
+
* Escape trace path for windows systems
|
744 |
+
*
|
745 |
+
* @param array $Trace
|
746 |
+
* @return array
|
747 |
+
*/
|
748 |
+
protected function _escapeTrace($Trace) {
|
749 |
+
if(!$Trace) return $Trace;
|
750 |
+
for( $i=0 ; $i<sizeof($Trace) ; $i++ ) {
|
751 |
+
if(isset($Trace[$i]['file'])) {
|
752 |
+
$Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']);
|
753 |
+
}
|
754 |
+
if(isset($Trace[$i]['args'])) {
|
755 |
+
$Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']);
|
756 |
+
}
|
757 |
+
}
|
758 |
+
return $Trace;
|
759 |
+
}
|
760 |
+
|
761 |
+
/**
|
762 |
+
* Escape file information of trace for windows systems
|
763 |
+
*
|
764 |
+
* @param string $File
|
765 |
+
* @return string
|
766 |
+
*/
|
767 |
+
protected function _escapeTraceFile($File) {
|
768 |
+
/* Check if we have a windows filepath */
|
769 |
+
if(strpos($File,'\\')) {
|
770 |
+
/* First strip down to single \ */
|
771 |
+
|
772 |
+
$file = preg_replace('/\\\\+/','\\',$File);
|
773 |
+
|
774 |
+
return $file;
|
775 |
+
}
|
776 |
+
return $File;
|
777 |
+
}
|
778 |
+
|
779 |
+
/**
|
780 |
+
* Send header
|
781 |
+
*
|
782 |
+
* @param string $Name
|
783 |
+
* @param string_type $Value
|
784 |
+
*/
|
785 |
+
protected function setHeader($Name, $Value) {
|
786 |
+
return header($Name.': '.$Value);
|
787 |
+
}
|
788 |
+
|
789 |
+
/**
|
790 |
+
* Get user agent
|
791 |
+
*
|
792 |
+
* @return string|false
|
793 |
+
*/
|
794 |
+
protected function getUserAgent() {
|
795 |
+
if(!isset($_SERVER['HTTP_USER_AGENT'])) return false;
|
796 |
+
return $_SERVER['HTTP_USER_AGENT'];
|
797 |
+
}
|
798 |
+
|
799 |
+
/**
|
800 |
+
* Returns a new exception
|
801 |
+
*
|
802 |
+
* @param string $Message
|
803 |
+
* @return Exception
|
804 |
+
*/
|
805 |
+
protected function newException($Message) {
|
806 |
+
return new Exception($Message);
|
807 |
+
}
|
808 |
+
|
809 |
+
/**
|
810 |
+
* Encode an object into a JSON string
|
811 |
+
*
|
812 |
+
* Uses PHP's jeson_encode() if available
|
813 |
+
*
|
814 |
+
* @param object $Object The object to be encoded
|
815 |
+
* @return string The JSON string
|
816 |
+
*/
|
817 |
+
protected function jsonEncode($Object, $skipObjectEncode=false)
|
818 |
+
{
|
819 |
+
if(!$skipObjectEncode) {
|
820 |
+
$Object = $this->encodeObject($Object);
|
821 |
+
}
|
822 |
+
|
823 |
+
if(function_exists('json_encode')
|
824 |
+
&& $this->options['useNativeJsonEncode']!=false) {
|
825 |
+
|
826 |
+
return json_encode($Object);
|
827 |
+
} else {
|
828 |
+
return $this->json_encode($Object);
|
829 |
+
}
|
830 |
+
}
|
831 |
+
|
832 |
+
/**
|
833 |
+
* Encodes a table by encoding each row and column with encodeObject()
|
834 |
+
*
|
835 |
+
* @param array $Table The table to be encoded
|
836 |
+
* @return array
|
837 |
+
*/
|
838 |
+
protected function encodeTable($Table) {
|
839 |
+
if(!$Table) return $Table;
|
840 |
+
for( $i=0 ; $i<count($Table) ; $i++ ) {
|
841 |
+
if(is_array($Table[$i])) {
|
842 |
+
for( $j=0 ; $j<count($Table[$i]) ; $j++ ) {
|
843 |
+
$Table[$i][$j] = $this->encodeObject($Table[$i][$j]);
|
844 |
+
}
|
845 |
+
}
|
846 |
+
}
|
847 |
+
return $Table;
|
848 |
+
}
|
849 |
+
|
850 |
+
/**
|
851 |
+
* Encodes an object including members with
|
852 |
+
* protected and private visibility
|
853 |
+
*
|
854 |
+
* @param Object $Object The object to be encoded
|
855 |
+
* @param int $Depth The current traversal depth
|
856 |
+
* @return array All members of the object
|
857 |
+
*/
|
858 |
+
protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1)
|
859 |
+
{
|
860 |
+
$return = array();
|
861 |
+
|
862 |
+
if (is_object($Object)) {
|
863 |
+
|
864 |
+
if ($ObjectDepth > $this->options['maxObjectDepth']) {
|
865 |
+
return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **';
|
866 |
+
}
|
867 |
+
|
868 |
+
foreach ($this->objectStack as $refVal) {
|
869 |
+
if ($refVal === $Object) {
|
870 |
+
return '** Recursion ('.get_class($Object).') **';
|
871 |
+
}
|
872 |
+
}
|
873 |
+
array_push($this->objectStack, $Object);
|
874 |
+
|
875 |
+
$return['__className'] = $class = get_class($Object);
|
876 |
+
|
877 |
+
$reflectionClass = new ReflectionClass($class);
|
878 |
+
$properties = array();
|
879 |
+
foreach( $reflectionClass->getProperties() as $property) {
|
880 |
+
$properties[$property->getName()] = $property;
|
881 |
+
}
|
882 |
+
|
883 |
+
$members = (array)$Object;
|
884 |
+
|
885 |
+
foreach( $properties as $raw_name => $property ) {
|
886 |
+
|
887 |
+
$name = $raw_name;
|
888 |
+
if($property->isStatic()) {
|
889 |
+
$name = 'static:'.$name;
|
890 |
+
}
|
891 |
+
if($property->isPublic()) {
|
892 |
+
$name = 'public:'.$name;
|
893 |
+
} else
|
894 |
+
if($property->isPrivate()) {
|
895 |
+
$name = 'private:'.$name;
|
896 |
+
$raw_name = "\0".$class."\0".$raw_name;
|
897 |
+
} else
|
898 |
+
if($property->isProtected()) {
|
899 |
+
$name = 'protected:'.$name;
|
900 |
+
$raw_name = "\0".'*'."\0".$raw_name;
|
901 |
+
}
|
902 |
+
|
903 |
+
if(!(isset($this->objectFilters[$class])
|
904 |
+
&& is_array($this->objectFilters[$class])
|
905 |
+
&& in_array($raw_name,$this->objectFilters[$class]))) {
|
906 |
+
|
907 |
+
if(array_key_exists($raw_name,$members)
|
908 |
+
&& !$property->isStatic()) {
|
909 |
+
|
910 |
+
$return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1);
|
911 |
+
|
912 |
+
} else {
|
913 |
+
if(method_exists($property,'setAccessible')) {
|
914 |
+
$property->setAccessible(true);
|
915 |
+
$return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1);
|
916 |
+
} else
|
917 |
+
if($property->isPublic()) {
|
918 |
+
$return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1);
|
919 |
+
} else {
|
920 |
+
$return[$name] = '** Need PHP 5.3 to get value **';
|
921 |
+
}
|
922 |
+
}
|
923 |
+
} else {
|
924 |
+
$return[$name] = '** Excluded by Filter **';
|
925 |
+
}
|
926 |
+
}
|
927 |
+
|
928 |
+
// Include all members that are not defined in the class
|
929 |
+
// but exist in the object
|
930 |
+
foreach( $members as $raw_name => $value ) {
|
931 |
+
|
932 |
+
$name = $raw_name;
|
933 |
+
|
934 |
+
if ($name{0} == "\0") {
|
935 |
+
$parts = explode("\0", $name);
|
936 |
+
$name = $parts[2];
|
937 |
+
}
|
938 |
+
|
939 |
+
if(!isset($properties[$name])) {
|
940 |
+
$name = 'undeclared:'.$name;
|
941 |
+
|
942 |
+
if(!(isset($this->objectFilters[$class])
|
943 |
+
&& is_array($this->objectFilters[$class])
|
944 |
+
&& in_array($raw_name,$this->objectFilters[$class]))) {
|
945 |
+
|
946 |
+
$return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1);
|
947 |
+
} else {
|
948 |
+
$return[$name] = '** Excluded by Filter **';
|
949 |
+
}
|
950 |
+
}
|
951 |
+
}
|
952 |
+
|
953 |
+
array_pop($this->objectStack);
|
954 |
+
|
955 |
+
} elseif (is_array($Object)) {
|
956 |
+
|
957 |
+
if ($ArrayDepth > $this->options['maxArrayDepth']) {
|
958 |
+
return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **';
|
959 |
+
}
|
960 |
+
|
961 |
+
foreach ($Object as $key => $val) {
|
962 |
+
|
963 |
+
// Encoding the $GLOBALS PHP array causes an infinite loop
|
964 |
+
// if the recursion is not reset here as it contains
|
965 |
+
// a reference to itself. This is the only way I have come up
|
966 |
+
// with to stop infinite recursion in this case.
|
967 |
+
if($key=='GLOBALS'
|
968 |
+
&& is_array($val)
|
969 |
+
&& array_key_exists('GLOBALS',$val)) {
|
970 |
+
$val['GLOBALS'] = '** Recursion (GLOBALS) **';
|
971 |
+
}
|
972 |
+
|
973 |
+
$return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1);
|
974 |
+
}
|
975 |
+
} else {
|
976 |
+
if(self::is_utf8($Object)) {
|
977 |
+
return $Object;
|
978 |
+
} else {
|
979 |
+
return utf8_encode($Object);
|
980 |
+
}
|
981 |
+
}
|
982 |
+
return $return;
|
983 |
+
}
|
984 |
+
|
985 |
+
/**
|
986 |
+
* Returns true if $string is valid UTF-8 and false otherwise.
|
987 |
+
*
|
988 |
+
* @param mixed $str String to be tested
|
989 |
+
* @return boolean
|
990 |
+
*/
|
991 |
+
protected static function is_utf8($str) {
|
992 |
+
$c=0; $b=0;
|
993 |
+
$bits=0;
|
994 |
+
$len=strlen($str);
|
995 |
+
for($i=0; $i<$len; $i++){
|
996 |
+
$c=ord($str[$i]);
|
997 |
+
if($c > 128){
|
998 |
+
if(($c >= 254)) return false;
|
999 |
+
elseif($c >= 252) $bits=6;
|
1000 |
+
elseif($c >= 248) $bits=5;
|
1001 |
+
elseif($c >= 240) $bits=4;
|
1002 |
+
elseif($c >= 224) $bits=3;
|
1003 |
+
elseif($c >= 192) $bits=2;
|
1004 |
+
else return false;
|
1005 |
+
if(($i+$bits) > $len) return false;
|
1006 |
+
while($bits > 1){
|
1007 |
+
$i++;
|
1008 |
+
$b=ord($str[$i]);
|
1009 |
+
if($b < 128 || $b > 191) return false;
|
1010 |
+
$bits--;
|
1011 |
+
}
|
1012 |
+
}
|
1013 |
+
}
|
1014 |
+
return true;
|
1015 |
+
}
|
1016 |
+
|
1017 |
+
/**
|
1018 |
+
* Converts to and from JSON format.
|
1019 |
+
*
|
1020 |
+
* JSON (JavaScript Object Notation) is a lightweight data-interchange
|
1021 |
+
* format. It is easy for humans to read and write. It is easy for machines
|
1022 |
+
* to parse and generate. It is based on a subset of the JavaScript
|
1023 |
+
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
|
1024 |
+
* This feature can also be found in Python. JSON is a text format that is
|
1025 |
+
* completely language independent but uses conventions that are familiar
|
1026 |
+
* to programmers of the C-family of languages, including C, C++, C#, Java,
|
1027 |
+
* JavaScript, Perl, TCL, and many others. These properties make JSON an
|
1028 |
+
* ideal data-interchange language.
|
1029 |
+
*
|
1030 |
+
* This package provides a simple encoder and decoder for JSON notation. It
|
1031 |
+
* is intended for use with client-side Javascript applications that make
|
1032 |
+
* use of HTTPRequest to perform server communication functions - data can
|
1033 |
+
* be encoded into JSON notation for use in a client-side javascript, or
|
1034 |
+
* decoded from incoming Javascript requests. JSON format is native to
|
1035 |
+
* Javascript, and can be directly eval()'ed with no further parsing
|
1036 |
+
* overhead
|
1037 |
+
*
|
1038 |
+
* All strings should be in ASCII or UTF-8 format!
|
1039 |
+
*
|
1040 |
+
* LICENSE: Redistribution and use in source and binary forms, with or
|
1041 |
+
* without modification, are permitted provided that the following
|
1042 |
+
* conditions are met: Redistributions of source code must retain the
|
1043 |
+
* above copyright notice, this list of conditions and the following
|
1044 |
+
* disclaimer. Redistributions in binary form must reproduce the above
|
1045 |
+
* copyright notice, this list of conditions and the following disclaimer
|
1046 |
+
* in the documentation and/or other materials provided with the
|
1047 |
+
* distribution.
|
1048 |
+
*
|
1049 |
+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
1050 |
+
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
1051 |
+
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
1052 |
+
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
1053 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
1054 |
+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
1055 |
+
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
1056 |
+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
1057 |
+
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
1058 |
+
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
1059 |
+
* DAMAGE.
|
1060 |
+
*
|
1061 |
+
* @category
|
1062 |
+
* @package Services_JSON
|
1063 |
+
* @author Michal Migurski <mike-json@teczno.com>
|
1064 |
+
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
|
1065 |
+
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
|
1066 |
+
* @author Christoph Dorn <christoph@christophdorn.com>
|
1067 |
+
* @copyright 2005 Michal Migurski
|
1068 |
+
* @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
|
1069 |
+
* @license http://www.opensource.org/licenses/bsd-license.php
|
1070 |
+
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
|
1071 |
+
*/
|
1072 |
+
|
1073 |
+
|
1074 |
+
/**
|
1075 |
+
* Keep a list of objects as we descend into the array so we can detect recursion.
|
1076 |
+
*/
|
1077 |
+
private $json_objectStack = array();
|
1078 |
+
|
1079 |
+
|
1080 |
+
/**
|
1081 |
+
* convert a string from one UTF-8 char to one UTF-16 char
|
1082 |
+
*
|
1083 |
+
* Normally should be handled by mb_convert_encoding, but
|
1084 |
+
* provides a slower PHP-only method for installations
|
1085 |
+
* that lack the multibye string extension.
|
1086 |
+
*
|
1087 |
+
* @param string $utf8 UTF-8 character
|
1088 |
+
* @return string UTF-16 character
|
1089 |
+
* @access private
|
1090 |
+
*/
|
1091 |
+
private function json_utf82utf16($utf8)
|
1092 |
+
{
|
1093 |
+
// oh please oh please oh please oh please oh please
|
1094 |
+
if(function_exists('mb_convert_encoding')) {
|
1095 |
+
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
|
1096 |
+
}
|
1097 |
+
|
1098 |
+
switch(strlen($utf8)) {
|
1099 |
+
case 1:
|
1100 |
+
// this case should never be reached, because we are in ASCII range
|
1101 |
+
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
1102 |
+
return $utf8;
|
1103 |
+
|
1104 |
+
case 2:
|
1105 |
+
// return a UTF-16 character from a 2-byte UTF-8 char
|
1106 |
+
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
1107 |
+
return chr(0x07 & (ord($utf8{0}) >> 2))
|
1108 |
+
. chr((0xC0 & (ord($utf8{0}) << 6))
|
1109 |
+
| (0x3F & ord($utf8{1})));
|
1110 |
+
|
1111 |
+
case 3:
|
1112 |
+
// return a UTF-16 character from a 3-byte UTF-8 char
|
1113 |
+
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
1114 |
+
return chr((0xF0 & (ord($utf8{0}) << 4))
|
1115 |
+
| (0x0F & (ord($utf8{1}) >> 2)))
|
1116 |
+
. chr((0xC0 & (ord($utf8{1}) << 6))
|
1117 |
+
| (0x7F & ord($utf8{2})));
|
1118 |
+
}
|
1119 |
+
|
1120 |
+
// ignoring UTF-32 for now, sorry
|
1121 |
+
return '';
|
1122 |
+
}
|
1123 |
+
|
1124 |
+
/**
|
1125 |
+
* encodes an arbitrary variable into JSON format
|
1126 |
+
*
|
1127 |
+
* @param mixed $var any number, boolean, string, array, or object to be encoded.
|
1128 |
+
* see argument 1 to Services_JSON() above for array-parsing behavior.
|
1129 |
+
* if var is a strng, note that encode() always expects it
|
1130 |
+
* to be in ASCII or UTF-8 format!
|
1131 |
+
*
|
1132 |
+
* @return mixed JSON string representation of input var or an error if a problem occurs
|
1133 |
+
* @access public
|
1134 |
+
*/
|
1135 |
+
private function json_encode($var)
|
1136 |
+
{
|
1137 |
+
|
1138 |
+
if(is_object($var)) {
|
1139 |
+
if(in_array($var,$this->json_objectStack)) {
|
1140 |
+
return '"** Recursion **"';
|
1141 |
+
}
|
1142 |
+
}
|
1143 |
+
|
1144 |
+
switch (gettype($var)) {
|
1145 |
+
case 'boolean':
|
1146 |
+
return $var ? 'true' : 'false';
|
1147 |
+
|
1148 |
+
case 'NULL':
|
1149 |
+
return 'null';
|
1150 |
+
|
1151 |
+
case 'integer':
|
1152 |
+
return (int) $var;
|
1153 |
+
|
1154 |
+
case 'double':
|
1155 |
+
case 'float':
|
1156 |
+
return (float) $var;
|
1157 |
+
|
1158 |
+
case 'string':
|
1159 |
+
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
|
1160 |
+
$ascii = '';
|
1161 |
+
$strlen_var = strlen($var);
|
1162 |
+
|
1163 |
+
/*
|
1164 |
+
* Iterate over every character in the string,
|
1165 |
+
* escaping with a slash or encoding to UTF-8 where necessary
|
1166 |
+
*/
|
1167 |
+
for ($c = 0; $c < $strlen_var; ++$c) {
|
1168 |
+
|
1169 |
+
$ord_var_c = ord($var{$c});
|
1170 |
+
|
1171 |
+
switch (true) {
|
1172 |
+
case $ord_var_c == 0x08:
|
1173 |
+
$ascii .= '\b';
|
1174 |
+
break;
|
1175 |
+
case $ord_var_c == 0x09:
|
1176 |
+
$ascii .= '\t';
|
1177 |
+
break;
|
1178 |
+
case $ord_var_c == 0x0A:
|
1179 |
+
$ascii .= '\n';
|
1180 |
+
break;
|
1181 |
+
case $ord_var_c == 0x0C:
|
1182 |
+
$ascii .= '\f';
|
1183 |
+
break;
|
1184 |
+
case $ord_var_c == 0x0D:
|
1185 |
+
$ascii .= '\r';
|
1186 |
+
break;
|
1187 |
+
|
1188 |
+
case $ord_var_c == 0x22:
|
1189 |
+
case $ord_var_c == 0x2F:
|
1190 |
+
case $ord_var_c == 0x5C:
|
1191 |
+
// double quote, slash, slosh
|
1192 |
+
$ascii .= '\\'.$var{$c};
|
1193 |
+
break;
|
1194 |
+
|
1195 |
+
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
|
1196 |
+
// characters U-00000000 - U-0000007F (same as ASCII)
|
1197 |
+
$ascii .= $var{$c};
|
1198 |
+
break;
|
1199 |
+
|
1200 |
+
case (($ord_var_c & 0xE0) == 0xC0):
|
1201 |
+
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
1202 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
1203 |
+
$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
|
1204 |
+
$c += 1;
|
1205 |
+
$utf16 = $this->json_utf82utf16($char);
|
1206 |
+
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
1207 |
+
break;
|
1208 |
+
|
1209 |
+
case (($ord_var_c & 0xF0) == 0xE0):
|
1210 |
+
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
1211 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
1212 |
+
$char = pack('C*', $ord_var_c,
|
1213 |
+
ord($var{$c + 1}),
|
1214 |
+
ord($var{$c + 2}));
|
1215 |
+
$c += 2;
|
1216 |
+
$utf16 = $this->json_utf82utf16($char);
|
1217 |
+
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
1218 |
+
break;
|
1219 |
+
|
1220 |
+
case (($ord_var_c & 0xF8) == 0xF0):
|
1221 |
+
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
1222 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
1223 |
+
$char = pack('C*', $ord_var_c,
|
1224 |
+
ord($var{$c + 1}),
|
1225 |
+
ord($var{$c + 2}),
|
1226 |
+
ord($var{$c + 3}));
|
1227 |
+
$c += 3;
|
1228 |
+
$utf16 = $this->json_utf82utf16($char);
|
1229 |
+
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
1230 |
+
break;
|
1231 |
+
|
1232 |
+
case (($ord_var_c & 0xFC) == 0xF8):
|
1233 |
+
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
1234 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
1235 |
+
$char = pack('C*', $ord_var_c,
|
1236 |
+
ord($var{$c + 1}),
|
1237 |
+
ord($var{$c + 2}),
|
1238 |
+
ord($var{$c + 3}),
|
1239 |
+
ord($var{$c + 4}));
|
1240 |
+
$c += 4;
|
1241 |
+
$utf16 = $this->json_utf82utf16($char);
|
1242 |
+
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
1243 |
+
break;
|
1244 |
+
|
1245 |
+
case (($ord_var_c & 0xFE) == 0xFC):
|
1246 |
+
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
1247 |
+
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
1248 |
+
$char = pack('C*', $ord_var_c,
|
1249 |
+
ord($var{$c + 1}),
|
1250 |
+
ord($var{$c + 2}),
|
1251 |
+
ord($var{$c + 3}),
|
1252 |
+
ord($var{$c + 4}),
|
1253 |
+
ord($var{$c + 5}));
|
1254 |
+
$c += 5;
|
1255 |
+
$utf16 = $this->json_utf82utf16($char);
|
1256 |
+
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
1257 |
+
break;
|
1258 |
+
}
|
1259 |
+
}
|
1260 |
+
|
1261 |
+
return '"'.$ascii.'"';
|
1262 |
+
|
1263 |
+
case 'array':
|
1264 |
+
/*
|
1265 |
+
* As per JSON spec if any array key is not an integer
|
1266 |
+
* we must treat the the whole array as an object. We
|
1267 |
+
* also try to catch a sparsely populated associative
|
1268 |
+
* array with numeric keys here because some JS engines
|
1269 |
+
* will create an array with empty indexes up to
|
1270 |
+
* max_index which can cause memory issues and because
|
1271 |
+
* the keys, which may be relevant, will be remapped
|
1272 |
+
* otherwise.
|
1273 |
+
*
|
1274 |
+
* As per the ECMA and JSON specification an object may
|
1275 |
+
* have any string as a property. Unfortunately due to
|
1276 |
+
* a hole in the ECMA specification if the key is a
|
1277 |
+
* ECMA reserved word or starts with a digit the
|
1278 |
+
* parameter is only accessible using ECMAScript's
|
1279 |
+
* bracket notation.
|
1280 |
+
*/
|
1281 |
+
|
1282 |
+
// treat as a JSON object
|
1283 |
+
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
|
1284 |
+
|
1285 |
+
$this->json_objectStack[] = $var;
|
1286 |
+
|
1287 |
+
$properties = array_map(array($this, 'json_name_value'),
|
1288 |
+
array_keys($var),
|
1289 |
+
array_values($var));
|
1290 |
+
|
1291 |
+
array_pop($this->json_objectStack);
|
1292 |
+
|
1293 |
+
foreach($properties as $property) {
|
1294 |
+
if($property instanceof Exception) {
|
1295 |
+
return $property;
|
1296 |
+
}
|
1297 |
+
}
|
1298 |
+
|
1299 |
+
return '{' . join(',', $properties) . '}';
|
1300 |
+
}
|
1301 |
+
|
1302 |
+
$this->json_objectStack[] = $var;
|
1303 |
+
|
1304 |
+
// treat it like a regular array
|
1305 |
+
$elements = array_map(array($this, 'json_encode'), $var);
|
1306 |
+
|
1307 |
+
array_pop($this->json_objectStack);
|
1308 |
+
|
1309 |
+
foreach($elements as $element) {
|
1310 |
+
if($element instanceof Exception) {
|
1311 |
+
return $element;
|
1312 |
+
}
|
1313 |
+
}
|
1314 |
+
|
1315 |
+
return '[' . join(',', $elements) . ']';
|
1316 |
+
|
1317 |
+
case 'object':
|
1318 |
+
$vars = self::encodeObject($var);
|
1319 |
+
|
1320 |
+
$this->json_objectStack[] = $var;
|
1321 |
+
|
1322 |
+
$properties = array_map(array($this, 'json_name_value'),
|
1323 |
+
array_keys($vars),
|
1324 |
+
array_values($vars));
|
1325 |
+
|
1326 |
+
array_pop($this->json_objectStack);
|
1327 |
+
|
1328 |
+
foreach($properties as $property) {
|
1329 |
+
if($property instanceof Exception) {
|
1330 |
+
return $property;
|
1331 |
+
}
|
1332 |
+
}
|
1333 |
+
|
1334 |
+
return '{' . join(',', $properties) . '}';
|
1335 |
+
|
1336 |
+
default:
|
1337 |
+
return null;
|
1338 |
+
}
|
1339 |
+
}
|
1340 |
+
|
1341 |
+
/**
|
1342 |
+
* array-walking function for use in generating JSON-formatted name-value pairs
|
1343 |
+
*
|
1344 |
+
* @param string $name name of key to use
|
1345 |
+
* @param mixed $value reference to an array element to be encoded
|
1346 |
+
*
|
1347 |
+
* @return string JSON-formatted name-value pair, like '"name":value'
|
1348 |
+
* @access private
|
1349 |
+
*/
|
1350 |
+
private function json_name_value($name, $value)
|
1351 |
+
{
|
1352 |
+
// Encoding the $GLOBALS PHP array causes an infinite loop
|
1353 |
+
// if the recursion is not reset here as it contains
|
1354 |
+
// a reference to itself. This is the only way I have come up
|
1355 |
+
// with to stop infinite recursion in this case.
|
1356 |
+
if($name=='GLOBALS'
|
1357 |
+
&& is_array($value)
|
1358 |
+
&& array_key_exists('GLOBALS',$value)) {
|
1359 |
+
$value['GLOBALS'] = '** Recursion **';
|
1360 |
+
}
|
1361 |
+
|
1362 |
+
$encoded_value = $this->json_encode($value);
|
1363 |
+
|
1364 |
+
if($encoded_value instanceof Exception) {
|
1365 |
+
return $encoded_value;
|
1366 |
+
}
|
1367 |
+
|
1368 |
+
return $this->json_encode(strval($name)) . ':' . $encoded_value;
|
1369 |
+
}
|
1370 |
+
}
|
min/lib/HTTP/ConditionalGet.php
ADDED
@@ -0,0 +1,368 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class HTTP_ConditionalGet
|
4 |
+
* @package Minify
|
5 |
+
* @subpackage HTTP
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Implement conditional GET via a timestamp or hash of content
|
10 |
+
*
|
11 |
+
* E.g. Content from DB with update time:
|
12 |
+
* <code>
|
13 |
+
* list($updateTime, $content) = getDbUpdateAndContent();
|
14 |
+
* $cg = new HTTP_ConditionalGet(array(
|
15 |
+
* 'lastModifiedTime' => $updateTime
|
16 |
+
* ,'isPublic' => true
|
17 |
+
* ));
|
18 |
+
* $cg->sendHeaders();
|
19 |
+
* if ($cg->cacheIsValid) {
|
20 |
+
* exit();
|
21 |
+
* }
|
22 |
+
* echo $content;
|
23 |
+
* </code>
|
24 |
+
*
|
25 |
+
* E.g. Shortcut for the above
|
26 |
+
* <code>
|
27 |
+
* HTTP_ConditionalGet::check($updateTime, true); // exits if client has cache
|
28 |
+
* echo $content;
|
29 |
+
* </code>
|
30 |
+
*
|
31 |
+
* E.g. Content from DB with no update time:
|
32 |
+
* <code>
|
33 |
+
* $content = getContentFromDB();
|
34 |
+
* $cg = new HTTP_ConditionalGet(array(
|
35 |
+
* 'contentHash' => md5($content)
|
36 |
+
* ));
|
37 |
+
* $cg->sendHeaders();
|
38 |
+
* if ($cg->cacheIsValid) {
|
39 |
+
* exit();
|
40 |
+
* }
|
41 |
+
* echo $content;
|
42 |
+
* </code>
|
43 |
+
*
|
44 |
+
* E.g. Static content with some static includes:
|
45 |
+
* <code>
|
46 |
+
* // before content
|
47 |
+
* $cg = new HTTP_ConditionalGet(array(
|
48 |
+
* 'lastUpdateTime' => max(
|
49 |
+
* filemtime(__FILE__)
|
50 |
+
* ,filemtime('/path/to/header.inc')
|
51 |
+
* ,filemtime('/path/to/footer.inc')
|
52 |
+
* )
|
53 |
+
* ));
|
54 |
+
* $cg->sendHeaders();
|
55 |
+
* if ($cg->cacheIsValid) {
|
56 |
+
* exit();
|
57 |
+
* }
|
58 |
+
* </code>
|
59 |
+
* @package Minify
|
60 |
+
* @subpackage HTTP
|
61 |
+
* @author Stephen Clay <steve@mrclay.org>
|
62 |
+
*/
|
63 |
+
class HTTP_ConditionalGet {
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Does the client have a valid copy of the requested resource?
|
67 |
+
*
|
68 |
+
* You'll want to check this after instantiating the object. If true, do
|
69 |
+
* not send content, just call sendHeaders() if you haven't already.
|
70 |
+
*
|
71 |
+
* @var bool
|
72 |
+
*/
|
73 |
+
public $cacheIsValid = null;
|
74 |
+
|
75 |
+
/**
|
76 |
+
* @param array $spec options
|
77 |
+
*
|
78 |
+
* 'isPublic': (bool) if false, the Cache-Control header will contain
|
79 |
+
* "private", allowing only browser caching. (default false)
|
80 |
+
*
|
81 |
+
* 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers
|
82 |
+
* will be sent with content. This is recommended.
|
83 |
+
*
|
84 |
+
* 'encoding': (string) if set, the header "Vary: Accept-Encoding" will
|
85 |
+
* always be sent and a truncated version of the encoding will be appended
|
86 |
+
* to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient
|
87 |
+
* checking of the client's If-None-Match header, as the encoding portion of
|
88 |
+
* the ETag will be stripped before comparison.
|
89 |
+
*
|
90 |
+
* 'contentHash': (string) if given, only the ETag header can be sent with
|
91 |
+
* content (only HTTP1.1 clients can conditionally GET). The given string
|
92 |
+
* should be short with no quote characters and always change when the
|
93 |
+
* resource changes (recommend md5()). This is not needed/used if
|
94 |
+
* lastModifiedTime is given.
|
95 |
+
*
|
96 |
+
* 'eTag': (string) if given, this will be used as the ETag header rather
|
97 |
+
* than values based on lastModifiedTime or contentHash. Also the encoding
|
98 |
+
* string will not be appended to the given value as described above.
|
99 |
+
*
|
100 |
+
* 'invalidate': (bool) if true, the client cache will be considered invalid
|
101 |
+
* without testing. Effectively this disables conditional GET.
|
102 |
+
* (default false)
|
103 |
+
*
|
104 |
+
* 'maxAge': (int) if given, this will set the Cache-Control max-age in
|
105 |
+
* seconds, and also set the Expires header to the equivalent GMT date.
|
106 |
+
* After the max-age period has passed, the browser will again send a
|
107 |
+
* conditional GET to revalidate its cache.
|
108 |
+
*/
|
109 |
+
public function __construct($spec)
|
110 |
+
{
|
111 |
+
$scope = (isset($spec['isPublic']) && $spec['isPublic'])
|
112 |
+
? 'public'
|
113 |
+
: 'private';
|
114 |
+
$maxAge = 0;
|
115 |
+
// backwards compatibility (can be removed later)
|
116 |
+
if (isset($spec['setExpires'])
|
117 |
+
&& is_numeric($spec['setExpires'])
|
118 |
+
&& ! isset($spec['maxAge'])) {
|
119 |
+
$spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME'];
|
120 |
+
}
|
121 |
+
if (isset($spec['maxAge'])) {
|
122 |
+
$maxAge = $spec['maxAge'];
|
123 |
+
$this->_headers['Expires'] = self::gmtDate(
|
124 |
+
$_SERVER['REQUEST_TIME'] + $spec['maxAge']
|
125 |
+
);
|
126 |
+
}
|
127 |
+
$etagAppend = '';
|
128 |
+
if (isset($spec['encoding'])) {
|
129 |
+
$this->_stripEtag = true;
|
130 |
+
$this->_headers['Vary'] = 'Accept-Encoding';
|
131 |
+
if ('' !== $spec['encoding']) {
|
132 |
+
if (0 === strpos($spec['encoding'], 'x-')) {
|
133 |
+
$spec['encoding'] = substr($spec['encoding'], 2);
|
134 |
+
}
|
135 |
+
$etagAppend = ';' . substr($spec['encoding'], 0, 2);
|
136 |
+
}
|
137 |
+
}
|
138 |
+
if (isset($spec['lastModifiedTime'])) {
|
139 |
+
$this->_setLastModified($spec['lastModifiedTime']);
|
140 |
+
if (isset($spec['eTag'])) { // Use it
|
141 |
+
$this->_setEtag($spec['eTag'], $scope);
|
142 |
+
} else { // base both headers on time
|
143 |
+
$this->_setEtag($spec['lastModifiedTime'] . $etagAppend, $scope);
|
144 |
+
}
|
145 |
+
} elseif (isset($spec['eTag'])) { // Use it
|
146 |
+
$this->_setEtag($spec['eTag'], $scope);
|
147 |
+
} elseif (isset($spec['contentHash'])) { // Use the hash as the ETag
|
148 |
+
$this->_setEtag($spec['contentHash'] . $etagAppend, $scope);
|
149 |
+
}
|
150 |
+
$privacy = ($scope === 'private')
|
151 |
+
? ', private'
|
152 |
+
: '';
|
153 |
+
$this->_headers['Cache-Control'] = "max-age={$maxAge}{$privacy}";
|
154 |
+
// invalidate cache if disabled, otherwise check
|
155 |
+
$this->cacheIsValid = (isset($spec['invalidate']) && $spec['invalidate'])
|
156 |
+
? false
|
157 |
+
: $this->_isCacheValid();
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Get array of output headers to be sent
|
162 |
+
*
|
163 |
+
* In the case of 304 responses, this array will only contain the response
|
164 |
+
* code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified')
|
165 |
+
*
|
166 |
+
* Otherwise something like:
|
167 |
+
* <code>
|
168 |
+
* array(
|
169 |
+
* 'Cache-Control' => 'max-age=0, public'
|
170 |
+
* ,'ETag' => '"foobar"'
|
171 |
+
* )
|
172 |
+
* </code>
|
173 |
+
*
|
174 |
+
* @return array
|
175 |
+
*/
|
176 |
+
public function getHeaders()
|
177 |
+
{
|
178 |
+
return $this->_headers;
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Set the Content-Length header in bytes
|
183 |
+
*
|
184 |
+
* With most PHP configs, as long as you don't flush() output, this method
|
185 |
+
* is not needed and PHP will buffer all output and set Content-Length for
|
186 |
+
* you. Otherwise you'll want to call this to let the client know up front.
|
187 |
+
*
|
188 |
+
* @param int $bytes
|
189 |
+
*
|
190 |
+
* @return int copy of input $bytes
|
191 |
+
*/
|
192 |
+
public function setContentLength($bytes)
|
193 |
+
{
|
194 |
+
return $this->_headers['Content-Length'] = $bytes;
|
195 |
+
}
|
196 |
+
|
197 |
+
/**
|
198 |
+
* Send headers
|
199 |
+
*
|
200 |
+
* @see getHeaders()
|
201 |
+
*
|
202 |
+
* Note this doesn't "clear" the headers. Calling sendHeaders() will
|
203 |
+
* call header() again (but probably have not effect) and getHeaders() will
|
204 |
+
* still return the headers.
|
205 |
+
*
|
206 |
+
* @return null
|
207 |
+
*/
|
208 |
+
public function sendHeaders()
|
209 |
+
{
|
210 |
+
$headers = $this->_headers;
|
211 |
+
if (array_key_exists('_responseCode', $headers)) {
|
212 |
+
// FastCGI environments require 3rd arg to header() to be set
|
213 |
+
list(, $code) = explode(' ', $headers['_responseCode'], 3);
|
214 |
+
header($headers['_responseCode'], true, $code);
|
215 |
+
unset($headers['_responseCode']);
|
216 |
+
}
|
217 |
+
foreach ($headers as $name => $val) {
|
218 |
+
header($name . ': ' . $val);
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Exit if the client's cache is valid for this resource
|
224 |
+
*
|
225 |
+
* This is a convenience method for common use of the class
|
226 |
+
*
|
227 |
+
* @param int $lastModifiedTime if given, both ETag AND Last-Modified headers
|
228 |
+
* will be sent with content. This is recommended.
|
229 |
+
*
|
230 |
+
* @param bool $isPublic (default false) if true, the Cache-Control header
|
231 |
+
* will contain "public", allowing proxies to cache the content. Otherwise
|
232 |
+
* "private" will be sent, allowing only browser caching.
|
233 |
+
*
|
234 |
+
* @param array $options (default empty) additional options for constructor
|
235 |
+
*/
|
236 |
+
public static function check($lastModifiedTime = null, $isPublic = false, $options = array())
|
237 |
+
{
|
238 |
+
if (null !== $lastModifiedTime) {
|
239 |
+
$options['lastModifiedTime'] = (int)$lastModifiedTime;
|
240 |
+
}
|
241 |
+
$options['isPublic'] = (bool)$isPublic;
|
242 |
+
$cg = new HTTP_ConditionalGet($options);
|
243 |
+
$cg->sendHeaders();
|
244 |
+
if ($cg->cacheIsValid) {
|
245 |
+
exit();
|
246 |
+
}
|
247 |
+
}
|
248 |
+
|
249 |
+
|
250 |
+
/**
|
251 |
+
* Get a GMT formatted date for use in HTTP headers
|
252 |
+
*
|
253 |
+
* <code>
|
254 |
+
* header('Expires: ' . HTTP_ConditionalGet::gmtdate($time));
|
255 |
+
* </code>
|
256 |
+
*
|
257 |
+
* @param int $time unix timestamp
|
258 |
+
*
|
259 |
+
* @return string
|
260 |
+
*/
|
261 |
+
public static function gmtDate($time)
|
262 |
+
{
|
263 |
+
return gmdate('D, d M Y H:i:s \G\M\T', $time);
|
264 |
+
}
|
265 |
+
|
266 |
+
protected $_headers = array();
|
267 |
+
protected $_lmTime = null;
|
268 |
+
protected $_etag = null;
|
269 |
+
protected $_stripEtag = false;
|
270 |
+
|
271 |
+
/**
|
272 |
+
* @param string $hash
|
273 |
+
*
|
274 |
+
* @param string $scope
|
275 |
+
*/
|
276 |
+
protected function _setEtag($hash, $scope)
|
277 |
+
{
|
278 |
+
/* BWP Minify - do not use ETag */
|
279 |
+
/* $this->_etag = '"' . substr($scope, 0, 3) . $hash . '"'; */
|
280 |
+
/* $this->_headers['ETag'] = $this->_etag; */
|
281 |
+
$this->_etag = '';
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* @param int $time
|
286 |
+
*/
|
287 |
+
protected function _setLastModified($time)
|
288 |
+
{
|
289 |
+
$this->_lmTime = (int)$time;
|
290 |
+
$this->_headers['Last-Modified'] = self::gmtDate($time);
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* Determine validity of client cache and queue 304 header if valid
|
295 |
+
*
|
296 |
+
* @return bool
|
297 |
+
*/
|
298 |
+
protected function _isCacheValid()
|
299 |
+
{
|
300 |
+
if (null === $this->_etag) {
|
301 |
+
// lmTime is copied to ETag, so this condition implies that the
|
302 |
+
// server sent neither ETag nor Last-Modified, so the client can't
|
303 |
+
// possibly has a valid cache.
|
304 |
+
return false;
|
305 |
+
}
|
306 |
+
$isValid = ($this->resourceMatchedEtag() || $this->resourceNotModified());
|
307 |
+
if ($isValid) {
|
308 |
+
$this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified';
|
309 |
+
}
|
310 |
+
return $isValid;
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* @return bool
|
315 |
+
*/
|
316 |
+
protected function resourceMatchedEtag()
|
317 |
+
{
|
318 |
+
if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
|
319 |
+
return false;
|
320 |
+
}
|
321 |
+
$clientEtagList = get_magic_quotes_gpc()
|
322 |
+
? stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])
|
323 |
+
: $_SERVER['HTTP_IF_NONE_MATCH'];
|
324 |
+
$clientEtags = explode(',', $clientEtagList);
|
325 |
+
|
326 |
+
$compareTo = $this->normalizeEtag($this->_etag);
|
327 |
+
foreach ($clientEtags as $clientEtag) {
|
328 |
+
if ($this->normalizeEtag($clientEtag) === $compareTo) {
|
329 |
+
// respond with the client's matched ETag, even if it's not what
|
330 |
+
// we would've sent by default
|
331 |
+
$this->_headers['ETag'] = trim($clientEtag);
|
332 |
+
return true;
|
333 |
+
}
|
334 |
+
}
|
335 |
+
return false;
|
336 |
+
}
|
337 |
+
|
338 |
+
/**
|
339 |
+
* @param string $etag
|
340 |
+
*
|
341 |
+
* @return string
|
342 |
+
*/
|
343 |
+
protected function normalizeEtag($etag) {
|
344 |
+
$etag = trim($etag);
|
345 |
+
return $this->_stripEtag
|
346 |
+
? preg_replace('/;\\w\\w"$/', '"', $etag)
|
347 |
+
: $etag;
|
348 |
+
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* @return bool
|
352 |
+
*/
|
353 |
+
protected function resourceNotModified()
|
354 |
+
{
|
355 |
+
if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
356 |
+
return false;
|
357 |
+
}
|
358 |
+
// strip off IE's extra data (semicolon)
|
359 |
+
list($ifModifiedSince) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'], 2);
|
360 |
+
if (strtotime($ifModifiedSince) >= $this->_lmTime) {
|
361 |
+
// Apache 2.2's behavior. If there was no ETag match, send the
|
362 |
+
// non-encoded version of the ETag value.
|
363 |
+
$this->_headers['ETag'] = $this->normalizeEtag($this->_etag);
|
364 |
+
return true;
|
365 |
+
}
|
366 |
+
return false;
|
367 |
+
}
|
368 |
+
}
|
min/lib/HTTP/Encoder.php
ADDED
@@ -0,0 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class HTTP_Encoder
|
4 |
+
* @package Minify
|
5 |
+
* @subpackage HTTP
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Encode and send gzipped/deflated content
|
10 |
+
*
|
11 |
+
* The "Vary: Accept-Encoding" header is sent. If the client allows encoding,
|
12 |
+
* Content-Encoding and Content-Length are added.
|
13 |
+
*
|
14 |
+
* <code>
|
15 |
+
* // Send a CSS file, compressed if possible
|
16 |
+
* $he = new HTTP_Encoder(array(
|
17 |
+
* 'content' => file_get_contents($cssFile)
|
18 |
+
* ,'type' => 'text/css'
|
19 |
+
* ));
|
20 |
+
* $he->encode();
|
21 |
+
* $he->sendAll();
|
22 |
+
* </code>
|
23 |
+
*
|
24 |
+
* <code>
|
25 |
+
* // Shortcut to encoding output
|
26 |
+
* header('Content-Type: text/css'); // needed if not HTML
|
27 |
+
* HTTP_Encoder::output($css);
|
28 |
+
* </code>
|
29 |
+
*
|
30 |
+
* <code>
|
31 |
+
* // Just sniff for the accepted encoding
|
32 |
+
* $encoding = HTTP_Encoder::getAcceptedEncoding();
|
33 |
+
* </code>
|
34 |
+
*
|
35 |
+
* For more control over headers, use getHeaders() and getData() and send your
|
36 |
+
* own output.
|
37 |
+
*
|
38 |
+
* Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate,
|
39 |
+
* and gzcompress functions for gzip, deflate, and compress-encoding
|
40 |
+
* respectively.
|
41 |
+
*
|
42 |
+
* @package Minify
|
43 |
+
* @subpackage HTTP
|
44 |
+
* @author Stephen Clay <steve@mrclay.org>
|
45 |
+
*/
|
46 |
+
class HTTP_Encoder {
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Should the encoder allow HTTP encoding to IE6?
|
50 |
+
*
|
51 |
+
* If you have many IE6 users and the bandwidth savings is worth troubling
|
52 |
+
* some of them, set this to true.
|
53 |
+
*
|
54 |
+
* By default, encoding is only offered to IE7+. When this is true,
|
55 |
+
* getAcceptedEncoding() will return an encoding for IE6 if its user agent
|
56 |
+
* string contains "SV1". This has been documented in many places as "safe",
|
57 |
+
* but there seem to be remaining, intermittent encoding bugs in patched
|
58 |
+
* IE6 on the wild web.
|
59 |
+
*
|
60 |
+
* @var bool
|
61 |
+
*/
|
62 |
+
public static $encodeToIe6 = true;
|
63 |
+
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Default compression level for zlib operations
|
67 |
+
*
|
68 |
+
* This level is used if encode() is not given a $compressionLevel
|
69 |
+
*
|
70 |
+
* @var int
|
71 |
+
*/
|
72 |
+
public static $compressionLevel = 6;
|
73 |
+
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Get an HTTP Encoder object
|
77 |
+
*
|
78 |
+
* @param array $spec options
|
79 |
+
*
|
80 |
+
* 'content': (string required) content to be encoded
|
81 |
+
*
|
82 |
+
* 'type': (string) if set, the Content-Type header will have this value.
|
83 |
+
*
|
84 |
+
* 'method: (string) only set this if you are forcing a particular encoding
|
85 |
+
* method. If not set, the best method will be chosen by getAcceptedEncoding()
|
86 |
+
* The available methods are 'gzip', 'deflate', 'compress', and '' (no
|
87 |
+
* encoding)
|
88 |
+
*/
|
89 |
+
public function __construct($spec)
|
90 |
+
{
|
91 |
+
$this->_useMbStrlen = (function_exists('mb_strlen')
|
92 |
+
&& (ini_get('mbstring.func_overload') !== '')
|
93 |
+
&& ((int)ini_get('mbstring.func_overload') & 2));
|
94 |
+
$this->_content = $spec['content'];
|
95 |
+
$this->_headers['Content-Length'] = $this->_useMbStrlen
|
96 |
+
? (string)mb_strlen($this->_content, '8bit')
|
97 |
+
: (string)strlen($this->_content);
|
98 |
+
if (isset($spec['type'])) {
|
99 |
+
$this->_headers['Content-Type'] = $spec['type'];
|
100 |
+
}
|
101 |
+
if (isset($spec['method'])
|
102 |
+
&& in_array($spec['method'], array('gzip', 'deflate', 'compress', '')))
|
103 |
+
{
|
104 |
+
$this->_encodeMethod = array($spec['method'], $spec['method']);
|
105 |
+
} else {
|
106 |
+
$this->_encodeMethod = self::getAcceptedEncoding();
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Get content in current form
|
112 |
+
*
|
113 |
+
* Call after encode() for encoded content.
|
114 |
+
*
|
115 |
+
* @return string
|
116 |
+
*/
|
117 |
+
public function getContent()
|
118 |
+
{
|
119 |
+
return $this->_content;
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Get array of output headers to be sent
|
124 |
+
*
|
125 |
+
* E.g.
|
126 |
+
* <code>
|
127 |
+
* array(
|
128 |
+
* 'Content-Length' => '615'
|
129 |
+
* ,'Content-Encoding' => 'x-gzip'
|
130 |
+
* ,'Vary' => 'Accept-Encoding'
|
131 |
+
* )
|
132 |
+
* </code>
|
133 |
+
*
|
134 |
+
* @return array
|
135 |
+
*/
|
136 |
+
public function getHeaders()
|
137 |
+
{
|
138 |
+
return $this->_headers;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Send output headers
|
143 |
+
*
|
144 |
+
* You must call this before headers are sent and it probably cannot be
|
145 |
+
* used in conjunction with zlib output buffering / mod_gzip. Errors are
|
146 |
+
* not handled purposefully.
|
147 |
+
*
|
148 |
+
* @see getHeaders()
|
149 |
+
*/
|
150 |
+
public function sendHeaders()
|
151 |
+
{
|
152 |
+
foreach ($this->_headers as $name => $val) {
|
153 |
+
header($name . ': ' . $val);
|
154 |
+
}
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Send output headers and content
|
159 |
+
*
|
160 |
+
* A shortcut for sendHeaders() and echo getContent()
|
161 |
+
*
|
162 |
+
* You must call this before headers are sent and it probably cannot be
|
163 |
+
* used in conjunction with zlib output buffering / mod_gzip. Errors are
|
164 |
+
* not handled purposefully.
|
165 |
+
*/
|
166 |
+
public function sendAll()
|
167 |
+
{
|
168 |
+
$this->sendHeaders();
|
169 |
+
echo $this->_content;
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Determine the client's best encoding method from the HTTP Accept-Encoding
|
174 |
+
* header.
|
175 |
+
*
|
176 |
+
* If no Accept-Encoding header is set, or the browser is IE before v6 SP2,
|
177 |
+
* this will return ('', ''), the "identity" encoding.
|
178 |
+
*
|
179 |
+
* A syntax-aware scan is done of the Accept-Encoding, so the method must
|
180 |
+
* be non 0. The methods are favored in order of gzip, deflate, then
|
181 |
+
* compress. Deflate is always smallest and generally faster, but is
|
182 |
+
* rarely sent by servers, so client support could be buggier.
|
183 |
+
*
|
184 |
+
* @param bool $allowCompress allow the older compress encoding
|
185 |
+
*
|
186 |
+
* @param bool $allowDeflate allow the more recent deflate encoding
|
187 |
+
*
|
188 |
+
* @return array two values, 1st is the actual encoding method, 2nd is the
|
189 |
+
* alias of that method to use in the Content-Encoding header (some browsers
|
190 |
+
* call gzip "x-gzip" etc.)
|
191 |
+
*/
|
192 |
+
public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true)
|
193 |
+
{
|
194 |
+
// @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
195 |
+
|
196 |
+
if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
|
197 |
+
|| self::isBuggyIe())
|
198 |
+
{
|
199 |
+
return array('', '');
|
200 |
+
}
|
201 |
+
$ae = $_SERVER['HTTP_ACCEPT_ENCODING'];
|
202 |
+
// gzip checks (quick)
|
203 |
+
if (0 === strpos($ae, 'gzip,') // most browsers
|
204 |
+
|| 0 === strpos($ae, 'deflate, gzip,') // opera
|
205 |
+
) {
|
206 |
+
return array('gzip', 'gzip');
|
207 |
+
}
|
208 |
+
// gzip checks (slow)
|
209 |
+
if (preg_match(
|
210 |
+
'@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
211 |
+
,$ae
|
212 |
+
,$m)) {
|
213 |
+
return array('gzip', $m[1]);
|
214 |
+
}
|
215 |
+
if ($allowDeflate) {
|
216 |
+
// deflate checks
|
217 |
+
$aeRev = strrev($ae);
|
218 |
+
if (0 === strpos($aeRev, 'etalfed ,') // ie, webkit
|
219 |
+
|| 0 === strpos($aeRev, 'etalfed,') // gecko
|
220 |
+
|| 0 === strpos($ae, 'deflate,') // opera
|
221 |
+
// slow parsing
|
222 |
+
|| preg_match(
|
223 |
+
'@(?:^|,)\\s*deflate\\s*(?:$|,|;\\s*q=(?:0\\.|1))@', $ae)) {
|
224 |
+
return array('deflate', 'deflate');
|
225 |
+
}
|
226 |
+
}
|
227 |
+
if ($allowCompress && preg_match(
|
228 |
+
'@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
229 |
+
,$ae
|
230 |
+
,$m)) {
|
231 |
+
return array('compress', $m[1]);
|
232 |
+
}
|
233 |
+
return array('', '');
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Encode (compress) the content
|
238 |
+
*
|
239 |
+
* If the encode method is '' (none) or compression level is 0, or the 'zlib'
|
240 |
+
* extension isn't loaded, we return false.
|
241 |
+
*
|
242 |
+
* Then the appropriate gz_* function is called to compress the content. If
|
243 |
+
* this fails, false is returned.
|
244 |
+
*
|
245 |
+
* The header "Vary: Accept-Encoding" is added. If encoding is successful,
|
246 |
+
* the Content-Length header is updated, and Content-Encoding is also added.
|
247 |
+
*
|
248 |
+
* @param int $compressionLevel given to zlib functions. If not given, the
|
249 |
+
* class default will be used.
|
250 |
+
*
|
251 |
+
* @return bool success true if the content was actually compressed
|
252 |
+
*/
|
253 |
+
public function encode($compressionLevel = null)
|
254 |
+
{
|
255 |
+
if (! self::isBuggyIe()) {
|
256 |
+
$this->_headers['Vary'] = 'Accept-Encoding';
|
257 |
+
}
|
258 |
+
if (null === $compressionLevel) {
|
259 |
+
$compressionLevel = self::$compressionLevel;
|
260 |
+
}
|
261 |
+
if ('' === $this->_encodeMethod[0]
|
262 |
+
|| ($compressionLevel == 0)
|
263 |
+
|| !extension_loaded('zlib'))
|
264 |
+
{
|
265 |
+
return false;
|
266 |
+
}
|
267 |
+
if ($this->_encodeMethod[0] === 'deflate') {
|
268 |
+
$encoded = gzdeflate($this->_content, $compressionLevel);
|
269 |
+
} elseif ($this->_encodeMethod[0] === 'gzip') {
|
270 |
+
$encoded = gzencode($this->_content, $compressionLevel);
|
271 |
+
} else {
|
272 |
+
$encoded = gzcompress($this->_content, $compressionLevel);
|
273 |
+
}
|
274 |
+
if (false === $encoded) {
|
275 |
+
return false;
|
276 |
+
}
|
277 |
+
$this->_headers['Content-Length'] = $this->_useMbStrlen
|
278 |
+
? (string)mb_strlen($encoded, '8bit')
|
279 |
+
: (string)strlen($encoded);
|
280 |
+
$this->_headers['Content-Encoding'] = $this->_encodeMethod[1];
|
281 |
+
$this->_content = $encoded;
|
282 |
+
return true;
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* Encode and send appropriate headers and content
|
287 |
+
*
|
288 |
+
* This is a convenience method for common use of the class
|
289 |
+
*
|
290 |
+
* @param string $content
|
291 |
+
*
|
292 |
+
* @param int $compressionLevel given to zlib functions. If not given, the
|
293 |
+
* class default will be used.
|
294 |
+
*
|
295 |
+
* @return bool success true if the content was actually compressed
|
296 |
+
*/
|
297 |
+
public static function output($content, $compressionLevel = null)
|
298 |
+
{
|
299 |
+
if (null === $compressionLevel) {
|
300 |
+
$compressionLevel = self::$compressionLevel;
|
301 |
+
}
|
302 |
+
$he = new HTTP_Encoder(array('content' => $content));
|
303 |
+
$ret = $he->encode($compressionLevel);
|
304 |
+
$he->sendAll();
|
305 |
+
return $ret;
|
306 |
+
}
|
307 |
+
|
308 |
+
/**
|
309 |
+
* Is the browser an IE version earlier than 6 SP2?
|
310 |
+
*
|
311 |
+
* @return bool
|
312 |
+
*/
|
313 |
+
public static function isBuggyIe()
|
314 |
+
{
|
315 |
+
if (empty($_SERVER['HTTP_USER_AGENT'])) {
|
316 |
+
return false;
|
317 |
+
}
|
318 |
+
$ua = $_SERVER['HTTP_USER_AGENT'];
|
319 |
+
// quick escape for non-IEs
|
320 |
+
if (0 !== strpos($ua, 'Mozilla/4.0 (compatible; MSIE ')
|
321 |
+
|| false !== strpos($ua, 'Opera')) {
|
322 |
+
return false;
|
323 |
+
}
|
324 |
+
// no regex = faaast
|
325 |
+
$version = (float)substr($ua, 30);
|
326 |
+
return self::$encodeToIe6
|
327 |
+
? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1')))
|
328 |
+
: ($version < 7);
|
329 |
+
}
|
330 |
+
|
331 |
+
protected $_content = '';
|
332 |
+
protected $_headers = array();
|
333 |
+
protected $_encodeMethod = array('', '');
|
334 |
+
protected $_useMbStrlen = false;
|
335 |
+
}
|
min/lib/JSMin.php
ADDED
@@ -0,0 +1,437 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
|
4 |
+
*
|
5 |
+
* <code>
|
6 |
+
* $minifiedJs = JSMin::minify($js);
|
7 |
+
* </code>
|
8 |
+
*
|
9 |
+
* This is a modified port of jsmin.c. Improvements:
|
10 |
+
*
|
11 |
+
* Does not choke on some regexp literals containing quote characters. E.g. /'/
|
12 |
+
*
|
13 |
+
* Spaces are preserved after some add/sub operators, so they are not mistakenly
|
14 |
+
* converted to post-inc/dec. E.g. a + ++b -> a+ ++b
|
15 |
+
*
|
16 |
+
* Preserves multi-line comments that begin with /*!
|
17 |
+
*
|
18 |
+
* PHP 5 or higher is required.
|
19 |
+
*
|
20 |
+
* Permission is hereby granted to use this version of the library under the
|
21 |
+
* same terms as jsmin.c, which has the following license:
|
22 |
+
*
|
23 |
+
* --
|
24 |
+
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
25 |
+
*
|
26 |
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
27 |
+
* this software and associated documentation files (the "Software"), to deal in
|
28 |
+
* the Software without restriction, including without limitation the rights to
|
29 |
+
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
30 |
+
* of the Software, and to permit persons to whom the Software is furnished to do
|
31 |
+
* so, subject to the following conditions:
|
32 |
+
*
|
33 |
+
* The above copyright notice and this permission notice shall be included in all
|
34 |
+
* copies or substantial portions of the Software.
|
35 |
+
*
|
36 |
+
* The Software shall be used for Good, not Evil.
|
37 |
+
*
|
38 |
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
39 |
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
40 |
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
41 |
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
42 |
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
43 |
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
44 |
+
* SOFTWARE.
|
45 |
+
* --
|
46 |
+
*
|
47 |
+
* @package JSMin
|
48 |
+
* @author Ryan Grove <ryan@wonko.com> (PHP port)
|
49 |
+
* @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
|
50 |
+
* @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
|
51 |
+
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
|
52 |
+
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
|
53 |
+
* @license http://opensource.org/licenses/mit-license.php MIT License
|
54 |
+
* @link http://code.google.com/p/jsmin-php/
|
55 |
+
*/
|
56 |
+
|
57 |
+
class JSMin {
|
58 |
+
const ORD_LF = 10;
|
59 |
+
const ORD_SPACE = 32;
|
60 |
+
const ACTION_KEEP_A = 1;
|
61 |
+
const ACTION_DELETE_A = 2;
|
62 |
+
const ACTION_DELETE_A_B = 3;
|
63 |
+
|
64 |
+
protected $a = "\n";
|
65 |
+
protected $b = '';
|
66 |
+
protected $input = '';
|
67 |
+
protected $inputIndex = 0;
|
68 |
+
protected $inputLength = 0;
|
69 |
+
protected $lookAhead = null;
|
70 |
+
protected $output = '';
|
71 |
+
protected $lastByteOut = '';
|
72 |
+
protected $keptComment = '';
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Minify Javascript.
|
76 |
+
*
|
77 |
+
* @param string $js Javascript to be minified
|
78 |
+
*
|
79 |
+
* @return string
|
80 |
+
*/
|
81 |
+
public static function minify($js)
|
82 |
+
{
|
83 |
+
$jsmin = new JSMin($js);
|
84 |
+
return $jsmin->min();
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* @param string $input
|
89 |
+
*/
|
90 |
+
public function __construct($input)
|
91 |
+
{
|
92 |
+
$this->input = $input;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Perform minification, return result
|
97 |
+
*
|
98 |
+
* @return string
|
99 |
+
*/
|
100 |
+
public function min()
|
101 |
+
{
|
102 |
+
if ($this->output !== '') { // min already run
|
103 |
+
return $this->output;
|
104 |
+
}
|
105 |
+
|
106 |
+
$mbIntEnc = null;
|
107 |
+
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
|
108 |
+
$mbIntEnc = mb_internal_encoding();
|
109 |
+
mb_internal_encoding('8bit');
|
110 |
+
}
|
111 |
+
$this->input = str_replace("\r\n", "\n", $this->input);
|
112 |
+
$this->inputLength = strlen($this->input);
|
113 |
+
|
114 |
+
$this->action(self::ACTION_DELETE_A_B);
|
115 |
+
|
116 |
+
while ($this->a !== null) {
|
117 |
+
// determine next command
|
118 |
+
$command = self::ACTION_KEEP_A; // default
|
119 |
+
if ($this->a === ' ') {
|
120 |
+
if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
|
121 |
+
&& ($this->b === $this->lastByteOut)) {
|
122 |
+
// Don't delete this space. If we do, the addition/subtraction
|
123 |
+
// could be parsed as a post-increment
|
124 |
+
} elseif (! $this->isAlphaNum($this->b)) {
|
125 |
+
$command = self::ACTION_DELETE_A;
|
126 |
+
}
|
127 |
+
} elseif ($this->a === "\n") {
|
128 |
+
if ($this->b === ' ') {
|
129 |
+
$command = self::ACTION_DELETE_A_B;
|
130 |
+
|
131 |
+
// in case of mbstring.func_overload & 2, must check for null b,
|
132 |
+
// otherwise mb_strpos will give WARNING
|
133 |
+
} elseif ($this->b === null
|
134 |
+
|| (false === strpos('{[(+-!~', $this->b)
|
135 |
+
&& ! $this->isAlphaNum($this->b))) {
|
136 |
+
$command = self::ACTION_DELETE_A;
|
137 |
+
}
|
138 |
+
} elseif (! $this->isAlphaNum($this->a)) {
|
139 |
+
if ($this->b === ' '
|
140 |
+
|| ($this->b === "\n"
|
141 |
+
&& (false === strpos('}])+-"\'', $this->a)))) {
|
142 |
+
$command = self::ACTION_DELETE_A_B;
|
143 |
+
}
|
144 |
+
}
|
145 |
+
$this->action($command);
|
146 |
+
}
|
147 |
+
$this->output = trim($this->output);
|
148 |
+
|
149 |
+
if ($mbIntEnc !== null) {
|
150 |
+
mb_internal_encoding($mbIntEnc);
|
151 |
+
}
|
152 |
+
return $this->output;
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
|
157 |
+
* ACTION_DELETE_A = Copy B to A. Get the next B.
|
158 |
+
* ACTION_DELETE_A_B = Get the next B.
|
159 |
+
*
|
160 |
+
* @param int $command
|
161 |
+
* @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
|
162 |
+
*/
|
163 |
+
protected function action($command)
|
164 |
+
{
|
165 |
+
// make sure we don't compress "a + ++b" to "a+++b", etc.
|
166 |
+
if ($command === self::ACTION_DELETE_A_B
|
167 |
+
&& $this->b === ' '
|
168 |
+
&& ($this->a === '+' || $this->a === '-')) {
|
169 |
+
// Note: we're at an addition/substraction operator; the inputIndex
|
170 |
+
// will certainly be a valid index
|
171 |
+
if ($this->input[$this->inputIndex] === $this->a) {
|
172 |
+
// This is "+ +" or "- -". Don't delete the space.
|
173 |
+
$command = self::ACTION_KEEP_A;
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
switch ($command) {
|
178 |
+
case self::ACTION_KEEP_A: // 1
|
179 |
+
$this->output .= $this->a;
|
180 |
+
|
181 |
+
if ($this->keptComment) {
|
182 |
+
$this->output = rtrim($this->output, "\n");
|
183 |
+
$this->output .= $this->keptComment;
|
184 |
+
$this->keptComment = '';
|
185 |
+
}
|
186 |
+
|
187 |
+
$this->lastByteOut = $this->a;
|
188 |
+
|
189 |
+
// fallthrough intentional
|
190 |
+
case self::ACTION_DELETE_A: // 2
|
191 |
+
$this->a = $this->b;
|
192 |
+
if ($this->a === "'" || $this->a === '"') { // string literal
|
193 |
+
$str = $this->a; // in case needed for exception
|
194 |
+
for(;;) {
|
195 |
+
$this->output .= $this->a;
|
196 |
+
$this->lastByteOut = $this->a;
|
197 |
+
|
198 |
+
$this->a = $this->get();
|
199 |
+
if ($this->a === $this->b) { // end quote
|
200 |
+
break;
|
201 |
+
}
|
202 |
+
if ($this->isEOF($this->a)) {
|
203 |
+
throw new JSMin_UnterminatedStringException(
|
204 |
+
"JSMin: Unterminated String at byte {$this->inputIndex}: {$str}");
|
205 |
+
}
|
206 |
+
$str .= $this->a;
|
207 |
+
if ($this->a === '\\') {
|
208 |
+
$this->output .= $this->a;
|
209 |
+
$this->lastByteOut = $this->a;
|
210 |
+
|
211 |
+
$this->a = $this->get();
|
212 |
+
$str .= $this->a;
|
213 |
+
}
|
214 |
+
}
|
215 |
+
}
|
216 |
+
|
217 |
+
// fallthrough intentional
|
218 |
+
case self::ACTION_DELETE_A_B: // 3
|
219 |
+
$this->b = $this->next();
|
220 |
+
if ($this->b === '/' && $this->isRegexpLiteral()) {
|
221 |
+
$this->output .= $this->a . $this->b;
|
222 |
+
$pattern = '/'; // keep entire pattern in case we need to report it in the exception
|
223 |
+
for(;;) {
|
224 |
+
$this->a = $this->get();
|
225 |
+
$pattern .= $this->a;
|
226 |
+
if ($this->a === '[') {
|
227 |
+
for(;;) {
|
228 |
+
$this->output .= $this->a;
|
229 |
+
$this->a = $this->get();
|
230 |
+
$pattern .= $this->a;
|
231 |
+
if ($this->a === ']') {
|
232 |
+
break;
|
233 |
+
}
|
234 |
+
if ($this->a === '\\') {
|
235 |
+
$this->output .= $this->a;
|
236 |
+
$this->a = $this->get();
|
237 |
+
$pattern .= $this->a;
|
238 |
+
}
|
239 |
+
if ($this->isEOF($this->a)) {
|
240 |
+
throw new JSMin_UnterminatedRegExpException(
|
241 |
+
"JSMin: Unterminated set in RegExp at byte "
|
242 |
+
. $this->inputIndex .": {$pattern}");
|
243 |
+
}
|
244 |
+
}
|
245 |
+
}
|
246 |
+
|
247 |
+
if ($this->a === '/') { // end pattern
|
248 |
+
break; // while (true)
|
249 |
+
} elseif ($this->a === '\\') {
|
250 |
+
$this->output .= $this->a;
|
251 |
+
$this->a = $this->get();
|
252 |
+
$pattern .= $this->a;
|
253 |
+
} elseif ($this->isEOF($this->a)) {
|
254 |
+
throw new JSMin_UnterminatedRegExpException(
|
255 |
+
"JSMin: Unterminated RegExp at byte {$this->inputIndex}: {$pattern}");
|
256 |
+
}
|
257 |
+
$this->output .= $this->a;
|
258 |
+
$this->lastByteOut = $this->a;
|
259 |
+
}
|
260 |
+
$this->b = $this->next();
|
261 |
+
}
|
262 |
+
// end case ACTION_DELETE_A_B
|
263 |
+
}
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* @return bool
|
268 |
+
*/
|
269 |
+
protected function isRegexpLiteral()
|
270 |
+
{
|
271 |
+
if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
|
272 |
+
// we obviously aren't dividing
|
273 |
+
return true;
|
274 |
+
}
|
275 |
+
if ($this->a === ' ' || $this->a === "\n") {
|
276 |
+
$length = strlen($this->output);
|
277 |
+
if ($length < 2) { // weird edge case
|
278 |
+
return true;
|
279 |
+
}
|
280 |
+
// you can't divide a keyword
|
281 |
+
if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
|
282 |
+
if ($this->output === $m[0]) { // odd but could happen
|
283 |
+
return true;
|
284 |
+
}
|
285 |
+
// make sure it's a keyword, not end of an identifier
|
286 |
+
$charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
|
287 |
+
if (! $this->isAlphaNum($charBeforeKeyword)) {
|
288 |
+
return true;
|
289 |
+
}
|
290 |
+
}
|
291 |
+
}
|
292 |
+
return false;
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Return the next character from stdin. Watch out for lookahead. If the character is a control character,
|
297 |
+
* translate it to a space or linefeed.
|
298 |
+
*
|
299 |
+
* @return string
|
300 |
+
*/
|
301 |
+
protected function get()
|
302 |
+
{
|
303 |
+
$c = $this->lookAhead;
|
304 |
+
$this->lookAhead = null;
|
305 |
+
if ($c === null) {
|
306 |
+
// getc(stdin)
|
307 |
+
if ($this->inputIndex < $this->inputLength) {
|
308 |
+
$c = $this->input[$this->inputIndex];
|
309 |
+
$this->inputIndex += 1;
|
310 |
+
} else {
|
311 |
+
$c = null;
|
312 |
+
}
|
313 |
+
}
|
314 |
+
if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
|
315 |
+
return $c;
|
316 |
+
}
|
317 |
+
if ($c === "\r") {
|
318 |
+
return "\n";
|
319 |
+
}
|
320 |
+
return ' ';
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Does $a indicate end of input?
|
325 |
+
*
|
326 |
+
* @param string $a
|
327 |
+
* @return bool
|
328 |
+
*/
|
329 |
+
protected function isEOF($a)
|
330 |
+
{
|
331 |
+
return ord($a) <= self::ORD_LF;
|
332 |
+
}
|
333 |
+
|
334 |
+
/**
|
335 |
+
* Get next char (without getting it). If is ctrl character, translate to a space or newline.
|
336 |
+
*
|
337 |
+
* @return string
|
338 |
+
*/
|
339 |
+
protected function peek()
|
340 |
+
{
|
341 |
+
$this->lookAhead = $this->get();
|
342 |
+
return $this->lookAhead;
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
|
347 |
+
*
|
348 |
+
* @param string $c
|
349 |
+
*
|
350 |
+
* @return bool
|
351 |
+
*/
|
352 |
+
protected function isAlphaNum($c)
|
353 |
+
{
|
354 |
+
return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* Consume a single line comment from input (possibly retaining it)
|
359 |
+
*/
|
360 |
+
protected function consumeSingleLineComment()
|
361 |
+
{
|
362 |
+
$comment = '';
|
363 |
+
while (true) {
|
364 |
+
$get = $this->get();
|
365 |
+
$comment .= $get;
|
366 |
+
if (ord($get) <= self::ORD_LF) { // end of line reached
|
367 |
+
// if IE conditional comment
|
368 |
+
if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
|
369 |
+
$this->keptComment .= "/{$comment}";
|
370 |
+
}
|
371 |
+
return;
|
372 |
+
}
|
373 |
+
}
|
374 |
+
}
|
375 |
+
|
376 |
+
/**
|
377 |
+
* Consume a multiple line comment from input (possibly retaining it)
|
378 |
+
*
|
379 |
+
* @throws JSMin_UnterminatedCommentException
|
380 |
+
*/
|
381 |
+
protected function consumeMultipleLineComment()
|
382 |
+
{
|
383 |
+
$this->get();
|
384 |
+
$comment = '';
|
385 |
+
for(;;) {
|
386 |
+
$get = $this->get();
|
387 |
+
if ($get === '*') {
|
388 |
+
if ($this->peek() === '/') { // end of comment reached
|
389 |
+
$this->get();
|
390 |
+
if (0 === strpos($comment, '!')) {
|
391 |
+
// preserved by YUI Compressor
|
392 |
+
if (!$this->keptComment) {
|
393 |
+
// don't prepend a newline if two comments right after one another
|
394 |
+
$this->keptComment = "\n";
|
395 |
+
}
|
396 |
+
$this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
|
397 |
+
} else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
|
398 |
+
// IE conditional
|
399 |
+
$this->keptComment .= "/*{$comment}*/";
|
400 |
+
}
|
401 |
+
return;
|
402 |
+
}
|
403 |
+
} elseif ($get === null) {
|
404 |
+
throw new JSMin_UnterminatedCommentException(
|
405 |
+
"JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
|
406 |
+
}
|
407 |
+
$comment .= $get;
|
408 |
+
}
|
409 |
+
}
|
410 |
+
|
411 |
+
/**
|
412 |
+
* Get the next character, skipping over comments. Some comments may be preserved.
|
413 |
+
*
|
414 |
+
* @return string
|
415 |
+
*/
|
416 |
+
protected function next()
|
417 |
+
{
|
418 |
+
$get = $this->get();
|
419 |
+
if ($get === '/') {
|
420 |
+
switch ($this->peek()) {
|
421 |
+
case '/':
|
422 |
+
$this->consumeSingleLineComment();
|
423 |
+
$get = "\n";
|
424 |
+
break;
|
425 |
+
case '*':
|
426 |
+
$this->consumeMultipleLineComment();
|
427 |
+
$get = ' ';
|
428 |
+
break;
|
429 |
+
}
|
430 |
+
}
|
431 |
+
return $get;
|
432 |
+
}
|
433 |
+
}
|
434 |
+
|
435 |
+
class JSMin_UnterminatedStringException extends Exception {}
|
436 |
+
class JSMin_UnterminatedCommentException extends Exception {}
|
437 |
+
class JSMin_UnterminatedRegExpException extends Exception {}
|
min/lib/JSMinPlus.php
ADDED
@@ -0,0 +1,2086 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* JSMinPlus version 1.4
|
5 |
+
*
|
6 |
+
* Minifies a javascript file using a javascript parser
|
7 |
+
*
|
8 |
+
* This implements a PHP port of Brendan Eich's Narcissus open source javascript engine (in javascript)
|
9 |
+
* References: http://en.wikipedia.org/wiki/Narcissus_(JavaScript_engine)
|
10 |
+
* Narcissus sourcecode: http://mxr.mozilla.org/mozilla/source/js/narcissus/
|
11 |
+
* JSMinPlus weblog: http://crisp.tweakblogs.net/blog/cat/716
|
12 |
+
*
|
13 |
+
* Tino Zijdel <crisp@tweakers.net>
|
14 |
+
*
|
15 |
+
* Usage: $minified = JSMinPlus::minify($script [, $filename])
|
16 |
+
*
|
17 |
+
* Versionlog (see also changelog.txt):
|
18 |
+
* 23-07-2011 - remove dynamic creation of OP_* and KEYWORD_* defines and declare them on top
|
19 |
+
* reduce memory footprint by minifying by block-scope
|
20 |
+
* some small byte-saving and performance improvements
|
21 |
+
* 12-05-2009 - fixed hook:colon precedence, fixed empty body in loop and if-constructs
|
22 |
+
* 18-04-2009 - fixed crashbug in PHP 5.2.9 and several other bugfixes
|
23 |
+
* 12-04-2009 - some small bugfixes and performance improvements
|
24 |
+
* 09-04-2009 - initial open sourced version 1.0
|
25 |
+
*
|
26 |
+
* Latest version of this script: http://files.tweakers.net/jsminplus/jsminplus.zip
|
27 |
+
*
|
28 |
+
*/
|
29 |
+
|
30 |
+
/* ***** BEGIN LICENSE BLOCK *****
|
31 |
+
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
32 |
+
*
|
33 |
+
* The contents of this file are subject to the Mozilla Public License Version
|
34 |
+
* 1.1 (the "License"); you may not use this file except in compliance with
|
35 |
+
* the License. You may obtain a copy of the License at
|
36 |
+
* http://www.mozilla.org/MPL/
|
37 |
+
*
|
38 |
+
* Software distributed under the License is distributed on an "AS IS" basis,
|
39 |
+
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
40 |
+
* for the specific language governing rights and limitations under the
|
41 |
+
* License.
|
42 |
+
*
|
43 |
+
* The Original Code is the Narcissus JavaScript engine.
|
44 |
+
*
|
45 |
+
* The Initial Developer of the Original Code is
|
46 |
+
* Brendan Eich <brendan@mozilla.org>.
|
47 |
+
* Portions created by the Initial Developer are Copyright (C) 2004
|
48 |
+
* the Initial Developer. All Rights Reserved.
|
49 |
+
*
|
50 |
+
* Contributor(s): Tino Zijdel <crisp@tweakers.net>
|
51 |
+
* PHP port, modifications and minifier routine are (C) 2009-2011
|
52 |
+
*
|
53 |
+
* Alternatively, the contents of this file may be used under the terms of
|
54 |
+
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
55 |
+
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
56 |
+
* in which case the provisions of the GPL or the LGPL are applicable instead
|
57 |
+
* of those above. If you wish to allow use of your version of this file only
|
58 |
+
* under the terms of either the GPL or the LGPL, and not to allow others to
|
59 |
+
* use your version of this file under the terms of the MPL, indicate your
|
60 |
+
* decision by deleting the provisions above and replace them with the notice
|
61 |
+
* and other provisions required by the GPL or the LGPL. If you do not delete
|
62 |
+
* the provisions above, a recipient may use your version of this file under
|
63 |
+
* the terms of any one of the MPL, the GPL or the LGPL.
|
64 |
+
*
|
65 |
+
* ***** END LICENSE BLOCK ***** */
|
66 |
+
|
67 |
+
define('TOKEN_END', 1);
|
68 |
+
define('TOKEN_NUMBER', 2);
|
69 |
+
define('TOKEN_IDENTIFIER', 3);
|
70 |
+
define('TOKEN_STRING', 4);
|
71 |
+
define('TOKEN_REGEXP', 5);
|
72 |
+
define('TOKEN_NEWLINE', 6);
|
73 |
+
define('TOKEN_CONDCOMMENT_START', 7);
|
74 |
+
define('TOKEN_CONDCOMMENT_END', 8);
|
75 |
+
|
76 |
+
define('JS_SCRIPT', 100);
|
77 |
+
define('JS_BLOCK', 101);
|
78 |
+
define('JS_LABEL', 102);
|
79 |
+
define('JS_FOR_IN', 103);
|
80 |
+
define('JS_CALL', 104);
|
81 |
+
define('JS_NEW_WITH_ARGS', 105);
|
82 |
+
define('JS_INDEX', 106);
|
83 |
+
define('JS_ARRAY_INIT', 107);
|
84 |
+
define('JS_OBJECT_INIT', 108);
|
85 |
+
define('JS_PROPERTY_INIT', 109);
|
86 |
+
define('JS_GETTER', 110);
|
87 |
+
define('JS_SETTER', 111);
|
88 |
+
define('JS_GROUP', 112);
|
89 |
+
define('JS_LIST', 113);
|
90 |
+
|
91 |
+
define('JS_MINIFIED', 999);
|
92 |
+
|
93 |
+
define('DECLARED_FORM', 0);
|
94 |
+
define('EXPRESSED_FORM', 1);
|
95 |
+
define('STATEMENT_FORM', 2);
|
96 |
+
|
97 |
+
/* Operators */
|
98 |
+
define('OP_SEMICOLON', ';');
|
99 |
+
define('OP_COMMA', ',');
|
100 |
+
define('OP_HOOK', '?');
|
101 |
+
define('OP_COLON', ':');
|
102 |
+
define('OP_OR', '||');
|
103 |
+
define('OP_AND', '&&');
|
104 |
+
define('OP_BITWISE_OR', '|');
|
105 |
+
define('OP_BITWISE_XOR', '^');
|
106 |
+
define('OP_BITWISE_AND', '&');
|
107 |
+
define('OP_STRICT_EQ', '===');
|
108 |
+
define('OP_EQ', '==');
|
109 |
+
define('OP_ASSIGN', '=');
|
110 |
+
define('OP_STRICT_NE', '!==');
|
111 |
+
define('OP_NE', '!=');
|
112 |
+
define('OP_LSH', '<<');
|
113 |
+
define('OP_LE', '<=');
|
114 |
+
define('OP_LT', '<');
|
115 |
+
define('OP_URSH', '>>>');
|
116 |
+
define('OP_RSH', '>>');
|
117 |
+
define('OP_GE', '>=');
|
118 |
+
define('OP_GT', '>');
|
119 |
+
define('OP_INCREMENT', '++');
|
120 |
+
define('OP_DECREMENT', '--');
|
121 |
+
define('OP_PLUS', '+');
|
122 |
+
define('OP_MINUS', '-');
|
123 |
+
define('OP_MUL', '*');
|
124 |
+
define('OP_DIV', '/');
|
125 |
+
define('OP_MOD', '%');
|
126 |
+
define('OP_NOT', '!');
|
127 |
+
define('OP_BITWISE_NOT', '~');
|
128 |
+
define('OP_DOT', '.');
|
129 |
+
define('OP_LEFT_BRACKET', '[');
|
130 |
+
define('OP_RIGHT_BRACKET', ']');
|
131 |
+
define('OP_LEFT_CURLY', '{');
|
132 |
+
define('OP_RIGHT_CURLY', '}');
|
133 |
+
define('OP_LEFT_PAREN', '(');
|
134 |
+
define('OP_RIGHT_PAREN', ')');
|
135 |
+
define('OP_CONDCOMMENT_END', '@*/');
|
136 |
+
|
137 |
+
define('OP_UNARY_PLUS', 'U+');
|
138 |
+
define('OP_UNARY_MINUS', 'U-');
|
139 |
+
|
140 |
+
/* Keywords */
|
141 |
+
define('KEYWORD_BREAK', 'break');
|
142 |
+
define('KEYWORD_CASE', 'case');
|
143 |
+
define('KEYWORD_CATCH', 'catch');
|
144 |
+
define('KEYWORD_CONST', 'const');
|
145 |
+
define('KEYWORD_CONTINUE', 'continue');
|
146 |
+
define('KEYWORD_DEBUGGER', 'debugger');
|
147 |
+
define('KEYWORD_DEFAULT', 'default');
|
148 |
+
define('KEYWORD_DELETE', 'delete');
|
149 |
+
define('KEYWORD_DO', 'do');
|
150 |
+
define('KEYWORD_ELSE', 'else');
|
151 |
+
define('KEYWORD_ENUM', 'enum');
|
152 |
+
define('KEYWORD_FALSE', 'false');
|
153 |
+
define('KEYWORD_FINALLY', 'finally');
|
154 |
+
define('KEYWORD_FOR', 'for');
|
155 |
+
define('KEYWORD_FUNCTION', 'function');
|
156 |
+
define('KEYWORD_IF', 'if');
|
157 |
+
define('KEYWORD_IN', 'in');
|
158 |
+
define('KEYWORD_INSTANCEOF', 'instanceof');
|
159 |
+
define('KEYWORD_NEW', 'new');
|
160 |
+
define('KEYWORD_NULL', 'null');
|
161 |
+
define('KEYWORD_RETURN', 'return');
|
162 |
+
define('KEYWORD_SWITCH', 'switch');
|
163 |
+
define('KEYWORD_THIS', 'this');
|
164 |
+
define('KEYWORD_THROW', 'throw');
|
165 |
+
define('KEYWORD_TRUE', 'true');
|
166 |
+
define('KEYWORD_TRY', 'try');
|
167 |
+
define('KEYWORD_TYPEOF', 'typeof');
|
168 |
+
define('KEYWORD_VAR', 'var');
|
169 |
+
define('KEYWORD_VOID', 'void');
|
170 |
+
define('KEYWORD_WHILE', 'while');
|
171 |
+
define('KEYWORD_WITH', 'with');
|
172 |
+
|
173 |
+
|
174 |
+
class JSMinPlus
|
175 |
+
{
|
176 |
+
private $parser;
|
177 |
+
private $reserved = array(
|
178 |
+
'break', 'case', 'catch', 'continue', 'default', 'delete', 'do',
|
179 |
+
'else', 'finally', 'for', 'function', 'if', 'in', 'instanceof',
|
180 |
+
'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var',
|
181 |
+
'void', 'while', 'with',
|
182 |
+
// Words reserved for future use
|
183 |
+
'abstract', 'boolean', 'byte', 'char', 'class', 'const', 'debugger',
|
184 |
+
'double', 'enum', 'export', 'extends', 'final', 'float', 'goto',
|
185 |
+
'implements', 'import', 'int', 'interface', 'long', 'native',
|
186 |
+
'package', 'private', 'protected', 'public', 'short', 'static',
|
187 |
+
'super', 'synchronized', 'throws', 'transient', 'volatile',
|
188 |
+
// These are not reserved, but should be taken into account
|
189 |
+
// in isValidIdentifier (See jslint source code)
|
190 |
+
'arguments', 'eval', 'true', 'false', 'Infinity', 'NaN', 'null', 'undefined'
|
191 |
+
);
|
192 |
+
|
193 |
+
private function __construct()
|
194 |
+
{
|
195 |
+
$this->parser = new JSParser($this);
|
196 |
+
}
|
197 |
+
|
198 |
+
public static function minify($js, $filename='')
|
199 |
+
{
|
200 |
+
static $instance;
|
201 |
+
|
202 |
+
// this is a singleton
|
203 |
+
if(!$instance)
|
204 |
+
$instance = new JSMinPlus();
|
205 |
+
|
206 |
+
return $instance->min($js, $filename);
|
207 |
+
}
|
208 |
+
|
209 |
+
private function min($js, $filename)
|
210 |
+
{
|
211 |
+
try
|
212 |
+
{
|
213 |
+
$n = $this->parser->parse($js, $filename, 1);
|
214 |
+
return $this->parseTree($n);
|
215 |
+
}
|
216 |
+
catch(Exception $e)
|
217 |
+
{
|
218 |
+
echo $e->getMessage() . "\n";
|
219 |
+
}
|
220 |
+
|
221 |
+
return false;
|
222 |
+
}
|
223 |
+
|
224 |
+
public function parseTree($n, $noBlockGrouping = false)
|
225 |
+
{
|
226 |
+
$s = '';
|
227 |
+
|
228 |
+
switch ($n->type)
|
229 |
+
{
|
230 |
+
case JS_MINIFIED:
|
231 |
+
$s = $n->value;
|
232 |
+
break;
|
233 |
+
|
234 |
+
case JS_SCRIPT:
|
235 |
+
// we do nothing yet with funDecls or varDecls
|
236 |
+
$noBlockGrouping = true;
|
237 |
+
// FALL THROUGH
|
238 |
+
|
239 |
+
case JS_BLOCK:
|
240 |
+
$childs = $n->treeNodes;
|
241 |
+
$lastType = 0;
|
242 |
+
for ($c = 0, $i = 0, $j = count($childs); $i < $j; $i++)
|
243 |
+
{
|
244 |
+
$type = $childs[$i]->type;
|
245 |
+
$t = $this->parseTree($childs[$i]);
|
246 |
+
if (strlen($t))
|
247 |
+
{
|
248 |
+
if ($c)
|
249 |
+
{
|
250 |
+
$s = rtrim($s, ';');
|
251 |
+
|
252 |
+
if ($type == KEYWORD_FUNCTION && $childs[$i]->functionForm == DECLARED_FORM)
|
253 |
+
{
|
254 |
+
// put declared functions on a new line
|
255 |
+
$s .= "\n";
|
256 |
+
}
|
257 |
+
elseif ($type == KEYWORD_VAR && $type == $lastType)
|
258 |
+
{
|
259 |
+
// mutiple var-statements can go into one
|
260 |
+
$t = ',' . substr($t, 4);
|
261 |
+
}
|
262 |
+
else
|
263 |
+
{
|
264 |
+
// add terminator
|
265 |
+
$s .= ';';
|
266 |
+
}
|
267 |
+
}
|
268 |
+
|
269 |
+
$s .= $t;
|
270 |
+
|
271 |
+
$c++;
|
272 |
+
$lastType = $type;
|
273 |
+
}
|
274 |
+
}
|
275 |
+
|
276 |
+
if ($c > 1 && !$noBlockGrouping)
|
277 |
+
{
|
278 |
+
$s = '{' . $s . '}';
|
279 |
+
}
|
280 |
+
break;
|
281 |
+
|
282 |
+
case KEYWORD_FUNCTION:
|
283 |
+
$s .= 'function' . ($n->name ? ' ' . $n->name : '') . '(';
|
284 |
+
$params = $n->params;
|
285 |
+
for ($i = 0, $j = count($params); $i < $j; $i++)
|
286 |
+
$s .= ($i ? ',' : '') . $params[$i];
|
287 |
+
$s .= '){' . $this->parseTree($n->body, true) . '}';
|
288 |
+
break;
|
289 |
+
|
290 |
+
case KEYWORD_IF:
|
291 |
+
$s = 'if(' . $this->parseTree($n->condition) . ')';
|
292 |
+
$thenPart = $this->parseTree($n->thenPart);
|
293 |
+
$elsePart = $n->elsePart ? $this->parseTree($n->elsePart) : null;
|
294 |
+
|
295 |
+
// empty if-statement
|
296 |
+
if ($thenPart == '')
|
297 |
+
$thenPart = ';';
|
298 |
+
|
299 |
+
if ($elsePart)
|
300 |
+
{
|
301 |
+
// be carefull and always make a block out of the thenPart; could be more optimized but is a lot of trouble
|
302 |
+
if ($thenPart != ';' && $thenPart[0] != '{')
|
303 |
+
$thenPart = '{' . $thenPart . '}';
|
304 |
+
|
305 |
+
$s .= $thenPart . 'else';
|
306 |
+
|
307 |
+
// we could check for more, but that hardly ever applies so go for performance
|
308 |
+
if ($elsePart[0] != '{')
|
309 |
+
$s .= ' ';
|
310 |
+
|
311 |
+
$s .= $elsePart;
|
312 |
+
}
|
313 |
+
else
|
314 |
+
{
|
315 |
+
$s .= $thenPart;
|
316 |
+
}
|
317 |
+
break;
|
318 |
+
|
319 |
+
case KEYWORD_SWITCH:
|
320 |
+
$s = 'switch(' . $this->parseTree($n->discriminant) . '){';
|
321 |
+
$cases = $n->cases;
|
322 |
+
for ($i = 0, $j = count($cases); $i < $j; $i++)
|
323 |
+
{
|
324 |
+
$case = $cases[$i];
|
325 |
+
if ($case->type == KEYWORD_CASE)
|
326 |
+
$s .= 'case' . ($case->caseLabel->type != TOKEN_STRING ? ' ' : '') . $this->parseTree($case->caseLabel) . ':';
|
327 |
+
else
|
328 |
+
$s .= 'default:';
|
329 |
+
|
330 |
+
$statement = $this->parseTree($case->statements, true);
|
331 |
+
if ($statement)
|
332 |
+
{
|
333 |
+
$s .= $statement;
|
334 |
+
// no terminator for last statement
|
335 |
+
if ($i + 1 < $j)
|
336 |
+
$s .= ';';
|
337 |
+
}
|
338 |
+
}
|
339 |
+
$s .= '}';
|
340 |
+
break;
|
341 |
+
|
342 |
+
case KEYWORD_FOR:
|
343 |
+
$s = 'for(' . ($n->setup ? $this->parseTree($n->setup) : '')
|
344 |
+
. ';' . ($n->condition ? $this->parseTree($n->condition) : '')
|
345 |
+
. ';' . ($n->update ? $this->parseTree($n->update) : '') . ')';
|
346 |
+
|
347 |
+
$body = $this->parseTree($n->body);
|
348 |
+
if ($body == '')
|
349 |
+
$body = ';';
|
350 |
+
|
351 |
+
$s .= $body;
|
352 |
+
break;
|
353 |
+
|
354 |
+
case KEYWORD_WHILE:
|
355 |
+
$s = 'while(' . $this->parseTree($n->condition) . ')';
|
356 |
+
|
357 |
+
$body = $this->parseTree($n->body);
|
358 |
+
if ($body == '')
|
359 |
+
$body = ';';
|
360 |
+
|
361 |
+
$s .= $body;
|
362 |
+
break;
|
363 |
+
|
364 |
+
case JS_FOR_IN:
|
365 |
+
$s = 'for(' . ($n->varDecl ? $this->parseTree($n->varDecl) : $this->parseTree($n->iterator)) . ' in ' . $this->parseTree($n->object) . ')';
|
366 |
+
|
367 |
+
$body = $this->parseTree($n->body);
|
368 |
+
if ($body == '')
|
369 |
+
$body = ';';
|
370 |
+
|
371 |
+
$s .= $body;
|
372 |
+
break;
|
373 |
+
|
374 |
+
case KEYWORD_DO:
|
375 |
+
$s = 'do{' . $this->parseTree($n->body, true) . '}while(' . $this->parseTree($n->condition) . ')';
|
376 |
+
break;
|
377 |
+
|
378 |
+
case KEYWORD_BREAK:
|
379 |
+
case KEYWORD_CONTINUE:
|
380 |
+
$s = $n->value . ($n->label ? ' ' . $n->label : '');
|
381 |
+
break;
|
382 |
+
|
383 |
+
case KEYWORD_TRY:
|
384 |
+
$s = 'try{' . $this->parseTree($n->tryBlock, true) . '}';
|
385 |
+
$catchClauses = $n->catchClauses;
|
386 |
+
for ($i = 0, $j = count($catchClauses); $i < $j; $i++)
|
387 |
+
{
|
388 |
+
$t = $catchClauses[$i];
|
389 |
+
$s .= 'catch(' . $t->varName . ($t->guard ? ' if ' . $this->parseTree($t->guard) : '') . '){' . $this->parseTree($t->block, true) . '}';
|
390 |
+
}
|
391 |
+
if ($n->finallyBlock)
|
392 |
+
$s .= 'finally{' . $this->parseTree($n->finallyBlock, true) . '}';
|
393 |
+
break;
|
394 |
+
|
395 |
+
case KEYWORD_THROW:
|
396 |
+
case KEYWORD_RETURN:
|
397 |
+
$s = $n->type;
|
398 |
+
if ($n->value)
|
399 |
+
{
|
400 |
+
$t = $this->parseTree($n->value);
|
401 |
+
if (strlen($t))
|
402 |
+
{
|
403 |
+
if ($this->isWordChar($t[0]) || $t[0] == '\\')
|
404 |
+
$s .= ' ';
|
405 |
+
|
406 |
+
$s .= $t;
|
407 |
+
}
|
408 |
+
}
|
409 |
+
break;
|
410 |
+
|
411 |
+
case KEYWORD_WITH:
|
412 |
+
$s = 'with(' . $this->parseTree($n->object) . ')' . $this->parseTree($n->body);
|
413 |
+
break;
|
414 |
+
|
415 |
+
case KEYWORD_VAR:
|
416 |
+
case KEYWORD_CONST:
|
417 |
+
$s = $n->value . ' ';
|
418 |
+
$childs = $n->treeNodes;
|
419 |
+
for ($i = 0, $j = count($childs); $i < $j; $i++)
|
420 |
+
{
|
421 |
+
$t = $childs[$i];
|
422 |
+
$s .= ($i ? ',' : '') . $t->name;
|
423 |
+
$u = $t->initializer;
|
424 |
+
if ($u)
|
425 |
+
$s .= '=' . $this->parseTree($u);
|
426 |
+
}
|
427 |
+
break;
|
428 |
+
|
429 |
+
case KEYWORD_IN:
|
430 |
+
case KEYWORD_INSTANCEOF:
|
431 |
+
$left = $this->parseTree($n->treeNodes[0]);
|
432 |
+
$right = $this->parseTree($n->treeNodes[1]);
|
433 |
+
|
434 |
+
$s = $left;
|
435 |
+
|
436 |
+
if ($this->isWordChar(substr($left, -1)))
|
437 |
+
$s .= ' ';
|
438 |
+
|
439 |
+
$s .= $n->type;
|
440 |
+
|
441 |
+
if ($this->isWordChar($right[0]) || $right[0] == '\\')
|
442 |
+
$s .= ' ';
|
443 |
+
|
444 |
+
$s .= $right;
|
445 |
+
break;
|
446 |
+
|
447 |
+
case KEYWORD_DELETE:
|
448 |
+
case KEYWORD_TYPEOF:
|
449 |
+
$right = $this->parseTree($n->treeNodes[0]);
|
450 |
+
|
451 |
+
$s = $n->type;
|
452 |
+
|
453 |
+
if ($this->isWordChar($right[0]) || $right[0] == '\\')
|
454 |
+
$s .= ' ';
|
455 |
+
|
456 |
+
$s .= $right;
|
457 |
+
break;
|
458 |
+
|
459 |
+
case KEYWORD_VOID:
|
460 |
+
$s = 'void(' . $this->parseTree($n->treeNodes[0]) . ')';
|
461 |
+
break;
|
462 |
+
|
463 |
+
case KEYWORD_DEBUGGER:
|
464 |
+
throw new Exception('NOT IMPLEMENTED: DEBUGGER');
|
465 |
+
break;
|
466 |
+
|
467 |
+
case TOKEN_CONDCOMMENT_START:
|
468 |
+
case TOKEN_CONDCOMMENT_END:
|
469 |
+
$s = $n->value . ($n->type == TOKEN_CONDCOMMENT_START ? ' ' : '');
|
470 |
+
$childs = $n->treeNodes;
|
471 |
+
for ($i = 0, $j = count($childs); $i < $j; $i++)
|
472 |
+
$s .= $this->parseTree($childs[$i]);
|
473 |
+
break;
|
474 |
+
|
475 |
+
case OP_SEMICOLON:
|
476 |
+
if ($expression = $n->expression)
|
477 |
+
$s = $this->parseTree($expression);
|
478 |
+
break;
|
479 |
+
|
480 |
+
case JS_LABEL:
|
481 |
+
$s = $n->label . ':' . $this->parseTree($n->statement);
|
482 |
+
break;
|
483 |
+
|
484 |
+
case OP_COMMA:
|
485 |
+
$childs = $n->treeNodes;
|
486 |
+
for ($i = 0, $j = count($childs); $i < $j; $i++)
|
487 |
+
$s .= ($i ? ',' : '') . $this->parseTree($childs[$i]);
|
488 |
+
break;
|
489 |
+
|
490 |
+
case OP_ASSIGN:
|
491 |
+
$s = $this->parseTree($n->treeNodes[0]) . $n->value . $this->parseTree($n->treeNodes[1]);
|
492 |
+
break;
|
493 |
+
|
494 |
+
case OP_HOOK:
|
495 |
+
$s = $this->parseTree($n->treeNodes[0]) . '?' . $this->parseTree($n->treeNodes[1]) . ':' . $this->parseTree($n->treeNodes[2]);
|
496 |
+
break;
|
497 |
+
|
498 |
+
case OP_OR: case OP_AND:
|
499 |
+
case OP_BITWISE_OR: case OP_BITWISE_XOR: case OP_BITWISE_AND:
|
500 |
+
case OP_EQ: case OP_NE: case OP_STRICT_EQ: case OP_STRICT_NE:
|
501 |
+
case OP_LT: case OP_LE: case OP_GE: case OP_GT:
|
502 |
+
case OP_LSH: case OP_RSH: case OP_URSH:
|
503 |
+
case OP_MUL: case OP_DIV: case OP_MOD:
|
504 |
+
$s = $this->parseTree($n->treeNodes[0]) . $n->type . $this->parseTree($n->treeNodes[1]);
|
505 |
+
break;
|
506 |
+
|
507 |
+
case OP_PLUS:
|
508 |
+
case OP_MINUS:
|
509 |
+
$left = $this->parseTree($n->treeNodes[0]);
|
510 |
+
$right = $this->parseTree($n->treeNodes[1]);
|
511 |
+
|
512 |
+
switch ($n->treeNodes[1]->type)
|
513 |
+
{
|
514 |
+
case OP_PLUS:
|
515 |
+
case OP_MINUS:
|
516 |
+
case OP_INCREMENT:
|
517 |
+
case OP_DECREMENT:
|
518 |
+
case OP_UNARY_PLUS:
|
519 |
+
case OP_UNARY_MINUS:
|
520 |
+
$s = $left . $n->type . ' ' . $right;
|
521 |
+
break;
|
522 |
+
|
523 |
+
case TOKEN_STRING:
|
524 |
+
//combine concatted strings with same quotestyle
|
525 |
+
if ($n->type == OP_PLUS && substr($left, -1) == $right[0])
|
526 |
+
{
|
527 |
+
$s = substr($left, 0, -1) . substr($right, 1);
|
528 |
+
break;
|
529 |
+
}
|
530 |
+
// FALL THROUGH
|
531 |
+
|
532 |
+
default:
|
533 |
+
$s = $left . $n->type . $right;
|
534 |
+
}
|
535 |
+
break;
|
536 |
+
|
537 |
+
case OP_NOT:
|
538 |
+
case OP_BITWISE_NOT:
|
539 |
+
case OP_UNARY_PLUS:
|
540 |
+
case OP_UNARY_MINUS:
|
541 |
+
$s = $n->value . $this->parseTree($n->treeNodes[0]);
|
542 |
+
break;
|
543 |
+
|
544 |
+
case OP_INCREMENT:
|
545 |
+
case OP_DECREMENT:
|
546 |
+
if ($n->postfix)
|
547 |
+
$s = $this->parseTree($n->treeNodes[0]) . $n->value;
|
548 |
+
else
|
549 |
+
$s = $n->value . $this->parseTree($n->treeNodes[0]);
|
550 |
+
break;
|
551 |
+
|
552 |
+
case OP_DOT:
|
553 |
+
$s = $this->parseTree($n->treeNodes[0]) . '.' . $this->parseTree($n->treeNodes[1]);
|
554 |
+
break;
|
555 |
+
|
556 |
+
case JS_INDEX:
|
557 |
+
$s = $this->parseTree($n->treeNodes[0]);
|
558 |
+
// See if we can replace named index with a dot saving 3 bytes
|
559 |
+
if ( $n->treeNodes[0]->type == TOKEN_IDENTIFIER &&
|
560 |
+
$n->treeNodes[1]->type == TOKEN_STRING &&
|
561 |
+
$this->isValidIdentifier(substr($n->treeNodes[1]->value, 1, -1))
|
562 |
+
)
|
563 |
+
$s .= '.' . substr($n->treeNodes[1]->value, 1, -1);
|
564 |
+
else
|
565 |
+
$s .= '[' . $this->parseTree($n->treeNodes[1]) . ']';
|
566 |
+
break;
|
567 |
+
|
568 |
+
case JS_LIST:
|
569 |
+
$childs = $n->treeNodes;
|
570 |
+
for ($i = 0, $j = count($childs); $i < $j; $i++)
|
571 |
+
$s .= ($i ? ',' : '') . $this->parseTree($childs[$i]);
|
572 |
+
break;
|
573 |
+
|
574 |
+
case JS_CALL:
|
575 |
+
$s = $this->parseTree($n->treeNodes[0]) . '(' . $this->parseTree($n->treeNodes[1]) . ')';
|
576 |
+
break;
|
577 |
+
|
578 |
+
case KEYWORD_NEW:
|
579 |
+
case JS_NEW_WITH_ARGS:
|
580 |
+
$s = 'new ' . $this->parseTree($n->treeNodes[0]) . '(' . ($n->type == JS_NEW_WITH_ARGS ? $this->parseTree($n->treeNodes[1]) : '') . ')';
|
581 |
+
break;
|
582 |
+
|
583 |
+
case JS_ARRAY_INIT:
|
584 |
+
$s = '[';
|
585 |
+
$childs = $n->treeNodes;
|
586 |
+
for ($i = 0, $j = count($childs); $i < $j; $i++)
|
587 |
+
{
|
588 |
+
$s .= ($i ? ',' : '') . $this->parseTree($childs[$i]);
|
589 |
+
}
|
590 |
+
$s .= ']';
|
591 |
+
break;
|
592 |
+
|
593 |
+
case JS_OBJECT_INIT:
|
594 |
+
$s = '{';
|
595 |
+
$childs = $n->treeNodes;
|
596 |
+
for ($i = 0, $j = count($childs); $i < $j; $i++)
|
597 |
+
{
|
598 |
+
$t = $childs[$i];
|
599 |
+
if ($i)
|
600 |
+
$s .= ',';
|
601 |
+
if ($t->type == JS_PROPERTY_INIT)
|
602 |
+
{
|
603 |
+
// Ditch the quotes when the index is a valid identifier
|
604 |
+
if ( $t->treeNodes[0]->type == TOKEN_STRING &&
|
605 |
+
$this->isValidIdentifier(substr($t->treeNodes[0]->value, 1, -1))
|
606 |
+
)
|
607 |
+
$s .= substr($t->treeNodes[0]->value, 1, -1);
|
608 |
+
else
|
609 |
+
$s .= $t->treeNodes[0]->value;
|
610 |
+
|
611 |
+
$s .= ':' . $this->parseTree($t->treeNodes[1]);
|
612 |
+
}
|
613 |
+
else
|
614 |
+
{
|
615 |
+
$s .= $t->type == JS_GETTER ? 'get' : 'set';
|
616 |
+
$s .= ' ' . $t->name . '(';
|
617 |
+
$params = $t->params;
|
618 |
+
for ($i = 0, $j = count($params); $i < $j; $i++)
|
619 |
+
$s .= ($i ? ',' : '') . $params[$i];
|
620 |
+
$s .= '){' . $this->parseTree($t->body, true) . '}';
|
621 |
+
}
|
622 |
+
}
|
623 |
+
$s .= '}';
|
624 |
+
break;
|
625 |
+
|
626 |
+
case TOKEN_NUMBER:
|
627 |
+
$s = $n->value;
|
628 |
+
if (preg_match('/^([1-9]+)(0{3,})$/', $s, $m))
|
629 |
+
$s = $m[1] . 'e' . strlen($m[2]);
|
630 |
+
break;
|
631 |
+
|
632 |
+
case KEYWORD_NULL: case KEYWORD_THIS: case KEYWORD_TRUE: case KEYWORD_FALSE:
|
633 |
+
case TOKEN_IDENTIFIER: case TOKEN_STRING: case TOKEN_REGEXP:
|
634 |
+
$s = $n->value;
|
635 |
+
break;
|
636 |
+
|
637 |
+
case JS_GROUP:
|
638 |
+
if (in_array(
|
639 |
+
$n->treeNodes[0]->type,
|
640 |
+
array(
|
641 |
+
JS_ARRAY_INIT, JS_OBJECT_INIT, JS_GROUP,
|
642 |
+
TOKEN_NUMBER, TOKEN_STRING, TOKEN_REGEXP, TOKEN_IDENTIFIER,
|
643 |
+
KEYWORD_NULL, KEYWORD_THIS, KEYWORD_TRUE, KEYWORD_FALSE
|
644 |
+
)
|
645 |
+
))
|
646 |
+
{
|
647 |
+
$s = $this->parseTree($n->treeNodes[0]);
|
648 |
+
}
|
649 |
+
else
|
650 |
+
{
|
651 |
+
$s = '(' . $this->parseTree($n->treeNodes[0]) . ')';
|
652 |
+
}
|
653 |
+
break;
|
654 |
+
|
655 |
+
default:
|
656 |
+
throw new Exception('UNKNOWN TOKEN TYPE: ' . $n->type);
|
657 |
+
}
|
658 |
+
|
659 |
+
return $s;
|
660 |
+
}
|
661 |
+
|
662 |
+
private function isValidIdentifier($string)
|
663 |
+
{
|
664 |
+
return preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $string) && !in_array($string, $this->reserved);
|
665 |
+
}
|
666 |
+
|
667 |
+
private function isWordChar($char)
|
668 |
+
{
|
669 |
+
return $char == '_' || $char == '$' || ctype_alnum($char);
|
670 |
+
}
|
671 |
+
}
|
672 |
+
|
673 |
+
class JSParser
|
674 |
+
{
|
675 |
+
private $t;
|
676 |
+
private $minifier;
|
677 |
+
|
678 |
+
private $opPrecedence = array(
|
679 |
+
';' => 0,
|
680 |
+
',' => 1,
|
681 |
+
'=' => 2, '?' => 2, ':' => 2,
|
682 |
+
// The above all have to have the same precedence, see bug 330975
|
683 |
+
'||' => 4,
|
684 |
+
'&&' => 5,
|
685 |
+
'|' => 6,
|
686 |
+
'^' => 7,
|
687 |
+
'&' => 8,
|
688 |
+
'==' => 9, '!=' => 9, '===' => 9, '!==' => 9,
|
689 |
+
'<' => 10, '<=' => 10, '>=' => 10, '>' => 10, 'in' => 10, 'instanceof' => 10,
|
690 |
+
'<<' => 11, '>>' => 11, '>>>' => 11,
|
691 |
+
'+' => 12, '-' => 12,
|
692 |
+
'*' => 13, '/' => 13, '%' => 13,
|
693 |
+
'delete' => 14, 'void' => 14, 'typeof' => 14,
|
694 |
+
'!' => 14, '~' => 14, 'U+' => 14, 'U-' => 14,
|
695 |
+
'++' => 15, '--' => 15,
|
696 |
+
'new' => 16,
|
697 |
+
'.' => 17,
|
698 |
+
JS_NEW_WITH_ARGS => 0, JS_INDEX => 0, JS_CALL => 0,
|
699 |
+
JS_ARRAY_INIT => 0, JS_OBJECT_INIT => 0, JS_GROUP => 0
|
700 |
+
);
|
701 |
+
|
702 |
+
private $opArity = array(
|
703 |
+
',' => -2,
|
704 |
+
'=' => 2,
|
705 |
+
'?' => 3,
|
706 |
+
'||' => 2,
|
707 |
+
'&&' => 2,
|
708 |
+
'|' => 2,
|
709 |
+
'^' => 2,
|
710 |
+
'&' => 2,
|
711 |
+
'==' => 2, '!=' => 2, '===' => 2, '!==' => 2,
|
712 |
+
'<' => 2, '<=' => 2, '>=' => 2, '>' => 2, 'in' => 2, 'instanceof' => 2,
|
713 |
+
'<<' => 2, '>>' => 2, '>>>' => 2,
|
714 |
+
'+' => 2, '-' => 2,
|
715 |
+
'*' => 2, '/' => 2, '%' => 2,
|
716 |
+
'delete' => 1, 'void' => 1, 'typeof' => 1,
|
717 |
+
'!' => 1, '~' => 1, 'U+' => 1, 'U-' => 1,
|
718 |
+
'++' => 1, '--' => 1,
|
719 |
+
'new' => 1,
|
720 |
+
'.' => 2,
|
721 |
+
JS_NEW_WITH_ARGS => 2, JS_INDEX => 2, JS_CALL => 2,
|
722 |
+
JS_ARRAY_INIT => 1, JS_OBJECT_INIT => 1, JS_GROUP => 1,
|
723 |
+
TOKEN_CONDCOMMENT_START => 1, TOKEN_CONDCOMMENT_END => 1
|
724 |
+
);
|
725 |
+
|
726 |
+
public function __construct($minifier=null)
|
727 |
+
{
|
728 |
+
$this->minifier = $minifier;
|
729 |
+
$this->t = new JSTokenizer();
|
730 |
+
}
|
731 |
+
|
732 |
+
public function parse($s, $f, $l)
|
733 |
+
{
|
734 |
+
// initialize tokenizer
|
735 |
+
$this->t->init($s, $f, $l);
|
736 |
+
|
737 |
+
$x = new JSCompilerContext(false);
|
738 |
+
$n = $this->Script($x);
|
739 |
+
if (!$this->t->isDone())
|
740 |
+
throw $this->t->newSyntaxError('Syntax error');
|
741 |
+
|
742 |
+
return $n;
|
743 |
+
}
|
744 |
+
|
745 |
+
private function Script($x)
|
746 |
+
{
|
747 |
+
$n = $this->Statements($x);
|
748 |
+
$n->type = JS_SCRIPT;
|
749 |
+
$n->funDecls = $x->funDecls;
|
750 |
+
$n->varDecls = $x->varDecls;
|
751 |
+
|
752 |
+
// minify by scope
|
753 |
+
if ($this->minifier)
|
754 |
+
{
|
755 |
+
$n->value = $this->minifier->parseTree($n);
|
756 |
+
|
757 |
+
// clear tree from node to save memory
|
758 |
+
$n->treeNodes = null;
|
759 |
+
$n->funDecls = null;
|
760 |
+
$n->varDecls = null;
|
761 |
+
|
762 |
+
$n->type = JS_MINIFIED;
|
763 |
+
}
|
764 |
+
|
765 |
+
return $n;
|
766 |
+
}
|
767 |
+
|
768 |
+
private function Statements($x)
|
769 |
+
{
|
770 |
+
$n = new JSNode($this->t, JS_BLOCK);
|
771 |
+
array_push($x->stmtStack, $n);
|
772 |
+
|
773 |
+
while (!$this->t->isDone() && $this->t->peek() != OP_RIGHT_CURLY)
|
774 |
+
$n->addNode($this->Statement($x));
|
775 |
+
|
776 |
+
array_pop($x->stmtStack);
|
777 |
+
|
778 |
+
return $n;
|
779 |
+
}
|
780 |
+
|
781 |
+
private function Block($x)
|
782 |
+
{
|
783 |
+
$this->t->mustMatch(OP_LEFT_CURLY);
|
784 |
+
$n = $this->Statements($x);
|
785 |
+
$this->t->mustMatch(OP_RIGHT_CURLY);
|
786 |
+
|
787 |
+
return $n;
|
788 |
+
}
|
789 |
+
|
790 |
+
private function Statement($x)
|
791 |
+
{
|
792 |
+
$tt = $this->t->get();
|
793 |
+
$n2 = null;
|
794 |
+
|
795 |
+
// Cases for statements ending in a right curly return early, avoiding the
|
796 |
+
// common semicolon insertion magic after this switch.
|
797 |
+
switch ($tt)
|
798 |
+
{
|
799 |
+
case KEYWORD_FUNCTION:
|
800 |
+
return $this->FunctionDefinition(
|
801 |
+
$x,
|
802 |
+
true,
|
803 |
+
count($x->stmtStack) > 1 ? STATEMENT_FORM : DECLARED_FORM
|
804 |
+
);
|
805 |
+
break;
|
806 |
+
|
807 |
+
case OP_LEFT_CURLY:
|
808 |
+
$n = $this->Statements($x);
|
809 |
+
$this->t->mustMatch(OP_RIGHT_CURLY);
|
810 |
+
return $n;
|
811 |
+
|
812 |
+
case KEYWORD_IF:
|
813 |
+
$n = new JSNode($this->t);
|
814 |
+
$n->condition = $this->ParenExpression($x);
|
815 |
+
array_push($x->stmtStack, $n);
|
816 |
+
$n->thenPart = $this->Statement($x);
|
817 |
+
$n->elsePart = $this->t->match(KEYWORD_ELSE) ? $this->Statement($x) : null;
|
818 |
+
array_pop($x->stmtStack);
|
819 |
+
return $n;
|
820 |
+
|
821 |
+
case KEYWORD_SWITCH:
|
822 |
+
$n = new JSNode($this->t);
|
823 |
+
$this->t->mustMatch(OP_LEFT_PAREN);
|
824 |
+
$n->discriminant = $this->Expression($x);
|
825 |
+
$this->t->mustMatch(OP_RIGHT_PAREN);
|
826 |
+
$n->cases = array();
|
827 |
+
$n->defaultIndex = -1;
|
828 |
+
|
829 |
+
array_push($x->stmtStack, $n);
|
830 |
+
|
831 |
+
$this->t->mustMatch(OP_LEFT_CURLY);
|
832 |
+
|
833 |
+
while (($tt = $this->t->get()) != OP_RIGHT_CURLY)
|
834 |
+
{
|
835 |
+
switch ($tt)
|
836 |
+
{
|
837 |
+
case KEYWORD_DEFAULT:
|
838 |
+
if ($n->defaultIndex >= 0)
|
839 |
+
throw $this->t->newSyntaxError('More than one switch default');
|
840 |
+
// FALL THROUGH
|
841 |
+
case KEYWORD_CASE:
|
842 |
+
$n2 = new JSNode($this->t);
|
843 |
+
if ($tt == KEYWORD_DEFAULT)
|
844 |
+
$n->defaultIndex = count($n->cases);
|
845 |
+
else
|
846 |
+
$n2->caseLabel = $this->Expression($x, OP_COLON);
|
847 |
+
break;
|
848 |
+
default:
|
849 |
+
throw $this->t->newSyntaxError('Invalid switch case');
|
850 |
+
}
|
851 |
+
|
852 |
+
$this->t->mustMatch(OP_COLON);
|
853 |
+
$n2->statements = new JSNode($this->t, JS_BLOCK);
|
854 |
+
while (($tt = $this->t->peek()) != KEYWORD_CASE && $tt != KEYWORD_DEFAULT && $tt != OP_RIGHT_CURLY)
|
855 |
+
$n2->statements->addNode($this->Statement($x));
|
856 |
+
|
857 |
+
array_push($n->cases, $n2);
|
858 |
+
}
|
859 |
+
|
860 |
+
array_pop($x->stmtStack);
|
861 |
+
return $n;
|
862 |
+
|
863 |
+
case KEYWORD_FOR:
|
864 |
+
$n = new JSNode($this->t);
|
865 |
+
$n->isLoop = true;
|
866 |
+
$this->t->mustMatch(OP_LEFT_PAREN);
|
867 |
+
|
868 |
+
if (($tt = $this->t->peek()) != OP_SEMICOLON)
|
869 |
+
{
|
870 |
+
$x->inForLoopInit = true;
|
871 |
+
if ($tt == KEYWORD_VAR || $tt == KEYWORD_CONST)
|
872 |
+
{
|
873 |
+
$this->t->get();
|
874 |
+
$n2 = $this->Variables($x);
|
875 |
+
}
|
876 |
+
else
|
877 |
+
{
|
878 |
+
$n2 = $this->Expression($x);
|
879 |
+
}
|
880 |
+
$x->inForLoopInit = false;
|
881 |
+
}
|
882 |
+
|
883 |
+
if ($n2 && $this->t->match(KEYWORD_IN))
|
884 |
+
{
|
885 |
+
$n->type = JS_FOR_IN;
|
886 |
+
if ($n2->type == KEYWORD_VAR)
|
887 |
+
{
|
888 |
+
if (count($n2->treeNodes) != 1)
|
889 |
+
{
|
890 |
+
throw $this->t->SyntaxError(
|
891 |
+
'Invalid for..in left-hand side',
|
892 |
+
$this->t->filename,
|
893 |
+
$n2->lineno
|
894 |
+
);
|
895 |
+
}
|
896 |
+
|
897 |
+
// NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name.
|
898 |
+
$n->iterator = $n2->treeNodes[0];
|
899 |
+
$n->varDecl = $n2;
|
900 |
+
}
|
901 |
+
else
|
902 |
+
{
|
903 |
+
$n->iterator = $n2;
|
904 |
+
$n->varDecl = null;
|
905 |
+
}
|
906 |
+
|
907 |
+
$n->object = $this->Expression($x);
|
908 |
+
}
|
909 |
+
else
|
910 |
+
{
|
911 |
+
$n->setup = $n2 ? $n2 : null;
|
912 |
+
$this->t->mustMatch(OP_SEMICOLON);
|
913 |
+
$n->condition = $this->t->peek() == OP_SEMICOLON ? null : $this->Expression($x);
|
914 |
+
$this->t->mustMatch(OP_SEMICOLON);
|
915 |
+
$n->update = $this->t->peek() == OP_RIGHT_PAREN ? null : $this->Expression($x);
|
916 |
+
}
|
917 |
+
|
918 |
+
$this->t->mustMatch(OP_RIGHT_PAREN);
|
919 |
+
$n->body = $this->nest($x, $n);
|
920 |
+
return $n;
|
921 |
+
|
922 |
+
case KEYWORD_WHILE:
|
923 |
+
$n = new JSNode($this->t);
|
924 |
+
$n->isLoop = true;
|
925 |
+
$n->condition = $this->ParenExpression($x);
|
926 |
+
$n->body = $this->nest($x, $n);
|
927 |
+
return $n;
|
928 |
+
|
929 |
+
case KEYWORD_DO:
|
930 |
+
$n = new JSNode($this->t);
|
931 |
+
$n->isLoop = true;
|
932 |
+
$n->body = $this->nest($x, $n, KEYWORD_WHILE);
|
933 |
+
$n->condition = $this->ParenExpression($x);
|
934 |
+
if (!$x->ecmaStrictMode)
|
935 |
+
{
|
936 |
+
// <script language="JavaScript"> (without version hints) may need
|
937 |
+
// automatic semicolon insertion without a newline after do-while.
|
938 |
+
// See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
|
939 |
+
$this->t->match(OP_SEMICOLON);
|
940 |
+
return $n;
|
941 |
+
}
|
942 |
+
break;
|
943 |
+
|
944 |
+
case KEYWORD_BREAK:
|
945 |
+
case KEYWORD_CONTINUE:
|
946 |
+
$n = new JSNode($this->t);
|
947 |
+
|
948 |
+
if ($this->t->peekOnSameLine() == TOKEN_IDENTIFIER)
|
949 |
+
{
|
950 |
+
$this->t->get();
|
951 |
+
$n->label = $this->t->currentToken()->value;
|
952 |
+
}
|
953 |
+
|
954 |
+
$ss = $x->stmtStack;
|
955 |
+
$i = count($ss);
|
956 |
+
$label = $n->label;
|
957 |
+
if ($label)
|
958 |
+
{
|
959 |
+
do
|
960 |
+
{
|
961 |
+
if (--$i < 0)
|
962 |
+
throw $this->t->newSyntaxError('Label not found');
|
963 |
+
}
|
964 |
+
while ($ss[$i]->label != $label);
|
965 |
+
}
|
966 |
+
else
|
967 |
+
{
|
968 |
+
do
|
969 |
+
{
|
970 |
+
if (--$i < 0)
|
971 |
+
throw $this->t->newSyntaxError('Invalid ' . $tt);
|
972 |
+
}
|
973 |
+
while (!$ss[$i]->isLoop && ($tt != KEYWORD_BREAK || $ss[$i]->type != KEYWORD_SWITCH));
|
974 |
+
}
|
975 |
+
|
976 |
+
$n->target = $ss[$i];
|
977 |
+
break;
|
978 |
+
|
979 |
+
case KEYWORD_TRY:
|
980 |
+
$n = new JSNode($this->t);
|
981 |
+
$n->tryBlock = $this->Block($x);
|
982 |
+
$n->catchClauses = array();
|
983 |
+
|
984 |
+
while ($this->t->match(KEYWORD_CATCH))
|
985 |
+
{
|
986 |
+
$n2 = new JSNode($this->t);
|
987 |
+
$this->t->mustMatch(OP_LEFT_PAREN);
|
988 |
+
$n2->varName = $this->t->mustMatch(TOKEN_IDENTIFIER)->value;
|
989 |
+
|
990 |
+
if ($this->t->match(KEYWORD_IF))
|
991 |
+
{
|
992 |
+
if ($x->ecmaStrictMode)
|
993 |
+
throw $this->t->newSyntaxError('Illegal catch guard');
|
994 |
+
|
995 |
+
if (count($n->catchClauses) && !end($n->catchClauses)->guard)
|
996 |
+
throw $this->t->newSyntaxError('Guarded catch after unguarded');
|
997 |
+
|
998 |
+
$n2->guard = $this->Expression($x);
|
999 |
+
}
|
1000 |
+
else
|
1001 |
+
{
|
1002 |
+
$n2->guard = null;
|
1003 |
+
}
|
1004 |
+
|
1005 |
+
$this->t->mustMatch(OP_RIGHT_PAREN);
|
1006 |
+
$n2->block = $this->Block($x);
|
1007 |
+
array_push($n->catchClauses, $n2);
|
1008 |
+
}
|
1009 |
+
|
1010 |
+
if ($this->t->match(KEYWORD_FINALLY))
|
1011 |
+
$n->finallyBlock = $this->Block($x);
|
1012 |
+
|
1013 |
+
if (!count($n->catchClauses) && !$n->finallyBlock)
|
1014 |
+
throw $this->t->newSyntaxError('Invalid try statement');
|
1015 |
+
return $n;
|
1016 |
+
|
1017 |
+
case KEYWORD_CATCH:
|
1018 |
+
case KEYWORD_FINALLY:
|
1019 |
+
throw $this->t->newSyntaxError($tt + ' without preceding try');
|
1020 |
+
|
1021 |
+
case KEYWORD_THROW:
|
1022 |
+
$n = new JSNode($this->t);
|
1023 |
+
$n->value = $this->Expression($x);
|
1024 |
+
break;
|
1025 |
+
|
1026 |
+
case KEYWORD_RETURN:
|
1027 |
+
if (!$x->inFunction)
|
1028 |
+
throw $this->t->newSyntaxError('Invalid return');
|
1029 |
+
|
1030 |
+
$n = new JSNode($this->t);
|
1031 |
+
$tt = $this->t->peekOnSameLine();
|
1032 |
+
if ($tt != TOKEN_END && $tt != TOKEN_NEWLINE && $tt != OP_SEMICOLON && $tt != OP_RIGHT_CURLY)
|
1033 |
+
$n->value = $this->Expression($x);
|
1034 |
+
else
|
1035 |
+
$n->value = null;
|
1036 |
+
break;
|
1037 |
+
|
1038 |
+
case KEYWORD_WITH:
|
1039 |
+
$n = new JSNode($this->t);
|
1040 |
+
$n->object = $this->ParenExpression($x);
|
1041 |
+
$n->body = $this->nest($x, $n);
|
1042 |
+
return $n;
|
1043 |
+
|
1044 |
+
case KEYWORD_VAR:
|
1045 |
+
case KEYWORD_CONST:
|
1046 |
+
$n = $this->Variables($x);
|
1047 |
+
break;
|
1048 |
+
|
1049 |
+
case TOKEN_CONDCOMMENT_START:
|
1050 |
+
case TOKEN_CONDCOMMENT_END:
|
1051 |
+
$n = new JSNode($this->t);
|
1052 |
+
return $n;
|
1053 |
+
|
1054 |
+
case KEYWORD_DEBUGGER:
|
1055 |
+
$n = new JSNode($this->t);
|
1056 |
+
break;
|
1057 |
+
|
1058 |
+
case TOKEN_NEWLINE:
|
1059 |
+
case OP_SEMICOLON:
|
1060 |
+
$n = new JSNode($this->t, OP_SEMICOLON);
|
1061 |
+
$n->expression = null;
|
1062 |
+
return $n;
|
1063 |
+
|
1064 |
+
default:
|
1065 |
+
if ($tt == TOKEN_IDENTIFIER)
|
1066 |
+
{
|
1067 |
+
$this->t->scanOperand = false;
|
1068 |
+
$tt = $this->t->peek();
|
1069 |
+
$this->t->scanOperand = true;
|
1070 |
+
if ($tt == OP_COLON)
|
1071 |
+
{
|
1072 |
+
$label = $this->t->currentToken()->value;
|
1073 |
+
$ss = $x->stmtStack;
|
1074 |
+
for ($i = count($ss) - 1; $i >= 0; --$i)
|
1075 |
+
{
|
1076 |
+
if ($ss[$i]->label == $label)
|
1077 |
+
throw $this->t->newSyntaxError('Duplicate label');
|
1078 |
+
}
|
1079 |
+
|
1080 |
+
$this->t->get();
|
1081 |
+
$n = new JSNode($this->t, JS_LABEL);
|
1082 |
+
$n->label = $label;
|
1083 |
+
$n->statement = $this->nest($x, $n);
|
1084 |
+
|
1085 |
+
return $n;
|
1086 |
+
}
|
1087 |
+
}
|
1088 |
+
|
1089 |
+
$n = new JSNode($this->t, OP_SEMICOLON);
|
1090 |
+
$this->t->unget();
|
1091 |
+
$n->expression = $this->Expression($x);
|
1092 |
+
$n->end = $n->expression->end;
|
1093 |
+
break;
|
1094 |
+
}
|
1095 |
+
|
1096 |
+
if ($this->t->lineno == $this->t->currentToken()->lineno)
|
1097 |
+
{
|
1098 |
+
$tt = $this->t->peekOnSameLine();
|
1099 |
+
if ($tt != TOKEN_END && $tt != TOKEN_NEWLINE && $tt != OP_SEMICOLON && $tt != OP_RIGHT_CURLY)
|
1100 |
+
throw $this->t->newSyntaxError('Missing ; before statement');
|
1101 |
+
}
|
1102 |
+
|
1103 |
+
$this->t->match(OP_SEMICOLON);
|
1104 |
+
|
1105 |
+
return $n;
|
1106 |
+
}
|
1107 |
+
|
1108 |
+
private function FunctionDefinition($x, $requireName, $functionForm)
|
1109 |
+
{
|
1110 |
+
$f = new JSNode($this->t);
|
1111 |
+
|
1112 |
+
if ($f->type != KEYWORD_FUNCTION)
|
1113 |
+
$f->type = ($f->value == 'get') ? JS_GETTER : JS_SETTER;
|
1114 |
+
|
1115 |
+
if ($this->t->match(TOKEN_IDENTIFIER))
|
1116 |
+
$f->name = $this->t->currentToken()->value;
|
1117 |
+
elseif ($requireName)
|
1118 |
+
throw $this->t->newSyntaxError('Missing function identifier');
|
1119 |
+
|
1120 |
+
$this->t->mustMatch(OP_LEFT_PAREN);
|
1121 |
+
$f->params = array();
|
1122 |
+
|
1123 |
+
while (($tt = $this->t->get()) != OP_RIGHT_PAREN)
|
1124 |
+
{
|
1125 |
+
if ($tt != TOKEN_IDENTIFIER)
|
1126 |
+
throw $this->t->newSyntaxError('Missing formal parameter');
|
1127 |
+
|
1128 |
+
array_push($f->params, $this->t->currentToken()->value);
|
1129 |
+
|
1130 |
+
if ($this->t->peek() != OP_RIGHT_PAREN)
|
1131 |
+
$this->t->mustMatch(OP_COMMA);
|
1132 |
+
}
|
1133 |
+
|
1134 |
+
$this->t->mustMatch(OP_LEFT_CURLY);
|
1135 |
+
|
1136 |
+
$x2 = new JSCompilerContext(true);
|
1137 |
+
$f->body = $this->Script($x2);
|
1138 |
+
|
1139 |
+
$this->t->mustMatch(OP_RIGHT_CURLY);
|
1140 |
+
$f->end = $this->t->currentToken()->end;
|
1141 |
+
|
1142 |
+
$f->functionForm = $functionForm;
|
1143 |
+
if ($functionForm == DECLARED_FORM)
|
1144 |
+
array_push($x->funDecls, $f);
|
1145 |
+
|
1146 |
+
return $f;
|
1147 |
+
}
|
1148 |
+
|
1149 |
+
private function Variables($x)
|
1150 |
+
{
|
1151 |
+
$n = new JSNode($this->t);
|
1152 |
+
|
1153 |
+
do
|
1154 |
+
{
|
1155 |
+
$this->t->mustMatch(TOKEN_IDENTIFIER);
|
1156 |
+
|
1157 |
+
$n2 = new JSNode($this->t);
|
1158 |
+
$n2->name = $n2->value;
|
1159 |
+
|
1160 |
+
if ($this->t->match(OP_ASSIGN))
|
1161 |
+
{
|
1162 |
+
if ($this->t->currentToken()->assignOp)
|
1163 |
+
throw $this->t->newSyntaxError('Invalid variable initialization');
|
1164 |
+
|
1165 |
+
$n2->initializer = $this->Expression($x, OP_COMMA);
|
1166 |
+
}
|
1167 |
+
|
1168 |
+
$n2->readOnly = $n->type == KEYWORD_CONST;
|
1169 |
+
|
1170 |
+
$n->addNode($n2);
|
1171 |
+
array_push($x->varDecls, $n2);
|
1172 |
+
}
|
1173 |
+
while ($this->t->match(OP_COMMA));
|
1174 |
+
|
1175 |
+
return $n;
|
1176 |
+
}
|
1177 |
+
|
1178 |
+
private function Expression($x, $stop=false)
|
1179 |
+
{
|
1180 |
+
$operators = array();
|
1181 |
+
$operands = array();
|
1182 |
+
$n = false;
|
1183 |
+
|
1184 |
+
$bl = $x->bracketLevel;
|
1185 |
+
$cl = $x->curlyLevel;
|
1186 |
+
$pl = $x->parenLevel;
|
1187 |
+
$hl = $x->hookLevel;
|
1188 |
+
|
1189 |
+
while (($tt = $this->t->get()) != TOKEN_END)
|
1190 |
+
{
|
1191 |
+
if ($tt == $stop &&
|
1192 |
+
$x->bracketLevel == $bl &&
|
1193 |
+
$x->curlyLevel == $cl &&
|
1194 |
+
$x->parenLevel == $pl &&
|
1195 |
+
$x->hookLevel == $hl
|
1196 |
+
)
|
1197 |
+
{
|
1198 |
+
// Stop only if tt matches the optional stop parameter, and that
|
1199 |
+
// token is not quoted by some kind of bracket.
|
1200 |
+
break;
|
1201 |
+
}
|
1202 |
+
|
1203 |
+
switch ($tt)
|
1204 |
+
{
|
1205 |
+
case OP_SEMICOLON:
|
1206 |
+
// NB: cannot be empty, Statement handled that.
|
1207 |
+
break 2;
|
1208 |
+
|
1209 |
+
case OP_HOOK:
|
1210 |
+
if ($this->t->scanOperand)
|
1211 |
+
break 2;
|
1212 |
+
|
1213 |
+
while ( !empty($operators) &&
|
1214 |
+
$this->opPrecedence[end($operators)->type] > $this->opPrecedence[$tt]
|
1215 |
+
)
|
1216 |
+
$this->reduce($operators, $operands);
|
1217 |
+
|
1218 |
+
array_push($operators, new JSNode($this->t));
|
1219 |
+
|
1220 |
+
++$x->hookLevel;
|
1221 |
+
$this->t->scanOperand = true;
|
1222 |
+
$n = $this->Expression($x);
|
1223 |
+
|
1224 |
+
if (!$this->t->match(OP_COLON))
|
1225 |
+
break 2;
|
1226 |
+
|
1227 |
+
--$x->hookLevel;
|
1228 |
+
array_push($operands, $n);
|
1229 |
+
break;
|
1230 |
+
|
1231 |
+
case OP_COLON:
|
1232 |
+
if ($x->hookLevel)
|
1233 |
+
break 2;
|
1234 |
+
|
1235 |
+
throw $this->t->newSyntaxError('Invalid label');
|
1236 |
+
break;
|
1237 |
+
|
1238 |
+
case OP_ASSIGN:
|
1239 |
+
if ($this->t->scanOperand)
|
1240 |
+
break 2;
|
1241 |
+
|
1242 |
+
// Use >, not >=, for right-associative ASSIGN
|
1243 |
+
while ( !empty($operators) &&
|
1244 |
+
$this->opPrecedence[end($operators)->type] > $this->opPrecedence[$tt]
|
1245 |
+
)
|
1246 |
+
$this->reduce($operators, $operands);
|
1247 |
+
|
1248 |
+
array_push($operators, new JSNode($this->t));
|
1249 |
+
end($operands)->assignOp = $this->t->currentToken()->assignOp;
|
1250 |
+
$this->t->scanOperand = true;
|
1251 |
+
break;
|
1252 |
+
|
1253 |
+
case KEYWORD_IN:
|
1254 |
+
// An in operator should not be parsed if we're parsing the head of
|
1255 |
+
// a for (...) loop, unless it is in the then part of a conditional
|
1256 |
+
// expression, or parenthesized somehow.
|
1257 |
+
if ($x->inForLoopInit && !$x->hookLevel &&
|
1258 |
+
!$x->bracketLevel && !$x->curlyLevel &&
|
1259 |
+
!$x->parenLevel
|
1260 |
+
)
|
1261 |
+
break 2;
|
1262 |
+
// FALL THROUGH
|
1263 |
+
case OP_COMMA:
|
1264 |
+
// A comma operator should not be parsed if we're parsing the then part
|
1265 |
+
// of a conditional expression unless it's parenthesized somehow.
|
1266 |
+
if ($tt == OP_COMMA && $x->hookLevel &&
|
1267 |
+
!$x->bracketLevel && !$x->curlyLevel &&
|
1268 |
+
!$x->parenLevel
|
1269 |
+
)
|
1270 |
+
break 2;
|
1271 |
+
// Treat comma as left-associative so reduce can fold left-heavy
|
1272 |
+
// COMMA trees into a single array.
|
1273 |
+
// FALL THROUGH
|
1274 |
+
case OP_OR:
|
1275 |
+
case OP_AND:
|
1276 |
+
case OP_BITWISE_OR:
|
1277 |
+
case OP_BITWISE_XOR:
|
1278 |
+
case OP_BITWISE_AND:
|
1279 |
+
case OP_EQ: case OP_NE: case OP_STRICT_EQ: case OP_STRICT_NE:
|
1280 |
+
case OP_LT: case OP_LE: case OP_GE: case OP_GT:
|
1281 |
+
case KEYWORD_INSTANCEOF:
|
1282 |
+
case OP_LSH: case OP_RSH: case OP_URSH:
|
1283 |
+
case OP_PLUS: case OP_MINUS:
|
1284 |
+
case OP_MUL: case OP_DIV: case OP_MOD:
|
1285 |
+
case OP_DOT:
|
1286 |
+
if ($this->t->scanOperand)
|
1287 |
+
break 2;
|
1288 |
+
|
1289 |
+
while ( !empty($operators) &&
|
1290 |
+
$this->opPrecedence[end($operators)->type] >= $this->opPrecedence[$tt]
|
1291 |
+
)
|
1292 |
+
$this->reduce($operators, $operands);
|
1293 |
+
|
1294 |
+
if ($tt == OP_DOT)
|
1295 |
+
{
|
1296 |
+
$this->t->mustMatch(TOKEN_IDENTIFIER);
|
1297 |
+
array_push($operands, new JSNode($this->t, OP_DOT, array_pop($operands), new JSNode($this->t)));
|
1298 |
+
}
|
1299 |
+
else
|
1300 |
+
{
|
1301 |
+
array_push($operators, new JSNode($this->t));
|
1302 |
+
$this->t->scanOperand = true;
|
1303 |
+
}
|
1304 |
+
break;
|
1305 |
+
|
1306 |
+
case KEYWORD_DELETE: case KEYWORD_VOID: case KEYWORD_TYPEOF:
|
1307 |
+
case OP_NOT: case OP_BITWISE_NOT: case OP_UNARY_PLUS: case OP_UNARY_MINUS:
|
1308 |
+
case KEYWORD_NEW:
|
1309 |
+
if (!$this->t->scanOperand)
|
1310 |
+
break 2;
|
1311 |
+
|
1312 |
+
array_push($operators, new JSNode($this->t));
|
1313 |
+
break;
|
1314 |
+
|
1315 |
+
case OP_INCREMENT: case OP_DECREMENT:
|
1316 |
+
if ($this->t->scanOperand)
|
1317 |
+
{
|
1318 |
+
array_push($operators, new JSNode($this->t)); // prefix increment or decrement
|
1319 |
+
}
|
1320 |
+
else
|
1321 |
+
{
|
1322 |
+
// Don't cross a line boundary for postfix {in,de}crement.
|
1323 |
+
$t = $this->t->tokens[($this->t->tokenIndex + $this->t->lookahead - 1) & 3];
|
1324 |
+
if ($t && $t->lineno != $this->t->lineno)
|
1325 |
+
break 2;
|
1326 |
+
|
1327 |
+
if (!empty($operators))
|
1328 |
+
{
|
1329 |
+
// Use >, not >=, so postfix has higher precedence than prefix.
|
1330 |
+
while ($this->opPrecedence[end($operators)->type] > $this->opPrecedence[$tt])
|
1331 |
+
$this->reduce($operators, $operands);
|
1332 |
+
}
|
1333 |
+
|
1334 |
+
$n = new JSNode($this->t, $tt, array_pop($operands));
|
1335 |
+
$n->postfix = true;
|
1336 |
+
array_push($operands, $n);
|
1337 |
+
}
|
1338 |
+
break;
|
1339 |
+
|
1340 |
+
case KEYWORD_FUNCTION:
|
1341 |
+
if (!$this->t->scanOperand)
|
1342 |
+
break 2;
|
1343 |
+
|
1344 |
+
array_push($operands, $this->FunctionDefinition($x, false, EXPRESSED_FORM));
|
1345 |
+
$this->t->scanOperand = false;
|
1346 |
+
break;
|
1347 |
+
|
1348 |
+
case KEYWORD_NULL: case KEYWORD_THIS: case KEYWORD_TRUE: case KEYWORD_FALSE:
|
1349 |
+
case TOKEN_IDENTIFIER: case TOKEN_NUMBER: case TOKEN_STRING: case TOKEN_REGEXP:
|
1350 |
+
if (!$this->t->scanOperand)
|
1351 |
+
break 2;
|
1352 |
+
|
1353 |
+
array_push($operands, new JSNode($this->t));
|
1354 |
+
$this->t->scanOperand = false;
|
1355 |
+
break;
|
1356 |
+
|
1357 |
+
case TOKEN_CONDCOMMENT_START:
|
1358 |
+
case TOKEN_CONDCOMMENT_END:
|
1359 |
+
if ($this->t->scanOperand)
|
1360 |
+
array_push($operators, new JSNode($this->t));
|
1361 |
+
else
|
1362 |
+
array_push($operands, new JSNode($this->t));
|
1363 |
+
break;
|
1364 |
+
|
1365 |
+
case OP_LEFT_BRACKET:
|
1366 |
+
if ($this->t->scanOperand)
|
1367 |
+
{
|
1368 |
+
// Array initialiser. Parse using recursive descent, as the
|
1369 |
+
// sub-grammar here is not an operator grammar.
|
1370 |
+
$n = new JSNode($this->t, JS_ARRAY_INIT);
|
1371 |
+
while (($tt = $this->t->peek()) != OP_RIGHT_BRACKET)
|
1372 |
+
{
|
1373 |
+
if ($tt == OP_COMMA)
|
1374 |
+
{
|
1375 |
+
$this->t->get();
|
1376 |
+
$n->addNode(null);
|
1377 |
+
continue;
|
1378 |
+
}
|
1379 |
+
|
1380 |
+
$n->addNode($this->Expression($x, OP_COMMA));
|
1381 |
+
if (!$this->t->match(OP_COMMA))
|
1382 |
+
break;
|
1383 |
+
}
|
1384 |
+
|
1385 |
+
$this->t->mustMatch(OP_RIGHT_BRACKET);
|
1386 |
+
array_push($operands, $n);
|
1387 |
+
$this->t->scanOperand = false;
|
1388 |
+
}
|
1389 |
+
else
|
1390 |
+
{
|
1391 |
+
// Property indexing operator.
|
1392 |
+
array_push($operators, new JSNode($this->t, JS_INDEX));
|
1393 |
+
$this->t->scanOperand = true;
|
1394 |
+
++$x->bracketLevel;
|
1395 |
+
}
|
1396 |
+
break;
|
1397 |
+
|
1398 |
+
case OP_RIGHT_BRACKET:
|
1399 |
+
if ($this->t->scanOperand || $x->bracketLevel == $bl)
|
1400 |
+
break 2;
|
1401 |
+
|
1402 |
+
while ($this->reduce($operators, $operands)->type != JS_INDEX)
|
1403 |
+
continue;
|
1404 |
+
|
1405 |
+
--$x->bracketLevel;
|
1406 |
+
break;
|
1407 |
+
|
1408 |
+
case OP_LEFT_CURLY:
|
1409 |
+
if (!$this->t->scanOperand)
|
1410 |
+
break 2;
|
1411 |
+
|
1412 |
+
// Object initialiser. As for array initialisers (see above),
|
1413 |
+
// parse using recursive descent.
|
1414 |
+
++$x->curlyLevel;
|
1415 |
+
$n = new JSNode($this->t, JS_OBJECT_INIT);
|
1416 |
+
while (!$this->t->match(OP_RIGHT_CURLY))
|
1417 |
+
{
|
1418 |
+
do
|
1419 |
+
{
|
1420 |
+
$tt = $this->t->get();
|
1421 |
+
$tv = $this->t->currentToken()->value;
|
1422 |
+
if (($tv == 'get' || $tv == 'set') && $this->t->peek() == TOKEN_IDENTIFIER)
|
1423 |
+
{
|
1424 |
+
if ($x->ecmaStrictMode)
|
1425 |
+
throw $this->t->newSyntaxError('Illegal property accessor');
|
1426 |
+
|
1427 |
+
$n->addNode($this->FunctionDefinition($x, true, EXPRESSED_FORM));
|
1428 |
+
}
|
1429 |
+
else
|
1430 |
+
{
|
1431 |
+
switch ($tt)
|
1432 |
+
{
|
1433 |
+
case TOKEN_IDENTIFIER:
|
1434 |
+
case TOKEN_NUMBER:
|
1435 |
+
case TOKEN_STRING:
|
1436 |
+
$id = new JSNode($this->t);
|
1437 |
+
break;
|
1438 |
+
|
1439 |
+
case OP_RIGHT_CURLY:
|
1440 |
+
if ($x->ecmaStrictMode)
|
1441 |
+
throw $this->t->newSyntaxError('Illegal trailing ,');
|
1442 |
+
break 3;
|
1443 |
+
|
1444 |
+
default:
|
1445 |
+
throw $this->t->newSyntaxError('Invalid property name');
|
1446 |
+
}
|
1447 |
+
|
1448 |
+
$this->t->mustMatch(OP_COLON);
|
1449 |
+
$n->addNode(new JSNode($this->t, JS_PROPERTY_INIT, $id, $this->Expression($x, OP_COMMA)));
|
1450 |
+
}
|
1451 |
+
}
|
1452 |
+
while ($this->t->match(OP_COMMA));
|
1453 |
+
|
1454 |
+
$this->t->mustMatch(OP_RIGHT_CURLY);
|
1455 |
+
break;
|
1456 |
+
}
|
1457 |
+
|
1458 |
+
array_push($operands, $n);
|
1459 |
+
$this->t->scanOperand = false;
|
1460 |
+
--$x->curlyLevel;
|
1461 |
+
break;
|
1462 |
+
|
1463 |
+
case OP_RIGHT_CURLY:
|
1464 |
+
if (!$this->t->scanOperand && $x->curlyLevel != $cl)
|
1465 |
+
throw new Exception('PANIC: right curly botch');
|
1466 |
+
break 2;
|
1467 |
+
|
1468 |
+
case OP_LEFT_PAREN:
|
1469 |
+
if ($this->t->scanOperand)
|
1470 |
+
{
|
1471 |
+
array_push($operators, new JSNode($this->t, JS_GROUP));
|
1472 |
+
}
|
1473 |
+
else
|
1474 |
+
{
|
1475 |
+
while ( !empty($operators) &&
|
1476 |
+
$this->opPrecedence[end($operators)->type] > $this->opPrecedence[KEYWORD_NEW]
|
1477 |
+
)
|
1478 |
+
$this->reduce($operators, $operands);
|
1479 |
+
|
1480 |
+
// Handle () now, to regularize the n-ary case for n > 0.
|
1481 |
+
// We must set scanOperand in case there are arguments and
|
1482 |
+
// the first one is a regexp or unary+/-.
|
1483 |
+
$n = end($operators);
|
1484 |
+
$this->t->scanOperand = true;
|
1485 |
+
if ($this->t->match(OP_RIGHT_PAREN))
|
1486 |
+
{
|
1487 |
+
if ($n && $n->type == KEYWORD_NEW)
|
1488 |
+
{
|
1489 |
+
array_pop($operators);
|
1490 |
+
$n->addNode(array_pop($operands));
|
1491 |
+
}
|
1492 |
+
else
|
1493 |
+
{
|
1494 |
+
$n = new JSNode($this->t, JS_CALL, array_pop($operands), new JSNode($this->t, JS_LIST));
|
1495 |
+
}
|
1496 |
+
|
1497 |
+
array_push($operands, $n);
|
1498 |
+
$this->t->scanOperand = false;
|
1499 |
+
break;
|
1500 |
+
}
|
1501 |
+
|
1502 |
+
if ($n && $n->type == KEYWORD_NEW)
|
1503 |
+
$n->type = JS_NEW_WITH_ARGS;
|
1504 |
+
else
|
1505 |
+
array_push($operators, new JSNode($this->t, JS_CALL));
|
1506 |
+
}
|
1507 |
+
|
1508 |
+
++$x->parenLevel;
|
1509 |
+
break;
|
1510 |
+
|
1511 |
+
case OP_RIGHT_PAREN:
|
1512 |
+
if ($this->t->scanOperand || $x->parenLevel == $pl)
|
1513 |
+
break 2;
|
1514 |
+
|
1515 |
+
while (($tt = $this->reduce($operators, $operands)->type) != JS_GROUP &&
|
1516 |
+
$tt != JS_CALL && $tt != JS_NEW_WITH_ARGS
|
1517 |
+
)
|
1518 |
+
{
|
1519 |
+
continue;
|
1520 |
+
}
|
1521 |
+
|
1522 |
+
if ($tt != JS_GROUP)
|
1523 |
+
{
|
1524 |
+
$n = end($operands);
|
1525 |
+
if ($n->treeNodes[1]->type != OP_COMMA)
|
1526 |
+
$n->treeNodes[1] = new JSNode($this->t, JS_LIST, $n->treeNodes[1]);
|
1527 |
+
else
|
1528 |
+
$n->treeNodes[1]->type = JS_LIST;
|
1529 |
+
}
|
1530 |
+
|
1531 |
+
--$x->parenLevel;
|
1532 |
+
break;
|
1533 |
+
|
1534 |
+
// Automatic semicolon insertion means we may scan across a newline
|
1535 |
+
// and into the beginning of another statement. If so, break out of
|
1536 |
+
// the while loop and let the t.scanOperand logic handle errors.
|
1537 |
+
default:
|
1538 |
+
break 2;
|
1539 |
+
}
|
1540 |
+
}
|
1541 |
+
|
1542 |
+
if ($x->hookLevel != $hl)
|
1543 |
+
throw $this->t->newSyntaxError('Missing : in conditional expression');
|
1544 |
+
|
1545 |
+
if ($x->parenLevel != $pl)
|
1546 |
+
throw $this->t->newSyntaxError('Missing ) in parenthetical');
|
1547 |
+
|
1548 |
+
if ($x->bracketLevel != $bl)
|
1549 |
+
throw $this->t->newSyntaxError('Missing ] in index expression');
|
1550 |
+
|
1551 |
+
if ($this->t->scanOperand)
|
1552 |
+
throw $this->t->newSyntaxError('Missing operand');
|
1553 |
+
|
1554 |
+
// Resume default mode, scanning for operands, not operators.
|
1555 |
+
$this->t->scanOperand = true;
|
1556 |
+
$this->t->unget();
|
1557 |
+
|
1558 |
+
while (count($operators))
|
1559 |
+
$this->reduce($operators, $operands);
|
1560 |
+
|
1561 |
+
return array_pop($operands);
|
1562 |
+
}
|
1563 |
+
|
1564 |
+
private function ParenExpression($x)
|
1565 |
+
{
|
1566 |
+
$this->t->mustMatch(OP_LEFT_PAREN);
|
1567 |
+
$n = $this->Expression($x);
|
1568 |
+
$this->t->mustMatch(OP_RIGHT_PAREN);
|
1569 |
+
|
1570 |
+
return $n;
|
1571 |
+
}
|
1572 |
+
|
1573 |
+
// Statement stack and nested statement handler.
|
1574 |
+
private function nest($x, $node, $end = false)
|
1575 |
+
{
|
1576 |
+
array_push($x->stmtStack, $node);
|
1577 |
+
$n = $this->statement($x);
|
1578 |
+
array_pop($x->stmtStack);
|
1579 |
+
|
1580 |
+
if ($end)
|
1581 |
+
$this->t->mustMatch($end);
|
1582 |
+
|
1583 |
+
return $n;
|
1584 |
+
}
|
1585 |
+
|
1586 |
+
private function reduce(&$operators, &$operands)
|
1587 |
+
{
|
1588 |
+
$n = array_pop($operators);
|
1589 |
+
$op = $n->type;
|
1590 |
+
$arity = $this->opArity[$op];
|
1591 |
+
$c = count($operands);
|
1592 |
+
if ($arity == -2)
|
1593 |
+
{
|
1594 |
+
// Flatten left-associative trees
|
1595 |
+
if ($c >= 2)
|
1596 |
+
{
|
1597 |
+
$left = $operands[$c - 2];
|
1598 |
+
if ($left->type == $op)
|
1599 |
+
{
|
1600 |
+
$right = array_pop($operands);
|
1601 |
+
$left->addNode($right);
|
1602 |
+
return $left;
|
1603 |
+
}
|
1604 |
+
}
|
1605 |
+
$arity = 2;
|
1606 |
+
}
|
1607 |
+
|
1608 |
+
// Always use push to add operands to n, to update start and end
|
1609 |
+
$a = array_splice($operands, $c - $arity);
|
1610 |
+
for ($i = 0; $i < $arity; $i++)
|
1611 |
+
$n->addNode($a[$i]);
|
1612 |
+
|
1613 |
+
// Include closing bracket or postfix operator in [start,end]
|
1614 |
+
$te = $this->t->currentToken()->end;
|
1615 |
+
if ($n->end < $te)
|
1616 |
+
$n->end = $te;
|
1617 |
+
|
1618 |
+
array_push($operands, $n);
|
1619 |
+
|
1620 |
+
return $n;
|
1621 |
+
}
|
1622 |
+
}
|
1623 |
+
|
1624 |
+
class JSCompilerContext
|
1625 |
+
{
|
1626 |
+
public $inFunction = false;
|
1627 |
+
public $inForLoopInit = false;
|
1628 |
+
public $ecmaStrictMode = false;
|
1629 |
+
public $bracketLevel = 0;
|
1630 |
+
public $curlyLevel = 0;
|
1631 |
+
public $parenLevel = 0;
|
1632 |
+
public $hookLevel = 0;
|
1633 |
+
|
1634 |
+
public $stmtStack = array();
|
1635 |
+
public $funDecls = array();
|
1636 |
+
public $varDecls = array();
|
1637 |
+
|
1638 |
+
public function __construct($inFunction)
|
1639 |
+
{
|
1640 |
+
$this->inFunction = $inFunction;
|
1641 |
+
}
|
1642 |
+
}
|
1643 |
+
|
1644 |
+
class JSNode
|
1645 |
+
{
|
1646 |
+
private $type;
|
1647 |
+
private $value;
|
1648 |
+
private $lineno;
|
1649 |
+
private $start;
|
1650 |
+
private $end;
|
1651 |
+
|
1652 |
+
public $treeNodes = array();
|
1653 |
+
public $funDecls = array();
|
1654 |
+
public $varDecls = array();
|
1655 |
+
|
1656 |
+
public function __construct($t, $type=0)
|
1657 |
+
{
|
1658 |
+
if ($token = $t->currentToken())
|
1659 |
+
{
|
1660 |
+
$this->type = $type ? $type : $token->type;
|
1661 |
+
$this->value = $token->value;
|
1662 |
+
$this->lineno = $token->lineno;
|
1663 |
+
$this->start = $token->start;
|
1664 |
+
$this->end = $token->end;
|
1665 |
+
}
|
1666 |
+
else
|
1667 |
+
{
|
1668 |
+
$this->type = $type;
|
1669 |
+
$this->lineno = $t->lineno;
|
1670 |
+
}
|
1671 |
+
|
1672 |
+
if (($numargs = func_num_args()) > 2)
|
1673 |
+
{
|
1674 |
+
$args = func_get_args();
|
1675 |
+
for ($i = 2; $i < $numargs; $i++)
|
1676 |
+
$this->addNode($args[$i]);
|
1677 |
+
}
|
1678 |
+
}
|
1679 |
+
|
1680 |
+
// we don't want to bloat our object with all kind of specific properties, so we use overloading
|
1681 |
+
public function __set($name, $value)
|
1682 |
+
{
|
1683 |
+
$this->$name = $value;
|
1684 |
+
}
|
1685 |
+
|
1686 |
+
public function __get($name)
|
1687 |
+
{
|
1688 |
+
if (isset($this->$name))
|
1689 |
+
return $this->$name;
|
1690 |
+
|
1691 |
+
return null;
|
1692 |
+
}
|
1693 |
+
|
1694 |
+
public function addNode($node)
|
1695 |
+
{
|
1696 |
+
if ($node !== null)
|
1697 |
+
{
|
1698 |
+
if ($node->start < $this->start)
|
1699 |
+
$this->start = $node->start;
|
1700 |
+
if ($this->end < $node->end)
|
1701 |
+
$this->end = $node->end;
|
1702 |
+
}
|
1703 |
+
|
1704 |
+
$this->treeNodes[] = $node;
|
1705 |
+
}
|
1706 |
+
}
|
1707 |
+
|
1708 |
+
class JSTokenizer
|
1709 |
+
{
|
1710 |
+
private $cursor = 0;
|
1711 |
+
private $source;
|
1712 |
+
|
1713 |
+
public $tokens = array();
|
1714 |
+
public $tokenIndex = 0;
|
1715 |
+
public $lookahead = 0;
|
1716 |
+
public $scanNewlines = false;
|
1717 |
+
public $scanOperand = true;
|
1718 |
+
|
1719 |
+
public $filename;
|
1720 |
+
public $lineno;
|
1721 |
+
|
1722 |
+
private $keywords = array(
|
1723 |
+
'break',
|
1724 |
+
'case', 'catch', 'const', 'continue',
|
1725 |
+
'debugger', 'default', 'delete', 'do',
|
1726 |
+
'else', 'enum',
|
1727 |
+
'false', 'finally', 'for', 'function',
|
1728 |
+
'if', 'in', 'instanceof',
|
1729 |
+
'new', 'null',
|
1730 |
+
'return',
|
1731 |
+
'switch',
|
1732 |
+
'this', 'throw', 'true', 'try', 'typeof',
|
1733 |
+
'var', 'void',
|
1734 |
+
'while', 'with'
|
1735 |
+
);
|
1736 |
+
|
1737 |
+
private $opTypeNames = array(
|
1738 |
+
';', ',', '?', ':', '||', '&&', '|', '^',
|
1739 |
+
'&', '===', '==', '=', '!==', '!=', '<<', '<=',
|
1740 |
+
'<', '>>>', '>>', '>=', '>', '++', '--', '+',
|
1741 |
+
'-', '*', '/', '%', '!', '~', '.', '[',
|
1742 |
+
']', '{', '}', '(', ')', '@*/'
|
1743 |
+
);
|
1744 |
+
|
1745 |
+
private $assignOps = array('|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%');
|
1746 |
+
private $opRegExp;
|
1747 |
+
|
1748 |
+
public function __construct()
|
1749 |
+
{
|
1750 |
+
$this->opRegExp = '#^(' . implode('|', array_map('preg_quote', $this->opTypeNames)) . ')#';
|
1751 |
+
}
|
1752 |
+
|
1753 |
+
public function init($source, $filename = '', $lineno = 1)
|
1754 |
+
{
|
1755 |
+
$this->source = $source;
|
1756 |
+
$this->filename = $filename ? $filename : '[inline]';
|
1757 |
+
$this->lineno = $lineno;
|
1758 |
+
|
1759 |
+
$this->cursor = 0;
|
1760 |
+
$this->tokens = array();
|
1761 |
+
$this->tokenIndex = 0;
|
1762 |
+
$this->lookahead = 0;
|
1763 |
+
$this->scanNewlines = false;
|
1764 |
+
$this->scanOperand = true;
|
1765 |
+
}
|
1766 |
+
|
1767 |
+
public function getInput($chunksize)
|
1768 |
+
{
|
1769 |
+
if ($chunksize)
|
1770 |
+
return substr($this->source, $this->cursor, $chunksize);
|
1771 |
+
|
1772 |
+
return substr($this->source, $this->cursor);
|
1773 |
+
}
|
1774 |
+
|
1775 |
+
public function isDone()
|
1776 |
+
{
|
1777 |
+
return $this->peek() == TOKEN_END;
|
1778 |
+
}
|
1779 |
+
|
1780 |
+
public function match($tt)
|
1781 |
+
{
|
1782 |
+
return $this->get() == $tt || $this->unget();
|
1783 |
+
}
|
1784 |
+
|
1785 |
+
public function mustMatch($tt)
|
1786 |
+
{
|
1787 |
+
if (!$this->match($tt))
|
1788 |
+
throw $this->newSyntaxError('Unexpected token; token ' . $tt . ' expected');
|
1789 |
+
|
1790 |
+
return $this->currentToken();
|
1791 |
+
}
|
1792 |
+
|
1793 |
+
public function peek()
|
1794 |
+
{
|
1795 |
+
if ($this->lookahead)
|
1796 |
+
{
|
1797 |
+
$next = $this->tokens[($this->tokenIndex + $this->lookahead) & 3];
|
1798 |
+
if ($this->scanNewlines && $next->lineno != $this->lineno)
|
1799 |
+
$tt = TOKEN_NEWLINE;
|
1800 |
+
else
|
1801 |
+
$tt = $next->type;
|
1802 |
+
}
|
1803 |
+
else
|
1804 |
+
{
|
1805 |
+
$tt = $this->get();
|
1806 |
+
$this->unget();
|
1807 |
+
}
|
1808 |
+
|
1809 |
+
return $tt;
|
1810 |
+
}
|
1811 |
+
|
1812 |
+
public function peekOnSameLine()
|
1813 |
+
{
|
1814 |
+
$this->scanNewlines = true;
|
1815 |
+
$tt = $this->peek();
|
1816 |
+
$this->scanNewlines = false;
|
1817 |
+
|
1818 |
+
return $tt;
|
1819 |
+
}
|
1820 |
+
|
1821 |
+
public function currentToken()
|
1822 |
+
{
|
1823 |
+
if (!empty($this->tokens))
|
1824 |
+
return $this->tokens[$this->tokenIndex];
|
1825 |
+
}
|
1826 |
+
|
1827 |
+
public function get($chunksize = 1000)
|
1828 |
+
{
|
1829 |
+
while($this->lookahead)
|
1830 |
+
{
|
1831 |
+
$this->lookahead--;
|
1832 |
+
$this->tokenIndex = ($this->tokenIndex + 1) & 3;
|
1833 |
+
$token = $this->tokens[$this->tokenIndex];
|
1834 |
+
if ($token->type != TOKEN_NEWLINE || $this->scanNewlines)
|
1835 |
+
return $token->type;
|
1836 |
+
}
|
1837 |
+
|
1838 |
+
$conditional_comment = false;
|
1839 |
+
|
1840 |
+
// strip whitespace and comments
|
1841 |
+
while(true)
|
1842 |
+
{
|
1843 |
+
$input = $this->getInput($chunksize);
|
1844 |
+
|
1845 |
+
// whitespace handling; gobble up \r as well (effectively we don't have support for MAC newlines!)
|
1846 |
+
$re = $this->scanNewlines ? '/^[ \r\t]+/' : '/^\s+/';
|
1847 |
+
if (preg_match($re, $input, $match))
|
1848 |
+
{
|
1849 |
+
$spaces = $match[0];
|
1850 |
+
$spacelen = strlen($spaces);
|
1851 |
+
$this->cursor += $spacelen;
|
1852 |
+
if (!$this->scanNewlines)
|
1853 |
+
$this->lineno += substr_count($spaces, "\n");
|
1854 |
+
|
1855 |
+
if ($spacelen == $chunksize)
|
1856 |
+
continue; // complete chunk contained whitespace
|
1857 |
+
|
1858 |
+
$input = $this->getInput($chunksize);
|
1859 |
+
if ($input == '' || $input[0] != '/')
|
1860 |
+
break;
|
1861 |
+
}
|
1862 |
+
|
1863 |
+
// Comments
|
1864 |
+
if (!preg_match('/^\/(?:\*(@(?:cc_on|if|elif|else|end))?.*?\*\/|\/[^\n]*)/s', $input, $match))
|
1865 |
+
{
|
1866 |
+
if (!$chunksize)
|
1867 |
+
break;
|
1868 |
+
|
1869 |
+
// retry with a full chunk fetch; this also prevents breakage of long regular expressions (which will never match a comment)
|
1870 |
+
$chunksize = null;
|
1871 |
+
continue;
|
1872 |
+
}
|
1873 |
+
|
1874 |
+
// check if this is a conditional (JScript) comment
|
1875 |
+
if (!empty($match[1]))
|
1876 |
+
{
|
1877 |
+
$match[0] = '/*' . $match[1];
|
1878 |
+
$conditional_comment = true;
|
1879 |
+
break;
|
1880 |
+
}
|
1881 |
+
else
|
1882 |
+
{
|
1883 |
+
$this->cursor += strlen($match[0]);
|
1884 |
+
$this->lineno += substr_count($match[0], "\n");
|
1885 |
+
}
|
1886 |
+
}
|
1887 |
+
|
1888 |
+
if ($input == '')
|
1889 |
+
{
|
1890 |
+
$tt = TOKEN_END;
|
1891 |
+
$match = array('');
|
1892 |
+
}
|
1893 |
+
elseif ($conditional_comment)
|
1894 |
+
{
|
1895 |
+
$tt = TOKEN_CONDCOMMENT_START;
|
1896 |
+
}
|
1897 |
+
else
|
1898 |
+
{
|
1899 |
+
switch ($input[0])
|
1900 |
+
{
|
1901 |
+
case '0':
|
1902 |
+
// hexadecimal
|
1903 |
+
if (($input[1] == 'x' || $input[1] == 'X') && preg_match('/^0x[0-9a-f]+/i', $input, $match))
|
1904 |
+
{
|
1905 |
+
$tt = TOKEN_NUMBER;
|
1906 |
+
break;
|
1907 |
+
}
|
1908 |
+
// FALL THROUGH
|
1909 |
+
|
1910 |
+
case '1': case '2': case '3': case '4': case '5':
|
1911 |
+
case '6': case '7': case '8': case '9':
|
1912 |
+
// should always match
|
1913 |
+
preg_match('/^\d+(?:\.\d*)?(?:[eE][-+]?\d+)?/', $input, $match);
|
1914 |
+
$tt = TOKEN_NUMBER;
|
1915 |
+
break;
|
1916 |
+
|
1917 |
+
case "'":
|
1918 |
+
if (preg_match('/^\'(?:[^\\\\\'\r\n]++|\\\\(?:.|\r?\n))*\'/', $input, $match))
|
1919 |
+
{
|
1920 |
+
$tt = TOKEN_STRING;
|
1921 |
+
}
|
1922 |
+
else
|
1923 |
+
{
|
1924 |
+
if ($chunksize)
|
1925 |
+
return $this->get(null); // retry with a full chunk fetch
|
1926 |
+
|
1927 |
+
throw $this->newSyntaxError('Unterminated string literal');
|
1928 |
+
}
|
1929 |
+
break;
|
1930 |
+
|
1931 |
+
case '"':
|
1932 |
+
if (preg_match('/^"(?:[^\\\\"\r\n]++|\\\\(?:.|\r?\n))*"/', $input, $match))
|
1933 |
+
{
|
1934 |
+
$tt = TOKEN_STRING;
|
1935 |
+
}
|
1936 |
+
else
|
1937 |
+
{
|
1938 |
+
if ($chunksize)
|
1939 |
+
return $this->get(null); // retry with a full chunk fetch
|
1940 |
+
|
1941 |
+
throw $this->newSyntaxError('Unterminated string literal');
|
1942 |
+
}
|
1943 |
+
break;
|
1944 |
+
|
1945 |
+
case '/':
|
1946 |
+
if ($this->scanOperand && preg_match('/^\/((?:\\\\.|\[(?:\\\\.|[^\]])*\]|[^\/])+)\/([gimy]*)/', $input, $match))
|
1947 |
+
{
|
1948 |
+
$tt = TOKEN_REGEXP;
|
1949 |
+
break;
|
1950 |
+
}
|
1951 |
+
// FALL THROUGH
|
1952 |
+
|
1953 |
+
case '|':
|
1954 |
+
case '^':
|
1955 |
+
case '&':
|
1956 |
+
case '<':
|
1957 |
+
case '>':
|
1958 |
+
case '+':
|
1959 |
+
case '-':
|
1960 |
+
case '*':
|
1961 |
+
case '%':
|
1962 |
+
case '=':
|
1963 |
+
case '!':
|
1964 |
+
// should always match
|
1965 |
+
preg_match($this->opRegExp, $input, $match);
|
1966 |
+
$op = $match[0];
|
1967 |
+
if (in_array($op, $this->assignOps) && $input[strlen($op)] == '=')
|
1968 |
+
{
|
1969 |
+
$tt = OP_ASSIGN;
|
1970 |
+
$match[0] .= '=';
|
1971 |
+
}
|
1972 |
+
else
|
1973 |
+
{
|
1974 |
+
$tt = $op;
|
1975 |
+
if ($this->scanOperand)
|
1976 |
+
{
|
1977 |
+
if ($op == OP_PLUS)
|
1978 |
+
$tt = OP_UNARY_PLUS;
|
1979 |
+
elseif ($op == OP_MINUS)
|
1980 |
+
$tt = OP_UNARY_MINUS;
|
1981 |
+
}
|
1982 |
+
$op = null;
|
1983 |
+
}
|
1984 |
+
break;
|
1985 |
+
|
1986 |
+
case '.':
|
1987 |
+
if (preg_match('/^\.\d+(?:[eE][-+]?\d+)?/', $input, $match))
|
1988 |
+
{
|
1989 |
+
$tt = TOKEN_NUMBER;
|
1990 |
+
break;
|
1991 |
+
}
|
1992 |
+
// FALL THROUGH
|
1993 |
+
|
1994 |
+
case ';':
|
1995 |
+
case ',':
|
1996 |
+
case '?':
|
1997 |
+
case ':':
|
1998 |
+
case '~':
|
1999 |
+
case '[':
|
2000 |
+
case ']':
|
2001 |
+
case '{':
|
2002 |
+
case '}':
|
2003 |
+
case '(':
|
2004 |
+
case ')':
|
2005 |
+
// these are all single
|
2006 |
+
$match = array($input[0]);
|
2007 |
+
$tt = $input[0];
|
2008 |
+
break;
|
2009 |
+
|
2010 |
+
case '@':
|
2011 |
+
// check end of conditional comment
|
2012 |
+
if (substr($input, 0, 3) == '@*/')
|
2013 |
+
{
|
2014 |
+
$match = array('@*/');
|
2015 |
+
$tt = TOKEN_CONDCOMMENT_END;
|
2016 |
+
}
|
2017 |
+
else
|
2018 |
+
throw $this->newSyntaxError('Illegal token');
|
2019 |
+
break;
|
2020 |
+
|
2021 |
+
case "\n":
|
2022 |
+
if ($this->scanNewlines)
|
2023 |
+
{
|
2024 |
+
$match = array("\n");
|
2025 |
+
$tt = TOKEN_NEWLINE;
|
2026 |
+
}
|
2027 |
+
else
|
2028 |
+
throw $this->newSyntaxError('Illegal token');
|
2029 |
+
break;
|
2030 |
+
|
2031 |
+
default:
|
2032 |
+
// FIXME: add support for unicode and unicode escape sequence \uHHHH
|
2033 |
+
if (preg_match('/^[$\w]+/', $input, $match))
|
2034 |
+
{
|
2035 |
+
$tt = in_array($match[0], $this->keywords) ? $match[0] : TOKEN_IDENTIFIER;
|
2036 |
+
}
|
2037 |
+
else
|
2038 |
+
throw $this->newSyntaxError('Illegal token');
|
2039 |
+
}
|
2040 |
+
}
|
2041 |
+
|
2042 |
+
$this->tokenIndex = ($this->tokenIndex + 1) & 3;
|
2043 |
+
|
2044 |
+
if (!isset($this->tokens[$this->tokenIndex]))
|
2045 |
+
$this->tokens[$this->tokenIndex] = new JSToken();
|
2046 |
+
|
2047 |
+
$token = $this->tokens[$this->tokenIndex];
|
2048 |
+
$token->type = $tt;
|
2049 |
+
|
2050 |
+
if ($tt == OP_ASSIGN)
|
2051 |
+
$token->assignOp = $op;
|
2052 |
+
|
2053 |
+
$token->start = $this->cursor;
|
2054 |
+
|
2055 |
+
$token->value = $match[0];
|
2056 |
+
$this->cursor += strlen($match[0]);
|
2057 |
+
|
2058 |
+
$token->end = $this->cursor;
|
2059 |
+
$token->lineno = $this->lineno;
|
2060 |
+
|
2061 |
+
return $tt;
|
2062 |
+
}
|
2063 |
+
|
2064 |
+
public function unget()
|
2065 |
+
{
|
2066 |
+
if (++$this->lookahead == 4)
|
2067 |
+
throw $this->newSyntaxError('PANIC: too much lookahead!');
|
2068 |
+
|
2069 |
+
$this->tokenIndex = ($this->tokenIndex - 1) & 3;
|
2070 |
+
}
|
2071 |
+
|
2072 |
+
public function newSyntaxError($m)
|
2073 |
+
{
|
2074 |
+
return new Exception('Parse error: ' . $m . ' in file \'' . $this->filename . '\' on line ' . $this->lineno);
|
2075 |
+
}
|
2076 |
+
}
|
2077 |
+
|
2078 |
+
class JSToken
|
2079 |
+
{
|
2080 |
+
public $type;
|
2081 |
+
public $value;
|
2082 |
+
public $start;
|
2083 |
+
public $end;
|
2084 |
+
public $lineno;
|
2085 |
+
public $assignOp;
|
2086 |
+
}
|
min/lib/Minify.php
ADDED
@@ -0,0 +1,641 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Minify - Combines, minifies, and caches JavaScript and CSS files on demand.
|
9 |
+
*
|
10 |
+
* See README for usage instructions (for now).
|
11 |
+
*
|
12 |
+
* This library was inspired by {@link mailto:flashkot@mail.ru jscsscomp by Maxim Martynyuk}
|
13 |
+
* and by the article {@link http://www.hunlock.com/blogs/Supercharged_Javascript "Supercharged JavaScript" by Patrick Hunlock}.
|
14 |
+
*
|
15 |
+
* Requires PHP 5.1.0.
|
16 |
+
* Tested on PHP 5.1.6.
|
17 |
+
*
|
18 |
+
* @package Minify
|
19 |
+
* @author Ryan Grove <ryan@wonko.com>
|
20 |
+
* @author Stephen Clay <steve@mrclay.org>
|
21 |
+
* @copyright 2008 Ryan Grove, Stephen Clay. All rights reserved.
|
22 |
+
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
23 |
+
* @link http://code.google.com/p/minify/
|
24 |
+
*/
|
25 |
+
class Minify {
|
26 |
+
|
27 |
+
const VERSION = '2.1.7';
|
28 |
+
const TYPE_CSS = 'text/css';
|
29 |
+
const TYPE_HTML = 'text/html';
|
30 |
+
// there is some debate over the ideal JS Content-Type, but this is the
|
31 |
+
// Apache default and what Yahoo! uses..
|
32 |
+
const TYPE_JS = 'application/x-javascript';
|
33 |
+
const URL_DEBUG = 'http://code.google.com/p/minify/wiki/Debugging';
|
34 |
+
|
35 |
+
/**
|
36 |
+
* How many hours behind are the file modification times of uploaded files?
|
37 |
+
*
|
38 |
+
* If you upload files from Windows to a non-Windows server, Windows may report
|
39 |
+
* incorrect mtimes for the files. Immediately after modifying and uploading a
|
40 |
+
* file, use the touch command to update the mtime on the server. If the mtime
|
41 |
+
* jumps ahead by a number of hours, set this variable to that number. If the mtime
|
42 |
+
* moves back, this should not be needed.
|
43 |
+
*
|
44 |
+
* @var int $uploaderHoursBehind
|
45 |
+
*/
|
46 |
+
public static $uploaderHoursBehind = 0;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* If this string is not empty AND the serve() option 'bubbleCssImports' is
|
50 |
+
* NOT set, then serve() will check CSS files for @import declarations that
|
51 |
+
* appear too late in the combined stylesheet. If found, serve() will prepend
|
52 |
+
* the output with this warning.
|
53 |
+
*
|
54 |
+
* @var string $importWarning
|
55 |
+
*/
|
56 |
+
public static $importWarning = "/* See http://code.google.com/p/minify/wiki/CommonProblems#@imports_can_appear_in_invalid_locations_in_combined_CSS_files */\n";
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Has the DOCUMENT_ROOT been set in user code?
|
60 |
+
*
|
61 |
+
* @var bool
|
62 |
+
*/
|
63 |
+
public static $isDocRootSet = false;
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Holds the cacheId passed via BWP Minify
|
67 |
+
*
|
68 |
+
* @var string
|
69 |
+
*/
|
70 |
+
public static $cacheId;
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Specify a cache object (with identical interface as Minify_Cache_File) or
|
74 |
+
* a path to use with Minify_Cache_File.
|
75 |
+
*
|
76 |
+
* If not called, Minify will not use a cache and, for each 200 response, will
|
77 |
+
* need to recombine files, minify and encode the output.
|
78 |
+
*
|
79 |
+
* @param mixed $cache object with identical interface as Minify_Cache_File or
|
80 |
+
* a directory path, or null to disable caching. (default = '')
|
81 |
+
*
|
82 |
+
* @param bool $fileLocking (default = true) This only applies if the first
|
83 |
+
* parameter is a string.
|
84 |
+
*
|
85 |
+
* @return null
|
86 |
+
*/
|
87 |
+
public static function setCache($cache = '', $fileLocking = true)
|
88 |
+
{
|
89 |
+
if (is_string($cache)) {
|
90 |
+
self::$_cache = new Minify_Cache_File($cache, $fileLocking);
|
91 |
+
} else {
|
92 |
+
self::$_cache = $cache;
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Serve a request for a minified file.
|
98 |
+
*
|
99 |
+
* Here are the available options and defaults in the base controller:
|
100 |
+
*
|
101 |
+
* 'isPublic' : send "public" instead of "private" in Cache-Control
|
102 |
+
* headers, allowing shared caches to cache the output. (default true)
|
103 |
+
*
|
104 |
+
* 'quiet' : set to true to have serve() return an array rather than sending
|
105 |
+
* any headers/output (default false)
|
106 |
+
*
|
107 |
+
* 'encodeOutput' : set to false to disable content encoding, and not send
|
108 |
+
* the Vary header (default true)
|
109 |
+
*
|
110 |
+
* 'encodeMethod' : generally you should let this be determined by
|
111 |
+
* HTTP_Encoder (leave null), but you can force a particular encoding
|
112 |
+
* to be returned, by setting this to 'gzip' or '' (no encoding)
|
113 |
+
*
|
114 |
+
* 'encodeLevel' : level of encoding compression (0 to 9, default 9)
|
115 |
+
*
|
116 |
+
* 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey
|
117 |
+
* value to remove. (default 'utf-8')
|
118 |
+
*
|
119 |
+
* 'maxAge' : set this to the number of seconds the client should use its cache
|
120 |
+
* before revalidating with the server. This sets Cache-Control: max-age and the
|
121 |
+
* Expires header. Unlike the old 'setExpires' setting, this setting will NOT
|
122 |
+
* prevent conditional GETs. Note this has nothing to do with server-side caching.
|
123 |
+
*
|
124 |
+
* 'rewriteCssUris' : If true, serve() will automatically set the 'currentDir'
|
125 |
+
* minifier option to enable URI rewriting in CSS files (default true)
|
126 |
+
*
|
127 |
+
* 'bubbleCssImports' : If true, all @import declarations in combined CSS
|
128 |
+
* files will be move to the top. Note this may alter effective CSS values
|
129 |
+
* due to a change in order. (default false)
|
130 |
+
*
|
131 |
+
* 'debug' : set to true to minify all sources with the 'Lines' controller, which
|
132 |
+
* eases the debugging of combined files. This also prevents 304 responses.
|
133 |
+
* @see Minify_Lines::minify()
|
134 |
+
*
|
135 |
+
* 'minifiers' : to override Minify's default choice of minifier function for
|
136 |
+
* a particular content-type, specify your callback under the key of the
|
137 |
+
* content-type:
|
138 |
+
* <code>
|
139 |
+
* // call customCssMinifier($css) for all CSS minification
|
140 |
+
* $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier';
|
141 |
+
*
|
142 |
+
* // don't minify Javascript at all
|
143 |
+
* $options['minifiers'][Minify::TYPE_JS] = '';
|
144 |
+
* </code>
|
145 |
+
*
|
146 |
+
* 'minifierOptions' : to send options to the minifier function, specify your options
|
147 |
+
* under the key of the content-type. E.g. To send the CSS minifier an option:
|
148 |
+
* <code>
|
149 |
+
* // give CSS minifier array('optionName' => 'optionValue') as 2nd argument
|
150 |
+
* $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue';
|
151 |
+
* </code>
|
152 |
+
*
|
153 |
+
* 'contentType' : (optional) this is only needed if your file extension is not
|
154 |
+
* js/css/html. The given content-type will be sent regardless of source file
|
155 |
+
* extension, so this should not be used in a Groups config with other
|
156 |
+
* Javascript/CSS files.
|
157 |
+
*
|
158 |
+
* Any controller options are documented in that controller's setupSources() method.
|
159 |
+
*
|
160 |
+
* @param mixed $controller instance of subclass of Minify_Controller_Base or string
|
161 |
+
* name of controller. E.g. 'Files'
|
162 |
+
*
|
163 |
+
* @param array $options controller/serve options
|
164 |
+
*
|
165 |
+
* @return null|array if the 'quiet' option is set to true, an array
|
166 |
+
* with keys "success" (bool), "statusCode" (int), "content" (string), and
|
167 |
+
* "headers" (array).
|
168 |
+
*
|
169 |
+
* @throws Exception
|
170 |
+
*/
|
171 |
+
public static function serve($controller, $options = array())
|
172 |
+
{
|
173 |
+
if (! self::$isDocRootSet && 0 === stripos(PHP_OS, 'win')) {
|
174 |
+
self::setDocRoot();
|
175 |
+
}
|
176 |
+
|
177 |
+
if (is_string($controller)) {
|
178 |
+
// make $controller into object
|
179 |
+
$class = 'Minify_Controller_' . $controller;
|
180 |
+
$controller = new $class();
|
181 |
+
/* @var Minify_Controller_Base $controller */
|
182 |
+
}
|
183 |
+
|
184 |
+
// set up controller sources and mix remaining options with
|
185 |
+
// controller defaults
|
186 |
+
$options = $controller->setupSources($options);
|
187 |
+
$options = $controller->analyzeSources($options);
|
188 |
+
self::$_options = $controller->mixInDefaultOptions($options);
|
189 |
+
|
190 |
+
// check request validity
|
191 |
+
if (! $controller->sources) {
|
192 |
+
// invalid request!
|
193 |
+
if (! self::$_options['quiet']) {
|
194 |
+
self::_errorExit(self::$_options['badRequestHeader'], self::URL_DEBUG);
|
195 |
+
} else {
|
196 |
+
list(,$statusCode) = explode(' ', self::$_options['badRequestHeader']);
|
197 |
+
return array(
|
198 |
+
'success' => false
|
199 |
+
,'statusCode' => (int)$statusCode
|
200 |
+
,'content' => ''
|
201 |
+
,'headers' => array()
|
202 |
+
);
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
self::$_controller = $controller;
|
207 |
+
|
208 |
+
if (self::$_options['debug']) {
|
209 |
+
self::_setupDebug($controller->sources);
|
210 |
+
self::$_options['maxAge'] = 0;
|
211 |
+
}
|
212 |
+
|
213 |
+
// determine encoding
|
214 |
+
if (self::$_options['encodeOutput']) {
|
215 |
+
$sendVary = true;
|
216 |
+
if (self::$_options['encodeMethod'] !== null) {
|
217 |
+
// controller specifically requested this
|
218 |
+
$contentEncoding = self::$_options['encodeMethod'];
|
219 |
+
} else {
|
220 |
+
// sniff request header
|
221 |
+
// depending on what the client accepts, $contentEncoding may be
|
222 |
+
// 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
|
223 |
+
// getAcceptedEncoding(false, false) leaves out compress and deflate as options.
|
224 |
+
list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false);
|
225 |
+
$sendVary = ! HTTP_Encoder::isBuggyIe();
|
226 |
+
}
|
227 |
+
} else {
|
228 |
+
self::$_options['encodeMethod'] = ''; // identity (no encoding)
|
229 |
+
}
|
230 |
+
|
231 |
+
// check client cache
|
232 |
+
$cgOptions = array(
|
233 |
+
'lastModifiedTime' => self::$_options['lastModifiedTime']
|
234 |
+
,'isPublic' => self::$_options['isPublic']
|
235 |
+
,'encoding' => self::$_options['encodeMethod']
|
236 |
+
);
|
237 |
+
if (self::$_options['maxAge'] > 0) {
|
238 |
+
$cgOptions['maxAge'] = self::$_options['maxAge'];
|
239 |
+
} elseif (self::$_options['debug']) {
|
240 |
+
$cgOptions['invalidate'] = true;
|
241 |
+
}
|
242 |
+
$cg = new HTTP_ConditionalGet($cgOptions);
|
243 |
+
if ($cg->cacheIsValid) {
|
244 |
+
// client's cache is valid
|
245 |
+
if (! self::$_options['quiet']) {
|
246 |
+
$cg->sendHeaders();
|
247 |
+
return;
|
248 |
+
} else {
|
249 |
+
return array(
|
250 |
+
'success' => true
|
251 |
+
,'statusCode' => 304
|
252 |
+
,'content' => ''
|
253 |
+
,'headers' => $cg->getHeaders()
|
254 |
+
);
|
255 |
+
}
|
256 |
+
} else {
|
257 |
+
// client will need output
|
258 |
+
$headers = $cg->getHeaders();
|
259 |
+
unset($cg);
|
260 |
+
}
|
261 |
+
|
262 |
+
if (self::$_options['contentType'] === self::TYPE_CSS
|
263 |
+
&& self::$_options['rewriteCssUris']) {
|
264 |
+
foreach($controller->sources as $key => $source) {
|
265 |
+
if ($source->filepath
|
266 |
+
&& !isset($source->minifyOptions['currentDir'])
|
267 |
+
&& !isset($source->minifyOptions['prependRelativePath'])
|
268 |
+
) {
|
269 |
+
$source->minifyOptions['currentDir'] = dirname($source->filepath);
|
270 |
+
}
|
271 |
+
}
|
272 |
+
}
|
273 |
+
|
274 |
+
// check server cache
|
275 |
+
if (null !== self::$_cache && ! self::$_options['debug']) {
|
276 |
+
// using cache
|
277 |
+
// the goal is to use only the cache methods to sniff the length and
|
278 |
+
// output the content, as they do not require ever loading the file into
|
279 |
+
// memory.
|
280 |
+
$cacheId = self::_getCacheId();
|
281 |
+
$fullCacheId = (self::$_options['encodeMethod'])
|
282 |
+
? $cacheId . '.gz'
|
283 |
+
: $cacheId;
|
284 |
+
// check cache for valid entry
|
285 |
+
$cacheIsReady = self::$_cache->isValid($fullCacheId, self::$_options['lastModifiedTime']);
|
286 |
+
if ($cacheIsReady) {
|
287 |
+
$cacheContentLength = self::$_cache->getSize($fullCacheId);
|
288 |
+
} else {
|
289 |
+
// generate & cache content
|
290 |
+
try {
|
291 |
+
$content = self::_combineMinify();
|
292 |
+
} catch (Exception $e) {
|
293 |
+
self::$_controller->log($e->getMessage());
|
294 |
+
if (! self::$_options['quiet']) {
|
295 |
+
self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG);
|
296 |
+
}
|
297 |
+
throw $e;
|
298 |
+
}
|
299 |
+
self::$_cache->store($cacheId, $content);
|
300 |
+
if (function_exists('gzencode') && self::$_options['encodeMethod']) {
|
301 |
+
self::$_cache->store($cacheId . '.gz', gzencode($content, self::$_options['encodeLevel']));
|
302 |
+
}
|
303 |
+
}
|
304 |
+
} else {
|
305 |
+
// no cache
|
306 |
+
$cacheIsReady = false;
|
307 |
+
try {
|
308 |
+
$content = self::_combineMinify();
|
309 |
+
} catch (Exception $e) {
|
310 |
+
self::$_controller->log($e->getMessage());
|
311 |
+
if (! self::$_options['quiet']) {
|
312 |
+
self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG);
|
313 |
+
}
|
314 |
+
throw $e;
|
315 |
+
}
|
316 |
+
}
|
317 |
+
if (! $cacheIsReady && self::$_options['encodeMethod']) {
|
318 |
+
// still need to encode
|
319 |
+
$content = gzencode($content, self::$_options['encodeLevel']);
|
320 |
+
}
|
321 |
+
|
322 |
+
// add headers
|
323 |
+
$headers['Content-Length'] = $cacheIsReady
|
324 |
+
? $cacheContentLength
|
325 |
+
: ((function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
|
326 |
+
? mb_strlen($content, '8bit')
|
327 |
+
: strlen($content)
|
328 |
+
);
|
329 |
+
$headers['Content-Type'] = self::$_options['contentTypeCharset']
|
330 |
+
? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset']
|
331 |
+
: self::$_options['contentType'];
|
332 |
+
if (self::$_options['encodeMethod'] !== '') {
|
333 |
+
$headers['Content-Encoding'] = $contentEncoding;
|
334 |
+
}
|
335 |
+
if (self::$_options['encodeOutput'] && $sendVary) {
|
336 |
+
$headers['Vary'] = 'Accept-Encoding';
|
337 |
+
}
|
338 |
+
|
339 |
+
if (! self::$_options['quiet']) {
|
340 |
+
// output headers & content
|
341 |
+
foreach ($headers as $name => $val) {
|
342 |
+
header($name . ': ' . $val);
|
343 |
+
}
|
344 |
+
if ($cacheIsReady) {
|
345 |
+
self::$_cache->display($fullCacheId);
|
346 |
+
} else {
|
347 |
+
echo $content;
|
348 |
+
}
|
349 |
+
} else {
|
350 |
+
return array(
|
351 |
+
'success' => true
|
352 |
+
,'statusCode' => 200
|
353 |
+
,'content' => $cacheIsReady
|
354 |
+
? self::$_cache->fetch($fullCacheId)
|
355 |
+
: $content
|
356 |
+
,'headers' => $headers
|
357 |
+
);
|
358 |
+
}
|
359 |
+
}
|
360 |
+
|
361 |
+
/**
|
362 |
+
* Return combined minified content for a set of sources
|
363 |
+
*
|
364 |
+
* No internal caching will be used and the content will not be HTTP encoded.
|
365 |
+
*
|
366 |
+
* @param array $sources array of filepaths and/or Minify_Source objects
|
367 |
+
*
|
368 |
+
* @param array $options (optional) array of options for serve. By default
|
369 |
+
* these are already set: quiet = true, encodeMethod = '', lastModifiedTime = 0.
|
370 |
+
*
|
371 |
+
* @return string
|
372 |
+
*/
|
373 |
+
public static function combine($sources, $options = array())
|
374 |
+
{
|
375 |
+
$cache = self::$_cache;
|
376 |
+
self::$_cache = null;
|
377 |
+
$options = array_merge(array(
|
378 |
+
'files' => (array)$sources
|
379 |
+
,'quiet' => true
|
380 |
+
,'encodeMethod' => ''
|
381 |
+
,'lastModifiedTime' => 0
|
382 |
+
), $options);
|
383 |
+
$out = self::serve('Files', $options);
|
384 |
+
self::$_cache = $cache;
|
385 |
+
return $out['content'];
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* Set $_SERVER['DOCUMENT_ROOT']. On IIS, the value is created from SCRIPT_FILENAME and SCRIPT_NAME.
|
390 |
+
*
|
391 |
+
* @param string $docRoot value to use for DOCUMENT_ROOT
|
392 |
+
*/
|
393 |
+
public static function setDocRoot($docRoot = '')
|
394 |
+
{
|
395 |
+
self::$isDocRootSet = true;
|
396 |
+
if ($docRoot) {
|
397 |
+
$_SERVER['DOCUMENT_ROOT'] = $docRoot;
|
398 |
+
} elseif (isset($_SERVER['SERVER_SOFTWARE'])
|
399 |
+
&& 0 === strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/')) {
|
400 |
+
$_SERVER['DOCUMENT_ROOT'] = substr(
|
401 |
+
$_SERVER['SCRIPT_FILENAME']
|
402 |
+
,0
|
403 |
+
,strlen($_SERVER['SCRIPT_FILENAME']) - strlen($_SERVER['SCRIPT_NAME']));
|
404 |
+
$_SERVER['DOCUMENT_ROOT'] = rtrim($_SERVER['DOCUMENT_ROOT'], '\\');
|
405 |
+
}
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* Any Minify_Cache_* object or null (i.e. no server cache is used)
|
410 |
+
*
|
411 |
+
* @var Minify_Cache_File
|
412 |
+
*/
|
413 |
+
private static $_cache = null;
|
414 |
+
|
415 |
+
/**
|
416 |
+
* Active controller for current request
|
417 |
+
*
|
418 |
+
* @var Minify_Controller_Base
|
419 |
+
*/
|
420 |
+
protected static $_controller = null;
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Options for current request
|
424 |
+
*
|
425 |
+
* @var array
|
426 |
+
*/
|
427 |
+
protected static $_options = null;
|
428 |
+
|
429 |
+
/**
|
430 |
+
* @param string $header
|
431 |
+
*
|
432 |
+
* @param string $url
|
433 |
+
*/
|
434 |
+
protected static function _errorExit($header, $url)
|
435 |
+
{
|
436 |
+
$url = htmlspecialchars($url, ENT_QUOTES);
|
437 |
+
list(,$h1) = explode(' ', $header, 2);
|
438 |
+
$h1 = htmlspecialchars($h1);
|
439 |
+
// FastCGI environments require 3rd arg to header() to be set
|
440 |
+
list(, $code) = explode(' ', $header, 3);
|
441 |
+
header($header, true, $code);
|
442 |
+
header('Content-Type: text/html; charset=utf-8');
|
443 |
+
echo "<h1>$h1</h1>";
|
444 |
+
echo "<p>Please see <a href='$url'>$url</a>.</p>";
|
445 |
+
exit();
|
446 |
+
}
|
447 |
+
|
448 |
+
/**
|
449 |
+
* Set up sources to use Minify_Lines
|
450 |
+
*
|
451 |
+
* @param Minify_Source[] $sources Minify_Source instances
|
452 |
+
*/
|
453 |
+
protected static function _setupDebug($sources)
|
454 |
+
{
|
455 |
+
foreach ($sources as $source) {
|
456 |
+
$source->minifier = array('Minify_Lines', 'minify');
|
457 |
+
$id = $source->getId();
|
458 |
+
$source->minifyOptions = array(
|
459 |
+
'id' => (is_file($id) ? basename($id) : $id)
|
460 |
+
);
|
461 |
+
}
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Combines sources and minifies the result.
|
466 |
+
*
|
467 |
+
* @return string
|
468 |
+
*
|
469 |
+
* @throws Exception
|
470 |
+
*/
|
471 |
+
protected static function _combineMinify()
|
472 |
+
{
|
473 |
+
$type = self::$_options['contentType']; // ease readability
|
474 |
+
|
475 |
+
// when combining scripts, make sure all statements separated and
|
476 |
+
// trailing single line comment is terminated
|
477 |
+
$implodeSeparator = ($type === self::TYPE_JS)
|
478 |
+
? "\n;"
|
479 |
+
: '';
|
480 |
+
// allow the user to pass a particular array of options to each
|
481 |
+
// minifier (designated by type). source objects may still override
|
482 |
+
// these
|
483 |
+
$defaultOptions = isset(self::$_options['minifierOptions'][$type])
|
484 |
+
? self::$_options['minifierOptions'][$type]
|
485 |
+
: array();
|
486 |
+
// if minifier not set, default is no minification. source objects
|
487 |
+
// may still override this
|
488 |
+
$defaultMinifier = isset(self::$_options['minifiers'][$type])
|
489 |
+
? self::$_options['minifiers'][$type]
|
490 |
+
: false;
|
491 |
+
|
492 |
+
// process groups of sources with identical minifiers/options
|
493 |
+
$content = array();
|
494 |
+
$i = 0;
|
495 |
+
$l = count(self::$_controller->sources);
|
496 |
+
$groupToProcessTogether = array();
|
497 |
+
$lastMinifier = null;
|
498 |
+
$lastOptions = null;
|
499 |
+
do {
|
500 |
+
// get next source
|
501 |
+
$source = null;
|
502 |
+
if ($i < $l) {
|
503 |
+
$source = self::$_controller->sources[$i];
|
504 |
+
/* @var Minify_Source $source */
|
505 |
+
$sourceContent = $source->getContent();
|
506 |
+
|
507 |
+
// allow the source to override our minifier and options
|
508 |
+
$minifier = (null !== $source->minifier)
|
509 |
+
? $source->minifier
|
510 |
+
: $defaultMinifier;
|
511 |
+
$options = (null !== $source->minifyOptions)
|
512 |
+
? array_merge($defaultOptions, $source->minifyOptions)
|
513 |
+
: $defaultOptions;
|
514 |
+
}
|
515 |
+
// do we need to process our group right now?
|
516 |
+
if ($i > 0 // yes, we have at least the first group populated
|
517 |
+
&& (
|
518 |
+
! $source // yes, we ran out of sources
|
519 |
+
|| $type === self::TYPE_CSS // yes, to process CSS individually (avoiding PCRE bugs/limits)
|
520 |
+
|| $minifier !== $lastMinifier // yes, minifier changed
|
521 |
+
|| $options !== $lastOptions) // yes, options changed
|
522 |
+
)
|
523 |
+
{
|
524 |
+
// minify previous sources with last settings
|
525 |
+
$imploded = implode($implodeSeparator, $groupToProcessTogether);
|
526 |
+
$groupToProcessTogether = array();
|
527 |
+
if ($lastMinifier) {
|
528 |
+
try {
|
529 |
+
$content[] = call_user_func($lastMinifier, $imploded, $lastOptions);
|
530 |
+
} catch (Exception $e) {
|
531 |
+
throw new Exception("Exception in minifier: " . $e->getMessage());
|
532 |
+
}
|
533 |
+
} else {
|
534 |
+
$content[] = $imploded;
|
535 |
+
}
|
536 |
+
}
|
537 |
+
// add content to the group
|
538 |
+
if ($source) {
|
539 |
+
$groupToProcessTogether[] = $sourceContent;
|
540 |
+
$lastMinifier = $minifier;
|
541 |
+
$lastOptions = $options;
|
542 |
+
}
|
543 |
+
$i++;
|
544 |
+
} while ($source);
|
545 |
+
|
546 |
+
$content = implode($implodeSeparator, $content);
|
547 |
+
|
548 |
+
if ($type === self::TYPE_CSS && false !== strpos($content, '@import')) {
|
549 |
+
$content = self::_handleCssImports($content);
|
550 |
+
}
|
551 |
+
|
552 |
+
// do any post-processing (esp. for editing build URIs)
|
553 |
+
if (self::$_options['postprocessorRequire']) {
|
554 |
+
require_once self::$_options['postprocessorRequire'];
|
555 |
+
}
|
556 |
+
if (self::$_options['postprocessor']) {
|
557 |
+
$content = call_user_func(self::$_options['postprocessor'], $content, $type);
|
558 |
+
}
|
559 |
+
return $content;
|
560 |
+
}
|
561 |
+
|
562 |
+
public static function setCacheId($cacheId)
|
563 |
+
{
|
564 |
+
self::$cacheId = $cacheId;
|
565 |
+
}
|
566 |
+
|
567 |
+
/**
|
568 |
+
* Make a unique cache id for for this request.
|
569 |
+
*
|
570 |
+
* Any settings that could affect output are taken into consideration
|
571 |
+
*
|
572 |
+
* @param string $prefix
|
573 |
+
*
|
574 |
+
* @return string
|
575 |
+
*/
|
576 |
+
protected static function _getCacheId($prefix = 'minify')
|
577 |
+
{
|
578 |
+
// BEGIN BWP Minify Customization
|
579 |
+
if (!empty(self::$cacheId))
|
580 |
+
return self::$cacheId;
|
581 |
+
// switch cache path and create new directory if needed, this is for
|
582 |
+
// multisite compatible, this is reserved for future version
|
583 |
+
if (!empty($blog_id) && 1 == 2)
|
584 |
+
{
|
585 |
+
global $min_cacheFileLocking, $min_cachePath;
|
586 |
+
$blog_dir = str_pad($blog_id, 4, '0', STR_PAD_LEFT);
|
587 |
+
$cache_dir = $min_cachePath . '/' . $blog_dir;
|
588 |
+
// only switch to blog cache dir if it has been set, if not we
|
589 |
+
// let Minify handle the cache dir
|
590 |
+
if (!empty($min_cachePath))
|
591 |
+
{
|
592 |
+
if (!file_exists($cache_dir))
|
593 |
+
mkdir($cache_dir, 0755);
|
594 |
+
self::setCache($cache_dir, $min_cacheFileLocking);
|
595 |
+
}
|
596 |
+
}
|
597 |
+
// END BWP Minify Customization
|
598 |
+
|
599 |
+
$name = preg_replace('/[^a-zA-Z0-9\\.=_,]/', '', self::$_controller->selectionId);
|
600 |
+
$name = preg_replace('/\\.+/', '.', $name);
|
601 |
+
$name = substr($name, 0, 100 - 34 - strlen($prefix));
|
602 |
+
$md5 = md5(serialize(array(
|
603 |
+
Minify_Source::getDigest(self::$_controller->sources)
|
604 |
+
,self::$_options['minifiers']
|
605 |
+
,self::$_options['minifierOptions']
|
606 |
+
,self::$_options['postprocessor']
|
607 |
+
,self::$_options['bubbleCssImports']
|
608 |
+
,self::VERSION
|
609 |
+
)));
|
610 |
+
return "{$prefix}_{$name}_{$md5}";
|
611 |
+
}
|
612 |
+
|
613 |
+
/**
|
614 |
+
* Bubble CSS @imports to the top or prepend a warning if an import is detected not at the top.
|
615 |
+
*
|
616 |
+
* @param string $css
|
617 |
+
*
|
618 |
+
* @return string
|
619 |
+
*/
|
620 |
+
protected static function _handleCssImports($css)
|
621 |
+
{
|
622 |
+
if (self::$_options['bubbleCssImports']) {
|
623 |
+
// bubble CSS imports
|
624 |
+
preg_match_all('/@import.*?;/', $css, $imports);
|
625 |
+
$css = implode('', $imports[0]) . preg_replace('/@import.*?;/', '', $css);
|
626 |
+
} else if ('' !== self::$importWarning) {
|
627 |
+
// remove comments so we don't mistake { in a comment as a block
|
628 |
+
$noCommentCss = preg_replace('@/\\*[\\s\\S]*?\\*/@', '', $css);
|
629 |
+
$lastImportPos = strrpos($noCommentCss, '@import');
|
630 |
+
$firstBlockPos = strpos($noCommentCss, '{');
|
631 |
+
if (false !== $lastImportPos
|
632 |
+
&& false !== $firstBlockPos
|
633 |
+
&& $firstBlockPos < $lastImportPos
|
634 |
+
) {
|
635 |
+
// { appears before @import : prepend warning
|
636 |
+
$css = self::$importWarning . $css;
|
637 |
+
}
|
638 |
+
}
|
639 |
+
return $css;
|
640 |
+
}
|
641 |
+
}
|
min/lib/Minify/Build.php
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Build
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Maintain a single last modification time for a group of Minify sources to
|
9 |
+
* allow use of far off Expires headers in Minify.
|
10 |
+
*
|
11 |
+
* <code>
|
12 |
+
* // in config file
|
13 |
+
* $groupSources = array(
|
14 |
+
* 'js' => array('file1.js', 'file2.js')
|
15 |
+
* ,'css' => array('file1.css', 'file2.css', 'file3.css')
|
16 |
+
* )
|
17 |
+
*
|
18 |
+
* // during HTML generation
|
19 |
+
* $jsBuild = new Minify_Build($groupSources['js']);
|
20 |
+
* $cssBuild = new Minify_Build($groupSources['css']);
|
21 |
+
*
|
22 |
+
* $script = "<script type='text/javascript' src='"
|
23 |
+
* . $jsBuild->uri('/min.php/js') . "'></script>";
|
24 |
+
* $link = "<link rel='stylesheet' type='text/css' href='"
|
25 |
+
* . $cssBuild->uri('/min.php/css') . "'>";
|
26 |
+
*
|
27 |
+
* // in min.php
|
28 |
+
* Minify::serve('Groups', array(
|
29 |
+
* 'groups' => $groupSources
|
30 |
+
* ,'setExpires' => (time() + 86400 * 365)
|
31 |
+
* ));
|
32 |
+
* </code>
|
33 |
+
*
|
34 |
+
* @package Minify
|
35 |
+
* @author Stephen Clay <steve@mrclay.org>
|
36 |
+
*/
|
37 |
+
class Minify_Build {
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Last modification time of all files in the build
|
41 |
+
*
|
42 |
+
* @var int
|
43 |
+
*/
|
44 |
+
public $lastModified = 0;
|
45 |
+
|
46 |
+
/**
|
47 |
+
* String to use as ampersand in uri(). Set this to '&' if
|
48 |
+
* you are not HTML-escaping URIs.
|
49 |
+
*
|
50 |
+
* @var string
|
51 |
+
*/
|
52 |
+
public static $ampersand = '&';
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Get a time-stamped URI
|
56 |
+
*
|
57 |
+
* <code>
|
58 |
+
* echo $b->uri('/site.js');
|
59 |
+
* // outputs "/site.js?1678242"
|
60 |
+
*
|
61 |
+
* echo $b->uri('/scriptaculous.js?load=effects');
|
62 |
+
* // outputs "/scriptaculous.js?load=effects&1678242"
|
63 |
+
* </code>
|
64 |
+
*
|
65 |
+
* @param string $uri
|
66 |
+
* @param boolean $forceAmpersand (default = false) Force the use of ampersand to
|
67 |
+
* append the timestamp to the URI.
|
68 |
+
* @return string
|
69 |
+
*/
|
70 |
+
public function uri($uri, $forceAmpersand = false) {
|
71 |
+
$sep = ($forceAmpersand || strpos($uri, '?') !== false)
|
72 |
+
? self::$ampersand
|
73 |
+
: '?';
|
74 |
+
return "{$uri}{$sep}{$this->lastModified}";
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Create a build object
|
79 |
+
*
|
80 |
+
* @param array $sources array of Minify_Source objects and/or file paths
|
81 |
+
*
|
82 |
+
* @return null
|
83 |
+
*/
|
84 |
+
public function __construct($sources)
|
85 |
+
{
|
86 |
+
$max = 0;
|
87 |
+
foreach ((array)$sources as $source) {
|
88 |
+
if ($source instanceof Minify_Source) {
|
89 |
+
$max = max($max, $source->lastModified);
|
90 |
+
} elseif (is_string($source)) {
|
91 |
+
if (0 === strpos($source, '//')) {
|
92 |
+
$source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1);
|
93 |
+
}
|
94 |
+
if (is_file($source)) {
|
95 |
+
$max = max($max, filemtime($source));
|
96 |
+
}
|
97 |
+
}
|
98 |
+
}
|
99 |
+
$this->lastModified = $max;
|
100 |
+
}
|
101 |
+
}
|
min/lib/Minify/CSS.php
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_CSS
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Minify CSS
|
9 |
+
*
|
10 |
+
* This class uses Minify_CSS_Compressor and Minify_CSS_UriRewriter to
|
11 |
+
* minify CSS and rewrite relative URIs.
|
12 |
+
*
|
13 |
+
* @package Minify
|
14 |
+
* @author Stephen Clay <steve@mrclay.org>
|
15 |
+
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
16 |
+
*/
|
17 |
+
class Minify_CSS {
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Minify a CSS string
|
21 |
+
*
|
22 |
+
* @param string $css
|
23 |
+
*
|
24 |
+
* @param array $options available options:
|
25 |
+
*
|
26 |
+
* 'preserveComments': (default true) multi-line comments that begin
|
27 |
+
* with "/*!" will be preserved with newlines before and after to
|
28 |
+
* enhance readability.
|
29 |
+
*
|
30 |
+
* 'removeCharsets': (default true) remove all @charset at-rules
|
31 |
+
*
|
32 |
+
* 'prependRelativePath': (default null) if given, this string will be
|
33 |
+
* prepended to all relative URIs in import/url declarations
|
34 |
+
*
|
35 |
+
* 'currentDir': (default null) if given, this is assumed to be the
|
36 |
+
* directory of the current CSS file. Using this, minify will rewrite
|
37 |
+
* all relative URIs in import/url declarations to correctly point to
|
38 |
+
* the desired files. For this to work, the files *must* exist and be
|
39 |
+
* visible by the PHP process.
|
40 |
+
*
|
41 |
+
* 'symlinks': (default = array()) If the CSS file is stored in
|
42 |
+
* a symlink-ed directory, provide an array of link paths to
|
43 |
+
* target paths, where the link paths are within the document root. Because
|
44 |
+
* paths need to be normalized for this to work, use "//" to substitute
|
45 |
+
* the doc root in the link paths (the array keys). E.g.:
|
46 |
+
* <code>
|
47 |
+
* array('//symlink' => '/real/target/path') // unix
|
48 |
+
* array('//static' => 'D:\\staticStorage') // Windows
|
49 |
+
* </code>
|
50 |
+
*
|
51 |
+
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
|
52 |
+
* see Minify_CSS_UriRewriter::rewrite
|
53 |
+
*
|
54 |
+
* @return string
|
55 |
+
*/
|
56 |
+
public static function minify($css, $options = array())
|
57 |
+
{
|
58 |
+
$options = array_merge(array(
|
59 |
+
'compress' => true,
|
60 |
+
'removeCharsets' => true,
|
61 |
+
'preserveComments' => true,
|
62 |
+
'currentDir' => null,
|
63 |
+
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
|
64 |
+
'prependRelativePath' => null,
|
65 |
+
'symlinks' => array(),
|
66 |
+
), $options);
|
67 |
+
|
68 |
+
if ($options['removeCharsets']) {
|
69 |
+
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
|
70 |
+
}
|
71 |
+
if ($options['compress']) {
|
72 |
+
if (! $options['preserveComments']) {
|
73 |
+
$css = Minify_CSS_Compressor::process($css, $options);
|
74 |
+
} else {
|
75 |
+
$css = Minify_CommentPreserver::process(
|
76 |
+
$css
|
77 |
+
,array('Minify_CSS_Compressor', 'process')
|
78 |
+
,array($options)
|
79 |
+
);
|
80 |
+
}
|
81 |
+
}
|
82 |
+
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
|
83 |
+
return $css;
|
84 |
+
}
|
85 |
+
if ($options['currentDir']) {
|
86 |
+
return Minify_CSS_UriRewriter::rewrite(
|
87 |
+
$css
|
88 |
+
,$options['currentDir']
|
89 |
+
,$options['docRoot']
|
90 |
+
,$options['symlinks']
|
91 |
+
);
|
92 |
+
} else {
|
93 |
+
return Minify_CSS_UriRewriter::prepend(
|
94 |
+
$css
|
95 |
+
,$options['prependRelativePath']
|
96 |
+
);
|
97 |
+
}
|
98 |
+
}
|
99 |
+
}
|
min/lib/Minify/CSS/Compressor.php
ADDED
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_CSS_Compressor
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Compress CSS
|
9 |
+
*
|
10 |
+
* This is a heavy regex-based removal of whitespace, unnecessary
|
11 |
+
* comments and tokens, and some CSS value minimization, where practical.
|
12 |
+
* Many steps have been taken to avoid breaking comment-based hacks,
|
13 |
+
* including the ie5/mac filter (and its inversion), but expect tricky
|
14 |
+
* hacks involving comment tokens in 'content' value strings to break
|
15 |
+
* minimization badly. A test suite is available.
|
16 |
+
*
|
17 |
+
* @package Minify
|
18 |
+
* @author Stephen Clay <steve@mrclay.org>
|
19 |
+
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
20 |
+
*/
|
21 |
+
class Minify_CSS_Compressor {
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Minify a CSS string
|
25 |
+
*
|
26 |
+
* @param string $css
|
27 |
+
*
|
28 |
+
* @param array $options (currently ignored)
|
29 |
+
*
|
30 |
+
* @return string
|
31 |
+
*/
|
32 |
+
public static function process($css, $options = array())
|
33 |
+
{
|
34 |
+
$obj = new Minify_CSS_Compressor($options);
|
35 |
+
return $obj->_process($css);
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @var array
|
40 |
+
*/
|
41 |
+
protected $_options = null;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Are we "in" a hack? I.e. are some browsers targetted until the next comment?
|
45 |
+
*
|
46 |
+
* @var bool
|
47 |
+
*/
|
48 |
+
protected $_inHack = false;
|
49 |
+
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Constructor
|
53 |
+
*
|
54 |
+
* @param array $options (currently ignored)
|
55 |
+
*/
|
56 |
+
private function __construct($options) {
|
57 |
+
$this->_options = $options;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Minify a CSS string
|
62 |
+
*
|
63 |
+
* @param string $css
|
64 |
+
*
|
65 |
+
* @return string
|
66 |
+
*/
|
67 |
+
protected function _process($css)
|
68 |
+
{
|
69 |
+
$css = str_replace("\r\n", "\n", $css);
|
70 |
+
|
71 |
+
// preserve empty comment after '>'
|
72 |
+
// http://www.webdevout.net/css-hacks#in_css-selectors
|
73 |
+
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
|
74 |
+
|
75 |
+
// preserve empty comment between property and value
|
76 |
+
// http://css-discuss.incutio.com/?page=BoxModelHack
|
77 |
+
$css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
|
78 |
+
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
|
79 |
+
|
80 |
+
// apply callback to all valid comments (and strip out surrounding ws
|
81 |
+
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
|
82 |
+
,array($this, '_commentCB'), $css);
|
83 |
+
|
84 |
+
// remove ws around { } and last semicolon in declaration block
|
85 |
+
$css = preg_replace('/\\s*{\\s*/', '{', $css);
|
86 |
+
$css = preg_replace('/;?\\s*}\\s*/', '}', $css);
|
87 |
+
|
88 |
+
// remove ws surrounding semicolons
|
89 |
+
$css = preg_replace('/\\s*;\\s*/', ';', $css);
|
90 |
+
|
91 |
+
// remove ws around urls
|
92 |
+
$css = preg_replace('/
|
93 |
+
url\\( # url(
|
94 |
+
\\s*
|
95 |
+
([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
|
96 |
+
\\s*
|
97 |
+
\\) # )
|
98 |
+
/x', 'url($1)', $css);
|
99 |
+
|
100 |
+
// remove ws between rules and colons
|
101 |
+
$css = preg_replace('/
|
102 |
+
\\s*
|
103 |
+
([{;]) # 1 = beginning of block or rule separator
|
104 |
+
\\s*
|
105 |
+
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
|
106 |
+
\\s*
|
107 |
+
:
|
108 |
+
\\s*
|
109 |
+
(\\b|[#\'"-]) # 3 = first character of a value
|
110 |
+
/x', '$1$2:$3', $css);
|
111 |
+
|
112 |
+
// remove ws in selectors
|
113 |
+
$css = preg_replace_callback('/
|
114 |
+
(?: # non-capture
|
115 |
+
\\s*
|
116 |
+
[^~>+,\\s]+ # selector part
|
117 |
+
\\s*
|
118 |
+
[,>+~] # combinators
|
119 |
+
)+
|
120 |
+
\\s*
|
121 |
+
[^~>+,\\s]+ # selector part
|
122 |
+
{ # open declaration block
|
123 |
+
/x'
|
124 |
+
,array($this, '_selectorsCB'), $css);
|
125 |
+
|
126 |
+
// minimize hex colors
|
127 |
+
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
|
128 |
+
, '$1#$2$3$4$5', $css);
|
129 |
+
|
130 |
+
// remove spaces between font families
|
131 |
+
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
|
132 |
+
,array($this, '_fontFamilyCB'), $css);
|
133 |
+
|
134 |
+
$css = preg_replace('/@import\\s+url/', '@import url', $css);
|
135 |
+
|
136 |
+
// replace any ws involving newlines with a single newline
|
137 |
+
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
|
138 |
+
|
139 |
+
// separate common descendent selectors w/ newlines (to limit line lengths)
|
140 |
+
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
|
141 |
+
|
142 |
+
// Use newline after 1st numeric value (to limit line lengths).
|
143 |
+
$css = preg_replace('/
|
144 |
+
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
|
145 |
+
\\s+
|
146 |
+
/x'
|
147 |
+
,"$1\n", $css);
|
148 |
+
|
149 |
+
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
|
150 |
+
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
|
151 |
+
|
152 |
+
return trim($css);
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Replace what looks like a set of selectors
|
157 |
+
*
|
158 |
+
* @param array $m regex matches
|
159 |
+
*
|
160 |
+
* @return string
|
161 |
+
*/
|
162 |
+
protected function _selectorsCB($m)
|
163 |
+
{
|
164 |
+
// remove ws around the combinators
|
165 |
+
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Process a comment and return a replacement
|
170 |
+
*
|
171 |
+
* @param array $m regex matches
|
172 |
+
*
|
173 |
+
* @return string
|
174 |
+
*/
|
175 |
+
protected function _commentCB($m)
|
176 |
+
{
|
177 |
+
$hasSurroundingWs = (trim($m[0]) !== $m[1]);
|
178 |
+
$m = $m[1];
|
179 |
+
// $m is the comment content w/o the surrounding tokens,
|
180 |
+
// but the return value will replace the entire comment.
|
181 |
+
if ($m === 'keep') {
|
182 |
+
return '/**/';
|
183 |
+
}
|
184 |
+
if ($m === '" "') {
|
185 |
+
// component of http://tantek.com/CSS/Examples/midpass.html
|
186 |
+
return '/*" "*/';
|
187 |
+
}
|
188 |
+
if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
|
189 |
+
// component of http://tantek.com/CSS/Examples/midpass.html
|
190 |
+
return '/*";}}/* */';
|
191 |
+
}
|
192 |
+
if ($this->_inHack) {
|
193 |
+
// inversion: feeding only to one browser
|
194 |
+
if (preg_match('@
|
195 |
+
^/ # comment started like /*/
|
196 |
+
\\s*
|
197 |
+
(\\S[\\s\\S]+?) # has at least some non-ws content
|
198 |
+
\\s*
|
199 |
+
/\\* # ends like /*/ or /**/
|
200 |
+
@x', $m, $n)) {
|
201 |
+
// end hack mode after this comment, but preserve the hack and comment content
|
202 |
+
$this->_inHack = false;
|
203 |
+
return "/*/{$n[1]}/**/";
|
204 |
+
}
|
205 |
+
}
|
206 |
+
if (substr($m, -1) === '\\') { // comment ends like \*/
|
207 |
+
// begin hack mode and preserve hack
|
208 |
+
$this->_inHack = true;
|
209 |
+
return '/*\\*/';
|
210 |
+
}
|
211 |
+
if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
|
212 |
+
// begin hack mode and preserve hack
|
213 |
+
$this->_inHack = true;
|
214 |
+
return '/*/*/';
|
215 |
+
}
|
216 |
+
if ($this->_inHack) {
|
217 |
+
// a regular comment ends hack mode but should be preserved
|
218 |
+
$this->_inHack = false;
|
219 |
+
return '/**/';
|
220 |
+
}
|
221 |
+
// Issue 107: if there's any surrounding whitespace, it may be important, so
|
222 |
+
// replace the comment with a single space
|
223 |
+
return $hasSurroundingWs // remove all other comments
|
224 |
+
? ' '
|
225 |
+
: '';
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* Process a font-family listing and return a replacement
|
230 |
+
*
|
231 |
+
* @param array $m regex matches
|
232 |
+
*
|
233 |
+
* @return string
|
234 |
+
*/
|
235 |
+
protected function _fontFamilyCB($m)
|
236 |
+
{
|
237 |
+
// Issue 210: must not eliminate WS between words in unquoted families
|
238 |
+
$pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
239 |
+
$out = 'font-family:';
|
240 |
+
while (null !== ($piece = array_shift($pieces))) {
|
241 |
+
if ($piece[0] !== '"' && $piece[0] !== "'") {
|
242 |
+
$piece = preg_replace('/\\s+/', ' ', $piece);
|
243 |
+
$piece = preg_replace('/\\s?,\\s?/', ',', $piece);
|
244 |
+
}
|
245 |
+
$out .= $piece;
|
246 |
+
}
|
247 |
+
return $out . $m[2];
|
248 |
+
}
|
249 |
+
}
|
min/lib/Minify/CSS/UriRewriter.php
ADDED
@@ -0,0 +1,310 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_CSS_UriRewriter
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Rewrite file-relative URIs as root-relative in CSS files
|
9 |
+
*
|
10 |
+
* @package Minify
|
11 |
+
* @author Stephen Clay <steve@mrclay.org>
|
12 |
+
*/
|
13 |
+
class Minify_CSS_UriRewriter {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* rewrite() and rewriteRelative() append debugging information here
|
17 |
+
*
|
18 |
+
* @var string
|
19 |
+
*/
|
20 |
+
public static $debugText = '';
|
21 |
+
|
22 |
+
/**
|
23 |
+
* In CSS content, rewrite file relative URIs as root relative
|
24 |
+
*
|
25 |
+
* @param string $css
|
26 |
+
*
|
27 |
+
* @param string $currentDir The directory of the current CSS file.
|
28 |
+
*
|
29 |
+
* @param string $docRoot The document root of the web site in which
|
30 |
+
* the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
|
31 |
+
*
|
32 |
+
* @param array $symlinks (default = array()) If the CSS file is stored in
|
33 |
+
* a symlink-ed directory, provide an array of link paths to
|
34 |
+
* target paths, where the link paths are within the document root. Because
|
35 |
+
* paths need to be normalized for this to work, use "//" to substitute
|
36 |
+
* the doc root in the link paths (the array keys). E.g.:
|
37 |
+
* <code>
|
38 |
+
* array('//symlink' => '/real/target/path') // unix
|
39 |
+
* array('//static' => 'D:\\staticStorage') // Windows
|
40 |
+
* </code>
|
41 |
+
*
|
42 |
+
* @return string
|
43 |
+
*/
|
44 |
+
public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array())
|
45 |
+
{
|
46 |
+
self::$_docRoot = self::_realpath(
|
47 |
+
$docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
|
48 |
+
);
|
49 |
+
self::$_currentDir = self::_realpath($currentDir);
|
50 |
+
self::$_symlinks = array();
|
51 |
+
|
52 |
+
// normalize symlinks
|
53 |
+
foreach ($symlinks as $link => $target) {
|
54 |
+
$link = ($link === '//')
|
55 |
+
? self::$_docRoot
|
56 |
+
: str_replace('//', self::$_docRoot . '/', $link);
|
57 |
+
$link = strtr($link, '/', DIRECTORY_SEPARATOR);
|
58 |
+
self::$_symlinks[$link] = self::_realpath($target);
|
59 |
+
}
|
60 |
+
|
61 |
+
self::$debugText .= "docRoot : " . self::$_docRoot . "\n"
|
62 |
+
. "currentDir : " . self::$_currentDir . "\n";
|
63 |
+
if (self::$_symlinks) {
|
64 |
+
self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n";
|
65 |
+
}
|
66 |
+
self::$debugText .= "\n";
|
67 |
+
|
68 |
+
$css = self::_trimUrls($css);
|
69 |
+
|
70 |
+
// rewrite
|
71 |
+
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
72 |
+
,array(self::$className, '_processUriCB'), $css);
|
73 |
+
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
74 |
+
,array(self::$className, '_processUriCB'), $css);
|
75 |
+
|
76 |
+
return $css;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* In CSS content, prepend a path to relative URIs
|
81 |
+
*
|
82 |
+
* @param string $css
|
83 |
+
*
|
84 |
+
* @param string $path The path to prepend.
|
85 |
+
*
|
86 |
+
* @return string
|
87 |
+
*/
|
88 |
+
public static function prepend($css, $path)
|
89 |
+
{
|
90 |
+
self::$_prependPath = $path;
|
91 |
+
|
92 |
+
$css = self::_trimUrls($css);
|
93 |
+
|
94 |
+
// append
|
95 |
+
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
96 |
+
,array(self::$className, '_processUriCB'), $css);
|
97 |
+
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
98 |
+
,array(self::$className, '_processUriCB'), $css);
|
99 |
+
|
100 |
+
self::$_prependPath = null;
|
101 |
+
return $css;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Get a root relative URI from a file relative URI
|
106 |
+
*
|
107 |
+
* <code>
|
108 |
+
* Minify_CSS_UriRewriter::rewriteRelative(
|
109 |
+
* '../img/hello.gif'
|
110 |
+
* , '/home/user/www/css' // path of CSS file
|
111 |
+
* , '/home/user/www' // doc root
|
112 |
+
* );
|
113 |
+
* // returns '/img/hello.gif'
|
114 |
+
*
|
115 |
+
* // example where static files are stored in a symlinked directory
|
116 |
+
* Minify_CSS_UriRewriter::rewriteRelative(
|
117 |
+
* 'hello.gif'
|
118 |
+
* , '/var/staticFiles/theme'
|
119 |
+
* , '/home/user/www'
|
120 |
+
* , array('/home/user/www/static' => '/var/staticFiles')
|
121 |
+
* );
|
122 |
+
* // returns '/static/theme/hello.gif'
|
123 |
+
* </code>
|
124 |
+
*
|
125 |
+
* @param string $uri file relative URI
|
126 |
+
*
|
127 |
+
* @param string $realCurrentDir realpath of the current file's directory.
|
128 |
+
*
|
129 |
+
* @param string $realDocRoot realpath of the site document root.
|
130 |
+
*
|
131 |
+
* @param array $symlinks (default = array()) If the file is stored in
|
132 |
+
* a symlink-ed directory, provide an array of link paths to
|
133 |
+
* real target paths, where the link paths "appear" to be within the document
|
134 |
+
* root. E.g.:
|
135 |
+
* <code>
|
136 |
+
* array('/home/foo/www/not/real/path' => '/real/target/path') // unix
|
137 |
+
* array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
|
138 |
+
* </code>
|
139 |
+
*
|
140 |
+
* @return string
|
141 |
+
*/
|
142 |
+
public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array())
|
143 |
+
{
|
144 |
+
// prepend path with current dir separator (OS-independent)
|
145 |
+
$path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)
|
146 |
+
. DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
|
147 |
+
|
148 |
+
self::$debugText .= "file-relative URI : {$uri}\n"
|
149 |
+
. "path prepended : {$path}\n";
|
150 |
+
|
151 |
+
// "unresolve" a symlink back to doc root
|
152 |
+
foreach ($symlinks as $link => $target) {
|
153 |
+
if (0 === strpos($path, $target)) {
|
154 |
+
// replace $target with $link
|
155 |
+
$path = $link . substr($path, strlen($target));
|
156 |
+
|
157 |
+
self::$debugText .= "symlink unresolved : {$path}\n";
|
158 |
+
|
159 |
+
break;
|
160 |
+
}
|
161 |
+
}
|
162 |
+
// strip doc root
|
163 |
+
$path = substr($path, strlen($realDocRoot));
|
164 |
+
|
165 |
+
self::$debugText .= "docroot stripped : {$path}\n";
|
166 |
+
|
167 |
+
// fix to root-relative URI
|
168 |
+
$uri = strtr($path, '/\\', '//');
|
169 |
+
$uri = self::removeDots($uri);
|
170 |
+
|
171 |
+
self::$debugText .= "traversals removed : {$uri}\n\n";
|
172 |
+
|
173 |
+
return $uri;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Remove instances of "./" and "../" where possible from a root-relative URI
|
178 |
+
*
|
179 |
+
* @param string $uri
|
180 |
+
*
|
181 |
+
* @return string
|
182 |
+
*/
|
183 |
+
public static function removeDots($uri)
|
184 |
+
{
|
185 |
+
$uri = str_replace('/./', '/', $uri);
|
186 |
+
// inspired by patch from Oleg Cherniy
|
187 |
+
do {
|
188 |
+
$uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
|
189 |
+
} while ($changed);
|
190 |
+
return $uri;
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* Defines which class to call as part of callbacks, change this
|
195 |
+
* if you extend Minify_CSS_UriRewriter
|
196 |
+
*
|
197 |
+
* @var string
|
198 |
+
*/
|
199 |
+
protected static $className = 'Minify_CSS_UriRewriter';
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Get realpath with any trailing slash removed. If realpath() fails,
|
203 |
+
* just remove the trailing slash.
|
204 |
+
*
|
205 |
+
* @param string $path
|
206 |
+
*
|
207 |
+
* @return mixed path with no trailing slash
|
208 |
+
*/
|
209 |
+
protected static function _realpath($path)
|
210 |
+
{
|
211 |
+
$realPath = realpath($path);
|
212 |
+
if ($realPath !== false) {
|
213 |
+
$path = $realPath;
|
214 |
+
}
|
215 |
+
return rtrim($path, '/\\');
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Directory of this stylesheet
|
220 |
+
*
|
221 |
+
* @var string
|
222 |
+
*/
|
223 |
+
private static $_currentDir = '';
|
224 |
+
|
225 |
+
/**
|
226 |
+
* DOC_ROOT
|
227 |
+
*
|
228 |
+
* @var string
|
229 |
+
*/
|
230 |
+
private static $_docRoot = '';
|
231 |
+
|
232 |
+
/**
|
233 |
+
* directory replacements to map symlink targets back to their
|
234 |
+
* source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
|
235 |
+
*
|
236 |
+
* @var array
|
237 |
+
*/
|
238 |
+
private static $_symlinks = array();
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Path to prepend
|
242 |
+
*
|
243 |
+
* @var string
|
244 |
+
*/
|
245 |
+
private static $_prependPath = null;
|
246 |
+
|
247 |
+
/**
|
248 |
+
* @param string $css
|
249 |
+
*
|
250 |
+
* @return string
|
251 |
+
*/
|
252 |
+
private static function _trimUrls($css)
|
253 |
+
{
|
254 |
+
return preg_replace('/
|
255 |
+
url\\( # url(
|
256 |
+
\\s*
|
257 |
+
([^\\)]+?) # 1 = URI (assuming does not contain ")")
|
258 |
+
\\s*
|
259 |
+
\\) # )
|
260 |
+
/x', 'url($1)', $css);
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* @param array $m
|
265 |
+
*
|
266 |
+
* @return string
|
267 |
+
*/
|
268 |
+
private static function _processUriCB($m)
|
269 |
+
{
|
270 |
+
// $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
271 |
+
$isImport = ($m[0][0] === '@');
|
272 |
+
// determine URI and the quote character (if any)
|
273 |
+
if ($isImport) {
|
274 |
+
$quoteChar = $m[1];
|
275 |
+
$uri = $m[2];
|
276 |
+
} else {
|
277 |
+
// $m[1] is either quoted or not
|
278 |
+
$quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
|
279 |
+
? $m[1][0]
|
280 |
+
: '';
|
281 |
+
$uri = ($quoteChar === '')
|
282 |
+
? $m[1]
|
283 |
+
: substr($m[1], 1, strlen($m[1]) - 2);
|
284 |
+
}
|
285 |
+
// analyze URI
|
286 |
+
if ('/' !== $uri[0] // root-relative
|
287 |
+
&& false === strpos($uri, '//') // protocol (non-data)
|
288 |
+
&& 0 !== strpos($uri, 'data:') // data protocol
|
289 |
+
) {
|
290 |
+
// URI is file-relative: rewrite depending on options
|
291 |
+
if (self::$_prependPath === null) {
|
292 |
+
$uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
|
293 |
+
} else {
|
294 |
+
$uri = self::$_prependPath . $uri;
|
295 |
+
if ($uri[0] === '/') {
|
296 |
+
$root = '';
|
297 |
+
$rootRelative = $uri;
|
298 |
+
$uri = $root . self::removeDots($rootRelative);
|
299 |
+
} elseif (preg_match('@^((https?\:)?//([^/]+))/@', $uri, $m) && (false !== strpos($m[3], '.'))) {
|
300 |
+
$root = $m[1];
|
301 |
+
$rootRelative = substr($uri, strlen($root));
|
302 |
+
$uri = $root . self::removeDots($rootRelative);
|
303 |
+
}
|
304 |
+
}
|
305 |
+
}
|
306 |
+
return $isImport
|
307 |
+
? "@import {$quoteChar}{$uri}{$quoteChar}"
|
308 |
+
: "url({$quoteChar}{$uri}{$quoteChar})";
|
309 |
+
}
|
310 |
+
}
|
min/lib/Minify/Cache/APC.php
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Cache_APC
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* APC-based cache class for Minify
|
9 |
+
*
|
10 |
+
* <code>
|
11 |
+
* Minify::setCache(new Minify_Cache_APC());
|
12 |
+
* </code>
|
13 |
+
*
|
14 |
+
* @package Minify
|
15 |
+
* @author Chris Edwards
|
16 |
+
**/
|
17 |
+
class Minify_Cache_APC {
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Create a Minify_Cache_APC object, to be passed to
|
21 |
+
* Minify::setCache().
|
22 |
+
*
|
23 |
+
*
|
24 |
+
* @param int $expire seconds until expiration (default = 0
|
25 |
+
* meaning the item will not get an expiration date)
|
26 |
+
*
|
27 |
+
* @return null
|
28 |
+
*/
|
29 |
+
public function __construct($expire = 0)
|
30 |
+
{
|
31 |
+
$this->_exp = $expire;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Write data to cache.
|
36 |
+
*
|
37 |
+
* @param string $id cache id
|
38 |
+
*
|
39 |
+
* @param string $data
|
40 |
+
*
|
41 |
+
* @return bool success
|
42 |
+
*/
|
43 |
+
public function store($id, $data)
|
44 |
+
{
|
45 |
+
return apc_store($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp);
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Get the size of a cache entry
|
50 |
+
*
|
51 |
+
* @param string $id cache id
|
52 |
+
*
|
53 |
+
* @return int size in bytes
|
54 |
+
*/
|
55 |
+
public function getSize($id)
|
56 |
+
{
|
57 |
+
if (! $this->_fetch($id)) {
|
58 |
+
return false;
|
59 |
+
}
|
60 |
+
return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
|
61 |
+
? mb_strlen($this->_data, '8bit')
|
62 |
+
: strlen($this->_data);
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Does a valid cache entry exist?
|
67 |
+
*
|
68 |
+
* @param string $id cache id
|
69 |
+
*
|
70 |
+
* @param int $srcMtime mtime of the original source file(s)
|
71 |
+
*
|
72 |
+
* @return bool exists
|
73 |
+
*/
|
74 |
+
public function isValid($id, $srcMtime)
|
75 |
+
{
|
76 |
+
return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Send the cached content to output
|
81 |
+
*
|
82 |
+
* @param string $id cache id
|
83 |
+
*/
|
84 |
+
public function display($id)
|
85 |
+
{
|
86 |
+
echo $this->_fetch($id)
|
87 |
+
? $this->_data
|
88 |
+
: '';
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Fetch the cached content
|
93 |
+
*
|
94 |
+
* @param string $id cache id
|
95 |
+
*
|
96 |
+
* @return string
|
97 |
+
*/
|
98 |
+
public function fetch($id)
|
99 |
+
{
|
100 |
+
return $this->_fetch($id)
|
101 |
+
? $this->_data
|
102 |
+
: '';
|
103 |
+
}
|
104 |
+
|
105 |
+
private $_exp = null;
|
106 |
+
|
107 |
+
// cache of most recently fetched id
|
108 |
+
private $_lm = null;
|
109 |
+
private $_data = null;
|
110 |
+
private $_id = null;
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Fetch data and timestamp from apc, store in instance
|
114 |
+
*
|
115 |
+
* @param string $id
|
116 |
+
*
|
117 |
+
* @return bool success
|
118 |
+
*/
|
119 |
+
private function _fetch($id)
|
120 |
+
{
|
121 |
+
if ($this->_id === $id) {
|
122 |
+
return true;
|
123 |
+
}
|
124 |
+
$ret = apc_fetch($id);
|
125 |
+
if (false === $ret) {
|
126 |
+
$this->_id = null;
|
127 |
+
return false;
|
128 |
+
}
|
129 |
+
list($this->_lm, $this->_data) = explode('|', $ret, 2);
|
130 |
+
$this->_id = $id;
|
131 |
+
return true;
|
132 |
+
}
|
133 |
+
}
|
min/lib/Minify/Cache/File.php
ADDED
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Cache_File
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
class Minify_Cache_File {
|
8 |
+
|
9 |
+
public function __construct($path = '', $fileLocking = false)
|
10 |
+
{
|
11 |
+
if (! $path) {
|
12 |
+
$path = self::tmp();
|
13 |
+
}
|
14 |
+
$this->_locking = $fileLocking;
|
15 |
+
$this->_path = $path;
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Write data to cache.
|
20 |
+
*
|
21 |
+
* @param string $id cache id (e.g. a filename)
|
22 |
+
*
|
23 |
+
* @param string $data
|
24 |
+
*
|
25 |
+
* @return bool success
|
26 |
+
*/
|
27 |
+
public function store($id, $data)
|
28 |
+
{
|
29 |
+
$flag = $this->_locking
|
30 |
+
? LOCK_EX
|
31 |
+
: null;
|
32 |
+
$file = $this->_path . '/' . $id;
|
33 |
+
if (! @file_put_contents($file, $data, $flag)) {
|
34 |
+
$this->_log("Minify_Cache_File: Write failed to '$file'");
|
35 |
+
}
|
36 |
+
// write control
|
37 |
+
if ($data !== $this->fetch($id)) {
|
38 |
+
@unlink($file);
|
39 |
+
$this->_log("Minify_Cache_File: Post-write read failed for '$file'");
|
40 |
+
return false;
|
41 |
+
}
|
42 |
+
return true;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Get the size of a cache entry
|
47 |
+
*
|
48 |
+
* @param string $id cache id (e.g. a filename)
|
49 |
+
*
|
50 |
+
* @return int size in bytes
|
51 |
+
*/
|
52 |
+
public function getSize($id)
|
53 |
+
{
|
54 |
+
return filesize($this->_path . '/' . $id);
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Does a valid cache entry exist?
|
59 |
+
*
|
60 |
+
* @param string $id cache id (e.g. a filename)
|
61 |
+
*
|
62 |
+
* @param int $srcMtime mtime of the original source file(s)
|
63 |
+
*
|
64 |
+
* @return bool exists
|
65 |
+
*/
|
66 |
+
public function isValid($id, $srcMtime)
|
67 |
+
{
|
68 |
+
$file = $this->_path . '/' . $id;
|
69 |
+
return (is_file($file) && (filemtime($file) >= $srcMtime));
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Send the cached content to output
|
74 |
+
*
|
75 |
+
* @param string $id cache id (e.g. a filename)
|
76 |
+
*/
|
77 |
+
public function display($id)
|
78 |
+
{
|
79 |
+
if ($this->_locking) {
|
80 |
+
$fp = fopen($this->_path . '/' . $id, 'rb');
|
81 |
+
flock($fp, LOCK_SH);
|
82 |
+
fpassthru($fp);
|
83 |
+
flock($fp, LOCK_UN);
|
84 |
+
fclose($fp);
|
85 |
+
} else {
|
86 |
+
readfile($this->_path . '/' . $id);
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Fetch the cached content
|
92 |
+
*
|
93 |
+
* @param string $id cache id (e.g. a filename)
|
94 |
+
*
|
95 |
+
* @return string
|
96 |
+
*/
|
97 |
+
public function fetch($id)
|
98 |
+
{
|
99 |
+
if ($this->_locking) {
|
100 |
+
$fp = fopen($this->_path . '/' . $id, 'rb');
|
101 |
+
flock($fp, LOCK_SH);
|
102 |
+
$ret = stream_get_contents($fp);
|
103 |
+
flock($fp, LOCK_UN);
|
104 |
+
fclose($fp);
|
105 |
+
return $ret;
|
106 |
+
} else {
|
107 |
+
return file_get_contents($this->_path . '/' . $id);
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Fetch the cache path used
|
113 |
+
*
|
114 |
+
* @return string
|
115 |
+
*/
|
116 |
+
public function getPath()
|
117 |
+
{
|
118 |
+
return $this->_path;
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Get a usable temp directory
|
123 |
+
*
|
124 |
+
* Adapted from Solar/Dir.php
|
125 |
+
* @author Paul M. Jones <pmjones@solarphp.com>
|
126 |
+
* @license http://opensource.org/licenses/bsd-license.php BSD
|
127 |
+
* @link http://solarphp.com/trac/core/browser/trunk/Solar/Dir.php
|
128 |
+
*
|
129 |
+
* @return string
|
130 |
+
*/
|
131 |
+
public static function tmp()
|
132 |
+
{
|
133 |
+
static $tmp = null;
|
134 |
+
if (! $tmp) {
|
135 |
+
$tmp = function_exists('sys_get_temp_dir')
|
136 |
+
? sys_get_temp_dir()
|
137 |
+
: self::_tmp();
|
138 |
+
$tmp = rtrim($tmp, DIRECTORY_SEPARATOR);
|
139 |
+
}
|
140 |
+
return $tmp;
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Returns the OS-specific directory for temporary files
|
145 |
+
*
|
146 |
+
* @author Paul M. Jones <pmjones@solarphp.com>
|
147 |
+
* @license http://opensource.org/licenses/bsd-license.php BSD
|
148 |
+
* @link http://solarphp.com/trac/core/browser/trunk/Solar/Dir.php
|
149 |
+
*
|
150 |
+
* @return string
|
151 |
+
*/
|
152 |
+
protected static function _tmp()
|
153 |
+
{
|
154 |
+
// non-Windows system?
|
155 |
+
if (strtolower(substr(PHP_OS, 0, 3)) != 'win') {
|
156 |
+
$tmp = empty($_ENV['TMPDIR']) ? getenv('TMPDIR') : $_ENV['TMPDIR'];
|
157 |
+
if ($tmp) {
|
158 |
+
return $tmp;
|
159 |
+
} else {
|
160 |
+
return '/tmp';
|
161 |
+
}
|
162 |
+
}
|
163 |
+
// Windows 'TEMP'
|
164 |
+
$tmp = empty($_ENV['TEMP']) ? getenv('TEMP') : $_ENV['TEMP'];
|
165 |
+
if ($tmp) {
|
166 |
+
return $tmp;
|
167 |
+
}
|
168 |
+
// Windows 'TMP'
|
169 |
+
$tmp = empty($_ENV['TMP']) ? getenv('TMP') : $_ENV['TMP'];
|
170 |
+
if ($tmp) {
|
171 |
+
return $tmp;
|
172 |
+
}
|
173 |
+
// Windows 'windir'
|
174 |
+
$tmp = empty($_ENV['windir']) ? getenv('windir') : $_ENV['windir'];
|
175 |
+
if ($tmp) {
|
176 |
+
return $tmp;
|
177 |
+
}
|
178 |
+
// final fallback for Windows
|
179 |
+
return getenv('SystemRoot') . '\\temp';
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Send message to the Minify logger
|
184 |
+
* @param string $msg
|
185 |
+
* @return null
|
186 |
+
*/
|
187 |
+
protected function _log($msg)
|
188 |
+
{
|
189 |
+
Minify_Logger::log($msg);
|
190 |
+
}
|
191 |
+
|
192 |
+
private $_path = null;
|
193 |
+
private $_locking = null;
|
194 |
+
}
|
min/lib/Minify/Cache/Memcache.php
ADDED
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Cache_Memcache
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Memcache-based cache class for Minify
|
9 |
+
*
|
10 |
+
* <code>
|
11 |
+
* // fall back to disk caching if memcache can't connect
|
12 |
+
* $memcache = new Memcache;
|
13 |
+
* if ($memcache->connect('localhost', 11211)) {
|
14 |
+
* Minify::setCache(new Minify_Cache_Memcache($memcache));
|
15 |
+
* } else {
|
16 |
+
* Minify::setCache();
|
17 |
+
* }
|
18 |
+
* </code>
|
19 |
+
**/
|
20 |
+
class Minify_Cache_Memcache {
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Create a Minify_Cache_Memcache object, to be passed to
|
24 |
+
* Minify::setCache().
|
25 |
+
*
|
26 |
+
* @param Memcache $memcache already-connected instance
|
27 |
+
*
|
28 |
+
* @param int $expire seconds until expiration (default = 0
|
29 |
+
* meaning the item will not get an expiration date)
|
30 |
+
*
|
31 |
+
* @return null
|
32 |
+
*/
|
33 |
+
public function __construct($memcache, $expire = 0)
|
34 |
+
{
|
35 |
+
$this->_mc = $memcache;
|
36 |
+
$this->_exp = $expire;
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Write data to cache.
|
41 |
+
*
|
42 |
+
* @param string $id cache id
|
43 |
+
*
|
44 |
+
* @param string $data
|
45 |
+
*
|
46 |
+
* @return bool success
|
47 |
+
*/
|
48 |
+
public function store($id, $data)
|
49 |
+
{
|
50 |
+
return $this->_mc->set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", 0, $this->_exp);
|
51 |
+
}
|
52 |
+
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Get the size of a cache entry
|
56 |
+
*
|
57 |
+
* @param string $id cache id
|
58 |
+
*
|
59 |
+
* @return int size in bytes
|
60 |
+
*/
|
61 |
+
public function getSize($id)
|
62 |
+
{
|
63 |
+
if (! $this->_fetch($id)) {
|
64 |
+
return false;
|
65 |
+
}
|
66 |
+
return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
|
67 |
+
? mb_strlen($this->_data, '8bit')
|
68 |
+
: strlen($this->_data);
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Does a valid cache entry exist?
|
73 |
+
*
|
74 |
+
* @param string $id cache id
|
75 |
+
*
|
76 |
+
* @param int $srcMtime mtime of the original source file(s)
|
77 |
+
*
|
78 |
+
* @return bool exists
|
79 |
+
*/
|
80 |
+
public function isValid($id, $srcMtime)
|
81 |
+
{
|
82 |
+
return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Send the cached content to output
|
87 |
+
*
|
88 |
+
* @param string $id cache id
|
89 |
+
*/
|
90 |
+
public function display($id)
|
91 |
+
{
|
92 |
+
echo $this->_fetch($id)
|
93 |
+
? $this->_data
|
94 |
+
: '';
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Fetch the cached content
|
99 |
+
*
|
100 |
+
* @param string $id cache id
|
101 |
+
*
|
102 |
+
* @return string
|
103 |
+
*/
|
104 |
+
public function fetch($id)
|
105 |
+
{
|
106 |
+
return $this->_fetch($id)
|
107 |
+
? $this->_data
|
108 |
+
: '';
|
109 |
+
}
|
110 |
+
|
111 |
+
private $_mc = null;
|
112 |
+
private $_exp = null;
|
113 |
+
|
114 |
+
// cache of most recently fetched id
|
115 |
+
private $_lm = null;
|
116 |
+
private $_data = null;
|
117 |
+
private $_id = null;
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Fetch data and timestamp from memcache, store in instance
|
121 |
+
*
|
122 |
+
* @param string $id
|
123 |
+
*
|
124 |
+
* @return bool success
|
125 |
+
*/
|
126 |
+
private function _fetch($id)
|
127 |
+
{
|
128 |
+
if ($this->_id === $id) {
|
129 |
+
return true;
|
130 |
+
}
|
131 |
+
$ret = $this->_mc->get($id);
|
132 |
+
if (false === $ret) {
|
133 |
+
$this->_id = null;
|
134 |
+
return false;
|
135 |
+
}
|
136 |
+
list($this->_lm, $this->_data) = explode('|', $ret, 2);
|
137 |
+
$this->_id = $id;
|
138 |
+
return true;
|
139 |
+
}
|
140 |
+
}
|
min/lib/Minify/Cache/XCache.php
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Cache_XCache
|
4 |
+
*
|
5 |
+
* @link http://xcache.lighttpd.net/
|
6 |
+
* @package Minify
|
7 |
+
*/
|
8 |
+
|
9 |
+
/**
|
10 |
+
* XCache-based cache class for Minify
|
11 |
+
* {@see http://xcache.lighttpd.net/wiki/XcacheApi XCache API}
|
12 |
+
*
|
13 |
+
* <code>
|
14 |
+
* Minify::setCache(new Minify_Cache_XCache());
|
15 |
+
* </code>
|
16 |
+
*
|
17 |
+
* @package Minify
|
18 |
+
* @author Elan Ruusamäe <glen@delfi.ee>
|
19 |
+
**/
|
20 |
+
class Minify_Cache_XCache {
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Create a Minify_Cache_XCache object, to be passed to
|
24 |
+
* Minify::setCache().
|
25 |
+
*
|
26 |
+
* @param int $expire seconds until expiration (default = 0
|
27 |
+
* meaning the item will not get an expiration date)
|
28 |
+
*/
|
29 |
+
public function __construct($expire = 0)
|
30 |
+
{
|
31 |
+
$this->_exp = $expire;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Write data to cache.
|
36 |
+
*
|
37 |
+
* @param string $id cache id
|
38 |
+
* @param string $data
|
39 |
+
* @return bool success
|
40 |
+
*/
|
41 |
+
public function store($id, $data)
|
42 |
+
{
|
43 |
+
return xcache_set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp);
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Get the size of a cache entry
|
48 |
+
*
|
49 |
+
* @param string $id cache id
|
50 |
+
* @return int size in bytes
|
51 |
+
*/
|
52 |
+
public function getSize($id)
|
53 |
+
{
|
54 |
+
if (! $this->_fetch($id)) {
|
55 |
+
return false;
|
56 |
+
}
|
57 |
+
return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
|
58 |
+
? mb_strlen($this->_data, '8bit')
|
59 |
+
: strlen($this->_data);
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Does a valid cache entry exist?
|
64 |
+
*
|
65 |
+
* @param string $id cache id
|
66 |
+
* @param int $srcMtime mtime of the original source file(s)
|
67 |
+
* @return bool exists
|
68 |
+
*/
|
69 |
+
public function isValid($id, $srcMtime)
|
70 |
+
{
|
71 |
+
return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Send the cached content to output
|
76 |
+
*
|
77 |
+
* @param string $id cache id
|
78 |
+
*/
|
79 |
+
public function display($id)
|
80 |
+
{
|
81 |
+
echo $this->_fetch($id)
|
82 |
+
? $this->_data
|
83 |
+
: '';
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Fetch the cached content
|
88 |
+
*
|
89 |
+
* @param string $id cache id
|
90 |
+
* @return string
|
91 |
+
*/
|
92 |
+
public function fetch($id)
|
93 |
+
{
|
94 |
+
return $this->_fetch($id)
|
95 |
+
? $this->_data
|
96 |
+
: '';
|
97 |
+
}
|
98 |
+
|
99 |
+
private $_exp = null;
|
100 |
+
|
101 |
+
// cache of most recently fetched id
|
102 |
+
private $_lm = null;
|
103 |
+
private $_data = null;
|
104 |
+
private $_id = null;
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Fetch data and timestamp from xcache, store in instance
|
108 |
+
*
|
109 |
+
* @param string $id
|
110 |
+
* @return bool success
|
111 |
+
*/
|
112 |
+
private function _fetch($id)
|
113 |
+
{
|
114 |
+
if ($this->_id === $id) {
|
115 |
+
return true;
|
116 |
+
}
|
117 |
+
$ret = xcache_get($id);
|
118 |
+
if (false === $ret) {
|
119 |
+
$this->_id = null;
|
120 |
+
return false;
|
121 |
+
}
|
122 |
+
list($this->_lm, $this->_data) = explode('|', $ret, 2);
|
123 |
+
$this->_id = $id;
|
124 |
+
return true;
|
125 |
+
}
|
126 |
+
}
|
min/lib/Minify/Cache/ZendPlatform.php
ADDED
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Cache_ZendPlatform
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
|
8 |
+
/**
|
9 |
+
* ZendPlatform-based cache class for Minify
|
10 |
+
*
|
11 |
+
* Based on Minify_Cache_APC, uses output_cache_get/put (currently deprecated)
|
12 |
+
*
|
13 |
+
* <code>
|
14 |
+
* Minify::setCache(new Minify_Cache_ZendPlatform());
|
15 |
+
* </code>
|
16 |
+
*
|
17 |
+
* @package Minify
|
18 |
+
* @author Patrick van Dissel
|
19 |
+
*/
|
20 |
+
class Minify_Cache_ZendPlatform {
|
21 |
+
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Create a Minify_Cache_ZendPlatform object, to be passed to
|
25 |
+
* Minify::setCache().
|
26 |
+
*
|
27 |
+
* @param int $expire seconds until expiration (default = 0
|
28 |
+
* meaning the item will not get an expiration date)
|
29 |
+
*
|
30 |
+
* @return null
|
31 |
+
*/
|
32 |
+
public function __construct($expire = 0)
|
33 |
+
{
|
34 |
+
$this->_exp = $expire;
|
35 |
+
}
|
36 |
+
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Write data to cache.
|
40 |
+
*
|
41 |
+
* @param string $id cache id
|
42 |
+
*
|
43 |
+
* @param string $data
|
44 |
+
*
|
45 |
+
* @return bool success
|
46 |
+
*/
|
47 |
+
public function store($id, $data)
|
48 |
+
{
|
49 |
+
return output_cache_put($id, "{$_SERVER['REQUEST_TIME']}|{$data}");
|
50 |
+
}
|
51 |
+
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Get the size of a cache entry
|
55 |
+
*
|
56 |
+
* @param string $id cache id
|
57 |
+
*
|
58 |
+
* @return int size in bytes
|
59 |
+
*/
|
60 |
+
public function getSize($id)
|
61 |
+
{
|
62 |
+
return $this->_fetch($id)
|
63 |
+
? strlen($this->_data)
|
64 |
+
: false;
|
65 |
+
}
|
66 |
+
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Does a valid cache entry exist?
|
70 |
+
*
|
71 |
+
* @param string $id cache id
|
72 |
+
*
|
73 |
+
* @param int $srcMtime mtime of the original source file(s)
|
74 |
+
*
|
75 |
+
* @return bool exists
|
76 |
+
*/
|
77 |
+
public function isValid($id, $srcMtime)
|
78 |
+
{
|
79 |
+
$ret = ($this->_fetch($id) && ($this->_lm >= $srcMtime));
|
80 |
+
return $ret;
|
81 |
+
}
|
82 |
+
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Send the cached content to output
|
86 |
+
*
|
87 |
+
* @param string $id cache id
|
88 |
+
*/
|
89 |
+
public function display($id)
|
90 |
+
{
|
91 |
+
echo $this->_fetch($id)
|
92 |
+
? $this->_data
|
93 |
+
: '';
|
94 |
+
}
|
95 |
+
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Fetch the cached content
|
99 |
+
*
|
100 |
+
* @param string $id cache id
|
101 |
+
*
|
102 |
+
* @return string
|
103 |
+
*/
|
104 |
+
public function fetch($id)
|
105 |
+
{
|
106 |
+
return $this->_fetch($id)
|
107 |
+
? $this->_data
|
108 |
+
: '';
|
109 |
+
}
|
110 |
+
|
111 |
+
|
112 |
+
private $_exp = null;
|
113 |
+
|
114 |
+
|
115 |
+
// cache of most recently fetched id
|
116 |
+
private $_lm = null;
|
117 |
+
private $_data = null;
|
118 |
+
private $_id = null;
|
119 |
+
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Fetch data and timestamp from ZendPlatform, store in instance
|
123 |
+
*
|
124 |
+
* @param string $id
|
125 |
+
*
|
126 |
+
* @return bool success
|
127 |
+
*/
|
128 |
+
private function _fetch($id)
|
129 |
+
{
|
130 |
+
if ($this->_id === $id) {
|
131 |
+
return true;
|
132 |
+
}
|
133 |
+
$ret = output_cache_get($id, $this->_exp);
|
134 |
+
if (false === $ret) {
|
135 |
+
$this->_id = null;
|
136 |
+
return false;
|
137 |
+
}
|
138 |
+
list($this->_lm, $this->_data) = explode('|', $ret, 2);
|
139 |
+
$this->_id = $id;
|
140 |
+
return true;
|
141 |
+
}
|
142 |
+
}
|
min/lib/Minify/ClosureCompiler.php
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_ClosureCompiler
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Compress Javascript using the Closure Compiler
|
9 |
+
*
|
10 |
+
* You must set $jarFile and $tempDir before calling the minify functions.
|
11 |
+
* Also, depending on your shell's environment, you may need to specify
|
12 |
+
* the full path to java in $javaExecutable or use putenv() to setup the
|
13 |
+
* Java environment.
|
14 |
+
*
|
15 |
+
* <code>
|
16 |
+
* Minify_ClosureCompiler::$jarFile = '/path/to/closure-compiler-20120123.jar';
|
17 |
+
* Minify_ClosureCompiler::$tempDir = '/tmp';
|
18 |
+
* $code = Minify_ClosureCompiler::minify(
|
19 |
+
* $code,
|
20 |
+
* array('compilation_level' => 'SIMPLE_OPTIMIZATIONS')
|
21 |
+
* );
|
22 |
+
*
|
23 |
+
* --compilation_level WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS
|
24 |
+
*
|
25 |
+
* </code>
|
26 |
+
*
|
27 |
+
* @todo unit tests, $options docs
|
28 |
+
* @todo more options support (or should just passthru them all?)
|
29 |
+
*
|
30 |
+
* @package Minify
|
31 |
+
* @author Stephen Clay <steve@mrclay.org>
|
32 |
+
* @author Elan Ruusamäe <glen@delfi.ee>
|
33 |
+
*/
|
34 |
+
class Minify_ClosureCompiler {
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Filepath of the Closure Compiler jar file. This must be set before
|
38 |
+
* calling minifyJs().
|
39 |
+
*
|
40 |
+
* @var string
|
41 |
+
*/
|
42 |
+
public static $jarFile = null;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Writable temp directory. This must be set before calling minifyJs().
|
46 |
+
*
|
47 |
+
* @var string
|
48 |
+
*/
|
49 |
+
public static $tempDir = null;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Filepath of "java" executable (may be needed if not in shell's PATH)
|
53 |
+
*
|
54 |
+
* @var string
|
55 |
+
*/
|
56 |
+
public static $javaExecutable = 'java';
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Minify a Javascript string
|
60 |
+
*
|
61 |
+
* @param string $js
|
62 |
+
*
|
63 |
+
* @param array $options (verbose is ignored)
|
64 |
+
*
|
65 |
+
* @see https://code.google.com/p/closure-compiler/source/browse/trunk/README
|
66 |
+
*
|
67 |
+
* @return string
|
68 |
+
*/
|
69 |
+
public static function minify($js, $options = array())
|
70 |
+
{
|
71 |
+
self::_prepare();
|
72 |
+
if (! ($tmpFile = tempnam(self::$tempDir, 'cc_'))) {
|
73 |
+
throw new Exception('Minify_ClosureCompiler : could not create temp file.');
|
74 |
+
}
|
75 |
+
file_put_contents($tmpFile, $js);
|
76 |
+
exec(self::_getCmd($options, $tmpFile), $output, $result_code);
|
77 |
+
unlink($tmpFile);
|
78 |
+
if ($result_code != 0) {
|
79 |
+
throw new Exception('Minify_ClosureCompiler : Closure Compiler execution failed.');
|
80 |
+
}
|
81 |
+
return implode("\n", $output);
|
82 |
+
}
|
83 |
+
|
84 |
+
private static function _getCmd($userOptions, $tmpFile)
|
85 |
+
{
|
86 |
+
$o = array_merge(
|
87 |
+
array(
|
88 |
+
'charset' => 'utf-8',
|
89 |
+
'compilation_level' => 'SIMPLE_OPTIMIZATIONS',
|
90 |
+
),
|
91 |
+
$userOptions
|
92 |
+
);
|
93 |
+
$cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile)
|
94 |
+
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
|
95 |
+
? " --charset {$o['charset']}"
|
96 |
+
: '');
|
97 |
+
|
98 |
+
foreach (array('compilation_level') as $opt) {
|
99 |
+
if ($o[$opt]) {
|
100 |
+
$cmd .= " --{$opt} ". escapeshellarg($o[$opt]);
|
101 |
+
}
|
102 |
+
}
|
103 |
+
return $cmd . ' ' . escapeshellarg($tmpFile);
|
104 |
+
}
|
105 |
+
|
106 |
+
private static function _prepare()
|
107 |
+
{
|
108 |
+
if (! is_file(self::$jarFile)) {
|
109 |
+
throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.');
|
110 |
+
}
|
111 |
+
if (! is_readable(self::$jarFile)) {
|
112 |
+
throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.');
|
113 |
+
}
|
114 |
+
if (! is_dir(self::$tempDir)) {
|
115 |
+
throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.');
|
116 |
+
}
|
117 |
+
if (! is_writable(self::$tempDir)) {
|
118 |
+
throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.');
|
119 |
+
}
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
/* vim:ts=4:sw=4:et */
|
min/lib/Minify/CommentPreserver.php
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_CommentPreserver
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Process a string in pieces preserving C-style comments that begin with "/*!"
|
9 |
+
*
|
10 |
+
* @package Minify
|
11 |
+
* @author Stephen Clay <steve@mrclay.org>
|
12 |
+
*/
|
13 |
+
class Minify_CommentPreserver {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* String to be prepended to each preserved comment
|
17 |
+
*
|
18 |
+
* @var string
|
19 |
+
*/
|
20 |
+
public static $prepend = "\n";
|
21 |
+
|
22 |
+
/**
|
23 |
+
* String to be appended to each preserved comment
|
24 |
+
*
|
25 |
+
* @var string
|
26 |
+
*/
|
27 |
+
public static $append = "\n";
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Process a string outside of C-style comments that begin with "/*!"
|
31 |
+
*
|
32 |
+
* On each non-empty string outside these comments, the given processor
|
33 |
+
* function will be called. The comments will be surrounded by
|
34 |
+
* Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append.
|
35 |
+
*
|
36 |
+
* @param string $content
|
37 |
+
* @param callback $processor function
|
38 |
+
* @param array $args array of extra arguments to pass to the processor
|
39 |
+
* function (default = array())
|
40 |
+
* @return string
|
41 |
+
*/
|
42 |
+
public static function process($content, $processor, $args = array())
|
43 |
+
{
|
44 |
+
$ret = '';
|
45 |
+
while (true) {
|
46 |
+
list($beforeComment, $comment, $afterComment) = self::_nextComment($content);
|
47 |
+
if ('' !== $beforeComment) {
|
48 |
+
$callArgs = $args;
|
49 |
+
array_unshift($callArgs, $beforeComment);
|
50 |
+
$ret .= call_user_func_array($processor, $callArgs);
|
51 |
+
}
|
52 |
+
if (false === $comment) {
|
53 |
+
break;
|
54 |
+
}
|
55 |
+
$ret .= $comment;
|
56 |
+
$content = $afterComment;
|
57 |
+
}
|
58 |
+
return $ret;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Extract comments that YUI Compressor preserves.
|
63 |
+
*
|
64 |
+
* @param string $in input
|
65 |
+
*
|
66 |
+
* @return array 3 elements are returned. If a YUI comment is found, the
|
67 |
+
* 2nd element is the comment and the 1st and 3rd are the surrounding
|
68 |
+
* strings. If no comment is found, the entire string is returned as the
|
69 |
+
* 1st element and the other two are false.
|
70 |
+
*/
|
71 |
+
private static function _nextComment($in)
|
72 |
+
{
|
73 |
+
if (
|
74 |
+
false === ($start = strpos($in, '/*!'))
|
75 |
+
|| false === ($end = strpos($in, '*/', $start + 3))
|
76 |
+
) {
|
77 |
+
return array($in, false, false);
|
78 |
+
}
|
79 |
+
$ret = array(
|
80 |
+
substr($in, 0, $start)
|
81 |
+
,self::$prepend . '/*!' . substr($in, $start + 3, $end - $start - 1) . self::$append
|
82 |
+
);
|
83 |
+
$endChars = (strlen($in) - $end - 2);
|
84 |
+
$ret[] = (0 === $endChars)
|
85 |
+
? ''
|
86 |
+
: substr($in, -$endChars);
|
87 |
+
return $ret;
|
88 |
+
}
|
89 |
+
}
|
min/lib/Minify/Controller/Base.php
ADDED
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Controller_Base
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Base class for Minify controller
|
9 |
+
*
|
10 |
+
* The controller class validates a request and uses it to create sources
|
11 |
+
* for minification and set options like contentType. It's also responsible
|
12 |
+
* for loading minifier code upon request.
|
13 |
+
*
|
14 |
+
* @package Minify
|
15 |
+
* @author Stephen Clay <steve@mrclay.org>
|
16 |
+
*/
|
17 |
+
abstract class Minify_Controller_Base {
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Setup controller sources and set an needed options for Minify::source
|
21 |
+
*
|
22 |
+
* You must override this method in your subclass controller to set
|
23 |
+
* $this->sources. If the request is NOT valid, make sure $this->sources
|
24 |
+
* is left an empty array. Then strip any controller-specific options from
|
25 |
+
* $options and return it. To serve files, $this->sources must be an array of
|
26 |
+
* Minify_Source objects.
|
27 |
+
*
|
28 |
+
* @param array $options controller and Minify options
|
29 |
+
*
|
30 |
+
* @return array $options Minify::serve options
|
31 |
+
*/
|
32 |
+
abstract public function setupSources($options);
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Get default Minify options for this controller.
|
36 |
+
*
|
37 |
+
* Override in subclass to change defaults
|
38 |
+
*
|
39 |
+
* @return array options for Minify
|
40 |
+
*/
|
41 |
+
public function getDefaultMinifyOptions() {
|
42 |
+
return array(
|
43 |
+
'isPublic' => true
|
44 |
+
,'encodeOutput' => function_exists('gzdeflate')
|
45 |
+
,'encodeMethod' => null // determine later
|
46 |
+
,'encodeLevel' => 9
|
47 |
+
,'minifierOptions' => array() // no minifier options
|
48 |
+
,'contentTypeCharset' => 'utf-8'
|
49 |
+
,'maxAge' => 1800 // 30 minutes
|
50 |
+
,'rewriteCssUris' => true
|
51 |
+
,'bubbleCssImports' => false
|
52 |
+
,'quiet' => false // serve() will send headers and output
|
53 |
+
,'debug' => false
|
54 |
+
|
55 |
+
// if you override these, the response codes MUST be directly after
|
56 |
+
// the first space.
|
57 |
+
,'badRequestHeader' => 'HTTP/1.0 400 Bad Request'
|
58 |
+
,'errorHeader' => 'HTTP/1.0 500 Internal Server Error'
|
59 |
+
|
60 |
+
// callback function to see/modify content of all sources
|
61 |
+
,'postprocessor' => null
|
62 |
+
// file to require to load preprocessor
|
63 |
+
,'postprocessorRequire' => null
|
64 |
+
);
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Get default minifiers for this controller.
|
69 |
+
*
|
70 |
+
* Override in subclass to change defaults
|
71 |
+
*
|
72 |
+
* @return array minifier callbacks for common types
|
73 |
+
*/
|
74 |
+
public function getDefaultMinifers() {
|
75 |
+
$ret[Minify::TYPE_JS] = array('JSMin', 'minify');
|
76 |
+
$ret[Minify::TYPE_CSS] = array('Minify_CSS', 'minify');
|
77 |
+
$ret[Minify::TYPE_HTML] = array('Minify_HTML', 'minify');
|
78 |
+
return $ret;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Is a user-given file within an allowable directory, existing,
|
83 |
+
* and having an extension js/css/html/txt ?
|
84 |
+
*
|
85 |
+
* This is a convenience function for controllers that have to accept
|
86 |
+
* user-given paths
|
87 |
+
*
|
88 |
+
* @param string $file full file path (already processed by realpath())
|
89 |
+
*
|
90 |
+
* @param array $safeDirs directories where files are safe to serve. Files can also
|
91 |
+
* be in subdirectories of these directories.
|
92 |
+
*
|
93 |
+
* @return bool file is safe
|
94 |
+
*
|
95 |
+
* @deprecated use checkAllowDirs, checkNotHidden instead
|
96 |
+
*/
|
97 |
+
public static function _fileIsSafe($file, $safeDirs)
|
98 |
+
{
|
99 |
+
$pathOk = false;
|
100 |
+
foreach ((array)$safeDirs as $safeDir) {
|
101 |
+
if (strpos($file, $safeDir) === 0) {
|
102 |
+
$pathOk = true;
|
103 |
+
break;
|
104 |
+
}
|
105 |
+
}
|
106 |
+
$base = basename($file);
|
107 |
+
if (! $pathOk || ! is_file($file) || $base[0] === '.') {
|
108 |
+
return false;
|
109 |
+
}
|
110 |
+
list($revExt) = explode('.', strrev($base));
|
111 |
+
return in_array(strrev($revExt), array('js', 'css', 'html', 'txt'));
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* @param string $file
|
116 |
+
* @param array $allowDirs
|
117 |
+
* @param string $uri
|
118 |
+
* @return bool
|
119 |
+
* @throws Exception
|
120 |
+
*/
|
121 |
+
public static function checkAllowDirs($file, $allowDirs, $uri)
|
122 |
+
{
|
123 |
+
foreach ((array)$allowDirs as $allowDir) {
|
124 |
+
if (strpos($file, $allowDir) === 0) {
|
125 |
+
return true;
|
126 |
+
}
|
127 |
+
}
|
128 |
+
throw new Exception("File '$file' is outside \$allowDirs. If the path is"
|
129 |
+
. " resolved via an alias/symlink, look into the \$min_symlinks option."
|
130 |
+
. " E.g. \$min_symlinks['/" . dirname($uri) . "'] = '" . dirname($file) . "';");
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @param string $file
|
135 |
+
* @throws Exception
|
136 |
+
*/
|
137 |
+
public static function checkNotHidden($file)
|
138 |
+
{
|
139 |
+
$b = basename($file);
|
140 |
+
if (0 === strpos($b, '.')) {
|
141 |
+
throw new Exception("Filename '$b' starts with period (may be hidden)");
|
142 |
+
}
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* instances of Minify_Source, which provide content and any individual minification needs.
|
147 |
+
*
|
148 |
+
* @var array
|
149 |
+
*
|
150 |
+
* @see Minify_Source
|
151 |
+
*/
|
152 |
+
public $sources = array();
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Short name to place inside cache id
|
156 |
+
*
|
157 |
+
* The setupSources() method may choose to set this, making it easier to
|
158 |
+
* recognize a particular set of sources/settings in the cache folder. It
|
159 |
+
* will be filtered and truncated to make the final cache id <= 250 bytes.
|
160 |
+
*
|
161 |
+
* @var string
|
162 |
+
*/
|
163 |
+
public $selectionId = '';
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Mix in default controller options with user-given options
|
167 |
+
*
|
168 |
+
* @param array $options user options
|
169 |
+
*
|
170 |
+
* @return array mixed options
|
171 |
+
*/
|
172 |
+
public final function mixInDefaultOptions($options)
|
173 |
+
{
|
174 |
+
$ret = array_merge(
|
175 |
+
$this->getDefaultMinifyOptions(), $options
|
176 |
+
);
|
177 |
+
if (! isset($options['minifiers'])) {
|
178 |
+
$options['minifiers'] = array();
|
179 |
+
}
|
180 |
+
$ret['minifiers'] = array_merge(
|
181 |
+
$this->getDefaultMinifers(), $options['minifiers']
|
182 |
+
);
|
183 |
+
return $ret;
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Analyze sources (if there are any) and set $options 'contentType'
|
188 |
+
* and 'lastModifiedTime' if they already aren't.
|
189 |
+
*
|
190 |
+
* @param array $options options for Minify
|
191 |
+
*
|
192 |
+
* @return array options for Minify
|
193 |
+
*/
|
194 |
+
public final function analyzeSources($options = array())
|
195 |
+
{
|
196 |
+
if ($this->sources) {
|
197 |
+
if (! isset($options['contentType'])) {
|
198 |
+
$options['contentType'] = Minify_Source::getContentType($this->sources);
|
199 |
+
}
|
200 |
+
// last modified is needed for caching, even if setExpires is set
|
201 |
+
if (! isset($options['lastModifiedTime'])) {
|
202 |
+
$max = 0;
|
203 |
+
foreach ($this->sources as $source) {
|
204 |
+
$max = max($source->lastModified, $max);
|
205 |
+
}
|
206 |
+
$options['lastModifiedTime'] = $max;
|
207 |
+
}
|
208 |
+
}
|
209 |
+
return $options;
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Send message to the Minify logger
|
214 |
+
*
|
215 |
+
* @param string $msg
|
216 |
+
*
|
217 |
+
* @return null
|
218 |
+
*/
|
219 |
+
public function log($msg) {
|
220 |
+
Minify_Logger::log($msg);
|
221 |
+
}
|
222 |
+
}
|
min/lib/Minify/Controller/Files.php
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Controller_Files
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Controller class for minifying a set of files
|
9 |
+
*
|
10 |
+
* E.g. the following would serve the minified Javascript for a site
|
11 |
+
* <code>
|
12 |
+
* Minify::serve('Files', array(
|
13 |
+
* 'files' => array(
|
14 |
+
* '//js/jquery.js'
|
15 |
+
* ,'//js/plugins.js'
|
16 |
+
* ,'/home/username/file.js'
|
17 |
+
* )
|
18 |
+
* ));
|
19 |
+
* </code>
|
20 |
+
*
|
21 |
+
* As a shortcut, the controller will replace "//" at the beginning
|
22 |
+
* of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'.
|
23 |
+
*
|
24 |
+
* @package Minify
|
25 |
+
* @author Stephen Clay <steve@mrclay.org>
|
26 |
+
*/
|
27 |
+
class Minify_Controller_Files extends Minify_Controller_Base {
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Set up file sources
|
31 |
+
*
|
32 |
+
* @param array $options controller and Minify options
|
33 |
+
* @return array Minify options
|
34 |
+
*
|
35 |
+
* Controller options:
|
36 |
+
*
|
37 |
+
* 'files': (required) array of complete file paths, or a single path
|
38 |
+
*/
|
39 |
+
public function setupSources($options) {
|
40 |
+
// strip controller options
|
41 |
+
|
42 |
+
$files = $options['files'];
|
43 |
+
// if $files is a single object, casting will break it
|
44 |
+
if (is_object($files)) {
|
45 |
+
$files = array($files);
|
46 |
+
} elseif (! is_array($files)) {
|
47 |
+
$files = (array)$files;
|
48 |
+
}
|
49 |
+
unset($options['files']);
|
50 |
+
|
51 |
+
$sources = array();
|
52 |
+
foreach ($files as $file) {
|
53 |
+
if ($file instanceof Minify_Source) {
|
54 |
+
$sources[] = $file;
|
55 |
+
continue;
|
56 |
+
}
|
57 |
+
if (0 === strpos($file, '//')) {
|
58 |
+
$file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1);
|
59 |
+
}
|
60 |
+
$realPath = realpath($file);
|
61 |
+
if (is_file($realPath)) {
|
62 |
+
$sources[] = new Minify_Source(array(
|
63 |
+
'filepath' => $realPath
|
64 |
+
));
|
65 |
+
} else {
|
66 |
+
$this->log("The path \"{$file}\" could not be found (or was not a file)");
|
67 |
+
return $options;
|
68 |
+
}
|
69 |
+
}
|
70 |
+
if ($sources) {
|
71 |
+
$this->sources = $sources;
|
72 |
+
}
|
73 |
+
return $options;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
min/lib/Minify/Controller/Groups.php
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Controller_Groups
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Controller class for serving predetermined groups of minimized sets, selected
|
9 |
+
* by PATH_INFO
|
10 |
+
*
|
11 |
+
* <code>
|
12 |
+
* Minify::serve('Groups', array(
|
13 |
+
* 'groups' => array(
|
14 |
+
* 'css' => array('//css/type.css', '//css/layout.css')
|
15 |
+
* ,'js' => array('//js/jquery.js', '//js/site.js')
|
16 |
+
* )
|
17 |
+
* ));
|
18 |
+
* </code>
|
19 |
+
*
|
20 |
+
* If the above code were placed in /serve.php, it would enable the URLs
|
21 |
+
* /serve.php/js and /serve.php/css
|
22 |
+
*
|
23 |
+
* As a shortcut, the controller will replace "//" at the beginning
|
24 |
+
* of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'.
|
25 |
+
*
|
26 |
+
* @package Minify
|
27 |
+
* @author Stephen Clay <steve@mrclay.org>
|
28 |
+
*/
|
29 |
+
class Minify_Controller_Groups extends Minify_Controller_Base {
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Set up groups of files as sources
|
33 |
+
*
|
34 |
+
* @param array $options controller and Minify options
|
35 |
+
*
|
36 |
+
* 'groups': (required) array mapping PATH_INFO strings to arrays
|
37 |
+
* of complete file paths. @see Minify_Controller_Groups
|
38 |
+
*
|
39 |
+
* @return array Minify options
|
40 |
+
*/
|
41 |
+
public function setupSources($options) {
|
42 |
+
// strip controller options
|
43 |
+
$groups = $options['groups'];
|
44 |
+
unset($options['groups']);
|
45 |
+
|
46 |
+
// mod_fcgid places PATH_INFO in ORIG_PATH_INFO
|
47 |
+
$pi = isset($_SERVER['ORIG_PATH_INFO'])
|
48 |
+
? substr($_SERVER['ORIG_PATH_INFO'], 1)
|
49 |
+
: (isset($_SERVER['PATH_INFO'])
|
50 |
+
? substr($_SERVER['PATH_INFO'], 1)
|
51 |
+
: false
|
52 |
+
);
|
53 |
+
if (false === $pi || ! isset($groups[$pi])) {
|
54 |
+
// no PATH_INFO or not a valid group
|
55 |
+
$this->log("Missing PATH_INFO or no group set for \"$pi\"");
|
56 |
+
return $options;
|
57 |
+
}
|
58 |
+
$sources = array();
|
59 |
+
|
60 |
+
$files = $groups[$pi];
|
61 |
+
// if $files is a single object, casting will break it
|
62 |
+
if (is_object($files)) {
|
63 |
+
$files = array($files);
|
64 |
+
} elseif (! is_array($files)) {
|
65 |
+
$files = (array)$files;
|
66 |
+
}
|
67 |
+
foreach ($files as $file) {
|
68 |
+
if ($file instanceof Minify_Source) {
|
69 |
+
$sources[] = $file;
|
70 |
+
continue;
|
71 |
+
}
|
72 |
+
if (0 === strpos($file, '//')) {
|
73 |
+
$file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1);
|
74 |
+
}
|
75 |
+
$realPath = realpath($file);
|
76 |
+
if (is_file($realPath)) {
|
77 |
+
$sources[] = new Minify_Source(array(
|
78 |
+
'filepath' => $realPath
|
79 |
+
));
|
80 |
+
} else {
|
81 |
+
$this->log("The path \"{$file}\" could not be found (or was not a file)");
|
82 |
+
return $options;
|
83 |
+
}
|
84 |
+
}
|
85 |
+
if ($sources) {
|
86 |
+
$this->sources = $sources;
|
87 |
+
}
|
88 |
+
return $options;
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
min/lib/Minify/Controller/MinApp.php
ADDED
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Controller_MinApp
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Controller class for requests to /min/index.php
|
9 |
+
*
|
10 |
+
* @package Minify
|
11 |
+
* @author Stephen Clay <steve@mrclay.org>
|
12 |
+
*/
|
13 |
+
class Minify_Controller_MinApp extends Minify_Controller_Base {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Set up groups of files as sources
|
17 |
+
*
|
18 |
+
* @param array $options controller and Minify options
|
19 |
+
*
|
20 |
+
* @return array Minify options
|
21 |
+
*/
|
22 |
+
public function setupSources($options) {
|
23 |
+
// PHP insecure by default: realpath() and other FS functions can't handle null bytes.
|
24 |
+
foreach (array('g', 'b', 'f') as $key) {
|
25 |
+
if (isset($_GET[$key])) {
|
26 |
+
$_GET[$key] = str_replace("\x00", '', (string)$_GET[$key]);
|
27 |
+
}
|
28 |
+
}
|
29 |
+
|
30 |
+
// filter controller options
|
31 |
+
$cOptions = array_merge(
|
32 |
+
array(
|
33 |
+
'allowDirs' => '//'
|
34 |
+
,'groupsOnly' => false
|
35 |
+
,'groups' => array()
|
36 |
+
,'noMinPattern' => '@[-\\.]min\\.(?:js|css)$@i' // matched against basename
|
37 |
+
)
|
38 |
+
,(isset($options['minApp']) ? $options['minApp'] : array())
|
39 |
+
);
|
40 |
+
unset($options['minApp']);
|
41 |
+
$sources = array();
|
42 |
+
$this->selectionId = '';
|
43 |
+
$firstMissingResource = null;
|
44 |
+
if (isset($_GET['g'])) {
|
45 |
+
// add group(s)
|
46 |
+
$this->selectionId .= 'g=' . $_GET['g'];
|
47 |
+
$keys = explode(',', $_GET['g']);
|
48 |
+
if ($keys != array_unique($keys)) {
|
49 |
+
$this->log("Duplicate group key found.");
|
50 |
+
return $options;
|
51 |
+
}
|
52 |
+
$keys = explode(',', $_GET['g']);
|
53 |
+
foreach ($keys as $key) {
|
54 |
+
if (! isset($cOptions['groups'][$key])) {
|
55 |
+
$this->log("A group configuration for \"{$key}\" was not found");
|
56 |
+
return $options;
|
57 |
+
}
|
58 |
+
$files = $cOptions['groups'][$key];
|
59 |
+
// if $files is a single object, casting will break it
|
60 |
+
if (is_object($files)) {
|
61 |
+
$files = array($files);
|
62 |
+
} elseif (! is_array($files)) {
|
63 |
+
$files = (array)$files;
|
64 |
+
}
|
65 |
+
foreach ($files as $file) {
|
66 |
+
if ($file instanceof Minify_Source) {
|
67 |
+
$sources[] = $file;
|
68 |
+
continue;
|
69 |
+
}
|
70 |
+
if (0 === strpos($file, '//')) {
|
71 |
+
$file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1);
|
72 |
+
}
|
73 |
+
$realpath = realpath($file);
|
74 |
+
if ($realpath && is_file($realpath)) {
|
75 |
+
$sources[] = $this->_getFileSource($realpath, $cOptions);
|
76 |
+
} else {
|
77 |
+
$this->log("The path \"{$file}\" (realpath \"{$realpath}\") could not be found (or was not a file)");
|
78 |
+
if (null === $firstMissingResource) {
|
79 |
+
$firstMissingResource = basename($file);
|
80 |
+
continue;
|
81 |
+
} else {
|
82 |
+
$secondMissingResource = basename($file);
|
83 |
+
$this->log("More than one file was missing: '$firstMissingResource', '$secondMissingResource'");
|
84 |
+
return $options;
|
85 |
+
}
|
86 |
+
}
|
87 |
+
}
|
88 |
+
if ($sources) {
|
89 |
+
try {
|
90 |
+
$this->checkType($sources[0]);
|
91 |
+
} catch (Exception $e) {
|
92 |
+
$this->log($e->getMessage());
|
93 |
+
return $options;
|
94 |
+
}
|
95 |
+
}
|
96 |
+
}
|
97 |
+
}
|
98 |
+
if (! $cOptions['groupsOnly'] && isset($_GET['f'])) {
|
99 |
+
// try user files
|
100 |
+
// The following restrictions are to limit the URLs that minify will
|
101 |
+
// respond to.
|
102 |
+
if (// verify at least one file, files are single comma separated,
|
103 |
+
// and are all same extension
|
104 |
+
! preg_match('/^[^,]+\\.(css|js)(?:,[^,]+\\.\\1)*$/', $_GET['f'], $m)
|
105 |
+
// no "//"
|
106 |
+
|| strpos($_GET['f'], '//') !== false
|
107 |
+
// no "\"
|
108 |
+
|| strpos($_GET['f'], '\\') !== false
|
109 |
+
) {
|
110 |
+
$this->log("GET param 'f' was invalid");
|
111 |
+
return $options;
|
112 |
+
}
|
113 |
+
$ext = ".{$m[1]}";
|
114 |
+
try {
|
115 |
+
$this->checkType($m[1]);
|
116 |
+
} catch (Exception $e) {
|
117 |
+
$this->log($e->getMessage());
|
118 |
+
return $options;
|
119 |
+
}
|
120 |
+
$files = explode(',', $_GET['f']);
|
121 |
+
if ($files != array_unique($files)) {
|
122 |
+
$this->log("Duplicate files were specified");
|
123 |
+
return $options;
|
124 |
+
}
|
125 |
+
if (isset($_GET['b'])) {
|
126 |
+
// check for validity
|
127 |
+
if (preg_match('@^[^/]+(?:/[^/]+)*$@', $_GET['b'])
|
128 |
+
&& false === strpos($_GET['b'], '..')
|
129 |
+
&& $_GET['b'] !== '.') {
|
130 |
+
// valid base
|
131 |
+
$base = "/{$_GET['b']}/";
|
132 |
+
} else {
|
133 |
+
$this->log("GET param 'b' was invalid");
|
134 |
+
return $options;
|
135 |
+
}
|
136 |
+
} else {
|
137 |
+
$base = '/';
|
138 |
+
}
|
139 |
+
$allowDirs = array();
|
140 |
+
foreach ((array)$cOptions['allowDirs'] as $allowDir) {
|
141 |
+
$allowDirs[] = realpath(str_replace('//', $_SERVER['DOCUMENT_ROOT'] . '/', $allowDir));
|
142 |
+
}
|
143 |
+
$basenames = array(); // just for cache id
|
144 |
+
foreach ($files as $file) {
|
145 |
+
$uri = $base . $file;
|
146 |
+
$path = $_SERVER['DOCUMENT_ROOT'] . $uri;
|
147 |
+
$realpath = realpath($path);
|
148 |
+
if (false === $realpath || ! is_file($realpath)) {
|
149 |
+
$this->log("The path \"{$path}\" (realpath \"{$realpath}\") could not be found (or was not a file)");
|
150 |
+
if (null === $firstMissingResource) {
|
151 |
+
$firstMissingResource = $uri;
|
152 |
+
continue;
|
153 |
+
} else {
|
154 |
+
$secondMissingResource = $uri;
|
155 |
+
$this->log("More than one file was missing: '$firstMissingResource', '$secondMissingResource`'");
|
156 |
+
return $options;
|
157 |
+
}
|
158 |
+
}
|
159 |
+
try {
|
160 |
+
parent::checkNotHidden($realpath);
|
161 |
+
parent::checkAllowDirs($realpath, $allowDirs, $uri);
|
162 |
+
} catch (Exception $e) {
|
163 |
+
$this->log($e->getMessage());
|
164 |
+
return $options;
|
165 |
+
}
|
166 |
+
$sources[] = $this->_getFileSource($realpath, $cOptions);
|
167 |
+
$basenames[] = basename($realpath, $ext);
|
168 |
+
}
|
169 |
+
if ($this->selectionId) {
|
170 |
+
$this->selectionId .= '_f=';
|
171 |
+
}
|
172 |
+
$this->selectionId .= implode(',', $basenames) . $ext;
|
173 |
+
}
|
174 |
+
if ($sources) {
|
175 |
+
if (null !== $firstMissingResource) {
|
176 |
+
array_unshift($sources, new Minify_Source(array(
|
177 |
+
'id' => 'missingFile'
|
178 |
+
// should not cause cache invalidation
|
179 |
+
,'lastModified' => 0
|
180 |
+
// due to caching, filename is unreliable.
|
181 |
+
,'content' => "/* Minify: at least one missing file. See " . Minify::URL_DEBUG . " */\n"
|
182 |
+
,'minifier' => ''
|
183 |
+
)));
|
184 |
+
}
|
185 |
+
$this->sources = $sources;
|
186 |
+
} else {
|
187 |
+
$this->log("No sources to serve");
|
188 |
+
}
|
189 |
+
return $options;
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* @param string $file
|
194 |
+
*
|
195 |
+
* @param array $cOptions
|
196 |
+
*
|
197 |
+
* @return Minify_Source
|
198 |
+
*/
|
199 |
+
protected function _getFileSource($file, $cOptions)
|
200 |
+
{
|
201 |
+
$spec['filepath'] = $file;
|
202 |
+
if ($cOptions['noMinPattern'] && preg_match($cOptions['noMinPattern'], basename($file))) {
|
203 |
+
if (preg_match('~\.css$~i', $file)) {
|
204 |
+
$spec['minifyOptions']['compress'] = false;
|
205 |
+
} else {
|
206 |
+
$spec['minifier'] = '';
|
207 |
+
}
|
208 |
+
}
|
209 |
+
return new Minify_Source($spec);
|
210 |
+
}
|
211 |
+
|
212 |
+
protected $_type = null;
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Make sure that only source files of a single type are registered
|
216 |
+
*
|
217 |
+
* @param string $sourceOrExt
|
218 |
+
*
|
219 |
+
* @throws Exception
|
220 |
+
*/
|
221 |
+
public function checkType($sourceOrExt)
|
222 |
+
{
|
223 |
+
if ($sourceOrExt === 'js') {
|
224 |
+
$type = Minify::TYPE_JS;
|
225 |
+
} elseif ($sourceOrExt === 'css') {
|
226 |
+
$type = Minify::TYPE_CSS;
|
227 |
+
} elseif ($sourceOrExt->contentType !== null) {
|
228 |
+
$type = $sourceOrExt->contentType;
|
229 |
+
} else {
|
230 |
+
return;
|
231 |
+
}
|
232 |
+
if ($this->_type === null) {
|
233 |
+
$this->_type = $type;
|
234 |
+
} elseif ($this->_type !== $type) {
|
235 |
+
throw new Exception('Content-Type mismatch');
|
236 |
+
}
|
237 |
+
}
|
238 |
+
}
|
min/lib/Minify/Controller/Page.php
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Controller_Page
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Controller class for serving a single HTML page
|
9 |
+
*
|
10 |
+
* @link http://code.google.com/p/minify/source/browse/trunk/web/examples/1/index.php#59
|
11 |
+
* @package Minify
|
12 |
+
* @author Stephen Clay <steve@mrclay.org>
|
13 |
+
*/
|
14 |
+
class Minify_Controller_Page extends Minify_Controller_Base {
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Set up source of HTML content
|
18 |
+
*
|
19 |
+
* @param array $options controller and Minify options
|
20 |
+
* @return array Minify options
|
21 |
+
*
|
22 |
+
* Controller options:
|
23 |
+
*
|
24 |
+
* 'content': (required) HTML markup
|
25 |
+
*
|
26 |
+
* 'id': (required) id of page (string for use in server-side caching)
|
27 |
+
*
|
28 |
+
* 'lastModifiedTime': timestamp of when this content changed. This
|
29 |
+
* is recommended to allow both server and client-side caching.
|
30 |
+
*
|
31 |
+
* 'minifyAll': should all CSS and Javascript blocks be individually
|
32 |
+
* minified? (default false)
|
33 |
+
*
|
34 |
+
* @todo Add 'file' option to read HTML file.
|
35 |
+
*/
|
36 |
+
public function setupSources($options) {
|
37 |
+
if (isset($options['file'])) {
|
38 |
+
$sourceSpec = array(
|
39 |
+
'filepath' => $options['file']
|
40 |
+
);
|
41 |
+
$f = $options['file'];
|
42 |
+
} else {
|
43 |
+
// strip controller options
|
44 |
+
$sourceSpec = array(
|
45 |
+
'content' => $options['content']
|
46 |
+
,'id' => $options['id']
|
47 |
+
);
|
48 |
+
$f = $options['id'];
|
49 |
+
unset($options['content'], $options['id']);
|
50 |
+
}
|
51 |
+
// something like "builder,index.php" or "directory,file.html"
|
52 |
+
$this->selectionId = strtr(substr($f, 1 + strlen(dirname(dirname($f)))), '/\\', ',,');
|
53 |
+
|
54 |
+
if (isset($options['minifyAll'])) {
|
55 |
+
// this will be the 2nd argument passed to Minify_HTML::minify()
|
56 |
+
$sourceSpec['minifyOptions'] = array(
|
57 |
+
'cssMinifier' => array('Minify_CSS', 'minify')
|
58 |
+
,'jsMinifier' => array('JSMin', 'minify')
|
59 |
+
);
|
60 |
+
unset($options['minifyAll']);
|
61 |
+
}
|
62 |
+
$this->sources[] = new Minify_Source($sourceSpec);
|
63 |
+
|
64 |
+
$options['contentType'] = Minify::TYPE_HTML;
|
65 |
+
return $options;
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
min/lib/Minify/Controller/Version1.php
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Controller_Version1
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Controller class for emulating version 1 of minify.php (mostly a proof-of-concept)
|
9 |
+
*
|
10 |
+
* <code>
|
11 |
+
* Minify::serve('Version1');
|
12 |
+
* </code>
|
13 |
+
*
|
14 |
+
* @package Minify
|
15 |
+
* @author Stephen Clay <steve@mrclay.org>
|
16 |
+
*/
|
17 |
+
class Minify_Controller_Version1 extends Minify_Controller_Base {
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Set up groups of files as sources
|
21 |
+
*
|
22 |
+
* @param array $options controller and Minify options
|
23 |
+
* @return array Minify options
|
24 |
+
*
|
25 |
+
*/
|
26 |
+
public function setupSources($options) {
|
27 |
+
// PHP insecure by default: realpath() and other FS functions can't handle null bytes.
|
28 |
+
if (isset($_GET['files'])) {
|
29 |
+
$_GET['files'] = str_replace("\x00", '', (string)$_GET['files']);
|
30 |
+
}
|
31 |
+
|
32 |
+
self::_setupDefines();
|
33 |
+
if (MINIFY_USE_CACHE) {
|
34 |
+
$cacheDir = defined('MINIFY_CACHE_DIR')
|
35 |
+
? MINIFY_CACHE_DIR
|
36 |
+
: '';
|
37 |
+
Minify::setCache($cacheDir);
|
38 |
+
}
|
39 |
+
$options['badRequestHeader'] = 'HTTP/1.0 404 Not Found';
|
40 |
+
$options['contentTypeCharset'] = MINIFY_ENCODING;
|
41 |
+
|
42 |
+
// The following restrictions are to limit the URLs that minify will
|
43 |
+
// respond to. Ideally there should be only one way to reference a file.
|
44 |
+
if (! isset($_GET['files'])
|
45 |
+
// verify at least one file, files are single comma separated,
|
46 |
+
// and are all same extension
|
47 |
+
|| ! preg_match('/^[^,]+\\.(css|js)(,[^,]+\\.\\1)*$/', $_GET['files'], $m)
|
48 |
+
// no "//" (makes URL rewriting easier)
|
49 |
+
|| strpos($_GET['files'], '//') !== false
|
50 |
+
// no "\"
|
51 |
+
|| strpos($_GET['files'], '\\') !== false
|
52 |
+
// no "./"
|
53 |
+
|| preg_match('/(?:^|[^\\.])\\.\\//', $_GET['files'])
|
54 |
+
) {
|
55 |
+
return $options;
|
56 |
+
}
|
57 |
+
|
58 |
+
$files = explode(',', $_GET['files']);
|
59 |
+
if (count($files) > MINIFY_MAX_FILES) {
|
60 |
+
return $options;
|
61 |
+
}
|
62 |
+
|
63 |
+
// strings for prepending to relative/absolute paths
|
64 |
+
$prependRelPaths = dirname($_SERVER['SCRIPT_FILENAME'])
|
65 |
+
. DIRECTORY_SEPARATOR;
|
66 |
+
$prependAbsPaths = $_SERVER['DOCUMENT_ROOT'];
|
67 |
+
|
68 |
+
$goodFiles = array();
|
69 |
+
$hasBadSource = false;
|
70 |
+
|
71 |
+
$allowDirs = isset($options['allowDirs'])
|
72 |
+
? $options['allowDirs']
|
73 |
+
: MINIFY_BASE_DIR;
|
74 |
+
|
75 |
+
foreach ($files as $file) {
|
76 |
+
// prepend appropriate string for abs/rel paths
|
77 |
+
$file = ($file[0] === '/' ? $prependAbsPaths : $prependRelPaths) . $file;
|
78 |
+
// make sure a real file!
|
79 |
+
$file = realpath($file);
|
80 |
+
// don't allow unsafe or duplicate files
|
81 |
+
if (parent::_fileIsSafe($file, $allowDirs)
|
82 |
+
&& !in_array($file, $goodFiles))
|
83 |
+
{
|
84 |
+
$goodFiles[] = $file;
|
85 |
+
$srcOptions = array(
|
86 |
+
'filepath' => $file
|
87 |
+
);
|
88 |
+
$this->sources[] = new Minify_Source($srcOptions);
|
89 |
+
} else {
|
90 |
+
$hasBadSource = true;
|
91 |
+
break;
|
92 |
+
}
|
93 |
+
}
|
94 |
+
if ($hasBadSource) {
|
95 |
+
$this->sources = array();
|
96 |
+
}
|
97 |
+
if (! MINIFY_REWRITE_CSS_URLS) {
|
98 |
+
$options['rewriteCssUris'] = false;
|
99 |
+
}
|
100 |
+
return $options;
|
101 |
+
}
|
102 |
+
|
103 |
+
private static function _setupDefines()
|
104 |
+
{
|
105 |
+
$defaults = array(
|
106 |
+
'MINIFY_BASE_DIR' => realpath($_SERVER['DOCUMENT_ROOT'])
|
107 |
+
,'MINIFY_ENCODING' => 'utf-8'
|
108 |
+
,'MINIFY_MAX_FILES' => 16
|
109 |
+
,'MINIFY_REWRITE_CSS_URLS' => true
|
110 |
+
,'MINIFY_USE_CACHE' => true
|
111 |
+
);
|
112 |
+
foreach ($defaults as $const => $val) {
|
113 |
+
if (! defined($const)) {
|
114 |
+
define($const, $val);
|
115 |
+
}
|
116 |
+
}
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
min/lib/Minify/DebugDetector.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Detect whether request should be debugged
|
5 |
+
*
|
6 |
+
* @package Minify
|
7 |
+
* @author Stephen Clay <steve@mrclay.org>
|
8 |
+
*/
|
9 |
+
class Minify_DebugDetector {
|
10 |
+
public static function shouldDebugRequest($cookie, $get, $requestUri)
|
11 |
+
{
|
12 |
+
if (isset($get['debug'])) {
|
13 |
+
return true;
|
14 |
+
}
|
15 |
+
if (! empty($cookie['minifyDebug'])) {
|
16 |
+
foreach (preg_split('/\\s+/', $cookie['minifyDebug']) as $debugUri) {
|
17 |
+
$pattern = '@' . preg_quote($debugUri, '@') . '@i';
|
18 |
+
$pattern = str_replace(array('\\*', '\\?'), array('.*', '.'), $pattern);
|
19 |
+
if (preg_match($pattern, $requestUri)) {
|
20 |
+
return true;
|
21 |
+
}
|
22 |
+
}
|
23 |
+
}
|
24 |
+
return false;
|
25 |
+
}
|
26 |
+
}
|
min/lib/Minify/HTML.php
ADDED
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_HTML
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Compress HTML
|
9 |
+
*
|
10 |
+
* This is a heavy regex-based removal of whitespace, unnecessary comments and
|
11 |
+
* tokens. IE conditional comments are preserved. There are also options to have
|
12 |
+
* STYLE and SCRIPT blocks compressed by callback functions.
|
13 |
+
*
|
14 |
+
* A test suite is available.
|
15 |
+
*
|
16 |
+
* @package Minify
|
17 |
+
* @author Stephen Clay <steve@mrclay.org>
|
18 |
+
*/
|
19 |
+
class Minify_HTML {
|
20 |
+
/**
|
21 |
+
* @var boolean
|
22 |
+
*/
|
23 |
+
protected $_jsCleanComments = true;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* "Minify" an HTML page
|
27 |
+
*
|
28 |
+
* @param string $html
|
29 |
+
*
|
30 |
+
* @param array $options
|
31 |
+
*
|
32 |
+
* 'cssMinifier' : (optional) callback function to process content of STYLE
|
33 |
+
* elements.
|
34 |
+
*
|
35 |
+
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
|
36 |
+
* elements. Note: the type attribute is ignored.
|
37 |
+
*
|
38 |
+
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
39 |
+
* unset, minify will sniff for an XHTML doctype.
|
40 |
+
*
|
41 |
+
* @return string
|
42 |
+
*/
|
43 |
+
public static function minify($html, $options = array()) {
|
44 |
+
$min = new self($html, $options);
|
45 |
+
return $min->process();
|
46 |
+
}
|
47 |
+
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Create a minifier object
|
51 |
+
*
|
52 |
+
* @param string $html
|
53 |
+
*
|
54 |
+
* @param array $options
|
55 |
+
*
|
56 |
+
* 'cssMinifier' : (optional) callback function to process content of STYLE
|
57 |
+
* elements.
|
58 |
+
*
|
59 |
+
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
|
60 |
+
* elements. Note: the type attribute is ignored.
|
61 |
+
*
|
62 |
+
* 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
|
63 |
+
*
|
64 |
+
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
65 |
+
* unset, minify will sniff for an XHTML doctype.
|
66 |
+
*
|
67 |
+
* @return null
|
68 |
+
*/
|
69 |
+
public function __construct($html, $options = array())
|
70 |
+
{
|
71 |
+
$this->_html = str_replace("\r\n", "\n", trim($html));
|
72 |
+
if (isset($options['xhtml'])) {
|
73 |
+
$this->_isXhtml = (bool)$options['xhtml'];
|
74 |
+
}
|
75 |
+
if (isset($options['cssMinifier'])) {
|
76 |
+
$this->_cssMinifier = $options['cssMinifier'];
|
77 |
+
}
|
78 |
+
if (isset($options['jsMinifier'])) {
|
79 |
+
$this->_jsMinifier = $options['jsMinifier'];
|
80 |
+
}
|
81 |
+
if (isset($options['jsCleanComments'])) {
|
82 |
+
$this->_jsCleanComments = (bool)$options['jsCleanComments'];
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Minify the markeup given in the constructor
|
89 |
+
*
|
90 |
+
* @return string
|
91 |
+
*/
|
92 |
+
public function process()
|
93 |
+
{
|
94 |
+
if ($this->_isXhtml === null) {
|
95 |
+
$this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
|
96 |
+
}
|
97 |
+
|
98 |
+
$this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
|
99 |
+
$this->_placeholders = array();
|
100 |
+
|
101 |
+
// replace SCRIPTs (and minify) with placeholders
|
102 |
+
$this->_html = preg_replace_callback(
|
103 |
+
'/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
|
104 |
+
,array($this, '_removeScriptCB')
|
105 |
+
,$this->_html);
|
106 |
+
|
107 |
+
// replace STYLEs (and minify) with placeholders
|
108 |
+
$this->_html = preg_replace_callback(
|
109 |
+
'/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
|
110 |
+
,array($this, '_removeStyleCB')
|
111 |
+
,$this->_html);
|
112 |
+
|
113 |
+
// remove HTML comments (not containing IE conditional comments).
|
114 |
+
$this->_html = preg_replace_callback(
|
115 |
+
'/<!--([\\s\\S]*?)-->/'
|
116 |
+
,array($this, '_commentCB')
|
117 |
+
,$this->_html);
|
118 |
+
|
119 |
+
// replace PREs with placeholders
|
120 |
+
$this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
|
121 |
+
,array($this, '_removePreCB')
|
122 |
+
,$this->_html);
|
123 |
+
|
124 |
+
// replace TEXTAREAs with placeholders
|
125 |
+
$this->_html = preg_replace_callback(
|
126 |
+
'/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
|
127 |
+
,array($this, '_removeTextareaCB')
|
128 |
+
,$this->_html);
|
129 |
+
|
130 |
+
// trim each line.
|
131 |
+
// @todo take into account attribute values that span multiple lines.
|
132 |
+
$this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
|
133 |
+
|
134 |
+
// remove ws around block/undisplayed elements
|
135 |
+
$this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
|
136 |
+
.'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
|
137 |
+
.'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
|
138 |
+
.'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
|
139 |
+
.'|ul)\\b[^>]*>)/i', '$1', $this->_html);
|
140 |
+
|
141 |
+
// remove ws outside of all elements
|
142 |
+
$this->_html = preg_replace(
|
143 |
+
'/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</'
|
144 |
+
,'>$1$2$3<'
|
145 |
+
,$this->_html);
|
146 |
+
|
147 |
+
// use newlines before 1st attribute in open tags (to limit line lengths)
|
148 |
+
$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
|
149 |
+
|
150 |
+
// fill placeholders
|
151 |
+
$this->_html = str_replace(
|
152 |
+
array_keys($this->_placeholders)
|
153 |
+
,array_values($this->_placeholders)
|
154 |
+
,$this->_html
|
155 |
+
);
|
156 |
+
// issue 229: multi-pass to catch scripts that didn't get replaced in textareas
|
157 |
+
$this->_html = str_replace(
|
158 |
+
array_keys($this->_placeholders)
|
159 |
+
,array_values($this->_placeholders)
|
160 |
+
,$this->_html
|
161 |
+
);
|
162 |
+
return $this->_html;
|
163 |
+
}
|
164 |
+
|
165 |
+
protected function _commentCB($m)
|
166 |
+
{
|
167 |
+
return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
|
168 |
+
? $m[0]
|
169 |
+
: '';
|
170 |
+
}
|
171 |
+
|
172 |
+
protected function _reservePlace($content)
|
173 |
+
{
|
174 |
+
$placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
|
175 |
+
$this->_placeholders[$placeholder] = $content;
|
176 |
+
return $placeholder;
|
177 |
+
}
|
178 |
+
|
179 |
+
protected $_isXhtml = null;
|
180 |
+
protected $_replacementHash = null;
|
181 |
+
protected $_placeholders = array();
|
182 |
+
protected $_cssMinifier = null;
|
183 |
+
protected $_jsMinifier = null;
|
184 |
+
|
185 |
+
protected function _removePreCB($m)
|
186 |
+
{
|
187 |
+
return $this->_reservePlace("<pre{$m[1]}");
|
188 |
+
}
|
189 |
+
|
190 |
+
protected function _removeTextareaCB($m)
|
191 |
+
{
|
192 |
+
return $this->_reservePlace("<textarea{$m[1]}");
|
193 |
+
}
|
194 |
+
|
195 |
+
protected function _removeStyleCB($m)
|
196 |
+
{
|
197 |
+
$openStyle = "<style{$m[1]}";
|
198 |
+
$css = $m[2];
|
199 |
+
// remove HTML comments
|
200 |
+
$css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
|
201 |
+
|
202 |
+
// remove CDATA section markers
|
203 |
+
$css = $this->_removeCdata($css);
|
204 |
+
|
205 |
+
// minify
|
206 |
+
$minifier = $this->_cssMinifier
|
207 |
+
? $this->_cssMinifier
|
208 |
+
: 'trim';
|
209 |
+
$css = call_user_func($minifier, $css);
|
210 |
+
|
211 |
+
return $this->_reservePlace($this->_needsCdata($css)
|
212 |
+
? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
|
213 |
+
: "{$openStyle}{$css}</style>"
|
214 |
+
);
|
215 |
+
}
|
216 |
+
|
217 |
+
protected function _removeScriptCB($m)
|
218 |
+
{
|
219 |
+
$openScript = "<script{$m[2]}";
|
220 |
+
$js = $m[3];
|
221 |
+
|
222 |
+
// whitespace surrounding? preserve at least one space
|
223 |
+
$ws1 = ($m[1] === '') ? '' : ' ';
|
224 |
+
$ws2 = ($m[4] === '') ? '' : ' ';
|
225 |
+
|
226 |
+
// remove HTML comments (and ending "//" if present)
|
227 |
+
if ($this->_jsCleanComments) {
|
228 |
+
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
|
229 |
+
}
|
230 |
+
|
231 |
+
// remove CDATA section markers
|
232 |
+
$js = $this->_removeCdata($js);
|
233 |
+
|
234 |
+
// minify
|
235 |
+
$minifier = $this->_jsMinifier
|
236 |
+
? $this->_jsMinifier
|
237 |
+
: 'trim';
|
238 |
+
$js = call_user_func($minifier, $js);
|
239 |
+
|
240 |
+
return $this->_reservePlace($this->_needsCdata($js)
|
241 |
+
? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
|
242 |
+
: "{$ws1}{$openScript}{$js}</script>{$ws2}"
|
243 |
+
);
|
244 |
+
}
|
245 |
+
|
246 |
+
protected function _removeCdata($str)
|
247 |
+
{
|
248 |
+
return (false !== strpos($str, '<![CDATA['))
|
249 |
+
? str_replace(array('<![CDATA[', ']]>'), '', $str)
|
250 |
+
: $str;
|
251 |
+
}
|
252 |
+
|
253 |
+
protected function _needsCdata($str)
|
254 |
+
{
|
255 |
+
return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
|
256 |
+
}
|
257 |
+
}
|
min/lib/Minify/HTML/Helper.php
ADDED
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_HTML_Helper
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Helpers for writing Minfy URIs into HTML
|
9 |
+
*
|
10 |
+
* @package Minify
|
11 |
+
* @author Stephen Clay <steve@mrclay.org>
|
12 |
+
*/
|
13 |
+
class Minify_HTML_Helper {
|
14 |
+
public $rewriteWorks = true;
|
15 |
+
public $minAppUri = '/min';
|
16 |
+
public $groupsConfigFile = '';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Get an HTML-escaped Minify URI for a group or set of files
|
20 |
+
*
|
21 |
+
* @param string|array $keyOrFiles a group key or array of filepaths/URIs
|
22 |
+
* @param array $opts options:
|
23 |
+
* 'farExpires' : (default true) append a modified timestamp for cache revving
|
24 |
+
* 'debug' : (default false) append debug flag
|
25 |
+
* 'charset' : (default 'UTF-8') for htmlspecialchars
|
26 |
+
* 'minAppUri' : (default '/min') URI of min directory
|
27 |
+
* 'rewriteWorks' : (default true) does mod_rewrite work in min app?
|
28 |
+
* 'groupsConfigFile' : specify if different
|
29 |
+
* @return string
|
30 |
+
*/
|
31 |
+
public static function getUri($keyOrFiles, $opts = array())
|
32 |
+
{
|
33 |
+
$opts = array_merge(array( // default options
|
34 |
+
'farExpires' => true
|
35 |
+
,'debug' => false
|
36 |
+
,'charset' => 'UTF-8'
|
37 |
+
,'minAppUri' => '/min'
|
38 |
+
,'rewriteWorks' => true
|
39 |
+
,'groupsConfigFile' => ''
|
40 |
+
), $opts);
|
41 |
+
$h = new self;
|
42 |
+
$h->minAppUri = $opts['minAppUri'];
|
43 |
+
$h->rewriteWorks = $opts['rewriteWorks'];
|
44 |
+
$h->groupsConfigFile = $opts['groupsConfigFile'];
|
45 |
+
if (is_array($keyOrFiles)) {
|
46 |
+
$h->setFiles($keyOrFiles, $opts['farExpires']);
|
47 |
+
} else {
|
48 |
+
$h->setGroup($keyOrFiles, $opts['farExpires']);
|
49 |
+
}
|
50 |
+
$uri = $h->getRawUri($opts['farExpires'], $opts['debug']);
|
51 |
+
return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']);
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Get non-HTML-escaped URI to minify the specified files
|
56 |
+
*
|
57 |
+
* @param bool $farExpires
|
58 |
+
* @param bool $debug
|
59 |
+
* @return string
|
60 |
+
*/
|
61 |
+
public function getRawUri($farExpires = true, $debug = false)
|
62 |
+
{
|
63 |
+
$path = rtrim($this->minAppUri, '/') . '/';
|
64 |
+
if (! $this->rewriteWorks) {
|
65 |
+
$path .= '?';
|
66 |
+
}
|
67 |
+
if (null === $this->_groupKey) {
|
68 |
+
// @todo: implement shortest uri
|
69 |
+
$path = self::_getShortestUri($this->_filePaths, $path);
|
70 |
+
} else {
|
71 |
+
$path .= "g=" . $this->_groupKey;
|
72 |
+
}
|
73 |
+
if ($debug) {
|
74 |
+
$path .= "&debug";
|
75 |
+
} elseif ($farExpires && $this->_lastModified) {
|
76 |
+
$path .= "&" . $this->_lastModified;
|
77 |
+
}
|
78 |
+
return $path;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Set the files that will comprise the URI we're building
|
83 |
+
*
|
84 |
+
* @param array $files
|
85 |
+
* @param bool $checkLastModified
|
86 |
+
*/
|
87 |
+
public function setFiles($files, $checkLastModified = true)
|
88 |
+
{
|
89 |
+
$this->_groupKey = null;
|
90 |
+
if ($checkLastModified) {
|
91 |
+
$this->_lastModified = self::getLastModified($files);
|
92 |
+
}
|
93 |
+
// normalize paths like in /min/f=<paths>
|
94 |
+
foreach ($files as $k => $file) {
|
95 |
+
if (0 === strpos($file, '//')) {
|
96 |
+
$file = substr($file, 2);
|
97 |
+
} elseif (0 === strpos($file, '/')
|
98 |
+
|| 1 === strpos($file, ':\\')) {
|
99 |
+
$file = substr($file, strlen($_SERVER['DOCUMENT_ROOT']) + 1);
|
100 |
+
}
|
101 |
+
$file = strtr($file, '\\', '/');
|
102 |
+
$files[$k] = $file;
|
103 |
+
}
|
104 |
+
$this->_filePaths = $files;
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Set the group of files that will comprise the URI we're building
|
109 |
+
*
|
110 |
+
* @param string $key
|
111 |
+
* @param bool $checkLastModified
|
112 |
+
*/
|
113 |
+
public function setGroup($key, $checkLastModified = true)
|
114 |
+
{
|
115 |
+
$this->_groupKey = $key;
|
116 |
+
if ($checkLastModified) {
|
117 |
+
if (! $this->groupsConfigFile) {
|
118 |
+
$this->groupsConfigFile = dirname(dirname(dirname(dirname(__FILE__)))) . '/groupsConfig.php';
|
119 |
+
}
|
120 |
+
if (is_file($this->groupsConfigFile)) {
|
121 |
+
$gc = (require $this->groupsConfigFile);
|
122 |
+
$keys = explode(',', $key);
|
123 |
+
foreach ($keys as $key) {
|
124 |
+
if (isset($gc[$key])) {
|
125 |
+
$this->_lastModified = self::getLastModified($gc[$key], $this->_lastModified);
|
126 |
+
}
|
127 |
+
}
|
128 |
+
}
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Get the max(lastModified) of all files
|
134 |
+
*
|
135 |
+
* @param array|string $sources
|
136 |
+
* @param int $lastModified
|
137 |
+
* @return int
|
138 |
+
*/
|
139 |
+
public static function getLastModified($sources, $lastModified = 0)
|
140 |
+
{
|
141 |
+
$max = $lastModified;
|
142 |
+
foreach ((array)$sources as $source) {
|
143 |
+
if (is_object($source) && isset($source->lastModified)) {
|
144 |
+
$max = max($max, $source->lastModified);
|
145 |
+
} elseif (is_string($source)) {
|
146 |
+
if (0 === strpos($source, '//')) {
|
147 |
+
$source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1);
|
148 |
+
}
|
149 |
+
if (is_file($source)) {
|
150 |
+
$max = max($max, filemtime($source));
|
151 |
+
}
|
152 |
+
}
|
153 |
+
}
|
154 |
+
return $max;
|
155 |
+
}
|
156 |
+
|
157 |
+
protected $_groupKey = null; // if present, URI will be like g=...
|
158 |
+
protected $_filePaths = array();
|
159 |
+
protected $_lastModified = null;
|
160 |
+
|
161 |
+
|
162 |
+
/**
|
163 |
+
* In a given array of strings, find the character they all have at
|
164 |
+
* a particular index
|
165 |
+
*
|
166 |
+
* @param array $arr array of strings
|
167 |
+
* @param int $pos index to check
|
168 |
+
* @return mixed a common char or '' if any do not match
|
169 |
+
*/
|
170 |
+
protected static function _getCommonCharAtPos($arr, $pos) {
|
171 |
+
if (!isset($arr[0][$pos])) {
|
172 |
+
return '';
|
173 |
+
}
|
174 |
+
$c = $arr[0][$pos];
|
175 |
+
$l = count($arr);
|
176 |
+
if ($l === 1) {
|
177 |
+
return $c;
|
178 |
+
}
|
179 |
+
for ($i = 1; $i < $l; ++$i) {
|
180 |
+
if ($arr[$i][$pos] !== $c) {
|
181 |
+
return '';
|
182 |
+
}
|
183 |
+
}
|
184 |
+
return $c;
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Get the shortest URI to minify the set of source files
|
189 |
+
*
|
190 |
+
* @param array $paths root-relative URIs of files
|
191 |
+
* @param string $minRoot root-relative URI of the "min" application
|
192 |
+
* @return string
|
193 |
+
*/
|
194 |
+
protected static function _getShortestUri($paths, $minRoot = '/min/') {
|
195 |
+
$pos = 0;
|
196 |
+
$base = '';
|
197 |
+
while (true) {
|
198 |
+
$c = self::_getCommonCharAtPos($paths, $pos);
|
199 |
+
if ($c === '') {
|
200 |
+
break;
|
201 |
+
} else {
|
202 |
+
$base .= $c;
|
203 |
+
}
|
204 |
+
++$pos;
|
205 |
+
}
|
206 |
+
$base = preg_replace('@[^/]+$@', '', $base);
|
207 |
+
$uri = $minRoot . 'f=' . implode(',', $paths);
|
208 |
+
|
209 |
+
if (substr($base, -1) === '/') {
|
210 |
+
// we have a base dir!
|
211 |
+
$basedPaths = $paths;
|
212 |
+
$l = count($paths);
|
213 |
+
for ($i = 0; $i < $l; ++$i) {
|
214 |
+
$basedPaths[$i] = substr($paths[$i], strlen($base));
|
215 |
+
}
|
216 |
+
$base = substr($base, 0, strlen($base) - 1);
|
217 |
+
$bUri = $minRoot . 'b=' . $base . '&f=' . implode(',', $basedPaths);
|
218 |
+
|
219 |
+
$uri = strlen($uri) < strlen($bUri)
|
220 |
+
? $uri
|
221 |
+
: $bUri;
|
222 |
+
}
|
223 |
+
return $uri;
|
224 |
+
}
|
225 |
+
}
|
min/lib/Minify/ImportProcessor.php
ADDED
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_ImportProcessor
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Linearize a CSS/JS file by including content specified by CSS import
|
9 |
+
* declarations. In CSS files, relative URIs are fixed.
|
10 |
+
*
|
11 |
+
* @imports will be processed regardless of where they appear in the source
|
12 |
+
* files; i.e. @imports commented out or in string content will still be
|
13 |
+
* processed!
|
14 |
+
*
|
15 |
+
* This has a unit test but should be considered "experimental".
|
16 |
+
*
|
17 |
+
* @package Minify
|
18 |
+
* @author Stephen Clay <steve@mrclay.org>
|
19 |
+
* @author Simon Schick <simonsimcity@gmail.com>
|
20 |
+
*/
|
21 |
+
class Minify_ImportProcessor {
|
22 |
+
|
23 |
+
public static $filesIncluded = array();
|
24 |
+
|
25 |
+
public static function process($file)
|
26 |
+
{
|
27 |
+
self::$filesIncluded = array();
|
28 |
+
self::$_isCss = (strtolower(substr($file, -4)) === '.css');
|
29 |
+
$obj = new Minify_ImportProcessor(dirname($file));
|
30 |
+
return $obj->_getContent($file);
|
31 |
+
}
|
32 |
+
|
33 |
+
// allows callback funcs to know the current directory
|
34 |
+
private $_currentDir = null;
|
35 |
+
|
36 |
+
// allows callback funcs to know the directory of the file that inherits this one
|
37 |
+
private $_previewsDir = null;
|
38 |
+
|
39 |
+
// allows _importCB to write the fetched content back to the obj
|
40 |
+
private $_importedContent = '';
|
41 |
+
|
42 |
+
private static $_isCss = null;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @param String $currentDir
|
46 |
+
* @param String $previewsDir Is only used internally
|
47 |
+
*/
|
48 |
+
private function __construct($currentDir, $previewsDir = "")
|
49 |
+
{
|
50 |
+
$this->_currentDir = $currentDir;
|
51 |
+
$this->_previewsDir = $previewsDir;
|
52 |
+
}
|
53 |
+
|
54 |
+
private function _getContent($file, $is_imported = false)
|
55 |
+
{
|
56 |
+
$file = realpath($file);
|
57 |
+
if (! $file
|
58 |
+
|| in_array($file, self::$filesIncluded)
|
59 |
+
|| false === ($content = @file_get_contents($file))
|
60 |
+
) {
|
61 |
+
// file missing, already included, or failed read
|
62 |
+
return '';
|
63 |
+
}
|
64 |
+
self::$filesIncluded[] = realpath($file);
|
65 |
+
$this->_currentDir = dirname($file);
|
66 |
+
|
67 |
+
// remove UTF-8 BOM if present
|
68 |
+
if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) {
|
69 |
+
$content = substr($content, 3);
|
70 |
+
}
|
71 |
+
// ensure uniform EOLs
|
72 |
+
$content = str_replace("\r\n", "\n", $content);
|
73 |
+
|
74 |
+
// process @imports
|
75 |
+
$content = preg_replace_callback(
|
76 |
+
'/
|
77 |
+
@import\\s+
|
78 |
+
(?:url\\(\\s*)? # maybe url(
|
79 |
+
[\'"]? # maybe quote
|
80 |
+
(.*?) # 1 = URI
|
81 |
+
[\'"]? # maybe end quote
|
82 |
+
(?:\\s*\\))? # maybe )
|
83 |
+
([a-zA-Z,\\s]*)? # 2 = media list
|
84 |
+
; # end token
|
85 |
+
/x'
|
86 |
+
,array($this, '_importCB')
|
87 |
+
,$content
|
88 |
+
);
|
89 |
+
|
90 |
+
// You only need to rework the import-path if the script is imported
|
91 |
+
if (self::$_isCss && $is_imported) {
|
92 |
+
// rewrite remaining relative URIs
|
93 |
+
$content = preg_replace_callback(
|
94 |
+
'/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
95 |
+
,array($this, '_urlCB')
|
96 |
+
,$content
|
97 |
+
);
|
98 |
+
}
|
99 |
+
|
100 |
+
return $this->_importedContent . $content;
|
101 |
+
}
|
102 |
+
|
103 |
+
private function _importCB($m)
|
104 |
+
{
|
105 |
+
$url = $m[1];
|
106 |
+
$mediaList = preg_replace('/\\s+/', '', $m[2]);
|
107 |
+
|
108 |
+
if (strpos($url, '://') > 0) {
|
109 |
+
// protocol, leave in place for CSS, comment for JS
|
110 |
+
return self::$_isCss
|
111 |
+
? $m[0]
|
112 |
+
: "/* Minify_ImportProcessor will not include remote content */";
|
113 |
+
}
|
114 |
+
if ('/' === $url[0]) {
|
115 |
+
// protocol-relative or root path
|
116 |
+
$url = ltrim($url, '/');
|
117 |
+
$file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR
|
118 |
+
. strtr($url, '/', DIRECTORY_SEPARATOR);
|
119 |
+
} else {
|
120 |
+
// relative to current path
|
121 |
+
$file = $this->_currentDir . DIRECTORY_SEPARATOR
|
122 |
+
. strtr($url, '/', DIRECTORY_SEPARATOR);
|
123 |
+
}
|
124 |
+
$obj = new Minify_ImportProcessor(dirname($file), $this->_currentDir);
|
125 |
+
$content = $obj->_getContent($file, true);
|
126 |
+
if ('' === $content) {
|
127 |
+
// failed. leave in place for CSS, comment for JS
|
128 |
+
return self::$_isCss
|
129 |
+
? $m[0]
|
130 |
+
: "/* Minify_ImportProcessor could not fetch '{$file}' */";
|
131 |
+
}
|
132 |
+
return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList))
|
133 |
+
? $content
|
134 |
+
: "@media {$mediaList} {\n{$content}\n}\n";
|
135 |
+
}
|
136 |
+
|
137 |
+
private function _urlCB($m)
|
138 |
+
{
|
139 |
+
// $m[1] is either quoted or not
|
140 |
+
$quote = ($m[1][0] === "'" || $m[1][0] === '"')
|
141 |
+
? $m[1][0]
|
142 |
+
: '';
|
143 |
+
$url = ($quote === '')
|
144 |
+
? $m[1]
|
145 |
+
: substr($m[1], 1, strlen($m[1]) - 2);
|
146 |
+
if ('/' !== $url[0]) {
|
147 |
+
if (strpos($url, '//') > 0) {
|
148 |
+
// probably starts with protocol, do not alter
|
149 |
+
} else {
|
150 |
+
// prepend path with current dir separator (OS-independent)
|
151 |
+
$path = $this->_currentDir
|
152 |
+
. DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR);
|
153 |
+
// update the relative path by the directory of the file that imported this one
|
154 |
+
$url = self::getPathDiff(realpath($this->_previewsDir), $path);
|
155 |
+
}
|
156 |
+
}
|
157 |
+
return "url({$quote}{$url}{$quote})";
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* @param string $from
|
162 |
+
* @param string $to
|
163 |
+
* @param string $ps
|
164 |
+
* @return string
|
165 |
+
*/
|
166 |
+
private function getPathDiff($from, $to, $ps = DIRECTORY_SEPARATOR)
|
167 |
+
{
|
168 |
+
$realFrom = $this->truepath($from);
|
169 |
+
$realTo = $this->truepath($to);
|
170 |
+
|
171 |
+
$arFrom = explode($ps, rtrim($realFrom, $ps));
|
172 |
+
$arTo = explode($ps, rtrim($realTo, $ps));
|
173 |
+
while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0]))
|
174 |
+
{
|
175 |
+
array_shift($arFrom);
|
176 |
+
array_shift($arTo);
|
177 |
+
}
|
178 |
+
return str_pad("", count($arFrom) * 3, '..' . $ps) . implode($ps, $arTo);
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* This function is to replace PHP's extremely buggy realpath().
|
183 |
+
* @param string $path The original path, can be relative etc.
|
184 |
+
* @return string The resolved path, it might not exist.
|
185 |
+
* @see http://stackoverflow.com/questions/4049856/replace-phps-realpath
|
186 |
+
*/
|
187 |
+
function truepath($path)
|
188 |
+
{
|
189 |
+
// whether $path is unix or not
|
190 |
+
$unipath = strlen($path) == 0 || $path{0} != '/';
|
191 |
+
// attempts to detect if path is relative in which case, add cwd
|
192 |
+
if (strpos($path, ':') === false && $unipath)
|
193 |
+
$path = $this->_currentDir . DIRECTORY_SEPARATOR . $path;
|
194 |
+
|
195 |
+
// resolve path parts (single dot, double dot and double delimiters)
|
196 |
+
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
|
197 |
+
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
|
198 |
+
$absolutes = array();
|
199 |
+
foreach ($parts as $part) {
|
200 |
+
if ('.' == $part)
|
201 |
+
continue;
|
202 |
+
if ('..' == $part) {
|
203 |
+
array_pop($absolutes);
|
204 |
+
} else {
|
205 |
+
$absolutes[] = $part;
|
206 |
+
}
|
207 |
+
}
|
208 |
+
$path = implode(DIRECTORY_SEPARATOR, $absolutes);
|
209 |
+
// resolve any symlinks
|
210 |
+
if (file_exists($path) && linkinfo($path) > 0)
|
211 |
+
$path = readlink($path);
|
212 |
+
// put initial separator that could have been lost
|
213 |
+
$path = !$unipath ? '/' . $path : $path;
|
214 |
+
return $path;
|
215 |
+
}
|
216 |
+
}
|
min/lib/Minify/JS/ClosureCompiler.php
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_JS_ClosureCompiler
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Minify Javascript using Google's Closure Compiler API
|
9 |
+
*
|
10 |
+
* @link http://code.google.com/closure/compiler/
|
11 |
+
* @package Minify
|
12 |
+
* @author Stephen Clay <steve@mrclay.org>
|
13 |
+
*
|
14 |
+
* @todo can use a stream wrapper to unit test this?
|
15 |
+
*/
|
16 |
+
class Minify_JS_ClosureCompiler {
|
17 |
+
const URL = 'http://closure-compiler.appspot.com/compile';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Minify Javascript code via HTTP request to the Closure Compiler API
|
21 |
+
*
|
22 |
+
* @param string $js input code
|
23 |
+
* @param array $options unused at this point
|
24 |
+
* @return string
|
25 |
+
*/
|
26 |
+
public static function minify($js, array $options = array())
|
27 |
+
{
|
28 |
+
$obj = new self($options);
|
29 |
+
return $obj->min($js);
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
*
|
34 |
+
* @param array $options
|
35 |
+
*
|
36 |
+
* fallbackFunc : default array($this, 'fallback');
|
37 |
+
*/
|
38 |
+
public function __construct(array $options = array())
|
39 |
+
{
|
40 |
+
$this->_fallbackFunc = isset($options['fallbackMinifier'])
|
41 |
+
? $options['fallbackMinifier']
|
42 |
+
: array($this, '_fallback');
|
43 |
+
}
|
44 |
+
|
45 |
+
public function min($js)
|
46 |
+
{
|
47 |
+
$postBody = $this->_buildPostBody($js);
|
48 |
+
$bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
|
49 |
+
? mb_strlen($postBody, '8bit')
|
50 |
+
: strlen($postBody);
|
51 |
+
if ($bytes > 200000) {
|
52 |
+
throw new Minify_JS_ClosureCompiler_Exception(
|
53 |
+
'POST content larger than 200000 bytes'
|
54 |
+
);
|
55 |
+
}
|
56 |
+
$response = $this->_getResponse($postBody);
|
57 |
+
if (preg_match('/^Error\(\d\d?\):/', $response)) {
|
58 |
+
if (is_callable($this->_fallbackFunc)) {
|
59 |
+
$response = "/* Received errors from Closure Compiler API:\n$response"
|
60 |
+
. "\n(Using fallback minifier)\n*/\n";
|
61 |
+
$response .= call_user_func($this->_fallbackFunc, $js);
|
62 |
+
} else {
|
63 |
+
throw new Minify_JS_ClosureCompiler_Exception($response);
|
64 |
+
}
|
65 |
+
}
|
66 |
+
if ($response === '') {
|
67 |
+
$errors = $this->_getResponse($this->_buildPostBody($js, true));
|
68 |
+
throw new Minify_JS_ClosureCompiler_Exception($errors);
|
69 |
+
}
|
70 |
+
return $response;
|
71 |
+
}
|
72 |
+
|
73 |
+
protected $_fallbackFunc = null;
|
74 |
+
|
75 |
+
protected function _getResponse($postBody)
|
76 |
+
{
|
77 |
+
$allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
|
78 |
+
if ($allowUrlFopen) {
|
79 |
+
$contents = file_get_contents(self::URL, false, stream_context_create(array(
|
80 |
+
'http' => array(
|
81 |
+
'method' => 'POST',
|
82 |
+
'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
|
83 |
+
'content' => $postBody,
|
84 |
+
'max_redirects' => 0,
|
85 |
+
'timeout' => 15,
|
86 |
+
)
|
87 |
+
)));
|
88 |
+
} elseif (defined('CURLOPT_POST')) {
|
89 |
+
$ch = curl_init(self::URL);
|
90 |
+
curl_setopt($ch, CURLOPT_POST, true);
|
91 |
+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
92 |
+
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
|
93 |
+
curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody);
|
94 |
+
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
|
95 |
+
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
|
96 |
+
$contents = curl_exec($ch);
|
97 |
+
curl_close($ch);
|
98 |
+
} else {
|
99 |
+
throw new Minify_JS_ClosureCompiler_Exception(
|
100 |
+
"Could not make HTTP request: allow_url_open is false and cURL not available"
|
101 |
+
);
|
102 |
+
}
|
103 |
+
if (false === $contents) {
|
104 |
+
throw new Minify_JS_ClosureCompiler_Exception(
|
105 |
+
"No HTTP response from server"
|
106 |
+
);
|
107 |
+
}
|
108 |
+
return trim($contents);
|
109 |
+
}
|
110 |
+
|
111 |
+
protected function _buildPostBody($js, $returnErrors = false)
|
112 |
+
{
|
113 |
+
return http_build_query(array(
|
114 |
+
'js_code' => $js,
|
115 |
+
'output_info' => ($returnErrors ? 'errors' : 'compiled_code'),
|
116 |
+
'output_format' => 'text',
|
117 |
+
'compilation_level' => 'SIMPLE_OPTIMIZATIONS'
|
118 |
+
), null, '&');
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Default fallback function if CC API fails
|
123 |
+
* @param string $js
|
124 |
+
* @return string
|
125 |
+
*/
|
126 |
+
protected function _fallback($js)
|
127 |
+
{
|
128 |
+
return JSMin::minify($js);
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
class Minify_JS_ClosureCompiler_Exception extends Exception {}
|
min/lib/Minify/Lines.php
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Lines
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Add line numbers in C-style comments for easier debugging of combined content
|
9 |
+
*
|
10 |
+
* @package Minify
|
11 |
+
* @author Stephen Clay <steve@mrclay.org>
|
12 |
+
* @author Adam Pedersen (Issue 55 fix)
|
13 |
+
*/
|
14 |
+
class Minify_Lines {
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Add line numbers in C-style comments
|
18 |
+
*
|
19 |
+
* This uses a very basic parser easily fooled by comment tokens inside
|
20 |
+
* strings or regexes, but, otherwise, generally clean code will not be
|
21 |
+
* mangled. URI rewriting can also be performed.
|
22 |
+
*
|
23 |
+
* @param string $content
|
24 |
+
*
|
25 |
+
* @param array $options available options:
|
26 |
+
*
|
27 |
+
* 'id': (optional) string to identify file. E.g. file name/path
|
28 |
+
*
|
29 |
+
* 'currentDir': (default null) if given, this is assumed to be the
|
30 |
+
* directory of the current CSS file. Using this, minify will rewrite
|
31 |
+
* all relative URIs in import/url declarations to correctly point to
|
32 |
+
* the desired files, and prepend a comment with debugging information about
|
33 |
+
* this process.
|
34 |
+
*
|
35 |
+
* @return string
|
36 |
+
*/
|
37 |
+
public static function minify($content, $options = array())
|
38 |
+
{
|
39 |
+
$id = (isset($options['id']) && $options['id'])
|
40 |
+
? $options['id']
|
41 |
+
: '';
|
42 |
+
$content = str_replace("\r\n", "\n", $content);
|
43 |
+
|
44 |
+
// Hackily rewrite strings with XPath expressions that are
|
45 |
+
// likely to throw off our dumb parser (for Prototype 1.6.1).
|
46 |
+
$content = str_replace('"/*"', '"/"+"*"', $content);
|
47 |
+
$content = preg_replace('@([\'"])(\\.?//?)\\*@', '$1$2$1+$1*', $content);
|
48 |
+
|
49 |
+
$lines = explode("\n", $content);
|
50 |
+
$numLines = count($lines);
|
51 |
+
// determine left padding
|
52 |
+
$padTo = strlen((string) $numLines); // e.g. 103 lines = 3 digits
|
53 |
+
$inComment = false;
|
54 |
+
$i = 0;
|
55 |
+
$newLines = array();
|
56 |
+
while (null !== ($line = array_shift($lines))) {
|
57 |
+
if (('' !== $id) && (0 == $i % 50)) {
|
58 |
+
if ($inComment) {
|
59 |
+
array_push($newLines, '', "/* {$id} *|", '');
|
60 |
+
} else {
|
61 |
+
array_push($newLines, '', "/* {$id} */", '');
|
62 |
+
}
|
63 |
+
}
|
64 |
+
++$i;
|
65 |
+
$newLines[] = self::_addNote($line, $i, $inComment, $padTo);
|
66 |
+
$inComment = self::_eolInComment($line, $inComment);
|
67 |
+
}
|
68 |
+
$content = implode("\n", $newLines) . "\n";
|
69 |
+
|
70 |
+
// check for desired URI rewriting
|
71 |
+
if (isset($options['currentDir'])) {
|
72 |
+
Minify_CSS_UriRewriter::$debugText = '';
|
73 |
+
$content = Minify_CSS_UriRewriter::rewrite(
|
74 |
+
$content
|
75 |
+
,$options['currentDir']
|
76 |
+
,isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT']
|
77 |
+
,isset($options['symlinks']) ? $options['symlinks'] : array()
|
78 |
+
);
|
79 |
+
$content = "/* Minify_CSS_UriRewriter::\$debugText\n\n"
|
80 |
+
. Minify_CSS_UriRewriter::$debugText . "*/\n"
|
81 |
+
. $content;
|
82 |
+
}
|
83 |
+
|
84 |
+
return $content;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Is the parser within a C-style comment at the end of this line?
|
89 |
+
*
|
90 |
+
* @param string $line current line of code
|
91 |
+
*
|
92 |
+
* @param bool $inComment was the parser in a comment at the
|
93 |
+
* beginning of the line?
|
94 |
+
*
|
95 |
+
* @return bool
|
96 |
+
*/
|
97 |
+
private static function _eolInComment($line, $inComment)
|
98 |
+
{
|
99 |
+
// crude way to avoid things like // */
|
100 |
+
$line = preg_replace('~//.*?(\\*/|/\\*).*~', '', $line);
|
101 |
+
|
102 |
+
while (strlen($line)) {
|
103 |
+
$search = $inComment
|
104 |
+
? '*/'
|
105 |
+
: '/*';
|
106 |
+
$pos = strpos($line, $search);
|
107 |
+
if (false === $pos) {
|
108 |
+
return $inComment;
|
109 |
+
} else {
|
110 |
+
if ($pos == 0
|
111 |
+
|| ($inComment
|
112 |
+
? substr($line, $pos, 3)
|
113 |
+
: substr($line, $pos-1, 3)) != '*/*')
|
114 |
+
{
|
115 |
+
$inComment = ! $inComment;
|
116 |
+
}
|
117 |
+
$line = substr($line, $pos + 2);
|
118 |
+
}
|
119 |
+
}
|
120 |
+
return $inComment;
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Prepend a comment (or note) to the given line
|
125 |
+
*
|
126 |
+
* @param string $line current line of code
|
127 |
+
*
|
128 |
+
* @param string $note content of note/comment
|
129 |
+
*
|
130 |
+
* @param bool $inComment was the parser in a comment at the
|
131 |
+
* beginning of the line?
|
132 |
+
*
|
133 |
+
* @param int $padTo minimum width of comment
|
134 |
+
*
|
135 |
+
* @return string
|
136 |
+
*/
|
137 |
+
private static function _addNote($line, $note, $inComment, $padTo)
|
138 |
+
{
|
139 |
+
return $inComment
|
140 |
+
? '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line
|
141 |
+
: '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line;
|
142 |
+
}
|
143 |
+
}
|
min/lib/Minify/Loader.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Loader
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class autoloader
|
9 |
+
*
|
10 |
+
* @package Minify
|
11 |
+
* @author Stephen Clay <steve@mrclay.org>
|
12 |
+
*/
|
13 |
+
class Minify_Loader {
|
14 |
+
public function loadClass($class)
|
15 |
+
{
|
16 |
+
$file = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR;
|
17 |
+
$file .= strtr($class, "\\_", DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR) . '.php';
|
18 |
+
if (is_readable($file)) {
|
19 |
+
require $file;
|
20 |
+
}
|
21 |
+
}
|
22 |
+
|
23 |
+
static public function register()
|
24 |
+
{
|
25 |
+
$inst = new self();
|
26 |
+
spl_autoload_register(array($inst, 'loadClass'));
|
27 |
+
}
|
28 |
+
}
|
min/lib/Minify/Logger.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Logger
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Message logging class
|
9 |
+
*
|
10 |
+
* @package Minify
|
11 |
+
* @author Stephen Clay <steve@mrclay.org>
|
12 |
+
*
|
13 |
+
* @todo lose this singleton! pass log object in Minify::serve and distribute to others
|
14 |
+
*/
|
15 |
+
class Minify_Logger {
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Set logger object.
|
19 |
+
*
|
20 |
+
* The object should have a method "log" that accepts a value as 1st argument and
|
21 |
+
* an optional string label as the 2nd.
|
22 |
+
*
|
23 |
+
* @param mixed $obj or a "falsey" value to disable
|
24 |
+
* @return null
|
25 |
+
*/
|
26 |
+
public static function setLogger($obj = null) {
|
27 |
+
self::$_logger = $obj
|
28 |
+
? $obj
|
29 |
+
: null;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Pass a message to the logger (if set)
|
34 |
+
*
|
35 |
+
* @param string $msg message to log
|
36 |
+
* @return null
|
37 |
+
*/
|
38 |
+
public static function log($msg, $label = 'Minify') {
|
39 |
+
if (! self::$_logger) return;
|
40 |
+
self::$_logger->log($msg, $label);
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* @var mixed logger object (like FirePHP) or null (i.e. no logger available)
|
45 |
+
*/
|
46 |
+
private static $_logger = null;
|
47 |
+
}
|
min/lib/Minify/Packer.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Packer
|
4 |
+
*
|
5 |
+
* To use this class you must first download the PHP port of Packer
|
6 |
+
* and place the file "class.JavaScriptPacker.php" in /lib (or your
|
7 |
+
* include_path).
|
8 |
+
* @link http://joliclic.free.fr/php/javascript-packer/en/
|
9 |
+
*
|
10 |
+
* Be aware that, as long as HTTP encoding is used, scripts minified with JSMin
|
11 |
+
* will provide better client-side performance, as they need not be unpacked in
|
12 |
+
* client-side code.
|
13 |
+
*
|
14 |
+
* @package Minify
|
15 |
+
*/
|
16 |
+
|
17 |
+
if (false === (@include 'class.JavaScriptPacker.php')) {
|
18 |
+
trigger_error(
|
19 |
+
'The script "class.JavaScriptPacker.php" is required. Please see: http:'
|
20 |
+
.'//code.google.com/p/minify/source/browse/trunk/min/lib/Minify/Packer.php'
|
21 |
+
,E_USER_ERROR
|
22 |
+
);
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Minify Javascript using Dean Edward's Packer
|
27 |
+
*
|
28 |
+
* @package Minify
|
29 |
+
*/
|
30 |
+
class Minify_Packer {
|
31 |
+
public static function minify($code, $options = array())
|
32 |
+
{
|
33 |
+
// @todo: set encoding options based on $options :)
|
34 |
+
$packer = new JavascriptPacker($code, 'Normal', true, false);
|
35 |
+
return trim($packer->pack());
|
36 |
+
}
|
37 |
+
}
|
min/lib/Minify/Source.php
ADDED
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_Source
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* A content source to be minified by Minify.
|
9 |
+
*
|
10 |
+
* This allows per-source minification options and the mixing of files with
|
11 |
+
* content from other sources.
|
12 |
+
*
|
13 |
+
* @package Minify
|
14 |
+
* @author Stephen Clay <steve@mrclay.org>
|
15 |
+
*/
|
16 |
+
class Minify_Source {
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var int time of last modification
|
20 |
+
*/
|
21 |
+
public $lastModified = null;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var callback minifier function specifically for this source.
|
25 |
+
*/
|
26 |
+
public $minifier = null;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array minification options specific to this source.
|
30 |
+
*/
|
31 |
+
public $minifyOptions = null;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @var string full path of file
|
35 |
+
*/
|
36 |
+
public $filepath = null;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @var string HTTP Content Type (Minify requires one of the constants Minify::TYPE_*)
|
40 |
+
*/
|
41 |
+
public $contentType = null;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Create a Minify_Source
|
45 |
+
*
|
46 |
+
* In the $spec array(), you can either provide a 'filepath' to an existing
|
47 |
+
* file (existence will not be checked!) or give 'id' (unique string for
|
48 |
+
* the content), 'content' (the string content) and 'lastModified'
|
49 |
+
* (unixtime of last update).
|
50 |
+
*
|
51 |
+
* As a shortcut, the controller will replace "//" at the beginning
|
52 |
+
* of a filepath with $_SERVER['DOCUMENT_ROOT'] . '/'.
|
53 |
+
*
|
54 |
+
* @param array $spec options
|
55 |
+
*/
|
56 |
+
public function __construct($spec)
|
57 |
+
{
|
58 |
+
if (isset($spec['filepath'])) {
|
59 |
+
if (0 === strpos($spec['filepath'], '//')) {
|
60 |
+
$spec['filepath'] = $_SERVER['DOCUMENT_ROOT'] . substr($spec['filepath'], 1);
|
61 |
+
}
|
62 |
+
$segments = explode('.', $spec['filepath']);
|
63 |
+
$ext = strtolower(array_pop($segments));
|
64 |
+
switch ($ext) {
|
65 |
+
case 'js' : $this->contentType = 'application/x-javascript';
|
66 |
+
break;
|
67 |
+
case 'css' : $this->contentType = 'text/css';
|
68 |
+
break;
|
69 |
+
case 'htm' : // fallthrough
|
70 |
+
case 'html' : $this->contentType = 'text/html';
|
71 |
+
break;
|
72 |
+
}
|
73 |
+
$this->filepath = $spec['filepath'];
|
74 |
+
$this->_id = $spec['filepath'];
|
75 |
+
$this->lastModified = filemtime($spec['filepath'])
|
76 |
+
// offset for Windows uploaders with out of sync clocks
|
77 |
+
+ round(Minify::$uploaderHoursBehind * 3600);
|
78 |
+
} elseif (isset($spec['id'])) {
|
79 |
+
$this->_id = 'id::' . $spec['id'];
|
80 |
+
if (isset($spec['content'])) {
|
81 |
+
$this->_content = $spec['content'];
|
82 |
+
} else {
|
83 |
+
$this->_getContentFunc = $spec['getContentFunc'];
|
84 |
+
}
|
85 |
+
$this->lastModified = isset($spec['lastModified'])
|
86 |
+
? $spec['lastModified']
|
87 |
+
: time();
|
88 |
+
}
|
89 |
+
if (isset($spec['contentType'])) {
|
90 |
+
$this->contentType = $spec['contentType'];
|
91 |
+
}
|
92 |
+
if (isset($spec['minifier'])) {
|
93 |
+
$this->minifier = $spec['minifier'];
|
94 |
+
}
|
95 |
+
if (isset($spec['minifyOptions'])) {
|
96 |
+
$this->minifyOptions = $spec['minifyOptions'];
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Get content
|
102 |
+
*
|
103 |
+
* @return string
|
104 |
+
*/
|
105 |
+
public function getContent()
|
106 |
+
{
|
107 |
+
$content = (null !== $this->filepath)
|
108 |
+
? file_get_contents($this->filepath)
|
109 |
+
: ((null !== $this->_content)
|
110 |
+
? $this->_content
|
111 |
+
: call_user_func($this->_getContentFunc, $this->_id)
|
112 |
+
);
|
113 |
+
// remove UTF-8 BOM if present
|
114 |
+
return (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3))
|
115 |
+
? substr($content, 3)
|
116 |
+
: $content;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Get id
|
121 |
+
*
|
122 |
+
* @return string
|
123 |
+
*/
|
124 |
+
public function getId()
|
125 |
+
{
|
126 |
+
return $this->_id;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Verifies a single minification call can handle all sources
|
131 |
+
*
|
132 |
+
* @param array $sources Minify_Source instances
|
133 |
+
*
|
134 |
+
* @return bool true iff there no sources with specific minifier preferences.
|
135 |
+
*/
|
136 |
+
public static function haveNoMinifyPrefs($sources)
|
137 |
+
{
|
138 |
+
foreach ($sources as $source) {
|
139 |
+
if (null !== $source->minifier
|
140 |
+
|| null !== $source->minifyOptions) {
|
141 |
+
return false;
|
142 |
+
}
|
143 |
+
}
|
144 |
+
return true;
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Get unique string for a set of sources
|
149 |
+
*
|
150 |
+
* @param array $sources Minify_Source instances
|
151 |
+
*
|
152 |
+
* @return string
|
153 |
+
*/
|
154 |
+
public static function getDigest($sources)
|
155 |
+
{
|
156 |
+
foreach ($sources as $source) {
|
157 |
+
$info[] = array(
|
158 |
+
$source->_id, $source->minifier, $source->minifyOptions
|
159 |
+
);
|
160 |
+
}
|
161 |
+
return md5(serialize($info));
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* Get content type from a group of sources
|
166 |
+
*
|
167 |
+
* This is called if the user doesn't pass in a 'contentType' options
|
168 |
+
*
|
169 |
+
* @param array $sources Minify_Source instances
|
170 |
+
*
|
171 |
+
* @return string content type. e.g. 'text/css'
|
172 |
+
*/
|
173 |
+
public static function getContentType($sources)
|
174 |
+
{
|
175 |
+
foreach ($sources as $source) {
|
176 |
+
if ($source->contentType !== null) {
|
177 |
+
return $source->contentType;
|
178 |
+
}
|
179 |
+
}
|
180 |
+
return 'text/plain';
|
181 |
+
}
|
182 |
+
|
183 |
+
protected $_content = null;
|
184 |
+
protected $_getContentFunc = null;
|
185 |
+
protected $_id = null;
|
186 |
+
}
|
187 |
+
|
min/lib/Minify/YUI/CssCompressor.java
ADDED
@@ -0,0 +1,382 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
* YUI Compressor
|
3 |
+
* http://developer.yahoo.com/yui/compressor/
|
4 |
+
* Author: Julien Lecomte - http://www.julienlecomte.net/
|
5 |
+
* Author: Isaac Schlueter - http://foohack.com/
|
6 |
+
* Author: Stoyan Stefanov - http://phpied.com/
|
7 |
+
* Copyright (c) 2011 Yahoo! Inc. All rights reserved.
|
8 |
+
* The copyrights embodied in the content of this file are licensed
|
9 |
+
* by Yahoo! Inc. under the BSD (revised) open source license.
|
10 |
+
*/
|
11 |
+
package com.yahoo.platform.yui.compressor;
|
12 |
+
|
13 |
+
import java.io.IOException;
|
14 |
+
import java.io.Reader;
|
15 |
+
import java.io.Writer;
|
16 |
+
import java.util.regex.Pattern;
|
17 |
+
import java.util.regex.Matcher;
|
18 |
+
import java.util.ArrayList;
|
19 |
+
|
20 |
+
public class CssCompressor {
|
21 |
+
|
22 |
+
private StringBuffer srcsb = new StringBuffer();
|
23 |
+
|
24 |
+
public CssCompressor(Reader in) throws IOException {
|
25 |
+
// Read the stream...
|
26 |
+
int c;
|
27 |
+
while ((c = in.read()) != -1) {
|
28 |
+
srcsb.append((char) c);
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
// Leave data urls alone to increase parse performance.
|
33 |
+
protected String extractDataUrls(String css, ArrayList preservedTokens) {
|
34 |
+
|
35 |
+
int maxIndex = css.length() - 1;
|
36 |
+
int appendIndex = 0;
|
37 |
+
|
38 |
+
StringBuffer sb = new StringBuffer();
|
39 |
+
|
40 |
+
Pattern p = Pattern.compile("url\\(\\s*([\"']?)data\\:");
|
41 |
+
Matcher m = p.matcher(css);
|
42 |
+
|
43 |
+
/*
|
44 |
+
* Since we need to account for non-base64 data urls, we need to handle
|
45 |
+
* ' and ) being part of the data string. Hence switching to indexOf,
|
46 |
+
* to determine whether or not we have matching string terminators and
|
47 |
+
* handling sb appends directly, instead of using matcher.append* methods.
|
48 |
+
*/
|
49 |
+
|
50 |
+
while (m.find()) {
|
51 |
+
|
52 |
+
int startIndex = m.start() + 4; // "url(".length()
|
53 |
+
String terminator = m.group(1); // ', " or empty (not quoted)
|
54 |
+
|
55 |
+
if (terminator.length() == 0) {
|
56 |
+
terminator = ")";
|
57 |
+
}
|
58 |
+
|
59 |
+
boolean foundTerminator = false;
|
60 |
+
|
61 |
+
int endIndex = m.end() - 1;
|
62 |
+
while(foundTerminator == false && endIndex+1 <= maxIndex) {
|
63 |
+
endIndex = css.indexOf(terminator, endIndex+1);
|
64 |
+
|
65 |
+
if ((endIndex > 0) && (css.charAt(endIndex-1) != '\\')) {
|
66 |
+
foundTerminator = true;
|
67 |
+
if (!")".equals(terminator)) {
|
68 |
+
endIndex = css.indexOf(")", endIndex);
|
69 |
+
}
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
// Enough searching, start moving stuff over to the buffer
|
74 |
+
sb.append(css.substring(appendIndex, m.start()));
|
75 |
+
|
76 |
+
if (foundTerminator) {
|
77 |
+
String token = css.substring(startIndex, endIndex);
|
78 |
+
token = token.replaceAll("\\s+", "");
|
79 |
+
preservedTokens.add(token);
|
80 |
+
|
81 |
+
String preserver = "url(___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___)";
|
82 |
+
sb.append(preserver);
|
83 |
+
|
84 |
+
appendIndex = endIndex + 1;
|
85 |
+
} else {
|
86 |
+
// No end terminator found, re-add the whole match. Should we throw/warn here?
|
87 |
+
sb.append(css.substring(m.start(), m.end()));
|
88 |
+
appendIndex = m.end();
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
sb.append(css.substring(appendIndex));
|
93 |
+
|
94 |
+
return sb.toString();
|
95 |
+
}
|
96 |
+
|
97 |
+
public void compress(Writer out, int linebreakpos)
|
98 |
+
throws IOException {
|
99 |
+
|
100 |
+
Pattern p;
|
101 |
+
Matcher m;
|
102 |
+
String css = srcsb.toString();
|
103 |
+
|
104 |
+
int startIndex = 0;
|
105 |
+
int endIndex = 0;
|
106 |
+
int i = 0;
|
107 |
+
int max = 0;
|
108 |
+
ArrayList preservedTokens = new ArrayList(0);
|
109 |
+
ArrayList comments = new ArrayList(0);
|
110 |
+
String token;
|
111 |
+
int totallen = css.length();
|
112 |
+
String placeholder;
|
113 |
+
|
114 |
+
css = this.extractDataUrls(css, preservedTokens);
|
115 |
+
|
116 |
+
StringBuffer sb = new StringBuffer(css);
|
117 |
+
|
118 |
+
// collect all comment blocks...
|
119 |
+
while ((startIndex = sb.indexOf("/*", startIndex)) >= 0) {
|
120 |
+
endIndex = sb.indexOf("*/", startIndex + 2);
|
121 |
+
if (endIndex < 0) {
|
122 |
+
endIndex = totallen;
|
123 |
+
}
|
124 |
+
|
125 |
+
token = sb.substring(startIndex + 2, endIndex);
|
126 |
+
comments.add(token);
|
127 |
+
sb.replace(startIndex + 2, endIndex, "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + (comments.size() - 1) + "___");
|
128 |
+
startIndex += 2;
|
129 |
+
}
|
130 |
+
css = sb.toString();
|
131 |
+
|
132 |
+
// preserve strings so their content doesn't get accidentally minified
|
133 |
+
sb = new StringBuffer();
|
134 |
+
p = Pattern.compile("(\"([^\\\\\"]|\\\\.|\\\\)*\")|(\'([^\\\\\']|\\\\.|\\\\)*\')");
|
135 |
+
m = p.matcher(css);
|
136 |
+
while (m.find()) {
|
137 |
+
token = m.group();
|
138 |
+
char quote = token.charAt(0);
|
139 |
+
token = token.substring(1, token.length() - 1);
|
140 |
+
|
141 |
+
// maybe the string contains a comment-like substring?
|
142 |
+
// one, maybe more? put'em back then
|
143 |
+
if (token.indexOf("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_") >= 0) {
|
144 |
+
for (i = 0, max = comments.size(); i < max; i += 1) {
|
145 |
+
token = token.replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___", comments.get(i).toString());
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
// minify alpha opacity in filter strings
|
150 |
+
token = token.replaceAll("(?i)progid:DXImageTransform.Microsoft.Alpha\\(Opacity=", "alpha(opacity=");
|
151 |
+
|
152 |
+
preservedTokens.add(token);
|
153 |
+
String preserver = quote + "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___" + quote;
|
154 |
+
m.appendReplacement(sb, preserver);
|
155 |
+
}
|
156 |
+
m.appendTail(sb);
|
157 |
+
css = sb.toString();
|
158 |
+
|
159 |
+
|
160 |
+
// strings are safe, now wrestle the comments
|
161 |
+
for (i = 0, max = comments.size(); i < max; i += 1) {
|
162 |
+
|
163 |
+
token = comments.get(i).toString();
|
164 |
+
placeholder = "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___";
|
165 |
+
|
166 |
+
// ! in the first position of the comment means preserve
|
167 |
+
// so push to the preserved tokens while stripping the !
|
168 |
+
if (token.startsWith("!")) {
|
169 |
+
preservedTokens.add(token);
|
170 |
+
css = css.replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___");
|
171 |
+
continue;
|
172 |
+
}
|
173 |
+
|
174 |
+
// \ in the last position looks like hack for Mac/IE5
|
175 |
+
// shorten that to /*\*/ and the next one to /**/
|
176 |
+
if (token.endsWith("\\")) {
|
177 |
+
preservedTokens.add("\\");
|
178 |
+
css = css.replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___");
|
179 |
+
i = i + 1; // attn: advancing the loop
|
180 |
+
preservedTokens.add("");
|
181 |
+
css = css.replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___", "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___");
|
182 |
+
continue;
|
183 |
+
}
|
184 |
+
|
185 |
+
// keep empty comments after child selectors (IE7 hack)
|
186 |
+
// e.g. html >/**/ body
|
187 |
+
if (token.length() == 0) {
|
188 |
+
startIndex = css.indexOf(placeholder);
|
189 |
+
if (startIndex > 2) {
|
190 |
+
if (css.charAt(startIndex - 3) == '>') {
|
191 |
+
preservedTokens.add("");
|
192 |
+
css = css.replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___");
|
193 |
+
}
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
// in all other cases kill the comment
|
198 |
+
css = css.replace("/*" + placeholder + "*/", "");
|
199 |
+
}
|
200 |
+
|
201 |
+
|
202 |
+
// Normalize all whitespace strings to single spaces. Easier to work with that way.
|
203 |
+
css = css.replaceAll("\\s+", " ");
|
204 |
+
|
205 |
+
// Remove the spaces before the things that should not have spaces before them.
|
206 |
+
// But, be careful not to turn "p :link {...}" into "p:link{...}"
|
207 |
+
// Swap out any pseudo-class colons with the token, and then swap back.
|
208 |
+
sb = new StringBuffer();
|
209 |
+
p = Pattern.compile("(^|\\})(([^\\{:])+:)+([^\\{]*\\{)");
|
210 |
+
m = p.matcher(css);
|
211 |
+
while (m.find()) {
|
212 |
+
String s = m.group();
|
213 |
+
s = s.replaceAll(":", "___YUICSSMIN_PSEUDOCLASSCOLON___");
|
214 |
+
s = s.replaceAll( "\\\\", "\\\\\\\\" ).replaceAll( "\\$", "\\\\\\$" );
|
215 |
+
m.appendReplacement(sb, s);
|
216 |
+
}
|
217 |
+
m.appendTail(sb);
|
218 |
+
css = sb.toString();
|
219 |
+
// Remove spaces before the things that should not have spaces before them.
|
220 |
+
css = css.replaceAll("\\s+([!{};:>+\\(\\)\\],])", "$1");
|
221 |
+
// bring back the colon
|
222 |
+
css = css.replaceAll("___YUICSSMIN_PSEUDOCLASSCOLON___", ":");
|
223 |
+
|
224 |
+
// retain space for special IE6 cases
|
225 |
+
css = css.replaceAll(":first\\-(line|letter)(\\{|,)", ":first-$1 $2");
|
226 |
+
|
227 |
+
// no space after the end of a preserved comment
|
228 |
+
css = css.replaceAll("\\*/ ", "*/");
|
229 |
+
|
230 |
+
// If there is a @charset, then only allow one, and push to the top of the file.
|
231 |
+
css = css.replaceAll("^(.*)(@charset \"[^\"]*\";)", "$2$1");
|
232 |
+
css = css.replaceAll("^(\\s*@charset [^;]+;\\s*)+", "$1");
|
233 |
+
|
234 |
+
// Put the space back in some cases, to support stuff like
|
235 |
+
// @media screen and (-webkit-min-device-pixel-ratio:0){
|
236 |
+
css = css.replaceAll("\\band\\(", "and (");
|
237 |
+
|
238 |
+
// Remove the spaces after the things that should not have spaces after them.
|
239 |
+
css = css.replaceAll("([!{}:;>+\\(\\[,])\\s+", "$1");
|
240 |
+
|
241 |
+
// remove unnecessary semicolons
|
242 |
+
css = css.replaceAll(";+}", "}");
|
243 |
+
|
244 |
+
// Replace 0(px,em,%) with 0.
|
245 |
+
css = css.replaceAll("([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", "$1$2");
|
246 |
+
|
247 |
+
// Replace 0 0 0 0; with 0.
|
248 |
+
css = css.replaceAll(":0 0 0 0(;|})", ":0$1");
|
249 |
+
css = css.replaceAll(":0 0 0(;|})", ":0$1");
|
250 |
+
css = css.replaceAll(":0 0(;|})", ":0$1");
|
251 |
+
|
252 |
+
|
253 |
+
// Replace background-position:0; with background-position:0 0;
|
254 |
+
// same for transform-origin
|
255 |
+
sb = new StringBuffer();
|
256 |
+
p = Pattern.compile("(?i)(background-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin):0(;|})");
|
257 |
+
m = p.matcher(css);
|
258 |
+
while (m.find()) {
|
259 |
+
m.appendReplacement(sb, m.group(1).toLowerCase() + ":0 0" + m.group(2));
|
260 |
+
}
|
261 |
+
m.appendTail(sb);
|
262 |
+
css = sb.toString();
|
263 |
+
|
264 |
+
// Replace 0.6 to .6, but only when preceded by : or a white-space
|
265 |
+
css = css.replaceAll("(:|\\s)0+\\.(\\d+)", "$1.$2");
|
266 |
+
|
267 |
+
// Shorten colors from rgb(51,102,153) to #336699
|
268 |
+
// This makes it more likely that it'll get further compressed in the next step.
|
269 |
+
p = Pattern.compile("rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)");
|
270 |
+
m = p.matcher(css);
|
271 |
+
sb = new StringBuffer();
|
272 |
+
while (m.find()) {
|
273 |
+
String[] rgbcolors = m.group(1).split(",");
|
274 |
+
StringBuffer hexcolor = new StringBuffer("#");
|
275 |
+
for (i = 0; i < rgbcolors.length; i++) {
|
276 |
+
int val = Integer.parseInt(rgbcolors[i]);
|
277 |
+
if (val < 16) {
|
278 |
+
hexcolor.append("0");
|
279 |
+
}
|
280 |
+
hexcolor.append(Integer.toHexString(val));
|
281 |
+
}
|
282 |
+
m.appendReplacement(sb, hexcolor.toString());
|
283 |
+
}
|
284 |
+
m.appendTail(sb);
|
285 |
+
css = sb.toString();
|
286 |
+
|
287 |
+
// Shorten colors from #AABBCC to #ABC. Note that we want to make sure
|
288 |
+
// the color is not preceded by either ", " or =. Indeed, the property
|
289 |
+
// filter: chroma(color="#FFFFFF");
|
290 |
+
// would become
|
291 |
+
// filter: chroma(color="#FFF");
|
292 |
+
// which makes the filter break in IE.
|
293 |
+
// We also want to make sure we're only compressing #AABBCC patterns inside { }, not id selectors ( #FAABAC {} )
|
294 |
+
// We also want to avoid compressing invalid values (e.g. #AABBCCD to #ABCD)
|
295 |
+
p = Pattern.compile("(\\=\\s*?[\"']?)?" + "#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])" + "(:?\\}|[^0-9a-fA-F{][^{]*?\\})");
|
296 |
+
|
297 |
+
m = p.matcher(css);
|
298 |
+
sb = new StringBuffer();
|
299 |
+
int index = 0;
|
300 |
+
|
301 |
+
while (m.find(index)) {
|
302 |
+
|
303 |
+
sb.append(css.substring(index, m.start()));
|
304 |
+
|
305 |
+
boolean isFilter = (m.group(1) != null && !"".equals(m.group(1)));
|
306 |
+
|
307 |
+
if (isFilter) {
|
308 |
+
// Restore, as is. Compression will break filters
|
309 |
+
sb.append(m.group(1) + "#" + m.group(2) + m.group(3) + m.group(4) + m.group(5) + m.group(6) + m.group(7));
|
310 |
+
} else {
|
311 |
+
if( m.group(2).equalsIgnoreCase(m.group(3)) &&
|
312 |
+
m.group(4).equalsIgnoreCase(m.group(5)) &&
|
313 |
+
m.group(6).equalsIgnoreCase(m.group(7))) {
|
314 |
+
|
315 |
+
// #AABBCC pattern
|
316 |
+
sb.append("#" + (m.group(3) + m.group(5) + m.group(7)).toLowerCase());
|
317 |
+
|
318 |
+
} else {
|
319 |
+
|
320 |
+
// Non-compressible color, restore, but lower case.
|
321 |
+
sb.append("#" + (m.group(2) + m.group(3) + m.group(4) + m.group(5) + m.group(6) + m.group(7)).toLowerCase());
|
322 |
+
}
|
323 |
+
}
|
324 |
+
|
325 |
+
index = m.end(7);
|
326 |
+
}
|
327 |
+
|
328 |
+
sb.append(css.substring(index));
|
329 |
+
css = sb.toString();
|
330 |
+
|
331 |
+
// border: none -> border:0
|
332 |
+
sb = new StringBuffer();
|
333 |
+
p = Pattern.compile("(?i)(border|border-top|border-right|border-bottom|border-right|outline|background):none(;|})");
|
334 |
+
m = p.matcher(css);
|
335 |
+
while (m.find()) {
|
336 |
+
m.appendReplacement(sb, m.group(1).toLowerCase() + ":0" + m.group(2));
|
337 |
+
}
|
338 |
+
m.appendTail(sb);
|
339 |
+
css = sb.toString();
|
340 |
+
|
341 |
+
// shorter opacity IE filter
|
342 |
+
css = css.replaceAll("(?i)progid:DXImageTransform.Microsoft.Alpha\\(Opacity=", "alpha(opacity=");
|
343 |
+
|
344 |
+
// Remove empty rules.
|
345 |
+
css = css.replaceAll("[^\\}\\{/;]+\\{\\}", "");
|
346 |
+
|
347 |
+
// TODO: Should this be after we re-insert tokens. These could alter the break points. However then
|
348 |
+
// we'd need to make sure we don't break in the middle of a string etc.
|
349 |
+
if (linebreakpos >= 0) {
|
350 |
+
// Some source control tools don't like it when files containing lines longer
|
351 |
+
// than, say 8000 characters, are checked in. The linebreak option is used in
|
352 |
+
// that case to split long lines after a specific column.
|
353 |
+
i = 0;
|
354 |
+
int linestartpos = 0;
|
355 |
+
sb = new StringBuffer(css);
|
356 |
+
while (i < sb.length()) {
|
357 |
+
char c = sb.charAt(i++);
|
358 |
+
if (c == '}' && i - linestartpos > linebreakpos) {
|
359 |
+
sb.insert(i, '\n');
|
360 |
+
linestartpos = i;
|
361 |
+
}
|
362 |
+
}
|
363 |
+
|
364 |
+
css = sb.toString();
|
365 |
+
}
|
366 |
+
|
367 |
+
// Replace multiple semi-colons in a row by a single one
|
368 |
+
// See SF bug #1980989
|
369 |
+
css = css.replaceAll(";;+", ";");
|
370 |
+
|
371 |
+
// restore preserved comments and strings
|
372 |
+
for(i = 0, max = preservedTokens.size(); i < max; i++) {
|
373 |
+
css = css.replace("___YUICSSMIN_PRESERVED_TOKEN_" + i + "___", preservedTokens.get(i).toString());
|
374 |
+
}
|
375 |
+
|
376 |
+
// Trim the final string (for any leading or trailing white spaces)
|
377 |
+
css = css.trim();
|
378 |
+
|
379 |
+
// Write the output...
|
380 |
+
out.write(css);
|
381 |
+
}
|
382 |
+
}
|
min/lib/Minify/YUI/CssCompressor.php
ADDED
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_YUI_CssCompressor
|
4 |
+
* @package Minify
|
5 |
+
*
|
6 |
+
* YUI Compressor
|
7 |
+
* Author: Julien Lecomte - http://www.julienlecomte.net/
|
8 |
+
* Author: Isaac Schlueter - http://foohack.com/
|
9 |
+
* Author: Stoyan Stefanov - http://phpied.com/
|
10 |
+
* Author: Steve Clay - http://www.mrclay.org/ (PHP port)
|
11 |
+
* Copyright (c) 2009 Yahoo! Inc. All rights reserved.
|
12 |
+
* The copyrights embodied in the content of this file are licensed
|
13 |
+
* by Yahoo! Inc. under the BSD (revised) open source license.
|
14 |
+
*/
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Compress CSS (incomplete DO NOT USE)
|
18 |
+
*
|
19 |
+
* @see https://github.com/yui/yuicompressor/blob/master/src/com/yahoo/platform/yui/compressor/CssCompressor.java
|
20 |
+
*
|
21 |
+
* @package Minify
|
22 |
+
*/
|
23 |
+
class Minify_YUI_CssCompressor {
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Minify a CSS string
|
27 |
+
*
|
28 |
+
* @param string $css
|
29 |
+
*
|
30 |
+
* @return string
|
31 |
+
*/
|
32 |
+
public function compress($css, $linebreakpos = 0)
|
33 |
+
{
|
34 |
+
$css = str_replace("\r\n", "\n", $css);
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @todo comment removal
|
38 |
+
* @todo re-port from newer Java version
|
39 |
+
*/
|
40 |
+
|
41 |
+
// Normalize all whitespace strings to single spaces. Easier to work with that way.
|
42 |
+
$css = preg_replace('@\s+@', ' ', $css);
|
43 |
+
|
44 |
+
// Make a pseudo class for the Box Model Hack
|
45 |
+
$css = preg_replace("@\"\\\\\"}\\\\\"\"@", "___PSEUDOCLASSBMH___", $css);
|
46 |
+
|
47 |
+
// Remove the spaces before the things that should not have spaces before them.
|
48 |
+
// But, be careful not to turn "p :link {...}" into "p:link{...}"
|
49 |
+
// Swap out any pseudo-class colons with the token, and then swap back.
|
50 |
+
$css = preg_replace_callback("@(^|\\})(([^\\{:])+:)+([^\\{]*\\{)@", array($this, '_removeSpacesCB'), $css);
|
51 |
+
|
52 |
+
$css = preg_replace("@\\s+([!{};:>+\\(\\)\\],])@", "$1", $css);
|
53 |
+
$css = str_replace("___PSEUDOCLASSCOLON___", ":", $css);
|
54 |
+
|
55 |
+
// Remove the spaces after the things that should not have spaces after them.
|
56 |
+
$css = preg_replace("@([!{}:;>+\\(\\[,])\\s+@", "$1", $css);
|
57 |
+
|
58 |
+
// Add the semicolon where it's missing.
|
59 |
+
$css = preg_replace("@([^;\\}])}@", "$1;}", $css);
|
60 |
+
|
61 |
+
// Replace 0(px,em,%) with 0.
|
62 |
+
$css = preg_replace("@([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)@", "$1$2", $css);
|
63 |
+
|
64 |
+
// Replace 0 0 0 0; with 0.
|
65 |
+
$css = str_replace(":0 0 0 0;", ":0;", $css);
|
66 |
+
$css = str_replace(":0 0 0;", ":0;", $css);
|
67 |
+
$css = str_replace(":0 0;", ":0;", $css);
|
68 |
+
|
69 |
+
// Replace background-position:0; with background-position:0 0;
|
70 |
+
$css = str_replace("background-position:0;", "background-position:0 0;", $css);
|
71 |
+
|
72 |
+
// Replace 0.6 to .6, but only when preceded by : or a white-space
|
73 |
+
$css = preg_replace("@(:|\\s)0+\\.(\\d+)@", "$1.$2", $css);
|
74 |
+
|
75 |
+
// Shorten colors from rgb(51,102,153) to #336699
|
76 |
+
// This makes it more likely that it'll get further compressed in the next step.
|
77 |
+
$css = preg_replace_callback("@rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)@", array($this, '_shortenRgbCB'), $css);
|
78 |
+
|
79 |
+
// Shorten colors from #AABBCC to #ABC. Note that we want to make sure
|
80 |
+
// the color is not preceded by either ", " or =. Indeed, the property
|
81 |
+
// filter: chroma(color="#FFFFFF");
|
82 |
+
// would become
|
83 |
+
// filter: chroma(color="#FFF");
|
84 |
+
// which makes the filter break in IE.
|
85 |
+
$css = preg_replace_callback("@([^\"'=\\s])(\\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])@", array($this, '_shortenHexCB'), $css);
|
86 |
+
|
87 |
+
// Remove empty rules.
|
88 |
+
$css = preg_replace("@[^\\}]+\\{;\\}@", "", $css);
|
89 |
+
|
90 |
+
$linebreakpos = isset($this->_options['linebreakpos'])
|
91 |
+
? $this->_options['linebreakpos']
|
92 |
+
: 0;
|
93 |
+
|
94 |
+
if ($linebreakpos > 0) {
|
95 |
+
// Some source control tools don't like it when files containing lines longer
|
96 |
+
// than, say 8000 characters, are checked in. The linebreak option is used in
|
97 |
+
// that case to split long lines after a specific column.
|
98 |
+
$i = 0;
|
99 |
+
$linestartpos = 0;
|
100 |
+
$sb = $css;
|
101 |
+
|
102 |
+
// make sure strlen returns byte count
|
103 |
+
$mbIntEnc = null;
|
104 |
+
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
|
105 |
+
$mbIntEnc = mb_internal_encoding();
|
106 |
+
mb_internal_encoding('8bit');
|
107 |
+
}
|
108 |
+
$sbLength = strlen($css);
|
109 |
+
while ($i < $sbLength) {
|
110 |
+
$c = $sb[$i++];
|
111 |
+
if ($c === '}' && $i - $linestartpos > $linebreakpos) {
|
112 |
+
$sb = substr_replace($sb, "\n", $i, 0);
|
113 |
+
$sbLength++;
|
114 |
+
$linestartpos = $i;
|
115 |
+
}
|
116 |
+
}
|
117 |
+
$css = $sb;
|
118 |
+
|
119 |
+
// undo potential mb_encoding change
|
120 |
+
if ($mbIntEnc !== null) {
|
121 |
+
mb_internal_encoding($mbIntEnc);
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
// Replace the pseudo class for the Box Model Hack
|
126 |
+
$css = str_replace("___PSEUDOCLASSBMH___", "\"\\\\\"}\\\\\"\"", $css);
|
127 |
+
|
128 |
+
// Replace multiple semi-colons in a row by a single one
|
129 |
+
// See SF bug #1980989
|
130 |
+
$css = preg_replace("@;;+@", ";", $css);
|
131 |
+
|
132 |
+
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
|
133 |
+
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
|
134 |
+
|
135 |
+
// Trim the final string (for any leading or trailing white spaces)
|
136 |
+
$css = trim($css);
|
137 |
+
|
138 |
+
return $css;
|
139 |
+
}
|
140 |
+
|
141 |
+
protected function _removeSpacesCB($m)
|
142 |
+
{
|
143 |
+
return str_replace(':', '___PSEUDOCLASSCOLON___', $m[0]);
|
144 |
+
}
|
145 |
+
|
146 |
+
protected function _shortenRgbCB($m)
|
147 |
+
{
|
148 |
+
$rgbcolors = explode(',', $m[1]);
|
149 |
+
$hexcolor = '#';
|
150 |
+
for ($i = 0; $i < count($rgbcolors); $i++) {
|
151 |
+
$val = round($rgbcolors[$i]);
|
152 |
+
if ($val < 16) {
|
153 |
+
$hexcolor .= '0';
|
154 |
+
}
|
155 |
+
$hexcolor .= dechex($val);
|
156 |
+
}
|
157 |
+
return $hexcolor;
|
158 |
+
}
|
159 |
+
|
160 |
+
protected function _shortenHexCB($m)
|
161 |
+
{
|
162 |
+
// Test for AABBCC pattern
|
163 |
+
if ((strtolower($m[3])===strtolower($m[4])) &&
|
164 |
+
(strtolower($m[5])===strtolower($m[6])) &&
|
165 |
+
(strtolower($m[7])===strtolower($m[8]))) {
|
166 |
+
return $m[1] . $m[2] . "#" . $m[3] . $m[5] . $m[7];
|
167 |
+
} else {
|
168 |
+
return $m[0];
|
169 |
+
}
|
170 |
+
}
|
171 |
+
}
|
min/lib/Minify/YUICompressor.php
ADDED
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Minify_YUICompressor
|
4 |
+
* @package Minify
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Compress Javascript/CSS using the YUI Compressor
|
9 |
+
*
|
10 |
+
* You must set $jarFile and $tempDir before calling the minify functions.
|
11 |
+
* Also, depending on your shell's environment, you may need to specify
|
12 |
+
* the full path to java in $javaExecutable or use putenv() to setup the
|
13 |
+
* Java environment.
|
14 |
+
*
|
15 |
+
* <code>
|
16 |
+
* Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.4.6.jar';
|
17 |
+
* Minify_YUICompressor::$tempDir = '/tmp';
|
18 |
+
* $code = Minify_YUICompressor::minifyJs(
|
19 |
+
* $code
|
20 |
+
* ,array('nomunge' => true, 'line-break' => 1000)
|
21 |
+
* );
|
22 |
+
* </code>
|
23 |
+
*
|
24 |
+
* Note: In case you run out stack (default is 512k), you may increase stack size in $options:
|
25 |
+
* array('stack-size' => '2048k')
|
26 |
+
*
|
27 |
+
* @todo unit tests, $options docs
|
28 |
+
*
|
29 |
+
* @package Minify
|
30 |
+
* @author Stephen Clay <steve@mrclay.org>
|
31 |
+
*/
|
32 |
+
class Minify_YUICompressor {
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Filepath of the YUI Compressor jar file. This must be set before
|
36 |
+
* calling minifyJs() or minifyCss().
|
37 |
+
*
|
38 |
+
* @var string
|
39 |
+
*/
|
40 |
+
public static $jarFile = null;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Writable temp directory. This must be set before calling minifyJs()
|
44 |
+
* or minifyCss().
|
45 |
+
*
|
46 |
+
* @var string
|
47 |
+
*/
|
48 |
+
public static $tempDir = null;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Filepath of "java" executable (may be needed if not in shell's PATH)
|
52 |
+
*
|
53 |
+
* @var string
|
54 |
+
*/
|
55 |
+
public static $javaExecutable = 'java';
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Minify a Javascript string
|
59 |
+
*
|
60 |
+
* @param string $js
|
61 |
+
*
|
62 |
+
* @param array $options (verbose is ignored)
|
63 |
+
*
|
64 |
+
* @see http://www.julienlecomte.net/yuicompressor/README
|
65 |
+
*
|
66 |
+
* @return string
|
67 |
+
*/
|
68 |
+
public static function minifyJs($js, $options = array())
|
69 |
+
{
|
70 |
+
return self::_minify('js', $js, $options);
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Minify a CSS string
|
75 |
+
*
|
76 |
+
* @param string $css
|
77 |
+
*
|
78 |
+
* @param array $options (verbose is ignored)
|
79 |
+
*
|
80 |
+
* @see http://www.julienlecomte.net/yuicompressor/README
|
81 |
+
*
|
82 |
+
* @return string
|
83 |
+
*/
|
84 |
+
public static function minifyCss($css, $options = array())
|
85 |
+
{
|
86 |
+
return self::_minify('css', $css, $options);
|
87 |
+
}
|
88 |
+
|
89 |
+
private static function _minify($type, $content, $options)
|
90 |
+
{
|
91 |
+
self::_prepare();
|
92 |
+
if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) {
|
93 |
+
throw new Exception('Minify_YUICompressor : could not create temp file.');
|
94 |
+
}
|
95 |
+
file_put_contents($tmpFile, $content);
|
96 |
+
exec(self::_getCmd($options, $type, $tmpFile), $output, $result_code);
|
97 |
+
unlink($tmpFile);
|
98 |
+
if ($result_code != 0) {
|
99 |
+
throw new Exception('Minify_YUICompressor : YUI compressor execution failed.');
|
100 |
+
}
|
101 |
+
return implode("\n", $output);
|
102 |
+
}
|
103 |
+
|
104 |
+
private static function _getCmd($userOptions, $type, $tmpFile)
|
105 |
+
{
|
106 |
+
$o = array_merge(
|
107 |
+
array(
|
108 |
+
'charset' => ''
|
109 |
+
,'line-break' => 5000
|
110 |
+
,'type' => $type
|
111 |
+
,'nomunge' => false
|
112 |
+
,'preserve-semi' => false
|
113 |
+
,'disable-optimizations' => false
|
114 |
+
,'stack-size' => ''
|
115 |
+
)
|
116 |
+
,$userOptions
|
117 |
+
);
|
118 |
+
$cmd = self::$javaExecutable
|
119 |
+
. (!empty($o['stack-size'])
|
120 |
+
? ' -Xss' . $o['stack-size']
|
121 |
+
: '')
|
122 |
+
. ' -jar ' . escapeshellarg(self::$jarFile)
|
123 |
+
. " --type {$type}"
|
124 |
+
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
|
125 |
+
? " --charset {$o['charset']}"
|
126 |
+
: '')
|
127 |
+
. (is_numeric($o['line-break']) && $o['line-break'] >= 0
|
128 |
+
? ' --line-break ' . (int)$o['line-break']
|
129 |
+
: '');
|
130 |
+
if ($type === 'js') {
|
131 |
+
foreach (array('nomunge', 'preserve-semi', 'disable-optimizations') as $opt) {
|
132 |
+
$cmd .= $o[$opt]
|
133 |
+
? " --{$opt}"
|
134 |
+
: '';
|
135 |
+
}
|
136 |
+
}
|
137 |
+
return $cmd . ' ' . escapeshellarg($tmpFile);
|
138 |
+
}
|
139 |
+
|
140 |
+
private static function _prepare()
|
141 |
+
{
|
142 |
+
if (! is_file(self::$jarFile)) {
|
143 |
+
throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.');
|
144 |
+
}
|
145 |
+
if (! is_readable(self::$jarFile)) {
|
146 |
+
throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not readable.');
|
147 |
+
}
|
148 |
+
if (! is_dir(self::$tempDir)) {
|
149 |
+
throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.');
|
150 |
+
}
|
151 |
+
if (! is_writable(self::$tempDir)) {
|
152 |
+
throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not writable.');
|
153 |
+
}
|
154 |
+
}
|
155 |
+
}
|
156 |
+
|
min/lib/MrClay/Cli.php
ADDED
@@ -0,0 +1,384 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace MrClay;
|
4 |
+
|
5 |
+
use MrClay\Cli\Arg;
|
6 |
+
use InvalidArgumentException;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Forms a front controller for a console app, handling and validating arguments (options)
|
10 |
+
*
|
11 |
+
* Instantiate, add arguments, then call validate(). Afterwards, the user's valid arguments
|
12 |
+
* and their values will be available in $cli->values.
|
13 |
+
*
|
14 |
+
* You may also specify that some arguments be used to provide input/output. By communicating
|
15 |
+
* solely through the file pointers provided by openInput()/openOutput(), you can make your
|
16 |
+
* app more flexible to end users.
|
17 |
+
*
|
18 |
+
* @author Steve Clay <steve@mrclay.org>
|
19 |
+
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
20 |
+
*/
|
21 |
+
class Cli {
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var array validation errors
|
25 |
+
*/
|
26 |
+
public $errors = array();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array option values available after validation.
|
30 |
+
*
|
31 |
+
* E.g. array(
|
32 |
+
* 'a' => false // option was missing
|
33 |
+
* ,'b' => true // option was present
|
34 |
+
* ,'c' => "Hello" // option had value
|
35 |
+
* ,'f' => "/home/user/file" // file path from root
|
36 |
+
* ,'f.raw' => "~/file" // file path as given to option
|
37 |
+
* )
|
38 |
+
*/
|
39 |
+
public $values = array();
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @var array
|
43 |
+
*/
|
44 |
+
public $moreArgs = array();
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @var array
|
48 |
+
*/
|
49 |
+
public $debug = array();
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @var bool The user wants help info
|
53 |
+
*/
|
54 |
+
public $isHelpRequest = false;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* @var Arg[]
|
58 |
+
*/
|
59 |
+
protected $_args = array();
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @var resource
|
63 |
+
*/
|
64 |
+
protected $_stdin = null;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @var resource
|
68 |
+
*/
|
69 |
+
protected $_stdout = null;
|
70 |
+
|
71 |
+
/**
|
72 |
+
* @param bool $exitIfNoStdin (default true) Exit() if STDIN is not defined
|
73 |
+
*/
|
74 |
+
public function __construct($exitIfNoStdin = true)
|
75 |
+
{
|
76 |
+
if ($exitIfNoStdin && ! defined('STDIN')) {
|
77 |
+
exit('This script is for command-line use only.');
|
78 |
+
}
|
79 |
+
if (isset($GLOBALS['argv'][1])
|
80 |
+
&& ($GLOBALS['argv'][1] === '-?' || $GLOBALS['argv'][1] === '--help')) {
|
81 |
+
$this->isHelpRequest = true;
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* @param Arg|string $letter
|
87 |
+
* @return Arg
|
88 |
+
*/
|
89 |
+
public function addOptionalArg($letter)
|
90 |
+
{
|
91 |
+
return $this->addArgument($letter, false);
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @param Arg|string $letter
|
96 |
+
* @return Arg
|
97 |
+
*/
|
98 |
+
public function addRequiredArg($letter)
|
99 |
+
{
|
100 |
+
return $this->addArgument($letter, true);
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* @param string $letter
|
105 |
+
* @param bool $required
|
106 |
+
* @param Arg|null $arg
|
107 |
+
* @return Arg
|
108 |
+
* @throws InvalidArgumentException
|
109 |
+
*/
|
110 |
+
public function addArgument($letter, $required, Arg $arg = null)
|
111 |
+
{
|
112 |
+
if (! preg_match('/^[a-zA-Z]$/', $letter)) {
|
113 |
+
throw new InvalidArgumentException('$letter must be in [a-zA-Z]');
|
114 |
+
}
|
115 |
+
if (! $arg) {
|
116 |
+
$arg = new Arg($required);
|
117 |
+
}
|
118 |
+
$this->_args[$letter] = $arg;
|
119 |
+
return $arg;
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* @param string $letter
|
124 |
+
* @return Arg|null
|
125 |
+
*/
|
126 |
+
public function getArgument($letter)
|
127 |
+
{
|
128 |
+
return isset($this->_args[$letter]) ? $this->_args[$letter] : null;
|
129 |
+
}
|
130 |
+
|
131 |
+
/*
|
132 |
+
* Read and validate options
|
133 |
+
*
|
134 |
+
* @return bool true if all options are valid
|
135 |
+
*/
|
136 |
+
public function validate()
|
137 |
+
{
|
138 |
+
$options = '';
|
139 |
+
$this->errors = array();
|
140 |
+
$this->values = array();
|
141 |
+
$this->_stdin = null;
|
142 |
+
|
143 |
+
if ($this->isHelpRequest) {
|
144 |
+
return false;
|
145 |
+
}
|
146 |
+
|
147 |
+
$lettersUsed = '';
|
148 |
+
foreach ($this->_args as $letter => $arg) {
|
149 |
+
/* @var Arg $arg */
|
150 |
+
$options .= $letter;
|
151 |
+
$lettersUsed .= $letter;
|
152 |
+
|
153 |
+
if ($arg->mayHaveValue || $arg->mustHaveValue) {
|
154 |
+
$options .= ($arg->mustHaveValue ? ':' : '::');
|
155 |
+
}
|
156 |
+
}
|
157 |
+
|
158 |
+
$this->debug['argv'] = $GLOBALS['argv'];
|
159 |
+
$argvCopy = array_slice($GLOBALS['argv'], 1);
|
160 |
+
$o = getopt($options);
|
161 |
+
$this->debug['getopt_options'] = $options;
|
162 |
+
$this->debug['getopt_return'] = $o;
|
163 |
+
|
164 |
+
foreach ($this->_args as $letter => $arg) {
|
165 |
+
/* @var Arg $arg */
|
166 |
+
$this->values[$letter] = false;
|
167 |
+
if (isset($o[$letter])) {
|
168 |
+
if (is_bool($o[$letter])) {
|
169 |
+
|
170 |
+
// remove from argv copy
|
171 |
+
$k = array_search("-$letter", $argvCopy);
|
172 |
+
if ($k !== false) {
|
173 |
+
array_splice($argvCopy, $k, 1);
|
174 |
+
}
|
175 |
+
|
176 |
+
if ($arg->mustHaveValue) {
|
177 |
+
$this->addError($letter, "Missing value");
|
178 |
+
} else {
|
179 |
+
$this->values[$letter] = true;
|
180 |
+
}
|
181 |
+
} else {
|
182 |
+
// string
|
183 |
+
$this->values[$letter] = $o[$letter];
|
184 |
+
$v =& $this->values[$letter];
|
185 |
+
|
186 |
+
// remove from argv copy
|
187 |
+
// first look for -ovalue or -o=value
|
188 |
+
$pattern = "/^-{$letter}=?" . preg_quote($v, '/') . "$/";
|
189 |
+
$foundInArgv = false;
|
190 |
+
foreach ($argvCopy as $k => $argV) {
|
191 |
+
if (preg_match($pattern, $argV)) {
|
192 |
+
array_splice($argvCopy, $k, 1);
|
193 |
+
$foundInArgv = true;
|
194 |
+
break;
|
195 |
+
}
|
196 |
+
}
|
197 |
+
if (! $foundInArgv) {
|
198 |
+
// space separated
|
199 |
+
$k = array_search("-$letter", $argvCopy);
|
200 |
+
if ($k !== false) {
|
201 |
+
array_splice($argvCopy, $k, 2);
|
202 |
+
}
|
203 |
+
}
|
204 |
+
|
205 |
+
// check that value isn't really another option
|
206 |
+
if (strlen($lettersUsed) > 1) {
|
207 |
+
$pattern = "/^-[" . str_replace($letter, '', $lettersUsed) . "]/i";
|
208 |
+
if (preg_match($pattern, $v)) {
|
209 |
+
$this->addError($letter, "Value was read as another option: %s", $v);
|
210 |
+
return false;
|
211 |
+
}
|
212 |
+
}
|
213 |
+
if ($arg->assertFile || $arg->assertDir) {
|
214 |
+
if ($v[0] !== '/' && $v[0] !== '~') {
|
215 |
+
$this->values["$letter.raw"] = $v;
|
216 |
+
$v = getcwd() . "/$v";
|
217 |
+
}
|
218 |
+
}
|
219 |
+
if ($arg->assertFile) {
|
220 |
+
if ($arg->useAsInfile) {
|
221 |
+
$this->_stdin = $v;
|
222 |
+
} elseif ($arg->useAsOutfile) {
|
223 |
+
$this->_stdout = $v;
|
224 |
+
}
|
225 |
+
if ($arg->assertReadable && ! is_readable($v)) {
|
226 |
+
$this->addError($letter, "File not readable: %s", $v);
|
227 |
+
continue;
|
228 |
+
}
|
229 |
+
if ($arg->assertWritable) {
|
230 |
+
if (is_file($v)) {
|
231 |
+
if (! is_writable($v)) {
|
232 |
+
$this->addError($letter, "File not writable: %s", $v);
|
233 |
+
}
|
234 |
+
} else {
|
235 |
+
if (! is_writable(dirname($v))) {
|
236 |
+
$this->addError($letter, "Directory not writable: %s", dirname($v));
|
237 |
+
}
|
238 |
+
}
|
239 |
+
}
|
240 |
+
} elseif ($arg->assertDir && $arg->assertWritable && ! is_writable($v)) {
|
241 |
+
$this->addError($letter, "Directory not readable: %s", $v);
|
242 |
+
}
|
243 |
+
}
|
244 |
+
} else {
|
245 |
+
if ($arg->isRequired()) {
|
246 |
+
$this->addError($letter, "Missing");
|
247 |
+
}
|
248 |
+
}
|
249 |
+
}
|
250 |
+
$this->moreArgs = $argvCopy;
|
251 |
+
reset($this->moreArgs);
|
252 |
+
return empty($this->errors);
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Get the full paths of file(s) passed in as unspecified arguments
|
257 |
+
*
|
258 |
+
* @return array
|
259 |
+
*/
|
260 |
+
public function getPathArgs()
|
261 |
+
{
|
262 |
+
$r = $this->moreArgs;
|
263 |
+
foreach ($r as $k => $v) {
|
264 |
+
if ($v[0] !== '/' && $v[0] !== '~') {
|
265 |
+
$v = getcwd() . "/$v";
|
266 |
+
$v = str_replace('/./', '/', $v);
|
267 |
+
do {
|
268 |
+
$v = preg_replace('@/[^/]+/\\.\\./@', '/', $v, 1, $changed);
|
269 |
+
} while ($changed);
|
270 |
+
$r[$k] = $v;
|
271 |
+
}
|
272 |
+
}
|
273 |
+
return $r;
|
274 |
+
}
|
275 |
+
|
276 |
+
/**
|
277 |
+
* Get a short list of errors with options
|
278 |
+
*
|
279 |
+
* @return string
|
280 |
+
*/
|
281 |
+
public function getErrorReport()
|
282 |
+
{
|
283 |
+
if (empty($this->errors)) {
|
284 |
+
return '';
|
285 |
+
}
|
286 |
+
$r = "Some arguments did not pass validation:\n";
|
287 |
+
foreach ($this->errors as $letter => $arr) {
|
288 |
+
$r .= " $letter : " . implode(', ', $arr) . "\n";
|
289 |
+
}
|
290 |
+
$r .= "\n";
|
291 |
+
return $r;
|
292 |
+
}
|
293 |
+
|
294 |
+
/**
|
295 |
+
* @return string
|
296 |
+
*/
|
297 |
+
public function getArgumentsListing()
|
298 |
+
{
|
299 |
+
$r = "\n";
|
300 |
+
foreach ($this->_args as $letter => $arg) {
|
301 |
+
/* @var Arg $arg */
|
302 |
+
$desc = $arg->getDescription();
|
303 |
+
$flag = " -$letter ";
|
304 |
+
if ($arg->mayHaveValue) {
|
305 |
+
$flag .= "[VAL]";
|
306 |
+
} elseif ($arg->mustHaveValue) {
|
307 |
+
$flag .= "VAL";
|
308 |
+
}
|
309 |
+
if ($arg->assertFile) {
|
310 |
+
$flag = str_replace('VAL', 'FILE', $flag);
|
311 |
+
} elseif ($arg->assertDir) {
|
312 |
+
$flag = str_replace('VAL', 'DIR', $flag);
|
313 |
+
}
|
314 |
+
if ($arg->isRequired()) {
|
315 |
+
$desc = "(required) $desc";
|
316 |
+
}
|
317 |
+
$flag = str_pad($flag, 12, " ", STR_PAD_RIGHT);
|
318 |
+
$desc = wordwrap($desc, 70);
|
319 |
+
$r .= $flag . str_replace("\n", "\n ", $desc) . "\n\n";
|
320 |
+
}
|
321 |
+
return $r;
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Get resource of open input stream. May be STDIN or a file pointer
|
326 |
+
* to the file specified by an option with 'STDIN'.
|
327 |
+
*
|
328 |
+
* @return resource
|
329 |
+
*/
|
330 |
+
public function openInput()
|
331 |
+
{
|
332 |
+
if (null === $this->_stdin) {
|
333 |
+
return STDIN;
|
334 |
+
} else {
|
335 |
+
$this->_stdin = fopen($this->_stdin, 'rb');
|
336 |
+
return $this->_stdin;
|
337 |
+
}
|
338 |
+
}
|
339 |
+
|
340 |
+
public function closeInput()
|
341 |
+
{
|
342 |
+
if (null !== $this->_stdin) {
|
343 |
+
fclose($this->_stdin);
|
344 |
+
}
|
345 |
+
}
|
346 |
+
|
347 |
+
/**
|
348 |
+
* Get resource of open output stream. May be STDOUT or a file pointer
|
349 |
+
* to the file specified by an option with 'STDOUT'. The file will be
|
350 |
+
* truncated to 0 bytes on opening.
|
351 |
+
*
|
352 |
+
* @return resource
|
353 |
+
*/
|
354 |
+
public function openOutput()
|
355 |
+
{
|
356 |
+
if (null === $this->_stdout) {
|
357 |
+
return STDOUT;
|
358 |
+
} else {
|
359 |
+
$this->_stdout = fopen($this->_stdout, 'wb');
|
360 |
+
return $this->_stdout;
|
361 |
+
}
|
362 |
+
}
|
363 |
+
|
364 |
+
public function closeOutput()
|
365 |
+
{
|
366 |
+
if (null !== $this->_stdout) {
|
367 |
+
fclose($this->_stdout);
|
368 |
+
}
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* @param string $letter
|
373 |
+
* @param string $msg
|
374 |
+
* @param string $value
|
375 |
+
*/
|
376 |
+
protected function addError($letter, $msg, $value = null)
|
377 |
+
{
|
378 |
+
if ($value !== null) {
|
379 |
+
$value = var_export($value, 1);
|
380 |
+
}
|
381 |
+
$this->errors[$letter][] = sprintf($msg, $value);
|
382 |
+
}
|
383 |
+
}
|
384 |
+
|
min/lib/MrClay/Cli/Arg.php
ADDED
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace MrClay\Cli;
|
4 |
+
|
5 |
+
use BadMethodCallException;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* An argument for a CLI app. This specifies the argument, what values it expects and
|
9 |
+
* how it's treated during validation.
|
10 |
+
*
|
11 |
+
* By default, the argument will be assumed to be an optional letter flag with no value following.
|
12 |
+
*
|
13 |
+
* If the argument may receive a value, call mayHaveValue(). If there's whitespace after the
|
14 |
+
* flag, the value will be returned as true instead of the string.
|
15 |
+
*
|
16 |
+
* If the argument MUST be accompanied by a value, call mustHaveValue(). In this case, whitespace
|
17 |
+
* is permitted between the flag and its value.
|
18 |
+
*
|
19 |
+
* Use assertFile() or assertDir() to indicate that the argument must return a string value
|
20 |
+
* specifying a file or directory. During validation, the value will be resolved to a
|
21 |
+
* full file/dir path (not necessarily existing!) and the original value will be accessible
|
22 |
+
* via a "*.raw" key. E.g. $cli->values['f.raw']
|
23 |
+
*
|
24 |
+
* Use assertReadable()/assertWritable() to cause the validator to test the file/dir for
|
25 |
+
* read/write permissions respectively.
|
26 |
+
*
|
27 |
+
* @method \MrClay\Cli\Arg mayHaveValue() Assert that the argument, if present, may receive a string value
|
28 |
+
* @method \MrClay\Cli\Arg mustHaveValue() Assert that the argument, if present, must receive a string value
|
29 |
+
* @method \MrClay\Cli\Arg assertFile() Assert that the argument's value must specify a file
|
30 |
+
* @method \MrClay\Cli\Arg assertDir() Assert that the argument's value must specify a directory
|
31 |
+
* @method \MrClay\Cli\Arg assertReadable() Assert that the specified file/dir must be readable
|
32 |
+
* @method \MrClay\Cli\Arg assertWritable() Assert that the specified file/dir must be writable
|
33 |
+
*
|
34 |
+
* @property-read bool mayHaveValue
|
35 |
+
* @property-read bool mustHaveValue
|
36 |
+
* @property-read bool assertFile
|
37 |
+
* @property-read bool assertDir
|
38 |
+
* @property-read bool assertReadable
|
39 |
+
* @property-read bool assertWritable
|
40 |
+
* @property-read bool useAsInfile
|
41 |
+
* @property-read bool useAsOutfile
|
42 |
+
*
|
43 |
+
* @author Steve Clay <steve@mrclay.org>
|
44 |
+
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
45 |
+
*/
|
46 |
+
class Arg {
|
47 |
+
/**
|
48 |
+
* @return array
|
49 |
+
*/
|
50 |
+
public function getDefaultSpec()
|
51 |
+
{
|
52 |
+
return array(
|
53 |
+
'mayHaveValue' => false,
|
54 |
+
'mustHaveValue' => false,
|
55 |
+
'assertFile' => false,
|
56 |
+
'assertDir' => false,
|
57 |
+
'assertReadable' => false,
|
58 |
+
'assertWritable' => false,
|
59 |
+
'useAsInfile' => false,
|
60 |
+
'useAsOutfile' => false,
|
61 |
+
);
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* @var array
|
66 |
+
*/
|
67 |
+
protected $spec = array();
|
68 |
+
|
69 |
+
/**
|
70 |
+
* @var bool
|
71 |
+
*/
|
72 |
+
protected $required = false;
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @var string
|
76 |
+
*/
|
77 |
+
protected $description = '';
|
78 |
+
|
79 |
+
/**
|
80 |
+
* @param bool $isRequired
|
81 |
+
*/
|
82 |
+
public function __construct($isRequired = false)
|
83 |
+
{
|
84 |
+
$this->spec = $this->getDefaultSpec();
|
85 |
+
$this->required = (bool) $isRequired;
|
86 |
+
if ($isRequired) {
|
87 |
+
$this->spec['mustHaveValue'] = true;
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Assert that the argument's value points to a writable file. When
|
93 |
+
* Cli::openOutput() is called, a write pointer to this file will
|
94 |
+
* be provided.
|
95 |
+
* @return Arg
|
96 |
+
*/
|
97 |
+
public function useAsOutfile()
|
98 |
+
{
|
99 |
+
$this->spec['useAsOutfile'] = true;
|
100 |
+
return $this->assertFile()->assertWritable();
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Assert that the argument's value points to a readable file. When
|
105 |
+
* Cli::openInput() is called, a read pointer to this file will
|
106 |
+
* be provided.
|
107 |
+
* @return Arg
|
108 |
+
*/
|
109 |
+
public function useAsInfile()
|
110 |
+
{
|
111 |
+
$this->spec['useAsInfile'] = true;
|
112 |
+
return $this->assertFile()->assertReadable();
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* @return array
|
117 |
+
*/
|
118 |
+
public function getSpec()
|
119 |
+
{
|
120 |
+
return $this->spec;
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* @param string $desc
|
125 |
+
* @return Arg
|
126 |
+
*/
|
127 |
+
public function setDescription($desc)
|
128 |
+
{
|
129 |
+
$this->description = $desc;
|
130 |
+
return $this;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @return string
|
135 |
+
*/
|
136 |
+
public function getDescription()
|
137 |
+
{
|
138 |
+
return $this->description;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* @return bool
|
143 |
+
*/
|
144 |
+
public function isRequired()
|
145 |
+
{
|
146 |
+
return $this->required;
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Note: magic methods declared in class PHPDOC
|
151 |
+
*
|
152 |
+
* @param string $name
|
153 |
+
* @param array $args
|
154 |
+
* @return Arg
|
155 |
+
* @throws BadMethodCallException
|
156 |
+
*/
|
157 |
+
public function __call($name, array $args = array())
|
158 |
+
{
|
159 |
+
if (array_key_exists($name, $this->spec)) {
|
160 |
+
$this->spec[$name] = true;
|
161 |
+
if ($name === 'assertFile' || $name === 'assertDir') {
|
162 |
+
$this->spec['mustHaveValue'] = true;
|
163 |
+
}
|
164 |
+
} else {
|
165 |
+
throw new BadMethodCallException('Method does not exist');
|
166 |
+
}
|
167 |
+
return $this;
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Note: magic properties declared in class PHPDOC
|
172 |
+
*
|
173 |
+
* @param string $name
|
174 |
+
* @return bool|null
|
175 |
+
*/
|
176 |
+
public function __get($name)
|
177 |
+
{
|
178 |
+
if (array_key_exists($name, $this->spec)) {
|
179 |
+
return $this->spec[$name];
|
180 |
+
}
|
181 |
+
return null;
|
182 |
+
}
|
183 |
+
}
|
min/lib/Solar/Dir.php
ADDED
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
*
|
4 |
+
* Utility class for static directory methods.
|
5 |
+
*
|
6 |
+
* @category Solar
|
7 |
+
*
|
8 |
+
* @package Solar
|
9 |
+
*
|
10 |
+
* @author Paul M. Jones <pmjones@solarphp.com>
|
11 |
+
*
|
12 |
+
* @license http://opensource.org/licenses/bsd-license.php BSD
|
13 |
+
*
|
14 |
+
* @version $Id: Dir.php 2926 2007-11-09 16:25:44Z pmjones $
|
15 |
+
*
|
16 |
+
*/
|
17 |
+
class Solar_Dir {
|
18 |
+
|
19 |
+
/**
|
20 |
+
*
|
21 |
+
* The OS-specific temporary directory location.
|
22 |
+
*
|
23 |
+
* @var string
|
24 |
+
*
|
25 |
+
*/
|
26 |
+
protected static $_tmp;
|
27 |
+
|
28 |
+
/**
|
29 |
+
*
|
30 |
+
* Hack for [[php::is_dir() | ]] that checks the include_path.
|
31 |
+
*
|
32 |
+
* Use this to see if a directory exists anywhere in the include_path.
|
33 |
+
*
|
34 |
+
* {{code: php
|
35 |
+
* $dir = Solar_Dir::exists('path/to/dir')
|
36 |
+
* if ($dir) {
|
37 |
+
* $files = scandir($dir);
|
38 |
+
* } else {
|
39 |
+
* echo "Not found in the include-path.";
|
40 |
+
* }
|
41 |
+
* }}
|
42 |
+
*
|
43 |
+
* @param string $dir Check for this directory in the include_path.
|
44 |
+
*
|
45 |
+
* @return mixed If the directory exists in the include_path, returns the
|
46 |
+
* absolute path; if not, returns boolean false.
|
47 |
+
*
|
48 |
+
*/
|
49 |
+
public static function exists($dir)
|
50 |
+
{
|
51 |
+
// no file requested?
|
52 |
+
$dir = trim($dir);
|
53 |
+
if (! $dir) {
|
54 |
+
return false;
|
55 |
+
}
|
56 |
+
|
57 |
+
// using an absolute path for the file?
|
58 |
+
// dual check for Unix '/' and Windows '\',
|
59 |
+
// or Windows drive letter and a ':'.
|
60 |
+
$abs = ($dir[0] == '/' || $dir[0] == '\\' || $dir[1] == ':');
|
61 |
+
if ($abs && is_dir($dir)) {
|
62 |
+
return $dir;
|
63 |
+
}
|
64 |
+
|
65 |
+
// using a relative path on the file
|
66 |
+
$path = explode(PATH_SEPARATOR, ini_get('include_path'));
|
67 |
+
foreach ($path as $base) {
|
68 |
+
// strip Unix '/' and Windows '\'
|
69 |
+
$target = rtrim($base, '\\/') . DIRECTORY_SEPARATOR . $dir;
|
70 |
+
if (is_dir($target)) {
|
71 |
+
return $target;
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
// never found it
|
76 |
+
return false;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
*
|
81 |
+
* "Fixes" a directory string for the operating system.
|
82 |
+
*
|
83 |
+
* Use slashes anywhere you need a directory separator. Then run the
|
84 |
+
* string through fixdir() and the slashes will be converted to the
|
85 |
+
* proper separator (for example '\' on Windows).
|
86 |
+
*
|
87 |
+
* Always adds a final trailing separator.
|
88 |
+
*
|
89 |
+
* @param string $dir The directory string to 'fix'.
|
90 |
+
*
|
91 |
+
* @return string The "fixed" directory string.
|
92 |
+
*
|
93 |
+
*/
|
94 |
+
public static function fix($dir)
|
95 |
+
{
|
96 |
+
$dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
|
97 |
+
return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
*
|
102 |
+
* Convenience method for dirname() and higher-level directories.
|
103 |
+
*
|
104 |
+
* @param string $file Get the dirname() of this file.
|
105 |
+
*
|
106 |
+
* @param int $up Move up in the directory structure this many
|
107 |
+
* times, default 0.
|
108 |
+
*
|
109 |
+
* @return string The dirname() of the file.
|
110 |
+
*
|
111 |
+
*/
|
112 |
+
public static function name($file, $up = 0)
|
113 |
+
{
|
114 |
+
$dir = dirname($file);
|
115 |
+
while ($up --) {
|
116 |
+
$dir = dirname($dir);
|
117 |
+
}
|
118 |
+
return $dir;
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
*
|
123 |
+
* Returns the OS-specific directory for temporary files.
|
124 |
+
*
|
125 |
+
* @param string $sub Add this subdirectory to the returned temporary
|
126 |
+
* directory name.
|
127 |
+
*
|
128 |
+
* @return string The temporary directory path.
|
129 |
+
*
|
130 |
+
*/
|
131 |
+
public static function tmp($sub = '')
|
132 |
+
{
|
133 |
+
// find the tmp dir if needed
|
134 |
+
if (! Solar_Dir::$_tmp) {
|
135 |
+
|
136 |
+
// use the system if we can
|
137 |
+
if (function_exists('sys_get_temp_dir')) {
|
138 |
+
$tmp = sys_get_temp_dir();
|
139 |
+
} else {
|
140 |
+
$tmp = Solar_Dir::_tmp();
|
141 |
+
}
|
142 |
+
|
143 |
+
// remove trailing separator and save
|
144 |
+
Solar_Dir::$_tmp = rtrim($tmp, DIRECTORY_SEPARATOR);
|
145 |
+
}
|
146 |
+
|
147 |
+
// do we have a subdirectory request?
|
148 |
+
$sub = trim($sub);
|
149 |
+
if ($sub) {
|
150 |
+
// remove leading and trailing separators, and force exactly
|
151 |
+
// one trailing separator
|
152 |
+
$sub = trim($sub, DIRECTORY_SEPARATOR)
|
153 |
+
. DIRECTORY_SEPARATOR;
|
154 |
+
}
|
155 |
+
|
156 |
+
return Solar_Dir::$_tmp . DIRECTORY_SEPARATOR . $sub;
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
*
|
161 |
+
* Returns the OS-specific temporary directory location.
|
162 |
+
*
|
163 |
+
* @return string The temp directory path.
|
164 |
+
*
|
165 |
+
*/
|
166 |
+
protected static function _tmp()
|
167 |
+
{
|
168 |
+
// non-Windows system?
|
169 |
+
if (strtolower(substr(PHP_OS, 0, 3)) != 'win') {
|
170 |
+
$tmp = empty($_ENV['TMPDIR']) ? getenv('TMPDIR') : $_ENV['TMPDIR'];
|
171 |
+
if ($tmp) {
|
172 |
+
return $tmp;
|
173 |
+
} else {
|
174 |
+
return '/tmp';
|
175 |
+
}
|
176 |
+
}
|
177 |
+
|
178 |
+
// Windows 'TEMP'
|
179 |
+
$tmp = empty($_ENV['TEMP']) ? getenv('TEMP') : $_ENV['TEMP'];
|
180 |
+
if ($tmp) {
|
181 |
+
return $tmp;
|
182 |
+
}
|
183 |
+
|
184 |
+
// Windows 'TMP'
|
185 |
+
$tmp = empty($_ENV['TMP']) ? getenv('TMP') : $_ENV['TMP'];
|
186 |
+
if ($tmp) {
|
187 |
+
return $tmp;
|
188 |
+
}
|
189 |
+
|
190 |
+
// Windows 'windir'
|
191 |
+
$tmp = empty($_ENV['windir']) ? getenv('windir') : $_ENV['windir'];
|
192 |
+
if ($tmp) {
|
193 |
+
return $tmp;
|
194 |
+
}
|
195 |
+
|
196 |
+
// final fallback for Windows
|
197 |
+
return getenv('SystemRoot') . '\\temp';
|
198 |
+
}
|
199 |
+
}
|
min/utils.php
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Utility functions for generating URIs in HTML files
|
4 |
+
*
|
5 |
+
* @warning These functions execute min/groupsConfig.php, sometimes multiple times.
|
6 |
+
* You must make sure that functions are not redefined, and if your use custom sources,
|
7 |
+
* you must require_once dirname(__FILE__) . '/lib/Minify/Source.php' so that
|
8 |
+
* class is available.
|
9 |
+
*
|
10 |
+
* @package Minify
|
11 |
+
*/
|
12 |
+
|
13 |
+
if (! class_exists('Minify_Loader', false)) {
|
14 |
+
require dirname(__FILE__) . '/lib/Minify/Loader.php';
|
15 |
+
Minify_Loader::register();
|
16 |
+
}
|
17 |
+
|
18 |
+
/*
|
19 |
+
* Get an HTML-escaped Minify URI for a group or set of files. By default, URIs
|
20 |
+
* will contain timestamps to allow far-future Expires headers.
|
21 |
+
*
|
22 |
+
* <code>
|
23 |
+
* <link rel="stylesheet" type="text/css" href="<?= Minify_getUri('css'); ?>" />
|
24 |
+
* <script src="<?= Minify_getUri('js'); ?>"></script>
|
25 |
+
* <script src="<?= Minify_getUri(array(
|
26 |
+
* '//scripts/file1.js'
|
27 |
+
* ,'//scripts/file2.js'
|
28 |
+
* )); ?>"></script>
|
29 |
+
* </code>
|
30 |
+
*
|
31 |
+
* @param mixed $keyOrFiles a group key or array of file paths/URIs
|
32 |
+
* @param array $opts options:
|
33 |
+
* 'farExpires' : (default true) append a modified timestamp for cache revving
|
34 |
+
* 'debug' : (default false) append debug flag
|
35 |
+
* 'charset' : (default 'UTF-8') for htmlspecialchars
|
36 |
+
* 'minAppUri' : (default '/min') URI of min directory
|
37 |
+
* 'rewriteWorks' : (default true) does mod_rewrite work in min app?
|
38 |
+
* 'groupsConfigFile' : specify if different
|
39 |
+
* @return string
|
40 |
+
*/
|
41 |
+
function Minify_getUri($keyOrFiles, $opts = array())
|
42 |
+
{
|
43 |
+
return Minify_HTML_Helper::getUri($keyOrFiles, $opts);
|
44 |
+
}
|
45 |
+
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Get the last modification time of several source js/css files. If you're
|
49 |
+
* caching the output of Minify_getUri(), you might want to know if one of the
|
50 |
+
* dependent source files has changed so you can update the HTML.
|
51 |
+
*
|
52 |
+
* Since this makes a bunch of stat() calls, you might not want to check this
|
53 |
+
* on every request.
|
54 |
+
*
|
55 |
+
* @param array $keysAndFiles group keys and/or file paths/URIs.
|
56 |
+
* @return int latest modification time of all given keys/files
|
57 |
+
*/
|
58 |
+
function Minify_mtime($keysAndFiles, $groupsConfigFile = null)
|
59 |
+
{
|
60 |
+
$gc = null;
|
61 |
+
if (! $groupsConfigFile) {
|
62 |
+
$groupsConfigFile = dirname(__FILE__) . '/groupsConfig.php';
|
63 |
+
}
|
64 |
+
$sources = array();
|
65 |
+
foreach ($keysAndFiles as $keyOrFile) {
|
66 |
+
if (is_object($keyOrFile)
|
67 |
+
|| 0 === strpos($keyOrFile, '/')
|
68 |
+
|| 1 === strpos($keyOrFile, ':\\')) {
|
69 |
+
// a file/source obj
|
70 |
+
$sources[] = $keyOrFile;
|
71 |
+
} else {
|
72 |
+
if (! $gc) {
|
73 |
+
$gc = (require $groupsConfigFile);
|
74 |
+
}
|
75 |
+
foreach ($gc[$keyOrFile] as $source) {
|
76 |
+
$sources[] = $source;
|
77 |
+
}
|
78 |
+
}
|
79 |
+
}
|
80 |
+
return Minify_HTML_Helper::getLastModified($sources);
|
81 |
+
}
|
readme.txt
ADDED
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== Better WordPress Minify ===
|
2 |
+
Contributors: OddOneOut
|
3 |
+
Donate link: http://betterwp.net/wordpress-plugins/bwp-minify/
|
4 |
+
Tags: minify, minify js, minify css, minify javascript, minify stylesheet, minification, optimization, optimize, stylesheet, css, javascript, js
|
5 |
+
Requires at least: 3.0
|
6 |
+
Tested up to: 3.9
|
7 |
+
Stable tag: 1.3.0
|
8 |
+
License: GPLv3 or later
|
9 |
+
|
10 |
+
Allows you to combine and minify your CSS and JS files to improve page load time.
|
11 |
+
|
12 |
+
== Description ==
|
13 |
+
|
14 |
+
Allows you to combine and minify your CSS and JS files to improve page load time. This plugin uses the PHP library [Minify](http://code.google.com/p/minify/) and relies on WordPress's enqueueing system rather than the output buffer, which respects the order of CSS and JS files as well as their dependencies. BWP Minify is very customizable and easy to use.
|
15 |
+
|
16 |
+
**Useful resources to help you get started and make the most out of BWP Minify**
|
17 |
+
|
18 |
+
* [Official Documentation](http://betterwp.net/wordpress-plugins/bwp-minify/#usage)
|
19 |
+
* [WordPress Minify Best Practices](http://betterwp.net/wordpress-minify-javascript-css/)
|
20 |
+
|
21 |
+
**Some Features**
|
22 |
+
|
23 |
+
* Uses enqueueing system of WordPress which improves compatibility with other plugins and themes
|
24 |
+
* Allows you to move enqueued files to desired locations (header, footer, oblivion, etc.) via a dedicated management page
|
25 |
+
* Allows you to change various Minify settings (cache directory, cache age, debug mode, etc.) directly in admin
|
26 |
+
* Allows you to use friendly Minify urls, such as `http://example.com/path/to/cache/somestring.js`
|
27 |
+
* Allows you to use CDN for minified contents, one CDN host for JS and one for CSS with SSL support
|
28 |
+
* Allows you to split long Minify strings into shorter ones
|
29 |
+
* Offers various way to add a cache buster to your minify string such as WordPress's version, Theme's version, Cache folder's last modified timestap, etc.
|
30 |
+
* Supports script localization (`wp_localize_script()`)
|
31 |
+
* Supports inline styles
|
32 |
+
* Supports RTL stylesheets
|
33 |
+
* Supports media-specific stylesheets (e.g. 'screen', 'print', etc.)
|
34 |
+
* Supports conditional stylesheets (e.g. `<!--[if lt IE 7]>`)
|
35 |
+
* Provides hooks for further customization
|
36 |
+
* WordPress Multi-site compatible
|
37 |
+
|
38 |
+
Please don't forget to rate this plugin [5 shining stars](http://wordpress.org/support/view/plugin-reviews/bwp-minify?filter=5) if you like it, thanks!
|
39 |
+
|
40 |
+
**Get in touch**
|
41 |
+
|
42 |
+
* Support is provided via [BetterWP.net Community](http://betterwp.net/community/).
|
43 |
+
* Follow and contribute to development via [Github](https://github.com/OddOneOut/Better-WordPress-Minify).
|
44 |
+
* You can also follow me on [Twitter](http://twitter.com/0dd0ne0ut).
|
45 |
+
* Check out [latest WordPress Tips and Ideas](http://feeds.feedburner.com/BetterWPnet) from BetterWP.net.
|
46 |
+
|
47 |
+
**Languages**
|
48 |
+
|
49 |
+
* English (default)
|
50 |
+
* Romanian (ro_RO) - Thanks to [Luke Tyler, International Calling Cards](www.enjoyprepaid.com)!
|
51 |
+
* Turkish (tr_TR) - Thanks to Hakan E
|
52 |
+
* French (fr_FR) - Thanks to Sebastien
|
53 |
+
* Italian (it_IT) - Thanks to Gabriele - http://cookspot.it
|
54 |
+
* Spanish (es_ES) - Thanks to Ruben Hernandez - http://usitility.com/
|
55 |
+
* Dutch (nl_NL) - Thanks to Martijn van Egmond
|
56 |
+
* German (de_DE) - Thanks to Matthias
|
57 |
+
* Serbo-Croatian (sr_RS) - Thanks to Borisa Djuraskovic - [Web Hosting Hub](http://www.webhostinghub.com/)
|
58 |
+
* Indonesian (id_ID) - Thanks to Nasrulhaq Muiz - http://al-badar.net
|
59 |
+
* Russian (ru_RU) - Thanks to Эдуард Валеев
|
60 |
+
|
61 |
+
Please [help translate](http://betterwp.net/wordpress-tips/create-pot-file-using-poedit/) this plugin!
|
62 |
+
|
63 |
+
== Installation ==
|
64 |
+
|
65 |
+
1. Upload the `bwp-minify` folder to the `/wp-content/plugins/` directory
|
66 |
+
2. Activate the plugin through the Plugins menu in WordPress. After activation, you should see three menus of this plugin on your left.
|
67 |
+
3. Configure the plugin.
|
68 |
+
4. Make sure the `cache` folder is writable, by `CHMOD` it to either `755` or `777`, depending on which one works for you.
|
69 |
+
5. For Advanced Configuration, take a look at [BWP Minify Advanced Usage](http://betterwp.net/wordpress-plugins/bwp-minify/#advanced_usage).
|
70 |
+
|
71 |
+
**Enjoy!**
|
72 |
+
|
73 |
+
== Frequently Asked Questions ==
|
74 |
+
|
75 |
+
[Check plugin news and ask questions](http://betterwp.net/topic/bwp-minify/).
|
76 |
+
|
77 |
+
== Screenshots ==
|
78 |
+
|
79 |
+
1. Basic Functionality
|
80 |
+
2. Advanced Settings
|
81 |
+
3. Enqueued files management
|
82 |
+
4. Minify in action!
|
83 |
+
|
84 |
+
== Changelog ==
|
85 |
+
|
86 |
+
= 1.3.0 =
|
87 |
+
* **New Features**
|
88 |
+
* Added support for Friendly Minify strings, e.g. `http://example.com/path/to/script.js` (best used with CDN). This feature should work well on **nginx** server.
|
89 |
+
* Provides a much better method to capture and print JS, CSS files.
|
90 |
+
* Dependencies are more intelligently handled. This should fix many incompatibility issues with other plugins. **Note**: users of **Leaflet Map Markers** plugin will still need to add `leafletmapsmarker` handle to `Script to be ignored (not minified)`.
|
91 |
+
* Provides full support for conditional and alternate CSS files.
|
92 |
+
* Auto-detects CSS, JS files used on site. Added a new admin page to easily add CSS, JS files to desired positions.
|
93 |
+
* Added a new position called `oblivion`, admin can put CSS, JS files to this position to remove them completely from the site, this is useful when you want to remove duplicate files.
|
94 |
+
* You can now control Minify Library's settings via `wp-admin` setting page.
|
95 |
+
* Basic CDN support with SSL.
|
96 |
+
* **Bugs fixed**
|
97 |
+
* Fixed issues with certain installations where WordPress is installed into a sub-directory or `wp-content` folder is moved.
|
98 |
+
* Fixed incompatibility issues with protocol-relative URLs.
|
99 |
+
* Fixed possible incompatibility issues with forced-SSL URLs.
|
100 |
+
* Other minor fixes.
|
101 |
+
* **Enhancements**
|
102 |
+
* BWP Minify is WordPress 3.9 compatible.
|
103 |
+
* BWP Minify should now be able to handle **very late** JS, CSS files that are queued/printed directly using `wp_print_scripts` and `wp_print_styles`.
|
104 |
+
* Changed Minify URL setting to contain a relative URL (from site root) instead of an absolute URL. This should be useful when switching between staging and live site, or between mirror sites.
|
105 |
+
* Changed Minify strings' default length to `10` to avoid errors on certain servers. Users are encouraged to increase/decrease the length when needed, or enable Pretty Minify String instead.
|
106 |
+
* Disable `Minify bloginfo()` setting by default. Modern themes should always use the enqueue system for any stylesheet.
|
107 |
+
* `admin-bar`, `jquery-core`, and `jquery-migrate` are now ignored by default.
|
108 |
+
* `admin-bar` and `dashicon` are now ignored by default
|
109 |
+
* Changed default cache age to 1 day instead of 2 hours.
|
110 |
+
* Users can now use `BWP_MINIFY_CACHE_DIR` and `BWP_MINIFY_MIN_PATH` constants to override the Cache Directory and Min Path setting in admin. This can become useful when mirroring a site.
|
111 |
+
* Other minor enhancements.
|
112 |
+
* **Misc**
|
113 |
+
* Added a Serbo-Croatian translation - Thanks to Borisa Djuraskovic!
|
114 |
+
* Added an Indonesian translation - Thanks to Nasrulhaq Muiz!
|
115 |
+
* Added a Russian translation - Thanks to Эдуард Валеев!
|
116 |
+
|
117 |
+
**Migration from 1.2.x**
|
118 |
+
|
119 |
+
* Minify URL setting has been replaced with Minify Path setting and you will have to manually update this setting if you're using a non-default one. **Note to Developers**: the setting's key has been changed from `input_minurl` to `input_minpath`. The hook `bwp_minify_min_dir` is still available but deprecated in favor of `bwp_minify_min_path`
|
120 |
+
* Minify Path setting and Cache Directory setting are now default to empty value, which means they're automatically detected.
|
121 |
+
|
122 |
+
Enjoy this release, and please don't forget to [rate BWP Minify](http://wordpress.org/support/view/plugin-reviews/bwp-minify?filter=5) **5 shining stars** if you like it, thanks!
|
123 |
+
|
124 |
+
**Bonus**: Check out these [Minifying Best Practices](http://betterwp.net/wordpress-minify-javascript-css/) to apply to your site today!
|
125 |
+
|
126 |
+
= 1.2.3 =
|
127 |
+
* BWP Minify is now WordPress 3.7 compatible (compatibility issues with WordPress 3.5 and 3.6 have been fixed).
|
128 |
+
* Updated Minify library to version 2.1.7 (security fix). This updated version of Minify also comes with an updated version of CSSMin library, which solves relative path issues in some plugins' CSS files (such as TablePress).
|
129 |
+
* Added support for protocol-relative media sources.
|
130 |
+
* Added partial support for bbPress forum plugin (`quicktags` script must be ignored for the Text editor to work, more info [here](http://betterwp.net/community/topic/144/wordpress-plugins-minify-compatibility-report/)).
|
131 |
+
* Added a filter (`bwp_minify_is_loadable`) to allow other plugins to disable BWP Minify based on some criteria.
|
132 |
+
* Added a Spanish translation - Thanks to Ruben Hernandez!
|
133 |
+
* Added a Dutch translation - Thanks to Martijn van Egmond!
|
134 |
+
* Added a German translation - Thanks to Matthias!
|
135 |
+
* Disabled BWP Minify on Ajax Edit Comment plugin's pages (for now).
|
136 |
+
* Disabled BWP Minify on SimplePress forum page.
|
137 |
+
* Updated BWP Framework to fix a possible bug that causes BWP setting pages to go blank.
|
138 |
+
* Fixed a bug that makes BWP Minify fails to split long minify string into shorter ones.
|
139 |
+
* **Good news**: I have created [an official Github repository for BWP Minify](https://github.com/OddOneOut/Better-WordPress-Minify), awaiting coders worldwide.
|
140 |
+
* **Good news**: ManageWP.com has become the official sponsor for BWP Minify - [Read more](http://betterwp.net/319-better-wordpress-plugins-updates-2013/).
|
141 |
+
|
142 |
+
**Notes:** If you're using All-in-one Calendar plugin and still having issues with it after update, please try this [suggestion](http://betterwp.net/community/post/426/#p426).
|
143 |
+
|
144 |
+
= 1.2.2 =
|
145 |
+
* Fixed a possible fatal error on certain installations.
|
146 |
+
|
147 |
+
= 1.2.1 =
|
148 |
+
* Added support for inline styles (available since WordPress 3.3).
|
149 |
+
* Cache directory is now hidden from normal admins when used on a Multi-site environment.
|
150 |
+
* Added additional hooks allowing developers to control Minify Tags and Minify Src, more info: http://betterwp.net/community/topic/226/additional-hooks-filters-to-getminifytag/ (`bwp_get_minify_src`, `bwp_get_minify_tag`)
|
151 |
+
* Added a new hook for buster allowing developers to dynamically work with this variable (`bwp_minify_get_buster`)
|
152 |
+
* Fixed the pass-by-references fatal error when activating the plugin on a host using PHP 5.4 or higher.
|
153 |
+
* Fixed a possible bug where dependencies are ignored, more info: http://betterwp.net/community/topic/153/dependencies-ignored/
|
154 |
+
* Fixed a possible bug where scripts are echoed twice.
|
155 |
+
* Updated Turkish (tr_TR) translation - Thanks to Hakan E!
|
156 |
+
|
157 |
+
= 1.2.0 =
|
158 |
+
* New Features:
|
159 |
+
* Added a Flush cache button
|
160 |
+
* Split auto minify option into two options, auto minify js and auto minify css
|
161 |
+
* Added a new buster: theme version - thanks to Jon for the patch!
|
162 |
+
* Bugs fixed:
|
163 |
+
* Fixed a bug that broke minify strings if current port is not 80
|
164 |
+
* Fixed two bugs that broke minify strings when `HTTPS` is enabled
|
165 |
+
* Fixed `wp_localize_script` duplication issue
|
166 |
+
* Fixed media style duplication issue
|
167 |
+
* Fixed a deprecate notice caused by the use of `print_scripts_l10n`
|
168 |
+
* Fixed some script dependency issue when you choose to ignore certain scripts
|
169 |
+
* Enhancements:
|
170 |
+
* Increased default cache time to something longer (2 hours)
|
171 |
+
* Cache directory can now be edited in admin area. Please note that changing the cache directory is still a two-step process, which has been described in great details [here](http://betterwp.net/wordpress-plugins/bwp-minify/#cache_directory).
|
172 |
+
* Misc:
|
173 |
+
* Added a Turkish translation - Thanks to Hakan E.
|
174 |
+
* Added a French translation - Thanks to Sebastien
|
175 |
+
* Added an Italian translation - Thanks to Gabriele
|
176 |
+
|
177 |
+
= 1.0.10 =
|
178 |
+
* Fixed two possible PHP notices when using root-relative paths as Minify URL. Thanks to [Marcus](http://marcuspope.com/)!
|
179 |
+
* Fixed wrongly closed HTML `<link>` tags.
|
180 |
+
* Fixed a bug that breaks the dynamic JS file enqueued by Mingle plugin.
|
181 |
+
* Fixed an incompatibility issue with WP Download Monitor.
|
182 |
+
* Fixed an incompatibility issue with Geo-Mashup, thanks to JeremyCherfas for reporting!
|
183 |
+
* Added support for the new script localization function introduced in WordPress 3.3. Thanks to **workshopshed** for reporting!
|
184 |
+
* Added Romanian translation, thanks to Luke Tyler!
|
185 |
+
|
186 |
+
= 1.0.9 =
|
187 |
+
* Fixed a possible PHP warning about an argument not being an array.
|
188 |
+
|
189 |
+
= 1.0.8 =
|
190 |
+
* Hot fix for 1.0.7, which resolves the broken CSS issues for the wp-login page when you install WordPress in a sub-directory.
|
191 |
+
|
192 |
+
= 1.0.7 =
|
193 |
+
* Hot fix for 1.0.6, which resolves some compatibility issues with certain plugins.
|
194 |
+
|
195 |
+
= 1.0.6 =
|
196 |
+
* Added four more hooks for theme developers to fully control how scripts and styles should be enqueued and minified.
|
197 |
+
* Changed the Min URL hook a bit so themes can actually filter it.
|
198 |
+
* Added support for plugins or themes that try to enqueue and print script using the `wp_footer` action instead of the `init` action. Plugins like 'Jetpack by WordPress.com' should be working correctly now.
|
199 |
+
* Other improvements made to the positioning of styles and scripts.
|
200 |
+
|
201 |
+
**Enjoy BWP Minify!**
|
202 |
+
|
203 |
+
= 1.0.5 =
|
204 |
+
* Added support for theme developers who would like to integreate BWP Minify into their themes
|
205 |
+
* Added a new hook added for `min` path.
|
206 |
+
* Added new hooks to allow theme developers to only minify certain media files (see [this section](http://betterwp.net/wordpress-plugins/bwp-minify/#allowed-handles) for more details).
|
207 |
+
* Some bug fixes.
|
208 |
+
* A lot of improvements have been made to catch styles and scripts printed using `wp_print_scripts` and `wp_print_styles`.
|
209 |
+
* The base (`b`) parameter has been removed from the minify string to add support for non-standard WordPress installation (`wp-content` has been moved or renamed.) Thanks to [Lee Powell](http://twitter.com/leepowell) for bug reports and patches!
|
210 |
+
* Fixed a bug that makes BWP Minify fail to determine the cache directory in a sub-folder installation of Multi-site.
|
211 |
+
* Fixed a possible incompatibility issue with Easy Fancybox, thanks to Bob for reporting!
|
212 |
+
* Minor bug fixes for login and signup pages.
|
213 |
+
|
214 |
+
= 1.0.4 =
|
215 |
+
* Fixed an incompatibility issue with media files' uppercase letters.
|
216 |
+
* Fixed a minor undefined offset notice, thanks to Torsten!
|
217 |
+
|
218 |
+
= 1.0.3 =
|
219 |
+
* Fixed a compatibility issue with dynamically generated media files, thanks to naimer!
|
220 |
+
* Not really a changelog, but [a small snippet](http://betterwp.net/wordpress-plugins/bwp-minify/#positioning-your-scripts) for users who want to exclude CSS files has been posted.
|
221 |
+
|
222 |
+
= 1.0.2 =
|
223 |
+
* Fixed a compatibility issue with other plugins loading styles and scripts on a separate .php page. Thanks to larry!
|
224 |
+
* Also fixed a possible bug in 1.0.1
|
225 |
+
|
226 |
+
= 1.0.1 =
|
227 |
+
* The plugin should now detect cache folder correctly for users who install WordPress in a sub-directory.
|
228 |
+
|
229 |
+
= 1.0.0 =
|
230 |
+
* Initial Release.
|
231 |
+
|
232 |
+
== Upgrade Notice ==
|
233 |
+
|
234 |
+
= 1.0.0 =
|
235 |
+
* Enjoy the plugin!
|
screenshot-1.png
ADDED
Binary file
|
screenshot-2.png
ADDED
Binary file
|
screenshot-3.png
ADDED
Binary file
|
screenshot-4.png
ADDED
Binary file
|