Fast Velocity Minify - Version 3.0.0

Version Description

Please backup your site before updating. Version 3.0 is a major code rewrite to improve JS and CSS merging, but it requires JS settings to be readjusted after the update.

=

Download this release

Release Info

Developer Alignak
Plugin Icon 128x128 Fast Velocity Minify
Version 3.0.0
Comparing to
See all releases

Code changes from version 2.8.9 to 3.0.0

Files changed (50) hide show
  1. LICENSE +674 -0
  2. assets/fvm.css +53 -0
  3. assets/fvm.js +35 -0
  4. assets/images/ui-icons_444444_256x240.png +0 -0
  5. assets/images/ui-icons_555555_256x240.png +0 -0
  6. assets/images/ui-icons_ffffff_256x240.png +0 -0
  7. fvm.css +0 -33
  8. fvm.js +0 -59
  9. fvm.php +58 -2981
  10. inc/admin.php +494 -0
  11. inc/common.php +1390 -0
  12. inc/frontend.php +886 -0
  13. inc/functions-cache.php +0 -449
  14. inc/functions-cli.php +0 -34
  15. inc/functions-serverinfo.php +0 -374
  16. inc/functions-upgrade.php +0 -41
  17. inc/functions.php +0 -988
  18. inc/serverinfo.php +318 -0
  19. inc/updates.php +84 -0
  20. inc/wp-cli.php +43 -0
  21. layout/admin-layout-help.php +379 -0
  22. layout/admin-layout-settings.php +347 -0
  23. layout/admin-layout-status.php +38 -0
  24. layout/admin-layout-upgrade.php +12 -0
  25. layout/admin-layout.php +29 -0
  26. libs/matthiasmullie/info.txt +4 -0
  27. libs/matthiasmullie/minify/bin/minifycss +45 -45
  28. libs/matthiasmullie/minify/bin/minifyjs +45 -45
  29. libs/matthiasmullie/minify/data/js/keywords_after.txt +6 -6
  30. libs/matthiasmullie/minify/data/js/keywords_before.txt +25 -25
  31. libs/matthiasmullie/minify/data/js/keywords_reserved.txt +62 -62
  32. libs/matthiasmullie/minify/data/js/operators.txt +45 -45
  33. libs/matthiasmullie/minify/data/js/operators_after.txt +42 -42
  34. libs/matthiasmullie/minify/data/js/operators_before.txt +43 -43
  35. libs/matthiasmullie/minify/src/CSS.php +752 -751
  36. libs/matthiasmullie/minify/src/Exception.php +20 -20
  37. libs/matthiasmullie/minify/src/Exceptions/BasicException.php +23 -23
  38. libs/matthiasmullie/minify/src/Exceptions/FileImportException.php +21 -21
  39. libs/matthiasmullie/minify/src/Exceptions/IOException.php +21 -21
  40. libs/matthiasmullie/minify/src/JS.php +612 -612
  41. libs/matthiasmullie/minify/src/Minify.php +497 -459
  42. libs/matthiasmullie/path-converter/src/Converter.php +196 -196
  43. libs/matthiasmullie/path-converter/src/ConverterInterface.php +24 -24
  44. libs/matthiasmullie/path-converter/src/NoConverter.php +23 -23
  45. libs/mrclay/HTML.php +0 -241
  46. libs/{mrclay → raisermin}/index.html +0 -0
  47. libs/raisermin/minify.php +157 -0
  48. libs/simplehtmldom/index.html +0 -0
  49. libs/simplehtmldom/simple_html_dom.php +2357 -0
  50. readme.txt +17 -159
LICENSE ADDED
@@ -0,0 +1,674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU General Public License is a free, copyleft license for
11
+ software and other kinds of works.
12
+
13
+ The licenses for most software and other practical works are designed
14
+ to take away your freedom to share and change the works. By contrast,
15
+ the GNU General Public License is intended to guarantee your freedom to
16
+ share and change all versions of a program--to make sure it remains free
17
+ software for all its users. We, the Free Software Foundation, use the
18
+ GNU General Public License for most of our software; it applies also to
19
+ any other work released this way by its authors. You can apply it to
20
+ your programs, too.
21
+
22
+ When we speak of free software, we are referring to freedom, not
23
+ price. Our General Public Licenses are designed to make sure that you
24
+ have the freedom to distribute copies of free software (and charge for
25
+ them if you wish), that you receive source code or can get it if you
26
+ want it, that you can change the software or use pieces of it in new
27
+ free programs, and that you know you can do these things.
28
+
29
+ To protect your rights, we need to prevent others from denying you
30
+ these rights or asking you to surrender the rights. Therefore, you have
31
+ certain responsibilities if you distribute copies of the software, or if
32
+ you modify it: responsibilities to respect the freedom of others.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must pass on to the recipients the same
36
+ freedoms that you received. You must make sure that they, too, receive
37
+ or can get the source code. And you must show them these terms so they
38
+ know their rights.
39
+
40
+ Developers that use the GNU GPL protect your rights with two steps:
41
+ (1) assert copyright on the software, and (2) offer you this License
42
+ giving you legal permission to copy, distribute and/or modify it.
43
+
44
+ For the developers' and authors' protection, the GPL clearly explains
45
+ that there is no warranty for this free software. For both users' and
46
+ authors' sake, the GPL requires that modified versions be marked as
47
+ changed, so that their problems will not be attributed erroneously to
48
+ authors of previous versions.
49
+
50
+ Some devices are designed to deny users access to install or run
51
+ modified versions of the software inside them, although the manufacturer
52
+ can do so. This is fundamentally incompatible with the aim of
53
+ protecting users' freedom to change the software. The systematic
54
+ pattern of such abuse occurs in the area of products for individuals to
55
+ use, which is precisely where it is most unacceptable. Therefore, we
56
+ have designed this version of the GPL to prohibit the practice for those
57
+ products. If such problems arise substantially in other domains, we
58
+ stand ready to extend this provision to those domains in future versions
59
+ of the GPL, as needed to protect the freedom of users.
60
+
61
+ Finally, every program is threatened constantly by software patents.
62
+ States should not allow patents to restrict development and use of
63
+ software on general-purpose computers, but in those that do, we wish to
64
+ avoid the special danger that patents applied to a free program could
65
+ make it effectively proprietary. To prevent this, the GPL assures that
66
+ patents cannot be used to render the program non-free.
67
+
68
+ The precise terms and conditions for copying, distribution and
69
+ modification follow.
70
+
71
+ TERMS AND CONDITIONS
72
+
73
+ 0. Definitions.
74
+
75
+ "This License" refers to version 3 of the GNU General Public License.
76
+
77
+ "Copyright" also means copyright-like laws that apply to other kinds of
78
+ works, such as semiconductor masks.
79
+
80
+ "The Program" refers to any copyrightable work licensed under this
81
+ License. Each licensee is addressed as "you". "Licensees" and
82
+ "recipients" may be individuals or organizations.
83
+
84
+ To "modify" a work means to copy from or adapt all or part of the work
85
+ in a fashion requiring copyright permission, other than the making of an
86
+ exact copy. The resulting work is called a "modified version" of the
87
+ earlier work or a work "based on" the earlier work.
88
+
89
+ A "covered work" means either the unmodified Program or a work based
90
+ on the Program.
91
+
92
+ To "propagate" a work means to do anything with it that, without
93
+ permission, would make you directly or secondarily liable for
94
+ infringement under applicable copyright law, except executing it on a
95
+ computer or modifying a private copy. Propagation includes copying,
96
+ distribution (with or without modification), making available to the
97
+ public, and in some countries other activities as well.
98
+
99
+ To "convey" a work means any kind of propagation that enables other
100
+ parties to make or receive copies. Mere interaction with a user through
101
+ a computer network, with no transfer of a copy, is not conveying.
102
+
103
+ An interactive user interface displays "Appropriate Legal Notices"
104
+ to the extent that it includes a convenient and prominently visible
105
+ feature that (1) displays an appropriate copyright notice, and (2)
106
+ tells the user that there is no warranty for the work (except to the
107
+ extent that warranties are provided), that licensees may convey the
108
+ work under this License, and how to view a copy of this License. If
109
+ the interface presents a list of user commands or options, such as a
110
+ menu, a prominent item in the list meets this criterion.
111
+
112
+ 1. Source Code.
113
+
114
+ The "source code" for a work means the preferred form of the work
115
+ for making modifications to it. "Object code" means any non-source
116
+ form of a work.
117
+
118
+ A "Standard Interface" means an interface that either is an official
119
+ standard defined by a recognized standards body, or, in the case of
120
+ interfaces specified for a particular programming language, one that
121
+ is widely used among developers working in that language.
122
+
123
+ The "System Libraries" of an executable work include anything, other
124
+ than the work as a whole, that (a) is included in the normal form of
125
+ packaging a Major Component, but which is not part of that Major
126
+ Component, and (b) serves only to enable use of the work with that
127
+ Major Component, or to implement a Standard Interface for which an
128
+ implementation is available to the public in source code form. A
129
+ "Major Component", in this context, means a major essential component
130
+ (kernel, window system, and so on) of the specific operating system
131
+ (if any) on which the executable work runs, or a compiler used to
132
+ produce the work, or an object code interpreter used to run it.
133
+
134
+ The "Corresponding Source" for a work in object code form means all
135
+ the source code needed to generate, install, and (for an executable
136
+ work) run the object code and to modify the work, including scripts to
137
+ control those activities. However, it does not include the work's
138
+ System Libraries, or general-purpose tools or generally available free
139
+ programs which are used unmodified in performing those activities but
140
+ which are not part of the work. For example, Corresponding Source
141
+ includes interface definition files associated with source files for
142
+ the work, and the source code for shared libraries and dynamically
143
+ linked subprograms that the work is specifically designed to require,
144
+ such as by intimate data communication or control flow between those
145
+ subprograms and other parts of the work.
146
+
147
+ The Corresponding Source need not include anything that users
148
+ can regenerate automatically from other parts of the Corresponding
149
+ Source.
150
+
151
+ The Corresponding Source for a work in source code form is that
152
+ same work.
153
+
154
+ 2. Basic Permissions.
155
+
156
+ All rights granted under this License are granted for the term of
157
+ copyright on the Program, and are irrevocable provided the stated
158
+ conditions are met. This License explicitly affirms your unlimited
159
+ permission to run the unmodified Program. The output from running a
160
+ covered work is covered by this License only if the output, given its
161
+ content, constitutes a covered work. This License acknowledges your
162
+ rights of fair use or other equivalent, as provided by copyright law.
163
+
164
+ You may make, run and propagate covered works that you do not
165
+ convey, without conditions so long as your license otherwise remains
166
+ in force. You may convey covered works to others for the sole purpose
167
+ of having them make modifications exclusively for you, or provide you
168
+ with facilities for running those works, provided that you comply with
169
+ the terms of this License in conveying all material for which you do
170
+ not control copyright. Those thus making or running the covered works
171
+ for you must do so exclusively on your behalf, under your direction
172
+ and control, on terms that prohibit them from making any copies of
173
+ your copyrighted material outside their relationship with you.
174
+
175
+ Conveying under any other circumstances is permitted solely under
176
+ the conditions stated below. Sublicensing is not allowed; section 10
177
+ makes it unnecessary.
178
+
179
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
+
181
+ No covered work shall be deemed part of an effective technological
182
+ measure under any applicable law fulfilling obligations under article
183
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
+ similar laws prohibiting or restricting circumvention of such
185
+ measures.
186
+
187
+ When you convey a covered work, you waive any legal power to forbid
188
+ circumvention of technological measures to the extent such circumvention
189
+ is effected by exercising rights under this License with respect to
190
+ the covered work, and you disclaim any intention to limit operation or
191
+ modification of the work as a means of enforcing, against the work's
192
+ users, your or third parties' legal rights to forbid circumvention of
193
+ technological measures.
194
+
195
+ 4. Conveying Verbatim Copies.
196
+
197
+ You may convey verbatim copies of the Program's source code as you
198
+ receive it, in any medium, provided that you conspicuously and
199
+ appropriately publish on each copy an appropriate copyright notice;
200
+ keep intact all notices stating that this License and any
201
+ non-permissive terms added in accord with section 7 apply to the code;
202
+ keep intact all notices of the absence of any warranty; and give all
203
+ recipients a copy of this License along with the Program.
204
+
205
+ You may charge any price or no price for each copy that you convey,
206
+ and you may offer support or warranty protection for a fee.
207
+
208
+ 5. Conveying Modified Source Versions.
209
+
210
+ You may convey a work based on the Program, or the modifications to
211
+ produce it from the Program, in the form of source code under the
212
+ terms of section 4, provided that you also meet all of these conditions:
213
+
214
+ a) The work must carry prominent notices stating that you modified
215
+ it, and giving a relevant date.
216
+
217
+ b) The work must carry prominent notices stating that it is
218
+ released under this License and any conditions added under section
219
+ 7. This requirement modifies the requirement in section 4 to
220
+ "keep intact all notices".
221
+
222
+ c) You must license the entire work, as a whole, under this
223
+ License to anyone who comes into possession of a copy. This
224
+ License will therefore apply, along with any applicable section 7
225
+ additional terms, to the whole of the work, and all its parts,
226
+ regardless of how they are packaged. This License gives no
227
+ permission to license the work in any other way, but it does not
228
+ invalidate such permission if you have separately received it.
229
+
230
+ d) If the work has interactive user interfaces, each must display
231
+ Appropriate Legal Notices; however, if the Program has interactive
232
+ interfaces that do not display Appropriate Legal Notices, your
233
+ work need not make them do so.
234
+
235
+ A compilation of a covered work with other separate and independent
236
+ works, which are not by their nature extensions of the covered work,
237
+ and which are not combined with it such as to form a larger program,
238
+ in or on a volume of a storage or distribution medium, is called an
239
+ "aggregate" if the compilation and its resulting copyright are not
240
+ used to limit the access or legal rights of the compilation's users
241
+ beyond what the individual works permit. Inclusion of a covered work
242
+ in an aggregate does not cause this License to apply to the other
243
+ parts of the aggregate.
244
+
245
+ 6. Conveying Non-Source Forms.
246
+
247
+ You may convey a covered work in object code form under the terms
248
+ of sections 4 and 5, provided that you also convey the
249
+ machine-readable Corresponding Source under the terms of this License,
250
+ in one of these ways:
251
+
252
+ a) Convey the object code in, or embodied in, a physical product
253
+ (including a physical distribution medium), accompanied by the
254
+ Corresponding Source fixed on a durable physical medium
255
+ customarily used for software interchange.
256
+
257
+ b) Convey the object code in, or embodied in, a physical product
258
+ (including a physical distribution medium), accompanied by a
259
+ written offer, valid for at least three years and valid for as
260
+ long as you offer spare parts or customer support for that product
261
+ model, to give anyone who possesses the object code either (1) a
262
+ copy of the Corresponding Source for all the software in the
263
+ product that is covered by this License, on a durable physical
264
+ medium customarily used for software interchange, for a price no
265
+ more than your reasonable cost of physically performing this
266
+ conveying of source, or (2) access to copy the
267
+ Corresponding Source from a network server at no charge.
268
+
269
+ c) Convey individual copies of the object code with a copy of the
270
+ written offer to provide the Corresponding Source. This
271
+ alternative is allowed only occasionally and noncommercially, and
272
+ only if you received the object code with such an offer, in accord
273
+ with subsection 6b.
274
+
275
+ d) Convey the object code by offering access from a designated
276
+ place (gratis or for a charge), and offer equivalent access to the
277
+ Corresponding Source in the same way through the same place at no
278
+ further charge. You need not require recipients to copy the
279
+ Corresponding Source along with the object code. If the place to
280
+ copy the object code is a network server, the Corresponding Source
281
+ may be on a different server (operated by you or a third party)
282
+ that supports equivalent copying facilities, provided you maintain
283
+ clear directions next to the object code saying where to find the
284
+ Corresponding Source. Regardless of what server hosts the
285
+ Corresponding Source, you remain obligated to ensure that it is
286
+ available for as long as needed to satisfy these requirements.
287
+
288
+ e) Convey the object code using peer-to-peer transmission, provided
289
+ you inform other peers where the object code and Corresponding
290
+ Source of the work are being offered to the general public at no
291
+ charge under subsection 6d.
292
+
293
+ A separable portion of the object code, whose source code is excluded
294
+ from the Corresponding Source as a System Library, need not be
295
+ included in conveying the object code work.
296
+
297
+ A "User Product" is either (1) a "consumer product", which means any
298
+ tangible personal property which is normally used for personal, family,
299
+ or household purposes, or (2) anything designed or sold for incorporation
300
+ into a dwelling. In determining whether a product is a consumer product,
301
+ doubtful cases shall be resolved in favor of coverage. For a particular
302
+ product received by a particular user, "normally used" refers to a
303
+ typical or common use of that class of product, regardless of the status
304
+ of the particular user or of the way in which the particular user
305
+ actually uses, or expects or is expected to use, the product. A product
306
+ is a consumer product regardless of whether the product has substantial
307
+ commercial, industrial or non-consumer uses, unless such uses represent
308
+ the only significant mode of use of the product.
309
+
310
+ "Installation Information" for a User Product means any methods,
311
+ procedures, authorization keys, or other information required to install
312
+ and execute modified versions of a covered work in that User Product from
313
+ a modified version of its Corresponding Source. The information must
314
+ suffice to ensure that the continued functioning of the modified object
315
+ code is in no case prevented or interfered with solely because
316
+ modification has been made.
317
+
318
+ If you convey an object code work under this section in, or with, or
319
+ specifically for use in, a User Product, and the conveying occurs as
320
+ part of a transaction in which the right of possession and use of the
321
+ User Product is transferred to the recipient in perpetuity or for a
322
+ fixed term (regardless of how the transaction is characterized), the
323
+ Corresponding Source conveyed under this section must be accompanied
324
+ by the Installation Information. But this requirement does not apply
325
+ if neither you nor any third party retains the ability to install
326
+ modified object code on the User Product (for example, the work has
327
+ been installed in ROM).
328
+
329
+ The requirement to provide Installation Information does not include a
330
+ requirement to continue to provide support service, warranty, or updates
331
+ for a work that has been modified or installed by the recipient, or for
332
+ the User Product in which it has been modified or installed. Access to a
333
+ network may be denied when the modification itself materially and
334
+ adversely affects the operation of the network or violates the rules and
335
+ protocols for communication across the network.
336
+
337
+ Corresponding Source conveyed, and Installation Information provided,
338
+ in accord with this section must be in a format that is publicly
339
+ documented (and with an implementation available to the public in
340
+ source code form), and must require no special password or key for
341
+ unpacking, reading or copying.
342
+
343
+ 7. Additional Terms.
344
+
345
+ "Additional permissions" are terms that supplement the terms of this
346
+ License by making exceptions from one or more of its conditions.
347
+ Additional permissions that are applicable to the entire Program shall
348
+ be treated as though they were included in this License, to the extent
349
+ that they are valid under applicable law. If additional permissions
350
+ apply only to part of the Program, that part may be used separately
351
+ under those permissions, but the entire Program remains governed by
352
+ this License without regard to the additional permissions.
353
+
354
+ When you convey a copy of a covered work, you may at your option
355
+ remove any additional permissions from that copy, or from any part of
356
+ it. (Additional permissions may be written to require their own
357
+ removal in certain cases when you modify the work.) You may place
358
+ additional permissions on material, added by you to a covered work,
359
+ for which you have or can give appropriate copyright permission.
360
+
361
+ Notwithstanding any other provision of this License, for material you
362
+ add to a covered work, you may (if authorized by the copyright holders of
363
+ that material) supplement the terms of this License with terms:
364
+
365
+ a) Disclaiming warranty or limiting liability differently from the
366
+ terms of sections 15 and 16 of this License; or
367
+
368
+ b) Requiring preservation of specified reasonable legal notices or
369
+ author attributions in that material or in the Appropriate Legal
370
+ Notices displayed by works containing it; or
371
+
372
+ c) Prohibiting misrepresentation of the origin of that material, or
373
+ requiring that modified versions of such material be marked in
374
+ reasonable ways as different from the original version; or
375
+
376
+ d) Limiting the use for publicity purposes of names of licensors or
377
+ authors of the material; or
378
+
379
+ e) Declining to grant rights under trademark law for use of some
380
+ trade names, trademarks, or service marks; or
381
+
382
+ f) Requiring indemnification of licensors and authors of that
383
+ material by anyone who conveys the material (or modified versions of
384
+ it) with contractual assumptions of liability to the recipient, for
385
+ any liability that these contractual assumptions directly impose on
386
+ those licensors and authors.
387
+
388
+ All other non-permissive additional terms are considered "further
389
+ restrictions" within the meaning of section 10. If the Program as you
390
+ received it, or any part of it, contains a notice stating that it is
391
+ governed by this License along with a term that is a further
392
+ restriction, you may remove that term. If a license document contains
393
+ a further restriction but permits relicensing or conveying under this
394
+ License, you may add to a covered work material governed by the terms
395
+ of that license document, provided that the further restriction does
396
+ not survive such relicensing or conveying.
397
+
398
+ If you add terms to a covered work in accord with this section, you
399
+ must place, in the relevant source files, a statement of the
400
+ additional terms that apply to those files, or a notice indicating
401
+ where to find the applicable terms.
402
+
403
+ Additional terms, permissive or non-permissive, may be stated in the
404
+ form of a separately written license, or stated as exceptions;
405
+ the above requirements apply either way.
406
+
407
+ 8. Termination.
408
+
409
+ You may not propagate or modify a covered work except as expressly
410
+ provided under this License. Any attempt otherwise to propagate or
411
+ modify it is void, and will automatically terminate your rights under
412
+ this License (including any patent licenses granted under the third
413
+ paragraph of section 11).
414
+
415
+ However, if you cease all violation of this License, then your
416
+ license from a particular copyright holder is reinstated (a)
417
+ provisionally, unless and until the copyright holder explicitly and
418
+ finally terminates your license, and (b) permanently, if the copyright
419
+ holder fails to notify you of the violation by some reasonable means
420
+ prior to 60 days after the cessation.
421
+
422
+ Moreover, your license from a particular copyright holder is
423
+ reinstated permanently if the copyright holder notifies you of the
424
+ violation by some reasonable means, this is the first time you have
425
+ received notice of violation of this License (for any work) from that
426
+ copyright holder, and you cure the violation prior to 30 days after
427
+ your receipt of the notice.
428
+
429
+ Termination of your rights under this section does not terminate the
430
+ licenses of parties who have received copies or rights from you under
431
+ this License. If your rights have been terminated and not permanently
432
+ reinstated, you do not qualify to receive new licenses for the same
433
+ material under section 10.
434
+
435
+ 9. Acceptance Not Required for Having Copies.
436
+
437
+ You are not required to accept this License in order to receive or
438
+ run a copy of the Program. Ancillary propagation of a covered work
439
+ occurring solely as a consequence of using peer-to-peer transmission
440
+ to receive a copy likewise does not require acceptance. However,
441
+ nothing other than this License grants you permission to propagate or
442
+ modify any covered work. These actions infringe copyright if you do
443
+ not accept this License. Therefore, by modifying or propagating a
444
+ covered work, you indicate your acceptance of this License to do so.
445
+
446
+ 10. Automatic Licensing of Downstream Recipients.
447
+
448
+ Each time you convey a covered work, the recipient automatically
449
+ receives a license from the original licensors, to run, modify and
450
+ propagate that work, subject to this License. You are not responsible
451
+ for enforcing compliance by third parties with this License.
452
+
453
+ An "entity transaction" is a transaction transferring control of an
454
+ organization, or substantially all assets of one, or subdividing an
455
+ organization, or merging organizations. If propagation of a covered
456
+ work results from an entity transaction, each party to that
457
+ transaction who receives a copy of the work also receives whatever
458
+ licenses to the work the party's predecessor in interest had or could
459
+ give under the previous paragraph, plus a right to possession of the
460
+ Corresponding Source of the work from the predecessor in interest, if
461
+ the predecessor has it or can get it with reasonable efforts.
462
+
463
+ You may not impose any further restrictions on the exercise of the
464
+ rights granted or affirmed under this License. For example, you may
465
+ not impose a license fee, royalty, or other charge for exercise of
466
+ rights granted under this License, and you may not initiate litigation
467
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
468
+ any patent claim is infringed by making, using, selling, offering for
469
+ sale, or importing the Program or any portion of it.
470
+
471
+ 11. Patents.
472
+
473
+ A "contributor" is a copyright holder who authorizes use under this
474
+ License of the Program or a work on which the Program is based. The
475
+ work thus licensed is called the contributor's "contributor version".
476
+
477
+ A contributor's "essential patent claims" are all patent claims
478
+ owned or controlled by the contributor, whether already acquired or
479
+ hereafter acquired, that would be infringed by some manner, permitted
480
+ by this License, of making, using, or selling its contributor version,
481
+ but do not include claims that would be infringed only as a
482
+ consequence of further modification of the contributor version. For
483
+ purposes of this definition, "control" includes the right to grant
484
+ patent sublicenses in a manner consistent with the requirements of
485
+ this License.
486
+
487
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
488
+ patent license under the contributor's essential patent claims, to
489
+ make, use, sell, offer for sale, import and otherwise run, modify and
490
+ propagate the contents of its contributor version.
491
+
492
+ In the following three paragraphs, a "patent license" is any express
493
+ agreement or commitment, however denominated, not to enforce a patent
494
+ (such as an express permission to practice a patent or covenant not to
495
+ sue for patent infringement). To "grant" such a patent license to a
496
+ party means to make such an agreement or commitment not to enforce a
497
+ patent against the party.
498
+
499
+ If you convey a covered work, knowingly relying on a patent license,
500
+ and the Corresponding Source of the work is not available for anyone
501
+ to copy, free of charge and under the terms of this License, through a
502
+ publicly available network server or other readily accessible means,
503
+ then you must either (1) cause the Corresponding Source to be so
504
+ available, or (2) arrange to deprive yourself of the benefit of the
505
+ patent license for this particular work, or (3) arrange, in a manner
506
+ consistent with the requirements of this License, to extend the patent
507
+ license to downstream recipients. "Knowingly relying" means you have
508
+ actual knowledge that, but for the patent license, your conveying the
509
+ covered work in a country, or your recipient's use of the covered work
510
+ in a country, would infringe one or more identifiable patents in that
511
+ country that you have reason to believe are valid.
512
+
513
+ If, pursuant to or in connection with a single transaction or
514
+ arrangement, you convey, or propagate by procuring conveyance of, a
515
+ covered work, and grant a patent license to some of the parties
516
+ receiving the covered work authorizing them to use, propagate, modify
517
+ or convey a specific copy of the covered work, then the patent license
518
+ you grant is automatically extended to all recipients of the covered
519
+ work and works based on it.
520
+
521
+ A patent license is "discriminatory" if it does not include within
522
+ the scope of its coverage, prohibits the exercise of, or is
523
+ conditioned on the non-exercise of one or more of the rights that are
524
+ specifically granted under this License. You may not convey a covered
525
+ work if you are a party to an arrangement with a third party that is
526
+ in the business of distributing software, under which you make payment
527
+ to the third party based on the extent of your activity of conveying
528
+ the work, and under which the third party grants, to any of the
529
+ parties who would receive the covered work from you, a discriminatory
530
+ patent license (a) in connection with copies of the covered work
531
+ conveyed by you (or copies made from those copies), or (b) primarily
532
+ for and in connection with specific products or compilations that
533
+ contain the covered work, unless you entered into that arrangement,
534
+ or that patent license was granted, prior to 28 March 2007.
535
+
536
+ Nothing in this License shall be construed as excluding or limiting
537
+ any implied license or other defenses to infringement that may
538
+ otherwise be available to you under applicable patent law.
539
+
540
+ 12. No Surrender of Others' Freedom.
541
+
542
+ If conditions are imposed on you (whether by court order, agreement or
543
+ otherwise) that contradict the conditions of this License, they do not
544
+ excuse you from the conditions of this License. If you cannot convey a
545
+ covered work so as to satisfy simultaneously your obligations under this
546
+ License and any other pertinent obligations, then as a consequence you may
547
+ not convey it at all. For example, if you agree to terms that obligate you
548
+ to collect a royalty for further conveying from those to whom you convey
549
+ the Program, the only way you could satisfy both those terms and this
550
+ License would be to refrain entirely from conveying the Program.
551
+
552
+ 13. Use with the GNU Affero General Public License.
553
+
554
+ Notwithstanding any other provision of this License, you have
555
+ permission to link or combine any covered work with a work licensed
556
+ under version 3 of the GNU Affero General Public License into a single
557
+ combined work, and to convey the resulting work. The terms of this
558
+ License will continue to apply to the part which is the covered work,
559
+ but the special requirements of the GNU Affero General Public License,
560
+ section 13, concerning interaction through a network will apply to the
561
+ combination as such.
562
+
563
+ 14. Revised Versions of this License.
564
+
565
+ The Free Software Foundation may publish revised and/or new versions of
566
+ the GNU General Public License from time to time. Such new versions will
567
+ be similar in spirit to the present version, but may differ in detail to
568
+ address new problems or concerns.
569
+
570
+ Each version is given a distinguishing version number. If the
571
+ Program specifies that a certain numbered version of the GNU General
572
+ Public License "or any later version" applies to it, you have the
573
+ option of following the terms and conditions either of that numbered
574
+ version or of any later version published by the Free Software
575
+ Foundation. If the Program does not specify a version number of the
576
+ GNU General Public License, you may choose any version ever published
577
+ by the Free Software Foundation.
578
+
579
+ If the Program specifies that a proxy can decide which future
580
+ versions of the GNU General Public License can be used, that proxy's
581
+ public statement of acceptance of a version permanently authorizes you
582
+ to choose that version for the Program.
583
+
584
+ Later license versions may give you additional or different
585
+ permissions. However, no additional obligations are imposed on any
586
+ author or copyright holder as a result of your choosing to follow a
587
+ later version.
588
+
589
+ 15. Disclaimer of Warranty.
590
+
591
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
+
600
+ 16. Limitation of Liability.
601
+
602
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
+ SUCH DAMAGES.
611
+
612
+ 17. Interpretation of Sections 15 and 16.
613
+
614
+ If the disclaimer of warranty and limitation of liability provided
615
+ above cannot be given local legal effect according to their terms,
616
+ reviewing courts shall apply local law that most closely approximates
617
+ an absolute waiver of all civil liability in connection with the
618
+ Program, unless a warranty or assumption of liability accompanies a
619
+ copy of the Program in return for a fee.
620
+
621
+ END OF TERMS AND CONDITIONS
622
+
623
+ How to Apply These Terms to Your New Programs
624
+
625
+ If you develop a new program, and you want it to be of the greatest
626
+ possible use to the public, the best way to achieve this is to make it
627
+ free software which everyone can redistribute and change under these terms.
628
+
629
+ To do so, attach the following notices to the program. It is safest
630
+ to attach them to the start of each source file to most effectively
631
+ state the exclusion of warranty; and each file should have at least
632
+ the "copyright" line and a pointer to where the full notice is found.
633
+
634
+ <one line to give the program's name and a brief idea of what it does.>
635
+ Copyright (C) <year> <name of author>
636
+
637
+ This program is free software: you can redistribute it and/or modify
638
+ it under the terms of the GNU General Public License as published by
639
+ the Free Software Foundation, either version 3 of the License, or
640
+ (at your option) any later version.
641
+
642
+ This program is distributed in the hope that it will be useful,
643
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
644
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645
+ GNU General Public License for more details.
646
+
647
+ You should have received a copy of the GNU General Public License
648
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
649
+
650
+ Also add information on how to contact you by electronic and paper mail.
651
+
652
+ If the program does terminal interaction, make it output a short
653
+ notice like this when it starts in an interactive mode:
654
+
655
+ <program> Copyright (C) <year> <name of author>
656
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
+ This is free software, and you are welcome to redistribute it
658
+ under certain conditions; type `show c' for details.
659
+
660
+ The hypothetical commands `show w' and `show c' should show the appropriate
661
+ parts of the General Public License. Of course, your program's commands
662
+ might be different; for a GUI interface, you would use an "about box".
663
+
664
+ You should also get your employer (if you work as a programmer) or school,
665
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
666
+ For more information on this, and how to apply and follow the GNU GPL, see
667
+ <https://www.gnu.org/licenses/>.
668
+
669
+ The GNU General Public License does not permit incorporating your program
670
+ into proprietary programs. If your program is a subroutine library, you
671
+ may consider it more useful to permit linking proprietary applications with
672
+ the library. If this is what you want to do, use the GNU Lesser General
673
+ Public License instead of this License. But first, please read
674
+ <https://www.gnu.org/licenses/why-not-lgpl.html>.
assets/fvm.css ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* FVM 3 */
2
+ #fastvelocity-min{margin-top:10px}
3
+ #fastvelocity-min .clear{clear:both}
4
+ .fvm-settings label{font-size:15px;color:#222;padding-top:1px}
5
+ .fvm-settings span.note-info{font-size:15px;margin-left:8px;color:#666;font-style:italic}
6
+ .fvm-settings p.description{font-size:15px;color:#666;font-style:italic}
7
+ #tab-info h4{font-size:15px;margin-bottom:-10px}
8
+ .fvm-hide{max-height:1px;overflow:none;position:absolute;top:-9999px;left:-9999px;visibility:hidden}
9
+ .fvm-warning{font-weight:500;color:#A00}
10
+ h3.fvm-bold-green{font-weight:500;font-size:15px;color:#1196A3}
11
+ .fvm-bold-green{font-weight:500;color:#1196A3}
12
+ .fvm-label-special{line-height:38px}
13
+ .fvm-label-pad{line-height:21px}
14
+ .fvm-rowintro{padding-bottom:10px;font-size:15px}
15
+ table.fvm-settings td,table.fvm-settings td p,table.fvm-settings th{font-size:15px}
16
+ .fvm-wrapper{font-size:16px;padding:18px}
17
+ .fvm-wrapper h4{font-size:16px;margin:0;color:#1196A3}
18
+ .fvm-wrapper .accordion p,.fvm-wrapper .accordion code{font-size:16px;line-height:1.3}
19
+ .fvm-wrapper .accordion a, .fvm-wrapper .accordion a:hover { color: #0073aa; }
20
+ .fvm-code-full{width:100%;padding:8px;background:#F3F3F3;box-sizing:border-box;font-family:Consolas,Monaco,monospace;font-size:13px;line-height:1.5;margin-bottom:15px}
21
+ #status .fvm-cache-stats{margin:10px 0 0}
22
+ #status textarea.row-log{display:block;margin:0;padding:6px;border:none;background:#23282d;color:#ccc;height:300px;max-width:100%;overflow:auto;line-height:1.5;font-size:.8125rem;white-space:pre;font-family:monospace}
23
+ #status textarea.row-log::-webkit-scrollbar{width:8px;height:8px;background-color:#222}
24
+ #status textarea.row-log::-webkit-scrollbar-thumb{background-color:#000}
25
+ #status textarea.row-log::-webkit-scrollbar-track{background-color:#999}
26
+
27
+ /* jQuery UI */
28
+ .ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}
29
+ .ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}
30
+ .ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0;padding:.5em .5em .5em .7em;font-size:100%}
31
+ .ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}
32
+ .ui-widget{font-size:1em}
33
+ .ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}
34
+ .ui-widget-content a{color:#333}
35
+ .ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:400;color:#454545}
36
+ .ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:400;color:#2b2b2b}
37
+ .ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #bbb;background:#0073aa;font-weight:400;color:#fff}
38
+ .ui-icon{width:16px;height:16px}
39
+ .ui-icon,.ui-widget-content .ui-icon{background-image:url(images/ui-icons_444444_256x240.png)}
40
+ .ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url(images/ui-icons_555555_256x240.png)}
41
+ .ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url(images/ui-icons_ffffff_256x240.png)}
42
+ .ui-icon-triangle-1-e{background-position:-32px -16px}
43
+ .ui-icon-triangle-1-s{background-position:-65px -16px}
44
+ .ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}
45
+ .ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}
46
+ .ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}
47
+ .ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}
48
+ .ui-state-focus:focus{outline:none}
49
+
50
+ /* Mobile */
51
+ @media screen and (max-width:520px) {
52
+ .fvm-label-special { line-height: inherit; }
53
+ }
assets/fvm.js ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // get logs via ajax
2
+ function fvm_get_logs() {
3
+
4
+ // ajax request
5
+ jQuery( document ).ready(function() {
6
+ var data = { 'action': 'fvm_get_logs' };
7
+ jQuery.post(ajaxurl, data, function(resp) {
8
+ if(resp.success == 'OK') {
9
+
10
+ // cache stats
11
+ jQuery('.fvm-cache-stats').html("There are "+resp.stats_css.count+" CSS files and "+resp.stats_js.count+" JS files using a total of "+resp.stats_total.size+" on your cache directory");
12
+
13
+ // css log
14
+ jQuery('textarea.log-css').val(resp.css_log);
15
+ jQuery('textarea.log-css').scrollTop(0);
16
+
17
+ // js log
18
+ jQuery('textarea.log-js').val(resp.js_log);
19
+ jQuery('textarea.log-js').scrollTop(0);
20
+
21
+ } else {
22
+ // error log
23
+ console.error(resp.success);
24
+ }
25
+ });
26
+ });
27
+ }
28
+
29
+
30
+ jQuery( document ).ready(function() {
31
+
32
+ // help section
33
+ jQuery( ".accordion" ).accordion({ active: false, collapsible: true, heightStyle: "content" });
34
+
35
+ });
assets/images/ui-icons_444444_256x240.png ADDED
Binary file
assets/images/ui-icons_555555_256x240.png ADDED
Binary file
assets/images/ui-icons_ffffff_256x240.png ADDED
Binary file
fvm.css DELETED
@@ -1,33 +0,0 @@
1
- #fastvelocity-min{margin-top:10px;}
2
- #fastvelocity-min .processed{ min-height: 20px; margin: -4px 0 -2px 0; border: none !important; }
3
- #fastvelocity-min .processed li{margin:0; padding:10px 0; border: none !important; }
4
- #fastvelocity-min .processed > li:last-of-type { border: 0; }
5
- #fastvelocity-min .processed li .filename{width: 50%;float:left;font-family:monospace;display:block;word-wrap:break-word;line-height:30px;}
6
- #fastvelocity-min .processed li .accessed{display:block;}
7
- #fastvelocity-min .processed li .error{color:red}
8
- #fastvelocity-min .processed li .actions{display:block; padding:0; margin:0; text-align: right;}
9
- #fastvelocity-min .processed li a{margin-left:10px}
10
- #fastvelocity-min .processed pre{background:#EEE;max-height:300px;overflow:auto;display:none;margin-top:18px;margin-bottom:7px;padding:5px}
11
- #fastvelocity-min .clear { clear:both; }
12
- #fastvelocity-min #purgeall-row { padding-bottom: 2px; }
13
- .fvm-settings label { font-size: 14px; color: #222; padding-top: 1px; }
14
- .fvm-settings span.note-info { font-size: 14px; margin-left: 8px; color: #666; font-style: italic; }
15
- .fvm-settings p.description { font-size: 14px; color: #666; font-style: italic; font-size: 14px; }
16
- #tab-info h4{font-size: 14px; margin-bottom:-10px; }
17
- h3.hndle { background: #F7F7F7; color: #000; cursor: default !important; }
18
- .fvm-hide { max-height: 1px; overflow: none; position: absolute; top: -9999px; left: -9999px; visibility: hidden; }
19
- .fvm-warning { font-weight: 600; color: #AA0000; }
20
- .fvm-bold-green { font-weight: 600; color: #1196A3; }
21
- .fvm-label-special { line-height: 38px; }
22
- .fvm-label-pad { line-height: 21px; }
23
- .fvm-rowintro { padding-bottom: 10px; }
24
-
25
-
26
-
27
- @media screen and (max-width:520px) {
28
- #fastvelocity-min .processed li .filename{width: 100%;float:none;}
29
- #fastvelocity-min .processed li .actions{text-align: left;margin-top: 4px}
30
- #fastvelocity-min .processed li a{margin-left:0;margin-right:10px}
31
- #fastvelocity-min .processed pre{margin-top:12px;margin-bottom:5px;}
32
- .fvm-label-special { line-height: inherit; }
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fvm.js DELETED
@@ -1,59 +0,0 @@
1
- jQuery( document ).ready(function() {
2
-
3
- // disable collapse
4
- jQuery('.postbox h3, .postbox .handlediv').unbind('click.postboxes');
5
-
6
- // show logs
7
- jQuery('#fastvelocity_min_processed').on('click','.log',function(e){
8
- e.preventDefault();
9
- jQuery(this).parent().nextAll('pre').slideToggle();
10
- });
11
-
12
- function getFiles() {
13
- stamp = new Date().getTime();
14
- var data = { 'action': 'fastvelocity_min_files' };
15
-
16
- jQuery.post(ajaxurl, data, function(response) {
17
-
18
- if(response.cachesize.length > 0) {
19
- jQuery("#fvm_cache_size").html(response.cachesize);
20
- }
21
-
22
- // reset
23
- var fvmarr = [];
24
-
25
- // js
26
- if(response.js.length > 0) {
27
- jQuery(response.js).each(function(){
28
- fvmarr.push(this.uid);
29
- if(jQuery('#'+this.uid).length == 0) {
30
- jQuery('#fastvelocity_min_jsprocessed ul').append('<li id="'+this.uid+'"><span class="filename">'+this.filename+' ('+this.fsize+')</span> <span class="actions"><a href="#" class="log button button-primary">View Log</a></span><pre>'+this.log+'</pre></li><div class="clear"></div>');
31
- }
32
- });
33
- }
34
-
35
- // css
36
- if(response.css.length > 0) {
37
- jQuery(response.css).each(function(){
38
- fvmarr.push(this.uid);
39
- if(jQuery('#'+this.uid).length == 0) {
40
- jQuery('#fastvelocity_min_cssprocessed ul').append('<li id="'+this.uid+'"><span class="filename">'+this.filename+' ('+this.fsize+')</span> <span class="actions"><a href="#" class="log button button-primary">View Log</a></span><pre>'+this.log+'</pre></li><div class="clear"></div>');
41
- }
42
- });
43
- }
44
-
45
- // remove li, if not set (JS)
46
- jQuery('#fastvelocity_min_jsprocessed ul li, #fastvelocity_min_cssprocessed ul li').each(function(){
47
- if(jQuery.inArray(jQuery(this).attr('id'), fvmarr) == -1) {
48
- jQuery('#' + jQuery(this).attr('id')).remove();
49
- }
50
- });
51
-
52
- // check for new files
53
- timeout = setTimeout(getFiles, 4000);
54
- });
55
- }
56
-
57
- getFiles();
58
-
59
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fvm.php CHANGED
@@ -5,7 +5,7 @@ Plugin URI: http://fastvelocity.com
5
  Description: Improve your speed score on GTmetrix, Pingdom Tools and Google PageSpeed Insights by merging and minifying CSS and JavaScript files into groups, compressing HTML and other speed optimizations.
6
  Author: Raul Peixoto
7
  Author URI: http://fastvelocity.com
8
- Version: 2.8.9
9
  License: GPL2
10
 
11
  ------------------------------------------------------------------------
@@ -24,3012 +24,89 @@ along with this program; if not, write to the Free Software
24
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
  */
26
 
27
- # check for minimum requirements and prevent activation or disable if not fully compatible
28
- function fvm_compat_checker() {
29
- global $wp_version;
30
-
31
- # defaults
32
- $error = '';
33
-
34
- # php version requirements
35
- if (version_compare( PHP_VERSION, '5.5', '<' )) {
36
- $error = 'Fast Velocity Minify requires PHP 5.5 or higher. You’re still on '. PHP_VERSION;
37
- }
38
 
39
- # php extension requirements
40
- if (!extension_loaded('mbstring')) {
41
- $error = 'Fast Velocity Minify requires the PHP mbstring module to be installed on the server.';
42
- }
43
-
44
- # wp version requirements
45
- if ( version_compare( $GLOBALS['wp_version'], '4.5', '<' ) ) {
46
- $error = 'Fast Velocity Minify requires WP 4.5 or higher. You’re still on ' . $GLOBALS['wp_version'];
47
- }
48
-
49
- if ((is_plugin_active(plugin_basename( __FILE__ )) && !empty($error)) || !empty($error)) {
50
- if (isset($_GET['activate'])) { unset($_GET['activate']); }
51
- deactivate_plugins( plugin_basename( __FILE__ ));
52
- add_action('admin_notices', function() use ($error){
53
- echo '<div class="notice notice-error is-dismissible"><p><strong>'.$error.'</strong></p></div>';
54
- });
55
- }
56
  }
57
- add_action('admin_init', 'fvm_compat_checker');
58
 
 
 
 
 
 
 
 
 
59
 
60
- # get plugin version
61
- $fastvelocity_plugin_version_get_data = get_file_data(__FILE__, array('Version' => 'Version'), false);
62
- $fastvelocity_plugin_version = $fastvelocity_plugin_version_get_data['Version'];
63
-
64
- # get the plugin directory
65
- $plugindir = plugin_dir_path( __FILE__ ); # prints with trailing slash
66
-
67
- # reusable functions
68
- include($plugindir.'inc/functions.php');
69
- include($plugindir.'inc/functions-serverinfo.php');
70
- include($plugindir.'inc/functions-upgrade.php');
71
- include($plugindir.'inc/functions-cache.php');
72
 
73
  # wp-cli support
74
- if ( defined( 'WP_CLI' ) && WP_CLI ) {
75
- include($plugindir.'inc/functions-cli.php');
76
- }
77
-
78
-
79
- # get cache directories and urls
80
- $cachepath = fvm_cachepath();
81
- $tmpdir = $cachepath['tmpdir'];
82
- $cachedir = $cachepath['cachedir'];
83
- $cachedirurl = $cachepath['cachedirurl'];
84
-
85
- $wp_home = site_url(); # get the current wordpress installation url
86
- $wp_domain = trim(str_ireplace(array('http://', 'https://'), '', trim($wp_home, '/')));
87
- $wp_home_path = ABSPATH;
88
-
89
- # default globals
90
- $fastvelocity_min_global_js_done = array();
91
- $fvm_collect_google_fonts = array();
92
- $collect_preload_css = array();
93
- $collect_preload_js = array();
94
- $fvm_debug = get_option('fastvelocity_fvm_debug');
95
-
96
- ###########################################
97
- # build control panel pages ###############
98
- ###########################################
99
-
100
- # options from the database, false if not set
101
- $ignore = array_filter(array_map('trim', explode(PHP_EOL, get_option('fastvelocity_min_ignore', ''))));
102
- $blacklist = array_filter(array_map('trim', explode(PHP_EOL, get_option('fastvelocity_min_blacklist', ''))));
103
- $ignorelist = array_filter(array_map('trim', explode(PHP_EOL, get_option('fastvelocity_min_ignorelist', ''))));
104
- $fvm_min_excludecsslist = array_filter(array_map('trim', explode(PHP_EOL, get_option('fastvelocity_min_excludecsslist', ''))));
105
- $fvm_min_excludejslist = array_filter(array_map('trim', explode(PHP_EOL, get_option('fastvelocity_min_excludejslist', ''))));
106
-
107
- $fvm_enable_purgemenu = get_option('fastvelocity_min_enable_purgemenu');
108
- $default_protocol = get_option('fastvelocity_min_default_protocol', 'dynamic');
109
- $disable_js_merge = get_option('fastvelocity_min_disable_js_merge');
110
- $disable_css_merge = get_option('fastvelocity_min_disable_css_merge');
111
- $disable_js_minification = get_option('fastvelocity_min_disable_js_minification');
112
- $disable_css_minification = get_option('fastvelocity_min_disable_css_minification');
113
- $remove_print_mediatypes = get_option('fastvelocity_min_remove_print_mediatypes');
114
- $skip_html_minification = get_option('fastvelocity_min_skip_html_minification');
115
- $strip_htmlcomments = get_option('fastvelocity_min_strip_htmlcomments');
116
- $skip_cssorder = get_option('fastvelocity_min_skip_cssorder');
117
- $skip_google_fonts = get_option('fastvelocity_min_skip_google_fonts');
118
- $skip_emoji_removal = get_option('fastvelocity_min_skip_emoji_removal');
119
- $fvm_clean_header_one = get_option('fastvelocity_fvm_clean_header_one');
120
- $enable_defer_js = get_option('fastvelocity_min_enable_defer_js');
121
- $exclude_defer_jquery = get_option('fastvelocity_min_exclude_defer_jquery');
122
- $force_inline_css = get_option('fastvelocity_min_force_inline_css');
123
- $force_inline_css_footer = get_option('fastvelocity_min_force_inline_css_footer');
124
- $remove_googlefonts = get_option('fastvelocity_min_remove_googlefonts');
125
- $defer_for_pagespeed = get_option('fastvelocity_min_defer_for_pagespeed');
126
- $defer_for_pagespeed_optimize = get_option('fastvelocity_min_defer_for_pagespeed_optimize');
127
- $exclude_defer_login = get_option('fastvelocity_min_exclude_defer_login');
128
- $skip_defer_lists = get_option('fastvelocity_min_skip_defer_lists');
129
- $fvm_fix_editor = get_option('fastvelocity_min_fvm_fix_editor');
130
- $fvmloadcss = get_option('fastvelocity_min_loadcss');
131
- $fvm_remove_css = get_option('fastvelocity_min_fvm_removecss');
132
- $fvm_cdn_url = get_option('fastvelocity_min_fvm_cdn_url');
133
- $fvm_enabled_css_preload = get_option('fastvelocity_enabled_css_preload');
134
- $fvm_enabled_js_preload = get_option('fastvelocity_enabled_css_preload');
135
- $fvm_fawesome_method = get_option("fastvelocity_fontawesome_method");
136
-
137
- # default options
138
- $used_css_files = array();
139
- $force_inline_googlefonts = true;
140
- $min_async_googlefonts = false;
141
- $css_hide_googlefonts = false;
142
-
143
-
144
- # define google fonts options based on a radio form
145
- $fvm_gfonts_method = get_option("fastvelocity_gfonts_method");
146
- if($fvm_gfonts_method != false) {
147
- if($fvm_gfonts_method == 2) { # load Async
148
- $force_inline_googlefonts = false;
149
- $min_async_googlefonts = true;
150
- $css_hide_googlefonts = false;
151
- }
152
- if($fvm_gfonts_method == 3) { # hide from PSI
153
- $force_inline_googlefonts = false;
154
- $min_async_googlefonts = false;
155
- $css_hide_googlefonts = true;
156
- }
157
  }
158
 
 
 
159
 
160
- # default ua list
161
- $fvmualist = array('x11.*fox\/54', 'oid\s4.*xus.*ome\/62', 'oobot', 'ighth', 'tmetr', 'eadles', 'ingdo');
162
-
163
- # header and footer markers
164
- add_action('wp_head','fastvelocity_add_fvmuag', -PHP_INT_MAX);
165
- function fastvelocity_add_fvmuag() {
166
- global $fvmualist;
167
- if(!fastvelocity_exclude_contents() || fastvelocity_load_fvuag()) {
168
- echo '<script>'.fastvelocity_get_fvmuag($fvmualist).'</script>';
169
- }
170
- }
171
-
172
- # generate fvmuag js function
173
- function fastvelocity_get_fvmuag($fvmualist) {
174
- return 'function fvmuag(){if(navigator.userAgent.match(/'.implode('|', $fvmualist).'/i))return!1;if(navigator.userAgent.match(/x11.*ome\/75\.0\.3770\.100/i)){var e=screen.width,t=screen.height;if("number"==typeof e&&"number"==typeof t&&862==t&&1367==e)return!1}return!0}';
175
- }
176
 
 
 
177
 
178
 
179
- # add admin page and rewrite defaults
180
  if(is_admin()) {
181
- add_action('admin_menu', 'fastvelocity_min_admin_menu');
182
- add_action('admin_enqueue_scripts', 'fastvelocity_min_load_admin_jscss');
183
- add_action('wp_ajax_fastvelocity_min_files', 'fastvelocity_min_files_callback');
184
- add_action('admin_init', 'fastvelocity_min_register_settings');
185
-
186
- # This function runs when WordPress updates or installs/remove something
187
- add_action('upgrader_process_complete', 'fastvelocity_purge_all_global');
188
- add_action('after_switch_theme', 'fastvelocity_purge_all_global');
189
- add_action('admin_init', 'fastvelocity_purge_onsave', 1);
190
-
191
- # activation, deactivation
192
- register_activation_hook( __FILE__, 'fastvelocity_plugin_activate' );
193
- register_deactivation_hook( __FILE__, 'fastvelocity_plugin_deactivate');
194
- register_uninstall_hook( __FILE__, 'fastvelocity_plugin_uninstall');
195
 
196
- } else {
197
- add_action('setup_theme', 'fastvelocity_process_frontend' );
198
- }
199
 
200
- function fastvelocity_process_frontend() {
201
- global $disable_js_merge, $disable_css_merge, $force_inline_css, $skip_emoji_removal, $fvm_clean_header_one, $skip_html_minification, $fvmloadcss, $fvm_fawesome_method, $fvm_gfonts_method;
202
-
203
- # skip on certain post_types or if there are specific keys on the url or if editor or admin
204
- if(!fastvelocity_exclude_contents()) {
205
-
206
- # actions for frontend only
207
- if(!$disable_js_merge) {
208
- add_action( 'wp_print_scripts', 'fastvelocity_min_merge_header_scripts', PHP_INT_MAX );
209
- add_action( 'wp_print_footer_scripts', 'fastvelocity_min_merge_footer_scripts', 9.999999 );
210
- }
211
- if(!$disable_css_merge) {
212
- add_action('wp_head', 'fvm_add_criticial_path', 2);
213
-
214
- # merge, if inline is not selected
215
- if($force_inline_css != true) {
216
- add_action('wp_print_styles', 'fastvelocity_min_merge_header_css', PHP_INT_MAX );
217
- add_action('wp_print_footer_scripts', 'fastvelocity_min_merge_footer_css', 9.999999 );
218
- } else {
219
- add_filter('style_loader_tag', 'fastvelocity_optimizecss', PHP_INT_MAX, 4 );
220
- add_action('wp_print_styles','fastvelocity_add_google_fonts_merged', PHP_INT_MAX);
221
- add_action('wp_print_footer_scripts','fastvelocity_add_google_fonts_merged', PHP_INT_MAX );
222
- }
223
-
224
- }
225
- if(!$skip_emoji_removal) {
226
- add_action( 'init', 'fastvelocity_min_disable_wp_emojicons' );
227
- add_filter( 'tiny_mce_plugins', 'fastvelocity_disable_emojis_tinymce' );
228
- }
229
-
230
- if($fvm_clean_header_one) {
231
- # no resource hints, generator tag, shortlinks, manifest link, etc
232
- remove_action('wp_head', 'wp_resource_hints', 2);
233
- remove_action('wp_head', 'wp_generator');
234
- remove_action('template_redirect', 'wp_shortlink_header', 11);
235
- remove_action('wp_head', 'wlwmanifest_link');
236
- remove_action('wp_head', 'rsd_link');
237
- remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0);
238
- remove_action('wp_head','feed_links', 2);
239
- remove_action('wp_head','feed_links_extra', 3);
240
- add_filter('after_setup_theme', 'fastvelocity_remove_redundant_shortlink');
241
- }
242
-
243
- # enable html minification
244
- if(!$skip_html_minification && !is_admin()) {
245
- add_action('template_redirect', 'fastvelocity_min_html_compression_start', PHP_INT_MAX);
246
- }
247
-
248
- # add the LoadCSS polyfil
249
- if($fvmloadcss || $fvm_fawesome_method == 2 || $fvm_gfonts_method == 2) {
250
- add_action('wp_footer', 'fvm_add_loadcss', PHP_INT_MAX);
251
- }
252
 
253
- # add the LoadAsync JavaScript function
254
- add_action('wp_head', 'fvm_add_loadasync', 0);
 
 
 
 
 
 
255
 
256
- # remove query from static assets and process defering (if enabled)
257
- add_filter('style_loader_src', 'fastvelocity_remove_cssjs_ver', 10, 2);
258
- add_filter('script_loader_tag', 'fastvelocity_min_defer_js', 10, 3);
259
-
260
- # headers
261
- add_action( 'send_headers', 'fvm_extra_preload_headers' );
262
- add_action( 'wp_footer', 'fastvelocity_generate_preload_headers', PHP_INT_MAX);
263
- add_filter( 'script_loader_tag', 'fastvelocity_collect_js_preload_headers', PHP_INT_MAX, 3 );
264
-
265
- }
266
- }
267
-
268
- # exclude processing for editors and administrators (fix editors)
269
- add_action( 'plugins_loaded', 'fastvelocity_fix_editor' );
270
- function fastvelocity_fix_editor() {
271
- global $fvm_fix_editor;
272
- if($fvm_fix_editor == true && is_user_logged_in()) {
273
- remove_action('setup_theme', 'fastvelocity_process_frontend' );
274
- }
275
- }
276
-
277
- # create admin menu
278
- function fastvelocity_min_admin_menu() {
279
- add_options_page('Fast Velocity Minify Settings', 'Fast Velocity Minify', 'manage_options', 'fastvelocity-min', 'fastvelocity_min_settings');
280
- }
281
-
282
-
283
- # add admin toolbar
284
- if($fvm_enable_purgemenu == true) {
285
- add_action( 'admin_bar_menu', 'fastvelocity_admintoolbar', 100 );
286
- }
287
-
288
- # admin toolbar processing
289
- function fastvelocity_admintoolbar() {
290
- if(current_user_can('manage_options')) {
291
- global $wp_admin_bar;
292
-
293
- # Create or add new items into the Admin Toolbar.
294
- $wp_admin_bar->add_node(array(
295
- 'id' => 'fvm',
296
- 'title' => '<span class="ab-icon"></span><span class="ab-label">' . __("FVM Purge",'fvm') . '</span>',
297
- 'href' => wp_nonce_url( add_query_arg('_fvmcache', 'clear'), 'fvm_clear_nonce')
298
- ));
299
-
300
- }
301
- }
302
-
303
-
304
- # function to list all cache files
305
- function fastvelocity_min_files_callback() {
306
-
307
- # must be able to cleanup cache
308
- if (!current_user_can('manage_options')) {
309
- wp_die( __('You do not have sufficient permissions to access this page.'));
310
- }
311
 
312
- global $cachedir;
313
-
314
- # default
315
- $size = fastvelocity_get_cachestats();
316
- $return = array('js' => array(), 'css' => array(), 'cachesize'=> $size);
317
-
318
- # inspect directory with opendir, since glob might not be available in some systems
319
- clearstatcache();
320
- if ($handle = opendir($cachedir.fastvelocity_get_os_slash())) {
321
- while (false !== ($file = readdir($handle))) {
322
- $file = $cachedir.fastvelocity_get_os_slash().$file;
323
- $ext = pathinfo($file, PATHINFO_EXTENSION);
324
- if (in_array($ext, array('js', 'css'))) {
325
- $log = ''; if (file_exists($file.'.txt')) { $log = file_get_contents($file.'.txt'); }
326
- $mincss = substr($file, 0, -4).'.min.css';
327
- $minjs = substr($file, 0, -3).'.min.js';
328
- $filename = basename($file);
329
- if ($ext == 'css' && file_exists($mincss)) { $filename = basename($mincss); }
330
- if ($ext == 'js' && file_exists($minjs)) { $filename = basename($minjs); }
331
- $fsize = fastvelocity_format_filesize(filesize($file));
332
- $uid = hash('sha1', $filename);
333
- array_push($return[$ext], array('uid'=>$uid, 'filename' => $filename, 'log' => $log, 'fsize' => $fsize));
334
- }
335
- }
336
- closedir($handle);
337
- }
338
-
339
- header('Content-Type: application/json');
340
- echo json_encode($return);
341
- wp_die();
342
  }
343
 
344
 
345
- # load wp-admin css and js files
346
- function fastvelocity_min_load_admin_jscss($hook) {
347
- if ('settings_page_fastvelocity-min' != $hook) { return; }
348
- wp_enqueue_script('postbox');
349
- wp_enqueue_style('fastvelocity-min', plugins_url('fvm.css', __FILE__), array(), filemtime(plugin_dir_path( __FILE__).'fvm.css'));
350
- wp_enqueue_script('fastvelocity-min', plugins_url('fvm.js', __FILE__), array('jquery'), filemtime(plugin_dir_path( __FILE__).'fvm.js'), true);
351
- }
352
-
353
 
354
- # register plugin settings
355
- function fastvelocity_min_register_settings() {
356
- register_setting('fvm-group', 'fastvelocity_min_enable_purgemenu');
357
- register_setting('fvm-group', 'fastvelocity_preserve_settings_on_uninstall');
358
- register_setting('fvm-group', 'fastvelocity_min_default_protocol');
359
- register_setting('fvm-group', 'fastvelocity_min_disable_js_merge');
360
- register_setting('fvm-group', 'fastvelocity_min_disable_css_merge');
361
- register_setting('fvm-group', 'fastvelocity_min_disable_js_minification');
362
- register_setting('fvm-group', 'fastvelocity_min_disable_css_minification');
363
- register_setting('fvm-group', 'fastvelocity_min_remove_print_mediatypes');
364
- register_setting('fvm-group', 'fastvelocity_min_skip_html_minification');
365
- register_setting('fvm-group', 'fastvelocity_min_strip_htmlcomments');
366
- register_setting('fvm-group', 'fastvelocity_min_skip_cssorder');
367
- register_setting('fvm-group', 'fastvelocity_min_skip_google_fonts');
368
- register_setting('fvm-group', 'fastvelocity_min_skip_fontawesome_fonts');
369
- register_setting('fvm-group', 'fastvelocity_min_skip_emoji_removal');
370
- register_setting('fvm-group', 'fastvelocity_fvm_clean_header_one');
371
- register_setting('fvm-group', 'fastvelocity_min_enable_defer_js');
372
- register_setting('fvm-group', 'fastvelocity_min_exclude_defer_jquery');
373
- register_setting('fvm-group', 'fastvelocity_min_force_inline_css');
374
- register_setting('fvm-group', 'fastvelocity_min_force_inline_css_footer');
375
- register_setting('fvm-group', 'fastvelocity_min_remove_googlefonts');
376
- register_setting('fvm-group', 'fastvelocity_gfonts_method');
377
- register_setting('fvm-group', 'fastvelocity_fontawesome_method');
378
- register_setting('fvm-group', 'fastvelocity_min_defer_for_pagespeed');
379
- register_setting('fvm-group', 'fastvelocity_min_defer_for_pagespeed_optimize');
380
- register_setting('fvm-group', 'fastvelocity_min_exclude_defer_login');
381
- register_setting('fvm-group', 'fastvelocity_min_skip_defer_lists');
382
- register_setting('fvm-group', 'fastvelocity_min_fvm_fix_editor');
383
- register_setting('fvm-group', 'fastvelocity_min_fvm_cdn_url');
384
- register_setting('fvm-group', 'fastvelocity_min_fvm_cdn_force');
385
- register_setting('fvm-group', 'fastvelocity_min_change_cache_base_url');
386
- register_setting('fvm-group', 'fastvelocity_min_change_cache_path');
387
-
388
- # pro tab
389
- register_setting('fvm-group-pro', 'fastvelocity_min_ignore');
390
- register_setting('fvm-group-pro', 'fastvelocity_min_ignorelist');
391
- register_setting('fvm-group-pro', 'fastvelocity_min_excludecsslist');
392
- register_setting('fvm-group-pro', 'fastvelocity_min_excludejslist');
393
- register_setting('fvm-group-pro', 'fastvelocity_min_blacklist');
394
- register_setting('fvm-group-pro', 'fastvelocity_min_merge_allowed_urls');
395
-
396
- # dev tab
397
- register_setting('fvm-group-dev', 'fastvelocity_fvm_debug');
398
- register_setting('fvm-group-dev', 'fastvelocity_enabled_css_preload');
399
- register_setting('fvm-group-dev', 'fastvelocity_enabled_js_preload');
400
- register_setting('fvm-group-dev', 'fastvelocity_min_hpreload');
401
- register_setting('fvm-group-dev', 'fastvelocity_min_hpreconnect');
402
- register_setting('fvm-group-dev', 'fastvelocity_min_loadcss');
403
- register_setting('fvm-group-dev', 'fastvelocity_min_fvm_removecss');
404
- register_setting('fvm-group-dev', 'fastvelocity_min_critical_path_css');
405
- register_setting('fvm-group-dev', 'fastvelocity_min_critical_path_css_is_front_page');
406
 
 
 
407
 
 
 
 
408
 
409
- }
410
-
411
-
412
-
413
- # add settings link on plugin page
414
- function fastvelocity_min_settings_link($links) {
415
- if (is_plugin_active(plugin_basename( __FILE__ ))) {
416
- $settings_link = '<a href="options-general.php?page=fastvelocity-min&tab=set">Settings</a>';
417
- array_unshift($links, $settings_link);
418
- }
419
- return $links;
420
- }
421
- add_filter("plugin_action_links_".plugin_basename(__FILE__), 'fastvelocity_min_settings_link' );
422
-
423
-
424
- # purge all caches by request
425
- add_action('init','fastvelocity_process_cache_purge_request');
426
- function fastvelocity_process_cache_purge_request(){
427
- if((isset($_POST['purgeall']) && $_POST['purgeall'] == 1) || isset($_GET['_fvmcache'])) {
428
-
429
- # must be able to cleanup cache
430
- if (!current_user_can('manage_options')) { wp_die( __('You do not have sufficient permissions to access this page.')); }
431
 
432
- # validate nonce
433
- if(empty($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'fvm_clear_nonce')) {
434
- wp_die( __('Invalid or expired request... please go back and refresh before trying again!'));
435
- }
436
-
437
- if(is_admin()) {
438
- fvm_purge_all(); # purge all
439
- $others = fvm_purge_others(); # purge third party caches
440
- $notice = array('All caches from <strong>FVM</strong> have been purged!', strip_tags($others, '<strong>'));
441
- $notice = array_filter($notice);
442
- $notice = json_encode($notice); # encode
443
- set_transient( 'wordpress_fvmcache', $notice, 10);
444
- wp_safe_redirect(remove_query_arg('_wpnonce', remove_query_arg('_fvmcache', wp_get_referer())));
445
- } else {
446
- fvm_purge_all(); # purge all
447
- fvm_purge_others(); # purge third party caches
448
- wp_safe_redirect(remove_query_arg('_wpnonce', remove_query_arg('_fvmcache', wp_get_referer())));
449
- }
450
- }
451
- }
452
-
453
- # print admin notices after purging caches, if on wp-admin
454
- add_action( 'admin_notices', 'fastvelocity_cachepurge_admin_notices' );
455
- function fastvelocity_cachepurge_admin_notices() {
456
 
457
- # skip on submit
458
- if((isset($_POST['purgeall']) && $_POST['purgeall'] == 1) || isset($_GET['_fvmcache'])) {
459
- return true;
460
- }
461
-
462
- # cache purge notices
463
- $inf = get_transient('wordpress_fvmcache');
464
- if($inf != false && !empty($inf)) {
465
-
466
- # decode to array or null
467
- $jsonarr = json_decode($inf, true);
468
- if(!is_null($jsonarr) && is_array($jsonarr)){
469
-
470
- # print notices
471
- foreach ($jsonarr as $notice) {
472
- echo __('<div class="notice notice-success is-dismissible"><p>'.$notice.'</p></div>');
473
- }
474
- }
475
-
476
- # remove
477
- delete_transient('wordpress_fvmcache');
478
- }
479
- }
480
-
481
-
482
- # print admin notices if we don't have enough file permissions to write
483
- add_action( 'admin_notices', 'fastvelocity_check_permissions_admin_notices' );
484
- function fastvelocity_check_permissions_admin_notices() {
485
-
486
- # get cache path
487
- $cachepath = fvm_cachepath();
488
- $cachebase = $cachepath['cachebase'];
489
- if(is_dir($cachebase) && !is_writable($cachebase)) {
490
- $chmod = substr(sprintf('%o', fileperms($cachebase)), -4);
491
- echo __('<div class="notice notice-error is-dismissible"><p>FVM needs writting permissions on '.$cachebase.'</p></div>');
492
- echo __('<div class="notice notice-error is-dismissible"><p>The current permissions for FVM are chmod '.$chmod.'</p></div>');
493
- echo __('<div class="notice notice-error is-dismissible"><p>If you need something higher than 755 for it to work, your server is probaly misconfigured. Please contact your hosting provider or check the help section for other providers.</p></div>');
494
- }
495
-
496
  }
497
 
498
-
499
- # manage settings page
500
- function fastvelocity_min_settings() {
501
- if (!current_user_can('manage_options')) { wp_die( __('You do not have sufficient permissions to access this page.')); }
502
-
503
- # tmp folder
504
- global $tmpdir, $cachedir, $plugindir;
505
-
506
- # get active tab, set default
507
- $active_tab = isset($_GET['tab']) ? $_GET['tab'] : 'status';
508
-
509
- ?>
510
- <div class="wrap">
511
- <h1>Fast Velocity Minify</h1>
512
-
513
- <h2 class="nav-tab-wrapper wp-clearfix">
514
- <a href="?page=fastvelocity-min&tab=status" class="nav-tab <?php echo $active_tab == 'status' ? 'nav-tab-active' : ''; ?>">Status</a>
515
- <a href="?page=fastvelocity-min&tab=set" class="nav-tab <?php echo $active_tab == 'set' ? 'nav-tab-active' : ''; ?>">Settings</a>
516
- <a href="?page=fastvelocity-min&tab=pro" class="nav-tab <?php echo $active_tab == 'pro' ? 'nav-tab-active' : ''; ?>">Pro</a>
517
- <a href="?page=fastvelocity-min&tab=dev" class="nav-tab <?php echo $active_tab == 'dev' ? 'nav-tab-active' : ''; ?>">Developers</a>
518
- <a href="?page=fastvelocity-min&tab=server" class="nav-tab <?php echo $active_tab == 'server' ? 'nav-tab-active' : ''; ?>">Server Info</a>
519
- <a href="?page=fastvelocity-min&tab=help" class="nav-tab <?php echo $active_tab == 'help' ? 'nav-tab-active' : ''; ?>">Help</a>
520
- </h2>
521
-
522
-
523
- <?php if( $active_tab == 'status' ) { ?>
524
-
525
- <div id="fastvelocity-min">
526
- <div id="poststuff">
527
- <div id="fastvelocity_min_processed" class="postbox-container">
528
- <div class="meta-box">
529
-
530
- <div class="postbox" id="tab-purge">
531
- <h3 class="hndle"><span>Purge the cache files </span></h3>
532
- <div class="inside" id="fastvelocity_min_topbtns">
533
- <ul class="processed">
534
- <li id="purgeall-row">
535
- <span class="filename">Purge FVM cache (<span id="fvm_cache_size"><?php echo fastvelocity_get_cachestats(); ?></span>)</span>
536
- <span class="actions">
537
- <form method="post" id="fastvelocity_min_clearall" action="<?php echo wp_nonce_url( add_query_arg('_fvmcache', 'clear'), 'fvm_clear_nonce'); ?>">
538
- <input type="hidden" name="purgeall" value="1" />
539
- <?php submit_button('Delete', 'button-secondary', 'submit', false); ?>
540
- </form>
541
- </li>
542
- </ul>
543
- </div>
544
- </div>
545
-
546
- <div class="postbox" id="tab-js">
547
- <h3 class="hndle"><span>List of processed JS files</span></h3>
548
- <div class="inside" id="fastvelocity_min_jsprocessed">
549
- <ul class="processed"></ul>
550
- </div>
551
- </div>
552
-
553
- <div class="postbox" id="tab-css">
554
- <h3 class="hndle"><span>List of processed CSS files</span></h3>
555
- <div class="inside" id="fastvelocity_min_cssprocessed">
556
- <?php
557
- $force_inline_css = get_option('fastvelocity_min_force_inline_css');
558
- if($force_inline_css != false) {
559
- echo '<p>There are no merged CSS files listed here, because you are inlining all CSS directly.</p>';
560
- } else {
561
- echo '<ul class="processed"></ul>';
562
- }
563
- ?>
564
-
565
- </div>
566
- </div>
567
-
568
- </div>
569
- </div>
570
- </div>
571
- </div>
572
- <?php } ?>
573
-
574
- <?php if( $active_tab == 'set' ) { ?>
575
- <form method="post" action="options.php">
576
- <?php settings_fields('fvm-group'); do_settings_sections('fvm-group'); ?>
577
-
578
-
579
- <div style="height: 20px;"></div>
580
- <h2 class="title">Basic Settings</h2>
581
- <p class="fvm-bold-green">These options are generaly safe to edit as needed. If you use a cache plugin, kindly purge all your caches once you're done with the changes.</p>
582
-
583
- <table class="form-table fvm-settings">
584
- <tbody>
585
-
586
-
587
- <tr>
588
- <th scope="row">Functionality</th>
589
- <td>
590
- <p class="fvm-bold-green fvm-rowintro">The HTML minification is ON by default, but you can:</p>
591
-
592
- <fieldset>
593
- <label for="fastvelocity_min_enable_purgemenu">
594
- <input name="fastvelocity_min_enable_purgemenu" type="checkbox" id="fastvelocity_min_enable_purgemenu" value="1" <?php echo checked(1 == get_option('fastvelocity_min_enable_purgemenu'), true, false); ?>>
595
- Admin Bar Purge <span class="note-info">[ If selected, a new option to purge FVM cache from the admin bar will show up ]</span></label>
596
- <br />
597
-
598
- <label for="fastvelocity_preserve_settings_on_uninstall">
599
- <input name="fastvelocity_preserve_settings_on_uninstall" type="checkbox" id="fastvelocity_preserve_settings_on_uninstall" value="1" <?php echo checked(1 == get_option('fastvelocity_preserve_settings_on_uninstall'), true, false); ?>>
600
- Preserve Settings<span class="note-info">[ If selected, all FVM settings will be preserved, even if you uninstall the plugin ]</span></label>
601
- <br />
602
-
603
- <label for="fastvelocity_min_fvm_fix_editor">
604
- <input name="fastvelocity_min_fvm_fix_editor" type="checkbox" id="fastvelocity_min_fvm_fix_editor" value="1" <?php echo checked(1 == get_option('fastvelocity_min_fvm_fix_editor'), true, false); ?>>
605
- Fix Page Editors <span class="note-info">[ Will disable merging of JS and CSS for logged in users, to improve compatibility with visual editors ]</span></label>
606
- <br />
607
-
608
- </fieldset></td>
609
- </tr>
610
-
611
-
612
- <tr>
613
- <th scope="row">URL Options</th>
614
- <td>
615
- <?php
616
- # what to select
617
- $sel = get_option('fastvelocity_min_default_protocol');
618
- $a = ''; if($sel == 'dynamic' || empty($sel)) { $a = ' checked="checked"'; }
619
- $b = ''; if($sel == 'http') { $b = ' checked="checked"'; }
620
- $c = ''; if($sel == 'https') { $c = ' checked="checked"'; }
621
- ?>
622
- <p class="fvm-bold-green fvm-rowintro">You may need to force http or https, for some CDN plugins to work:</p>
623
- <fieldset>
624
- <label><input type="radio" name="fastvelocity_min_default_protocol" value="dynamic" <?php echo $a; ?>> Auto Detect </label><br>
625
- <label><input type="radio" name="fastvelocity_min_default_protocol" value="http"<?php echo $b; ?>> Force HTTP urls (if you don't have SSL)</label><br>
626
- <label><input type="radio" name="fastvelocity_min_default_protocol" value="https"<?php echo $c; ?>> Force HTTPS urls (recommended if you have SSL)</span></label><br>
627
- </fieldset>
628
- </td>
629
- </tr>
630
-
631
- <tr>
632
- <th scope="row">HTML Options</th>
633
- <td>
634
- <p class="fvm-bold-green fvm-rowintro">The HTML minification is ON by default, but you can:</p>
635
-
636
- <fieldset>
637
- <label for="fastvelocity_min_skip_html_minification">
638
- <input name="fastvelocity_min_skip_html_minification" type="checkbox" id="fastvelocity_min_skip_html_minification" value="1" <?php echo checked(1 == get_option('fastvelocity_min_skip_html_minification'), true, false); ?>>
639
- Disable HTML Minification <span class="note-info">[ This will disable HTML minification ]</span></label>
640
- <br />
641
-
642
- <label for="fastvelocity_min_strip_htmlcomments">
643
- <input name="fastvelocity_min_strip_htmlcomments" type="checkbox" id="fastvelocity_min_strip_htmlcomments" value="1" <?php echo checked(1 == get_option('fastvelocity_min_strip_htmlcomments'), true, false); ?>>
644
- Strip HTML comments <span class="note-info">[ Only works with the default HTML minification, but note that some plugins need HTML comments to work properly ]</span></label>
645
- <br />
646
-
647
- <label for="fastvelocity_fvm_clean_header_one">
648
- <input name="fastvelocity_fvm_clean_header_one" type="checkbox" id="fastvelocity_fvm_clean_header_one" value="1" <?php echo checked(1 == get_option('fastvelocity_fvm_clean_header_one'), true, false); ?>>
649
- Cleanup Header <span class="note-info">[ Remove resource hints, generator tag, shortlinks, manifest link, etc ]</span></label>
650
- <br />
651
-
652
- </fieldset></td>
653
- </tr>
654
-
655
-
656
- <tr>
657
- <th scope="row">Font Options</th>
658
- <td>
659
- <p class="fvm-bold-green fvm-rowintro">The default options are usually good for performance.</p>
660
- <fieldset>
661
- <label for="fastvelocity_min_skip_emoji_removal">
662
- <input name="fastvelocity_min_skip_emoji_removal" type="checkbox" id="fastvelocity_min_skip_emoji_removal" class="jsprocessor" value="1" <?php echo checked(1 == get_option('fastvelocity_min_skip_emoji_removal'), true, false); ?> >
663
- Stop removing Emojis and smileys <span class="note-info">[ If selected, Emojis will be left alone and won't be removed from wordpress ]</span></label>
664
- <br />
665
-
666
- <label for="fastvelocity_min_skip_google_fonts">
667
- <input name="fastvelocity_min_skip_google_fonts" type="checkbox" id="fastvelocity_min_skip_google_fonts" value="1" <?php echo checked(1 == get_option('fastvelocity_min_skip_google_fonts'), true, false); ?> >
668
- Disable Google Fonts merging <span class="note-info">[ If selected, Google Fonts will no longer be merged into one request ]</span></label>
669
- <br />
670
-
671
- <label for="fastvelocity_min_remove_googlefonts">
672
- <input name="fastvelocity_min_remove_googlefonts" type="checkbox" id="fastvelocity_min_remove_googlefonts" value="1" <?php echo checked(1 == get_option('fastvelocity_min_remove_googlefonts'), true, false); ?> >
673
- Remove Google Fonts completely <span class="note-info">[ If selected, all enqueued Google Fonts will be removed from the site ]</span></label>
674
- <br />
675
-
676
- </fieldset></td>
677
- </tr>
678
-
679
-
680
- <tr>
681
- <th scope="row">Google Fonts</th>
682
- <td>
683
- <?php
684
- # what to select
685
- $sel = get_option('fastvelocity_gfonts_method');
686
- $a = ''; if($sel == 1 || empty($sel)) { $a = ' checked="checked"'; }
687
- $b = ''; if($sel == 2) { $b = ' checked="checked"'; }
688
- $c = ''; if($sel == 3) { $c = ' checked="checked"'; }
689
- ?>
690
- <p class="fvm-bold-green fvm-rowintro">Choose how to include Google Fonts on your pages, when available:</p>
691
- <fieldset>
692
- <label><input type="radio" name="fastvelocity_gfonts_method" value="1" <?php echo $a; ?>> Inline Google Fonts CSS</label> <span class="note-info">[ Will inline the <a target="_blank" href="https://caniuse.com/#feat=woff">woof</a> format only (with font hinting) ]</span><br>
693
- <label><input type="radio" name="fastvelocity_gfonts_method" value="2"<?php echo $b; ?>> Async Google Fonts CSS files</label> <span class="note-info">[ Will use <a target="_blank" href="https://caniuse.com/#feat=link-rel-preload">preload</a> with <a href="https://github.com/filamentgroup/loadCSS">LoadCSS</a> polyfill ]</span><br>
694
- <label><input type="radio" name="fastvelocity_gfonts_method" value="3"<?php echo $c; ?>> Async and exclude Google Fonts CSS from PSI</label> <span class="note-info">[ Will use JavaScript to load the fonts conditionally ] </span><br>
695
- </fieldset>
696
- </td>
697
- </tr>
698
-
699
- <tr>
700
- <th scope="row">Font Awesome</th>
701
- <td>
702
- <?php
703
- # what to select
704
- $sel = get_option('fastvelocity_fontawesome_method');
705
- $a = ''; if($sel == 1 || empty($sel)) { $a = ' checked="checked"'; }
706
- $b = ''; if($sel == 2) { $b = ' checked="checked"'; }
707
- $c = ''; if($sel == 3) { $c = ' checked="checked"'; }
708
- ?>
709
- <p class="fvm-bold-green fvm-rowintro">Only if available and if it has "font-awesome" in the url:</p>
710
- <fieldset>
711
- <label><input type="radio" name="fastvelocity_fontawesome_method" value="1" <?php echo $a; ?>> Merge or Inline Font Awesome CSS</label> <span class="note-info">[ Depends on if you have the Inline CSS option enabled or not ]</span><br>
712
- <label><input type="radio" name="fastvelocity_fontawesome_method" value="2"<?php echo $b; ?>> Async Font Awesome CSS file</label> <span class="note-info">[ Will use <a target="_blank" href="https://caniuse.com/#feat=link-rel-preload">preload</a> with <a href="https://github.com/filamentgroup/loadCSS">LoadCSS</a> polyfill ]</span><br>
713
- <label><input type="radio" name="fastvelocity_fontawesome_method" value="3"<?php echo $c; ?>> Async and exclude Font Awesome CSS from PSI</label> <span class="note-info">[ Will use JavaScript to load the fonts conditionally ] </span><br>
714
- </fieldset>
715
- </td>
716
- </tr>
717
-
718
-
719
- <tr>
720
- <th scope="row">CSS Options</th>
721
- <td>
722
- <p class="fvm-bold-green fvm-rowintro">It's recommended that you Inline all CSS files, if they are small enough.</p>
723
-
724
- <fieldset>
725
- <label for="fastvelocity_min_disable_css_merge">
726
- <input name="fastvelocity_min_disable_css_merge" type="checkbox" id="fastvelocity_min_disable_css_merge" value="1" <?php echo checked(1 == get_option('fastvelocity_min_disable_css_merge'), true, false); ?>>
727
- Disable CSS processing<span class="note-info">[ If selected, this plugin will ignore CSS files completely ]</span></label>
728
- <br />
729
- <label for="fastvelocity_min_disable_css_minification">
730
- <input name="fastvelocity_min_disable_css_minification" type="checkbox" id="fastvelocity_min_disable_css_minification" value="1" <?php echo checked(1 == get_option('fastvelocity_min_disable_css_minification'), true, false); ?>>
731
- Disable minification on CSS files <span class="note-info">[ If selected, CSS files will be merged but not minified ]</span></label>
732
- <br />
733
- <label for="fastvelocity_min_skip_cssorder">
734
- <input name="fastvelocity_min_skip_cssorder" type="checkbox" id="fastvelocity_min_skip_cssorder" value="1" <?php echo checked(1 == get_option('fastvelocity_min_skip_cssorder'), true, false); ?> >
735
- Preserve the order of CSS files <span class="note-info">[ If selected, you will have better CSS compatibility when merging but possibly more CSS files ]</span></label>
736
- <br />
737
- <label for="fastvelocity_min_remove_print_mediatypes">
738
- <input name="fastvelocity_min_remove_print_mediatypes" type="checkbox" id="fastvelocity_min_remove_print_mediatypes" value="1" <?php echo checked(1 == get_option('fastvelocity_min_remove_print_mediatypes'), true, false); ?> >
739
- Disable the "Print" related stylesheets <span class="note-info">[ If selected, CSS files of mediatype "print" will be removed from the site ]</span></label>
740
- <br />
741
- <label for="fastvelocity_min_force_inline_css_footer">
742
- <input name="fastvelocity_min_force_inline_css_footer" type="checkbox" id="fastvelocity_min_force_inline_css_footer" value="1" <?php echo checked(1 == get_option('fastvelocity_min_force_inline_css_footer'), true, false); ?>>
743
- Inline CSS in the footer <span class="note-info">[ If selected, any FVM generated CSS files in the footer, will be inlined ]</span></label>
744
- <br />
745
- <label for="fastvelocity_min_force_inline_css">
746
- <input name="fastvelocity_min_force_inline_css" type="checkbox" id="fastvelocity_min_force_inline_css" value="1" <?php echo checked(1 == get_option('fastvelocity_min_force_inline_css'), true, false); ?>>
747
- Inline CSS both in the header and footer <span class="note-info">[ If selected, any FVM generated CSS files (header + footer) will be inlined ]</span></label>
748
- <br />
749
- </fieldset></td>
750
- </tr>
751
-
752
-
753
- <tr>
754
- <th scope="row">JavaScript Options</th>
755
- <td>
756
- <p class="fvm-bold-green fvm-rowintro">Try to disable minification (and purge the cache) first, if you have trouble with JavaScript in the frontend.</p>
757
- <fieldset>
758
- <label for="fastvelocity_min_disable_js_merge">
759
- <input name="fastvelocity_min_disable_js_merge" type="checkbox" id="fastvelocity_min_disable_js_merge" value="1" <?php echo checked(1 == get_option('fastvelocity_min_disable_js_merge'), true, false); ?> >
760
- Disable JavaScript processing <span class="note-info">[ If selected, this plugin will ignore JS files completely ]</span></label>
761
- <br />
762
-
763
- <label for="fastvelocity_min_disable_js_minification">
764
- <input name="fastvelocity_min_disable_js_minification" type="checkbox" id="fastvelocity_min_disable_js_minification" value="1" <?php echo checked(1 == get_option('fastvelocity_min_disable_js_minification'), true, false); ?> >
765
- Disable minification on JS files <span class="note-info">[ If selected, JS files will be merged but not minified ]</span></label>
766
- <br />
767
- </fieldset></td>
768
- </tr>
769
-
770
- <tr>
771
- <th scope="row">Render-blocking JS</th>
772
- <td>
773
- <fieldset><legend class="screen-reader-text"><span>Render-blocking</span></legend>
774
-
775
- <p class="fvm-bold-green fvm-rowintro">Some themes and plugins "need" render blocking scripts to work, so please take a look at the dev console for errors.</p>
776
- <label for="fastvelocity_min_enable_defer_js">
777
- <input name="fastvelocity_min_enable_defer_js" type="checkbox" id="fastvelocity_min_enable_defer_js" value="1" <?php echo checked(1 == get_option('fastvelocity_min_enable_defer_js'), true, false); ?>>
778
- Enable defer parsing of FVM JS files globally <span class="note-info">[ Not all browsers, themes or plugins support this. Beware of broken functionality and design ]</span></label>
779
- <br />
780
-
781
- <label for="fastvelocity_min_exclude_defer_jquery">
782
- <input name="fastvelocity_min_exclude_defer_jquery" type="checkbox" id="fastvelocity_min_exclude_defer_jquery" value="1" <?php echo checked(1 == get_option('fastvelocity_min_exclude_defer_jquery'), true, false); ?> >
783
- Skip deferring the jQuery library <span class="note-info">[ Will probably fix "undefined jQuery" errors on the Google Chrome console log ]</span></label>
784
- <br />
785
- <label for="fastvelocity_min_exclude_defer_login">
786
- <input name="fastvelocity_min_exclude_defer_login" type="checkbox" id="fastvelocity_min_exclude_defer_login" value="1" <?php echo checked(1 == get_option('fastvelocity_min_exclude_defer_login'), true, false); ?> >
787
- Skip deferring JS on the login page <span class="note-info">[ If selected, will disable JS deferring on your login page ]</span></label>
788
- <br />
789
- <label for="fastvelocity_min_skip_defer_lists">
790
- <input name="fastvelocity_min_skip_defer_lists" type="checkbox" id="fastvelocity_min_skip_defer_lists" value="1" <?php echo checked(1 == get_option('fastvelocity_min_skip_defer_lists'), true, false); ?> >
791
- Skip deferring the ignore list <span class="note-info">[ If selected, files on the blacklist, ignore list, etc, won't be deferred ]</span></label>
792
- <br />
793
-
794
- </fieldset></td>
795
- </tr>
796
-
797
- <tr>
798
- <th scope="row">PageSpeed Settings</th>
799
- <td>
800
- <p class="fvm-bold-green fvm-rowintro">Note that this will overwrite any other behaviour defined above and "may" cause errors.</p>
801
- <fieldset>
802
- <label for="fastvelocity_min_defer_for_pagespeed">
803
- <input name="fastvelocity_min_defer_for_pagespeed" type="checkbox" id="fastvelocity_min_defer_for_pagespeed" value="1" <?php echo checked(1 == get_option('fastvelocity_min_defer_for_pagespeed'), true, false); ?>>
804
- Enable defer of all JS files for PSI only <span class="note-info">[ Will use JavaScript to defer all JS files for PSI ]</span></label>
805
-
806
- <br />
807
- <label for="fastvelocity_min_defer_for_pagespeed_optimize">
808
- <input name="fastvelocity_min_defer_for_pagespeed_optimize" type="checkbox" id="fastvelocity_min_defer_for_pagespeed_optimize" value="1" <?php echo checked(1 == get_option('fastvelocity_min_defer_for_pagespeed_optimize'), true, false); ?>>
809
- Exclude JS files in the "ignore list" from PSI <span class="note-info">[ This will hide the "ignored files" from PSI instead of simply deferring ]</span></label>
810
-
811
- </fieldset></td>
812
- </tr>
813
-
814
- </tbody></table>
815
-
816
-
817
- <div style="height: 20px;"></div>
818
- <h2 class="title">CDN Options</h2>
819
- <p class="fvm-bold-green">When the "Enable defer of JS for Pagespeed Insights" option is enabled, JS and CSS files will not be loaded from the CDN due to <a target="_blank" href="https://www.chromestatus.com/feature/5718547946799104">compatibility</a> reasons.<br />However, you can define a CDN Domain below, in order to use it for all of the static assets "inside" your CSS and JS files.</p>
820
-
821
- <table class="form-table fvm-settings">
822
- <tbody>
823
- <tr>
824
- <th scope="row"><span class="fvm-label-special">Your CDN domain</span></th>
825
- <td><fieldset>
826
- <label for="fastvelocity_min_fvm_cdn_url">
827
- <p><input type="text" name="fastvelocity_min_fvm_cdn_url" id="fastvelocity_min_fvm_cdn_url" value="<?php echo get_option('fastvelocity_min_fvm_cdn_url', ''); ?>" size="80" /></p>
828
- <p class="description">[ Will rewrite the static assets urls inside FVM merged files to your cdn domain. Usage: cdn.example.com ]</p></label>
829
- </fieldset>
830
- </td>
831
- </tr>
832
-
833
- <tr>
834
- <th scope="row">Force the CDN Usage</th>
835
- <td>
836
- <p class="fvm-bold-green fvm-rowintro">If you force this, your JS files may not load for certain slow internet users on Google Chrome.</p>
837
- <fieldset>
838
- <label for="fastvelocity_min_fvm_cdn_force">
839
- <input name="fastvelocity_min_fvm_cdn_force" type="checkbox" id="fastvelocity_min_fvm_cdn_force" value="1" <?php echo checked(1 == get_option('fastvelocity_min_fvm_cdn_force'), true, false); ?>>
840
- I know what I'm doing... <span class="note-info">[ Load my JS files from the CDN, even when "defer for Pagespeed Insights" is enabled ]</span></label>
841
- </fieldset></td>
842
- </tr>
843
-
844
- </tbody></table>
845
-
846
- <div style="height: 20px;"></div>
847
- <h2 class="title">Cache Location</h2>
848
- <p class="fvm-bold-green">If your server blocks JavaScript on the uploads directory, you can change "wp-content/uploads" with "wp-content/cache" or other allowed public directory.</p>
849
- <table class="form-table fvm-settings">
850
- <tbody>
851
- <tr>
852
- <th scope="row"><span class="fvm-label-special">Cache Path</span></th>
853
- <td><fieldset>
854
- <label for="fastvelocity_min_change_cache_path">
855
- <p><input type="text" name="fastvelocity_min_change_cache_path" id="fastvelocity_min_change_cache_path" value="<?php echo get_option('fastvelocity_min_change_cache_path', ''); ?>" size="80" /></p>
856
- <p class="description">[ Default cache path is: <?php echo rtrim(wp_upload_dir()['basedir'], '/'); ?> ]</p>
857
- </label>
858
- <br />
859
- <label for="fastvelocity_min_change_cache_base_url">
860
- <p><input type="text" name="fastvelocity_min_change_cache_base_url" id="fastvelocity_min_change_cache_base_url" value="<?php echo get_option('fastvelocity_min_change_cache_base_url', ''); ?>" size="80" /></p>
861
- <p class="description">[ Default cache base url is: <?php echo rtrim(fvm_get_protocol(wp_upload_dir()['baseurl']), '/'); ?> ]</p>
862
- </label>
863
- </fieldset></td>
864
- </tr>
865
- </tbody></table>
866
-
867
-
868
- <p class="submit"><input type="submit" name="fastvelocity_min_save_options" id="fastvelocity_min_save_options" class="button button-primary" value="Save Changes"></p>
869
- </form>
870
- <?php } ?>
871
-
872
-
873
- <?php if( $active_tab == 'pro' ) { ?>
874
-
875
- <form method="post" action="options.php">
876
- <?php settings_fields('fvm-group-pro'); do_settings_sections('fvm-group-pro'); ?>
877
-
878
-
879
- <div style="height: 20px;"></div>
880
- <h2 class="title">Special JS and CSS Exceptions</h2>
881
- <p class="fvm-bold-green">You can use this section to edit or change our default exclusions, as well as to add your own.<br />Make sure you understand the difference between Defer and Async.</p>
882
- <p class="fvm-bold-green">When you use an option here that uses "Async", styles and scripts load "out of order" and completely independent from the others. That means, the files that end up loading later, will overwrite any previously loaded code. On the other hand, when you use the "ignore list" or when you select an option to "defer", the order of scripts and styles is preserved and should not overwrite previously loaded code, unless there is an higher specificy somewhere else.</p>
883
-
884
- <div style="height: 20px;"></div>
885
- <table class="form-table fvm-settings">
886
- <tbody>
887
- <tr>
888
- <th scope="row">Ignore List</th>
889
- <td><fieldset>
890
- <label for="blacklist_keys"><span class="fvm-label-pad">Ignore the following CSS and JS paths below:</span></label>
891
- <p>
892
- <textarea name="fastvelocity_min_ignore" rows="7" cols="50" id="fastvelocity_min_ignore" class="large-text code" placeholder="ex: /wp-includes/js/jquery/jquery.js"><?php echo get_option('fastvelocity_min_ignore'); ?></textarea>
893
- </p>
894
- <p class="description">[ Your own list of js /css files to ignore with wildcard support (read the faqs) ]</p>
895
- </fieldset></td>
896
- </tr>
897
- </tbody></table>
898
-
899
-
900
- <div style="height: 20px;"></div>
901
- <table class="form-table fvm-settings">
902
- <tbody>
903
- <tr>
904
- <th scope="row">External URLs to Merge</th>
905
- <td><fieldset><label for="blacklist_keys"><span class="fvm-label-pad">List of external domains that can be fetched and merged together:</span></label>
906
- <p>
907
- <textarea name="fastvelocity_min_merge_allowed_urls" rows="7" cols="50" id="fastvelocity_min_merge_allowed_urls" class="large-text code" placeholder="ex: example.com"><?php echo get_option('fastvelocity_min_merge_allowed_urls'); ?></textarea>
908
- </p>
909
- <p class="description">[ Add any external "domains" for JS or CSS files than can be merged fetched and merged together by FVM, ie: cdnjs.cloudflare.com ]</p>
910
- </fieldset></td>
911
- </tr>
912
- </tbody></table>
913
-
914
-
915
- <div style="height: 20px;"></div>
916
- <table class="form-table fvm-settings">
917
- <tbody>
918
- <tr>
919
- <th scope="row">Exclude JS files from PSI</th>
920
- <td><fieldset><label for="fastvelocity_min_excludejslist"><span class="fvm-label-pad">Files will be loaded Async and excluded from PSI:</span></label>
921
- <p>
922
- <textarea name="fastvelocity_min_excludejslist" rows="7" cols="50" id="fastvelocity_min_excludejslist" class="large-text code" placeholder="ex: /pixelyoursite/js/public.js"><?php echo get_option('fastvelocity_min_excludejslist'); ?></textarea>
923
- </p>
924
- <p class="description">[ Any JS file that can load Async and completely independent, such as analytics or pixel scripts ]</p>
925
-
926
- </fieldset></td>
927
- </tr>
928
- </tbody></table>
929
-
930
-
931
- <div style="height: 20px;"></div>
932
- <table class="form-table fvm-settings">
933
- <tbody>
934
- <tr>
935
- <th scope="row">Exclude CSS files from PSI</th>
936
- <td><fieldset><label for="fastvelocity_min_excludecsslist"><span class="fvm-label-pad">Files will be loaded Async and excluded from PSI:</span></label>
937
- <p>
938
- <textarea name="fastvelocity_min_excludecsslist" rows="7" cols="50" id="fastvelocity_min_excludecsslist" class="large-text code" placeholder="ex: /wp-content/themes/my-theme/css/some-other-font.min.css"><?php echo get_option('fastvelocity_min_excludecsslist'); ?></textarea>
939
- </p>
940
- <p class="description">[ Any CSS file that can load completely independent, such as fontawesome or other icons ]</p>
941
-
942
- </fieldset></td>
943
- </tr>
944
- </tbody></table>
945
-
946
-
947
-
948
-
949
- <div style="height: 20px;"></div>
950
- <table class="form-table fvm-settings">
951
- <tbody>
952
- <tr>
953
- <th scope="row">Default Ignore List</th>
954
- <td>
955
- <fieldset><label for="blacklist_keys"><span class="fvm-label-pad">Do not edit, if you're not sure what this is:</span></label>
956
- <p>
957
- <textarea name="fastvelocity_min_ignorelist" rows="7" cols="50" id="fastvelocity_min_ignorelist" class="large-text code" placeholder="ex: /wp-includes/js/jquery/jquery.js"><?php echo get_option('fastvelocity_min_ignorelist'); ?></textarea>
958
- </p>
959
- <p class="description">[ Files that have been consistently reported by other users to cause trouble when merged ]</p>
960
- </fieldset></td>
961
- </tr>
962
- </tbody></table>
963
-
964
- <div style="height: 20px;"></div>
965
- <table class="form-table fvm-settings">
966
- <tbody>
967
- <tr>
968
- <th scope="row">Default Blacklist</th>
969
- <td><fieldset><label for="blacklist_keys"><span class="fvm-label-pad">Do not edit, if you're not sure what this is:</span></label>
970
- <p>
971
- <textarea name="fastvelocity_min_blacklist" rows="7" cols="50" id="fastvelocity_min_blacklist" class="large-text code" placeholder="ex: /wp-includes/js/jquery/jquery.js"><?php echo get_option('fastvelocity_min_blacklist'); ?></textarea>
972
- </p>
973
- <p class="description">[ Usually, any IE css /js files that should always be ignored without incrementing the groups ]</p>
974
- </fieldset></td>
975
- </tr>
976
- </tbody></table>
977
-
978
-
979
- <p class="submit"><input type="submit" name="fastvelocity_min_save_options" id="fastvelocity_min_save_options" class="button button-primary" value="Save Changes"></p>
980
- </form>
981
-
982
- <?php
983
- }
984
-
985
- # start developers tab
986
- if( $active_tab == 'dev' ) { ?>
987
-
988
- <form method="post" action="options.php">
989
- <?php settings_fields('fvm-group-dev'); do_settings_sections('fvm-group-dev'); ?>
990
-
991
- <div style="height: 20px;"></div>
992
- <h2 class="title">Development</h2>
993
- <p class="fvm-bold-green">This are handy things for the plugin author, but may be of use to you if you are looking to debug some issue.</p>
994
- <p class="fvm-bold-green">Please note that the automatic headers, are only available after the first, uncached pageview (you may need to purge your cache to see them, or your server may not support this at all).</p>
995
-
996
- <table class="form-table fvm-settings">
997
- <tbody>
998
- <tr>
999
- <th scope="row">Dev Options</th>
1000
- <td><fieldset>
1001
- <label for="fastvelocity_fvm_debug">
1002
- <input name="fastvelocity_fvm_debug" type="checkbox" id="fastvelocity_fvm_debug" value="1" <?php echo checked(1 == get_option('fastvelocity_fvm_debug'), true, false); ?>>
1003
- Enable FVM Debug Mode<span class="note-info">[ Add extra info to the "status page" logs as well as some comments on the HTML frontend (beta) ]</span></label>
1004
-
1005
- <br />
1006
- <label for="fastvelocity_enabled_css_preload">
1007
- <input name="fastvelocity_enabled_css_preload" type="checkbox" id="fastvelocity_enabled_css_preload" value="1" <?php echo checked(1 == get_option('fastvelocity_enabled_css_preload'), true, false); ?>>
1008
- Enable FVM CSS files Preload<span class="note-info">[ Automatically create http headers for FVM generated CSS files (when not inlined) ]</span></label>
1009
-
1010
- <br />
1011
- <label for="fastvelocity_enabled_js_preload">
1012
- <input name="fastvelocity_enabled_js_preload" type="checkbox" id="fastvelocity_enabled_js_preload" value="1" <?php echo checked(1 == get_option('fastvelocity_enabled_js_preload'), true, false); ?>>
1013
- Enable FVM JS files Preload<span class="note-info">[ Automatically create http headers for FVM generated JS files ]</span></label>
1014
-
1015
-
1016
- </fieldset>
1017
- </td>
1018
- </tr>
1019
- </tbody></table>
1020
-
1021
-
1022
-
1023
-
1024
-
1025
-
1026
- <div style="height: 20px;"></div>
1027
- <h2 class="title">HTTP Headers</h2>
1028
- <p class="fvm-bold-green">Preconnect Headers: This will add link headers to your http response to instruct the browser to preconnect to other domains (ex: fonts, images, videos, etc)</p>
1029
- <p class="fvm-bold-green">Preload Headers: Use this for preloading specific, high priority resources that exist across all of your pages.</p>
1030
- <p class="fvm-bold-green">Note: Some servers do not support http push or headers. If you get a server error: a) rename the plugin directory via SFTP or your hosting control panel, b) go to your plugins page (plugin will be disabled on access), c) rename it back and d) activate it back (reset to default settings).</p>
1031
-
1032
- <table class="form-table fvm-settings">
1033
- <tbody>
1034
- <tr>
1035
- <th scope="row">Preconnect Headers</th>
1036
- <td><fieldset><legend class="screen-reader-text"><span>Preconnect</span></legend>
1037
- <label for="fastvelocity_min_hpreconnect"><span class="fvm-label-pad">Use only the strictly minimum necessary domain names, (cdn or frequent embeds):</span></label>
1038
- <p>
1039
- <textarea name="fastvelocity_min_hpreconnect" rows="7" cols="50" id="fastvelocity_min_hpreconnect" class="large-text code" placeholder="https://cdn.example.com"><?php echo get_option('fastvelocity_min_hpreconnect'); ?></textarea>
1040
- </p>
1041
- <p class="description">[ Use the complete scheme (http:// or https://) followed by the domain name only (no file paths). ]</p>
1042
- <p class="description">[ Examples: ]</p>
1043
- <p class="description">https://fonts.googleapis.com</p>
1044
- <p class="description">https://fonts.gstatic.com</p>
1045
- </fieldset></td>
1046
- </tr>
1047
-
1048
- </tbody></table>
1049
-
1050
- <table class="form-table fvm-settings">
1051
- <tbody>
1052
- <tr>
1053
- <th scope="row">Preload Headers</th>
1054
- <td><fieldset><legend class="screen-reader-text"><span>Preload Headers</span></legend>
1055
- <label for="fastvelocity_min_hpreload"><span class="fvm-label-pad">Insert your "complete php header code" here:</span></label>
1056
- <p>
1057
- <textarea name="fastvelocity_min_hpreload" rows="7" cols="50" id="fastvelocity_min_hpreload" class="large-text code" placeholder="Link: &lt;https://cdn.example.com/s/font/v15/somefile.woff&gt;; rel=preload; as=font; crossorigin"><?php echo get_option('fastvelocity_min_hpreload'); ?></textarea>
1058
- </p>
1059
- <p class="description">[ Example of a "complete php header code" to paste above ]</p>
1060
- <p class="description">Link: &lt;https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFVZ0d.woff&gt;; rel=preload; as=font; crossorigin</p>
1061
- <p class="description">Link: &lt;https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2&gt;; rel=preload; as=font; crossorigin</p>
1062
- </fieldset></td>
1063
- </tr>
1064
- </tbody></table>
1065
-
1066
-
1067
- <div style="height: 20px;"></div>
1068
- <h2 class="title">Async CSS</h2>
1069
- <p class="fvm-bold-green">If you have multiple css files per media type, they may load out of order and break your design.<br />These options won't work, if you select "Disable CSS Processing" on the settings page. </p>
1070
-
1071
- <table class="form-table fvm-settings">
1072
- <tbody>
1073
- <tr>
1074
- <th scope="row">Enable Async CSS</th>
1075
- <td><fieldset>
1076
- <label for="fastvelocity_min_loadcss">
1077
- <input name="fastvelocity_min_loadcss" type="checkbox" id="fastvelocity_min_loadcss" value="1" <?php echo checked(1 == get_option('fastvelocity_min_loadcss'), true, false); ?>>
1078
- Async CSS with LoadCSS <span class="note-info">[ Note that inline CSS won't work if this is active ]</span></label>
1079
-
1080
- <br />
1081
- <label for="fastvelocity_min_fvm_removecss">
1082
- <input name="fastvelocity_min_fvm_removecss" type="checkbox" id="fastvelocity_min_fvm_removecss" value="1" <?php echo checked(1 == get_option('fastvelocity_min_fvm_removecss'), true, false); ?>>
1083
- Dequeue all CSS files <span class="note-info">[ Use this if you want to test how your Critical Path CSS looks like ]</span></label>
1084
-
1085
- </fieldset>
1086
- </td>
1087
- </tr>
1088
-
1089
- </tbody></table>
1090
-
1091
-
1092
- <div style="height: 20px;"></div>
1093
- <h2 class="title">Critical Path CSS</h2>
1094
- <p class="fvm-bold-green">Files that are inlined, or end up loading later (note: async css files load out of order) will overwritte existing styles, which can cause your design to break.</p>
1095
- <p class="fvm-bold-green">It's probably better to (uncss) remove all unused CSS on your site, select the "Dequeue all CSS files" and paste your code on the "Fallback CSS" section.</p>
1096
- <p class="fvm-bold-green">All code posted here will be inline early in the header, regardless of the "Async CSS with LoadCSS" being active or not (so you can use this to inline your extra css code without other plugins).</p>
1097
-
1098
- <table class="form-table fvm-settings">
1099
- <tbody>
1100
-
1101
- <tr>
1102
- <th scope="row">Fallback CSS</th>
1103
- <td>
1104
- <fieldset>
1105
- <p>
1106
- <textarea name="fastvelocity_min_critical_path_css" rows="7" cols="50" id="fastvelocity_min_critical_path_css" class="large-text code" placeholder="your css code here"><?php echo get_option('fastvelocity_min_critical_path_css'); ?></textarea>
1107
- </p>
1108
- <p class="description">[ It will be overwritten, if some other more specific critical path code exists below ]</p>
1109
- </fieldset>
1110
- </td>
1111
- </tr>
1112
-
1113
- <tr>
1114
- <th scope="row">is_front_page (conditional)</th>
1115
- <td>
1116
- <fieldset>
1117
- <p>
1118
- <textarea name="fastvelocity_min_critical_path_css_is_front_page" rows="7" cols="50" id="fastvelocity_min_critical_path_css_is_front_page" class="large-text code" placeholder="your css code here"><?php echo get_option('fastvelocity_min_critical_path_css_is_front_page'); ?></textarea>
1119
- </p>
1120
- <p class="description">[ Will show up if your page matches the WP conditional, is_front_page() ]</p>
1121
- </fieldset>
1122
- </td>
1123
- </tr>
1124
-
1125
- </tbody></table>
1126
-
1127
-
1128
-
1129
- <p class="submit"><input type="submit" name="fastvelocity_min_save_options" id="fastvelocity_min_save_options" class="button button-primary" value="Save Changes"></p>
1130
- </form>
1131
-
1132
- <?php
1133
- }
1134
-
1135
-
1136
- # start server info tab
1137
- if( $active_tab == 'server' ) {
1138
- fvm_get_generalinfo();
1139
- }
1140
-
1141
-
1142
- # start help tab
1143
- if( $active_tab == 'help' ) { ?>
1144
-
1145
- <div class="wrap" id="fastvelocity-min">
1146
- <div id="poststuff">
1147
- <div id="fastvelocity_min_processed" class="postbox-container">
1148
- <div class="meta-box-sortables ui-sortable">
1149
-
1150
- <div class="postbox" id="tab-info">
1151
- <h3 class="hndle"><span>Paid Clients / Custom Requests</span></h3>
1152
- <div class="inside">
1153
- <p>Please Visit: <a href="https://www.upwork.com/fl/fvmpeixoto">https://www.upwork.com/fl/fvmpeixoto</a></p>
1154
- <p>Alternatively: <a href="https://fastvelocity.com/">https://fastvelocity.com/</a></p>
1155
- </div>
1156
- </div>
1157
-
1158
- <div class="postbox" id="tab-info">
1159
- <h3 class="hndle"><span>Frequently Asked Questions</span></h3>
1160
- <div class="inside">
1161
- <p>Please Visit: <a href="https://wordpress.org/plugins/fast-velocity-minify/#faq">https://wordpress.org/plugins/fast-velocity-minify/#faq</a></p>
1162
- </div>
1163
- </div>
1164
-
1165
- <div class="postbox" id="tab-info">
1166
- <h3 class="hndle"><span>Open Source Support / Bug Report</span></h3>
1167
- <div class="inside">
1168
- <p>Please Visit: <a href="https://wordpress.org/support/plugin/fast-velocity-minify">https://wordpress.org/support/plugin/fast-velocity-minify</a></p>
1169
- </div>
1170
- </div>
1171
-
1172
- <div class="postbox" id="tab-info">
1173
- <h3 class="hndle"><span>Need faster hosting?</span></h3>
1174
- <div class="inside">
1175
- <p>Digital Ocean: (aff) <a href="https://m.do.co/c/039860472caf">https://www.digitalocean.com/</a></p>
1176
- <p>Vultr: (aff) <a href="https://www.vultr.com/?ref=6879450">https://www.vultr.com/</a></p>
1177
- <p>Linode: (aff) <a href="https://www.linode.com/?r=4b0ae524a0e54b1c11abb8014be4068f5a5d607a">https://www.linode.com/</a></p>
1178
- <p>Amazon Lightsail: <a href="https://aws.amazon.com/lightsail/">https://aws.amazon.com/lightsail/</a></p>
1179
- <p>Google Cloud: <a href="https://cloud.google.com/">https://cloud.google.com/</a></p>
1180
- </div>
1181
- </div>
1182
-
1183
- <div class="postbox" id="tab-info">
1184
- <h3 class="hndle"><span>Donations (Thank You)</span></h3>
1185
- <div class="inside">
1186
- <p>PayPal: <a href="https://goo.gl/vpLrSV">https://goo.gl/vpLrSV</a><br /></p>
1187
- </div>
1188
- </div>
1189
-
1190
- </div>
1191
- </div>
1192
- </div>
1193
- </div>
1194
-
1195
- <?php } ?>
1196
-
1197
-
1198
-
1199
- </div>
1200
-
1201
- <div class="clear"></div>
1202
-
1203
- <?php
1204
- }
1205
-
1206
-
1207
- ###########################################
1208
- # process header javascript ###############
1209
- ###########################################
1210
- function fastvelocity_min_merge_header_scripts() {
1211
- global $wp_scripts, $wp_domain, $wp_home, $wp_home_path, $cachedir, $cachedirurl, $ignore, $disable_js_merge, $disable_js_minification, $enable_defer_js, $exclude_defer_jquery, $fvm_debug, $fvm_min_excludejslist, $fvmualist;
1212
- if(!is_object($wp_scripts)) { return false; }
1213
- $scripts = wp_clone($wp_scripts);
1214
- $scripts->all_deps($scripts->queue);
1215
- $ctime = get_option('fvm-last-cache-update', '0');
1216
- $header = array();
1217
-
1218
- # mark as done (as we go)
1219
- $done = $scripts->done;
1220
-
1221
- # add defaults to ignore list
1222
- $ignore = fastvelocity_default_ignore($ignore);
1223
-
1224
- # get groups of handles
1225
- foreach( $scripts->to_do as $handle ) :
1226
-
1227
- # is it a footer script?
1228
- $is_footer = 0;
1229
- if (isset($wp_scripts->registered[$handle]->extra["group"]) || isset($wp_scripts->registered[$handle]->args)) {
1230
- $is_footer = 1;
1231
- }
1232
-
1233
- # skip footer scripts for now
1234
- if($is_footer != 1) {
1235
-
1236
- # get full url
1237
- $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
1238
-
1239
- # inlined scripts without file
1240
- if( empty($hurl)) {
1241
- continue;
1242
- }
1243
-
1244
- # Exclude JS files from PSI (Async) takes priority over the ignore list
1245
- if($fvm_min_excludejslist != false || is_array($fvm_min_excludejslist)) {
1246
-
1247
- # check for string match
1248
- $skipjs = false;
1249
- foreach($fvm_min_excludejslist as $l) {
1250
- if (stripos($hurl, $l) !== false) {
1251
- # print code if there are no linebreaks, or return
1252
- echo '<script type="text/javascript">if(fvmuag()){';
1253
- echo "loadAsync('$hurl', null);";
1254
- echo '}</script>';
1255
- $skipjs = true;
1256
- break;
1257
- }
1258
- }
1259
- if($skipjs != false) { continue; }
1260
- }
1261
-
1262
-
1263
- # IE only files don't increment things
1264
- $ieonly = fastvelocity_ie_blacklist($hurl);
1265
- if($ieonly == true) { continue; }
1266
-
1267
- # skip ignore list, scripts with conditionals, external scripts
1268
- if ((!fastvelocity_min_in_arrayi($hurl, $ignore) && !isset($wp_scripts->registered[$handle]->extra["conditional"]) && fvm_internal_url($hurl, $wp_home)) || empty($hurl)) {
1269
-
1270
- # process
1271
- if(isset($header[count($header)-1]['handle']) || count($header) == 0) {
1272
- array_push($header, array('handles'=>array()));
1273
- }
1274
-
1275
- # push it to the array
1276
- array_push($header[count($header)-1]['handles'], $handle);
1277
-
1278
- # external and ignored scripts
1279
- } else {
1280
- array_push($header, array('handle'=>$handle));
1281
- }
1282
-
1283
- # make sure that the scripts skipped here, show up in the footer
1284
- } else {
1285
- $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
1286
-
1287
- # inlined scripts without file
1288
- if( empty($hurl)) {
1289
- wp_enqueue_script($handle, false);
1290
- } else {
1291
- wp_enqueue_script($handle, $hurl, array(), null, true);
1292
- }
1293
- }
1294
- endforeach;
1295
-
1296
- # loop through header scripts and merge
1297
- for($i=0,$l=count($header);$i<$l;$i++) {
1298
- if(!isset($header[$i]['handle'])) {
1299
-
1300
- # static cache file info + done
1301
- $done = array_merge($done, $header[$i]['handles']);
1302
- $hash = 'header-'.hash('sha1',implode('',$header[$i]['handles']));
1303
-
1304
- # create cache files and urls
1305
- $file = $cachedir.fastvelocity_get_os_slash().$hash.'.min.js';
1306
- $file_url = fvm_get_protocol($cachedirurl.'/'.$hash.'.min.js');
1307
-
1308
- # generate a new cache file
1309
- clearstatcache();
1310
- if (!file_exists($file)) {
1311
-
1312
- # code and log initialization
1313
- $log = '';
1314
- $code = '';
1315
-
1316
- # minify and write to file
1317
- foreach($header[$i]['handles'] as $handle) :
1318
- if(!empty($wp_scripts->registered[$handle]->src)) {
1319
-
1320
- # get hurl per handle
1321
- $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
1322
-
1323
- # inlined scripts without file
1324
- if( empty($hurl)) {
1325
- continue;
1326
- }
1327
-
1328
- # print url
1329
- $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
1330
-
1331
- # download, minify, cache
1332
- $tkey = 'js-'.hash('sha1', $handle.$hurl).'.js';
1333
- $json = false; $json = fvm_get_transient($tkey);
1334
- if ( $json === false) {
1335
- $json = fvm_download_and_minify($hurl, null, $disable_js_minification, 'js', $handle);
1336
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Uncached file processing now for $handle / $hurl -->" . PHP_EOL; }
1337
- fvm_set_transient($tkey, $json);
1338
- }
1339
-
1340
- # decode
1341
- $res = json_decode($json, true);
1342
-
1343
- # response has failed
1344
- if($res['status'] != true) {
1345
- $log.= $res['log'];
1346
- continue;
1347
- }
1348
-
1349
- # Add extra data from wp_add_inline_script before
1350
- if (!empty($wp_scripts->registered[$handle]->extra)){
1351
- if (isset($wp_scripts->registered[$handle]->extra['before'])){
1352
- if(is_array($wp_scripts->registered[$handle]->extra['before'])) {
1353
- $arr = array_filter($wp_scripts->registered[$handle]->extra['before']);
1354
- $code.= PHP_EOL . fastvelocity_try_catch_wrap(implode(PHP_EOL, $arr));
1355
- }
1356
- }
1357
- }
1358
-
1359
- # append code to merged file
1360
- $code.= "/* $hurl */". PHP_EOL . fastvelocity_try_catch_wrap($res['code']);
1361
- $log.= $res['log'];
1362
-
1363
- # Add extra data from wp_add_inline_script after
1364
- if (!empty($wp_scripts->registered[$handle]->extra)){
1365
- if (isset($wp_scripts->registered[$handle]->extra['after'])){
1366
- if(is_array($wp_scripts->registered[$handle]->extra['after'])) {
1367
- $arr = array_filter($wp_scripts->registered[$handle]->extra['after']);
1368
- $code.= PHP_EOL . fastvelocity_try_catch_wrap(implode(PHP_EOL, $arr));
1369
- }
1370
- }
1371
- }
1372
-
1373
- # consider dependencies on handles with an empty src
1374
- } else {
1375
- wp_dequeue_script($handle); wp_enqueue_script($handle);
1376
- }
1377
- endforeach;
1378
-
1379
- # prepare log
1380
- $log = "PROCESSED on ".date('r').PHP_EOL.$log."PROCESSED from ".home_url(add_query_arg( NULL, NULL )).PHP_EOL;
1381
-
1382
- # generate cache, write log
1383
- if(!empty($code)) {
1384
- file_put_contents($file.'.txt', $log);
1385
- file_put_contents($file, $code);
1386
- file_put_contents($file.'.gz', gzencode(file_get_contents($file), 9));
1387
-
1388
- # permissions
1389
- fastvelocity_fix_permission_bits($file.'.txt');
1390
- fastvelocity_fix_permission_bits($file);
1391
- fastvelocity_fix_permission_bits($file.'.gz');
1392
-
1393
- # brotli static support
1394
- if(function_exists('brotli_compress')) {
1395
- file_put_contents($file.'.br', brotli_compress(file_get_contents($file), 11));
1396
- fastvelocity_fix_permission_bits($file.'.br');
1397
- }
1398
- }
1399
- }
1400
-
1401
- # register minified file
1402
- wp_register_script("fvm-header-$i", $file_url, array(), null, false);
1403
-
1404
- # add all extra data from wp_localize_script
1405
- $data = array();
1406
- foreach($header[$i]['handles'] as $handle) {
1407
- if(isset($wp_scripts->registered[$handle]->extra['data'])) { $data[] = $wp_scripts->registered[$handle]->extra['data']; }
1408
- }
1409
- if(count($data) > 0) { $wp_scripts->registered["fvm-header-$i"]->extra['data'] = implode(PHP_EOL, $data); }
1410
-
1411
- # enqueue file, if not empty
1412
- if(file_exists($file) && (filesize($file) > 0 || count($data) > 0)) {
1413
- wp_enqueue_script("fvm-header-$i");
1414
- } else {
1415
- # file could not be generated, output something meaningful
1416
- echo "<!-- ERROR: FVM was not allowed to save it's cache on - $file -->";
1417
- echo "<!-- Please check if the path above is correct and ensure your server has writting permission there! -->";
1418
- echo "<!-- If you found a bug, please report this on https://wordpress.org/support/plugin/fast-velocity-minify/ -->";
1419
- }
1420
-
1421
- # other scripts need to be requeued for the order of files to be kept
1422
- } else {
1423
- wp_dequeue_script($header[$i]['handle']);
1424
- wp_enqueue_script($header[$i]['handle']);
1425
- }
1426
- }
1427
-
1428
- # remove from queue
1429
- $wp_scripts->done = $done;
1430
- }
1431
- ###########################################
1432
-
1433
-
1434
-
1435
- ###########################################
1436
- # process js in the footer ################
1437
- ###########################################
1438
- function fastvelocity_min_merge_footer_scripts() {
1439
- global $wp_scripts, $wp_domain, $wp_home, $wp_home_path, $cachedir, $cachedirurl, $ignore, $disable_js_merge, $disable_js_minification, $enable_defer_js, $exclude_defer_jquery, $fvm_debug, $fvm_min_excludejslist, $fvmualist;
1440
- if(!is_object($wp_scripts)) { return false; }
1441
- $ctime = get_option('fvm-last-cache-update', '0');
1442
- $scripts = wp_clone($wp_scripts);
1443
- $scripts->all_deps($scripts->queue);
1444
- $footer = array();
1445
-
1446
- # mark as done (as we go)
1447
- $done = $scripts->done;
1448
-
1449
- # add defaults to ignore list
1450
- $ignore = fastvelocity_default_ignore($ignore);
1451
-
1452
-
1453
- # get groups of handles
1454
- foreach( $scripts->to_do as $handle ) :
1455
-
1456
- # get full url
1457
- $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
1458
-
1459
- # inlined scripts without file
1460
- if( empty($hurl)) {
1461
- continue;
1462
- }
1463
-
1464
- # Exclude JS files from PSI (Async) takes priority over the ignore list
1465
- if($fvm_min_excludejslist != false || is_array($fvm_min_excludejslist)) {
1466
-
1467
- # check for string match
1468
- $skipjs = false;
1469
- foreach($fvm_min_excludejslist as $l) {
1470
- if (stripos($hurl, $l) !== false) {
1471
- # print code if there are no linebreaks, or return
1472
- echo '<script type="text/javascript">if(fvmuag()){';
1473
- echo "loadAsync('$hurl', null);";
1474
- echo '}</script>';
1475
- $skipjs = true;
1476
- break;
1477
- }
1478
- }
1479
- if($skipjs != false) { continue; }
1480
- }
1481
-
1482
- # IE only files don't increment things
1483
- $ieonly = fastvelocity_ie_blacklist($hurl);
1484
- if($ieonly == true) { continue; }
1485
-
1486
- # skip ignore list, scripts with conditionals, external scripts
1487
- if ((!fastvelocity_min_in_arrayi($hurl, $ignore) && !isset($wp_scripts->registered[$handle]->extra["conditional"]) && fvm_internal_url($hurl, $wp_home)) || empty($hurl)) {
1488
-
1489
- # process
1490
- if(isset($footer[count($footer)-1]['handle']) || count($footer) == 0) {
1491
- array_push($footer, array('handles'=>array()));
1492
- }
1493
-
1494
- # push it to the array
1495
- array_push($footer[count($footer)-1]['handles'], $handle);
1496
-
1497
- # external and ignored scripts
1498
- } else {
1499
- array_push($footer, array('handle'=>$handle));
1500
- }
1501
- endforeach;
1502
-
1503
- # loop through footer scripts and merge
1504
- for($i=0,$l=count($footer);$i<$l;$i++) {
1505
- if(!isset($footer[$i]['handle'])) {
1506
-
1507
- # static cache file info + done
1508
- $done = array_merge($done, $footer[$i]['handles']);
1509
- $hash = 'footer-'.hash('sha1',implode('',$footer[$i]['handles']));
1510
-
1511
- # create cache files and urls
1512
- $file = $cachedir.fastvelocity_get_os_slash().$hash.'.min.js';
1513
- $file_url = fvm_get_protocol($cachedirurl.'/'.$hash.'.min.js');
1514
-
1515
- # generate a new cache file
1516
- clearstatcache();
1517
- if (!file_exists($file)) {
1518
-
1519
- # code and log initialization
1520
- $log = '';
1521
- $code = '';
1522
-
1523
- # minify and write to file
1524
- foreach($footer[$i]['handles'] as $handle) :
1525
- if(!empty($wp_scripts->registered[$handle]->src)) {
1526
-
1527
- # get hurl per handle
1528
- $hurl = fastvelocity_min_get_hurl($wp_scripts->registered[$handle]->src, $wp_domain, $wp_home);
1529
-
1530
- # inlined scripts without file
1531
- if( empty($hurl)) {
1532
- continue;
1533
- }
1534
-
1535
- # print url
1536
- $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
1537
-
1538
-
1539
- # download, minify, cache
1540
- $tkey = 'js-'.hash('sha1', $handle.$hurl).'.js';
1541
- $json = false; $json = fvm_get_transient($tkey);
1542
- if ( $json === false) {
1543
- $json = fvm_download_and_minify($hurl, null, $disable_js_minification, 'js', $handle);
1544
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Uncached file processing now for $handle / $hurl -->" . PHP_EOL; }
1545
- fvm_set_transient($tkey, $json);
1546
- }
1547
-
1548
- # decode
1549
- $res = json_decode($json, true);
1550
-
1551
- # response has failed
1552
- if($res['status'] != true) {
1553
- $log.= $res['log'];
1554
- continue;
1555
- }
1556
-
1557
- # Add extra data from wp_add_inline_script before
1558
- if (!empty($wp_scripts->registered[$handle]->extra)){
1559
- if (isset($wp_scripts->registered[$handle]->extra['before'])){
1560
- if(is_array($wp_scripts->registered[$handle]->extra['before'])) {
1561
- $arr = array_filter($wp_scripts->registered[$handle]->extra['before']);
1562
- $code.= PHP_EOL . fastvelocity_try_catch_wrap(implode(PHP_EOL, $arr));
1563
- }
1564
- }
1565
- }
1566
-
1567
- # append code to merged file
1568
- $code.= "/* $hurl */". PHP_EOL . fastvelocity_try_catch_wrap($res['code']);
1569
- $log.= $res['log'];
1570
-
1571
- # Add extra data from wp_add_inline_script after
1572
- if (!empty($wp_scripts->registered[$handle]->extra)){
1573
- if (isset($wp_scripts->registered[$handle]->extra['after'])){
1574
- if(is_array($wp_scripts->registered[$handle]->extra['after'])) {
1575
- $arr = array_filter($wp_scripts->registered[$handle]->extra['after']);
1576
- $code.= PHP_EOL . fastvelocity_try_catch_wrap(implode(PHP_EOL, $arr));
1577
- }
1578
- }
1579
- }
1580
-
1581
- # consider dependencies on handles with an empty src
1582
- } else {
1583
- wp_dequeue_script($handle);
1584
- wp_enqueue_script($handle);
1585
- }
1586
- endforeach;
1587
-
1588
- # prepare log
1589
- $log = "PROCESSED on ".date('r').PHP_EOL.$log."PROCESSED from ".home_url(add_query_arg( NULL, NULL )).PHP_EOL;
1590
-
1591
- # generate cache, write log
1592
- if(!empty($code)) {
1593
- file_put_contents($file.'.txt', $log);
1594
- file_put_contents($file, $code);
1595
- file_put_contents($file.'.gz', gzencode(file_get_contents($file), 9));
1596
-
1597
- # permissions
1598
- fastvelocity_fix_permission_bits($file.'.txt');
1599
- fastvelocity_fix_permission_bits($file);
1600
- fastvelocity_fix_permission_bits($file.'.gz');
1601
-
1602
- # brotli static support
1603
- if(function_exists('brotli_compress')) {
1604
- file_put_contents($file.'.br', brotli_compress(file_get_contents($file), 11));
1605
- fastvelocity_fix_permission_bits($file.'.br');
1606
- }
1607
- }
1608
- }
1609
-
1610
- # register minified file
1611
- wp_register_script("fvm-footer-$i", $file_url, array(), null, false);
1612
-
1613
- # add all extra data from wp_localize_script
1614
- $data = array();
1615
- foreach($footer[$i]['handles'] as $handle) {
1616
- if(isset($wp_scripts->registered[$handle]->extra['data'])) { $data[] = $wp_scripts->registered[$handle]->extra['data']; }
1617
- }
1618
- if(count($data) > 0) { $wp_scripts->registered["fvm-footer-$i"]->extra['data'] = implode(PHP_EOL, $data); }
1619
-
1620
- # enqueue file, if not empty
1621
- if(file_exists($file) && (filesize($file) > 0 || count($data) > 0)) {
1622
- wp_enqueue_script("fvm-footer-$i");
1623
- } else {
1624
- # file could not be generated, output something meaningful
1625
- echo "<!-- ERROR: FVM was not allowed to save it's cache on - $file -->";
1626
- echo "<!-- Please check if the path above is correct and ensure your server has writting permission there! -->";
1627
- echo "<!-- If you found a bug, please report this on https://wordpress.org/support/plugin/fast-velocity-minify/ -->";
1628
- }
1629
-
1630
- # other scripts need to be requeued for the order of files to be kept
1631
- } else {
1632
- wp_dequeue_script($footer[$i]['handle']); wp_enqueue_script($footer[$i]['handle']);
1633
- }
1634
- }
1635
-
1636
- # remove from queue
1637
- $wp_scripts->done = $done;
1638
- }
1639
- ##############################
1640
-
1641
-
1642
-
1643
- ###########################################
1644
- # enable defer for JavaScript (WP 4.1 and above) and remove query strings for ignored files
1645
- ###########################################
1646
- function fastvelocity_min_defer_js($tag, $handle, $src) {
1647
- global $ignore, $blacklist, $ignorelist, $enable_defer_js, $defer_for_pagespeed, $wp_domain, $exclude_defer_login, $fvm_fix_editor, $fvmualist, $defer_for_pagespeed_optimize, $exclude_defer_jquery, $skip_defer_lists;
1648
-
1649
- # no query strings
1650
- $tag = trim($tag); # must cleanup
1651
- if (stripos($src, '?ver') !== false) {
1652
- $srcf = stristr($src, '?ver', true);
1653
- $tag = str_ireplace($src, $srcf, $tag);
1654
- $src = $srcf;
1655
- }
1656
-
1657
-
1658
- # fix page editors, admin, amp, etc
1659
- if(fastvelocity_exclude_contents()) { return $tag; }
1660
-
1661
- # return if defer option is not selected
1662
- if ($defer_for_pagespeed != true && $enable_defer_js != true) { return $tag; }
1663
-
1664
- # Skip deferring the jQuery library option
1665
- if($exclude_defer_jquery != false && (stripos($tag, '/jquery.js') !== false || stripos($tag, '/jquery.min.js') !== false || (stripos($tag, '/jquery-') !== false && stripos($tag, '.js') !== false))) {
1666
- return $tag;
1667
- }
1668
-
1669
- # return if external script url https://www.chromestatus.com/feature/5718547946799104
1670
- if (fvm_is_local_domain($src) !== true) { return $tag; }
1671
-
1672
- # bypass if there are linebreaks (will break document.write) or already being optimized
1673
- if (stripos($tag, PHP_EOL) !== false || stripos($tag, 'navigator.userAgent.match') !== false) {
1674
- return $tag;
1675
- }
1676
-
1677
- # should we exclude defer on the login page?
1678
- if($exclude_defer_login == true && stripos($_SERVER["SCRIPT_NAME"], strrchr(wp_login_url(), '/')) !== false){
1679
- return $tag;
1680
- }
1681
-
1682
- # add defer attribute, but only if not having async or defer already
1683
- if (stripos($tag, 'defer') === false && stripos($tag, 'async') === false) {
1684
-
1685
- # defer tag globally
1686
- $jsdefer = str_ireplace('<script ', '<script defer ', $tag);
1687
- $jsdeferpsi = $jsdefer;
1688
-
1689
- # add cdn for PSI
1690
- $fvm_cdn_url = get_option('fastvelocity_min_fvm_cdn_url');
1691
- if(!empty($fvm_cdn_url)) {
1692
- $fvm_cdn_url = trim(trim(str_ireplace(array('http://', 'https://'), '', trim($fvm_cdn_url, '/'))), '/');
1693
- $jsdeferpsi = str_ireplace($src, $fvm_cdn_url, $jsdefer);
1694
- }
1695
-
1696
-
1697
- # defer tag for PSI only
1698
- $jsdeferpsionly = '<script type="text/javascript">if(navigator.userAgent.match(/'.implode('|', $fvmualist).'/i)){document.write('.fastvelocity_escape_url_js($jsdeferpsi).');}else{document.write('.fastvelocity_escape_url_js($tag).');}</script>';
1699
-
1700
- # hide tag from PSI
1701
- $jsdeferhidepsi = '<script type="text/javascript">if(fvmuag()){document.write('.fastvelocity_escape_url_js($tag).');}</script>';
1702
-
1703
- # must return by this order...
1704
-
1705
- # remove FVM from the ignore list
1706
- array_filter($ignore, function ($var) { return (stripos($var, '/fvm/') === false); });
1707
-
1708
- # Exclude JS files in the "ignore list" from PSI
1709
- if($defer_for_pagespeed_optimize != false) {
1710
- if((count($ignore) > 0 && fastvelocity_min_in_arrayi($src, $ignore)) || (count($blacklist) > 0 && fastvelocity_min_in_arrayi($src, $blacklist)) || (count($ignorelist) > 0 && fastvelocity_min_in_arrayi($src, $ignorelist))) {
1711
- return $jsdeferhidepsi;
1712
- }
1713
- }
1714
-
1715
- # Enable defer of JS files for PSI
1716
- if($defer_for_pagespeed != false) {
1717
- return $jsdeferpsionly;
1718
- }
1719
-
1720
- # Enable defer parsing of FVM JS files globally
1721
- if($enable_defer_js == true) {
1722
-
1723
- # consider "Skip deferring the ignore list"
1724
- if($skip_defer_lists != false && ((count($ignore) > 0 && fastvelocity_min_in_arrayi($src, $ignore)) || (count($blacklist) > 0 && fastvelocity_min_in_arrayi($src, $blacklist)) || (count($ignorelist) > 0 && fastvelocity_min_in_arrayi($src, $ignorelist)))) {
1725
- return $tag;
1726
- } else {
1727
- return $jsdefer;
1728
- }
1729
- }
1730
-
1731
- }
1732
-
1733
- # fallback
1734
- return $tag;
1735
- }
1736
- ###########################################
1737
-
1738
-
1739
- ###########################################
1740
- # process header css ######################
1741
- ###########################################
1742
- function fastvelocity_min_merge_header_css() {
1743
- global $wp_styles, $wp_domain, $wp_home, $wp_home_path, $cachedir, $cachedirurl, $ignore, $disable_css_merge, $disable_css_minification, $skip_google_fonts, $skip_cssorder, $remove_print_mediatypes, $force_inline_googlefonts, $css_hide_googlefonts, $min_async_googlefonts, $remove_googlefonts, $fvmloadcss, $fvm_remove_css, $fvmualist, $fvm_min_excludecsslist, $fvm_debug, $fvm_min_excludecsslist, $fvm_fawesome_method;
1744
- if(!is_object($wp_styles)) { return false; }
1745
- $ctime = get_option('fvm-last-cache-update', '0');
1746
- $styles = wp_clone($wp_styles);
1747
- $styles->all_deps($styles->queue);
1748
- $done = $styles->done;
1749
- $header = array();
1750
- $google_fonts = array();
1751
- $process = array();
1752
- $inline_css = array();
1753
- $log = '';
1754
-
1755
- # dequeue all styles
1756
- if($fvm_remove_css != false) {
1757
- foreach( $styles->to_do as $handle ) :
1758
- $done = array_merge($done, array($handle));
1759
- endforeach;
1760
-
1761
- # remove from queue
1762
- $wp_styles->done = $done;
1763
- return false;
1764
- }
1765
-
1766
- # add defaults to ignore list
1767
- $ignore = fastvelocity_default_ignore($ignore);
1768
-
1769
- # get list of handles to process, dequeue duplicate css urls and keep empty source handles (for dependencies)
1770
- $uniq = array(); $gfonts = array();
1771
- foreach( $styles->to_do as $handle):
1772
-
1773
- # conditionals
1774
- $conditional = NULL; if(isset($wp_styles->registered[$handle]->extra["conditional"])) {
1775
- $conditional = $wp_styles->registered[$handle]->extra["conditional"]; # such as ie7, ie8, ie9, etc
1776
- }
1777
-
1778
- # mediatype
1779
- $mt = isset($wp_styles->registered[$handle]->args) ? $wp_styles->registered[$handle]->args : 'all';
1780
- if ($mt == 'screen' || $mt == 'screen, print' || empty($mt) || is_null($mt) || $mt == false) { $mt = 'all'; }
1781
- $mediatype = $mt;
1782
-
1783
- # full url or empty
1784
- $hurl = fastvelocity_min_get_hurl($wp_styles->registered[$handle]->src, $wp_domain, $wp_home);
1785
-
1786
- # inlined scripts without file
1787
- if( empty($hurl)) {
1788
- continue;
1789
- }
1790
-
1791
- # mark duplicates as done and remove from the queue
1792
- if(!empty($hurl)) {
1793
- $key = hash('sha1', $hurl);
1794
- if (isset($uniq[$key])) { $done = array_merge($done, array($handle)); continue; } else { $uniq[$key] = $handle; }
1795
- }
1796
-
1797
- # Exclude specific CSS files from PSI?
1798
- if($fvm_min_excludecsslist != false && is_array($fvm_min_excludecsslist) && fastvelocity_min_in_arrayi($hurl, $fvm_min_excludecsslist)) {
1799
- $cssguid = 'fvm'.hash('sha1', $hurl);
1800
- echo '<script type="text/javascript">if(fvmuag()){';
1801
- echo 'var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$hurl.'",'.$cssguid.'.onload=function(){'.$cssguid.'.media="'.$mediatype.'"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');';
1802
- echo '}</script>';
1803
- $done = array_merge($done, array($handle)); continue;
1804
- }
1805
-
1806
- # font awesome processing, async css
1807
- if($fvm_fawesome_method == 2 && stripos($hurl, 'font-awesome') !== false) {
1808
- echo '<link rel="preload" href="'.$hurl.'" as="style" media="'.$mediatype.'" onload="this.onload=null;this.rel=\'stylesheet\'" />';
1809
- echo '<noscript><link rel="stylesheet" href="'.$hurl.'" media="'.$mediatype.'" /></noscript>';
1810
- echo '<!--[if IE]><link rel="stylesheet" href="'.$hurl.'" media="'.$mediatype.'" /><![endif]-->';
1811
- $done = array_merge($done, array($handle)); continue;
1812
- }
1813
-
1814
- # font awesome processing, async and exclude from PSI
1815
- if($fvm_fawesome_method == 3 && stripos($hurl, 'font-awesome') !== false) {
1816
- $cssguid = 'fvm'.hash('sha1', $hurl);
1817
- echo '<script type="text/javascript">if(fvmuag()){';
1818
- echo 'var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$hurl.'",'.$cssguid.'.onload=function(){'.$cssguid.'.media="'.$mediatype.'"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');';
1819
- echo '}</script>';
1820
- $done = array_merge($done, array($handle)); continue;
1821
- }
1822
-
1823
- # array of info to save
1824
- $arr = array('handle'=>$handle, 'url'=>$hurl, 'conditional'=>$conditional, 'mediatype'=>$mediatype);
1825
-
1826
- # google fonts to the top (collect and skip process array)
1827
- if (stripos($hurl, 'fonts.googleapis.com') !== false) {
1828
- if($remove_googlefonts != false) { $done = array_merge($done, array($handle)); continue; } # mark as done if to be removed
1829
- if($skip_google_fonts != true || $force_inline_googlefonts != false) {
1830
- $google_fonts[$handle] = $hurl;
1831
-
1832
- } else {
1833
- wp_enqueue_style($handle); # skip google fonts optimization?
1834
- }
1835
- continue;
1836
- }
1837
-
1838
- # all else
1839
- $process[$handle] = $arr;
1840
-
1841
- endforeach;
1842
-
1843
-
1844
- # concat google fonts, if enabled
1845
- if(!$skip_google_fonts && count($google_fonts) > 0 || ($force_inline_googlefonts != false && count($google_fonts) > 0)) {
1846
- foreach ($google_fonts as $h=>$a) { $done = array_merge($done, array($h)); } # mark as done
1847
-
1848
- # merge google fonts if force inlining is enabled?
1849
- $nfonts = array();
1850
- if($skip_google_fonts != true) {
1851
- $nfonts[] = fvm_get_protocol(fastvelocity_min_concatenate_google_fonts($google_fonts));
1852
- } else {
1853
- foreach ($google_fonts as $a) { if(!empty($a)) { $nfonts[] = $a; } }
1854
- }
1855
-
1856
- # foreach google font (will be one if merged is not disabled)
1857
- if(count($nfonts) > 0) {
1858
- foreach($nfonts as $gfurl) {
1859
-
1860
- # hide from PSI, async, inline, or default
1861
- if($css_hide_googlefonts == true) {
1862
-
1863
- # make a stylesheet, hide from PSI
1864
- $cssguid = 'fvm'.hash('sha1', $gfurl);
1865
- echo '<script type="text/javascript">if(fvmuag()){';
1866
- echo 'var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$gfurl.'",'.$cssguid.'.onload=function(){'.$cssguid.'.media="all"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');';
1867
- echo '}</script>';
1868
-
1869
- # async CSS
1870
- } elseif ($min_async_googlefonts == true) {
1871
- echo '<link rel="preload" href="'.$gfurl.'" as="style" media="all" onload="this.onload=null;this.rel=\'stylesheet\'" />';
1872
- echo '<noscript><link rel="stylesheet" href="'.$gfurl.'" media="all" /></noscript>';
1873
- echo '<!--[if IE]><link rel="stylesheet" href="'.$gfurl.'" media="all" /><![endif]-->';
1874
-
1875
- # inline css
1876
- } elseif($force_inline_googlefonts == true) {
1877
-
1878
- # download, minify, cache
1879
- $tkey = 'css-'.hash('sha1', $gfurl).'.css';
1880
- $json = false; $json = fvm_get_transient($tkey);
1881
- if ( $json === false) {
1882
- $json = fvm_download_and_minify($gfurl, null, $disable_css_minification, 'css', null);
1883
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Uncached file processing now for $gfurl -->" . PHP_EOL; }
1884
- fvm_set_transient($tkey, $json);
1885
- }
1886
-
1887
- # decode
1888
- $res = json_decode($json, true);
1889
-
1890
- # response has failed
1891
- if($res['status'] != true) {
1892
- $log.= $res['log'];
1893
- continue;
1894
- }
1895
-
1896
- # inline css or fail
1897
- if($res['code'] !== false) {
1898
-
1899
- # add font-display
1900
- # https://developers.google.com/web/updates/2016/02/font-display
1901
- $res['code'] = str_ireplace('font-style:normal;', 'font-display:block;font-style:normal;', $res['code']);
1902
-
1903
- # inline
1904
- echo '<style type="text/css" media="all">'.$res['code'].'</style>'.PHP_EOL;
1905
- } else {
1906
- echo "<!-- GOOGLE FONTS REQUEST FAILED for $gfurl -->\n";
1907
- }
1908
-
1909
- # fallback, enqueue google fonts
1910
- } else {
1911
- wp_enqueue_style('header-fvm-fonts', $gfurl, array(), null, 'all');
1912
- }
1913
-
1914
- }
1915
- }
1916
- }
1917
-
1918
-
1919
- # get groups of handles
1920
- foreach( $styles->to_do as $handle ) :
1921
-
1922
- # skip already processed google fonts and empty dependencies
1923
- if(isset($google_fonts[$handle])) { continue; } # skip google fonts
1924
- if(empty($wp_styles->registered[$handle]->src)) { continue; } # skip empty src
1925
- if (fastvelocity_min_in_arrayi($handle, $done)) { continue; } # skip if marked as done before
1926
- if (!isset($process[$handle])) { continue; } # skip if not on our unique process list
1927
-
1928
- # get full url
1929
- $hurl = $process[$handle]['url'];
1930
- $conditional = $process[$handle]['conditional'];
1931
- $mediatype = $process[$handle]['mediatype'];
1932
-
1933
- # IE only files don't increment things
1934
- $ieonly = fastvelocity_ie_blacklist($hurl);
1935
- if($ieonly == true) { continue; }
1936
-
1937
- # skip ignore list, conditional css, external css, font-awesome merge
1938
- if ( (!fastvelocity_min_in_arrayi($hurl, $ignore) && !isset($conditional) && fvm_internal_url($hurl, $wp_home))
1939
- || empty($hurl)
1940
- || ($fvm_fawesome_method == 1 && stripos($hurl, 'font-awesome') !== false)) {
1941
-
1942
- # colect inline css for this handle
1943
- if(isset($wp_styles->registered[$handle]->extra['after']) && is_array($wp_styles->registered[$handle]->extra['after'])) {
1944
- $inline_css[$handle] = fastvelocity_min_minify_css_string(implode('', $wp_styles->registered[$handle]->extra['after'])); # save
1945
- $wp_styles->registered[$handle]->extra['after'] = null; # dequeue
1946
- }
1947
-
1948
- # process
1949
- if(isset($header[count($header)-1]['handle']) || count($header) == 0 || $header[count($header)-1]['media'] != $mediatype) {
1950
- array_push($header, array('handles'=>array(), 'media'=>$mediatype));
1951
- }
1952
-
1953
- # push it to the array
1954
- array_push($header[count($header)-1]['handles'], $handle);
1955
-
1956
- # external and ignored css
1957
- } else {
1958
-
1959
- # normal enqueuing
1960
- array_push($header, array('handle'=>$handle));
1961
- }
1962
- endforeach;
1963
-
1964
- # reorder CSS by mediatypes
1965
- if(!$skip_cssorder) {
1966
- if(count($header) > 0) {
1967
-
1968
- # get unique mediatypes
1969
- $allmedia = array();
1970
- foreach($header as $array) {
1971
- if(isset($array['media'])) { $allmedia[$array['media']] = ''; }
1972
- }
1973
-
1974
- # extract handles by mediatype
1975
- $grouphandles = array();
1976
- foreach ($allmedia as $md=>$var) {
1977
- foreach($header as $array) {
1978
- if (isset($array['media']) && $array['media'] === $md) {
1979
- foreach($array['handles'] as $h) { $grouphandles[$md][] = $h; }
1980
- }
1981
- }
1982
- }
1983
-
1984
- # reset and reorder header by mediatypes
1985
- $newheader = array();
1986
- foreach ($allmedia as $md=>$var) { $newheader[] = array('handles' => $grouphandles[$md], 'media'=>$md); }
1987
- if(count($newheader) > 0) { $header = $newheader; }
1988
- }
1989
- }
1990
-
1991
- # loop through header css and merge
1992
- for($i=0,$l=count($header);$i<$l;$i++) {
1993
- if(!isset($header[$i]['handle'])) {
1994
-
1995
- # get has for the inline css in this group
1996
- $inline_css_group = array();
1997
- foreach($header[$i]['handles'] as $h) { if(isset($inline_css[$h]) && !empty($inline_css[$h])) { $inline_css_group[] = $inline_css[$h]; } }
1998
- $inline_css_hash = md5(implode('',$inline_css_group));
1999
-
2000
- # static cache file info + done
2001
- $done = array_merge($done, $header[$i]['handles']);
2002
- $hash = 'header-'.hash('sha1',implode('',$header[$i]['handles']).$inline_css_hash);
2003
-
2004
- # create cache files and urls
2005
- $file = $cachedir.fastvelocity_get_os_slash().$hash.'.min.css';
2006
- $file_url = fvm_get_protocol($cachedirurl.'/'.$hash.'.min.css');
2007
-
2008
- # generate a new cache file
2009
- clearstatcache();
2010
- if (!file_exists($file)) {
2011
-
2012
- # code and log initialization
2013
- $log = '';
2014
- $code = '';
2015
-
2016
- # minify and write to file
2017
- foreach($header[$i]['handles'] as $handle) :
2018
- if(!empty($wp_styles->registered[$handle]->src)) {
2019
-
2020
- # get hurl per handle
2021
- $hurl = fastvelocity_min_get_hurl($wp_styles->registered[$handle]->src, $wp_domain, $wp_home);
2022
-
2023
- # inlined scripts without file
2024
- if( empty($hurl)) {
2025
- continue;
2026
- }
2027
-
2028
- # print url
2029
- $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
2030
-
2031
- # download, minify, cache
2032
- $tkey = 'css-'.hash('sha1', $handle.$hurl).'.css';
2033
- $json = false; $json = fvm_get_transient($tkey);
2034
- if ( $json === false) {
2035
- $json = fvm_download_and_minify($hurl, null, $disable_css_minification, 'css', $handle);
2036
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Uncached file processing now for $handle / $hurl -->" . PHP_EOL; }
2037
- fvm_set_transient($tkey, $json);
2038
- }
2039
-
2040
- # decode
2041
- $res = json_decode($json, true);
2042
-
2043
- # response has failed
2044
- if($res['status'] != true) {
2045
- $log.= $res['log'];
2046
- continue;
2047
- }
2048
-
2049
- # append code to merged file
2050
- $code.= $res['code'];
2051
- $log.= $res['log'];
2052
-
2053
- # append inlined styles
2054
- if(isset($inline_css[$handle]) && !empty($inline_css[$handle])) {
2055
- $code.= $inline_css[$handle];
2056
- }
2057
-
2058
- # consider dependencies on handles with an empty src
2059
- } else {
2060
- wp_dequeue_script($handle);
2061
- wp_enqueue_script($handle);
2062
- }
2063
- endforeach;
2064
-
2065
- # prepare log
2066
- $log = "PROCESSED on ".date('r').PHP_EOL.$log."PROCESSED from ".home_url(add_query_arg( NULL, NULL )).PHP_EOL;
2067
-
2068
- # generate cache, write log
2069
- if(!empty($code)) {
2070
- file_put_contents($file.'.txt', $log);
2071
- file_put_contents($file, $code);
2072
- file_put_contents($file.'.gz', gzencode(file_get_contents($file), 9));
2073
-
2074
- # permissions
2075
- fastvelocity_fix_permission_bits($file.'.txt');
2076
- fastvelocity_fix_permission_bits($file);
2077
- fastvelocity_fix_permission_bits($file.'.gz');
2078
-
2079
- # brotli static support
2080
- if(function_exists('brotli_compress')) {
2081
- file_put_contents($file.'.br', brotli_compress(file_get_contents($file), 11));
2082
- fastvelocity_fix_permission_bits($file.'.br');
2083
- }
2084
- }
2085
- }
2086
-
2087
- # register and enqueue minified file, consider excluding of mediatype "print" and inline css
2088
- if ($remove_print_mediatypes != true || ($remove_print_mediatypes == true && $header[$i]['media'] != 'print')) {
2089
-
2090
- # the developers tab, takes precedence
2091
-
2092
- # Async CSS with loadCSS ?
2093
- if($fvmloadcss != false && $fvm_remove_css != true) {
2094
- $mt = $header[$i]['media'];
2095
- echo '<link rel="preload" href="'.$file_url.'" as="style" media="'.$mt.'" onload="this.onload=null;this.rel=\'stylesheet\'" />';
2096
- echo '<noscript><link rel="stylesheet" href="'.$file_url.'" media="'.$mt.'" /></noscript>';
2097
- echo '<!--[if IE]><link rel="stylesheet" href="'.$file_url.'" media="'.$mt.'" /><![endif]-->';
2098
-
2099
- # enqueue file, if not empty
2100
- } else {
2101
- if(file_exists($file) && filesize($file) > 0) {
2102
-
2103
- # inline CSS if mediatype is not of type "all" (such as mobile only), if the file is smaller than 20KB
2104
- if(filesize($file) < 20000 && $header[$i]['media'] != 'all') {
2105
- echo '<style id="fvm-header-'.$i.'" media="'.$header[$i]['media'].'">'.file_get_contents($file).'</style>';
2106
- } else {
2107
- # enqueue it
2108
- wp_enqueue_style("fvm-header-$i", $file_url, array(), null, $header[$i]['media']);
2109
- }
2110
- } else {
2111
- # file could not be generated, output something meaningful
2112
- echo "<!-- ERROR: FVM was not allowed to save it's cache on - $file -->";
2113
- echo "<!-- Please check if the path above is correct and ensure your server has writting permission there! -->";
2114
- echo "<!-- If you found a bug, please report this on https://wordpress.org/support/plugin/fast-velocity-minify/ -->";
2115
- }
2116
- }
2117
- }
2118
-
2119
- # other css need to be requeued for the order of files to be kept
2120
- } else {
2121
- wp_dequeue_style($header[$i]['handle']);
2122
- wp_enqueue_style($header[$i]['handle']);
2123
- }
2124
- }
2125
-
2126
- # remove from queue
2127
- $wp_styles->done = $done;
2128
-
2129
- }
2130
- ###########################################
2131
-
2132
-
2133
- ###########################################
2134
- # process css in the footer ###############
2135
- ###########################################
2136
- function fastvelocity_min_merge_footer_css() {
2137
- global $wp_styles, $wp_domain, $wp_home, $wp_home_path, $cachedir, $cachedirurl, $ignore, $disable_css_merge, $disable_css_minification, $skip_google_fonts, $skip_cssorder, $remove_print_mediatypes, $force_inline_googlefonts, $css_hide_googlefonts, $min_async_googlefonts, $remove_googlefonts, $fvmloadcss, $fvm_remove_css, $fvmualist, $fvm_debug, $fvm_fawesome_method, $fvm_min_excludecsslist, $force_inline_css_footer;
2138
-
2139
- if(!is_object($wp_styles)) { return false; }
2140
- $ctime = get_option('fvm-last-cache-update', '0');
2141
- $styles = wp_clone($wp_styles);
2142
- $styles->all_deps($styles->queue);
2143
- $done = $styles->done;
2144
- $footer = array();
2145
- $google_fonts = array();
2146
- $inline_css = array();
2147
- $code = '';
2148
- $log = '';
2149
-
2150
- # dequeue all styles
2151
- if($fvm_remove_css != false) {
2152
- foreach( $styles->to_do as $handle ) :
2153
- $done = array_merge($done, array($handle));
2154
- endforeach;
2155
-
2156
- # remove from queue
2157
- $wp_styles->done = $done;
2158
- return false;
2159
- }
2160
-
2161
-
2162
- # add defaults to ignore list
2163
- $ignore = fastvelocity_default_ignore($ignore);
2164
-
2165
- # google fonts to the top
2166
- foreach( $styles->to_do as $handle ) :
2167
-
2168
- # dequeue and get a list of google fonts, or requeue external
2169
- $hurl = fastvelocity_min_get_hurl($wp_styles->registered[$handle]->src, $wp_domain, $wp_home);
2170
-
2171
- # inlined scripts without file
2172
- if( empty($hurl)) {
2173
- continue;
2174
- }
2175
-
2176
- if (stripos($hurl, 'fonts.googleapis.com') !== false) {
2177
- wp_dequeue_style($handle);
2178
- if($remove_googlefonts != false) { $done = array_merge($done, array($handle)); continue; } # mark as done if to be removed
2179
- if($skip_google_fonts != true || $force_inline_googlefonts != false) {
2180
- $google_fonts[$handle] = $hurl;
2181
- } else {
2182
- wp_enqueue_style($handle); # skip google fonts optimization?
2183
- }
2184
- } else {
2185
- wp_dequeue_style($handle); wp_enqueue_style($handle); # failsafe
2186
- }
2187
-
2188
- endforeach;
2189
-
2190
-
2191
- # concat google fonts, if enabled
2192
- if(!$skip_google_fonts && count($google_fonts) > 0 || ($force_inline_googlefonts != false && count($google_fonts) > 0)) {
2193
- foreach ($google_fonts as $h=>$a) { $done = array_merge($done, array($h)); } # mark as done
2194
-
2195
- # merge google fonts if force inlining is enabled?
2196
- $nfonts = array();
2197
- if($skip_google_fonts != true) {
2198
- $nfonts[] = fvm_get_protocol(fastvelocity_min_concatenate_google_fonts($google_fonts));
2199
- } else {
2200
- foreach ($google_fonts as $a) { if(!empty($a)) { $nfonts[] = $a; } }
2201
- }
2202
-
2203
- # foreach google font (will be one if merged is not disabled)
2204
- if(count($nfonts) > 0) {
2205
- foreach($nfonts as $gfurl) {
2206
-
2207
- # hide from PSI, async, inline, or default
2208
- if($css_hide_googlefonts == true) {
2209
-
2210
- # make a stylesheet, hide from PSI
2211
- $cssguid = 'fvm'.hash('sha1', $gfurl);
2212
- echo '<script type="text/javascript">if(fvmuag()){';
2213
- echo 'var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$gfurl.'",'.$cssguid.'.onload=function(){'.$cssguid.'.media="all"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');';
2214
- echo '}</script>';
2215
-
2216
- # async CSS
2217
- } elseif ($min_async_googlefonts == true) {
2218
- echo '<link rel="preload" href="'.$gfurl.'" as="style" media="all" onload="this.onload=null;this.rel=\'stylesheet\'" />';
2219
- echo '<noscript><link rel="stylesheet" href="'.$gfurl.'" media="all" /></noscript>';
2220
- echo '<!--[if IE]><link rel="stylesheet" href="'.$gfurl.'" media="all" /><![endif]-->';
2221
-
2222
- # inline css
2223
- } elseif($force_inline_googlefonts == true) {
2224
-
2225
- # download, minify, cache
2226
- $tkey = 'css-'.hash('sha1', $gfurl).'.css';
2227
- $json = false; $json = fvm_get_transient($tkey);
2228
- if ( $json === false) {
2229
- $json = fvm_download_and_minify($gfurl, null, $disable_css_minification, 'css', null);
2230
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Uncached file processing now for $gfurl -->" . PHP_EOL; }
2231
- fvm_set_transient($tkey, $json);
2232
- }
2233
-
2234
- # decode
2235
- $res = json_decode($json, true);
2236
-
2237
- # response has failed
2238
- if($res['status'] != true) {
2239
- $log.= $res['log'];
2240
- continue;
2241
- }
2242
-
2243
- # append code to merged file
2244
- $code.= $res['code'];
2245
- $log.= $res['log'];
2246
-
2247
- # inline css or fail
2248
- if($res['code'] !== false) {
2249
- echo '<style type="text/css" media="all">'.$res['code'].'</style>'.PHP_EOL;
2250
- } else {
2251
- echo "<!-- GOOGLE FONTS REQUEST FAILED for $gfurl -->\n";
2252
- }
2253
-
2254
- # fallback, enqueue google fonts
2255
- } else {
2256
- wp_enqueue_style('footer-fvm-fonts', $gfurl, array(), null, 'all');
2257
- }
2258
-
2259
- }
2260
- }
2261
- }
2262
-
2263
-
2264
- # get groups of handles
2265
- $uniq = array();
2266
- foreach( $styles->to_do as $handle ) :
2267
-
2268
- # skip already processed google fonts
2269
- if(isset($google_fonts[$handle])) { continue; }
2270
-
2271
- # conditionals
2272
- $conditional = NULL; if(isset($wp_styles->registered[$handle]->extra["conditional"])) {
2273
- $conditional = $wp_styles->registered[$handle]->extra["conditional"]; # such as ie7, ie8, ie9, etc
2274
- }
2275
-
2276
- # mediatype
2277
- $mt = isset($wp_styles->registered[$handle]->args) ? $wp_styles->registered[$handle]->args : 'all';
2278
- if ($mt == 'screen' || $mt == 'screen, print' || empty($mt) || is_null($mt) || $mt == false) { $mt = 'all'; }
2279
- $mediatype = $mt;
2280
-
2281
- # get full url
2282
- $hurl = fastvelocity_min_get_hurl($wp_styles->registered[$handle]->src, $wp_domain, $wp_home);
2283
-
2284
- # inlined scripts without file
2285
- if( empty($hurl)) {
2286
- continue;
2287
- }
2288
-
2289
- # mark duplicates as done and remove from the queue
2290
- if(!empty($hurl)) {
2291
- $key = hash('sha1', $hurl);
2292
- if (isset($uniq[$key])) { $done = array_merge($done, array($handle)); continue; } else { $uniq[$key] = $handle; }
2293
- }
2294
-
2295
- # IE only files don't increment things
2296
- $ieonly = fastvelocity_ie_blacklist($hurl);
2297
- if($ieonly == true) { continue; }
2298
-
2299
-
2300
- # Exclude specific CSS files from PSI?
2301
- if($fvm_min_excludecsslist != false && is_array($fvm_min_excludecsslist) && fastvelocity_min_in_arrayi($hurl, $fvm_min_excludecsslist)) {
2302
- $cssguid = 'fvm'.hash('sha1', $hurl);
2303
- echo '<script type="text/javascript">if(fvmuag()){';
2304
- echo 'var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$hurl.'",'.$cssguid.'.onload=function(){'.$cssguid.'.media="'.$mediatype.'"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');';
2305
- echo '}</script>';
2306
- $done = array_merge($done, array($handle)); continue;
2307
- }
2308
-
2309
- # font awesome processing, async css
2310
- if($fvm_fawesome_method == 2 && stripos($hurl, 'font-awesome') !== false) {
2311
- echo '<link rel="preload" href="'.$hurl.'" as="style" media="'.$mediatype.'" onload="this.onload=null;this.rel=\'stylesheet\'" />';
2312
- echo '<noscript><link rel="stylesheet" href="'.$hurl.'" media="'.$mediatype.'" /></noscript>';
2313
- echo '<!--[if IE]><link rel="stylesheet" href="'.$hurl.'" media="'.$mediatype.'" /><![endif]-->';
2314
- $done = array_merge($done, array($handle)); continue;
2315
- }
2316
-
2317
- # font awesome processing, async and exclude from PSI
2318
- if($fvm_fawesome_method == 3 && stripos($hurl, 'font-awesome') !== false) {
2319
- $cssguid = 'fvm'.hash('sha1', $hurl);
2320
- echo '<script type="text/javascript">if(fvmuag()){';
2321
- echo 'var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$hurl.'",'.$cssguid.'.onload=function(){'.$cssguid.'.media="'.$mediatype.'"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');';
2322
- echo '}</script>';
2323
- $done = array_merge($done, array($handle)); continue;
2324
- }
2325
-
2326
- # skip ignore list, conditional css, external css, font-awesome merge
2327
- if ( (!fastvelocity_min_in_arrayi($hurl, $ignore) && !isset($conditional) && fvm_internal_url($hurl, $wp_home))
2328
- || empty($hurl)
2329
- || ($fvm_fawesome_method == 1 && stripos($hurl, 'font-awesome') !== false)) {
2330
-
2331
- # colect inline css for this handle
2332
- if(isset($wp_styles->registered[$handle]->extra['after']) && is_array($wp_styles->registered[$handle]->extra['after'])) {
2333
- $inline_css[$handle] = fastvelocity_min_minify_css_string(implode('', $wp_styles->registered[$handle]->extra['after'])); # save
2334
- $wp_styles->registered[$handle]->extra['after'] = null; # dequeue
2335
- }
2336
-
2337
- # process
2338
- if(isset($footer[count($footer)-1]['handle']) || count($footer) == 0 || $footer[count($footer)-1]['media'] != $wp_styles->registered[$handle]->args) {
2339
- array_push($footer, array('handles'=>array(),'media'=>$mediatype));
2340
- }
2341
-
2342
- # push it to the array get latest modified time
2343
- array_push($footer[count($footer)-1]['handles'], $handle);
2344
-
2345
- # external and ignored css
2346
- } else {
2347
-
2348
- # normal enqueueing
2349
- array_push($footer, array('handle'=>$handle));
2350
- }
2351
- endforeach;
2352
-
2353
-
2354
- # reorder CSS by mediatypes
2355
- if(!$skip_cssorder) {
2356
- if(count($footer) > 0) {
2357
-
2358
- # get unique mediatypes
2359
- $allmedia = array();
2360
- foreach($footer as $key=>$array) {
2361
- if(isset($array['media'])) { $allmedia[$array['media']] = ''; }
2362
- }
2363
-
2364
- # extract handles by mediatype
2365
- $grouphandles = array();
2366
- foreach ($allmedia as $md=>$var) {
2367
- foreach($footer as $array) {
2368
- if (isset($array['media']) && $array['media'] === $md) {
2369
- foreach($array['handles'] as $h) { $grouphandles[$md][] = $h; }
2370
- }
2371
- }
2372
- }
2373
-
2374
- # reset and reorder footer by mediatypes
2375
- $newfooter = array();
2376
- foreach ($allmedia as $md=>$var) { $newfooter[] = array('handles' => $grouphandles[$md], 'media'=>$md); }
2377
- if(count($newfooter) > 0) { $footer = $newfooter; }
2378
- }
2379
- }
2380
-
2381
- # loop through footer css and merge
2382
- for($i=0,$l=count($footer);$i<$l;$i++) {
2383
- if(!isset($footer[$i]['handle'])) {
2384
-
2385
- # get has for the inline css in this group
2386
- $inline_css_group = array();
2387
- foreach($footer[$i]['handles'] as $h) { if(isset($inline_css[$h]) && !empty($inline_css[$h])) { $inline_css_group[] = $inline_css[$h]; } }
2388
- $inline_css_hash = md5(implode('',$inline_css_group));
2389
-
2390
- # static cache file info + done
2391
- $done = array_merge($done, $footer[$i]['handles']);
2392
- $hash = 'footer-'.hash('sha1',implode('',$footer[$i]['handles']).$inline_css_hash);
2393
-
2394
- # create cache files and urls
2395
- $file = $cachedir.fastvelocity_get_os_slash().$hash.'.min.css';
2396
- $file_url = fvm_get_protocol($cachedirurl.'/'.$hash.'.min.css');
2397
-
2398
- # generate a new cache file
2399
- clearstatcache();
2400
- if (!file_exists($file)) {
2401
-
2402
- # code and log initialization
2403
- $log = '';
2404
- $code = '';
2405
-
2406
- # minify and write to file
2407
- foreach($footer[$i]['handles'] as $handle) :
2408
- if(!empty($wp_styles->registered[$handle]->src)) {
2409
-
2410
- # get hurl per handle
2411
- $hurl = fastvelocity_min_get_hurl($wp_styles->registered[$handle]->src, $wp_domain, $wp_home);
2412
-
2413
- # inlined scripts without file
2414
- if( empty($hurl)) {
2415
- continue;
2416
- }
2417
-
2418
- # print url
2419
- $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
2420
-
2421
- # download, minify, cache
2422
- $tkey = 'css-'.hash('sha1', $handle.$hurl).'.css';
2423
- $json = false; $json = fvm_get_transient($tkey);
2424
- if ( $json === false) {
2425
- $json = fvm_download_and_minify($hurl, null, $disable_css_minification, 'css', $handle);
2426
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Uncached file processing now for $handle / $hurl -->" . PHP_EOL; }
2427
- fvm_set_transient($tkey, $json);
2428
- }
2429
-
2430
- # decode
2431
- $res = json_decode($json, true);
2432
-
2433
- # response has failed
2434
- if($res['status'] != true) {
2435
- $log.= $res['log'];
2436
- continue;
2437
- }
2438
-
2439
- # append code to merged file
2440
- $code.= $res['code'];
2441
- $log.= $res['log'];
2442
-
2443
- # append inlined styles
2444
- if(isset($inline_css[$handle]) && !empty($inline_css[$handle])) {
2445
- $code.= $inline_css[$handle];
2446
- }
2447
-
2448
- # consider dependencies on handles with an empty src
2449
- } else {
2450
- wp_dequeue_script($handle);
2451
- wp_enqueue_script($handle);
2452
- }
2453
- endforeach;
2454
-
2455
- # prepare log
2456
- $log = "PROCESSED on ".date('r').PHP_EOL.$log."PROCESSED from ".home_url(add_query_arg( NULL, NULL )).PHP_EOL;
2457
-
2458
- # generate cache, add inline css, write log
2459
- if(!empty($code)) {
2460
- file_put_contents($file.'.txt', $log);
2461
- file_put_contents($file, $code); # preserve style tags
2462
- file_put_contents($file.'.gz', gzencode(file_get_contents($file), 9));
2463
-
2464
- # permissions
2465
- fastvelocity_fix_permission_bits($file.'.txt');
2466
- fastvelocity_fix_permission_bits($file);
2467
- fastvelocity_fix_permission_bits($file.'.gz');
2468
-
2469
- # brotli static support
2470
- if(function_exists('brotli_compress')) {
2471
- file_put_contents($file.'.br', brotli_compress(file_get_contents($file), 11));
2472
- fastvelocity_fix_permission_bits($file.'.br');
2473
- }
2474
- }
2475
- }
2476
-
2477
- # register and enqueue minified file, consider excluding of mediatype "print" and inline css
2478
- if ($remove_print_mediatypes != true || ($remove_print_mediatypes == true && $footer[$i]['media'] != 'print')) {
2479
-
2480
- # the developers tab, takes precedence
2481
-
2482
- # Async CSS with loadCSS ?
2483
- if($fvmloadcss != false && $fvm_remove_css != true) {
2484
- $mt = $footer[$i]['media'];
2485
- echo '<link rel="preload" href="'.$file_url.'" as="style" media="'.$mt.'" onload="this.onload=null;this.rel=\'stylesheet\'" />';
2486
- echo '<noscript><link rel="stylesheet" href="'.$file_url.'" media="'.$mt.'" /></noscript>';
2487
- echo '<!--[if IE]><link rel="stylesheet" href="'.$file_url.'" media="'.$mt.'" /><![endif]-->';
2488
-
2489
- # enqueue file, if not empty
2490
- } else {
2491
- if(file_exists($file) && filesize($file) > 0) {
2492
-
2493
- # inline if the file is smaller than 20KB or option has been enabled
2494
- if(filesize($file) < 20000 || $force_inline_css_footer != false) {
2495
- echo '<style id="fvm-footer-'.$i.'" media="'.$footer[$i]['media'].'">'.file_get_contents($file).'</style>';
2496
- } else {
2497
- # enqueue it
2498
- wp_enqueue_style("fvm-footer-$i", $file_url, array(), null, $footer[$i]['media']);
2499
- }
2500
- } else {
2501
- # file could not be generated, output something meaningful
2502
- echo "<!-- ERROR: FVM was not allowed to save it's cache on - $file -->";
2503
- echo "<!-- Please check if the path above is correct and ensure your server has writting permission there! -->";
2504
- echo "<!-- If you found a bug, please report this on https://wordpress.org/support/plugin/fast-velocity-minify/ -->";
2505
- }
2506
- }
2507
- }
2508
-
2509
- # other css need to be requeued for the order of files to be kept
2510
- } else {
2511
- wp_dequeue_style($footer[$i]['handle']);
2512
- wp_enqueue_style($footer[$i]['handle']);
2513
- }
2514
- }
2515
-
2516
- # remove from queue
2517
- $wp_styles->done = $done;
2518
- }
2519
- ###########################################
2520
-
2521
-
2522
-
2523
-
2524
- ###########################################
2525
- # defer CSS globally from the header (order matters)
2526
- # dev: https://www.filamentgroup.com/lab/async-css.html
2527
- ###########################################
2528
- function fvm_add_loadcss() {
2529
-
2530
- echo <<<EOF
2531
- <script>
2532
- /* loadCSS. [c]2017 Filament Group, Inc. MIT License */
2533
- (function(w){if(!w.loadCSS)w.loadCSS=function(){};var rp=loadCSS.relpreload={};rp.support=function(){var ret;try{ret=w.document.createElement("link").relList.supports("preload")}catch(e){ret=false}return function(){return ret}}();rp.bindMediaToggle=function(link){var finalMedia=link.media||"all";function enableStylesheet(){if(link.addEventListener)link.removeEventListener("load",enableStylesheet);else if(link.attachEvent)link.detachEvent("onload",enableStylesheet);link.setAttribute("onload",null);link.media=finalMedia}if(link.addEventListener)link.addEventListener("load",enableStylesheet);else if(link.attachEvent)link.attachEvent("onload",enableStylesheet);setTimeout(function(){link.rel="stylesheet";link.media="only x"});setTimeout(enableStylesheet,3E3)};rp.poly=function(){if(rp.support())return;var links=w.document.getElementsByTagName("link");for(var i=0;i<links.length;i++){var link=links[i];if(link.rel==="preload"&&link.getAttribute("as")==="style"&&!link.getAttribute("data-loadcss")){link.setAttribute("data-loadcss", true);rp.bindMediaToggle(link)}}};if(!rp.support()){rp.poly();var run=w.setInterval(rp.poly,500);if(w.addEventListener)w.addEventListener("load",function(){rp.poly();w.clearInterval(run)});else if(w.attachEvent)w.attachEvent("onload",function(){rp.poly();w.clearInterval(run)})}if(typeof exports!=="undefined")exports.loadCSS=loadCSS;else w.loadCSS=loadCSS})(typeof global!=="undefined"?global:this);
2534
- </script>
2535
- EOF;
2536
-
2537
- }
2538
-
2539
- # fvm load async scripts with callback
2540
- function fvm_add_loadasync() {
2541
- global $fvm_min_excludejslist;
2542
- if($fvm_min_excludejslist != false && is_array($fvm_min_excludejslist) && count($fvm_min_excludejslist) > 0) {
2543
-
2544
- echo <<<EOF
2545
- <script>function loadAsync(e,a){var t=document.createElement("script");t.src=e,null!==a&&(t.readyState?t.onreadystatechange=function(){"loaded"!=t.readyState&&"complete"!=t.readyState||(t.onreadystatechange=null,a())}:t.onload=function(){a()}),document.getElementsByTagName("head")[0].appendChild(t)}</script>
2546
- EOF;
2547
-
2548
- }
2549
- }
2550
-
2551
-
2552
-
2553
- # add inline CSS code / Critical Path
2554
- function fvm_add_criticial_path() {
2555
- $no_global_critical_path_css = false;
2556
- $critical_path_css = get_option('fastvelocity_min_critical_path_css');
2557
- $critical_path_css_is_front_page = get_option('fastvelocity_min_critical_path_css_is_front_page');
2558
-
2559
- # critical path (is_front_page only)
2560
- if(!empty($critical_path_css_is_front_page) && $critical_path_css_is_front_page !== false) {
2561
- echo '<style id="critical-path-is-front-page" type="text/css" media="all">'.$critical_path_css_is_front_page.'</style>'.PHP_EOL;
2562
- $no_global_critical_path_css = 1;
2563
- }
2564
-
2565
- # global path, except if there's something else more specific
2566
- if(!empty($critical_path_css) && $critical_path_css !== false && $no_global_critical_path_css === false) {
2567
- echo '<style id="critical-path-global" type="text/css" media="all">'.$critical_path_css.'</style>'.PHP_EOL;
2568
- }
2569
- }
2570
-
2571
-
2572
-
2573
-
2574
-
2575
- ###########################################
2576
- # add preconnect and preload headers
2577
- ###########################################
2578
- function fvm_extra_preload_headers() {
2579
-
2580
- # fetch headers
2581
- $preload = array_map('trim', explode(PHP_EOL, get_option('fastvelocity_min_hpreload')));
2582
- $preconnect = array_map('trim', explode(PHP_EOL, get_option('fastvelocity_min_hpreconnect')));
2583
-
2584
- # preconnect
2585
- if(is_array($preload) && count($preload) > 0) {
2586
- foreach ($preload as $h) {
2587
- if(!empty($h)) {
2588
- header($h, false);
2589
- }
2590
- }
2591
- }
2592
-
2593
- # preload
2594
- if(is_array($preconnect) && count($preconnect) > 0) {
2595
- foreach ($preconnect as $url) {
2596
- if(!empty($url) && filter_var($url, FILTER_VALIDATE_URL)) {
2597
- header("Link: <$url>; rel=preconnect", false);
2598
- }
2599
- }
2600
- }
2601
-
2602
- # fvm css and js generated files
2603
- $fvm_headers = fastvelocity_get_preload_headers();
2604
- if($fvm_headers != false) {
2605
- $nh = array_map('trim', explode(PHP_EOL, $fvm_headers));
2606
- foreach ($nh as $h) {
2607
- if(!empty($h)) {
2608
- header($h, false);
2609
- }
2610
- }
2611
- }
2612
-
2613
- }
2614
-
2615
-
2616
-
2617
- # inline css in place, instead of inlining the large file
2618
- function fastvelocity_optimizecss($html, $handle, $href, $media){
2619
- global $fvm_debug, $wp_domain, $wp_home, $force_inline_css, $fvmualist, $fvm_collect_google_fonts, $force_inline_googlefonts, $min_async_googlefonts, $remove_googlefonts, $skip_google_fonts, $css_hide_googlefonts, $remove_print_mediatypes, $ignore, $blacklist, $ignorelist, $wp_home, $fvmloadcss, $fvm_remove_css, $fvm_cdn_url, $disable_minification, $fvm_min_excludecsslist, $disable_css_minification, $fvm_fix_editor, $fvm_fawesome_method;
2620
-
2621
- # current timestamp
2622
- $ctime = get_option('fvm-last-cache-update', '0');
2623
-
2624
- # make sure href is complete
2625
- $href = fastvelocity_min_get_hurl($href, $wp_domain, $wp_home);
2626
-
2627
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Inline CSS processing start $handle / $href -->" . PHP_EOL; }
2628
-
2629
- # prevent optimization for these locations
2630
- if(fastvelocity_exclude_contents()) {
2631
- return $html;
2632
- }
2633
-
2634
- # skip all this, if the async css option is enabled
2635
- if($fvmloadcss != false) {
2636
- return $html;
2637
- }
2638
-
2639
- # remove all css?
2640
- if($fvm_remove_css != false) {
2641
- return false;
2642
- }
2643
-
2644
- # leave conditionals alone
2645
- $conditional = wp_styles()->get_data($handle, 'conditional');
2646
- if($conditional != false) {
2647
- return $html;
2648
- }
2649
-
2650
- # mediatype fix for some plugins + remove print mediatypes
2651
- if ($media == 'screen' || $media == 'screen, print' || empty($media) || is_null($media) || $media == false) { $media = 'all'; }
2652
- if($remove_print_mediatypes != false && $media == 'print') {
2653
- return false;
2654
- }
2655
-
2656
- # Exclude specific CSS files from PSI?
2657
- if($fvm_min_excludecsslist != false && is_array($fvm_min_excludecsslist) && fastvelocity_min_in_arrayi($href, $fvm_min_excludecsslist)) {
2658
- $cssguid = 'fvm'.hash('sha1', $href);
2659
- echo '<script type="text/javascript">if(fvmuag()){';
2660
- echo 'var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$href.'",'.$cssguid.'.onload=function(){'.$cssguid.'.media="'.$media.'"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');';
2661
- echo '}</script>';
2662
- return false;
2663
- }
2664
-
2665
- # remove FVM from the ignore list
2666
- array_filter($ignore, function ($var) { return (stripos($var, '/fvm/') === false); });
2667
-
2668
- # return if in any ignore or black list
2669
- if (count($ignore) > 0 && fastvelocity_min_in_arrayi($href, $ignore) || count($blacklist) > 0 && fastvelocity_min_in_arrayi($href, $blacklist) || count($ignorelist) > 0 && fastvelocity_min_in_arrayi($href, $ignorelist)) {
2670
- return $html;
2671
- }
2672
-
2673
- # remove google fonts completely?
2674
- if($remove_googlefonts != false && stripos($href, 'fonts.googleapis.com') !== false) {
2675
- return false;
2676
- }
2677
-
2678
- # handle google fonts here, when merging is disabled
2679
- if(stripos($href, 'fonts.googleapis.com') !== false && $skip_google_fonts != false) {
2680
-
2681
- # hide google fonts from PSI
2682
- if($css_hide_googlefonts == true) {
2683
- $cssguid = 'fvm'.hash('sha1', $href);
2684
- echo '<script type="text/javascript">if(fvmuag()){';
2685
- echo 'var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$href.'",'.$cssguid.'.onload=function(){'.$cssguid.'.media="all"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');';
2686
- echo '}</script>';
2687
- return false;
2688
- }
2689
-
2690
- # load google fonts async
2691
- if($min_async_googlefonts != false) {
2692
- echo '<link rel="preload" href="'.$href.'" as="style" media="all" onload="this.onload=null;this.rel=\'stylesheet\'" />';
2693
- echo '<noscript><link rel="stylesheet" href="'.$href.'" media="all" /></noscript>';
2694
- echo '<!--[if IE]><link rel="stylesheet" href="'.$href.'" media="all" /><![endif]-->';
2695
- return false;
2696
- }
2697
- }
2698
-
2699
- # font awesome processing, async css
2700
- if($fvm_fawesome_method == 2 && stripos($href, 'font-awesome') !== false) {
2701
- echo '<link rel="preload" href="'.$href.'" as="style" media="'.$media.'" onload="this.onload=null;this.rel=\'stylesheet\'" />';
2702
- echo '<noscript><link rel="stylesheet" href="'.$href.'" media="'.$media.'" /></noscript>';
2703
- echo '<!--[if IE]><link rel="stylesheet" href="'.$href.'" media="'.$media.'" /><![endif]-->';
2704
- return false;
2705
- }
2706
-
2707
- # font awesome processing, async and exclude from PSI
2708
- if($fvm_fawesome_method == 3 && stripos($href, 'font-awesome') !== false) {
2709
- $cssguid = 'fvm'.hash('sha1', $href);
2710
- echo '<script type="text/javascript">if(fvmuag()){';
2711
- echo 'var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$href.'",'.$cssguid.'.onload=function(){'.$cssguid.'.media="'.$media.'"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');';
2712
- echo '}</script>';
2713
- return false;
2714
- }
2715
-
2716
- # font awesome processing, inline
2717
- if($fvm_fawesome_method == 1 && stripos($href, 'font-awesome') !== false) {
2718
-
2719
- # download, minify, cache
2720
- $tkey = 'css-'.hash('sha1', $handle.$href).'.css';
2721
- $json = false; $json = fvm_get_transient($tkey);
2722
- if ( $json === false) {
2723
- $json = fvm_download_and_minify($href, null, $disable_css_minification, 'css', $handle);
2724
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Uncached file processing now for $handle / $href -->" . PHP_EOL; }
2725
- fvm_set_transient($tkey, $json);
2726
- }
2727
-
2728
- # decode
2729
- $res = json_decode($json, true);
2730
-
2731
- # add font-display
2732
- # https://developers.google.com/web/updates/2016/02/font-display
2733
- $res['code'] = str_ireplace('font-style:normal;', 'font-display:block;font-style:normal;', $res['code']);
2734
-
2735
- # inline css or fail
2736
- if($res['status'] != false) {
2737
- echo '<style type="text/css" media="all">'.$res['code'].'</style>'.PHP_EOL;
2738
- return false;
2739
- } else {
2740
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Font Awesome request failed for $href -->" . PHP_EOL; }
2741
- return $html;
2742
- }
2743
- }
2744
-
2745
- # inline google fonts, do not collect
2746
- if(stripos($href, 'fonts.googleapis.com') !== false && $force_inline_googlefonts != false && $css_hide_googlefonts != true && $min_async_googlefonts != true) {
2747
-
2748
- # download, minify, cache
2749
- $tkey = 'css-'.hash('sha1', $handle.$href).'.css';
2750
- $json = false; $json = fvm_get_transient($tkey);
2751
- if ( $json === false) {
2752
- $json = fvm_download_and_minify($href, null, $disable_css_minification, 'css', $handle);
2753
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Uncached file processing now for $handle / $href -->" . PHP_EOL; }
2754
- fvm_set_transient($tkey, $json);
2755
- }
2756
-
2757
- # decode
2758
- $res = json_decode($json, true);
2759
-
2760
- # add font-display
2761
- # https://developers.google.com/web/updates/2016/02/font-display
2762
- $res['code'] = str_ireplace('font-style:normal;', 'font-display:block;font-style:normal;', $res['code']);
2763
-
2764
- # inline css or fail
2765
- if($res['status'] != false) {
2766
- echo '<style type="text/css" media="all">'.$res['code'].'</style>'.PHP_EOL;
2767
- return false;
2768
- } else {
2769
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Google fonts request failed for $href -->" . PHP_EOL; }
2770
- return $html;
2771
- }
2772
- }
2773
-
2774
- # collect and remove google fonts for merging
2775
- if(stripos($href, 'fonts.googleapis.com') !== false){
2776
- $fvm_collect_google_fonts[$handle] = $href;
2777
- return false;
2778
- }
2779
-
2780
- # skip external scripts that are not specifically allowed
2781
- if (fvm_internal_url($href, $wp_home) === false || empty($href)) {
2782
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Skipped the next external enqueued CSS -->" . PHP_EOL; }
2783
- return $html;
2784
- }
2785
-
2786
- # download, minify, cache
2787
- $tkey = 'css-'.hash('sha1', $handle.$href).'.css';
2788
- $json = false; $json = fvm_get_transient($tkey);
2789
- if ( $json === false) {
2790
- $json = fvm_download_and_minify($href, null, $disable_css_minification, 'css', $handle);
2791
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: Uncached file processing now for $handle / $href -->" . PHP_EOL; }
2792
- fvm_set_transient($tkey, $json);
2793
- }
2794
-
2795
- # decode
2796
- $res = json_decode($json, true);
2797
-
2798
- # inline it + other inlined children styles
2799
- if($res['status'] != false) {
2800
- echo '<style type="text/css" media="'.$media.'">'.$res['code'].'</style>';
2801
-
2802
- # get inline_styles for this handle, minify and print
2803
- $inline_styles = array();
2804
- $inline_styles = wp_styles()->get_data( $handle, 'after' );
2805
- if($inline_styles != false) {
2806
-
2807
- # string type
2808
- if(is_string($inline_styles)) {
2809
- $code = fastvelocity_min_get_css($href, $inline_styles, $disable_css_minification);
2810
- if(!empty($code) && $code != false) {
2811
- echo '<style type="text/css" media="'.$media.'">'.$code.'</style>';
2812
- }
2813
- }
2814
-
2815
- # array type
2816
- if(is_array($inline_styles)) {
2817
- foreach ($inline_styles as $st) {
2818
- $code = fastvelocity_min_get_css($href, $st, $disable_css_minification);
2819
- if(!empty($code) && $code != false) {
2820
- echo '<style type="text/css" media="'.$media.'">'.$code.'</style>';
2821
- }
2822
- }
2823
- }
2824
- }
2825
-
2826
- # prevent default
2827
- return false;
2828
-
2829
- } else {
2830
- if($fvm_debug == true) { echo "<!-- FVM DEBUG: $handle / $href returned an empty from minification -->" . PHP_EOL; }
2831
- return $html;
2832
- }
2833
-
2834
- # fallback, for whatever reason
2835
- echo "<!-- ERROR: FVM couldn't catch the CSS file below. Please report this on https://wordpress.org/support/plugin/fast-velocity-minify/ -->";
2836
- return $html;
2837
- }
2838
-
2839
-
2840
- # critical css for the page
2841
- function fastvelocity_add_google_fonts_merged() {
2842
- global $fvm_collect_google_fonts, $fvmualist, $css_hide_googlefonts, $skip_google_fonts, $min_async_googlefonts, $fvm_debug;
2843
-
2844
- # prevent optimization for logged in users
2845
- if (is_admin() || is_preview() || is_customize_preview()) {
2846
- return false;
2847
- }
2848
-
2849
- # must have something to do
2850
- if(!is_array($fvm_collect_google_fonts) || count($fvm_collect_google_fonts) == 0) {
2851
- return false;
2852
- }
2853
-
2854
- # merge google fonts
2855
- $gfurl = fastvelocity_min_concatenate_google_fonts($fvm_collect_google_fonts);
2856
- if(empty($gfurl)) {
2857
- return false;
2858
- }
2859
-
2860
- # hide google fonts from PSI
2861
- if($css_hide_googlefonts == true) {
2862
-
2863
- # make a stylesheet, hide from PSI
2864
- $cssguid = 'fvm'.hash('sha1', $gfurl);
2865
- echo '<script type="text/javascript">if(fvmuag()){';
2866
- echo 'var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$gfurl.'",'.$cssguid.'.onload=function(){'.$cssguid.'.media="all"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');';
2867
- echo '}</script>';
2868
-
2869
- # load google fonts async
2870
- } elseif($min_async_googlefonts != false) {
2871
- echo '<link rel="preload" href="'.$gfurl.'" as="style" media="all" onload="this.onload=null;this.rel=\'stylesheet\'" />';
2872
- echo '<noscript><link rel="stylesheet" href="'.$gfurl.'" media="all" /></noscript>';
2873
- echo '<!--[if IE]><link rel="stylesheet" href="'.$gfurl.'" media="all" /><![endif]-->';
2874
-
2875
- # fallback to normal inline
2876
- } else {
2877
- echo '<link rel="stylesheet" href="'.$gfurl.'" media="all" />';
2878
- }
2879
-
2880
- # unset per hook
2881
- foreach($fvm_collect_google_fonts as $k=>$v) {
2882
- unset($fvm_collect_google_fonts[$k]);
2883
- }
2884
-
2885
- }
2886
-
2887
-
2888
-
2889
- # collect all fvm JS files and save them to an headers file
2890
- function fastvelocity_collect_js_preload_headers($html, $handle, $src){
2891
- global $cachedirurl, $collect_preload_js, $fvm_enabled_css_preload, $fvm_enabled_js_preload;
2892
-
2893
- # return if disabled
2894
- if ($fvm_enabled_js_preload != true) {
2895
- return $html;
2896
- }
2897
-
2898
- # collect
2899
- if (stripos($src, $cachedirurl) !== false) {
2900
- $collect_preload_js[] = $src;
2901
- }
2902
- return $html;
2903
- }
2904
-
2905
- # generate preload headers file
2906
- function fastvelocity_generate_preload_headers(){
2907
- global $cachedirurl, $collect_preload_css, $collect_preload_js, $fvm_enabled_css_preload, $fvm_enabled_js_preload;
2908
-
2909
- # return if disabled
2910
- if ($fvm_enabled_css_preload != true && $fvm_enabled_js_preload != true) {
2911
- return false;
2912
- }
2913
-
2914
- # get host with multisite support and query strings
2915
- $host = htmlentities($_SERVER['SERVER_NAME']);
2916
- if(empty($hosts)) { $host = htmlentities($_SERVER['HTTP_HOST']); }
2917
- $request_query = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY);
2918
- $request_uri = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
2919
- $is_admin = strpos( $request_uri, '/wp-admin/' );
2920
-
2921
- # always false for admin pages
2922
- if( false !== $is_admin){
2923
- return false;
2924
- }
2925
-
2926
- # initialize headers
2927
- $headers = array();
2928
-
2929
- # css headers
2930
- if ($fvm_enabled_css_preload != false && is_array($collect_preload_css) && count($collect_preload_css) > 0) {
2931
- foreach($collect_preload_css as $u) {
2932
-
2933
- # filter out footer footer files, because they are not in the critical path
2934
- if(stripos($u, $cachedirurl . '/footer-') !== false) { continue; }
2935
-
2936
- # add headers
2937
- $headers[] = "Link: <$u>; rel=preload; as=style";
2938
- }
2939
- }
2940
-
2941
- # js headers
2942
- if ($fvm_enabled_js_preload != false && is_array($collect_preload_js) && count($collect_preload_js) > 0) {
2943
- foreach($collect_preload_js as $u) {
2944
-
2945
- # filter out footer footer files, because they are not in the critical path
2946
- if(stripos($u, $cachedirurl . '/footer-') !== false) { continue; }
2947
-
2948
- # add headers
2949
- $headers[] = "Link: <$u>; rel=preload; as=script";
2950
- }
2951
- }
2952
-
2953
- # must have something
2954
- if(count($headers) == 0) {
2955
- return false;
2956
- } else {
2957
- $headers = implode(PHP_EOL, $headers);
2958
- }
2959
-
2960
- # get cache path
2961
- $cachepath = fvm_cachepath();
2962
- $headerdir = $cachepath['headerdir'];
2963
- $cachefilebase = $headerdir.'/';
2964
-
2965
- # possible cache file locations
2966
- $b = $cachefilebase . md5($host.'-'.$request_uri).'.header';
2967
- $a = $cachefilebase . md5($host.'-'.$request_uri.$request_query).'.header';
2968
-
2969
- # reset file cache
2970
- clearstatcache();
2971
-
2972
- # if there are no query strings
2973
- if($b == $a) {
2974
- if(!file_exists($a)) {
2975
- file_put_contents($a, $headers);
2976
- fastvelocity_fix_permission_bits($a);
2977
- }
2978
- return false;
2979
- }
2980
-
2981
- # b fallback
2982
- if($b != $a && !file_exists($b)) {
2983
- file_put_contents($b, $headers);
2984
- fastvelocity_fix_permission_bits($b);
2985
- }
2986
-
2987
- return false;
2988
- }
2989
-
2990
-
2991
- # get current headers file for the url
2992
- function fastvelocity_get_preload_headers(){
2993
- global $fvm_enabled_css_preload, $fvm_enabled_js_preload;
2994
-
2995
- # return if disabled
2996
- if ($fvm_enabled_css_preload != true && $fvm_enabled_js_preload != true) {
2997
- return false;
2998
- }
2999
-
3000
- # get host with multisite support and query strings
3001
- $host = htmlentities($_SERVER['SERVER_NAME']);
3002
- if(empty($hosts)) { $host = htmlentities($_SERVER['HTTP_HOST']); }
3003
- $request_query = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY);
3004
- $request_uri = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
3005
- $is_admin = strpos( $request_uri, '/wp-admin/' );
3006
-
3007
- # always false for admin pages
3008
- if( false !== $is_admin){
3009
- return false;
3010
- }
3011
-
3012
- # get cache path
3013
- $cachepath = fvm_cachepath();
3014
- $headerdir = $cachepath['headerdir'];
3015
- $cachefilebase = $headerdir.'/';
3016
-
3017
- # possible cache file locations
3018
- $b = $cachefilebase . md5($host.'-'.$request_uri).'.header';
3019
- $a = $cachefilebase . md5($host.'-'.$request_uri.$request_query).'.header';
3020
-
3021
- # reset file cache
3022
- clearstatcache();
3023
-
3024
- # return header files or fallback
3025
- if($b == $a && file_exists($a)) { return file_get_contents($a); }
3026
- if($b != $a && file_exists($b)) { return file_get_contents($b); }
3027
-
3028
- return false;
3029
- }
3030
-
3031
-
3032
-
3033
- # cron job to delete old FVM cache
3034
- add_action('fastvelocity_purge_old_cron_event', 'fvm_purge_old');
3035
-
5
  Description: Improve your speed score on GTmetrix, Pingdom Tools and Google PageSpeed Insights by merging and minifying CSS and JavaScript files into groups, compressing HTML and other speed optimizations.
6
  Author: Raul Peixoto
7
  Author URI: http://fastvelocity.com
8
+ Version: 3.0.0
9
  License: GPL2
10
 
11
  ------------------------------------------------------------------------
24
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
  */
26
 
27
+ # Exit if accessed directly
28
+ if (!defined('ABSPATH')){ exit(); }
 
 
 
 
 
 
 
 
 
29
 
30
+ # Invalidate OPCache for current file on WP 5.5+
31
+ if(function_exists('wp_opcache_invalidate') && stripos(__FILE__, '/fvm.php') !== false) {
32
+ wp_opcache_invalidate(__FILE__, true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
 
34
 
35
+ # info, variables, paths
36
+ $fvm_var_file = __FILE__; # /home/path/plugins/pluginname/wpr.php
37
+ $fvm_var_basename = plugin_basename($fvm_var_file); # pluginname/wpr.php
38
+ $fvm_var_dir_path = plugin_dir_path($fvm_var_file); # /home/path/plugins/pluginname/
39
+ $fvm_var_url_path = plugins_url(dirname($fvm_var_basename)) . '/'; # https://example.com/wp-content/plugins/pluginname/
40
+ $fvm_var_plugin_version = get_file_data($fvm_var_file, array('Version' => 'Version'), false)['Version'];
41
+ $fvm_var_inc_dir = $fvm_var_dir_path . DIRECTORY_SEPARATOR . 'inc' . DIRECTORY_SEPARATOR; # /home/path/plugins/pluginname/inc/
42
+ $fvm_var_inc_lib = $fvm_var_dir_path . DIRECTORY_SEPARATOR . 'libs' . DIRECTORY_SEPARATOR; # /home/path/plugins/pluginname/libs/
43
 
44
+ # global functions for backend, frontend, ajax, etc
45
+ require_once($fvm_var_inc_dir . 'common.php');
46
+ require_once($fvm_var_inc_dir . 'updates.php');
 
 
 
 
 
 
 
 
 
47
 
48
  # wp-cli support
49
+ if (defined('WP_CLI') && WP_CLI) {
50
+ require_once($fvm_var_inc_dir . 'wp-cli.php');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
 
53
+ # get all options from database
54
+ $fvm_settings = fvm_get_settings();
55
 
56
+ # get cache paths and info
57
+ $fvm_cache_paths = fvm_cachepath();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ # site url, domain name
60
+ $fvm_urls = array('wp_home'=>site_url(), 'wp_domain'=>fvm_get_domain());
61
 
62
 
63
+ # only on backend
64
  if(is_admin()) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
+ # admin functionality
67
+ require_once($fvm_var_inc_dir . 'admin.php');
68
+ require_once($fvm_var_inc_dir . 'serverinfo.php');
69
 
70
+ # both backend and frontend, as long as user can manage options
71
+ add_action('admin_bar_menu', 'fvm_admintoolbar', 100);
72
+ add_action('init', 'fvm_process_cache_purge_request');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
+ # do admin stuff, as long as user can manage options
75
+ add_action('admin_init', 'fvm_save_settings');
76
+ add_action('admin_init', 'fvm_check_minimum_requirements');
77
+ add_action('admin_init', 'fvm_check_misconfiguration');
78
+ add_action('admin_enqueue_scripts', 'fvm_add_admin_jscss');
79
+ add_action('admin_menu', 'fvm_add_admin_menu');
80
+ add_action('admin_notices', 'fvm_show_admin_notice_from_transient');
81
+ add_action('wp_ajax_fvm_get_logs', 'fvm_get_logs_callback');
82
 
83
+ # purge everything
84
+ add_action('switch_theme', 'fvm_purge_all');
85
+ add_action('customize_save', 'fvm_purge_all');
86
+ add_action('avada_clear_dynamic_css_cache', 'fvm_purge_all');
87
+ add_action('upgrader_process_complete', 'fvm_purge_all');
88
+ add_action('update_option_theme_mods_' . get_option('stylesheet'), 'fvm_purge_all');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  }
91
 
92
 
 
 
 
 
 
 
 
 
93
 
94
+ # frontend only, any user permissions
95
+ if(!is_admin()) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
+ # frontend functionality
98
+ require_once($fvm_var_inc_dir . 'frontend.php');
99
 
100
+ # both back and front, as long as the option is enabled
101
+ add_action('init', 'fvm_disable_emojis');
102
+ add_action('wp_loaded', 'fvm_ajax_optimizer');
103
 
104
+ # both backend and frontend, as long as user can manage options
105
+ add_action('admin_bar_menu', 'fvm_admintoolbar', 100);
106
+ add_action('init', 'fvm_process_cache_purge_request');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
+ # actions for frontend only
109
+ add_action('template_redirect', 'fvm_start_buffer', -PHP_INT_MAX);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  }
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/admin.php ADDED
@@ -0,0 +1,494 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ # Exit if accessed directly
4
+ if (!defined('ABSPATH')){ exit(); }
5
+
6
+ # check for minimum requirements and prevent activation or disable if not fully compatible
7
+ function fvm_check_minimum_requirements() {
8
+ if(current_user_can('manage_options')) {
9
+
10
+ # defaults
11
+ $error = '';
12
+
13
+ # php version requirements
14
+ if (version_compare( PHP_VERSION, '5.6', '<' )) {
15
+ $error = 'FVM requires PHP 5.6 or higher. You’re still on '. PHP_VERSION;
16
+ }
17
+
18
+ # php extension requirements
19
+ if (!extension_loaded('mbstring')) {
20
+ $error = 'FVM requires the PHP mbstring module to be installed on the server.';
21
+ }
22
+
23
+ # wp version requirements
24
+ if ( version_compare( $GLOBALS['wp_version'], '4.5', '<' ) ) {
25
+ $error = 'FVM requires WP 4.5 or higher. You’re still on ' . $GLOBALS['wp_version'];
26
+ }
27
+
28
+ # cache permissions
29
+ global $fvm_cache_paths;
30
+ if(is_dir($fvm_cache_paths['cache_base_dir']) && !is_writable($fvm_cache_paths['cache_base_dir'])) {
31
+ $error = 'FVM needs writing permissions on '.$fvm_cache_paths['cache_base_dir'];
32
+ }
33
+
34
+ # deactivate plugin forcefully
35
+ global $fvm_var_basename;
36
+ if ((is_plugin_active($fvm_var_basename) && !empty($error)) || !empty($error)) {
37
+ if (isset($_GET['activate'])) { unset($_GET['activate']); }
38
+ deactivate_plugins($fvm_var_basename);
39
+ add_settings_error( 'fvm_admin_notice', 'fvm_admin_notice', $error, 'success' );
40
+ }
41
+
42
+ }
43
+ }
44
+
45
+
46
+ # check for soft errors and misconfiguration
47
+ function fvm_check_misconfiguration() {
48
+ if(current_user_can('manage_options')) {
49
+
50
+ global $fvm_settings, $fvm_cache_paths;
51
+
52
+ # check if custom cache directory exists
53
+ if(isset($fvm_settings['cache']['path']) && !empty($fvm_settings['cache']['path']) && !is_dir($fvm_settings['cache']['path']) && !is_writeable($fvm_settings['cache']['path'])) {
54
+ add_settings_error( 'fvm_admin_notice', 'fvm_admin_notice', 'FVM needs writing permissions on '.$fvm_settings['cache']['path'] , 'success' );
55
+ }
56
+
57
+ # cache permissions
58
+ if(!is_dir($fvm_cache_paths['cache_base_dir']) && !is_writeable($fvm_settings['cache']['path'])) {
59
+ $error = 'FVM needs writing permissions on '.$fvm_cache_paths['cache_base_dir'];
60
+ }
61
+
62
+ # initialize database routine if not available
63
+ fvm_initialize_database();
64
+
65
+ }
66
+ }
67
+
68
+
69
+
70
+
71
+ # save plugin settings on wp-admin
72
+ function fvm_save_settings() {
73
+
74
+ # save settings
75
+ if(isset($_POST['fvm_action']) && isset($_POST['fvm_settings_nonce']) && $_POST['fvm_action'] == 'save_settings') {
76
+
77
+ if(!current_user_can('manage_options')) {
78
+ wp_die( __('You do not have sufficient permissions to access this page.'), __('Error:'), array('response'=>200));
79
+ }
80
+
81
+ if(!wp_verify_nonce($_POST['fvm_settings_nonce'], 'fvm_settings_nonce')) {
82
+ wp_die( __('Invalid nounce. Please refresh and try again.'), __('Error:'), array('response'=>200));
83
+ }
84
+
85
+ # update fvm_settings in the global scope
86
+ if(isset($_POST['fvm_settings']) && is_array($_POST['fvm_settings'])) {
87
+
88
+ # sanitize recursively
89
+ if(is_array($_POST['fvm_settings'])) {
90
+ foreach ($_POST['fvm_settings'] as $group=>$arr) {
91
+ if(is_array($arr)) {
92
+ foreach ($arr as $k=>$v) {
93
+
94
+ # only numeric, string or arrays allowed at this level
95
+ if(!is_string($v) && !is_numeric($v) && !is_array($v)) { $_POST['fvm_settings'][$group][$k] = ''; }
96
+
97
+ # numeric fields, only positive integers allowed
98
+ if(is_numeric($v)) { $_POST['fvm_settings'][$group][$k] = abs(intval($v)); }
99
+
100
+ # sanitize text area content
101
+ if(is_string($v)) { $_POST['fvm_settings'][$group][$k] = strip_tags($v); }
102
+
103
+ # clean cdn url
104
+ if($group == 'cdn' && $k == 'url') {
105
+ $_POST['fvm_settings'][$group][$k] = trim(trim(str_replace(array('http://', 'https://'), '', $v), '/'));
106
+ }
107
+
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ # get mandatory default exclusions
114
+ global $fvm_settings;
115
+ $fvm_settings = fvm_get_default_settings($_POST['fvm_settings']);
116
+
117
+ # purge caches
118
+ fvm_purge_all();
119
+
120
+ # save settings
121
+ update_option('fvm_settings', json_encode($fvm_settings), false);
122
+ add_settings_error( 'fvm_admin_notice', 'fvm_admin_notice', 'Settings saved successfully!', 'success' );
123
+
124
+ } else {
125
+ wp_die( __('Invalid data!'), __('Error:'), array('response'=>200));
126
+ }
127
+ }
128
+ }
129
+
130
+ # return checked, or empty for checkboxes in admin
131
+ function fvm_get_settings_checkbox($value) {
132
+ if($value == 1) { return 'checked'; }
133
+ return '';
134
+ }
135
+
136
+ # return checked, or empty for checkboxes in admin
137
+ function fvm_get_settings_radio($key, $value) {
138
+ if($key == $value) { return 'checked'; }
139
+ return '';
140
+ }
141
+
142
+
143
+ # add settings link on plugins listing page
144
+ add_filter("plugin_action_links_".$fvm_var_basename, 'fvm_min_settings_link' );
145
+ function fvm_min_settings_link($links) {
146
+ global $fvm_var_basename;
147
+ if (is_plugin_active($fvm_var_basename)) {
148
+ $settings_link = '<a href="'.admin_url('options-general.php?page=fvm').'">Settings</a>';
149
+ array_unshift($links, $settings_link);
150
+ }
151
+ return $links;
152
+ }
153
+
154
+
155
+ # Enqueue plugin UI CSS and JS files
156
+ function fvm_add_admin_jscss($hook) {
157
+ if(current_user_can('manage_options')) {
158
+ if ('settings_page_fvm' != $hook) { return; }
159
+ global $fvm_var_dir_path, $fvm_var_url_path;
160
+
161
+ # ui
162
+ wp_enqueue_script( 'jquery-ui-core' );
163
+ wp_enqueue_script( 'jquery-ui-accordion' );
164
+
165
+ # js
166
+ wp_enqueue_script('fvm', $fvm_var_url_path . 'assets/fvm.js', array('jquery'), filemtime($fvm_var_dir_path.'assets'. DIRECTORY_SEPARATOR .'fvm.js'));
167
+
168
+ # css
169
+ wp_enqueue_style('fvm', $fvm_var_url_path . 'assets/fvm.css', array(), filemtime($fvm_var_dir_path.'assets'. DIRECTORY_SEPARATOR .'fvm.css'));
170
+
171
+ }
172
+ }
173
+
174
+
175
+ # create sidebar admin menu and add templates to admin
176
+ function fvm_add_admin_menu() {
177
+ if (current_user_can('manage_options')) {
178
+ add_options_page('FVM Settings', 'Fast Velocity Minify', 'manage_options', 'fvm', 'fvm_add_settings_admin');
179
+ }
180
+ }
181
+
182
+
183
+ # print admin notices when needed (json)
184
+ function fvm_show_admin_notice_from_transient() {
185
+ if(current_user_can('manage_options')) {
186
+ $inf = get_transient('fvm_admin_notice');
187
+ if($inf != false && !empty($inf)) {
188
+ $jsonarr = json_decode($inf, true);
189
+ if(!is_null($jsonarr) && is_array($jsonarr)){
190
+ foreach ($jsonarr as $notice) {
191
+ add_settings_error( 'fvm_admin_notice', 'fvm_admin_notice', $notice, 'info' );
192
+ }
193
+ }
194
+
195
+ # remove
196
+ delete_transient('fvm_admin_notice');
197
+ }
198
+ }
199
+ }
200
+
201
+ # manage settings page
202
+ function fvm_add_settings_admin() {
203
+
204
+ # admin only
205
+ if (!current_user_can('manage_options')) {
206
+ wp_die( __('You do not have sufficient permissions to access this page.'), __('Error:'), array('response'=>200));
207
+ }
208
+
209
+ # include admin html template
210
+ global $fvm_cache_paths, $fvm_settings, $fvm_var_dir_path;
211
+
212
+ # admin html templates
213
+ include($fvm_var_dir_path . 'layout' . DIRECTORY_SEPARATOR . 'admin-layout.php');
214
+
215
+ }
216
+
217
+
218
+ # function to list all cache files on the status page (js ajax code)
219
+ function fvm_get_logs_callback() {
220
+
221
+ # must be able to cleanup cache
222
+ if (!current_user_can('manage_options')) {
223
+ wp_die( __('You do not have sufficient permissions to access this page.'), __('Error:'), array('response'=>200));
224
+ }
225
+
226
+ # must have
227
+ if(!defined('WP_CONTENT_DIR')) {
228
+ wp_die( __('WP_CONTENT_DIR is undefined!'), __('Error:'), array('response'=>200));
229
+ }
230
+
231
+ # get info
232
+ global $fvm_cache_paths;
233
+
234
+ # must have valid cache paths
235
+ if(isset($fvm_cache_paths['cache_dir_min']) && !empty($fvm_cache_paths['cache_dir_min'])) {
236
+
237
+ # defaults
238
+ $count_css = 0;
239
+ $count_js = 0;
240
+ $size_css = 0;
241
+ $size_js = 0;
242
+
243
+ # scan min directory recursively
244
+ $errora = false;
245
+ if(is_dir($fvm_cache_paths['cache_dir_min'])) {
246
+ try {
247
+ $i = new DirectoryIterator($fvm_cache_paths['cache_dir_min']);
248
+ foreach($i as $f){
249
+ if($f->isFile()){
250
+
251
+ # javascript
252
+ if(stripos($f->getRealPath(), '.js') !== false) {
253
+ $count_js = $count_js + 1;
254
+ $size_js = $size_js + intval($f->getSize());
255
+ }
256
+
257
+ # css
258
+ if(stripos($f->getRealPath(), '.css') !== false) {
259
+ $count_css = $count_css + 1;
260
+ $size_css = $size_css + intval($f->getSize());
261
+ }
262
+
263
+ }
264
+ }
265
+ } catch (Exception $e) {
266
+ $errora = get_class($e) . ": " . $e->getMessage();
267
+ }
268
+ }
269
+
270
+ # return early if errors
271
+ if($errora != false) {
272
+ header('Content-Type: application/json');
273
+ echo json_encode(array('error' => $errora));
274
+ exit();
275
+ }
276
+
277
+
278
+ # defaults
279
+ global $wpdb;
280
+
281
+ # initialize log
282
+ $css_log = '';
283
+
284
+ # build css logs from database
285
+ $results = $wpdb->get_results($wpdb->prepare("SELECT date, content, meta FROM ".$wpdb->prefix."fvm_logs WHERE type = 'css' ORDER BY id DESC LIMIT 20"));
286
+
287
+ # build second query
288
+ foreach ($results as $log) {
289
+
290
+ # get meta into an array
291
+ $meta = json_decode($log->meta, true);
292
+
293
+ # start log
294
+ $css_log.= '+++++++++' . PHP_EOL;
295
+ $css_log.= 'PROCESSED - ' . date('r', $log->date) . ' - VIA - '. $meta['loc'] . PHP_EOL;
296
+ $css_log.= 'GENERATED - ' . $meta['fl'] . PHP_EOL;
297
+ $css_log.= 'MEDIATYPE - ' . $meta['mt'] . PHP_EOL;
298
+ $css_log.= '---' . PHP_EOL;
299
+
300
+ # generate uid's from json
301
+ $list = array(); $list = json_decode($log->content);
302
+
303
+ # get rows to log file
304
+ if(count($list) > 0) {
305
+ $listuids = implode(', ', array_fill(0, count($list), '%s'));
306
+ if(!empty($listuids)) {
307
+ $rs = array(); $rs = $wpdb->get_results($wpdb->prepare("SELECT meta FROM ".$wpdb->prefix."fvm_cache WHERE uid IN (".$listuids.") ORDER BY FIELD(uid, '".implode("', '", $list)."')", $list));
308
+ foreach ($rs as $r) {
309
+ $imt = json_decode($r->meta, true);
310
+ $css_log.= '[Size: '.str_pad(fvm_format_filesize($imt['fs']), 10,' ',STR_PAD_LEFT).']'."\t". $imt['url'] . PHP_EOL;
311
+ }
312
+ }
313
+ $css_log.= '+++++++++' . PHP_EOL . PHP_EOL;
314
+ }
315
+ }
316
+
317
+ # trim
318
+ $css_log = trim($css_log);
319
+
320
+ # initialize log
321
+ $js_log = '';
322
+
323
+ # build css logs from database
324
+ $results = $wpdb->get_results($wpdb->prepare("SELECT date, content, meta FROM ".$wpdb->prefix."fvm_logs WHERE type = 'js' ORDER BY id DESC LIMIT 20"));
325
+
326
+ # build second query
327
+ foreach ($results as $log) {
328
+
329
+ # get meta into an array
330
+ $meta = json_decode($log->meta, true);
331
+
332
+ # start log
333
+ $js_log.= '+++++++++' . PHP_EOL;
334
+ $js_log.= 'PROCESSED - ' . date('r', $log->date) . ' - VIA - '. $meta['loc'] . PHP_EOL;
335
+ $js_log.= 'GENERATED - ' . $meta['fl'] . PHP_EOL;
336
+ $js_log.= '---' . PHP_EOL;
337
+
338
+ # generate uid's from json
339
+ $list = array(); $list = json_decode($log->content);
340
+
341
+ # get rows to log file
342
+ if(count($list) > 0) {
343
+ $listuids = implode(', ', array_fill(0, count($list), '%s'));
344
+ if(!empty($listuids)) {
345
+ $rs = array(); $rs = $wpdb->get_results($wpdb->prepare("SELECT meta FROM ".$wpdb->prefix."fvm_cache WHERE uid IN (".$listuids.") ORDER BY FIELD(uid, '".implode("', '", $list)."')", $list));
346
+ foreach ($rs as $r) {
347
+ $imt = json_decode($r->meta, true);
348
+ $js_log.= '[Size: '.str_pad(fvm_format_filesize($imt['fs']), 10,' ',STR_PAD_LEFT).']'."\t". $imt['url'] . PHP_EOL;
349
+ }
350
+ }
351
+ $js_log.= '+++++++++' . PHP_EOL . PHP_EOL;
352
+ }
353
+ }
354
+
355
+ # trim
356
+ $js_log = trim($js_log);
357
+
358
+ # default message
359
+ if(empty($css_log)) { $css_log = 'No CSS files generated yet.'; }
360
+ if(empty($js_log)) { $js_log = 'No JS files generated yet.'; }
361
+
362
+ # build info
363
+ $result = array(
364
+ 'stats_total' => array('count'=>($count_css+$count_js), 'size'=>fvm_format_filesize($size_css+$size_js)),
365
+ 'stats_css' => array('count'=>$count_css, 'size'=>fvm_format_filesize($size_css)),
366
+ 'stats_js' => array('count'=>$count_js, 'size'=>fvm_format_filesize($size_js)),
367
+ 'js_log' => $js_log,
368
+ 'css_log' => $css_log,
369
+ 'success' => 'OK'
370
+ );
371
+
372
+ # return result
373
+ header('Content-Type: application/json');
374
+ echo json_encode($result);
375
+ exit();
376
+
377
+ }
378
+
379
+ # default
380
+ wp_die( __('Unknown cache path!'), __('Error:'), array('response'=>200));
381
+ }
382
+
383
+
384
+ # run during activation
385
+ register_activation_hook($fvm_var_file, 'fvm_plugin_activate');
386
+ function fvm_plugin_activate() {
387
+
388
+ global $wpdb;
389
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
390
+
391
+ # defauls
392
+ $sql = array();
393
+ $wpdb_collate = $wpdb->collate;
394
+
395
+ # create cache table
396
+ $sqla_table_name = $wpdb->prefix . 'fvm_cache';
397
+ $sqla = "CREATE TABLE IF NOT EXISTS {$sqla_table_name} (
398
+ `id` bigint(20) unsigned NOT NULL auto_increment ,
399
+ `uid` varchar(60) NOT NULL,
400
+ `date` bigint(20) unsigned NOT NULL,
401
+ `type` varchar(32) NOT NULL,
402
+ `content` mediumtext NOT NULL,
403
+ `meta` mediumtext NOT NULL,
404
+ PRIMARY KEY (id),
405
+ UNIQUE KEY uid (uid),
406
+ KEY date (date), KEY type (type)
407
+ )
408
+ COLLATE {$wpdb_collate}";
409
+
410
+ # create logs table
411
+ $sqlb_table_name = $wpdb->prefix . 'fvm_logs';
412
+ $sqlb = "CREATE TABLE IF NOT EXISTS {$sqlb_table_name} (
413
+ `id` bigint(20) unsigned NOT NULL auto_increment,
414
+ `uid` varchar(60) NOT NULL,
415
+ `date` bigint(20) unsigned NOT NULL,
416
+ `type` varchar(32) NOT NULL,
417
+ `content` mediumtext NOT NULL,
418
+ `meta` mediumtext NOT NULL,
419
+ PRIMARY KEY (id),
420
+ UNIQUE KEY uid (uid),
421
+ KEY date (date),
422
+ KEY type (type)
423
+ )
424
+ COLLATE {$wpdb_collate}";
425
+
426
+ # run sql
427
+ dbDelta($sqla);
428
+ dbDelta($sqlb);
429
+
430
+ # initialize cache time
431
+ fvm_cache_increment();
432
+
433
+ }
434
+
435
+
436
+ # run during deactivation
437
+ register_deactivation_hook($fvm_var_file, 'fvm_plugin_deactivate');
438
+ function fvm_plugin_deactivate() {
439
+ global $wpdb, $fvm_settings, $fvm_cache_paths;
440
+
441
+ # remove all caches on deactivation
442
+ if(isset($fvm_cache_paths['cache_dir_min']) && stripos($fvm_cache_paths['cache_dir_min'], '/fvm') !== false) {
443
+ fvm_rrmdir($fvm_cache_paths['cache_base_dir']);
444
+ }
445
+
446
+ # delete tables
447
+ $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_cache");
448
+ $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_logs");
449
+
450
+ }
451
+
452
+ # run during uninstall
453
+ register_uninstall_hook($fvm_var_file, 'fvm_plugin_uninstall');
454
+ function fvm_plugin_uninstall() {
455
+ global $wpdb, $fvm_settings, $fvm_cache_paths;
456
+
457
+ # remove options and tables
458
+ $wpdb->query("DELETE FROM {$wpdb->prefix}options WHERE option_name = 'fvm_settings'");
459
+ $wpdb->query("DELETE FROM {$wpdb->prefix}options WHERE option_name = 'fvm_last_cache_update'");
460
+ $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_cache");
461
+ $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_logs");
462
+
463
+ # remove all cache directories
464
+ if(isset($fvm_cache_paths['cache_dir_min']) && stripos($fvm_cache_paths['cache_dir_min'], '/fvm') !== false) {
465
+ fvm_rrmdir($fvm_cache_paths['cache_base_dir']);
466
+ }
467
+ }
468
+
469
+ # initialize database if it doesn't exist
470
+ function fvm_initialize_database() {
471
+ global $wpdb;
472
+ $check1 = $wpdb->query("SHOW TABLES LIKE '{$wpdb->prefix}fvm_cache'");
473
+ if(!$check1){ fvm_plugin_activate(); }
474
+ }
475
+
476
+
477
+ # get all known roles
478
+ function fvm_get_user_roles_checkboxes() {
479
+
480
+ global $wp_roles, $fvm_settings;
481
+ $roles_list = array();
482
+ if(is_object($wp_roles)) {
483
+ $roles = (array) $wp_roles->get_names();
484
+ foreach ($roles as $role=>$rname) {
485
+
486
+ $roles_list[] = '<label for="fvm_settings_minify_'.$role.'"><input name="fvm_settings[minify]['.$role.']" type="checkbox" id="fvm_settings_minify_'.$role.'" value="1" '. fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'minify', $role)).'> '.$rname.' </label><br />';
487
+
488
+ }
489
+ }
490
+
491
+ # return
492
+ if(!empty($roles_list)) { return implode(PHP_EOL, $roles_list); } else { return 'No roles detected!'; }
493
+
494
+ }
inc/common.php ADDED
@@ -0,0 +1,1390 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ # Exit if accessed directly
4
+ if (!defined('ABSPATH')){ exit(); }
5
+
6
+ # functions needed for both frontend or backend
7
+
8
+ # top admin toolbar for cache purging
9
+ function fvm_admintoolbar() {
10
+ if(current_user_can('manage_options')) {
11
+ global $wp_admin_bar;
12
+
13
+ # Add top menu to admin bar
14
+ $wp_admin_bar->add_node(array(
15
+ 'id' => 'fvm_menu',
16
+ 'title' => __("FVM", 'fvm') . '</span>',
17
+ 'href' => wp_nonce_url(add_query_arg('fvm_do', 'clear_all'), 'fvm_clear', '_wpnonce')
18
+ ));
19
+
20
+ # Add submenu
21
+ $wp_admin_bar->add_node(array(
22
+ 'id' => 'fvm_submenu_purge_all',
23
+ 'parent' => 'fvm_menu',
24
+ 'title' => __("Clear Everything", 'fvm'),
25
+ 'href' => wp_nonce_url(add_query_arg('fvm_do', 'clear_all'), 'fvm_clear', '_wpnonce')
26
+ ));
27
+
28
+ # Add submenu
29
+ $wp_admin_bar->add_node(array(
30
+ 'id' => 'fvm_submenu_settings',
31
+ 'parent' => 'fvm_menu',
32
+ 'title' => __("FVM Settings", 'fvm'),
33
+ 'href' => admin_url('options-general.php?page=fvm')
34
+ ));
35
+
36
+ /*
37
+ # Add submenu
38
+ $wp_admin_bar->add_node(array(
39
+ 'id' => 'fvm_submenu_upgrade',
40
+ 'parent' => 'fvm_menu',
41
+ 'title' => __("Upgrade", 'fvm'),
42
+ 'href' => admin_url('options-general.php?page=fvm&tab=upgrade')
43
+ ));
44
+ */
45
+
46
+ # Add submenu
47
+ $wp_admin_bar->add_node(array(
48
+ 'id' => 'fvm_submenu_help',
49
+ 'parent' => 'fvm_menu',
50
+ 'title' => __("Help", 'fvm'),
51
+ 'href' => admin_url('options-general.php?page=fvm&tab=help')
52
+ ));
53
+
54
+ }
55
+ }
56
+
57
+
58
+ # purge all caches when clicking the button on the admin bar
59
+ function fvm_process_cache_purge_request(){
60
+
61
+ if(isset($_GET['fvm_do']) && isset($_GET['_wpnonce'])) {
62
+
63
+ # must be able to cleanup cache
64
+ if (!current_user_can('manage_options')) {
65
+ wp_die( __('You do not have sufficient permissions to access this page.'), __('Error:'), array('response'=>200));
66
+ }
67
+
68
+ # validate nonce
69
+ if(!wp_verify_nonce($_GET['_wpnonce'], 'fvm_clear')) {
70
+ wp_die( __('Invalid or expired request... please go back and refresh before trying again!'), __('Error:'), array('response'=>200));
71
+ }
72
+
73
+ # Purge All
74
+ if($_GET['fvm_do'] == 'clear_all') {
75
+
76
+ # purge everything
77
+ $cache = fvm_purge_minification();
78
+ $others = fvm_purge_others();
79
+
80
+ if(is_admin()) {
81
+
82
+ # merge notices
83
+ $notices = array();
84
+ if(is_string($cache)) { $notices[] = $cache; }
85
+ if(is_string($others)) { $notices[] = $others; }
86
+
87
+ # save transient for after the redirect
88
+ if(count($notices) == 0) { $notices[] = 'FVM: All Caches are now cleared. ('.date("D, d M Y @ H:i:s e").')'; }
89
+ set_transient( 'fvm_admin_notice', json_encode($notices), 10);
90
+
91
+ }
92
+
93
+ }
94
+
95
+ # https://developer.wordpress.org/reference/functions/wp_safe_redirect/
96
+ nocache_headers();
97
+ wp_safe_redirect(remove_query_arg('_wpnonce', remove_query_arg('_fvm', wp_get_referer())));
98
+ exit();
99
+ }
100
+ }
101
+
102
+
103
+ # get cache directories and urls
104
+ function fvm_cachepath() {
105
+
106
+ # must have
107
+ if(!defined('WP_CONTENT_DIR')) { return false; }
108
+ if(!defined('WP_CONTENT_URL')) { return false; }
109
+
110
+ global $fvm_settings;
111
+
112
+ # define cache directory
113
+ $cache_dir = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'cache';
114
+ $cache_base_dir = $cache_dir . DIRECTORY_SEPARATOR .'fvm';
115
+ $cache_base_dirurl = WP_CONTENT_URL . '/cache/fvm';
116
+
117
+ # use alternative directory?
118
+ if(isset($fvm_settings['cache']['path']) && !empty($fvm_settings['cache']['path']) && isset($fvm_settings['cache']['url']) && !empty($fvm_settings['cache']['url']) && is_dir($fvm_settings['cache']['path'])) {
119
+ $cache_dir = rtrim($fvm_settings['cache']['path'], '/');
120
+ $cache_base_dir = $cache_dir . DIRECTORY_SEPARATOR .'fvm';
121
+ $cache_base_dirurl = rtrim($fvm_settings['cache']['url'], '/') . '/fvm';
122
+ }
123
+
124
+ # get requested hostname
125
+ $host = fvm_get_domain();
126
+
127
+ $cache_dir_min = $cache_base_dir . DIRECTORY_SEPARATOR . 'min' . DIRECTORY_SEPARATOR . $host;
128
+ $cache_url_min = $cache_base_dirurl . '/min/' .$host;
129
+
130
+ # mkdir and check if umask requires chmod, but only for hosts matching the site_url'
131
+ $dirs = array($cache_dir, $cache_base_dir, $cache_dir_min);
132
+ foreach ($dirs as $d) {
133
+ fvm_create_dir($d);
134
+ }
135
+
136
+ # return
137
+ return array(
138
+ 'cache_base_dir'=>$cache_base_dir,
139
+ 'cache_base_dirurl'=>$cache_base_dirurl,
140
+ 'cache_dir_min'=>$cache_dir_min,
141
+ 'cache_url_min'=>$cache_url_min
142
+ );
143
+ }
144
+
145
+
146
+ # Purge everything
147
+ function fvm_purge_all() {
148
+ fvm_purge_minification();
149
+ fvm_purge_others();
150
+ return true;
151
+ }
152
+
153
+ # Purge minification only
154
+ function fvm_purge_minification() {
155
+
156
+ # flush opcache
157
+ if(function_exists('opcache_reset')) {
158
+ @opcache_reset();
159
+ }
160
+
161
+ # increment cache file names
162
+ $now = fvm_cache_increment();
163
+
164
+ # truncate cache table
165
+ global $wpdb;
166
+ $wpdb->query("TRUNCATE TABLE {$wpdb->prefix}fvm_cache");
167
+ $wpdb->query("TRUNCATE TABLE {$wpdb->prefix}fvm_logs");
168
+
169
+ # get cache and min directories
170
+ global $fvm_cache_paths, $fvm_settings;
171
+
172
+ # fetch settings on wp-cli
173
+ if(is_null($fvm_settings)) { $fvm_settings = fvm_get_settings(); }
174
+ if(is_null($fvm_cache_paths)) { $fvm_cache_paths = fvm_cachepath(); }
175
+
176
+ # purge html directory?
177
+ if(isset($fvm_cache_paths['cache_dir_min']) && is_dir($fvm_cache_paths['cache_dir_min']) && is_writable($fvm_cache_paths['cache_dir_min']) && stripos($fvm_cache_paths['cache_dir_min'], '/fvm') !== false) {
178
+
179
+ # purge css/js files instantly
180
+ if(isset($fvm_settings['cache']['min_instant_purge']) && $fvm_settings['cache']['min_instant_purge'] == true) {
181
+ $result = fvm_purge_minification_now();
182
+ return $result;
183
+ } else {
184
+ # schedule purge for 24 hours later, only once
185
+ add_action( 'fvm_purge_minification_later', 'fvm_purge_minification_expired' );
186
+ wp_schedule_single_event(time() + 3600 * 24, 'fvm_purge_minification_later');
187
+ return 'Expired minification files are set to be deleted in 24 hours.';
188
+ }
189
+
190
+ } else {
191
+ return 'The cache directory is not rewritable!';
192
+ }
193
+
194
+ return false;
195
+ }
196
+
197
+
198
+ # purge minified files right now
199
+ function fvm_purge_minification_now() {
200
+ global $fvm_cache_paths;
201
+ if(isset($fvm_cache_paths['cache_dir_min']) && stripos($fvm_cache_paths['cache_dir_min'], '/fvm') !== false) {
202
+ $result = fvm_rrmdir($fvm_cache_paths['cache_dir_min']);
203
+ return $result;
204
+ } else {
205
+ return 'The cache directory is not writeable!';
206
+ }
207
+ }
208
+
209
+ # purge expired minification files only
210
+ function fvm_purge_minification_expired() {
211
+ global $fvm_cache_paths;
212
+ if(isset($fvm_cache_paths['cache_dir_min']) && !empty($fvm_cache_paths['cache_dir_min']) && stripos($fvm_cache_paths['cache_dir_min'], '/fvm') !== false) {
213
+
214
+ # must be on the allowed path
215
+ $wd = $fvm_cache_paths['cache_dir_min'];
216
+ if(empty($wd) || !defined('WP_CONTENT_DIR') || stripos($wd, '/fvm') === false) {
217
+ return 'Requested purge path is not allowed!';
218
+ }
219
+
220
+ # prefix
221
+ $skip = get_option('fvm_last_cache_update', '0');
222
+
223
+ # purge only the expired cache that doesn't match the current cache version prefix and it's older than 24 hours
224
+ clearstatcache();
225
+ if(is_dir($wd)) {
226
+ try {
227
+ $i = new DirectoryIterator($wd);
228
+ foreach($i as $f){
229
+ if($f->isFile() && stripos(basename($f->getRealPath()), $skip) === false){
230
+ if($f->getMTime() <= time() - 86400) {
231
+ @unlink($f->getRealPath());
232
+ }
233
+ }
234
+ }
235
+ } catch (Exception $e) {
236
+ return get_class($e) . ": " . $e->getMessage();
237
+ }
238
+ }
239
+
240
+ return 'Expired Cache Deleted!';
241
+ }
242
+ }
243
+
244
+
245
+ # purge supported hosting and plugins
246
+ function fvm_purge_others(){
247
+
248
+ # third party plugins
249
+
250
+ # Purge all W3 Total Cache
251
+ if (function_exists('w3tc_pgcache_flush')) {
252
+ w3tc_pgcache_flush();
253
+ return __('All caches on <strong>W3 Total Cache</strong> have been purged.');
254
+ }
255
+
256
+ # Purge WP Super Cache
257
+ if (function_exists('wp_cache_clear_cache')) {
258
+ wp_cache_clear_cache();
259
+ return __('All caches on <strong>WP Super Cache</strong> have been purged.');
260
+ }
261
+
262
+ # Purge WP Rocket
263
+ if (function_exists('rocket_clean_domain')) {
264
+ rocket_clean_domain();
265
+ return __('All caches on <strong>WP Rocket</strong> have been purged.');
266
+ }
267
+
268
+ # Purge Cachify
269
+ if (function_exists('cachify_flush_cache')) {
270
+ cachify_flush_cache();
271
+ return __('All caches on <strong>Cachify</strong> have been purged.');
272
+ }
273
+
274
+ # Purge Comet Cache
275
+ if ( class_exists("comet_cache") ) {
276
+ comet_cache::clear();
277
+ return __('All caches on <strong>Comet Cache</strong> have been purged.');
278
+ }
279
+
280
+ # Purge Zen Cache
281
+ if ( class_exists("zencache") ) {
282
+ zencache::clear();
283
+ return __('All caches on <strong>Comet Cache</strong> have been purged.');
284
+ }
285
+
286
+ # Purge LiteSpeed Cache
287
+ if (class_exists('LiteSpeed_Cache_Tags')) {
288
+ LiteSpeed_Cache_Tags::add_purge_tag('*');
289
+ return __('All caches on <strong>LiteSpeed Cache</strong> have been purged.');
290
+ }
291
+
292
+ # Purge Hyper Cache
293
+ if (class_exists( 'HyperCache' )) {
294
+ do_action( 'autoptimize_action_cachepurged' );
295
+ return __( 'All caches on <strong>HyperCache</strong> have been purged.');
296
+ }
297
+
298
+ # purge cache enabler
299
+ if ( has_action('ce_clear_cache') ) {
300
+ do_action('ce_clear_cache');
301
+ return __( 'All caches on <strong>Cache Enabler</strong> have been purged.');
302
+ }
303
+
304
+ # purge wpfc
305
+ if (function_exists('wpfc_clear_all_cache')) {
306
+ wpfc_clear_all_cache(true);
307
+ }
308
+
309
+ # add breeze cache purge support
310
+ if (class_exists("Breeze_PurgeCache")) {
311
+ Breeze_PurgeCache::breeze_cache_flush();
312
+ return __( 'All caches on <strong>Breeze</strong> have been purged.');
313
+ }
314
+
315
+
316
+ # swift
317
+ if (class_exists("Swift_Performance_Cache")) {
318
+ Swift_Performance_Cache::clear_all_cache();
319
+ return __( 'All caches on <strong>Swift Performance</strong> have been purged.');
320
+ }
321
+
322
+
323
+ # hosting companies
324
+
325
+ # Purge SG Optimizer (Siteground)
326
+ if (function_exists('sg_cachepress_purge_cache')) {
327
+ sg_cachepress_purge_cache();
328
+ return __('All caches on <strong>SG Optimizer</strong> have been purged.');
329
+ }
330
+
331
+ # Purge Godaddy Managed WordPress Hosting (Varnish + APC)
332
+ if (class_exists('WPaaS\Plugin') && method_exists( 'WPass\Plugin', 'vip' )) {
333
+ fvm_godaddy_request('BAN');
334
+ return __('A cache purge request has been sent to <strong>Go Daddy Varnish</strong>');
335
+ }
336
+
337
+
338
+ # Purge WP Engine
339
+ if (class_exists("WpeCommon")) {
340
+ if (method_exists('WpeCommon', 'purge_memcached')) { WpeCommon::purge_memcached(); }
341
+ if (method_exists('WpeCommon', 'purge_varnish_cache')) { WpeCommon::purge_varnish_cache(); }
342
+ if (method_exists('WpeCommon', 'purge_memcached') || method_exists('WpeCommon', 'purge_varnish_cache')) {
343
+ return __('A cache purge request has been sent to <strong>WP Engine</strong>');
344
+ }
345
+ }
346
+
347
+ # Purge Kinsta
348
+ global $kinsta_cache;
349
+ if ( isset($kinsta_cache) && class_exists('\\Kinsta\\CDN_Enabler')) {
350
+ if (!empty( $kinsta_cache->kinsta_cache_purge)){
351
+ $kinsta_cache->kinsta_cache_purge->purge_complete_caches();
352
+ return __('A cache purge request has been sent to <strong>Kinsta</strong>');
353
+ }
354
+ }
355
+
356
+ # Purge Pagely
357
+ if ( class_exists( 'PagelyCachePurge' ) ) {
358
+ $purge_pagely = new PagelyCachePurge();
359
+ $purge_pagely->purgeAll();
360
+ return __('A cache purge request has been sent to <strong>Pagely</strong>');
361
+ }
362
+
363
+ # Purge Pressidum
364
+ if (defined('WP_NINUKIS_WP_NAME') && class_exists('Ninukis_Plugin')){
365
+ $purge_pressidum = Ninukis_Plugin::get_instance();
366
+ $purge_pressidum->purgeAllCaches();
367
+ return __('A cache purge request has been sent to <strong>Pressidium</strong>');
368
+ }
369
+
370
+ # Purge Savvii
371
+ if (defined( '\Savvii\CacheFlusherPlugin::NAME_DOMAINFLUSH_NOW')) {
372
+ $purge_savvii = new \Savvii\CacheFlusherPlugin();
373
+ if ( method_exists( $plugin, 'domainflush' ) ) {
374
+ $purge_savvii->domainflush();
375
+ return __('A cache purge request has been sent to <strong>Savvii</strong>');
376
+ }
377
+ }
378
+
379
+ # Purge Pantheon Advanced Page Cache plugin
380
+ if(function_exists('pantheon_wp_clear_edge_all')) {
381
+ pantheon_wp_clear_edge_all();
382
+ }
383
+
384
+ # wordpress default cache
385
+ if (function_exists('wp_cache_flush')) {
386
+ wp_cache_flush();
387
+ }
388
+
389
+ }
390
+
391
+
392
+ # Purge Godaddy Managed WordPress Hosting (Varnish)
393
+ function fvm_godaddy_request( $method, $url = null ) {
394
+ $url = empty( $url ) ? home_url() : $url;
395
+ $host = parse_url( $url, PHP_URL_HOST );
396
+ $url = set_url_scheme( str_replace( $host, WPaas\Plugin::vip(), $url ), 'http' );
397
+ update_option( 'gd_system_last_cache_flush', time(), 'no'); # purge apc
398
+ wp_remote_request( esc_url_raw( $url ), array('method' => $method, 'blocking' => false, 'headers' => array('Host' => $host)) );
399
+ }
400
+
401
+
402
+ # check if we can minify the page
403
+ function fvm_can_minify() {
404
+
405
+ global $fvm_urls;
406
+
407
+ # only GET requests allowed
408
+ if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
409
+ return false;
410
+ }
411
+
412
+ # compatibility with DONOTCACHEPAGE
413
+ if( defined('DONOTCACHEPAGE') && DONOTCACHEPAGE ){ return false; }
414
+
415
+ # detect api requests (only defined after parse_request hook)
416
+ if( defined('REST_REQUEST') && REST_REQUEST ){ return false; }
417
+
418
+ # always skip on these tasks
419
+ if( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ){ return false; }
420
+ if( defined('WP_INSTALLING') && WP_INSTALLING ){ return false; }
421
+ if( defined('WP_REPAIRING') && WP_REPAIRING ){ return false; }
422
+ if( defined('WP_IMPORTING') && WP_IMPORTING ){ return false; }
423
+ if( defined('DOING_AJAX') && DOING_AJAX ){ return false; }
424
+ if( defined('WP_CLI') && WP_CLI ){ return false; }
425
+ if( defined('XMLRPC_REQUEST') && XMLRPC_REQUEST ){ return false; }
426
+ if( defined('WP_ADMIN') && WP_ADMIN ){ return false; }
427
+ if( defined('SHORTINIT') && SHORTINIT ){ return false; }
428
+ if( defined('IFRAME_REQUEST') && IFRAME_REQUEST ){ return false; }
429
+
430
+ # don't minify specific WordPress areas
431
+ if(function_exists('is_404') && is_404()){ return false; }
432
+ if(function_exists('is_feed') && is_feed()){ return false; }
433
+ if(function_exists('is_comment_feed') && is_comment_feed()){ return false; }
434
+ if(function_exists('is_attachment') && is_attachment()){ return false; }
435
+ if(function_exists('is_trackback') && is_trackback()){ return false; }
436
+ if(function_exists('is_robots') && is_robots()){ return false; }
437
+ if(function_exists('is_preview') && is_preview()){ return false; }
438
+ if(function_exists('is_customize_preview') && is_customize_preview()){ return false; }
439
+ if(function_exists('is_embed') && is_embed()){ return false; }
440
+ if(function_exists('is_admin') && is_admin()){ return false; }
441
+ if(function_exists('is_blog_admin') && is_blog_admin()){ return false; }
442
+ if(function_exists('is_network_admin') && is_network_admin()){ return false; }
443
+
444
+ # don't minify specific WooCommerce areas
445
+ if(function_exists('is_checkout') && is_checkout()){ return false; }
446
+ if(function_exists('is_account_page') && is_account_page()){ return false; }
447
+ if(function_exists('is_ajax') && is_ajax()){ return false; }
448
+ if(function_exists('is_wc_endpoint_url') && is_wc_endpoint_url()){ return false; }
449
+
450
+ # don't minify amp pages by the amp plugin
451
+ if(function_exists('is_amp_endpoint') && is_amp_endpoint()){ return false; }
452
+ if(function_exists('ampforwp_is_amp_endpoint') && ampforwp_is_amp_endpoint()){ return false; }
453
+
454
+ # get requested hostname
455
+ $host = fvm_get_domain();
456
+
457
+ # only for hosts matching the site_url
458
+ if(isset($fvm_urls['wp_domain']) && !empty($fvm_urls['wp_domain'])) {
459
+ if($host != $fvm_urls['wp_domain']) {
460
+ return false;
461
+ }
462
+ }
463
+
464
+ # if there is an url, avoid known static files
465
+ if(isset($_SERVER['REQUEST_URI']) && !empty($_SERVER['REQUEST_URI'])) {
466
+
467
+ # parse url (path, query)
468
+ $ruri = str_replace('//', '/', str_replace('..', '', preg_replace( '/[ <>\'\"\r\n\t\(\)]/', '', strtok($_SERVER['REQUEST_URI'], '?'))));
469
+
470
+ # avoid robots.txt and other situations
471
+ $noext = array('.txt', '.xml', '.map', '.css', '.js', '.png', '.jpeg', '.jpg', '.gif', '.webp', '.ico', '.php', '.htaccess', '.json', '.pdf', '.mp4', '.webm', '.zip', '.sql', '.gz');
472
+ foreach ($noext as $ext) {
473
+ if(substr($ruri, -strlen($ext)) == $ext) {
474
+ return false;
475
+ }
476
+ }
477
+
478
+ }
479
+
480
+ # user roles
481
+ if(function_exists('is_user_logged_in')) {
482
+ if(is_user_logged_in()) {
483
+
484
+ # get user roles
485
+ global $fvm_settings;
486
+ $user = wp_get_current_user();
487
+ $roles = (array) $user->roles;
488
+ foreach($roles as $role) {
489
+ if(isset($fvm_settings['minify'][$role])) { return true; }
490
+ }
491
+
492
+ # disable for logged in users by default
493
+ return false;
494
+ }
495
+ }
496
+
497
+
498
+
499
+ # default
500
+ return true;
501
+ }
502
+
503
+
504
+ # create a directory, recursively
505
+ function fvm_create_dir($d) {
506
+
507
+ # must have
508
+ if(!defined('WP_CONTENT_DIR')) { return false; }
509
+
510
+ # use alternative directory?
511
+ if(isset($fvm_settings['cache']['path']) && !empty($fvm_settings['cache']['path']) && isset($fvm_settings['cache']['url']) && !empty($fvm_settings['cache']['url']) && is_dir($fvm_settings['cache']['path'])) {
512
+ $cache_dir = rtrim($fvm_settings['cache']['path'], '/');
513
+ $cache_base_dir = $cache_dir . DIRECTORY_SEPARATOR .'fvm';
514
+ $cache_base_dirurl = rtrim($fvm_settings['cache']['url'], '/') . '/fvm';
515
+ }
516
+
517
+
518
+ # get permissions from parent directory, or default to 777
519
+ $ch = 0777;
520
+ $parent = dirname($d);
521
+ if(is_dir($parent) && function_exists('stat') && fvm_function_available('stat')) {
522
+ if ($stat = @stat($parent)) { $ch = $stat['mode'] & 0007777; }
523
+ }
524
+
525
+ # create recursively
526
+ if(!is_dir($d)) {
527
+ if ( @mkdir($d, $ch, true) ) {
528
+ if ( $ch != ($ch & ~umask()) ) {
529
+ $p = explode(DIRECTORY_SEPARATOR, substr($d, strlen(dirname($d)) + 1 ));
530
+ for ($i = 1, $c = count($p ); $i <= $c; $i++) {
531
+ @chmod(dirname($d) . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, array_slice($p, 0, $i)), $ch);
532
+ }
533
+ }
534
+ } else {
535
+ # fallback
536
+ wp_mkdir_p($d);
537
+ }
538
+ }
539
+
540
+ return true;
541
+ }
542
+
543
+
544
+ # check if PHP has some functions disabled
545
+ function fvm_function_available($func) {
546
+ if (ini_get('safe_mode')) return false;
547
+ $disabled = ini_get('disable_functions');
548
+ if ($disabled) {
549
+ $disabled = explode(',', $disabled);
550
+ $disabled = array_map('trim', $disabled);
551
+ return !in_array($func, $disabled);
552
+ }
553
+ return true;
554
+ }
555
+
556
+
557
+ # open a multiline string, order, filter duplicates and return as array
558
+ function fvm_string_toarray($value){
559
+ $arr = explode(PHP_EOL, $value);
560
+ return fvm_array_order($arr);}
561
+
562
+ # filter duplicates, order and return array
563
+ function fvm_array_order($arr){
564
+ if(!is_array($arr)) { return array(); }
565
+ $a = array_map('trim', $arr);
566
+ $b = array_filter($a);
567
+ $c = array_unique($b);
568
+ sort($c);
569
+ return $c;
570
+ }
571
+
572
+
573
+ # return size in human format
574
+ function fvm_format_filesize($bytes, $decimals = 2) {
575
+ $units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' );
576
+ for ($i = 0; ($bytes / 1024) > 0.9; $i++, $bytes /= 1024) {}
577
+ if($i == 0) { $i = 1; $bytes = $bytes / 1024; } # KB+ only
578
+ return sprintf( "%1.{$decimals}f %s", round( $bytes, $decimals ), $units[$i] );
579
+ }
580
+
581
+
582
+ # increment file names
583
+ function fvm_cache_increment() {
584
+ $now = time();
585
+ update_option('fvm_last_cache_update', $now, 'no');
586
+ return $now;
587
+ }
588
+
589
+
590
+ # remove a director, recursively
591
+ function fvm_rrmdir($path) {
592
+
593
+ # must be on the allowed path
594
+ if(empty($path) || !defined('WP_CONTENT_DIR') || stripos($path, '/fvm') === false) {
595
+ return 'Requested purge path is not allowed!';
596
+ }
597
+
598
+ # purge recursively
599
+ clearstatcache();
600
+ if(is_dir($path)) {
601
+ try {
602
+ $i = new DirectoryIterator($path);
603
+ foreach($i as $f){
604
+ if($f->isFile()){ @unlink($f->getRealPath());
605
+ } else if(!$f->isDot() && $f->isDir()){
606
+ fvm_rrmdir($f->getRealPath());
607
+ if(is_dir($f->getRealPath())) { @rmdir($f->getRealPath()); }
608
+ }
609
+ }
610
+ } catch (Exception $e) {
611
+ return get_class($e) . ": " . $e->getMessage();
612
+ }
613
+
614
+ # self
615
+ if(is_dir($path)) { @rmdir($path); }
616
+ }
617
+
618
+ }
619
+
620
+
621
+ # Fix the permission bits on generated files
622
+ function fvm_fix_permission_bits($file){
623
+
624
+ # must be on the allowed path
625
+ if(empty($file) || !defined('WP_CONTENT_DIR') || stripos($file, '/fvm') === false) {
626
+ return 'Requested path is not allowed!';
627
+ }
628
+
629
+ if(function_exists('stat') && fvm_function_available('stat')) {
630
+ if ($stat = @stat(dirname($file))) {
631
+ $perms = $stat['mode'] & 0007777;
632
+ @chmod($file, $perms);
633
+ clearstatcache();
634
+ return true;
635
+ }
636
+ }
637
+
638
+ # get permissions from parent directory
639
+ $perms = 0777;
640
+ if(function_exists('stat') && fvm_function_available('stat')) {
641
+ if ($stat = @stat(dirname($file))) { $perms = $stat['mode'] & 0007777; }
642
+ }
643
+
644
+ if (file_exists($file)){
645
+ if ($perms != ($perms & ~umask())){
646
+ $folder_parts = explode( DIRECTORY_SEPARATOR, substr( $file, strlen(dirname($file)) + 1 ) );
647
+ for ( $i = 1, $c = count( $folder_parts ); $i <= $c; $i++ ) {
648
+ @chmod(dirname($file) . DIRECTORY_SEPARATOR . implode( DIRECTORY_SEPARATOR, array_slice( $folder_parts, 0, $i ) ), $perms );
649
+ }
650
+ }
651
+ return true;
652
+ }
653
+
654
+ return false;
655
+ }
656
+
657
+
658
+ # get options into an array
659
+ function fvm_get_settings() {
660
+
661
+ $fvm_settings = json_decode(get_option('fvm_settings'), true);
662
+
663
+ # mandatory default exclusions
664
+ $fvm_settings_default = fvm_get_default_settings($fvm_settings);
665
+
666
+ # check if there are any pending field update routines
667
+ $fvm_settings_default = fvm_get_updated_field_routines($fvm_settings_default);
668
+
669
+ # update database if needed
670
+ if($fvm_settings != $fvm_settings_default) {
671
+ update_option('fvm_settings', json_encode($fvm_settings_default), false);
672
+ }
673
+
674
+ # return
675
+ return $fvm_settings;
676
+ }
677
+
678
+ # return value from section and key name
679
+ function fvm_get_settings_value($fvm_settings, $section, $key) {
680
+ if($fvm_settings != false && is_array($fvm_settings) && count($fvm_settings) > 1) {
681
+ if(isset($fvm_settings[$section][$key])) {
682
+ return $fvm_settings[$section][$key];
683
+ }
684
+ }
685
+ return '';
686
+ }
687
+
688
+
689
+ # default exclusions by seting name
690
+ function fvm_get_default_settings($fvm_settings) {
691
+ if(!is_array($fvm_settings) || empty($fvm_settings)){
692
+
693
+ # initialize
694
+ $fvm_settings = array();
695
+
696
+ # html
697
+ $fvm_settings['html']['enable'] = 1;
698
+ $fvm_settings['html']['nocomments'] = 1;
699
+ $fvm_settings['html']['cleanup_header'] = 1;
700
+ $fvm_settings['html']['disable_emojis'] = 1;
701
+
702
+ # css
703
+ $fvm_settings['css']['enable'] = 1;
704
+ $fvm_settings['css']['noprint'] = 1;
705
+
706
+ # cdn
707
+ $arr = array('img[src*=/wp-content/], img[data-src*=/wp-content/], img[data-srcset*=/wp-content/]', 'picture source[srcset*=/wp-content/]', 'video source[type*=video]', 'image[height]', 'link[rel=icon], link[rel=apple-touch-icon]', 'meta[name=msapplication-TileImage]', 'a[data-interchange*=/wp-content/]', 'rs-slide[data-thumb]', 'form[data-product_variations]', 'div[data-background-image], section[data-background-image]');
708
+ $fvm_settings['cdn']['integration'] = implode(PHP_EOL, fvm_array_order($arr));
709
+
710
+ }
711
+
712
+ # return
713
+ return $fvm_settings;
714
+ }
715
+
716
+
717
+ # save log to database
718
+ function fvm_save_log($arr) {
719
+
720
+ # must have
721
+ if(!is_array($arr) || (is_array($arr) && (count($arr) == 0 || empty($arr)))) { return false; }
722
+ if(!isset($arr['uid']) || !isset($arr['date']) || !isset($arr['type']) || !isset($arr['content']) || !isset($arr['meta'])) { return false; }
723
+
724
+ # normalize unknown keys
725
+ if(strlen($arr['uid']) != 40) { $arr['uid'] = hash('sha1', $arr['uid']); }
726
+
727
+ # else insert
728
+ global $wpdb, $fvm_cache_paths;
729
+
730
+ # initialize arrays (fields, types, values)
731
+ $fld = array();
732
+ $tpe = array();
733
+ $vls = array();
734
+
735
+ # define possible data types
736
+ $str = array('uid', 'type', 'content', 'meta');
737
+ $int = array('date');
738
+ $all = array_merge($str, $int);
739
+
740
+ # process only recognized columns
741
+ foreach($arr as $k=>$v) {
742
+ if(in_array($k, $all)) {
743
+ if(in_array($k, $str)) { $tpe[] = '%s'; } else { $tpe[] = '%d'; }
744
+ if($k == 'content') { $v = json_encode($v); }
745
+ if($k == 'meta') { $v = json_encode($v); }
746
+ if($k == 'uid') { $v = hash('sha1', $v); }
747
+
748
+ # array for prepare
749
+ $fld[] = $k;
750
+ $vls[] = $v;
751
+ }
752
+ }
753
+
754
+ # prepare and insert to database
755
+ $sql = $wpdb->prepare("INSERT IGNORE INTO ".$wpdb->prefix."fvm_logs (".implode(', ', $fld).") VALUES (".implode(', ', $tpe).")", $vls);
756
+ $result = $wpdb->query($sql);
757
+
758
+ # check if it already exists
759
+ if($result) {
760
+ return true;
761
+ }
762
+
763
+ # fallback
764
+ return false;
765
+
766
+ }
767
+
768
+
769
+
770
+
771
+ # try to open the file from the disk, before downloading
772
+ # try to open the file from the disk, before downloading
773
+ function fvm_maybe_download($url) {
774
+
775
+ # must have
776
+ if(is_null($url) || empty($url)) { return false; }
777
+
778
+ # get domain
779
+ global $fvm_urls;
780
+
781
+ # check if we can open the file locally first
782
+ if (stripos($url, $fvm_urls['wp_domain']) !== false && isset($_SERVER['DOCUMENT_ROOT'])) {
783
+
784
+ # file path
785
+ $f = str_replace(rtrim($fvm_urls['wp_home'], '/'), $_SERVER['DOCUMENT_ROOT'], $url);
786
+ $f = str_replace('/', DIRECTORY_SEPARATOR, $f); # windows compatibility
787
+
788
+ # did it work?
789
+ if (file_exists($f)) {
790
+ return array('content'=>file_get_contents($f), 'src'=>'Disk');
791
+ }
792
+ }
793
+
794
+ # fallback to downloading
795
+
796
+ # this useragent is needed for google fonts (woff files only + hinted fonts)
797
+ $uagent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586';
798
+
799
+ # cache buster
800
+ $query = 'cache='.time();
801
+ $parsedUrl = parse_url($url);
802
+ if ($parsedUrl['path'] === null) { $url .= '/'; }
803
+ if ($parsedUrl['query'] === null) { $separator = '?'; } else { $separator = '&'; }
804
+ $url .= $separator.$query;
805
+
806
+ # fetch via wordpress functions
807
+ $response = wp_remote_get($url, array('user-agent'=>$uagent, 'timeout' => 7, 'httpversion' => '1.1', 'sslverify'=>false));
808
+ $res_code = wp_remote_retrieve_response_code($response);
809
+ if($res_code == '200') {
810
+ $content = wp_remote_retrieve_body($response);
811
+ if(strlen($content) > 1) {
812
+ return array('content'=>$content, 'src'=>'Web');
813
+ }
814
+ }
815
+
816
+ # failed
817
+ return array('error'=>'Could not read or fetch from '. $url);
818
+ }
819
+
820
+
821
+ # save cache file, if allowed
822
+ function fvm_save_file($file, $content) {
823
+
824
+ # get directory
825
+ $path = dirname($file);
826
+
827
+ # must be on the allowed path
828
+ if(empty($path) || !defined('WP_CONTENT_DIR') || stripos($path, '/fvm') === false) {
829
+ return 'Requested path is not allowed!';
830
+ }
831
+
832
+ # create directory structure
833
+ fvm_create_dir($path);
834
+
835
+ # save file
836
+ file_put_contents($file, $content);
837
+ fvm_fix_permission_bits($file);
838
+ return true;
839
+
840
+ }
841
+
842
+
843
+ # get transients
844
+ function fvm_get_transient($key, $check=null) {
845
+
846
+ global $wpdb;
847
+
848
+ # normalize unknown keys
849
+ if(strlen($key) != 40) { $key = hash('sha1', $key); }
850
+
851
+ # check or fetch
852
+ if($check) {
853
+ $sql = $wpdb->prepare("SELECT id FROM {$wpdb->prefix}fvm_cache WHERE uid = %s LIMIT 1", $key);
854
+ } else {
855
+ $sql = $wpdb->prepare("SELECT content FROM {$wpdb->prefix}fvm_cache WHERE uid = %s LIMIT 1", $key);
856
+ }
857
+
858
+ # get result from database
859
+ $result = $wpdb->get_row($sql);
860
+
861
+ # return true if just checking if it exists
862
+ if(isset($result->id)) {
863
+ return true;
864
+ }
865
+
866
+ # return content if found
867
+ if(isset($result->content)) {
868
+ return $result->content;
869
+ }
870
+
871
+ # fallback
872
+ return false;
873
+ }
874
+
875
+ # set cache
876
+ function fvm_set_transient($arr) {
877
+
878
+ # must have
879
+ if(!is_array($arr) || (is_array($arr) && (count($arr) == 0 || empty($arr)))) { return false; }
880
+ if(!isset($arr['uid']) || !isset($arr['date']) || !isset($arr['type']) || !isset($arr['content']) || !isset($arr['meta'])) { return false; }
881
+
882
+ # normalize unknown keys
883
+ if(strlen($arr['uid']) != 40) { $arr['uid'] = hash('sha1', $arr['uid']); }
884
+
885
+ # check if it already exists and return early if it does
886
+ $status = fvm_get_transient($arr['uid'], true);
887
+ if($status) { return true; }
888
+
889
+ # else insert
890
+ global $wpdb;
891
+
892
+ # initialize arrays (fields, types, values)
893
+ $fld = array();
894
+ $tpe = array();
895
+ $vls = array();
896
+
897
+ # define possible data types
898
+ $str = array('uid', 'type', 'content', 'meta');
899
+ $int = array('date');
900
+ $all = array_merge($str, $int);
901
+
902
+ # process only recognized columns
903
+ foreach($arr as $k=>$v) {
904
+ if(in_array($k, $all)) {
905
+ if(in_array($k, $str)) { $tpe[] = '%s'; } else { $tpe[] = '%d'; }
906
+ if($k == 'meta') { $v = json_encode($v); }
907
+ $fld[] = $k;
908
+ $vls[] = $v;
909
+ }
910
+ }
911
+
912
+ # prepare and insert to database
913
+ $sql = $wpdb->prepare("INSERT IGNORE INTO ".$wpdb->prefix."fvm_cache (".implode(', ', $fld).") VALUES (".implode(', ', $tpe).")", $vls);
914
+ $result = $wpdb->query($sql);
915
+
916
+ # check if it already exists
917
+ if($result) {
918
+ return true;
919
+ }
920
+
921
+ # fallback
922
+ return false;
923
+
924
+ }
925
+
926
+ # delete transient
927
+ function fvm_del_transient($key) {
928
+
929
+ global $wpdb;
930
+
931
+ # normalize unknown keys
932
+ if(strlen($key) != 40) { $key = hash('sha1', $key); }
933
+
934
+ # delete
935
+ $sql = $wpdb->prepare("DELETE FROM {$wpdb->prefix}fvm_cache WHERE uid = %s", $key);
936
+ $result = $wpdb->get_row($sql);
937
+ return true;
938
+ }
939
+
940
+
941
+ # functions, get full url
942
+ function fvm_normalize_url($src, $wp_domain, $wp_home) {
943
+
944
+ # preserve empty source handles
945
+ $hurl = trim($src); if(empty($hurl)) { return $hurl; }
946
+
947
+ # some fixes
948
+ $hurl = str_replace(array('&#038;', '&amp;'), '&', $hurl);
949
+
950
+ #make sure wp_home doesn't have a forward slash
951
+ $wp_home = rtrim($wp_home, '/');
952
+
953
+ # protocol scheme
954
+ $scheme = parse_url($wp_home)['scheme'].'://';
955
+
956
+ # apply some filters
957
+ if (substr($hurl, 0, 2) === "//") { $hurl = $scheme.ltrim($hurl, "/"); } # protocol only
958
+ if (substr($hurl, 0, 4) === "http" && stripos($hurl, $wp_domain) === false) { return $hurl; } # return if external domain
959
+ if (substr($hurl, 0, 4) !== "http" && stripos($hurl, $wp_domain) !== false) { $hurl = $wp_home.'/'.ltrim($hurl, "/"); } # protocol + home
960
+
961
+ # prevent double forward slashes in the middle
962
+ $hurl = str_replace('###', '://', str_replace('//', '/', str_replace('://', '###', $hurl)));
963
+
964
+ # consider different wp-content directory for relative paths
965
+ $proceed = 0;
966
+ if(!empty($wp_home)) {
967
+ $alt_wp_content = basename($wp_home);
968
+ if(substr($hurl, 0, strlen($alt_wp_content)) === $alt_wp_content) { $proceed = 1; }
969
+ }
970
+
971
+ # protocol + home for relative paths
972
+ if (substr($hurl, 0, 12) === "/wp-includes" || substr($hurl, 0, 9) === "/wp-admin" || substr($hurl, 0, 11) === "/wp-content" || $proceed == 1) {
973
+ $hurl = $wp_home.'/'.ltrim($hurl, "/");
974
+ }
975
+
976
+ # make sure there is a protocol prefix as required
977
+ $hurl = $scheme.str_replace(array('http://', 'https://'), '', $hurl); # enforce protocol
978
+
979
+ # no query strings on css and js files
980
+ if (stripos($hurl, '.js?') !== false) { $hurl = stristr($hurl, '.js?', true).'.js'; } # no query strings
981
+ if (stripos($hurl, '.css?') !== false) { $hurl = stristr($hurl, '.css?', true).'.css'; } # no query strings
982
+
983
+ # add filter for developers
984
+ $hurl = apply_filters('fvm_get_url', $hurl);
985
+
986
+ return $hurl;
987
+ }
988
+
989
+
990
+ # minify ld+json scripts
991
+ function fvm_minify_microdata($data) {
992
+ # remove // comments
993
+ $data = trim(preg_replace('/(\v)+(\h)+[\/]{2}(.*)+(\v)+/u', '', $data));
994
+
995
+ # minify
996
+ $data = trim(preg_replace('/\s+/u', ' ', $data));
997
+ $data = str_replace(array('" ', ' "'), '"', $data);
998
+ $data = str_replace(array('[ ', ' ['), '[', $data);
999
+ $data = str_replace(array('] ', ' ]'), ']', $data);
1000
+ $data = str_replace(array('} ', ' }'), '}', $data);
1001
+ $data = str_replace(array('{ ', ' {'), '{', $data);
1002
+ return $data;
1003
+ }
1004
+
1005
+
1006
+ # check for php or html, skip if found
1007
+ function fvm_not_php_html($code) {
1008
+ if((strtolower(substr($code, 0, 2)) != "<?" && stripos($code, "<?php") === false) || strtolower(substr($code, 0, 9)) != "<!doctype") {
1009
+ return true;
1010
+ }
1011
+ return false;
1012
+ }
1013
+
1014
+
1015
+ # remove UTF8 BOM
1016
+ function fvm_remove_utf8_bom($text) {
1017
+ $bom = pack('H*','EFBBBF');
1018
+ while (preg_match("/^$bom/", $text)) {
1019
+ $text = preg_replace("/^$bom/ui", '', $text);
1020
+ }
1021
+ return $text;
1022
+ }
1023
+
1024
+
1025
+ # validate and minify css
1026
+ function fvm_maybe_minify_css_file($css, $url, $min) {
1027
+
1028
+ # return early if empty
1029
+ if(empty($css) || $css == false) { return $css; }
1030
+
1031
+ # process css only if it's not php or html
1032
+ if(fvm_not_php_html($css)) {
1033
+
1034
+ # filtering
1035
+ $css = fvm_remove_utf8_bom($css);
1036
+ $css = str_ireplace('@charset "UTF-8";', '', $css);
1037
+
1038
+ # remove query strings from fonts
1039
+ $css = preg_replace('/(.eot|.woff2|.woff|.ttf)+[?+](.+?)(\)|\'|\")/ui', "$1"."$3", $css);
1040
+
1041
+ # remove sourceMappingURL
1042
+ $css = preg_replace('/(\/\/\s*[#]\s*sourceMappingURL\s*[=]\s*)([a-zA-Z0-9-_\.\/]+)(\.map)/ui', '', $css);
1043
+
1044
+ # fix url paths
1045
+ if(!empty($url)) {
1046
+ $matches = array(); preg_match_all("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", $css, $matches);
1047
+ foreach($matches[1] as $a) { $b = trim($a); if($b != $a) { $css = str_replace($a, $b, $css); } }
1048
+ $css = preg_replace("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"#])(.+?)['\"]?\s*\)/ui", "url(".dirname($url)."/$1)", $css);
1049
+ }
1050
+
1051
+ # minify string with relative urls
1052
+ if($min) {
1053
+ $css = fvm_minify_css_string($css);
1054
+ }
1055
+
1056
+ # add font-display for google fonts and fontawesome
1057
+ # https://developers.google.com/web/updates/2016/02/font-display
1058
+ $css = str_ireplace('font-family:', 'font-display:block;font-family:', $css);
1059
+
1060
+ # return css
1061
+ return trim($css);
1062
+
1063
+ }
1064
+
1065
+ return false;
1066
+ }
1067
+
1068
+
1069
+ # validate and minify js
1070
+ function fvm_maybe_minify_js($js, $url, $enable_js_minification) {
1071
+
1072
+ # return early if empty
1073
+ if(empty($js) || $js == false) { return $js; }
1074
+
1075
+ # process js only if it's not php or html
1076
+ if(fvm_not_php_html($js)) {
1077
+
1078
+ # globals
1079
+ global $fvm_settings;
1080
+
1081
+ # filtering
1082
+ $js = fvm_remove_utf8_bom($js);
1083
+
1084
+ # remove sourceMappingURL
1085
+ $js = preg_replace('/(\/\/\s*[#]\s*sourceMappingURL\s*[=]\s*)([a-zA-Z0-9-_\.\/]+)(\.map)/ui', '', $js);
1086
+
1087
+ # minify?
1088
+ if($enable_js_minification == true) {
1089
+
1090
+ # PHP Minify from https://github.com/matthiasmullie/minify
1091
+ $minifier = new FVM\MatthiasMullie\Minify\JS($js);
1092
+ $min = $minifier->minify();
1093
+
1094
+ # return if not empty
1095
+ if($min !== false && strlen(trim($min)) > 0) {
1096
+ return $min;
1097
+ }
1098
+ }
1099
+
1100
+ # return js
1101
+ return trim($js);
1102
+
1103
+ }
1104
+
1105
+ return false;
1106
+ }
1107
+
1108
+
1109
+ # minify css string with PHP Minify
1110
+ function fvm_minify_css_string($css) {
1111
+
1112
+ # return early if empty
1113
+ if(empty($css) || $css == false) { return $css; }
1114
+
1115
+ # get domain
1116
+ global $fvm_urls;
1117
+
1118
+ # minify
1119
+ $minifier = new FVM\MatthiasMullie\Minify\CSS($css);
1120
+ $minifier->setMaxImportSize(10); # embed assets up to 10 Kb (default 5Kb) - processes gif, png, jpg, jpeg, svg & woff
1121
+ $min = $minifier->minify();
1122
+
1123
+ # make relative urls
1124
+ $min = str_replace('http://'.$fvm_urls['wp_domain'], '', $min);
1125
+ $min = str_replace('https://'.$fvm_urls['wp_domain'], '', $min);
1126
+ $min = str_replace('//'.$fvm_urls['wp_domain'], '', $min);
1127
+ $min = str_replace('http://'.str_ireplace('www.', '', $fvm_urls['wp_domain']), '', $min);
1128
+ $min = str_replace('https://'.str_ireplace('www.', '', $fvm_urls['wp_domain']), '', $min);
1129
+ $min = str_replace('//'.str_ireplace('www.', '', $fvm_urls['wp_domain']), '', $min);
1130
+
1131
+ # return
1132
+ if($min != false) {
1133
+ return $min;
1134
+ }
1135
+
1136
+ # fallback
1137
+ return $css;
1138
+ }
1139
+
1140
+
1141
+ # escape html tags for document.write
1142
+ function fvm_escape_url_js($str) {
1143
+ $str = trim(preg_replace('/[\t\n\r\s]+/iu', ' ', $str));
1144
+ return str_replace(array('\\\\\"', '\\\\"', '\\\"', '\\"'), '\"', json_encode($str));
1145
+ }
1146
+
1147
+
1148
+ # try catch wrapper for merged javascript
1149
+ function fvm_try_catch_wrap($js, $href=null) {
1150
+ $loc = ''; if(isset($href)) { $loc = '[ Merged: '. $href . ' ] '; }
1151
+ return 'try{'. PHP_EOL . $js . PHP_EOL . '}catch(e){console.error("An error has occurred. '.$loc.'[ "+e.stack+" ]");}';
1152
+ }
1153
+
1154
+
1155
+ # wrap html tag in our function for low priority processing inplace
1156
+ function fvm_wrap_script_inline($tag) {
1157
+
1158
+ # must be a valid type
1159
+ if(!is_object($tag) && !is_array($tag)) {
1160
+ return $tag;
1161
+ }
1162
+
1163
+ # skip application/ld+json
1164
+ if(isset($tag->type) && $tag->type == 'application/ld+json') {
1165
+ return $tag;
1166
+ }
1167
+
1168
+ # scripts with src
1169
+ if(isset($tag->src)) {
1170
+
1171
+ # get all attributes into $rem
1172
+ $rem = '';
1173
+ foreach($tag->getAllAttributes() as $k=>$v){
1174
+ if($k != 'async' && $k != 'defer' && $k != 'src' && $k != 'type') {
1175
+ $rem.= "b.setAttribute('$k','$v');";
1176
+ }
1177
+ }
1178
+
1179
+ # rewrite scripts without document.write, for async scripts
1180
+ if(isset($tag->async)) {
1181
+ $tag->outertext = "<script data-cfasync='false'>if(wpruag()){(function(a){var b=a.createElement('script'),c=a.scripts[0];b.src='".$tag->src."';".$rem."c.parentNode.insertBefore(b,c);}(document));}</script>";
1182
+ return $tag;
1183
+ }
1184
+
1185
+ # rewrite scripts without document.write, for defer scripts
1186
+ if (isset($tag->defer)) {
1187
+ $tag->outertext = "<script data-cfasync='false'>if(wpruag()){(function(a){var b=a.createElement('script'),c=a.scripts[0];b.src='".$tag->src."';b.async=false;".$rem."c.parentNode.insertBefore(b,c);}(document));}</script>";
1188
+ return $tag;
1189
+ }
1190
+
1191
+ # check for line breaks, skip if found and not empty code inside
1192
+ if(stripos(trim($tag->innertext), PHP_EOL) !== false) {
1193
+ return $tag;
1194
+ }
1195
+
1196
+ # fallback to document.write (outerHTML won't work)
1197
+ $tag->outertext = '<script data-cfasync="false">if(wpruag()){document.write('.fvm_escape_url_js($tag->outertext).');}</script>';
1198
+ return $tag;
1199
+
1200
+ }
1201
+
1202
+ # fallback
1203
+ return $tag;
1204
+ }
1205
+
1206
+
1207
+ # Disable the emoji's on the frontend
1208
+ function fvm_disable_emojis() {
1209
+ global $fvm_settings;
1210
+ if(isset($fvm_settings['html']['disable_emojis']) && $fvm_settings['html']['disable_emojis'] == true) {
1211
+ remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
1212
+ remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
1213
+ remove_action( 'wp_print_styles', 'print_emoji_styles' );
1214
+ remove_action( 'admin_print_styles', 'print_emoji_styles' );
1215
+ remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
1216
+ remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
1217
+ remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
1218
+ }
1219
+ }
1220
+
1221
+
1222
+ # stop slow ajax requests for bots
1223
+ function fvm_ajax_optimizer() {
1224
+ if(isset($_SERVER['HTTP_USER_AGENT']) && (defined('DOING_AJAX') && DOING_AJAX) || (function_exists('is_ajax') && is_ajax()) || (function_exists('wp_doing_ajax') && wp_doing_ajax())){
1225
+ if (preg_match('/'.implode('|', array('x11.*fox\/54', 'oid\s4.*xus.*ome\/62', 'x11.*ome\/86\.0\.4', 'oobot', 'ighth', 'tmetr', 'eadles', 'ingdo', 'PTST')).'/i', $_SERVER['HTTP_USER_AGENT'])){ echo '0'; exit(); }
1226
+ }
1227
+ }
1228
+
1229
+
1230
+ # rewrite assets to cdn
1231
+ function fvm_rewrite_assets_cdn($html) {
1232
+
1233
+ # settings
1234
+ global $fvm_settings, $fvm_urls;
1235
+
1236
+ if(isset($fvm_urls['wp_domain']) && !empty($fvm_urls['wp_domain']) &&
1237
+ isset($fvm_settings['cdn']['enable']) && $fvm_settings['cdn']['enable'] == true &&
1238
+ isset($fvm_settings['cdn']['domain']) && !empty($fvm_settings['cdn']['domain']) &&
1239
+ isset($fvm_settings['cdn']['integration']) && !empty($fvm_settings['cdn']['integration'])) {
1240
+ $arr = fvm_string_toarray($fvm_settings['cdn']['integration']);
1241
+ if(is_array($arr) && count($arr) > 0) {
1242
+ foreach($html->find(implode(', ', $arr) ) as $elem) {
1243
+
1244
+ # preserve some attributes but replace others
1245
+ if (is_object($elem) && isset($elem->attr)) {
1246
+
1247
+ # get all attributes
1248
+ foreach ($elem->attr as $key=>$val) {
1249
+
1250
+ # skip href attribute for links
1251
+ if($key == 'href' && stripos($elem->outertext, '<a ') !== false) { continue; }
1252
+
1253
+ # skip certain attributes
1254
+ if(in_array($key, array('id', 'class', 'action'))) { continue; }
1255
+
1256
+ # replace other attributes
1257
+ $elem->{$key} = str_replace('//'.$fvm_urls['wp_domain'], '//'.$fvm_settings['cdn']['domain'], $elem->{$key});
1258
+ $elem->{$key} = str_replace('\/\/'.$fvm_urls['wp_domain'], '\/\/'.$fvm_settings['cdn']['domain'], $elem->{$key});
1259
+
1260
+ }
1261
+
1262
+ }
1263
+
1264
+ }
1265
+ }
1266
+ }
1267
+
1268
+ return $html;
1269
+ }
1270
+
1271
+
1272
+
1273
+ # replace css imports with origin css code
1274
+ function fvm_replace_css_imports($css, $rq=null) {
1275
+
1276
+ # globals
1277
+ global $fvm_urls, $fvm_settings;
1278
+
1279
+ # handle import url rules
1280
+ $cssimports = array();
1281
+ preg_match_all ("/@import[ ]*['\"]{0,}(url\()*['\"]*([^;'\"\)]*)['\"\)]*[;]{0,}/ui", $css, $cssimports);
1282
+ if(isset($cssimports[0]) && isset($cssimports[2])) {
1283
+ foreach($cssimports[0] as $k=>$cssimport) {
1284
+
1285
+ # if @import url rule, or guess full url
1286
+ if(stripos($cssimport, 'import url') !== false && isset($cssimports[2][$k])) {
1287
+ $url = trim($cssimports[2][$k]);
1288
+ } else {
1289
+ if(!is_null($rq) && !empty($rq)) {
1290
+ $url = dirname($rq) . '/' . trim($cssimports[2][$k]);
1291
+ }
1292
+ }
1293
+
1294
+ # must have
1295
+ if(!empty($url)) {
1296
+
1297
+ # make sure we have a complete url
1298
+ $href = fvm_normalize_url($url, $fvm_urls['wp_domain'], $fvm_urls['wp_home']);
1299
+
1300
+ # download, minify, cache (no ver query string)
1301
+ $tkey = hash('sha1', $href);
1302
+ $subcss = fvm_get_transient($tkey);
1303
+ if ($subcss === false) {
1304
+
1305
+ # get minification settings for files
1306
+ if(isset($fvm_settings['css']['min_files'])) {
1307
+ $enable_css_minification = $fvm_settings['css']['min_files'];
1308
+ }
1309
+
1310
+ # force minification on google fonts
1311
+ if(stripos($href, 'fonts.googleapis.com') !== false) {
1312
+ $enable_css_minification = true;
1313
+ }
1314
+
1315
+ # download file, get contents, merge
1316
+ $ddl = fvm_maybe_download($href);
1317
+
1318
+ # if success
1319
+ if(isset($ddl['content'])) {
1320
+
1321
+ # contents
1322
+ $subcss = $ddl['content'];
1323
+
1324
+ # minify
1325
+ $subcss = fvm_maybe_minify_css_file($subcss, $href, $enable_css_minification);
1326
+
1327
+ # developers filter
1328
+ $subcss = apply_filters( 'fvm_after_download_and_minify_code', $subcss, 'css');
1329
+
1330
+ # remove specific, minified CSS code
1331
+ if(isset($fvm_settings['css']['remove_code']) && !empty($fvm_settings['css']['remove_code'])) {
1332
+ $arr = fvm_string_toarray($fvm_settings['css']['remove_code']);
1333
+ if(is_array($arr) && count($arr) > 0) {
1334
+ foreach($arr as $str) {
1335
+ $subcss = str_replace($str, '', $subcss);
1336
+ }
1337
+ }
1338
+ }
1339
+
1340
+ # trim code
1341
+ $subcss = trim($subcss);
1342
+
1343
+ # size in bytes
1344
+ $fs = strlen($subcss);
1345
+ $ur = str_replace($fvm_urls['wp_home'], '', $href);
1346
+ $tkey_meta = array('fs'=>$fs, 'url'=>str_replace($fvm_cache_paths['cache_url_min'].'/', '', $ur), 'mt'=>$media);
1347
+
1348
+ # save
1349
+ fvm_set_transient(array('uid'=>$tkey, 'date'=>$tvers, 'type'=>'css', 'content'=>$subcss, 'meta'=>$tkey_meta));
1350
+ }
1351
+ }
1352
+
1353
+ # replace import rule with inline code
1354
+ if ($subcss !== false && !empty($subcss)) {
1355
+ $css = str_replace($cssimport, $subcss, $css);
1356
+ }
1357
+
1358
+ }
1359
+ }
1360
+ }
1361
+
1362
+ # return
1363
+ return $css;
1364
+
1365
+ }
1366
+
1367
+
1368
+ # get the domain name
1369
+ function fvm_get_domain() {
1370
+ if(isset($_SERVER['SERVER_NAME']) && !empty($_SERVER['SERVER_NAME'])) {
1371
+ return $_SERVER['SERVER_NAME'];
1372
+ } elseif (isset($_SERVER['HTTP_HOST']) && !empty($_SERVER['HTTP_HOST'])) {
1373
+ return $_SERVER['HTTP_HOST'];
1374
+ } elseif (function_exists('site_url')) {
1375
+ return parse_url(site_url())['host'];
1376
+ } else {
1377
+ return false;
1378
+ }
1379
+ }
1380
+
1381
+ # get the settings file path, current domain name, and uri path without query strings
1382
+ function fvm_get_uripath() {
1383
+ if (isset($_SERVER['REQUEST_URI']) && !empty($_SERVER['REQUEST_URI'])) {
1384
+ $current_uri = strtok($_SERVER['REQUEST_URI'], '?');
1385
+ $current_uri = str_replace('//', '/', str_replace('..', '', preg_replace( '/[ <>\'\"\r\n\t\(\)]/', '', $current_uri)));
1386
+ return $current_uri;
1387
+ } else {
1388
+ return false;
1389
+ }
1390
+ }
inc/frontend.php ADDED
@@ -0,0 +1,886 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ # Exit if accessed directly
4
+ if (!defined('ABSPATH')){ exit(); }
5
+
6
+ # functions needed only for frontend ###########
7
+
8
+ # must have for large strings processing during minification
9
+ @ini_set('pcre.backtrack_limit', 5000000);
10
+ @ini_set('pcre.recursion_limit', 5000000);
11
+
12
+ # our own minification libraries
13
+ include_once($fvm_var_inc_lib . DIRECTORY_SEPARATOR . 'raisermin' . DIRECTORY_SEPARATOR . 'minify.php');
14
+
15
+ # php simple html
16
+ # https://sourceforge.net/projects/simplehtmldom/
17
+ define('MAX_FILE_SIZE', 2000000); # Process HTML up to 2 Mb
18
+ include_once($fvm_var_inc_lib . DIRECTORY_SEPARATOR . 'simplehtmldom' . DIRECTORY_SEPARATOR . 'simple_html_dom.php');
19
+
20
+ # PHP Minify [1.3.60] for CSS minification only
21
+ # https://github.com/matthiasmullie/minify
22
+ $fvm_var_inc_lib_mm = $fvm_var_inc_lib . DIRECTORY_SEPARATOR . 'matthiasmullie' . DIRECTORY_SEPARATOR;
23
+ include_once($fvm_var_inc_lib_mm . 'minify' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Minify.php');
24
+ include_once($fvm_var_inc_lib_mm . 'minify' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'CSS.php');
25
+ include_once $fvm_var_inc_lib_mm . 'minify'. DIRECTORY_SEPARATOR .'src'. DIRECTORY_SEPARATOR .'JS.php';
26
+ include_once $fvm_var_inc_lib_mm . 'minify'. DIRECTORY_SEPARATOR .'src'. DIRECTORY_SEPARATOR .'Exception.php';
27
+ include_once $fvm_var_inc_lib_mm . 'minify'. DIRECTORY_SEPARATOR .'src'. DIRECTORY_SEPARATOR .'Exceptions'. DIRECTORY_SEPARATOR .'BasicException.php';
28
+ include_once $fvm_var_inc_lib_mm . 'minify'. DIRECTORY_SEPARATOR .'src'. DIRECTORY_SEPARATOR .'Exceptions'. DIRECTORY_SEPARATOR .'FileImportException.php';
29
+ include_once $fvm_var_inc_lib_mm . 'minify'. DIRECTORY_SEPARATOR .'src'. DIRECTORY_SEPARATOR .'Exceptions'. DIRECTORY_SEPARATOR .'IOException.php';
30
+ include_once($fvm_var_inc_lib_mm . 'path-converter' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'ConverterInterface.php');
31
+ include_once($fvm_var_inc_lib_mm . 'path-converter' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Converter.php');
32
+
33
+ ################################################
34
+
35
+
36
+ # start buffering before template
37
+ function fvm_start_buffer() {
38
+ if(fvm_can_minify()) {
39
+ ob_start('fvm_process_page', 0, PHP_OUTPUT_HANDLER_REMOVABLE);
40
+ }
41
+ }
42
+
43
+ # process html from fvm_end_buffer
44
+ function fvm_process_page($html) {
45
+
46
+ # get globals
47
+ global $fvm_settings, $fvm_cache_paths, $fvm_urls;
48
+
49
+ # can process minification?
50
+ if(fvm_can_minify()) {
51
+
52
+ # defaults
53
+ $tvers = get_option('fvm_last_cache_update', '0');
54
+ $now = time();
55
+ $htmlpreloader = array();
56
+ $htmlcssheader = array();
57
+ $lp_css_last_ff_inline = '';
58
+
59
+ # get html into an object
60
+ # https://simplehtmldom.sourceforge.io/manual.htm
61
+ $html_object = str_get_html($html, true, true, 'UTF-8', false, PHP_EOL, ' ');
62
+
63
+ # return early if html is not an object, or overwrite html into an object for processing
64
+ if (!is_object($html_object)) {
65
+ return $html . '<!-- simplehtmldom failed to process the html -->';
66
+ } else {
67
+ $html = $html_object;
68
+ }
69
+
70
+
71
+ # process css, if not disabled
72
+ if(isset($fvm_settings['css']['enable']) && $fvm_settings['css']['enable'] == true) {
73
+
74
+ # defaults
75
+ $fvm_styles = array();
76
+ $fvm_styles_log = array();
77
+ $enable_css_minification = true;
78
+
79
+ # exclude styles and link tags inside scripts, no scripts or html comments
80
+ $excl = array();
81
+ foreach($html->find('script link[rel=stylesheet], script style, noscript style, noscript link[rel=stylesheet], comment') as $element) {
82
+ $excl[] = $element->outertext;
83
+ }
84
+
85
+ # collect all styles, but filter out if excluded
86
+ $allcss = array();
87
+ foreach($html->find('link[rel=stylesheet], style') as $element) {
88
+ if(!in_array($element->outertext, $excl)) {
89
+ $allcss[] = $element;
90
+ }
91
+ }
92
+
93
+ # merge and process
94
+ foreach($allcss as $k=>$tag) {
95
+
96
+ # ignore list, leave these alone
97
+ if(isset($tag->href) && isset($fvm_settings['css']['ignore']) && !empty($fvm_settings['css']['ignore'])) {
98
+ $arr = fvm_string_toarray($fvm_settings['css']['ignore']);
99
+ if(is_array($arr) && count($arr) > 0) {
100
+ foreach ($arr as $e) {
101
+ if(stripos($tag->href, $e) !== false) {
102
+ continue 2;
103
+ }
104
+ }
105
+ }
106
+ }
107
+
108
+ # remove css files
109
+ if(isset($tag->href) && isset($fvm_settings['css']['remove']) && !empty($fvm_settings['css']['remove'])) {
110
+ $arr = fvm_string_toarray($fvm_settings['css']['remove']);
111
+ if(is_array($arr) && count($arr) > 0) {
112
+ foreach ($arr as $e) {
113
+ if(stripos($tag->href, $e) !== false) {
114
+ $tag->outertext = '';
115
+ unset($allcss[$k]);
116
+ continue 2;
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ # change the mediatype for files that are to be merged into the fonts css
123
+ if(isset($tag->href) && isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
124
+ $arr = array('/fonts.googleapis.com', '/animate.css', '/animate.min.css', '/icomoon.css', '/animations/', '/eicons/css/', 'font-awesome', 'fontawesome', '/flag-icon.min.css', '/fonts.css', '/pe-icon-7-stroke.css', '/fontello.css', '/dashicons.min.css');
125
+ if(is_array($arr) && count($arr) > 0) {
126
+ foreach ($arr as $e) {
127
+ if(stripos($tag->href, $e) !== false) {
128
+ $tag->media = 'fonts';
129
+ break;
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+
136
+ # normalize mediatypes
137
+ $media = 'all';
138
+ if(isset($tag->media)) {
139
+ $media = $tag->media;
140
+ if ($media == 'screen' || $media == 'screen, print' || empty($media) || is_null($media) || $media == false) {
141
+ $media = 'all';
142
+ }
143
+ }
144
+
145
+ # remove print mediatypes
146
+ if(isset($fvm_settings['css']['noprint']) && $fvm_settings['css']['noprint'] == true && $media == 'print') {
147
+ $tag->outertext = '';
148
+ unset($allcss[$k]);
149
+ continue;
150
+ }
151
+
152
+ # process css files
153
+ if($tag->tag == 'link' && isset($tag->href)) {
154
+
155
+ # default
156
+ $css = '';
157
+
158
+ # make sure we have a complete url
159
+ $href = fvm_normalize_url($tag->href, $fvm_urls['wp_domain'], $fvm_urls['wp_home']);
160
+
161
+ # get minification settings for files
162
+ if(isset($fvm_settings['css']['min_disable']) && $fvm_settings['css']['min_disable'] == '1') {
163
+ $enable_css_minification = true;
164
+ }
165
+
166
+ # force minification on google fonts
167
+ if(stripos($href, 'fonts.googleapis.com') !== false) {
168
+ $enable_css_minification = true;
169
+ }
170
+
171
+ # download, minify, cache (no ver query string)
172
+ $tkey = hash('sha1', $href);
173
+ $css = fvm_get_transient($tkey);
174
+ if ($css === false) {
175
+
176
+ # open or download file, get contents
177
+ $ddl = array();
178
+ $ddl = fvm_maybe_download($href);
179
+
180
+ # if success
181
+ if(isset($ddl['content'])) {
182
+
183
+ # contents
184
+ $css = $ddl['content'];
185
+
186
+ # open or download file, get contents
187
+ $css = fvm_maybe_minify_css_file($css, $href, $enable_css_minification);
188
+
189
+ # developers filter
190
+ $css = apply_filters( 'fvm_after_download_and_minify_code', $css, 'css');
191
+
192
+ # quick integrity check
193
+ if(!empty($css) && $css != false) {
194
+
195
+ # trim code
196
+ $css = trim($css);
197
+
198
+ # execution time in ms, size in bytes
199
+ $fs = strlen($css);
200
+ $ur = str_replace($fvm_urls['wp_home'], '', $href);
201
+ $tkey_meta = array('fs'=>$fs, 'url'=>str_replace($fvm_cache_paths['cache_url_min'].'/', '', $ur), 'mt'=>$media);
202
+
203
+ # save
204
+ fvm_set_transient(array('uid'=>$tkey, 'date'=>$tvers, 'type'=>'css', 'content'=>$css, 'meta'=>$tkey_meta));
205
+
206
+ }
207
+ }
208
+ }
209
+
210
+ # success, get final contents to array
211
+ if($css !== false) {
212
+ $fvm_styles[$media][] = $css;
213
+ $fvm_styles_log[$media][] = $tkey;
214
+ $tag->outertext = '';
215
+ unset($allcss[$k]);
216
+ continue;
217
+ }
218
+
219
+ }
220
+
221
+
222
+ # process styles
223
+ if($tag->tag == 'style' && !isset($tag->href)) {
224
+
225
+ # default
226
+ $css = '';
227
+
228
+ # get minification settings for files
229
+ if(isset($fvm_settings['css']['min_disable']) && $fvm_settings['css']['min_disable'] == '1') {
230
+ $enable_css_minification = true;
231
+ }
232
+
233
+ # minify inline CSS
234
+ $css = $tag->innertext;
235
+ if($enable_css_minification) {
236
+ $css = fvm_minify_css_string($css);
237
+ }
238
+
239
+ # handle import rules
240
+ $css = fvm_replace_css_imports($css);
241
+
242
+ # remove fonts and icons and collect for later
243
+ if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
244
+ $mff = array();
245
+ preg_match_all('/(\@font-face)([^}]+)(\})/', $css, $mff);
246
+ if(isset($mff[0]) && is_array($mff[0])) {
247
+ foreach($mff[0] as $ff) {
248
+ $css = str_replace($ff, '', $css);
249
+ $lp_css_last_ff_inline.= $ff . PHP_EOL;
250
+ }
251
+ }
252
+ }
253
+
254
+ # add cdn support
255
+ if(isset($fvm_settings['cdn']['enable']) && $fvm_settings['cdn']['enable'] == true &&
256
+ isset($fvm_settings['cdn']['domain']) && !empty($fvm_settings['cdn']['domain'])) {
257
+ if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
258
+
259
+ # scheme + site url
260
+ $fcdn = str_replace($fvm_urls['wp_domain'], $fvm_settings['cdn']['domain'], $fvm_urls['wp_home']);
261
+
262
+ # replacements
263
+ $css = str_ireplace('url(/wp-content/', 'url('.$fcdn.'/wp-content/', $css);
264
+ $css = str_ireplace('url("/wp-content/', 'url("'.$fcdn.'/wp-content/', $css);
265
+ $css = str_ireplace('url(\'/wp-content/', 'url(\''.$fcdn.'/wp-content/', $css);
266
+
267
+ }
268
+ }
269
+
270
+ # trim code
271
+ $css = trim($css);
272
+
273
+ # decide what to do with the inlined css
274
+ if(empty($css)) {
275
+ # delete empty style tags
276
+ $tag->outertext = '';
277
+ unset($allcss[$k]);
278
+ continue;
279
+ } else {
280
+ # process inlined styles
281
+ $tag->innertext = $css;
282
+ unset($allcss[$k]);
283
+ continue;
284
+ }
285
+
286
+ }
287
+
288
+ }
289
+
290
+ # generate merged css files, foreach mediatype
291
+ if(is_array($fvm_styles) && count($fvm_styles) > 0) {
292
+
293
+ # collect fonts for last
294
+ $lp_css_last = '';
295
+ $lp_css_last_ff = '';
296
+
297
+ # merge files
298
+ foreach ($fvm_styles as $mediatype=>$css_process) {
299
+
300
+ # skip fonts file
301
+ if($mediatype == 'fonts') {
302
+ $lp_css_last = $fvm_styles['fonts'];
303
+ continue;
304
+ }
305
+
306
+ # merge code, generate cache file paths and urls
307
+ $file_css_code = implode('', $css_process);
308
+ $css_uid = $tvers.'-'.hash('sha1', $file_css_code);
309
+ $file_css = $fvm_cache_paths['cache_dir_min'] . DIRECTORY_SEPARATOR . $css_uid.'.min.css';
310
+ $file_css_url = $fvm_cache_paths['cache_url_min'].'/'.$css_uid.'.min.css';
311
+
312
+ # remove fonts and icons from final css
313
+ if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
314
+ $mff = array();
315
+ preg_match_all('/(\@font-face)([^}]+)(\})/', $file_css_code, $mff);
316
+ if(isset($mff[0]) && is_array($mff[0])) {
317
+ foreach($mff[0] as $ff) {
318
+ $file_css_code = str_replace($ff, '', $file_css_code);
319
+ $lp_css_last_ff.= $ff . PHP_EOL;
320
+ }
321
+ }
322
+ }
323
+
324
+ # add cdn support
325
+ if(isset($fvm_settings['cdn']['enable']) && $fvm_settings['cdn']['enable'] == true &&
326
+ isset($fvm_settings['cdn']['domain']) && !empty($fvm_settings['cdn']['domain'])) {
327
+ if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
328
+ $file_css_url = str_replace('//'.$wpraiser_urls['wp_domain'], '//'.$wpraiser_settings['cdn']['domain'], $file_css_url);
329
+ }
330
+ }
331
+
332
+ # generate cache file
333
+ clearstatcache();
334
+ if (!file_exists($file_css)) {
335
+
336
+ # prepare log
337
+ $log = (array) array_values($fvm_styles_log[$mediatype]);
338
+ $log_meta = array('loc'=>home_url(add_query_arg(NULL, NULL)), 'fl'=>$file_css_url, 'mt'=>$mediatype);
339
+
340
+ # generate cache, write log
341
+ if(!empty($file_css_code)) {
342
+ fvm_save_log(array('uid'=>$file_css_url, 'date'=>$now, 'type'=>'css', 'meta'=>$log_meta, 'content'=>$log));
343
+ fvm_save_file($file_css, $file_css_code);
344
+ }
345
+
346
+ }
347
+
348
+ # if file exists
349
+ clearstatcache();
350
+ if (file_exists($file_css)) {
351
+
352
+ # preload and save for html implementation (with priority order prefix)
353
+ $htmlpreloader['b_'.$css_uid] = '<link rel="preload" href="'.$file_css_url.'" as="style" media="'.$mediatype.'" />';
354
+
355
+ # async or render block css
356
+ if(isset($fvm_settings['css']['async']) && $fvm_settings['css']['async'] == true) {
357
+ $htmlcssheader['b_'.$css_uid] = '<link rel="stylesheet" href="'.$file_css_url.'" media="print" onload="this.media=\''.$mediatype.'\'">';
358
+ } else {
359
+ $htmlcssheader['b_'.$css_uid] = '<link rel="stylesheet" href="'.$file_css_url.'" media="'.$mediatype.'" />';
360
+ }
361
+ }
362
+
363
+ }
364
+
365
+
366
+ # generate merged css files, foreach mediatype
367
+ if(!empty($lp_css_last) || !empty($lp_css_last_ff) || !empty($lp_css_last_ff_inline)) {
368
+
369
+ # reset to all
370
+ $mediatype = 'all';
371
+
372
+ # merge code, generate cache file paths and urls
373
+ $file_css_code = implode('', $lp_css_last).$lp_css_last_ff.$lp_css_last_ff_inline;
374
+ $css_uid = $tvers.'-'.hash('sha1', $file_css_code);
375
+ $file_css = $fvm_cache_paths['cache_dir_min'] . DIRECTORY_SEPARATOR . $css_uid.'.min.css';
376
+ $file_css_url = $fvm_cache_paths['cache_url_min'].'/'.$css_uid.'.min.css';
377
+
378
+ # add cdn support
379
+ if(isset($fvm_settings['cdn']['enable']) && $fvm_settings['cdn']['enable'] == true &&
380
+ isset($fvm_settings['cdn']['domain']) && !empty($fvm_settings['cdn']['domain'])) {
381
+ if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
382
+ $file_css_url = str_replace('//'.$wpraiser_urls['wp_domain'], '//'.$wpraiser_settings['cdn']['domain'], $file_css_url);
383
+ }
384
+ }
385
+
386
+ # generate cache file
387
+ clearstatcache();
388
+ if (!file_exists($file_css)) {
389
+
390
+ # prepare log
391
+ $log = (array) array_values($fvm_styles_log[$mediatype]);
392
+ $log_meta = array('loc'=>home_url(add_query_arg(NULL, NULL)), 'fl'=>$file_css_url, 'mt'=>$mediatype);
393
+
394
+ # generate cache, write log
395
+ if(!empty($file_css_code)) {
396
+ fvm_save_log(array('uid'=>$file_css_url, 'date'=>$now, 'type'=>'css', 'meta'=>$log_meta, 'content'=>$log));
397
+ fvm_save_file($file_css, $file_css_code);
398
+ }
399
+ }
400
+
401
+ # if file exists
402
+ clearstatcache();
403
+ if (file_exists($file_css)) {
404
+ # preload and save for html implementation (with priority order prefix)
405
+ $htmlpreloader['b_'.$css_uid] = '<link rel="preload" href="'.$file_css_url.'" as="style" media="'.$mediatype.'" />';
406
+
407
+ # async or render block css
408
+ if(isset($fvm_settings['css']['async']) && $fvm_settings['css']['async'] == true) {
409
+
410
+ # Load CSS Asynchronously with javascript
411
+ # https://www.filamentgroup.com/lab/load-css-simpler/
412
+ $htmlcssheader['a_'.$css_uid] = '<link rel="stylesheet" href="'.$file_css_url.'" media="print" onload="this.media=\''.$mediatype.'\'">';
413
+
414
+ } else {
415
+ $htmlcssheader['b_'.$css_uid] = '<link rel="stylesheet" href="'.$file_css_url.'" media="'.$mediatype.'" />';
416
+ }
417
+
418
+ }
419
+
420
+ }
421
+
422
+ }
423
+ }
424
+
425
+
426
+
427
+
428
+ # always disable js minification in certain areas
429
+ $nojsmin = false;
430
+ if(function_exists('is_cart') && is_cart()){ $nojsmin = true; } # cart
431
+
432
+ # process js, if not disabled
433
+ if(isset($fvm_settings['js']['enable']) && $fvm_settings['js']['enable'] == true && $nojsmin === false) {
434
+
435
+ # defaults
436
+ $scripts_duplicate_check = array();
437
+ $enable_js_minification = true;
438
+ $htmljscodeheader = array();
439
+ $htmljscodedefer = array();
440
+ $scripts_header = array();
441
+ $scripts_footer = array();
442
+
443
+ # get all scripts
444
+ $allscripts = array();
445
+ foreach($html->find('script') as $element) {
446
+ $allscripts[] = $element;
447
+ }
448
+
449
+ # process all scripts
450
+ if (is_array($allscripts) && count($allscripts) > 0) {
451
+ foreach($allscripts as $k=>$tag) {
452
+
453
+ # handle application/ld+json or application/json before anything else
454
+ if(isset($tag->type) && ($tag->type == 'application/ld+json' || $tag->type == 'application/json')) {
455
+ $tag->innertext = fvm_minify_microdata($tag->innertext);
456
+ unset($allscripts[$k]);
457
+ continue;
458
+ }
459
+
460
+ # remove js code
461
+ if(isset($tag->outertext) && isset($fvm_settings['js']['remove']) && !empty($fvm_settings['js']['remove'])) {
462
+ $arr = fvm_string_toarray($fvm_settings['js']['remove']);
463
+ if(is_array($arr) && count($arr) > 0) {
464
+ foreach ($arr as $e) {
465
+ if(stripos($tag->outertext, $e) !== false) {
466
+ $tag->outertext = '';
467
+ unset($allscripts[$k]);
468
+ continue 2;
469
+ }
470
+ }
471
+ }
472
+ }
473
+
474
+
475
+ # process inline scripts
476
+ if(!isset($tag->src)) {
477
+
478
+ # default
479
+ $js = '';
480
+
481
+ # get minification settings for files
482
+ if(isset($fvm_settings['js']['min_disable']) && $fvm_settings['js']['min_disable'] == true) {
483
+ $enable_js_minification = false;
484
+ }
485
+
486
+ # minify inline scripts
487
+ $js = $tag->innertext;
488
+ $js = fvm_maybe_minify_js($js, null, $enable_js_minification);
489
+
490
+ # Delay third party scripts and tracking codes (uses PHP stripos against the script innerHTML or the src attribute)
491
+ if(isset($fvm_settings['js']['thirdparty']) && !empty($fvm_settings['js']['thirdparty'])) {
492
+ if(isset($fvm_settings['js']['thirdparty']) && !empty($fvm_settings['js']['thirdparty'])) {
493
+ $arr = fvm_string_toarray($fvm_settings['js']['thirdparty']);
494
+ if(is_array($arr) && count($arr) > 0) {
495
+ foreach ($arr as $b) {
496
+ if(stripos($js, $b) !== false) {
497
+ $js = 'window.addEventListener("load",function(){var c=setTimeout(b,5E3),d=["mouseover","keydown","touchmove","touchstart"];d.forEach(function(a){window.addEventListener(a,e,{passive:!0})});function e(){b();clearTimeout(c);d.forEach(function(a){window.removeEventListener(a,e,{passive:!0})})}function b(){console.log("FVM: Loading Third Party Script (inline)!");'.$js.'};});';
498
+ break;
499
+ }
500
+ }
501
+ }
502
+ }
503
+ }
504
+
505
+ # delay inline scripts until after the 'window.load' event
506
+ if(isset($fvm_settings['js']['defer_dependencies']) && !empty($fvm_settings['js']['defer_dependencies'])) {
507
+ $arr = fvm_string_toarray($fvm_settings['js']['defer_dependencies']);
508
+ if(is_array($arr) && count($arr) > 0) {
509
+ foreach ($arr as $e) {
510
+ if(stripos($js, $e) !== false && stripos($js, 'FVM:') === false) {
511
+ $js = 'window.addEventListener("load",function(){console.log("FVM: Loading Inline Dependency!");'.$js.'});';
512
+ }
513
+ }
514
+ }
515
+ }
516
+
517
+
518
+ # replace tag on the html
519
+ $tag->innertext = $js;
520
+
521
+ # mark as processed, unset and break inner loop
522
+ unset($allscripts[$k]);
523
+ continue;
524
+
525
+ }
526
+
527
+
528
+ # process js files
529
+ if(isset($tag->src)) {
530
+
531
+ # make sure we have a complete url
532
+ $href = fvm_normalize_url($tag->src, $fvm_urls['wp_domain'], $fvm_urls['wp_home']);
533
+
534
+ # upgrade jQuery library and jQuery migrate to version 3
535
+ if(isset($fvm_settings['js']['jqupgrade']) && $fvm_settings['js']['jqupgrade'] == true) {
536
+ # jquery 3
537
+ if(stripos($tag->src, '/jquery.js') !== false || stripos($tag->src, '/jquery.min.js') !== false) {
538
+ $href = 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js';
539
+ }
540
+ # jquery migrate 3
541
+ if(stripos($tag->src, '/jquery-migrate.') !== false || stripos($tag->src, '/jquery-migrate-') !== false) { $href = 'https://cdnjs.cloudflare.com/ajax/libs/jquery-migrate/3.3.1/jquery-migrate.min.js'; }
542
+ }
543
+
544
+
545
+ # get minification settings for files
546
+ if(isset($fvm_settings['js']['min_disable']) && $fvm_settings['js']['min_disable'] == true) {
547
+ $enable_js_minification = false;
548
+ }
549
+
550
+
551
+ # Delay third party scripts and tracking codes
552
+ # uses PHP stripos against the script src, if it's async or deferred
553
+ if(isset($tag->async) || isset($tag->defer)) {
554
+ if(isset($fvm_settings['js']['thirdparty']) && !empty($fvm_settings['js']['thirdparty'])) {
555
+ if(isset($fvm_settings['js']['thirdparty']) && !empty($fvm_settings['js']['thirdparty'])) {
556
+ $arr = fvm_string_toarray($fvm_settings['js']['thirdparty']);
557
+ if(is_array($arr) && count($arr) > 0) {
558
+ foreach ($arr as $b) {
559
+ if(stripos($tag->src, $b) !== false) {
560
+
561
+ # get all extra attributes into $rem
562
+ $rem = '';
563
+ foreach($tag->getAllAttributes() as $k=>$v){
564
+ $k = trim($k); $v = trim($v);
565
+ if($k != 'async' && $k != 'defer' && $k != 'src' && $k != 'type') {
566
+ $rem.= "b.setAttribute('$k','$v');";
567
+ }
568
+ }
569
+
570
+ # defer attribute? (async is always by default)
571
+ if(isset($tag->defer)) {
572
+ $rem.= 'b.async=false;';
573
+ }
574
+
575
+ # generate and set delayed script tag
576
+ $tag->outertext = "<script data-cfasync='false'>".'window.addEventListener("load",function(){var c=setTimeout(b,5E3),d=["mouseover","keydown","touchmove","touchstart"];d.forEach(function(a){window.addEventListener(a,e,{passive:!0})});function e(){b();clearTimeout(c);d.forEach(function(a){window.removeEventListener(a,e,{passive:!0})})}function b(){'."(function(a){var b=a.createElement('script'),c=a.scripts[0];b.src='".trim($tag->src)."';".$rem."c.parentNode.insertBefore(b,c);}(document));".'};});'."</script>";
577
+ unset($allscripts[$k]);
578
+ continue 2;
579
+
580
+ }
581
+ }
582
+ }
583
+ }
584
+ }
585
+ }
586
+
587
+ # render blocking scripts in the header
588
+ if(isset($fvm_settings['js']['merge_header']) && !empty($fvm_settings['js']['merge_header'])) {
589
+ $arr = fvm_string_toarray($fvm_settings['js']['merge_header']);
590
+ if(is_array($arr) && count($arr) > 0) {
591
+ foreach ($arr as $e) {
592
+ if(stripos($href, $e) !== false) {
593
+
594
+ # download, minify, cache
595
+ $tkey = hash('sha1', $href);
596
+ $js = fvm_get_transient($tkey);
597
+ if ($js === false) {
598
+
599
+ # open or download file, get contents
600
+ $ddl = array();
601
+ $ddl = fvm_maybe_download($href);
602
+
603
+ # if success
604
+ if(isset($ddl['content'])) {
605
+
606
+ # contents
607
+ $js = $ddl['content'];
608
+
609
+ # minify, save and wrap
610
+ $js = fvm_maybe_minify_js($js, $href, $enable_js_minification);
611
+
612
+ # try catch
613
+ $js = fvm_try_catch_wrap($js);
614
+
615
+ # developers filter
616
+ $js = apply_filters( 'fvm_after_download_and_minify_code', $js, 'js');
617
+
618
+ # quick integrity check
619
+ if(!empty($js) && $js != false) {
620
+
621
+ # execution time in ms, size in bytes
622
+ $fs = strlen($js);
623
+ $ur = str_replace($fvm_urls['wp_home'], '', $href);
624
+ $tkey_meta = array('fs'=>$fs, 'url'=>str_replace($fvm_cache_paths['cache_url_min'].'/', '', $ur));
625
+
626
+ # save
627
+ fvm_set_transient(array('uid'=>$tkey, 'date'=>$tvers, 'type'=>'js', 'content'=>$js, 'meta'=>$tkey_meta));
628
+
629
+ }
630
+ }
631
+ }
632
+
633
+ # collect and mark as done for html removal
634
+ $scripts_header[$tkey] = $js;
635
+ $scripts_header_log[$tkey] = $tkey;
636
+
637
+ # mark as processed, unset and break inner loop
638
+ $tag->outertext = '';
639
+ unset($allscripts[$k]);
640
+ continue 2;
641
+
642
+ }
643
+ }
644
+ }
645
+ }
646
+
647
+
648
+ # merge and defer scripts
649
+ if(isset($fvm_settings['js']['merge_defer']) && !empty($fvm_settings['js']['merge_defer'])) {
650
+ $arr = fvm_string_toarray($fvm_settings['js']['merge_defer']);
651
+ if(is_array($arr) && count($arr) > 0) {
652
+ foreach ($arr as $e) {
653
+ if(stripos($href, $e) !== false) {
654
+
655
+ # download, minify, cache
656
+ $tkey = hash('sha1', $href);
657
+ $js = fvm_get_transient($tkey);
658
+ if ($js === false) {
659
+
660
+ # open or download file, get contents
661
+ $ddl = array();
662
+ $ddl = fvm_maybe_download($href);
663
+
664
+ # if success
665
+ if(isset($ddl['content'])) {
666
+
667
+ # contents
668
+ $js = $ddl['content'];
669
+
670
+ # minify, save and wrap
671
+ $js = fvm_maybe_minify_js($js, $href, $enable_js_minification);
672
+
673
+ # try catch
674
+ $js = fvm_try_catch_wrap($js);
675
+
676
+ # developers filter
677
+ $js = apply_filters( 'fvm_after_download_and_minify_code', $js, 'js');
678
+
679
+ # quick integrity check
680
+ if(!empty($js) && $js != false) {
681
+
682
+ # execution time in ms, size in bytes
683
+ $fs = strlen($js);
684
+ $ur = str_replace($fvm_urls['wp_home'], '', $href);
685
+ $tkey_meta = array('fs'=>$fs, 'url'=>str_replace($fvm_cache_paths['cache_url_min'].'/', '', $ur));
686
+
687
+ # save
688
+ fvm_set_transient(array('uid'=>$tkey, 'date'=>$tvers, 'type'=>'js', 'content'=>$js, 'meta'=>$tkey_meta));
689
+
690
+ }
691
+ }
692
+ }
693
+
694
+ # collect and mark as done for html removal
695
+ $scripts_footer[$tkey] = $js;
696
+ $scripts_footer_log[$tkey] = $tkey;
697
+
698
+ # mark as processed, unset and break inner loop
699
+ $tag->outertext = '';
700
+ unset($allscripts[$k]);
701
+ continue 2;
702
+
703
+ }
704
+ }
705
+ }
706
+ }
707
+
708
+ }
709
+
710
+ }
711
+ }
712
+
713
+
714
+
715
+ # generate header merged scripts
716
+ if(count($scripts_header) > 0) {
717
+
718
+ # merge code, generate cache file paths and urls
719
+ $fheader_code = implode('', $scripts_header);
720
+ $js_header_uid = $tvers.'-'.hash('sha1', $fheader_code).'.header';
721
+ $fheader = $fvm_cache_paths['cache_dir_min'] . DIRECTORY_SEPARATOR . $js_header_uid.'.min.js';
722
+ $fheader_url = $fvm_cache_paths['cache_url_min'].'/'.$js_header_uid.'.min.js';
723
+
724
+ # add cdn support
725
+ if(isset($fvm_settings['cdn']['enable']) && $fvm_settings['cdn']['enable'] == true &&
726
+ isset($fvm_settings['cdn']['domain']) && !empty($fvm_settings['cdn']['domain'])) {
727
+ if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
728
+ $fheader_url = str_replace('//'.$wpraiser_urls['wp_domain'], '//'.$wpraiser_settings['cdn']['domain'], $fheader_url);
729
+ }
730
+ }
731
+
732
+ # generate cache file
733
+ clearstatcache();
734
+ if (!file_exists($fheader)) {
735
+
736
+ # prepare log
737
+ $log = (array) array_values($scripts_header_log);
738
+ $log_meta = array('loc'=>home_url(add_query_arg(NULL, NULL)), 'fl'=>$fheader_url);
739
+
740
+ # generate cache, write log
741
+ if(!empty($fheader_code)) {
742
+ fvm_save_log(array('uid'=>$fheader_url, 'date'=>$now, 'type'=>'js', 'meta'=>$log_meta, 'content'=>$log));
743
+ fvm_save_file($fheader, $fheader_code);
744
+ }
745
+ }
746
+
747
+ # preload and save for html implementation (with priority order prefix)
748
+ $htmlpreloader['c_'.$fheader_url] = '<link rel="preload" href="'.$fheader_url.'" as="script" />';
749
+ $htmljscodeheader['c_'.$js_header_uid] = "<script data-cfasync='false' src='".$fheader_url."'></script>";
750
+
751
+ }
752
+
753
+ # generate footer merged scripts
754
+ if(count($scripts_footer) > 0) {
755
+
756
+ # merge code, generate cache file paths and urls
757
+ $ffooter_code = implode('', $scripts_footer);
758
+ $js_ffooter_uid = $tvers.'-'.hash('sha1', $ffooter_code).'.footer';
759
+ $ffooter = $fvm_cache_paths['cache_dir_min'] . DIRECTORY_SEPARATOR . $js_ffooter_uid.'.min.js';
760
+ $ffooter_url = $fvm_cache_paths['cache_url_min'].'/'.$js_ffooter_uid.'.min.js';
761
+
762
+ # add cdn support
763
+ if(isset($fvm_settings['cdn']['enable']) && $fvm_settings['cdn']['enable'] == true &&
764
+ isset($fvm_settings['cdn']['domain']) && !empty($fvm_settings['cdn']['domain'])) {
765
+ if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
766
+ $ffooter_url = str_replace('//'.$wpraiser_urls['wp_domain'], '//'.$wpraiser_settings['cdn']['domain'], $ffooter_url);
767
+
768
+ }
769
+ }
770
+
771
+ # generate cache file
772
+ clearstatcache();
773
+ if (!file_exists($ffooter)) {
774
+
775
+ # prepare log
776
+ $log = (array) array_values($scripts_footer_log);
777
+ $log_meta = array('loc'=>home_url(add_query_arg(NULL, NULL)), 'fl'=>$ffooter_url);
778
+
779
+ # generate cache, write log
780
+ if(!empty($ffooter_code)) {
781
+ fvm_save_log(array('uid'=>$ffooter_url, 'date'=>$now, 'type'=>'js', 'meta'=>$log_meta, 'content'=>$log));
782
+ fvm_save_file($ffooter, $ffooter_code);
783
+ }
784
+ }
785
+
786
+ # preload and save for html implementation (with priority order prefix)
787
+ $htmlpreloader['d_'.$ffooter_url] = '<link rel="preload" href="'.$ffooter_url.'" as="script" />';
788
+ $htmljscodedefer['d_'.$js_ffooter_uid] = "<script defer src='".$ffooter_url."'></script>";
789
+
790
+ }
791
+
792
+ }
793
+
794
+
795
+
796
+ # process html, if not disabled
797
+ if(isset($fvm_settings['html']['enable']) && $fvm_settings['html']['enable'] == true) {
798
+
799
+ # Remove HTML comments and IE conditionals
800
+ if(isset($fvm_settings['html']['nocomments']) && $fvm_settings['html']['nocomments'] == true) {
801
+ foreach($html->find('comment') as $element) {
802
+ $element->outertext = '';
803
+ }
804
+ }
805
+
806
+ # cleanup header
807
+ if(isset($fvm_settings['html']['cleanup_header']) && $fvm_settings['html']['cleanup_header'] == true) {
808
+ foreach($html->find('head meta[name=generator], head link[rel=shortlink], head link[rel=dns-prefetch], head link[rel=preconnect], head link[rel=prefetch], head link[rel=prerender], head link[rel=EditURI], head link[rel=preconnect], head link[rel=wlwmanifest], head link[type=application/rss+xml], head link[rel=https://api.w.org/], head link[type=application/json+oembed], head link[type=text/xml+oembed], head meta[name*=msapplication], head link[rel=apple-touch-icon]') as $element) {
809
+ $element->outertext = '';
810
+ }
811
+ }
812
+
813
+ }
814
+
815
+ # cdn rewrites, when needed
816
+ $html = fvm_rewrite_assets_cdn($html);
817
+
818
+ # build extra head and footer ###############################
819
+
820
+ # header and footer markers
821
+ $hm = '<!-- h_preheader --><!-- h_header_function --><!-- h_cssheader --><!-- h_jsheader -->';
822
+ $fm = '<!-- h_footer_lozad -->';
823
+
824
+ # remove charset meta tag and collect it to first position
825
+ if(!is_null($html->find('meta[charset]', 0))) {
826
+ $hm = str_replace('<!-- h_preheader -->', $html->find('meta[charset]', 0)->outertext.'<!-- h_preheader -->', $hm);
827
+ foreach($html->find('meta[charset]') as $element) { $element->outertext = ''; }
828
+ }
829
+
830
+ # add preload headers
831
+ if(is_array($htmlpreloader)) {
832
+ ksort($htmlpreloader); # priority
833
+ $hm = str_replace('<!-- h_preheader -->', implode(PHP_EOL, $htmlpreloader), $hm);
834
+ }
835
+
836
+ # add stylesheets
837
+ if(is_array($htmlcssheader) && count($htmlcssheader) > 0) {
838
+ ksort($htmlcssheader); # priority
839
+ $hm = str_replace('<!-- h_cssheader -->', implode(PHP_EOL, $htmlcssheader).'<!-- h_cssheader -->', $hm);
840
+ }
841
+
842
+ # add header scripts
843
+ if(is_array($htmljscodeheader) && count($htmljscodeheader) > 0) {
844
+ ksort($htmljscodeheader); # priority
845
+ $hm = str_replace('<!-- h_jsheader -->', implode(PHP_EOL, $htmljscodeheader).'<!-- h_jsheader -->', $hm);
846
+ }
847
+
848
+ # add defer scripts
849
+ if(is_array($htmljscodedefer) && count($htmljscodedefer) > 0) {
850
+ ksort($htmljscodedefer); # priority
851
+ $hm = str_replace('<!-- h_jsheader -->', implode(PHP_EOL, $htmljscodedefer), $hm);
852
+ }
853
+
854
+ # cleanup leftover markers
855
+ $hm = str_replace(
856
+ array('<!-- h_preheader -->', '<!-- h_header_function -->', '<!-- h_cssheader -->', '<!-- h_jsheader -->'), '', $hm);
857
+ $fm = str_replace('<!-- h_footer_lozad -->', '', $fm);
858
+
859
+
860
+ # Save HTML and output page ###############################
861
+
862
+ # append header and footer, if available
863
+ if(!is_null($html->find('head', 0)) && !is_null($html->find('body', -1))) {
864
+ if(!is_null($html->find('head', 0)->first_child()) && !is_null($html->find('body', -1)->last_child())) {
865
+ $html->find('head', 0)->first_child()->outertext = $hm . $html->find('head', 0)->first_child()->outertext;
866
+ $html->find('body', -1)->last_child()->outertext = $html->find('body', -1)->last_child ()->outertext . $fm;
867
+ }
868
+ }
869
+
870
+ # convert html object to string
871
+ $html = trim($html->save());
872
+
873
+ # minify HTML, if not disabled
874
+ if(!isset($fvm_settings['html']['min_disable']) || (isset($fvm_settings['html']['min_disable']) && $fvm_settings['html']['min_disable'] != true)) {
875
+ $html = fvm_raisermin_html($html, true);
876
+ }
877
+
878
+ }
879
+
880
+ # return html
881
+ return $html;
882
+
883
+ }
884
+
885
+
886
+
inc/functions-cache.php DELETED
@@ -1,449 +0,0 @@
1
- <?php
2
-
3
-
4
- # get windows or unix slashes
5
- function fastvelocity_get_os_slash() {
6
-
7
- if(fvm_server_is_windows() === false) {
8
- $slash = '/'; # unix
9
- } else {
10
- $slash = '\\'; # windows
11
- }
12
-
13
- return $slash;
14
- }
15
-
16
-
17
- # Fix the permission bits on generated files
18
- function fastvelocity_fix_permission_bits($file){
19
- if(function_exists('stat') && fvm_function_available('stat')) {
20
- if ($stat = @stat(dirname($file))) {
21
- $perms = $stat['mode'] & 0007777;
22
- @chmod($file, $perms);
23
- clearstatcache();
24
- return true;
25
- }
26
- }
27
-
28
-
29
- # get permissions from parent directory
30
- $perms = 0777;
31
- if(function_exists('stat') && fvm_function_available('stat')) {
32
- if ($stat = @stat(dirname($file))) { $perms = $stat['mode'] & 0007777; }
33
- }
34
-
35
- if (file_exists($file)){
36
- if ($perms != ($perms & ~umask())){
37
- $folder_parts = explode( '/', substr( $file, strlen(dirname($file)) + 1 ) );
38
- for ( $i = 1, $c = count( $folder_parts ); $i <= $c; $i++ ) {
39
- @chmod(dirname($file) . fastvelocity_get_os_slash() . implode( fastvelocity_get_os_slash(), array_slice( $folder_parts, 0, $i ) ), $perms );
40
- }
41
- }
42
- }
43
-
44
- return true;
45
- }
46
-
47
-
48
- # get cache directories and urls
49
- function fvm_cachepath() {
50
-
51
- # custom directory
52
- $fvm_change_cache_path = trim(rtrim(get_option('fastvelocity_min_change_cache_path', ''), fastvelocity_get_os_slash()));
53
- $fvm_change_cache_base = trim(rtrim(get_option('fastvelocity_min_change_cache_base_url', ''), fastvelocity_get_os_slash()));
54
- $upload = array();
55
-
56
- if(strlen($fvm_change_cache_path) > 1 && strlen($fvm_change_cache_base) > 10 && is_dir($fvm_change_cache_path) && is_writable($fvm_change_cache_path)) {
57
- $upload['basedir'] = trim($fvm_change_cache_path);
58
- $upload['baseurl'] = trim($fvm_change_cache_base);
59
- } else {
60
- $up = wp_upload_dir(); # default
61
- $upload['basedir'] = rtrim($up['basedir']);
62
- $upload['baseurl'] = rtrim($up['baseurl']);
63
- }
64
-
65
- # last update or zero
66
- $ctime = get_option('fvm-last-cache-update', '0');
67
-
68
- # create
69
- $uploadsdir = str_ireplace('cache'.fastvelocity_get_os_slash().'cache', 'cache', $upload['basedir'].fastvelocity_get_os_slash().'cache');
70
- $uploadsurl = str_ireplace('cache/cache', 'cache', $upload['baseurl'].'/cache');
71
- $cachebase = $uploadsdir.fastvelocity_get_os_slash().'fvm'.fastvelocity_get_os_slash().$ctime;
72
- $cachebaseurl = $uploadsurl.'/fvm/'.$ctime;
73
- $cachedir = $cachebase.fastvelocity_get_os_slash().'out';
74
- $tmpdir = $cachebase.fastvelocity_get_os_slash().'tmp';
75
- $headerdir = $cachebase.fastvelocity_get_os_slash().'header';
76
- $cachedirurl = $cachebaseurl.'/out';
77
-
78
- # get permissions from uploads directory
79
- $dir_perms = 0777;
80
- if(is_dir($uploadsdir) && function_exists('stat') && fvm_function_available('stat')) {
81
- if ($stat = @stat($uploadsdir)) { $dir_perms = $stat['mode'] & 0007777; }
82
- }
83
-
84
- # mkdir and check if umask requires chmod
85
- $dirs = array($cachebase, $cachedir, $tmpdir, $headerdir);
86
- foreach ($dirs as $target) {
87
- if(!is_dir($target)) {
88
- if (@mkdir($target, $dir_perms, true)){
89
- if ($dir_perms != ($dir_perms & ~umask())){
90
- $folder_parts = explode( fastvelocity_get_os_slash(), substr($target, strlen(dirname($target)) + 1 ));
91
- for ($i = 1, $c = count($folder_parts ); $i <= $c; $i++){
92
- @chmod(dirname($target) . fastvelocity_get_os_slash() . implode( fastvelocity_get_os_slash(), array_slice( $folder_parts, 0, $i ) ), $dir_perms );
93
- }
94
- }
95
- } else {
96
- # fallback
97
- wp_mkdir_p($target);
98
- }
99
- }
100
- }
101
-
102
- # return
103
- return array('cachebase'=>$cachebase,'tmpdir'=>$tmpdir, 'cachedir'=>$cachedir, 'cachedirurl'=>$cachedirurl, 'headerdir'=>$headerdir);
104
- }
105
-
106
-
107
-
108
- # increment file names
109
- function fvm_cache_increment() {
110
- update_option('fvm-last-cache-update', time(), 'no');
111
- }
112
-
113
- # will delete temporary intermediate stuff but leave final css/js alone for compatibility
114
- function fvm_purge_all() {
115
-
116
- # flush opcache
117
- if(function_exists('opcache_reset')) {
118
- opcache_reset();
119
- }
120
-
121
- # get cache directories and urls
122
- $cachepath = fvm_cachepath();
123
- $tmpdir = $cachepath['tmpdir'];
124
- $headerdir = $cachepath['headerdir'];
125
-
126
- # increment cache file names
127
- fvm_cache_increment();
128
-
129
- # delete temporary directories only
130
- if(is_dir($tmpdir)) { fastvelocity_rrmdir($tmpdir); }
131
- if(is_dir($headerdir)) { fastvelocity_rrmdir($headerdir); }
132
-
133
- # extra hook for developers
134
- do_action('fvm_after_purge_all');
135
- return true;
136
- }
137
-
138
-
139
- # purge all public files on uninstall
140
- function fvm_purge_all_uninstall() {
141
-
142
- # flush opcache
143
- if(function_exists('opcache_reset')) {
144
- opcache_reset();
145
- }
146
-
147
- # de;ete dirs
148
- $cachepath = fvm_cachepath();
149
- $cachebaseparent = dirname($cachepath['cachebase']);
150
- if(is_dir($cachebaseparent)) { fastvelocity_rrmdir($cachebaseparent); }
151
- return true;
152
- }
153
-
154
- # purge cache files older than 30 days
155
- function fvm_purge_old() {
156
-
157
- # flush opcache
158
- if(function_exists('opcache_reset')) {
159
- opcache_reset();
160
- }
161
-
162
- # get cache directories and urls
163
- $cachepath = fvm_cachepath();
164
- $cachebaseparent = dirname($cachepath['cachebase']);
165
- $ctime = get_option('fvm-last-cache-update', '0');
166
- $expires = time() - 86400 * 30;
167
-
168
- # get all directories that are a direct child of current directory
169
- if(is_dir($cachebaseparent) && is_writable(dirname($cachebaseparent))) {
170
- if ($handle = opendir($cachebaseparent)) {
171
- while (false !== ($d = readdir($handle))) {
172
- if (strcmp($d, '.')==0 || strcmp($d, '..')==0) { continue; }
173
- if($d != $ctime && (is_numeric($d) && $d <= $expires)) {
174
- $dir = $cachebaseparent.fastvelocity_get_os_slash().$d;
175
- if(is_dir($dir)) {
176
- fastvelocity_rrmdir($dir);
177
- if(is_dir($dir)) { @rmdir($dir); }
178
- }
179
- }
180
- }
181
- closedir($handle);
182
- }
183
- }
184
- return true;
185
- }
186
-
187
-
188
- # purge temp cache on save settings
189
- function fastvelocity_purge_onsave() {
190
- if(current_user_can( 'manage_options') && isset($_POST['fastvelocity_min_save_options'])) {
191
- fvm_purge_all();
192
- fvm_purge_others();
193
- }
194
- }
195
-
196
-
197
- # purge temp cache globally, after updates
198
- function fastvelocity_purge_all_global() {
199
- if(current_user_can( 'manage_options')) {
200
- fvm_purge_all();
201
- fvm_purge_others();
202
- }
203
- }
204
-
205
-
206
- # get transients on the disk
207
- function fvm_get_transient($key) {
208
- $cachepath = fvm_cachepath();
209
- $tmpdir = $cachepath['tmpdir'];
210
- $f = $tmpdir.fastvelocity_get_os_slash().$key.'.transient';
211
- clearstatcache();
212
- if(file_exists($f)) {
213
- return file_get_contents($f);
214
- } else {
215
- return false;
216
- }
217
- }
218
-
219
- # set cache on disk
220
- function fvm_set_transient($key, $code) {
221
- if(is_null($code) || empty($code)) { return false; }
222
- $cachepath = fvm_cachepath();
223
- $tmpdir = $cachepath['tmpdir'];
224
- $f = $tmpdir.fastvelocity_get_os_slash().$key.'.transient';
225
- file_put_contents($f, $code);
226
- fastvelocity_fix_permission_bits($f);
227
- return true;
228
- }
229
-
230
-
231
-
232
- # get cache size and count
233
- function fastvelocity_get_cachestats() {
234
- clearstatcache();
235
- $cachepath = fvm_cachepath();
236
- $cachedir = $cachepath['cachedir'];
237
- if(is_dir($cachedir)) {
238
- $dir = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($cachedir, FilesystemIterator::SKIP_DOTS));
239
- $size = 0;
240
- foreach ($dir as $file) {
241
- $size += $file->getSize();
242
- }
243
- return fastvelocity_format_filesize($size);
244
- } else {
245
- return 'Error: '.$cachedir. ' is not a directory!';
246
- }
247
- }
248
-
249
- # remove all cache files
250
- function fastvelocity_rrmdir($path) {
251
- clearstatcache();
252
- if(is_dir($path)) {
253
- try {
254
- $i = new DirectoryIterator($path);
255
- foreach($i as $f){
256
- if($f->isFile()){ @unlink($f->getRealPath());
257
- } else if(!$f->isDot() && $f->isDir()){
258
- fastvelocity_rrmdir($f->getRealPath());
259
- if(is_dir($f->getRealPath())) { @rmdir($f->getRealPath()); }
260
- }
261
- }
262
- } catch (Exception $e) {
263
- return get_class($e) . ": " . $e->getMessage();
264
- }
265
-
266
- # self
267
- if(is_dir($path)) { @rmdir($path); }
268
- }
269
- }
270
-
271
-
272
- # return size in human format
273
- function fastvelocity_format_filesize($bytes, $decimals = 2) {
274
- $units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' );
275
- for ($i = 0; ($bytes / 1024) > 0.9; $i++, $bytes /= 1024) {}
276
- return sprintf( "%1.{$decimals}f %s", round( $bytes, $decimals ), $units[$i] );
277
- }
278
-
279
-
280
- # Purge Godaddy Managed WordPress Hosting (Varnish)
281
- # https://github.com/wp-media/wp-rocket/blob/master/inc/3rd-party/hosting/godaddy.php
282
- function fastvelocity_godaddy_request( $method, $url = null ) {
283
- $url = empty( $url ) ? home_url() : $url;
284
- $host = parse_url( $url, PHP_URL_HOST );
285
- $url = set_url_scheme( str_replace( $host, WPaas\Plugin::vip(), $url ), 'http' );
286
- wp_cache_flush();
287
- update_option( 'gd_system_last_cache_flush', time(), 'no'); # purge apc
288
- wp_remote_request( esc_url_raw( $url ), array('method' => $method, 'blocking' => false, 'headers' => array('Host' => $host)) );
289
- }
290
-
291
-
292
- # purge supported hosting and plugins
293
- function fvm_purge_others(){
294
-
295
- # third party plugins
296
-
297
- # Purge all W3 Total Cache
298
- if (function_exists('w3tc_pgcache_flush')) {
299
- w3tc_pgcache_flush();
300
- return __('<div class="notice notice-info is-dismissible"><p>All caches on <strong>W3 Total Cache</strong> have been purged.</p></div>');
301
- }
302
-
303
- # Purge WP Super Cache
304
- if (function_exists('wp_cache_clear_cache')) {
305
- wp_cache_clear_cache();
306
- return __('<div class="notice notice-info is-dismissible"><p>All caches on <strong>WP Super Cache</strong> have been purged.</p></div>');
307
- }
308
-
309
- # Purge WP Rocket
310
- if (function_exists('rocket_clean_domain')) {
311
- rocket_clean_domain();
312
- return __('<div class="notice notice-info is-dismissible"><p>All caches on <strong>WP Rocket</strong> have been purged.</p></div>');
313
- }
314
-
315
- # Purge Cachify
316
- if (function_exists('cachify_flush_cache')) {
317
- cachify_flush_cache();
318
- return __('<div class="notice notice-info is-dismissible"><p>All caches on <strong>Cachify</strong> have been purged.</p></div>');
319
- }
320
-
321
- # Purge Comet Cache
322
- if ( class_exists("comet_cache") ) {
323
- comet_cache::clear();
324
- return __('<div class="notice notice-info is-dismissible"><p>All caches on <strong>Comet Cache</strong> have been purged.</p></div>');
325
- }
326
-
327
- # Purge Zen Cache
328
- if ( class_exists("zencache") ) {
329
- zencache::clear();
330
- return __('<div class="notice notice-info is-dismissible"><p>All caches on <strong>Zen Cache</strong> have been purged.</p></div>');
331
- }
332
-
333
- # Purge LiteSpeed Cache
334
- if (class_exists('LiteSpeed_Cache_Tags')) {
335
- LiteSpeed_Cache_Tags::add_purge_tag('*');
336
- return __('<div class="notice notice-info is-dismissible"><p>All caches on <strong>LiteSpeed Cache</strong> have been purged.</p></div>');
337
- }
338
-
339
- # Purge Hyper Cache
340
- if (class_exists( 'HyperCache' )) {
341
- do_action( 'autoptimize_action_cachepurged' );
342
- return __( '<div class="notice notice-info is-dismissible"><p>All caches on <strong>Hyper Cache</strong> have been purged.</p></div>');
343
- }
344
-
345
- # purge cache enabler
346
- if ( has_action('ce_clear_cache') ) {
347
- do_action('ce_clear_cache');
348
- return __( '<div class="notice notice-info is-dismissible"><p>All caches on <strong>Cache Enabler</strong> have been purged.</p></div>');
349
- }
350
-
351
- # purge wpfc
352
- if (function_exists('wpfc_clear_all_cache')) {
353
- wpfc_clear_all_cache(true);
354
- return __( '<div class="notice notice-info is-dismissible"><p>All caches on <strong>WPFC</strong> have been purged.</p></div>');
355
- }
356
-
357
- # add breeze cache purge support
358
- if (class_exists("Breeze_PurgeCache")) {
359
- Breeze_PurgeCache::breeze_cache_flush();
360
- return __( '<div class="notice notice-info is-dismissible"><p>All caches on <strong>Breeze</strong> have been purged.</p></div>');
361
- }
362
-
363
-
364
- # swift
365
- if (class_exists("Swift_Performance_Cache")) {
366
- Swift_Performance_Cache::clear_all_cache();
367
- return __( '<div class="notice notice-info is-dismissible"><p>All caches on <strong>Swift Performance</strong> have been purged.</p></div>');
368
- }
369
-
370
-
371
- # hosting companies
372
-
373
- # Purge SG Optimizer (Siteground)
374
- if (function_exists('sg_cachepress_purge_cache')) {
375
- sg_cachepress_purge_cache();
376
- return __('<div class="notice notice-info is-dismissible"><p>All caches on <strong>SG Optimizer</strong> have been purged.</p></div>');
377
- }
378
-
379
- # Purge Godaddy Managed WordPress Hosting (Varnish + APC)
380
- if (class_exists('WPaaS\Plugin') && method_exists( 'WPass\Plugin', 'vip' )) {
381
- fastvelocity_godaddy_request('BAN');
382
- return __('<div class="notice notice-info is-dismissible"><p>A cache purge request has been sent to <strong>Go Daddy Varnish</strong></p></div>');
383
- }
384
-
385
-
386
- # Purge WP Engine
387
- if (class_exists("WpeCommon")) {
388
- if (method_exists('WpeCommon', 'purge_memcached')) { WpeCommon::purge_memcached(); }
389
- if (method_exists('WpeCommon', 'purge_varnish_cache')) { WpeCommon::purge_varnish_cache(); }
390
-
391
- if (method_exists('WpeCommon', 'purge_memcached') || method_exists('WpeCommon', 'purge_varnish_cache')) {
392
- return __('<div class="notice notice-info is-dismissible"><p>A cache purge request has been sent to <strong>WP Engine</strong></p></div>');
393
- }
394
- }
395
-
396
-
397
- # Purge Kinsta
398
- global $kinsta_cache;
399
- if ( isset($kinsta_cache) && class_exists('\\Kinsta\\CDN_Enabler')) {
400
- if (!empty( $kinsta_cache->kinsta_cache_purge)){
401
- $kinsta_cache->kinsta_cache_purge->purge_complete_caches();
402
- return __('<div class="notice notice-info is-dismissible"><p>A cache purge request has been sent to <strong>Kinsta</strong></p></div>');
403
- }
404
- }
405
-
406
- # Purge Pagely
407
- if ( class_exists( 'PagelyCachePurge' ) ) {
408
- $purge_pagely = new PagelyCachePurge();
409
- $purge_pagely->purgeAll();
410
- return __('<div class="notice notice-info is-dismissible"><p>A cache purge request has been sent to <strong>Pagely</strong></p></div>');
411
- }
412
-
413
- # Purge Pressidum
414
- if (defined('WP_NINUKIS_WP_NAME') && class_exists('Ninukis_Plugin')){
415
- $purge_pressidum = Ninukis_Plugin::get_instance();
416
- $purge_pressidum->purgeAllCaches();
417
- return __('<div class="notice notice-info is-dismissible"><p>A cache purge request has been sent to <strong>Pressidium</strong></p></div>');
418
- }
419
-
420
- # Purge Savvii
421
- if (defined( '\Savvii\CacheFlusherPlugin::NAME_DOMAINFLUSH_NOW')) {
422
- $purge_savvii = new \Savvii\CacheFlusherPlugin();
423
- if ( method_exists( $plugin, 'domainflush' ) ) {
424
- $purge_savvii->domainflush();
425
- return __('<div class="notice notice-info is-dismissible"><p>A cache purge request has been sent to <strong>Savvii</strong></p></div>');
426
- }
427
- }
428
-
429
- # Purge Pantheon Advanced Page Cache plugin
430
- if(function_exists('pantheon_wp_clear_edge_all')) {
431
- pantheon_wp_clear_edge_all();
432
- }
433
-
434
- # last actions, php and wordpress in this order
435
-
436
- # flush opcache if available
437
- if(function_exists('opcache_reset')) {
438
- opcache_reset();
439
- return __( '<div class="notice notice-info is-dismissible"><p>All caches on <strong>OPCache</strong> have been purged.</p></div>');
440
- }
441
-
442
- # wordpress default cache
443
- if (function_exists('wp_cache_flush')) {
444
- wp_cache_flush();
445
- }
446
-
447
- }
448
-
449
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/functions-cli.php DELETED
@@ -1,34 +0,0 @@
1
- <?php
2
-
3
- # only for wp-cli
4
- if ( defined( 'WP_CLI' ) && WP_CLI ) {
5
-
6
-
7
- ###################################################
8
- # extend wp-cli to purge cache, usage: wp fvm purge
9
- ###################################################
10
-
11
- class fastvelocity_WPCLI {
12
-
13
- # purge files + cache
14
- public function purge() {
15
- fvm_purge_all();
16
- fvm_purge_others();
17
- WP_CLI::success('FVM and other caches were purged.');
18
- }
19
-
20
- # get cache size
21
- public function stats() {
22
- $stats = fastvelocity_get_cachestats();
23
- WP_CLI::success('FVM is using '.$stats.' for cache.');
24
- }
25
-
26
- }
27
-
28
- # add commands
29
- WP_CLI::add_command( 'fvm', 'fastvelocity_WPCLI' );
30
-
31
-
32
-
33
- ###################################################
34
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/functions-serverinfo.php DELETED
@@ -1,374 +0,0 @@
1
- <?php
2
-
3
- ### Get General Information
4
- function fvm_get_generalinfo() {
5
- global $is_IIS;
6
- if( is_rtl() ) : ?>
7
- <style type="text/css">
8
- #GeneralOverview table,
9
- #GeneralOverview th,
10
- #GeneralOverview td {
11
- direction: ltr;
12
- text-align: left;
13
- }
14
- #GeneralOverview h2 {
15
- padding: 0.5em 0 0;
16
- }
17
- </style>
18
- <?php endif;
19
- ?>
20
- <div class="wrap" id="GeneralOverview">
21
- <h2><?php _e('Server Info','fvm-serverinfo'); ?></h2>
22
- <br />
23
- <table class="widefat">
24
- <thead>
25
- <tr>
26
- <th><?php _e('Variable Name', 'fvm-serverinfo'); ?></th>
27
- <th><?php _e('Value', 'fvm-serverinfo'); ?></th>
28
- <th><?php _e('Variable Name', 'fvm-serverinfo'); ?></th>
29
- <th><?php _e('Value', 'fvm-serverinfo'); ?></th>
30
- </tr>
31
- </thead>
32
- <tbody>
33
- <tr>
34
- <td><?php _e('OS', 'fvm-serverinfo'); ?></td>
35
- <td><?php echo PHP_OS; ?></td>
36
- <td><?php _e('Database Data Disk Usage', 'fvm-serverinfo'); ?></td>
37
- <td><?php echo fvm_format_filesize(fvm_get_mysql_data_usage()); ?></td>
38
- </tr>
39
- <tr class="alternate">
40
- <td><?php _e('Server', 'fvm-serverinfo'); ?></td>
41
- <td><?php echo $_SERVER["SERVER_SOFTWARE"]; ?></td>
42
- <td><?php _e('Database Index Disk Usage', 'fvm-serverinfo'); ?></td>
43
- <td><?php echo fvm_format_filesize(fvm_get_mysql_index_usage()); ?></td>
44
- </tr>
45
- <tr>
46
- <td>PHP</td>
47
- <td>v<?php echo PHP_VERSION; ?></td>
48
- <td><?php _e('MYSQL Maximum Packet Size', 'fvm-serverinfo'); ?></td>
49
- <td><?php echo fvm_format_filesize(fvm_get_mysql_max_allowed_packet()); ?></td>
50
- </tr>
51
- <tr class="alternate">
52
- <td>MYSQL</td>
53
- <td>v<?php echo fvm_get_mysql_version(); ?></td>
54
- <td><?php _e('MYSQL Maximum No. Connection', 'fvm-serverinfo'); ?></td>
55
- <td><?php echo number_format_i18n(fvm_get_mysql_max_allowed_connections()); ?></td>
56
- </tr>
57
- <tr>
58
- <td><?php _e('Server Load', 'fvm-serverinfo'); ?></td>
59
- <td><?php echo fvm_get_serverload(); ?></td>
60
- <td><?php _e( 'MYSQL Query Cache Size', 'fvm-serverinfo' ); ?></td>
61
- <td><?php echo fvm_format_filesize( fvm_get_mysql_query_cache_size() ); ?></td>
62
- </tr>
63
- <tr class="alternate">
64
- <td><?php _e("Server CPU's", 'fvm-serverinfo'); ?></td>
65
- <td><?php echo fvm_get_servercpu(); ?></td>
66
- <td><?php _e('PHP Short Tag', 'fvm-serverinfo'); ?></td>
67
- <td><?php echo fvm_get_php_short_tag(); ?></td>
68
- </tr>
69
- <tr>
70
- <td><?php _e('Server Hostname', 'fvm-serverinfo'); ?></td>
71
- <td><?php echo $_SERVER['SERVER_NAME']; ?></td>
72
- <td><?php _e('PHP Magic Quotes GPC', 'fvm-serverinfo'); ?></td>
73
- <td><?php echo fvm_get_php_magic_quotes_gpc(); ?></td>
74
- </tr>
75
- <tr class="alternate">
76
- <td><?php _e('Server Document Root','fvm-serverinfo'); ?></td>
77
- <td><?php echo $_SERVER['DOCUMENT_ROOT']; ?></td>
78
- <td><?php _e('PHP Memory Limit', 'fvm-serverinfo'); ?></td>
79
- <td><?php echo fvm_format_php_size(fvm_get_php_memory_limit()); ?></td>
80
- </tr>
81
- <tr>
82
- <td><?php _e('Site Url', 'fvm-serverinfo'); ?></td>
83
- <td><?php echo site_url(); ?></td>
84
- <td><?php _e('PHP Max Upload Size', 'fvm-serverinfo'); ?></td>
85
- <td><?php echo fvm_format_php_size(fvm_get_php_upload_max()); ?></td>
86
- </tr>
87
- <tr class="alternate">
88
- <td><?php _e('Home Url', 'fvm-serverinfo'); ?></td>
89
- <td><?php echo home_url(); ?></td>
90
- <td><?php _e('PHP Max Post Size', 'fvm-serverinfo'); ?></td>
91
- <td><?php echo fvm_format_php_size(fvm_get_php_post_max()); ?></td>
92
- </tr>
93
- <tr>
94
- <td><?php _e('MySQL Date/Time', 'fvm-serverinfo'); ?></td>
95
- <td><?php echo mysql2date(sprintf(__('%s @ %s', 'wp-postratings'), get_option('date_format'), get_option('time_format')), current_time('mysql', 1)). ' GMT'; ?></td>
96
- <td><?php _e('PHP Max Script Execute Time', 'fvm-serverinfo'); ?></td>
97
- <td><?php echo fvm_get_php_max_execution(); ?>s</td>
98
- </tr>
99
- </tbody>
100
- </table>
101
- </div>
102
- <?php
103
- }
104
-
105
-
106
- ### Function: Format Bytes Into TiB/GiB/MiB/KiB/Bytes
107
- if(!function_exists('fvm_format_filesize')) {
108
- function fvm_format_filesize($rawSize) {
109
- if(is_numeric($rawSize)) {
110
- if($rawSize / 1099511627776 > 1) {
111
- return number_format_i18n($rawSize/1099511627776, 1).' '.__('TiB', 'fvm-serverinfo');
112
- } elseif($rawSize / 1073741824 > 1) {
113
- return number_format_i18n($rawSize/1073741824, 1).' '.__('GiB', 'fvm-serverinfo');
114
- } elseif($rawSize / 1048576 > 1) {
115
- return number_format_i18n($rawSize/1048576, 1).' '.__('MiB', 'fvm-serverinfo');
116
- } elseif($rawSize / 1024 > 1) {
117
- return number_format_i18n($rawSize/1024, 1).' '.__('KiB', 'fvm-serverinfo');
118
- } elseif($rawSize > 1) {
119
- return number_format_i18n($rawSize, 0).' '.__('bytes', 'fvm-serverinfo');
120
- } else {
121
- return __('N/A', 'fvm-serverinfo');
122
- }
123
- } else {
124
- return __('N/A', 'fvm-serverinfo');
125
- }
126
- }
127
- }
128
-
129
- ### Function: Convert PHP Size Format to Localized
130
- function fvm_format_php_size($size) {
131
- if (!is_numeric($size)) {
132
- if (strpos($size, 'M') !== false) {
133
- $size = intval($size)*1024*1024;
134
- } elseif (strpos($size, 'K') !== false) {
135
- $size = intval($size)*1024;
136
- } elseif (strpos($size, 'G') !== false) {
137
- $size = intval($size)*1024*1024*1024;
138
- }
139
- }
140
- return is_numeric($size) ? fvm_format_filesize($size) : $size;
141
- }
142
-
143
- ### Function: Get PHP Short Tag
144
- if(!function_exists('fvm_get_php_short_tag')) {
145
- function fvm_get_php_short_tag() {
146
- if(ini_get('short_open_tag')) {
147
- $short_tag = __('On', 'fvm-serverinfo');
148
- } else {
149
- $short_tag = __('Off', 'fvm-serverinfo');
150
- }
151
- return $short_tag;
152
- }
153
- }
154
-
155
-
156
- ### Function: Get PHP Magic Quotes GPC
157
- if(!function_exists('fvm_get_php_magic_quotes_gpc')) {
158
- function fvm_get_php_magic_quotes_gpc() {
159
- if(get_magic_quotes_gpc()) {
160
- $magic_quotes_gpc = __('On', 'fvm-serverinfo');
161
- } else {
162
- $magic_quotes_gpc = __('Off', 'fvm-serverinfo');
163
- }
164
- return $magic_quotes_gpc;
165
- }
166
- }
167
-
168
-
169
- ### Function: Get PHP Max Upload Size
170
- if(!function_exists('fvm_get_php_upload_max')) {
171
- function fvm_get_php_upload_max() {
172
- if(ini_get('upload_max_filesize')) {
173
- $upload_max = ini_get('upload_max_filesize');
174
- } else {
175
- $upload_max = __('N/A', 'fvm-serverinfo');
176
- }
177
- return $upload_max;
178
- }
179
- }
180
-
181
-
182
- ### Function: Get PHP Max Post Size
183
- if(!function_exists('fvm_get_php_post_max')) {
184
- function fvm_get_php_post_max() {
185
- if(ini_get('post_max_size')) {
186
- $post_max = ini_get('post_max_size');
187
- } else {
188
- $post_max = __('N/A', 'fvm-serverinfo');
189
- }
190
- return $post_max;
191
- }
192
- }
193
-
194
-
195
- ### Function: PHP Maximum Execution Time
196
- if(!function_exists('fvm_get_php_max_execution')) {
197
- function fvm_get_php_max_execution() {
198
- if(ini_get('max_execution_time')) {
199
- $max_execute = ini_get('max_execution_time');
200
- } else {
201
- $max_execute = __('N/A', 'fvm-serverinfo');
202
- }
203
- return $max_execute;
204
- }
205
- }
206
-
207
-
208
- ### Function: PHP Memory Limit
209
- if(!function_exists('fvm_get_php_memory_limit')) {
210
- function fvm_get_php_memory_limit() {
211
- if(ini_get('memory_limit')) {
212
- $memory_limit = ini_get('memory_limit');
213
- } else {
214
- $memory_limit = __('N/A', 'fvm-serverinfo');
215
- }
216
- return $memory_limit;
217
- }
218
- }
219
-
220
-
221
- ### Function: Get MYSQL Version
222
- if(!function_exists('fvm_get_mysql_version')) {
223
- function fvm_get_mysql_version() {
224
- global $wpdb;
225
- return $wpdb->get_var("SELECT VERSION() AS version");
226
- }
227
- }
228
-
229
-
230
- ### Function: Get MYSQL Data Usage
231
- if(!function_exists('fvm_get_mysql_data_usage')) {
232
- function fvm_get_mysql_data_usage() {
233
- global $wpdb;
234
- $data_usage = 0;
235
- $tablesstatus = $wpdb->get_results("SHOW TABLE STATUS");
236
- foreach($tablesstatus as $tablestatus) {
237
- if(is_numeric($tablestatus->Data_length)) { $data_usage += $tablestatus->Data_length; } else { $data_usage += 0; }
238
- }
239
- if (!$data_usage) {
240
- $data_usage = __('N/A', 'fvm-serverinfo');
241
- }
242
- return $data_usage;
243
- }
244
- }
245
-
246
-
247
- ### Function: Get MYSQL Index Usage
248
- if(!function_exists('fvm_get_mysql_index_usage')) {
249
- function fvm_get_mysql_index_usage() {
250
- global $wpdb;
251
- $index_usage = 0;
252
- $tablesstatus = $wpdb->get_results("SHOW TABLE STATUS");
253
- foreach($tablesstatus as $tablestatus) {
254
- if(is_numeric($tablestatus->Index_length)) { $index_usage += $tablestatus->Index_length; } else { $index_usage += 0; }
255
- }
256
- if (!$index_usage){
257
- $index_usage = __('N/A', 'fvm-serverinfo');
258
- }
259
- return $index_usage;
260
- }
261
- }
262
-
263
-
264
- ### Function: Get MYSQL Max Allowed Packet
265
- if(!function_exists('fvm_get_mysql_max_allowed_packet')) {
266
- function fvm_get_mysql_max_allowed_packet() {
267
- global $wpdb;
268
- $packet_max_query = $wpdb->get_row("SHOW VARIABLES LIKE 'max_allowed_packet'");
269
- $packet_max = $packet_max_query->Value;
270
- if(!$packet_max) {
271
- $packet_max = __('N/A', 'fvm-serverinfo');
272
- }
273
- return $packet_max;
274
- }
275
- }
276
-
277
-
278
- ### Function:Get MYSQL Max Allowed Connections
279
- if(!function_exists('fvm_get_mysql_max_allowed_connections')) {
280
- function fvm_get_mysql_max_allowed_connections() {
281
- global $wpdb;
282
- $connection_max_query = $wpdb->get_row("SHOW VARIABLES LIKE 'max_connections'");
283
- $connection_max = $connection_max_query->Value;
284
- if(!$connection_max) {
285
- $connection_max = __('N/A', 'fvm-serverinfo');
286
- }
287
- return $connection_max;
288
- }
289
- }
290
-
291
- ### Function:Get MYSQL Query Cache Size
292
- if(!function_exists('fvm_get_mysql_query_cache_size')) {
293
- function fvm_get_mysql_query_cache_size() {
294
- global $wpdb;
295
- $query_cache_size_query = $wpdb->get_row( "SHOW VARIABLES LIKE 'query_cache_size'" );
296
- $query_cache_size = $query_cache_size_query->Value;
297
- if ( empty( $query_cache_size ) ) {
298
- $query_cache_size = __( 'N/A', 'fvm-serverinfo' );
299
- }
300
- return $query_cache_size;
301
- }
302
- }
303
-
304
-
305
- ### Function: Get The Server Load
306
- if(!function_exists('fvm_get_serverload')) {
307
- function fvm_get_serverload() {
308
- $server_load = 0;
309
- $numCpus = 'N/A';
310
- if(PHP_OS != 'WINNT' && PHP_OS != 'WIN32') {
311
- clearstatcache();
312
- if(@file_exists('/proc/loadavg') ) {
313
- if ($fh = @fopen( '/proc/loadavg', 'r' )) {
314
- $data = @fread( $fh, 6 );
315
- @fclose( $fh );
316
- $load_avg = explode( " ", $data );
317
- $server_load = trim($load_avg[0]);
318
- }
319
- } else if ('WIN' == strtoupper(substr(PHP_OS, 0, 3)) && function_exists('popen') && fvm_function_available('popen')) {
320
- $process = @popen('wmic cpu get NumberOfCores', 'rb');
321
- if (false !== $process && null !== $process) {
322
- fgets($process);
323
- $numCpus = intval(fgets($process));
324
- pclose($process);
325
- }
326
- } else if (function_exists('system') && fvm_function_available('system')){
327
- $data = @system('uptime');
328
- preg_match('/(.*):{1}(.*)/', $data, $matches);
329
- if(isset($matches[2])) {
330
- $load_arr = explode(',', $matches[2]);
331
- $server_load = trim($load_arr[0]);
332
- } else {
333
- $server_load = __('N/A', 'fvm-serverinfo');
334
- }
335
- } else {
336
- $server_load = __('N/A', 'fvm-serverinfo');
337
- }
338
- }
339
- if(empty($server_load)) {
340
- $server_load = __('N/A', 'fvm-serverinfo');
341
- }
342
- return $server_load;
343
- }
344
- }
345
-
346
-
347
- ### Function: Get The Server CPU's
348
- if(!function_exists('fvm_get_servercpu')) {
349
- function fvm_get_servercpu() {
350
- $numCpus = 0;
351
- if(PHP_OS != 'WINNT' && PHP_OS != 'WIN32') {
352
- clearstatcache();
353
- if (is_file('/proc/cpuinfo')) {
354
- $cpuinfo = file_get_contents('/proc/cpuinfo');
355
- preg_match_all('/^processor/m', $cpuinfo, $matches);
356
- $numCpus = count($matches[0]);
357
- } else if (function_exists('popen') && fvm_function_available('popen')) {
358
- $process = @popen('sysctl -a', 'rb');
359
- if (false !== $process && null !== $process) {
360
- $output = stream_get_contents($process);
361
- preg_match('/hw.ncpu: (\d+)/', $output, $matches);
362
- if ($matches) { $numCpus = intval($matches[1][0]); }
363
- pclose($process);
364
- }
365
- } else {
366
- $numCpus = __('N/A', 'fvm-serverinfo');
367
- }
368
- }
369
- if(empty($numCpus)) {
370
- $numCpus = __('N/A', 'fvm-serverinfo');
371
- }
372
- return $numCpus;
373
- }
374
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/functions-upgrade.php DELETED
@@ -1,41 +0,0 @@
1
- <?php
2
-
3
-
4
- # run after updating
5
- add_action( 'plugins_loaded', 'fastvelocity_plugin_upgrade_completed');
6
- function fastvelocity_plugin_upgrade_completed() {
7
-
8
- global $fastvelocity_plugin_version;
9
-
10
- # current FVM install date, create if it doesn't exist
11
- $ver = get_option("fastvelocity_plugin_version");
12
- if ($ver == false) { $ver = '1'; }
13
-
14
- # save current version on upgrade
15
- if ($ver != $fastvelocity_plugin_version) {
16
- update_option( "fastvelocity_plugin_version", $fastvelocity_plugin_version, 'no');
17
- }
18
-
19
- # run for any update lower than 2.8.6
20
- if (version_compare($ver, '2.8.6', '<')) {
21
-
22
- # default ignore list update
23
- $exc = array('/themes/Avada/assets/js/main.min.js', '/plugins/woocommerce-product-search/js/product-search.js', '/plugins/revslider/public/assets/js/jquery.themepunch.tools.min.js', '/js/TweenMax.min.js', '/themes/jupiter/assets/js/min/full-scripts', '/plugins/LayerSlider/static/layerslider/js/greensock.js', '/themes/kalium/assets/js/main.min.js', '/js/mediaelement/', '/plugins/elementor/assets/js/common.min.js', '/plugins/elementor/assets/js/frontend.min.js', '/plugins/elementor-pro/assets/js/frontend.min.js', '/themes/kalium/assets/js/main.min.js', '/wp-includes/js/mediaelement/wp-mediaelement.min.js');
24
- update_option('fastvelocity_min_ignorelist', implode(PHP_EOL, $exc), 'no');
25
-
26
- }
27
- }
28
-
29
-
30
- # upgrade notifications
31
- function fastvelocity_plugin_update_message($currentPluginMetadata, $newPluginMetadata) {
32
- if (isset($newPluginMetadata->upgrade_notice) && strlen(trim($newPluginMetadata->upgrade_notice)) > 0){
33
- echo '<span style="display:block; background: #F7FCFE; padding: 14px 0 6px 0; margin: 10px -12px -12px -16px;">';
34
- echo '<span class="notice notice-info" style="display:block; padding: 10px; margin: 0;">';
35
- echo '<span class="dashicons dashicons-megaphone" style="margin-left: 2px; margin-right: 6px;"></span>';
36
- echo strip_tags($newPluginMetadata->upgrade_notice);
37
- echo '</span>';
38
- echo '</span>';
39
- }
40
- }
41
- add_action( 'in_plugin_update_message-fast-velocity-minify/fvm.php', 'fastvelocity_plugin_update_message', 10, 2 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/functions.php DELETED
@@ -1,988 +0,0 @@
1
- <?php
2
-
3
- # handle better utf-8 and unicode encoding
4
- if(function_exists('mb_internal_encoding')) { mb_internal_encoding('UTF-8'); }
5
-
6
- # must have
7
- @ini_set('pcre.backtrack_limit',5000000);
8
- @ini_set('pcre.recursion_limit',5000000);
9
-
10
- # PHP Minify [1.3.60] (must be defined on the outer scope)
11
- # https://github.com/matthiasmullie/minify
12
- $path = $plugindir . 'libs/matthiasmullie';
13
- require_once $path . '/minify/src/Minify.php';
14
- require_once $path . '/minify/src/CSS.php';
15
- require_once $path . '/minify/src/JS.php';
16
- require_once $path . '/minify/src/Exception.php';
17
- require_once $path . '/minify/src/Exceptions/BasicException.php';
18
- require_once $path . '/minify/src/Exceptions/FileImportException.php';
19
- require_once $path . '/minify/src/Exceptions/IOException.php';
20
- require_once $path . '/path-converter/src/ConverterInterface.php';
21
- require_once $path . '/path-converter/src/Converter.php';
22
- use MatthiasMullie\Minify;
23
-
24
- # use HTML minification
25
- require_once ($plugindir . 'libs/mrclay/HTML.php');
26
-
27
- # get list of allowed google fonts from the API (847 fonts on 2018-12-13)
28
- # https://www.googleapis.com/webfonts/v1/webfonts?sort=alpha
29
- # https://www.xcartmods.co.uk/google-fonts-list.php
30
- $gfontswhitelist = array('ABeeZee','Abel','Abhaya Libre','Abril Fatface','Aclonica','Acme','Actor','Adamina','Advent Pro','Aguafina Script','Akronim','Aladin','Aldrich','Alef','Alegreya','Alegreya SC','Alegreya Sans','Alegreya Sans SC','Alex Brush','Alfa Slab One','Alice','Alike','Alike Angular','Allan','Allerta','Allerta Stencil','Allura','Almendra','Almendra Display','Almendra SC','Amarante','Amaranth','Amatic SC','Amatica SC','Amethysta','Amiko','Amiri','Amita','Anaheim','Andada','Andika','Angkor','Annie Use Your Telescope','Anonymous Pro','Antic','Antic Didone','Antic Slab','Anton','Arapey','Arbutus','Arbutus Slab','Architects Daughter','Archivo','Archivo Black','Archivo Narrow','Aref Ruqaa','Arima Madurai','Arimo','Arizonia','Armata','Arsenal','Artifika','Arvo','Arya','Asap','Asap Condensed','Asar','Asset','Assistant','Astloch','Asul','Athiti','Atma','Atomic Age','Aubrey','Audiowide','Autour One','Average','Average Sans','Averia Gruesa Libre','Averia Libre','Averia Sans Libre','Averia Serif Libre','Bad Script','Bahiana','Baloo','Baloo Bhai','Baloo Bhaijaan','Baloo Bhaina','Baloo Chettan','Baloo Da','Baloo Paaji','Baloo Tamma','Baloo Tammudu','Baloo Thambi','Balthazar','Bangers','Barrio','Basic','Battambang','Baumans','Bayon','Belgrano','Bellefair','Belleza','BenchNine','Bentham','Berkshire Swash','Bevan','Bigelow Rules','Bigshot One','Bilbo','Bilbo Swash Caps','BioRhyme','BioRhyme Expanded','Biryani','Bitter','Black Ops One','Bokor','Bonbon','Boogaloo','Bowlby One','Bowlby One SC','Brawler','Bree Serif','Bubblegum Sans','Bubbler One','Buda','Buenard','Bungee','Bungee Hairline','Bungee Inline','Bungee Outline','Bungee Shade','Butcherman','Butterfly Kids','Cabin','Cabin Condensed','Cabin Sketch','Caesar Dressing','Cagliostro','Cairo','Calligraffitti','Cambay','Cambo','Candal','Cantarell','Cantata One','Cantora One','Capriola','Cardo','Carme','Carrois Gothic','Carrois Gothic SC','Carter One','Catamaran','Caudex','Caveat','Caveat Brush','Cedarville Cursive','Ceviche One','Changa','Changa One','Chango','Chathura','Chau Philomene One','Chela One','Chelsea Market','Chenla','Cherry Cream Soda','Cherry Swash','Chewy','Chicle','Chivo','Chonburi','Cinzel','Cinzel Decorative','Clicker Script','Coda','Coda Caption','Codystar','Coiny','Combo','Comfortaa','Coming Soon','Concert One','Condiment','Content','Contrail One','Convergence','Cookie','Copse','Corben','Cormorant','Cormorant Garamond','Cormorant Infant','Cormorant SC','Cormorant Unicase','Cormorant Upright','Courgette','Cousine','Coustard','Covered By Your Grace','Crafty Girls','Creepster','Crete Round','Crimson Text','Croissant One','Crushed','Cuprum','Cutive','Cutive Mono','Damion','Dancing Script','Dangrek','David Libre','Dawning of a New Day','Days One','Dekko','Delius','Delius Swash Caps','Delius Unicase','Della Respira','Denk One','Devonshire','Dhurjati','Didact Gothic','Diplomata','Diplomata SC','Domine','Donegal One','Doppio One','Dorsa','Dosis','Dr Sugiyama','Droid Sans','Droid Sans Mono','Droid Serif','Duru Sans','Dynalight','EB Garamond','Eagle Lake','Eater','Economica','Eczar','El Messiri','Electrolize','Elsie','Elsie Swash Caps','Emblema One','Emilys Candy','Encode Sans','Encode Sans Condensed','Encode Sans Expanded','Encode Sans Semi Condensed','Encode Sans Semi Expanded','Engagement','Englebert','Enriqueta','Erica One','Esteban','Euphoria Script','Ewert','Exo','Exo 2','Expletus Sans','Fanwood Text','Farsan','Fascinate','Fascinate Inline','Faster One','Fasthand','Fauna One','Faustina','Federant','Federo','Felipa','Fenix','Finger Paint','Fira Mono','Fira Sans','Fira Sans Condensed','Fira Sans Extra Condensed','Fjalla One','Fjord One','Flamenco','Flavors','Fondamento','Fontdiner Swanky','Forum','Francois One','Frank Ruhl Libre','Freckle Face','Fredericka the Great','Fredoka One','Freehand','Fresca','Frijole','Fruktur','Fugaz One','GFS Didot','GFS Neohellenic','Gabriela','Gafata','Galada','Galdeano','Galindo','Gentium Basic','Gentium Book Basic','Geo','Geostar','Geostar Fill','Germania One','Gidugu','Gilda Display','Give You Glory','Glass Antiqua','Glegoo','Gloria Hallelujah','Goblin One','Gochi Hand','Gorditas','Goudy Bookletter 1911','Graduate','Grand Hotel','Gravitas One','Great Vibes','Griffy','Gruppo','Gudea','Gurajada','Habibi','Halant','Hammersmith One','Hanalei','Hanalei Fill','Handlee','Hanuman','Happy Monkey','Harmattan','Headland One','Heebo','Henny Penny','Herr Von Muellerhoff','Hind','Hind Guntur','Hind Madurai','Hind Siliguri','Hind Vadodara','Holtwood One SC','Homemade Apple','Homenaje','IM Fell DW Pica','IM Fell DW Pica SC','IM Fell Double Pica','IM Fell Double Pica SC','IM Fell English','IM Fell English SC','IM Fell French Canon','IM Fell French Canon SC','IM Fell Great Primer','IM Fell Great Primer SC','Iceberg','Iceland','Imprima','Inconsolata','Inder','Indie Flower','Inika','Inknut Antiqua','Irish Grover','Istok Web','Italiana','Italianno','Itim','Jacques Francois','Jacques Francois Shadow','Jaldi','Jim Nightshade','Jockey One','Jolly Lodger','Jomhuria','Josefin Sans','Josefin Slab','Joti One','Judson','Julee','Julius Sans One','Junge','Jura','Just Another Hand','Just Me Again Down Here','Kadwa','Kalam','Kameron','Kanit','Kantumruy','Karla','Karma','Katibeh','Kaushan Script','Kavivanar','Kavoon','Kdam Thmor','Keania One','Kelly Slab','Kenia','Khand','Khmer','Khula','Kite One','Knewave','Kotta One','Koulen','Kranky','Kreon','Kristi','Krona One','Kumar One','Kumar One Outline','Kurale','La Belle Aurore','Laila','Lakki Reddy','Lalezar','Lancelot','Lateef','Lato','League Script','Leckerli One','Ledger','Lekton','Lemon','Lemonada','Libre Barcode 128','Libre Barcode 128 Text','Libre Barcode 39','Libre Barcode 39 Extended','Libre Barcode 39 Extended Text','Libre Barcode 39 Text','Libre Baskerville','Libre Franklin','Life Savers','Lilita One','Lily Script One','Limelight','Linden Hill','Lobster','Lobster Two','Londrina Outline','Londrina Shadow','Londrina Sketch','Londrina Solid','Lora','Love Ya Like A Sister','Loved by the King','Lovers Quarrel','Luckiest Guy','Lusitana','Lustria','Macondo','Macondo Swash Caps','Mada','Magra','Maiden Orange','Maitree','Mako','Mallanna','Mandali','Manuale','Marcellus','Marcellus SC','Marck Script','Margarine','Marko One','Marmelad','Martel','Martel Sans','Marvel','Mate','Mate SC','Maven Pro','McLaren','Meddon','MedievalSharp','Medula One','Meera Inimai','Megrim','Meie Script','Merienda','Merienda One','Merriweather','Merriweather Sans','Metal','Metal Mania','Metamorphous','Metrophobic','Michroma','Milonga','Miltonian','Miltonian Tattoo','Miniver','Miriam Libre','Mirza','Miss Fajardose','Mitr','Modak','Modern Antiqua','Mogra','Molengo','Molle','Monda','Monofett','Monoton','Monsieur La Doulaise','Montaga','Montez','Montserrat','Montserrat Alternates','Montserrat Subrayada','Moul','Moulpali','Mountains of Christmas','Mouse Memoirs','Mr Bedfort','Mr Dafoe','Mr De Haviland','Mrs Saint Delafield','Mrs Sheppards','Mukta','Mukta Mahee','Mukta Malar','Mukta Vaani','Muli','Mystery Quest','NTR','Neucha','Neuton','New Rocker','News Cycle','Niconne','Nixie One','Nobile','Nokora','Norican','Nosifer','Nothing You Could Do','Noticia Text','Noto Sans','Noto Serif','Nova Cut','Nova Flat','Nova Mono','Nova Oval','Nova Round','Nova Script','Nova Slim','Nova Square','Numans','Nunito','Nunito Sans','Odor Mean Chey','Offside','Old Standard TT','Oldenburg','Oleo Script','Oleo Script Swash Caps','Open Sans','Open Sans Condensed','Oranienbaum','Orbitron','Oregano','Orienta','Original Surfer','Oswald','Over the Rainbow','Overlock','Overlock SC','Overpass','Overpass Mono','Ovo','Oxygen','Oxygen Mono','PT Mono','PT Sans','PT Sans Caption','PT Sans Narrow','PT Serif','PT Serif Caption','Pacifico','Padauk','Palanquin','Palanquin Dark','Pangolin','Paprika','Parisienne','Passero One','Passion One','Pathway Gothic One','Patrick Hand','Patrick Hand SC','Pattaya','Patua One','Pavanam','Paytone One','Peddana','Peralta','Permanent Marker','Petit Formal Script','Petrona','Philosopher','Piedra','Pinyon Script','Pirata One','Plaster','Play','Playball','Playfair Display','Playfair Display SC','Podkova','Poiret One','Poller One','Poly','Pompiere','Pontano Sans','Poppins','Port Lligat Sans','Port Lligat Slab','Pragati Narrow','Prata','Preahvihear','Press Start 2P','Pridi','Princess Sofia','Prociono','Prompt','Prosto One','Proza Libre','Puritan','Purple Purse','Quando','Quantico','Quattrocento','Quattrocento Sans','Questrial','Quicksand','Quintessential','Qwigley','Racing Sans One','Radley','Rajdhani','Rakkas','Raleway','Raleway Dots','Ramabhadra','Ramaraja','Rambla','Rammetto One','Ranchers','Rancho','Ranga','Rasa','Rationale','Ravi Prakash','Redressed','Reem Kufi','Reenie Beanie','Revalia','Rhodium Libre','Ribeye','Ribeye Marrow','Righteous','Risque','Roboto','Roboto Condensed','Roboto Mono','Roboto Slab','Rochester','Rock Salt','Rokkitt','Romanesco','Ropa Sans','Rosario','Rosarivo','Rouge Script','Rozha One','Rubik','Rubik Mono One','Ruda','Rufina','Ruge Boogie','Ruluko','Rum Raisin','Ruslan Display','Russo One','Ruthie','Rye','Sacramento','Sahitya','Sail','Saira','Saira Condensed','Saira Extra Condensed','Saira Semi Condensed','Salsa','Sanchez','Sancreek','Sansita','Sarala','Sarina','Sarpanch','Satisfy','Scada','Scheherazade','Schoolbell','Scope One','Seaweed Script','Secular One','Sedgwick Ave','Sedgwick Ave Display','Sevillana','Seymour One','Shadows Into Light','Shadows Into Light Two','Shanti','Share','Share Tech','Share Tech Mono','Shojumaru','Short Stack','Shrikhand','Siemreap','Sigmar One','Signika','Signika Negative','Simonetta','Sintony','Sirin Stencil','Six Caps','Skranji','Slabo 13px','Slabo 27px','Slackey','Smokum','Smythe','Sniglet','Snippet','Snowburst One','Sofadi One','Sofia','Sonsie One','Sorts Mill Goudy','Source Code Pro','Source Sans Pro','Source Serif Pro','Space Mono','Special Elite','Spectral','Spicy Rice','Spinnaker','Spirax','Squada One','Sree Krushnadevaraya','Sriracha','Stalemate','Stalinist One','Stardos Stencil','Stint Ultra Condensed','Stint Ultra Expanded','Stoke','Strait','Sue Ellen Francisco','Suez One','Sumana','Sunshiney','Supermercado One','Sura','Suranna','Suravaram','Suwannaphum','Swanky and Moo Moo','Syncopate','Tangerine','Taprom','Tauri','Taviraj','Teko','Telex','Tenali Ramakrishna','Tenor Sans','Text Me One','The Girl Next Door','Tienne','Tillana','Timmana','Tinos','Titan One','Titillium Web','Trade Winds','Trirong','Trocchi','Trochut','Trykker','Tulpen One','Ubuntu','Ubuntu Condensed','Ubuntu Mono','Ultra','Uncial Antiqua','Underdog','Unica One','UnifrakturCook','UnifrakturMaguntia','Unkempt','Unlock','Unna','VT323','Vampiro One','Varela','Varela Round','Vast Shadow','Vesper Libre','Vibur','Vidaloka','Viga','Voces','Volkhov','Vollkorn','Voltaire','Waiting for the Sunrise','Wallpoet','Walter Turncoat','Warnes','Wellfleet','Wendy One','Wire One','Work Sans','Yanone Kaffeesatz','Yantramanav','Yatra One','Yellowtail','Yeseva One','Yesteryear','Yrsa','Zeyada','Zilla Slab','Zilla Slab Highlight');
31
-
32
-
33
- # check if php has disabled some functions by default
34
- function fvm_function_available($func) {
35
- if (ini_get('safe_mode')) return false;
36
- $disabled = ini_get('disable_functions');
37
- if ($disabled) {
38
- $disabled = explode(',', $disabled);
39
- $disabled = array_map('trim', $disabled);
40
- return !in_array($func, $disabled);
41
- }
42
- return true;
43
- }
44
-
45
- # run during activation
46
- function fastvelocity_plugin_activate() {
47
-
48
- # increment cache time
49
- fvm_cache_increment();
50
-
51
- # old cache purge event cron
52
- wp_clear_scheduled_hook( 'fastvelocity_purge_old_cron_event' );
53
- if (!wp_next_scheduled('fastvelocity_purge_old_cron_event')) {
54
- wp_schedule_event(time()+86400, 'daily', 'fastvelocity_purge_old_cron_event');
55
- }
56
-
57
- # setup defaults if no option to preserve exists
58
- if(get_option('fastvelocity_preserve_settings_on_uninstall') == false) {
59
-
60
- # default options to enable (1)
61
- $options_enable_default = array('fastvelocity_min_fvm_fix_editor', 'fastvelocity_min_remove_print_mediatypes', 'fastvelocity_fvm_clean_header_one', 'fastvelocity_min_skip_google_fonts', 'fastvelocity_min_force_inline_css_footer', 'fastvelocity_min_skip_cssorder', 'fastvelocity_gfonts_method', 'fastvelocity_fontawesome_method', 'fastvelocity_min_disable_css_inline_merge');
62
- foreach($options_enable_default as $option) {
63
- update_option($option, 1, 'no');
64
- }
65
-
66
- # default blacklist
67
- $exc = array('/html5shiv.js', '/html5shiv-printshiv.min.js', '/excanvas.js', '/avada-ie9.js', '/respond.js', '/respond.min.js', '/selectivizr.js', '/Avada/assets/css/ie.css', '/html5.js', '/IE9.js', '/fusion-ie9.js', '/vc_lte_ie9.min.css', '/old-ie.css', '/ie.css', '/vc-ie8.min.css', '/mailchimp-for-wp/assets/js/third-party/placeholders.min.js', '/assets/js/plugins/wp-enqueue/min/webfontloader.js', '/a.optnmstr.com/app/js/api.min.js', '/pixelyoursite/js/public.js', '/assets/js/wcdrip-drip.js');
68
- update_option('fastvelocity_min_blacklist', implode(PHP_EOL, $exc), 'no');
69
-
70
- # default ignore list
71
- $exc = array('/themes/Avada/assets/js/main.min.js', '/plugins/woocommerce-product-search/js/product-search.js', '/plugins/revslider/public/assets/js/jquery.themepunch.tools.min.js', '/js/TweenMax.min.js', '/themes/jupiter/assets/js/min/full-scripts', '/plugins/LayerSlider/static/layerslider/js/greensock.js', '/themes/kalium/assets/js/main.min.js', '/js/mediaelement/', '/plugins/elementor/assets/js/common.min.js', '/plugins/elementor/assets/js/frontend.min.js', '/plugins/elementor-pro/assets/js/frontend.min.js', '/themes/kalium/assets/js/main.min.js', '/wp-includes/js/mediaelement/wp-mediaelement.min.js');
72
- update_option('fastvelocity_min_ignorelist', implode(PHP_EOL, $exc), 'no');
73
-
74
- }
75
- }
76
-
77
- # run during deactivation
78
- function fastvelocity_plugin_deactivate() {
79
-
80
- # remove all on deactivation
81
- fvm_purge_all();
82
- fvm_purge_others();
83
-
84
- # old cache purge event cron
85
- wp_clear_scheduled_hook( 'fastvelocity_purge_old_cron_event' );
86
-
87
- # delete leftovers from older versions
88
- if(function_exists('_get_cron_array') && function_exists('_set_cron_array')) {
89
- $cron = _get_cron_array();
90
- if (is_array($cron)) {
91
-
92
- # count
93
- $a = count($cron);
94
-
95
- # keep only the ones that don't match the cron name
96
- $updated = array_filter($cron, function($v){return !array_key_exists("fastvelocity_purge_old_cron_event",$v);});
97
-
98
- # count
99
- $b = count($updated);
100
-
101
- # update
102
- if ($a != $b && is_array($updated) && count($updated) > 0) {
103
- _set_cron_array($updated);
104
- }
105
-
106
- }
107
- }
108
-
109
- }
110
-
111
- # run during uninstall
112
- function fastvelocity_plugin_uninstall() {
113
- global $wpdb;
114
-
115
- # remove defaults if no option to preserve exists
116
- if(get_option('fastvelocity_preserve_settings_on_uninstall') == false) {
117
-
118
- # delete all fvm options
119
- $plugin_options = $wpdb->get_results( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE 'fastvelocity_%' OR option_name LIKE 'fvm-%'" );
120
- if(is_array($plugin_options) && count($plugin_options) > 0) {
121
- foreach( $plugin_options as $option ) { delete_option( $option->option_name ); }
122
- }
123
-
124
- # purge all caches
125
- if(function_exists('fvm_purge_all_uninstall') && function_exists('fvm_purge_others')) {
126
- fvm_purge_all_uninstall();
127
- fvm_purge_others();
128
- }
129
-
130
- }
131
- }
132
-
133
-
134
-
135
-
136
- # try catch wrapper for merged javascript
137
- function fastvelocity_try_catch_wrap($js) {
138
- return 'try{'.PHP_EOL . $js . PHP_EOL . '}' . PHP_EOL . 'catch(e){console.error("An error has occurred: "+e.stack);}'.PHP_EOL;
139
- }
140
-
141
-
142
- # detect external or internal scripts
143
- function fvm_is_local_domain($src) {
144
- $locations = array(home_url(), site_url(), network_home_url(), network_site_url());
145
-
146
- # cdn support
147
- $fvm_cdn_url = get_option('fastvelocity_min_fvm_cdn_url');
148
- $defer_for_pagespeed = get_option('fastvelocity_min_defer_for_pagespeed');
149
- $fvm_cdn_force = get_option('fastvelocity_min_fvm_cdn_force');
150
-
151
- # excluded from cdn because of https://www.chromestatus.com/feature/5718547946799104 (we use document.write to preserve render blocking)
152
- if(!empty($fvm_cdn_url) && ($defer_for_pagespeed != true || $fvm_cdn_force != false) ) {
153
- array_push($locations, $fvm_cdn_url);
154
- }
155
-
156
- # cleanup locations
157
- $locations = array_filter(array_unique($locations));
158
-
159
- # debug
160
- $debug = array('src'=>$src, 'fvm_cdn_url'=>$fvm_cdn_url, 'defer_for_pagespeed'=>$defer_for_pagespeed, 'fvm_cdn_force'=>$fvm_cdn_force, 'locations'=>$locations);
161
-
162
-
163
- # external or not?
164
- $ret = false;
165
- foreach ($locations as $l) {
166
- $l = trim(trim(str_ireplace(array('http://', 'https://', 'www.'), '', trim($l)), '/'));
167
- if (stripos($src, $l) !== false && $ret === false) { $ret = true; }
168
- }
169
-
170
- # response
171
- return $ret;
172
- }
173
-
174
-
175
- # functions, get hurl info
176
- function fastvelocity_min_get_hurl($src, $wp_domain, $wp_home) {
177
-
178
- # preserve empty source handles
179
- $hurl = trim($src);
180
- if(empty($hurl)) { return $hurl; }
181
-
182
- # some fixes
183
- $hurl = str_ireplace(array('&#038;', '&amp;'), '&', $hurl);
184
-
185
- $default_protocol = get_option('fastvelocity_min_default_protocol', 'dynamic');
186
- if($default_protocol == 'dynamic' || empty($default_protocol)) {
187
- if ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1)) || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) { $default_protocol = 'https://'; } else { $default_protocol = 'http://'; }
188
- } else {
189
- $default_protocol = $default_protocol.'://';
190
- }
191
-
192
- #make sure wp_home doesn't have a forward slash
193
- $wp_home = rtrim($wp_home, '/');
194
-
195
- # apply some filters
196
- if (substr($hurl, 0, 2) === "//") { $hurl = $default_protocol.ltrim($hurl, "/"); } # protocol only
197
- if (substr($hurl, 0, 4) === "http" && stripos($hurl, $wp_domain) === false) { return $hurl; } # return if external domain
198
- if (substr($hurl, 0, 4) !== "http" && stripos($hurl, $wp_domain) !== false) { $hurl = $wp_home.'/'.ltrim($hurl, "/"); } # protocol + home
199
-
200
- # prevent double forward slashes in the middle
201
- $hurl = str_ireplace('###', '://', str_ireplace('//', '/', str_ireplace('://', '###', $hurl)));
202
-
203
- # consider different wp-content directory
204
- $proceed = 0; if(!empty($wp_home)) {
205
- $alt_wp_content = basename($wp_home);
206
- if(substr($hurl, 0, strlen($alt_wp_content)) === $alt_wp_content) { $proceed = 1; }
207
- }
208
-
209
- # protocol + home for relative paths
210
- if (substr($hurl, 0, 12) === "/wp-includes" || substr($hurl, 0, 9) === "/wp-admin" || substr($hurl, 0, 11) === "/wp-content" || $proceed == 1) {
211
- $hurl = $wp_home.'/'.ltrim($hurl, "/"); }
212
-
213
- # make sure there is a protocol prefix as required
214
- $hurl = $default_protocol.str_ireplace(array('http://', 'https://'), '', $hurl); # enforce protocol
215
-
216
- # no query strings
217
- if (stripos($hurl, '.js?v') !== false) { $hurl = stristr($hurl, '.js?v', true).'.js'; } # no query strings
218
- if (stripos($hurl, '.css?v') !== false) { $hurl = stristr($hurl, '.css?v', true).'.css'; } # no query strings
219
-
220
- # make sure there is a protocol prefix as required
221
- $hurl = fvm_compat_urls($hurl); # enforce protocol
222
-
223
- # add filter for developers
224
- $hurl = apply_filters('fvm_get_url', $hurl);
225
-
226
- return $hurl;
227
- }
228
-
229
-
230
- # check if it's an internal url or not
231
- function fvm_internal_url($hurl, $wp_home, $noxtra=NULL) {
232
- if (substr($hurl, 0, strlen($wp_home)) === $wp_home) { return true; }
233
- if (stripos($hurl, $wp_home) !== false) { return true; }
234
- if (isset($_SERVER['HTTP_HOST']) && stripos($hurl, preg_replace('/:\d+$/', '', $_SERVER['HTTP_HOST'])) !== false) { return true; }
235
- if (isset($_SERVER['SERVER_NAME']) && stripos($hurl, preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME'])) !== false) { return true; }
236
- if (isset($_SERVER['SERVER_ADDR']) && stripos($hurl, preg_replace('/:\d+$/', '', $_SERVER['SERVER_ADDR'])) !== false) { return true; }
237
-
238
- # allow specific external urls to be merged
239
- if($noxtra === NULL) {
240
- $merge_allowed_urls = array_map('trim', explode(PHP_EOL, get_option('fastvelocity_min_merge_allowed_urls', '')));
241
- if(is_array($merge_allowed_urls) && strlen(implode($merge_allowed_urls)) > 0) {
242
- foreach ($merge_allowed_urls as $e) {
243
- if (stripos($hurl, $e) !== false && !empty($e)) { return true; }
244
- }
245
- }
246
- }
247
-
248
- return false;
249
- }
250
-
251
-
252
- # case-insensitive in_array() wrapper
253
- function fastvelocity_min_in_arrayi($hurl, $ignore){
254
- $hurl = str_ireplace(array('http://', 'https://'), '//', $hurl); # better compatibility
255
- $hurl = strtok(urldecode(rawurldecode($hurl)), '?'); # no query string, decode entities
256
-
257
- if (!empty($hurl) && is_array($ignore)) {
258
- foreach ($ignore as $i) {
259
- $i = str_ireplace(array('http://', 'https://'), '//', $i); # better compatibility
260
- $i = strtok(urldecode(rawurldecode($i)), '?'); # no query string, decode entities
261
- $i = trim(trim(trim(rtrim($i, '/')), '*')); # wildcard char removal
262
- if (stripos($hurl, $i) !== false) { return true; }
263
- }
264
- }
265
- return false;
266
- }
267
-
268
-
269
- # better compatibility urls + fix w3.org NamespaceAndDTDIdentifiers
270
- function fvm_compat_urls($code) {
271
- $default_protocol = get_option('fastvelocity_min_default_protocol', 'dynamic');
272
- if($default_protocol == 'dynamic' || empty($default_protocol)) {
273
- if ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1)) || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) { $default_protocol = 'https://'; } else { $default_protocol = 'http://'; }
274
- } else {
275
- $default_protocol = $default_protocol.'://';
276
- }
277
- $code = str_ireplace(array('http://', 'https://'), $default_protocol, $code);
278
- $code = str_ireplace($default_protocol.'www.w3.org', 'http://www.w3.org', $code);
279
- return $code;
280
- }
281
-
282
-
283
- # minify css string with PHP Minify
284
- function fastvelocity_min_minify_css_string($css) {
285
- $minifier = new Minify\CSS($css);
286
- $minifier->setMaxImportSize(15); # [css only] embed assets up to 15 Kb (default 5Kb) - processes gif, png, jpg, jpeg, svg & woff
287
- $min = $minifier->minify();
288
- if($min !== false) { return fvm_compat_urls($min); }
289
- return fvm_compat_urls($css);
290
- }
291
-
292
-
293
- # find if we are running windows
294
- function fvm_server_is_windows() {
295
- # PHP 7.2.0+
296
- if(defined('PHP_OS_FAMILY')) {
297
- if(strtolower(PHP_OS_FAMILY) == 'windows') { return true; }
298
- }
299
- if(function_exists('php_uname')) {
300
- $os = @php_uname('s');
301
- if (stripos($os, 'Windows') !== false) {
302
- return true;
303
- }
304
- }
305
- return false;
306
- }
307
-
308
-
309
-
310
- # minify js on demand (one file at one time, for compatibility)
311
- function fastvelocity_min_get_js($url, $js, $disable_js_minification) {
312
- global $fvm_debug;
313
-
314
- # exclude minification on already minified files + jquery (because minification might break those)
315
- $excl = array('jquery.js', '.min.js', '-min.js', '/uploads/fusion-scripts/', '/min/', '.packed.js', '/includes/builder/scripts/');
316
- foreach($excl as $e) { if (stripos(basename($url), $e) !== false) { $disable_js_minification = true; break; } }
317
-
318
- # remove BOM
319
- $js = fastvelocity_min_remove_utf8_bom($js);
320
-
321
- # minify JS
322
- if(!$disable_js_minification) {
323
- $js = fastvelocity_min_minify_js_string($js);
324
- } else {
325
- $js = fvm_compat_urls($js);
326
- }
327
-
328
- # remove sourceMappingURL
329
- $js = preg_replace('/(\/\/\s*[#]\s*sourceMappingURL\s*[=]\s*)([a-zA-Z0-9-_\.\/]+)(\.map)/ui', '', $js);
330
-
331
- # needed when merging js files
332
- $js = trim($js);
333
- if(substr($js, -1) != ';'){ $js = $js.';'; }
334
- if($fvm_debug == true) { $js = '/* info: ' . $url . ' */' . PHP_EOL . $js; }
335
-
336
- # return html
337
- return $js . PHP_EOL;
338
- }
339
-
340
-
341
- # minify JS string with PHP Minify or YUI Compressors
342
- function fastvelocity_min_minify_js_string($js) {
343
- global $tmpdir, $plugindir;
344
-
345
- # PHP Minify from https://github.com/matthiasmullie/minify
346
- $minifier = new Minify\JS($js);
347
- $min = $minifier->minify();
348
- if($min !== false && (strlen(trim($js)) == strlen(trim($min)) || strlen(trim($min)) > 0)) {
349
- return fvm_compat_urls($min);
350
- }
351
-
352
- # if we are here, something went wrong and minification didn't work
353
- $js = "\n/*! FVM: Minification of the following section failed, so it has been merged instead. */\n".$js;
354
- return fvm_compat_urls($js);
355
- }
356
-
357
- # functions, minify html
358
- function fastvelocity_min_minify_html($html) {
359
- return fastvelocity_min_Minify_HTML::minify($html);
360
- }
361
-
362
- # functions to minify HTML
363
- function fastvelocity_min_html_compression_finish($html) { return fastvelocity_min_minify_html($html); }
364
- function fastvelocity_min_html_compression_start() {
365
- if (fastvelocity_exclude_contents() == true) { return; }
366
- ob_start('fastvelocity_min_html_compression_finish');
367
- }
368
-
369
-
370
- # remove default HTTP headers
371
- function fastvelocity_remove_redundant_shortlink() {
372
- remove_action('wp_head', 'wp_shortlink_wp_head', 10);
373
- remove_action( 'template_redirect', 'wp_shortlink_header', 11);
374
- }
375
-
376
- # minify css on demand (one file at one time, for compatibility)
377
- function fastvelocity_min_get_css($url, $css, $disable_css_minification) {
378
- global $wp_domain, $fvm_debug;
379
-
380
- # remove BOM
381
- $css = fastvelocity_min_remove_utf8_bom($css);
382
-
383
- # fix url paths
384
- if(!empty($url)) {
385
- $matches = array(); preg_match_all("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", $css, $matches);
386
- foreach($matches[1] as $a) { $b = trim($a); if($b != $a) { $css = str_replace($a, $b, $css); } }
387
- $css = preg_replace("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"#])(.+?)['\"]?\s*\)/ui", "url(".dirname($url)."/$1)", $css);
388
- }
389
-
390
- # no utf8 garbage
391
- $css = str_ireplace('@charset "UTF-8";', '', $css);
392
-
393
- # remove query strings from fonts (for better seo, but add a small cache buster based on most recent updates)
394
- $ctime = get_option('fvm-last-cache-update', '0'); # last update or zero
395
- $css = preg_replace('/(.eot|.woff2|.woff|.ttf)+[?+](.+?)(\)|\'|\")/ui', "$1"."#".$ctime."$3", $css); # fonts cache buster
396
-
397
- # remove sourceMappingURL
398
- $css = preg_replace('/(\/\/\s*[#]\s*sourceMappingURL\s*[=]\s*)([a-zA-Z0-9-_\.\/]+)(\.map)/ui', '', $css);
399
-
400
- # minify CSS
401
- if(!$disable_css_minification) {
402
- $css = fastvelocity_min_minify_css_string($css);
403
- } else {
404
- $css = fvm_compat_urls($css);
405
- }
406
-
407
- # cdn urls
408
- $fvm_cdn_url = get_option('fastvelocity_min_fvm_cdn_url');
409
- if(!empty($fvm_cdn_url)) {
410
- $fvm_cdn_url = trim(trim(str_ireplace(array('http://', 'https://'), '', trim($fvm_cdn_url, '/'))), '/');
411
- $css = str_ireplace($wp_domain, $fvm_cdn_url, $css);
412
- }
413
-
414
- # add css comment
415
- $css = trim($css);
416
- if($fvm_debug == true) { $css = '/* info: ' . $url . ' */' . PHP_EOL . trim($css); }
417
-
418
- # return html
419
- return $css;
420
- }
421
-
422
-
423
- # download and cache css and js files
424
- function fvm_download_and_minify($hurl, $inline, $disable_minification, $type, $handle){
425
- global $cachedir, $cachedirurl, $wp_domain, $wp_home, $wp_home_path, $fvm_debug;
426
-
427
- # must have
428
- if(is_null($hurl) || empty($hurl)) { return false; }
429
- if(!in_array($type, array('js', 'css'))) { return false; }
430
-
431
- # defaults
432
- if($disable_minification != true) { $disable_minification = false; }
433
- if(is_null($inline) || empty($inline)) { $inline = ''; }
434
- $printhandle = ''; if(is_null($handle) || empty($handle)) { $handle = ''; } else { $printhandle = "[$handle]"; }
435
-
436
- # debug request
437
- $dreq = array('hurl'=>$hurl, 'inline'=>$inline, 'disable_minification'=>$disable_minification, 'type'=>$type, 'handle'=>$handle);
438
-
439
- # filters and defaults
440
- $printurl = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl);
441
-
442
- # linux servers
443
- if(fvm_server_is_windows() === false) {
444
- if (stripos($hurl, $wp_domain) !== false) {
445
- # default
446
- $f = str_ireplace(rtrim($wp_home, '/'), rtrim($wp_home_path, '/'), $hurl);
447
- clearstatcache();
448
- if (file_exists($f)) {
449
- if($type == 'js') {
450
- $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification);
451
- } else {
452
- $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification);
453
- }
454
-
455
- # wp hide filter request
456
- $code = apply_filters( 'fvm_after_download_and_minify_code', $code, $type);
457
-
458
- # check for php code, skip if found
459
- if(strtolower(substr($code, 0, 5)) != "<?php" && stripos($code, "<?php") === false) {
460
- # log, save and return
461
- $log = $printurl;
462
- if($fvm_debug == true) { $log.= " --- Debug: $printhandle was opened from $f ---"; }
463
- $log.= PHP_EOL;
464
- $return = array('request'=>$dreq, 'log'=>$log, 'code'=>$code, 'status'=>true);
465
- return json_encode($return);
466
- }
467
-
468
-
469
- }
470
-
471
- # failover when home_url != site_url
472
- $nhurl = str_ireplace(site_url(), home_url(), $hurl);
473
- $f = str_ireplace(rtrim($wp_home, '/'), rtrim($wp_home_path, '/'), $nhurl);
474
- clearstatcache();
475
- if (file_exists($f)) {
476
- if($type == 'js') {
477
- $code = fastvelocity_min_get_js($hurl, file_get_contents($f), $disable_minification);
478
- } else {
479
- $code = fastvelocity_min_get_css($hurl, file_get_contents($f).$inline, $disable_minification);
480
- }
481
-
482
- # wp hide filter request
483
- $code = apply_filters( 'fvm_after_download_and_minify_code', $code, $type);
484
-
485
- # check for php code, skip if found
486
- if(strtolower(substr($code, 0, 5)) != "<?php" && stripos($code, "<?php") === false) {
487
- # log, save and return
488
- $log = $printurl;
489
- if($fvm_debug == true) { $log.= " --- Debug: $printhandle was opened from $f ---"; }
490
- $log.= PHP_EOL;
491
- $return = array('request'=>$dreq, 'log'=>$log, 'code'=>$code, 'status'=>true);
492
- return json_encode($return);
493
- }
494
- }
495
- }
496
- }
497
-
498
-
499
- # else, fallback to remote urls (or windows)
500
- $code = fastvelocity_download($hurl);
501
- if($code !== false && !empty($code) && strtolower(substr($code, 0, 9)) != "<!doctype") {
502
-
503
- # check if we got HTML instead of js or css code
504
-
505
- if($type == 'js') {
506
- $code = fastvelocity_min_get_js($hurl, $code, $disable_minification);
507
- } else {
508
- $code = fastvelocity_min_get_css($hurl, $code.$inline, $disable_minification);
509
- }
510
-
511
- # wp hide filter request
512
- $code = apply_filters( 'fvm_after_download_and_minify_code', $code, $type);
513
-
514
- # log, save and return
515
- $log = $printurl;
516
- if($fvm_debug == true) { $log.= " --- Debug: $printhandle was fetched from $hurl ---"; }
517
- $log.= PHP_EOL;
518
- $return = array('request'=>$dreq, 'log'=>$log, 'code'=>$code, 'status'=>true);
519
- return json_encode($return);
520
- }
521
-
522
-
523
- # fallback when home_url != site_url
524
- if(stripos($hurl, $wp_domain) !== false && home_url() != site_url()) {
525
- $nhurl = str_ireplace(site_url(), home_url(), $hurl);
526
- $code = fastvelocity_download($nhurl);
527
- if($code !== false && !empty($code) && strtolower(substr($code, 0, 9)) != "<!doctype") {
528
- if($type == 'js') {
529
- $code = fastvelocity_min_get_js($hurl, $code, $disable_minification);
530
- } else {
531
- $code = fastvelocity_min_get_css($hurl, $code.$inline, $disable_minification);
532
- }
533
-
534
- # wp hide filter request
535
- $code = apply_filters( 'fvm_after_download_and_minify_code', $code, $type);
536
-
537
- # log, save and return
538
- $log = $printurl;
539
- if($fvm_debug == true) { $log.= " --- Debug: $printhandle was fetched from $hurl ---"; }
540
- $log.= PHP_EOL;
541
- $return = array('request'=>$dreq, 'log'=>$log, 'code'=>$code, 'status'=>true);
542
- return json_encode($return);
543
- }
544
- }
545
-
546
- # else fail
547
- $log = $printurl;
548
- if($fvm_debug == true) { $log.= " --- Debug: $printhandle failed. Tried wp_remote_get and local file_get_contents. ---"; }
549
- $return = array('request'=>$dreq, 'log'=>$log, 'code'=>'', 'status'=>false);
550
- return json_encode($return);
551
- }
552
-
553
-
554
- # check if the google font exist or not
555
- function fastvelocity_min_concatenate_google_fonts_allowed($font) {
556
- global $gfontswhitelist;
557
-
558
- #normalize
559
- $gfontswhitelist = array_map('strtolower', $gfontswhitelist);
560
- $font = str_ireplace('+', ' ', strtolower($font));
561
-
562
- # check
563
- if(in_array($font, $gfontswhitelist)) {
564
- return true;
565
- }
566
-
567
- # fallback
568
- return false;
569
- }
570
-
571
-
572
- # Concatenate Google Fonts tags (http://fonts.googleapis.com/css?...)
573
- function fastvelocity_min_concatenate_google_fonts($array) {
574
-
575
- # extract unique font families
576
- $families = array();
577
- foreach ($array as $font) {
578
-
579
- # cleanup font display
580
- $font = str_ireplace(array('&display=auto', '&display=block', '&display=swap', '&display=fallback', '&display=optional'), '', html_entity_decode(urldecode($font)));
581
-
582
- # must have
583
- if (stripos($font, 'family=') !== false) {
584
-
585
- # get fonts name, type and subset, remove wp query strings
586
- $font = explode('family=', htmlspecialchars_decode(rawurldecode(urldecode($font))));
587
- $a = explode('&v', end($font));
588
- $font = trim(trim(trim(current($a)), ','));
589
-
590
- # reprocess if fonts are already concatenated in this url
591
- if(stristr($font, '|') !== false) {
592
- $multiple = explode('|', $font);
593
- if (count($multiple) > 0) {
594
- foreach ($multiple as $f) {
595
- $families[] = str_ireplace('subsets', 'subset', trim($f));
596
- }
597
- }
598
- } else {
599
- $families[] = str_ireplace('subsets', 'subset', trim($font));
600
- }
601
- }
602
- }
603
-
604
- # return if empty
605
- if(count($families) == 0) {
606
- return false;
607
- }
608
-
609
- # process names, types, subsets
610
- $fonts = array();
611
- $subsets = array();
612
- foreach ($families as $font) {
613
-
614
- # extract the subsets
615
- if (stripos($font, 'subset') !== false) {
616
- $sub = trim(str_ireplace('&subset=', '', stristr($font, '&'))); # second part of the string, after &
617
- $font = stristr($font, '&', true); # font name, before &
618
-
619
- # subsets to array, unique, trim
620
- if (stripos($sub, ',') !== false) {
621
- $ft = explode(',', $sub);
622
- $ft = array_filter(array_map('trim', array_flip(array_flip($ft))));
623
- foreach ($ft as $s) {
624
- $subsets[$s] = $s;
625
- }
626
- } else {
627
- if (!empty($sub)) {
628
- $subsets[$sub] = $sub;
629
- }
630
- }
631
-
632
- }
633
-
634
- # check for font name and weights
635
- $ftypes = array();
636
- $name = $font;
637
- if (stripos($font, ':') !== false) {
638
- $name = stristr($font, ':', true); # font name, before :
639
- $fwe = trim(stristr($font, ':'), ':'); # second part of the string, after :
640
-
641
- # cleanup, void stuff like FONT:regular:regular:regular:regular,regular
642
- if (stripos($fwe, ':') !== false) {
643
- $fwe = stristr($fwe, ':', true);
644
- }
645
-
646
- # ftypes to array, unique, trim
647
- if (stripos($font, ',') !== false) {
648
- $ft = explode(',', $fwe);
649
- $ftypes = array_filter(array_map('trim', array_flip(array_flip($ft))));
650
- } else {
651
- if (!empty($fwe)) {
652
- $ftypes[] = $fwe;
653
- }
654
- }
655
-
656
- }
657
-
658
- # name filter
659
- $name = str_ireplace(' ', '+', trim($name));
660
-
661
- # save fonts list, merge fontweights
662
- if(!isset($fonts[$name])) {
663
- $fonts[$name] = array('name'=>$name, 'type'=>$ftypes);
664
- } else {
665
- $ftypes = array_filter(array_map('trim', array_unique(array_merge($ftypes, $fonts[$name]['type']))));
666
- $fonts[$name] = array('name'=>$name, 'type'=>$ftypes);
667
- }
668
-
669
- }
670
-
671
- # build font names with font weights, if allowed
672
- $build = array();
673
- foreach($fonts as $farr) {
674
- if(fastvelocity_min_concatenate_google_fonts_allowed($farr['name']) == true) {
675
- $f = $farr['name'];
676
- if(count($farr['type']) > 0) {
677
- $f.= ':'. implode(',', $farr['type']);
678
- }
679
- $build[] = $f;
680
- }
681
- }
682
-
683
- # merge, append subsets
684
- $merge = '';
685
- if(count($build) > 0) {
686
- $merge = implode('|', $build);
687
- if(count($subsets) > 0) {
688
- $subsetfilter = str_replace('latin,latin-ext', 'latin-ext', implode(',', $subsets));
689
- $merge.= '&subset='.$subsetfilter;
690
- }
691
- }
692
-
693
- # return
694
- if(!empty($merge)) {
695
- return 'https://fonts.googleapis.com/css?family='.$merge;
696
- } else {
697
- return false;
698
- }
699
- }
700
-
701
- # remove emoji support
702
- function fastvelocity_min_disable_wp_emojicons() {
703
- remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
704
- remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
705
- remove_action( 'wp_print_styles', 'print_emoji_styles' );
706
- remove_action( 'admin_print_styles', 'print_emoji_styles' );
707
- remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
708
- remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
709
- remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
710
- }
711
-
712
- # remove from tinymce
713
- function fastvelocity_disable_emojis_tinymce( $plugins ) {
714
- if ( is_array( $plugins ) ) {
715
- return array_diff( $plugins, array( 'wpemoji' ) );
716
- } else {
717
- return array();
718
- }
719
- }
720
-
721
-
722
-
723
- # escape double quotes
724
- function fastvelocity_escape_double($string) {
725
- return str_ireplace(array('"', '\\"', '\\\"'), '\"', $string);
726
- }
727
-
728
-
729
- # remove UTF8 BOM
730
- function fastvelocity_min_remove_utf8_bom($text) {
731
- $bom = pack('H*','EFBBBF');
732
- $text = preg_replace("/^$bom/ui", '', $text);
733
- return $text;
734
- }
735
-
736
-
737
- # Remove query string from static css files
738
- function fastvelocity_remove_cssjs_ver( $src ) {
739
- if(stripos($src, '?ver=')) { $src = remove_query_arg('ver', $src); }
740
- return $src;
741
- }
742
-
743
-
744
- # rewrite cache files to http, https or dynamic
745
- function fvm_get_protocol($url) {
746
- global $wp_domain;
747
- $url = ltrim(str_ireplace(array('http://', 'https://'), '', $url), '/'); # better compatibility
748
-
749
- # cdn support
750
- $fvm_cdn_url = get_option('fastvelocity_min_fvm_cdn_url');
751
- $fvm_cdn_url = trim(trim(str_ireplace(array('http://', 'https://'), '', trim($fvm_cdn_url, '/'))), '/');
752
-
753
- # process cdn rewrite
754
- if(!empty($fvm_cdn_url) && fvm_is_local_domain($url) !== false) {
755
-
756
- # for js files, we need to consider thew defer for insights option
757
- if(substr($url, -3) == '.js') {
758
-
759
- $defer_for_pagespeed = get_option('fastvelocity_min_defer_for_pagespeed');
760
- $fvm_cdn_force = get_option('fastvelocity_min_fvm_cdn_force');
761
-
762
- if($defer_for_pagespeed != true || $fvm_cdn_force != false) {
763
- $url = str_ireplace($wp_domain, $fvm_cdn_url, $url);
764
- }
765
-
766
- } else {
767
- $url = str_ireplace($wp_domain, $fvm_cdn_url, $url);
768
- }
769
- }
770
-
771
- # enforce protocol if needed
772
- $default_protocol = get_option('fastvelocity_min_default_protocol', 'dynamic');
773
- if($default_protocol == 'dynamic' || empty($default_protocol)) {
774
- if ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1)) || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) { $default_protocol = 'https://'; } else { $default_protocol = 'http://'; }
775
- } else {
776
- $default_protocol = $default_protocol.'://';
777
- }
778
-
779
- # return
780
- return $default_protocol.$url;
781
- }
782
-
783
-
784
-
785
-
786
-
787
- # generate ascii slug
788
- function fvm_safename($str, $noname=NULL) {
789
- $nstr = preg_replace("/[^a-zA-Z0-9]+/", "-", $str);
790
- $nstr = trim(trim($nstr, '-'));
791
- if(strlen($nstr) > 1) {
792
- return $nstr;
793
- }
794
-
795
- # return false if no empty name rewrite requested
796
- if($noname !== NULL) {
797
- return false;
798
- }
799
-
800
- # fallback
801
- return 'noname-'.hash('sha1', $str);
802
- }
803
-
804
-
805
- # escape html tags for document.write
806
- function fastvelocity_escape_url_js($str) {
807
- return str_ireplace(array('\\\\\"', '\\\\"', '\\\"', '\\"'), '\"', json_encode($str));
808
- }
809
-
810
-
811
-
812
- # always load the fvmua javascript code
813
- function fastvelocity_load_fvuag() {
814
-
815
- # for compatibility, let's always skip the checkout page
816
- if(function_exists('is_checkout') && is_checkout() === true || is_preview() || is_customize_preview()) {
817
- return true;
818
- }
819
-
820
- # customizer preview, visual composer
821
- $arr = array('customize_theme', 'preview_id', 'preview');
822
- foreach ($arr as $a) { if(isset($_GET[$a])) { return true; } }
823
-
824
- # Thrive plugins and other post_types
825
- $arr = array('tve_form_type', 'tve_lead_shortcode', 'tqb_splash');
826
- foreach ($arr as $a) { if(isset($_GET['post_type']) && $_GET['post_type'] == $a) { return true; } }
827
-
828
- # thrive architect
829
- if(isset($_GET['tve']) && $_GET['tve'] == 'true') { return true; }
830
-
831
- # elementor
832
- if(isset($_GET['elementor-preview'])) { return true; }
833
- if(is_array($_GET)) {
834
- foreach ($_GET as $k=>$v) {
835
- if(is_string($v) && is_string($k)) {
836
- if(stripos($k, 'elementor') !== false || stripos($v, 'elementor') !== false) {
837
- return true;
838
- }
839
- }
840
- }
841
- }
842
-
843
- # do not load
844
- return false;
845
- }
846
-
847
-
848
- # exclude processing from some pages / posts / contents
849
- function fastvelocity_exclude_contents() {
850
-
851
- # prevent execution for specific urls
852
- if(isset($_SERVER['REQUEST_URI']) && !empty($_SERVER['REQUEST_URI'])) {
853
- $disable_on_url = array_filter(array_map('trim', explode(PHP_EOL, get_option('fastvelocity_disable_on_url', ''))));
854
- foreach ($disable_on_url as $url) {
855
- if($url == parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)) { return true; }
856
- }
857
- }
858
-
859
- # for compatibility, let's always skip the checkout page
860
- if(function_exists('is_checkout') && is_checkout() === true) {
861
- return true;
862
- }
863
-
864
- # exclude processing here
865
- if (is_feed() || is_admin() || is_preview() || is_customize_preview() || (defined('DOING_AJAX') && DOING_AJAX) || (function_exists('wp_doing_ajax') && wp_doing_ajax()) || (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || (defined('WP_BLOG_ADMIN') && WP_BLOG_ADMIN) || (defined('WP_NETWORK_ADMIN') && WP_NETWORK_ADMIN) || (defined('WP_INSTALLING') && WP_INSTALLING) || (defined('WP_IMPORTING') && WP_IMPORTING) || (defined('WP_REPAIRING') && WP_REPAIRING) || (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) || (defined('SHORTINIT') && SHORTINIT) || (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST') || (function_exists( 'is_amp_endpoint' ) && is_amp_endpoint()) ||
866
- (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') || (isset($_SERVER['REQUEST_URI']) && (substr($_SERVER['REQUEST_URI'], -4) == '.txt' || substr($_SERVER['REQUEST_URI'], -4) == '.xml'))) {
867
- return true;
868
- }
869
-
870
- # get params exclusions
871
- if(is_array($_GET)) {
872
- foreach ($_GET as $k=>$v) {
873
- if(is_string($v) && is_string($k)) {
874
-
875
- # elementor
876
- if(stripos($k, 'elementor') !== false || stripos($v, 'elementor') !== false) {
877
- return true;
878
- }
879
-
880
- # customizer preview, visual composer
881
- if(stripos($k, 'customize_theme') !== false || stripos($k, 'preview_id') !== false || stripos($k, 'preview') !== false) {
882
- return true;
883
- }
884
-
885
- # thrive plugins post_types
886
- if(stripos($k, 'post_type') !== false && (stripos($v, 'tve_') !== false || stripos($v, 'tqb_') !== false)) {
887
- return true;
888
- }
889
-
890
- # thrive architect
891
- if($k == 'tve' && $v == 'true') {
892
- return true;
893
- }
894
-
895
- # Divi Builder
896
- if($k == 'et_fb' || $k == 'PageSpeed') {
897
- return true;
898
- }
899
-
900
- }
901
- }
902
- }
903
-
904
-
905
- # any wp-admin url
906
- if(isset($_SERVER['REQUEST_URI']) && stripos($_SERVER['REQUEST_URI'], '/wp-admin/') !== false) {
907
- return true;
908
- }
909
-
910
- # default
911
- return false;
912
- }
913
-
914
-
915
- # Know files that should always be ignored
916
- function fastvelocity_default_ignore($ignore) {
917
- if(is_array($ignore)) {
918
-
919
- # from the database
920
- $exc = array_map('trim', explode(PHP_EOL, get_option('fastvelocity_min_ignorelist', '')));
921
-
922
- # should we exclude jquery when defer is enabled?
923
- $exclude_defer_jquery = get_option('fastvelocity_min_exclude_defer_jquery');
924
- if($exclude_defer_jquery == true) {
925
- $exc[] = '/jquery.js';
926
- $exc[] = '/jquery.min.js';
927
- }
928
-
929
- # make sure it's unique and not empty
930
- $uniq = array();
931
- foreach ($ignore as $i) { $k = hash('sha1', $i); if(!empty($i)) { $uniq[$k] = $i; } }
932
- foreach ($exc as $e) { $k = hash('sha1', $e); if(!empty($e)) { $uniq[$k] = $e; } }
933
-
934
- # merge and return
935
- return $uniq;
936
- } else { return $ignore; }
937
- }
938
-
939
-
940
- # IE only files that should always be ignored, without incrementing our groups
941
- function fastvelocity_ie_blacklist($url) {
942
-
943
- # from the database
944
- $exc = array_map('trim', explode(PHP_EOL, get_option('fastvelocity_min_blacklist', '')));
945
-
946
- # must have
947
- $exc[] = '/fvm/cache/';
948
-
949
- # is the url on our list and return
950
- $res = fastvelocity_min_in_arrayi($url, $exc);
951
- if($res == true) { return true; } else { return false; }
952
- }
953
-
954
-
955
- # download function with fallback
956
- function fastvelocity_download($url) {
957
-
958
- # info (needed for google fonts woff files + hinted fonts) as well as to bypass some security filters
959
- $uagent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586';
960
-
961
- # fetch via wordpress functions
962
- $response = wp_remote_get($url, array('user-agent'=>$uagent, 'timeout' => 7, 'httpversion' => '1.1', 'sslverify'=>false));
963
- $res_code = wp_remote_retrieve_response_code($response);
964
- if($res_code == '200') {
965
- $data = wp_remote_retrieve_body($response);
966
- if(strlen($data) > 1) {
967
- return $data;
968
- }
969
- }
970
-
971
- # verify
972
- if(!isset($res_code) || empty($res_code) || $res_code == false || is_null($res_code)) {
973
- return false;
974
- }
975
-
976
- # stop here, error 4xx or 5xx
977
- if($res_code[0] == '4' || $res_code[0] == '5') {
978
- return false;
979
- }
980
-
981
- # fallback fail
982
- return false;
983
- }
984
-
985
-
986
-
987
-
988
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/serverinfo.php ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ # Exit if accessed directly
4
+ if (!defined('ABSPATH')){ exit(); }
5
+
6
+ ### Get General Information
7
+ function fvm_get_generalinfo() {
8
+
9
+ # check if user has admin rights
10
+ if(!current_user_can('manage_options')) {
11
+ echo 'You are not allowed to execute this function!';
12
+ }
13
+
14
+ echo'+++'. PHP_EOL;
15
+ echo'SERVER INFO:'. PHP_EOL;
16
+ echo'OS: '. PHP_OS . PHP_EOL;
17
+ echo'Server: '. $_SERVER["SERVER_SOFTWARE"] . PHP_EOL;
18
+ echo'PHP: '. PHP_VERSION . PHP_EOL;
19
+ echo'MySQL: '. fvm_get_mysql_version() . PHP_EOL;
20
+ echo'CPU Cores: '. fvm_get_servercpu() . PHP_EOL;
21
+ echo'Server Load: '. fvm_get_serverload() . PHP_EOL;
22
+
23
+ echo'---'. PHP_EOL;
24
+ echo'SITE INFO:'. PHP_EOL;
25
+ echo'Site Path: '. $_SERVER['DOCUMENT_ROOT'] . PHP_EOL;
26
+ echo'Hostname: '. $_SERVER['SERVER_NAME'] . PHP_EOL;
27
+ echo'DB Data Size: '. fvm_format_php_size(fvm_get_mysql_data_usage()) . PHP_EOL;
28
+ echo'DB Index Size: '. fvm_format_php_size(fvm_get_mysql_index_usage()) . PHP_EOL;
29
+
30
+ echo'---'. PHP_EOL;
31
+ echo'LIMITS:'. PHP_EOL;
32
+ echo'PHP Max Exec Time: '. fvm_get_php_max_execution(). PHP_EOL;
33
+ echo'PHP Memory Limit: '. fvm_format_php_size(fvm_get_php_memory_limit()) . PHP_EOL;
34
+ echo'PHP Max Upload Size: '. fvm_format_php_size(fvm_get_php_upload_max()) . PHP_EOL;
35
+ echo'PHP Max Post Size: '. fvm_format_php_size(fvm_get_php_post_max()) . PHP_EOL;
36
+ echo'MySQL Max Packet Size: '. fvm_format_php_size(fvm_get_mysql_max_allowed_packet()) . PHP_EOL;
37
+ echo'MySQL Max Connections: '. fvm_get_mysql_max_allowed_connections() . PHP_EOL;
38
+ echo'+++';
39
+ }
40
+
41
+
42
+ ### Convert PHP Size Format to an int, then readable format
43
+ function fvm_format_php_size($size) {
44
+ if (!is_numeric($size)) {
45
+ if (strpos($size, 'M') !== false) {
46
+ $size = intval($size)*1024*1024;
47
+ } elseif (strpos($size, 'K') !== false) {
48
+ $size = intval($size)*1024;
49
+ } elseif (strpos($size, 'G') !== false) {
50
+ $size = intval($size)*1024*1024*1024;
51
+ }
52
+ }
53
+
54
+ $size = is_numeric($size) ? fvm_format_filesize($size, 0) : $size;
55
+ return str_pad($size, 8, " ", STR_PAD_LEFT);
56
+ }
57
+
58
+ ### Function: Get PHP Max Upload Size
59
+ if(!function_exists('fvm_get_php_upload_max')) {
60
+ function fvm_get_php_upload_max() {
61
+
62
+ # check if user has admin rights
63
+ if(!current_user_can('manage_options')) {
64
+ return 'You are not allowed to execute this function!';
65
+ }
66
+
67
+ if(ini_get('upload_max_filesize')) {
68
+ $upload_max = ini_get('upload_max_filesize');
69
+ } else {
70
+ $upload_max = strval('N/A');
71
+ }
72
+ return $upload_max;
73
+ }
74
+ }
75
+
76
+
77
+ ### Function: Get PHP Max Post Size
78
+ if(!function_exists('fvm_get_php_post_max')) {
79
+ function fvm_get_php_post_max() {
80
+
81
+ # check if user has admin rights
82
+ if(!current_user_can('manage_options')) {
83
+ return 'You are not allowed to execute this function!';
84
+ }
85
+
86
+ if(ini_get('post_max_size')) {
87
+ $post_max = ini_get('post_max_size');
88
+ } else {
89
+ $post_max = strval('N/A');
90
+ }
91
+ return $post_max;
92
+ }
93
+ }
94
+
95
+
96
+ ### Function: PHP Maximum Execution Time
97
+ if(!function_exists('fvm_get_php_max_execution')) {
98
+ function fvm_get_php_max_execution() {
99
+
100
+ # check if user has admin rights
101
+ if(!current_user_can('manage_options')) {
102
+ return 'You are not allowed to execute this function!';
103
+ }
104
+
105
+ if(ini_get('max_execution_time')) {
106
+ $max_execute = intval(ini_get('max_execution_time'));
107
+ } else {
108
+ $max_execute = strval('N/A');
109
+ }
110
+
111
+ return str_pad($max_execute, 5, " ", STR_PAD_LEFT);
112
+ }
113
+ }
114
+
115
+
116
+ ### Function: PHP Memory Limit
117
+ if(!function_exists('fvm_get_php_memory_limit')) {
118
+ function fvm_get_php_memory_limit() {
119
+
120
+ # check if user has admin rights
121
+ if(!current_user_can('manage_options')) {
122
+ return 'You are not allowed to execute this function!';
123
+ }
124
+
125
+ if(ini_get('memory_limit')) {
126
+ $memory_limit = ini_get('memory_limit');
127
+ } else {
128
+ $memory_limit = strval('N/A');
129
+ }
130
+ return $memory_limit;
131
+ }
132
+ }
133
+
134
+
135
+ ### Function: Get MYSQL Version
136
+ if(!function_exists('fvm_get_mysql_version')) {
137
+ function fvm_get_mysql_version() {
138
+
139
+ # check if user has admin rights
140
+ if(!current_user_can('manage_options')) {
141
+ return 'You are not allowed to execute this function!';
142
+ }
143
+
144
+ global $wpdb;
145
+ return $wpdb->get_var("SELECT VERSION() AS version");
146
+ }
147
+ }
148
+
149
+
150
+ ### Function: Get MYSQL Data Usage
151
+ if(!function_exists('fvm_get_mysql_data_usage')) {
152
+ function fvm_get_mysql_data_usage() {
153
+
154
+ # check if user has admin rights
155
+ if(!current_user_can('manage_options')) {
156
+ return 'You are not allowed to execute this function!';
157
+ }
158
+
159
+ global $wpdb;
160
+ $data_usage = 0;
161
+ $tablesstatus = $wpdb->get_results("SHOW TABLE STATUS");
162
+ foreach($tablesstatus as $tablestatus) {
163
+ if(is_numeric($tablestatus->Data_length)) { $data_usage += $tablestatus->Data_length; } else { $data_usage += 0; }
164
+ }
165
+ if (!$data_usage) {
166
+ $data_usage = strval('N/A');
167
+ }
168
+ return $data_usage;
169
+ }
170
+ }
171
+
172
+
173
+ ### Function: Get MYSQL Index Usage
174
+ if(!function_exists('fvm_get_mysql_index_usage')) {
175
+ function fvm_get_mysql_index_usage() {
176
+
177
+ # check if user has admin rights
178
+ if(!current_user_can('manage_options')) {
179
+ return 'You are not allowed to execute this function!';
180
+ }
181
+
182
+ global $wpdb;
183
+ $index_usage = 0;
184
+ $tablesstatus = $wpdb->get_results("SHOW TABLE STATUS");
185
+ foreach($tablesstatus as $tablestatus) {
186
+ if(is_numeric($tablestatus->Index_length)) { $index_usage += $tablestatus->Index_length; } else { $index_usage += 0; }
187
+ }
188
+ if (!$index_usage){
189
+ $index_usage = strval('N/A');
190
+ }
191
+ return $index_usage;
192
+ }
193
+ }
194
+
195
+
196
+ ### Function: Get MYSQL Max Allowed Packet
197
+ if(!function_exists('fvm_get_mysql_max_allowed_packet')) {
198
+ function fvm_get_mysql_max_allowed_packet() {
199
+
200
+ # check if user has admin rights
201
+ if(!current_user_can('manage_options')) {
202
+ return 'You are not allowed to execute this function!';
203
+ }
204
+
205
+ global $wpdb;
206
+ $packet_max_query = $wpdb->get_row("SHOW VARIABLES LIKE 'max_allowed_packet'");
207
+ $packet_max = $packet_max_query->Value;
208
+ if(!$packet_max) {
209
+ $packet_max = strval('N/A');
210
+ }
211
+ return $packet_max;
212
+ }
213
+ }
214
+
215
+
216
+ ### Function:Get MYSQL Max Allowed Connections
217
+ if(!function_exists('fvm_get_mysql_max_allowed_connections')) {
218
+ function fvm_get_mysql_max_allowed_connections() {
219
+
220
+ # check if user has admin rights
221
+ if(!current_user_can('manage_options')) {
222
+ return 'You are not allowed to execute this function!';
223
+ }
224
+
225
+ global $wpdb;
226
+ $connection_max_query = $wpdb->get_row("SHOW VARIABLES LIKE 'max_connections'");
227
+ $connection_max = $connection_max_query->Value;
228
+ if(!$connection_max) {
229
+ $connection_max = strval('N/A');
230
+ }
231
+
232
+ return str_pad($connection_max, 5, " ", STR_PAD_LEFT);
233
+ }
234
+ }
235
+
236
+
237
+ ### Function: Get The Server Load
238
+ if(!function_exists('fvm_get_serverload')) {
239
+ function fvm_get_serverload() {
240
+
241
+ # check if user has admin rights
242
+ if(!current_user_can('manage_options')) {
243
+ return 'You are not allowed to execute this function!';
244
+ }
245
+
246
+ $server_load = 0;
247
+ $numCpus = 'N/A';
248
+ if(PHP_OS != 'WINNT' && PHP_OS != 'WIN32') {
249
+ clearstatcache();
250
+ if(@file_exists('/proc/loadavg') ) {
251
+ if ($fh = @fopen( '/proc/loadavg', 'r' )) {
252
+ $data = @fread( $fh, 6 );
253
+ @fclose( $fh );
254
+ $load_avg = explode( " ", $data );
255
+ $server_load = trim($load_avg[0]);
256
+ }
257
+ } else if ('WIN' == strtoupper(substr(PHP_OS, 0, 3)) && function_exists('popen') && fvm_function_available('popen')) {
258
+ $process = @popen('wmic cpu get NumberOfCores', 'rb');
259
+ if (false !== $process && null !== $process) {
260
+ fgets($process);
261
+ $numCpus = intval(fgets($process));
262
+ pclose($process);
263
+ }
264
+ } else if (function_exists('system') && fvm_function_available('system')){
265
+ $data = @system('uptime');
266
+ preg_match('/(.*):{1}(.*)/', $data, $matches);
267
+ if(isset($matches[2])) {
268
+ $load_arr = explode(',', $matches[2]);
269
+ $server_load = trim($load_arr[0]);
270
+ } else {
271
+ $server_load = strval('N/A');
272
+ }
273
+ } else {
274
+ $server_load = strval('N/A');
275
+ }
276
+ }
277
+ if(empty($server_load)) {
278
+ $server_load = strval('N/A');
279
+ }
280
+ return $server_load;
281
+ }
282
+ }
283
+
284
+
285
+ ### Function: Get The Server CPU's
286
+ if(!function_exists('fvm_get_servercpu')) {
287
+ function fvm_get_servercpu() {
288
+
289
+ # check if user has admin rights
290
+ if(!current_user_can('manage_options')) {
291
+ return 'You are not allowed to execute this function!';
292
+ }
293
+
294
+ $numCpus = 0;
295
+ if(PHP_OS != 'WINNT' && PHP_OS != 'WIN32') {
296
+ clearstatcache();
297
+ if (@is_file('/proc/cpuinfo')) {
298
+ $cpuinfo = file_get_contents('/proc/cpuinfo');
299
+ preg_match_all('/^processor/m', $cpuinfo, $matches);
300
+ $numCpus = count($matches[0]);
301
+ } else if (function_exists('popen') && fvm_function_available('popen')) {
302
+ $process = @popen('sysctl -a', 'rb');
303
+ if (false !== $process && null !== $process) {
304
+ $output = stream_get_contents($process);
305
+ preg_match('/hw.ncpu: (\d+)/', $output, $matches);
306
+ if ($matches) { $numCpus = intval($matches[1][0]); }
307
+ pclose($process);
308
+ }
309
+ } else {
310
+ $numCpus = strval('N/A');
311
+ }
312
+ }
313
+ if(empty($numCpus)) {
314
+ $numCpus = strval('N/A');
315
+ }
316
+ return $numCpus;
317
+ }
318
+ }
inc/updates.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ # Exit if accessed directly
4
+ if (!defined('ABSPATH')){ exit(); }
5
+
6
+ # update routines for new fields and replacements
7
+ function fvm_get_updated_field_routines($fvm_settings) {
8
+
9
+ # must have
10
+ if(!is_array($fvm_settings)) { return $fvm_settings; }
11
+
12
+ # Version 3.0 routines start
13
+ if (get_option("fastvelocity_plugin_version") !== false) {
14
+
15
+ # cache path
16
+ if (get_option("fastvelocity_min_change_cache_path") !== false) {
17
+ $fvm_settings['cache']['path'] = get_option("fastvelocity_min_change_cache_path");
18
+ delete_option('fastvelocity_min_change_cache_path');
19
+ }
20
+
21
+ # cache base_url
22
+ if (get_option("fastvelocity_min_change_cache_base_url") !== false) {
23
+ $fvm_settings['cache']['url'] = get_option("fastvelocity_min_change_cache_base_url");
24
+ delete_option('fastvelocity_min_change_cache_base_url');
25
+ }
26
+
27
+ # cdn url
28
+ if (get_option("fastvelocity_min_fvm_cdn_url") !== false) {
29
+ $fvm_settings['cdn']['enable'] = 1;
30
+ $fvm_settings['cdn']['cssok'] = 1;
31
+ $fvm_settings['cdn']['jsok'] = 1;
32
+ $fvm_settings['cdn']['domain'] = get_option("fastvelocity_min_fvm_cdn_url");
33
+ delete_option('fastvelocity_min_fvm_cdn_url');
34
+ }
35
+
36
+ # cleanup
37
+ delete_option('fastvelocity_plugin_version');
38
+ delete_option('fvm-last-cache-update');
39
+ delete_option('fastvelocity_min_ignore');
40
+ delete_option('fastvelocity_min_blacklist');
41
+ delete_option('fastvelocity_min_ignorelist');
42
+ delete_option('fastvelocity_min_excludecsslist');
43
+ delete_option('fastvelocity_min_excludejslist');
44
+ delete_option('fastvelocity_min_enable_purgemenu');
45
+ delete_option('fastvelocity_min_default_protocol');
46
+ delete_option('fastvelocity_min_disable_js_merge');
47
+ delete_option('fastvelocity_min_disable_css_merge');
48
+ delete_option('fastvelocity_min_disable_js_minification');
49
+ delete_option('fastvelocity_min_disable_css_minification');
50
+ delete_option('fastvelocity_min_remove_print_mediatypes');
51
+ delete_option('fastvelocity_min_skip_html_minification');
52
+ delete_option('fastvelocity_min_strip_htmlcomments');
53
+ delete_option('fastvelocity_min_skip_cssorder');
54
+ delete_option('fastvelocity_min_skip_google_fonts');
55
+ delete_option('fastvelocity_min_skip_emoji_removal');
56
+ delete_option('fastvelocity_fvm_clean_header_one');
57
+ delete_option('fastvelocity_min_enable_defer_js');
58
+ delete_option('fastvelocity_min_exclude_defer_jquery');
59
+ delete_option('fastvelocity_min_force_inline_css');
60
+ delete_option('fastvelocity_min_force_inline_css_footer');
61
+ delete_option('fastvelocity_min_remove_googlefonts');
62
+ delete_option('fastvelocity_min_defer_for_pagespeed');
63
+ delete_option('fastvelocity_min_defer_for_pagespeed_optimize');
64
+ delete_option('fastvelocity_min_exclude_defer_login');
65
+ delete_option('fastvelocity_min_skip_defer_lists');
66
+ delete_option('fastvelocity_min_fvm_fix_editor');
67
+ delete_option('fastvelocity_min_loadcss');
68
+ delete_option('fastvelocity_min_fvm_removecss');
69
+ delete_option('fastvelocity_enabled_css_preload');
70
+ delete_option('fastvelocity_enabled_js_preload');
71
+ delete_option('fastvelocity_fontawesome_method');
72
+ delete_option('fastvelocity_gfonts_method');
73
+
74
+ # clear cron
75
+ wp_clear_scheduled_hook( 'fastvelocity_purge_old_cron_event' );
76
+
77
+ }
78
+ # Version 3.0 routines end
79
+
80
+ # return settings array
81
+ return $fvm_settings;
82
+ }
83
+
84
+
inc/wp-cli.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ # Exit if accessed directly
4
+ if (!defined('ABSPATH')){ exit(); }
5
+
6
+ ###################################################
7
+ # extend wp-cli to purge cache, usage: wp fvm purge
8
+ ###################################################
9
+
10
+ # only for wp-cli
11
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
12
+
13
+ class fastvelocity_WPCLI {
14
+
15
+ # purge files + cache
16
+ public function purge() {
17
+ WP_CLI::success('FVM and other caches were purged.');
18
+ fvm_purge_minification();
19
+ fvm_purge_others();
20
+
21
+ # purge everything
22
+ $cache = fvm_purge_minification();
23
+ $others = fvm_purge_others();
24
+
25
+ # notices
26
+ WP_CLI::success('FVM: All Caches are now cleared. ('.date("D, d M Y @ H:i:s e").')');
27
+ if(is_string($cache)) { WP_CLI::warning($cache); }
28
+ if(is_string($others)) { WP_CLI::success($others); }
29
+
30
+ }
31
+
32
+ # get cache size
33
+ public function stats() {
34
+ WP_CLI::error('This feature is currently under development.');
35
+ }
36
+
37
+ }
38
+
39
+ # add commands
40
+ WP_CLI::add_command( 'fvm', 'fastvelocity_WPCLI' );
41
+
42
+ }
43
+
layout/admin-layout-help.php ADDED
@@ -0,0 +1,379 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( $at == 'help' ) { ?>
2
+
3
+ <div class="fvm-wrapper">
4
+
5
+ <h2 class="title">FVM 3 Release Notes</h2>
6
+
7
+ <div class="accordion">
8
+ <h3>Important JS and JavaScript changes</h3>
9
+ <div>
10
+ <p><strong>Notes:</strong></p>
11
+ <p>JavaScript merging functionality went through a significant change on FVM 3 and it now requires manual configuration to work.</p>
12
+ <p>If you are upgrading from FVM 2 please refer to the help section below, to understand how to reconfigure the plugin settings.</p>
13
+ <p>Please note that this plugin is usually for advanced users and developers. If you just installed the plugin, please note that JS is not being optimized yet. You have to choose which files to be render blocking and which ones to be deferred, plus it's dependencies. </p>
14
+ <p>Previously, FVM merged everything and relied on having options to filter out, or ignore scripts. This option frequently created issues after some plugin updates (most notably elementor, gravity forms and other plugins).</p>
15
+ <p>The new method only merges what you tell it to merge, hence it's safer. Please understand, this plugin is and it has always been, aimed at advanced users and developers, so it's not meant to work fully without manual settings in place.</p>
16
+ <p>On FVM 2 we were merging all scripts automatically which caused many scripts and plugins to stop working (we cannot test every single plugin). With the new method, you now have full control of your scripts.</p>
17
+ <p>In addition, we now added a method to optimize third party scripts and load them on user interaction or automatically, after 5 seconds. This is a more recommended method to optimize scripts, as compared to FVM 2 which used document.write and other methods.</p>
18
+ <p>Please refer to the JavaScript help section further down on this page to understand how you can optimize your scripts.</p>
19
+ </div>
20
+ <h3>Relevant CSS and Fonts changes</h3>
21
+ <div>
22
+ <p><strong>Notes:</strong></p>
23
+ <p>You can now ignore, or completely remove CSS files by URI Path or domain name (such as google fonts or other unwanted CSS files).</p>
24
+ <p>Known fonts, icon, animation and some other CSS files now have an option to be merged separately and loaded Async.</p>
25
+ <p>The option to inline CSS has been removed because it's no longer best practice (unless your CSS is tiny), however FVM now preloads the external CSS files on the header.</p>
26
+ </div>
27
+ <h3>Relevant Cache changes</h3>
28
+ <div>
29
+ <p><strong>Notes:</strong></p>
30
+ <p>Purging cache on FVM renames the cache directory path and file names in order to bypass CDN and Browser caches, however, expired css and js cache files are only deleted 24 hours after your last cache purge request.</p>
31
+ <p>This is needed because some hosting services can cache your HTML regardless of your cache purge request. When this happens, your users are still seeing a cached version of your site, with references to the expired FVM CSS/JS files for a while longer, until their cache expires or you manually trigger a cache purge on your hosting or other page cache plugin. If we were to delete the FVM cached files right away, it would cause your layout to break in those situations, as the files would no longer exist.</p>
32
+ <p>On FVM 3 you can now choose to purge the cache immediately, if you are sure no other page cache is going to do the above.</p>
33
+ </div>
34
+ <h3>Other changes</h3>
35
+ <div>
36
+ <p><strong>Notes:</strong></p>
37
+ <p>CSS and JS files are now preloaded in the header by default.</p>
38
+ <p>Preconnect and Preload Headers have been removed (please use your own PHP code and conditional tags for that).</p>
39
+ <p>Critical Path CSS option has been removed (please use your own PHP code and conditional tags, or another plugin for that).</p>
40
+ </div>
41
+ </div>
42
+
43
+
44
+ <div style="height: 20px;"></div>
45
+ <h2 class="title">HTML Settings</h2>
46
+
47
+ <div class="accordion">
48
+ <h3>Enable HTML Processing</h3>
49
+ <div>
50
+ <p><strong>Notes:</strong></p>
51
+ <p>You need to enable this option, for any other options in the HTML section to work.</p>
52
+ </div>
53
+ <h3>Disable HTML Minification</h3>
54
+ <div>
55
+ <p><strong>Notes:</strong></p>
56
+ <p>Although rare, it's possible that HTML minification may strip too much code thus breaking something.</p>
57
+ <p>You can use this option to test if that is the case.</p>
58
+ </div>
59
+ <h3>Strip HTML Comments</h3>
60
+ <div>
61
+ <p><strong>Notes:</strong></p>
62
+ <p>Some plugins may need to use comments for certain functionality to work, however this is quite rare. This option is enabled by default.</p>
63
+ </div>
64
+ <h3>Cleanup Header</h3>
65
+ <div>
66
+ <p><strong>Notes:</strong></p>
67
+ <p>This options removes resource hints, generator tag, shortlinks, emoji, manifest link, etc from the HTML header.</p>
68
+ <p>This is recommended because the head section, should be kept as lean as possible for the best TTFB response times and LCP metrics.</p>
69
+ </div>
70
+ </div>
71
+
72
+
73
+ <div style="height: 20px;"></div>
74
+ <h2 class="title">CSS Settings</h2>
75
+
76
+ <div class="accordion">
77
+ <h3>Enable CSS Processing</h3>
78
+ <div>
79
+ <p><strong>Notes:</strong></p>
80
+ <p>You need to enable this option, for any other options in the CSS section to work.</p>
81
+ </div>
82
+ <h3>Disable CSS Minification</h3>
83
+ <div>
84
+ <p><strong>Notes:</strong></p>
85
+ <p>Although rare, it's possible that CSS minification may strip too much code thus breaking your styles.</p>
86
+ <p>Also note, that if your theme or some plugin uses <code>@import</code> rules, they may be the reason for your styles to be broken.</p>
87
+ <p>You can use this option to test if minification is the reason for your broken styles, but it won't do anything about your @import rules (remove the <code>@import</code> rules and enqueue them properly before using FVM).</p>
88
+ </div>
89
+ <h3>Remove "Print" stylesheets</h3>
90
+ <div>
91
+ <p><strong>Notes:</strong></p>
92
+ <p>As a generic rule, it's safe to remove it for the vast majority of sites, unless your users often need to print pages from your site, and you have customized styles for when they do so.</p>
93
+ </div>
94
+ <h3>Merge Fonts and Icons Separately</h3>
95
+ <div>
96
+ <p><strong>Notes:</strong></p>
97
+ <p>This will try to collect all your icon and animation know files into a separate file.</p>
98
+ <p>It may be useful for debugging purposes or to evaluate how many fonts are in use.</p>
99
+ </div>
100
+ <h3>Load generated CSS files Async</h3>
101
+ <div>
102
+ <p><strong>Notes:</strong></p>
103
+ <p>This will load the generated CSS files Async, however, without a manually added critical path code, you will likely see a Flash of Unstyled Content before that CSS finishes loading.</p>
104
+ <p>Use your own PHP code or another plugin to add the critical path CSS code using conditional tags, filters, hooks or other method.</p>
105
+ </div>
106
+ <h3>CSS Ignore List</h3>
107
+ <div>
108
+ <p><strong>Notes:</strong></p>
109
+ <p>You can use this option to prevent a certain CSS file from being merged, for example, when it breaks something when merged.</p>
110
+ <p>This uses uses PHP stripos against the href attribute on the link tag, to decide if a CSS should be left alone or not.</p>
111
+ <p>If you are having issues with a CSS file being merged, please make sure that the file being merged doesn't have any <code>@import</code> rules (remove and enqueue those files properly instead).</p>
112
+ <p>This should be left empty if there are no CSS issues breaking your layout.</p>
113
+ </div>
114
+ <h3>Remove CSS files</h3>
115
+ <div>
116
+ <p><strong>Notes:</strong></p>
117
+ <p>There may be situations, where you wish to remove some CSS file you are not using anywhere on the site, without editing the originating file.</p>
118
+ <p>For example, you could remove google fonts, or the default Gutenberg CSS file enqueued by WordPress.</p>
119
+ <p>You should still first try to remove it or dequeue it with PHP code, when possible.</p>
120
+ </div>
121
+ </div>
122
+
123
+
124
+ <div style="height: 20px;"></div>
125
+ <h2 class="title">JS Settings</h2>
126
+
127
+ <div class="accordion">
128
+ <h3>Enable JS Processing</h3>
129
+ <div>
130
+ <p><strong>Notes:</strong></p>
131
+ <p>You need to enable this option, for any other options in the JS section to work.</p>
132
+ </div>
133
+ <h3>Disable JS Minification</h3>
134
+ <div>
135
+ <p><strong>Notes:</strong></p>
136
+ <p>Although rare, it's possible that JS minification may strip too much white space, or fail to minify some more complex JavaScript code, which can lead to breaking some functionality or triggering browser console log errors.</p>
137
+ <p>You can use this option to test if that is the case.</p>
138
+ </div>
139
+ <h3>Upgrade to jQuery 3</h3>
140
+ <div>
141
+ <p><strong>Notes:</strong></p>
142
+ <p>If your theme and plugins make use of modern standards and doesn't include outdated jQuery code, you can force the usage of the smaller jQuery 3 instead of the older, default WordPress jQuery.</p>
143
+ <p>You must check your browser console log in incognito mode, for possible errors after enabling this feature. Sometimes there are no errors but some scripts may not work as well, so use this feature with care.</p>
144
+ </div>
145
+ <h3>Merge render blocking JS files in the header</h3>
146
+ <div>
147
+ <p><strong>Notes:</strong></p>
148
+ <p>In most WordPress themes and for a significant amount of plugins, you need to render block jQuery and possibly other scripts.</p>
149
+ <p>If you are a developer and are sure that there is no inlined code requiring jQuery to be defined earlier, then leave this section empty and add your scripts on the "Merge and Defer Scripts" section instead.</p>
150
+ <p>It's important for speed, that you keep this section to a bare minimum, usually, jQuery and jQuery migrate.</p>
151
+ <p>Some plugins such as gravity forms may also require to be render blocking, so you should look out for browser console log errors in incognito mode.</p>
152
+ <p><strong>Recommended Default Settings:</strong></p>
153
+ <p class="fvm-code-full">
154
+ /jquery-migrate-<br>
155
+ /jquery-migrate.js<br>
156
+ /jquery-migrate.min.js<br>
157
+ /jquery.js<br>
158
+ /jquery.min.js<br>
159
+ </p>
160
+ </div>
161
+ <h3>Merge and Defer Scripts</h3>
162
+ <div>
163
+ <p><strong>Notes:</strong></p>
164
+ <p>This option uses PHP stripos against the script <code>outerHTML</code> to decide what to merge and defer.</p>
165
+ <p>Very frequently, jQuery, jQuery migrate and some plugins, need to be render blocking for everything to work, so you cannot just put all scripts in this section.</p>
166
+ <p>You must check your browser console log in incognito mode, for possible errors after enabling this feature, and either move them to the header, remove them from this list (be more specific with paths so it doesn't match certain files), or use the Inline JavaScript Dependencies section, to force inline scripts to wait for this file to load.</p>
167
+ <p>Note that this is an advanced feature that can stop your scripts from working, hence it requires manual configuration.</p>
168
+ <p><strong>Recommended Default Settings:</strong></p>
169
+ <p class="fvm-code-full">
170
+ /ajax.aspnetcdn.com/ajax/<br>
171
+ /ajax.googleapis.com/ajax/libs/<br>
172
+ /cdnjs.cloudflare.com/ajax/libs/<br>
173
+ /stackpath.bootstrapcdn.com/bootstrap/<br>
174
+ /wp-admin/<br>
175
+ /wp-content/<br>
176
+ /wp-includes/
177
+ </p>
178
+ </div>
179
+ <h3>Inline JavaScript Dependencies</h3>
180
+ <div>
181
+ <p><strong>Notes:</strong></p>
182
+ <p>This option uses PHP stripos against the script <code>innerHTML</code> to decide what to merge and defer.</p>
183
+ <p>When you merge JS files and defer them, you are effectively changing the order in which they load, however for certain scripts, the order of loading matters.</p>
184
+ <p>If you defer a certain script and you see an "undefined" error triggered on some inline code, you can try this option to force it to wait for the deferred scripts to finish, but it may still not work if dependencies are more complex.</p>
185
+ <p>You must check your browser console log in incognito mode, for possible errors after enabling this feature, and either move them to the header, remove them from this list (be more specific with paths so it doesn't match certain files), or use the Inline JavaScript Dependencies to force inline scripts to wait for this file to load.</p>
186
+ <p>This is empty by default, unless you determine that it's needed (the plugin is for advanced users and developers, so you need to debug yourself).</p>
187
+ </div>
188
+ <h3>Execute third party inline scripts after user interaction</h3>
189
+ <div>
190
+ <p><strong>Notes:</strong></p>
191
+ <p>Scripts like analytics, ads, tracking codes, etc, consume important CPU and Network resources needed for the initial pageview.</p>
192
+ <p>This option uses PHP stripos against the script <code>innerHTML</code> or <code>src</code> attribute for async/defer scripts.</p>
193
+ <p>It will delay the specified script execution until the user interacts with the page, on the first <code>'mouseover','keydown','touchmove','touchstart'</code> event, or <code>up to 5 seconds after page load</code>(whichever happens first).</p>
194
+ <p>Most Async and Defer scripts can be rewritten to support this feature with the <code>HTML DOM createElement() Method</code>, however, note that if you blindly use this method for render blocking scripts, it may trigger "undefined" errors on the browser console log or some elements may stop working (some scripts only work in render blocking mode).</p>
195
+ <p>If you have render blocking third party scripts, ask your provider if they can provide you with an async implementation (else remove them, because render blocking scripts are not recommended for speed).</p>
196
+ <p><strong>Example Settings:</strong></p>
197
+ <p class="fvm-code-full">
198
+ function(w,d,s,l,i)<br>
199
+ function(f,b,e,v,n,t,s)<br>
200
+ function(h,o,t,j,a,r)<br>
201
+ www.googletagmanager.com/gtm.js
202
+ </p>
203
+ </div>
204
+ <h3>Remove JavaScript Scripts</h3>
205
+ <div>
206
+ <p><strong>Notes:</strong></p>
207
+ <p>This option uses PHP stripos against the script <code>outerHTML</code> to decide what to remove.</p>
208
+ <p>It can be used when you want to remove some JS file that you cannot remove directly from the source.</p>
209
+ <p>If it's a third party script you added to the header or footer (or via some plugin), it's better if you delete it at the source.</p>
210
+ </div>
211
+ </div>
212
+
213
+
214
+
215
+ <div style="height: 20px;"></div>
216
+ <h2 class="title">CDN Settings</h2>
217
+
218
+ <div class="accordion">
219
+ <h3>Enable CDN Processing</h3>
220
+ <div>
221
+ <p><strong>Notes:</strong></p>
222
+ <p>You need to enable this option, for any other options in the CDN section to work.</p>
223
+ </div>
224
+ <h3>Enable CDN for merged CSS files</h3>
225
+ <div>
226
+ <p><strong>Notes:</strong></p>
227
+ <p>When selecting this option, FVM will replace your domain name with the CDN domain, for the generated CSS cache file.</p>
228
+ <p>Under certain situations, you may not want to serve the CSS file from the CDN, such as when your server compression level is significantly higher than the CDN (smaller file than the one delivered by the CDN).</p>
229
+ <p>Also bare in mind, that if the CSS file is served from the CDN, any static assets inside the CSS file that make use of relative paths, will also be cached and served from the CDN, which may also be undesirable in certain situations.</p>
230
+ </div>
231
+ <h3>Enable CDN for merged JS files</h3>
232
+ <div>
233
+ <p><strong>Notes:</strong></p>
234
+ <p>When selecting this option, FVM will replace your domain name with the CDN domain, for the generated JS cache file.</p>
235
+ <p>Under certain situations, you may not want to serve the JS file from the CDN, such as when your server compression level is significantly higher than the CDN (smaller file than the one delivered by the CDN).</p>
236
+ <p>Also bare in mind, that if the JS file is served from the CDN, any static assets inside the JS file that make use of relative paths, will also be cached and served from the CDN, which may also be undesirable in certain situations.</p>
237
+ </div>
238
+ <h3>CDN URL</h3>
239
+ <div>
240
+ <p><strong>Notes:</strong></p>
241
+ <p>This is not required for providers such as cloudflare.com or sucuri.com as well as any reverse proxy CDN service that doesn't change your domain name (the whole site is proxified through their service).</p>
242
+ <p>For other types of CDN, you are usually provided with an alternative domain name from where your static files can be served, and in those cases, you would introduce your new domain name here, for your static assets.</p>
243
+ </div>
244
+ <h3>CDN Integration</h3>
245
+ <div>
246
+ <p><strong>Notes:</strong></p>
247
+ <p>Uses syntax from <a target="_blank" href="https://simplehtmldom.sourceforge.io/manual.htm">https://simplehtmldom.sourceforge.io/manual.htm</a> for modifying the urls on the HTML.</p>
248
+ <p>The plugin will only replace your site domain, with the CDN domain for the matching HTML tags.</p>
249
+ <p><strong>Recommended Default Settings:</strong></p>
250
+ <p class="fvm-code-full">
251
+ a[data-interchange*=/wp-content/]<br>
252
+ image[height]<br>
253
+ img[src*=/wp-content/], img[data-src*=/wp-content/], img[data-srcset*=/wp-content/]<br>
254
+ link[rel=icon]<br>
255
+ picture source[srcset*=/wp-content/]<br>
256
+ video source[type*=video]
257
+ </p>
258
+ </div>
259
+ </div>
260
+
261
+
262
+ <div style="height: 20px;"></div>
263
+ <h2 class="title">Cache Settings</h2>
264
+
265
+ <div class="accordion">
266
+ <h3>Instant Cache Purge</h3>
267
+ <div>
268
+ <p><strong>Notes:</strong></p>
269
+ <p>This option should be disabled when your hosting or server is doing page cache without integration with FVM.</p>
270
+ <p>If you enable this option and FVM doesn't have integration with your hosting or cache plugin, your HTML page will still be pointing to your (now deleted) generated JS/CSS files, resulting in a broken layout for your anonymous users until your page cache expires or is manually purged.</p>
271
+ </div>
272
+ <h3>Public Cache Path</h3>
273
+ <div>
274
+ <p><strong>Notes:</strong></p>
275
+ <p>This can be any writeable directory or mounting point on your server, as long as it's mapped to be served on publicly available URL.</p>
276
+ <p>Should be left empty by default, unless your hosting blocks writing files to the default cache directory.</p>
277
+ </div>
278
+ <h3>Public Cache URL</h3>
279
+ <div>
280
+ <p><strong>Notes:</strong></p>
281
+ <p>This must be a publicly available URL on your site, where users can download the generated js/css files.</p>
282
+ <p>Should be left empty by default, unless your hosting blocks writing files to the default cache directory.</p>
283
+ </div>
284
+ </div>
285
+
286
+
287
+
288
+
289
+ <div style="height: 20px;"></div>
290
+ <h2 class="title">Other FAQ's</h2>
291
+
292
+ <div class="accordion">
293
+ <h3>Is the plugin GDPR compatible?</h3>
294
+ <div>
295
+ <p><strong>Notes:</strong></p>
296
+ <p>FVM does not collect any information from you, your site or your users. It also doesn't require cookies to work, therefore it's fully GDPR compatible.</p>
297
+ </div>
298
+ <h3>How do I know if the plugin is working?</h3>
299
+ <div>
300
+ <p><strong>Notes:</strong></p>
301
+ <p>For compatibility reasons, the plugin only optimizes anonymous users by default. That means, you need to open another browser window, or use incognito mode to test and see what it's doing. Logged in users will not see the optimizations, unless you manually enable certain user roles (not recommended for complex websites, unless you know what you are doing).</p>
302
+ </div>
303
+ <h3>How do I purge the cache?</h3>
304
+ <div>
305
+ <p><strong>Notes:</strong></p>
306
+ <p>Please note that FVM is not a page cache plugin and it doesn't cache your content. The only time you should need to purge it's cache, is when you edit a css or js file.</p>
307
+ <p>If your HTML page is being cached somewhere else, you must purge your cache either, unless FVM supports it natively.</p>
308
+ </div>
309
+ <h3>Why am I getting 404 error not found for the generated CSS or JS files?</h3>
310
+ <div>
311
+ <p><strong>Notes:</strong></p>
312
+ <p>You deleted the FVM cache but forgot to purge the HTML page cache, or you are lacking writing permissions on the cache directory and files are not being created. </p>
313
+ <p>You must purge your page cache either on some other cache plugin or at your server/hosting level for your page to update and find the latest merged file paths.</p>
314
+ <p>Note that some hosts rate limit the amount of times you can purge caches to once every few minutes, so you may be purging and it doesn't work, because you are being rate limited by your hosting cache system.</p>
315
+ <p>Avoid doing development on live sites and use a staging server without cache for testing. Production servers are for deploying once and leave it until the next deployment cycle.</p>
316
+ </div>
317
+ <h3>Why is my site layout broke after an update, a configuration change, or some other change?</h3>
318
+ <div>
319
+ <p><strong>Notes:</strong></p>
320
+ <p>You must check your browser console log in incognito mode, for possible errors after enabling certain features, deferring scripts or using some other optimization plugins, which may be conflicting with FVM optimization.</p>
321
+ <p>If there are no errors, disable each option one by one on FVM (html processing, css processing, js processing, etc) until you find the feature breaking it. After that, adjust and tweak those settings accordingly, or hire a developer to help you.</p>
322
+ </div>
323
+ <h3>How can I download an older version of FVM for testing purposes?</h3>
324
+ <div>
325
+ <p><strong>Notes:</strong></p>
326
+ <p>It's not recommended you do that, but if you want to test something, you can do so from the <a target="_blank" href="https://plugins.svn.wordpress.org/fast-velocity-minify/tags/">SVN repository</a> on WordPress.</p>
327
+ </div>
328
+ <h3>How do I undo all optimizations done by FVM?</h3>
329
+ <div>
330
+ <p><strong>Notes:</strong></p>
331
+ <p>Simply disable the plugin, and make sure to purge all page caches (Cache Plugins, Hosting, OPCache, etc).</p>
332
+ <p>Note that some hosts rate limit the amount of times you can purge caches to once every few minutes, so you may be purging and it doesn't work, because you are being rate limited by your hosting cache system. If that happens, jsut ask your hosting to manually purge all caches on the server.</p>
333
+ <p>FVM does not modify your site. It runs after your template loads and filters the final HTML to present it to your users, in a more optimized way.</p>
334
+ </div>
335
+ <h3>I have disabled FVM but somehow the cache files are still being generated?</h3>
336
+ <div>
337
+ <p><strong>Notes:</strong></p>
338
+ <p>If you have disabled the plugin and purged all caches available, this is simply not possible.</p>
339
+ <p>Please ensure you have delete the plugin on the correct site location and that all caches are emptied.</p>
340
+ <p>A few hosting providers will cache your disk in memory to speed things up when they use remote disk locations, which may cause code to be cached even if you have deleted it completely from the disk. In that case, restart the server or ask your hosting to purge all disk caches.</p>
341
+ </div>
342
+ <h3>Where can I get support or ask questions about the plugin?</h3>
343
+ <div>
344
+ <p><strong>Notes:</strong></p>
345
+ <p>You can ask for help on <a href="https://wordpress.org/support/plugin/fast-velocity-minify/">https://wordpress.org/support/plugin/fast-velocity-minify/</a> but please note we cannot guide you on which files to merge or how to solve JavaScript conflicts. You need to try different settings (trial and error) and open a separate window in incognito mode, to look for console log errors on your browser, and adjust settings as needed.</p>
346
+ </div>
347
+ <h3>How is it possible that some scan is showing malware on the plugin?</h3>
348
+ <div>
349
+ <p><strong>Notes:</strong></p>
350
+ <p>I guarantee that the plugin is 100% clean of malware, provided you have downloaded the plugin from the official WordPress source.</p>
351
+ <p>Understand that Malware can infect any plugin you have on your site (even security plugins), regardless of the point of entry. Sometimes it propagates to/from different areas (including other sites you may have on the same server). </p>
352
+ <p>If there is any malware on any scripts being merged by FVM, they would be merged as they are until your next cache purge. </p>
353
+ <p>If you are seeing malware on any file related to FVM, simply delete the plugin, purge all cache files and make sure to only download the plugin from the official source on wordpress.org or via wp-admin.</p>
354
+ </div>
355
+ <h3>How do I report a security issue or file a bug report?</h3>
356
+ <div>
357
+ <p><strong>Notes:</strong></p>
358
+ <p>If you are sure it's a bug and not a misconfiguration specific to your site, thank you for taking the time to report it.</p>
359
+ <p>You can contact me on <a href="https://fastvelocity.com/">https://fastvelocity.com/</a> using the contact form.</p>
360
+ <p>You are also welcome to submit patches and fixes via <a href="https://github.com/peixotorms/fast-velocity-minify">https://github.com/peixotorms/fast-velocity-minify</a> if you are a developer.</p>
361
+
362
+ </div>
363
+ <h3>I'm not a developer, can I hire you for a more complete speed optimization?</h3>
364
+ <div>
365
+ <p>You can contact me on <a href="https://fastvelocity.com/">https://fastvelocity.com/</a> using the contact form, providing me your site URL and what issues you are trying to fix, for a more exact quote.</p>
366
+ <p>My speed optimization starts from $500 for small sites and from $850 for woocommerce and membership sites.</p>
367
+ <p>I do not use the free FVM for my professional work, but I guarantee as best performance as possible for your site content.</p>
368
+ </div>
369
+ <h3>How can I donate to the plugin author?</h3>
370
+ <div>
371
+ <p><strong>Notes:</strong></p>
372
+ <p>While not required, if you are happy with my work and would like to buy me a <del>beer</del> green tea, you can do it via PayPal at <a target="_blank" href="https://goo.gl/vpLrSV">https://goo.gl/vpLrSV</a> and thank you in advance :)</p>
373
+ </div>
374
+ </div>
375
+
376
+
377
+ </div>
378
+ <?php
379
+ }
layout/admin-layout-settings.php ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( $at == 'settings' ) { ?>
2
+ <div class="fvm-wrapper">
3
+
4
+ <form method="post" id="fvm-save-changes">
5
+
6
+ <?php
7
+ # nounce
8
+ wp_nonce_field('fvm_settings_nonce', 'fvm_settings_nonce');
9
+ ?>
10
+
11
+ <h2 class="title">HTML Settings</h2>
12
+ <h3 class="fvm-bold-green">Optimize your HTML and remove some clutter from the HTML page.</h3>
13
+
14
+ <table class="form-table fvm-settings">
15
+ <tbody>
16
+
17
+ <tr>
18
+ <th scope="row">HTML Options</th>
19
+ <td>
20
+ <p class="fvm-bold-green fvm-rowintro">Select your options below</p>
21
+
22
+ <fieldset>
23
+ <label for="fvm_settings_html_enable">
24
+ <input name="fvm_settings[html][enable]" type="checkbox" id="fvm_settings_html_enable" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'html', 'enable')); ?>>
25
+ Enable HTML Processing <span class="note-info">[ Will enable processing for the settings below ]</span></label>
26
+ <br />
27
+
28
+ <label for="fvm_settings_html_min_disable">
29
+ <input name="fvm_settings[html][min_disable]" type="checkbox" id="fvm_settings_html_min_disable" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'html', 'min_disable')); ?>>
30
+ Disable HTML Minification <span class="note-info">[ Will disable HTML minification for testing purposes ]</span></label>
31
+ <br />
32
+
33
+ <label for="fvm_settings_html_nocomments">
34
+ <input name="fvm_settings[html][nocomments]" type="checkbox" id="fvm_settings_html_nocomments" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'html', 'nocomments')); ?>>
35
+ Strip HTML Comments <span class="note-info">[ Will strip HTML comments from your HTML page ]</span></label>
36
+ <br />
37
+
38
+ <label for="fvm_settings_html_cleanup_header">
39
+ <input name="fvm_settings[html][cleanup_header]" type="checkbox" id="fvm_settings_html_cleanup_header" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'html', 'cleanup_header')); ?>>
40
+ Cleanup Header <span class="note-info">[ Removes resource hints, generator tag, shortlinks, manifest link, etc ]</span></label>
41
+ <br />
42
+
43
+ <label for="fvm_settings_html_disable_emojis">
44
+ <input name="fvm_settings[html][disable_emojis]" type="checkbox" id="fvm_settings_html_disable_emojis" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'html', 'disable_emojis')); ?>>
45
+ Remove Emoji <span class="note-info">[ Removes the default emoji scripts and styles that come with WordPress ]</span></label>
46
+ <br />
47
+
48
+ </fieldset></td>
49
+ </tr>
50
+
51
+ </tbody>
52
+ </table>
53
+
54
+
55
+
56
+
57
+
58
+ <div style="height: 60px;"></div>
59
+ <h2 class="title">CSS Settings</h2>
60
+ <h3 class="fvm-bold-green">Optimize your CSS and Styles settings.</h3>
61
+
62
+ <table class="form-table fvm-settings">
63
+ <tbody>
64
+
65
+ <tr>
66
+ <th scope="row">CSS Options</th>
67
+ <td>
68
+ <p class="fvm-bold-green fvm-rowintro">Select your options below</p>
69
+
70
+ <fieldset>
71
+ <label for="fvm_settings_css_enable">
72
+ <input name="fvm_settings[css][enable]" type="checkbox" id="fvm_settings_css_enable" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'css', 'enable')); ?>>
73
+ Enable CSS Processing <span class="note-info">[ Will enable processing for the settings below ]</span></label>
74
+ <br />
75
+
76
+ <label for="fvm_settings_css_min_disable">
77
+ <input name="fvm_settings[css][min_disable]" type="checkbox" id="fvm_settings_css_min_disable" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'css', 'min_disable')); ?>>
78
+ Disable CSS Minification <span class="note-info">[ Will allow merging but without CSS minification for testing purposes ]</span></label>
79
+ <br />
80
+
81
+ <label for="fvm_settings_css_noprint">
82
+ <input name="fvm_settings[css][noprint]" type="checkbox" id="fvm_settings_css_noprint" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'css', 'noprint')); ?>>
83
+ Remove "Print" stylesheets <span class="note-info">[ Will remove CSS files of mediatype "print" from the frontend ]</span></label>
84
+ <br />
85
+
86
+ <label for="fvm_settings_css_fonts">
87
+ <input name="fvm_settings[css][fonts]" type="checkbox" id="fvm_settings_css_fonts" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'css', 'fonts')); ?>>
88
+ Merge Fonts and Icons Separately<span class="note-info">[ Will merge fonts and icons into a separate CSS file ]</span></label>
89
+ <br />
90
+
91
+ <label for="fvm_settings_css_async">
92
+ <input name="fvm_settings[css][async]" type="checkbox" id="fvm_settings_css_async" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'css', 'async')); ?>>
93
+ Load generated CSS files Async <span class="note-info">[ Will load the merged CSS files Async (use your own inline code for the critical path) ]</span></label>
94
+ <br />
95
+
96
+ </fieldset></td>
97
+ </tr>
98
+
99
+ <tr>
100
+ <th scope="row">CSS Ignore List</th>
101
+ <td><fieldset>
102
+ <label for="fvm_settings_css_ignore"><span class="fvm-bold-green fvm-rowintro">Ignore the following CSS URL's</span></label>
103
+ <p><textarea name="fvm_settings[css][ignore]" rows="7" cols="50" id="fvm_settings_css_ignore" class="large-text code" placeholder="ex: /plugins/something/assets/problem.css"><?php echo fvm_get_settings_value($fvm_settings, 'css', 'ignore'); ?></textarea></p>
104
+ <p class="description">[ CSS files are merged and grouped automatically by mediatype, hence you have an option to exclude files. ]</p>
105
+ <p class="description">[ Will match using <code>PHP stripos</code> against the <code>href attribute</code> on the <code>link tag</code> ]</p>
106
+ </fieldset></td>
107
+ </tr>
108
+
109
+ <tr>
110
+ <th scope="row">Remove CSS files</th>
111
+ <td><fieldset>
112
+ <label for="fvm_settings_css_remove"><span class="fvm-bold-green fvm-rowintro">Remove the following CSS files</span></label>
113
+ <p><textarea name="fvm_settings[css][remove]" rows="7" cols="50" id="fvm_settings_css_remove" class="large-text code" placeholder="ex: fonts.googleapis.com"><?php echo fvm_get_settings_value($fvm_settings, 'css', 'remove'); ?></textarea></p>
114
+ <p class="description">[ This will allow you to remove unwanted CSS files by URL path from the frontend ]</p>
115
+ <p class="description">[ Will match using <code>PHP stripos</code> against the <code>href attribute</code> on the <code>link tag</code> ]</p>
116
+ </fieldset></td>
117
+ </tr>
118
+
119
+
120
+ </tbody>
121
+ </table>
122
+
123
+
124
+
125
+ <div style="height: 60px;"></div>
126
+ <h2 class="title">JS Settings</h2>
127
+ <h3 class="fvm-bold-green">In this section, you can optimize your JS files and inline scripts</h3>
128
+
129
+ <table class="form-table fvm-settings">
130
+ <tbody>
131
+
132
+ <tr>
133
+ <th scope="row">JS Options</th>
134
+ <td>
135
+ <p class="fvm-bold-green fvm-rowintro">Select your options below</p>
136
+
137
+ <fieldset>
138
+ <label for="fvm_settings_css_enable">
139
+ <input name="fvm_settings[js][enable]" type="checkbox" id="fvm_settings_css_enable" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'js', 'enable')); ?>>
140
+ Enable JS Processing <span class="note-info">[ Will enable processing for the settings below ]</span></label>
141
+ <br />
142
+
143
+ <label for="fvm_settings_js_min_disable">
144
+ <input name="fvm_settings[js][min_disable]" type="checkbox" id="fvm_settings_js_min_disable" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'js', 'min_disable')); ?>>
145
+ Disable JS Minification <span class="note-info">[ Will disable JS minification (merge only) for testing purposes ]</span></label>
146
+ <br />
147
+
148
+ <label for="fvm_settings_js_jqupgrade">
149
+ <input name="fvm_settings[js][jqupgrade]" type="checkbox" id="fvm_settings_js_jqupgrade" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'js', 'jqupgrade')); ?>>
150
+ Upgrade to jQuery 3 <span class="note-info">[ Will use jQuery 3.5.1 and jQuery Migrate 3.3.1 from Cloudflare (if enqueued) ]</span></label>
151
+ <br />
152
+
153
+ </fieldset></td>
154
+ </tr>
155
+
156
+
157
+
158
+
159
+ <tr>
160
+ <th scope="row">Merge render blocking JS files in the header</th>
161
+ <td><fieldset>
162
+ <label for="fvm_settings_merge_header"><span class="fvm-bold-green fvm-rowintro">This will merge and render block all JS files that match the paths below</span></label>
163
+ <p><textarea name="fvm_settings[js][merge_header]" rows="7" cols="50" id="fvm_settings_js_merge_header" class="large-text code" placeholder="--- suggested ---
164
+
165
+ /jquery-migrate.js
166
+ /jquery.js
167
+ /jquery.min.js"><?php echo fvm_get_settings_value($fvm_settings, 'js', 'merge_header'); ?></textarea></p>
168
+ <p class="description">[ One possible match per line, after minification and processing, as seen on the frontend. ]</p>
169
+ <p class="description">[ Will match using <code>PHP stripos</code> against the script <code>src attribute</code> ]</p>
170
+ </fieldset></td>
171
+ </tr>
172
+
173
+ <tr>
174
+ <th scope="row">Merge and Defer Scripts</th>
175
+ <td><fieldset>
176
+ <label for="fvm_settings_merge_defer"><span class="fvm-bold-green fvm-rowintro">This will merge and defer all JS files that match the paths below</span></label>
177
+ <p><textarea name="fvm_settings[js][merge_defer]" rows="7" cols="50" id="fvm_settings_js_merge_defer" class="large-text code" placeholder="--- example ---
178
+
179
+ /wp-admin/
180
+ /wp-includes/
181
+ /wp-content/"><?php echo fvm_get_settings_value($fvm_settings, 'js', 'merge_defer'); ?></textarea></p>
182
+ <p class="description">[ One possible match per line, after minification and processing, as seen on the frontend. ]</p>
183
+ <p class="description">[ Will match using <code>PHP stripos</code> against the script <code>src attribute</code> ]</p>
184
+ </fieldset></td>
185
+ </tr>
186
+
187
+ <tr>
188
+ <th scope="row">Inline JavaScript Dependencies</th>
189
+ <td><fieldset>
190
+ <label for="fvm_settings_defer_dependencies"><span class="fvm-bold-green fvm-rowintro">Delay Inline JavaScript until after the deferred scripts merged above finish loading</span></label>
191
+ <p><textarea name="fvm_settings[js][defer_dependencies]" rows="7" cols="50" id="fvm_settings_js_defer_dependencies" class="large-text code" placeholder="--- a small, partial snippet that should match on inline scripts and load them after the deferred scripts above ---"><?php echo fvm_get_settings_value($fvm_settings, 'js', 'defer_dependencies'); ?></textarea></p>
192
+ <p class="description">[ Inline JavaScript matching these rules, will wait until after the window.load event ]</p>
193
+ <p class="description">[ Will match using <code>PHP stripos</code> against the script <code>innerHTML</code> ]</p>
194
+ </fieldset></td>
195
+ </tr>
196
+
197
+ <tr>
198
+ <th scope="row">Execute matching third party scripts after user interaction</th>
199
+ <td><fieldset>
200
+ <label for="fvm_settings_js_thirdparty"><span class="fvm-bold-green fvm-rowintro">Delay the following inline scripts until after user interaction</span></label>
201
+ <p><textarea name="fvm_settings[js][thirdparty]" rows="7" cols="50" id="fvm_settings_js_thirdparty" class="large-text code" placeholder="--- example ---
202
+
203
+ function(w,d,s,l,i)
204
+ function(f,b,e,v,n,t,s)
205
+ function(h,o,t,j,a,r)
206
+ www.googletagmanager.com/gtm.js"><?php echo fvm_get_settings_value($fvm_settings, 'js', 'thirdparty'); ?></textarea></p>
207
+ <p class="description">[ If there is no interaction from the user, scripts will still load after 5 seconds automatically. ]</p>
208
+ <p class="description">[ Will match using <code>PHP stripos</code> against the script <code>innerHTML</code> or <code>src</code> attribute for async/defer scripts (only) ]</p>
209
+ </fieldset></td>
210
+ </tr>
211
+
212
+ <tr>
213
+ <th scope="row">Remove JavaScript Scripts</th>
214
+ <td><fieldset>
215
+ <label for="fvm_settings_js_remove"><span class="fvm-bold-green fvm-rowintro">Remove the following JS files or Inline Scripts</span></label>
216
+ <p><textarea name="fvm_settings[js][remove]" rows="7" cols="50" id="fvm_settings_js_remove" class="large-text code" placeholder="--- should be empty in most cases ---"><?php echo fvm_get_settings_value($fvm_settings, 'js', 'remove'); ?></textarea></p>
217
+ <p class="description">[ This will allow you to remove unwanted script tags from the frontend ]</p>
218
+ <p class="description">[ Will match using <code>PHP stripos</code> against the script <code>outerHTML</code> ]</p>
219
+ </fieldset></td>
220
+ </tr>
221
+
222
+
223
+ </tbody>
224
+ </table>
225
+
226
+
227
+
228
+ <div style="height: 60px;"></div>
229
+ <h2 class="title">CDN Settings</h2>
230
+ <h3 class="fvm-bold-green">If your CDN provider gives you a different URL for your assets, you can use it here</h3>
231
+ <table class="form-table fvm-settings">
232
+ <tbody>
233
+ <tr>
234
+ <th scope="row">CDN Options</th>
235
+ <td>
236
+ <p class="fvm-bold-green fvm-rowintro">Select your options below</p>
237
+
238
+ <fieldset>
239
+ <label for="fvm_settings_cdn_enable">
240
+ <input name="fvm_settings[cdn][enable]" type="checkbox" id="fvm_settings_cdn_enable" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'cdn', 'enable')); ?>>
241
+ Enable CDN Processing <span class="note-info">[ Will enable processing for the settings below ]</span></label>
242
+ <br />
243
+
244
+ <label for="fvm_settings_cdn_cssok">
245
+ <input name="fvm_settings[cdn][cssok]" type="checkbox" id="fvm_settings_cdn_cssok" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'cdn', 'cssok')); ?>>
246
+ Enable CDN for merged CSS files <span class="note-info">[ Will serve the FVM generated CSS files from the CDN ]</span></label>
247
+ <br />
248
+
249
+ <label for="fvm_settings_cdn_jsok">
250
+ <input name="fvm_settings[cdn][jsok]" type="checkbox" id="fvm_settings_cdn_jsok" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'cdn', 'jsok')); ?>>
251
+ Enable CDN for merged JS files <span class="note-info">[ Will serve the FVM generated JS files from the CDN ]</span></label>
252
+ <br />
253
+
254
+ </fieldset></td>
255
+ </tr>
256
+ <tr>
257
+ <th scope="row"><span class="fvm-label-special">CDN URL</span></th>
258
+ <td><fieldset>
259
+ <label for="fvm_settings_cdn_domain">
260
+ <p><input type="text" name="fvm_settings[cdn][domain]" id="fvm_settings_cdn_domain" value="<?php echo fvm_get_settings_value($fvm_settings, 'cdn', 'domain'); ?>" size="80" /></p>
261
+ <p class="description">[ Not needed for Cloudflare or same domain reverse proxy cdn services. ]</p>
262
+ </label>
263
+ <br />
264
+ </fieldset></td>
265
+ </tr>
266
+ <tr>
267
+ <th scope="row">CDN Integration</th>
268
+ <td><fieldset>
269
+ <label for="fvm_settings_cdn_integration"><span class="fvm-bold-green fvm-rowintro">Replace the following elements</span></label>
270
+ <p><textarea name="fvm_settings[cdn][integration]" rows="7" cols="50" id="fvm_settings_cdn_integration" class="large-text code" placeholder="--- check the help section for suggestions ---"><?php echo fvm_get_settings_value($fvm_settings, 'cdn', 'integration'); ?></textarea></p>
271
+ <p class="description">[ Uses syntax from <code>https://simplehtmldom.sourceforge.io/manual.htm</code> ]</p>
272
+ <p class="description">[ You can target a child of a specific html tag, an element with a specific attribute, class or id. ]</p>
273
+ </fieldset></td>
274
+ </tr>
275
+ </tbody></table>
276
+
277
+
278
+
279
+ <div style="height: 60px;"></div>
280
+ <h2 class="title">Cache Settings</h2>
281
+ <h3 class="fvm-bold-green">FVM does not have page caching, so these settings are for the generated CSS and JS files only</h3>
282
+ <table class="form-table fvm-settings">
283
+ <tbody>
284
+
285
+ <tr>
286
+ <th scope="row">Cache Preferences</th>
287
+ <td>
288
+ <p class="fvm-bold-green fvm-rowintro">Select your options below</p>
289
+
290
+ <fieldset>
291
+ <label for="fvm_settings_cache_min_instant_purge">
292
+ <input name="fvm_settings[cache][min_instant_purge]" type="checkbox" id="fvm_settings_cache_min_instant_purge" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'cache', 'min_instant_purge')); ?>>
293
+ Purge Minified CSS/JS files instantly <span class="note-info">[ Cache files can take up to 24 hours to be deleted by default, for compatibility reasons with certain hosts. ]</span></label>
294
+ <br />
295
+
296
+ </fieldset></td>
297
+ </tr>
298
+ <tr>
299
+ <th scope="row"><span class="fvm-label-special">Public Files Cache Path</span></th>
300
+ <td><fieldset>
301
+ <label for="fvm_settings_cache_path">
302
+ <p><input type="text" name="fvm_settings[cache][path]" id="fvm_settings_cache_path" value="<?php echo fvm_get_settings_value($fvm_settings, 'cache', 'path'); ?>" size="80" /></p>
303
+ <p class="description">[ Current base path: <code><?php echo $fvm_cache_paths['cache_base_dir']; ?></code> ]</p>
304
+ </label>
305
+ <br />
306
+ </fieldset></td>
307
+ </tr>
308
+ <tr>
309
+ <th scope="row"><span class="fvm-label-special">Public Files Cache URL</span></th>
310
+ <td><fieldset>
311
+ <label for="fvm_settings_cache_url">
312
+ <p><input type="text" name="fvm_settings[cache][url]" id="fvm_settings_cache_url" value="<?php echo fvm_get_settings_value($fvm_settings, 'cache', 'url'); ?>" size="80" /></p>
313
+ <p class="description">[ Current base url: <code><?php echo $fvm_cache_paths['cache_base_dirurl']; ?></code> ]</p>
314
+ </label>
315
+ <br />
316
+ </fieldset></td>
317
+ </tr>
318
+ </tbody></table>
319
+
320
+
321
+ <div style="height: 60px;"></div>
322
+ <h2 class="title">User Settings</h2>
323
+ <h3 class="fvm-bold-green">For compatibility reasons, only anonymous users should be optimized by default.</h3>
324
+ <table class="form-table fvm-settings">
325
+ <tbody>
326
+
327
+ <tr>
328
+ <th scope="row">User Options</th>
329
+ <td>
330
+ <p class="fvm-bold-green fvm-rowintro">Force optimization for the following user roles</p>
331
+
332
+ <fieldset>
333
+ <?php
334
+ # output user roles checkboxes
335
+ echo fvm_get_user_roles_checkboxes();
336
+ ?>
337
+ </fieldset></td>
338
+ </tbody></table>
339
+
340
+
341
+ <input type="hidden" name="fvm_action" value="save_settings" />
342
+ <p class="submit"><input type="submit" class="button button-primary" value="Save Changes"></p>
343
+
344
+ </form>
345
+ </div>
346
+ <?php
347
+ }
layout/admin-layout-status.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ # server info
4
+ if( $at == 'status' ) {
5
+ ?>
6
+ <div class="fvm-wrapper">
7
+
8
+ <div id="status">
9
+ <h2 class="title">Cache Stats</h2>
10
+ <h3 class="fvm-cache-stats fvm-bold-green"></h3>
11
+
12
+
13
+ <div style="height: 40px;"></div>
14
+ <h2 class="title">CSS Logs</h2>
15
+ <h3 class="fvm-bold-green">In this section, you can check the latest CSS merging logs</h3>
16
+ <textarea rows="10" cols="50" class="large-text code row-log log-css" disabled></textarea>
17
+
18
+
19
+ <div style="height: 40px;"></div>
20
+ <h2 class="title">JS Logs</h2>
21
+ <h3 class="fvm-bold-green">In this section, you can check the latest JS merging logs</h3>
22
+ <textarea rows="10" cols="50" class="large-text code row-log log-js" disabled></textarea>
23
+ <div style="height: 20px;"></div>
24
+
25
+
26
+ <div style="height: 40px;"></div>
27
+ <h2 class="title">Server Info</h2>
28
+ <h3 class="fvm-bold-green">In this section, you can check some server stats and information</h3>
29
+ <textarea rows="10" cols="50" class="large-text code row-log" disabled><?php fvm_get_generalinfo(); ?></textarea>
30
+
31
+
32
+ <script>fvm_get_logs();</script>
33
+ </div>
34
+
35
+ </div>
36
+ <?php
37
+
38
+ }
layout/admin-layout-upgrade.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( $at == 'upgrade' ) { ?>
2
+
3
+ <div class="fvm-wrapper">
4
+
5
+ <h2 class="title">Upgrade Your Speed</h2>
6
+ <h3 class="fvm-bold-green">Would you like to improve your speed and google scores further?</h3>
7
+
8
+
9
+
10
+ </div>
11
+ <?php
12
+ }
layout/admin-layout.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <h1>Fast Velocity Minify</h1>
3
+ <div style="height: 20px;"></div>
4
+
5
+ <?php
6
+ # get active tab, set default
7
+ $at = isset($_GET['tab']) ? $_GET['tab'] : 'settings';
8
+ ?>
9
+
10
+ <h2 class="nav-tab-wrapper wp-clearfix">
11
+ <a href="?page=fvm" class="nav-tab <?php echo $at == 'settings' ? 'nav-tab-active' : ''; ?>">Settings</a>
12
+ <a href="?page=fvm&tab=status" class="nav-tab <?php echo $at == 'status' ? 'nav-tab-active' : ''; ?>">Status</a>
13
+ <?php /*<a href="?page=fvm&tab=upgrade" class="nav-tab <?php echo $at == 'upgrade' ? 'nav-tab-active' : ''; ?>">Upgrade</a>*/ ?>
14
+ <a href="?page=fvm&tab=help" class="nav-tab <?php echo $at == 'help' ? 'nav-tab-active' : ''; ?>">Help</a>
15
+ </h2>
16
+
17
+ <div id="fvm">
18
+
19
+ <?php
20
+ # settings
21
+ include_once($fvm_var_dir_path . 'layout' . DIRECTORY_SEPARATOR . 'admin-layout-settings.php');
22
+
23
+ # include other tabs
24
+ include_once($fvm_var_dir_path . 'layout' . DIRECTORY_SEPARATOR . 'admin-layout-status.php');
25
+ #include_once($fvm_var_dir_path . 'layout' . DIRECTORY_SEPARATOR . 'admin-layout-upgrade.php');
26
+ include_once($fvm_var_dir_path . 'layout' . DIRECTORY_SEPARATOR . 'admin-layout-help.php');
27
+ ?>
28
+
29
+ </div>
libs/matthiasmullie/info.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+
2
+ # replace
3
+ namespace MatthiasMullie > namespace FVM\MatthiasMullie
4
+ use MatthiasMullie > use FVM\MatthiasMullie
libs/matthiasmullie/minify/bin/minifycss CHANGED
@@ -1,45 +1,45 @@
1
- #!/usr/bin/env php
2
- <?php
3
- use MatthiasMullie\Minify;
4
-
5
- // command line utility to minify CSS
6
- if (file_exists(__DIR__ . '/../../../autoload.php')) {
7
- // if composer install
8
- require_once __DIR__ . '/../../../autoload.php';
9
- } else {
10
- require_once __DIR__ . '/../src/Minify.php';
11
- require_once __DIR__ . '/../src/CSS.php';
12
- require_once __DIR__ . '/../src/Exception.php';
13
- }
14
-
15
- error_reporting(E_ALL);
16
- // check PHP setup for cli arguments
17
- if (!isset($_SERVER['argv']) && !isset($argv)) {
18
- fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
19
- exit(1);
20
- } elseif (!isset($argv)) {
21
- $argv = $_SERVER['argv'];
22
- }
23
- // check if path to file given
24
- if (!isset($argv[1])) {
25
- fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
26
- exit(1);
27
- }
28
- // check if script run in cli environment
29
- if ('cli' !== php_sapi_name()) {
30
- fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
31
- exit(1);
32
- }
33
- // check if source file exists
34
- if (!file_exists($argv[1])) {
35
- fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
36
- exit(1);
37
- }
38
-
39
- try {
40
- $minifier = new Minify\CSS($argv[1]);
41
- echo $minifier->minify();
42
- } catch (Exception $e) {
43
- fwrite(STDERR, $e->getMessage(), PHP_EOL);
44
- exit(1);
45
- }
1
+ #!/usr/bin/env php
2
+ <?php
3
+ use MatthiasMullie\Minify;
4
+
5
+ // command line utility to minify CSS
6
+ if (file_exists(__DIR__ . '/../../../autoload.php')) {
7
+ // if composer install
8
+ require_once __DIR__ . '/../../../autoload.php';
9
+ } else {
10
+ require_once __DIR__ . '/../src/Minify.php';
11
+ require_once __DIR__ . '/../src/CSS.php';
12
+ require_once __DIR__ . '/../src/Exception.php';
13
+ }
14
+
15
+ error_reporting(E_ALL);
16
+ // check PHP setup for cli arguments
17
+ if (!isset($_SERVER['argv']) && !isset($argv)) {
18
+ fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
19
+ exit(1);
20
+ } elseif (!isset($argv)) {
21
+ $argv = $_SERVER['argv'];
22
+ }
23
+ // check if path to file given
24
+ if (!isset($argv[1])) {
25
+ fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
26
+ exit(1);
27
+ }
28
+ // check if script run in cli environment
29
+ if ('cli' !== php_sapi_name()) {
30
+ fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
31
+ exit(1);
32
+ }
33
+ // check if source file exists
34
+ if (!file_exists($argv[1])) {
35
+ fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
36
+ exit(1);
37
+ }
38
+
39
+ try {
40
+ $minifier = new Minify\CSS($argv[1]);
41
+ echo $minifier->minify();
42
+ } catch (Exception $e) {
43
+ fwrite(STDERR, $e->getMessage(), PHP_EOL);
44
+ exit(1);
45
+ }
libs/matthiasmullie/minify/bin/minifyjs CHANGED
@@ -1,45 +1,45 @@
1
- #!/usr/bin/env php
2
- <?php
3
- use MatthiasMullie\Minify;
4
-
5
- // command line utility to minify JS
6
- if (file_exists(__DIR__ . '/../../../autoload.php')) {
7
- // if composer install
8
- require_once __DIR__ . '/../../../autoload.php';
9
- } else {
10
- require_once __DIR__ . '/../src/Minify.php';
11
- require_once __DIR__ . '/../src/JS.php';
12
- require_once __DIR__ . '/../src/Exception.php';
13
- }
14
-
15
- error_reporting(E_ALL);
16
- // check PHP setup for cli arguments
17
- if (!isset($_SERVER['argv']) && !isset($argv)) {
18
- fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
19
- exit(1);
20
- } elseif (!isset($argv)) {
21
- $argv = $_SERVER['argv'];
22
- }
23
- // check if path to file given
24
- if (!isset($argv[1])) {
25
- fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
26
- exit(1);
27
- }
28
- // check if script run in cli environment
29
- if ('cli' !== php_sapi_name()) {
30
- fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
31
- exit(1);
32
- }
33
- // check if source file exists
34
- if (!file_exists($argv[1])) {
35
- fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
36
- exit(1);
37
- }
38
-
39
- try {
40
- $minifier = new Minify\JS($argv[1]);
41
- echo $minifier->minify();
42
- } catch (Exception $e) {
43
- fwrite(STDERR, $e->getMessage(), PHP_EOL);
44
- exit(1);
45
- }
1
+ #!/usr/bin/env php
2
+ <?php
3
+ use MatthiasMullie\Minify;
4
+
5
+ // command line utility to minify JS
6
+ if (file_exists(__DIR__ . '/../../../autoload.php')) {
7
+ // if composer install
8
+ require_once __DIR__ . '/../../../autoload.php';
9
+ } else {
10
+ require_once __DIR__ . '/../src/Minify.php';
11
+ require_once __DIR__ . '/../src/JS.php';
12
+ require_once __DIR__ . '/../src/Exception.php';
13
+ }
14
+
15
+ error_reporting(E_ALL);
16
+ // check PHP setup for cli arguments
17
+ if (!isset($_SERVER['argv']) && !isset($argv)) {
18
+ fwrite(STDERR, 'Please enable the "register_argc_argv" directive in your php.ini' . PHP_EOL);
19
+ exit(1);
20
+ } elseif (!isset($argv)) {
21
+ $argv = $_SERVER['argv'];
22
+ }
23
+ // check if path to file given
24
+ if (!isset($argv[1])) {
25
+ fwrite(STDERR, 'Argument expected: path to file' . PHP_EOL);
26
+ exit(1);
27
+ }
28
+ // check if script run in cli environment
29
+ if ('cli' !== php_sapi_name()) {
30
+ fwrite(STDERR, $argv[1] . ' must be run in the command line' . PHP_EOL);
31
+ exit(1);
32
+ }
33
+ // check if source file exists
34
+ if (!file_exists($argv[1])) {
35
+ fwrite(STDERR, 'Source file "' . $argv[1] . '" not found' . PHP_EOL);
36
+ exit(1);
37
+ }
38
+
39
+ try {
40
+ $minifier = new Minify\JS($argv[1]);
41
+ echo $minifier->minify();
42
+ } catch (Exception $e) {
43
+ fwrite(STDERR, $e->getMessage(), PHP_EOL);
44
+ exit(1);
45
+ }
libs/matthiasmullie/minify/data/js/keywords_after.txt CHANGED
@@ -1,7 +1,7 @@
1
- in
2
- public
3
- extends
4
- private
5
- protected
6
- implements
7
  instanceof
1
+ in
2
+ public
3
+ extends
4
+ private
5
+ protected
6
+ implements
7
  instanceof
libs/matthiasmullie/minify/data/js/keywords_before.txt CHANGED
@@ -1,26 +1,26 @@
1
- do
2
- in
3
- let
4
- new
5
- var
6
- case
7
- else
8
- enum
9
- void
10
- with
11
- class
12
- const
13
- yield
14
- delete
15
- export
16
- import
17
- public
18
- static
19
- typeof
20
- extends
21
- package
22
- private
23
- function
24
- protected
25
- implements
26
  instanceof
1
+ do
2
+ in
3
+ let
4
+ new
5
+ var
6
+ case
7
+ else
8
+ enum
9
+ void
10
+ with
11
+ class
12
+ const
13
+ yield
14
+ delete
15
+ export
16
+ import
17
+ public
18
+ static
19
+ typeof
20
+ extends
21
+ package
22
+ private
23
+ function
24
+ protected
25
+ implements
26
  instanceof
libs/matthiasmullie/minify/data/js/keywords_reserved.txt CHANGED
@@ -1,63 +1,63 @@
1
- do
2
- if
3
- in
4
- for
5
- let
6
- new
7
- try
8
- var
9
- case
10
- else
11
- enum
12
- eval
13
- null
14
- this
15
- true
16
- void
17
- with
18
- break
19
- catch
20
- class
21
- const
22
- false
23
- super
24
- throw
25
- while
26
- yield
27
- delete
28
- export
29
- import
30
- public
31
- return
32
- static
33
- switch
34
- typeof
35
- default
36
- extends
37
- finally
38
- package
39
- private
40
- continue
41
- debugger
42
- function
43
- arguments
44
- interface
45
- protected
46
- implements
47
- instanceof
48
- abstract
49
- boolean
50
- byte
51
- char
52
- double
53
- final
54
- float
55
- goto
56
- int
57
- long
58
- native
59
- short
60
- synchronized
61
- throws
62
- transient
63
  volatile
1
+ do
2
+ if
3
+ in
4
+ for
5
+ let
6
+ new
7
+ try
8
+ var
9
+ case
10
+ else
11
+ enum
12
+ eval
13
+ null
14
+ this
15
+ true
16
+ void
17
+ with
18
+ break
19
+ catch
20
+ class
21
+ const
22
+ false
23
+ super
24
+ throw
25
+ while
26
+ yield
27
+ delete
28
+ export
29
+ import
30
+ public
31
+ return
32
+ static
33
+ switch
34
+ typeof
35
+ default
36
+ extends
37
+ finally
38
+ package
39
+ private
40
+ continue
41
+ debugger
42
+ function
43
+ arguments
44
+ interface
45
+ protected
46
+ implements
47
+ instanceof
48
+ abstract
49
+ boolean
50
+ byte
51
+ char
52
+ double
53
+ final
54
+ float
55
+ goto
56
+ int
57
+ long
58
+ native
59
+ short
60
+ synchronized
61
+ throws
62
+ transient
63
  volatile
libs/matthiasmullie/minify/data/js/operators.txt CHANGED
@@ -1,46 +1,46 @@
1
- +
2
- -
3
- *
4
- /
5
- %
6
- =
7
- +=
8
- -=
9
- *=
10
- /=
11
- %=
12
- <<=
13
- >>=
14
- >>>=
15
- &=
16
- ^=
17
- |=
18
- &
19
- |
20
- ^
21
- ~
22
- <<
23
- >>
24
- >>>
25
- ==
26
- ===
27
- !=
28
- !==
29
- >
30
- <
31
- >=
32
- <=
33
- &&
34
- ||
35
- !
36
- .
37
- [
38
- ]
39
- ?
40
- :
41
- ,
42
- ;
43
- (
44
- )
45
- {
46
  }
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ ~
22
+ <<
23
+ >>
24
+ >>>
25
+ ==
26
+ ===
27
+ !=
28
+ !==
29
+ >
30
+ <
31
+ >=
32
+ <=
33
+ &&
34
+ ||
35
+ !
36
+ .
37
+ [
38
+ ]
39
+ ?
40
+ :
41
+ ,
42
+ ;
43
+ (
44
+ )
45
+ {
46
  }
libs/matthiasmullie/minify/data/js/operators_after.txt CHANGED
@@ -1,43 +1,43 @@
1
- +
2
- -
3
- *
4
- /
5
- %
6
- =
7
- +=
8
- -=
9
- *=
10
- /=
11
- %=
12
- <<=
13
- >>=
14
- >>>=
15
- &=
16
- ^=
17
- |=
18
- &
19
- |
20
- ^
21
- <<
22
- >>
23
- >>>
24
- ==
25
- ===
26
- !=
27
- !==
28
- >
29
- <
30
- >=
31
- <=
32
- &&
33
- ||
34
- .
35
- [
36
- ]
37
- ?
38
- :
39
- ,
40
- ;
41
- (
42
- )
43
  }
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ <<
22
+ >>
23
+ >>>
24
+ ==
25
+ ===
26
+ !=
27
+ !==
28
+ >
29
+ <
30
+ >=
31
+ <=
32
+ &&
33
+ ||
34
+ .
35
+ [
36
+ ]
37
+ ?
38
+ :
39
+ ,
40
+ ;
41
+ (
42
+ )
43
  }
libs/matthiasmullie/minify/data/js/operators_before.txt CHANGED
@@ -1,43 +1,43 @@
1
- +
2
- -
3
- *
4
- /
5
- %
6
- =
7
- +=
8
- -=
9
- *=
10
- /=
11
- %=
12
- <<=
13
- >>=
14
- >>>=
15
- &=
16
- ^=
17
- |=
18
- &
19
- |
20
- ^
21
- ~
22
- <<
23
- >>
24
- >>>
25
- ==
26
- ===
27
- !=
28
- !==
29
- >
30
- <
31
- >=
32
- <=
33
- &&
34
- ||
35
- !
36
- .
37
- [
38
- ?
39
- :
40
- ,
41
- ;
42
- (
43
- {
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ ~
22
+ <<
23
+ >>
24
+ >>>
25
+ ==
26
+ ===
27
+ !=
28
+ !==
29
+ >
30
+ <
31
+ >=
32
+ <=
33
+ &&
34
+ ||
35
+ !
36
+ .
37
+ [
38
+ ?
39
+ :
40
+ ,
41
+ ;
42
+ (
43
+ {
libs/matthiasmullie/minify/src/CSS.php CHANGED
@@ -1,751 +1,752 @@
1
- <?php
2
- /**
3
- * CSS Minifier
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
-
12
- namespace MatthiasMullie\Minify;
13
-
14
- use MatthiasMullie\Minify\Exceptions\FileImportException;
15
- use MatthiasMullie\PathConverter\ConverterInterface;
16
- use MatthiasMullie\PathConverter\Converter;
17
-
18
- /**
19
- * CSS minifier
20
- *
21
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
22
- *
23
- * @package Minify
24
- * @author Matthias Mullie <minify@mullie.eu>
25
- * @author Tijs Verkoyen <minify@verkoyen.eu>
26
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
27
- * @license MIT License
28
- */
29
- class CSS extends Minify
30
- {
31
- /**
32
- * @var int maximum inport size in kB
33
- */
34
- protected $maxImportSize = 5;
35
-
36
- /**
37
- * @var string[] valid import extensions
38
- */
39
- protected $importExtensions = array(
40
- 'gif' => 'data:image/gif',
41
- 'png' => 'data:image/png',
42
- 'jpe' => 'data:image/jpeg',
43
- 'jpg' => 'data:image/jpeg',
44
- 'jpeg' => 'data:image/jpeg',
45
- 'svg' => 'data:image/svg+xml',
46
- 'woff' => 'data:application/x-font-woff',
47
- 'tif' => 'image/tiff',
48
- 'tiff' => 'image/tiff',
49
- 'xbm' => 'image/x-xbitmap',
50
- );
51
-
52
- /**
53
- * Set the maximum size if files to be imported.
54
- *
55
- * Files larger than this size (in kB) will not be imported into the CSS.
56
- * Importing files into the CSS as data-uri will save you some connections,
57
- * but we should only import relatively small decorative images so that our
58
- * CSS file doesn't get too bulky.
59
- *
60
- * @param int $size Size in kB
61
- */
62
- public function setMaxImportSize($size)
63
- {
64
- $this->maxImportSize = $size;
65
- }
66
-
67
- /**
68
- * Set the type of extensions to be imported into the CSS (to save network
69
- * connections).
70
- * Keys of the array should be the file extensions & respective values
71
- * should be the data type.
72
- *
73
- * @param string[] $extensions Array of file extensions
74
- */
75
- public function setImportExtensions(array $extensions)
76
- {
77
- $this->importExtensions = $extensions;
78
- }
79
-
80
- /**
81
- * Move any import statements to the top.
82
- *
83
- * @param string $content Nearly finished CSS content
84
- *
85
- * @return string
86
- */
87
- protected function moveImportsToTop($content)
88
- {
89
- if (preg_match_all('/(;?)(@import (?<url>url\()?(?P<quotes>["\']?).+?(?P=quotes)(?(url)\)));?/', $content, $matches)) {
90
- // remove from content
91
- foreach ($matches[0] as $import) {
92
- $content = str_replace($import, '', $content);
93
- }
94
-
95
- // add to top
96
- $content = implode(';', $matches[2]).';'.trim($content, ';');
97
- }
98
-
99
- return $content;
100
- }
101
-
102
- /**
103
- * Combine CSS from import statements.
104
- *
105
- * @import's will be loaded and their content merged into the original file,
106
- * to save HTTP requests.
107
- *
108
- * @param string $source The file to combine imports for
109
- * @param string $content The CSS content to combine imports for
110
- * @param string[] $parents Parent paths, for circular reference checks
111
- *
112
- * @return string
113
- *
114
- * @throws FileImportException
115
- */
116
- protected function combineImports($source, $content, $parents)
117
- {
118
- $importRegexes = array(
119
- // @import url(xxx)
120
- '/
121
- # import statement
122
- @import
123
-
124
- # whitespace
125
- \s+
126
-
127
- # open url()
128
- url\(
129
-
130
- # (optional) open path enclosure
131
- (?P<quotes>["\']?)
132
-
133
- # fetch path
134
- (?P<path>.+?)
135
-
136
- # (optional) close path enclosure
137
- (?P=quotes)
138
-
139
- # close url()
140
- \)
141
-
142
- # (optional) trailing whitespace
143
- \s*
144
-
145
- # (optional) media statement(s)
146
- (?P<media>[^;]*)
147
-
148
- # (optional) trailing whitespace
149
- \s*
150
-
151
- # (optional) closing semi-colon
152
- ;?
153
-
154
- /ix',
155
-
156
- // @import 'xxx'
157
- '/
158
-
159
- # import statement
160
- @import
161
-
162
- # whitespace
163
- \s+
164
-
165
- # open path enclosure
166
- (?P<quotes>["\'])
167
-
168
- # fetch path
169
- (?P<path>.+?)
170
-
171
- # close path enclosure
172
- (?P=quotes)
173
-
174
- # (optional) trailing whitespace
175
- \s*
176
-
177
- # (optional) media statement(s)
178
- (?P<media>[^;]*)
179
-
180
- # (optional) trailing whitespace
181
- \s*
182
-
183
- # (optional) closing semi-colon
184
- ;?
185
-
186
- /ix',
187
- );
188
-
189
- // find all relative imports in css
190
- $matches = array();
191
- foreach ($importRegexes as $importRegex) {
192
- if (preg_match_all($importRegex, $content, $regexMatches, PREG_SET_ORDER)) {
193
- $matches = array_merge($matches, $regexMatches);
194
- }
195
- }
196
-
197
- $search = array();
198
- $replace = array();
199
-
200
- // loop the matches
201
- foreach ($matches as $match) {
202
- // get the path for the file that will be imported
203
- $importPath = dirname($source).'/'.$match['path'];
204
-
205
- // only replace the import with the content if we can grab the
206
- // content of the file
207
- if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) {
208
- continue;
209
- }
210
-
211
- // check if current file was not imported previously in the same
212
- // import chain.
213
- if (in_array($importPath, $parents)) {
214
- throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.');
215
- }
216
-
217
- // grab referenced file & minify it (which may include importing
218
- // yet other @import statements recursively)
219
- $minifier = new static($importPath);
220
- $minifier->setMaxImportSize($this->maxImportSize);
221
- $minifier->setImportExtensions($this->importExtensions);
222
- $importContent = $minifier->execute($source, $parents);
223
-
224
- // check if this is only valid for certain media
225
- if (!empty($match['media'])) {
226
- $importContent = '@media '.$match['media'].'{'.$importContent.'}';
227
- }
228
-
229
- // add to replacement array
230
- $search[] = $match[0];
231
- $replace[] = $importContent;
232
- }
233
-
234
- // replace the import statements
235
- return str_replace($search, $replace, $content);
236
- }
237
-
238
- /**
239
- * Import files into the CSS, base64-ized.
240
- *
241
- * @url(image.jpg) images will be loaded and their content merged into the
242
- * original file, to save HTTP requests.
243
- *
244
- * @param string $source The file to import files for
245
- * @param string $content The CSS content to import files for
246
- *
247
- * @return string
248
- */
249
- protected function importFiles($source, $content)
250
- {
251
- $regex = '/url\((["\']?)(.+?)\\1\)/i';
252
- if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
253
- $search = array();
254
- $replace = array();
255
-
256
- // loop the matches
257
- foreach ($matches as $match) {
258
- $extension = substr(strrchr($match[2], '.'), 1);
259
- if ($extension && !array_key_exists($extension, $this->importExtensions)) {
260
- continue;
261
- }
262
-
263
- // get the path for the file that will be imported
264
- $path = $match[2];
265
- $path = dirname($source).'/'.$path;
266
-
267
- // only replace the import with the content if we're able to get
268
- // the content of the file, and it's relatively small
269
- if ($this->canImportFile($path) && $this->canImportBySize($path)) {
270
- // grab content && base64-ize
271
- $importContent = $this->load($path);
272
- $importContent = base64_encode($importContent);
273
-
274
- // build replacement
275
- $search[] = $match[0];
276
- $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
277
- }
278
- }
279
-
280
- // replace the import statements
281
- $content = str_replace($search, $replace, $content);
282
- }
283
-
284
- return $content;
285
- }
286
-
287
- /**
288
- * Minify the data.
289
- * Perform CSS optimizations.
290
- *
291
- * @param string[optional] $path Path to write the data to
292
- * @param string[] $parents Parent paths, for circular reference checks
293
- *
294
- * @return string The minified data
295
- */
296
- public function execute($path = null, $parents = array())
297
- {
298
- $content = '';
299
-
300
- // loop CSS data (raw data and files)
301
- foreach ($this->data as $source => $css) {
302
- /*
303
- * Let's first take out strings & comments, since we can't just
304
- * remove whitespace anywhere. If whitespace occurs inside a string,
305
- * we should leave it alone. E.g.:
306
- * p { content: "a test" }
307
- */
308
- $this->extractStrings();
309
- $this->stripComments();
310
- $this->extractCalcs();
311
- $css = $this->replace($css);
312
-
313
- $css = $this->stripWhitespace($css);
314
- $css = $this->shortenColors($css);
315
- $css = $this->shortenZeroes($css);
316
- $css = $this->shortenFontWeights($css);
317
- $css = $this->stripEmptyTags($css);
318
-
319
- // restore the string we've extracted earlier
320
- $css = $this->restoreExtractedData($css);
321
-
322
- $source = is_int($source) ? '' : $source;
323
- $parents = $source ? array_merge($parents, array($source)) : $parents;
324
- $css = $this->combineImports($source, $css, $parents);
325
- $css = $this->importFiles($source, $css);
326
-
327
- /*
328
- * If we'll save to a new path, we'll have to fix the relative paths
329
- * to be relative no longer to the source file, but to the new path.
330
- * If we don't write to a file, fall back to same path so no
331
- * conversion happens (because we still want it to go through most
332
- * of the move code, which also addresses url() & @import syntax...)
333
- */
334
- $converter = $this->getPathConverter($source, $path ?: $source);
335
- $css = $this->move($converter, $css);
336
-
337
- // combine css
338
- $content .= $css;
339
- }
340
-
341
- $content = $this->moveImportsToTop($content);
342
-
343
- return $content;
344
- }
345
-
346
- /**
347
- * Moving a css file should update all relative urls.
348
- * Relative references (e.g. ../images/image.gif) in a certain css file,
349
- * will have to be updated when a file is being saved at another location
350
- * (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
351
- *
352
- * @param ConverterInterface $converter Relative path converter
353
- * @param string $content The CSS content to update relative urls for
354
- *
355
- * @return string
356
- */
357
- protected function move(ConverterInterface $converter, $content)
358
- {
359
- /*
360
- * Relative path references will usually be enclosed by url(). @import
361
- * is an exception, where url() is not necessary around the path (but is
362
- * allowed).
363
- * This *could* be 1 regular expression, where both regular expressions
364
- * in this array are on different sides of a |. But we're using named
365
- * patterns in both regexes, the same name on both regexes. This is only
366
- * possible with a (?J) modifier, but that only works after a fairly
367
- * recent PCRE version. That's why I'm doing 2 separate regular
368
- * expressions & combining the matches after executing of both.
369
- */
370
- $relativeRegexes = array(
371
- // url(xxx)
372
- '/
373
- # open url()
374
- url\(
375
-
376
- \s*
377
-
378
- # open path enclosure
379
- (?P<quotes>["\'])?
380
-
381
- # fetch path
382
- (?P<path>.+?)
383
-
384
- # close path enclosure
385
- (?(quotes)(?P=quotes))
386
-
387
- \s*
388
-
389
- # close url()
390
- \)
391
-
392
- /ix',
393
-
394
- // @import "xxx"
395
- '/
396
- # import statement
397
- @import
398
-
399
- # whitespace
400
- \s+
401
-
402
- # we don\'t have to check for @import url(), because the
403
- # condition above will already catch these
404
-
405
- # open path enclosure
406
- (?P<quotes>["\'])
407
-
408
- # fetch path
409
- (?P<path>.+?)
410
-
411
- # close path enclosure
412
- (?P=quotes)
413
-
414
- /ix',
415
- );
416
-
417
- // find all relative urls in css
418
- $matches = array();
419
- foreach ($relativeRegexes as $relativeRegex) {
420
- if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) {
421
- $matches = array_merge($matches, $regexMatches);
422
- }
423
- }
424
-
425
- $search = array();
426
- $replace = array();
427
-
428
- // loop all urls
429
- foreach ($matches as $match) {
430
- // determine if it's a url() or an @import match
431
- $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
432
-
433
- $url = $match['path'];
434
- if ($this->canImportByPath($url)) {
435
- // attempting to interpret GET-params makes no sense, so let's discard them for awhile
436
- $params = strrchr($url, '?');
437
- $url = $params ? substr($url, 0, -strlen($params)) : $url;
438
-
439
- // fix relative url
440
- $url = $converter->convert($url);
441
-
442
- // now that the path has been converted, re-apply GET-params
443
- $url .= $params;
444
- }
445
-
446
- /*
447
- * Urls with control characters above 0x7e should be quoted.
448
- * According to Mozilla's parser, whitespace is only allowed at the
449
- * end of unquoted urls.
450
- * Urls with `)` (as could happen with data: uris) should also be
451
- * quoted to avoid being confused for the url() closing parentheses.
452
- * And urls with a # have also been reported to cause issues.
453
- * Urls with quotes inside should also remain escaped.
454
- *
455
- * @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
456
- * @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
457
- * @see https://github.com/matthiasmullie/minify/issues/193
458
- */
459
- $url = trim($url);
460
- if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) {
461
- $url = $match['quotes'] . $url . $match['quotes'];
462
- }
463
-
464
- // build replacement
465
- $search[] = $match[0];
466
- if ($type === 'url') {
467
- $replace[] = 'url('.$url.')';
468
- } elseif ($type === 'import') {
469
- $replace[] = '@import "'.$url.'"';
470
- }
471
- }
472
-
473
- // replace urls
474
- return str_replace($search, $replace, $content);
475
- }
476
-
477
- /**
478
- * Shorthand hex color codes.
479
- * #FF0000 -> #F00.
480
- *
481
- * @param string $content The CSS content to shorten the hex color codes for
482
- *
483
- * @return string
484
- */
485
- protected function shortenColors($content)
486
- {
487
- $content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?:([0-9a-z])\\4)?(?=[; }])/i', '#$1$2$3$4', $content);
488
-
489
- // remove alpha channel if it's pointless...
490
- $content = preg_replace('/(?<=[: ])#([0-9a-z]{6})ff?(?=[; }])/i', '#$1', $content);
491
- $content = preg_replace('/(?<=[: ])#([0-9a-z]{3})f?(?=[; }])/i', '#$1', $content);
492
-
493
- $colors = array(
494
- // we can shorten some even more by replacing them with their color name
495
- '#F0FFFF' => 'azure',
496
- '#F5F5DC' => 'beige',
497
- '#A52A2A' => 'brown',
498
- '#FF7F50' => 'coral',
499
- '#FFD700' => 'gold',
500
- '#808080' => 'gray',
501
- '#008000' => 'green',
502
- '#4B0082' => 'indigo',
503
- '#FFFFF0' => 'ivory',
504
- '#F0E68C' => 'khaki',
505
- '#FAF0E6' => 'linen',
506
- '#800000' => 'maroon',
507
- '#000080' => 'navy',
508
- '#808000' => 'olive',
509
- '#CD853F' => 'peru',
510
- '#FFC0CB' => 'pink',
511
- '#DDA0DD' => 'plum',
512
- '#800080' => 'purple',
513
- '#F00' => 'red',
514
- '#FA8072' => 'salmon',
515
- '#A0522D' => 'sienna',
516
- '#C0C0C0' => 'silver',
517
- '#FFFAFA' => 'snow',
518
- '#D2B48C' => 'tan',
519
- '#FF6347' => 'tomato',
520
- '#EE82EE' => 'violet',
521
- '#F5DEB3' => 'wheat',
522
- // or the other way around
523
- 'WHITE' => '#fff',
524
- 'BLACK' => '#000',
525
- );
526
-
527
- return preg_replace_callback(
528
- '/(?<=[: ])('.implode('|', array_keys($colors)).')(?=[; }])/i',
529
- function ($match) use ($colors) {
530
- return $colors[strtoupper($match[0])];
531
- },
532
- $content
533
- );
534
- }
535
-
536
- /**
537
- * Shorten CSS font weights.
538
- *
539
- * @param string $content The CSS content to shorten the font weights for
540
- *
541
- * @return string
542
- */
543
- protected function shortenFontWeights($content)
544
- {
545
- $weights = array(
546
- 'normal' => 400,
547
- 'bold' => 700,
548
- );
549
-
550
- $callback = function ($match) use ($weights) {
551
- return $match[1].$weights[$match[2]];
552
- };
553
-
554
- return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
555
- }
556
-
557
- /**
558
- * Shorthand 0 values to plain 0, instead of e.g. -0em.
559
- *
560
- * @param string $content The CSS content to shorten the zero values for
561
- *
562
- * @return string
563
- */
564
- protected function shortenZeroes($content)
565
- {
566
- // we don't want to strip units in `calc()` expressions:
567
- // `5px - 0px` is valid, but `5px - 0` is not
568
- // `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
569
- // `10 * 0` is invalid
570
- // we've extracted calcs earlier, so we don't need to worry about this
571
-
572
- // reusable bits of code throughout these regexes:
573
- // before & after are used to make sure we don't match lose unintended
574
- // 0-like values (e.g. in #000, or in http://url/1.0)
575
- // units can be stripped from 0 values, or used to recognize non 0
576
- // values (where wa may be able to strip a .0 suffix)
577
- $before = '(?<=[:(, ])';
578
- $after = '(?=[ ,);}])';
579
- $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
580
-
581
- // strip units after zeroes (0px -> 0)
582
- // NOTE: it should be safe to remove all units for a 0 value, but in
583
- // practice, Webkit (especially Safari) seems to stumble over at least
584
- // 0%, potentially other units as well. Only stripping 'px' for now.
585
- // @see https://github.com/matthiasmullie/minify/issues/60
586
- $content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
587
-
588
- // strip 0-digits (.0 -> 0)
589
- $content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
590
- // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
591
- $content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
592
- // strip trailing 0: 50.00 -> 50, 50.00px -> 50px
593
- $content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
594
- // strip leading 0: 0.1 -> .1, 01.1 -> 1.1
595
- $content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
596
-
597
- // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
598
- $content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
599
-
600
- // IE doesn't seem to understand a unitless flex-basis value (correct -
601
- // it goes against the spec), so let's add it in again (make it `%`,
602
- // which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
603
- // @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
604
- $content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
605
- $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
606
-
607
- return $content;
608
- }
609
-
610
- /**
611
- * Strip empty tags from source code.
612
- *
613
- * @param string $content
614
- *
615
- * @return string
616
- */
617
- protected function stripEmptyTags($content)
618
- {
619
- $content = preg_replace('/(?<=^)[^\{\};]+\{\s*\}/', '', $content);
620
- $content = preg_replace('/(?<=(\}|;))[^\{\};]+\{\s*\}/', '', $content);
621
-
622
- return $content;
623
- }
624
-
625
- /**
626
- * Strip comments from source code.
627
- */
628
- protected function stripComments()
629
- {
630
- // PHP only supports $this inside anonymous functions since 5.4
631
- $minifier = $this;
632
- $callback = function ($match) use ($minifier) {
633
- $count = count($minifier->extracted);
634
- $placeholder = '/*'.$count.'*/';
635
- $minifier->extracted[$placeholder] = $match[0];
636
-
637
- return $placeholder;
638
- };
639
- $this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
640
-
641
- $this->registerPattern('/\/\*.*?\*\//s', '');
642
- }
643
-
644
- /**
645
- * Strip whitespace.
646
- *
647
- * @param string $content The CSS content to strip the whitespace for
648
- *
649
- * @return string
650
- */
651
- protected function stripWhitespace($content)
652
- {
653
- // remove leading & trailing whitespace
654
- $content = preg_replace('/^\s*/m', '', $content);
655
- $content = preg_replace('/\s*$/m', '', $content);
656
-
657
- // replace newlines with a single space
658
- $content = preg_replace('/\s+/', ' ', $content);
659
-
660
- // remove whitespace around meta characters
661
- // inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
662
- $content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
663
- $content = preg_replace('/([\[(:>\+])\s+/', '$1', $content);
664
- $content = preg_replace('/\s+([\]\)>\+])/', '$1', $content);
665
- $content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
666
-
667
- // whitespace around + and - can only be stripped inside some pseudo-
668
- // classes, like `:nth-child(3+2n)`
669
- // not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
670
- // selectors like `div.weird- p`
671
- $pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
672
- $content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
673
-
674
- // remove semicolon/whitespace followed by closing bracket
675
- $content = str_replace(';}', '}', $content);
676
-
677
- return trim($content);
678
- }
679
-
680
- /**
681
- * Replace all `calc()` occurrences.
682
- */
683
- protected function extractCalcs()
684
- {
685
- // PHP only supports $this inside anonymous functions since 5.4
686
- $minifier = $this;
687
- $callback = function ($match) use ($minifier) {
688
- $length = strlen($match[1]);
689
- $expr = '';
690
- $opened = 0;
691
-
692
- for ($i = 0; $i < $length; $i++) {
693
- $char = $match[1][$i];
694
- $expr .= $char;
695
- if ($char === '(') {
696
- $opened++;
697
- } elseif ($char === ')' && --$opened === 0) {
698
- break;
699
- }
700
- }
701
- $rest = str_replace($expr, '', $match[1]);
702
- $expr = trim(substr($expr, 1, -1));
703
-
704
- $count = count($minifier->extracted);
705
- $placeholder = 'calc('.$count.')';
706
- $minifier->extracted[$placeholder] = 'calc('.$expr.')';
707
-
708
- return $placeholder.$rest;
709
- };
710
-
711
- $this->registerPattern('/calc(\(.+?)(?=$|;|calc\()/', $callback);
712
- }
713
-
714
- /**
715
- * Check if file is small enough to be imported.
716
- *
717
- * @param string $path The path to the file
718
- *
719
- * @return bool
720
- */
721
- protected function canImportBySize($path)
722
- {
723
- return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
724
- }
725
-
726
- /**
727
- * Check if file a file can be imported, going by the path.
728
- *
729
- * @param string $path
730
- *
731
- * @return bool
732
- */
733
- protected function canImportByPath($path)
734
- {
735
- return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
736
- }
737
-
738
- /**
739
- * Return a converter to update relative paths to be relative to the new
740
- * destination.
741
- *
742
- * @param string $source
743
- * @param string $target
744
- *
745
- * @return ConverterInterface
746
- */
747
- protected function getPathConverter($source, $target)
748
- {
749
- return new Converter($source, $target);
750
- }
751
- }
 
1
+ <?php
2
+ /**
3
+ * CSS Minifier
4
+ *
5
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
+ *
7
+ * @author Matthias Mullie <minify@mullie.eu>
8
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
+ * @license MIT License
10
+ */
11
+
12
+ namespace FVM\MatthiasMullie\Minify;
13
+
14
+ use FVM\MatthiasMullie\Minify\Exceptions\FileImportException;
15
+ use FVM\MatthiasMullie\PathConverter\ConverterInterface;
16
+ use FVM\MatthiasMullie\PathConverter\Converter;
17
+
18
+ /**
19
+ * CSS minifier
20
+ *
21
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
22
+ *
23
+ * @package Minify
24
+ * @author Matthias Mullie <minify@mullie.eu>
25
+ * @author Tijs Verkoyen <minify@verkoyen.eu>
26
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
27
+ * @license MIT License
28
+ */
29
+ class CSS extends Minify
30
+ {
31
+ /**
32
+ * @var int maximum inport size in kB
33
+ */
34
+ protected $maxImportSize = 5;
35
+
36
+ /**
37
+ * @var string[] valid import extensions
38
+ */
39
+ protected $importExtensions = array(
40
+ 'gif' => 'data:image/gif',
41
+ 'png' => 'data:image/png',
42
+ 'jpe' => 'data:image/jpeg',
43
+ 'jpg' => 'data:image/jpeg',
44
+ 'jpeg' => 'data:image/jpeg',
45
+ 'svg' => 'data:image/svg+xml',
46
+ 'woff' => 'data:application/x-font-woff',
47
+ 'tif' => 'image/tiff',
48
+ 'tiff' => 'image/tiff',
49
+ 'xbm' => 'image/x-xbitmap',
50
+ );
51
+
52
+ /**
53
+ * Set the maximum size if files to be imported.
54
+ *
55
+ * Files larger than this size (in kB) will not be imported into the CSS.
56
+ * Importing files into the CSS as data-uri will save you some connections,
57
+ * but we should only import relatively small decorative images so that our
58
+ * CSS file doesn't get too bulky.
59
+ *
60
+ * @param int $size Size in kB
61
+ */
62
+ public function setMaxImportSize($size)
63
+ {
64
+ $this->maxImportSize = $size;
65
+ }
66
+
67
+ /**
68
+ * Set the type of extensions to be imported into the CSS (to save network
69
+ * connections).
70
+ * Keys of the array should be the file extensions & respective values
71
+ * should be the data type.
72
+ *
73
+ * @param string[] $extensions Array of file extensions
74
+ */
75
+ public function setImportExtensions(array $extensions)
76
+ {
77
+ $this->importExtensions = $extensions;
78
+ }
79
+
80
+ /**
81
+ * Move any import statements to the top.
82
+ *
83
+ * @param string $content Nearly finished CSS content
84
+ *
85
+ * @return string
86
+ */
87
+ protected function moveImportsToTop($content)
88
+ {
89
+ if (preg_match_all('/(;?)(@import (?<url>url\()?(?P<quotes>["\']?).+?(?P=quotes)(?(url)\)));?/', $content, $matches)) {
90
+ // remove from content
91
+ foreach ($matches[0] as $import) {
92
+ $content = str_replace($import, '', $content);
93
+ }
94
+
95
+ // add to top
96
+ $content = implode(';', $matches[2]).';'.trim($content, ';');
97
+ }
98
+
99
+ return $content;
100
+ }
101
+
102
+ /**
103
+ * Combine CSS from import statements.
104
+ *
105
+ * @import's will be loaded and their content merged into the original file,
106
+ * to save HTTP requests.
107
+ *
108
+ * @param string $source The file to combine imports for
109
+ * @param string $content The CSS content to combine imports for
110
+ * @param string[] $parents Parent paths, for circular reference checks
111
+ *
112
+ * @return string
113
+ *
114
+ * @throws FileImportException
115
+ */
116
+ protected function combineImports($source, $content, $parents)
117
+ {
118
+ $importRegexes = array(
119
+ // @import url(xxx)
120
+ '/
121
+ # import statement
122
+ @import
123
+
124
+ # whitespace
125
+ \s+
126
+
127
+ # open url()
128
+ url\(
129
+
130
+ # (optional) open path enclosure
131
+ (?P<quotes>["\']?)
132
+
133
+ # fetch path
134
+ (?P<path>.+?)
135
+
136
+ # (optional) close path enclosure
137
+ (?P=quotes)
138
+
139
+ # close url()
140
+ \)
141
+
142
+ # (optional) trailing whitespace
143
+ \s*
144
+
145
+ # (optional) media statement(s)
146
+ (?P<media>[^;]*)
147
+
148
+ # (optional) trailing whitespace
149
+ \s*
150
+
151
+ # (optional) closing semi-colon
152
+ ;?
153
+
154
+ /ix',
155
+
156
+ // @import 'xxx'
157
+ '/
158
+
159
+ # import statement
160
+ @import
161
+
162
+ # whitespace
163
+ \s+
164
+
165
+ # open path enclosure
166
+ (?P<quotes>["\'])
167
+
168
+ # fetch path
169
+ (?P<path>.+?)
170
+
171
+ # close path enclosure
172
+ (?P=quotes)
173
+
174
+ # (optional) trailing whitespace
175
+ \s*
176
+
177
+ # (optional) media statement(s)
178
+ (?P<media>[^;]*)
179
+
180
+ # (optional) trailing whitespace
181
+ \s*
182
+
183
+ # (optional) closing semi-colon
184
+ ;?
185
+
186
+ /ix',
187
+ );
188
+
189
+ // find all relative imports in css
190
+ $matches = array();
191
+ foreach ($importRegexes as $importRegex) {
192
+ if (preg_match_all($importRegex, $content, $regexMatches, PREG_SET_ORDER)) {
193
+ $matches = array_merge($matches, $regexMatches);
194
+ }
195
+ }
196
+
197
+ $search = array();
198
+ $replace = array();
199
+
200
+ // loop the matches
201
+ foreach ($matches as $match) {
202
+ // get the path for the file that will be imported
203
+ $importPath = dirname($source).'/'.$match['path'];
204
+
205
+ // only replace the import with the content if we can grab the
206
+ // content of the file
207
+ if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) {
208
+ continue;
209
+ }
210
+
211
+ // check if current file was not imported previously in the same
212
+ // import chain.
213
+ if (in_array($importPath, $parents)) {
214
+ throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.');
215
+ }
216
+
217
+ // grab referenced file & minify it (which may include importing
218
+ // yet other @import statements recursively)
219
+ $minifier = new static($importPath);
220
+ $minifier->setMaxImportSize($this->maxImportSize);
221
+ $minifier->setImportExtensions($this->importExtensions);
222
+ $importContent = $minifier->execute($source, $parents);
223
+
224
+ // check if this is only valid for certain media
225
+ if (!empty($match['media'])) {
226
+ $importContent = '@media '.$match['media'].'{'.$importContent.'}';
227
+ }
228
+
229
+ // add to replacement array
230
+ $search[] = $match[0];
231
+ $replace[] = $importContent;
232
+ }
233
+
234
+ // replace the import statements
235
+ return str_replace($search, $replace, $content);
236
+ }
237
+
238
+ /**
239
+ * Import files into the CSS, base64-ized.
240
+ *
241
+ * @url(image.jpg) images will be loaded and their content merged into the
242
+ * original file, to save HTTP requests.
243
+ *
244
+ * @param string $source The file to import files for
245
+ * @param string $content The CSS content to import files for
246
+ *
247
+ * @return string
248
+ */
249
+ protected function importFiles($source, $content)
250
+ {
251
+ $regex = '/url\((["\']?)(.+?)\\1\)/i';
252
+ if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
253
+ $search = array();
254
+ $replace = array();
255
+
256
+ // loop the matches
257
+ foreach ($matches as $match) {
258
+ $extension = substr(strrchr($match[2], '.'), 1);
259
+ if ($extension && !array_key_exists($extension, $this->importExtensions)) {
260
+ continue;
261
+ }
262
+
263
+ // get the path for the file that will be imported
264
+ $path = $match[2];
265
+ $path = dirname($source).'/'.$path;
266
+
267
+ // only replace the import with the content if we're able to get
268
+ // the content of the file, and it's relatively small
269
+ if ($this->canImportFile($path) && $this->canImportBySize($path)) {
270
+ // grab content && base64-ize
271
+ $importContent = $this->load($path);
272
+ $importContent = base64_encode($importContent);
273
+
274
+ // build replacement
275
+ $search[] = $match[0];
276
+ $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
277
+ }
278
+ }
279
+
280
+ // replace the import statements
281
+ $content = str_replace($search, $replace, $content);
282
+ }
283
+
284
+ return $content;
285
+ }
286
+
287
+ /**
288
+ * Minify the data.
289
+ * Perform CSS optimizations.
290
+ *
291
+ * @param string[optional] $path Path to write the data to
292
+ * @param string[] $parents Parent paths, for circular reference checks
293
+ *
294
+ * @return string The minified data
295
+ */
296
+ public function execute($path = null, $parents = array())
297
+ {
298
+ $content = '';
299
+
300
+ // loop CSS data (raw data and files)
301
+ foreach ($this->data as $source => $css) {
302
+ /*
303
+ * Let's first take out strings & comments, since we can't just
304
+ * remove whitespace anywhere. If whitespace occurs inside a string,
305
+ * we should leave it alone. E.g.:
306
+ * p { content: "a test" }
307
+ */
308
+ $this->extractStrings();
309
+ $this->stripComments();
310
+ $this->extractCalcs();
311
+ $css = $this->replace($css);
312
+
313
+ $css = $this->stripWhitespace($css);
314
+ $css = $this->shortenColors($css);
315
+ $css = $this->shortenZeroes($css);
316
+ $css = $this->shortenFontWeights($css);
317
+ $css = $this->stripEmptyTags($css);
318
+
319
+ // restore the string we've extracted earlier
320
+ $css = $this->restoreExtractedData($css);
321
+
322
+ $source = is_int($source) ? '' : $source;
323
+ $parents = $source ? array_merge($parents, array($source)) : $parents;
324
+ $css = $this->combineImports($source, $css, $parents);
325
+ $css = $this->importFiles($source, $css);
326
+
327
+ /*
328
+ * If we'll save to a new path, we'll have to fix the relative paths
329
+ * to be relative no longer to the source file, but to the new path.
330
+ * If we don't write to a file, fall back to same path so no
331
+ * conversion happens (because we still want it to go through most
332
+ * of the move code, which also addresses url() & @import syntax...)
333
+ */
334
+ $converter = $this->getPathConverter($source, $path ?: $source);
335
+ $css = $this->move($converter, $css);
336
+
337
+ // combine css
338
+ $content .= $css;
339
+ }
340
+
341
+ $content = $this->moveImportsToTop($content);
342
+
343
+ return $content;
344
+ }
345
+
346
+ /**
347
+ * Moving a css file should update all relative urls.
348
+ * Relative references (e.g. ../images/image.gif) in a certain css file,
349
+ * will have to be updated when a file is being saved at another location
350
+ * (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
351
+ *
352
+ * @param ConverterInterface $converter Relative path converter
353
+ * @param string $content The CSS content to update relative urls for
354
+ *
355
+ * @return string
356
+ */
357
+ protected function move(ConverterInterface $converter, $content)
358
+ {
359
+ /*
360
+ * Relative path references will usually be enclosed by url(). @import
361
+ * is an exception, where url() is not necessary around the path (but is
362
+ * allowed).
363
+ * This *could* be 1 regular expression, where both regular expressions
364
+ * in this array are on different sides of a |. But we're using named
365
+ * patterns in both regexes, the same name on both regexes. This is only
366
+ * possible with a (?J) modifier, but that only works after a fairly
367
+ * recent PCRE version. That's why I'm doing 2 separate regular
368
+ * expressions & combining the matches after executing of both.
369
+ */
370
+ $relativeRegexes = array(
371
+ // url(xxx)
372
+ '/
373
+ # open url()
374
+ url\(
375
+
376
+ \s*
377
+
378
+ # open path enclosure
379
+ (?P<quotes>["\'])?
380
+
381
+ # fetch path
382
+ (?P<path>.+?)
383
+
384
+ # close path enclosure
385
+ (?(quotes)(?P=quotes))
386
+
387
+ \s*
388
+
389
+ # close url()
390
+ \)
391
+
392
+ /ix',
393
+
394
+ // @import "xxx"
395
+ '/
396
+ # import statement
397
+ @import
398
+
399
+ # whitespace
400
+ \s+
401
+
402
+ # we don\'t have to check for @import url(), because the
403
+ # condition above will already catch these
404
+
405
+ # open path enclosure
406
+ (?P<quotes>["\'])
407
+
408
+ # fetch path
409
+ (?P<path>.+?)
410
+
411
+ # close path enclosure
412
+ (?P=quotes)
413
+
414
+ /ix',
415
+ );
416
+
417
+ // find all relative urls in css
418
+ $matches = array();
419
+ foreach ($relativeRegexes as $relativeRegex) {
420
+ if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) {
421
+ $matches = array_merge($matches, $regexMatches);
422
+ }
423
+ }
424
+
425
+ $search = array();
426
+ $replace = array();
427
+
428
+ // loop all urls
429
+ foreach ($matches as $match) {
430
+ // determine if it's a url() or an @import match
431
+ $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
432
+
433
+ $url = $match['path'];
434
+ if ($this->canImportByPath($url)) {
435
+ // attempting to interpret GET-params makes no sense, so let's discard them for awhile
436
+ $params = strrchr($url, '?');
437
+ $url = $params ? substr($url, 0, -strlen($params)) : $url;
438
+
439
+ // fix relative url
440
+ $url = $converter->convert($url);
441
+
442
+ // now that the path has been converted, re-apply GET-params
443
+ $url .= $params;
444
+ }
445
+
446
+ /*
447
+ * Urls with control characters above 0x7e should be quoted.
448
+ * According to Mozilla's parser, whitespace is only allowed at the
449
+ * end of unquoted urls.
450
+ * Urls with `)` (as could happen with data: uris) should also be
451
+ * quoted to avoid being confused for the url() closing parentheses.
452
+ * And urls with a # have also been reported to cause issues.
453
+ * Urls with quotes inside should also remain escaped.
454
+ *
455
+ * @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
456
+ * @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
457
+ * @see https://github.com/matthiasmullie/minify/issues/193
458
+ */
459
+ $url = trim($url);
460
+ if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) {
461
+ $url = $match['quotes'] . $url . $match['quotes'];
462
+ }
463
+
464
+ // build replacement
465
+ $search[] = $match[0];
466
+ if ($type === 'url') {
467
+ $replace[] = 'url('.$url.')';
468
+ } elseif ($type === 'import') {
469
+ $replace[] = '@import "'.$url.'"';
470
+ }
471
+ }
472
+
473
+ // replace urls
474
+ return str_replace($search, $replace, $content);
475
+ }
476
+
477
+ /**
478
+ * Shorthand hex color codes.
479
+ * #FF0000 -> #F00.
480
+ *
481
+ * @param string $content The CSS content to shorten the hex color codes for
482
+ *
483
+ * @return string
484
+ */
485
+ protected function shortenColors($content)
486
+ {
487
+ $content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?:([0-9a-z])\\4)?(?=[; }])/i', '#$1$2$3$4', $content);
488
+
489
+ // remove alpha channel if it's pointless...
490
+ $content = preg_replace('/(?<=[: ])#([0-9a-z]{6})ff?(?=[; }])/i', '#$1', $content);
491
+ $content = preg_replace('/(?<=[: ])#([0-9a-z]{3})f?(?=[; }])/i', '#$1', $content);
492
+
493
+ $colors = array(
494
+ // we can shorten some even more by replacing them with their color name
495
+ '#F0FFFF' => 'azure',
496
+ '#F5F5DC' => 'beige',
497
+ '#A52A2A' => 'brown',
498
+ '#FF7F50' => 'coral',
499
+ '#FFD700' => 'gold',
500
+ '#808080' => 'gray',
501
+ '#008000' => 'green',
502
+ '#4B0082' => 'indigo',
503
+ '#FFFFF0' => 'ivory',
504
+ '#F0E68C' => 'khaki',
505
+ '#FAF0E6' => 'linen',
506
+ '#800000' => 'maroon',
507
+ '#000080' => 'navy',
508
+ '#808000' => 'olive',
509
+ '#CD853F' => 'peru',
510
+ '#FFC0CB' => 'pink',
511
+ '#DDA0DD' => 'plum',
512
+ '#800080' => 'purple',
513
+ '#F00' => 'red',
514
+ '#FA8072' => 'salmon',
515
+ '#A0522D' => 'sienna',
516
+ '#C0C0C0' => 'silver',
517
+ '#FFFAFA' => 'snow',
518
+ '#D2B48C' => 'tan',
519
+ '#FF6347' => 'tomato',
520
+ '#EE82EE' => 'violet',
521
+ '#F5DEB3' => 'wheat',
522
+ // or the other way around
523
+ 'WHITE' => '#fff',
524
+ 'BLACK' => '#000',
525
+ );
526
+
527
+ return preg_replace_callback(
528
+ '/(?<=[: ])('.implode('|', array_keys($colors)).')(?=[; }])/i',
529
+ function ($match) use ($colors) {
530
+ return $colors[strtoupper($match[0])];
531
+ },
532
+ $content
533
+ );
534
+ }
535
+
536
+ /**
537
+ * Shorten CSS font weights.
538
+ *
539
+ * @param string $content The CSS content to shorten the font weights for
540
+ *
541
+ * @return string
542
+ */
543
+ protected function shortenFontWeights($content)
544
+ {
545
+ $weights = array(
546
+ 'normal' => 400,
547
+ 'bold' => 700,
548
+ );
549
+
550
+ $callback = function ($match) use ($weights) {
551
+ return $match[1].$weights[$match[2]];
552
+ };
553
+
554
+ return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
555
+ }
556
+
557
+ /**
558
+ * Shorthand 0 values to plain 0, instead of e.g. -0em.
559
+ *
560
+ * @param string $content The CSS content to shorten the zero values for
561
+ *
562
+ * @return string
563
+ */
564
+ protected function shortenZeroes($content)
565
+ {
566
+ // we don't want to strip units in `calc()` expressions:
567
+ // `5px - 0px` is valid, but `5px - 0` is not
568
+ // `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
569
+ // `10 * 0` is invalid
570
+ // we've extracted calcs earlier, so we don't need to worry about this
571
+
572
+ // reusable bits of code throughout these regexes:
573
+ // before & after are used to make sure we don't match lose unintended
574
+ // 0-like values (e.g. in #000, or in http://url/1.0)
575
+ // units can be stripped from 0 values, or used to recognize non 0
576
+ // values (where wa may be able to strip a .0 suffix)
577
+ $before = '(?<=[:(, ])';
578
+ $after = '(?=[ ,);}])';
579
+ $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
580
+
581
+ // strip units after zeroes (0px -> 0)
582
+ // NOTE: it should be safe to remove all units for a 0 value, but in
583
+ // practice, Webkit (especially Safari) seems to stumble over at least
584
+ // 0%, potentially other units as well. Only stripping 'px' for now.
585
+ // @see https://github.com/matthiasmullie/minify/issues/60
586
+ $content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
587
+
588
+ // strip 0-digits (.0 -> 0)
589
+ $content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
590
+ // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
591
+ $content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
592
+ // strip trailing 0: 50.00 -> 50, 50.00px -> 50px
593
+ $content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
594
+ // strip leading 0: 0.1 -> .1, 01.1 -> 1.1
595
+ $content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
596
+
597
+ // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
598
+ $content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
599
+
600
+ // IE doesn't seem to understand a unitless flex-basis value (correct -
601
+ // it goes against the spec), so let's add it in again (make it `%`,
602
+ // which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
603
+ // @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
604
+ $content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
605
+ $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
606
+
607
+ return $content;
608
+ }
609
+
610
+ /**
611
+ * Strip empty tags from source code.
612
+ *
613
+ * @param string $content
614
+ *
615
+ * @return string
616
+ */
617
+ protected function stripEmptyTags($content)
618
+ {
619
+ $content = preg_replace('/(?<=^)[^\{\};]+\{\s*\}/', '', $content);
620
+ $content = preg_replace('/(?<=(\}|;))[^\{\};]+\{\s*\}/', '', $content);
621
+
622
+ return $content;
623
+ }
624
+
625
+ /**
626
+ * Strip comments from source code.
627
+ */
628
+ protected function stripComments()
629
+ {
630
+ // PHP only supports $this inside anonymous functions since 5.4
631
+ $minifier = $this;
632
+ $callback = function ($match) use ($minifier) {
633
+ $count = count($minifier->extracted);
634
+ $placeholder = '/*'.$count.'*/';
635
+ $minifier->extracted[$placeholder] = $match[0];
636
+
637
+ return $placeholder;
638
+ };
639
+ $this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
640
+
641
+ $this->registerPattern('/\/\*.*?\*\//s', '');
642
+ }
643
+
644
+ /**
645
+ * Strip whitespace.
646
+ *
647
+ * @param string $content The CSS content to strip the whitespace for
648
+ *
649
+ * @return string
650
+ */
651
+ protected function stripWhitespace($content)
652
+ {
653
+ // remove leading & trailing whitespace
654
+ $content = preg_replace('/^\s*/m', '', $content);
655
+ $content = preg_replace('/\s*$/m', '', $content);
656
+
657
+ // replace newlines with a single space
658
+ $content = preg_replace('/\s+/', ' ', $content);
659
+
660
+ // remove whitespace around meta characters
661
+ // inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
662
+ $content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
663
+ $content = preg_replace('/([\[(:>\+])\s+/', '$1', $content);
664
+ $content = preg_replace('/\s+([\]\)>\+])/', '$1', $content);
665
+ $content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
666
+
667
+ // whitespace around + and - can only be stripped inside some pseudo-
668
+ // classes, like `:nth-child(3+2n)`
669
+ // not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
670
+ // selectors like `div.weird- p`
671
+ $pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
672
+ $content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
673
+
674
+ // remove semicolon/whitespace followed by closing bracket
675
+ $content = str_replace(';}', '}', $content);
676
+
677
+ return trim($content);
678
+ }
679
+
680
+ /**
681
+ * Replace all `calc()` occurrences.
682
+ */
683
+ protected function extractCalcs()
684
+ {
685
+ // PHP only supports $this inside anonymous functions since 5.4
686
+ $minifier = $this;
687
+ $callback = function ($match) use ($minifier) {
688
+ $length = strlen($match[1]);
689
+ $expr = '';
690
+ $opened = 0;
691
+
692
+ for ($i = 0; $i < $length; $i++) {
693
+ $char = $match[1][$i];
694
+ $expr .= $char;
695
+ if ($char === '(') {
696
+ $opened++;
697
+ } elseif ($char === ')' && --$opened === 0) {
698
+ break;
699
+ }
700
+ }
701
+ $rest = str_replace($expr, '', $match[1]);
702
+ $expr = trim(substr($expr, 1, -1));
703
+
704
+ $count = count($minifier->extracted);
705
+ $placeholder = 'calc('.$count.')';
706
+ $minifier->extracted[$placeholder] = 'calc('.$expr.')';
707
+
708
+ return $placeholder.$rest;
709
+ };
710
+
711
+ $this->registerPattern('/calc(\(.+?)(?=$|;|}|calc\()/', $callback);
712
+ $this->registerPattern('/calc(\(.+?)(?=$|;|}|calc\()/m', $callback);
713
+ }
714
+
715
+ /**
716
+ * Check if file is small enough to be imported.
717
+ *
718
+ * @param string $path The path to the file
719
+ *
720
+ * @return bool
721
+ */
722
+ protected function canImportBySize($path)
723
+ {
724
+ return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
725
+ }
726
+
727
+ /**
728
+ * Check if file a file can be imported, going by the path.
729
+ *
730
+ * @param string $path
731
+ *
732
+ * @return bool
733
+ */
734
+ protected function canImportByPath($path)
735
+ {
736
+ return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
737
+ }
738
+
739
+ /**
740
+ * Return a converter to update relative paths to be relative to the new
741
+ * destination.
742
+ *
743
+ * @param string $source
744
+ * @param string $target
745
+ *
746
+ * @return ConverterInterface
747
+ */
748
+ protected function getPathConverter($source, $target)
749
+ {
750
+ return new Converter($source, $target);
751
+ }
752
+ }
libs/matthiasmullie/minify/src/Exception.php CHANGED
@@ -1,20 +1,20 @@
1
- <?php
2
- /**
3
- * Base Exception
4
- *
5
- * @deprecated Use Exceptions\BasicException instead
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- */
9
- namespace MatthiasMullie\Minify;
10
-
11
- /**
12
- * Base Exception Class
13
- * @deprecated Use Exceptions\BasicException instead
14
- *
15
- * @package Minify
16
- * @author Matthias Mullie <minify@mullie.eu>
17
- */
18
- abstract class Exception extends \Exception
19
- {
20
- }
1
+ <?php
2
+ /**
3
+ * Base Exception
4
+ *
5
+ * @deprecated Use Exceptions\BasicException instead
6
+ *
7
+ * @author Matthias Mullie <minify@mullie.eu>
8
+ */
9
+ namespace FVM\MatthiasMullie\Minify;
10
+
11
+ /**
12
+ * Base Exception Class
13
+ * @deprecated Use Exceptions\BasicException instead
14
+ *
15
+ * @package Minify
16
+ * @author Matthias Mullie <minify@mullie.eu>
17
+ */
18
+ abstract class Exception extends \Exception
19
+ {
20
+ }
libs/matthiasmullie/minify/src/Exceptions/BasicException.php CHANGED
@@ -1,23 +1,23 @@
1
- <?php
2
- /**
3
- * Basic exception
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
- namespace MatthiasMullie\Minify\Exceptions;
12
-
13
- use MatthiasMullie\Minify\Exception;
14
-
15
- /**
16
- * Basic Exception Class
17
- *
18
- * @package Minify\Exception
19
- * @author Matthias Mullie <minify@mullie.eu>
20
- */
21
- abstract class BasicException extends Exception
22
- {
23
- }
1
+ <?php
2
+ /**
3
+ * Basic exception
4
+ *
5
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
+ *
7
+ * @author Matthias Mullie <minify@mullie.eu>
8
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
+ * @license MIT License
10
+ */
11
+ namespace FVM\MatthiasMullie\Minify\Exceptions;
12
+
13
+ use FVM\MatthiasMullie\Minify\Exception;
14
+
15
+ /**
16
+ * Basic Exception Class
17
+ *
18
+ * @package Minify\Exception
19
+ * @author Matthias Mullie <minify@mullie.eu>
20
+ */
21
+ abstract class BasicException extends Exception
22
+ {
23
+ }
libs/matthiasmullie/minify/src/Exceptions/FileImportException.php CHANGED
@@ -1,21 +1,21 @@
1
- <?php
2
- /**
3
- * File Import Exception
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
- namespace MatthiasMullie\Minify\Exceptions;
12
-
13
- /**
14
- * File Import Exception Class
15
- *
16
- * @package Minify\Exception
17
- * @author Matthias Mullie <minify@mullie.eu>
18
- */
19
- class FileImportException extends BasicException
20
- {
21
- }
1
+ <?php
2
+ /**
3
+ * File Import Exception
4
+ *
5
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
+ *
7
+ * @author Matthias Mullie <minify@mullie.eu>
8
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
+ * @license MIT License
10
+ */
11
+ namespace FVM\MatthiasMullie\Minify\Exceptions;
12
+
13
+ /**
14
+ * File Import Exception Class
15
+ *
16
+ * @package Minify\Exception
17
+ * @author Matthias Mullie <minify@mullie.eu>
18
+ */
19
+ class FileImportException extends BasicException
20
+ {
21
+ }
libs/matthiasmullie/minify/src/Exceptions/IOException.php CHANGED
@@ -1,21 +1,21 @@
1
- <?php
2
- /**
3
- * IO Exception
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
- namespace MatthiasMullie\Minify\Exceptions;
12
-
13
- /**
14
- * IO Exception Class
15
- *
16
- * @package Minify\Exception
17
- * @author Matthias Mullie <minify@mullie.eu>
18
- */
19
- class IOException extends BasicException
20
- {
21
- }
1
+ <?php
2
+ /**
3
+ * IO Exception
4
+ *
5
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
+ *
7
+ * @author Matthias Mullie <minify@mullie.eu>
8
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
+ * @license MIT License
10
+ */
11
+ namespace FVM\MatthiasMullie\Minify\Exceptions;
12
+
13
+ /**
14
+ * IO Exception Class
15
+ *
16
+ * @package Minify\Exception
17
+ * @author Matthias Mullie <minify@mullie.eu>
18
+ */
19
+ class IOException extends BasicException
20
+ {
21
+ }
libs/matthiasmullie/minify/src/JS.php CHANGED
@@ -1,612 +1,612 @@
1
- <?php
2
- /**
3
- * JavaScript minifier
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
- namespace MatthiasMullie\Minify;
12
-
13
- /**
14
- * JavaScript Minifier Class
15
- *
16
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
17
- *
18
- * @package Minify
19
- * @author Matthias Mullie <minify@mullie.eu>
20
- * @author Tijs Verkoyen <minify@verkoyen.eu>
21
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
22
- * @license MIT License
23
- */
24
- class JS extends Minify
25
- {
26
- /**
27
- * Var-matching regex based on http://stackoverflow.com/a/9337047/802993.
28
- *
29
- * Note that regular expressions using that bit must have the PCRE_UTF8
30
- * pattern modifier (/u) set.
31
- *
32
- * @var string
33
- */
34
- const REGEX_VARIABLE = '\b[$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}][$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}0-9\x{0300}-\x{036f}\x{0483}-\x{0487}\x{0591}-\x{05bd}\x{05bf}\x{05c1}\x{05c2}\x{05c4}\x{05c5}\x{05c7}\x{0610}-\x{061a}\x{064b}-\x{0669}\x{0670}\x{06d6}-\x{06dc}\x{06df}-\x{06e4}\x{06e7}\x{06e8}\x{06ea}-\x{06ed}\x{06f0}-\x{06f9}\x{0711}\x{0730}-\x{074a}\x{07a6}-\x{07b0}\x{07c0}-\x{07c9}\x{07eb}-\x{07f3}\x{0816}-\x{0819}\x{081b}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082d}\x{0859}-\x{085b}\x{08e4}-\x{08fe}\x{0900}-\x{0903}\x{093a}-\x{093c}\x{093e}-\x{094f}\x{0951}-\x{0957}\x{0962}\x{0963}\x{0966}-\x{096f}\x{0981}-\x{0983}\x{09bc}\x{09be}-\x{09c4}\x{09c7}\x{09c8}\x{09cb}-\x{09cd}\x{09d7}\x{09e2}\x{09e3}\x{09e6}-\x{09ef}\x{0a01}-\x{0a03}\x{0a3c}\x{0a3e}-\x{0a42}\x{0a47}\x{0a48}\x{0a4b}-\x{0a4d}\x{0a51}\x{0a66}-\x{0a71}\x{0a75}\x{0a81}-\x{0a83}\x{0abc}\x{0abe}-\x{0ac5}\x{0ac7}-\x{0ac9}\x{0acb}-\x{0acd}\x{0ae2}\x{0ae3}\x{0ae6}-\x{0aef}\x{0b01}-\x{0b03}\x{0b3c}\x{0b3e}-\x{0b44}\x{0b47}\x{0b48}\x{0b4b}-\x{0b4d}\x{0b56}\x{0b57}\x{0b62}\x{0b63}\x{0b66}-\x{0b6f}\x{0b82}\x{0bbe}-\x{0bc2}\x{0bc6}-\x{0bc8}\x{0bca}-\x{0bcd}\x{0bd7}\x{0be6}-\x{0bef}\x{0c01}-\x{0c03}\x{0c3e}-\x{0c44}\x{0c46}-\x{0c48}\x{0c4a}-\x{0c4d}\x{0c55}\x{0c56}\x{0c62}\x{0c63}\x{0c66}-\x{0c6f}\x{0c82}\x{0c83}\x{0cbc}\x{0cbe}-\x{0cc4}\x{0cc6}-\x{0cc8}\x{0cca}-\x{0ccd}\x{0cd5}\x{0cd6}\x{0ce2}\x{0ce3}\x{0ce6}-\x{0cef}\x{0d02}\x{0d03}\x{0d3e}-\x{0d44}\x{0d46}-\x{0d48}\x{0d4a}-\x{0d4d}\x{0d57}\x{0d62}\x{0d63}\x{0d66}-\x{0d6f}\x{0d82}\x{0d83}\x{0dca}\x{0dcf}-\x{0dd4}\x{0dd6}\x{0dd8}-\x{0ddf}\x{0df2}\x{0df3}\x{0e31}\x{0e34}-\x{0e3a}\x{0e47}-\x{0e4e}\x{0e50}-\x{0e59}\x{0eb1}\x{0eb4}-\x{0eb9}\x{0ebb}\x{0ebc}\x{0ec8}-\x{0ecd}\x{0ed0}-\x{0ed9}\x{0f18}\x{0f19}\x{0f20}-\x{0f29}\x{0f35}\x{0f37}\x{0f39}\x{0f3e}\x{0f3f}\x{0f71}-\x{0f84}\x{0f86}\x{0f87}\x{0f8d}-\x{0f97}\x{0f99}-\x{0fbc}\x{0fc6}\x{102b}-\x{103e}\x{1040}-\x{1049}\x{1056}-\x{1059}\x{105e}-\x{1060}\x{1062}-\x{1064}\x{1067}-\x{106d}\x{1071}-\x{1074}\x{1082}-\x{108d}\x{108f}-\x{109d}\x{135d}-\x{135f}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17d3}\x{17dd}\x{17e0}-\x{17e9}\x{180b}-\x{180d}\x{1810}-\x{1819}\x{18a9}\x{1920}-\x{192b}\x{1930}-\x{193b}\x{1946}-\x{194f}\x{19b0}-\x{19c0}\x{19c8}\x{19c9}\x{19d0}-\x{19d9}\x{1a17}-\x{1a1b}\x{1a55}-\x{1a5e}\x{1a60}-\x{1a7c}\x{1a7f}-\x{1a89}\x{1a90}-\x{1a99}\x{1b00}-\x{1b04}\x{1b34}-\x{1b44}\x{1b50}-\x{1b59}\x{1b6b}-\x{1b73}\x{1b80}-\x{1b82}\x{1ba1}-\x{1bad}\x{1bb0}-\x{1bb9}\x{1be6}-\x{1bf3}\x{1c24}-\x{1c37}\x{1c40}-\x{1c49}\x{1c50}-\x{1c59}\x{1cd0}-\x{1cd2}\x{1cd4}-\x{1ce8}\x{1ced}\x{1cf2}-\x{1cf4}\x{1dc0}-\x{1de6}\x{1dfc}-\x{1dff}\x{200c}\x{200d}\x{203f}\x{2040}\x{2054}\x{20d0}-\x{20dc}\x{20e1}\x{20e5}-\x{20f0}\x{2cef}-\x{2cf1}\x{2d7f}\x{2de0}-\x{2dff}\x{302a}-\x{302f}\x{3099}\x{309a}\x{a620}-\x{a629}\x{a66f}\x{a674}-\x{a67d}\x{a69f}\x{a6f0}\x{a6f1}\x{a802}\x{a806}\x{a80b}\x{a823}-\x{a827}\x{a880}\x{a881}\x{a8b4}-\x{a8c4}\x{a8d0}-\x{a8d9}\x{a8e0}-\x{a8f1}\x{a900}-\x{a909}\x{a926}-\x{a92d}\x{a947}-\x{a953}\x{a980}-\x{a983}\x{a9b3}-\x{a9c0}\x{a9d0}-\x{a9d9}\x{aa29}-\x{aa36}\x{aa43}\x{aa4c}\x{aa4d}\x{aa50}-\x{aa59}\x{aa7b}\x{aab0}\x{aab2}-\x{aab4}\x{aab7}\x{aab8}\x{aabe}\x{aabf}\x{aac1}\x{aaeb}-\x{aaef}\x{aaf5}\x{aaf6}\x{abe3}-\x{abea}\x{abec}\x{abed}\x{abf0}-\x{abf9}\x{fb1e}\x{fe00}-\x{fe0f}\x{fe20}-\x{fe26}\x{fe33}\x{fe34}\x{fe4d}-\x{fe4f}\x{ff10}-\x{ff19}\x{ff3f}]*\b';
35
-
36
- /**
37
- * Full list of JavaScript reserved words.
38
- * Will be loaded from /data/js/keywords_reserved.txt.
39
- *
40
- * @see https://mathiasbynens.be/notes/reserved-keywords
41
- *
42
- * @var string[]
43
- */
44
- protected $keywordsReserved = array();
45
-
46
- /**
47
- * List of JavaScript reserved words that accept a <variable, value, ...>
48
- * after them. Some end of lines are not the end of a statement, like with
49
- * these keywords.
50
- *
51
- * E.g.: we shouldn't insert a ; after this else
52
- * else
53
- * console.log('this is quite fine')
54
- *
55
- * Will be loaded from /data/js/keywords_before.txt
56
- *
57
- * @var string[]
58
- */
59
- protected $keywordsBefore = array();
60
-
61
- /**
62
- * List of JavaScript reserved words that accept a <variable, value, ...>
63
- * before them. Some end of lines are not the end of a statement, like when
64
- * continued by one of these keywords on the newline.
65
- *
66
- * E.g.: we shouldn't insert a ; before this instanceof
67
- * variable
68
- * instanceof String
69
- *
70
- * Will be loaded from /data/js/keywords_after.txt
71
- *
72
- * @var string[]
73
- */
74
- protected $keywordsAfter = array();
75
-
76
- /**
77
- * List of all JavaScript operators.
78
- *
79
- * Will be loaded from /data/js/operators.txt
80
- *
81
- * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
82
- *
83
- * @var string[]
84
- */
85
- protected $operators = array();
86
-
87
- /**
88
- * List of JavaScript operators that accept a <variable, value, ...> after
89
- * them. Some end of lines are not the end of a statement, like with these
90
- * operators.
91
- *
92
- * Note: Most operators are fine, we've only removed ++ and --.
93
- * ++ & -- have to be joined with the value they're in-/decrementing.
94
- *
95
- * Will be loaded from /data/js/operators_before.txt
96
- *
97
- * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
98
- *
99
- * @var string[]
100
- */
101
- protected $operatorsBefore = array();
102
-
103
- /**
104
- * List of JavaScript operators that accept a <variable, value, ...> before
105
- * them. Some end of lines are not the end of a statement, like when
106
- * continued by one of these operators on the newline.
107
- *
108
- * Note: Most operators are fine, we've only removed ), ], ++, --, ! and ~.
109
- * There can't be a newline separating ! or ~ and whatever it is negating.
110
- * ++ & -- have to be joined with the value they're in-/decrementing.
111
- * ) & ] are "special" in that they have lots or usecases. () for example
112
- * is used for function calls, for grouping, in if () and for (), ...
113
- *
114
- * Will be loaded from /data/js/operators_after.txt
115
- *
116
- * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
117
- *
118
- * @var string[]
119
- */
120
- protected $operatorsAfter = array();
121
-
122
- /**
123
- * {@inheritdoc}
124
- */
125
- public function __construct()
126
- {
127
- call_user_func_array(array('parent', '__construct'), func_get_args());
128
-
129
- $dataDir = __DIR__.'/../data/js/';
130
- $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
131
- $this->keywordsReserved = file($dataDir.'keywords_reserved.txt', $options);
132
- $this->keywordsBefore = file($dataDir.'keywords_before.txt', $options);
133
- $this->keywordsAfter = file($dataDir.'keywords_after.txt', $options);
134
- $this->operators = file($dataDir.'operators.txt', $options);
135
- $this->operatorsBefore = file($dataDir.'operators_before.txt', $options);
136
- $this->operatorsAfter = file($dataDir.'operators_after.txt', $options);
137
- }
138
-
139
- /**
140
- * Minify the data.
141
- * Perform JS optimizations.
142
- *
143
- * @param string[optional] $path Path to write the data to
144
- *
145
- * @return string The minified data
146
- */
147
- public function execute($path = null)
148
- {
149
- $content = '';
150
-
151
- /*
152
- * Let's first take out strings, comments and regular expressions.
153
- * All of these can contain JS code-like characters, and we should make
154
- * sure any further magic ignores anything inside of these.
155
- *
156
- * Consider this example, where we should not strip any whitespace:
157
- * var str = "a test";
158
- *
159
- * Comments will be removed altogether, strings and regular expressions
160
- * will be replaced by placeholder text, which we'll restore later.
161
- */
162
- $this->extractStrings('\'"`');
163
- $this->stripComments();
164
- $this->extractRegex();
165
-
166
- // loop files
167
- foreach ($this->data as $source => $js) {
168
- // take out strings, comments & regex (for which we've registered
169
- // the regexes just a few lines earlier)
170
- $js = $this->replace($js);
171
-
172
- $js = $this->propertyNotation($js);
173
- $js = $this->shortenBools($js);
174
- $js = $this->stripWhitespace($js);
175
-
176
- // combine js: separating the scripts by a ;
177
- $content .= $js.";";
178
- }
179
-
180
- // clean up leftover `;`s from the combination of multiple scripts
181
- $content = ltrim($content, ';');
182
- $content = (string) substr($content, 0, -1);
183
-
184
- /*
185
- * Earlier, we extracted strings & regular expressions and replaced them
186
- * with placeholder text. This will restore them.
187
- */
188
- $content = $this->restoreExtractedData($content);
189
-
190
- return $content;
191
- }
192
-
193
- /**
194
- * Strip comments from source code.
195
- */
196
- protected function stripComments()
197
- {
198
- // PHP only supports $this inside anonymous functions since 5.4
199
- $minifier = $this;
200
- $callback = function ($match) use ($minifier) {
201
- $count = count($minifier->extracted);
202
- $placeholder = '/*'.$count.'*/';
203
- $minifier->extracted[$placeholder] = $match[0];
204
-
205
- return $placeholder;
206
- };
207
- // multi-line comments
208
- $this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
209
- $this->registerPattern('/\/\*.*?\*\//s', '');
210
-
211
- // single-line comments
212
- $this->registerPattern('/\/\/.*$/m', '');
213
- }
214
-
215
- /**
216
- * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
217
- *
218
- * The content inside the regex can contain characters that may be confused
219
- * for JS code: e.g. it could contain whitespace it needs to match & we
220
- * don't want to strip whitespace in there.
221
- *
222
- * The regex can be pretty simple: we don't have to care about comments,
223
- * (which also use slashes) because stripComments() will have stripped those
224
- * already.
225
- *
226
- * This method will replace all string content with simple REGEX#
227
- * placeholder text, so we've rid all regular expressions from characters
228
- * that may be misinterpreted. Original regex content will be saved in
229
- * $this->extracted and after doing all other minifying, we can restore the
230
- * original content via restoreRegex()
231
- */
232
- protected function extractRegex()
233
- {
234
- // PHP only supports $this inside anonymous functions since 5.4
235
- $minifier = $this;
236
- $callback = function ($match) use ($minifier) {
237
- $count = count($minifier->extracted);
238
- $placeholder = '"'.$count.'"';
239
- $minifier->extracted[$placeholder] = $match[0];
240
-
241
- return $placeholder;
242
- };
243
-
244
- // match all chars except `/` and `\`
245
- // `\` is allowed though, along with whatever char follows (which is the
246
- // one being escaped)
247
- // this should allow all chars, except for an unescaped `/` (= the one
248
- // closing the regex)
249
- // then also ignore bare `/` inside `[]`, where they don't need to be
250
- // escaped: anything inside `[]` can be ignored safely
251
- $pattern = '\\/(?!\*)(?:[^\\[\\/\\\\\n\r]++|(?:\\\\.)++|(?:\\[(?:[^\\]\\\\\n\r]++|(?:\\\\.)++)++\\])++)++\\/[gimuy]*';
252
-
253
- // a regular expression can only be followed by a few operators or some
254
- // of the RegExp methods (a `\` followed by a variable or value is
255
- // likely part of a division, not a regex)
256
- $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
257
- $before = '([=:,;\+\-\*\/\}\(\{\[&\|!]|^|'.implode('|', $keywords).')\s*';
258
- $propertiesAndMethods = array(
259
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
260
- 'constructor',
261
- 'flags',
262
- 'global',
263
- 'ignoreCase',
264
- 'multiline',
265
- 'source',
266
- 'sticky',
267
- 'unicode',
268
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
269
- 'compile(',
270
- 'exec(',
271
- 'test(',
272
- 'toSource(',
273
- 'toString(',
274
- );
275
- $delimiters = array_fill(0, count($propertiesAndMethods), '/');
276
- $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
277
- $after = '(?=\s*([\.,;\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).')))';
278
- $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
279
-
280
- // regular expressions following a `)` are rather annoying to detect...
281
- // quite often, `/` after `)` is a division operator & if it happens to
282
- // be followed by another one (or a comment), it is likely to be
283
- // confused for a regular expression
284
- // however, it's perfectly possible for a regex to follow a `)`: after
285
- // a single-line `if()`, `while()`, ... statement, for example
286
- // since, when they occur like that, they're always the start of a
287
- // statement, there's only a limited amount of ways they can be useful:
288
- // by calling the regex methods directly
289
- // if a regex following `)` is not followed by `.<property or method>`,
290
- // it's quite likely not a regex
291
- $before = '\)\s*';
292
- $after = '(?=\s*\.('.implode('|', $propertiesAndMethods).'))';
293
- $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
294
-
295
- // 1 more edge case: a regex can be followed by a lot more operators or
296
- // keywords if there's a newline (ASI) in between, where the operator
297
- // actually starts a new statement
298
- // (https://github.com/matthiasmullie/minify/issues/56)
299
- $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
300
- $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
301
- $after = '(?=\s*\n\s*('.implode('|', $operators).'))';
302
- $this->registerPattern('/'.$pattern.$after.'/', $callback);
303
- }
304
-
305
- /**
306
- * Strip whitespace.
307
- *
308
- * We won't strip *all* whitespace, but as much as possible. The thing that
309
- * we'll preserve are newlines we're unsure about.
310
- * JavaScript doesn't require statements to be terminated with a semicolon.
311
- * It will automatically fix missing semicolons with ASI (automatic semi-
312
- * colon insertion) at the end of line causing errors (without semicolon.)
313
- *
314
- * Because it's sometimes hard to tell if a newline is part of a statement
315
- * that should be terminated or not, we'll just leave some of them alone.
316
- *
317
- * @param string $content The content to strip the whitespace for
318
- *
319
- * @return string
320
- */
321
- protected function stripWhitespace($content)
322
- {
323
- // uniform line endings, make them all line feed
324
- $content = str_replace(array("\r\n", "\r"), "\n", $content);
325
-
326
- // collapse all non-line feed whitespace into a single space
327
- $content = preg_replace('/[^\S\n]+/', ' ', $content);
328
-
329
- // strip leading & trailing whitespace
330
- $content = str_replace(array(" \n", "\n "), "\n", $content);
331
-
332
- // collapse consecutive line feeds into just 1
333
- $content = preg_replace('/\n+/', "\n", $content);
334
-
335
- $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
336
- $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
337
- $operators = $this->getOperatorsForRegex($this->operators, '/');
338
- $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
339
- $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
340
-
341
- // strip whitespace that ends in (or next line begin with) an operator
342
- // that allows statements to be broken up over multiple lines
343
- unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
344
- $content = preg_replace(
345
- array(
346
- '/('.implode('|', $operatorsBefore).')\s+/',
347
- '/\s+('.implode('|', $operatorsAfter).')/',
348
- ),
349
- '\\1',
350
- $content
351
- );
352
-
353
- // make sure + and - can't be mistaken for, or joined into ++ and --
354
- $content = preg_replace(
355
- array(
356
- '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
357
- '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
358
- ),
359
- '\\1',
360
- $content
361
- );
362
-
363
- // collapse whitespace around reserved words into single space
364
- $content = preg_replace('/(^|[;\}\s])\K('.implode('|', $keywordsBefore).')\s+/', '\\2 ', $content);
365
- $content = preg_replace('/\s+('.implode('|', $keywordsAfter).')(?=([;\{\s]|$))/', ' \\1', $content);
366
-
367
- /*
368
- * We didn't strip whitespace after a couple of operators because they
369
- * could be used in different contexts and we can't be sure it's ok to
370
- * strip the newlines. However, we can safely strip any non-line feed
371
- * whitespace that follows them.
372
- */
373
- $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
374
- $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
375
- $content = preg_replace('/('.implode('|', $operatorsDiffBefore).')[^\S\n]+/', '\\1', $content);
376
- $content = preg_replace('/[^\S\n]+('.implode('|', $operatorsDiffAfter).')/', '\\1', $content);
377
-
378
- /*
379
- * Whitespace after `return` can be omitted in a few occasions
380
- * (such as when followed by a string or regex)
381
- * Same for whitespace in between `)` and `{`, or between `{` and some
382
- * keywords.
383
- */
384
- $content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
385
- $content = preg_replace('/\)\s+\{/', '){', $content);
386
- $content = preg_replace('/}\n(else|catch|finally)\b/', '}$1', $content);
387
-
388
- /*
389
- * Get rid of double semicolons, except where they can be used like:
390
- * "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
391
- * I'll safeguard these double semicolons inside for-loops by
392
- * temporarily replacing them with an invalid condition: they won't have
393
- * a double semicolon and will be easy to spot to restore afterwards.
394
- */
395
- $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
396
- $content = preg_replace('/;+/', ';', $content);
397
- $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
398
-
399
- /*
400
- * Next, we'll be removing all semicolons where ASI kicks in.
401
- * for-loops however, can have an empty body (ending in only a
402
- * semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
403
- * Here, nothing happens during the loop; it's just used to keep
404
- * increasing `i`. With that ; omitted, the next line would be expected
405
- * to be the for-loop's body... Same goes for while loops.
406
- * I'm going to double that semicolon (if any) so after the next line,
407
- * which strips semicolons here & there, we're still left with this one.
408
- */
409
- $content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
410
- $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
411
- /*
412
- * Below will also keep `;` after a `do{}while();` along with `while();`
413
- * While these could be stripped after do-while, detecting this
414
- * distinction is cumbersome, so I'll play it safe and make sure `;`
415
- * after any kind of `while` is kept.
416
- */
417
- $content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
418
-
419
- /*
420
- * We also can't strip empty else-statements. Even though they're
421
- * useless and probably shouldn't be in the code in the first place, we
422
- * shouldn't be stripping the `;` that follows it as it breaks the code.
423
- * We can just remove those useless else-statements completely.
424
- *
425
- * @see https://github.com/matthiasmullie/minify/issues/91
426
- */
427
- $content = preg_replace('/else;/s', '', $content);
428
-
429
- /*
430
- * We also don't really want to terminate statements followed by closing
431
- * curly braces (which we've ignored completely up until now) or end-of-
432
- * script: ASI will kick in here & we're all about minifying.
433
- * Semicolons at beginning of the file don't make any sense either.
434
- */
435
- $content = preg_replace('/;(\}|$)/s', '\\1', $content);
436
- $content = ltrim($content, ';');
437
-
438
- // get rid of remaining whitespace af beginning/end
439
- return trim($content);
440
- }
441
-
442
- /**
443
- * We'll strip whitespace around certain operators with regular expressions.
444
- * This will prepare the given array by escaping all characters.
445
- *
446
- * @param string[] $operators
447
- * @param string $delimiter
448
- *
449
- * @return string[]
450
- */
451
- protected function getOperatorsForRegex(array $operators, $delimiter = '/')
452
- {
453
- // escape operators for use in regex
454
- $delimiters = array_fill(0, count($operators), $delimiter);
455
- $escaped = array_map('preg_quote', $operators, $delimiters);
456
-
457
- $operators = array_combine($operators, $escaped);
458
-
459
- // ignore + & - for now, they'll get special treatment
460
- unset($operators['+'], $operators['-']);
461
-
462
- // dot can not just immediately follow a number; it can be confused for
463
- // decimal point, or calling a method on it, e.g. 42 .toString()
464
- $operators['.'] = '(?<![0-9]\s)\.';
465
-
466
- // don't confuse = with other assignment shortcuts (e.g. +=)
467
- $chars = preg_quote('+-*\=<>%&|', $delimiter);
468
- $operators['='] = '(?<!['.$chars.'])\=';
469
-
470
- return $operators;
471
- }
472
-
473
- /**
474
- * We'll strip whitespace around certain keywords with regular expressions.
475
- * This will prepare the given array by escaping all characters.
476
- *
477
- * @param string[] $keywords
478
- * @param string $delimiter
479
- *
480
- * @return string[]
481
- */
482
- protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
483
- {
484
- // escape keywords for use in regex
485
- $delimiter = array_fill(0, count($keywords), $delimiter);
486
- $escaped = array_map('preg_quote', $keywords, $delimiter);
487
-
488
- // add word boundaries
489
- array_walk($keywords, function ($value) {
490
- return '\b'.$value.'\b';
491
- });
492
-
493
- $keywords = array_combine($keywords, $escaped);
494
-
495
- return $keywords;
496
- }
497
-
498
- /**
499
- * Replaces all occurrences of array['key'] by array.key.
500
- *
501
- * @param string $content
502
- *
503
- * @return string
504
- */
505
- protected function propertyNotation($content)
506
- {
507
- // PHP only supports $this inside anonymous functions since 5.4
508
- $minifier = $this;
509
- $keywords = $this->keywordsReserved;
510
- $callback = function ($match) use ($minifier, $keywords) {
511
- $property = trim($minifier->extracted[$match[1]], '\'"');
512
-
513
- /*
514
- * Check if the property is a reserved keyword. In this context (as
515
- * property of an object literal/array) it shouldn't matter, but IE8
516
- * freaks out with "Expected identifier".
517
- */
518
- if (in_array($property, $keywords)) {
519
- return $match[0];
520
- }
521
-
522
- /*
523
- * See if the property is in a variable-like format (e.g.
524
- * array['key-here'] can't be replaced by array.key-here since '-'
525
- * is not a valid character there.
526
- */
527
- if (!preg_match('/^'.$minifier::REGEX_VARIABLE.'$/u', $property)) {
528
- return $match[0];
529
- }
530
-
531
- return '.'.$property;
532
- };
533
-
534
- /*
535
- * Figure out if previous character is a variable name (of the array
536
- * we want to use property notation on) - this is to make sure
537
- * standalone ['value'] arrays aren't confused for keys-of-an-array.
538
- * We can (and only have to) check the last character, because PHP's
539
- * regex implementation doesn't allow unfixed-length look-behind
540
- * assertions.
541
- */
542
- preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
543
- $previousChar = $previousChar[1];
544
-
545
- /*
546
- * Make sure word preceding the ['value'] is not a keyword, e.g.
547
- * return['x']. Because -again- PHP's regex implementation doesn't allow
548
- * unfixed-length look-behind assertions, I'm just going to do a lot of
549
- * separate look-behind assertions, one for each keyword.
550
- */
551
- $keywords = $this->getKeywordsForRegex($keywords);
552
- $keywords = '(?<!'.implode(')(?<!', $keywords).')';
553
-
554
- return preg_replace_callback('/(?<='.$previousChar.'|\])'.$keywords.'\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
555
- }
556
-
557
- /**
558
- * Replaces true & false by !0 and !1.
559
- *
560
- * @param string $content
561
- *
562
- * @return string
563
- */
564
- protected function shortenBools($content)
565
- {
566
- /*
567
- * 'true' or 'false' could be used as property names (which may be
568
- * followed by whitespace) - we must not replace those!
569
- * Since PHP doesn't allow variable-length (to account for the
570
- * whitespace) lookbehind assertions, I need to capture the leading
571
- * character and check if it's a `.`
572
- */
573
- $callback = function ($match) {
574
- if (trim($match[1]) === '.') {
575
- return $match[0];
576
- }
577
-
578
- return $match[1].($match[2] === 'true' ? '!0' : '!1');
579
- };
580
- $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
581
-
582
- // for(;;) is exactly the same as while(true), but shorter :)
583
- $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
584
-
585
- // now make sure we didn't turn any do ... while(true) into do ... for(;;)
586
- preg_match_all('/\bdo\b/', $content, $dos, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
587
-
588
- // go backward to make sure positional offsets aren't altered when $content changes
589
- $dos = array_reverse($dos);
590
- foreach ($dos as $do) {
591
- $offsetDo = $do[0][1];
592
-
593
- // find all `while` (now `for`) following `do`: one of those must be
594
- // associated with the `do` and be turned back into `while`
595
- preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
596
- foreach ($whiles as $while) {
597
- $offsetWhile = $while[0][1];
598
-
599
- $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
600
- $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
601
- if ($open === $close) {
602
- // only restore `while` if amount of `{` and `}` are the same;
603
- // otherwise, that `for` isn't associated with this `do`
604
- $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
605
- break;
606
- }
607
- }
608
- }
609
-
610
- return $content;
611
- }
612
- }
1
+ <?php
2
+ /**
3
+ * JavaScript minifier
4
+ *
5
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
+ *
7
+ * @author Matthias Mullie <minify@mullie.eu>
8
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
+ * @license MIT License
10
+ */
11
+ namespace FVM\MatthiasMullie\Minify;
12
+
13
+ /**
14
+ * JavaScript Minifier Class
15
+ *
16
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
17
+ *
18
+ * @package Minify
19
+ * @author Matthias Mullie <minify@mullie.eu>
20
+ * @author Tijs Verkoyen <minify@verkoyen.eu>
21
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
22
+ * @license MIT License
23
+ */
24
+ class JS extends Minify
25
+ {
26
+ /**
27
+ * Var-matching regex based on http://stackoverflow.com/a/9337047/802993.
28
+ *
29
+ * Note that regular expressions using that bit must have the PCRE_UTF8
30
+ * pattern modifier (/u) set.
31
+ *
32
+ * @var string
33
+ */
34
+ const REGEX_VARIABLE = '\b[$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}][$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}0-9\x{0300}-\x{036f}\x{0483}-\x{0487}\x{0591}-\x{05bd}\x{05bf}\x{05c1}\x{05c2}\x{05c4}\x{05c5}\x{05c7}\x{0610}-\x{061a}\x{064b}-\x{0669}\x{0670}\x{06d6}-\x{06dc}\x{06df}-\x{06e4}\x{06e7}\x{06e8}\x{06ea}-\x{06ed}\x{06f0}-\x{06f9}\x{0711}\x{0730}-\x{074a}\x{07a6}-\x{07b0}\x{07c0}-\x{07c9}\x{07eb}-\x{07f3}\x{0816}-\x{0819}\x{081b}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082d}\x{0859}-\x{085b}\x{08e4}-\x{08fe}\x{0900}-\x{0903}\x{093a}-\x{093c}\x{093e}-\x{094f}\x{0951}-\x{0957}\x{0962}\x{0963}\x{0966}-\x{096f}\x{0981}-\x{0983}\x{09bc}\x{09be}-\x{09c4}\x{09c7}\x{09c8}\x{09cb}-\x{09cd}\x{09d7}\x{09e2}\x{09e3}\x{09e6}-\x{09ef}\x{0a01}-\x{0a03}\x{0a3c}\x{0a3e}-\x{0a42}\x{0a47}\x{0a48}\x{0a4b}-\x{0a4d}\x{0a51}\x{0a66}-\x{0a71}\x{0a75}\x{0a81}-\x{0a83}\x{0abc}\x{0abe}-\x{0ac5}\x{0ac7}-\x{0ac9}\x{0acb}-\x{0acd}\x{0ae2}\x{0ae3}\x{0ae6}-\x{0aef}\x{0b01}-\x{0b03}\x{0b3c}\x{0b3e}-\x{0b44}\x{0b47}\x{0b48}\x{0b4b}-\x{0b4d}\x{0b56}\x{0b57}\x{0b62}\x{0b63}\x{0b66}-\x{0b6f}\x{0b82}\x{0bbe}-\x{0bc2}\x{0bc6}-\x{0bc8}\x{0bca}-\x{0bcd}\x{0bd7}\x{0be6}-\x{0bef}\x{0c01}-\x{0c03}\x{0c3e}-\x{0c44}\x{0c46}-\x{0c48}\x{0c4a}-\x{0c4d}\x{0c55}\x{0c56}\x{0c62}\x{0c63}\x{0c66}-\x{0c6f}\x{0c82}\x{0c83}\x{0cbc}\x{0cbe}-\x{0cc4}\x{0cc6}-\x{0cc8}\x{0cca}-\x{0ccd}\x{0cd5}\x{0cd6}\x{0ce2}\x{0ce3}\x{0ce6}-\x{0cef}\x{0d02}\x{0d03}\x{0d3e}-\x{0d44}\x{0d46}-\x{0d48}\x{0d4a}-\x{0d4d}\x{0d57}\x{0d62}\x{0d63}\x{0d66}-\x{0d6f}\x{0d82}\x{0d83}\x{0dca}\x{0dcf}-\x{0dd4}\x{0dd6}\x{0dd8}-\x{0ddf}\x{0df2}\x{0df3}\x{0e31}\x{0e34}-\x{0e3a}\x{0e47}-\x{0e4e}\x{0e50}-\x{0e59}\x{0eb1}\x{0eb4}-\x{0eb9}\x{0ebb}\x{0ebc}\x{0ec8}-\x{0ecd}\x{0ed0}-\x{0ed9}\x{0f18}\x{0f19}\x{0f20}-\x{0f29}\x{0f35}\x{0f37}\x{0f39}\x{0f3e}\x{0f3f}\x{0f71}-\x{0f84}\x{0f86}\x{0f87}\x{0f8d}-\x{0f97}\x{0f99}-\x{0fbc}\x{0fc6}\x{102b}-\x{103e}\x{1040}-\x{1049}\x{1056}-\x{1059}\x{105e}-\x{1060}\x{1062}-\x{1064}\x{1067}-\x{106d}\x{1071}-\x{1074}\x{1082}-\x{108d}\x{108f}-\x{109d}\x{135d}-\x{135f}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17d3}\x{17dd}\x{17e0}-\x{17e9}\x{180b}-\x{180d}\x{1810}-\x{1819}\x{18a9}\x{1920}-\x{192b}\x{1930}-\x{193b}\x{1946}-\x{194f}\x{19b0}-\x{19c0}\x{19c8}\x{19c9}\x{19d0}-\x{19d9}\x{1a17}-\x{1a1b}\x{1a55}-\x{1a5e}\x{1a60}-\x{1a7c}\x{1a7f}-\x{1a89}\x{1a90}-\x{1a99}\x{1b00}-\x{1b04}\x{1b34}-\x{1b44}\x{1b50}-\x{1b59}\x{1b6b}-\x{1b73}\x{1b80}-\x{1b82}\x{1ba1}-\x{1bad}\x{1bb0}-\x{1bb9}\x{1be6}-\x{1bf3}\x{1c24}-\x{1c37}\x{1c40}-\x{1c49}\x{1c50}-\x{1c59}\x{1cd0}-\x{1cd2}\x{1cd4}-\x{1ce8}\x{1ced}\x{1cf2}-\x{1cf4}\x{1dc0}-\x{1de6}\x{1dfc}-\x{1dff}\x{200c}\x{200d}\x{203f}\x{2040}\x{2054}\x{20d0}-\x{20dc}\x{20e1}\x{20e5}-\x{20f0}\x{2cef}-\x{2cf1}\x{2d7f}\x{2de0}-\x{2dff}\x{302a}-\x{302f}\x{3099}\x{309a}\x{a620}-\x{a629}\x{a66f}\x{a674}-\x{a67d}\x{a69f}\x{a6f0}\x{a6f1}\x{a802}\x{a806}\x{a80b}\x{a823}-\x{a827}\x{a880}\x{a881}\x{a8b4}-\x{a8c4}\x{a8d0}-\x{a8d9}\x{a8e0}-\x{a8f1}\x{a900}-\x{a909}\x{a926}-\x{a92d}\x{a947}-\x{a953}\x{a980}-\x{a983}\x{a9b3}-\x{a9c0}\x{a9d0}-\x{a9d9}\x{aa29}-\x{aa36}\x{aa43}\x{aa4c}\x{aa4d}\x{aa50}-\x{aa59}\x{aa7b}\x{aab0}\x{aab2}-\x{aab4}\x{aab7}\x{aab8}\x{aabe}\x{aabf}\x{aac1}\x{aaeb}-\x{aaef}\x{aaf5}\x{aaf6}\x{abe3}-\x{abea}\x{abec}\x{abed}\x{abf0}-\x{abf9}\x{fb1e}\x{fe00}-\x{fe0f}\x{fe20}-\x{fe26}\x{fe33}\x{fe34}\x{fe4d}-\x{fe4f}\x{ff10}-\x{ff19}\x{ff3f}]*\b';
35
+
36
+ /**
37
+ * Full list of JavaScript reserved words.
38
+ * Will be loaded from /data/js/keywords_reserved.txt.
39
+ *
40
+ * @see https://mathiasbynens.be/notes/reserved-keywords
41
+ *
42
+ * @var string[]
43
+ */
44
+ protected $keywordsReserved = array();
45
+
46
+ /**
47
+ * List of JavaScript reserved words that accept a <variable, value, ...>
48
+ * after them. Some end of lines are not the end of a statement, like with
49
+ * these keywords.
50
+ *
51
+ * E.g.: we shouldn't insert a ; after this else
52
+ * else
53
+ * console.log('this is quite fine')
54
+ *
55
+ * Will be loaded from /data/js/keywords_before.txt
56
+ *
57
+ * @var string[]
58
+ */
59
+ protected $keywordsBefore = array();
60
+
61
+ /**
62
+ * List of JavaScript reserved words that accept a <variable, value, ...>
63
+ * before them. Some end of lines are not the end of a statement, like when
64
+ * continued by one of these keywords on the newline.
65
+ *
66
+ * E.g.: we shouldn't insert a ; before this instanceof
67
+ * variable
68
+ * instanceof String
69
+ *
70
+ * Will be loaded from /data/js/keywords_after.txt
71
+ *
72
+ * @var string[]
73
+ */
74
+ protected $keywordsAfter = array();
75
+
76
+ /**
77
+ * List of all JavaScript operators.
78
+ *
79
+ * Will be loaded from /data/js/operators.txt
80
+ *
81
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
82
+ *
83
+ * @var string[]
84
+ */
85
+ protected $operators = array();
86
+
87
+ /**
88
+ * List of JavaScript operators that accept a <variable, value, ...> after
89
+ * them. Some end of lines are not the end of a statement, like with these
90
+ * operators.
91
+ *
92
+ * Note: Most operators are fine, we've only removed ++ and --.
93
+ * ++ & -- have to be joined with the value they're in-/decrementing.
94
+ *
95
+ * Will be loaded from /data/js/operators_before.txt
96
+ *
97
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
98
+ *
99
+ * @var string[]
100
+ */
101
+ protected $operatorsBefore = array();
102
+
103
+ /**
104
+ * List of JavaScript operators that accept a <variable, value, ...> before
105
+ * them. Some end of lines are not the end of a statement, like when
106
+ * continued by one of these operators on the newline.
107
+ *
108
+ * Note: Most operators are fine, we've only removed ), ], ++, --, ! and ~.
109
+ * There can't be a newline separating ! or ~ and whatever it is negating.
110
+ * ++ & -- have to be joined with the value they're in-/decrementing.
111
+ * ) & ] are "special" in that they have lots or usecases. () for example
112
+ * is used for function calls, for grouping, in if () and for (), ...
113
+ *
114
+ * Will be loaded from /data/js/operators_after.txt
115
+ *
116
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
117
+ *
118
+ * @var string[]
119
+ */
120
+ protected $operatorsAfter = array();
121
+
122
+ /**
123
+ * {@inheritdoc}
124
+ */
125
+ public function __construct()
126
+ {
127
+ call_user_func_array(array('parent', '__construct'), func_get_args());
128
+
129
+ $dataDir = __DIR__.'/../data/js/';
130
+ $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
131
+ $this->keywordsReserved = file($dataDir.'keywords_reserved.txt', $options);
132
+ $this->keywordsBefore = file($dataDir.'keywords_before.txt', $options);
133
+ $this->keywordsAfter = file($dataDir.'keywords_after.txt', $options);
134
+ $this->operators = file($dataDir.'operators.txt', $options);
135
+ $this->operatorsBefore = file($dataDir.'operators_before.txt', $options);
136
+ $this->operatorsAfter = file($dataDir.'operators_after.txt', $options);
137
+ }
138
+
139
+ /**
140
+ * Minify the data.
141
+ * Perform JS optimizations.
142
+ *
143
+ * @param string[optional] $path Path to write the data to
144
+ *
145
+ * @return string The minified data
146
+ */
147
+ public function execute($path = null)
148
+ {
149
+ $content = '';
150
+
151
+ /*
152
+ * Let's first take out strings, comments and regular expressions.
153
+ * All of these can contain JS code-like characters, and we should make
154
+ * sure any further magic ignores anything inside of these.
155
+ *
156
+ * Consider this example, where we should not strip any whitespace:
157
+ * var str = "a test";
158
+ *
159
+ * Comments will be removed altogether, strings and regular expressions
160
+ * will be replaced by placeholder text, which we'll restore later.
161
+ */
162
+ $this->extractStrings('\'"`');
163
+ $this->stripComments();
164
+ $this->extractRegex();
165
+
166
+ // loop files
167
+ foreach ($this->data as $source => $js) {
168
+ // take out strings, comments & regex (for which we've registered
169
+ // the regexes just a few lines earlier)
170
+ $js = $this->replace($js);
171
+
172
+ $js = $this->propertyNotation($js);
173
+ $js = $this->shortenBools($js);
174
+ $js = $this->stripWhitespace($js);
175
+
176
+ // combine js: separating the scripts by a ;
177
+ $content .= $js.";";
178
+ }
179
+
180
+ // clean up leftover `;`s from the combination of multiple scripts
181
+ $content = ltrim($content, ';');
182
+ $content = (string) substr($content, 0, -1);
183
+
184
+ /*
185
+ * Earlier, we extracted strings & regular expressions and replaced them
186
+ * with placeholder text. This will restore them.
187
+ */
188
+ $content = $this->restoreExtractedData($content);
189
+
190
+ return $content;
191
+ }
192
+
193
+ /**
194
+ * Strip comments from source code.
195
+ */
196
+ protected function stripComments()
197
+ {
198
+ // PHP only supports $this inside anonymous functions since 5.4
199
+ $minifier = $this;
200
+ $callback = function ($match) use ($minifier) {
201
+ $count = count($minifier->extracted);
202
+ $placeholder = '/*'.$count.'*/';
203
+ $minifier->extracted[$placeholder] = $match[0];
204
+
205
+ return $placeholder;
206
+ };
207
+ // multi-line comments
208
+ $this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
209
+ $this->registerPattern('/\/\*.*?\*\//s', '');
210
+
211
+ // single-line comments
212
+ $this->registerPattern('/\/\/.*$/m', '');
213
+ }
214
+
215
+ /**
216
+ * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
217
+ *
218
+ * The content inside the regex can contain characters that may be confused
219
+ * for JS code: e.g. it could contain whitespace it needs to match & we
220
+ * don't want to strip whitespace in there.
221
+ *
222
+ * The regex can be pretty simple: we don't have to care about comments,
223
+ * (which also use slashes) because stripComments() will have stripped those
224
+ * already.
225
+ *
226
+ * This method will replace all string content with simple REGEX#
227
+ * placeholder text, so we've rid all regular expressions from characters
228
+ * that may be misinterpreted. Original regex content will be saved in
229
+ * $this->extracted and after doing all other minifying, we can restore the
230
+ * original content via restoreRegex()
231
+ */
232
+ protected function extractRegex()
233
+ {
234
+ // PHP only supports $this inside anonymous functions since 5.4
235
+ $minifier = $this;
236
+ $callback = function ($match) use ($minifier) {
237
+ $count = count($minifier->extracted);
238
+ $placeholder = '"'.$count.'"';
239
+ $minifier->extracted[$placeholder] = $match[0];
240
+
241
+ return $placeholder;
242
+ };
243
+
244
+ // match all chars except `/` and `\`
245
+ // `\` is allowed though, along with whatever char follows (which is the
246
+ // one being escaped)
247
+ // this should allow all chars, except for an unescaped `/` (= the one
248
+ // closing the regex)
249
+ // then also ignore bare `/` inside `[]`, where they don't need to be
250
+ // escaped: anything inside `[]` can be ignored safely
251
+ $pattern = '\\/(?!\*)(?:[^\\[\\/\\\\\n\r]++|(?:\\\\.)++|(?:\\[(?:[^\\]\\\\\n\r]++|(?:\\\\.)++)++\\])++)++\\/[gimuy]*';
252
+
253
+ // a regular expression can only be followed by a few operators or some
254
+ // of the RegExp methods (a `\` followed by a variable or value is
255
+ // likely part of a division, not a regex)
256
+ $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
257
+ $before = '([=:,;\+\-\*\/\}\(\{\[&\|!]|^|'.implode('|', $keywords).')\s*';
258
+ $propertiesAndMethods = array(
259
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
260
+ 'constructor',
261
+ 'flags',
262
+ 'global',
263
+ 'ignoreCase',
264
+ 'multiline',
265
+ 'source',
266
+ 'sticky',
267
+ 'unicode',
268
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
269
+ 'compile(',
270
+ 'exec(',
271
+ 'test(',
272
+ 'toSource(',
273
+ 'toString(',
274
+ );
275
+ $delimiters = array_fill(0, count($propertiesAndMethods), '/');
276
+ $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
277
+ $after = '(?=\s*([\.,;\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).')))';
278
+ $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
279
+
280
+ // regular expressions following a `)` are rather annoying to detect...
281
+ // quite often, `/` after `)` is a division operator & if it happens to
282
+ // be followed by another one (or a comment), it is likely to be
283
+ // confused for a regular expression
284
+ // however, it's perfectly possible for a regex to follow a `)`: after
285
+ // a single-line `if()`, `while()`, ... statement, for example
286
+ // since, when they occur like that, they're always the start of a
287
+ // statement, there's only a limited amount of ways they can be useful:
288
+ // by calling the regex methods directly
289
+ // if a regex following `)` is not followed by `.<property or method>`,
290
+ // it's quite likely not a regex
291
+ $before = '\)\s*';
292
+ $after = '(?=\s*\.('.implode('|', $propertiesAndMethods).'))';
293
+ $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
294
+
295
+ // 1 more edge case: a regex can be followed by a lot more operators or
296
+ // keywords if there's a newline (ASI) in between, where the operator
297
+ // actually starts a new statement
298
+ // (https://github.com/matthiasmullie/minify/issues/56)
299
+ $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
300
+ $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
301
+ $after = '(?=\s*\n\s*('.implode('|', $operators).'))';
302
+ $this->registerPattern('/'.$pattern.$after.'/', $callback);
303
+ }
304
+
305
+ /**
306
+ * Strip whitespace.
307
+ *
308
+ * We won't strip *all* whitespace, but as much as possible. The thing that
309
+ * we'll preserve are newlines we're unsure about.
310
+ * JavaScript doesn't require statements to be terminated with a semicolon.
311
+ * It will automatically fix missing semicolons with ASI (automatic semi-
312
+ * colon insertion) at the end of line causing errors (without semicolon.)
313
+ *
314
+ * Because it's sometimes hard to tell if a newline is part of a statement
315
+ * that should be terminated or not, we'll just leave some of them alone.
316
+ *
317
+ * @param string $content The content to strip the whitespace for
318
+ *
319
+ * @return string
320
+ */
321
+ protected function stripWhitespace($content)
322
+ {
323
+ // uniform line endings, make them all line feed
324
+ $content = str_replace(array("\r\n", "\r"), "\n", $content);
325
+
326
+ // collapse all non-line feed whitespace into a single space
327
+ $content = preg_replace('/[^\S\n]+/', ' ', $content);
328
+
329
+ // strip leading & trailing whitespace
330
+ $content = str_replace(array(" \n", "\n "), "\n", $content);
331
+
332
+ // collapse consecutive line feeds into just 1
333
+ $content = preg_replace('/\n+/', "\n", $content);
334
+
335
+ $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
336
+ $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
337
+ $operators = $this->getOperatorsForRegex($this->operators, '/');
338
+ $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
339
+ $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
340
+
341
+ // strip whitespace that ends in (or next line begin with) an operator
342
+ // that allows statements to be broken up over multiple lines
343
+ unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
344
+ $content = preg_replace(
345
+ array(
346
+ '/('.implode('|', $operatorsBefore).')\s+/',
347
+ '/\s+('.implode('|', $operatorsAfter).')/',
348
+ ),
349
+ '\\1',
350
+ $content
351
+ );
352
+
353
+ // make sure + and - can't be mistaken for, or joined into ++ and --
354
+ $content = preg_replace(
355
+ array(
356
+ '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
357
+ '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
358
+ ),
359
+ '\\1',
360
+ $content
361
+ );
362
+
363
+ // collapse whitespace around reserved words into single space
364
+ $content = preg_replace('/(^|[;\}\s])\K('.implode('|', $keywordsBefore).')\s+/', '\\2 ', $content);
365
+ $content = preg_replace('/\s+('.implode('|', $keywordsAfter).')(?=([;\{\s]|$))/', ' \\1', $content);
366
+
367
+ /*
368
+ * We didn't strip whitespace after a couple of operators because they
369
+ * could be used in different contexts and we can't be sure it's ok to
370
+ * strip the newlines. However, we can safely strip any non-line feed
371
+ * whitespace that follows them.
372
+ */
373
+ $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
374
+ $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
375
+ $content = preg_replace('/('.implode('|', $operatorsDiffBefore).')[^\S\n]+/', '\\1', $content);
376
+ $content = preg_replace('/[^\S\n]+('.implode('|', $operatorsDiffAfter).')/', '\\1', $content);
377
+
378
+ /*
379
+ * Whitespace after `return` can be omitted in a few occasions
380
+ * (such as when followed by a string or regex)
381
+ * Same for whitespace in between `)` and `{`, or between `{` and some
382
+ * keywords.
383
+ */
384
+ $content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
385
+ $content = preg_replace('/\)\s+\{/', '){', $content);
386
+ $content = preg_replace('/}\n(else|catch|finally)\b/', '}$1', $content);
387
+
388
+ /*
389
+ * Get rid of double semicolons, except where they can be used like:
390
+ * "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
391
+ * I'll safeguard these double semicolons inside for-loops by
392
+ * temporarily replacing them with an invalid condition: they won't have
393
+ * a double semicolon and will be easy to spot to restore afterwards.
394
+ */
395
+ $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
396
+ $content = preg_replace('/;+/', ';', $content);
397
+ $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
398
+
399
+ /*
400
+ * Next, we'll be removing all semicolons where ASI kicks in.
401
+ * for-loops however, can have an empty body (ending in only a
402
+ * semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
403
+ * Here, nothing happens during the loop; it's just used to keep
404
+ * increasing `i`. With that ; omitted, the next line would be expected
405
+ * to be the for-loop's body... Same goes for while loops.
406
+ * I'm going to double that semicolon (if any) so after the next line,
407
+ * which strips semicolons here & there, we're still left with this one.
408
+ */
409
+ $content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
410
+ $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
411
+ /*
412
+ * Below will also keep `;` after a `do{}while();` along with `while();`
413
+ * While these could be stripped after do-while, detecting this
414
+ * distinction is cumbersome, so I'll play it safe and make sure `;`
415
+ * after any kind of `while` is kept.
416
+ */
417
+ $content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
418
+
419
+ /*
420
+ * We also can't strip empty else-statements. Even though they're
421
+ * useless and probably shouldn't be in the code in the first place, we
422
+ * shouldn't be stripping the `;` that follows it as it breaks the code.
423
+ * We can just remove those useless else-statements completely.
424
+ *
425
+ * @see https://github.com/matthiasmullie/minify/issues/91
426
+ */
427
+ $content = preg_replace('/else;/s', '', $content);
428
+
429
+ /*
430
+ * We also don't really want to terminate statements followed by closing
431
+ * curly braces (which we've ignored completely up until now) or end-of-
432
+ * script: ASI will kick in here & we're all about minifying.
433
+ * Semicolons at beginning of the file don't make any sense either.
434
+ */
435
+ $content = preg_replace('/;(\}|$)/s', '\\1', $content);
436
+ $content = ltrim($content, ';');
437
+
438
+ // get rid of remaining whitespace af beginning/end
439
+ return trim($content);
440
+ }
441
+
442
+ /**
443
+ * We'll strip whitespace around certain operators with regular expressions.
444
+ * This will prepare the given array by escaping all characters.
445
+ *
446
+ * @param string[] $operators
447
+ * @param string $delimiter
448
+ *
449
+ * @return string[]
450
+ */
451
+ protected function getOperatorsForRegex(array $operators, $delimiter = '/')
452
+ {
453
+ // escape operators for use in regex
454
+ $delimiters = array_fill(0, count($operators), $delimiter);
455
+ $escaped = array_map('preg_quote', $operators, $delimiters);
456
+
457
+ $operators = array_combine($operators, $escaped);
458
+
459
+ // ignore + & - for now, they'll get special treatment
460
+ unset($operators['+'], $operators['-']);
461
+
462
+ // dot can not just immediately follow a number; it can be confused for
463
+ // decimal point, or calling a method on it, e.g. 42 .toString()
464
+ $operators['.'] = '(?<![0-9]\s)\.';
465
+
466
+ // don't confuse = with other assignment shortcuts (e.g. +=)
467
+ $chars = preg_quote('+-*\=<>%&|', $delimiter);
468
+ $operators['='] = '(?<!['.$chars.'])\=';
469
+
470
+ return $operators;
471
+ }
472
+
473
+ /**
474
+ * We'll strip whitespace around certain keywords with regular expressions.
475
+ * This will prepare the given array by escaping all characters.
476
+ *
477
+ * @param string[] $keywords
478
+ * @param string $delimiter
479
+ *
480
+ * @return string[]
481
+ */
482
+ protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
483
+ {
484
+ // escape keywords for use in regex
485
+ $delimiter = array_fill(0, count($keywords), $delimiter);
486
+ $escaped = array_map('preg_quote', $keywords, $delimiter);
487
+
488
+ // add word boundaries
489
+ array_walk($keywords, function ($value) {
490
+ return '\b'.$value.'\b';
491
+ });
492
+
493
+ $keywords = array_combine($keywords, $escaped);
494
+
495
+ return $keywords;
496
+ }
497
+
498
+ /**
499
+ * Replaces all occurrences of array['key'] by array.key.
500
+ *
501
+ * @param string $content
502
+ *
503
+ * @return string
504
+ */
505
+ protected function propertyNotation($content)
506
+ {
507
+ // PHP only supports $this inside anonymous functions since 5.4
508
+ $minifier = $this;
509
+ $keywords = $this->keywordsReserved;
510
+ $callback = function ($match) use ($minifier, $keywords) {
511
+ $property = trim($minifier->extracted[$match[1]], '\'"');
512
+
513
+ /*
514
+ * Check if the property is a reserved keyword. In this context (as
515
+ * property of an object literal/array) it shouldn't matter, but IE8
516
+ * freaks out with "Expected identifier".
517
+ */
518
+ if (in_array($property, $keywords)) {
519
+ return $match[0];
520
+ }
521
+
522
+ /*
523
+ * See if the property is in a variable-like format (e.g.
524
+ * array['key-here'] can't be replaced by array.key-here since '-'
525
+ * is not a valid character there.
526
+ */
527
+ if (!preg_match('/^'.$minifier::REGEX_VARIABLE.'$/u', $property)) {
528
+ return $match[0];
529
+ }
530
+
531
+ return '.'.$property;
532
+ };
533
+
534
+ /*
535
+ * Figure out if previous character is a variable name (of the array
536
+ * we want to use property notation on) - this is to make sure
537
+ * standalone ['value'] arrays aren't confused for keys-of-an-array.
538
+ * We can (and only have to) check the last character, because PHP's
539
+ * regex implementation doesn't allow unfixed-length look-behind
540
+ * assertions.
541
+ */
542
+ preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
543
+ $previousChar = $previousChar[1];
544
+
545
+ /*
546
+ * Make sure word preceding the ['value'] is not a keyword, e.g.
547
+ * return['x']. Because -again- PHP's regex implementation doesn't allow
548
+ * unfixed-length look-behind assertions, I'm just going to do a lot of
549
+ * separate look-behind assertions, one for each keyword.
550
+ */
551
+ $keywords = $this->getKeywordsForRegex($keywords);
552
+ $keywords = '(?<!'.implode(')(?<!', $keywords).')';
553
+
554
+ return preg_replace_callback('/(?<='.$previousChar.'|\])'.$keywords.'\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
555
+ }
556
+
557
+ /**
558
+ * Replaces true & false by !0 and !1.
559
+ *
560
+ * @param string $content
561
+ *
562
+ * @return string
563
+ */
564
+ protected function shortenBools($content)
565
+ {
566
+ /*
567
+ * 'true' or 'false' could be used as property names (which may be
568
+ * followed by whitespace) - we must not replace those!
569
+ * Since PHP doesn't allow variable-length (to account for the
570
+ * whitespace) lookbehind assertions, I need to capture the leading
571
+ * character and check if it's a `.`
572
+ */
573
+ $callback = function ($match) {
574
+ if (trim($match[1]) === '.') {
575
+ return $match[0];
576
+ }
577
+
578
+ return $match[1].($match[2] === 'true' ? '!0' : '!1');
579
+ };
580
+ $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
581
+
582
+ // for(;;) is exactly the same as while(true), but shorter :)
583
+ $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
584
+
585
+ // now make sure we didn't turn any do ... while(true) into do ... for(;;)
586
+ preg_match_all('/\bdo\b/', $content, $dos, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
587
+
588
+ // go backward to make sure positional offsets aren't altered when $content changes
589
+ $dos = array_reverse($dos);
590
+ foreach ($dos as $do) {
591
+ $offsetDo = $do[0][1];
592
+
593
+ // find all `while` (now `for`) following `do`: one of those must be
594
+ // associated with the `do` and be turned back into `while`
595
+ preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
596
+ foreach ($whiles as $while) {
597
+ $offsetWhile = $while[0][1];
598
+
599
+ $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
600
+ $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
601
+ if ($open === $close) {
602
+ // only restore `while` if amount of `{` and `}` are the same;
603
+ // otherwise, that `for` isn't associated with this `do`
604
+ $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
605
+ break;
606
+ }
607
+ }
608
+ }
609
+
610
+ return $content;
611
+ }
612
+ }
libs/matthiasmullie/minify/src/Minify.php CHANGED
@@ -1,459 +1,497 @@
1
- <?php
2
- /**
3
- * Abstract minifier class
4
- *
5
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
- *
7
- * @author Matthias Mullie <minify@mullie.eu>
8
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
- * @license MIT License
10
- */
11
- namespace MatthiasMullie\Minify;
12
-
13
- use MatthiasMullie\Minify\Exceptions\IOException;
14
- use Psr\Cache\CacheItemInterface;
15
-
16
- /**
17
- * Abstract minifier class.
18
- *
19
- * Please report bugs on https://github.com/matthiasmullie/minify/issues
20
- *
21
- * @package Minify
22
- * @author Matthias Mullie <minify@mullie.eu>
23
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
24
- * @license MIT License
25
- */
26
- abstract class Minify
27
- {
28
- /**
29
- * The data to be minified.
30
- *
31
- * @var string[]
32
- */
33
- protected $data = array();
34
-
35
- /**
36
- * Array of patterns to match.
37
- *
38
- * @var string[]
39
- */
40
- protected $patterns = array();
41
-
42
- /**
43
- * This array will hold content of strings and regular expressions that have
44
- * been extracted from the JS source code, so we can reliably match "code",
45
- * without having to worry about potential "code-like" characters inside.
46
- *
47
- * @var string[]
48
- */
49
- public $extracted = array();
50
-
51
- /**
52
- * Init the minify class - optionally, code may be passed along already.
53
- */
54
- public function __construct(/* $data = null, ... */)
55
- {
56
- // it's possible to add the source through the constructor as well ;)
57
- if (func_num_args()) {
58
- call_user_func_array(array($this, 'add'), func_get_args());
59
- }
60
- }
61
-
62
- /**
63
- * Add a file or straight-up code to be minified.
64
- *
65
- * @param string|string[] $data
66
- *
67
- * @return static
68
- */
69
- public function add($data /* $data = null, ... */)
70
- {
71
- // bogus "usage" of parameter $data: scrutinizer warns this variable is
72
- // not used (we're using func_get_args instead to support overloading),
73
- // but it still needs to be defined because it makes no sense to have
74
- // this function without argument :)
75
- $args = array($data) + func_get_args();
76
-
77
- // this method can be overloaded
78
- foreach ($args as $data) {
79
- if (is_array($data)) {
80
- call_user_func_array(array($this, 'add'), $data);
81
- continue;
82
- }
83
-
84
- // redefine var
85
- $data = (string) $data;
86
-
87
- // load data
88
- $value = $this->load($data);
89
- $key = ($data != $value) ? $data : count($this->data);
90
-
91
- // replace CR linefeeds etc.
92
- // @see https://github.com/matthiasmullie/minify/pull/139
93
- $value = str_replace(array("\r\n", "\r"), "\n", $value);
94
-
95
- // store data
96
- $this->data[$key] = $value;
97
- }
98
-
99
- return $this;
100
- }
101
-
102
- /**
103
- * Minify the data & (optionally) saves it to a file.
104
- *
105
- * @param string[optional] $path Path to write the data to
106
- *
107
- * @return string The minified data
108
- */
109
- public function minify($path = null)
110
- {
111
- $content = $this->execute($path);
112
-
113
- // save to path
114
- if ($path !== null) {
115
- $this->save($content, $path);
116
- }
117
-
118
- return $content;
119
- }
120
-
121
- /**
122
- * Minify & gzip the data & (optionally) saves it to a file.
123
- *
124
- * @param string[optional] $path Path to write the data to
125
- * @param int[optional] $level Compression level, from 0 to 9
126
- *
127
- * @return string The minified & gzipped data
128
- */
129
- public function gzip($path = null, $level = 9)
130
- {
131
- $content = $this->execute($path);
132
- $content = gzencode($content, $level, FORCE_GZIP);
133
-
134
- // save to path
135
- if ($path !== null) {
136
- $this->save($content, $path);
137
- }
138
-
139
- return $content;
140
- }
141
-
142
- /**
143
- * Minify the data & write it to a CacheItemInterface object.
144
- *
145
- * @param CacheItemInterface $item Cache item to write the data to
146
- *
147
- * @return CacheItemInterface Cache item with the minifier data
148
- */
149
- public function cache(CacheItemInterface $item)
150
- {
151
- $content = $this->execute();
152
- $item->set($content);
153
-
154
- return $item;
155
- }
156
-
157
- /**
158
- * Minify the data.
159
- *
160
- * @param string[optional] $path Path to write the data to
161
- *
162
- * @return string The minified data
163
- */
164
- abstract public function execute($path = null);
165
-
166
- /**
167
- * Load data.
168
- *
169
- * @param string $data Either a path to a file or the content itself
170
- *
171
- * @return string
172
- */
173
- protected function load($data)
174
- {
175
- // check if the data is a file
176
- if ($this->canImportFile($data)) {
177
- $data = file_get_contents($data);
178
-
179
- // strip BOM, if any
180
- if (substr($data, 0, 3) == "\xef\xbb\xbf") {
181
- $data = substr($data, 3);
182
- }
183
- }
184
-
185
- return $data;
186
- }
187
-
188
- /**
189
- * Save to file.
190
- *
191
- * @param string $content The minified data
192
- * @param string $path The path to save the minified data to
193
- *
194
- * @throws IOException
195
- */
196
- protected function save($content, $path)
197
- {
198
- $handler = $this->openFileForWriting($path);
199
-
200
- $this->writeToFile($handler, $content);
201
-
202
- @fclose($handler);
203
- }
204
-
205
- /**
206
- * Register a pattern to execute against the source content.
207
- *
208
- * @param string $pattern PCRE pattern
209
- * @param string|callable $replacement Replacement value for matched pattern
210
- */
211
- protected function registerPattern($pattern, $replacement = '')
212
- {
213
- // study the pattern, we'll execute it more than once
214
- $pattern .= 'S';
215
-
216
- $this->patterns[] = array($pattern, $replacement);
217
- }
218
-
219
- /**
220
- * We can't "just" run some regular expressions against JavaScript: it's a
221
- * complex language. E.g. having an occurrence of // xyz would be a comment,
222
- * unless it's used within a string. Of you could have something that looks
223
- * like a 'string', but inside a comment.
224
- * The only way to accurately replace these pieces is to traverse the JS one
225
- * character at a time and try to find whatever starts first.
226
- *
227
- * @param string $content The content to replace patterns in
228
- *
229
- * @return string The (manipulated) content
230
- */
231
- protected function replace($content)
232
- {
233
- $processed = '';
234
- $positions = array_fill(0, count($this->patterns), -1);
235
- $matches = array();
236
-
237
- while ($content) {
238
- // find first match for all patterns
239
- foreach ($this->patterns as $i => $pattern) {
240
- list($pattern, $replacement) = $pattern;
241
-
242
- // we can safely ignore patterns for positions we've unset earlier,
243
- // because we know these won't show up anymore
244
- if (array_key_exists($i, $positions) == false) {
245
- continue;
246
- }
247
-
248
- // no need to re-run matches that are still in the part of the
249
- // content that hasn't been processed
250
- if ($positions[$i] >= 0) {
251
- continue;
252
- }
253
-
254
- $match = null;
255
- if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
256
- $matches[$i] = $match;
257
-
258
- // we'll store the match position as well; that way, we
259
- // don't have to redo all preg_matches after changing only
260
- // the first (we'll still know where those others are)
261
- $positions[$i] = $match[0][1];
262
- } else {
263
- // if the pattern couldn't be matched, there's no point in
264
- // executing it again in later runs on this same content;
265
- // ignore this one until we reach end of content
266
- unset($matches[$i], $positions[$i]);
267
- }
268
- }
269
-
270
- // no more matches to find: everything's been processed, break out
271
- if (!$matches) {
272
- $processed .= $content;
273
- break;
274
- }
275
-
276
- // see which of the patterns actually found the first thing (we'll
277
- // only want to execute that one, since we're unsure if what the
278
- // other found was not inside what the first found)
279
- $discardLength = min($positions);
280
- $firstPattern = array_search($discardLength, $positions);
281
- $match = $matches[$firstPattern][0][0];
282
-
283
- // execute the pattern that matches earliest in the content string
284
- list($pattern, $replacement) = $this->patterns[$firstPattern];
285
- $replacement = $this->replacePattern($pattern, $replacement, $content);
286
-
287
- // figure out which part of the string was unmatched; that's the
288
- // part we'll execute the patterns on again next
289
- $content = (string) substr($content, $discardLength);
290
- $unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
291
-
292
- // move the replaced part to $processed and prepare $content to
293
- // again match batch of patterns against
294
- $processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
295
- $content = $unmatched;
296
-
297
- // first match has been replaced & that content is to be left alone,
298
- // the next matches will start after this replacement, so we should
299
- // fix their offsets
300
- foreach ($positions as $i => $position) {
301
- $positions[$i] -= $discardLength + strlen($match);
302
- }
303
- }
304
-
305
- return $processed;
306
- }
307
-
308
- /**
309
- * This is where a pattern is matched against $content and the matches
310
- * are replaced by their respective value.
311
- * This function will be called plenty of times, where $content will always
312
- * move up 1 character.
313
- *
314
- * @param string $pattern Pattern to match
315
- * @param string|callable $replacement Replacement value
316
- * @param string $content Content to match pattern against
317
- *
318
- * @return string
319
- */
320
- protected function replacePattern($pattern, $replacement, $content)
321
- {
322
- if (is_callable($replacement)) {
323
- return preg_replace_callback($pattern, $replacement, $content, 1, $count);
324
- } else {
325
- return preg_replace($pattern, $replacement, $content, 1, $count);
326
- }
327
- }
328
-
329
- /**
330
- * Strings are a pattern we need to match, in order to ignore potential
331
- * code-like content inside them, but we just want all of the string
332
- * content to remain untouched.
333
- *
334
- * This method will replace all string content with simple STRING#
335
- * placeholder text, so we've rid all strings from characters that may be
336
- * misinterpreted. Original string content will be saved in $this->extracted
337
- * and after doing all other minifying, we can restore the original content
338
- * via restoreStrings().
339
- *
340
- * @param string[optional] $chars
341
- * @param string[optional] $placeholderPrefix
342
- */
343
- protected function extractStrings($chars = '\'"', $placeholderPrefix = '')
344
- {
345
- // PHP only supports $this inside anonymous functions since 5.4
346
- $minifier = $this;
347
- $callback = function ($match) use ($minifier, $placeholderPrefix) {
348
- // check the second index here, because the first always contains a quote
349
- if ($match[2] === '') {
350
- /*
351
- * Empty strings need no placeholder; they can't be confused for
352
- * anything else anyway.
353
- * But we still needed to match them, for the extraction routine
354
- * to skip over this particular string.
355
- */
356
- return $match[0];
357
- }
358
-
359
- $count = count($minifier->extracted);
360
- $placeholder = $match[1].$placeholderPrefix.$count.$match[1];
361
- $minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
362
-
363
- return $placeholder;
364
- };
365
-
366
- /*
367
- * The \\ messiness explained:
368
- * * Don't count ' or " as end-of-string if it's escaped (has backslash
369
- * in front of it)
370
- * * Unless... that backslash itself is escaped (another leading slash),
371
- * in which case it's no longer escaping the ' or "
372
- * * So there can be either no backslash, or an even number
373
- * * multiply all of that times 4, to account for the escaping that has
374
- * to be done to pass the backslash into the PHP string without it being
375
- * considered as escape-char (times 2) and to get it in the regex,
376
- * escaped (times 2)
377
- */
378
- $this->registerPattern('/(['.$chars.'])(.*?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
379
- }
380
-
381
- /**
382
- * This method will restore all extracted data (strings, regexes) that were
383
- * replaced with placeholder text in extract*(). The original content was
384
- * saved in $this->extracted.
385
- *
386
- * @param string $content
387
- *
388
- * @return string
389
- */
390
- protected function restoreExtractedData($content)
391
- {
392
- if (!$this->extracted) {
393
- // nothing was extracted, nothing to restore
394
- return $content;
395
- }
396
-
397
- $content = strtr($content, $this->extracted);
398
-
399
- $this->extracted = array();
400
-
401
- return $content;
402
- }
403
-
404
- /**
405
- * Check if the path is a regular file and can be read.
406
- *
407
- * @param string $path
408
- *
409
- * @return bool
410
- */
411
- protected function canImportFile($path)
412
- {
413
- $parsed = parse_url($path);
414
- if (
415
- // file is elsewhere
416
- isset($parsed['host']) ||
417
- // file responds to queries (may change, or need to bypass cache)
418
- isset($parsed['query'])
419
- ) {
420
- return false;
421
- }
422
-
423
- return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
424
- }
425
-
426
- /**
427
- * Attempts to open file specified by $path for writing.
428
- *
429
- * @param string $path The path to the file
430
- *
431
- * @return resource Specifier for the target file
432
- *
433
- * @throws IOException
434
- */
435
- protected function openFileForWriting($path)
436
- {
437
- if (($handler = @fopen($path, 'w')) === false) {
438
- throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.');
439
- }
440
-
441
- return $handler;
442
- }
443
-
444
- /**
445
- * Attempts to write $content to the file specified by $handler. $path is used for printing exceptions.
446
- *
447
- * @param resource $handler The resource to write to
448
- * @param string $content The content to write
449
- * @param string $path The path to the file (for exception printing only)
450
- *
451
- * @throws IOException
452
- */
453
- protected function writeToFile($handler, $content, $path = '')
454
- {
455
- if (($result = @fwrite($handler, $content)) === false || ($result < strlen($content))) {
456
- throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
457
- }
458
- }
459
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Abstract minifier class
4
+ *
5
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
+ *
7
+ * @author Matthias Mullie <minify@mullie.eu>
8
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
+ * @license MIT License
10
+ */
11
+ namespace FVM\MatthiasMullie\Minify;
12
+
13
+ use FVM\MatthiasMullie\Minify\Exceptions\IOException;
14
+ use Psr\Cache\CacheItemInterface;
15
+
16
+ /**
17
+ * Abstract minifier class.
18
+ *
19
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
20
+ *
21
+ * @package Minify
22
+ * @author Matthias Mullie <minify@mullie.eu>
23
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
24
+ * @license MIT License
25
+ */
26
+ abstract class Minify
27
+ {
28
+ /**
29
+ * The data to be minified.
30
+ *
31
+ * @var string[]
32
+ */
33
+ protected $data = array();
34
+
35
+ /**
36
+ * Array of patterns to match.
37
+ *
38
+ * @var string[]
39
+ */
40
+ protected $patterns = array();
41
+
42
+ /**
43
+ * This array will hold content of strings and regular expressions that have
44
+ * been extracted from the JS source code, so we can reliably match "code",
45
+ * without having to worry about potential "code-like" characters inside.
46
+ *
47
+ * @var string[]
48
+ */
49
+ public $extracted = array();
50
+
51
+ /**
52
+ * Init the minify class - optionally, code may be passed along already.
53
+ */
54
+ public function __construct(/* $data = null, ... */)
55
+ {
56
+ // it's possible to add the source through the constructor as well ;)
57
+ if (func_num_args()) {
58
+ call_user_func_array(array($this, 'add'), func_get_args());
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Add a file or straight-up code to be minified.
64
+ *
65
+ * @param string|string[] $data
66
+ *
67
+ * @return static
68
+ */
69
+ public function add($data /* $data = null, ... */)
70
+ {
71
+ // bogus "usage" of parameter $data: scrutinizer warns this variable is
72
+ // not used (we're using func_get_args instead to support overloading),
73
+ // but it still needs to be defined because it makes no sense to have
74
+ // this function without argument :)
75
+ $args = array($data) + func_get_args();
76
+
77
+ // this method can be overloaded
78
+ foreach ($args as $data) {
79
+ if (is_array($data)) {
80
+ call_user_func_array(array($this, 'add'), $data);
81
+ continue;
82
+ }
83
+
84
+ // redefine var
85
+ $data = (string) $data;
86
+
87
+ // load data
88
+ $value = $this->load($data);
89
+ $key = ($data != $value) ? $data : count($this->data);
90
+
91
+ // replace CR linefeeds etc.
92
+ // @see https://github.com/matthiasmullie/minify/pull/139
93
+ $value = str_replace(array("\r\n", "\r"), "\n", $value);
94
+
95
+ // store data
96
+ $this->data[$key] = $value;
97
+ }
98
+
99
+ return $this;
100
+ }
101
+
102
+ /**
103
+ * Add a file to be minified.
104
+ *
105
+ * @param string|string[] $data
106
+ *
107
+ * @return static
108
+ *
109
+ * @throws IOException
110
+ */
111
+ public function addFile($data /* $data = null, ... */)
112
+ {
113
+ // bogus "usage" of parameter $data: scrutinizer warns this variable is
114
+ // not used (we're using func_get_args instead to support overloading),
115
+ // but it still needs to be defined because it makes no sense to have
116
+ // this function without argument :)
117
+ $args = array($data) + func_get_args();
118
+
119
+ // this method can be overloaded
120
+ foreach ($args as $path) {
121
+ if (is_array($path)) {
122
+ call_user_func_array(array($this, 'addFile'), $path);
123
+ continue;
124
+ }
125
+
126
+ // redefine var
127
+ $path = (string) $path;
128
+
129
+ // check if we can read the file
130
+ if (!$this->canImportFile($path)) {
131
+ throw new IOException('The file "'.$path.'" could not be opened for reading. Check if PHP has enough permissions.');
132
+ }
133
+
134
+ $this->add($path);
135
+ }
136
+
137
+ return $this;
138
+ }
139
+
140
+ /**
141
+ * Minify the data & (optionally) saves it to a file.
142
+ *
143
+ * @param string[optional] $path Path to write the data to
144
+ *
145
+ * @return string The minified data
146
+ */
147
+ public function minify($path = null)
148
+ {
149
+ $content = $this->execute($path);
150
+
151
+ // save to path
152
+ if ($path !== null) {
153
+ $this->save($content, $path);
154
+ }
155
+
156
+ return $content;
157
+ }
158
+
159
+ /**
160
+ * Minify & gzip the data & (optionally) saves it to a file.
161
+ *
162
+ * @param string[optional] $path Path to write the data to
163
+ * @param int[optional] $level Compression level, from 0 to 9
164
+ *
165
+ * @return string The minified & gzipped data
166
+ */
167
+ public function gzip($path = null, $level = 9)
168
+ {
169
+ $content = $this->execute($path);
170
+ $content = gzencode($content, $level, FORCE_GZIP);
171
+
172
+ // save to path
173
+ if ($path !== null) {
174
+ $this->save($content, $path);
175
+ }
176
+
177
+ return $content;
178
+ }
179
+
180
+ /**
181
+ * Minify the data & write it to a CacheItemInterface object.
182
+ *
183
+ * @param CacheItemInterface $item Cache item to write the data to
184
+ *
185
+ * @return CacheItemInterface Cache item with the minifier data
186
+ */
187
+ public function cache(CacheItemInterface $item)
188
+ {
189
+ $content = $this->execute();
190
+ $item->set($content);
191
+
192
+ return $item;
193
+ }
194
+
195
+ /**
196
+ * Minify the data.
197
+ *
198
+ * @param string[optional] $path Path to write the data to
199
+ *
200
+ * @return string The minified data
201
+ */
202
+ abstract public function execute($path = null);
203
+
204
+ /**
205
+ * Load data.
206
+ *
207
+ * @param string $data Either a path to a file or the content itself
208
+ *
209
+ * @return string
210
+ */
211
+ protected function load($data)
212
+ {
213
+ // check if the data is a file
214
+ if ($this->canImportFile($data)) {
215
+ $data = file_get_contents($data);
216
+
217
+ // strip BOM, if any
218
+ if (substr($data, 0, 3) == "\xef\xbb\xbf") {
219
+ $data = substr($data, 3);
220
+ }
221
+ }
222
+
223
+ return $data;
224
+ }
225
+
226
+ /**
227
+ * Save to file.
228
+ *
229
+ * @param string $content The minified data
230
+ * @param string $path The path to save the minified data to
231
+ *
232
+ * @throws IOException
233
+ */
234
+ protected function save($content, $path)
235
+ {
236
+ $handler = $this->openFileForWriting($path);
237
+
238
+ $this->writeToFile($handler, $content);
239
+
240
+ @fclose($handler);
241
+ }
242
+
243
+ /**
244
+ * Register a pattern to execute against the source content.
245
+ *
246
+ * @param string $pattern PCRE pattern
247
+ * @param string|callable $replacement Replacement value for matched pattern
248
+ */
249
+ protected function registerPattern($pattern, $replacement = '')
250
+ {
251
+ // study the pattern, we'll execute it more than once
252
+ $pattern .= 'S';
253
+
254
+ $this->patterns[] = array($pattern, $replacement);
255
+ }
256
+
257
+ /**
258
+ * We can't "just" run some regular expressions against JavaScript: it's a
259
+ * complex language. E.g. having an occurrence of // xyz would be a comment,
260
+ * unless it's used within a string. Of you could have something that looks
261
+ * like a 'string', but inside a comment.
262
+ * The only way to accurately replace these pieces is to traverse the JS one
263
+ * character at a time and try to find whatever starts first.
264
+ *
265
+ * @param string $content The content to replace patterns in
266
+ *
267
+ * @return string The (manipulated) content
268
+ */
269
+ protected function replace($content)
270
+ {
271
+ $processed = '';
272
+ $positions = array_fill(0, count($this->patterns), -1);
273
+ $matches = array();
274
+
275
+ while ($content) {
276
+ // find first match for all patterns
277
+ foreach ($this->patterns as $i => $pattern) {
278
+ list($pattern, $replacement) = $pattern;
279
+
280
+ // we can safely ignore patterns for positions we've unset earlier,
281
+ // because we know these won't show up anymore
282
+ if (array_key_exists($i, $positions) == false) {
283
+ continue;
284
+ }
285
+
286
+ // no need to re-run matches that are still in the part of the
287
+ // content that hasn't been processed
288
+ if ($positions[$i] >= 0) {
289
+ continue;
290
+ }
291
+
292
+ $match = null;
293
+ if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
294
+ $matches[$i] = $match;
295
+
296
+ // we'll store the match position as well; that way, we
297
+ // don't have to redo all preg_matches after changing only
298
+ // the first (we'll still know where those others are)
299
+ $positions[$i] = $match[0][1];
300
+ } else {
301
+ // if the pattern couldn't be matched, there's no point in
302
+ // executing it again in later runs on this same content;
303
+ // ignore this one until we reach end of content
304
+ unset($matches[$i], $positions[$i]);
305
+ }
306
+ }
307
+
308
+ // no more matches to find: everything's been processed, break out
309
+ if (!$matches) {
310
+ $processed .= $content;
311
+ break;
312
+ }
313
+
314
+ // see which of the patterns actually found the first thing (we'll
315
+ // only want to execute that one, since we're unsure if what the
316
+ // other found was not inside what the first found)
317
+ $discardLength = min($positions);
318
+ $firstPattern = array_search($discardLength, $positions);
319
+ $match = $matches[$firstPattern][0][0];
320
+
321
+ // execute the pattern that matches earliest in the content string
322
+ list($pattern, $replacement) = $this->patterns[$firstPattern];
323
+ $replacement = $this->replacePattern($pattern, $replacement, $content);
324
+
325
+ // figure out which part of the string was unmatched; that's the
326
+ // part we'll execute the patterns on again next
327
+ $content = (string) substr($content, $discardLength);
328
+ $unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
329
+
330
+ // move the replaced part to $processed and prepare $content to
331
+ // again match batch of patterns against
332
+ $processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
333
+ $content = $unmatched;
334
+
335
+ // first match has been replaced & that content is to be left alone,
336
+ // the next matches will start after this replacement, so we should
337
+ // fix their offsets
338
+ foreach ($positions as $i => $position) {
339
+ $positions[$i] -= $discardLength + strlen($match);
340
+ }
341
+ }
342
+
343
+ return $processed;
344
+ }
345
+
346
+ /**
347
+ * This is where a pattern is matched against $content and the matches
348
+ * are replaced by their respective value.
349
+ * This function will be called plenty of times, where $content will always
350
+ * move up 1 character.
351
+ *
352
+ * @param string $pattern Pattern to match
353
+ * @param string|callable $replacement Replacement value
354
+ * @param string $content Content to match pattern against
355
+ *
356
+ * @return string
357
+ */
358
+ protected function replacePattern($pattern, $replacement, $content)
359
+ {
360
+ if (is_callable($replacement)) {
361
+ return preg_replace_callback($pattern, $replacement, $content, 1, $count);
362
+ } else {
363
+ return preg_replace($pattern, $replacement, $content, 1, $count);
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Strings are a pattern we need to match, in order to ignore potential
369
+ * code-like content inside them, but we just want all of the string
370
+ * content to remain untouched.
371
+ *
372
+ * This method will replace all string content with simple STRING#
373
+ * placeholder text, so we've rid all strings from characters that may be
374
+ * misinterpreted. Original string content will be saved in $this->extracted
375
+ * and after doing all other minifying, we can restore the original content
376
+ * via restoreStrings().
377
+ *
378
+ * @param string[optional] $chars
379
+ * @param string[optional] $placeholderPrefix
380
+ */
381
+ protected function extractStrings($chars = '\'"', $placeholderPrefix = '')
382
+ {
383
+ // PHP only supports $this inside anonymous functions since 5.4
384
+ $minifier = $this;
385
+ $callback = function ($match) use ($minifier, $placeholderPrefix) {
386
+ // check the second index here, because the first always contains a quote
387
+ if ($match[2] === '') {
388
+ /*
389
+ * Empty strings need no placeholder; they can't be confused for
390
+ * anything else anyway.
391
+ * But we still needed to match them, for the extraction routine
392
+ * to skip over this particular string.
393
+ */
394
+ return $match[0];
395
+ }
396
+
397
+ $count = count($minifier->extracted);
398
+ $placeholder = $match[1].$placeholderPrefix.$count.$match[1];
399
+ $minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
400
+
401
+ return $placeholder;
402
+ };
403
+
404
+ /*
405
+ * The \\ messiness explained:
406
+ * * Don't count ' or " as end-of-string if it's escaped (has backslash
407
+ * in front of it)
408
+ * * Unless... that backslash itself is escaped (another leading slash),
409
+ * in which case it's no longer escaping the ' or "
410
+ * * So there can be either no backslash, or an even number
411
+ * * multiply all of that times 4, to account for the escaping that has
412
+ * to be done to pass the backslash into the PHP string without it being
413
+ * considered as escape-char (times 2) and to get it in the regex,
414
+ * escaped (times 2)
415
+ */
416
+ $this->registerPattern('/(['.$chars.'])(.*?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
417
+ }
418
+
419
+ /**
420
+ * This method will restore all extracted data (strings, regexes) that were
421
+ * replaced with placeholder text in extract*(). The original content was
422
+ * saved in $this->extracted.
423
+ *
424
+ * @param string $content
425
+ *
426
+ * @return string
427
+ */
428
+ protected function restoreExtractedData($content)
429
+ {
430
+ if (!$this->extracted) {
431
+ // nothing was extracted, nothing to restore
432
+ return $content;
433
+ }
434
+
435
+ $content = strtr($content, $this->extracted);
436
+
437
+ $this->extracted = array();
438
+
439
+ return $content;
440
+ }
441
+
442
+ /**
443
+ * Check if the path is a regular file and can be read.
444
+ *
445
+ * @param string $path
446
+ *
447
+ * @return bool
448
+ */
449
+ protected function canImportFile($path)
450
+ {
451
+ $parsed = parse_url($path);
452
+ if (
453
+ // file is elsewhere
454
+ isset($parsed['host']) ||
455
+ // file responds to queries (may change, or need to bypass cache)
456
+ isset($parsed['query'])
457
+ ) {
458
+ return false;
459
+ }
460
+
461
+ return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
462
+ }
463
+
464
+ /**
465
+ * Attempts to open file specified by $path for writing.
466
+ *
467
+ * @param string $path The path to the file
468
+ *
469
+ * @return resource Specifier for the target file
470
+ *
471
+ * @throws IOException
472
+ */
473
+ protected function openFileForWriting($path)
474
+ {
475
+ if (($handler = @fopen($path, 'w')) === false) {
476
+ throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.');
477
+ }
478
+
479
+ return $handler;
480
+ }
481
+
482
+ /**
483
+ * Attempts to write $content to the file specified by $handler. $path is used for printing exceptions.
484
+ *
485
+ * @param resource $handler The resource to write to
486
+ * @param string $content The content to write
487
+ * @param string $path The path to the file (for exception printing only)
488
+ *
489
+ * @throws IOException
490
+ */
491
+ protected function writeToFile($handler, $content, $path = '')
492
+ {
493
+ if (($result = @fwrite($handler, $content)) === false || ($result < strlen($content))) {
494
+ throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
495
+ }
496
+ }
497
+ }
libs/matthiasmullie/path-converter/src/Converter.php CHANGED
@@ -1,196 +1,196 @@
1
- <?php
2
-
3
- namespace MatthiasMullie\PathConverter;
4
-
5
- /**
6
- * Convert paths relative from 1 file to another.
7
- *
8
- * E.g.
9
- * ../../images/icon.jpg relative to /css/imports/icons.css
10
- * becomes
11
- * ../images/icon.jpg relative to /css/minified.css
12
- *
13
- * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
14
- *
15
- * @author Matthias Mullie <pathconverter@mullie.eu>
16
- * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
17
- * @license MIT License
18
- */
19
- class Converter implements ConverterInterface
20
- {
21
- /**
22
- * @var string
23
- */
24
- protected $from;
25
-
26
- /**
27
- * @var string
28
- */
29
- protected $to;
30
-
31
- /**
32
- * @param string $from The original base path (directory, not file!)
33
- * @param string $to The new base path (directory, not file!)
34
- * @param string $root Root directory (defaults to `getcwd`)
35
- */
36
- public function __construct($from, $to, $root = '')
37
- {
38
- $shared = $this->shared($from, $to);
39
- if ($shared === '') {
40
- // when both paths have nothing in common, one of them is probably
41
- // absolute while the other is relative
42
- $root = $root ?: getcwd();
43
- $from = strpos($from, $root) === 0 ? $from : preg_replace('/\/+/', '/', $root.'/'.$from);
44
- $to = strpos($to, $root) === 0 ? $to : preg_replace('/\/+/', '/', $root.'/'.$to);
45
-
46
- // or traveling the tree via `..`
47
- // attempt to resolve path, or assume it's fine if it doesn't exist
48
- $from = @realpath($from) ?: $from;
49
- $to = @realpath($to) ?: $to;
50
- }
51
-
52
- $from = $this->dirname($from);
53
- $to = $this->dirname($to);
54
-
55
- $from = $this->normalize($from);
56
- $to = $this->normalize($to);
57
-
58
- $this->from = $from;
59
- $this->to = $to;
60
- }
61
-
62
- /**
63
- * Normalize path.
64
- *
65
- * @param string $path
66
- *
67
- * @return string
68
- */
69
- protected function normalize($path)
70
- {
71
- // deal with different operating systems' directory structure
72
- $path = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
73
-
74
- /*
75
- * Example:
76
- * /home/forkcms/frontend/cache/compiled_templates/../../core/layout/css/../images/img.gif
77
- * to
78
- * /home/forkcms/frontend/core/layout/images/img.gif
79
- */
80
- do {
81
- $path = preg_replace('/[^\/]+(?<!\.\.)\/\.\.\//', '', $path, -1, $count);
82
- } while ($count);
83
-
84
- return $path;
85
- }
86
-
87
- /**
88
- * Figure out the shared path of 2 locations.
89
- *
90
- * Example:
91
- * /home/forkcms/frontend/core/layout/images/img.gif
92
- * and
93
- * /home/forkcms/frontend/cache/minified_css
94
- * share
95
- * /home/forkcms/frontend
96
- *
97
- * @param string $path1
98
- * @param string $path2
99
- *
100
- * @return string
101
- */
102
- protected function shared($path1, $path2)
103
- {
104
- // $path could theoretically be empty (e.g. no path is given), in which
105
- // case it shouldn't expand to array(''), which would compare to one's
106
- // root /
107
- $path1 = $path1 ? explode('/', $path1) : array();
108
- $path2 = $path2 ? explode('/', $path2) : array();
109
-
110
- $shared = array();
111
-
112
- // compare paths & strip identical ancestors
113
- foreach ($path1 as $i => $chunk) {
114
- if (isset($path2[$i]) && $path1[$i] == $path2[$i]) {
115
- $shared[] = $chunk;
116
- } else {
117
- break;
118
- }
119
- }
120
-
121
- return implode('/', $shared);
122
- }
123
-
124
- /**
125
- * Convert paths relative from 1 file to another.
126
- *
127
- * E.g.
128
- * ../images/img.gif relative to /home/forkcms/frontend/core/layout/css
129
- * should become:
130
- * ../../core/layout/images/img.gif relative to
131
- * /home/forkcms/frontend/cache/minified_css
132
- *
133
- * @param string $path The relative path that needs to be converted
134
- *
135
- * @return string The new relative path
136
- */
137
- public function convert($path)
138
- {
139
- // quit early if conversion makes no sense
140
- if ($this->from === $this->to) {
141
- return $path;
142
- }
143
-
144
- $path = $this->normalize($path);
145
- // if we're not dealing with a relative path, just return absolute
146
- if (strpos($path, '/') === 0) {
147
- return $path;
148
- }
149
-
150
- // normalize paths
151
- $path = $this->normalize($this->from.'/'.$path);
152
-
153
- // strip shared ancestor paths
154
- $shared = $this->shared($path, $this->to);
155
- $path = mb_substr($path, mb_strlen($shared));
156
- $to = mb_substr($this->to, mb_strlen($shared));
157
-
158
- // add .. for every directory that needs to be traversed to new path
159
- $to = str_repeat('../', count(array_filter(explode('/', $to))));
160
-
161
- return $to.ltrim($path, '/');
162
- }
163
-
164
- /**
165
- * Attempt to get the directory name from a path.
166
- *
167
- * @param string $path
168
- *
169
- * @return string
170
- */
171
- protected function dirname($path)
172
- {
173
- if (@is_file($path)) {
174
- return dirname($path);
175
- }
176
-
177
- if (@is_dir($path)) {
178
- return rtrim($path, '/');
179
- }
180
-
181
- // no known file/dir, start making assumptions
182
-
183
- // ends in / = dir
184
- if (mb_substr($path, -1) === '/') {
185
- return rtrim($path, '/');
186
- }
187
-
188
- // has a dot in the name, likely a file
189
- if (preg_match('/.*\..*$/', basename($path)) !== 0) {
190
- return dirname($path);
191
- }
192
-
193
- // you're on your own here!
194
- return $path;
195
- }
196
- }
1
+ <?php
2
+
3
+ namespace FVM\MatthiasMullie\PathConverter;
4
+
5
+ /**
6
+ * Convert paths relative from 1 file to another.
7
+ *
8
+ * E.g.
9
+ * ../../images/icon.jpg relative to /css/imports/icons.css
10
+ * becomes
11
+ * ../images/icon.jpg relative to /css/minified.css
12
+ *
13
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
14
+ *
15
+ * @author Matthias Mullie <pathconverter@mullie.eu>
16
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
17
+ * @license MIT License
18
+ */
19
+ class Converter implements ConverterInterface
20
+ {
21
+ /**
22
+ * @var string
23
+ */
24
+ protected $from;
25
+
26
+ /**
27
+ * @var string
28
+ */
29
+ protected $to;
30
+
31
+ /**
32
+ * @param string $from The original base path (directory, not file!)
33
+ * @param string $to The new base path (directory, not file!)
34
+ * @param string $root Root directory (defaults to `getcwd`)
35
+ */
36
+ public function __construct($from, $to, $root = '')
37
+ {
38
+ $shared = $this->shared($from, $to);
39
+ if ($shared === '') {
40
+ // when both paths have nothing in common, one of them is probably
41
+ // absolute while the other is relative
42
+ $root = $root ?: getcwd();
43
+ $from = strpos($from, $root) === 0 ? $from : preg_replace('/\/+/', '/', $root.'/'.$from);
44
+ $to = strpos($to, $root) === 0 ? $to : preg_replace('/\/+/', '/', $root.'/'.$to);
45
+
46
+ // or traveling the tree via `..`
47
+ // attempt to resolve path, or assume it's fine if it doesn't exist
48
+ $from = @realpath($from) ?: $from;
49
+ $to = @realpath($to) ?: $to;
50
+ }
51
+
52
+ $from = $this->dirname($from);
53
+ $to = $this->dirname($to);
54
+
55
+ $from = $this->normalize($from);
56
+ $to = $this->normalize($to);
57
+
58
+ $this->from = $from;
59
+ $this->to = $to;
60
+ }
61
+
62
+ /**
63
+ * Normalize path.
64
+ *
65
+ * @param string $path
66
+ *
67
+ * @return string
68
+ */
69
+ protected function normalize($path)
70
+ {
71
+ // deal with different operating systems' directory structure
72
+ $path = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
73
+
74
+ /*
75
+ * Example:
76
+ * /home/forkcms/frontend/cache/compiled_templates/../../core/layout/css/../images/img.gif
77
+ * to
78
+ * /home/forkcms/frontend/core/layout/images/img.gif
79
+ */
80
+ do {
81
+ $path = preg_replace('/[^\/]+(?<!\.\.)\/\.\.\//', '', $path, -1, $count);
82
+ } while ($count);
83
+
84
+ return $path;
85
+ }
86
+
87
+ /**
88
+ * Figure out the shared path of 2 locations.
89
+ *
90
+ * Example:
91
+ * /home/forkcms/frontend/core/layout/images/img.gif
92
+ * and
93
+ * /home/forkcms/frontend/cache/minified_css
94
+ * share
95
+ * /home/forkcms/frontend
96
+ *
97
+ * @param string $path1
98
+ * @param string $path2
99
+ *
100
+ * @return string
101
+ */
102
+ protected function shared($path1, $path2)
103
+ {
104
+ // $path could theoretically be empty (e.g. no path is given), in which
105
+ // case it shouldn't expand to array(''), which would compare to one's
106
+ // root /
107
+ $path1 = $path1 ? explode('/', $path1) : array();
108
+ $path2 = $path2 ? explode('/', $path2) : array();
109
+
110
+ $shared = array();
111
+
112
+ // compare paths & strip identical ancestors
113
+ foreach ($path1 as $i => $chunk) {
114
+ if (isset($path2[$i]) && $path1[$i] == $path2[$i]) {
115
+ $shared[] = $chunk;
116
+ } else {
117
+ break;
118
+ }
119
+ }
120
+
121
+ return implode('/', $shared);
122
+ }
123
+
124
+ /**
125
+ * Convert paths relative from 1 file to another.
126
+ *
127
+ * E.g.
128
+ * ../images/img.gif relative to /home/forkcms/frontend/core/layout/css
129
+ * should become:
130
+ * ../../core/layout/images/img.gif relative to
131
+ * /home/forkcms/frontend/cache/minified_css
132
+ *
133
+ * @param string $path The relative path that needs to be converted
134
+ *
135
+ * @return string The new relative path
136
+ */
137
+ public function convert($path)
138
+ {
139
+ // quit early if conversion makes no sense
140
+ if ($this->from === $this->to) {
141
+ return $path;
142
+ }
143
+
144
+ $path = $this->normalize($path);
145
+ // if we're not dealing with a relative path, just return absolute
146
+ if (strpos($path, '/') === 0) {
147
+ return $path;
148
+ }
149
+
150
+ // normalize paths
151
+ $path = $this->normalize($this->from.'/'.$path);
152
+
153
+ // strip shared ancestor paths
154
+ $shared = $this->shared($path, $this->to);
155
+ $path = mb_substr($path, mb_strlen($shared));
156
+ $to = mb_substr($this->to, mb_strlen($shared));
157
+
158
+ // add .. for every directory that needs to be traversed to new path
159
+ $to = str_repeat('../', count(array_filter(explode('/', $to))));
160
+
161
+ return $to.ltrim($path, '/');
162
+ }
163
+
164
+ /**
165
+ * Attempt to get the directory name from a path.
166
+ *
167
+ * @param string $path
168
+ *
169
+ * @return string
170
+ */
171
+ protected function dirname($path)
172
+ {
173
+ if (@is_file($path)) {
174
+ return dirname($path);
175
+ }
176
+
177
+ if (@is_dir($path)) {
178
+ return rtrim($path, '/');
179
+ }
180
+
181
+ // no known file/dir, start making assumptions
182
+
183
+ // ends in / = dir
184
+ if (mb_substr($path, -1) === '/') {
185
+ return rtrim($path, '/');
186
+ }
187
+
188
+ // has a dot in the name, likely a file
189
+ if (preg_match('/.*\..*$/', basename($path)) !== 0) {
190
+ return dirname($path);
191
+ }
192
+
193
+ // you're on your own here!
194
+ return $path;
195
+ }
196
+ }
libs/matthiasmullie/path-converter/src/ConverterInterface.php CHANGED
@@ -1,24 +1,24 @@
1
- <?php
2
-
3
- namespace MatthiasMullie\PathConverter;
4
-
5
- /**
6
- * Convert file paths.
7
- *
8
- * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
9
- *
10
- * @author Matthias Mullie <pathconverter@mullie.eu>
11
- * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
12
- * @license MIT License
13
- */
14
- interface ConverterInterface
15
- {
16
- /**
17
- * Convert file paths.
18
- *
19
- * @param string $path The path to be converted
20
- *
21
- * @return string The new path
22
- */
23
- public function convert($path);
24
- }
1
+ <?php
2
+
3
+ namespace FVM\MatthiasMullie\PathConverter;
4
+
5
+ /**
6
+ * Convert file paths.
7
+ *
8
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
9
+ *
10
+ * @author Matthias Mullie <pathconverter@mullie.eu>
11
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
12
+ * @license MIT License
13
+ */
14
+ interface ConverterInterface
15
+ {
16
+ /**
17
+ * Convert file paths.
18
+ *
19
+ * @param string $path The path to be converted
20
+ *
21
+ * @return string The new path
22
+ */
23
+ public function convert($path);
24
+ }
libs/matthiasmullie/path-converter/src/NoConverter.php CHANGED
@@ -1,23 +1,23 @@
1
- <?php
2
-
3
- namespace MatthiasMullie\PathConverter;
4
-
5
- /**
6
- * Don't convert paths.
7
- *
8
- * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
9
- *
10
- * @author Matthias Mullie <pathconverter@mullie.eu>
11
- * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
12
- * @license MIT License
13
- */
14
- class NoConverter implements ConverterInterface
15
- {
16
- /**
17
- * {@inheritdoc}
18
- */
19
- public function convert($path)
20
- {
21
- return $path;
22
- }
23
- }
1
+ <?php
2
+
3
+ namespace FVM\MatthiasMullie\PathConverter;
4
+
5
+ /**
6
+ * Don't convert paths.
7
+ *
8
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
9
+ *
10
+ * @author Matthias Mullie <pathconverter@mullie.eu>
11
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
12
+ * @license MIT License
13
+ */
14
+ class NoConverter implements ConverterInterface
15
+ {
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ public function convert($path)
20
+ {
21
+ return $path;
22
+ }
23
+ }
libs/mrclay/HTML.php DELETED
@@ -1,241 +0,0 @@
1
- <?php
2
-
3
- /* https://github.com/mrclay/minify/blob/2.x/min/lib/Minify/HTML.php - same as WP ROCKET v2.91 + custom changes */
4
- # line:
5
-
6
- class fastvelocity_min_Minify_HTML {
7
- protected $_jsCleanComments = true;
8
-
9
- /**
10
- * "Minify" an HTML page
11
- *
12
- * @param string $html
13
- *
14
- * @param array $options
15
- *
16
- * 'cssMinifier' : (optional) callback function to process content of STYLE
17
- * elements.
18
- *
19
- * 'jsMinifier' : (optional) callback function to process content of SCRIPT
20
- * elements. Note: the type attribute is ignored.
21
- *
22
- * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
23
- * unset, minify will sniff for an XHTML doctype.
24
- *
25
- * @return string
26
- */
27
- public static function minify($html, $options = array()) {
28
- $min = new self($html, $options);
29
- return $min->process();
30
- }
31
-
32
-
33
- /**
34
- * Create a minifier object
35
- *
36
- * @param string $html
37
- *
38
- * @param array $options
39
- *
40
- * 'cssMinifier' : (optional) callback function to process content of STYLE
41
- * elements.
42
- *
43
- * 'jsMinifier' : (optional) callback function to process content of SCRIPT
44
- * elements. Note: the type attribute is ignored.
45
- *
46
- * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
47
- *
48
- * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
49
- * unset, minify will sniff for an XHTML doctype.
50
- */
51
- public function __construct($html, $options = array())
52
- {
53
- $this->_html = str_replace("\r\n", "\n", trim($html));
54
- if (isset($options['xhtml'])) {
55
- $this->_isXhtml = (bool)$options['xhtml'];
56
- }
57
- if (isset($options['cssMinifier'])) {
58
- $this->_cssMinifier = $options['cssMinifier'];
59
- }
60
- if (isset($options['jsMinifier'])) {
61
- $this->_jsMinifier = $options['jsMinifier'];
62
- }
63
- if (isset($options['jsCleanComments'])) {
64
- $this->_jsCleanComments = (bool)$options['jsCleanComments'];
65
- }
66
- }
67
-
68
-
69
- /**
70
- * Minify the markeup given in the constructor
71
- *
72
- * @return string
73
- */
74
- public function process()
75
- {
76
- if ($this->_isXhtml === null) {
77
- $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
78
- }
79
-
80
- $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
81
- $this->_placeholders = array();
82
-
83
- // replace SCRIPTs (and minify) with placeholders
84
- $this->_html = preg_replace_callback(
85
- '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
86
- ,array($this, '_removeScriptCB')
87
- ,$this->_html);
88
-
89
- // replace STYLEs (and minify) with placeholders
90
- $this->_html = preg_replace_callback(
91
- '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
92
- ,array($this, '_removeStyleCB')
93
- ,$this->_html);
94
-
95
- // remove HTML comments (not containing IE conditional comments).
96
- global $strip_htmlcomments;
97
- if($strip_htmlcomments) {
98
- $this->_html = preg_replace_callback('/<!--([\\s\\S]*?)-->/' ,array($this, '_commentCB') ,$this->_html);
99
- }
100
-
101
- // replace PREs with placeholders
102
- $this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
103
- ,array($this, '_removePreCB')
104
- ,$this->_html);
105
-
106
- // replace TEXTAREAs with placeholders
107
- $this->_html = preg_replace_callback(
108
- '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
109
- ,array($this, '_removeTextareaCB')
110
- ,$this->_html);
111
-
112
- // trim each line.
113
- // @todo take into account attribute values that span multiple lines.
114
- $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
115
-
116
- // remove ws around block/undisplayed elements
117
- $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
118
- .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
119
- .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
120
- .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
121
- .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
122
-
123
- // remove ws outside of all elements
124
- $this->_html = preg_replace_callback(
125
- '/>([^<]+)</'
126
- ,array($this, '_outsideTagCB')
127
- ,$this->_html);
128
-
129
- // use newlines before 1st attribute in open tags (to limit line lengths)
130
- //$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
131
-
132
- // fill placeholders
133
- $this->_html = str_replace(
134
- array_keys($this->_placeholders)
135
- ,array_values($this->_placeholders)
136
- ,$this->_html
137
- );
138
- // issue 229: multi-pass to catch scripts that didn't get replaced in textareas
139
- $this->_html = str_replace(
140
- array_keys($this->_placeholders)
141
- ,array_values($this->_placeholders)
142
- ,$this->_html
143
- );
144
- return $this->_html;
145
- }
146
-
147
- protected function _commentCB($m)
148
- {
149
- return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<![') || 0 === strpos($m[1], 'esi')) ? $m[0] : '';
150
- }
151
-
152
- protected function _reservePlace($content)
153
- {
154
- $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
155
- $this->_placeholders[$placeholder] = $content;
156
- return $placeholder;
157
- }
158
-
159
- protected $_isXhtml = null;
160
- protected $_replacementHash = null;
161
- protected $_placeholders = array();
162
- protected $_cssMinifier = null;
163
- protected $_jsMinifier = null;
164
-
165
- protected function _outsideTagCB($m)
166
- {
167
- return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
168
- }
169
-
170
- protected function _removePreCB($m)
171
- {
172
- return $this->_reservePlace("<pre{$m[1]}");
173
- }
174
-
175
- protected function _removeTextareaCB($m)
176
- {
177
- return $this->_reservePlace("<textarea{$m[1]}");
178
- }
179
-
180
- protected function _removeStyleCB($m)
181
- {
182
- $openStyle = "<style{$m[1]}";
183
- $css = $m[2];
184
- // remove css comments
185
- $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
186
-
187
- // remove CDATA section markers
188
- $css = $this->_removeCdata($css);
189
-
190
- // minify
191
- $minifier = $this->_cssMinifier ? $this->_cssMinifier : 'trim';
192
- $css = call_user_func($minifier, $css);
193
- $css = preg_replace('/\s+/', ' ', $css);
194
-
195
- return $this->_reservePlace($this->_needsCdata($css)
196
- ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
197
- : "{$openStyle}{$css}</style>"
198
- );
199
- }
200
-
201
- protected function _removeScriptCB($m)
202
- {
203
- $openScript = "<script{$m[2]}";
204
- $js = $m[3];
205
-
206
- // whitespace surrounding? preserve at least one space
207
- $ws1 = ($m[1] === '') ? '' : ' ';
208
- $ws2 = ($m[4] === '') ? '' : ' ';
209
-
210
- // remove HTML comments (and ending "//" if present)
211
- if ($this->_jsCleanComments) {
212
- $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
213
- }
214
-
215
- // remove CDATA section markers
216
- $js = $this->_removeCdata($js);
217
-
218
- // minify
219
- $minifier = $this->_jsMinifier
220
- ? $this->_jsMinifier
221
- : 'trim';
222
- $js = call_user_func($minifier, $js);
223
-
224
- return $this->_reservePlace($this->_needsCdata($js)
225
- ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
226
- : "{$ws1}{$openScript}{$js}</script>{$ws2}"
227
- );
228
- }
229
-
230
- protected function _removeCdata($str)
231
- {
232
- return (false !== strpos($str, '<![CDATA['))
233
- ? str_replace(array('<![CDATA[', ']]>'), '', $str)
234
- : $str;
235
- }
236
-
237
- protected function _needsCdata($str)
238
- {
239
- return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
240
- }
241
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
libs/{mrclay → raisermin}/index.html RENAMED
File without changes
libs/raisermin/minify.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *
4
+ * Website: https://wpraiser.com/
5
+ * Author: Raul Peixoto (https://www.upwork.com/fl/raulpeixoto)
6
+ * Licensed under GPLv2 (or later)
7
+ * Version 1.0
8
+ *
9
+ * Usage: fvm_raisermin_js($js);
10
+ *
11
+ */
12
+
13
+ # Exit if accessed directly
14
+ if (!defined('ABSPATH')){ exit(); }
15
+
16
+ # minify js, whitespace only
17
+ function fvm_raisermin_js($code){
18
+
19
+ # remove // comments
20
+ $code = preg_replace('/(^|\s)\/\/(.*)\n/m', '', $code);
21
+ $code = preg_replace('/(\{|\}|\[|\]|\(|\)|\;)\/\/(.*)\n/m', '$1', $code);
22
+
23
+ # remove /* ... */ comments
24
+ $code = preg_replace('/(^|\s)\/\*(.*)\*\//Us', '', $code);
25
+ $code = preg_replace('/(\;|\{)\/\*(.*)\*\//Us', '$1', $code);
26
+
27
+ # remove sourceMappingURL
28
+ $code = preg_replace('/(\/\/\s*[#]\s*sourceMappingURL\s*[=]\s*)([a-zA-Z0-9-_\.\/]+)(\.map)/ui', '', $code);
29
+
30
+ # uniform line endings, make them all line feed
31
+ $code = str_replace(array("\r\n", "\r"), "\n", $code);
32
+
33
+ # collapse all non-line feed whitespace into a single space
34
+ $code = preg_replace('/[^\S\n]+/', ' ', $code);
35
+
36
+ # strip leading & trailing whitespace
37
+ $code = str_replace(array(" \n", "\n "), "\n", $code);
38
+
39
+ # collapse consecutive line feeds into just 1
40
+ $code = preg_replace('/\n+/', "\n", $code);
41
+
42
+ # process horizontal space
43
+ $code = preg_replace('/(\h?)(\|\||\&\&|[\{\}\[\]\?:\.;=])(\h?)/ui', '$2', $code);
44
+ $code = preg_replace('/([\[\]\(\)\{\}\;\<\>])(\h+)([\[\]\(\)\{\}\;\<\>])/ui', '$1 $3', $code);
45
+ $code = preg_replace('/([\)])(\h?)(\.)/ui', '$1$3', $code);
46
+ $code = preg_replace('/([\)\?])(\h?)(\.)/ui', '$1$3', $code);
47
+ $code = preg_replace('/(\,)(\h+)/ui', '$1 ', $code);
48
+ $code = preg_replace('/(\h+)(\,)/ui', ' $2', $code);
49
+ $code = preg_replace('/([if])(\h+)(\()/ui', '$1$3', $code);
50
+
51
+ # trim whitespace on beginning/end
52
+ return trim($code);
53
+ }
54
+
55
+
56
+ # remove UTF8 BOM
57
+ function fvm_min_remove_utf8_bom($text) {
58
+ $bom = pack('H*','EFBBBF');
59
+ while (preg_match("/^$bom/", $text)) {
60
+ $text = preg_replace("/^$bom/ui", '', $text);
61
+ }
62
+ return $text;
63
+ }
64
+
65
+
66
+
67
+
68
+ # minify html, don't touch certain tags
69
+ function fvm_raisermin_html($html, $xtra) {
70
+
71
+ # clone
72
+ $content = $html;
73
+
74
+ # get all scripts
75
+ $allscripts = array();
76
+ preg_match_all('/\<script(.*?)\<(\s*)\/script(\s*)\>/uis', $html, $allscripts);
77
+
78
+ # replace all scripts and styles with a marker
79
+ if(is_array($allscripts) && isset($allscripts[0]) && count($allscripts[0]) > 0) {
80
+ foreach ($allscripts[0] as $k=>$v) {
81
+ $content = str_replace($v, '<!-- SCRIPT '.$k.' -->', $content);
82
+ }
83
+ }
84
+
85
+ # remove line breaks, and colapse two or more white spaces into one
86
+ $content = preg_replace('/\s+/u', " ", $content);
87
+
88
+ # remove space between tags
89
+ $content = str_replace('> <', '><', $content);
90
+
91
+ # add extra line breaks to code?
92
+ if($xtra === true) {
93
+
94
+ # add linebreaks after meta tags, for readability
95
+ $allmeta = array();
96
+ preg_match_all('/\<meta(.*?)\>/uis', $html, $allmeta);
97
+
98
+ # replace all scripts and styles with a marker
99
+ if(is_array($allmeta) && isset($allmeta[0]) && count($allmeta[0]) > 0) {
100
+ foreach ($allmeta[0] as $k=>$v) {
101
+ $content = str_replace($v, PHP_EOL . $v . PHP_EOL, $content);
102
+ }
103
+ }
104
+
105
+ # add linebreaks after link tags, for readability
106
+ $alllink = array();
107
+ preg_match_all('/\<link(.*?)\>/uis', $html, $alllink);
108
+
109
+ # replace all scripts and styles with a marker
110
+ if(is_array($alllink) && isset($alllink[0]) && count($alllink[0]) > 0) {
111
+ foreach ($alllink[0] as $k=>$v) {
112
+ $content = str_replace($v, PHP_EOL . $v . PHP_EOL, $content);
113
+ }
114
+ }
115
+
116
+ # add linebreaks after style tags, for readability
117
+ $allstyles = array();
118
+ preg_match_all('/\<s(.*?)\<(\s*)\/style(\s*)\>/uis', $html, $allstyles);
119
+
120
+ # replace all scripts and styles with a marker
121
+ if(is_array($allstyles) && isset($allstyles[0]) && count($allstyles[0]) > 0) {
122
+ foreach ($allstyles[0] as $k=>$v) {
123
+ $content = str_replace($v, PHP_EOL . $v . PHP_EOL, $content);
124
+ }
125
+ }
126
+
127
+ # add linebreaks after html and head tags, for readability
128
+ $content = str_replace('<head>', PHP_EOL . '<head>' . PHP_EOL, $content);
129
+ $content = str_replace('</head>', PHP_EOL . '</head>' . PHP_EOL, $content);
130
+ $content = str_replace('<html', PHP_EOL . '<html', $content);
131
+ $content = str_replace('</html>', PHP_EOL . '</html>', $content);
132
+
133
+ }
134
+
135
+ # replace markers with scripts and styles
136
+ if(is_array($allscripts) && isset($allscripts[0]) && count($allscripts[0]) > 0) {
137
+ foreach ($allscripts[0] as $k=>$v) {
138
+ if($xtra === true) {
139
+ $content = str_replace('<!-- SCRIPT '.$k.' -->', PHP_EOL . $v . PHP_EOL, $content);
140
+ } else {
141
+ $content = str_replace('<!-- SCRIPT '.$k.' -->', $v, $content);
142
+ }
143
+ }
144
+ }
145
+
146
+
147
+ # no more than 1 linebreak
148
+ $content = preg_replace('/\v{2,}/u', PHP_EOL, $content);
149
+
150
+ # save as html, if not empty
151
+ if(!empty($content)) {
152
+ $html = $content;
153
+ }
154
+
155
+ # return
156
+ return $html;
157
+ }
libs/simplehtmldom/index.html ADDED
File without changes
libs/simplehtmldom/simple_html_dom.php ADDED
@@ -0,0 +1,2357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Website: http://sourceforge.net/projects/simplehtmldom/
4
+ * Additional projects: http://sourceforge.net/projects/debugobject/
5
+ * Acknowledge: Jose Solorzano (https://sourceforge.net/projects/php-html/)
6
+ *
7
+ * Licensed under The MIT License
8
+ * See the LICENSE file in the project root for more information.
9
+ *
10
+ * Authors:
11
+ * S.C. Chen
12
+ * John Schlick
13
+ * Rus Carroll
14
+ * logmanoriginal
15
+ *
16
+ * Contributors:
17
+ * Yousuke Kumakura
18
+ * Vadim Voituk
19
+ * Antcs
20
+ *
21
+ * Version Rev. 1.9.1 (291)
22
+ */
23
+
24
+ # Exit if accessed directly
25
+ if (!defined('ABSPATH')){ exit(); }
26
+
27
+
28
+ define('HDOM_TYPE_ELEMENT', 1);
29
+ define('HDOM_TYPE_COMMENT', 2);
30
+ define('HDOM_TYPE_TEXT', 3);
31
+ define('HDOM_TYPE_ENDTAG', 4);
32
+ define('HDOM_TYPE_ROOT', 5);
33
+ define('HDOM_TYPE_UNKNOWN', 6);
34
+ define('HDOM_QUOTE_DOUBLE', 0);
35
+ define('HDOM_QUOTE_SINGLE', 1);
36
+ define('HDOM_QUOTE_NO', 3);
37
+ define('HDOM_INFO_BEGIN', 0);
38
+ define('HDOM_INFO_END', 1);
39
+ define('HDOM_INFO_QUOTE', 2);
40
+ define('HDOM_INFO_SPACE', 3);
41
+ define('HDOM_INFO_TEXT', 4);
42
+ define('HDOM_INFO_INNER', 5);
43
+ define('HDOM_INFO_OUTER', 6);
44
+ define('HDOM_INFO_ENDSPACE', 7);
45
+
46
+ defined('DEFAULT_TARGET_CHARSET') || define('DEFAULT_TARGET_CHARSET', 'UTF-8');
47
+ defined('DEFAULT_BR_TEXT') || define('DEFAULT_BR_TEXT', "\r\n");
48
+ defined('DEFAULT_SPAN_TEXT') || define('DEFAULT_SPAN_TEXT', ' ');
49
+ defined('MAX_FILE_SIZE') || define('MAX_FILE_SIZE', 600000);
50
+ define('HDOM_SMARTY_AS_TEXT', 1);
51
+
52
+ function file_get_html(
53
+ $url,
54
+ $use_include_path = false,
55
+ $context = null,
56
+ $offset = 0,
57
+ $maxLen = -1,
58
+ $lowercase = true,
59
+ $forceTagsClosed = true,
60
+ $target_charset = DEFAULT_TARGET_CHARSET,
61
+ $stripRN = true,
62
+ $defaultBRText = DEFAULT_BR_TEXT,
63
+ $defaultSpanText = DEFAULT_SPAN_TEXT)
64
+ {
65
+ if($maxLen <= 0) { $maxLen = MAX_FILE_SIZE; }
66
+
67
+ $dom = new simple_html_dom(
68
+ null,
69
+ $lowercase,
70
+ $forceTagsClosed,
71
+ $target_charset,
72
+ $stripRN,
73
+ $defaultBRText,
74
+ $defaultSpanText
75
+ );
76
+
77
+ /**
78
+ * For sourceforge users: uncomment the next line and comment the
79
+ * retrieve_url_contents line 2 lines down if it is not already done.
80
+ */
81
+ $contents = file_get_contents(
82
+ $url,
83
+ $use_include_path,
84
+ $context,
85
+ $offset,
86
+ $maxLen
87
+ );
88
+ // $contents = retrieve_url_contents($url);
89
+
90
+ if (empty($contents) || strlen($contents) > $maxLen) {
91
+ $dom->clear();
92
+ return false;
93
+ }
94
+
95
+ return $dom->load($contents, $lowercase, $stripRN);
96
+ }
97
+
98
+ function str_get_html(
99
+ $str,
100
+ $lowercase = true,
101
+ $forceTagsClosed = true,
102
+ $target_charset = DEFAULT_TARGET_CHARSET,
103
+ $stripRN = true,
104
+ $defaultBRText = DEFAULT_BR_TEXT,
105
+ $defaultSpanText = DEFAULT_SPAN_TEXT)
106
+ {
107
+ $dom = new simple_html_dom(
108
+ null,
109
+ $lowercase,
110
+ $forceTagsClosed,
111
+ $target_charset,
112
+ $stripRN,
113
+ $defaultBRText,
114
+ $defaultSpanText
115
+ );
116
+
117
+ if (empty($str) || strlen($str) > MAX_FILE_SIZE) {
118
+ $dom->clear();
119
+ return false;
120
+ }
121
+
122
+ return $dom->load($str, $lowercase, $stripRN);
123
+ }
124
+
125
+ function dump_html_tree($node, $show_attr = true, $deep = 0)
126
+ {
127
+ $node->dump($node);
128
+ }
129
+
130
+ class simple_html_dom_node
131
+ {
132
+ public $nodetype = HDOM_TYPE_TEXT;
133
+ public $tag = 'text';
134
+ public $attr = array();
135
+ public $children = array();
136
+ public $nodes = array();
137
+ public $parent = null;
138
+ public $_ = array();
139
+ public $tag_start = 0;
140
+ private $dom = null;
141
+
142
+ function __construct($dom)
143
+ {
144
+ $this->dom = $dom;
145
+ $dom->nodes[] = $this;
146
+ }
147
+
148
+ function __destruct()
149
+ {
150
+ $this->clear();
151
+ }
152
+
153
+ function __toString()
154
+ {
155
+ return $this->outertext();
156
+ }
157
+
158
+ function clear()
159
+ {
160
+ $this->dom = null;
161
+ $this->nodes = null;
162
+ $this->parent = null;
163
+ $this->children = null;
164
+ }
165
+
166
+ function dump($show_attr = true, $depth = 0)
167
+ {
168
+ echo str_repeat("\t", $depth) . $this->tag;
169
+
170
+ if ($show_attr && count($this->attr) > 0) {
171
+ echo '(';
172
+ foreach ($this->attr as $k => $v) {
173
+ echo "[$k]=>\"$v\", ";
174
+ }
175
+ echo ')';
176
+ }
177
+
178
+ echo "\n";
179
+
180
+ if ($this->nodes) {
181
+ foreach ($this->nodes as $node) {
182
+ $node->dump($show_attr, $depth + 1);
183
+ }
184
+ }
185
+ }
186
+
187
+ function dump_node($echo = true)
188
+ {
189
+ $string = $this->tag;
190
+
191
+ if (count($this->attr) > 0) {
192
+ $string .= '(';
193
+ foreach ($this->attr as $k => $v) {
194
+ $string .= "[$k]=>\"$v\", ";
195
+ }
196
+ $string .= ')';
197
+ }
198
+
199
+ if (count($this->_) > 0) {
200
+ $string .= ' $_ (';
201
+ foreach ($this->_ as $k => $v) {
202
+ if (is_array($v)) {
203
+ $string .= "[$k]=>(";
204
+ foreach ($v as $k2 => $v2) {
205
+ $string .= "[$k2]=>\"$v2\", ";
206
+ }
207
+ $string .= ')';
208
+ } else {
209
+ $string .= "[$k]=>\"$v\", ";
210
+ }
211
+ }
212
+ $string .= ')';
213
+ }
214
+
215
+ if (isset($this->text)) {
216
+ $string .= " text: ({$this->text})";
217
+ }
218
+
219
+ $string .= ' HDOM_INNER_INFO: ';
220
+
221
+ if (isset($node->_[HDOM_INFO_INNER])) {
222
+ $string .= "'" . $node->_[HDOM_INFO_INNER] . "'";
223
+ } else {
224
+ $string .= ' NULL ';
225
+ }
226
+
227
+ $string .= ' children: ' . count($this->children);
228
+ $string .= ' nodes: ' . count($this->nodes);
229
+ $string .= ' tag_start: ' . $this->tag_start;
230
+ $string .= "\n";
231
+
232
+ if ($echo) {
233
+ echo $string;
234
+ return;
235
+ } else {
236
+ return $string;
237
+ }
238
+ }
239
+
240
+ function parent($parent = null)
241
+ {
242
+ // I am SURE that this doesn't work properly.
243
+ // It fails to unset the current node from it's current parents nodes or
244
+ // children list first.
245
+ if ($parent !== null) {
246
+ $this->parent = $parent;
247
+ $this->parent->nodes[] = $this;
248
+ $this->parent->children[] = $this;
249
+ }
250
+
251
+ return $this->parent;
252
+ }
253
+
254
+ function has_child()
255
+ {
256
+ return !empty($this->children);
257
+ }
258
+
259
+ function children($idx = -1)
260
+ {
261
+ if ($idx === -1) {
262
+ return $this->children;
263
+ }
264
+
265
+ if (isset($this->children[$idx])) {
266
+ return $this->children[$idx];
267
+ }
268
+
269
+ return null;
270
+ }
271
+
272
+ function first_child()
273
+ {
274
+ if (count($this->children) > 0) {
275
+ return $this->children[0];
276
+ }
277
+ return null;
278
+ }
279
+
280
+ function last_child()
281
+ {
282
+ if (count($this->children) > 0) {
283
+ return end($this->children);
284
+ }
285
+ return null;
286
+ }
287
+
288
+ function next_sibling()
289
+ {
290
+ if ($this->parent === null) {
291
+ return null;
292
+ }
293
+
294
+ $idx = array_search($this, $this->parent->children, true);
295
+
296
+ if ($idx !== false && isset($this->parent->children[$idx + 1])) {
297
+ return $this->parent->children[$idx + 1];
298
+ }
299
+
300
+ return null;
301
+ }
302
+
303
+ function prev_sibling()
304
+ {
305
+ if ($this->parent === null) {
306
+ return null;
307
+ }
308
+
309
+ $idx = array_search($this, $this->parent->children, true);
310
+
311
+ if ($idx !== false && $idx > 0) {
312
+ return $this->parent->children[$idx - 1];
313
+ }
314
+
315
+ return null;
316
+ }
317
+
318
+ function find_ancestor_tag($tag)
319
+ {
320
+ global $debug_object;
321
+ if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
322
+
323
+ if ($this->parent === null) {
324
+ return null;
325
+ }
326
+
327
+ $ancestor = $this->parent;
328
+
329
+ while (!is_null($ancestor)) {
330
+ if (is_object($debug_object)) {
331
+ $debug_object->debug_log(2, 'Current tag is: ' . $ancestor->tag);
332
+ }
333
+
334
+ if ($ancestor->tag === $tag) {
335
+ break;
336
+ }
337
+
338
+ $ancestor = $ancestor->parent;
339
+ }
340
+
341
+ return $ancestor;
342
+ }
343
+
344
+ function innertext()
345
+ {
346
+ if (isset($this->_[HDOM_INFO_INNER])) {
347
+ return $this->_[HDOM_INFO_INNER];
348
+ }
349
+
350
+ if (isset($this->_[HDOM_INFO_TEXT])) {
351
+ return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
352
+ }
353
+
354
+ $ret = '';
355
+
356
+ foreach ($this->nodes as $n) {
357
+ $ret .= $n->outertext();
358
+ }
359
+
360
+ return $ret;
361
+ }
362
+
363
+ function outertext()
364
+ {
365
+ global $debug_object;
366
+
367
+ if (is_object($debug_object)) {
368
+ $text = '';
369
+
370
+ if ($this->tag === 'text') {
371
+ if (!empty($this->text)) {
372
+ $text = ' with text: ' . $this->text;
373
+ }
374
+ }
375
+
376
+ $debug_object->debug_log(1, 'Innertext of tag: ' . $this->tag . $text);
377
+ }
378
+
379
+ if ($this->tag === 'root') {
380
+ return $this->innertext();
381
+ }
382
+
383
+ // todo: What is the use of this callback? Remove?
384
+ if ($this->dom && $this->dom->callback !== null) {
385
+ call_user_func_array($this->dom->callback, array($this));
386
+ }
387
+
388
+ if (isset($this->_[HDOM_INFO_OUTER])) {
389
+ return $this->_[HDOM_INFO_OUTER];
390
+ }
391
+
392
+ if (isset($this->_[HDOM_INFO_TEXT])) {
393
+ return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
394
+ }
395
+
396
+ $ret = '';
397
+
398
+ if ($this->dom && $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]) {
399
+ $ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup();
400
+ }
401
+
402
+ if (isset($this->_[HDOM_INFO_INNER])) {
403
+ // todo: <br> should either never have HDOM_INFO_INNER or always
404
+ if ($this->tag !== 'br') {
405
+ $ret .= $this->_[HDOM_INFO_INNER];
406
+ }
407
+ } elseif ($this->nodes) {
408
+ foreach ($this->nodes as $n) {
409
+ $ret .= $this->convert_text($n->outertext());
410
+ }
411
+ }
412
+
413
+ if (isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END] != 0) {
414
+ $ret .= '</' . $this->tag . '>';
415
+ }
416
+
417
+ return $ret;
418
+ }
419
+
420
+ function text()
421
+ {
422
+ if (isset($this->_[HDOM_INFO_INNER])) {
423
+ return $this->_[HDOM_INFO_INNER];
424
+ }
425
+
426
+ switch ($this->nodetype) {
427
+ case HDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
428
+ case HDOM_TYPE_COMMENT: return '';
429
+ case HDOM_TYPE_UNKNOWN: return '';
430
+ }
431
+
432
+ if (strcasecmp($this->tag, 'script') === 0) { return ''; }
433
+ if (strcasecmp($this->tag, 'style') === 0) { return ''; }
434
+
435
+ $ret = '';
436
+
437
+ // In rare cases, (always node type 1 or HDOM_TYPE_ELEMENT - observed
438
+ // for some span tags, and some p tags) $this->nodes is set to NULL.
439
+ // NOTE: This indicates that there is a problem where it's set to NULL
440
+ // without a clear happening.
441
+ // WHY is this happening?
442
+ if (!is_null($this->nodes)) {
443
+ foreach ($this->nodes as $n) {
444
+ // Start paragraph after a blank line
445
+ if ($n->tag === 'p') {
446
+ $ret = trim($ret) . "\n\n";
447
+ }
448
+
449
+ $ret .= $this->convert_text($n->text());
450
+
451
+ // If this node is a span... add a space at the end of it so
452
+ // multiple spans don't run into each other. This is plaintext
453
+ // after all.
454
+ if ($n->tag === 'span') {
455
+ $ret .= $this->dom->default_span_text;
456
+ }
457
+ }
458
+ }
459
+ return $ret;
460
+ }
461
+
462
+ function xmltext()
463
+ {
464
+ $ret = $this->innertext();
465
+ $ret = str_ireplace('<![CDATA[', '', $ret);
466
+ $ret = str_replace(']]>', '', $ret);
467
+ return $ret;
468
+ }
469
+
470
+ function makeup()
471
+ {
472
+ // text, comment, unknown
473
+ if (isset($this->_[HDOM_INFO_TEXT])) {
474
+ return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
475
+ }
476
+
477
+ $ret = '<' . $this->tag;
478
+ $i = -1;
479
+
480
+ foreach ($this->attr as $key => $val) {
481
+ ++$i;
482
+
483
+ // skip removed attribute
484
+ if ($val === null || $val === false) { continue; }
485
+
486
+ $ret .= $this->_[HDOM_INFO_SPACE][$i][0];
487
+
488
+ //no value attr: nowrap, checked selected...
489
+ if ($val === true) {
490
+ $ret .= $key;
491
+ } else {
492
+ switch ($this->_[HDOM_INFO_QUOTE][$i])
493
+ {
494
+ case HDOM_QUOTE_DOUBLE: $quote = '"'; break;
495
+ case HDOM_QUOTE_SINGLE: $quote = '\''; break;
496
+ default: $quote = '';
497
+ }
498
+
499
+ $ret .= $key
500
+ . $this->_[HDOM_INFO_SPACE][$i][1]
501
+ . '='
502
+ . $this->_[HDOM_INFO_SPACE][$i][2]
503
+ . $quote
504
+ . $val
505
+ . $quote;
506
+ }
507
+ }
508
+
509
+ $ret = $this->dom->restore_noise($ret);
510
+ return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>';
511
+ }
512
+
513
+ function find($selector, $idx = null, $lowercase = false)
514
+ {
515
+ $selectors = $this->parse_selector($selector);
516
+ if (($count = count($selectors)) === 0) { return array(); }
517
+ $found_keys = array();
518
+
519
+ // find each selector
520
+ for ($c = 0; $c < $count; ++$c) {
521
+ // The change on the below line was documented on the sourceforge
522
+ // code tracker id 2788009
523
+ // used to be: if (($levle=count($selectors[0]))===0) return array();
524
+ if (($levle = count($selectors[$c])) === 0) { return array(); }
525
+ if (!isset($this->_[HDOM_INFO_BEGIN])) { return array(); }
526
+
527
+ $head = array($this->_[HDOM_INFO_BEGIN] => 1);
528
+ $cmd = ' '; // Combinator
529
+
530
+ // handle descendant selectors, no recursive!
531
+ for ($l = 0; $l < $levle; ++$l) {
532
+ $ret = array();
533
+
534
+ foreach ($head as $k => $v) {
535
+ $n = ($k === -1) ? $this->dom->root : $this->dom->nodes[$k];
536
+ //PaperG - Pass this optional parameter on to the seek function.
537
+ $n->seek($selectors[$c][$l], $ret, $cmd, $lowercase);
538
+ }
539
+
540
+ $head = $ret;
541
+ $cmd = $selectors[$c][$l][4]; // Next Combinator
542
+ }
543
+
544
+ foreach ($head as $k => $v) {
545
+ if (!isset($found_keys[$k])) {
546
+ $found_keys[$k] = 1;
547
+ }
548
+ }
549
+ }
550
+
551
+ // sort keys
552
+ ksort($found_keys);
553
+
554
+ $found = array();
555
+ foreach ($found_keys as $k => $v) {
556
+ $found[] = $this->dom->nodes[$k];
557
+ }
558
+
559
+ // return nth-element or array
560
+ if (is_null($idx)) { return $found; }
561
+ elseif ($idx < 0) { $idx = count($found) + $idx; }
562
+ return (isset($found[$idx])) ? $found[$idx] : null;
563
+ }
564
+
565
+ protected function seek($selector, &$ret, $parent_cmd, $lowercase = false)
566
+ {
567
+ global $debug_object;
568
+ if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
569
+
570
+ list($tag, $id, $class, $attributes, $cmb) = $selector;
571
+ $nodes = array();
572
+
573
+ if ($parent_cmd === ' ') { // Descendant Combinator
574
+ // Find parent closing tag if the current element doesn't have a closing
575
+ // tag (i.e. void element)
576
+ $end = (!empty($this->_[HDOM_INFO_END])) ? $this->_[HDOM_INFO_END] : 0;
577
+ if ($end == 0) {
578
+ $parent = $this->parent;
579
+ while (!isset($parent->_[HDOM_INFO_END]) && $parent !== null) {
580
+ $end -= 1;
581
+ $parent = $parent->parent;
582
+ }
583
+ $end += $parent->_[HDOM_INFO_END];
584
+ }
585
+
586
+ // Get list of target nodes
587
+ $nodes_start = $this->_[HDOM_INFO_BEGIN] + 1;
588
+ $nodes_count = $end - $nodes_start;
589
+ $nodes = array_slice($this->dom->nodes, $nodes_start, $nodes_count, true);
590
+ } elseif ($parent_cmd === '>') { // Child Combinator
591
+ $nodes = $this->children;
592
+ } elseif ($parent_cmd === '+'
593
+ && $this->parent
594
+ && in_array($this, $this->parent->children)) { // Next-Sibling Combinator
595
+ $index = array_search($this, $this->parent->children, true) + 1;
596
+ if ($index < count($this->parent->children))
597
+ $nodes[] = $this->parent->children[$index];
598
+ } elseif ($parent_cmd === '~'
599
+ && $this->parent
600
+ && in_array($this, $this->parent->children)) { // Subsequent Sibling Combinator
601
+ $index = array_search($this, $this->parent->children, true);
602
+ $nodes = array_slice($this->parent->children, $index);
603
+ }
604
+
605
+ // Go throgh each element starting at this element until the end tag
606
+ // Note: If this element is a void tag, any previous void element is
607
+ // skipped.
608
+ foreach($nodes as $node) {
609
+ $pass = true;
610
+
611
+ // Skip root nodes
612
+ if(!$node->parent) {
613
+ $pass = false;
614
+ }
615
+
616
+ // Handle 'text' selector
617
+ if($pass && $tag === 'text' && $node->tag === 'text') {
618
+ $ret[array_search($node, $this->dom->nodes, true)] = 1;
619
+ unset($node);
620
+ continue;
621
+ }
622
+
623
+ // Skip if node isn't a child node (i.e. text nodes)
624
+ if($pass && !in_array($node, $node->parent->children, true)) {
625
+ $pass = false;
626
+ }
627
+
628
+ // Skip if tag doesn't match
629
+ if ($pass && $tag !== '' && $tag !== $node->tag && $tag !== '*') {
630
+ $pass = false;
631
+ }
632
+
633
+ // Skip if ID doesn't exist
634
+ if ($pass && $id !== '' && !isset($node->attr['id'])) {
635
+ $pass = false;
636
+ }
637
+
638
+ // Check if ID matches
639
+ if ($pass && $id !== '' && isset($node->attr['id'])) {
640
+ // Note: Only consider the first ID (as browsers do)
641
+ $node_id = explode(' ', trim($node->attr['id']))[0];
642
+
643
+ if($id !== $node_id) { $pass = false; }
644
+ }
645
+
646
+ // Check if all class(es) exist
647
+ if ($pass && $class !== '' && is_array($class) && !empty($class)) {
648
+ if (isset($node->attr['class'])) {
649
+ $node_classes = explode(' ', $node->attr['class']);
650
+
651
+ if ($lowercase) {
652
+ $node_classes = array_map('strtolower', $node_classes);
653
+ }
654
+
655
+ foreach($class as $c) {
656
+ if(!in_array($c, $node_classes)) {
657
+ $pass = false;
658
+ break;
659
+ }
660
+ }
661
+ } else {
662
+ $pass = false;
663
+ }
664
+ }
665
+
666
+ // Check attributes
667
+ if ($pass
668
+ && $attributes !== ''
669
+ && is_array($attributes)
670
+ && !empty($attributes)) {
671
+ foreach($attributes as $a) {
672
+ list (
673
+ $att_name,
674
+ $att_expr,
675
+ $att_val,
676
+ $att_inv,
677
+ $att_case_sensitivity
678
+ ) = $a;
679
+
680
+ // Handle indexing attributes (i.e. "[2]")
681
+ /**
682
+ * Note: This is not supported by the CSS Standard but adds
683
+ * the ability to select items compatible to XPath (i.e.
684
+ * the 3rd element within it's parent).
685
+ *
686
+ * Note: This doesn't conflict with the CSS Standard which
687
+ * doesn't work on numeric attributes anyway.
688
+ */
689
+ if (is_numeric($att_name)
690
+ && $att_expr === ''
691
+ && $att_val === '') {
692
+ $count = 0;
693
+
694
+ // Find index of current element in parent
695
+ foreach ($node->parent->children as $c) {
696
+ if ($c->tag === $node->tag) ++$count;
697
+ if ($c === $node) break;
698
+ }
699
+
700
+ // If this is the correct node, continue with next
701
+ // attribute
702
+ if ($count === (int)$att_name) continue;
703
+ }
704
+
705
+ // Check attribute availability
706
+ if ($att_inv) { // Attribute should NOT be set
707
+ if (isset($node->attr[$att_name])) {
708
+ $pass = false;
709
+ break;
710
+ }
711
+ } else { // Attribute should be set
712
+ // todo: "plaintext" is not a valid CSS selector!
713
+ if ($att_name !== 'plaintext'
714
+ && !isset($node->attr[$att_name])) {
715
+ $pass = false;
716
+ break;
717
+ }
718
+ }
719
+
720
+ // Continue with next attribute if expression isn't defined
721
+ if ($att_expr === '') continue;
722
+
723
+ // If they have told us that this is a "plaintext"
724
+ // search then we want the plaintext of the node - right?
725
+ // todo "plaintext" is not a valid CSS selector!
726
+ if ($att_name === 'plaintext') {
727
+ $nodeKeyValue = $node->text();
728
+ } else {
729
+ $nodeKeyValue = $node->attr[$att_name];
730
+ }
731
+
732
+ if (is_object($debug_object)) {
733
+ $debug_object->debug_log(2,
734
+ 'testing node: '
735
+ . $node->tag
736
+ . ' for attribute: '
737
+ . $att_name
738
+ . $att_expr
739
+ . $att_val
740
+ . ' where nodes value is: '
741
+ . $nodeKeyValue
742
+ );
743
+ }
744
+
745
+ // If lowercase is set, do a case insensitive test of
746
+ // the value of the selector.
747
+ if ($lowercase) {
748
+ $check = $this->match(
749
+ $att_expr,
750
+ strtolower($att_val),
751
+ strtolower($nodeKeyValue),
752
+ $att_case_sensitivity
753
+ );
754
+ } else {
755
+ $check = $this->match(
756
+ $att_expr,
757
+ $att_val,
758
+ $nodeKeyValue,
759
+ $att_case_sensitivity
760
+ );
761
+ }
762
+
763
+ if (is_object($debug_object)) {
764
+ $debug_object->debug_log(2,
765
+ 'after match: '
766
+ . ($check ? 'true' : 'false')
767
+ );
768
+ }
769
+
770
+ if (!$check) {
771
+ $pass = false;
772
+ break;
773
+ }
774
+ }
775
+ }
776
+
777
+ // Found a match. Add to list and clear node
778
+ if ($pass) $ret[$node->_[HDOM_INFO_BEGIN]] = 1;
779
+ unset($node);
780
+ }
781
+ // It's passed by reference so this is actually what this function returns.
782
+ if (is_object($debug_object)) {
783
+ $debug_object->debug_log(1, 'EXIT - ret: ', $ret);
784
+ }
785
+ }
786
+
787
+ protected function match($exp, $pattern, $value, $case_sensitivity)
788
+ {
789
+ global $debug_object;
790
+ if (is_object($debug_object)) {$debug_object->debug_log_entry(1);}
791
+
792
+ if ($case_sensitivity === 'i') {
793
+ $pattern = strtolower($pattern);
794
+ $value = strtolower($value);
795
+ }
796
+
797
+ switch ($exp) {
798
+ case '=':
799
+ return ($value === $pattern);
800
+ case '!=':
801
+ return ($value !== $pattern);
802
+ case '^=':
803
+ return preg_match('/^' . preg_quote($pattern, '/') . '/', $value);
804
+ case '$=':
805
+ return preg_match('/' . preg_quote($pattern, '/') . '$/', $value);
806
+ case '*=':
807
+ return preg_match('/' . preg_quote($pattern, '/') . '/', $value);
808
+ case '|=':
809
+ /**
810
+ * [att|=val]
811
+ *
812
+ * Represents an element with the att attribute, its value
813
+ * either being exactly "val" or beginning with "val"
814
+ * immediately followed by "-" (U+002D).
815
+ */
816
+ return strpos($value, $pattern) === 0;
817
+ case '~=':
818
+ /**
819
+ * [att~=val]
820
+ *
821
+ * Represents an element with the att attribute whose value is a
822
+ * whitespace-separated list of words, one of which is exactly
823
+ * "val". If "val" contains whitespace, it will never represent
824
+ * anything (since the words are separated by spaces). Also if
825
+ * "val" is the empty string, it will never represent anything.
826
+ */
827
+ return in_array($pattern, explode(' ', trim($value)), true);
828
+ }
829
+ return false;
830
+ }
831
+
832
+ protected function parse_selector($selector_string)
833
+ {
834
+ global $debug_object;
835
+ if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
836
+
837
+ /**
838
+ * Pattern of CSS selectors, modified from mootools (https://mootools.net/)
839
+ *
840
+ * Paperg: Add the colon to the attribute, so that it properly finds
841
+ * <tag attr:ibute="something" > like google does.
842
+ *
843
+ * Note: if you try to look at this attribute, you MUST use getAttribute
844
+ * since $dom->x:y will fail the php syntax check.
845
+ *
846
+ * Notice the \[ starting the attribute? and the @? following? This
847
+ * implies that an attribute can begin with an @ sign that is not
848
+ * captured. This implies that an html attribute specifier may start
849
+ * with an @ sign that is NOT captured by the expression. Farther study
850
+ * is required to determine of this should be documented or removed.
851
+ *
852
+ * Matches selectors in this order:
853
+ *
854
+ * [0] - full match
855
+ *
856
+ * [1] - tag name
857
+ * ([\w:\*-]*)
858
+ * Matches the tag name consisting of zero or more words, colons,
859
+ * asterisks and hyphens.
860
+ *
861
+ * [2] - id name
862
+ * (?:\#([\w-]+))
863
+ * Optionally matches a id name, consisting of an "#" followed by
864
+ * the id name (one or more words and hyphens).
865
+ *
866
+ * [3] - class names (including dots)
867
+ * (?:\.([\w\.-]+))?
868
+ * Optionally matches a list of classs, consisting of an "."
869
+ * followed by the class name (one or more words and hyphens)
870
+ * where multiple classes can be chained (i.e. ".foo.bar.baz")
871
+ *
872
+ * [4] - attributes
873
+ * ((?:\[@?(?:!?[\w:-]+)(?:(?:[!*^$|~]?=)[\"']?(?:.*?)[\"']?)?(?:\s*?(?:[iIsS])?)?\])+)?
874
+ * Optionally matches the attributes list
875
+ *
876
+ * [5] - separator
877
+ * ([\/, >+~]+)
878
+ * Matches the selector list separator
879
+ */
880
+ // phpcs:ignore Generic.Files.LineLength
881
+ $pattern = "/([\w:\*-]*)(?:\#([\w-]+))?(?:|\.([\w\.-]+))?((?:\[@?(?:!?[\w:-]+)(?:(?:[!*^$|~]?=)[\"']?(?:.*?)[\"']?)?(?:\s*?(?:[iIsS])?)?\])+)?([\/, >+~]+)/is";
882
+
883
+ preg_match_all(
884
+ $pattern,
885
+ trim($selector_string) . ' ', // Add final ' ' as pseudo separator
886
+ $matches,
887
+ PREG_SET_ORDER
888
+ );
889
+
890
+ if (is_object($debug_object)) {
891
+ $debug_object->debug_log(2, 'Matches Array: ', $matches);
892
+ }
893
+
894
+ $selectors = array();
895
+ $result = array();
896
+
897
+ foreach ($matches as $m) {
898
+ $m[0] = trim($m[0]);
899
+
900
+ // Skip NoOps
901
+ if ($m[0] === '' || $m[0] === '/' || $m[0] === '//') { continue; }
902
+
903
+ // Convert to lowercase
904
+ if ($this->dom->lowercase) {
905
+ $m[1] = strtolower($m[1]);
906
+ }
907
+
908
+ // Extract classes
909
+ if ($m[3] !== '') { $m[3] = explode('.', $m[3]); }
910
+
911
+ /* Extract attributes (pattern based on the pattern above!)
912
+
913
+ * [0] - full match
914
+ * [1] - attribute name
915
+ * [2] - attribute expression
916
+ * [3] - attribute value
917
+ * [4] - case sensitivity
918
+ *
919
+ * Note: Attributes can be negated with a "!" prefix to their name
920
+ */
921
+ if($m[4] !== '') {
922
+ preg_match_all(
923
+ "/\[@?(!?[\w:-]+)(?:([!*^$|~]?=)[\"']?(.*?)[\"']?)?(?:\s+?([iIsS])?)?\]/is",
924
+ trim($m[4]),
925
+ $attributes,
926
+ PREG_SET_ORDER
927
+ );
928
+
929
+ // Replace element by array
930
+ $m[4] = array();
931
+
932
+ foreach($attributes as $att) {
933
+ // Skip empty matches
934
+ if(trim($att[0]) === '') { continue; }
935
+
936
+ $inverted = (isset($att[1][0]) && $att[1][0] === '!');
937
+ $m[4][] = array(
938
+ $inverted ? substr($att[1], 1) : $att[1], // Name
939
+ (isset($att[2])) ? $att[2] : '', // Expression
940
+ (isset($att[3])) ? $att[3] : '', // Value
941
+ $inverted, // Inverted Flag
942
+ (isset($att[4])) ? strtolower($att[4]) : '', // Case-Sensitivity
943
+ );
944
+ }
945
+ }
946
+
947
+ // Sanitize Separator
948
+ if ($m[5] !== '' && trim($m[5]) === '') { // Descendant Separator
949
+ $m[5] = ' ';
950
+ } else { // Other Separator
951
+ $m[5] = trim($m[5]);
952
+ }
953
+
954
+ // Clear Separator if it's a Selector List
955
+ if ($is_list = ($m[5] === ',')) { $m[5] = ''; }
956
+
957
+ // Remove full match before adding to results
958
+ array_shift($m);
959
+ $result[] = $m;
960
+
961
+ if ($is_list) { // Selector List
962
+ $selectors[] = $result;
963
+ $result = array();
964
+ }
965
+ }
966
+
967
+ if (count($result) > 0) { $selectors[] = $result; }
968
+ return $selectors;
969
+ }
970
+
971
+ function __get($name)
972
+ {
973
+ if (isset($this->attr[$name])) {
974
+ return $this->convert_text($this->attr[$name]);
975
+ }
976
+ switch ($name) {
977
+ case 'outertext': return $this->outertext();
978
+ case 'innertext': return $this->innertext();
979
+ case 'plaintext': return $this->text();
980
+ case 'xmltext': return $this->xmltext();
981
+ default: return array_key_exists($name, $this->attr);
982
+ }
983
+ }
984
+
985
+ function __set($name, $value)
986
+ {
987
+ global $debug_object;
988
+ if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
989
+
990
+ switch ($name) {
991
+ case 'outertext': return $this->_[HDOM_INFO_OUTER] = $value;
992
+ case 'innertext':
993
+ if (isset($this->_[HDOM_INFO_TEXT])) {
994
+ return $this->_[HDOM_INFO_TEXT] = $value;
995
+ }
996
+ return $this->_[HDOM_INFO_INNER] = $value;
997
+ }
998
+
999
+ if (!isset($this->attr[$name])) {
1000
+ $this->_[HDOM_INFO_SPACE][] = array(' ', '', '');
1001
+ $this->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
1002
+ }
1003
+
1004
+ $this->attr[$name] = $value;
1005
+ }
1006
+
1007
+ function __isset($name)
1008
+ {
1009
+ switch ($name) {
1010
+ case 'outertext': return true;
1011
+ case 'innertext': return true;
1012
+ case 'plaintext': return true;
1013
+ }
1014
+ //no value attr: nowrap, checked selected...
1015
+ return (array_key_exists($name, $this->attr)) ? true : isset($this->attr[$name]);
1016
+ }
1017
+
1018
+ function __unset($name)
1019
+ {
1020
+ if (isset($this->attr[$name])) { unset($this->attr[$name]); }
1021
+ }
1022
+
1023
+ function convert_text($text)
1024
+ {
1025
+ global $debug_object;
1026
+ if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
1027
+
1028
+ $converted_text = $text;
1029
+
1030
+ $sourceCharset = '';
1031
+ $targetCharset = '';
1032
+
1033
+ if ($this->dom) {
1034
+ $sourceCharset = strtoupper($this->dom->_charset);
1035
+ $targetCharset = strtoupper($this->dom->_target_charset);
1036
+ }
1037
+
1038
+ if (is_object($debug_object)) {
1039
+ $debug_object->debug_log(3,
1040
+ 'source charset: '
1041
+ . $sourceCharset
1042
+ . ' target charaset: '
1043
+ . $targetCharset
1044
+ );
1045
+ }
1046
+
1047
+ if (!empty($sourceCharset)
1048
+ && !empty($targetCharset)
1049
+ && (strcasecmp($sourceCharset, $targetCharset) != 0)) {
1050
+ // Check if the reported encoding could have been incorrect and the text is actually already UTF-8
1051
+ if ((strcasecmp($targetCharset, 'UTF-8') == 0)
1052
+ && ($this->is_utf8($text))) {
1053
+ $converted_text = $text;
1054
+ } else {
1055
+ $converted_text = iconv($sourceCharset, $targetCharset, $text);
1056
+ }
1057
+ }
1058
+
1059
+ // Lets make sure that we don't have that silly BOM issue with any of the utf-8 text we output.
1060
+ if ($targetCharset === 'UTF-8') {
1061
+ if (substr($converted_text, 0, 3) === "\xef\xbb\xbf") {
1062
+ $converted_text = substr($converted_text, 3);
1063
+ }
1064
+
1065
+ if (substr($converted_text, -3) === "\xef\xbb\xbf") {
1066
+ $converted_text = substr($converted_text, 0, -3);
1067
+ }
1068
+ }
1069
+
1070
+ return $converted_text;
1071
+ }
1072
+
1073
+ static function is_utf8($str)
1074
+ {
1075
+ $c = 0; $b = 0;
1076
+ $bits = 0;
1077
+ $len = strlen($str);
1078
+ for($i = 0; $i < $len; $i++) {
1079
+ $c = ord($str[$i]);
1080
+ if($c > 128) {
1081
+ if(($c >= 254)) { return false; }
1082
+ elseif($c >= 252) { $bits = 6; }
1083
+ elseif($c >= 248) { $bits = 5; }
1084
+ elseif($c >= 240) { $bits = 4; }
1085
+ elseif($c >= 224) { $bits = 3; }
1086
+ elseif($c >= 192) { $bits = 2; }
1087
+ else { return false; }
1088
+ if(($i + $bits) > $len) { return false; }
1089
+ while($bits > 1) {
1090
+ $i++;
1091
+ $b = ord($str[$i]);
1092
+ if($b < 128 || $b > 191) { return false; }
1093
+ $bits--;
1094
+ }
1095
+ }
1096
+ }
1097
+ return true;
1098
+ }
1099
+
1100
+ function get_display_size()
1101
+ {
1102
+ global $debug_object;
1103
+
1104
+ $width = -1;
1105
+ $height = -1;
1106
+
1107
+ if ($this->tag !== 'img') {
1108
+ return false;
1109
+ }
1110
+
1111
+ // See if there is aheight or width attribute in the tag itself.
1112
+ if (isset($this->attr['width'])) {
1113
+ $width = $this->attr['width'];
1114
+ }
1115
+
1116
+ if (isset($this->attr['height'])) {
1117
+ $height = $this->attr['height'];
1118
+ }
1119
+
1120
+ // Now look for an inline style.
1121
+ if (isset($this->attr['style'])) {
1122
+ // Thanks to user gnarf from stackoverflow for this regular expression.
1123
+ $attributes = array();
1124
+
1125
+ preg_match_all(
1126
+ '/([\w-]+)\s*:\s*([^;]+)\s*;?/',
1127
+ $this->attr['style'],
1128
+ $matches,
1129
+ PREG_SET_ORDER
1130
+ );
1131
+
1132
+ foreach ($matches as $match) {
1133
+ $attributes[$match[1]] = $match[2];
1134
+ }
1135
+
1136
+ // If there is a width in the style attributes:
1137
+ if (isset($attributes['width']) && $width == -1) {
1138
+ // check that the last two characters are px (pixels)
1139
+ if (strtolower(substr($attributes['width'], -2)) === 'px') {
1140
+ $proposed_width = substr($attributes['width'], 0, -2);
1141
+ // Now make sure that it's an integer and not something stupid.
1142
+ if (filter_var($proposed_width, FILTER_VALIDATE_INT)) {
1143
+ $width = $proposed_width;
1144
+ }
1145
+ }
1146
+ }
1147
+
1148
+ // If there is a width in the style attributes:
1149
+ if (isset($attributes['height']) && $height == -1) {
1150
+ // check that the last two characters are px (pixels)
1151
+ if (strtolower(substr($attributes['height'], -2)) == 'px') {
1152
+ $proposed_height = substr($attributes['height'], 0, -2);
1153
+ // Now make sure that it's an integer and not something stupid.
1154
+ if (filter_var($proposed_height, FILTER_VALIDATE_INT)) {
1155
+ $height = $proposed_height;
1156
+ }
1157
+ }
1158
+ }
1159
+
1160
+ }
1161
+
1162
+ // Future enhancement:
1163
+ // Look in the tag to see if there is a class or id specified that has
1164
+ // a height or width attribute to it.
1165
+
1166
+ // Far future enhancement
1167
+ // Look at all the parent tags of this image to see if they specify a
1168
+ // class or id that has an img selector that specifies a height or width
1169
+ // Note that in this case, the class or id will have the img subselector
1170
+ // for it to apply to the image.
1171
+
1172
+ // ridiculously far future development
1173
+ // If the class or id is specified in a SEPARATE css file thats not on
1174
+ // the page, go get it and do what we were just doing for the ones on
1175
+ // the page.
1176
+
1177
+ $result = array(
1178
+ 'height' => $height,
1179
+ 'width' => $width
1180
+ );
1181
+
1182
+ return $result;
1183
+ }
1184
+
1185
+ function save($filepath = '')
1186
+ {
1187
+ $ret = $this->outertext();
1188
+
1189
+ if ($filepath !== '') {
1190
+ file_put_contents($filepath, $ret, LOCK_EX);
1191
+ }
1192
+
1193
+ return $ret;
1194
+ }
1195
+
1196
+ function addClass($class)
1197
+ {
1198
+ if (is_string($class)) {
1199
+ $class = explode(' ', $class);
1200
+ }
1201
+
1202
+ if (is_array($class)) {
1203
+ foreach($class as $c) {
1204
+ if (isset($this->class)) {
1205
+ if ($this->hasClass($c)) {
1206
+ continue;
1207
+ } else {
1208
+ $this->class .= ' ' . $c;
1209
+ }
1210
+ } else {
1211
+ $this->class = $c;
1212
+ }
1213
+ }
1214
+ } else {
1215
+ if (is_object($debug_object)) {
1216
+ $debug_object->debug_log(2, 'Invalid type: ', gettype($class));
1217
+ }
1218
+ }
1219
+ }
1220
+
1221
+ function hasClass($class)
1222
+ {
1223
+ if (is_string($class)) {
1224
+ if (isset($this->class)) {
1225
+ return in_array($class, explode(' ', $this->class), true);
1226
+ }
1227
+ } else {
1228
+ if (is_object($debug_object)) {
1229
+ $debug_object->debug_log(2, 'Invalid type: ', gettype($class));
1230
+ }
1231
+ }
1232
+
1233
+ return false;
1234
+ }
1235
+
1236
+ function removeClass($class = null)
1237
+ {
1238
+ if (!isset($this->class)) {
1239
+ return;
1240
+ }
1241
+
1242
+ if (is_null($class)) {
1243
+ $this->removeAttribute('class');
1244
+ return;
1245
+ }
1246
+
1247
+ if (is_string($class)) {
1248
+ $class = explode(' ', $class);
1249
+ }
1250
+
1251
+ if (is_array($class)) {
1252
+ $class = array_diff(explode(' ', $this->class), $class);
1253
+ if (empty($class)) {
1254
+ $this->removeAttribute('class');
1255
+ } else {
1256
+ $this->class = implode(' ', $class);
1257
+ }
1258
+ }
1259
+ }
1260
+
1261
+ function getAllAttributes()
1262
+ {
1263
+ return $this->attr;
1264
+ }
1265
+
1266
+ function getAttribute($name)
1267
+ {
1268
+ return $this->__get($name);
1269
+ }
1270
+
1271
+ function setAttribute($name, $value)
1272
+ {
1273
+ $this->__set($name, $value);
1274
+ }
1275
+
1276
+ function hasAttribute($name)
1277
+ {
1278
+ return $this->__isset($name);
1279
+ }
1280
+
1281
+ function removeAttribute($name)
1282
+ {
1283
+ $this->__set($name, null);
1284
+ }
1285
+
1286
+ function remove()
1287
+ {
1288
+ if ($this->parent) {
1289
+ $this->parent->removeChild($this);
1290
+ }
1291
+ }
1292
+
1293
+ function removeChild($node)
1294
+ {
1295
+ $nidx = array_search($node, $this->nodes, true);
1296
+ $cidx = array_search($node, $this->children, true);
1297
+ $didx = array_search($node, $this->dom->nodes, true);
1298
+
1299
+ if ($nidx !== false && $cidx !== false && $didx !== false) {
1300
+
1301
+ foreach($node->children as $child) {
1302
+ $node->removeChild($child);
1303
+ }
1304
+
1305
+ foreach($node->nodes as $entity) {
1306
+ $enidx = array_search($entity, $node->nodes, true);
1307
+ $edidx = array_search($entity, $node->dom->nodes, true);
1308
+
1309
+ if ($enidx !== false && $edidx !== false) {
1310
+ unset($node->nodes[$enidx]);
1311
+ unset($node->dom->nodes[$edidx]);
1312
+ }
1313
+ }
1314
+
1315
+ unset($this->nodes[$nidx]);
1316
+ unset($this->children[$cidx]);
1317
+ unset($this->dom->nodes[$didx]);
1318
+
1319
+ $node->clear();
1320
+
1321
+ }
1322
+ }
1323
+
1324
+ function getElementById($id)
1325
+ {
1326
+ return $this->find("#$id", 0);
1327
+ }
1328
+
1329
+ function getElementsById($id, $idx = null)
1330
+ {
1331
+ return $this->find("#$id", $idx);
1332
+ }
1333
+
1334
+ function getElementByTagName($name)
1335
+ {
1336
+ return $this->find($name, 0);
1337
+ }
1338
+
1339
+ function getElementsByTagName($name, $idx = null)
1340
+ {
1341
+ return $this->find($name, $idx);
1342
+ }
1343
+
1344
+ function parentNode()
1345
+ {
1346
+ return $this->parent();
1347
+ }
1348
+
1349
+ function childNodes($idx = -1)
1350
+ {
1351
+ return $this->children($idx);
1352
+ }
1353
+
1354
+ function firstChild()
1355
+ {
1356
+ return $this->first_child();
1357
+ }
1358
+
1359
+ function lastChild()
1360
+ {
1361
+ return $this->last_child();
1362
+ }
1363
+
1364
+ function nextSibling()
1365
+ {
1366
+ return $this->next_sibling();
1367
+ }
1368
+
1369
+ function previousSibling()
1370
+ {
1371
+ return $this->prev_sibling();
1372
+ }
1373
+
1374
+ function hasChildNodes()
1375
+ {
1376
+ return $this->has_child();
1377
+ }
1378
+
1379
+ function nodeName()
1380
+ {
1381
+ return $this->tag;
1382
+ }
1383
+
1384
+ function appendChild($node)
1385
+ {
1386
+ $node->parent($this);
1387
+ return $node;
1388
+ }
1389
+
1390
+ }
1391
+
1392
+ class simple_html_dom
1393
+ {
1394
+ public $root = null;
1395
+ public $nodes = array();
1396
+ public $callback = null;
1397
+ public $lowercase = false;
1398
+ public $original_size;
1399
+ public $size;
1400
+
1401
+ protected $pos;
1402
+ protected $doc;
1403
+ protected $char;
1404
+
1405
+ protected $cursor;
1406
+ protected $parent;
1407
+ protected $noise = array();
1408
+ protected $token_blank = " \t\r\n";
1409
+ protected $token_equal = ' =/>';
1410
+ protected $token_slash = " />\r\n\t";
1411
+ protected $token_attr = ' >';
1412
+
1413
+ public $_charset = '';
1414
+ public $_target_charset = '';
1415
+
1416
+ protected $default_br_text = '';
1417
+
1418
+ public $default_span_text = '';
1419
+
1420
+ protected $self_closing_tags = array(
1421
+ 'area' => 1,
1422
+ 'base' => 1,
1423
+ 'br' => 1,
1424
+ 'col' => 1,
1425
+ 'embed' => 1,
1426
+ 'hr' => 1,
1427
+ 'img' => 1,
1428
+ 'input' => 1,
1429
+ 'link' => 1,
1430
+ 'meta' => 1,
1431
+ 'param' => 1,
1432
+ 'source' => 1,
1433
+ 'track' => 1,
1434
+ 'wbr' => 1
1435
+ );
1436
+ protected $block_tags = array(
1437
+ 'body' => 1,
1438
+ 'div' => 1,
1439
+ 'form' => 1,
1440
+ 'root' => 1,
1441
+ 'span' => 1,
1442
+ 'table' => 1
1443
+ );
1444
+ protected $optional_closing_tags = array(
1445
+ // Not optional, see
1446
+ // https://www.w3.org/TR/html/textlevel-semantics.html#the-b-element
1447
+ 'b' => array('b' => 1),
1448
+ 'dd' => array('dd' => 1, 'dt' => 1),
1449
+ // Not optional, see
1450
+ // https://www.w3.org/TR/html/grouping-content.html#the-dl-element
1451
+ 'dl' => array('dd' => 1, 'dt' => 1),
1452
+ 'dt' => array('dd' => 1, 'dt' => 1),
1453
+ 'li' => array('li' => 1),
1454
+ 'optgroup' => array('optgroup' => 1, 'option' => 1),
1455
+ 'option' => array('optgroup' => 1, 'option' => 1),
1456
+ 'p' => array('p' => 1),
1457
+ 'rp' => array('rp' => 1, 'rt' => 1),
1458
+ 'rt' => array('rp' => 1, 'rt' => 1),
1459
+ 'td' => array('td' => 1, 'th' => 1),
1460
+ 'th' => array('td' => 1, 'th' => 1),
1461
+ 'tr' => array('td' => 1, 'th' => 1, 'tr' => 1),
1462
+ );
1463
+
1464
+ function __construct(
1465
+ $str = null,
1466
+ $lowercase = true,
1467
+ $forceTagsClosed = true,
1468
+ $target_charset = DEFAULT_TARGET_CHARSET,
1469
+ $stripRN = true,
1470
+ $defaultBRText = DEFAULT_BR_TEXT,
1471
+ $defaultSpanText = DEFAULT_SPAN_TEXT,
1472
+ $options = 0)
1473
+ {
1474
+ if ($str) {
1475
+ if (preg_match('/^http:\/\//i', $str) || is_file($str)) {
1476
+ $this->load_file($str);
1477
+ } else {
1478
+ $this->load(
1479
+ $str,
1480
+ $lowercase,
1481
+ $stripRN,
1482
+ $defaultBRText,
1483
+ $defaultSpanText,
1484
+ $options
1485
+ );
1486
+ }
1487
+ }
1488
+ // Forcing tags to be closed implies that we don't trust the html, but
1489
+ // it can lead to parsing errors if we SHOULD trust the html.
1490
+ if (!$forceTagsClosed) {
1491
+ $this->optional_closing_array = array();
1492
+ }
1493
+
1494
+ $this->_target_charset = $target_charset;
1495
+ }
1496
+
1497
+ function __destruct()
1498
+ {
1499
+ $this->clear();
1500
+ }
1501
+
1502
+ function load(
1503
+ $str,
1504
+ $lowercase = true,
1505
+ $stripRN = true,
1506
+ $defaultBRText = DEFAULT_BR_TEXT,
1507
+ $defaultSpanText = DEFAULT_SPAN_TEXT,
1508
+ $options = 0)
1509
+ {
1510
+ global $debug_object;
1511
+
1512
+ // prepare
1513
+ $this->prepare($str, $lowercase, $defaultBRText, $defaultSpanText);
1514
+
1515
+ // Per sourceforge http://sourceforge.net/tracker/?func=detail&aid=2949097&group_id=218559&atid=1044037
1516
+ // Script tags removal now preceeds style tag removal.
1517
+ // strip out <script> tags
1518
+ $this->remove_noise("'<\s*script[^>]*[^/]>(.*?)<\s*/\s*script\s*>'is");
1519
+ $this->remove_noise("'<\s*script\s*>(.*?)<\s*/\s*script\s*>'is");
1520
+
1521
+ // strip out the \r \n's if we are told to.
1522
+ if ($stripRN) {
1523
+ $this->doc = str_replace("\r", ' ', $this->doc);
1524
+ $this->doc = str_replace("\n", ' ', $this->doc);
1525
+
1526
+ // set the length of content since we have changed it.
1527
+ $this->size = strlen($this->doc);
1528
+ }
1529
+
1530
+ // strip out cdata
1531
+ $this->remove_noise("'<!\[CDATA\[(.*?)\]\]>'is", true);
1532
+ // strip out comments
1533
+ $this->remove_noise("'<!--(.*?)-->'is");
1534
+ // strip out <style> tags
1535
+ $this->remove_noise("'<\s*style[^>]*[^/]>(.*?)<\s*/\s*style\s*>'is");
1536
+ $this->remove_noise("'<\s*style\s*>(.*?)<\s*/\s*style\s*>'is");
1537
+ // strip out preformatted tags
1538
+ $this->remove_noise("'<\s*(?:code)[^>]*>(.*?)<\s*/\s*(?:code)\s*>'is");
1539
+ // strip out server side scripts
1540
+ $this->remove_noise("'(<\?)(.*?)(\?>)'s", true);
1541
+
1542
+ if($options & HDOM_SMARTY_AS_TEXT) { // Strip Smarty scripts
1543
+ $this->remove_noise("'(\{\w)(.*?)(\})'s", true);
1544
+ }
1545
+
1546
+ // parsing
1547
+ $this->parse();
1548
+ // end
1549
+ $this->root->_[HDOM_INFO_END] = $this->cursor;
1550
+ $this->parse_charset();
1551
+
1552
+ // make load function chainable
1553
+ return $this;
1554
+ }
1555
+
1556
+ function load_file()
1557
+ {
1558
+ $args = func_get_args();
1559
+
1560
+ if(($doc = call_user_func_array('file_get_contents', $args)) !== false) {
1561
+ $this->load($doc, true);
1562
+ } else {
1563
+ return false;
1564
+ }
1565
+ }
1566
+
1567
+ function set_callback($function_name)
1568
+ {
1569
+ $this->callback = $function_name;
1570
+ }
1571
+
1572
+ function remove_callback()
1573
+ {
1574
+ $this->callback = null;
1575
+ }
1576
+
1577
+ function save($filepath = '')
1578
+ {
1579
+ $ret = $this->root->innertext();
1580
+ if ($filepath !== '') { file_put_contents($filepath, $ret, LOCK_EX); }
1581
+ return $ret;
1582
+ }
1583
+
1584
+ function find($selector, $idx = null, $lowercase = false)
1585
+ {
1586
+ return $this->root->find($selector, $idx, $lowercase);
1587
+ }
1588
+
1589
+ function clear()
1590
+ {
1591
+ if (isset($this->nodes)) {
1592
+ foreach ($this->nodes as $n) {
1593
+ $n->clear();
1594
+ $n = null;
1595
+ }
1596
+ }
1597
+
1598
+ // This add next line is documented in the sourceforge repository.
1599
+ // 2977248 as a fix for ongoing memory leaks that occur even with the
1600
+ // use of clear.
1601
+ if (isset($this->children)) {
1602
+ foreach ($this->children as $n) {
1603
+ $n->clear();
1604
+ $n = null;
1605
+ }
1606
+ }
1607
+
1608
+ if (isset($this->parent)) {
1609
+ $this->parent->clear();
1610
+ unset($this->parent);
1611
+ }
1612
+
1613
+ if (isset($this->root)) {
1614
+ $this->root->clear();
1615
+ unset($this->root);
1616
+ }
1617
+
1618
+ unset($this->doc);
1619
+ unset($this->noise);
1620
+ }
1621
+
1622
+ function dump($show_attr = true)
1623
+ {
1624
+ $this->root->dump($show_attr);
1625
+ }
1626
+
1627
+ protected function prepare(
1628
+ $str, $lowercase = true,
1629
+ $defaultBRText = DEFAULT_BR_TEXT,
1630
+ $defaultSpanText = DEFAULT_SPAN_TEXT)
1631
+ {
1632
+ $this->clear();
1633
+
1634
+ $this->doc = trim($str);
1635
+ $this->size = strlen($this->doc);
1636
+ $this->original_size = $this->size; // original size of the html
1637
+ $this->pos = 0;
1638
+ $this->cursor = 1;
1639
+ $this->noise = array();
1640
+ $this->nodes = array();
1641
+ $this->lowercase = $lowercase;
1642
+ $this->default_br_text = $defaultBRText;
1643
+ $this->default_span_text = $defaultSpanText;
1644
+ $this->root = new simple_html_dom_node($this);
1645
+ $this->root->tag = 'root';
1646
+ $this->root->_[HDOM_INFO_BEGIN] = -1;
1647
+ $this->root->nodetype = HDOM_TYPE_ROOT;
1648
+ $this->parent = $this->root;
1649
+ if ($this->size > 0) { $this->char = $this->doc[0]; }
1650
+ }
1651
+
1652
+ protected function parse()
1653
+ {
1654
+ while (true) {
1655
+ // Read next tag if there is no text between current position and the
1656
+ // next opening tag.
1657
+ if (($s = $this->copy_until_char('<')) === '') {
1658
+ if($this->read_tag()) {
1659
+ continue;
1660
+ } else {
1661
+ return true;
1662
+ }
1663
+ }
1664
+
1665
+ // Add a text node for text between tags
1666
+ $node = new simple_html_dom_node($this);
1667
+ ++$this->cursor;
1668
+ $node->_[HDOM_INFO_TEXT] = $s;
1669
+ $this->link_nodes($node, false);
1670
+ }
1671
+ }
1672
+
1673
+ protected function parse_charset()
1674
+ {
1675
+ global $debug_object;
1676
+
1677
+ $charset = null;
1678
+
1679
+ if (function_exists('get_last_retrieve_url_contents_content_type')) {
1680
+ $contentTypeHeader = get_last_retrieve_url_contents_content_type();
1681
+ $success = preg_match('/charset=(.+)/', $contentTypeHeader, $matches);
1682
+ if ($success) {
1683
+ $charset = $matches[1];
1684
+ if (is_object($debug_object)) {
1685
+ $debug_object->debug_log(2,
1686
+ 'header content-type found charset of: '
1687
+ . $charset
1688
+ );
1689
+ }
1690
+ }
1691
+ }
1692
+
1693
+ if (empty($charset)) {
1694
+ // https://www.w3.org/TR/html/document-metadata.html#statedef-http-equiv-content-type
1695
+ $el = $this->root->find('meta[http-equiv=Content-Type]', 0, true);
1696
+
1697
+ if (!empty($el)) {
1698
+ $fullvalue = $el->content;
1699
+ if (is_object($debug_object)) {
1700
+ $debug_object->debug_log(2,
1701
+ 'meta content-type tag found'
1702
+ . $fullvalue
1703
+ );
1704
+ }
1705
+
1706
+ if (!empty($fullvalue)) {
1707
+ $success = preg_match(
1708
+ '/charset=(.+)/i',
1709
+ $fullvalue,
1710
+ $matches
1711
+ );
1712
+
1713
+ if ($success) {
1714
+ $charset = $matches[1];
1715
+ } else {
1716
+ // If there is a meta tag, and they don't specify the
1717
+ // character set, research says that it's typically
1718
+ // ISO-8859-1
1719
+ if (is_object($debug_object)) {
1720
+ $debug_object->debug_log(2,
1721
+ 'meta content-type tag couldn\'t be parsed. using iso-8859 default.'
1722
+ );
1723
+ }
1724
+
1725
+ $charset = 'ISO-8859-1';
1726
+ }
1727
+ }
1728
+ }
1729
+ }
1730
+
1731
+ if (empty($charset)) {
1732
+ // https://www.w3.org/TR/html/document-metadata.html#character-encoding-declaration
1733
+ if ($meta = $this->root->find('meta[charset]', 0)) {
1734
+ $charset = $meta->charset;
1735
+ if (is_object($debug_object)) {
1736
+ $debug_object->debug_log(2, 'meta charset: ' . $charset);
1737
+ }
1738
+ }
1739
+ }
1740
+
1741
+ if (empty($charset)) {
1742
+ // Try to guess the charset based on the content
1743
+ // Requires Multibyte String (mbstring) support (optional)
1744
+ if (function_exists('mb_detect_encoding')) {
1745
+ /**
1746
+ * mb_detect_encoding() is not intended to distinguish between
1747
+ * charsets, especially single-byte charsets. Its primary
1748
+ * purpose is to detect which multibyte encoding is in use,
1749
+ * i.e. UTF-8, UTF-16, shift-JIS, etc.
1750
+ *
1751
+ * -- https://bugs.php.net/bug.php?id=38138
1752
+ *
1753
+ * Adding both CP1251/ISO-8859-5 and CP1252/ISO-8859-1 will
1754
+ * always result in CP1251/ISO-8859-5 and vice versa.
1755
+ *
1756
+ * Thus, only detect if it's either UTF-8 or CP1252/ISO-8859-1
1757
+ * to stay compatible.
1758
+ */
1759
+ $encoding = mb_detect_encoding(
1760
+ $this->doc,
1761
+ array( 'UTF-8', 'CP1252', 'ISO-8859-1' )
1762
+ );
1763
+
1764
+ if ($encoding === 'CP1252' || $encoding === 'ISO-8859-1') {
1765
+ // Due to a limitation of mb_detect_encoding
1766
+ // 'CP1251'/'ISO-8859-5' will be detected as
1767
+ // 'CP1252'/'ISO-8859-1'. This will cause iconv to fail, in
1768
+ // which case we can simply assume it is the other charset.
1769
+ if (!@iconv('CP1252', 'UTF-8', $this->doc)) {
1770
+ $encoding = 'CP1251';
1771
+ }
1772
+ }
1773
+
1774
+ if ($encoding !== false) {
1775
+ $charset = $encoding;
1776
+ if (is_object($debug_object)) {
1777
+ $debug_object->debug_log(2, 'mb_detect: ' . $charset);
1778
+ }
1779
+ }
1780
+ }
1781
+ }
1782
+
1783
+ if (empty($charset)) {
1784
+ // Assume it's UTF-8 as it is the most likely charset to be used
1785
+ $charset = 'UTF-8';
1786
+ if (is_object($debug_object)) {
1787
+ $debug_object->debug_log(2, 'No match found, assume ' . $charset);
1788
+ }
1789
+ }
1790
+
1791
+ // Since CP1252 is a superset, if we get one of it's subsets, we want
1792
+ // it instead.
1793
+ if ((strtolower($charset) == 'iso-8859-1')
1794
+ || (strtolower($charset) == 'latin1')
1795
+ || (strtolower($charset) == 'latin-1')) {
1796
+ $charset = 'CP1252';
1797
+ if (is_object($debug_object)) {
1798
+ $debug_object->debug_log(2,
1799
+ 'replacing ' . $charset . ' with CP1252 as its a superset'
1800
+ );
1801
+ }
1802
+ }
1803
+
1804
+ if (is_object($debug_object)) {
1805
+ $debug_object->debug_log(1, 'EXIT - ' . $charset);
1806
+ }
1807
+
1808
+ return $this->_charset = $charset;
1809
+ }
1810
+
1811
+ protected function read_tag()
1812
+ {
1813
+ // Set end position if no further tags found
1814
+ if ($this->char !== '<') {
1815
+ $this->root->_[HDOM_INFO_END] = $this->cursor;
1816
+ return false;
1817
+ }
1818
+
1819
+ $begin_tag_pos = $this->pos;
1820
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
1821
+
1822
+ // end tag
1823
+ if ($this->char === '/') {
1824
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
1825
+
1826
+ // Skip whitespace in end tags (i.e. in "</ html>")
1827
+ $this->skip($this->token_blank);
1828
+ $tag = $this->copy_until_char('>');
1829
+
1830
+ // Skip attributes in end tags
1831
+ if (($pos = strpos($tag, ' ')) !== false) {
1832
+ $tag = substr($tag, 0, $pos);
1833
+ }
1834
+
1835
+ $parent_lower = strtolower($this->parent->tag);
1836
+ $tag_lower = strtolower($tag);
1837
+
1838
+ // The end tag is supposed to close the parent tag. Handle situations
1839
+ // when it doesn't
1840
+ if ($parent_lower !== $tag_lower) {
1841
+ // Parent tag does not have to be closed necessarily (optional closing tag)
1842
+ // Current tag is a block tag, so it may close an ancestor
1843
+ if (isset($this->optional_closing_tags[$parent_lower])
1844
+ && isset($this->block_tags[$tag_lower])) {
1845
+
1846
+ $this->parent->_[HDOM_INFO_END] = 0;
1847
+ $org_parent = $this->parent;
1848
+
1849
+ // Traverse ancestors to find a matching opening tag
1850
+ // Stop at root node
1851
+ while (($this->parent->parent)
1852
+ && strtolower($this->parent->tag) !== $tag_lower
1853
+ ){
1854
+ $this->parent = $this->parent->parent;
1855
+ }
1856
+
1857
+ // If we don't have a match add current tag as text node
1858
+ if (strtolower($this->parent->tag) !== $tag_lower) {
1859
+ $this->parent = $org_parent; // restore origonal parent
1860
+
1861
+ if ($this->parent->parent) {
1862
+ $this->parent = $this->parent->parent;
1863
+ }
1864
+
1865
+ $this->parent->_[HDOM_INFO_END] = $this->cursor;
1866
+ return $this->as_text_node($tag);
1867
+ }
1868
+ } elseif (($this->parent->parent)
1869
+ && isset($this->block_tags[$tag_lower])
1870
+ ) {
1871
+ // Grandparent exists and current tag is a block tag, so our
1872
+ // parent doesn't have an end tag
1873
+ $this->parent->_[HDOM_INFO_END] = 0; // No end tag
1874
+ $org_parent = $this->parent;
1875
+
1876
+ // Traverse ancestors to find a matching opening tag
1877
+ // Stop at root node
1878
+ while (($this->parent->parent)
1879
+ && strtolower($this->parent->tag) !== $tag_lower
1880
+ ) {
1881
+ $this->parent = $this->parent->parent;
1882
+ }
1883
+
1884
+ // If we don't have a match add current tag as text node
1885
+ if (strtolower($this->parent->tag) !== $tag_lower) {
1886
+ $this->parent = $org_parent; // restore origonal parent
1887
+ $this->parent->_[HDOM_INFO_END] = $this->cursor;
1888
+ return $this->as_text_node($tag);
1889
+ }
1890
+ } elseif (($this->parent->parent)
1891
+ && strtolower($this->parent->parent->tag) === $tag_lower
1892
+ ) { // Grandparent exists and current tag closes it
1893
+ $this->parent->_[HDOM_INFO_END] = 0;
1894
+ $this->parent = $this->parent->parent;
1895
+ } else { // Random tag, add as text node
1896
+ return $this->as_text_node($tag);
1897
+ }
1898
+ }
1899
+
1900
+ // Set end position of parent tag to current cursor position
1901
+ $this->parent->_[HDOM_INFO_END] = $this->cursor;
1902
+
1903
+ if ($this->parent->parent) {
1904
+ $this->parent = $this->parent->parent;
1905
+ }
1906
+
1907
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
1908
+ return true;
1909
+ }
1910
+
1911
+ // start tag
1912
+ $node = new simple_html_dom_node($this);
1913
+ $node->_[HDOM_INFO_BEGIN] = $this->cursor;
1914
+ ++$this->cursor;
1915
+ $tag = $this->copy_until($this->token_slash); // Get tag name
1916
+ $node->tag_start = $begin_tag_pos;
1917
+
1918
+ // doctype, cdata & comments...
1919
+ // <!DOCTYPE html>
1920
+ // <![CDATA[ ... ]]>
1921
+ // <!-- Comment -->
1922
+ if (isset($tag[0]) && $tag[0] === '!') {
1923
+ $node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until_char('>');
1924
+
1925
+ if (isset($tag[2]) && $tag[1] === '-' && $tag[2] === '-') { // Comment ("<!--")
1926
+ $node->nodetype = HDOM_TYPE_COMMENT;
1927
+ $node->tag = 'comment';
1928
+ } else { // Could be doctype or CDATA but we don't care
1929
+ $node->nodetype = HDOM_TYPE_UNKNOWN;
1930
+ $node->tag = 'unknown';
1931
+ }
1932
+
1933
+ if ($this->char === '>') { $node->_[HDOM_INFO_TEXT] .= '>'; }
1934
+
1935
+ $this->link_nodes($node, true);
1936
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
1937
+ return true;
1938
+ }
1939
+
1940
+ // The start tag cannot contain another start tag, if so add as text
1941
+ // i.e. "<<html>"
1942
+ if ($pos = strpos($tag, '<') !== false) {
1943
+ $tag = '<' . substr($tag, 0, -1);
1944
+ $node->_[HDOM_INFO_TEXT] = $tag;
1945
+ $this->link_nodes($node, false);
1946
+ $this->char = $this->doc[--$this->pos]; // prev
1947
+ return true;
1948
+ }
1949
+
1950
+ // Handle invalid tag names (i.e. "<html#doc>")
1951
+ if (!preg_match('/^\w[\w:-]*$/', $tag)) {
1952
+ $node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until('<>');
1953
+
1954
+ // Next char is the beginning of a new tag, don't touch it.
1955
+ if ($this->char === '<') {
1956
+ $this->link_nodes($node, false);
1957
+ return true;
1958
+ }
1959
+
1960
+ // Next char closes current tag, add and be done with it.
1961
+ if ($this->char === '>') { $node->_[HDOM_INFO_TEXT] .= '>'; }
1962
+ $this->link_nodes($node, false);
1963
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
1964
+ return true;
1965
+ }
1966
+
1967
+ // begin tag, add new node
1968
+ $node->nodetype = HDOM_TYPE_ELEMENT;
1969
+ $tag_lower = strtolower($tag);
1970
+ $node->tag = ($this->lowercase) ? $tag_lower : $tag;
1971
+
1972
+ // handle optional closing tags
1973
+ if (isset($this->optional_closing_tags[$tag_lower])) {
1974
+ // Traverse ancestors to close all optional closing tags
1975
+ while (isset($this->optional_closing_tags[$tag_lower][strtolower($this->parent->tag)])) {
1976
+ $this->parent->_[HDOM_INFO_END] = 0;
1977
+ $this->parent = $this->parent->parent;
1978
+ }
1979
+ $node->parent = $this->parent;
1980
+ }
1981
+
1982
+ $guard = 0; // prevent infinity loop
1983
+
1984
+ // [0] Space between tag and first attribute
1985
+ $space = array($this->copy_skip($this->token_blank), '', '');
1986
+
1987
+ // attributes
1988
+ do {
1989
+ // Everything until the first equal sign should be the attribute name
1990
+ $name = $this->copy_until($this->token_equal);
1991
+
1992
+ if ($name === '' && $this->char !== null && $space[0] === '') {
1993
+ break;
1994
+ }
1995
+
1996
+ if ($guard === $this->pos) { // Escape infinite loop
1997
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
1998
+ continue;
1999
+ }
2000
+
2001
+ $guard = $this->pos;
2002
+
2003
+ // handle endless '<'
2004
+ // Out of bounds before the tag ended
2005
+ if ($this->pos >= $this->size - 1 && $this->char !== '>') {
2006
+ $node->nodetype = HDOM_TYPE_TEXT;
2007
+ $node->_[HDOM_INFO_END] = 0;
2008
+ $node->_[HDOM_INFO_TEXT] = '<' . $tag . $space[0] . $name;
2009
+ $node->tag = 'text';
2010
+ $this->link_nodes($node, false);
2011
+ return true;
2012
+ }
2013
+
2014
+ // handle mismatch '<'
2015
+ // Attributes cannot start after opening tag
2016
+ if ($this->doc[$this->pos - 1] == '<') {
2017
+ $node->nodetype = HDOM_TYPE_TEXT;
2018
+ $node->tag = 'text';
2019
+ $node->attr = array();
2020
+ $node->_[HDOM_INFO_END] = 0;
2021
+ $node->_[HDOM_INFO_TEXT] = substr(
2022
+ $this->doc,
2023
+ $begin_tag_pos,
2024
+ $this->pos - $begin_tag_pos - 1
2025
+ );
2026
+ $this->pos -= 2;
2027
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2028
+ $this->link_nodes($node, false);
2029
+ return true;
2030
+ }
2031
+
2032
+ if ($name !== '/' && $name !== '') { // this is a attribute name
2033
+ // [1] Whitespace after attribute name
2034
+ $space[1] = $this->copy_skip($this->token_blank);
2035
+
2036
+ $name = $this->restore_noise($name); // might be a noisy name
2037
+
2038
+ if ($this->lowercase) { $name = strtolower($name); }
2039
+
2040
+ if ($this->char === '=') { // attribute with value
2041
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2042
+ $this->parse_attr($node, $name, $space); // get attribute value
2043
+ } else {
2044
+ //no value attr: nowrap, checked selected...
2045
+ $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
2046
+ $node->attr[$name] = true;
2047
+ if ($this->char != '>') { $this->char = $this->doc[--$this->pos]; } // prev
2048
+ }
2049
+
2050
+ $node->_[HDOM_INFO_SPACE][] = $space;
2051
+
2052
+ // prepare for next attribute
2053
+ $space = array(
2054
+ $this->copy_skip($this->token_blank),
2055
+ '',
2056
+ ''
2057
+ );
2058
+ } else { // no more attributes
2059
+ break;
2060
+ }
2061
+ } while ($this->char !== '>' && $this->char !== '/'); // go until the tag ended
2062
+
2063
+ $this->link_nodes($node, true);
2064
+ $node->_[HDOM_INFO_ENDSPACE] = $space[0];
2065
+
2066
+ // handle empty tags (i.e. "<div/>")
2067
+ if ($this->copy_until_char('>') === '/') {
2068
+ $node->_[HDOM_INFO_ENDSPACE] .= '/';
2069
+ $node->_[HDOM_INFO_END] = 0;
2070
+ } else {
2071
+ // reset parent
2072
+ if (!isset($this->self_closing_tags[strtolower($node->tag)])) {
2073
+ $this->parent = $node;
2074
+ }
2075
+ }
2076
+
2077
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2078
+
2079
+ // If it's a BR tag, we need to set it's text to the default text.
2080
+ // This way when we see it in plaintext, we can generate formatting that the user wants.
2081
+ // since a br tag never has sub nodes, this works well.
2082
+ if ($node->tag === 'br') {
2083
+ $node->_[HDOM_INFO_INNER] = $this->default_br_text;
2084
+ }
2085
+
2086
+ return true;
2087
+ }
2088
+
2089
+ protected function parse_attr($node, $name, &$space)
2090
+ {
2091
+ $is_duplicate = isset($node->attr[$name]);
2092
+
2093
+ if (!$is_duplicate) // Copy whitespace between "=" and value
2094
+ $space[2] = $this->copy_skip($this->token_blank);
2095
+
2096
+ switch ($this->char) {
2097
+ case '"':
2098
+ $quote_type = HDOM_QUOTE_DOUBLE;
2099
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2100
+ $value = $this->copy_until_char('"');
2101
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2102
+ break;
2103
+ case '\'':
2104
+ $quote_type = HDOM_QUOTE_SINGLE;
2105
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2106
+ $value = $this->copy_until_char('\'');
2107
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2108
+ break;
2109
+ default:
2110
+ $quote_type = HDOM_QUOTE_NO;
2111
+ $value = $this->copy_until($this->token_attr);
2112
+ }
2113
+
2114
+ $value = $this->restore_noise($value);
2115
+
2116
+ // PaperG: Attributes should not have \r or \n in them, that counts as
2117
+ // html whitespace.
2118
+ $value = str_replace("\r", '', $value);
2119
+ $value = str_replace("\n", '', $value);
2120
+
2121
+ // PaperG: If this is a "class" selector, lets get rid of the preceeding
2122
+ // and trailing space since some people leave it in the multi class case.
2123
+ if ($name === 'class') {
2124
+ $value = trim($value);
2125
+ }
2126
+
2127
+ if (!$is_duplicate) {
2128
+ $node->_[HDOM_INFO_QUOTE][] = $quote_type;
2129
+ $node->attr[$name] = $value;
2130
+ }
2131
+ }
2132
+
2133
+ protected function link_nodes(&$node, $is_child)
2134
+ {
2135
+ $node->parent = $this->parent;
2136
+ $this->parent->nodes[] = $node;
2137
+ if ($is_child) {
2138
+ $this->parent->children[] = $node;
2139
+ }
2140
+ }
2141
+
2142
+ protected function as_text_node($tag)
2143
+ {
2144
+ $node = new simple_html_dom_node($this);
2145
+ ++$this->cursor;
2146
+ $node->_[HDOM_INFO_TEXT] = '</' . $tag . '>';
2147
+ $this->link_nodes($node, false);
2148
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2149
+ return true;
2150
+ }
2151
+
2152
+ protected function skip($chars)
2153
+ {
2154
+ $this->pos += strspn($this->doc, $chars, $this->pos);
2155
+ $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2156
+ }
2157
+
2158
+ protected function copy_skip($chars)
2159
+ {
2160
+ $pos = $this->pos;
2161
+ $len = strspn($this->doc, $chars, $pos);
2162
+ $this->pos += $len;
2163
+ $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2164
+ if ($len === 0) { return ''; }
2165
+ return substr($this->doc, $pos, $len);
2166
+ }
2167
+
2168
+ protected function copy_until($chars)
2169
+ {
2170
+ $pos = $this->pos;
2171
+ $len = strcspn($this->doc, $chars, $pos);
2172
+ $this->pos += $len;
2173
+ $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2174
+ return substr($this->doc, $pos, $len);
2175
+ }
2176
+
2177
+ protected function copy_until_char($char)
2178
+ {
2179
+ if ($this->char === null) { return ''; }
2180
+
2181
+ if (($pos = strpos($this->doc, $char, $this->pos)) === false) {
2182
+ $ret = substr($this->doc, $this->pos, $this->size - $this->pos);
2183
+ $this->char = null;
2184
+ $this->pos = $this->size;
2185
+ return $ret;
2186
+ }
2187
+
2188
+ if ($pos === $this->pos) { return ''; }
2189
+
2190
+ $pos_old = $this->pos;
2191
+ $this->char = $this->doc[$pos];
2192
+ $this->pos = $pos;
2193
+ return substr($this->doc, $pos_old, $pos - $pos_old);
2194
+ }
2195
+
2196
+ protected function remove_noise($pattern, $remove_tag = false)
2197
+ {
2198
+ global $debug_object;
2199
+ if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
2200
+
2201
+ $count = preg_match_all(
2202
+ $pattern,
2203
+ $this->doc,
2204
+ $matches,
2205
+ PREG_SET_ORDER | PREG_OFFSET_CAPTURE
2206
+ );
2207
+
2208
+ for ($i = $count - 1; $i > -1; --$i) {
2209
+ $key = '___noise___' . sprintf('% 5d', count($this->noise) + 1000);
2210
+
2211
+ if (is_object($debug_object)) {
2212
+ $debug_object->debug_log(2, 'key is: ' . $key);
2213
+ }
2214
+
2215
+ $idx = ($remove_tag) ? 0 : 1; // 0 = entire match, 1 = submatch
2216
+ $this->noise[$key] = $matches[$i][$idx][0];
2217
+ $this->doc = substr_replace($this->doc, $key, $matches[$i][$idx][1], strlen($matches[$i][$idx][0]));
2218
+ }
2219
+
2220
+ // reset the length of content
2221
+ $this->size = strlen($this->doc);
2222
+
2223
+ if ($this->size > 0) {
2224
+ $this->char = $this->doc[0];
2225
+ }
2226
+ }
2227
+
2228
+ function restore_noise($text)
2229
+ {
2230
+ global $debug_object;
2231
+ if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
2232
+
2233
+ while (($pos = strpos($text, '___noise___')) !== false) {
2234
+ // Sometimes there is a broken piece of markup, and we don't GET the
2235
+ // pos+11 etc... token which indicates a problem outside of us...
2236
+
2237
+ // todo: "___noise___1000" (or any number with four or more digits)
2238
+ // in the DOM causes an infinite loop which could be utilized by
2239
+ // malicious software
2240
+ if (strlen($text) > $pos + 15) {
2241
+ $key = '___noise___'
2242
+ . $text[$pos + 11]
2243
+ . $text[$pos + 12]
2244
+ . $text[$pos + 13]
2245
+ . $text[$pos + 14]
2246
+ . $text[$pos + 15];
2247
+
2248
+ if (is_object($debug_object)) {
2249
+ $debug_object->debug_log(2, 'located key of: ' . $key);
2250
+ }
2251
+
2252
+ if (isset($this->noise[$key])) {
2253
+ $text = substr($text, 0, $pos)
2254
+ . $this->noise[$key]
2255
+ . substr($text, $pos + 16);
2256
+ } else {
2257
+ // do this to prevent an infinite loop.
2258
+ $text = substr($text, 0, $pos)
2259
+ . 'UNDEFINED NOISE FOR KEY: '
2260
+ . $key
2261
+ . substr($text, $pos + 16);
2262
+ }
2263
+ } else {
2264
+ // There is no valid key being given back to us... We must get
2265
+ // rid of the ___noise___ or we will have a problem.
2266
+ $text = substr($text, 0, $pos)
2267
+ . 'NO NUMERIC NOISE KEY'
2268
+ . substr($text, $pos + 11);
2269
+ }
2270
+ }
2271
+ return $text;
2272
+ }
2273
+
2274
+ function search_noise($text)
2275
+ {
2276
+ global $debug_object;
2277
+ if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
2278
+
2279
+ foreach($this->noise as $noiseElement) {
2280
+ if (strpos($noiseElement, $text) !== false) {
2281
+ return $noiseElement;
2282
+ }
2283
+ }
2284
+ }
2285
+
2286
+ function __toString()
2287
+ {
2288
+ return $this->root->innertext();
2289
+ }
2290
+
2291
+ function __get($name)
2292
+ {
2293
+ switch ($name) {
2294
+ case 'outertext':
2295
+ return $this->root->innertext();
2296
+ case 'innertext':
2297
+ return $this->root->innertext();
2298
+ case 'plaintext':
2299
+ return $this->root->text();
2300
+ case 'charset':
2301
+ return $this->_charset;
2302
+ case 'target_charset':
2303
+ return $this->_target_charset;
2304
+ }
2305
+ }
2306
+
2307
+ function childNodes($idx = -1)
2308
+ {
2309
+ return $this->root->childNodes($idx);
2310
+ }
2311
+
2312
+ function firstChild()
2313
+ {
2314
+ return $this->root->first_child();
2315
+ }
2316
+
2317
+ function lastChild()
2318
+ {
2319
+ return $this->root->last_child();
2320
+ }
2321
+
2322
+ function createElement($name, $value = null)
2323
+ {
2324
+ return @str_get_html("<$name>$value</$name>")->firstChild();
2325
+ }
2326
+
2327
+ function createTextNode($value)
2328
+ {
2329
+ return @end(str_get_html($value)->nodes);
2330
+ }
2331
+
2332
+ function getElementById($id)
2333
+ {
2334
+ return $this->find("#$id", 0);
2335
+ }
2336
+
2337
+ function getElementsById($id, $idx = null)
2338
+ {
2339
+ return $this->find("#$id", $idx);
2340
+ }
2341
+
2342
+ function getElementByTagName($name)
2343
+ {
2344
+ return $this->find($name, 0);
2345
+ }
2346
+
2347
+ function getElementsByTagName($name, $idx = -1)
2348
+ {
2349
+ return $this->find($name, $idx);
2350
+ }
2351
+
2352
+ function loadFile()
2353
+ {
2354
+ $args = func_get_args();
2355
+ $this->load_file($args);
2356
+ }
2357
+ }
readme.txt CHANGED
@@ -1,68 +1,33 @@
1
  === Fast Velocity Minify ===
2
  Contributors: Alignak
3
- Tags: PHP Minify, Lighthouse, GTmetrix, Pingdom, Pagespeed, CSS Merging, JS Merging, CSS Minification, JS Minification, Speed Optimization, HTML Minification, Performance, Optimization, FVM
4
  Requires at least: 4.7
5
  Requires PHP: 5.6
6
- Stable tag: 2.8.9
7
- Tested up to: 5.4
8
  License: GPLv3 or later
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
11
- Improve your speed score on GTmetrix, Pingdom Tools and Google PageSpeed Insights by merging and minifying CSS, JavaScript and HTML, setting up HTTP preload and preconnect headers, loading CSS async and a few more options.
12
 
13
 
14
  == Description ==
15
- WP speed optimization plugin for developers and advanced users. This plugin reduces HTTP requests by merging CSS & Javascript files into groups of files, while attempting to use the least amount of files as possible. It minifies CSS and JS files with PHP Minify (no extra requirements).
16
 
17
- Minification is done on the frontend during the first uncached request. Once the first request is processed, any other pages that require the same set of CSS and JavaScript files, will be served that same (static) cache file.
18
 
19
- This plugin includes options for developers and advanced users, however the default settings should work just fine for most sites.
20
- Kindly read our faqs about possible issues with you theme or specific plugins.
21
 
22
  = Aditional Optimization =
23
 
24
  I can offer you aditional `custom made` optimization on top of this plugin. If you would like to hire me, please visit my profile links for further information.
25
 
26
 
27
- = Features =
28
-
29
- * Merge JS and CSS files into groups to reduce the number of HTTP requests
30
- * Google Fonts merging, inlining and optimization
31
- * Handles scripts loaded both in the header & footer separately
32
- * Keeps the order of the scripts even if you exclude some files from minification
33
- * Supports localized scripts (https://codex.wordpress.org/Function_Reference/wp_localize_script)
34
- * Minifies CSS and JS with PHP Minify only, no third party software or libraries needed.
35
- * Option to defer JavaScript and CSS files, either globally or pagespeed insights only.
36
- * Creates static cache files in the uploads directory.
37
- * Preserves your original files, by duplicating and copying those files to the uploads directory
38
- * View the status and detailed logs on the WordPress admin page.
39
- * Option to Minify HTML, remove extra info from the header and other optimizations.
40
- * Ability to turn off minification for JS, CSS or HTML (purge the cache to see it)
41
- * Ability to turn off CSS or JS merging completely (so you can debug which section causes conflicts and exclude the offending files)
42
- * Ability to manually ignore JavaScript or CSS files that conflict when merged together (please report if you find some)
43
- * Support for conditional scripts and styles, as well as inlined code that depends on the handles
44
- * Support for multisite installations (each site has its own settings)
45
- * Support for gzip_static on Nginx
46
- * Support for preconnect and preload headers
47
- * CDN option, to rewrite all static assets inside the JS or CSS files
48
- * WP CLI support to check stats and purge the cache
49
- * Auto purging of cache files for W3 Total Cache, WP Supercache, WP Rocket, Cachify, Comet Cache, Zen Cache, LiteSpeed Cache, Nginx Cache (by Till Krüss ), SG Optimizer, HyperCache, Cache Enabler, Breeze (Cloudways), Godaddy Managed WordPress Hosting and WP Engine (read the FAQs)
50
- * and some more...
51
-
52
-
53
  = WP-CLI Commands =
54
  * Purge all caches: `wp fvm purge`
55
  * Purge all caches on a network site: `wp --url=blog.example.com fvm purge`
56
  * Purge all caches on the entire network (linux): `wp site list --field=url | xargs -n1 -I % wp --url=% fvm purge`
57
- * Get cache size: `wp fvm stats`
58
- * Get cache size on a network site: `wp --url=blog.example.com fvm stats`
59
- * Get cache size on each site (linux): `wp site list --field=url | xargs -n1 -I % wp --url=% fvm stats`
60
-
61
-
62
- = Notes =
63
- * The JavaScript minification is by [PHP Minify](https://github.com/matthiasmullie/minify)
64
- * Compatible with Nginx, HHVM and PHP 7
65
- * Minimum requirements are PHP 5.5 and WP 4.4, from version 1.4.0 onwards
66
 
67
 
68
  == Installation ==
@@ -74,130 +39,23 @@ I can offer you aditional `custom made` optimization on top of this plugin. If y
74
 
75
  == Screenshots ==
76
 
77
- 1. The Status and Logs page.
78
- 2. The Settings page.
79
- 3. The Pro settings.
80
- 4. The Developers settings.
81
-
82
- == Frequently Asked Questions ==
83
-
84
- = Can I update plugins and themes after installing FVM? =
85
- FVM doesn't touch your original files. It copies those files to the cache directory, minifies that copy and merges them together under a different name. If you install new plugins, change themes or do plugin updates, FVM will purge its cache as well as some of the most popular cache plugins.
86
-
87
- = After installing, why did my site feels slow to load? =
88
- Please see the question below.
89
-
90
- = Why are there lots of JS and CSS files listed on the status page and why is the cache directory taking so much space? =
91
- Some themes combine and enqueue their CSS using a PHP script with a query string that changes on every pageload... (this is to bust cache, but it's bad practice since it prevents caching at all). When FVM sees a different url being enqueued, it will consider that as a new file and try to create a new set of files on every pageview as well. You must then exclude that dynamic url via the Ignore List on the settings for your cache to be efficient and stop growing. Also note, if your pages enqueue different styles or javascript in different pages (fairly common), that is "one set" of files to be merged. Pay attention to the logs header and look for the page url where those files have ben generated. If you have multiple files generated for the same url, you have some css/js that keeps changing on every pageview (and thus needs exclusion).
92
-
93
-
94
- = How can I exclude certain assets? =
95
- Each line on the ignore list will try to match a substring against all CSS or JS files, for example `//yoursite.com/wp-content/plugins/some-plugin/js/` will ignore all files inside that directory. You can also shorten the URL like `/some-plugin/js/` and then it will match any css or js URL that has `/some-plugin/js/` on the path. Obviously, doing `/js/` would match any files inside any "/js/" directory and in any location, so to avoid unexpected situations please always use the longest, most specific path you can use. There is no need to use asterisks or regex code (it won't work).
96
-
97
-
98
- = Why is the ignore list not working? =
99
- The ignore list "is" working, just try to use partial paths (see previous faq) and use relative urls only without any query vars.
100
-
101
-
102
- = Is it compatible with other caching plugins? =
103
- You must disable any features on your theme or cache plugins which perform minification of css, html and js. Double minification not only slows the whole process, but also has the high potential of causing conflicts in javascript. The plugin will try to automatically purge several popular cache plugins, however if you have a cache on the server side (some hosting services have this) you may need to purge it manually, after you purge FVM to see the results you expect. The automatic purge is active for the following plugins and hosting: W3 Total Cache, WP Supercache, WP Rocket, Cachify, Comet Cache, Zen Cache, LiteSpeed Cache, Cache Enabler, SG Optimizer, Breeze (Cloudways), Godaddy Managed WordPress Hosting and WP Engine
104
-
105
-
106
- = Do you recommend a specific Cache Plugin? =
107
- Currently we recommend the "Cache Enabler" plugin, for it's simplicity, compatibility with most systems and performance. Alternatively, W3 Total Cache is a great choice as well.
108
-
109
-
110
- = Is it resource intensive, or will it use too much CPU on my shared hosting plan? =
111
- Unless you are not excluding dynamic CSS files that change the url in every pageload, its not heavy at all. On the first run, each single file is minified into an intermediate cache. When a new group of CSS/JS files is found on a new page, it reuses those files and merges them into a new static cache file. All pages that request the same group of CSS or JS files will also make use of that file, thus regeneration only happens once. In addition, gz and br files will be pre-compressed (if supported).
112
-
113
-
114
- = How do I use the pre-compressed files with gzip_static or brotli_static on Nginx? =
115
- When we merge and minify the css and js files, we also create a `.gz` file to be used with `gzip_static` on Nginx. You need to enable this feature on your Nginx configuration file if you want to make use of it. Likewise, if you have Nginx compiled with brotli and have enabled the php-ext-brotli extension for PHP, you can enable the brotli_static option and FVM will also generate .br files for you :)
116
-
117
-
118
- = Is it compatible with multisites? =
119
- Yes, it generates a new cache file for every different set of JS and CSS requirements it finds, but you must enable and configure FVM settings for each site in your network separatly (no global settings for all sites).
120
-
121
-
122
- = Is it compatible with AdSense and other ad networks? =
123
- If you are just inserting ads on your pages, yes. If you are using a custom script to inject those ads, please double check if it works.
124
-
125
-
126
- = After installing, why are some images, sections, sliders, galleries, menus (etc) not working? =
127
-
128
- a) You cannot do double minification, so make sure you have disabled any features on your theme or other plugins that perform minification of css, html and js files.
129
-
130
- b) If you enabled the option to defer JS or CSS, please note that some themes and plugins need jQuery and other libraries to be render blocking. If you enable the option to defer, any javascript code on the page will trigger an "undefined" error on the google chrome console log after page load.
131
-
132
- c) The plugin relies on PHP Minify to minify JavaScript and css files, however it is not a perfect library and there are plugins that are already minified and do not output a "min.js" or "min.css" filename (and end up being minified again). Try to disable minification on JS and CSS files and purge the cache, then either dequeue it and enqueue an alternative file or add it to the ignore list.
133
-
134
- d) Sometimes a plugin conflicts with another when merged (look at google chrome console log for hints). Try to disable CSS processing first and see if it works. Disable JS processing second and see if it works. Try to disable HTML minification last and see if it works. If one of those work, you know there is a conflict when merging/minifying.
135
-
136
- e) If you have a conflict, try to add each CSS and each JS file to the ignore list one by one, until you find the one that causes the conflict. If you have no idea of which files to add, check the log file on the "status page" for a list of files being merged into each generated file.
137
-
138
- f) If you coded some inline JS code that depends on a JS file being loaded before it's execution (render blocking), try to save that code into an external file and enqueue it as a dependency. It will be merged together and run after the other file, thus no longer being "undefined".
139
-
140
-
141
- = Why are some of the CSS and JS files not being merged? =
142
- The plugin only processes JS and CSS files enqueued using the official WordPress api method - https://developer.wordpress.org/themes/basics/including-css-javascript/ -as well as files from the same domain (unless specified on the settings).
143
-
144
-
145
- = Can I merge files from other domains? =
146
- Yes and no. You can for example, merge js files such as jQuery if they are loading from a CDN and it will work, because it doesn't matter where those files are being served from. However, stuff like Facebook and other social media widgets, as well as tracking codes, widgets and so on, cannot usually be merged and cached locally as they may load something different on every pageload, or anytime they change something. Ads and widgets make your site slow, so make sure you only use the minimum necessary plugins and widgets.
147
-
148
-
149
- = How to undo all changes done by the plugin? =
150
- The plugin itself does not do any "changes" to your site and all original files are untouched. It intercepts the enqueued CSS and JS files just before printing your HTML, copies them and enqueues the newly optimized cached version of those files to the frontend. As with any plugin... simply disable or uninstall the plugin, purge all caches you may have in use (plugins, server, cloudflare, etc.) and the site will go back to what it was before installing it. The plugin does not delete anything from the database or modify any of your files.
151
-
152
-
153
- = I have disabled or deleted the plugin but my design is still broken! =
154
- Some "cheap" (or sometimes expensive) "optimized" hosting providers, implement a (misconfigured) aggressive cache on their servers that caches PHP code execution and PHP files. I've seen people completely deleting all WordPress files from their host via SFTP/FTP and the website kept working fine for hours. Furthermore, very often they rate limit your cache purge requests... so if you delete FVM and are still seeing references to FVM files on the "view-source:https://example.com" please be patient and contact your web hosting to purge all caches. Providers known to have this issue are some plans on hostgator and iPage (please report others if you find them).
155
-
156
-
157
- = Why is my Visual Composer or Page Editor not working? =
158
- Some plugins and themes need to edit the layout and styles on the frontend. If you have trouble with page editors, please enable the "Fix Page Editors" option on FVM and purge your caches. Note: You will only see the FVM minification working when you're logged out or using another browser after this setting.
159
-
160
-
161
- = What are the recommended cloudflare settings for this plugin? =
162
- On the "Speed" tab, deselect the Auto Minify for JavaScript, CSS and HTML as well as the Rocket Loader option as there is no benefit of using them with our plugin (we already minify things). Those options can also break the design due to double minification or the fact that the Rocket Loader is still experimental (you can read about that on the "Help" link under each selected option on cloudflare).
163
-
164
-
165
- = How can I load CSS async? =
166
- You are probably a developer if you are trying this. The answer is: make sure FVM is only generating 1 CSS file, because "async" means multiple files will load out of order (however CSS needs order most of the times). If FVM is generating more than 1 CSS file per mediatype, try to manually dequeue some of the CSS files that are breaking the series on FVM (such as external enqueued files), or add their domain to the settings to be merged together. Please note... this is an advanced option for skilled developers. Do not try to fiddle with these settings if you are not one, as it will almost certainly break your site layout and functionality.
167
-
168
- = Why is FVM using defer instead of async javascript? =
169
- The answer is simple. For compatibility reasons and to avoid some undefined javascript errors, we need to preserve the order of scripts. Async means that any js files will load in parallel without waiting for each other or without following a specific order. If FVM generates multiple JS files for your site, using Async could cause footer scripts to load before the header scripts in an inconsistent manner. By using defer, we make sure the scripts load in order, as defined by each plugin and theme developer.
170
- Your ads or scripts wich are already specifically async will continue to be so, unless you specifically mark them to be merged as well.
171
-
172
- = I have a complaint or I need support right now. =
173
- Before getting angry because you have no answer within a few hours (even with paid plugins, sometimes it takes weeks...), please be informed about how wordpress.org and the plugins directory work. The plugins directory is an open source, free service where developers and programmers contribute (on their free time) with plugins that can be downloaded and installed by anyone "at their own risk" and are all released under the GPL license. While all plugins have to be approved and reviewed by the WordPress team before being published (for dangerous code, spam, etc.) this does not change the license or add any warranty. All plugins are provided as they are, free of charge and should be used at your own risk (so you should make backups before installing any plugin or performing updates) and it is your sole responsibility if you break your site after installing a plugin from the plugins directory. For a full version of the license, please read: https://wordpress.org/about/gpl/
174
-
175
- = Why haven't you replied to my topic on the support forum yet? =
176
- Support is provided by plugin authors on their free time and without warranty of a reply, so you can experience different levels of support level from plugin to plugin. As the author of this plugin I strive to provide support on a daily basis and I can take a look and help you with some issues related with my plugin, but please note that this is done out of my goodwill and in no way I have any legal or moral obligation for doing this. Sometimes I am extremely busy and may take a few days to reply, but I will always reply.
177
-
178
- = But I really need fast support right now, is there any other way? =
179
- I am also available for hiring if you need custom-made speed optimizations. After you have installed the plugin, check the "Help" tab for contact information, or check my profile links here on WordPress.
180
-
181
-
182
- = Where can I report bugs? =
183
- You can get support on the official WordPress plugin page at https://wordpress.org/support/plugin/fast-velocity-minify
184
- Alternatively, you can reach me via info (at) fastvelocity.com for security or other vulnerabilities.
185
-
186
- = How can I donate to the plugin author? =
187
- If you would like to donate any amount to the plugin author (thank you in advance), you can do it via PayPal at https://goo.gl/vpLrSV
188
 
189
 
190
  == Upgrade Notice ==
191
 
192
- = 2.5.9 =
193
- Minor bug fixes
194
-
195
- = 3.0 =
196
- Please backup your site before updating. Version 3.0 will have a major code rewrite to improve JS and CSS merging.
197
 
198
 
199
  == Changelog ==
200
 
 
 
 
 
 
201
  = 2.8.9 [2020.06.23] =
202
  * new filter for wp hide compatibility
203
 
1
  === Fast Velocity Minify ===
2
  Contributors: Alignak
3
+ Tags: PHP Minify, Lighthouse, GTmetrix, Pingdom, Pagespeed, Merging, Minification, Optimization, Speed, Performance, FVM
4
  Requires at least: 4.7
5
  Requires PHP: 5.6
6
+ Stable tag: 3.0.0
7
+ Tested up to: 5.6
8
  License: GPLv3 or later
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
11
+ Improve your speed score on GTmetrix, Pingdom Tools and Google PageSpeed Insights by merging and minifying CSS and JavaScript, compressing HTML and a few more speed optimization options.
12
 
13
 
14
  == Description ==
15
+ Speed optimization plugin for developers and advanced users. This plugin reduces HTTP requests by merging CSS & JavaScript files. It minifies CSS and JS files with PHP Minify, the same library used on most cache plugins.
16
 
17
+ Minification is done on the frontend during the first uncached request. Once the first request is processed, any other pages that require the same set of CSS and JavaScript files, will reuse the same generated file.
18
 
19
+ The plugin includes options for developers and advanced users, however the default settings should work just fine for most sites.
20
+ Kindly read the HELP section after installing the plugin, about possible issues and how to solve them.
21
 
22
  = Aditional Optimization =
23
 
24
  I can offer you aditional `custom made` optimization on top of this plugin. If you would like to hire me, please visit my profile links for further information.
25
 
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  = WP-CLI Commands =
28
  * Purge all caches: `wp fvm purge`
29
  * Purge all caches on a network site: `wp --url=blog.example.com fvm purge`
30
  * Purge all caches on the entire network (linux): `wp site list --field=url | xargs -n1 -I % wp --url=% fvm purge`
 
 
 
 
 
 
 
 
 
31
 
32
 
33
  == Installation ==
39
 
40
  == Screenshots ==
41
 
42
+ 1. The Settings page.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
 
45
  == Upgrade Notice ==
46
 
47
+ = 3.0.0 =
48
+ Please backup your site before updating.
49
+ Version 3.0 is a major code rewrite to improve JS and CSS merging, but it requires JS settings to be readjusted after the update.
 
 
50
 
51
 
52
  == Changelog ==
53
 
54
+ = 3.0.0 [2020.12.26] =
55
+ * New version has been remade from scratch
56
+ * JS Optimization is disabled by default and requires manual configuration
57
+ * Third party scripts can now be delayed until user interaction, to improve the initial loading time
58
+
59
  = 2.8.9 [2020.06.23] =
60
  * new filter for wp hide compatibility
61