bixbite 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.markdown +49 -0
- data/VERSION +1 -0
- data/bin/bixbite +73 -0
- data/lib/bixbite.rb +13 -0
- data/lib/bixbite/command.rb +14 -0
- data/lib/bixbite/create.rb +76 -0
- data/template/Rakefile +25 -0
- data/template/assets/bixbite/Rakefile.rb +297 -0
- data/template/assets/naturaldocs/NaturalDocs/Config/Languages.txt +286 -0
- data/template/assets/naturaldocs/NaturalDocs/Config/Topics.txt +382 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/customizinglanguages.html +52 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/customizingtopics.html +74 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/documenting.html +58 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/documenting/reference.html +146 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/documenting/walkthrough.html +180 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/example/Default.css +528 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/example/NaturalDocs.js +204 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/examples.css +90 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/header/background.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/header/leftside.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/header/logo.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overbody.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overbodybg.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overleftmargin.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overmenu.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overmenubg.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/header/rightside.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/logo.gif +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/about.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/background.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/bottomleft.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/bottomright.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/community.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/customizing.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/using.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/index.html +9 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/javascript/BrowserStyles.js +77 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/javascript/PNGHandling.js +72 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/keywords.html +38 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/languages.html +32 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/menu.html +79 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/output.html +84 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/running.html +40 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/styles.css +290 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/styles.html +52 -0
- data/template/assets/naturaldocs/NaturalDocs/Help/troubleshooting.html +18 -0
- data/template/assets/naturaldocs/NaturalDocs/Info/CSSGuide.txt +947 -0
- data/template/assets/naturaldocs/NaturalDocs/Info/File Parsing.txt +83 -0
- data/template/assets/naturaldocs/NaturalDocs/Info/HTMLTestCases.pm +269 -0
- data/template/assets/naturaldocs/NaturalDocs/Info/Languages.txt +107 -0
- data/template/assets/naturaldocs/NaturalDocs/Info/NDMarkup.txt +91 -0
- data/template/assets/naturaldocs/NaturalDocs/Info/Symbol Management.txt +59 -0
- data/template/assets/naturaldocs/NaturalDocs/Info/images/Logo.png +0 -0
- data/template/assets/naturaldocs/NaturalDocs/JavaScript/NaturalDocs.js +836 -0
- data/template/assets/naturaldocs/NaturalDocs/License-GPL.txt +341 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/BinaryFile.pm +294 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder.pm +280 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/Base.pm +348 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/FramedHTML.pm +345 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/HTML.pm +398 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/HTMLBase.pm +3693 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ClassHierarchy.pm +860 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ClassHierarchy/Class.pm +412 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ClassHierarchy/File.pm +157 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ConfigFile.pm +497 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Constants.pm +165 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/DefineMembers.pm +100 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Error.pm +305 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/File.pm +540 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ImageReferenceTable.pm +383 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ImageReferenceTable/Reference.pm +44 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ImageReferenceTable/String.pm +110 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages.pm +1475 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/ActionScript.pm +1473 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Ada.pm +38 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Advanced.pm +828 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Advanced/Scope.pm +95 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm +70 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Base.pm +832 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/CSharp.pm +1484 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/PLSQL.pm +319 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Pascal.pm +143 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Perl.pm +1370 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Prototype.pm +92 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Prototype/Parameter.pm +87 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Simple.pm +503 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Tcl.pm +219 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Menu.pm +3406 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Menu/Entry.pm +201 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/NDMarkup.pm +76 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser.pm +1331 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser/JavaDoc.pm +464 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser/Native.pm +1060 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser/ParsedTopic.pm +253 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Project.pm +1402 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Project/ImageFile.pm +160 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Project/SourceFile.pm +113 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ReferenceString.pm +334 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Settings.pm +1418 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Settings/BuildTarget.pm +66 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB.pm +678 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/Extension.pm +84 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/File.pm +129 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/Item.pm +201 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/ItemDefinition.pm +45 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/WatchedFileDefinitions.pm +159 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/StatusMessage.pm +102 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolString.pm +212 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable.pm +1984 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/File.pm +186 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/IndexElement.pm +522 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/Reference.pm +273 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm +97 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/Symbol.pm +428 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm +96 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Topics.pm +1319 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Topics/Type.pm +151 -0
- data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Version.pm +384 -0
- data/template/assets/naturaldocs/NaturalDocs/NaturalDocs +400 -0
- data/template/assets/naturaldocs/NaturalDocs/NaturalDocs.bat +17 -0
- data/template/assets/naturaldocs/NaturalDocs/Styles/Default.css +767 -0
- data/template/assets/naturaldocs/NaturalDocs/Styles/Roman.css +765 -0
- data/template/assets/naturaldocs/NaturalDocs/Styles/Small.css +763 -0
- data/template/assets/utilities/pngout +0 -0
- data/template/deploy/public_html/.htaccess +0 -0
- data/template/documentation/js/.htaccess +0 -0
- data/template/src/html/.htaccess +76 -0
- data/template/src/html/css/cmn/global.css +96 -0
- data/template/src/html/css/cmn/ie.css +15 -0
- data/template/src/html/css/cmn/ie6.css +15 -0
- data/template/src/html/images/cmn/.htaccess +0 -0
- data/template/src/html/images/tmp/.htaccess +0 -0
- data/template/src/html/includes/debug.inc +5 -0
- data/template/src/html/includes/footer.inc +52 -0
- data/template/src/html/includes/header.inc +61 -0
- data/template/src/html/includes/html.inc +3 -0
- data/template/src/html/includes/namespace.inc +19 -0
- data/template/src/html/includes/page.inc +151 -0
- data/template/src/html/index.html +35 -0
- data/template/src/html/js/cmn/bootstrap.js +74 -0
- data/template/src/html/js/cmn/global.js +142 -0
- data/template/src/html/js/cmn/lib/LAB.js +348 -0
- data/template/src/html/min/.htaccess +4 -0
- data/template/src/html/min/MinifyCLI.php +19 -0
- data/template/src/html/min/README.txt +132 -0
- data/template/src/html/min/builder/_index.js +242 -0
- data/template/src/html/min/builder/bm.js +36 -0
- data/template/src/html/min/builder/index.php +182 -0
- data/template/src/html/min/builder/ocCheck.php +36 -0
- data/template/src/html/min/builder/rewriteTest.js +1 -0
- data/template/src/html/min/config.php +187 -0
- data/template/src/html/min/groupsConfig.php +34 -0
- data/template/src/html/min/index.php +66 -0
- data/template/src/html/min/lib/FirePHP.php +1370 -0
- data/template/src/html/min/lib/HTTP/ConditionalGet.php +348 -0
- data/template/src/html/min/lib/HTTP/Encoder.php +326 -0
- data/template/src/html/min/lib/JSMin.php +314 -0
- data/template/src/html/min/lib/JSMinPlus.php +1872 -0
- data/template/src/html/min/lib/Minify.php +532 -0
- data/template/src/html/min/lib/Minify/Build.php +103 -0
- data/template/src/html/min/lib/Minify/CSS.php +83 -0
- data/template/src/html/min/lib/Minify/CSS/Compressor.php +250 -0
- data/template/src/html/min/lib/Minify/CSS/UriRewriter.php +270 -0
- data/template/src/html/min/lib/Minify/Cache/APC.php +130 -0
- data/template/src/html/min/lib/Minify/Cache/File.php +125 -0
- data/template/src/html/min/lib/Minify/Cache/Memcache.php +137 -0
- data/template/src/html/min/lib/Minify/ClosureCompiler.php +85 -0
- data/template/src/html/min/lib/Minify/CommentPreserver.php +90 -0
- data/template/src/html/min/lib/Minify/Controller/Base.php +202 -0
- data/template/src/html/min/lib/Minify/Controller/Files.php +78 -0
- data/template/src/html/min/lib/Minify/Controller/Groups.php +94 -0
- data/template/src/html/min/lib/Minify/Controller/MinApp.php +132 -0
- data/template/src/html/min/lib/Minify/Controller/Page.php +82 -0
- data/template/src/html/min/lib/Minify/Controller/Version1.php +118 -0
- data/template/src/html/min/lib/Minify/HTML.php +245 -0
- data/template/src/html/min/lib/Minify/ImportProcessor.php +157 -0
- data/template/src/html/min/lib/Minify/Lines.php +131 -0
- data/template/src/html/min/lib/Minify/Logger.php +45 -0
- data/template/src/html/min/lib/Minify/Packer.php +37 -0
- data/template/src/html/min/lib/Minify/Source.php +187 -0
- data/template/src/html/min/lib/Minify/YUICompressor.php +139 -0
- data/template/src/html/min/lib/Solar/Dir.php +199 -0
- data/template/src/html/min/lib/closure-compiler.jar +0 -0
- data/template/src/html/min/lib/yuicompressor-2.4.2.jar +0 -0
- data/template/src/html/min/utils.php +90 -0
- data/template/src/templates/css/template.css +7 -0
- data/template/src/templates/js/template.js +72 -0
- data/template/src/templates/template.html +18 -0
- data/template/src/yaml/config.yml +46 -0
- data/template/src/yaml/deploy.yml +35 -0
- data/test/bixbite_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- metadata +278 -0
@@ -0,0 +1,348 @@
|
|
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 true, the Cache-Control header will contain
|
79
|
+
* "public", allowing proxies to cache the content. Otherwise "private" will
|
80
|
+
* be sent, allowing only browser caching. (default false)
|
81
|
+
*
|
82
|
+
* 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers
|
83
|
+
* will be sent with content. This is recommended.
|
84
|
+
*
|
85
|
+
* 'encoding': (string) if set, the header "Vary: Accept-Encoding" will
|
86
|
+
* always be sent and a truncated version of the encoding will be appended
|
87
|
+
* to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient
|
88
|
+
* checking of the client's If-None-Match header, as the encoding portion of
|
89
|
+
* the ETag will be stripped before comparison.
|
90
|
+
*
|
91
|
+
* 'contentHash': (string) if given, only the ETag header can be sent with
|
92
|
+
* content (only HTTP1.1 clients can conditionally GET). The given string
|
93
|
+
* should be short with no quote characters and always change when the
|
94
|
+
* resource changes (recommend md5()). This is not needed/used if
|
95
|
+
* lastModifiedTime is given.
|
96
|
+
*
|
97
|
+
* 'eTag': (string) if given, this will be used as the ETag header rather
|
98
|
+
* than values based on lastModifiedTime or contentHash. Also the encoding
|
99
|
+
* string will not be appended to the given value as described above.
|
100
|
+
*
|
101
|
+
* 'invalidate': (bool) if true, the client cache will be considered invalid
|
102
|
+
* without testing. Effectively this disables conditional GET.
|
103
|
+
* (default false)
|
104
|
+
*
|
105
|
+
* 'maxAge': (int) if given, this will set the Cache-Control max-age in
|
106
|
+
* seconds, and also set the Expires header to the equivalent GMT date.
|
107
|
+
* After the max-age period has passed, the browser will again send a
|
108
|
+
* conditional GET to revalidate its cache.
|
109
|
+
*
|
110
|
+
* @return null
|
111
|
+
*/
|
112
|
+
public function __construct($spec)
|
113
|
+
{
|
114
|
+
$scope = (isset($spec['isPublic']) && $spec['isPublic'])
|
115
|
+
? 'public'
|
116
|
+
: 'private';
|
117
|
+
$maxAge = 0;
|
118
|
+
// backwards compatibility (can be removed later)
|
119
|
+
if (isset($spec['setExpires'])
|
120
|
+
&& is_numeric($spec['setExpires'])
|
121
|
+
&& ! isset($spec['maxAge'])) {
|
122
|
+
$spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME'];
|
123
|
+
}
|
124
|
+
if (isset($spec['maxAge'])) {
|
125
|
+
$maxAge = $spec['maxAge'];
|
126
|
+
$this->_headers['Expires'] = self::gmtDate(
|
127
|
+
$_SERVER['REQUEST_TIME'] + $spec['maxAge']
|
128
|
+
);
|
129
|
+
}
|
130
|
+
$etagAppend = '';
|
131
|
+
if (isset($spec['encoding'])) {
|
132
|
+
$this->_stripEtag = true;
|
133
|
+
$this->_headers['Vary'] = 'Accept-Encoding';
|
134
|
+
if ('' !== $spec['encoding']) {
|
135
|
+
if (0 === strpos($spec['encoding'], 'x-')) {
|
136
|
+
$spec['encoding'] = substr($spec['encoding'], 2);
|
137
|
+
}
|
138
|
+
$etagAppend = ';' . substr($spec['encoding'], 0, 2);
|
139
|
+
}
|
140
|
+
}
|
141
|
+
if (isset($spec['lastModifiedTime'])) {
|
142
|
+
$this->_setLastModified($spec['lastModifiedTime']);
|
143
|
+
if (isset($spec['eTag'])) { // Use it
|
144
|
+
$this->_setEtag($spec['eTag'], $scope);
|
145
|
+
} else { // base both headers on time
|
146
|
+
$this->_setEtag($spec['lastModifiedTime'] . $etagAppend, $scope);
|
147
|
+
}
|
148
|
+
} elseif (isset($spec['eTag'])) { // Use it
|
149
|
+
$this->_setEtag($spec['eTag'], $scope);
|
150
|
+
} elseif (isset($spec['contentHash'])) { // Use the hash as the ETag
|
151
|
+
$this->_setEtag($spec['contentHash'] . $etagAppend, $scope);
|
152
|
+
}
|
153
|
+
$this->_headers['Cache-Control'] = "max-age={$maxAge}, {$scope}";
|
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
|
+
header($headers['_responseCode']);
|
213
|
+
unset($headers['_responseCode']);
|
214
|
+
}
|
215
|
+
foreach ($headers as $name => $val) {
|
216
|
+
header($name . ': ' . $val);
|
217
|
+
}
|
218
|
+
}
|
219
|
+
|
220
|
+
/**
|
221
|
+
* Exit if the client's cache is valid for this resource
|
222
|
+
*
|
223
|
+
* This is a convenience method for common use of the class
|
224
|
+
*
|
225
|
+
* @param int $lastModifiedTime if given, both ETag AND Last-Modified headers
|
226
|
+
* will be sent with content. This is recommended.
|
227
|
+
*
|
228
|
+
* @param bool $isPublic (default false) if true, the Cache-Control header
|
229
|
+
* will contain "public", allowing proxies to cache the content. Otherwise
|
230
|
+
* "private" will be sent, allowing only browser caching.
|
231
|
+
*
|
232
|
+
* @param array $options (default empty) additional options for constructor
|
233
|
+
*
|
234
|
+
* @return null
|
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
|
+
protected function _setEtag($hash, $scope)
|
272
|
+
{
|
273
|
+
$this->_etag = '"' . substr($scope, 0, 3) . $hash . '"';
|
274
|
+
$this->_headers['ETag'] = $this->_etag;
|
275
|
+
}
|
276
|
+
|
277
|
+
protected function _setLastModified($time)
|
278
|
+
{
|
279
|
+
$this->_lmTime = (int)$time;
|
280
|
+
$this->_headers['Last-Modified'] = self::gmtDate($time);
|
281
|
+
}
|
282
|
+
|
283
|
+
/**
|
284
|
+
* Determine validity of client cache and queue 304 header if valid
|
285
|
+
*/
|
286
|
+
protected function _isCacheValid()
|
287
|
+
{
|
288
|
+
if (null === $this->_etag) {
|
289
|
+
// lmTime is copied to ETag, so this condition implies that the
|
290
|
+
// server sent neither ETag nor Last-Modified, so the client can't
|
291
|
+
// possibly has a valid cache.
|
292
|
+
return false;
|
293
|
+
}
|
294
|
+
$isValid = ($this->resourceMatchedEtag() || $this->resourceNotModified());
|
295
|
+
if ($isValid) {
|
296
|
+
$this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified';
|
297
|
+
}
|
298
|
+
return $isValid;
|
299
|
+
}
|
300
|
+
|
301
|
+
protected function resourceMatchedEtag()
|
302
|
+
{
|
303
|
+
if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
|
304
|
+
return false;
|
305
|
+
}
|
306
|
+
$clientEtagList = get_magic_quotes_gpc()
|
307
|
+
? stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])
|
308
|
+
: $_SERVER['HTTP_IF_NONE_MATCH'];
|
309
|
+
$clientEtags = explode(',', $clientEtagList);
|
310
|
+
|
311
|
+
$compareTo = $this->normalizeEtag($this->_etag);
|
312
|
+
foreach ($clientEtags as $clientEtag) {
|
313
|
+
if ($this->normalizeEtag($clientEtag) === $compareTo) {
|
314
|
+
// respond with the client's matched ETag, even if it's not what
|
315
|
+
// we would've sent by default
|
316
|
+
$this->_headers['ETag'] = trim($clientEtag);
|
317
|
+
return true;
|
318
|
+
}
|
319
|
+
}
|
320
|
+
return false;
|
321
|
+
}
|
322
|
+
|
323
|
+
protected function normalizeEtag($etag) {
|
324
|
+
$etag = trim($etag);
|
325
|
+
return $this->_stripEtag
|
326
|
+
? preg_replace('/;\\w\\w"$/', '"', $etag)
|
327
|
+
: $etag;
|
328
|
+
}
|
329
|
+
|
330
|
+
protected function resourceNotModified()
|
331
|
+
{
|
332
|
+
if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
333
|
+
return false;
|
334
|
+
}
|
335
|
+
$ifModifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
|
336
|
+
if (false !== ($semicolon = strrpos($ifModifiedSince, ';'))) {
|
337
|
+
// IE has tacked on extra data to this header, strip it
|
338
|
+
$ifModifiedSince = substr($ifModifiedSince, 0, $semicolon);
|
339
|
+
}
|
340
|
+
if ($ifModifiedSince == self::gmtDate($this->_lmTime)) {
|
341
|
+
// Apache 2.2's behavior. If there was no ETag match, send the
|
342
|
+
// non-encoded version of the ETag value.
|
343
|
+
$this->_headers['ETag'] = $this->normalizeEtag($this->_etag);
|
344
|
+
return true;
|
345
|
+
}
|
346
|
+
return false;
|
347
|
+
}
|
348
|
+
}
|
@@ -0,0 +1,326 @@
|
|
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 = false;
|
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
|
+
* @return null
|
90
|
+
*/
|
91
|
+
public function __construct($spec)
|
92
|
+
{
|
93
|
+
$this->_content = $spec['content'];
|
94
|
+
$this->_headers['Content-Length'] = (string)strlen($this->_content);
|
95
|
+
if (isset($spec['type'])) {
|
96
|
+
$this->_headers['Content-Type'] = $spec['type'];
|
97
|
+
}
|
98
|
+
if (isset($spec['method'])
|
99
|
+
&& in_array($spec['method'], array('gzip', 'deflate', 'compress', '')))
|
100
|
+
{
|
101
|
+
$this->_encodeMethod = array($spec['method'], $spec['method']);
|
102
|
+
} else {
|
103
|
+
$this->_encodeMethod = self::getAcceptedEncoding();
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Get content in current form
|
109
|
+
*
|
110
|
+
* Call after encode() for encoded content.
|
111
|
+
*
|
112
|
+
* return string
|
113
|
+
*/
|
114
|
+
public function getContent()
|
115
|
+
{
|
116
|
+
return $this->_content;
|
117
|
+
}
|
118
|
+
|
119
|
+
/**
|
120
|
+
* Get array of output headers to be sent
|
121
|
+
*
|
122
|
+
* E.g.
|
123
|
+
* <code>
|
124
|
+
* array(
|
125
|
+
* 'Content-Length' => '615'
|
126
|
+
* ,'Content-Encoding' => 'x-gzip'
|
127
|
+
* ,'Vary' => 'Accept-Encoding'
|
128
|
+
* )
|
129
|
+
* </code>
|
130
|
+
*
|
131
|
+
* @return array
|
132
|
+
*/
|
133
|
+
public function getHeaders()
|
134
|
+
{
|
135
|
+
return $this->_headers;
|
136
|
+
}
|
137
|
+
|
138
|
+
/**
|
139
|
+
* Send output headers
|
140
|
+
*
|
141
|
+
* You must call this before headers are sent and it probably cannot be
|
142
|
+
* used in conjunction with zlib output buffering / mod_gzip. Errors are
|
143
|
+
* not handled purposefully.
|
144
|
+
*
|
145
|
+
* @see getHeaders()
|
146
|
+
*
|
147
|
+
* @return null
|
148
|
+
*/
|
149
|
+
public function sendHeaders()
|
150
|
+
{
|
151
|
+
foreach ($this->_headers as $name => $val) {
|
152
|
+
header($name . ': ' . $val);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
/**
|
157
|
+
* Send output headers and content
|
158
|
+
*
|
159
|
+
* A shortcut for sendHeaders() and echo getContent()
|
160
|
+
*
|
161
|
+
* You must call this before headers are sent and it probably cannot be
|
162
|
+
* used in conjunction with zlib output buffering / mod_gzip. Errors are
|
163
|
+
* not handled purposefully.
|
164
|
+
*
|
165
|
+
* @return null
|
166
|
+
*/
|
167
|
+
public function sendAll()
|
168
|
+
{
|
169
|
+
$this->sendHeaders();
|
170
|
+
echo $this->_content;
|
171
|
+
}
|
172
|
+
|
173
|
+
/**
|
174
|
+
* Determine the client's best encoding method from the HTTP Accept-Encoding
|
175
|
+
* header.
|
176
|
+
*
|
177
|
+
* If no Accept-Encoding header is set, or the browser is IE before v6 SP2,
|
178
|
+
* this will return ('', ''), the "identity" encoding.
|
179
|
+
*
|
180
|
+
* A syntax-aware scan is done of the Accept-Encoding, so the method must
|
181
|
+
* be non 0. The methods are favored in order of gzip, deflate, then
|
182
|
+
* compress. Deflate is always smallest and generally faster, but is
|
183
|
+
* rarely sent by servers, so client support could be buggier.
|
184
|
+
*
|
185
|
+
* @param bool $allowCompress allow the older compress encoding
|
186
|
+
*
|
187
|
+
* @param bool $allowDeflate allow the more recent deflate encoding
|
188
|
+
*
|
189
|
+
* @return array two values, 1st is the actual encoding method, 2nd is the
|
190
|
+
* alias of that method to use in the Content-Encoding header (some browsers
|
191
|
+
* call gzip "x-gzip" etc.)
|
192
|
+
*/
|
193
|
+
public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true)
|
194
|
+
{
|
195
|
+
// @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
196
|
+
|
197
|
+
if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
|
198
|
+
|| self::_isBuggyIe())
|
199
|
+
{
|
200
|
+
return array('', '');
|
201
|
+
}
|
202
|
+
$ae = $_SERVER['HTTP_ACCEPT_ENCODING'];
|
203
|
+
// gzip checks (quick)
|
204
|
+
if (0 === strpos($ae, 'gzip,') // most browsers
|
205
|
+
|| 0 === strpos($ae, 'deflate, gzip,') // opera
|
206
|
+
) {
|
207
|
+
return array('gzip', 'gzip');
|
208
|
+
}
|
209
|
+
// gzip checks (slow)
|
210
|
+
if (preg_match(
|
211
|
+
'@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
212
|
+
,$ae
|
213
|
+
,$m)) {
|
214
|
+
return array('gzip', $m[1]);
|
215
|
+
}
|
216
|
+
if ($allowDeflate) {
|
217
|
+
// deflate checks
|
218
|
+
$aeRev = strrev($ae);
|
219
|
+
if (0 === strpos($aeRev, 'etalfed ,') // ie, webkit
|
220
|
+
|| 0 === strpos($aeRev, 'etalfed,') // gecko
|
221
|
+
|| 0 === strpos($ae, 'deflate,') // opera
|
222
|
+
// slow parsing
|
223
|
+
|| preg_match(
|
224
|
+
'@(?:^|,)\\s*deflate\\s*(?:$|,|;\\s*q=(?:0\\.|1))@', $ae)) {
|
225
|
+
return array('deflate', 'deflate');
|
226
|
+
}
|
227
|
+
}
|
228
|
+
if ($allowCompress && preg_match(
|
229
|
+
'@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
230
|
+
,$ae
|
231
|
+
,$m)) {
|
232
|
+
return array('compress', $m[1]);
|
233
|
+
}
|
234
|
+
return array('', '');
|
235
|
+
}
|
236
|
+
|
237
|
+
/**
|
238
|
+
* Encode (compress) the content
|
239
|
+
*
|
240
|
+
* If the encode method is '' (none) or compression level is 0, or the 'zlib'
|
241
|
+
* extension isn't loaded, we return false.
|
242
|
+
*
|
243
|
+
* Then the appropriate gz_* function is called to compress the content. If
|
244
|
+
* this fails, false is returned.
|
245
|
+
*
|
246
|
+
* The header "Vary: Accept-Encoding" is added. If encoding is successful,
|
247
|
+
* the Content-Length header is updated, and Content-Encoding is also added.
|
248
|
+
*
|
249
|
+
* @param int $compressionLevel given to zlib functions. If not given, the
|
250
|
+
* class default will be used.
|
251
|
+
*
|
252
|
+
* @return bool success true if the content was actually compressed
|
253
|
+
*/
|
254
|
+
public function encode($compressionLevel = null)
|
255
|
+
{
|
256
|
+
$this->_headers['Vary'] = 'Accept-Encoding';
|
257
|
+
if (null === $compressionLevel) {
|
258
|
+
$compressionLevel = self::$compressionLevel;
|
259
|
+
}
|
260
|
+
if ('' === $this->_encodeMethod[0]
|
261
|
+
|| ($compressionLevel == 0)
|
262
|
+
|| !extension_loaded('zlib'))
|
263
|
+
{
|
264
|
+
return false;
|
265
|
+
}
|
266
|
+
if ($this->_encodeMethod[0] === 'deflate') {
|
267
|
+
$encoded = gzdeflate($this->_content, $compressionLevel);
|
268
|
+
} elseif ($this->_encodeMethod[0] === 'gzip') {
|
269
|
+
$encoded = gzencode($this->_content, $compressionLevel);
|
270
|
+
} else {
|
271
|
+
$encoded = gzcompress($this->_content, $compressionLevel);
|
272
|
+
}
|
273
|
+
if (false === $encoded) {
|
274
|
+
return false;
|
275
|
+
}
|
276
|
+
$this->_headers['Content-Length'] = strlen($encoded);
|
277
|
+
$this->_headers['Content-Encoding'] = $this->_encodeMethod[1];
|
278
|
+
$this->_content = $encoded;
|
279
|
+
return true;
|
280
|
+
}
|
281
|
+
|
282
|
+
/**
|
283
|
+
* Encode and send appropriate headers and content
|
284
|
+
*
|
285
|
+
* This is a convenience method for common use of the class
|
286
|
+
*
|
287
|
+
* @param string $content
|
288
|
+
*
|
289
|
+
* @param int $compressionLevel given to zlib functions. If not given, the
|
290
|
+
* class default will be used.
|
291
|
+
*
|
292
|
+
* @return bool success true if the content was actually compressed
|
293
|
+
*/
|
294
|
+
public static function output($content, $compressionLevel = null)
|
295
|
+
{
|
296
|
+
if (null === $compressionLevel) {
|
297
|
+
$compressionLevel = self::$compressionLevel;
|
298
|
+
}
|
299
|
+
$he = new HTTP_Encoder(array('content' => $content));
|
300
|
+
$ret = $he->encode($compressionLevel);
|
301
|
+
$he->sendAll();
|
302
|
+
return $ret;
|
303
|
+
}
|
304
|
+
|
305
|
+
protected $_content = '';
|
306
|
+
protected $_headers = array();
|
307
|
+
protected $_encodeMethod = array('', '');
|
308
|
+
|
309
|
+
/**
|
310
|
+
* Is the browser an IE version earlier than 6 SP2?
|
311
|
+
*/
|
312
|
+
protected static function _isBuggyIe()
|
313
|
+
{
|
314
|
+
$ua = $_SERVER['HTTP_USER_AGENT'];
|
315
|
+
// quick escape for non-IEs
|
316
|
+
if (0 !== strpos($ua, 'Mozilla/4.0 (compatible; MSIE ')
|
317
|
+
|| false !== strpos($ua, 'Opera')) {
|
318
|
+
return false;
|
319
|
+
}
|
320
|
+
// no regex = faaast
|
321
|
+
$version = (float)substr($ua, 30);
|
322
|
+
return self::$encodeToIe6
|
323
|
+
? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1')))
|
324
|
+
: ($version < 7);
|
325
|
+
}
|
326
|
+
}
|