Drupal: dodan modul advagg (Advanced CSS/JS Aggregation)
This commit is contained in:
parent
37a32adbcb
commit
491828d08a
339
frontend/drupal/sites/all/modules/advagg/LICENSE.txt
Normal file
339
frontend/drupal/sites/all/modules/advagg/LICENSE.txt
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
806
frontend/drupal/sites/all/modules/advagg/README.md
Normal file
806
frontend/drupal/sites/all/modules/advagg/README.md
Normal file
@ -0,0 +1,806 @@
|
|||||||
|
|
||||||
|
ADVANCED CSS/JS AGGREGATION MODULE
|
||||||
|
==================================
|
||||||
|
|
||||||
|
|
||||||
|
CONTENTS OF THIS FILE
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
- Introduction
|
||||||
|
- Requirements
|
||||||
|
- Recommended modules
|
||||||
|
- Installation
|
||||||
|
- How to get a high PageSpeed score
|
||||||
|
- JSMin PHP Extension
|
||||||
|
- Brotli PHP Extension
|
||||||
|
- Zopfli PHP Extension
|
||||||
|
- nginx Configuration
|
||||||
|
- JavaScript Bookmarklet
|
||||||
|
- Troubleshooting
|
||||||
|
- Features & benefits
|
||||||
|
- Configuration
|
||||||
|
- Additional options for `drupal_add_css/js` functions
|
||||||
|
- Technical Details & Hooks
|
||||||
|
|
||||||
|
|
||||||
|
INTRODUCTION
|
||||||
|
------------
|
||||||
|
|
||||||
|
The Advanced CSS/JS Aggregation allows you to improve the frontend performance
|
||||||
|
of your site. Be sure to do a before and after comparison by using Google's
|
||||||
|
PageSpeed Insights and WebPagetest.org.
|
||||||
|
https://developers.google.com/speed/pagespeed/insights/
|
||||||
|
http://www.webpagetest.org/easy
|
||||||
|
|
||||||
|
|
||||||
|
REQUIREMENTS
|
||||||
|
------------
|
||||||
|
|
||||||
|
No special requirements.
|
||||||
|
|
||||||
|
|
||||||
|
RECOMMENDED MODULES
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- Libraries (https://www.drupal.org/project/libraries)
|
||||||
|
Allows for 3rd party code for minification to be used by AdvAgg.
|
||||||
|
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
------------
|
||||||
|
|
||||||
|
- Install as you would normally install a contributed Drupal module. Visit:
|
||||||
|
https://drupal.org/documentation/install/modules-themes/modules-7
|
||||||
|
for further information.
|
||||||
|
|
||||||
|
|
||||||
|
HOW TO GET A HIGH PAGESPEED SCORE
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Be sure to check the site after every section to make sure the change didn't
|
||||||
|
mess up your site. The changes under AdvAgg Modifier are usually the most
|
||||||
|
problematic but they offer the biggest improvements.
|
||||||
|
|
||||||
|
#### Advanced CSS/JS Aggregation ####
|
||||||
|
Go to `admin/config/development/performance/advagg`
|
||||||
|
|
||||||
|
Select "Use recommended (optimized) settings"
|
||||||
|
|
||||||
|
#### AdvAgg Compress Javascript ####
|
||||||
|
Install AdvAgg Compress Javascript if not enabled and go to
|
||||||
|
`admin/config/development/performance/advagg/js-compress`
|
||||||
|
|
||||||
|
- Select JSMin if available; otherwise select JSMin+
|
||||||
|
- Select Strip everything (smallest files)
|
||||||
|
- Save configuration
|
||||||
|
- Click the batch compress link to process these files at the top.
|
||||||
|
|
||||||
|
#### AdvAgg Async Font Loader ####
|
||||||
|
Install AdvAgg Async Font Loader if not enabled and go to
|
||||||
|
`admin/config/development/performance/advagg/font`
|
||||||
|
|
||||||
|
- Select Local file included in aggregate (version: X.X.X). If this option is
|
||||||
|
not available follow the directions right below the options on how to install
|
||||||
|
it.
|
||||||
|
|
||||||
|
Keep the 2 checkboxes checked.
|
||||||
|
|
||||||
|
#### AdvAgg Bundler ####
|
||||||
|
Install AdvAgg Bundler if not enabled and go to
|
||||||
|
`admin/config/development/performance/advagg/bundler`
|
||||||
|
|
||||||
|
If your server supports HTTP 2 then select "Use HTTP 2.0 settings"; otherwise
|
||||||
|
leave it at the "Use HTTP 1.1 settings".
|
||||||
|
|
||||||
|
#### AdvAgg Relocate ####
|
||||||
|
Install AdvAgg Relocate if not enabled and go to
|
||||||
|
`admin/config/development/performance/advagg/relocate`
|
||||||
|
|
||||||
|
Select "Use recommended (optimized) settings"
|
||||||
|
|
||||||
|
#### AdvAgg Modifier ####
|
||||||
|
Install AdvAgg Modifier if not enabled and go to
|
||||||
|
`admin/config/development/performance/advagg/mod`
|
||||||
|
|
||||||
|
Select "Use recommended (optimized) settings"
|
||||||
|
|
||||||
|
#### AdvAgg Critical CSS module ####
|
||||||
|
Install AdvAgg Critical CSS if not enabled and go to
|
||||||
|
`admin/config/development/performance/advagg/critical-css`
|
||||||
|
|
||||||
|
These are the directions for the front page of your site.
|
||||||
|
|
||||||
|
Under Add Critical CSS
|
||||||
|
- Select the theme that is your front page; usually the default is correct.
|
||||||
|
- User type should be set to 'anonymous' under most circumstances.
|
||||||
|
- Type of lookup, select URL
|
||||||
|
- Value to lookup, type in `<front>`
|
||||||
|
- Critical CSS, paste in the generated CSS from running your homepage url
|
||||||
|
through https://www.sitelocity.com/critical-path-css-generator which is inside
|
||||||
|
of the 'Critical Path CSS' textarea on the sitelocity page.
|
||||||
|
- Click Save Configuration.
|
||||||
|
|
||||||
|
Other landing pages should have their critical CSS added as well. If you have
|
||||||
|
Google Analytics this will show you how to find your top landing pages
|
||||||
|
https://developers.google.com/analytics/devguides/reporting/core/v3/common-queries#top-landing-pages
|
||||||
|
or for Piwik https://piwik.org/faq/how-to/faq_160/. You can also use this
|
||||||
|
chrome browser plugin to generate critical CSS
|
||||||
|
https://chrome.google.com/webstore/detail/critical-style-snapshot/gkoeffcejdhhojognlonafnijfkcepob?hl=en
|
||||||
|
|
||||||
|
|
||||||
|
JSMIN PHP EXTENSION
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
The AdvAgg JS Compress module can take advantage of jsmin.c. JavaScript parsing
|
||||||
|
and minimizing will be done in C instead of PHP dramatically speeding up the
|
||||||
|
process. If using PHP 5.3.10 or higher https://github.com/sqmk/pecl-jsmin is
|
||||||
|
recommended. If using PHP 5.3.9 or lower
|
||||||
|
http://www.ypass.net/software/php_jsmin/ is recommended.
|
||||||
|
|
||||||
|
|
||||||
|
BROTLI PHP EXTENSION
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The AdvAgg module can take advantage of Brotli compression. Install this
|
||||||
|
extension to take advantage of it. Should reduce CSS/JS files by 20%.
|
||||||
|
https://github.com/kjdev/php-ext-brotli
|
||||||
|
|
||||||
|
|
||||||
|
ZOPFLI PHP EXTENSION
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The AdvAgg module can take advantage of the Zopfli compression algorithm.
|
||||||
|
Install this extension to take advantage of it. This gives higher gzip
|
||||||
|
compression ratios compared to stock PHP.
|
||||||
|
https://github.com/kjdev/php-ext-zopfli
|
||||||
|
|
||||||
|
|
||||||
|
NGINX CONFIGURATION
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
https://drupal.org/node/1116618
|
||||||
|
Note that @drupal (last line of code below) might be @rewrite or @rewrites
|
||||||
|
depending on your servers configuration. If there are image style rules in your
|
||||||
|
Nginx configuration add this right below that. If you want to have brotli
|
||||||
|
support https://github.com/google/ngx_brotli is how to install that; add
|
||||||
|
`brotli_static on;` right above `gzip_static on;` in the configuration below.
|
||||||
|
|
||||||
|
###
|
||||||
|
### advagg_css and advagg_js support
|
||||||
|
###
|
||||||
|
location ~* files/advagg_(?:css|js)/ {
|
||||||
|
gzip_static on;
|
||||||
|
access_log off;
|
||||||
|
expires max;
|
||||||
|
add_header ETag "";
|
||||||
|
add_header Cache-Control "max-age=31449600, no-transform, public";
|
||||||
|
try_files $uri $uri/ @drupal;
|
||||||
|
}
|
||||||
|
|
||||||
|
Also noted that some ready made nginx configurations add in a Last-Modified
|
||||||
|
header inside the advagg directories. These should be removed.
|
||||||
|
|
||||||
|
|
||||||
|
JAVASCRIPT BOOKMARKLET
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
You can use this JS code as a bookmarklet for toggling the AdvAgg URL parameter.
|
||||||
|
See http://en.wikipedia.org/wiki/Bookmarklet for more details.
|
||||||
|
|
||||||
|
javascript:(function(){var loc = document.location.href,qs = document.location.search,regex_off = /\&?advagg=-1/,goto = loc;if(qs.match(regex_off)) {goto = loc.replace(regex_off, '');} else {qs = qs ? qs + '&advagg=-1' : '?advagg=-1';goto = document.location.pathname + qs;}window.location = goto;})();
|
||||||
|
|
||||||
|
|
||||||
|
TROUBLESHOOTING
|
||||||
|
---------------
|
||||||
|
|
||||||
|
If the core Fast 404 Pages functionality is enabled via settings.php, the
|
||||||
|
settings must be changed in order for the on-demand file compilation to work.
|
||||||
|
Change this:
|
||||||
|
|
||||||
|
$conf['404_fast_paths_exclude'] = '/\/(?:styles)\//';
|
||||||
|
|
||||||
|
to this:
|
||||||
|
|
||||||
|
$conf['404_fast_paths_exclude'] = '/\/(?:styles|advagg_(cs|j)s)\//';
|
||||||
|
|
||||||
|
Similarly, if the Fast_404 module is enabled, the 'fast_404_string_whitelisting'
|
||||||
|
variable must be set inside of settings.php. Add this to your settings.php file:
|
||||||
|
|
||||||
|
$conf['fast_404_string_whitelisting'][] = '/advagg_';
|
||||||
|
|
||||||
|
|
||||||
|
Modules like the Central Authentication Services https://drupal.org/project/cas
|
||||||
|
will redirect all anonymous requests to a login page. Most of the time there is
|
||||||
|
a setting that allows certain pages to be excluded from the redirect. You should
|
||||||
|
add the following to those exclusions. Note that sites/default/files is the
|
||||||
|
location of you public file system (public://) so you might have to adjust this
|
||||||
|
to fit your setup. services/* is the default (`CAS_EXCLUDE`) and
|
||||||
|
`httprl_async_function_callback` is needed if httprl will be used.
|
||||||
|
|
||||||
|
services/*
|
||||||
|
sites/default/files/advagg_css/*
|
||||||
|
sites/default/files/advagg_js/*
|
||||||
|
httprl_async_function_callback
|
||||||
|
|
||||||
|
In the example of CAS this setting can be found on the `admin/config/people/cas`
|
||||||
|
page and under Redirection there should be a setting called "Excluded Pages".
|
||||||
|
|
||||||
|
|
||||||
|
If Far-Future headers are not being sent out and you are using Apache here are
|
||||||
|
some tips to hopefully get it working. For Apache enable `mod_rewrite`,
|
||||||
|
`mod_headers`, and `mod_expires`. Add the following code to the bottom of
|
||||||
|
Drupal's core .htaccess file (located at the webroot level).
|
||||||
|
|
||||||
|
<FilesMatch "^(css|js)__[A-Za-z0-9-_]{43}__[A-Za-z0-9-_]{43}__[A-Za-z0-9-_]{43}.(css|js)(\.gz|\.br)?">
|
||||||
|
# No mod_headers. Apache module headers is not enabled.
|
||||||
|
<IfModule !mod_headers.c>
|
||||||
|
# No mod_expires. Apache module expires is not enabled.
|
||||||
|
<IfModule !mod_expires.c>
|
||||||
|
# Use ETags.
|
||||||
|
FileETag MTime Size
|
||||||
|
</IfModule>
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Use Expires Directive if apache module expires is enabled.
|
||||||
|
<IfModule mod_expires.c>
|
||||||
|
# Do not use ETags.
|
||||||
|
FileETag None
|
||||||
|
# Enable expirations.
|
||||||
|
ExpiresActive On
|
||||||
|
# Cache all aggregated css/js files for 52 weeks after access (A).
|
||||||
|
ExpiresDefault A31449600
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Use Headers Directive if apache module headers is enabled.
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
# Do not use etags for cache validation.
|
||||||
|
Header unset ETag
|
||||||
|
<IfModule !mod_expires.c>
|
||||||
|
# Set a far future Cache-Control header to 52 weeks.
|
||||||
|
Header set Cache-Control "max-age=31449600, no-transform, public"
|
||||||
|
</IfModule>
|
||||||
|
<IfModule mod_expires.c>
|
||||||
|
Header append Cache-Control "no-transform, public"
|
||||||
|
</IfModule>
|
||||||
|
</IfModule>
|
||||||
|
</FilesMatch>
|
||||||
|
# Force advagg .js file to have the type of application/javascript.
|
||||||
|
<FilesMatch "^js__[A-Za-z0-9-_]{43}__[A-Za-z0-9-_]{43}__[A-Za-z0-9-_]{43}.js(\.gz|\.br)?">
|
||||||
|
ForceType application/javascript
|
||||||
|
</FilesMatch>
|
||||||
|
|
||||||
|
|
||||||
|
If pages on the site stop working correctly or looks broken after Advanced
|
||||||
|
CSS/JS Aggregation is enabled, the first step should be to validate the
|
||||||
|
individual CSS and/or JS files using the included `advagg_validator` module -
|
||||||
|
something as simple as an errant unfinished comment in one file may cause entire
|
||||||
|
aggregates of files to be ignored.
|
||||||
|
|
||||||
|
|
||||||
|
If AdvAgg was installed via drush sometimes directory permissions need to be
|
||||||
|
fixed. Using `chown -R` on the advagg directories usually solves this issue.
|
||||||
|
|
||||||
|
|
||||||
|
If hosting on Pantheon, you might need to add this to your settings.php file if
|
||||||
|
you get Numerous login prompts after enabling Adv Agg module on Pantheon Test
|
||||||
|
and Live instances.
|
||||||
|
|
||||||
|
if (isset($_SERVER['PANTHEON_ENVIRONMENT'])) {
|
||||||
|
// NO trailing slash when setting the $base_url variable.
|
||||||
|
switch ($_SERVER['PANTHEON_ENVIRONMENT']) {
|
||||||
|
case 'dev':
|
||||||
|
$base_url = 'http://dev-sitename.gotpantheon.com';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'test':
|
||||||
|
$base_url = 'http://test-sitename.gotpantheon.com';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'live':
|
||||||
|
$base_url = 'http://www.domain.tld';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Remove a trailing slash if one was added.
|
||||||
|
if (!empty($base_url)) {
|
||||||
|
$base_url = rtrim($base_url, '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
If you're getting the "HTTP requests to advagg are not getting though" error,
|
||||||
|
you can try to fix it by making sure the `$base_url` is correctly set for
|
||||||
|
production and not production environments.
|
||||||
|
|
||||||
|
|
||||||
|
If you're getting mixed content error for CSS JS files over HTTPS then you can
|
||||||
|
try to redirect all http traffic to be https.
|
||||||
|
|
||||||
|
RewriteCond %{HTTPS} off
|
||||||
|
RewriteCond %{HTTP:X-Forwarded-Proto} !https
|
||||||
|
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||||
|
|
||||||
|
|
||||||
|
If brotli compression is not working and you are using Apache here are some tips
|
||||||
|
to hopefully get it working. For Apache enable `mod_rewrite`, `mod_headers`, and
|
||||||
|
`mod_expires`. Add the following code right above this line `# Rules to
|
||||||
|
correctly serve gzip compressed CSS and JS files.`
|
||||||
|
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
# Serve brotli compressed CSS if they exist and the client accepts br.
|
||||||
|
RewriteCond %{HTTP:Accept-encoding} br
|
||||||
|
RewriteCond %{REQUEST_FILENAME}\.br -s
|
||||||
|
RewriteRule ^(.*)\.css $1\.css\.br [QSA]
|
||||||
|
RewriteRule \.css\.br$ - [T=text/css,E=no-gzip:1]
|
||||||
|
|
||||||
|
# Serve brotli compressed JS if they exist and the client accepts br.
|
||||||
|
RewriteCond %{HTTP:Accept-encoding} br
|
||||||
|
RewriteCond %{REQUEST_FILENAME}\.br -s
|
||||||
|
RewriteRule ^(.*)\.js $1\.js\.br [QSA]
|
||||||
|
RewriteRule \.js\.br$ - [T=application/javascript,E=no-gzip:1]
|
||||||
|
|
||||||
|
<FilesMatch "(\.js\.br|\.css\.br)$">
|
||||||
|
# Serve correct encoding type.
|
||||||
|
Header set Content-Encoding br
|
||||||
|
# Force proxies to cache compressed and non-compressed css/js files
|
||||||
|
# separately.
|
||||||
|
Header append Vary Accept-Encoding
|
||||||
|
</FilesMatch>
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
|
||||||
|
If you are having 5 minute or longer timeouts on the admin/reports/status page
|
||||||
|
then you might need to use an alternative to drupal_httP_request(). The cURL
|
||||||
|
HTTP Request module https://www.drupal.org/project/chr might fix this issue.
|
||||||
|
|
||||||
|
|
||||||
|
FEATURES & BENEFITS
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
**Advanced CSS/JS Aggregation core features**
|
||||||
|
|
||||||
|
- On demand generation of CSS/JS Aggregates. If the file doesn't exist it will
|
||||||
|
be generated on demand.
|
||||||
|
- Stampede protection for CSS and JS aggregation. Uses locking so multiple
|
||||||
|
requests for the same thing will result in only one thread doing the work.
|
||||||
|
- Fully cached CSS/JS assets allow for zero file I/O if the Aggregated file
|
||||||
|
already exists. Results in better page generation performance.
|
||||||
|
- Smarter aggregate deletion. CSS/JS aggregates only get removed from the
|
||||||
|
folder if they have not been used/accessed in the last 30 days.
|
||||||
|
- Smarter cache flushing. Scans all CSS/JS files that have been added to any
|
||||||
|
aggregate; if that file has changed then flush the correct caches so the
|
||||||
|
changes go out. The new name ensures changes go out when using CDNs.
|
||||||
|
- One can add JS to any region of the theme & have it aggregated.
|
||||||
|
- Url query string to turn off aggregation for that request. `?advagg=0` will
|
||||||
|
turn off file aggregation if the user has the "bypass advanced aggregation"
|
||||||
|
permission. `?advagg=-1` will completely bypass all of Advanced CSS/JS
|
||||||
|
Aggregations modules and submodules. `?advagg=1` will enable Advanced CSS/JS
|
||||||
|
Aggregation if it is currently disabled.
|
||||||
|
- Button on the admin page for dropping a cookie that will turn off file
|
||||||
|
aggregation. Useful for theme development.
|
||||||
|
- Gzip support. All aggregated files can be pre-compressed into a .gz file and
|
||||||
|
served from Apache. This is faster then gzipping the file on each request.
|
||||||
|
|
||||||
|
**Included submodules**
|
||||||
|
|
||||||
|
- `advagg_bundler`:
|
||||||
|
Smartly groups files together - given a target number of CSS/JS aggregates,
|
||||||
|
this will try very hard to meet that goal.
|
||||||
|
- `advagg_css_cdn`:
|
||||||
|
Load CSS libraries from a public CDN; currently only supports Google's CDN.
|
||||||
|
- `advagg_css_compress`:
|
||||||
|
Compress the compiled CSS files using a 3rd party compressor; currently
|
||||||
|
supports YUI (included).
|
||||||
|
- `advagg_js_cdn`:
|
||||||
|
Load JavaScript libraries from a public CDN; currently only supports Google's
|
||||||
|
CDN.
|
||||||
|
- `advagg_js_compress`:
|
||||||
|
Compress the compiled JavaScript files using a 3rd party compressor;
|
||||||
|
currently supports JSMin+ (included).
|
||||||
|
- `advagg_mod`:
|
||||||
|
Includes additional tweaks that may not work for all sites:
|
||||||
|
- Force preprocessing for all CSS/JS.
|
||||||
|
- Move JS to footer.
|
||||||
|
- Add defer tag to all JS.
|
||||||
|
- Defer loading of CSS.
|
||||||
|
- Inline all CSS/JS for given paths.
|
||||||
|
- Use a shared directory for a unified multisite.
|
||||||
|
- `advagg_validator`:
|
||||||
|
Validate all CSS files using jigsaw.w3.org. Check all CSS files with CSSLint.
|
||||||
|
Check all JS files with JSHint.
|
||||||
|
|
||||||
|
|
||||||
|
CONFIGURATION
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Settings page is located at:
|
||||||
|
`admin/config/development/performance/advagg`
|
||||||
|
|
||||||
|
**Global Options**
|
||||||
|
|
||||||
|
- Enable Advanced Aggregation: Check this to start using this module. You can
|
||||||
|
also quickly disable the module here. For testing purposes, this has the same
|
||||||
|
effect as placing `?advagg=-1` in the URL. Disabled by default.
|
||||||
|
- Use Cores Grouping Logic: Leave this checkbox enabled until you are ready to
|
||||||
|
begin exploring the AdvAgg Bundler sub-module which overrides Core's
|
||||||
|
functionality. This groups files just like Core does so should just work.
|
||||||
|
Enabled by default. You will also have to disable this checkbox if you wish
|
||||||
|
to enable some of the CSS Options below on this settings page.
|
||||||
|
- Use HTTPRL to generate aggregates: If the HTTPRL module is enabled -
|
||||||
|
https://drupal.org/project/httprl - advagg will use it to generate aggregates
|
||||||
|
on the fly in a background parallel process. Enabling HTTPRL will improve
|
||||||
|
page generation speeds when a new aggregate is created because the CSS/JS
|
||||||
|
file creation will happen in a different process. If HTTPRL is installed it
|
||||||
|
is Enabled by default; otherwise is it Disabled.
|
||||||
|
- AdvAgg Cache Settings: As a reference, core takes about 25 ms to run.
|
||||||
|
Development will scan all files for a change on every page load. Normal is
|
||||||
|
fine for all use cases. Aggressive should be fine in almost all use cases;
|
||||||
|
if your inline css/js changes based off of a variable then the aggressive
|
||||||
|
cache hit ratio will be low; if that is the case consider using
|
||||||
|
Drupal.settings for a better cache hit ratio.
|
||||||
|
|
||||||
|
**Resource Hints**
|
||||||
|
|
||||||
|
Preemptively get resources (CSS/JS & sub requests). This will set tags in the
|
||||||
|
document head telling the browser to open up connections before they are needed.
|
||||||
|
|
||||||
|
- DNS Prefetch: Start the DNS lookup for external CSS and JavaScript files as
|
||||||
|
soon as possible.
|
||||||
|
- Preconnect: Start the connection to external resources before an HTTP request
|
||||||
|
is actually sent to the server. On HTTPS this can have a dramatic effect.
|
||||||
|
- Location of resource hints: This only needs to be changed if the above
|
||||||
|
settings are not working.
|
||||||
|
- Preload link http headers: If your server supports HTTP/2 push then this
|
||||||
|
allows for resources to be sent before the browser knows it needs it.
|
||||||
|
|
||||||
|
**Cron Options**
|
||||||
|
|
||||||
|
Adjusting the frequency of how often something happens on cron.
|
||||||
|
|
||||||
|
**Obscure Options**
|
||||||
|
|
||||||
|
- Create .gz files: Check this by default as it will improve your performance.
|
||||||
|
For every Aggregated file generated, this will create a gzip version of file
|
||||||
|
and then only serve it out if the browser accepts gzip files compression.
|
||||||
|
Enabled by default.
|
||||||
|
- Create .br files: Check this by default as it will improve your performance.
|
||||||
|
For every Aggregated file generated, this will create a brotli version of
|
||||||
|
file and then only serve it out if the browser accepts gzip files
|
||||||
|
compression. Enabled by default IF the Brotli Extension for PHP is installed.
|
||||||
|
See https://github.com/kjdev/php-ext-brotli
|
||||||
|
- Run advagg_ajax_render_alter(): Turn this off if you're having issues with
|
||||||
|
ajax. Also keep in mind that the max_input_vars setting can cause issues if
|
||||||
|
you are submitting a lot of data.
|
||||||
|
- Include the base_url variable in the hooks hash array: Enabled only if you
|
||||||
|
know you need it.
|
||||||
|
- Convert absolute paths to be self references: Turn on unless pages are used
|
||||||
|
inside of an iframe.
|
||||||
|
- Convert absolute paths to be protocol relative paths: Safe to use unless you
|
||||||
|
need to support IE6.
|
||||||
|
- Convert http:// to https://: Usually not needed, but here in case you do.
|
||||||
|
- Do not run CSS url() values through file_create_url(): Usually not needed,
|
||||||
|
but here in case you do.
|
||||||
|
|
||||||
|
|
||||||
|
**CSS Options & JS Options**
|
||||||
|
|
||||||
|
- Combine CSS files by using media queries: "Use cores grouping logic" needs to
|
||||||
|
be unchecked in order for this to work. Also noted is that due to an issue
|
||||||
|
with IE9, compatibility mode is forced off if this is enabled by adding this
|
||||||
|
tag in the html head:
|
||||||
|
|
||||||
|
<!--[if IE]>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
Disabled by default.
|
||||||
|
- Prevent more than 4095 CSS selectors in an aggregated CSS file: Internet
|
||||||
|
Explorer before version 10; IE9, IE8, IE7, & IE6 all have 4095 as the limit
|
||||||
|
for the maximum number of css selectors that can be in a file. Enabling this
|
||||||
|
will prevent CSS aggregates from being created that exceed this limit. For
|
||||||
|
more information see
|
||||||
|
http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/10164546.aspx Disabled
|
||||||
|
by default.
|
||||||
|
- Fix improperly set type (CSS/JS): If type is external but does not start with
|
||||||
|
http, https, or // change it to be type file. If type is file but it starts
|
||||||
|
with http, https, or // change type to be external.
|
||||||
|
|
||||||
|
**Information page**
|
||||||
|
|
||||||
|
located at `admin/config/development/performance/advagg/info`. This page
|
||||||
|
provides debugging information. There are no configuration options here.
|
||||||
|
|
||||||
|
- Hook Theme Info: Displays the `process_html` order. Used for debugging.
|
||||||
|
- Hook Render Info: Displays the scripts and styles render callbakcs. Used for
|
||||||
|
debugging.
|
||||||
|
- CSS files: Displays how often a file has changed.
|
||||||
|
- JS files: Displays how often a file has changed.
|
||||||
|
- Modules implementing AdvAgg CSS/JS hooks: Lets you know what modules are
|
||||||
|
using advagg.
|
||||||
|
- AdvAgg CSS/JS hooks implemented by modules: Lets you know what advagg hooks
|
||||||
|
are in use.
|
||||||
|
- Hooks And Variables Used In Hash: Show what is used to calculate the 3rd hash
|
||||||
|
of an aggregates filename.
|
||||||
|
- Get detailed info about an aggregate file: Look up detailed array about any
|
||||||
|
CSS or JS file listed.
|
||||||
|
|
||||||
|
**Operations page**
|
||||||
|
|
||||||
|
located at `admin/config/development/performance/advagg/operations`. This is a
|
||||||
|
collection of commands to control the cache and to manage testing of this
|
||||||
|
module. In general this page is useful when troubleshooting some aggregation
|
||||||
|
issues. For normal operations, you do not need to do anything on this page below
|
||||||
|
the Smart Cache Flush. There are no configuration options here.
|
||||||
|
|
||||||
|
- Smart Cache Flush
|
||||||
|
- Flush AdvAgg Cache: Scan all files referenced in aggregated files. If
|
||||||
|
any of them have changed, increment the counters containing that file and
|
||||||
|
rebuild the bundle.
|
||||||
|
|
||||||
|
- Aggregation Bypass Cookie
|
||||||
|
- Toggle The "aggregation bypass cookie" For This Browser: This will set or
|
||||||
|
remove a cookie that disables aggregation for the remainder of the browser
|
||||||
|
session. It acts almost the same as adding `?advagg=0` to every URL.
|
||||||
|
|
||||||
|
- Cron Maintenance Tasks
|
||||||
|
- Clear All Stale Files: Scan all files in the `advagg_css/js` directories
|
||||||
|
and remove the ones that have not been accessed in the last 30 days.
|
||||||
|
- Delete Orphaned Aggregates: Scan CSS/JS advagg dir and remove file if
|
||||||
|
there is no associated db record.
|
||||||
|
- Clear Missing Files From the Database: Scan for missing files and remove
|
||||||
|
the associated entries in the database.
|
||||||
|
- Delete Unused Aggregates from Database: Delete aggregates that have not
|
||||||
|
been accessed in the last 6 weeks.
|
||||||
|
- Delete orphaned/expired advagg locks from the semaphore database table.
|
||||||
|
- Delete leftover temporary files: Delete old temporary files from the
|
||||||
|
filesystem.
|
||||||
|
|
||||||
|
- Drastic Measures
|
||||||
|
- Clear All Caches: Remove all data stored in the advagg cache bins.
|
||||||
|
- Clear All Files: Remove All Generated Files. Remove all files in the
|
||||||
|
`advagg_(css|js)` directories.
|
||||||
|
- Force New Aggregates: Increment Global Counter. Force the creation of all
|
||||||
|
new aggregates by incrementing a global counter.
|
||||||
|
- Rescan All Files: Force rescan all files and clear the cache. Useful if a
|
||||||
|
css/js change from a deployment did not work.
|
||||||
|
- Remove deleted files in the advagg_files table: Remove entries in the
|
||||||
|
advagg_files table that have a filesize of 0 and delete the
|
||||||
|
javascript_parsed variable. This gets around the grace period that the
|
||||||
|
cron cleanup does.
|
||||||
|
- Reset the AdvAgg Files table: Truncate the advagg_files table and delete
|
||||||
|
the javascript_parsed variable. This may cause some 404s for CSS/JS assets
|
||||||
|
for a short amount of time (seconds). Useful if you really want to reset
|
||||||
|
some stuff. Might be best to put the site into maintenance mode before
|
||||||
|
doing this.
|
||||||
|
|
||||||
|
**Hidden Settings**
|
||||||
|
|
||||||
|
The following settings are not configurable from the admin UI and must be set in
|
||||||
|
settings.php. In general they are settings that should not be changed. The
|
||||||
|
current defaults are shown.
|
||||||
|
|
||||||
|
// Display a message that the bypass cookie is set.
|
||||||
|
$conf['advagg_show_bypass_cookie_message'] = TRUE;
|
||||||
|
|
||||||
|
// Display a message when a css/js file changed while in development mode.
|
||||||
|
$conf['advagg_show_file_changed_message'] = TRUE;
|
||||||
|
|
||||||
|
// Skip the 404 check on status page.
|
||||||
|
$conf['advagg_skip_404_check'] = FALSE;
|
||||||
|
|
||||||
|
// Force the scripts #aggregate_callback to always be _advagg_aggregate_js.
|
||||||
|
$conf['advagg_enforce_scripts_callback'] = TRUE;
|
||||||
|
|
||||||
|
// Default location of AdvAgg configuration items.
|
||||||
|
$conf['advagg_admin_config_root_path'] = 'admin/config/development/performance';
|
||||||
|
|
||||||
|
// Run advagg_url_inbound_alter().
|
||||||
|
$conf['advagg_url_inbound_alter'] = TRUE;
|
||||||
|
|
||||||
|
// Allow JavaScript insertion into any scope even if theme does not support
|
||||||
|
// it.
|
||||||
|
$conf['advagg_scripts_scope_anywhere'] = FALSE;
|
||||||
|
|
||||||
|
// Empty the scripts key inside of template_process_html replacement
|
||||||
|
// function.
|
||||||
|
$conf['advagg_scripts_scope_anywhere'] = FALSE;
|
||||||
|
|
||||||
|
// Do more file operations in main thread if the file system is fast. If
|
||||||
|
// AdvAgg's directories are mounted on something like S3, you might want to
|
||||||
|
// set this to FALSE.
|
||||||
|
$conf['advagg_fast_filesystem'] = TRUE;
|
||||||
|
|
||||||
|
// Pregenerate aggregate files. If disable the browser requesting the file
|
||||||
|
// will cause the generation to happen. If advagg 404 handling is broken
|
||||||
|
// then setting this to false will break your site in bad ways.
|
||||||
|
$conf['advagg_pregenerate_aggregate_files'] = TRUE;
|
||||||
|
|
||||||
|
// Set the jQuery UI version.
|
||||||
|
$conf['advagg_css_cdn_jquery_ui_version'] = '1.8.7';
|
||||||
|
|
||||||
|
// See if jQuery UI should be grabbed from the Google CDN.
|
||||||
|
$conf['advagg_css_cdn_jquery_ui'] = TRUE;
|
||||||
|
|
||||||
|
// Set the jQuery UI version.
|
||||||
|
$conf['advagg_js_cdn_jquery_ui_version'] = '1.8.7';
|
||||||
|
|
||||||
|
// Set the jQuery version.
|
||||||
|
$conf['advagg_js_cdn_jquery_version'] = '1.4.4';
|
||||||
|
|
||||||
|
// Use minification.
|
||||||
|
$conf['advagg_js_cdn_compression'] = TRUE;
|
||||||
|
|
||||||
|
// See if jQuery UI should be grabbed from the Google CDN.
|
||||||
|
$conf['advagg_js_cdn_jquery_ui'] = TRUE;
|
||||||
|
|
||||||
|
// See if jQuery should be grabbed from the Google CDN.
|
||||||
|
$conf['advagg_js_cdn_jquery'] = TRUE;
|
||||||
|
|
||||||
|
// Value for the compression ratio test.
|
||||||
|
$conf['advagg_js_compress_max_ratio'] = 0.9;
|
||||||
|
|
||||||
|
// Value for the compression ratio test.
|
||||||
|
$conf['advagg_js_compress_ratio'] = 0.1;
|
||||||
|
|
||||||
|
// Skip far future check on status page.
|
||||||
|
$conf['advagg_skip_far_future_check'] = FALSE;
|
||||||
|
|
||||||
|
// Skip preprocess and enabled checks.
|
||||||
|
$conf['advagg_skip_enabled_preprocess_check'] = FALSE;
|
||||||
|
|
||||||
|
// Default root dir for the advagg files; see advagg_get_root_files_dir().
|
||||||
|
$conf['advagg_root_dir_prefix'] = 'public://';
|
||||||
|
|
||||||
|
// How long to wait when writing the aggregate if a file is missing or the
|
||||||
|
// hash doesn't match.
|
||||||
|
$conf['advagg_file_read_failure_timeout'] = 3600;
|
||||||
|
|
||||||
|
// If FALSE mtime of files will only trigger a change if they are in the
|
||||||
|
// future.
|
||||||
|
$conf['advagg_strict_mtime_check'] = TRUE;
|
||||||
|
|
||||||
|
// Skip 304 check on status page.
|
||||||
|
$conf['advagg_skip_304_check'] = FALSE;
|
||||||
|
|
||||||
|
// Control how many bytes can be inlined.
|
||||||
|
$conf['advagg_mod_css_defer_inline_size_limit'] = 12288;
|
||||||
|
|
||||||
|
// Control how many bytes the preload header can use.
|
||||||
|
$conf['advagg_resource_hints_preload_max_size'] = 3072;
|
||||||
|
|
||||||
|
// If TRUE, only verify 1st hash instead of all 3 of the filename.
|
||||||
|
$conf['advagg_weak_file_verification'] = FALSE;
|
||||||
|
|
||||||
|
|
||||||
|
ADDITIONAL OPTIONS FOR DRUPAL_ADD_CSS/JS FUNCTIONS
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
AdvAgg extends the available options inside of `drupal_add_css` and
|
||||||
|
`drupal_add_js`.
|
||||||
|
|
||||||
|
`drupal_add_js` - additional keys for $options.
|
||||||
|
|
||||||
|
- `browsers`: Works the same as the one found in drupal_add_css.
|
||||||
|
- `onload`: Run this js code when after the js file has loaded.
|
||||||
|
- `onerror`: Run this js code when if the js file did not load.
|
||||||
|
- `async`: TRUE - Load this file using async.
|
||||||
|
- `no_defer`: TRUE - Never defer or async load this js file.
|
||||||
|
|
||||||
|
Both `drupal_add_js` + `drupal_add_css` - additional keys for $options.
|
||||||
|
|
||||||
|
- `scope_lock`: TRUE - Make sure the scope of this will not ever change.
|
||||||
|
- `movable`: FALSE - Make sure the ordering of this will not ever change.
|
||||||
|
- `preprocess_lock`: TRUE - Make sure the preprocess key will not ever change.
|
||||||
|
|
||||||
|
|
||||||
|
TECHNICAL DETAILS & HOOKS
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
**Technical Details**
|
||||||
|
|
||||||
|
- There are five database tables and two cache table used by advagg.
|
||||||
|
`advagg_schema` documents what they are used for.
|
||||||
|
- Files are generated by this pattern:
|
||||||
|
|
||||||
|
css__[BASE64_HASH]__[BASE64_HASH]__[BASE64_HASH].css
|
||||||
|
|
||||||
|
The first base64 hash value tells us what files are included in the
|
||||||
|
aggregate. Changing what files get included will change this value.
|
||||||
|
|
||||||
|
The second base64 hash value is used as a sort of version control; it changes
|
||||||
|
if any of the base files contents have changed. Changing a base files content
|
||||||
|
(like drupal.js) will change this value.
|
||||||
|
|
||||||
|
The third base64 hash value records what settings were used when generating
|
||||||
|
the aggregate. Changing a setting that affects how aggregates get built
|
||||||
|
(like toggling "Create .gz files") will change this value.
|
||||||
|
|
||||||
|
- To trigger scanning of the CSS / JS file cache to identify new files, run
|
||||||
|
the following:
|
||||||
|
|
||||||
|
// Trigger reloading the CSS and JS file cache in AdvAgg.
|
||||||
|
if (module_exists('advagg')) {
|
||||||
|
module_load_include('inc', 'advagg', 'advagg.cache');
|
||||||
|
advagg_push_new_changes();
|
||||||
|
}
|
||||||
|
|
||||||
|
- Aggressive Cache Setting: This will fully cache the rendered html generated
|
||||||
|
by AdvAgg. The cache ID is set by this code:
|
||||||
|
|
||||||
|
// CSS.
|
||||||
|
$hooks_hash = advagg_get_current_hooks_hash();
|
||||||
|
$css_cache_id_full =
|
||||||
|
'advagg:css:full:' . $hooks_hash . ':' .
|
||||||
|
drupal_hash_base64(serialize($full_css));
|
||||||
|
// JS.
|
||||||
|
$hooks_hash = advagg_get_current_hooks_hash();
|
||||||
|
$js_cache_id_full =
|
||||||
|
'advagg:js:full:' . $hooks_hash . ':' .
|
||||||
|
drupal_hash_base64(serialize($js_scope_array));
|
||||||
|
|
||||||
|
The second and final hash value in this cache id is the css/js_hash value.
|
||||||
|
This takes the input from `drupal_add_css/js()` and creates a hash value from
|
||||||
|
it. If a different file is added and/or inline code changed, this hash value
|
||||||
|
will be different.
|
||||||
|
|
||||||
|
The first hash value will take the current_hooks_hash value which is the
|
||||||
|
third base64 hash value listed above in this section (Technical Details) as
|
||||||
|
the first part of the hash. This means that if any value is changed in this
|
||||||
|
nested array a different cache id will be used. You can see the contents of
|
||||||
|
this nested array by going to
|
||||||
|
`admin/config/development/performance/advagg/info` under
|
||||||
|
"Hooks And Variables Used In Hash". An example of this being properly used is
|
||||||
|
if you enable the core locale module the language key will appear in the
|
||||||
|
array. This is needed because the `locale_css_alter` and `locale_js_alter`
|
||||||
|
functions both use the global $language variable in determining what css or
|
||||||
|
js files need to be altered. To add in your own context you can use
|
||||||
|
`hook_advagg_current_hooks_hash_array_alter` to do so. Be careful when doing
|
||||||
|
so as including something like the user id will make every user have a
|
||||||
|
different set of aggregate files.
|
||||||
|
|
||||||
|
**Hooks**
|
||||||
|
|
||||||
|
Modify file contents:
|
||||||
|
|
||||||
|
- `advagg_get_css_file_contents_alter`. Modify the data of each file before it
|
||||||
|
gets glued together into the bigger aggregate. Useful for minification.
|
||||||
|
- `advagg_get_js_file_contents_alter`. Modify the data of each file before it
|
||||||
|
gets glued together into the bigger aggregate. Useful for minification.
|
||||||
|
- `advagg_get_css_aggregate_contents_alter`. Modify the data of the complete
|
||||||
|
aggregate before it gets written to a file. Useful for minification.
|
||||||
|
- `advagg_get_js_aggregate_contents_alter`. Modify the data of the complete
|
||||||
|
aggregate before it gets written to a file.Useful for minification.
|
||||||
|
- `advagg_save_aggregate_alter`. Modify the data of the complete aggregate
|
||||||
|
allowing one create multiple files from one base file. Useful for gzip
|
||||||
|
compression. Also useful for mirroring data.
|
||||||
|
|
||||||
|
Modify file names and aggregate bundles:
|
||||||
|
|
||||||
|
- `advagg_current_hooks_hash_array_alter`. Add in your own settings and hooks
|
||||||
|
allowing one to modify the 3rd base64 hash in a filename.
|
||||||
|
- `advagg_build_aggregate_plans_alter`. Regroup files into different
|
||||||
|
aggregates.
|
||||||
|
- `advagg_css_groups_alter`. Allow other modules to modify `$css_groups` right
|
||||||
|
before it is processed.
|
||||||
|
- `advagg_js_groups_alter`. Allow other modules to modify `$js_groups` right
|
||||||
|
before it is processed.
|
||||||
|
|
||||||
|
Others:
|
||||||
|
|
||||||
|
- `advagg_hooks_implemented_alter`. Tell advagg about other hooks related to
|
||||||
|
advagg.
|
||||||
|
- `advagg_get_root_files_dir_alter`. Allow other modules to alter css and js
|
||||||
|
paths.
|
||||||
|
- `advagg_modify_css_pre_render_alter`. Allow other modules to modify $children
|
||||||
|
& $elements before they are rendered.
|
||||||
|
- `advagg_modify_js_pre_render_alter`. Allow other modules to modify $children
|
||||||
|
& $elements before they are rendered.
|
||||||
|
- `advagg_changed_files`. Let other modules know about the changed files.
|
||||||
|
- `advagg_removed_aggregates`. Let other modules know about removed aggregates.
|
||||||
|
- `advagg_scan_for_changes`. Let other modules see if files related to this
|
||||||
|
file has changed. Useful for detecting changes to referenced images in css.
|
||||||
|
- `advagg_get_info_on_files_alter`. Let other modules modify information about
|
||||||
|
the base CSS/JS files.
|
||||||
|
- `advagg_context_alter`. Allow other modules to swap important contextual
|
||||||
|
information on generation.
|
||||||
|
- `advagg_bundler_analysis`. If the bundler module is installed allow for other
|
||||||
|
modules to change the bundler analysis.
|
1965
frontend/drupal/sites/all/modules/advagg/advagg.admin.inc
Normal file
1965
frontend/drupal/sites/all/modules/advagg/advagg.admin.inc
Normal file
File diff suppressed because it is too large
Load Diff
132
frontend/drupal/sites/all/modules/advagg/advagg.admin.js
Normal file
132
frontend/drupal/sites/all/modules/advagg/advagg.admin.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Used to toggle the AdvAgg Bypass Cookie client side.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global Drupal:false */
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test to see if the given string contains unicode.
|
||||||
|
*
|
||||||
|
* @param {int} interval
|
||||||
|
* String to test.
|
||||||
|
* @param {int} granularity
|
||||||
|
* String to test.
|
||||||
|
* @param {string} langcode
|
||||||
|
* Language used in translation.
|
||||||
|
*
|
||||||
|
* @return {bool}
|
||||||
|
* true if string contains non ASCII characters.
|
||||||
|
* false if string only contains ASCII characters.
|
||||||
|
*/
|
||||||
|
Drupal.formatInterval = function (interval, granularity, langcode) {
|
||||||
|
'use strict';
|
||||||
|
granularity = typeof granularity !== 'undefined' ? granularity : 2;
|
||||||
|
langcode = typeof langcode !== 'undefined' ? langcode : null;
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
/* eslint-disable key-spacing */
|
||||||
|
while (granularity > 0) {
|
||||||
|
var value = 0;
|
||||||
|
if (interval >= 31536000) {
|
||||||
|
value = 31536000;
|
||||||
|
output += (output.length ? ' ' : '') + Drupal.formatPlural(Math.floor(interval / value), '1 year', '@count years', {langcode : langcode});
|
||||||
|
}
|
||||||
|
else if (interval >= 2592000) {
|
||||||
|
value = 2592000;
|
||||||
|
output += (output.length ? ' ' : '') + Drupal.formatPlural(Math.floor(interval / value), '1 month', '@count months', {langcode : langcode});
|
||||||
|
}
|
||||||
|
else if (interval >= 604800) {
|
||||||
|
value = 604800;
|
||||||
|
output += (output.length ? ' ' : '') + Drupal.formatPlural(Math.floor(interval / value), '1 week', '@count weeks', {langcode : langcode});
|
||||||
|
}
|
||||||
|
else if (interval >= 86400) {
|
||||||
|
value = 86400;
|
||||||
|
output += (output.length ? ' ' : '') + Drupal.formatPlural(Math.floor(interval / value), '1 day', '@count days', {langcode : langcode});
|
||||||
|
}
|
||||||
|
else if (interval >= 3600) {
|
||||||
|
value = 3600;
|
||||||
|
output += (output.length ? ' ' : '') + Drupal.formatPlural(Math.floor(interval / value), '1 hour', '@count hours', {langcode : langcode});
|
||||||
|
}
|
||||||
|
else if (interval >= 60) {
|
||||||
|
value = 60;
|
||||||
|
output += (output.length ? ' ' : '') + Drupal.formatPlural(Math.floor(interval / value), '1 min', '@count min', {langcode : langcode});
|
||||||
|
}
|
||||||
|
else if (interval >= 1) {
|
||||||
|
value = 1;
|
||||||
|
output += (output.length ? ' ' : '') + Drupal.formatPlural(Math.floor(interval / value), '1 sec', '@count sec', {langcode : langcode});
|
||||||
|
}
|
||||||
|
|
||||||
|
interval %= value;
|
||||||
|
granularity--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.length ? output : Drupal.t('0 sec', {}, {langcode : langcode});
|
||||||
|
/* eslint-enable key-spacing */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test to see if the given string contains unicode.
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* String to test.
|
||||||
|
*
|
||||||
|
* @return {bool}
|
||||||
|
* true if string contains non ASCII characters.
|
||||||
|
* false if string only contains ASCII characters.
|
||||||
|
*/
|
||||||
|
function advagg_is_unicode(str) {
|
||||||
|
'use strict';
|
||||||
|
for (var i = 0, n = str.length; i < n; i++) {
|
||||||
|
if (str.charCodeAt(i) > 255) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the advagg cookie.
|
||||||
|
*
|
||||||
|
* @return {bool}
|
||||||
|
* true if hostname contains unicode.
|
||||||
|
* false so the form does not get submitted.
|
||||||
|
*/
|
||||||
|
function advagg_toggle_cookie() {
|
||||||
|
'use strict';
|
||||||
|
// Fallback to submitting the form for Unicode domains like ".рф".
|
||||||
|
if (advagg_is_unicode(document.location.hostname)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cookie_name = 'AdvAggDisabled';
|
||||||
|
|
||||||
|
// See if the cookie exists.
|
||||||
|
var cookie_pos = document.cookie.indexOf(cookie_name + '=' + Drupal.settings.advagg.key);
|
||||||
|
|
||||||
|
// If the cookie does exist then remove it.
|
||||||
|
if (cookie_pos !== -1) {
|
||||||
|
document.cookie =
|
||||||
|
cookie_name + '='
|
||||||
|
+ '; expires=Thu, 01 Jan 1970 00:00:00 GMT'
|
||||||
|
+ '; path=' + Drupal.settings.basePath
|
||||||
|
+ '; domain=.' + document.location.hostname + ';';
|
||||||
|
alert(Drupal.t('AdvAgg Bypass Cookie Removed'));
|
||||||
|
}
|
||||||
|
// If the cookie does not exist then set it.
|
||||||
|
else {
|
||||||
|
var bypass_length = document.getElementById('edit-timespan').value;
|
||||||
|
var expire_date = new Date(new Date().getTime() + bypass_length * 1000);
|
||||||
|
|
||||||
|
document.cookie =
|
||||||
|
cookie_name + '=' + Drupal.settings.advagg.key
|
||||||
|
+ '; expires=' + expire_date.toGMTString()
|
||||||
|
+ '; path=' + Drupal.settings.basePath
|
||||||
|
+ '; domain=.' + document.location.hostname + ';';
|
||||||
|
alert(Drupal.t('AdvAgg Bypass Cookie Set for @time.', {'@time': Drupal.formatInterval(bypass_length)}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must return false, if returning true then form gets submitted.
|
||||||
|
return false;
|
||||||
|
}
|
662
frontend/drupal/sites/all/modules/advagg/advagg.advagg.inc
Normal file
662
frontend/drupal/sites/all/modules/advagg/advagg.advagg.inc
Normal file
@ -0,0 +1,662 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced CSS/JS aggregation module.
|
||||||
|
*
|
||||||
|
* File used to store hook_advagg_* hooks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_save_aggregate_alter().
|
||||||
|
*
|
||||||
|
* Used to add in a .gz file if none exits.
|
||||||
|
*/
|
||||||
|
function advagg_advagg_save_aggregate_alter(array &$files_to_save, array $aggregate_settings, array $other_parameters) {
|
||||||
|
// * @param array $files_to_save
|
||||||
|
// * Array($uri => $contents).
|
||||||
|
// * @param array $aggregate_settings
|
||||||
|
// * Array of settings.
|
||||||
|
// * @param array $other_parameters
|
||||||
|
// * Array of containing $files and $type.
|
||||||
|
$file_types = array();
|
||||||
|
// Handle gzip.
|
||||||
|
if (!empty($aggregate_settings['variables']['advagg_gzip'])) {
|
||||||
|
// Use zopfli_encode if it exists.
|
||||||
|
// See https://github.com/kjdev/php-ext-zopfli
|
||||||
|
if (function_exists('zopfli_encode')
|
||||||
|
&& defined('ZOPFLI_GZIP')
|
||||||
|
&& empty($aggregate_settings['variables']['advagg_no_zopfli'])
|
||||||
|
) {
|
||||||
|
$file_types['.gz'] = array('zopfli_encode', 15, constant('ZOPFLI_GZIP'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$file_types['.gz'] = array('gzencode', 9, FORCE_GZIP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle brotli.
|
||||||
|
// See https://github.com/kjdev/php-ext-brotli
|
||||||
|
if (!empty($aggregate_settings['variables']['advagg_brotli'])
|
||||||
|
&& defined('BROTLI_TEXT')
|
||||||
|
&& function_exists('brotli_compress')
|
||||||
|
) {
|
||||||
|
$file_types['.br'] = array('brotli_compress', 11, constant('BROTLI_TEXT'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($file_types)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special S3 handling.
|
||||||
|
$s3fs = FALSE;
|
||||||
|
$files_to_save_keys = array_keys($files_to_save);
|
||||||
|
foreach ($files_to_save_keys as $uri) {
|
||||||
|
$wrapper = file_stream_wrapper_get_instance_by_uri($uri);
|
||||||
|
if ($wrapper && get_class($wrapper) === 'S3fsStreamWrapper') {
|
||||||
|
$s3fs = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($file_types as $ext => $settings) {
|
||||||
|
// See if a file already exists with this extension.
|
||||||
|
$ext_exists = FALSE;
|
||||||
|
foreach ($files_to_save as $uri => $contents) {
|
||||||
|
// See if this uri contains $ext near the end of it.
|
||||||
|
if (strlen($uri) > 91 + strlen(ADVAGG_SPACE) * 3) {
|
||||||
|
$pos = strripos($uri, $ext, 91 + strlen(ADVAGG_SPACE) * 3);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$pos = strripos($uri, $ext);
|
||||||
|
}
|
||||||
|
if (!empty($pos)) {
|
||||||
|
$len = strlen($uri);
|
||||||
|
// $ext file exists, exit loop.
|
||||||
|
if ($pos == $len - 3) {
|
||||||
|
$ext_exists = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a $ext file does not exist, create one.
|
||||||
|
if (!$ext_exists) {
|
||||||
|
// Use the first file in the array.
|
||||||
|
$data = reset($files_to_save);
|
||||||
|
$uri = key($files_to_save);
|
||||||
|
// Compress it and add it to the $files_to_save array.
|
||||||
|
$callback = $settings[0];
|
||||||
|
$settings[0] = $data;
|
||||||
|
$compressed = call_user_func_array($callback, $settings);
|
||||||
|
if (!empty($compressed)) {
|
||||||
|
if ($s3fs && $ext === '.gz') {
|
||||||
|
// Only serve gzip files from S3.
|
||||||
|
$files_to_save[$uri] = $compressed;
|
||||||
|
}
|
||||||
|
elseif ($s3fs && $ext === '.br' && !isset($file_types['.gz'])) {
|
||||||
|
// Only serve br files from S3.
|
||||||
|
$files_to_save[$uri] = $compressed;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$files_to_save[$uri . $ext] = $compressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_build_aggregate_plans_alter().
|
||||||
|
*
|
||||||
|
* Used to alter the plan so it has the same grouping as cores.
|
||||||
|
*/
|
||||||
|
function advagg_advagg_build_aggregate_plans_alter(array &$files, &$modified, $type) {
|
||||||
|
// * @param array $files
|
||||||
|
// * List of files in the aggregate as well as the aggregate name.
|
||||||
|
// * @param bool $modified
|
||||||
|
// * Change this to TRUE if $files has been changed.
|
||||||
|
// * @param string $type
|
||||||
|
// * String containing css or js.
|
||||||
|
//
|
||||||
|
// Do nothing if core grouping is disabled.
|
||||||
|
if (!variable_get('advagg_core_groups', ADVAGG_CORE_GROUPS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$temp_new_files = array();
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($files as $data) {
|
||||||
|
$group = NULL;
|
||||||
|
$every_page = NULL;
|
||||||
|
foreach ($data['files'] as $fileinfo) {
|
||||||
|
// Grouped by group and every_page variables.
|
||||||
|
if (is_null($group)) {
|
||||||
|
$group = $fileinfo['group'];
|
||||||
|
}
|
||||||
|
if (is_null($every_page)) {
|
||||||
|
$every_page = $fileinfo['every_page'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bump Counter if group/every_page has changed from the last one.
|
||||||
|
if ($group != $fileinfo['group'] || $every_page != $fileinfo['every_page']) {
|
||||||
|
++$counter;
|
||||||
|
$group = $fileinfo['group'];
|
||||||
|
$every_page = $fileinfo['every_page'];
|
||||||
|
$modified = TRUE;
|
||||||
|
}
|
||||||
|
$temp_new_files[$counter][] = $fileinfo;
|
||||||
|
}
|
||||||
|
++$counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace $files array with new aggregate filenames.
|
||||||
|
$files = advagg_generate_filenames(array($temp_new_files), $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_context_alter().
|
||||||
|
*/
|
||||||
|
function advagg_advagg_context_alter(&$original, $aggregate_settings, $mode) {
|
||||||
|
if ($mode == 0) {
|
||||||
|
// Change context.
|
||||||
|
$original['base_root'] = $GLOBALS['base_root'];
|
||||||
|
$original['base_url'] = $GLOBALS['base_url'];
|
||||||
|
$original['base_path'] = $GLOBALS['base_path'];
|
||||||
|
$original['is_https'] = $GLOBALS['is_https'];
|
||||||
|
$original['language'] = isset($GLOBALS['language']) ? $GLOBALS['language'] : NULL;
|
||||||
|
|
||||||
|
$GLOBALS['is_https'] = $aggregate_settings['variables']['is_https'];
|
||||||
|
if ($aggregate_settings['variables']['is_https']) {
|
||||||
|
$GLOBALS['base_root'] = str_replace('http://', 'https://', $GLOBALS['base_root']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$GLOBALS['base_root'] = str_replace('https://', 'http://', $GLOBALS['base_root']);
|
||||||
|
}
|
||||||
|
$GLOBALS['base_path'] = $aggregate_settings['variables']['base_path'];
|
||||||
|
$GLOBALS['base_url'] = rtrim($GLOBALS['base_root'] . $GLOBALS['base_path'], '/');
|
||||||
|
|
||||||
|
if (isset($aggregate_settings['variables']['language'])) {
|
||||||
|
$languages = language_list();
|
||||||
|
if (isset($languages[$aggregate_settings['variables']['language']])) {
|
||||||
|
$GLOBALS['language'] = $languages[$aggregate_settings['variables']['language']];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($mode == 1) {
|
||||||
|
// Change context back.
|
||||||
|
if (isset($original['base_root'])) {
|
||||||
|
$GLOBALS['base_root'] = $original['base_root'];
|
||||||
|
}
|
||||||
|
if (isset($original['base_url'])) {
|
||||||
|
$GLOBALS['base_url'] = $original['base_url'];
|
||||||
|
}
|
||||||
|
if (isset($original['base_path'])) {
|
||||||
|
$GLOBALS['base_path'] = $original['base_path'];
|
||||||
|
}
|
||||||
|
if (isset($original['is_https'])) {
|
||||||
|
$GLOBALS['is_https'] = $original['is_https'];
|
||||||
|
}
|
||||||
|
if (isset($original['language'])) {
|
||||||
|
$GLOBALS['language'] = $original['language'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moved this in here due to incomplete bug reports from
|
||||||
|
// https://www.drupal.org/node/1942230. I can not reproduce the reported
|
||||||
|
// issues with the patch for the CDN module so I've moved the code into
|
||||||
|
// advagg.
|
||||||
|
if (!function_exists('cdn_advagg_context_alter') && module_exists('cdn')) {
|
||||||
|
if ($mode == 0) {
|
||||||
|
// Save original context.
|
||||||
|
$original[CDN_MODE_VARIABLE] = variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
|
||||||
|
$original[CDN_BASIC_FARFUTURE_VARIABLE] = variable_get(CDN_BASIC_FARFUTURE_VARIABLE, CDN_BASIC_FARFUTURE_DEFAULT);
|
||||||
|
$original[CDN_HTTPS_SUPPORT_VARIABLE] = variable_get(CDN_HTTPS_SUPPORT_VARIABLE, FALSE);
|
||||||
|
$original[CDN_STATUS_VARIABLE] = variable_get(CDN_STATUS_VARIABLE, CDN_DISABLED);
|
||||||
|
|
||||||
|
// Set context for file_create_url()/cdn_file_url_alter().
|
||||||
|
$GLOBALS['conf'][CDN_MODE_VARIABLE] = isset($aggregate_settings['variables'][CDN_MODE_VARIABLE])
|
||||||
|
? $aggregate_settings['variables'][CDN_MODE_VARIABLE]
|
||||||
|
: variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
|
||||||
|
$GLOBALS['conf'][CDN_BASIC_FARFUTURE_VARIABLE] = isset($aggregate_settings['variables'][CDN_BASIC_FARFUTURE_VARIABLE])
|
||||||
|
? $aggregate_settings['variables'][CDN_BASIC_FARFUTURE_VARIABLE]
|
||||||
|
: variable_get(CDN_BASIC_FARFUTURE_VARIABLE, CDN_BASIC_FARFUTURE_DEFAULT);
|
||||||
|
$GLOBALS['conf'][CDN_HTTPS_SUPPORT_VARIABLE] = isset($aggregate_settings['variables'][CDN_HTTPS_SUPPORT_VARIABLE])
|
||||||
|
? $aggregate_settings['variables'][CDN_HTTPS_SUPPORT_VARIABLE]
|
||||||
|
: variable_get(CDN_HTTPS_SUPPORT_VARIABLE, FALSE);
|
||||||
|
$GLOBALS['conf'][CDN_STATUS_VARIABLE] = isset($aggregate_settings['variables'][CDN_STATUS_VARIABLE])
|
||||||
|
? $aggregate_settings['variables'][CDN_STATUS_VARIABLE]
|
||||||
|
: variable_get(CDN_STATUS_VARIABLE, CDN_DISABLED);
|
||||||
|
|
||||||
|
// Disable CDN if cdn_check_drupal_path is FALSE.
|
||||||
|
if (empty($aggregate_settings['variables']['cdn_check_drupal_path'])) {
|
||||||
|
$original[CDN_STATUS_VARIABLE] = CDN_DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle HTTPS.
|
||||||
|
if (!empty($aggregate_settings['variables']['cdn_request_is_https']) && !cdn_request_is_https()) {
|
||||||
|
if (isset($_SERVER['HTTPS'])) {
|
||||||
|
$original['HTTPS'] = $_SERVER['HTTPS'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$original['HTTPS'] = FALSE;
|
||||||
|
}
|
||||||
|
$_SERVER['HTTPS'] = 'on';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($mode == 1) {
|
||||||
|
// Set context back.
|
||||||
|
$GLOBALS['conf'][CDN_MODE_VARIABLE] = $original[CDN_MODE_VARIABLE];
|
||||||
|
$GLOBALS['conf'][CDN_BASIC_FARFUTURE_VARIABLE] = $original[CDN_BASIC_FARFUTURE_VARIABLE];
|
||||||
|
$GLOBALS['conf'][CDN_HTTPS_SUPPORT_VARIABLE] = $original[CDN_HTTPS_SUPPORT_VARIABLE];
|
||||||
|
$GLOBALS['conf'][CDN_STATUS_VARIABLE] = $original[CDN_STATUS_VARIABLE];
|
||||||
|
|
||||||
|
// Handle HTTPS.
|
||||||
|
if (isset($original['HTTPS']) && !is_null($original['HTTPS'])) {
|
||||||
|
$_SERVER['HTTPS'] = $original['HTTPS'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_get_info_on_files_alter().
|
||||||
|
*
|
||||||
|
* Used to make sure the info is up to date in the cache.
|
||||||
|
*/
|
||||||
|
function advagg_advagg_get_info_on_files_alter(&$return, $cached_data, $bypass_cache) {
|
||||||
|
// Check for the ie_css_selector_limiter.
|
||||||
|
if (variable_get('advagg_ie_css_selector_limiter', ADVAGG_IE_CSS_SELECTOR_LIMITER)) {
|
||||||
|
$limit_value = variable_get('advagg_ie_css_selector_limiter_value', ADVAGG_IE_CSS_SELECTOR_LIMITER_VALUE);
|
||||||
|
|
||||||
|
// Get the css path.
|
||||||
|
list($css_path) = advagg_get_root_files_dir();
|
||||||
|
$css_parts_path = (advagg_s3fs_evaluate_no_rewrite_cssjs(FALSE)) ? $css_path[0] : $css_path[1];
|
||||||
|
$parts_path = $css_parts_path . '/parts/';
|
||||||
|
|
||||||
|
foreach ($return as &$info) {
|
||||||
|
// Skip if not a css file.
|
||||||
|
if (empty($info['fileext']) || $info['fileext'] !== 'css') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a split css file.
|
||||||
|
if (strpos($info['data'], $parts_path) !== FALSE) {
|
||||||
|
$info['split'] = TRUE;
|
||||||
|
}
|
||||||
|
// Break large file into multiple small files if needed.
|
||||||
|
elseif ($info['linecount'] > $limit_value) {
|
||||||
|
advagg_split_css_file($info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture resource_hints.
|
||||||
|
if (variable_get('advagg_resource_hints_dns_prefetch', ADVAGG_RESOURCE_HINTS_DNS_PREFETCH)
|
||||||
|
|| variable_get('advagg_resource_hints_preconnect', ADVAGG_RESOURCE_HINTS_PRECONNECT)
|
||||||
|
|| variable_get('advagg_resource_hints_preload', ADVAGG_RESOURCE_HINTS_PRELOAD)
|
||||||
|
) {
|
||||||
|
$aggregate_settings = advagg_current_hooks_hash_array();
|
||||||
|
foreach ($return as &$info) {
|
||||||
|
// Skip if not a css file.
|
||||||
|
if (empty($info['fileext']) || $info['fileext'] !== 'css') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Get the file contents.
|
||||||
|
$file_contents = (string) @advagg_file_get_contents($info['data']);
|
||||||
|
$file_contents = advagg_load_css_stylesheet($info['data'], FALSE, $aggregate_settings, $file_contents);
|
||||||
|
|
||||||
|
// Get domain names and external assets in this css file.
|
||||||
|
$hosts = array();
|
||||||
|
$urls = array();
|
||||||
|
$matches = array();
|
||||||
|
$pattern = '%url\(\s*+[\'"]?+(http:\/\/|https:\/\/|\/\/)([^\'"()\s]++)[\'"]?+\s*+\)%i';
|
||||||
|
preg_match_all($pattern, $file_contents, $matches);
|
||||||
|
if (!empty($matches[1])) {
|
||||||
|
foreach ($matches[1] as $key => $match) {
|
||||||
|
$url = $match . $matches[2][$key];
|
||||||
|
$parse = @parse_url($url);
|
||||||
|
if (!empty($parse['host'])) {
|
||||||
|
$extra = '';
|
||||||
|
$ext = strtolower(pathinfo($url, PATHINFO_EXTENSION));
|
||||||
|
$supported = array(
|
||||||
|
'eot',
|
||||||
|
'woff2',
|
||||||
|
'woff',
|
||||||
|
'ttf',
|
||||||
|
'otf',
|
||||||
|
);
|
||||||
|
if (in_array($ext, $supported)) {
|
||||||
|
$extra .= '#crossorigin';
|
||||||
|
}
|
||||||
|
$hosts[$parse['host'] . $extra] = $match . $parse['host'] . '/' . $extra;
|
||||||
|
$urls[$url] = $url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($hosts) && (variable_get('advagg_resource_hints_dns_prefetch', ADVAGG_RESOURCE_HINTS_DNS_PREFETCH)
|
||||||
|
|| variable_get('advagg_resource_hints_preconnect', ADVAGG_RESOURCE_HINTS_PRECONNECT)
|
||||||
|
)) {
|
||||||
|
$info['dns_prefetch'] = array_values($hosts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get local files to preload in this css file.
|
||||||
|
if (variable_get('advagg_resource_hints_preload', ADVAGG_RESOURCE_HINTS_PRELOAD)) {
|
||||||
|
$matches = array();
|
||||||
|
$pattern = '/url\(\s*+[\'"]?(.*?)[\'"\s]?+\)/i';
|
||||||
|
preg_match_all($pattern, $file_contents, $matches);
|
||||||
|
if (!empty($matches[1])) {
|
||||||
|
foreach ($matches[1] as $key => $match) {
|
||||||
|
$url = advagg_convert_abs_to_rel($match);
|
||||||
|
$parse = @parse_url($url);
|
||||||
|
if (empty($parse['host'])) {
|
||||||
|
$urls[$url] = $url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($urls)) {
|
||||||
|
$info['preload'] = array_values($urls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_changed_files().
|
||||||
|
*
|
||||||
|
* If the color module is enabled regenerate color module css when it changes.
|
||||||
|
* If a responsive file inside an adaptive theme has changed, regenerate it.
|
||||||
|
*/
|
||||||
|
function advagg_advagg_changed_files(array $files, array $types) {
|
||||||
|
// * @param array $files
|
||||||
|
// * List of files that have changed.
|
||||||
|
// * @param array $types
|
||||||
|
// * Array with the css and or the js key.
|
||||||
|
if (module_exists('locale')) {
|
||||||
|
_advagg_locale_changed_files($files, $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of what themes have been done.
|
||||||
|
static $themes_done;
|
||||||
|
if (!isset($themes_done)) {
|
||||||
|
$themes_done = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if no css changed.
|
||||||
|
if (empty($types['css'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($files as $filename => $meta_data) {
|
||||||
|
// Only care about css files.
|
||||||
|
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||||
|
if ($ext !== 'css') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
advagg_advagg_scan_for_changes($filename, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save error states and clear them.
|
||||||
|
$errors_before = drupal_static('form_set_error', array());
|
||||||
|
form_clear_error();
|
||||||
|
|
||||||
|
// See if the css file is used the theme.
|
||||||
|
$themes = list_themes();
|
||||||
|
$changed_files = array_keys($files);
|
||||||
|
|
||||||
|
$submit_ran = FALSE;
|
||||||
|
foreach ($themes as $theme_name => $theme_values) {
|
||||||
|
$files_in_theme = array();
|
||||||
|
foreach ($changed_files as $css_file) {
|
||||||
|
// Skip if we already did a form submit for this theme.
|
||||||
|
if (!empty($themes_done[$theme_name])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if the file that was changed is not in this themes directory.
|
||||||
|
$theme_path = drupal_get_path('theme', $theme_name);
|
||||||
|
if ((!empty($theme_path)) && strpos($css_file, $theme_path) !== 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$files_in_theme[] = $css_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the theme if none of the changed files live in here.
|
||||||
|
if (empty($files_in_theme)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the form for this theme.
|
||||||
|
$router_item = menu_get_item('admin/appearance/settings/' . $theme_name);
|
||||||
|
if ($router_item['include_file']) {
|
||||||
|
require_once DRUPAL_ROOT . '/' . $router_item['include_file'];
|
||||||
|
}
|
||||||
|
$form = drupal_get_form('system_theme_settings', $theme_name);
|
||||||
|
// Get the form defaults.
|
||||||
|
$defaults = array();
|
||||||
|
advagg_get_defaults_from_form($defaults, $form);
|
||||||
|
|
||||||
|
$rebuild = FALSE;
|
||||||
|
if (isset($defaults['atcore_version_test'])) {
|
||||||
|
// Rebuild if the theme is an adaptive theme.
|
||||||
|
$rebuild = TRUE;
|
||||||
|
}
|
||||||
|
if (!$rebuild && module_exists('color')) {
|
||||||
|
foreach ($files_in_theme as $css_file) {
|
||||||
|
if (isset($form['color'])) {
|
||||||
|
// Rebuild if the file that was changed is a color module file.
|
||||||
|
foreach ($defaults['info']['css'] as $theme_file) {
|
||||||
|
if ($theme_path . '/' . $theme_file === $css_file) {
|
||||||
|
$rebuild = TRUE;
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if themes css does not need to be generated dynamically.
|
||||||
|
if (empty($rebuild)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the palette value.
|
||||||
|
$palette = array();
|
||||||
|
if (isset($form['color'])) {
|
||||||
|
foreach (element_children($form['color']['palette']) as $key) {
|
||||||
|
$palette[$key] = $form['color']['palette'][$key]['#value'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the form state array.
|
||||||
|
$form_state = array(
|
||||||
|
'values' => array('palette' => $palette),
|
||||||
|
'build_info' => array('args' => array(0 => $theme_name)),
|
||||||
|
);
|
||||||
|
$form_state['values'] += $defaults;
|
||||||
|
|
||||||
|
if (isset($defaults['atcore_version_test'])) {
|
||||||
|
// Validate form.
|
||||||
|
at_core_settings_validate($form, $form_state);
|
||||||
|
$errors = form_set_error();
|
||||||
|
if (empty($errors)) {
|
||||||
|
// Only submit if no errors.
|
||||||
|
at_core_settings_submit($form, $form_state);
|
||||||
|
$themes_done[$theme_name] = TRUE;
|
||||||
|
$submit_ran = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif (isset($form['color'])) {
|
||||||
|
// Validate form.
|
||||||
|
color_scheme_form_validate($form, $form_state);
|
||||||
|
$errors = form_set_error();
|
||||||
|
if (empty($errors)) {
|
||||||
|
// Only submit if no errors.
|
||||||
|
color_scheme_form_submit($form, $form_state);
|
||||||
|
$themes_done[$theme_name] = TRUE;
|
||||||
|
$submit_ran = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Reset form errors.
|
||||||
|
form_clear_error();
|
||||||
|
}
|
||||||
|
// Save error states back.
|
||||||
|
$form_set_error = &drupal_static('form_set_error', array());
|
||||||
|
$form_set_error = $errors_before;
|
||||||
|
|
||||||
|
// Rescan again as the submit will generate new files in the css dir.
|
||||||
|
if ($submit_ran) {
|
||||||
|
advagg_push_new_changes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_scan_for_changes().
|
||||||
|
*
|
||||||
|
* Used to see if the responsive files inside an adaptive theme has changed.
|
||||||
|
*/
|
||||||
|
function advagg_advagg_scan_for_changes($filename, $save_changes = FALSE) {
|
||||||
|
// Skip if this file is not a css file.
|
||||||
|
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||||
|
if ($ext !== 'css') {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if the file is not in an adaptive theme.
|
||||||
|
$adaptivethemes = array();
|
||||||
|
$themes = list_themes();
|
||||||
|
foreach ($themes as $theme_name => $theme_values) {
|
||||||
|
$path = variable_get('theme_' . $theme_name . '_files_directory', '');
|
||||||
|
if (!empty($path) && strpos($filename, $path) !== FALSE) {
|
||||||
|
$adaptivethemes[$theme_name] = $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($adaptivethemes)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file_changed = array();
|
||||||
|
foreach ($adaptivethemes as $theme_name => $path) {
|
||||||
|
// Set up some paths we use to get and save files.
|
||||||
|
$path_to_responsive_css = drupal_get_path('theme', $theme_name) . '/css/';
|
||||||
|
$path_to_panels_css = drupal_get_path('theme', 'adaptivetheme') . '/layouts/css/';
|
||||||
|
|
||||||
|
// Map files to generated file names.
|
||||||
|
$file_map = array(
|
||||||
|
"$path/$theme_name.responsive.styles.css" => array(
|
||||||
|
$path_to_responsive_css . 'responsive.custom.css',
|
||||||
|
$path_to_responsive_css . 'responsive.smalltouch.portrait.css',
|
||||||
|
$path_to_responsive_css . 'responsive.smartphone.portrait.css',
|
||||||
|
$path_to_responsive_css . 'responsive.smalltouch.landscape.css',
|
||||||
|
$path_to_responsive_css . 'responsive.smartphone.landscape.css',
|
||||||
|
$path_to_responsive_css . 'responsive.tablet.portrait.css',
|
||||||
|
$path_to_responsive_css . 'responsive.tablet.landscape.css',
|
||||||
|
$path_to_responsive_css . 'responsive.desktop.css',
|
||||||
|
),
|
||||||
|
"$path/$theme_name.lt-ie8.layout.css" => array(
|
||||||
|
$path_to_panels_css . 'ie_defaults.css',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (!isset($file_map[$filename])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if anything has changed.
|
||||||
|
$changes = advagg_detect_subfile_changes($filename, $file_map[$filename], 'adaptivetheme', $save_changes);
|
||||||
|
if (!empty($changes)) {
|
||||||
|
$file_changed[$path] = $changes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file_changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the locale module is enabled regenerate locale translations.
|
||||||
|
*
|
||||||
|
* @param array $files
|
||||||
|
* List of files that have changed.
|
||||||
|
* @param array $types
|
||||||
|
* Array with the css and or the js key.
|
||||||
|
*/
|
||||||
|
function _advagg_locale_changed_files(array $files, array $types) {
|
||||||
|
// Skip if no js changed.
|
||||||
|
if (empty($types['js'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$javascript = array();
|
||||||
|
foreach ($files as $filename => $meta_data) {
|
||||||
|
// Only care about js files.
|
||||||
|
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||||
|
if ($ext !== 'js') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$javascript[] = array(
|
||||||
|
'type' => 'file',
|
||||||
|
'data' => $filename,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!empty($javascript)) {
|
||||||
|
$javascript_before = $javascript;
|
||||||
|
$language_before = $GLOBALS['language'];
|
||||||
|
$language_list = language_list();
|
||||||
|
foreach ($language_list as $lang) {
|
||||||
|
if ($lang->enabled) {
|
||||||
|
$GLOBALS['language'] = $lang;
|
||||||
|
$javascript = $javascript_before;
|
||||||
|
_advagg_locale_js_alter($javascript);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$GLOBALS['language'] = $language_before;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a form get the default values from it.
|
||||||
|
*
|
||||||
|
* @param array $defaults
|
||||||
|
* An empty array used to populate the default values.
|
||||||
|
* @param array $form
|
||||||
|
* The form returned from drupal_get_form().
|
||||||
|
* @param string $parent_key
|
||||||
|
* The key name of the parent.
|
||||||
|
*/
|
||||||
|
function advagg_get_defaults_from_form(array &$defaults, array $form, $parent_key = '') {
|
||||||
|
foreach (element_children($form) as $key) {
|
||||||
|
$values = $form[$key];
|
||||||
|
if (isset($values['#value'])) {
|
||||||
|
// Grab defaults at this level.
|
||||||
|
if (!isset($defaults[$key])) {
|
||||||
|
$defaults[$key] = $values['#value'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$defaults[$parent_key . '-' . $key] = $values['#value'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif (isset($values['#default_value'])) {
|
||||||
|
// Grab defaults at this level.
|
||||||
|
if (!isset($defaults[$key])) {
|
||||||
|
$defaults[$key] = $values['#default_value'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$defaults[$parent_key . '-' . $key] = $values['#default_value'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif (is_array($values)) {
|
||||||
|
// Go deeper if needed.
|
||||||
|
advagg_get_defaults_from_form($defaults, $values, $key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
700
frontend/drupal/sites/all/modules/advagg/advagg.api.php
Normal file
700
frontend/drupal/sites/all/modules/advagg/advagg.api.php
Normal file
@ -0,0 +1,700 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Hooks provided by the AdvAgg module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup advagg_hooks Advanced Aggregates Hooks
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
* Hooks for modules to implement to extend or modify Advanced Aggregates.
|
||||||
|
*
|
||||||
|
* For more examples of use see most of the Advanced Agregrates sub modules.
|
||||||
|
*
|
||||||
|
* @see https://api.drupal.org/api/drupal/includes%21module.inc/group/hooks/7.x
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow modules to modify the aggregate plan.
|
||||||
|
*
|
||||||
|
* @param array $files
|
||||||
|
* An associative array.
|
||||||
|
* filename - data.
|
||||||
|
* @param bool $modified
|
||||||
|
* Set this to TRUE if the $files structure has been changed.
|
||||||
|
* @param string $type
|
||||||
|
* Lowercase css or js.
|
||||||
|
*
|
||||||
|
* @see advagg_build_aggregate_plans()
|
||||||
|
* @see advagg_advagg_build_aggregate_plans_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_build_aggregate_plans_alter(array &$files, &$modified, $type) {
|
||||||
|
// Do nothing if core grouping is disabled.
|
||||||
|
if (!variable_get('advagg_core_groups', ADVAGG_CORE_GROUPS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$temp_new_files = array();
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($files as $filename => $data) {
|
||||||
|
if ($filename) {
|
||||||
|
// This is the filename.
|
||||||
|
}
|
||||||
|
$group = NULL;
|
||||||
|
$every_page = NULL;
|
||||||
|
foreach ($data['files'] as $fileinfo) {
|
||||||
|
// Grouped by group and every_page variables.
|
||||||
|
if (is_null($group)) {
|
||||||
|
$group = $fileinfo['group'];
|
||||||
|
}
|
||||||
|
if (is_null($every_page)) {
|
||||||
|
$every_page = $fileinfo['every_page'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bump Counter if group/every_page has changed from the last one.
|
||||||
|
if ($group != $fileinfo['group'] || $every_page != $fileinfo['every_page']) {
|
||||||
|
++$counter;
|
||||||
|
$group = $fileinfo['group'];
|
||||||
|
$every_page = $fileinfo['every_page'];
|
||||||
|
$modified = TRUE;
|
||||||
|
}
|
||||||
|
$temp_new_files[$counter][] = $fileinfo;
|
||||||
|
}
|
||||||
|
++$counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace $files array with new aggregate filenames.
|
||||||
|
$files = advagg_generate_filenames(array($temp_new_files), $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let other modules know about the changed files.
|
||||||
|
*
|
||||||
|
* @param array $files
|
||||||
|
* An associative array.
|
||||||
|
* filename - meta_data.
|
||||||
|
* @param array $types
|
||||||
|
* Array containing css and/or js.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Not used currently.
|
||||||
|
*
|
||||||
|
* @see advagg_push_new_changes()
|
||||||
|
* @see advagg_js_compress_advagg_changed_files()
|
||||||
|
*/
|
||||||
|
function hook_advagg_changed_files(array $files, array $types) {
|
||||||
|
// Only care about js files.
|
||||||
|
if (empty($types['js'])) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
$return = array();
|
||||||
|
foreach ($files as $filename => $meta_data) {
|
||||||
|
// Only care about js files.
|
||||||
|
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||||
|
if ($ext !== 'js') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$return[$filename] = advagg_js_compress_run_test($filename);
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to add in their own settings and hooks.
|
||||||
|
*
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* An associative array of hooks and settings used.
|
||||||
|
*
|
||||||
|
* @see advagg_current_hooks_hash_array()
|
||||||
|
* @see advagg_js_compress_advagg_current_hooks_hash_array_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_current_hooks_hash_array_alter(array &$aggregate_settings) {
|
||||||
|
$aggregate_settings['variables']['advagg_js_compressor'] = variable_get('advagg_js_compressor', ADVAGG_JS_COMPRESSOR);
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_packer'] = variable_get('advagg_js_compress_packer', ADVAGG_JS_COMPRESS_PACKER);
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_max_ratio'] = variable_get('advagg_js_compress_max_ratio', ADVAGG_JS_COMPRESS_MAX_RATIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to alter the contents and add new files to save (.gz).
|
||||||
|
*
|
||||||
|
* @param array $files_to_save
|
||||||
|
* Array($uri => $contents).
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* Array of settings.
|
||||||
|
* @param array $other_parameters
|
||||||
|
* Array of containing $files and $type.
|
||||||
|
*
|
||||||
|
* @see advagg_save_aggregate()
|
||||||
|
* @see advagg_advagg_save_aggregate_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_save_aggregate_alter(array &$files_to_save, array $aggregate_settings, array $other_parameters) {
|
||||||
|
// Return if gzip is disabled.
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_gzip'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if a .gz file already exists.
|
||||||
|
$gzip_exists = FALSE;
|
||||||
|
foreach ($files_to_save as $uri => $contents) {
|
||||||
|
// See if this uri contains .gz near the end of it.
|
||||||
|
$pos = strripos($uri, '.gz', 91 + strlen(ADVAGG_SPACE) * 3);
|
||||||
|
if (!empty($pos)) {
|
||||||
|
$len = strlen($uri);
|
||||||
|
// .gz file exists, exit loop.
|
||||||
|
if ($pos == $len - 3) {
|
||||||
|
$gzip_exists = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a .gz file does not exist, create one.
|
||||||
|
if (!$gzip_exists) {
|
||||||
|
// Use the first file in the array.
|
||||||
|
$data = reset($files_to_save);
|
||||||
|
$uri = key($files_to_save);
|
||||||
|
// Compress it and add it to the $files_to_save array.
|
||||||
|
$compressed = gzencode($data, 9, FORCE_GZIP);
|
||||||
|
$files_to_save[$uri . '.gz'] = $compressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to alter css and js paths.
|
||||||
|
*
|
||||||
|
* @param array $css_paths
|
||||||
|
* Array containing the local path and url path.
|
||||||
|
* @param array $js_paths
|
||||||
|
* Array containing the local path and url path.
|
||||||
|
*
|
||||||
|
* @see advagg_get_root_files_dir()
|
||||||
|
* @see advagg_mod_advagg_get_root_files_dir_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_get_root_files_dir(array &$css_paths, array &$js_paths) {
|
||||||
|
$dir = rtrim(variable_get('advagg_mod_unified_multisite_dir', ''), '/');
|
||||||
|
if (empty($dir) || !file_exists($dir) || !is_dir($dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Change directory.
|
||||||
|
$css_paths[0] = $dir . '/advagg_css';
|
||||||
|
$js_paths[0] = $dir . '/advagg_js';
|
||||||
|
|
||||||
|
file_prepare_directory($css_paths[0], FILE_CREATE_DIRECTORY);
|
||||||
|
file_prepare_directory($js_paths[0], FILE_CREATE_DIRECTORY);
|
||||||
|
|
||||||
|
// Set the URI of the directory.
|
||||||
|
$css_paths[1] = advagg_get_relative_path($css_paths[0]);
|
||||||
|
$js_paths[1] = advagg_get_relative_path($js_paths[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to modify this aggregates contents.
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* Raw CSS data.
|
||||||
|
* @param array $files
|
||||||
|
* List of files used to create this aggregate.
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* An associative array of hooks and settings used.
|
||||||
|
*
|
||||||
|
* @see advagg_get_css_aggregate_contents()
|
||||||
|
* @see advagg_css_compress_advagg_get_css_aggregate_contents_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_get_css_aggregate_contents_alter(&$data, array $files, array $aggregate_settings) {
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_css_compressor'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aggregate_settings['variables']['advagg_css_compressor'] == 2) {
|
||||||
|
advagg_css_compress_yui_cssmin($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to modify this aggregates contents.
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* Raw JS data.
|
||||||
|
* @param array $files
|
||||||
|
* List of files used to create this aggregate.
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* An associative array of hooks and settings used.
|
||||||
|
*
|
||||||
|
* @see advagg_get_js_aggregate_contents()
|
||||||
|
* @see advagg_js_compress_advagg_get_js_aggregate_contents_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_get_js_aggregate_contents_alter(&$data, array $files, array $aggregate_settings) {
|
||||||
|
// Do nothing if js file compression is disabled.
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_js_compressor'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compress it.
|
||||||
|
$filename = drupal_hash_base64(serialize($files));
|
||||||
|
advagg_js_compress_prep($data, $filename, $aggregate_settings, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to modify this files contents.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* Raw file data.
|
||||||
|
* @param string $file
|
||||||
|
* Filename.
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* An associative array of hooks and settings used.
|
||||||
|
*
|
||||||
|
* @see advagg_get_css_aggregate_contents()
|
||||||
|
* @see advagg_css_compress_advagg_get_css_aggregate_contents_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_get_css_file_contents_alter(&$contents, $file, array $aggregate_settings) {
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_css_compressor'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aggregate_settings['variables']['advagg_css_compressor'] == 2) {
|
||||||
|
advagg_css_compress_yui_cssmin($contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to modify this files contents.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* Raw file data.
|
||||||
|
* @param string $filename
|
||||||
|
* Filename.
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* An associative array of hooks and settings used.
|
||||||
|
*
|
||||||
|
* @see advagg_get_css_aggregate_contents()
|
||||||
|
* @see advagg_css_compress_advagg_get_css_aggregate_contents_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_get_js_file_contents_alter(&$contents, $filename, array $aggregate_settings) {
|
||||||
|
// Do nothing if js file compression is disabled.
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_js_compressor'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure this file has been tested.
|
||||||
|
$compressor = $aggregate_settings['variables']['advagg_js_compressor'];
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
$info = advagg_get_info_on_file($filename);
|
||||||
|
if (!isset($info['advagg_js_compress'][$compressor]['code'])) {
|
||||||
|
// Test file here on the spot.
|
||||||
|
$info = advagg_js_compress_run_test($filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compress it if it passes the test.
|
||||||
|
if (!empty($info['advagg_js_compress'][$compressor]['code']) && $info['advagg_js_compress'][$compressor]['code'] == 1) {
|
||||||
|
advagg_js_compress_prep($contents, $filename, $aggregate_settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to modify $css_groups right before it is processed.
|
||||||
|
*
|
||||||
|
* @param array $css_groups
|
||||||
|
* An associative array.
|
||||||
|
* key - group.
|
||||||
|
* @param bool $preprocess_css
|
||||||
|
* TRUE if preprocessing is enabled.
|
||||||
|
*
|
||||||
|
* @see _advagg_aggregate_css()
|
||||||
|
* @see advagg_css_cdn_advagg_css_groups_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_css_groups_alter(array &$css_groups, $preprocess_css) {
|
||||||
|
// Work around a bug with seven_css_alter.
|
||||||
|
// http://drupal.org/node/1937860
|
||||||
|
$theme_keys[] = $GLOBALS['theme'];
|
||||||
|
if (!empty($GLOBALS['base_theme_info'])) {
|
||||||
|
foreach ($GLOBALS['base_theme_info'] as $base) {
|
||||||
|
$theme_keys[] = $base->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$match = FALSE;
|
||||||
|
foreach ($theme_keys as $name) {
|
||||||
|
if ($name === 'seven') {
|
||||||
|
$match = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($match)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$target = FALSE;
|
||||||
|
$last_group = FALSE;
|
||||||
|
$last_key = FALSE;
|
||||||
|
$kill_key = FALSE;
|
||||||
|
$replaced = FALSE;
|
||||||
|
foreach ($css_groups as $key => $group) {
|
||||||
|
if (empty($target)) {
|
||||||
|
if ($group['type'] === 'external' && $group['preprocess'] && $preprocess_css) {
|
||||||
|
foreach ($group['items'] as $k => $value) {
|
||||||
|
if ($value['data'] === 'themes/seven/jquery.ui.theme.css') {
|
||||||
|
// Type should be file and not external (core bug).
|
||||||
|
$value['type'] = 'file';
|
||||||
|
$target = $value;
|
||||||
|
unset($css_groups[$key]['items'][$k]);
|
||||||
|
if (empty($css_groups[$key]['items'])) {
|
||||||
|
unset($css_groups[$key]);
|
||||||
|
$kill_key = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$diff = array_merge(array_diff_assoc($group['browsers'], $target['browsers']), array_diff_assoc($target['browsers'], $group['browsers']));
|
||||||
|
if ($group['type'] != $target['type']
|
||||||
|
|| $group['group'] != $target['group']
|
||||||
|
|| $group['every_page'] != $target['every_page']
|
||||||
|
|| $group['media'] != $target['media']
|
||||||
|
|| $group['media'] != $target['media']
|
||||||
|
|| $group['preprocess'] != $target['preprocess']
|
||||||
|
|| !empty($diff)
|
||||||
|
) {
|
||||||
|
if (!empty($last_group)) {
|
||||||
|
$diff = array_merge(array_diff_assoc($last_group['browsers'], $target['browsers']), array_diff_assoc($target['browsers'], $last_group['browsers']));
|
||||||
|
if ($last_group['type'] != $target['type']
|
||||||
|
|| $last_group['group'] != $target['group']
|
||||||
|
|| $last_group['every_page'] != $target['every_page']
|
||||||
|
|| $last_group['media'] != $target['media']
|
||||||
|
|| $last_group['media'] != $target['media']
|
||||||
|
|| $last_group['preprocess'] != $target['preprocess']
|
||||||
|
|| !empty($diff)
|
||||||
|
) {
|
||||||
|
// Insert New.
|
||||||
|
$css_groups[$kill_key] = array(
|
||||||
|
'group' => $target['group'],
|
||||||
|
'type' => $target['type'],
|
||||||
|
'every_page' => $target['every_page'],
|
||||||
|
'media' => $target['media'],
|
||||||
|
'preprocess' => $target['preprocess'],
|
||||||
|
'browsers' => $target['browsers'],
|
||||||
|
'items' => array($target),
|
||||||
|
);
|
||||||
|
$replaced = TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Insert above.
|
||||||
|
$css_groups[$last_key]['items'][] = $target;
|
||||||
|
$replaced = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Insert below.
|
||||||
|
array_unshift($css_groups[$key]['items'], $target);
|
||||||
|
$replaced = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$last_group = $group;
|
||||||
|
$last_key = $key;
|
||||||
|
if ($replaced) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ksort($css_groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to modify $js_groups right before it is processed.
|
||||||
|
*
|
||||||
|
* @param array $js_groups
|
||||||
|
* An associative array.
|
||||||
|
* key - group.
|
||||||
|
* @param bool $preprocess_js
|
||||||
|
* TRUE if preprocessing is enabled.
|
||||||
|
*
|
||||||
|
* @see _advagg_aggregate_js()
|
||||||
|
* @see labjs_advagg_js_groups_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_js_groups_alter(array &$js_groups, $preprocess_js) {
|
||||||
|
if (!$preprocess_js) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$labjs_location = labjs_get_path();
|
||||||
|
|
||||||
|
foreach ($js_groups as &$data) {
|
||||||
|
foreach ($data['items'] as &$values) {
|
||||||
|
if ($values['data'] == $labjs_location) {
|
||||||
|
// Strictly enforce preprocess = FALSE for labjs.
|
||||||
|
$values['preprocess'] = FALSE;
|
||||||
|
$data['preprocess'] = FALSE;
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($values);
|
||||||
|
}
|
||||||
|
unset($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to modify $children and $elements before rendering.
|
||||||
|
*
|
||||||
|
* @param array $children
|
||||||
|
* An array of children elements.
|
||||||
|
* @param array $elements
|
||||||
|
* A render array containing:
|
||||||
|
* - #items: The CSS items as returned by drupal_add_css() and
|
||||||
|
* altered by drupal_get_css().
|
||||||
|
* - #group_callback: A function to call to group #items. Following
|
||||||
|
* this function, #aggregate_callback is called to aggregate items within
|
||||||
|
* the same group into a single file.
|
||||||
|
* - #aggregate_callback: A function to call to aggregate the items within
|
||||||
|
* the groups arranged by the #group_callback function.
|
||||||
|
*
|
||||||
|
* @see advagg_modify_css_pre_render()
|
||||||
|
* @see advagg_css_compress_advagg_modify_css_pre_render_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_modify_css_pre_render_alter(array &$children, array &$elements) {
|
||||||
|
// Get variables.
|
||||||
|
$compressor = variable_get('advagg_css_compress_inline', ADVAGG_CSS_COMPRESS_INLINE);
|
||||||
|
|
||||||
|
// Do nothing if the compressor is disabled.
|
||||||
|
if (empty($compressor)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing if the page is not cacheable and inline compress if not
|
||||||
|
// cacheable is not checked.
|
||||||
|
if (!variable_get('advagg_css_compress_inline_if_not_cacheable', ADVAGG_CSS_COMPRESS_INLINE_IF_NOT_CACHEABLE) && !drupal_page_is_cacheable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_load_include('inc', 'advagg_css_compress', 'advagg_css_compress.advagg');
|
||||||
|
if ($compressor == 2) {
|
||||||
|
// Compress any inline CSS with YUI.
|
||||||
|
foreach ($children as &$values) {
|
||||||
|
if (!empty($values['#value'])) {
|
||||||
|
advagg_css_compress_yui_cssmin($values['#value']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to modify $children and $elements before rendering.
|
||||||
|
*
|
||||||
|
* @param array $children
|
||||||
|
* An array of children elements.
|
||||||
|
* @param array $elements
|
||||||
|
* A render array containing:
|
||||||
|
* - #items: The JavaScript items as returned by drupal_add_js() and
|
||||||
|
* altered by drupal_get_js().
|
||||||
|
* - #group_callback: A function to call to group #items. Following
|
||||||
|
* this function, #aggregate_callback is called to aggregate items within
|
||||||
|
* the same group into a single file.
|
||||||
|
* - #aggregate_callback: A function to call to aggregate the items within
|
||||||
|
* the groups arranged by the #group_callback function.
|
||||||
|
*
|
||||||
|
* @see advagg_modify_js_pre_render()
|
||||||
|
* @see advagg_js_compress_advagg_modify_js_pre_render_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_modify_js_pre_render_alter(array &$children, array &$elements) {
|
||||||
|
// Get variables.
|
||||||
|
$aggregate_settings['variables']['advagg_js_compressor'] = variable_get('advagg_js_compress_inline', ADVAGG_JS_COMPRESS_INLINE);
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_max_ratio'] = variable_get('advagg_js_compress_max_ratio', ADVAGG_JS_COMPRESS_MAX_RATIO);
|
||||||
|
|
||||||
|
// Do nothing if the compressor is disabled.
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_js_compressor'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing if the page is not cacheable and inline compress if not
|
||||||
|
// cacheable is not checked.
|
||||||
|
if (!variable_get('advagg_js_compress_inline_if_not_cacheable', ADVAGG_JS_COMPRESS_INLINE_IF_NOT_CACHEABLE) && !drupal_page_is_cacheable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compress any inline JS.
|
||||||
|
module_load_include('inc', 'advagg_js_compress', 'advagg_js_compress.advagg');
|
||||||
|
foreach ($children as &$values) {
|
||||||
|
if (!empty($values['#value'])) {
|
||||||
|
$contents = $values['#value'];
|
||||||
|
$filename = drupal_hash_base64($contents);
|
||||||
|
advagg_js_compress_prep($contents, $filename, $aggregate_settings, FALSE);
|
||||||
|
$values['#value'] = $contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow other modules to swap important contextual information on generation.
|
||||||
|
*
|
||||||
|
* @param array $original
|
||||||
|
* Array of original settings.
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* Array of contextual settings.
|
||||||
|
* @param int $mode
|
||||||
|
* Use 0 to change context to what is inside of $aggregate_settings.
|
||||||
|
* Use 1 to change context back.
|
||||||
|
*
|
||||||
|
* @see advagg_context_switch()
|
||||||
|
* @see advagg_advagg_context_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_context_alter(array &$original, array $aggregate_settings, $mode) {
|
||||||
|
if ($mode == 0) {
|
||||||
|
// Change context.
|
||||||
|
$original['base_root'] = $GLOBALS['base_root'];
|
||||||
|
$original['base_url'] = $GLOBALS['base_url'];
|
||||||
|
$original['base_path'] = $GLOBALS['base_path'];
|
||||||
|
$original['is_https'] = $GLOBALS['is_https'];
|
||||||
|
$GLOBALS['is_https'] = $aggregate_settings['variables']['is_https'];
|
||||||
|
if ($aggregate_settings['variables']['is_https']) {
|
||||||
|
$GLOBALS['base_root'] = str_replace('http://', 'https://', $GLOBALS['base_root']);
|
||||||
|
$GLOBALS['base_url'] = str_replace('http://', 'https://', $GLOBALS['base_url']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$GLOBALS['base_root'] = str_replace('https://', 'http://', $GLOBALS['base_root']);
|
||||||
|
$GLOBALS['base_url'] = str_replace('https://', 'http://', $GLOBALS['base_url']);
|
||||||
|
}
|
||||||
|
$GLOBALS['base_path'] = $aggregate_settings['variables']['base_path'];
|
||||||
|
}
|
||||||
|
elseif ($mode == 1) {
|
||||||
|
// Change context back.
|
||||||
|
if (isset($original['base_root'])) {
|
||||||
|
$GLOBALS['base_root'] = $original['base_root'];
|
||||||
|
}
|
||||||
|
if (isset($original['base_url'])) {
|
||||||
|
$GLOBALS['base_url'] = $original['base_url'];
|
||||||
|
}
|
||||||
|
if (isset($original['base_path'])) {
|
||||||
|
$GLOBALS['base_path'] = $original['base_path'];
|
||||||
|
}
|
||||||
|
if (isset($original['is_https'])) {
|
||||||
|
$GLOBALS['is_https'] = $original['is_https'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let other modules know about the aggregate files that have been removed.
|
||||||
|
*
|
||||||
|
* @param array $kill_list
|
||||||
|
* An array of aggregate files that have been removed.
|
||||||
|
*
|
||||||
|
* @see advagg_delete_files_if_stale()
|
||||||
|
*/
|
||||||
|
function hook_advagg_removed_aggregates(array $kill_list) {
|
||||||
|
foreach ($kill_list as $uri) {
|
||||||
|
if ($uri) {
|
||||||
|
// This is the uri.
|
||||||
|
}
|
||||||
|
// Do something else.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let other modules tell advagg that a file has changed.
|
||||||
|
*
|
||||||
|
* Useful for things like embedded images in CSS; generating a new aggregate
|
||||||
|
* when the image in the CSS file has changed.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* Name of the root CSS or JavaScript file.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* Set to TRUE to trigger a rebuild of the aggregates that contain this file.
|
||||||
|
*
|
||||||
|
* @see advagg_scan_for_changes()
|
||||||
|
* @see css_emimage_advagg_scan_for_changes()
|
||||||
|
*/
|
||||||
|
function hook_advagg_scan_for_changes($filename) {
|
||||||
|
if ($filename) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let other modules add/alter additional information about files passed in.
|
||||||
|
*
|
||||||
|
* @param array $return
|
||||||
|
* An associative array; filename -> data.
|
||||||
|
* @param array $cached_data
|
||||||
|
* What data was found in the cache; cache_id -> data.
|
||||||
|
* @param bool $bypass_cache
|
||||||
|
* If TRUE the loaded data did not come from the cache.
|
||||||
|
*
|
||||||
|
* @see advagg_get_info_on_files()
|
||||||
|
* @see advagg_advagg_get_info_on_files_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_get_info_on_files_alter(array &$return, array $cached_data, $bypass_cache) {
|
||||||
|
if (!variable_get('advagg_ie_css_selector_limiter', ADVAGG_IE_CSS_SELECTOR_LIMITER)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$limit_value = variable_get('advagg_ie_css_selector_limiter_value', ADVAGG_IE_CSS_SELECTOR_LIMITER_VALUE);
|
||||||
|
list($css_path, $js_path) = advagg_get_root_files_dir();
|
||||||
|
if ($js_path) {
|
||||||
|
// This is the js_path array.
|
||||||
|
}
|
||||||
|
$parts_path = $css_path[1] . '/parts';
|
||||||
|
|
||||||
|
foreach ($return as $filename => &$info) {
|
||||||
|
if ($filename) {
|
||||||
|
// This is the filename.
|
||||||
|
}
|
||||||
|
if (empty($info['fileext']) || $info['fileext'] !== 'css') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break large file into multiple small files.
|
||||||
|
if ($info['linecount'] > $limit_value) {
|
||||||
|
advagg_split_css_file($info);
|
||||||
|
}
|
||||||
|
elseif (strpos($info['data'], $parts_path) === 0) {
|
||||||
|
$info['split'] = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell advagg about other hooks related to advagg.
|
||||||
|
*
|
||||||
|
* @param array $hooks
|
||||||
|
* Array of hooks related to advagg.
|
||||||
|
* @param bool $all
|
||||||
|
* If FALSE get only the subset of hooks that alter the filename/contents.
|
||||||
|
*
|
||||||
|
* @see advagg_hooks_implemented()
|
||||||
|
* @see advagg_bundler_advagg_hooks_implemented_alter()
|
||||||
|
*/
|
||||||
|
function hook_advagg_hooks_implemented_alter(array &$hooks, $all) {
|
||||||
|
if ($all) {
|
||||||
|
$hooks['advagg_bundler_analysis_alter'] = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let other modules modify the analysis array before it is used.
|
||||||
|
*
|
||||||
|
* @param array $analysis
|
||||||
|
* An associative array; filename -> data.
|
||||||
|
*
|
||||||
|
* @see advagg_bundler_analysis()
|
||||||
|
*/
|
||||||
|
function hook_advagg_bundler_analysis_alter(array &$analysis) {
|
||||||
|
foreach ($analysis as $filename => &$data) {
|
||||||
|
if ($filename) {
|
||||||
|
// This is the filename.
|
||||||
|
}
|
||||||
|
|
||||||
|
// This changes often; 604800 is 1 week.
|
||||||
|
if ($data['changes'] > 10 && $data['mtime'] >= REQUEST_TIME - 604800) {
|
||||||
|
// Modify the group hash so this doesn't end up in a big aggregate.
|
||||||
|
$data['group_hash'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "defgroup advagg_hooks".
|
||||||
|
*/
|
945
frontend/drupal/sites/all/modules/advagg/advagg.cache.inc
Normal file
945
frontend/drupal/sites/all/modules/advagg/advagg.cache.inc
Normal file
@ -0,0 +1,945 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced CSS/JS aggregation module.
|
||||||
|
*
|
||||||
|
* Functions used for clearing caches and killing files.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the database to scan CSS/JS files for changes.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of files that have changed.
|
||||||
|
*/
|
||||||
|
function advagg_scan_for_changes() {
|
||||||
|
// Get all files stored in the database and filesystem.
|
||||||
|
$files_that_have_changed = array();
|
||||||
|
$result = db_select('advagg_files', 'af')
|
||||||
|
->fields('af')
|
||||||
|
->execute();
|
||||||
|
if (!empty($result)) {
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
$filenames = array();
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
foreach ($result as $row) {
|
||||||
|
$filenames[] = $row->filename;
|
||||||
|
$data[$row->filename] = (array) $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get filesystem data.
|
||||||
|
$files_info = advagg_get_info_on_files($filenames, TRUE);
|
||||||
|
foreach ($files_info as $info) {
|
||||||
|
if (!isset($data[$info['data']])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$row = $data[$info['data']];
|
||||||
|
|
||||||
|
// Select the keys to compare.
|
||||||
|
$keys_to_compare = array(
|
||||||
|
'filesize',
|
||||||
|
'content_hash',
|
||||||
|
'linecount',
|
||||||
|
);
|
||||||
|
$changed = array();
|
||||||
|
foreach ($keys_to_compare as $key) {
|
||||||
|
if ($row[$key] != $info[$key]) {
|
||||||
|
$changed[] = $key . ' db:' . $row[$key] . ' file:' . $info[$key];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Compare mtime if it is not zero.
|
||||||
|
if (empty($info['split']) && !empty($info['mtime'])) {
|
||||||
|
if (variable_get('advagg_strict_mtime_check', ADVAGG_STRICT_MTIME_CHECK) && $row['mtime'] != $info['mtime']) {
|
||||||
|
$changed[] = 'mtime db:' . $row['mtime'] . ' file:' . $info['mtime'];
|
||||||
|
}
|
||||||
|
elseif ($row['mtime'] < $info['mtime']) {
|
||||||
|
$changed[] = 'mtime db:' . $row['mtime'] . ' file:' . $info['mtime'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($changed)) {
|
||||||
|
// Call hook_advagg_scan_for_changes().
|
||||||
|
$changes_array = module_invoke_all('advagg_scan_for_changes', $row['filename']);
|
||||||
|
if (is_array($changes_array)) {
|
||||||
|
foreach ($changes_array as $value) {
|
||||||
|
if (!empty($value)) {
|
||||||
|
$changed[] = $value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If file has changed, add it to the array.
|
||||||
|
if (!empty($changed)) {
|
||||||
|
$info['changes'] = $changed;
|
||||||
|
$files_that_have_changed[$row['filename']] = $info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $files_that_have_changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the correct caches so CSS/JS changes go live.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of files that have changed and caches flushed.
|
||||||
|
*/
|
||||||
|
function advagg_push_new_changes(array $files = array()) {
|
||||||
|
$results = array();
|
||||||
|
// Scan the file system for changes to CSS/JS files.
|
||||||
|
if (empty($files)) {
|
||||||
|
$files = advagg_scan_for_changes();
|
||||||
|
if (variable_get('advagg_debug', ADVAGG_DEBUG) >= 2) {
|
||||||
|
watchdog('advagg-debug', 'Changes detected in <pre>@files</pre>.', array('@files' => print_r($files, TRUE)), WATCHDOG_DEBUG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear some static caches.
|
||||||
|
drupal_static_reset('advagg_get_info_on_file');
|
||||||
|
drupal_static_reset('advagg_drupal_hash_base64');
|
||||||
|
drupal_static_reset('advagg_current_hooks_hash_array');
|
||||||
|
drupal_static_reset('advagg_get_current_hooks_hash');
|
||||||
|
|
||||||
|
if (variable_get('advagg_debug', ADVAGG_DEBUG) >= 2) {
|
||||||
|
// Exception used to get a compact stack trace.
|
||||||
|
$e = new Exception();
|
||||||
|
watchdog('advagg-debug', 'New changes called by: <pre>@changes</pre>', array('@changes' => print_r($e->getTraceAsString(), TRUE)), WATCHDOG_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If something changed, flush the correct caches so that change goes out.
|
||||||
|
if (!empty($files)) {
|
||||||
|
$types = array();
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
foreach ($files as $filename => $meta_data) {
|
||||||
|
// Lookup the aggregates/cache ids that use this file.
|
||||||
|
$cache_ids = advagg_get_aggregates_using_file($meta_data['filename_hash'], TRUE);
|
||||||
|
$cache_hits = array();
|
||||||
|
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||||
|
$types[$ext] = TRUE;
|
||||||
|
|
||||||
|
if (!empty($cache_ids)) {
|
||||||
|
$cache_hits = cache_get_multiple($cache_ids, 'cache_advagg_info');
|
||||||
|
foreach ($cache_hits as $cid => $data) {
|
||||||
|
if (variable_get('advagg_debug', ADVAGG_DEBUG) >= 2) {
|
||||||
|
watchdog('advagg-debug', 'Clearing cache @cid.', array('@cid' => $cid), WATCHDOG_DEBUG);
|
||||||
|
}
|
||||||
|
cache_clear_all($cid, 'cache_advagg_info', FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$changes = array();
|
||||||
|
if (!empty($meta_data['changes'])) {
|
||||||
|
$changes = $meta_data['changes'];
|
||||||
|
unset($meta_data['changes']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$results[$filename] = array(
|
||||||
|
count($cache_ids),
|
||||||
|
count($cache_hits),
|
||||||
|
$changes,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update database.
|
||||||
|
advagg_insert_update_files(array($filename => $meta_data), $ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change query-strings on css/js files to enforce reload for all users.
|
||||||
|
// Change css_js_query_string variable.
|
||||||
|
_drupal_flush_css_js();
|
||||||
|
|
||||||
|
// Let other modules know about the changed files.
|
||||||
|
// Call hook_advagg_changed_files().
|
||||||
|
module_invoke_all('advagg_changed_files', $files, $types);
|
||||||
|
|
||||||
|
// Clear out the full aggregates cache.
|
||||||
|
foreach ($types as $ext => $bool) {
|
||||||
|
if (variable_get('advagg_debug', ADVAGG_DEBUG) >= 2) {
|
||||||
|
watchdog('advagg-debug', 'Clearing cache advagg:@ext: in cache_advagg_aggregates.', array('@ext' => print_r($ext, TRUE)), WATCHDOG_DEBUG);
|
||||||
|
}
|
||||||
|
cache_clear_all('advagg:' . $ext . ':', 'cache_advagg_aggregates', TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return what was done.
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a filename hash get back all aggregates that include it.
|
||||||
|
*
|
||||||
|
* @param string $filename_hash
|
||||||
|
* Hash of the filename.
|
||||||
|
* @param bool $cid_only
|
||||||
|
* Set to TRUE to only have cache ids returned.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of aggregates that use this file.
|
||||||
|
*/
|
||||||
|
function advagg_get_aggregates_using_file($filename_hash, $cid_only = FALSE) {
|
||||||
|
// Create main query for the advagg_aggregates table.
|
||||||
|
$query = db_select('advagg_aggregates', 'aa')
|
||||||
|
->condition('aa.filename_hash', $filename_hash);
|
||||||
|
// Create join query for the advagg_aggregates_versions table.
|
||||||
|
$query->join('advagg_aggregates_versions', 'aav', 'aa.aggregate_filenames_hash = aav.aggregate_filenames_hash AND aav.atime > 0');
|
||||||
|
$query = $query->fields('aav', array('aggregate_filenames_hash', 'aggregate_contents_hash'));
|
||||||
|
$query->comment('Query called from ' . __FUNCTION__ . '()');
|
||||||
|
$results = $query->execute();
|
||||||
|
|
||||||
|
// Put results into $aggregates array.
|
||||||
|
$aggregates = array();
|
||||||
|
foreach ($results as $row) {
|
||||||
|
$row = (array) $row;
|
||||||
|
$cid = 'advagg:db:' . $row['aggregate_filenames_hash'] . ADVAGG_SPACE . $row['aggregate_contents_hash'];
|
||||||
|
if ($cid_only) {
|
||||||
|
$aggregates[] = $cid;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$row['cid'] = $cid;
|
||||||
|
$aggregates[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $aggregates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all CSS/JS advagg files.
|
||||||
|
*
|
||||||
|
* @param array $options
|
||||||
|
* Array of options to pass along to file_scan_directory().
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of css and js files.
|
||||||
|
*/
|
||||||
|
function advagg_get_all_files(array $options = array()) {
|
||||||
|
list($css_path, $js_path) = advagg_get_root_files_dir();
|
||||||
|
$options += array('nomask' => '/(\.\.?|CVS|\.gz|\.br)$/');
|
||||||
|
|
||||||
|
// Get a list of files.
|
||||||
|
$css_files = file_scan_directory($css_path[0], '/.*/', $options);
|
||||||
|
$js_files = file_scan_directory($js_path[0], '/.*/', $options);
|
||||||
|
return array($css_files, $js_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan CSS/JS advagg dir and remove that file if atime is grater than 30 days.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of files that got removed.
|
||||||
|
*/
|
||||||
|
function advagg_delete_stale_aggregates() {
|
||||||
|
list($css_files, $js_files) = advagg_get_all_files();
|
||||||
|
|
||||||
|
// Make the advagg_get_hashes_from_filename() function available.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg.missing');
|
||||||
|
$css_files = advagg_delete_files_if_stale($css_files);
|
||||||
|
$js_files = advagg_delete_files_if_stale($js_files);
|
||||||
|
return array($css_files, $js_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of files remove that file if atime is grater than 30 days.
|
||||||
|
*
|
||||||
|
* @param array $files
|
||||||
|
* Array of files returned by file_scan_directory.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of files that got removed.
|
||||||
|
*/
|
||||||
|
function advagg_delete_files_if_stale(array $files) {
|
||||||
|
// Array used to record what files were deleted.
|
||||||
|
$kill_list = array();
|
||||||
|
|
||||||
|
foreach ($files as $uri => $file) {
|
||||||
|
// Get info on file.
|
||||||
|
$filename = $file->filename;
|
||||||
|
$data = advagg_get_hashes_from_filename($filename);
|
||||||
|
if (is_array($data)) {
|
||||||
|
list(, $aggregate_filenames_hash, $aggregate_contents_hash) = $data;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Can not get data on file, remove it.
|
||||||
|
$kill_list[] = advagg_delete_file_by_uri($uri);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get atime of file.
|
||||||
|
$atime = advagg_get_atime($aggregate_filenames_hash, $aggregate_contents_hash, $uri);
|
||||||
|
if (empty($atime)) {
|
||||||
|
$kill_list[] = advagg_delete_file_by_uri($uri);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default stale file threshold is 30 days.
|
||||||
|
if (REQUEST_TIME - $atime > variable_get('drupal_stale_file_threshold', 2592000)) {
|
||||||
|
$kill_list[] = advagg_delete_file_by_uri($uri);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// Let other modules know about the removed files.
|
||||||
|
// Call hook_advagg_removed_aggregates().
|
||||||
|
module_invoke_all('advagg_removed_aggregates', $kill_list);
|
||||||
|
return $kill_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan CSS/JS advagg dir and remove that file if it is empty.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of files that got removed.
|
||||||
|
*/
|
||||||
|
function advagg_delete_empty_aggregates() {
|
||||||
|
list($css_files, $js_files) = advagg_get_all_files();
|
||||||
|
$css_files = advagg_delete_files_if_empty($css_files);
|
||||||
|
$js_files = advagg_delete_files_if_empty($js_files);
|
||||||
|
return array($css_files, $js_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of files remove that file if it is empty.
|
||||||
|
*
|
||||||
|
* @param array $files
|
||||||
|
* Array of files returned by file_scan_directory.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of files that got removed.
|
||||||
|
*/
|
||||||
|
function advagg_delete_files_if_empty(array $files) {
|
||||||
|
// Array used to record what files were deleted.
|
||||||
|
$kill_list = array();
|
||||||
|
|
||||||
|
foreach ($files as $uri => $file) {
|
||||||
|
// Ignore temp files. There's a separate process for cleaning those up.
|
||||||
|
if (strpos($uri, '/advagg_file_') !== FALSE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$size = filesize($uri);
|
||||||
|
if ($size === 0) {
|
||||||
|
$kill_list[] = advagg_delete_file_by_uri($uri);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Let other modules know about the removed files.
|
||||||
|
// Call hook_advagg_removed_aggregates().
|
||||||
|
module_invoke_all('advagg_removed_aggregates', $kill_list);
|
||||||
|
return $kill_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a file, and any compressed versions.
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
* URI of the file to delete.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The given URI.
|
||||||
|
*/
|
||||||
|
function advagg_delete_file_by_uri($uri) {
|
||||||
|
if (file_exists($uri)) {
|
||||||
|
file_unmanaged_delete($uri);
|
||||||
|
}
|
||||||
|
if (file_exists($uri . '.gz')) {
|
||||||
|
file_unmanaged_delete($uri . '.gz');
|
||||||
|
}
|
||||||
|
if (file_exists($uri . '.br')) {
|
||||||
|
file_unmanaged_delete($uri . '.br');
|
||||||
|
}
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a cache_clear_all on all bins returned by advagg_flush_caches(TRUE).
|
||||||
|
*
|
||||||
|
* @param bool $push_new_changes
|
||||||
|
* FALSE: Do not scan for changes.
|
||||||
|
*/
|
||||||
|
function advagg_flush_all_cache_bins($push_new_changes = TRUE) {
|
||||||
|
$bins = advagg_flush_caches(TRUE, $push_new_changes);
|
||||||
|
foreach ($bins as $bin) {
|
||||||
|
cache_clear_all('*', $bin, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all files from the advagg CSS/JS directories.
|
||||||
|
*
|
||||||
|
* @param bool $kill_htaccess
|
||||||
|
* Set to TRUE to remove the htaccess files as well.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of all files removed.
|
||||||
|
*/
|
||||||
|
function advagg_remove_all_aggregated_files($kill_htaccess = FALSE) {
|
||||||
|
$options = array(
|
||||||
|
'callback' => 'file_unmanaged_delete',
|
||||||
|
'nomask' => '/(\.\.?|CVS)$/',
|
||||||
|
);
|
||||||
|
list($css_files, $js_files) = advagg_get_all_files($options);
|
||||||
|
// Let other modules know about the removed files.
|
||||||
|
// Call hook_advagg_removed_aggregates().
|
||||||
|
module_invoke_all('advagg_removed_aggregates', $css_files);
|
||||||
|
module_invoke_all('advagg_removed_aggregates', $js_files);
|
||||||
|
|
||||||
|
// Remove the htaccess files as well.
|
||||||
|
if ($kill_htaccess) {
|
||||||
|
list($css_path, $js_path) = advagg_get_root_files_dir();
|
||||||
|
if (file_exists($css_path[0] . '/.htaccess')) {
|
||||||
|
file_unmanaged_delete($css_path[0] . '/.htaccess');
|
||||||
|
$css_files[] = $css_path[0] . '/.htaccess';
|
||||||
|
}
|
||||||
|
if (file_exists($js_path[0] . '/.htaccess')) {
|
||||||
|
file_unmanaged_delete($js_path[0] . '/.htaccess');
|
||||||
|
$js_files[] = $js_path[0] . '/.htaccess';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array($css_files, $js_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the advagg_global_counter variable by one.
|
||||||
|
*
|
||||||
|
* @todo Allow this value to be kept in sync across a multisite.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* New value of advagg_global_counter.
|
||||||
|
*/
|
||||||
|
function advagg_increment_global_counter() {
|
||||||
|
$new_value = advagg_get_global_counter() + 1;
|
||||||
|
variable_set('advagg_global_counter', $new_value);
|
||||||
|
return $new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan for missing files and remove the associated entries in the database.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of what files were cleared out of the database.
|
||||||
|
*/
|
||||||
|
function advagg_remove_missing_files_from_db() {
|
||||||
|
$missing_files = array();
|
||||||
|
$deleted = array();
|
||||||
|
|
||||||
|
// Get all files stored in the database.
|
||||||
|
$result = db_select('advagg_files', 'af')
|
||||||
|
->fields('af')
|
||||||
|
->execute();
|
||||||
|
if (empty($result)) {
|
||||||
|
return $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find missing files.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
foreach ($result as $row) {
|
||||||
|
$row = (array) $row;
|
||||||
|
$info = advagg_get_info_on_file($row['filename'], TRUE);
|
||||||
|
|
||||||
|
// Make sure file exists.
|
||||||
|
if (empty($info['content_hash'])) {
|
||||||
|
$info += advagg_get_aggregates_using_file($info['filename_hash']);
|
||||||
|
$missing_files[$row['filename']] = $info;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($missing_files)) {
|
||||||
|
return $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove missing file database entries.
|
||||||
|
$types = array();
|
||||||
|
foreach ($missing_files as $filename => $data) {
|
||||||
|
// Setup this run.
|
||||||
|
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||||
|
$advagg_files_del = 0;
|
||||||
|
$advagg_aggregates_del = 0;
|
||||||
|
$advagg_aggregates_versions_del = 0;
|
||||||
|
$clean_sweep = TRUE;
|
||||||
|
$filename_hash = '';
|
||||||
|
|
||||||
|
// Scan the data.
|
||||||
|
foreach ($data as $key => $values) {
|
||||||
|
if (!is_numeric($key)) {
|
||||||
|
$filename_hash = $values;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Remove the entry from the database if this aggregate has not been
|
||||||
|
// accessed in the last 2 weeks.
|
||||||
|
$can_delete = db_delete('advagg_aggregates_versions')
|
||||||
|
->condition('aggregate_filenames_hash', $values['aggregate_filenames_hash'])
|
||||||
|
->condition('atime', REQUEST_TIME - variable_get('advagg_remove_missing_files_from_db_time', ADVAGG_REMOVE_MISSING_FILES_FROM_DB_TIME), '<')
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
if ($can_delete > 0) {
|
||||||
|
$advagg_aggregates_versions_del += $can_delete;
|
||||||
|
$advagg_aggregates_del += db_delete('advagg_aggregates')
|
||||||
|
->condition('aggregate_filenames_hash', $values['aggregate_filenames_hash'])
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$clean_sweep = FALSE;
|
||||||
|
}
|
||||||
|
// Clear the cache.
|
||||||
|
cache_clear_all($values['cid'], 'cache_advagg_info', FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the file entry if all aggregates referencing it have been removed.
|
||||||
|
if ($clean_sweep) {
|
||||||
|
$advagg_files_del += db_delete('advagg_files')
|
||||||
|
->condition('filename_hash', $filename_hash)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
// Add info to array.
|
||||||
|
if (!empty($advagg_files_del)
|
||||||
|
|| !empty($advagg_aggregates_versions_del)
|
||||||
|
|| !empty($advagg_aggregates_del)
|
||||||
|
) {
|
||||||
|
$types[$ext] = TRUE;
|
||||||
|
$deleted[$filename] = array(
|
||||||
|
'advagg_files' => $advagg_files_del,
|
||||||
|
'advagg_aggregates_versions' => $advagg_aggregates_versions_del,
|
||||||
|
'advagg_aggregates' => $advagg_aggregates_del,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If something was deleted, clear the full aggregates cache.
|
||||||
|
if (!empty($deleted)) {
|
||||||
|
foreach ($types as $ext => $bool) {
|
||||||
|
cache_clear_all('advagg:' . $ext . ':', 'cache_advagg_aggregates', TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return what was deleted.
|
||||||
|
return $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan CSS/JS advagg dir and remove file if there is no associated db record.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of files that got removed.
|
||||||
|
*/
|
||||||
|
function advagg_delete_orphaned_aggregates() {
|
||||||
|
list($css_files, $js_files) = advagg_get_all_files();
|
||||||
|
|
||||||
|
// Make the advagg_get_hashes_from_filename() function available.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg.missing');
|
||||||
|
$css_files = advagg_delete_files_if_orphaned($css_files);
|
||||||
|
$js_files = advagg_delete_files_if_orphaned($js_files);
|
||||||
|
return array($css_files, $js_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of files remove that file if there is no associated db record.
|
||||||
|
*
|
||||||
|
* @param array $files
|
||||||
|
* Array of files returned by file_scan_directory.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of files that got removed.
|
||||||
|
*/
|
||||||
|
function advagg_delete_files_if_orphaned(array $files) {
|
||||||
|
// Get the uri for the advagg_css/parts directory.
|
||||||
|
list($css_path) = advagg_get_root_files_dir();
|
||||||
|
$parts_uri = $css_path[0] . '/parts/';
|
||||||
|
|
||||||
|
// Array used to record what files were deleted.
|
||||||
|
$kill_list = $keyed_file_list = array();
|
||||||
|
|
||||||
|
// Create a listing of all file names and associated hashes.
|
||||||
|
foreach ($files as $uri => $file) {
|
||||||
|
// Get info on file.
|
||||||
|
$data = advagg_get_hashes_from_filename($file->filename, TRUE);
|
||||||
|
if (is_array($data)) {
|
||||||
|
list(, $aggregate_filenames_hash) = $data;
|
||||||
|
// Check to see if the file is in the database.
|
||||||
|
$keyed_file_list[$aggregate_filenames_hash] = $uri;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Check to see if this is a parts css file.
|
||||||
|
$start = strpos($file->uri, $parts_uri);
|
||||||
|
if ($start !== FALSE) {
|
||||||
|
// Get the original filename.
|
||||||
|
$original_file = substr($file->uri, $start + strlen($parts_uri));
|
||||||
|
$original_file = preg_replace('/(.\\d+\\.css)$/i', '.css', $original_file);
|
||||||
|
if (file_exists($original_file)) {
|
||||||
|
// Original file exists, do not delete.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can not get data on file, remove it.
|
||||||
|
$kill_list[] = $uri;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($keyed_file_list)) {
|
||||||
|
$filenames_hash = array_keys($keyed_file_list);
|
||||||
|
$aggregates_in_database = array();
|
||||||
|
// Process in chunks when a large array is passed.
|
||||||
|
do {
|
||||||
|
// Check if the aggregate_filenames_hash exists in the database.
|
||||||
|
$aggregates_in_database += db_select('advagg_aggregates_versions', 'av')
|
||||||
|
->fields('av', array('aggregate_filenames_hash'))
|
||||||
|
->condition('av.aggregate_filenames_hash', array_splice($filenames_hash, 0, 1000), 'IN')
|
||||||
|
->distinct()
|
||||||
|
->execute()
|
||||||
|
->fetchAllAssoc('aggregate_filenames_hash');
|
||||||
|
} while (count($filenames_hash));
|
||||||
|
|
||||||
|
// Get values not found in the database.
|
||||||
|
$to_delete = array_values(array_diff_key($keyed_file_list, $aggregates_in_database));
|
||||||
|
// Add the file uri to the kill list.
|
||||||
|
$kill_list = array_merge($kill_list, $to_delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($kill_list)) {
|
||||||
|
foreach ($kill_list as $uri) {
|
||||||
|
advagg_delete_file_by_uri($uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let other modules know about the removed files.
|
||||||
|
// Call hook_advagg_removed_aggregates().
|
||||||
|
module_invoke_all('advagg_removed_aggregates', $kill_list);
|
||||||
|
return $kill_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete aggregates that have not been accessed in the last 6 weeks.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* Count of the number of rows removed from the databases.
|
||||||
|
*/
|
||||||
|
function advagg_remove_old_unused_aggregates() {
|
||||||
|
$advagg_aggregates_versions_del = 0;
|
||||||
|
$advagg_aggregates_del = 0;
|
||||||
|
|
||||||
|
// Find orphaned aggregate versions entries.
|
||||||
|
// Create main query.
|
||||||
|
$query = db_select('advagg_aggregates_versions', 'aav')
|
||||||
|
->fields('aav', array('aggregate_filenames_hash'))
|
||||||
|
->groupBy('aav.aggregate_filenames_hash');
|
||||||
|
// Create join and add in query comment.
|
||||||
|
$query->leftjoin('advagg_aggregates', 'aa', 'aa.aggregate_filenames_hash=aav.aggregate_filenames_hash');
|
||||||
|
$query->isNull('aa.aggregate_filenames_hash');
|
||||||
|
$query->comment('Query called from ' . __FUNCTION__ . '()');
|
||||||
|
$results = $query->execute();
|
||||||
|
// If we have an orphaned db entry, delete it.
|
||||||
|
if (!empty($results)) {
|
||||||
|
foreach ($results as $row) {
|
||||||
|
$advagg_aggregates_versions_del += db_delete('advagg_aggregates_versions')
|
||||||
|
->condition('aggregate_filenames_hash', $row->aggregate_filenames_hash)
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete aggregate versions that have not been accessed in the last 45 days.
|
||||||
|
$advagg_aggregates_versions_del += db_delete('advagg_aggregates_versions')
|
||||||
|
->condition('atime', REQUEST_TIME - variable_get('advagg_remove_old_unused_aggregates_time', ADVAGG_REMOVE_OLD_UNUSED_AGGREGATES_TIME), '<')
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
// See if any aggregates are orphaned now.
|
||||||
|
// Create main query.
|
||||||
|
$query = db_select('advagg_aggregates', 'aa')
|
||||||
|
->fields('aa', array('aggregate_filenames_hash'))
|
||||||
|
->groupBy('aa.aggregate_filenames_hash');
|
||||||
|
// Create join and add in query comment.
|
||||||
|
$query->leftjoin('advagg_aggregates_versions', 'aav', 'aa.aggregate_filenames_hash=aav.aggregate_filenames_hash');
|
||||||
|
$query->isNull('aav.aggregate_filenames_hash');
|
||||||
|
$query->comment('Query called from ' . __FUNCTION__ . '()');
|
||||||
|
$results = $query->execute();
|
||||||
|
|
||||||
|
// If we have an orphaned db entry, delete it.
|
||||||
|
if (!empty($results)) {
|
||||||
|
foreach ($results as $row) {
|
||||||
|
$advagg_aggregates_del += db_delete('advagg_aggregates')
|
||||||
|
->condition('aggregate_filenames_hash', $row->aggregate_filenames_hash)
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the total count of entires removed from the database.
|
||||||
|
return $advagg_aggregates_versions_del + $advagg_aggregates_del;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete orphaned/expired advagg locks from the semaphore database table.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* Count of the number of rows removed from the databases.
|
||||||
|
*/
|
||||||
|
function advagg_cleanup_semaphore_table() {
|
||||||
|
// Let expiration times vary by 5 minutes.
|
||||||
|
$fuzz_factor = 300;
|
||||||
|
$results = db_delete('semaphore')
|
||||||
|
->condition('name', db_like('advagg_') . '%', 'LIKE')
|
||||||
|
->condition('expire', REQUEST_TIME - $fuzz_factor, '<')
|
||||||
|
->execute();
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete leftover temp files.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* Count of the number of files removed
|
||||||
|
*/
|
||||||
|
function advagg_remove_temp_files() {
|
||||||
|
// Make sure advagg_get_root_files_dir() is available.
|
||||||
|
drupal_load('module', 'advagg');
|
||||||
|
// Make sure advagg_install_delete_empty_file_if_stale() is available.
|
||||||
|
module_load_include('install', 'advagg', 'advagg');
|
||||||
|
|
||||||
|
// Get the advagg paths.
|
||||||
|
$advagg_path = advagg_get_root_files_dir();
|
||||||
|
$total_count = 0;
|
||||||
|
// Get the top level path.
|
||||||
|
$top_level = substr($advagg_path[0][0], 0, strpos($advagg_path[0][0], 'advagg_css'));
|
||||||
|
|
||||||
|
// Remove empty temp files from public://.
|
||||||
|
$files = file_scan_directory($top_level, '/file.*|fil.*\.tmp/', array(
|
||||||
|
'recurse' => FALSE,
|
||||||
|
'callback' => 'advagg_install_delete_empty_file_if_stale',
|
||||||
|
));
|
||||||
|
foreach ($files as $key => $file) {
|
||||||
|
if (file_exists($file->uri)) {
|
||||||
|
unset($files[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$total_count += count($files);
|
||||||
|
|
||||||
|
// Remove empty temp files from public://advagg_css.
|
||||||
|
$files = file_scan_directory($advagg_path[0][0], '/file.*|fil.*\.tmp/', array(
|
||||||
|
'recurse' => FALSE,
|
||||||
|
'callback' => 'advagg_install_delete_empty_file_if_stale',
|
||||||
|
));
|
||||||
|
foreach ($files as $key => $file) {
|
||||||
|
if (file_exists($file->uri)) {
|
||||||
|
unset($files[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$total_count += count($files);
|
||||||
|
|
||||||
|
// Remove empty temp files from public://advagg_js.
|
||||||
|
$files = file_scan_directory($advagg_path[1][0], '/file.*|fil.*\.tmp/', array(
|
||||||
|
'recurse' => FALSE,
|
||||||
|
'callback' => 'advagg_install_delete_empty_file_if_stale',
|
||||||
|
));
|
||||||
|
foreach ($files as $key => $file) {
|
||||||
|
if (file_exists($file->uri)) {
|
||||||
|
unset($files[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$total_count += count($files);
|
||||||
|
|
||||||
|
// Remove empty temp files from public://.
|
||||||
|
$files = file_scan_directory($top_level, '/file_advagg_.*/', array(
|
||||||
|
'recurse' => FALSE,
|
||||||
|
'callback' => 'advagg_delete_temp_file_if_stale',
|
||||||
|
));
|
||||||
|
foreach ($files as $key => $file) {
|
||||||
|
if (file_exists($file->uri)) {
|
||||||
|
unset($files[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$total_count += count($files);
|
||||||
|
|
||||||
|
// Remove empty temp files from public://advagg_css.
|
||||||
|
$files = file_scan_directory($advagg_path[0][0], '/file_advagg_.*/', array(
|
||||||
|
'recurse' => FALSE,
|
||||||
|
'callback' => 'advagg_delete_temp_file_if_stale',
|
||||||
|
));
|
||||||
|
foreach ($files as $key => $file) {
|
||||||
|
if (file_exists($file->uri)) {
|
||||||
|
unset($files[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$total_count += count($files);
|
||||||
|
|
||||||
|
// Remove empty temp files from public://advagg_js.
|
||||||
|
$files = file_scan_directory($advagg_path[1][0], '/file_advagg_.*/', array(
|
||||||
|
'recurse' => FALSE,
|
||||||
|
'callback' => 'advagg_delete_temp_file_if_stale',
|
||||||
|
));
|
||||||
|
foreach ($files as $key => $file) {
|
||||||
|
if (file_exists($file->uri)) {
|
||||||
|
unset($files[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$total_count += count($files);
|
||||||
|
|
||||||
|
// Remove empty temp files from public://.
|
||||||
|
$files = file_scan_directory($top_level, '/advagg_file_.*/', array(
|
||||||
|
'recurse' => FALSE,
|
||||||
|
'callback' => 'advagg_delete_temp_file_if_stale',
|
||||||
|
));
|
||||||
|
foreach ($files as $key => $file) {
|
||||||
|
if (file_exists($file->uri)) {
|
||||||
|
unset($files[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$total_count += count($files);
|
||||||
|
|
||||||
|
// Remove empty temp files from public://advagg_css.
|
||||||
|
$files = file_scan_directory($advagg_path[0][0], '/advagg_file_.*/', array(
|
||||||
|
'recurse' => FALSE,
|
||||||
|
'callback' => 'advagg_delete_temp_file_if_stale',
|
||||||
|
));
|
||||||
|
foreach ($files as $key => $file) {
|
||||||
|
if (file_exists($file->uri)) {
|
||||||
|
unset($files[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$total_count += count($files);
|
||||||
|
|
||||||
|
// Remove empty temp files from public://advagg_js.
|
||||||
|
$files = file_scan_directory($advagg_path[1][0], '/advagg_file_.*/', array(
|
||||||
|
'recurse' => FALSE,
|
||||||
|
'callback' => 'advagg_delete_temp_file_if_stale',
|
||||||
|
));
|
||||||
|
foreach ($files as $key => $file) {
|
||||||
|
if (file_exists($file->uri)) {
|
||||||
|
unset($files[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$total_count += count($files);
|
||||||
|
|
||||||
|
// Output info.
|
||||||
|
return $total_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh all locale files.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* Count of the number of files removed
|
||||||
|
*/
|
||||||
|
function advagg_refresh_all_locale_files() {
|
||||||
|
$locale_files = array();
|
||||||
|
if (!module_exists('locale')) {
|
||||||
|
return $locale_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = db_select('advagg_files', 'af')
|
||||||
|
->fields('af')
|
||||||
|
->condition('af.filetype', 'js')
|
||||||
|
->condition('af.filesize', 0, '>')
|
||||||
|
->execute();
|
||||||
|
$javascript = array();
|
||||||
|
foreach ($results as $row) {
|
||||||
|
$javascript[] = array(
|
||||||
|
'type' => 'file',
|
||||||
|
'data' => $row->filename,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($javascript)) {
|
||||||
|
$javascript_before = $javascript;
|
||||||
|
$language_before = $GLOBALS['language'];
|
||||||
|
$language_list = language_list();
|
||||||
|
foreach ($language_list as $lang) {
|
||||||
|
if ($lang->enabled) {
|
||||||
|
$GLOBALS['language'] = $lang;
|
||||||
|
$javascript = $javascript_before;
|
||||||
|
locale_js_alter($javascript);
|
||||||
|
$locale_file = array_diff_key($javascript, $javascript_before);
|
||||||
|
$locale_files += $locale_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$GLOBALS['language'] = $language_before;
|
||||||
|
}
|
||||||
|
return $locale_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to delete files if modified more than 60 seconds ago.
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
* Location of the file to check.
|
||||||
|
*/
|
||||||
|
function advagg_delete_temp_file_if_stale($uri) {
|
||||||
|
// Set stale file threshold to 60 seconds.
|
||||||
|
if (REQUEST_TIME - filemtime($uri) > 60) {
|
||||||
|
file_unmanaged_delete($uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See if any of the subfiles has changed.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* Name of the file that is related to the subfiles.
|
||||||
|
* @param array $subfiles
|
||||||
|
* An array of files to check for changes.
|
||||||
|
* @param string $keyname
|
||||||
|
* Under what key to save the info on the files.
|
||||||
|
* @param bool $save_changes
|
||||||
|
* If TRUE then the changes will be updated in the cache.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE if one of the subfiles has changed.
|
||||||
|
*/
|
||||||
|
function advagg_detect_subfile_changes($filename, array $subfiles, $keyname, $save_changes = FALSE) {
|
||||||
|
// Get the info on this file from the cache.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
$info = advagg_get_info_on_file($filename);
|
||||||
|
$hash_id = 'advagg:subfiles:' . $keyname . ':' . $info['filename_hash'];
|
||||||
|
if (!isset($info[$keyname])) {
|
||||||
|
// Pull up the info from the database if missing from the cache.
|
||||||
|
$info[$keyname] = advagg_get_hash_settings($hash_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$subfile_changed = array();
|
||||||
|
// Check every subfile seeing if they have changed.
|
||||||
|
foreach ($subfiles as $subfile) {
|
||||||
|
$current_file_info = $defaults = array(
|
||||||
|
'hash' => '',
|
||||||
|
'size' => 0,
|
||||||
|
'mtime' => 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the currently saved info on this file.
|
||||||
|
$saved_file_info = isset($info[$keyname][$subfile]) ? $info[$keyname][$subfile] : array();
|
||||||
|
$saved_file_info += $defaults;
|
||||||
|
|
||||||
|
// Get the current info on the file.
|
||||||
|
if (file_exists($subfile)) {
|
||||||
|
$current_file_info = array(
|
||||||
|
'hash' => drupal_hash_base64((string) @advagg_file_get_contents($subfile)),
|
||||||
|
'size' => filesize($subfile),
|
||||||
|
'mtime' => filemtime($subfile),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the info in case a save happens.
|
||||||
|
$info[$keyname][$subfile] = $current_file_info;
|
||||||
|
|
||||||
|
// Check for any differences.
|
||||||
|
$diff = array_diff_assoc($saved_file_info, $current_file_info);
|
||||||
|
if (!empty($diff)) {
|
||||||
|
$subfile_changed[$subfile] = $diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($subfile_changed) && $save_changes) {
|
||||||
|
$cache_id = 'advagg:file:' . $info['filename_hash'];
|
||||||
|
|
||||||
|
// Set static cache.
|
||||||
|
$filename_hashes = &drupal_static('advagg_get_info_on_file');
|
||||||
|
$filename_hashes[$cache_id] = $info;
|
||||||
|
|
||||||
|
// Set drupal cache.
|
||||||
|
cache_set($cache_id, $info, 'cache_advagg_info', CACHE_PERMANENT);
|
||||||
|
|
||||||
|
// Save to database.
|
||||||
|
advagg_set_hash_settings($hash_id, $info[$keyname]);
|
||||||
|
}
|
||||||
|
return $subfile_changed;
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Provides API homepage for Advanced Aggregates.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advanced Aggreggates API & development documentation.
|
||||||
|
*
|
||||||
|
* @mainpage Advanced Aggreggates API & Developer Documentation
|
||||||
|
* Welcome to the Advanced Aggreggates API & development documentation.
|
||||||
|
*
|
||||||
|
* Newcomers to Drupal development should read the conceptual information
|
||||||
|
* in @link https://www.drupal.org/docs/7/api/ Drupal API Introduction @endlink
|
||||||
|
* or @link https://api.drupal.org/api/drupal/7.x Drupal API Docs @endlink.
|
||||||
|
* The heavily documented
|
||||||
|
* @link https://api.drupal.org/api/examples/7.x-1.x Example modules @endlink
|
||||||
|
* may also be helpful.
|
||||||
|
*
|
||||||
|
* For any comments, support or questions see the
|
||||||
|
* @link https://drupal.org/project/advagg module page @endlink.
|
||||||
|
* Also, feel free to comment here if it is specific to one function/file.
|
||||||
|
*
|
||||||
|
* - Primary components of Advanced Aggregates:
|
||||||
|
* - @link advagg_forms Forms @endlink
|
||||||
|
* - @link advagg_hooks Hooks @endlink
|
||||||
|
* - @link advagg_tests Test Suite @endlink
|
||||||
|
* - Advanced Aggregate sub-modules:
|
||||||
|
* - @link advagg_bundler/advagg_bundler.module AdvAgg Bundler @endlink
|
||||||
|
* - @link advagg_css_cdn/advagg_css_cdn.module AdvAgg CSS CDN @endlink
|
||||||
|
* - @link advagg_css_compress/advagg_css_compress.module AdvAgg CSS Compress @endlink
|
||||||
|
* - @link advagg_ext_compress/advagg_ext_compress.module AdvAgg External Compress @endlink
|
||||||
|
* - @link advagg_font/advagg_font.module AdvAgg Font @endlink
|
||||||
|
* - @link advagg_js_cdn/advagg_js_cdn.module AdvAgg JS CDN @endlink
|
||||||
|
* - @link advagg_js_compress/advagg_js_compress.module AdvAgg JS Compress @endlink
|
||||||
|
* - @link advagg_mod/advagg_mod.module AdvAgg Modifier @endlink
|
||||||
|
* - @link advagg_relocate/advagg_relocate.module AdvAgg Relocate @endlink
|
||||||
|
* - @link advagg_sri/advagg_sri.module AdvAgg SRI @endlink
|
||||||
|
* - @link advagg_validator/advagg_validator.module AdvAgg Validator @endlink
|
||||||
|
*/
|
257
frontend/drupal/sites/all/modules/advagg/advagg.drush.inc
Normal file
257
frontend/drupal/sites/all/modules/advagg/advagg.drush.inc
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Drush commands for Advanced CSS/JS Aggregation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup 3rd_party_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_drush_cache_clear().
|
||||||
|
*/
|
||||||
|
function advagg_drush_cache_clear(&$types) {
|
||||||
|
// Add in Advanced CSS/JS Aggregation.
|
||||||
|
$types['advagg'] = 'drush_advagg_smart_cache_flush';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_drush_help().
|
||||||
|
*/
|
||||||
|
function advagg_drush_help($command) {
|
||||||
|
switch ($command) {
|
||||||
|
case 'drush:advagg-cron':
|
||||||
|
return dt('Run the advagg cron hook. This will clear out all stale advagg aggregated files, remove aggregates that include missing files, and remove unused aggregates.');
|
||||||
|
|
||||||
|
case 'drush:advagg-clear-db-cache':
|
||||||
|
return dt('Remove all entries from the advagg cache bins. Useful if you suspect a cache is not getting cleared.');
|
||||||
|
|
||||||
|
case 'drush:advagg-clear-all-files':
|
||||||
|
return dt('Remove all generated files. Useful if you think some of the generated files got corrupted and thus need to be deleted.');
|
||||||
|
|
||||||
|
case 'drush:advagg-force-new-aggregates':
|
||||||
|
return dt('Force the creation of all new aggregates by incrementing a global counter. Current value of counter: @value. This is useful if a CDN has cached an aggregate incorrectly as it will force new ones to be used even if nothing else has changed.', array('@value' => advagg_get_global_counter()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_drush_command().
|
||||||
|
*/
|
||||||
|
function advagg_drush_command() {
|
||||||
|
$items = array();
|
||||||
|
$items['advagg-cron'] = array(
|
||||||
|
'description' => dt('Run the advagg cron hook.'),
|
||||||
|
'examples' => array(
|
||||||
|
'Standard example' => 'drush advagg-cron',
|
||||||
|
),
|
||||||
|
'aliases' => array('advagg-c'),
|
||||||
|
);
|
||||||
|
$items['advagg-clear-db-cache'] = array(
|
||||||
|
'description' => dt('Remove all entries from the advagg cache bins.'),
|
||||||
|
'examples' => array(
|
||||||
|
'Standard example' => 'drush advagg-clear-db-cache',
|
||||||
|
),
|
||||||
|
'aliases' => array('advagg-cdc'),
|
||||||
|
);
|
||||||
|
$items['advagg-clear-all-files'] = array(
|
||||||
|
'description' => dt('Remove all generated files.'),
|
||||||
|
'examples' => array(
|
||||||
|
'Standard example' => 'drush advagg-clear-all-files',
|
||||||
|
),
|
||||||
|
'aliases' => array('advagg-caf'),
|
||||||
|
);
|
||||||
|
$items['advagg-force-new-aggregates'] = array(
|
||||||
|
'description' => dt('Force the creation of all new aggregates by incrementing a global counter.'),
|
||||||
|
'examples' => array(
|
||||||
|
'Standard example' => 'drush advagg-force-new-aggregates',
|
||||||
|
),
|
||||||
|
'aliases' => array('advagg-fna'),
|
||||||
|
);
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup 3rd_party_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for drush advagg-force-new-aggregates.
|
||||||
|
*
|
||||||
|
* Callback is called by using drush_hook_command() where
|
||||||
|
* hook is the name of the module (advagg) and command is the name of
|
||||||
|
* the Drush command with all "-" characters converted to "_" characters.
|
||||||
|
*/
|
||||||
|
function drush_advagg_force_new_aggregates() {
|
||||||
|
// Clear out the cache.
|
||||||
|
drush_advagg_clear_db_cache();
|
||||||
|
|
||||||
|
// Increment counter.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg.cache');
|
||||||
|
$new_value = advagg_increment_global_counter();
|
||||||
|
drush_log(dt('Global counter is now set to @new_value', array('@new_value' => $new_value)), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for drush advagg-clear-all-files.
|
||||||
|
*
|
||||||
|
* Callback is called by using drush_hook_command() where
|
||||||
|
* hook is the name of the module (advagg) and command is the name of
|
||||||
|
* the Drush command with all "-" characters converted to "_" characters.
|
||||||
|
*/
|
||||||
|
function drush_advagg_clear_all_files() {
|
||||||
|
// Clear out the cache.
|
||||||
|
drush_advagg_clear_db_cache();
|
||||||
|
|
||||||
|
// Run the command.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg.cache');
|
||||||
|
list($css_files, $js_files) = advagg_remove_all_aggregated_files();
|
||||||
|
|
||||||
|
// Report back the results.
|
||||||
|
drush_log(dt('All AdvAgg files have been deleted. @css_count CSS files and @js_count JS files have been removed.', array(
|
||||||
|
'@css_count' => count($css_files),
|
||||||
|
'@js_count' => count($js_files),
|
||||||
|
)), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for drush advagg-clear-db-cache.
|
||||||
|
*
|
||||||
|
* Callback is called by using drush_hook_command() where
|
||||||
|
* hook is the name of the module (advagg) and command is the name of
|
||||||
|
* the Drush command with all "-" characters converted to "_" characters.
|
||||||
|
*/
|
||||||
|
function drush_advagg_clear_db_cache() {
|
||||||
|
// Run the command.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg.cache');
|
||||||
|
advagg_flush_all_cache_bins();
|
||||||
|
|
||||||
|
// Report back the results.
|
||||||
|
drush_log(dt('All AdvAgg cache bins have been cleared.'), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for drush advagg-cron.
|
||||||
|
*
|
||||||
|
* Callback is called by using drush_hook_command() where
|
||||||
|
* hook is the name of the module (advagg) and command is the name of
|
||||||
|
* the Drush command with all "-" characters converted to "_" characters.
|
||||||
|
*/
|
||||||
|
function drush_advagg_cron() {
|
||||||
|
// Run AdvAgg cron job.
|
||||||
|
$output = advagg_cron(TRUE);
|
||||||
|
|
||||||
|
// Output results from running advagg_delete_stale_aggregates().
|
||||||
|
list($css_files, $js_files) = $output[0];
|
||||||
|
if (count($css_files) > 0 || count($js_files) > 0) {
|
||||||
|
drush_log(dt('All stale aggregates have been deleted. @css_count CSS files and @js_count JS files have been removed.', array(
|
||||||
|
'@css_count' => count($css_files),
|
||||||
|
'@js_count' => count($js_files),
|
||||||
|
)), 'ok');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drush_log(dt('No stale aggregates found. Nothing was deleted.'), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output results from running advagg_delete_orphaned_aggregates().
|
||||||
|
if (empty($output[1][0]) && empty($output[1][1])) {
|
||||||
|
drush_log(dt('All files have an associated db record; nothing was deleted.'), 'ok');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drush_log(dt('Some files had no associated db record and could be safely deleted from the file system. @raw', array('@raw' => print_r($output[1], TRUE))), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output results from running advagg_remove_missing_files_from_db().
|
||||||
|
if (empty($output[2])) {
|
||||||
|
drupal_set_message(dt('All source files where found, no database entries where pruned.'), 'ok');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// format_plural() not always available.
|
||||||
|
drupal_set_message(dt('Some source files are missing and as a result some unused aggregates were found. A total of @count database entries were removed.', array('@count' => count($output[2]))), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output results from running advagg_remove_old_unused_aggregates().
|
||||||
|
if (empty($output[3])) {
|
||||||
|
drupal_set_message(dt('No old and unused aggregates found. Nothing was deleted.'), 'ok');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// format_plural() not always available.
|
||||||
|
drupal_set_message(dt('Some old and unused aggregates were found. A total of @count database entries were removed.', array('@count' => $output[3])), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output results from running advagg_cleanup_semaphore_table().
|
||||||
|
if (empty($output[4])) {
|
||||||
|
drupal_set_message(dt('No old semaphore locks found.'), 'ok');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// format_plural() not always available.
|
||||||
|
drupal_set_message(dt('A total of @count old semaphore entries were removed.', array('@count' => count($output[4]))), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output results from running advagg_remove_temp_files().
|
||||||
|
if (empty($output[5])) {
|
||||||
|
drupal_set_message(dt('No leftover temporary files found. Nothing was deleted.'), 'ok');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// format_plural() not always available.
|
||||||
|
drupal_set_message(dt('Some oleftover temporary files were found. A total of @count temporary files were removed.', array('@count' => $output[5])), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output results from running advagg_refresh_all_locale_files().
|
||||||
|
if (empty($output[6])) {
|
||||||
|
drupal_set_message(dt('Locale did not translate anything in any JavaScript files.'), 'ok');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drupal_set_message(dt('Locale did translate some JavaScript files. Resulting locale js files: @files', array('@files' => print_r($output[6], TRUE))), 'ok');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the correct caches so CSS/JS changes go live.
|
||||||
|
*/
|
||||||
|
function drush_advagg_smart_cache_flush() {
|
||||||
|
// Clear the libraries cache.
|
||||||
|
if (function_exists('libraries_flush_caches')) {
|
||||||
|
$cache_tables = libraries_flush_caches();
|
||||||
|
foreach ($cache_tables as $table) {
|
||||||
|
cache_clear_all('*', $table, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the command.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg.cache');
|
||||||
|
$flushed = advagg_push_new_changes();
|
||||||
|
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 0) {
|
||||||
|
// Display a simple message if not in Development mode.
|
||||||
|
drush_log('Advagg Cache Cleared', 'ok');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
list($css_path) = advagg_get_root_files_dir();
|
||||||
|
$parts_uri = $css_path[1] . '/parts';
|
||||||
|
|
||||||
|
// Report back the results.
|
||||||
|
foreach ($flushed as $filename => $data) {
|
||||||
|
if (strpos($filename, $parts_uri) === 0) {
|
||||||
|
// Do not report on css files manged in the parts directory.
|
||||||
|
unset($flushed[$filename]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$ext = pathinfo($filename, PATHINFO_EXTENSION);
|
||||||
|
drush_log(dt('The file @filename has changed. @db_usage aggregates are using this file. @db_count db cache entries and all @type full cache entries have been flushed from the cache bins. Trigger: @changes', array(
|
||||||
|
'@filename' => $filename,
|
||||||
|
'@db_usage' => $data[0],
|
||||||
|
'@db_count' => $data[1],
|
||||||
|
'@changes' => print_r($data[2], TRUE),
|
||||||
|
'@type' => $ext,
|
||||||
|
)), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($flushed)) {
|
||||||
|
drush_log(dt('No changes found. Nothing was cleared.'), 'ok');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1805
frontend/drupal/sites/all/modules/advagg/advagg.inc
Normal file
1805
frontend/drupal/sites/all/modules/advagg/advagg.inc
Normal file
File diff suppressed because it is too large
Load Diff
13
frontend/drupal/sites/all/modules/advagg/advagg.info
Normal file
13
frontend/drupal/sites/all/modules/advagg/advagg.info
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
name = Advanced CSS/JS Aggregation (AdvAgg)
|
||||||
|
description = Aggregates multiple CSS/JS files in a way that prevents 404 from happening when accessing a CSS or JS file.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
files[] = tests/advagg.test
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
2868
frontend/drupal/sites/all/modules/advagg/advagg.install
Normal file
2868
frontend/drupal/sites/all/modules/advagg/advagg.install
Normal file
File diff suppressed because it is too large
Load Diff
50
frontend/drupal/sites/all/modules/advagg/advagg.make.example
Normal file
50
frontend/drupal/sites/all/modules/advagg/advagg.make.example
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
; Rename this file to advagg.make to have it included within a drush make
|
||||||
|
; command.
|
||||||
|
|
||||||
|
api = 2
|
||||||
|
core = 7.x
|
||||||
|
|
||||||
|
; Contrib module: libraries
|
||||||
|
; ---------------------------------------
|
||||||
|
projects[libraries][version] = "2"
|
||||||
|
|
||||||
|
; JavaScript libraries
|
||||||
|
; ---------------------------------------
|
||||||
|
|
||||||
|
; Library: CSSLint.
|
||||||
|
libraries[csslint][download][type] = git
|
||||||
|
libraries[csslint][download][url] = https://github.com/CSSLint/csslint
|
||||||
|
|
||||||
|
; Library: fontfaceobserver.
|
||||||
|
libraries[fontfaceobserver][download][type] = git
|
||||||
|
libraries[fontfaceobserver][download][url] = https://github.com/bramstein/fontfaceobserver.git
|
||||||
|
|
||||||
|
; Library: jshint.
|
||||||
|
libraries[jshint][download][type] = git
|
||||||
|
libraries[jshint][download][url] = https://github.com/jshint/jshint
|
||||||
|
|
||||||
|
; Library: JShrink.
|
||||||
|
libraries[JShrink][download][type] = git
|
||||||
|
libraries[JShrink][download][url] = https://github.com/tedious/JShrink
|
||||||
|
|
||||||
|
; Library: JSMinPlus.
|
||||||
|
libraries[jsminplus][download][type] = git
|
||||||
|
libraries[jsminplus][download][url] = https://github.com/JSMinPlus/JSMinPlus
|
||||||
|
libraries[jsminplus][directory_name] = "jsminplus"
|
||||||
|
|
||||||
|
; Library: jspacker.
|
||||||
|
libraries[jspacker][download][type] = git
|
||||||
|
libraries[jspacker][download][url] = https://github.com/tholu/php-packer
|
||||||
|
libraries[jspacker][directory_name] = "jspacker"
|
||||||
|
|
||||||
|
; Library: jsqueeze.
|
||||||
|
libraries[jsqueeze][download][type] = git
|
||||||
|
libraries[jsqueeze][download][url] = https://github.com/tchwork/jsqueeze
|
||||||
|
|
||||||
|
; Library: loadCSS.
|
||||||
|
libraries[loadCSS][download][type] = git
|
||||||
|
libraries[loadCSS][download][url] = https://github.com/filamentgroup/loadCSS
|
||||||
|
|
||||||
|
; Library: YUI-CSS-compressor-PHP-port.
|
||||||
|
libraries[YUI-CSS-compressor-PHP-port][download][type] = git
|
||||||
|
libraries[YUI-CSS-compressor-PHP-port][download][url] = https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
|
1657
frontend/drupal/sites/all/modules/advagg/advagg.missing.inc
Normal file
1657
frontend/drupal/sites/all/modules/advagg/advagg.missing.inc
Normal file
File diff suppressed because it is too large
Load Diff
6977
frontend/drupal/sites/all/modules/advagg/advagg.module
Normal file
6977
frontend/drupal/sites/all/modules/advagg/advagg.module
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,229 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the advagg bundler module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; Configure advagg settings.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*
|
||||||
|
* @see system_settings_form()
|
||||||
|
*/
|
||||||
|
function advagg_bundler_admin_settings_form() {
|
||||||
|
drupal_set_title(t('AdvAgg: Bundler'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
|
||||||
|
$form = array();
|
||||||
|
$options = array(
|
||||||
|
0 => t('Use HTTP 1.1 settings'),
|
||||||
|
2 => t('Use HTTP 2.0 settings'),
|
||||||
|
4 => t('Use customized settings'),
|
||||||
|
);
|
||||||
|
$form['advagg_bundler_admin_mode'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('AdvAgg Settings'),
|
||||||
|
'#default_value' => variable_get('advagg_bundler_admin_mode', ADVAGG_BUNDLER_ADMIN_MODE),
|
||||||
|
'#options' => $options,
|
||||||
|
'#description' => t("Default settings will mirror core as closely as possible. <br>Recommended settings are optimized for speed."),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test http2.
|
||||||
|
$http2_support = 0;
|
||||||
|
$url = 'https://tools.keycdn.com/http2-query.php?url=' . url('', array('absolute' => TRUE));
|
||||||
|
if (filter_var($_SERVER['HTTP_HOST'], FILTER_VALIDATE_IP) === FALSE && $_SERVER['HTTP_HOST'] !== 'localhost') {
|
||||||
|
$response = drupal_http_request($url, array(
|
||||||
|
'timeout' => 3,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$response = new stdClass();
|
||||||
|
$response->code = 0;
|
||||||
|
}
|
||||||
|
if ($response->code == 200 && !empty($response->data)) {
|
||||||
|
$http2_support = 1;
|
||||||
|
if (stripos($response->data, 'success') !== FALSE) {
|
||||||
|
$http2_support = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== FALSE && is_callable('apache_get_modules')) {
|
||||||
|
$modules = apache_get_modules();
|
||||||
|
$key_a = array_search('mod_http2', $modules);
|
||||||
|
$key_b = array_search('mod_h2', $modules);
|
||||||
|
if ($key_a !== FALSE || $key_b !== FALSE) {
|
||||||
|
$http2_support += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Display http2 info.
|
||||||
|
if ($http2_support & 2) {
|
||||||
|
$description = t('This server supports HTTP 2.0; the test from @url came back ok.', array('@url' => $url));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($http2_support == 0) {
|
||||||
|
$description = t('AdvAgg can not detect if this server supports HTTP 2.0. You can <a href="@url">go here to learn how to enable it</a>.', array('@url' => 'https://httpd.apache.org/docs/2.4/mod/mod_http2.html'));
|
||||||
|
}
|
||||||
|
elseif ($http2_support & 4) {
|
||||||
|
$description = t('It appears that this server could support HTTP 2.0 (apache <a href="@url">mod_http2</a> found)', array(
|
||||||
|
'@url' => 'https://httpd.apache.org/docs/2.4/mod/mod_http2.html',
|
||||||
|
));
|
||||||
|
if ($http2_support & 1) {
|
||||||
|
$description .= t(', but it may not be configured to do so. The test from <a href="@test">here</a> was inconclusive.', array(
|
||||||
|
'@test' => $url,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (count($response) == 1) {
|
||||||
|
$description .= t(', but the test from <a href="@test">here</a> was not actually done due to the URL being an IP address or localhost.', array(
|
||||||
|
'@test' => $url,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$description .= t(', but the test from <a href="@test">here</a> was inconclusive.', array(
|
||||||
|
'@test' => $url,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$description = t('This server does not support HTTP 2.0.');
|
||||||
|
}
|
||||||
|
$description .= ' ' . t('<a href="@url">This guide</a> can help you get http2 enabled on your site.', array('@url' => 'https://geekflare.com/http2-implementation-apache-nginx/'));
|
||||||
|
}
|
||||||
|
if (!empty($description)) {
|
||||||
|
$form['advagg_http2'] = array(
|
||||||
|
'#markup' => "<p>{$description}</p>",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form['global_container'] = array(
|
||||||
|
'#type' => 'container',
|
||||||
|
'#hidden' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'visible' => array(
|
||||||
|
':input[name="advagg_bundler_admin_mode"]' => array('value' => '4'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['global_container']['advagg_bundler_active'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Bundler is Active (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_bundler_active', ADVAGG_BUNDLER_ACTIVE),
|
||||||
|
'#description' => t('If not checked, the bundler will passively monitor your site, but it will not split up aggregates.'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$options = array(
|
||||||
|
0 => 0,
|
||||||
|
1 => 1,
|
||||||
|
2 => 2,
|
||||||
|
3 => 3,
|
||||||
|
4 => 4,
|
||||||
|
5 => 5,
|
||||||
|
6 => 6,
|
||||||
|
7 => 7,
|
||||||
|
8 => 8,
|
||||||
|
9 => 9,
|
||||||
|
10 => 10,
|
||||||
|
11 => 11,
|
||||||
|
12 => 12,
|
||||||
|
13 => 13,
|
||||||
|
14 => 14,
|
||||||
|
15 => 15,
|
||||||
|
16 => 16,
|
||||||
|
17 => 17,
|
||||||
|
18 => 18,
|
||||||
|
19 => 19,
|
||||||
|
20 => 20,
|
||||||
|
21 => 21,
|
||||||
|
22 => 22,
|
||||||
|
23 => 23,
|
||||||
|
24 => 24,
|
||||||
|
25 => 25,
|
||||||
|
);
|
||||||
|
$form['global_container']['advagg_bundler_max_css'] = array(
|
||||||
|
'#type' => 'select',
|
||||||
|
'#title' => t('Target Number Of CSS Bundles Per Page'),
|
||||||
|
'#default_value' => variable_get('advagg_bundler_max_css', ADVAGG_BUNDLER_MAX_CSS),
|
||||||
|
'#options' => $options,
|
||||||
|
'#description' => t('If 0 is selected then the bundler is disabled. 2 is recommended for HTTP 1.1 and 25 for HTTP 2.0.'),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
'#edit-advagg-bundler-active' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'#recommended_value' => 25,
|
||||||
|
);
|
||||||
|
$form['global_container']['advagg_bundler_max_js'] = array(
|
||||||
|
'#type' => 'select',
|
||||||
|
'#title' => t('Target Number Of JS Bundles Per Page'),
|
||||||
|
'#default_value' => variable_get('advagg_bundler_max_js', ADVAGG_BUNDLER_MAX_JS),
|
||||||
|
'#options' => $options,
|
||||||
|
'#description' => t('If 0 is selected then the bundler is disabled. 5 is recommended for HTTP 1.1 and 25 for HTTP 2.0.'),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
'#edit-advagg-bundler-active' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'#recommended_value' => 25,
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['global_container']['advagg_bundler_grouping_logic'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Grouping logic'),
|
||||||
|
'#default_value' => variable_get('advagg_bundler_grouping_logic', ADVAGG_BUNDLER_GROUPING_LOGIC),
|
||||||
|
'#options' => array(
|
||||||
|
0 => t('File count'),
|
||||||
|
1 => t('File size'),
|
||||||
|
),
|
||||||
|
'#description' => t('If file count is selected then each bundle will try to have a similar number of original files aggregated inside of it. If file size is selected then each bundle will try to have a similar file size.'),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
'#edit-advagg-bundler-active' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'#recommended_value' => 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['global_container']['info'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
'#title' => t('Raw Grouping Info'),
|
||||||
|
);
|
||||||
|
module_load_include('inc', 'advagg', 'advagg.admin');
|
||||||
|
$analysis = advagg_bundler_analysis('', TRUE);
|
||||||
|
|
||||||
|
$rawtext = print_r($analysis, TRUE);
|
||||||
|
$form['global_container']['info']['advagg_bundler_info'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('%count different groupings', array('%count' => count($analysis))),
|
||||||
|
'#default_value' => $rawtext,
|
||||||
|
'#rows' => 30,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Clear the cache bins on submit. Also remove advagg_bundler_info so it
|
||||||
|
// doesn't get added to the variable table.
|
||||||
|
$form['#submit'][] = 'advagg_bundler_admin_settings_form_submit';
|
||||||
|
|
||||||
|
return system_settings_form($form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback, clear out the advagg cache bin.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms_callback
|
||||||
|
*/
|
||||||
|
function advagg_bundler_admin_settings_form_submit($form, &$form_state) {
|
||||||
|
// Clear caches.
|
||||||
|
advagg_cache_clear_admin_submit();
|
||||||
|
|
||||||
|
// Unset advagg_bundler_info.
|
||||||
|
if (isset($form_state['values']['advagg_bundler_info'])) {
|
||||||
|
unset($form_state['values']['advagg_bundler_info']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset this form to defaults or recommended values; also show what changed.
|
||||||
|
advagg_set_admin_form_defaults_recommended($form_state, 'advagg_bundler_admin_mode');
|
||||||
|
}
|
@ -0,0 +1,286 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation bundler module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_build_aggregate_plans_alter().
|
||||||
|
*/
|
||||||
|
function advagg_bundler_advagg_build_aggregate_plans_alter(&$files, &$modified, $type) {
|
||||||
|
// Get max number of sub aggregates to create.
|
||||||
|
if ($type === 'css') {
|
||||||
|
$max = variable_get('advagg_bundler_max_css', ADVAGG_BUNDLER_MAX_CSS);
|
||||||
|
}
|
||||||
|
if ($type === 'js') {
|
||||||
|
$max = variable_get('advagg_bundler_max_js', ADVAGG_BUNDLER_MAX_JS);
|
||||||
|
}
|
||||||
|
// If bundles are disabled then do not do any more processing.
|
||||||
|
if (empty($max)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If bundler is disabled then do not do any more processing.
|
||||||
|
if (!advagg_bundler_enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$final_groupings = array();
|
||||||
|
$total_count = 0;
|
||||||
|
foreach ($files as $data) {
|
||||||
|
// Preserve the order while grouping.
|
||||||
|
$last_group = '';
|
||||||
|
$counter = 0;
|
||||||
|
$groupings = array();
|
||||||
|
foreach ($data['files'] as $fileinfo) {
|
||||||
|
// Assign each file to their group.
|
||||||
|
$group = advagg_bundler_analysis($fileinfo['data']);
|
||||||
|
$fileinfo['bundler'] = $group;
|
||||||
|
|
||||||
|
if (!empty($group['group_hash'])) {
|
||||||
|
// Set $last_group if this is the first run of this foreach loop.
|
||||||
|
if (empty($last_group)) {
|
||||||
|
$last_group = $group['group_hash'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($last_group != $group['group_hash']) {
|
||||||
|
// Bump Counter if group has changed from the last one.
|
||||||
|
++$counter;
|
||||||
|
$last_group = $group['group_hash'];
|
||||||
|
$modified = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$groupings[$counter][] = $fileinfo;
|
||||||
|
}
|
||||||
|
// Make sure we didn't go over the max; if we did merge the smallest bundles
|
||||||
|
// together.
|
||||||
|
advagg_bundler_merge($groupings, $max);
|
||||||
|
$total_count += count($groupings);
|
||||||
|
// Add to the final groupings array.
|
||||||
|
$final_groupings[] = $groupings;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to shrink bundles if they are still too big.
|
||||||
|
while ($total_count > $max) {
|
||||||
|
$remerge_candidate = NULL;
|
||||||
|
$remerge_key = NULL;
|
||||||
|
$total_count = 0;
|
||||||
|
foreach ($final_groupings as $key => $groupings) {
|
||||||
|
if (count($groupings) > 1) {
|
||||||
|
if (is_null($remerge_candidate)) {
|
||||||
|
$remerge_candidate = $groupings;
|
||||||
|
$remerge_key = $key;
|
||||||
|
}
|
||||||
|
elseif (count($remerge_candidate) > count($groupings)) {
|
||||||
|
$remerge_candidate = $groupings;
|
||||||
|
$remerge_key = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_null($remerge_candidate)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
advagg_bundler_merge($remerge_candidate, count($remerge_candidate) - 1);
|
||||||
|
$final_groupings[$remerge_key] = $remerge_candidate;
|
||||||
|
|
||||||
|
$total_count = 0;
|
||||||
|
foreach ($final_groupings as $key => $groupings) {
|
||||||
|
$total_count += count($groupings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace $files array with new aggregate filenames.
|
||||||
|
$files = advagg_generate_filenames($final_groupings, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge bundles together if too many where created.
|
||||||
|
*
|
||||||
|
* This preserves the order.
|
||||||
|
*
|
||||||
|
* @param array $groupings
|
||||||
|
* Array of requested groups.
|
||||||
|
* @param int $max
|
||||||
|
* Max number of grouping.
|
||||||
|
*/
|
||||||
|
function advagg_bundler_merge(array &$groupings, $max) {
|
||||||
|
$group_count = count($groupings);
|
||||||
|
|
||||||
|
if (!empty($max)) {
|
||||||
|
// Keep going till array has been merged to the desired size.
|
||||||
|
while ($group_count > $max) {
|
||||||
|
// Get array sizes.
|
||||||
|
// Counts the number of files that are placed into each bundle.
|
||||||
|
$counts = array();
|
||||||
|
$group_hash_counts = array();
|
||||||
|
foreach ($groupings as $key => $values) {
|
||||||
|
// Get the group hash counts.
|
||||||
|
$file_size = 0;
|
||||||
|
$group_hash_counts[$key] = 0;
|
||||||
|
foreach ($values as $data) {
|
||||||
|
// Skip if bundler data is missing.
|
||||||
|
if (empty($data['bundler'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$file_size += empty($data['bundler']['filesize_processed']) ? $data['bundler']['filesize'] : $data['bundler']['filesize_processed'];
|
||||||
|
$group_hash_counts[$key] += intval(substr($data['bundler']['group_hash'], 0, 8));
|
||||||
|
}
|
||||||
|
if (variable_get('advagg_bundler_grouping_logic', ADVAGG_BUNDLER_GROUPING_LOGIC) == 0) {
|
||||||
|
$counts[$key] = count($values);
|
||||||
|
}
|
||||||
|
elseif (variable_get('advagg_bundler_grouping_logic', ADVAGG_BUNDLER_GROUPING_LOGIC) == 1) {
|
||||||
|
$counts[$key] = $file_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create mapping.
|
||||||
|
// Calculates the file count of potential merges. It only merges with
|
||||||
|
// neighbors in order to preserve execution order.
|
||||||
|
$map = array();
|
||||||
|
$prev_key = NULL;
|
||||||
|
foreach ($counts as $key => $val) {
|
||||||
|
// First run of the foreach loop; populate prev key/values and continue.
|
||||||
|
// We can't merge with the previous group in this case.
|
||||||
|
if (is_null($prev_key)) {
|
||||||
|
$prev_key = $key;
|
||||||
|
$prev_val = $val;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array key ($prev_val + $val) is the file count of this new group if
|
||||||
|
// these 2 groups ($prev_key, $key) where to be merged together.
|
||||||
|
$map[] = array(
|
||||||
|
($prev_val + $val) => array($prev_key, $key),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prep for next run.
|
||||||
|
$prev_key = $key;
|
||||||
|
$prev_val = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
$group_hash_map = array();
|
||||||
|
$prev_key = NULL;
|
||||||
|
foreach ($group_hash_counts as $key => $val) {
|
||||||
|
// First run of the foreach loop; populate prev key/values and continue.
|
||||||
|
// We can't merge with the previous group in this case.
|
||||||
|
if (is_null($prev_key)) {
|
||||||
|
$prev_key = $key;
|
||||||
|
$prev_val = $val;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array value ($prev_val + $val) is the hash count of this new group if
|
||||||
|
// these 2 groups where to be merged together.
|
||||||
|
$group_hash_map[$prev_key . ' ' . $key] = $prev_val + $val;
|
||||||
|
|
||||||
|
// Prep for next run.
|
||||||
|
$prev_key = $key;
|
||||||
|
$prev_val = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get best merge candidate.
|
||||||
|
// We are looking for the smallest file count. $min is populated with a
|
||||||
|
// large number (15 bits) so it won't be selected in this process.
|
||||||
|
$min = PHP_INT_MAX;
|
||||||
|
$first = NULL;
|
||||||
|
$last = NULL;
|
||||||
|
$last_min = NULL;
|
||||||
|
foreach ($map as $v) {
|
||||||
|
foreach ($v as $key => $values) {
|
||||||
|
$min = min($min, $key);
|
||||||
|
// If the min value (number of files in the proposed merged bundle) is
|
||||||
|
// the same as the key, then get the 2 bundle keys that generated this
|
||||||
|
// new min value.
|
||||||
|
if ($min == $key) {
|
||||||
|
if ($last_min == $min && !is_null($first) && !is_null($last)) {
|
||||||
|
list($new_first, $new_last) = $values;
|
||||||
|
// All things being equal pick the smaller count on the hash.
|
||||||
|
if ($group_hash_map[$first . ' ' . $last] > $group_hash_map[$new_first . ' ' . $new_last]) {
|
||||||
|
$first = $new_first;
|
||||||
|
$last = $new_last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
list($first, $last) = $values;
|
||||||
|
}
|
||||||
|
$last_min = $min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the new merged set.
|
||||||
|
$a = $groupings[$first];
|
||||||
|
$b = $groupings[$last];
|
||||||
|
$new_set = array_merge($a, $b);
|
||||||
|
|
||||||
|
// Rebuild the array with the new set in the correct place.
|
||||||
|
$new_groupings = array();
|
||||||
|
foreach ($groupings as $key => $files) {
|
||||||
|
if ($key == $first) {
|
||||||
|
$new_groupings[$first . ' ' . $last] = $new_set;
|
||||||
|
}
|
||||||
|
elseif ($key != $last) {
|
||||||
|
$new_groupings[$key] = $files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$groupings = $new_groupings;
|
||||||
|
--$group_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error prevention below.
|
||||||
|
// Make sure there isn't a group that has all files that don't exist or empty.
|
||||||
|
$bad_groups = array();
|
||||||
|
foreach ($groupings as $key => $group) {
|
||||||
|
$missing_counter = 0;
|
||||||
|
foreach ($group as $data) {
|
||||||
|
if (empty($data['bundler']['filesize'])) {
|
||||||
|
++$missing_counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all files are missing/empty in this group then it is a bad set.
|
||||||
|
if ($missing_counter == count($group)) {
|
||||||
|
$bad_groups[$key] = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the bad groups to the smallest grouping in this set.
|
||||||
|
if (!empty($bad_groups)) {
|
||||||
|
$merge_candidate_key = '';
|
||||||
|
$merge_candidate_count = PHP_INT_MAX;
|
||||||
|
$bad_group = array();
|
||||||
|
foreach ($groupings as $key => $group) {
|
||||||
|
if (isset($bad_groups[$key])) {
|
||||||
|
// Merge all bad groups into one.
|
||||||
|
$bad_group = array_merge($bad_group, $group);
|
||||||
|
|
||||||
|
// Delete the bad group from the master set.
|
||||||
|
unset($groupings[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the smallest good grouping.
|
||||||
|
$min = min($merge_candidate_count, count($group));
|
||||||
|
if ($min < $merge_candidate_count) {
|
||||||
|
$merge_candidate_key = $key;
|
||||||
|
$merge_candidate_count = $min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the bad files into the smallest good group.
|
||||||
|
$new_set = isset($groupings[$merge_candidate_key]) ? $groupings[$merge_candidate_key] : array();
|
||||||
|
$new_set = array_merge($new_set, $bad_group);
|
||||||
|
$groupings[$merge_candidate_key] = $new_set;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
name = AdvAgg Bundler
|
||||||
|
description = Provides intelligent bundling of CSS and JS files by grouping files that belong together.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg/bundler
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles Advanced Aggregation installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_install().
|
||||||
|
*/
|
||||||
|
function advagg_bundler_install() {
|
||||||
|
// New install gets a locked admin section.
|
||||||
|
variable_set('advagg_bundler_admin_mode', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the advagg_bundler_info variable if set.
|
||||||
|
*/
|
||||||
|
function advagg_bundler_update_7201() {
|
||||||
|
$advagg_bundler_info = variable_get('advagg_bundler_info');
|
||||||
|
if (!empty($advagg_bundler_info)) {
|
||||||
|
variable_del('advagg_bundler_info');
|
||||||
|
return t('Removed the advagg_bundler_info variable.');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return t('Nothing needed to happen.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
@ -0,0 +1,409 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation bundler module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup default_variables
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default for maximum number of CSS bundles used in a themes region.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_BUNDLER_MAX_CSS', 2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default for maximum number of JS bundles used in a themes region.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_BUNDLER_MAX_JS', 5);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default of the last used time before the bundle is considered outdated.
|
||||||
|
*
|
||||||
|
* Value of 1209600 is 2 weeks in seconds.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_BUNDLER_OUTDATED', 1209600);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to see if the bundler should be active or passive.
|
||||||
|
*
|
||||||
|
* If it is passive, the bundler will only do analysis and not split up the
|
||||||
|
* aggregate.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_BUNDLER_ACTIVE', TRUE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to for bundler, set to file size.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_BUNDLER_GROUPING_LOGIC', 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If 4 the admin section gets unlocked.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_BUNDLER_ADMIN_MODE', 4);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup default_variables".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu().
|
||||||
|
*/
|
||||||
|
function advagg_bundler_menu() {
|
||||||
|
$file_path = drupal_get_path('module', 'advagg_bundler');
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
$items[$config_path . '/advagg/bundler'] = array(
|
||||||
|
'title' => 'Bundler',
|
||||||
|
'description' => 'Adjust Bundler settings.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('advagg_bundler_admin_settings_form'),
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file path' => $file_path,
|
||||||
|
'file' => 'advagg_bundler.admin.inc',
|
||||||
|
'weight' => 10,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_hooks_implemented_alter().
|
||||||
|
*/
|
||||||
|
function advagg_bundler_advagg_hooks_implemented_alter(&$hooks, $all) {
|
||||||
|
if ($all) {
|
||||||
|
$hooks['advagg_bundler_analysis_alter'] = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_init().
|
||||||
|
*/
|
||||||
|
function advagg_bundler_init() {
|
||||||
|
if (advagg_bundler_enabled()) {
|
||||||
|
$GLOBALS['conf']['advagg_core_groups'] = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_form_FORM_ID_alter().
|
||||||
|
*/
|
||||||
|
function advagg_bundler_form_advagg_admin_settings_form_alter(&$form, $form_state) {
|
||||||
|
if (advagg_bundler_enabled()) {
|
||||||
|
$form['global']['advagg_core_groups']['#disabled'] = TRUE;
|
||||||
|
$form['global']['advagg_core_groups']['#description'] = t('The bundler submodule disables core grouping logic.');
|
||||||
|
$form['global']['advagg_core_groups']['#states'] = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns TRUE if the bundler will run.
|
||||||
|
*/
|
||||||
|
function advagg_bundler_enabled() {
|
||||||
|
if (variable_get('advagg_bundler_active', ADVAGG_BUNDLER_ACTIVE) && (variable_get('advagg_bundler_max_css', ADVAGG_BUNDLER_MAX_CSS) || variable_get('advagg_bundler_max_js', ADVAGG_BUNDLER_MAX_JS))) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a filename return a bundle key.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* Filename.
|
||||||
|
* @param bool $force
|
||||||
|
* Bypass the cache and get a fresh version of the analysis.
|
||||||
|
* @param bool $safesql
|
||||||
|
* Turn off SQL language that might cause errors.
|
||||||
|
* @param int $depth
|
||||||
|
* Used to prevent endless loops.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* String to be used for the grouping key.
|
||||||
|
*/
|
||||||
|
function advagg_bundler_analysis($filename = '', $force = FALSE, $safesql = FALSE, $depth = 0) {
|
||||||
|
// Cache query in a static.
|
||||||
|
static $analysis = array();
|
||||||
|
if (empty($analysis)) {
|
||||||
|
// See if we have a cached version of this. Generate cache ID.
|
||||||
|
$query = db_select('advagg_aggregates_versions', 'aav')
|
||||||
|
->condition('aav.root', 1)
|
||||||
|
->condition('aav.atime', REQUEST_TIME - max(172800, variable_get('advagg_bundler_outdated', ADVAGG_BUNDLER_OUTDATED), '>'), '>');
|
||||||
|
$query->addExpression('COUNT(aggregate_filenames_hash)', 'counter');
|
||||||
|
$count = $query->execute()->fetchField();
|
||||||
|
$ideal_cid = 'advagg:bundler_analysis:' . $count;
|
||||||
|
|
||||||
|
if (!$force) {
|
||||||
|
// Generate cache IDs.
|
||||||
|
$counts = range(max(0, $count - 3), $count + 3);
|
||||||
|
foreach ($counts as $count) {
|
||||||
|
$cache_ids[] = 'advagg:bundler_analysis:' . $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a range of cached bundler_analysis data.
|
||||||
|
$cache_hits = cache_get_multiple($cache_ids, 'cache_advagg_aggregates');
|
||||||
|
if (!empty($cache_hits)) {
|
||||||
|
if (isset($cache_hits[$ideal_cid])) {
|
||||||
|
$cache = $cache_hits[$ideal_cid];
|
||||||
|
}
|
||||||
|
elseif (!$force && module_exists('httprl') && httprl_is_background_callback_capable()) {
|
||||||
|
// Setup callback options array.
|
||||||
|
$callback_options = array(
|
||||||
|
array(
|
||||||
|
'function' => 'advagg_bundler_analysis',
|
||||||
|
),
|
||||||
|
$filename, TRUE,
|
||||||
|
);
|
||||||
|
// Queue up the request.
|
||||||
|
httprl_queue_background_callback($callback_options);
|
||||||
|
// Execute request.
|
||||||
|
httprl_send_request();
|
||||||
|
|
||||||
|
// Use most recent bundler_analysis data.
|
||||||
|
$max = 0;
|
||||||
|
foreach ($cache_hits as $data) {
|
||||||
|
if ($data->created > $max) {
|
||||||
|
$max = $data->created;
|
||||||
|
$cache = $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($force || empty($cache->data)) {
|
||||||
|
try {
|
||||||
|
$analysis = advagg_bundler_analyisis_query($safesql);
|
||||||
|
// Save results to the cache.
|
||||||
|
cache_set($ideal_cid, $analysis, 'cache_advagg_aggregates', CACHE_TEMPORARY);
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
if ($depth > 2) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
$depth++;
|
||||||
|
return advagg_bundler_analysis($filename, TRUE, TRUE, $depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$analysis = $cache->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no filename is given pass back then entire query results.
|
||||||
|
if (empty($filename)) {
|
||||||
|
return $analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a key to be used in groupings.
|
||||||
|
if (!empty($analysis[$filename])) {
|
||||||
|
return $analysis[$filename];
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to return a value that can be used as an array key if the query
|
||||||
|
// didn't give us anything.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the analysis query and return the analysis array.
|
||||||
|
*
|
||||||
|
* "Magic Query"; only needs to run once. Results are cached.
|
||||||
|
* This is what the raw SQL looks like:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* SELECT
|
||||||
|
* af.filename AS filename,
|
||||||
|
* af.filesize AS filesize,
|
||||||
|
* af.mtime AS mtime,
|
||||||
|
* af.changes AS changes,
|
||||||
|
* af.linecount AS linecount,
|
||||||
|
* af.filename_hash AS filename_hash,
|
||||||
|
* aa.counter AS counter,
|
||||||
|
* aa.hashlist AS hashlist
|
||||||
|
* FROM advagg_files af
|
||||||
|
* INNER JOIN (
|
||||||
|
* SELECT
|
||||||
|
* aa.filename_hash AS filename_hash,
|
||||||
|
* LPAD(CAST(COUNT(aav.aggregate_filenames_hash) AS char(8)), 8, '0') AS counter,
|
||||||
|
* HEX(SHA1(GROUP_CONCAT(DISTINCT aa.aggregate_filenames_hash ORDER BY aa.aggregate_filenames_hash ASC))) AS hashlist
|
||||||
|
* FROM advagg_aggregates aa
|
||||||
|
* INNER JOIN advagg_aggregates_versions aav
|
||||||
|
* ON aav.aggregate_filenames_hash = aa.aggregate_filenames_hash
|
||||||
|
* AND aav.root = 1
|
||||||
|
* AND aav.atime > (UNIX_TIMESTAMP() - 1209600)
|
||||||
|
* GROUP BY aa.filename_hash
|
||||||
|
* ) aa ON af.filename_hash = aa.filename_hash
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @param bool $safesql
|
||||||
|
* Turn off SQL language that might cause errors.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* The analysis array.
|
||||||
|
*/
|
||||||
|
function advagg_bundler_analyisis_query($safesql) {
|
||||||
|
// Return a count of how many root bundles all files are used in. Count is
|
||||||
|
// padded with eight zeros so the count can be key sorted as a string
|
||||||
|
// without worrying about it getting put in the wrong order.
|
||||||
|
// Return the bundle_md5's value; we need something more unique than count
|
||||||
|
// when grouping together.
|
||||||
|
// Return the filename. Used for lookup.
|
||||||
|
// We join the advagg bundles and files together making sure to only use
|
||||||
|
// root bundles that have been used in the last 2 weeks. This prevents an
|
||||||
|
// old site structure from influencing new bundles.
|
||||||
|
// Grouping by the filename gives us the count and makes it so we don't
|
||||||
|
// return a lot of rows.
|
||||||
|
$db_type = Database::getConnection()->databaseType();
|
||||||
|
$schema = Database::getConnection()->schema();
|
||||||
|
if ($safesql) {
|
||||||
|
$mssql_group_concat = FALSE;
|
||||||
|
$mssql_lpad = FALSE;
|
||||||
|
$mssql_md5 = FALSE;
|
||||||
|
$pg9 = FALSE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$mssql_group_concat = method_exists($schema, 'functionExists') && $schema->functionExists('GROUP_CONCAT');
|
||||||
|
$mssql_lpad = method_exists($schema, 'functionExists') && $schema->functionExists('LPAD');
|
||||||
|
$mssql_md5 = method_exists($schema, 'functionExists') && $schema->functionExists('MD5');
|
||||||
|
if ($db_type === 'pgsql') {
|
||||||
|
$database_connection = Database::getConnection();
|
||||||
|
$pg9 = FALSE;
|
||||||
|
if (version_compare($database_connection->version(), '9') >= 0) {
|
||||||
|
$pg9 = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create join query for the advagg_aggregates table.
|
||||||
|
$subquery_aggregates = db_select('advagg_aggregates', 'aa');
|
||||||
|
|
||||||
|
// Counter column.
|
||||||
|
$fields = array('counter');
|
||||||
|
if ($db_type === 'sqlsrv' && !$mssql_lpad) {
|
||||||
|
// MS SQL does not support LPAD.
|
||||||
|
$subquery_aggregates->addExpression("RIGHT(REPLICATE('0',8) + CAST(COUNT(aav.aggregate_filenames_hash) AS char(8)),8)", 'counter');
|
||||||
|
}
|
||||||
|
elseif ($db_type === 'sqlite') {
|
||||||
|
// SQLite does not support LPAD.
|
||||||
|
$subquery_aggregates->addExpression("substr('00000000' || CAST(COUNT(aav.aggregate_filenames_hash) AS char(8)), -8, 8)", 'counter');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$subquery_aggregates->addExpression("LPAD(CAST(COUNT(aav.aggregate_filenames_hash) AS char(8)), 8, '0')", 'counter');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hashlist column.
|
||||||
|
if ($db_type === 'mysql') {
|
||||||
|
$fields[] = 'hashlist';
|
||||||
|
db_query('SET SESSION group_concat_max_len = 65535');
|
||||||
|
$subquery_aggregates->addExpression('HEX(SHA1(GROUP_CONCAT(DISTINCT aa.aggregate_filenames_hash ORDER BY aa.aggregate_filenames_hash ASC)))', 'hashlist');
|
||||||
|
}
|
||||||
|
elseif ($db_type === 'pgsql') {
|
||||||
|
if ($pg9) {
|
||||||
|
$fields[] = 'hashlist';
|
||||||
|
$subquery_aggregates->addExpression("MD5(STRING_AGG(DISTINCT(aa.aggregate_filenames_hash), ',' ORDER BY aa.aggregate_filenames_hash ASC))", 'hashlist');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($db_type === 'sqlite') {
|
||||||
|
$fields[] = 'hashlist';
|
||||||
|
$subquery_aggregates->addExpression('GROUP_CONCAT(DISTINCT aa.aggregate_filenames_hash)', 'hashlist');
|
||||||
|
$subquery_aggregates->orderBy("aa.aggregate_filenames_hash", "ASC");
|
||||||
|
}
|
||||||
|
elseif ($db_type === 'sqlsrv' && $mssql_group_concat) {
|
||||||
|
$fields[] = 'hashlist';
|
||||||
|
if ($mssql_md5) {
|
||||||
|
$subquery_aggregates->addExpression('MD5(GROUP_CONCAT(DISTINCT aa.aggregate_filenames_hash))', 'hashlist');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$subquery_aggregates->addExpression('GROUP_CONCAT(DISTINCT aa.aggregate_filenames_hash)', 'hashlist');
|
||||||
|
}
|
||||||
|
// The ORDER BY clause is invalid in views, inline functions,
|
||||||
|
// derived tables, subqueries, and common table expressions, unless TOP or
|
||||||
|
// FOR XML is also specified. So no point in doing an order-by like in the
|
||||||
|
// other cases.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create join for the advagg_aggregates_versions table.
|
||||||
|
// 1209600 = 2 weeks.
|
||||||
|
$time = REQUEST_TIME - max(172800, variable_get('advagg_bundler_outdated', ADVAGG_BUNDLER_OUTDATED), '>');
|
||||||
|
$subquery_aggregates->join('advagg_aggregates_versions', 'aav', "aav.aggregate_filenames_hash=aa.aggregate_filenames_hash AND aav.root=1 AND aav.atime > $time");
|
||||||
|
|
||||||
|
$subquery_aggregates = $subquery_aggregates->fields('aa', array('filename_hash'))
|
||||||
|
->groupBy('aa.filename_hash');
|
||||||
|
|
||||||
|
// Create main query for the advagg_files table.
|
||||||
|
$af_fields = array(
|
||||||
|
'filename',
|
||||||
|
'filesize',
|
||||||
|
'mtime',
|
||||||
|
'changes',
|
||||||
|
'linecount',
|
||||||
|
'filename_hash',
|
||||||
|
);
|
||||||
|
// Make drupal_get_installed_schema_version() available.
|
||||||
|
include_once DRUPAL_ROOT . '/includes/install.inc';
|
||||||
|
if (drupal_get_installed_schema_version('advagg') >= 7211) {
|
||||||
|
$af_fields[] = 'filesize_processed';
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = db_select('advagg_files', 'af');
|
||||||
|
$query->join($subquery_aggregates, 'aa', 'af.filename_hash=aa.filename_hash');
|
||||||
|
$query = $query->fields('af', $af_fields)
|
||||||
|
->fields('aa', $fields);
|
||||||
|
$query->comment('Query called from ' . __FUNCTION__ . '()');
|
||||||
|
$results = $query->execute();
|
||||||
|
|
||||||
|
$analysis = array();
|
||||||
|
foreach ($results as $row) {
|
||||||
|
// Implement slower GROUP_CONCAT functionality for non mysql databases.
|
||||||
|
if (empty($row->hashlist)) {
|
||||||
|
$subquery_aggregates_versions = db_select('advagg_aggregates_versions', 'aav')
|
||||||
|
->fields('aav')
|
||||||
|
->condition('aav.root', 1)
|
||||||
|
->condition('aav.atime', REQUEST_TIME - max(172800, variable_get('advagg_bundler_outdated', ADVAGG_BUNDLER_OUTDATED), '>'), '>');
|
||||||
|
|
||||||
|
$subquery_aggregates = db_select('advagg_aggregates', 'aa');
|
||||||
|
$subquery_aggregates->join($subquery_aggregates_versions, 'aav', 'aav.aggregate_filenames_hash=aa.aggregate_filenames_hash');
|
||||||
|
$subquery_aggregates = $subquery_aggregates->fields('aa', array('aggregate_filenames_hash'))
|
||||||
|
->condition('aa.filename_hash', $row->filename_hash)
|
||||||
|
->groupBy('aa.aggregate_filenames_hash')
|
||||||
|
->orderBy('aa.aggregate_filenames_hash', 'ASC');
|
||||||
|
$subquery_aggregates->comment('Query called from ' . __FUNCTION__ . '()');
|
||||||
|
$aa_results = $subquery_aggregates->execute();
|
||||||
|
$aa_rows = array();
|
||||||
|
foreach ($aa_results as $aa_row) {
|
||||||
|
$aa_rows[] = $aa_row->aggregate_filenames_hash;
|
||||||
|
}
|
||||||
|
$row->hashlist = implode(',', $aa_rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
$row->hashlist = drupal_hash_base64($row->hashlist);
|
||||||
|
$analysis[$row->filename] = array(
|
||||||
|
'group_hash' => $row->counter . ' ' . $row->hashlist,
|
||||||
|
'mtime' => $row->mtime,
|
||||||
|
'filesize' => $row->filesize,
|
||||||
|
'filesize_processed' => empty($row->filesize_processed) ? $row->filesize : $row->filesize_processed,
|
||||||
|
'linecount' => $row->linecount,
|
||||||
|
'changes' => $row->changes,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
arsort($analysis);
|
||||||
|
|
||||||
|
// Invoke hook_advagg_bundler_analysis_alter() to give installed modules a
|
||||||
|
// chance to alter the analysis array.
|
||||||
|
drupal_alter('advagg_bundler_analysis', $analysis);
|
||||||
|
|
||||||
|
return $analysis;
|
||||||
|
}
|
@ -0,0 +1,381 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the advagg critical css module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; Configure advagg settings.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*
|
||||||
|
* @see system_settings_form()
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_admin_settings_form() {
|
||||||
|
drupal_set_title(t('AdvAgg: Critical CSS'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
|
||||||
|
$form = array();
|
||||||
|
|
||||||
|
$default_theme = variable_get('theme_default', 'bartik');
|
||||||
|
$global_theme = $GLOBALS['theme'];
|
||||||
|
$themes = array_keys(list_themes());
|
||||||
|
|
||||||
|
$form['#attached']['css'][] = array(
|
||||||
|
'data' => ".form-item-lookup{padding-bottom:0;margin-bottom:0;}",
|
||||||
|
'type' => 'inline',
|
||||||
|
);
|
||||||
|
$form['add_item']['theme'] = array(
|
||||||
|
'#type' => 'select',
|
||||||
|
'#title' => t('Theme'),
|
||||||
|
'#options' => array_combine($themes, $themes),
|
||||||
|
'#default_value' => $default_theme,
|
||||||
|
'#description' => t('Theme Default: %default, Current Theme: %current', array(
|
||||||
|
'%default' => $default_theme,
|
||||||
|
'%current' => $global_theme,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
$form['add_item']['user'] = array(
|
||||||
|
'#type' => 'select',
|
||||||
|
'#title' => t('User type'),
|
||||||
|
'#default_value' => 0,
|
||||||
|
'#options' => array(
|
||||||
|
'anonymous' => t('anonymous'),
|
||||||
|
'authenticated' => t('authenticated'),
|
||||||
|
'all' => t('all'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$type_options = array(
|
||||||
|
0 => t('Disabled'),
|
||||||
|
2 => t('URL'),
|
||||||
|
8 => t('Node Type'),
|
||||||
|
);
|
||||||
|
$form['add_item']['type'] = array(
|
||||||
|
'#type' => 'select',
|
||||||
|
'#title' => t('Type of lookup'),
|
||||||
|
'#default_value' => 2,
|
||||||
|
'#options' => $type_options,
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['add_item']['lookup'] = array(
|
||||||
|
'#type' => 'textfield',
|
||||||
|
'#title' => t('Value to lookup'),
|
||||||
|
'#maxlength' => 255,
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
':input[name="type"]' => array('value' => 0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['add_item']['lookup_container_disabled'] = array(
|
||||||
|
'#type' => 'container',
|
||||||
|
'#states' => array(
|
||||||
|
'visible' => array(
|
||||||
|
':input[name="type"]' => array('value' => 0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['add_item']['lookup_container_disabled']['disabled'] = array(
|
||||||
|
'#markup' => '<br>',
|
||||||
|
);
|
||||||
|
$form['add_item']['lookup_container_current_path'] = array(
|
||||||
|
'#type' => 'container',
|
||||||
|
'#states' => array(
|
||||||
|
'visible' => array(
|
||||||
|
':input[name="type"]' => array('value' => 2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['add_item']['lookup_container_current_path']['current_path'] = array(
|
||||||
|
'#markup' => t('%front is the front page; can use internal URLs like %internal or an alias like %here', array(
|
||||||
|
'%front' => '<front>',
|
||||||
|
'%internal' => 'node/2',
|
||||||
|
'%here' => current_path(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
$form['add_item']['lookup_container_node_type'] = array(
|
||||||
|
'#type' => 'container',
|
||||||
|
'#states' => array(
|
||||||
|
'visible' => array(
|
||||||
|
':input[name="type"]' => array('value' => 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['add_item']['lookup_container_node_type']['node_type'] = array(
|
||||||
|
'#markup' => t('Node type is the machine name of the node; list of node types: @node_types', array(
|
||||||
|
'@current_path' => 'https://api.drupal.org/api/drupal/includes%21path.inc/function/current_path/7.x',
|
||||||
|
'@request_path' => 'https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/request_path/7.x',
|
||||||
|
'@request_uri' => 'https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/request_uri/7.x',
|
||||||
|
'@node_types' => implode(', ', array_keys(node_type_get_names())),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['add_item']['css'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('Critical CSS'),
|
||||||
|
'#description' => t('Can be generated via <a href="@url">https://www.sitelocity.com/critical-path-css-generator</a>. If this field is empty this entry will be deleted.', array(
|
||||||
|
'@url' => 'https://www.sitelocity.com/critical-path-css-generator',
|
||||||
|
)),
|
||||||
|
'#default_value' => '',
|
||||||
|
);
|
||||||
|
$form['add_item']['dns'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('Hostnames to lookup'),
|
||||||
|
'#description' => t('Hosts that will be connected to.'),
|
||||||
|
'#default_value' => '',
|
||||||
|
);
|
||||||
|
$form['add_item']['pre'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('Urls to Preload'),
|
||||||
|
'#description' => t('Assets for the browser that should be downloaded at a high priority.'),
|
||||||
|
'#default_value' => '',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Lookup saved data.
|
||||||
|
$query = db_select('advagg_critical_css', 'acc')
|
||||||
|
->fields('acc')
|
||||||
|
->comment('Query called from ' . __FUNCTION__ . '()');
|
||||||
|
$results = $query->execute();
|
||||||
|
// Put results into array.
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($results as $row) {
|
||||||
|
$counter++;
|
||||||
|
$row = (array) $row;
|
||||||
|
|
||||||
|
foreach ($form['add_item'] as $key => $values) {
|
||||||
|
// Fix the states array for type.
|
||||||
|
if (!empty($values['#states'])) {
|
||||||
|
foreach ($values['#states'] as $states_key => $states_values) {
|
||||||
|
$states_value = reset($values['#states'][$states_key]);
|
||||||
|
$values['#states'][$states_key] = array(":input[name=\"{$counter}_type\"]" => $states_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$form['existing_items'][$counter]["{$counter}_{$key}"] = $values;
|
||||||
|
if (isset($row[$key])) {
|
||||||
|
$form['existing_items'][$counter]["{$counter}_{$key}"]['#default_value'] = $row[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add in css to move the text hint up.
|
||||||
|
$form['#attached']['css'][] = array(
|
||||||
|
'data' => ".form-item-{$counter}-lookup{padding-bottom:0;margin-bottom:0;}",
|
||||||
|
'type' => 'inline',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add fieldset.
|
||||||
|
$filename = advagg_url_to_filename($row['lookup'], FALSE);
|
||||||
|
$base = drupal_get_path('theme', $row['theme']) . "/critical-css/{$row['user']}/";
|
||||||
|
if ($row['type'] == 2) {
|
||||||
|
$base .= "urls/$filename";
|
||||||
|
}
|
||||||
|
elseif ($row['type'] == 8) {
|
||||||
|
$base .= "type/$filename";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$base = '';
|
||||||
|
}
|
||||||
|
$form['existing_items'][$counter] += array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
'#title' => t('@type @theme @user @lookup', array(
|
||||||
|
'@theme' => $row['theme'],
|
||||||
|
'@type' => $type_options[$row['type']],
|
||||||
|
'@user' => $row['user'],
|
||||||
|
'@lookup' => $row['lookup'],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!empty($base)) {
|
||||||
|
$form['existing_items'][$counter]['#description'] = t('If you wish to store this configuration in a file<br>Critical CSS: <code>@css</code>', array(
|
||||||
|
'@css' => "$base.css",
|
||||||
|
));
|
||||||
|
if (!empty($row['dns'])) {
|
||||||
|
$form['existing_items'][$counter]['#description'] .= t('<br>Hostnames: <code>@dns</code>', array(
|
||||||
|
'@dns' => "$base.dns",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (!empty($row['pre'])) {
|
||||||
|
$form['existing_items'][$counter]['#description'] .= t('<br>Preload: <code>@pre</code>', array(
|
||||||
|
'@pre' => "$base.pre",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add top level fieldsets.
|
||||||
|
$form['add_item'] += array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Add Critical CSS'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => $results->rowCount(),
|
||||||
|
);
|
||||||
|
if (!empty($form['existing_items'])) {
|
||||||
|
$form['existing_items'] += array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Edit Critical CSS'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form['advagg_critical_css_selector_blacklist'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('Selector Blacklist'),
|
||||||
|
'#description' => t('Selectors to exclude. Enter one per line. Useful for things like google ads.'),
|
||||||
|
'#default_value' => variable_get('advagg_critical_css_selector_blacklist', ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Clear the cache bins on submit.
|
||||||
|
$form['#submit'][] = 'advagg_critical_css_admin_settings_form_submit';
|
||||||
|
|
||||||
|
// Most code below taken from system_settings_form().
|
||||||
|
$form['actions']['#type'] = 'actions';
|
||||||
|
$form['actions']['submit'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Save configuration'),
|
||||||
|
);
|
||||||
|
$form['actions']['disable'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Disable All From Database'),
|
||||||
|
'#submit' => array('advagg_critical_css_admin_settings_form_submit_disable'),
|
||||||
|
);
|
||||||
|
if (!empty($_POST) && form_get_errors()) {
|
||||||
|
drupal_set_message(t('The settings have not been saved because of the errors.'), 'error');
|
||||||
|
}
|
||||||
|
// By default, render the form using theme_system_settings_form().
|
||||||
|
if (!isset($form['#theme'])) {
|
||||||
|
$form['#theme'] = 'system_settings_form';
|
||||||
|
}
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback, process the advagg_critical_css form.
|
||||||
|
*
|
||||||
|
* Also clear out the advagg cache bin.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms_callback
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_admin_settings_form_submit_disable($form, &$form_state) {
|
||||||
|
$query = db_select('advagg_critical_css', 'acc')
|
||||||
|
->fields('acc')
|
||||||
|
->comment('Query called from ' . __FUNCTION__ . '()');
|
||||||
|
$results = $query->execute();
|
||||||
|
// Put results into array.
|
||||||
|
$insert_update = array();
|
||||||
|
foreach ($results as $row) {
|
||||||
|
$row = (array) $row;
|
||||||
|
$new_row = $row;
|
||||||
|
$new_row['type'] = 0;
|
||||||
|
$insert_update[] = array(
|
||||||
|
$row,
|
||||||
|
$new_row,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
advagg_critical_css_table_insert_update($insert_update);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback, process the advagg_critical_css form.
|
||||||
|
*
|
||||||
|
* Also clear out the advagg cache bin.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms_callback
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_admin_settings_form_submit($form, &$form_state) {
|
||||||
|
// Exclude unnecessary elements.
|
||||||
|
form_state_values_clean($form_state);
|
||||||
|
|
||||||
|
// Save advagg_critical_css_selector_blacklist.
|
||||||
|
if (!isset($form_state['values']['advagg_critical_css_selector_blacklist'])) {
|
||||||
|
$form_state['values']['advagg_critical_css_selector_blacklist'] = '';
|
||||||
|
}
|
||||||
|
$advagg_critical_css_selector_blacklist = variable_get('advagg_critical_css_selector_blacklist', '');
|
||||||
|
if ($form_state['values']['advagg_critical_css_selector_blacklist'] !== $advagg_critical_css_selector_blacklist) {
|
||||||
|
variable_set('advagg_critical_css_selector_blacklist', $form_state['values']['advagg_critical_css_selector_blacklist']);
|
||||||
|
}
|
||||||
|
unset($form_state['values']['advagg_critical_css_selector_blacklist']);
|
||||||
|
|
||||||
|
// Rearrange form values into key value pairs.
|
||||||
|
$items = advagg_critical_css_get_rows_from_form($form_state['values']);
|
||||||
|
// Get default values.
|
||||||
|
$default_values = advagg_find_all_recommended_admin_values($form_state['complete form'], '#default_value');
|
||||||
|
unset($default_values['form_token']);
|
||||||
|
$default_items = advagg_critical_css_get_rows_from_form($default_values);
|
||||||
|
|
||||||
|
// Get diff, see what items need to be saved.
|
||||||
|
$diff = advagg_diff_multi($default_items, $items);
|
||||||
|
$changed_items = array();
|
||||||
|
foreach ($diff as $key => $values) {
|
||||||
|
$changed_items[$key] = $items[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get items to insert/update and delete.
|
||||||
|
list($insert_update, $delete) = advagg_critical_css_get_db_operations_arrays($changed_items, $default_items);
|
||||||
|
advagg_critical_css_table_insert_update($insert_update);
|
||||||
|
advagg_critical_css_table_delete($delete);
|
||||||
|
|
||||||
|
// Clear caches.
|
||||||
|
advagg_cache_clear_admin_submit();
|
||||||
|
drupal_set_message(t('The configuration options have been saved.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate from state values into a nested array strucutre.
|
||||||
|
*
|
||||||
|
* @param array $form_state_values
|
||||||
|
* From state values; from $form_state['values'].
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Nested array strucutre, each index is a row in the db.
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_get_rows_from_form(array $form_state_values) {
|
||||||
|
$items = array();
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($form_state_values as $key => $values) {
|
||||||
|
// Get the index from the start of the form name.
|
||||||
|
$matches = array();
|
||||||
|
// 1_type turns into $counter = 1 and $key = type.
|
||||||
|
preg_match('/^(\d)_(.*)/', $key, $matches);
|
||||||
|
if (!empty($matches)) {
|
||||||
|
$counter = $matches[1];
|
||||||
|
$key = $matches[2];
|
||||||
|
}
|
||||||
|
$items[$counter][$key] = $values;
|
||||||
|
}
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a list of items see what ones need to be inserted/updated or deleted.
|
||||||
|
*
|
||||||
|
* @param array $items
|
||||||
|
* Array of values, representing a row in the db.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Nested array strucutre, index 0 is the insert update, 1 is the deleted.
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_get_db_operations_arrays(array $items, array $old_items) {
|
||||||
|
$insert_update = array();
|
||||||
|
$delete = array();
|
||||||
|
foreach ($items as $key => $values) {
|
||||||
|
// If the css is empty then this needs to be deleted.
|
||||||
|
if (empty($values['css'])) {
|
||||||
|
// Do not delete the new items entry (0); it's not in the db currently.
|
||||||
|
if (!empty($key)) {
|
||||||
|
$delete[$key] = $values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Pass along the old key value pairs for db_merge.
|
||||||
|
if (!empty($old_items[$key])) {
|
||||||
|
$keys = $old_items[$key] + $values;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$keys = $values;
|
||||||
|
}
|
||||||
|
$insert_update[$key] = array($keys, $values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array($insert_update, $delete);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
name = AdvAgg Critical CSS
|
||||||
|
description = Control Critical CSS via UI and/or a 3rd party service.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
dependencies[] = advagg_mod
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg/critical-css
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles AdvAgg Critical CSS installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_schema().
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_schema() {
|
||||||
|
// Create database table.
|
||||||
|
$schema['advagg_critical_css'] = array(
|
||||||
|
'description' => 'The critical css to inline.',
|
||||||
|
'fields' => array(
|
||||||
|
'theme' => array(
|
||||||
|
'description' => 'The theme name.',
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => 255,
|
||||||
|
'default' => '',
|
||||||
|
'binary' => TRUE,
|
||||||
|
),
|
||||||
|
'type' => array(
|
||||||
|
'description' => 'Type like url or node.',
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => 0,
|
||||||
|
),
|
||||||
|
'user' => array(
|
||||||
|
'description' => 'User Type or UID.',
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => 255,
|
||||||
|
'default' => '',
|
||||||
|
'binary' => TRUE,
|
||||||
|
),
|
||||||
|
'lookup' => array(
|
||||||
|
'description' => 'Value from current_path if url or node type if node.',
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => 255,
|
||||||
|
'default' => '',
|
||||||
|
'binary' => TRUE,
|
||||||
|
),
|
||||||
|
'css' => array(
|
||||||
|
'description' => 'Critical CSS.',
|
||||||
|
'type' => 'blob',
|
||||||
|
'size' => 'big',
|
||||||
|
),
|
||||||
|
'dns' => array(
|
||||||
|
'description' => 'Hosts for dns lookedup.',
|
||||||
|
'type' => 'blob',
|
||||||
|
'size' => 'big',
|
||||||
|
),
|
||||||
|
'pre' => array(
|
||||||
|
'description' => 'URLs for preloading.',
|
||||||
|
'type' => 'blob',
|
||||||
|
'size' => 'big',
|
||||||
|
),
|
||||||
|
'settings' => array(
|
||||||
|
'description' => 'Extra settings if desired.',
|
||||||
|
'type' => 'blob',
|
||||||
|
'size' => 'big',
|
||||||
|
'translatable' => TRUE,
|
||||||
|
'serialize' => TRUE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'primary key' => array(
|
||||||
|
'lookup',
|
||||||
|
'user',
|
||||||
|
'type',
|
||||||
|
'theme',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
@ -0,0 +1,239 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation critical css module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu().
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_menu() {
|
||||||
|
$file_path = drupal_get_path('module', 'advagg_critical_css');
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
$items[$config_path . '/advagg/critical-css'] = array(
|
||||||
|
'title' => 'Critical CSS',
|
||||||
|
'description' => 'Control critical css.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('advagg_critical_css_admin_settings_form'),
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file path' => $file_path,
|
||||||
|
'file' => 'advagg_critical_css.admin.inc',
|
||||||
|
'weight' => 10,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_module_implements_alter().
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_module_implements_alter(&$implementations, $hook) {
|
||||||
|
// Move critical_css_advagg_mod_critical_css_file_pre_alter to the bottom.
|
||||||
|
if ($hook === 'critical_css_advagg_mod_critical_css_file_pre_alter' && array_key_exists('advagg_critical_css', $implementations)) {
|
||||||
|
$item = $implementations['advagg_critical_css'];
|
||||||
|
unset($implementations['advagg_critical_css']);
|
||||||
|
$implementations['advagg_critical_css'] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_mod_critical_css_file_pre_alter().
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_advagg_mod_critical_css_file_pre_alter(&$filename, &$params, &$inline_strings) {
|
||||||
|
list($dirs, $front_page, $object) = $params;
|
||||||
|
|
||||||
|
// Build query parameters.
|
||||||
|
$lookup = array($dirs[6]);
|
||||||
|
if ($front_page) {
|
||||||
|
$lookup = array('<front>');
|
||||||
|
}
|
||||||
|
$lookup[] = $dirs[9];
|
||||||
|
$lookup[] = $dirs[10];
|
||||||
|
if (!empty($object->type)) {
|
||||||
|
$lookup[] = $object->type;
|
||||||
|
}
|
||||||
|
$type = array(2, 8);
|
||||||
|
$users = array(rtrim($dirs[2], '/\\'), rtrim($dirs[3], '/\\'));
|
||||||
|
|
||||||
|
// Get Results.
|
||||||
|
$result = advagg_critical_css_table_get($GLOBALS['theme'], $type, $lookup, $users);
|
||||||
|
|
||||||
|
// Put into the inline strings array.
|
||||||
|
if (!empty($result)) {
|
||||||
|
// Set string values.
|
||||||
|
$inline_strings[0] = $result['css'];
|
||||||
|
$inline_strings[1] = $result['dns'];
|
||||||
|
$inline_strings[2] = $result['pre'];
|
||||||
|
// Disable file lookup.
|
||||||
|
$dirs[0] = '';
|
||||||
|
$dirs[1] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repack the $params array.
|
||||||
|
$params = array($dirs, $front_page, $object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_mod_critical_css_file_post_alter().
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_advagg_mod_critical_css_file_post_alter(&$filename, &$params, &$inline_strings) {
|
||||||
|
if (!empty($inline_strings[0])) {
|
||||||
|
// Remove given css selectors.
|
||||||
|
$selectors = variable_get('advagg_critical_css_selector_blacklist', '');
|
||||||
|
$selectors_array = array_filter(array_map('trim', explode("\n", $selectors)));
|
||||||
|
foreach ($selectors_array as $pattern) {
|
||||||
|
$pattern = preg_quote($pattern, '/');
|
||||||
|
$pattern = "/([^}]*{$pattern}[^{]*[^}]*\})/s";
|
||||||
|
$inline_strings[0] = preg_replace($pattern, '', $inline_strings[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the db select return object.
|
||||||
|
*
|
||||||
|
* @param string $theme
|
||||||
|
* Name of the current theme.
|
||||||
|
* @param array $type
|
||||||
|
* Array of int types to lookup.
|
||||||
|
* @param array $lookup
|
||||||
|
* The lookup value.
|
||||||
|
* @param array $user
|
||||||
|
* Array of user string values.
|
||||||
|
*
|
||||||
|
* @return SelectQuery
|
||||||
|
* Return the SelectQuery object after it has been executed.
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_table_get($theme, array $type, array $lookup, array $user) {
|
||||||
|
$output = array();
|
||||||
|
try {
|
||||||
|
$results = db_select('advagg_critical_css', 'acc')
|
||||||
|
->fields('acc')
|
||||||
|
->condition('theme', $theme)
|
||||||
|
->condition('type', $type, 'IN')
|
||||||
|
->condition('user', $user, 'IN')
|
||||||
|
->condition('lookup', $lookup, 'IN')
|
||||||
|
->orderBy('type', 'DESC')
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
// Get first result.
|
||||||
|
$output = $results->fetchAssoc();
|
||||||
|
|
||||||
|
// Check for a better match in other results if they exist.
|
||||||
|
foreach ($results as $values) {
|
||||||
|
$values = (array) $values;
|
||||||
|
if ($values['type'] < $output['type']) {
|
||||||
|
$output = $values;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($values['type'] = $output['type']) {
|
||||||
|
if (($values['user'] === 'anonymous' || $values['user'] === 'authenticated')
|
||||||
|
&& $output['user'] === 'all'
|
||||||
|
) {
|
||||||
|
$output = $values;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (is_int($values['user'])) {
|
||||||
|
$output = $values;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
// Log the error if in development mode.
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) {
|
||||||
|
watchdog('advagg_critical_css', 'Development Mode - Caught PDO Exception: <code>@info</code>', array('@info' => $e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert/Update data in the advagg_critical_css table.
|
||||||
|
*
|
||||||
|
* @param array $records
|
||||||
|
* List of rows needed that need to be changed in the db.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Return array of booleans if anything was written to the database.
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_table_insert_update(array $records) {
|
||||||
|
$return = array();
|
||||||
|
foreach ($records as $values) {
|
||||||
|
list($keys, $record) = $values;
|
||||||
|
if (!isset($record['settings'])) {
|
||||||
|
$record['settings'] = '';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$return[] = db_merge('advagg_critical_css')
|
||||||
|
->key(array(
|
||||||
|
'theme' => $keys['theme'],
|
||||||
|
'user' => $keys['user'],
|
||||||
|
'type' => $keys['type'],
|
||||||
|
'lookup' => $keys['lookup'],
|
||||||
|
))
|
||||||
|
->fields($record)
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
// Log the error if in development mode.
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) {
|
||||||
|
watchdog('advagg_critical_css', 'Development Mode - Caught PDO Exception: <code>@info</code>', array('@info' => $e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete data in the advagg_critical_css table.
|
||||||
|
*
|
||||||
|
* @param array $records
|
||||||
|
* List of rows needed that need to be removed from the db.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Return array of booleans if anything was removed from the database.
|
||||||
|
*/
|
||||||
|
function advagg_critical_css_table_delete(array $records) {
|
||||||
|
$return = array();
|
||||||
|
foreach ($records as $record) {
|
||||||
|
try {
|
||||||
|
$return[] = db_delete('advagg_critical_css')
|
||||||
|
->condition('theme', $record['theme'])
|
||||||
|
->condition('user', $record['user'])
|
||||||
|
->condition('type', $record['type'])
|
||||||
|
->condition('lookup', $record['lookup'])
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
// Log the error if in development mode.
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) {
|
||||||
|
watchdog('advagg_critical_css', 'Development Mode - Caught PDO Exception: <code>@info</code>', array('@info' => $e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
name = AdvAgg CDN CSS
|
||||||
|
description = Use a shared CDN for CSS libraries, Google Libraries API currently.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles Advanced Aggregation installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_requirements().
|
||||||
|
*/
|
||||||
|
function advagg_css_cdn_requirements($phase) {
|
||||||
|
$requirements = array();
|
||||||
|
// Ensure translations don't break at install time.
|
||||||
|
$t = get_t();
|
||||||
|
|
||||||
|
// If not at runtime, return here.
|
||||||
|
if ($phase !== 'runtime') {
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module_exists('jquery_update')) {
|
||||||
|
$jquery_cdn = variable_get('jquery_update_jquery_cdn', 'none');
|
||||||
|
if ($jquery_cdn === 'none') {
|
||||||
|
$jquery_description = $t('The jquery update module has support for CDNs. Go to the <a href="@settings">jquery update settings page</a> and select a CDN instead of using this module.', array(
|
||||||
|
'@settings' => url('admin/config/development/jquery_update', array(
|
||||||
|
'fragment' => 'edit-jquery-update-jquery-cdn',
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$jquery_description = $t('The jquery update module is already configured to use the external CDN "@cdn".', array('@cdn' => $jquery_cdn));
|
||||||
|
}
|
||||||
|
|
||||||
|
$requirements['advagg_css_cdn_jquery_update'] = array(
|
||||||
|
'title' => $t('Adv CSS CDN - jquery update'),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('Duplicate Functionality: Use jquery update instead of the advagg_css_cdn sub module.'),
|
||||||
|
'description' => $jquery_description . ' ' . $t('You should go to the <a href="@modules">modules page</a> and disable the "AdvAgg CDN CSS" module.', array(
|
||||||
|
'@modules' => url('admin/modules', array(
|
||||||
|
'fragment' => 'edit-modules-advanced-cssjs-aggregation',
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($requirements)) {
|
||||||
|
$requirements['advagg_css_cdn'] = array(
|
||||||
|
'title' => $t('Adv CSS CDN'),
|
||||||
|
'severity' => REQUIREMENT_OK,
|
||||||
|
'value' => $t('OK'),
|
||||||
|
'description' => $t('jQuery UI CSS should be coming from a CDN.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
@ -0,0 +1,195 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation js cdn module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup default_variables
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to see if jquery-ui should be grabbed from the Google CDN.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_CSS_CDN_JQUERY_UI', TRUE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default jquery ui version.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_CSS_CDN_JQUERY_UI_VERSION', '1.8.7');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup default_variables".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_css_alter().
|
||||||
|
*/
|
||||||
|
function advagg_css_cdn_css_alter(&$css) {
|
||||||
|
// Only modify if jquery_update is not enabled.
|
||||||
|
if (module_exists('jquery_update')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup variables.
|
||||||
|
// jquery ui.
|
||||||
|
$jquery_ui_version = variable_get('advagg_css_cdn_jquery_ui_version', ADVAGG_CSS_CDN_JQUERY_UI_VERSION);
|
||||||
|
$ui_mapping = advagg_css_cdn_get_ui_mapping();
|
||||||
|
|
||||||
|
foreach ($css as $name => $values) {
|
||||||
|
// Only modify if
|
||||||
|
// advagg_css_cdn_jquery_ui is enabled,
|
||||||
|
// name is in the $ui_mapping array.
|
||||||
|
// and type is file.
|
||||||
|
if (variable_get('advagg_css_cdn_jquery_ui', ADVAGG_CSS_CDN_JQUERY_UI)
|
||||||
|
&& array_key_exists($name, $ui_mapping)
|
||||||
|
&& $css[$name]['type'] === 'file'
|
||||||
|
) {
|
||||||
|
$css[$name]['data'] = '//ajax.googleapis.com/ajax/libs/jqueryui/' . $jquery_ui_version . '/themes/base/jquery.' . $ui_mapping[$name] . '.css';
|
||||||
|
$css[$name]['type'] = 'external';
|
||||||
|
|
||||||
|
// Fallback does not work do to
|
||||||
|
// "SecurityError: The operation is insecure.".
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_css_groups_alter().
|
||||||
|
*/
|
||||||
|
function advagg_css_cdn_advagg_css_groups_alter(&$css_groups, $preprocess_css) {
|
||||||
|
// Work around a bug with seven_css_alter.
|
||||||
|
// http://drupal.org/node/1937860
|
||||||
|
$theme_keys[] = $GLOBALS['theme'];
|
||||||
|
if (!empty($GLOBALS['base_theme_info'])) {
|
||||||
|
foreach ($GLOBALS['base_theme_info'] as $base) {
|
||||||
|
$theme_keys[] = $base->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$match = FALSE;
|
||||||
|
foreach ($theme_keys as $name) {
|
||||||
|
if ($name === 'seven') {
|
||||||
|
$match = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($match)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$target = FALSE;
|
||||||
|
$last_group = FALSE;
|
||||||
|
$last_key = FALSE;
|
||||||
|
$kill_key = FALSE;
|
||||||
|
$replaced = FALSE;
|
||||||
|
foreach ($css_groups as $key => $group) {
|
||||||
|
if (empty($target)) {
|
||||||
|
if ($group['type'] === 'external' && $group['preprocess'] && $preprocess_css) {
|
||||||
|
foreach ($group['items'] as $k => $value) {
|
||||||
|
if ($value['data'] === 'themes/seven/jquery.ui.theme.css') {
|
||||||
|
// Type should be file and not external (core bug).
|
||||||
|
$value['type'] = 'file';
|
||||||
|
$target = $value;
|
||||||
|
unset($css_groups[$key]['items'][$k]);
|
||||||
|
if (empty($css_groups[$key]['items'])) {
|
||||||
|
unset($css_groups[$key]);
|
||||||
|
$kill_key = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$diff = array_merge(array_diff_assoc($group['browsers'], $target['browsers']), array_diff_assoc($target['browsers'], $group['browsers']));
|
||||||
|
if ($group['type'] != $target['type']
|
||||||
|
|| $group['group'] != $target['group']
|
||||||
|
|| $group['every_page'] != $target['every_page']
|
||||||
|
|| $group['media'] != $target['media']
|
||||||
|
|| $group['media'] != $target['media']
|
||||||
|
|| $group['preprocess'] != $target['preprocess']
|
||||||
|
|| !empty($diff)
|
||||||
|
) {
|
||||||
|
if (!empty($last_group)) {
|
||||||
|
$diff = array_merge(array_diff_assoc($last_group['browsers'], $target['browsers']), array_diff_assoc($target['browsers'], $last_group['browsers']));
|
||||||
|
if ($last_group['type'] != $target['type']
|
||||||
|
|| $last_group['group'] != $target['group']
|
||||||
|
|| $last_group['every_page'] != $target['every_page']
|
||||||
|
|| $last_group['media'] != $target['media']
|
||||||
|
|| $last_group['media'] != $target['media']
|
||||||
|
|| $last_group['preprocess'] != $target['preprocess']
|
||||||
|
|| !empty($diff)
|
||||||
|
) {
|
||||||
|
// Insert New.
|
||||||
|
$css_groups[$kill_key] = array(
|
||||||
|
'group' => $target['group'],
|
||||||
|
'type' => $target['type'],
|
||||||
|
'every_page' => $target['every_page'],
|
||||||
|
'media' => $target['media'],
|
||||||
|
'preprocess' => $target['preprocess'],
|
||||||
|
'browsers' => $target['browsers'],
|
||||||
|
'items' => array($target),
|
||||||
|
);
|
||||||
|
$replaced = TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Insert above.
|
||||||
|
$css_groups[$last_key]['items'][] = $target;
|
||||||
|
$replaced = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Insert below.
|
||||||
|
array_unshift($css_groups[$key]['items'], $target);
|
||||||
|
$replaced = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$last_group = $group;
|
||||||
|
$last_key = $key;
|
||||||
|
if ($replaced) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ksort($css_groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of jquery ui files.
|
||||||
|
*/
|
||||||
|
function advagg_css_cdn_get_ui_mapping() {
|
||||||
|
// Replace jQuery UI's CSS, beginning by defining the mapping.
|
||||||
|
$ui_mapping = array(
|
||||||
|
'misc/ui/jquery.ui.accordion.css' => 'ui.accordion',
|
||||||
|
'misc/ui/jquery.ui.autocomplete.css' => 'ui.autocomplete',
|
||||||
|
'misc/ui/jquery.ui.button.css' => 'ui.button',
|
||||||
|
'misc/ui/jquery.ui.core.css' => 'ui.core',
|
||||||
|
'misc/ui/jquery.ui.datepicker.css' => 'ui.datepicker',
|
||||||
|
'misc/ui/jquery.ui.dialog.css' => 'ui.dialog',
|
||||||
|
'misc/ui/jquery.ui.progressbar.css' => 'ui.progressbar',
|
||||||
|
'misc/ui/jquery.ui.resizable.css' => 'ui.resizable',
|
||||||
|
'misc/ui/jquery.ui.selectable.css' => 'ui.selectable',
|
||||||
|
'misc/ui/jquery.ui.slider.css' => 'ui.slider',
|
||||||
|
'misc/ui/jquery.ui.tabs.css' => 'ui.tabs',
|
||||||
|
'misc/ui/jquery.ui.theme.css' => 'ui.theme',
|
||||||
|
);
|
||||||
|
return $ui_mapping;
|
||||||
|
}
|
@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the advagg JS compression module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; Configure advagg settings.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*
|
||||||
|
* @see system_settings_form()
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_admin_settings_form($form, $form_state) {
|
||||||
|
drupal_set_title(t('AdvAgg: CSS Compression Settings'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
$form = array();
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) {
|
||||||
|
$form['advagg_devel_msg'] = array(
|
||||||
|
'#markup' => '<p>' . t('The settings below will not have any effect because AdvAgg is currently in <a href="@devel">development mode</a>. Once the cache settings have been set to normal or aggressive, CSS minification will take place.', array('@devel' => url($config_path . '/advagg', array('fragment' => 'edit-advagg-cache-level')))) . '</p>',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell user to update library if a new version is available.
|
||||||
|
$library_name = 'YUI-CSS-compressor-PHP-port';
|
||||||
|
$module_name = 'advagg_css_compress';
|
||||||
|
list($description) = advagg_get_version_description($library_name, $module_name);
|
||||||
|
if (!empty($description)) {
|
||||||
|
$form['advagg_version_msg'] = array(
|
||||||
|
'#markup' => "<p>{$description}</p>",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
list($options, $description) = advagg_css_compress_configuration();
|
||||||
|
|
||||||
|
$form['advagg_css_compressor'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('File Compression: Select a Compressor'),
|
||||||
|
'#default_value' => variable_get('advagg_css_compressor', ADVAGG_CSS_COMPRESSOR),
|
||||||
|
'#options' => $options,
|
||||||
|
'#description' => filter_xss($description),
|
||||||
|
);
|
||||||
|
$inline_options = $options;
|
||||||
|
unset($inline_options[-1]);
|
||||||
|
$inline_options[0] = t('Disabled');
|
||||||
|
$form['advagg_css_compress_inline'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Inline Compression: Select a Compressor'),
|
||||||
|
'#default_value' => variable_get('advagg_css_compress_inline', ADVAGG_CSS_COMPRESS_INLINE),
|
||||||
|
'#options' => $inline_options,
|
||||||
|
'#description' => filter_xss($description),
|
||||||
|
);
|
||||||
|
$form['advagg_css_compress_inline_if_not_cacheable'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Inline Compression: Use even if this page is not cacheable'),
|
||||||
|
'#default_value' => variable_get('advagg_css_compress_inline_if_not_cacheable', ADVAGG_CSS_COMPRESS_INLINE_IF_NOT_CACHEABLE),
|
||||||
|
'#description' => t('By checking this box, all Inline CSS will be compressed regardless of the state of <a href="@link">drupal_page_is_cacheable()</a>.', array('@link' => 'http://api.drupal.org/api/drupal/includes!bootstrap.inc/function/drupal_page_is_cacheable/7')),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
':input[name="advagg_css_compress_inline"]' => array('value' => "0"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$options[ADVAGG_CSS_COMPRESSOR_FILE_SETTINGS] = t('Default');
|
||||||
|
ksort($options);
|
||||||
|
|
||||||
|
$form['per_file_settings'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Per File Settings'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
);
|
||||||
|
// Get filename and filename_hash.
|
||||||
|
$results = db_select('advagg_files', 'af')
|
||||||
|
->fields('af', array('filename'))
|
||||||
|
->condition('filetype', 'css')
|
||||||
|
->orderBy('filename', 'ASC')
|
||||||
|
->execute();
|
||||||
|
$file_settings = variable_get('advagg_css_compressor_file_settings', array());
|
||||||
|
foreach ($results as $row) {
|
||||||
|
$dir = dirname($row->filename);
|
||||||
|
if (!isset($form['per_file_settings'][$dir])) {
|
||||||
|
$form['per_file_settings'][$dir] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => check_plain($dir),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$form_api_filename = str_replace(array('/', '.'), array('__', '--'), $row->filename);
|
||||||
|
$form['per_file_settings'][$dir]['advagg_css_compressor_file_settings_' . $form_api_filename] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('%filename: Select a Compressor', array('%filename' => $row->filename)),
|
||||||
|
'#default_value' => isset($file_settings[$form_api_filename]) ? $file_settings[$form_api_filename] : ADVAGG_CSS_COMPRESSOR_FILE_SETTINGS,
|
||||||
|
'#options' => $options,
|
||||||
|
);
|
||||||
|
if ($form['per_file_settings'][$dir]['advagg_css_compressor_file_settings_' . $form_api_filename]['#default_value'] != ADVAGG_CSS_COMPRESSOR_FILE_SETTINGS) {
|
||||||
|
$form['per_file_settings'][$dir]['#collapsed'] = FALSE;
|
||||||
|
$form['per_file_settings']['#collapsed'] = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No css files are found.
|
||||||
|
if (empty($results)) {
|
||||||
|
$form['per_file_settings']['#description'] = t('No CSS files have been aggregated. You need to enable aggregation. No css files where found in the advagg_files table.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the cache bins on submit.
|
||||||
|
$form['#submit'][] = 'advagg_css_compress_admin_settings_form_submit';
|
||||||
|
|
||||||
|
return system_settings_form($form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback that clears out the advagg cache bin.
|
||||||
|
*
|
||||||
|
* Also remove default settings inside of the per_file_settings fieldgroup.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms_callback
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_admin_settings_form_submit($form, &$form_state) {
|
||||||
|
// Clear caches.
|
||||||
|
advagg_cache_clear_admin_submit();
|
||||||
|
|
||||||
|
// Get current defaults.
|
||||||
|
$file_settings = variable_get('advagg_css_compressor_file_settings', array());
|
||||||
|
|
||||||
|
// Save per file settings.
|
||||||
|
$new_settings = array();
|
||||||
|
foreach ($form_state['values'] as $key => $value) {
|
||||||
|
// Skip if not advagg_css_compressor_file_settings.
|
||||||
|
if (strpos($key, 'advagg_css_compressor_file_settings_') === FALSE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Do not process default settings.
|
||||||
|
if ($value == ADVAGG_CSS_COMPRESSOR_FILE_SETTINGS) {
|
||||||
|
unset($form_state['values'][$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$new_settings[substr($key, 36)] = $value;
|
||||||
|
|
||||||
|
// Do not save this field into its own variable.
|
||||||
|
unset($form_state['values'][$key]);
|
||||||
|
}
|
||||||
|
if (!empty($new_settings) || !empty($file_settings)) {
|
||||||
|
if (empty($new_settings)) {
|
||||||
|
variable_del('advagg_css_compressor_file_settings');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
variable_set('advagg_css_compressor_file_settings', $new_settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation css compression module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_get_css_file_contents_pre_alter().
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_advagg_get_css_file_contents_pre_alter(&$file, &$optimize, &$aggregate_settings) {
|
||||||
|
// Get per file settings.
|
||||||
|
if (!empty($aggregate_settings['variables']['advagg_css_compressor_file_settings'])) {
|
||||||
|
$form_api_filename = str_replace(array('/', '.'), array('__', '--'), $file);
|
||||||
|
if (isset($aggregate_settings['variables']['advagg_css_compressor_file_settings'][$form_api_filename])) {
|
||||||
|
$aggregate_settings['variables']['advagg_css_compressor'] = $aggregate_settings['variables']['advagg_css_compressor_file_settings'][$form_api_filename];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aggregate_settings['variables']['advagg_css_compressor']) && $aggregate_settings['variables']['advagg_css_compressor'] == -1) {
|
||||||
|
$optimize = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_get_css_aggregate_contents_alter().
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_advagg_get_css_aggregate_contents_alter(&$data, $files, $aggregate_settings) {
|
||||||
|
// Get per file settings.
|
||||||
|
foreach ($files as $filename => $settings) {
|
||||||
|
if (!empty($aggregate_settings['variables']['advagg_css_compressor_file_settings'])) {
|
||||||
|
$form_api_filename = str_replace(array('/', '.'), array('__', '--'), $filename);
|
||||||
|
if (isset($aggregate_settings['variables']['advagg_css_compressor_file_settings'][$form_api_filename])) {
|
||||||
|
$aggregate_settings['variables']['advagg_css_compressor'] = $aggregate_settings['variables']['advagg_css_compressor_file_settings'][$form_api_filename];
|
||||||
|
// If one file can not be compressed then the whole aggregrate can not
|
||||||
|
// be compressed.
|
||||||
|
if ($aggregate_settings['variables']['advagg_css_compressor'] == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing if the compressor is disabled.
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_css_compressor'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Do nothing if the cache settings are set to Development.
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list(, , , $functions) = advagg_css_compress_configuration();
|
||||||
|
|
||||||
|
if (isset($functions[$aggregate_settings['variables']['advagg_css_compressor']])) {
|
||||||
|
$run = $functions[$aggregate_settings['variables']['advagg_css_compressor']];
|
||||||
|
if (function_exists($run)) {
|
||||||
|
$functions[$aggregate_settings['variables']['advagg_css_compressor']]($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the CSSmin library from YUI to compress the CSS.
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_yui_cssmin(&$data) {
|
||||||
|
// Try libraries for YUI.
|
||||||
|
if (is_callable('libraries_load')) {
|
||||||
|
libraries_load('YUI-CSS-compressor-PHP-port');
|
||||||
|
if (class_exists('tubalmartin\CssMin\Minifier')) {
|
||||||
|
// The "use" alias requires php 5.3.
|
||||||
|
// @codingStandardsIgnoreLine
|
||||||
|
$cssmin = new tubalmartin\CssMin\Minifier();
|
||||||
|
}
|
||||||
|
elseif (class_exists('CSSmin')) {
|
||||||
|
$cssmin = new CSSmin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isset($cssmin)) {
|
||||||
|
// Load CSSMin.inc if the CSSmin class variable is not set.
|
||||||
|
if (!class_exists('CSSmin')) {
|
||||||
|
include drupal_get_path('module', 'advagg_css_compress') . '/yui/CSSMin.inc';
|
||||||
|
}
|
||||||
|
$cssmin = new CSSmin();
|
||||||
|
}
|
||||||
|
if (!isset($cssmin)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set line break to 4k of text.
|
||||||
|
if (method_exists($cssmin, 'setLineBreakPosition')) {
|
||||||
|
$cssmin->setLineBreakPosition(4096);
|
||||||
|
}
|
||||||
|
// Compress the CSS splitting lines after 4k of text.
|
||||||
|
if (method_exists($cssmin, 'run')) {
|
||||||
|
$compressed = $cssmin->run($data, 4096);
|
||||||
|
}
|
||||||
|
if (!empty($compressed)) {
|
||||||
|
$data = $compressed;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
name = AdvAgg Compress CSS
|
||||||
|
description = Compress CSS with a 3rd party compressor, YUI currently.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
recommends[] = libraries
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg/css-compress
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles AdvAgg CSS compress installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_requirements().
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_requirements($phase) {
|
||||||
|
$requirements = array();
|
||||||
|
// Ensure translations don't break at install time.
|
||||||
|
$t = get_t();
|
||||||
|
|
||||||
|
// If not at runtime, return here.
|
||||||
|
if ($phase !== 'runtime') {
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure a compressor is being used.
|
||||||
|
if (variable_get('advagg_css_compressor', ADVAGG_CSS_COMPRESSOR) == 0
|
||||||
|
&& variable_get('advagg_css_compress_inline', ADVAGG_CSS_COMPRESS_INLINE) == 0
|
||||||
|
) {
|
||||||
|
// Check all files.
|
||||||
|
$file_settings = variable_get('advagg_css_compressor_file_settings', array());
|
||||||
|
$compression_used = FALSE;
|
||||||
|
foreach ($file_settings as $setting) {
|
||||||
|
if (!empty($setting)) {
|
||||||
|
$compression_used = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$compression_used) {
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
$requirements['advagg_css_compress_not_on'] = array(
|
||||||
|
'title' => $t('AdvAgg CSS Compressor'),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('AdvAgg CSS Compression is disabled.'),
|
||||||
|
'description' => $t('Go to the <a href="@settings">advagg css compress settings page</a> and select a compressor, or go to the <a href="@modules">modules page</a> and disable the "AdvAgg Compress CSS" module.', array(
|
||||||
|
'@settings' => url($config_path . '/advagg/css-compress'),
|
||||||
|
'@modules' => url('admin/modules', array(
|
||||||
|
'fragment' => 'edit-modules-advanced-cssjs-aggregation',
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check version.
|
||||||
|
$lib_name = 'YUI-CSS-compressor-PHP-port';
|
||||||
|
$module_name = 'advagg_css_compress';
|
||||||
|
list($description, $info) = advagg_get_version_description($lib_name, $module_name);
|
||||||
|
if (!empty($description)) {
|
||||||
|
$requirements["{$module_name}_{$lib_name}_updates"] = array(
|
||||||
|
'title' => $t('@module_name', array('@module_name' => $info['name'])),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('The @name library needs to be updated.', array('@name' => $lib_name)),
|
||||||
|
'description' => $description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade AdvAgg CSS Compress versions (6.x-1.x and 7.x-1.x) to 7.x-2.x.
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_update_7200(&$sandbox) {
|
||||||
|
// Bail if old DB Table does not exist.
|
||||||
|
if (!db_table_exists('cache_advagg_css_compress_inline')) {
|
||||||
|
return t('Nothing needed to happen in AdvAgg CSS Compress.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all old advagg css compress variables.
|
||||||
|
db_delete('variable')
|
||||||
|
->condition('name', 'advagg_css%compress%', 'LIKE')
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
// Remove old schema.
|
||||||
|
db_drop_table('cache_advagg_css_compress_inline');
|
||||||
|
|
||||||
|
return t('Upgraded AdvAgg CSS Compress to 7.x-2.x.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change variable names so they are prefixed with the modules name.
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_update_7201(&$sandbox) {
|
||||||
|
// Rename advagg_css_inline_compressor to advagg_css_compress_inline.
|
||||||
|
$old = variable_get('advagg_css_inline_compressor', NULL);
|
||||||
|
if (!is_null($old)) {
|
||||||
|
variable_del('advagg_css_inline_compressor');
|
||||||
|
}
|
||||||
|
if ($old !== ADVAGG_CSS_COMPRESS_INLINE) {
|
||||||
|
variable_set('advagg_css_compress_inline', $old);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename advagg_css_inline_compress_if_not_cacheable to
|
||||||
|
// advagg_css_compress_inline_if_not_cacheable.
|
||||||
|
$old = variable_get('advagg_css_inline_compress_if_not_cacheable', NULL);
|
||||||
|
if (!is_null($old)) {
|
||||||
|
variable_del('advagg_css_inline_compress_if_not_cacheable');
|
||||||
|
}
|
||||||
|
if ($old !== ADVAGG_CSS_COMPRESS_INLINE_IF_NOT_CACHEABLE) {
|
||||||
|
variable_set('advagg_css_compress_inline_if_not_cacheable', $old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove unused variables from the variable table.
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_update_7202(&$sandbox) {
|
||||||
|
// Remove all old advagg css compress variables.
|
||||||
|
db_delete('variable')
|
||||||
|
->condition('name', 'advagg_css_compressor_file_settings_%', 'LIKE')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
@ -0,0 +1,231 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation css compression module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup default_variables
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for which css compression library to use. 0 is Disabled.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_CSS_COMPRESSOR', 2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to see what compressor to use. 0 is Disabled.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_CSS_COMPRESS_INLINE', 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to if inline compression is used if page is not cacheable.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_CSS_COMPRESS_INLINE_IF_NOT_CACHEABLE', FALSE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for per file compression settings.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_CSS_COMPRESSOR_FILE_SETTINGS', -10);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup default_variables".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu().
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_menu() {
|
||||||
|
$file_path = drupal_get_path('module', 'advagg_css_compress');
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
$items[$config_path . '/advagg/css-compress'] = array(
|
||||||
|
'title' => 'CSS Compression',
|
||||||
|
'description' => 'Adjust CSS Compression settings.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('advagg_css_compress_admin_settings_form'),
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file path' => $file_path,
|
||||||
|
'file' => 'advagg_css_compress.admin.inc',
|
||||||
|
'weight' => 10,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_current_hooks_hash_array_alter().
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_advagg_current_hooks_hash_array_alter(&$aggregate_settings) {
|
||||||
|
$aggregate_settings['variables']['advagg_css_compressor'] = variable_get('advagg_css_compressor', ADVAGG_CSS_COMPRESSOR);
|
||||||
|
$aggregate_settings['variables']['advagg_css_compressor_file_settings'] = variable_get('advagg_css_compressor_file_settings', array());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_modify_css_pre_render_alter().
|
||||||
|
*
|
||||||
|
* Used to compress inline css.
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_advagg_modify_css_pre_render_alter(&$children, &$elements) {
|
||||||
|
// Get variables.
|
||||||
|
$compressor = variable_get('advagg_css_compress_inline', ADVAGG_CSS_COMPRESS_INLINE);
|
||||||
|
|
||||||
|
// Do nothing if the compressor is disabled.
|
||||||
|
if (empty($compressor)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing if the page is not cacheable and inline compress if not
|
||||||
|
// cacheable is not checked.
|
||||||
|
if (!variable_get('advagg_css_compress_inline_if_not_cacheable', ADVAGG_CSS_COMPRESS_INLINE_IF_NOT_CACHEABLE) && !drupal_page_is_cacheable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_load_include('inc', 'advagg_css_compress', 'advagg_css_compress.advagg');
|
||||||
|
if ($compressor == 2) {
|
||||||
|
// Compress any inline CSS with YUI.
|
||||||
|
foreach ($children as &$values) {
|
||||||
|
if (!empty($values['#value'])) {
|
||||||
|
advagg_css_compress_yui_cssmin($values['#value']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup 3rd_party_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_libraries_info().
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_libraries_info() {
|
||||||
|
$libraries['YUI-CSS-compressor-PHP-port'] = array(
|
||||||
|
// Only used in administrative UI of Libraries API.
|
||||||
|
'name' => 'YUI CSS compressor PHP port',
|
||||||
|
'vendor url' => 'https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port',
|
||||||
|
'download url' => 'https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port/archive/master.zip',
|
||||||
|
'version callback' => 'advagg_css_compress_libraries_get_version',
|
||||||
|
'version arguments' => array(
|
||||||
|
'file' => 'README.md',
|
||||||
|
'pattern' => '/###\s+v([0-9a-zA-Z\.-]+)/',
|
||||||
|
'lines' => 1000,
|
||||||
|
'cols' => 20,
|
||||||
|
'default_version' => '2.4.8',
|
||||||
|
),
|
||||||
|
'local version' => '2.4.8-p10',
|
||||||
|
'remote' => array(
|
||||||
|
'callback' => 'advagg_get_github_version_txt',
|
||||||
|
'url' => 'https://cdn.jsdelivr.net/gh/tubalmartin/YUI-CSS-compressor-PHP-port@master/README.md',
|
||||||
|
),
|
||||||
|
'versions' => array(
|
||||||
|
'2' => array(
|
||||||
|
'files' => array(
|
||||||
|
'php' => array(
|
||||||
|
'cssmin.php',
|
||||||
|
'data/hex-to-named-color-map.php',
|
||||||
|
'data/named-to-hex-color-map.php',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'3' => array(
|
||||||
|
'files' => array(
|
||||||
|
'php' => array(
|
||||||
|
'src/Minifier.php',
|
||||||
|
'src/Utils.php',
|
||||||
|
'src/Colors.php',
|
||||||
|
'src/data/hex-to-named-color-map.php',
|
||||||
|
'src/data/named-to-hex-color-map.php',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'4' => array(
|
||||||
|
'files' => array(
|
||||||
|
'php' => array(
|
||||||
|
'src/Minifier.php',
|
||||||
|
'src/Utils.php',
|
||||||
|
'src/Colors.php',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup 3rd_party_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try libraries_get_version(), on failure use the passed in default_version.
|
||||||
|
*
|
||||||
|
* @param array $library
|
||||||
|
* An associative array containing all information about the library.
|
||||||
|
* @param array $options
|
||||||
|
* An associative array containing options for the version parser.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Version number.
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_libraries_get_version(array $library, array $options) {
|
||||||
|
$return = libraries_get_version($library, $options);
|
||||||
|
if (empty($return) && !empty($options['default_version'])) {
|
||||||
|
$file = DRUPAL_ROOT . '/' . $library['library path'] . '/' . $options['file'];
|
||||||
|
if (is_readable($file)) {
|
||||||
|
return $options['default_version'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the js compress configuration.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array($options, $description, $compressors, $functions).
|
||||||
|
*/
|
||||||
|
function advagg_css_compress_configuration() {
|
||||||
|
$description = '';
|
||||||
|
$options = array(
|
||||||
|
-1 => t('Disable Core'),
|
||||||
|
0 => t('Core'),
|
||||||
|
2 => t('YUI'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$compressors = array();
|
||||||
|
$functions = array(
|
||||||
|
2 => 'advagg_css_compress_yui_cssmin',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Allow for other modules to alter this list.
|
||||||
|
$options_desc = array($options, $description);
|
||||||
|
drupal_alter('advagg_css_compress_configuration', $options_desc, $compressors, $functions);
|
||||||
|
list($options, $description) = $options_desc;
|
||||||
|
|
||||||
|
return array($options, $description, $compressors, $functions);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the advagg external compression module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; Configure advagg settings.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*
|
||||||
|
* @see system_settings_form()
|
||||||
|
*/
|
||||||
|
function advagg_ext_compress_admin_settings_form($form, $form_state) {
|
||||||
|
drupal_set_title(t('AdvAgg: External Compressor'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
|
||||||
|
$form = array();
|
||||||
|
// CSS command line.
|
||||||
|
advagg_ext_compress_admin_cmd_generate($form, array('css', t('CSS')));
|
||||||
|
// JS command line.
|
||||||
|
advagg_ext_compress_admin_cmd_generate($form, array('js', t('JavaScript')));
|
||||||
|
|
||||||
|
return system_settings_form($form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a form for css or js depending on the input.
|
||||||
|
*
|
||||||
|
* @param array $form
|
||||||
|
* The form array to add to.
|
||||||
|
* @param array $params
|
||||||
|
* An array where key 0 is the machine name and key 1 is the title.
|
||||||
|
*/
|
||||||
|
function advagg_ext_compress_admin_cmd_generate(array &$form, array $params) {
|
||||||
|
$form[$params[0]] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('@title', array('@title' => $params[1])),
|
||||||
|
);
|
||||||
|
$form[$params[0]]['cmd'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Command Line'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$description = t('{%CWD%} = DRUPAL_ROOT. <br /> {%IN%} = input file. <br /> {%IN_URL_ENC%} = url pointing to the input file that has been url encoded. <br /> {%OUT%} = output file. <br /><br />');
|
||||||
|
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
||||||
|
$description .= ' ' . t('Example using the <a href="@link1">Microsoft Ajax Minifier</a>. <p><code>@code1</code></p>', array(
|
||||||
|
'@link1' => 'http://ajaxmin.codeplex.com/',
|
||||||
|
'@code1' => 'AjaxMinifier {%IN%} -o {%OUT%}',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($params[0] === 'js') {
|
||||||
|
$description .= ' ' . t('Example using the <a href="@link1">Google Closure Compiler</a>. <p><code>@code1</code></p>', array(
|
||||||
|
'@link1' => 'https://developers.google.com/closure/compiler/docs/gettingstarted_app',
|
||||||
|
'@code1' => 'java -jar compiler.jar --js {%CWD%}/{%IN%} --js_output_file {%OUT%}',
|
||||||
|
));
|
||||||
|
|
||||||
|
$description .= ' ' . t('Example using curl to compress via the <a href="@link1">Online Google Closure Compiler</a>. <p><code>@code1</code></p>', array(
|
||||||
|
'@link1' => 'https://developers.google.com/closure/compiler/docs/api-ref',
|
||||||
|
'@code1' => 'curl -o {%OUT%} -d output_info=compiled_code -d code_url={%IN_URL_ENC%} http://closure-compiler.appspot.com/compile',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if ($params[0] === 'css') {
|
||||||
|
$description .= ' ' . t('Example using the <a href="@link1">YUI Compressor</a>. <p><code>@code1</code></p>', array(
|
||||||
|
'@link1' => 'http://yui.github.io/yuicompressor/',
|
||||||
|
'@code1' => 'java -jar yuicompressor-x.y.z.jar --type css --line-break 4096 {%CWD%}/{%IN%} -o {%OUT%}',
|
||||||
|
));
|
||||||
|
|
||||||
|
$description .= ' ' . t('Example using curl to compress via an online <a href="@link1">CSS Compressor</a>. <p><code>@code1</code></p>', array(
|
||||||
|
'@link1' => 'http://cnvyr.io/',
|
||||||
|
'@code1' => 'curl -o {%OUT%} -F \'files0=@{%IN%}\' http://srv.cnvyr.io/v1?min=css',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$form[$params[0]]['cmd']['advagg_ext_compress_' . $params[0] . '_cmd'] = array(
|
||||||
|
'#type' => 'textfield',
|
||||||
|
'#title' => t('Command to run'),
|
||||||
|
'#default_value' => variable_get('advagg_ext_compress_' . $params[0] . '_cmd', ''),
|
||||||
|
'#description' => $description,
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
name = AdvAgg External Compression
|
||||||
|
description = Compress Javascript and/or CSS with a command line compressor.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg/ext-compress
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,241 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced CSS/JS aggregation external compression module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu().
|
||||||
|
*/
|
||||||
|
function advagg_ext_compress_menu() {
|
||||||
|
$file_path = drupal_get_path('module', 'advagg_ext_compress');
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
$items[$config_path . '/advagg/ext-compress'] = array(
|
||||||
|
'title' => 'External Compression',
|
||||||
|
'description' => 'Adjust External Compression settings.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('advagg_ext_compress_admin_settings_form'),
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file path' => $file_path,
|
||||||
|
'file' => 'advagg_ext_compress.admin.inc',
|
||||||
|
'weight' => 10,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_js_compress_configuration_alter().
|
||||||
|
*/
|
||||||
|
function advagg_ext_compress_advagg_js_compress_configuration_alter(&$options_desc, &$compressors, &$functions) {
|
||||||
|
list($options, $description) = $options_desc;
|
||||||
|
|
||||||
|
$key = 10;
|
||||||
|
while (isset($options[$key])) {
|
||||||
|
$key++;
|
||||||
|
}
|
||||||
|
$options[$key] = t('AdvAgg Command Line Compressor');
|
||||||
|
$compressors[$key] = 'advagg_cmdline';
|
||||||
|
$functions[$key] = 'advagg_ext_compress_js_compress';
|
||||||
|
|
||||||
|
$options_desc = array($options, $description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_css_compress_configuration_alter().
|
||||||
|
*/
|
||||||
|
function advagg_ext_compress_advagg_css_compress_configuration_alter(&$options_desc, &$compressors, &$functions) {
|
||||||
|
list($options, $description) = $options_desc;
|
||||||
|
|
||||||
|
$key = 10;
|
||||||
|
while (isset($options[$key])) {
|
||||||
|
$key++;
|
||||||
|
}
|
||||||
|
$options[$key] = t('AdvAgg Command Line Compressor');
|
||||||
|
$functions[$key] = 'advagg_ext_compress_css_compress';
|
||||||
|
|
||||||
|
$options_desc = array($options, $description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress Javascript using via command line.
|
||||||
|
*
|
||||||
|
* @param string $input_file
|
||||||
|
* The file containing the uncompressed css/js data.
|
||||||
|
* @param string $ext
|
||||||
|
* The string css or js.
|
||||||
|
* @param array $debug
|
||||||
|
* Optional debug array.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The filename containing the compressed css/js data.
|
||||||
|
*/
|
||||||
|
function advagg_ext_compress_execute_cmd($input_file, $ext = '', array &$debug = array()) {
|
||||||
|
$run = variable_get("advagg_ext_compress_{$ext}_cmd", '');
|
||||||
|
if (empty($run)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get file extension.
|
||||||
|
if (empty($ext)) {
|
||||||
|
$ext = strtolower(pathinfo($input_file, PATHINFO_EXTENSION));
|
||||||
|
if ($ext !== 'css' && $ext !== 'js') {
|
||||||
|
// Get the $ext from the database.
|
||||||
|
$row = db_select('advagg_files', 'af')
|
||||||
|
->fields('af')
|
||||||
|
->condition('filename', $input_file)
|
||||||
|
->execute()->fetchAssoc();
|
||||||
|
if (!empty($row['filetype'])) {
|
||||||
|
$ext = $row['filetype'];
|
||||||
|
}
|
||||||
|
if ($ext === 'less') {
|
||||||
|
$ext = 'css';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate temp file.
|
||||||
|
$temp_file = drupal_tempnam('temporary://', 'advagg_file_');
|
||||||
|
$new_temp_file = $temp_file . '.' . basename($input_file);
|
||||||
|
@rename($temp_file, $new_temp_file);
|
||||||
|
// Set the permissions on the temp file.
|
||||||
|
drupal_chmod($new_temp_file);
|
||||||
|
$output = advagg_get_relative_path($new_temp_file);
|
||||||
|
|
||||||
|
// Create command to run.
|
||||||
|
$cmd = str_replace(array(
|
||||||
|
'{%CWD%}',
|
||||||
|
'{%IN%}',
|
||||||
|
'{%IN_URL_ENC%}',
|
||||||
|
'{%OUT%}',
|
||||||
|
), array(
|
||||||
|
DRUPAL_ROOT,
|
||||||
|
$input_file,
|
||||||
|
urlencode(file_create_url($input_file)),
|
||||||
|
escapeshellarg(realpath($output)),
|
||||||
|
), $run);
|
||||||
|
|
||||||
|
// Run command and return the output file.
|
||||||
|
$shell_output = array();
|
||||||
|
$return_var = 0;
|
||||||
|
$shell = exec($cmd, $shell_output, $return_var);
|
||||||
|
$debug = array($cmd, $shell_output, $return_var, $shell);
|
||||||
|
|
||||||
|
// Cleanup leftover files.
|
||||||
|
if (file_exists($temp_file)) {
|
||||||
|
@unlink($temp_file);
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress Javascript using via command line.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* The JavaScript to compress.
|
||||||
|
* @param bool $log_errors
|
||||||
|
* TRUE to log errors.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* FALSE if this failed.
|
||||||
|
*/
|
||||||
|
function advagg_ext_compress_js_compress(&$contents, $log_errors) {
|
||||||
|
return advagg_ext_compress_string($contents, 'js', $log_errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress CSS using via command line.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* The CSS to compress.
|
||||||
|
* @param bool $log_errors
|
||||||
|
* TRUE to log errors.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* FALSE if this failed.
|
||||||
|
*/
|
||||||
|
function advagg_ext_compress_css_compress(&$contents, $log_errors) {
|
||||||
|
return advagg_ext_compress_string($contents, 'css', $log_errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress CSS using via command line.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* The data to compress.
|
||||||
|
* @param string $type
|
||||||
|
* Should be css or js.
|
||||||
|
* @param bool $log_errors
|
||||||
|
* TRUE to log errors.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE on success.
|
||||||
|
*/
|
||||||
|
function advagg_ext_compress_string(&$contents, $type, $log_errors) {
|
||||||
|
list($css_path, $js_path) = advagg_get_root_files_dir();
|
||||||
|
if ($type === 'css') {
|
||||||
|
$dir = $css_path[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$dir = $js_path[0];
|
||||||
|
}
|
||||||
|
$new_temp_file = $dir . '/advagg_file_' . drupal_hash_base64(microtime(TRUE) . mt_rand()) . '.' . $type;
|
||||||
|
$temp_file_full = advagg_get_relative_path($new_temp_file);
|
||||||
|
|
||||||
|
file_put_contents($new_temp_file, $contents);
|
||||||
|
// Set the permissions on the temp file.
|
||||||
|
drupal_chmod($new_temp_file);
|
||||||
|
$debug = array();
|
||||||
|
$output = advagg_ext_compress_execute_cmd($temp_file_full, $type, $debug);
|
||||||
|
if (empty($output)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
$new_contents = advagg_file_get_contents($output);
|
||||||
|
if (strpos($new_contents, 'Error') === 0) {
|
||||||
|
if ($log_errors) {
|
||||||
|
watchdog('advagg_ext_compress', "@a \n<br>\n<br> @b", array(
|
||||||
|
// Only log 4k of data.
|
||||||
|
'@a' => substr($new_contents, 0, 4096),
|
||||||
|
'@b' => print_r($debug, TRUE),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
$return = FALSE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$contents = $new_contents;
|
||||||
|
$return = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup.
|
||||||
|
if (file_exists($new_temp_file)) {
|
||||||
|
unlink($new_temp_file);
|
||||||
|
}
|
||||||
|
if (file_exists($temp_file_full)) {
|
||||||
|
unlink($temp_file_full);
|
||||||
|
}
|
||||||
|
if (file_exists($output)) {
|
||||||
|
unlink($output);
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
@ -0,0 +1,209 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the advagg font module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; Configure advagg settings.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*
|
||||||
|
* @see system_settings_form()
|
||||||
|
*/
|
||||||
|
function advagg_font_admin_settings_form() {
|
||||||
|
drupal_set_title(t('AdvAgg: Async Font Loader'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
$form = array();
|
||||||
|
|
||||||
|
$library = advagg_get_library('fontfaceobserver', 'advagg_font');
|
||||||
|
$version = advagg_get_remote_libraries_version('fontfaceobserver', $library);
|
||||||
|
$options = array(
|
||||||
|
0 => t('Disabled'),
|
||||||
|
6 => t('Externally load the latest from github (version: @version)', array('@version' => $version)),
|
||||||
|
);
|
||||||
|
$description = t('This will use the fallback font until the font has been downloaded. See <a href="@link">fontfaceobserver</a> for more info.', array(
|
||||||
|
'@link' => $library['vendor url'],
|
||||||
|
));
|
||||||
|
if (function_exists('libraries_info')) {
|
||||||
|
if ($library['installed']) {
|
||||||
|
$options += array(
|
||||||
|
2 => t('Inline javascript (version: @version)', array('@version' => $library['version'])),
|
||||||
|
4 => t('Local file included in aggregate (version: @version)', array('@version' => $library['version'])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
elseif (!is_readable('sites/all/libraries/fontfaceobserver/fontfaceobserver.js')) {
|
||||||
|
$description .= ' ' . t('To use fontfaceobserver locally fontfaceobserver needs to be placed inside the sites/all/libraries directory so sites/all/libraries/fontfaceobserver/fontfaceobserver.js and package.json can be found at that location.', array(
|
||||||
|
'@url' => 'https://www.drupal.org/project/libraries',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$description .= ' ' . t('Go to the <a href="@url">library report page</a> and make sure fontfaceobserver is installed correctly.', array(
|
||||||
|
'@url' => url('admin/reports/libraries'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif (is_readable('sites/all/libraries/fontfaceobserver/fontfaceobserver.js')) {
|
||||||
|
$description .= ' ' . t('To use fontfaceobserver locally the <a href="@url">libraries api module</a> needs to be installed.', array(
|
||||||
|
'@url' => 'https://www.drupal.org/project/libraries',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$description .= ' ' . t('To use fontfaceobserver locally the <a href="@url">libraries api module</a> needs to be installed and then fontfaceobserver needs to be placed inside the sites/all/libraries directory so sites/all/libraries/fontfaceobserver/fontfaceobserver.js and package.json can be found at that location.', array(
|
||||||
|
'@url' => 'https://www.drupal.org/project/libraries',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($options);
|
||||||
|
$form['advagg_font_fontfaceobserver'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Use font face observer to load fonts asynchronously.'),
|
||||||
|
'#default_value' => variable_get('advagg_font_fontfaceobserver', ADVAGG_FONT_FONTFACEOBSERVER),
|
||||||
|
'#options' => $options,
|
||||||
|
'#description' => $description,
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['container'] = array(
|
||||||
|
'#type' => 'container',
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
':input[name="advagg_font_fontfaceobserver"]' => array('value' => '0'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['container']['advagg_font_storage'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Use localStorage so the flash of unstyled text (FOUT) only happens once.'),
|
||||||
|
'#default_value' => variable_get('advagg_font_storage', ADVAGG_FONT_STORAGE),
|
||||||
|
'#description' => t('Data is stored in localStorage under advagg_fonts. If this is a problem you can disable localStorage from being used; if doing so the FOUT (Flash of Unstyled Text) will happen on every page load if cookies is also not being used.', array('@cookie' => 'advaggfont_pt-sans=PT Sans')),
|
||||||
|
);
|
||||||
|
$form['container']['advagg_font_cookie'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Set a cookie so the flash of unstyled text (FOUT) only happens once.'),
|
||||||
|
'#default_value' => variable_get('advagg_font_cookie', ADVAGG_FONT_COOKIE),
|
||||||
|
'#description' => t('Cookies are name like <code>@cookie</code>. If this is a problem you can disable cookies from being set; if doing so the FOUT (Flash of Unstyled Text) will happen on every page load if localStorage is also not being used.', array('@cookie' => 'advaggfont_pt-sans=PT Sans')),
|
||||||
|
);
|
||||||
|
$form['container']['advagg_font_no_fout'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Prevent the Flash of Unstyled Text.'),
|
||||||
|
'#default_value' => variable_get('advagg_font_no_fout', ADVAGG_FONT_NO_FOUT),
|
||||||
|
'#description' => t('The font will not be changed unless the browser already has the font downloaded. Font gets downloaded on the first page view.', array('@cookie' => 'advaggfont_pt-sans=PT Sans')),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
'#edit-advagg-font-cookie' => array('checked' => FALSE),
|
||||||
|
'#edit-advagg-font-storage' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get all css files and scan for quoted fonts.
|
||||||
|
$form['fonts'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
'#title' => t('Async Loaded fonts in CSS'),
|
||||||
|
'#description' => t('Assumes quoted fonts will be downloaded and unquoted fonts are fallbacks.'),
|
||||||
|
);
|
||||||
|
$form['fonts_not_async'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
'#title' => t('NOT Async Loaded in CSS'),
|
||||||
|
'#description' => t('Assumes quoted fonts will be downloaded and unquoted fonts are fallbacks. If there is no fallback it will apear below.'),
|
||||||
|
);
|
||||||
|
// Get filename, filename_hash, and changes.
|
||||||
|
$results = db_select('advagg_files', 'af')
|
||||||
|
->fields('af', array('filename', 'filename_hash', 'changes'))
|
||||||
|
->condition('filetype', 'css')
|
||||||
|
->orderBy('filename', 'ASC')
|
||||||
|
->execute();
|
||||||
|
while ($row = $results->fetchAssoc()) {
|
||||||
|
if (!file_exists($row['filename'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Get the file contents.
|
||||||
|
$file_contents = (string) @advagg_file_get_contents($row['filename']);
|
||||||
|
// Get font names.
|
||||||
|
list($replacements, $fonts_with_no_replacements) = advagg_font_get_replacements_array($file_contents);
|
||||||
|
if (!empty($replacements)) {
|
||||||
|
$fonts = array();
|
||||||
|
foreach ($replacements as $key => $replacement) {
|
||||||
|
// Do not display !important after the fallback font name.
|
||||||
|
$replacement[5] = str_replace(' !important', '', $replacement[5]);
|
||||||
|
$fonts[$key . ' ' . $replacement[3]] = $replacement[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
$form['fonts'][$row['filename_hash']] = array(
|
||||||
|
'#markup' => '<div>' . t('%file - <code>@replacements</code><br />', array(
|
||||||
|
'@replacements' => str_ireplace('array', '', print_r($fonts, TRUE)),
|
||||||
|
'%file' => $row['filename'],
|
||||||
|
)) . '</div>',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!empty($fonts_with_no_replacements)) {
|
||||||
|
$fonts = array();
|
||||||
|
foreach ($fonts_with_no_replacements as $key => $replacement) {
|
||||||
|
// Do not display !important after the fallback font name.
|
||||||
|
$replacement = str_replace(' !important', '', $replacement);
|
||||||
|
$fonts[$key . ' ' . $replacement] = $replacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
$form['fonts_not_async'][$row['filename_hash']] = array(
|
||||||
|
'#markup' => '<div>' . t('%file - <code>@replacements</code><br />', array(
|
||||||
|
'@replacements' => str_ireplace('array', '', print_r($fonts, TRUE)),
|
||||||
|
'%file' => $row['filename'],
|
||||||
|
)) . '</div>',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$children = element_children($form['fonts']);
|
||||||
|
|
||||||
|
// If no fonts are found; disable this module.
|
||||||
|
if (count($children) == 0) {
|
||||||
|
$form['advagg_font_fontfaceobserver']['#default_value'] = 0;
|
||||||
|
$form['advagg_font_fontfaceobserver']['#disabled'] = TRUE;
|
||||||
|
|
||||||
|
if (empty($results)) {
|
||||||
|
$form['fonts'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('No CSS files have been aggregated.'),
|
||||||
|
'#description' => t('You need to enable aggregation. No css files where found in the advagg_files table.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$form['fonts'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('No CSS files with external fonts found.'),
|
||||||
|
'#description' => t('Currently this module is not doing anything. Recommend uninstalling it as advagg is not processing any css files that use an external font file.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the cache bins on submit.
|
||||||
|
$form['#submit'][] = 'advagg_font_admin_settings_form_submit';
|
||||||
|
|
||||||
|
return system_settings_form($form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback, clear out the advagg cache bin.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms_callback
|
||||||
|
*/
|
||||||
|
function advagg_font_admin_settings_form_submit($form, &$form_state) {
|
||||||
|
// Clear caches.
|
||||||
|
advagg_cache_clear_admin_submit();
|
||||||
|
|
||||||
|
// Disable cookie and local storage if ffo is disabled.
|
||||||
|
if (empty($form_state['values']['advagg_font_fontfaceobserver'])) {
|
||||||
|
$form_state['values']['advagg_font_cookie'] = 0;
|
||||||
|
$form_state['values']['advagg_font_storage'] = 0;
|
||||||
|
}
|
||||||
|
// Disable no fout if cookies and local storage are disabled.
|
||||||
|
if (empty($form_state['values']['advagg_font_cookie'])
|
||||||
|
&& empty($form_state['values']['advagg_font_storage'])
|
||||||
|
) {
|
||||||
|
$form_state['values']['advagg_font_no_fout'] = 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation css compression module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_get_css_file_contents_alter().
|
||||||
|
*/
|
||||||
|
function advagg_font_advagg_get_css_file_contents_alter(&$contents, $file, $aggregate_settings) {
|
||||||
|
// Do nothing if this is disabled.
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_font_fontfaceobserver'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($replacements) = advagg_font_get_replacements_array($contents);
|
||||||
|
foreach ($replacements as $replace) {
|
||||||
|
$contents = str_replace($replace[0], $replace[1] . $replace[2], $contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_get_info_on_files_alter().
|
||||||
|
*
|
||||||
|
* Used to add external font info for css file.
|
||||||
|
*/
|
||||||
|
function advagg_font_advagg_get_info_on_files_alter(&$return, $cached_data, $bypass_cache) {
|
||||||
|
$advagg_font_ffo = variable_get('advagg_font_fontfaceobserver', ADVAGG_FONT_FONTFACEOBSERVER);
|
||||||
|
if (!empty($advagg_font_ffo)) {
|
||||||
|
foreach ($return as &$info) {
|
||||||
|
// Skip if not a css file.
|
||||||
|
if (empty($info['fileext']) || $info['fileext'] !== 'css') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Get the file contents.
|
||||||
|
$file_contents = (string) @advagg_file_get_contents($info['data']);
|
||||||
|
if (!empty($file_contents)) {
|
||||||
|
// Get font names.
|
||||||
|
list($replacements) = advagg_font_get_replacements_array($file_contents);
|
||||||
|
|
||||||
|
// Remove old values.
|
||||||
|
if (isset($info['advagg_font'])) {
|
||||||
|
unset($info['advagg_font']);
|
||||||
|
}
|
||||||
|
// Add in new values.
|
||||||
|
foreach ($replacements as $replace) {
|
||||||
|
$info['advagg_font'][$replace[4]] = str_replace(array('"', "'"), '', $replace[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
@ -0,0 +1,14 @@
|
|||||||
|
name = AdvAgg Async Font Loader
|
||||||
|
description = Allows one to load fonts in an async manner
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
recommends[] = libraries
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg/font
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Used to add a class to the top level element based off of cookies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get advagg cookies for fonts or localStorage.
|
||||||
|
*
|
||||||
|
* Changes the top level class to include the font name found in the cookie.
|
||||||
|
*/
|
||||||
|
function advagg_font_inline() {
|
||||||
|
'use strict';
|
||||||
|
// Cookie handler.
|
||||||
|
var fonts = document.cookie.split('advaggf');
|
||||||
|
for (var i = 0; i < fonts.length; i++) {
|
||||||
|
var font = fonts[i].split('=');
|
||||||
|
var pos = font[0].indexOf('ont_');
|
||||||
|
if (pos !== -1) {
|
||||||
|
// Only allow alpha numeric class names.
|
||||||
|
window.document.documentElement.className += ' ' + font[0].substr(4).replace(/[^a-zA-Z0-9-]/g, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local Storage handler.
|
||||||
|
if (Storage !== void 0) {
|
||||||
|
fonts = JSON.parse(localStorage.getItem('advagg_fonts'));
|
||||||
|
var current_time = new Date().getTime();
|
||||||
|
for (var key in fonts) {
|
||||||
|
if (fonts[key] >= current_time) {
|
||||||
|
// Only allow alpha numeric class names.
|
||||||
|
window.document.documentElement.className += ' ' + key.replace(/[^a-zA-Z0-9-]/g, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check cookies ASAP and set class.
|
||||||
|
advagg_font_inline();
|
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles Advanced Aggregation installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_requirements().
|
||||||
|
*/
|
||||||
|
function advagg_font_requirements($phase) {
|
||||||
|
$requirements = array();
|
||||||
|
// If not at runtime, return here.
|
||||||
|
if ($phase !== 'runtime') {
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure translations don't break at install time.
|
||||||
|
$t = get_t();
|
||||||
|
// Get config path.
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
// See if module is on.
|
||||||
|
if (variable_get('advagg_font_fontfaceobserver', ADVAGG_FONT_FONTFACEOBSERVER) == 0) {
|
||||||
|
$requirements['advagg_font_not_on'] = array(
|
||||||
|
'title' => $t('AdvAgg Font'),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('AdvAgg async font loading is disabled.'),
|
||||||
|
'description' => $t('Go to the <a href="@settings">AdvAgg Async Font Loader settings page</a> and select an option other than disabled, or go to the <a href="@modules">modules page</a> and disable the "AdvAgg Async Font Loader" module.', array(
|
||||||
|
'@settings' => url($config_path . '/advagg/font'),
|
||||||
|
'@modules' => url('admin/modules', array(
|
||||||
|
'fragment' => 'edit-modules-advanced-cssjs-aggregation',
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check version.
|
||||||
|
$lib_name = 'fontfaceobserver';
|
||||||
|
$module_name = 'advagg_css_compress';
|
||||||
|
list($description, $info) = advagg_get_version_description($lib_name, $module_name);
|
||||||
|
if (!empty($description)) {
|
||||||
|
$requirements["{$module_name}_{$lib_name}_updates"] = array(
|
||||||
|
'title' => $t('@module_name', array('@module_name' => $info['name'])),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('The @name library needs to be updated.', array('@name' => $lib_name)),
|
||||||
|
'description' => $description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Used to add a class to the top level element when an external font is ready.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global Drupal:false */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the check.
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
* The class name to add to the html tag.
|
||||||
|
* @param {string} value
|
||||||
|
* The font name.
|
||||||
|
*/
|
||||||
|
function advagg_run_check(key, value) {
|
||||||
|
'use strict';
|
||||||
|
// Only run if window.FontFaceObserver is defined.
|
||||||
|
if (window.FontFaceObserver) {
|
||||||
|
// Only alpha numeric value.
|
||||||
|
key = key.replace(/[^a-zA-Z0-9-]/g, '');
|
||||||
|
if (typeof window.FontFaceObserver.prototype.load === 'function') {
|
||||||
|
new window.FontFaceObserver(value).load().then(function () {
|
||||||
|
advagg_run_check_inner(key, value);
|
||||||
|
}, function () {});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new window.FontFaceObserver(value).check().then(function () {
|
||||||
|
advagg_run_check_inner(key, value);
|
||||||
|
}, function () {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Try again in 100 ms.
|
||||||
|
window.setTimeout(function () {
|
||||||
|
advagg_run_check(key, value);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the check.
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
* The class name to add to the html tag.
|
||||||
|
* @param {string} value
|
||||||
|
* The font name.
|
||||||
|
*/
|
||||||
|
function advagg_run_check_inner(key, value) {
|
||||||
|
'use strict';
|
||||||
|
// Set Class.
|
||||||
|
if (parseInt(Drupal.settings.advagg_font_no_fout, 10) !== 1) {
|
||||||
|
window.document.documentElement.className += ' ' + key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set for a day.
|
||||||
|
var expire_date = new Date().getTime() + 86400 * 1000;
|
||||||
|
|
||||||
|
if (Storage !== void 0 && parseInt(Drupal.settings.advagg_font_storage, 10) === 1) {
|
||||||
|
// Use local storage.
|
||||||
|
var fonts = JSON.parse(localStorage.getItem('advagg_fonts'));
|
||||||
|
if (!fonts) {
|
||||||
|
fonts = {};
|
||||||
|
}
|
||||||
|
fonts[key] = expire_date;
|
||||||
|
localStorage.setItem('advagg_fonts', JSON.stringify(fonts));
|
||||||
|
}
|
||||||
|
else if (parseInt(Drupal.settings.advagg_font_cookie, 10) === 1) {
|
||||||
|
// Use cookies if enabled and local storage not available.
|
||||||
|
expire_date = new Date(expire_date).toUTCString();
|
||||||
|
document.cookie = 'advaggfont_' + key + '=' + value
|
||||||
|
+ '; expires=' + expire_date
|
||||||
|
+ '; domain=.' + document.location.hostname
|
||||||
|
+ '; path=/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of fonts to check for.
|
||||||
|
*/
|
||||||
|
function advagg_font_add_font_classes_on_load() {
|
||||||
|
'use strict';
|
||||||
|
for (var key in Drupal.settings.advagg_font) {
|
||||||
|
if (Drupal.settings.advagg_font.hasOwnProperty(key)) {
|
||||||
|
var html_class = (' ' + window.document.documentElement.className + ' ').indexOf(' ' + key + ' ');
|
||||||
|
// If the class already exists in the html element do nothing.
|
||||||
|
if (html_class === -1) {
|
||||||
|
// Wait till the font is downloaded, then set cookie & class.
|
||||||
|
advagg_run_check(key, Drupal.settings.advagg_font[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure window.Drupal.settings.advagg_font is defined before running.
|
||||||
|
*/
|
||||||
|
function advagg_font_check() {
|
||||||
|
'use strict';
|
||||||
|
if (window.Drupal && window.Drupal.settings && window.Drupal.settings.advagg_font) {
|
||||||
|
advagg_font_add_font_classes_on_load();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Try again in 20 ms.
|
||||||
|
window.setTimeout(advagg_font_check, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the process.
|
||||||
|
advagg_font_check();
|
@ -0,0 +1,542 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation font module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup default_variables
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to use font face observer for asynchronous font loading.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_FONT_FONTFACEOBSERVER', 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to include font info in critical css.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_FONT_ADD_TO_CRITICAL_CSS', 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to use localStorage in order to prevent the FOUT.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_FONT_STORAGE', 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to use a cookie in order to prevent the FOUT.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_FONT_COOKIE', 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to only replace the font if it's been downloaded.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_FONT_NO_FOUT', 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup default_variables".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_module_implements_alter().
|
||||||
|
*/
|
||||||
|
function advagg_font_module_implements_alter(&$implementations, $hook) {
|
||||||
|
// Move advagg to the bottom.
|
||||||
|
if ($hook === 'page_alter' && array_key_exists('advagg_font', $implementations)) {
|
||||||
|
$item = $implementations['advagg_font'];
|
||||||
|
unset($implementations['advagg_font']);
|
||||||
|
$implementations['advagg_font'] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_page_alter().
|
||||||
|
*/
|
||||||
|
function advagg_font_page_alter() {
|
||||||
|
// Skip if advagg is disabled.
|
||||||
|
if (!advagg_enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$advagg_font_ffo = variable_get('advagg_font_fontfaceobserver', ADVAGG_FONT_FONTFACEOBSERVER);
|
||||||
|
// Fontface Observer is disabled.
|
||||||
|
if (empty($advagg_font_ffo)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add settings.
|
||||||
|
drupal_add_js(array(
|
||||||
|
'advagg_font' => array(),
|
||||||
|
'advagg_font_storage' => variable_get('advagg_font_storage', ADVAGG_FONT_STORAGE),
|
||||||
|
'advagg_font_cookie' => variable_get('advagg_font_cookie', ADVAGG_FONT_COOKIE),
|
||||||
|
'advagg_font_no_fout' => variable_get('advagg_font_no_fout', ADVAGG_FONT_NO_FOUT),
|
||||||
|
), array('type' => 'setting'));
|
||||||
|
|
||||||
|
// Add inline script for reading the cookies and adding the fonts already
|
||||||
|
// loaded to the html class.
|
||||||
|
if (variable_get('advagg_font_cookie', ADVAGG_FONT_COOKIE) || variable_get('advagg_font_storage', ADVAGG_FONT_STORAGE)) {
|
||||||
|
$inline_script_min = 'for(var fonts=document.cookie.split("advaggf"),i=0;i<fonts.length;i++){var font=fonts[i].split("="),pos=font[0].indexOf("ont_");-1!==pos&&(window.document.documentElement.className+=" "+font[0].substr(4).replace(/[^a-zA-Z0-9\-]/g,""))}if(void 0!==Storage){fonts=JSON.parse(localStorage.getItem("advagg_fonts"));var current_time=(new Date).getTime();for(var key in fonts)fonts[key]>=current_time&&(window.document.documentElement.className+=" "+key.replace(/[^a-zA-Z0-9\-]/g,""))}';
|
||||||
|
drupal_add_js($inline_script_min, array(
|
||||||
|
'type' => 'inline',
|
||||||
|
'group' => JS_LIBRARY - 1,
|
||||||
|
'weight' => -50000,
|
||||||
|
'scope' => 'above_css',
|
||||||
|
'scope_lock' => TRUE,
|
||||||
|
'movable' => FALSE,
|
||||||
|
'no_defer' => TRUE,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get library data for fontfaceobserver.
|
||||||
|
$library = advagg_get_library('fontfaceobserver', 'advagg_font');
|
||||||
|
// If libraries_load() does not exist load library externally.
|
||||||
|
if (!is_callable('libraries_load')) {
|
||||||
|
$advagg_font_ffo = 6;
|
||||||
|
}
|
||||||
|
// Add fontfaceobserver.js.
|
||||||
|
if ($advagg_font_ffo != 6 && empty($library['installed'])) {
|
||||||
|
// The fontfaceobserver library is not installed; use external variant.
|
||||||
|
$advagg_font_ffo = 6;
|
||||||
|
}
|
||||||
|
if ($advagg_font_ffo == 6) {
|
||||||
|
// Use the external variant.
|
||||||
|
foreach ($library['variants']['external']['files']['js'] as $data => $options) {
|
||||||
|
drupal_add_js($data, $options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Load the fontfaceobserver library.
|
||||||
|
if ($advagg_font_ffo == 2) {
|
||||||
|
// Use the inline variant.
|
||||||
|
libraries_load('fontfaceobserver', 'inline');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
libraries_load('fontfaceobserver');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add advagg_font.js; sets cookie and changes the class of the top level
|
||||||
|
// element once a font has been downloaded.
|
||||||
|
$file_path = drupal_get_path('module', 'advagg_font') . '/advagg_font.js';
|
||||||
|
drupal_add_js($file_path, array(
|
||||||
|
'async' => TRUE,
|
||||||
|
'defer' => TRUE,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_css_alter().
|
||||||
|
*/
|
||||||
|
function advagg_css_alter(&$css) {
|
||||||
|
// Skip if advagg is disabled.
|
||||||
|
if (!advagg_enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if fontface is disabled.
|
||||||
|
if (empty(variable_get('advagg_font_fontfaceobserver', ADVAGG_FONT_FONTFACEOBSERVER))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if fonts added to critical css is disabled.
|
||||||
|
if (empty(variable_get('advagg_font_add_to_critical_css', ADVAGG_FONT_ADD_TO_CRITICAL_CSS))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$critical_css_key = NULL;
|
||||||
|
foreach ($css as $key => $values) {
|
||||||
|
if (!empty($values['critical-css']) && $values['type'] === 'inline') {
|
||||||
|
$critical_css_key = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if no critical css.
|
||||||
|
if (is_null($critical_css_key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
$css_to_add = '';
|
||||||
|
foreach ($css as $key => $values) {
|
||||||
|
if ($values['type'] === 'file') {
|
||||||
|
$info = advagg_get_info_on_file($key);
|
||||||
|
if (!empty($info['advagg_font'])) {
|
||||||
|
// Get the file contents.
|
||||||
|
$file_contents = (string) @advagg_file_get_contents($info['data']);
|
||||||
|
if (empty($file_contents)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
list($replacements) = advagg_font_get_replacements_array($file_contents);
|
||||||
|
foreach ($replacements as $replace) {
|
||||||
|
$css_to_add .= $replace[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($css_to_add)) {
|
||||||
|
$css[$critical_css_key]['data'] .= "\n{$css_to_add}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu().
|
||||||
|
*/
|
||||||
|
function advagg_font_menu() {
|
||||||
|
$file_path = drupal_get_path('module', 'advagg_font');
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
$items[$config_path . '/advagg/font'] = array(
|
||||||
|
'title' => 'Async Font Loader',
|
||||||
|
'description' => 'Load external fonts in a non blocking manner.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('advagg_font_admin_settings_form'),
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file path' => $file_path,
|
||||||
|
'file' => 'advagg_font.admin.inc',
|
||||||
|
'weight' => 10,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup 3rd_party_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_libraries_info().
|
||||||
|
*/
|
||||||
|
function advagg_font_libraries_info() {
|
||||||
|
$libraries['fontfaceobserver'] = array(
|
||||||
|
// Only used in administrative UI of Libraries API.
|
||||||
|
'name' => 'fontfaceobserver',
|
||||||
|
'vendor url' => 'https://github.com/bramstein/fontfaceobserver',
|
||||||
|
'download url' => 'https://github.com/bramstein/fontfaceobserver/archive/master.zip',
|
||||||
|
'version arguments' => array(
|
||||||
|
'file' => 'package.json',
|
||||||
|
// 1.50. : "version": "1.5.0".
|
||||||
|
'pattern' => '/"version":\\s+"([0-9\.]+)"/',
|
||||||
|
'lines' => 10,
|
||||||
|
),
|
||||||
|
'remote' => array(
|
||||||
|
'callback' => 'advagg_get_github_version_json',
|
||||||
|
'url' => 'https://cdn.jsdelivr.net/gh/bramstein/fontfaceobserver@master/package.json',
|
||||||
|
),
|
||||||
|
'files' => array(
|
||||||
|
'js' => array(
|
||||||
|
'fontfaceobserver.js' => array(
|
||||||
|
'type' => 'file',
|
||||||
|
'group' => JS_LIBRARY,
|
||||||
|
'async' => TRUE,
|
||||||
|
'defer' => TRUE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'variants' => array(),
|
||||||
|
);
|
||||||
|
// Get the latest tagged version for external file loading.
|
||||||
|
$version = advagg_get_remote_libraries_version('fontfaceobserver', $libraries['fontfaceobserver']);
|
||||||
|
$libraries['fontfaceobserver']['variants'] += array(
|
||||||
|
'external' => array(
|
||||||
|
'files' => array(
|
||||||
|
'js' => array(
|
||||||
|
"https://cdn.jsdelivr.net/gh/bramstein/fontfaceobserver@v{$version}/fontfaceobserver.js" => array(
|
||||||
|
'type' => 'external',
|
||||||
|
'data' => "https://cdn.jsdelivr.net/gh/bramstein/fontfaceobserver@v{$version}/fontfaceobserver.js",
|
||||||
|
'async' => TRUE,
|
||||||
|
'defer' => TRUE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// Inline if local js is there.
|
||||||
|
$libraries_paths = array();
|
||||||
|
if (is_callable('libraries_get_libraries')) {
|
||||||
|
$libraries_paths = libraries_get_libraries();
|
||||||
|
}
|
||||||
|
if (!empty($libraries_paths['fontfaceobserver']) && is_readable($libraries_paths['fontfaceobserver'] . '/fontfaceobserver.js')) {
|
||||||
|
$libraries['fontfaceobserver']['variants'] += array(
|
||||||
|
'inline' => array(
|
||||||
|
'files' => array(
|
||||||
|
'js' => array(
|
||||||
|
'loadCSS_inline' => array(
|
||||||
|
'type' => 'inline',
|
||||||
|
'data' => (string) @advagg_file_get_contents($libraries_paths['fontfaceobserver'] . '/fontfaceobserver.js'),
|
||||||
|
'no_defer' => TRUE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup 3rd_party_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_current_hooks_hash_array_alter().
|
||||||
|
*/
|
||||||
|
function advagg_font_advagg_current_hooks_hash_array_alter(&$aggregate_settings) {
|
||||||
|
$aggregate_settings['variables']['advagg_font_fontfaceobserver'] = variable_get('advagg_font_fontfaceobserver', ADVAGG_FONT_FONTFACEOBSERVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the replacements array for the css.
|
||||||
|
*
|
||||||
|
* @param string $css_string
|
||||||
|
* String of CSS.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array containing the replacemnts and the font class name.
|
||||||
|
*/
|
||||||
|
function advagg_font_get_replacements_array($css_string) {
|
||||||
|
// Get the CSS that contains a font-family rule.
|
||||||
|
$length = strlen($css_string);
|
||||||
|
$property_position = 0;
|
||||||
|
$property = 'font';
|
||||||
|
$property_alt = 'font-family';
|
||||||
|
$replacements = array();
|
||||||
|
$fonts_with_no_replacements = array();
|
||||||
|
$lower = strtolower($css_string);
|
||||||
|
$safe_fonts_list = array(
|
||||||
|
'georgia' => TRUE,
|
||||||
|
'palatino' => TRUE,
|
||||||
|
'times new roman' => TRUE,
|
||||||
|
'times' => TRUE,
|
||||||
|
|
||||||
|
'arial' => TRUE,
|
||||||
|
'helvetica' => TRUE,
|
||||||
|
'gadget' => TRUE,
|
||||||
|
'verdana' => TRUE,
|
||||||
|
'geneva' => TRUE,
|
||||||
|
'tahoma' => TRUE,
|
||||||
|
'garamond' => TRUE,
|
||||||
|
'bookman' => TRUE,
|
||||||
|
'comic sans ms' => TRUE,
|
||||||
|
'cursive' => TRUE,
|
||||||
|
'trebuchet ms' => TRUE,
|
||||||
|
'arial black' => TRUE,
|
||||||
|
'impact' => TRUE,
|
||||||
|
'charcoal' => TRUE,
|
||||||
|
|
||||||
|
'courier new' => TRUE,
|
||||||
|
'courier' => TRUE,
|
||||||
|
'monaco' => TRUE,
|
||||||
|
|
||||||
|
'system' => TRUE,
|
||||||
|
);
|
||||||
|
|
||||||
|
while (($property_position = strpos($lower, $property, $property_position)) !== FALSE) {
|
||||||
|
// Find the start of the values for the property.
|
||||||
|
$start_of_values = strpos($css_string, ':', $property_position);
|
||||||
|
// Get the property at this location of the css.
|
||||||
|
$property_in_loop = trim(substr($css_string, $property_position, ($start_of_values - $property_position)));
|
||||||
|
|
||||||
|
// Make sure this property is one of the ones we're looking for.
|
||||||
|
if ($property_in_loop !== $property && $property_in_loop !== $property_alt) {
|
||||||
|
$property_position += strlen($property);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get position of the last closing bracket plus 1 (start of this section).
|
||||||
|
$start = strrpos($css_string, '}', -($length - $property_position));
|
||||||
|
if ($start === FALSE) {
|
||||||
|
// Property is in the first selector and a declaration block (full rule
|
||||||
|
// set).
|
||||||
|
$start = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Add one to start after the }.
|
||||||
|
$start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get closing bracket (end of this section).
|
||||||
|
$end = strpos($css_string, '}', $property_position);
|
||||||
|
if ($end === FALSE) {
|
||||||
|
// The end is the end of this file.
|
||||||
|
$end = $length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get closing ; in order to get the end of the declaration of the property.
|
||||||
|
$declaration_end_a = strpos($css_string, ';', $property_position);
|
||||||
|
$declaration_end_b = strpos($css_string, '}', $property_position);
|
||||||
|
if ($declaration_end_a === FALSE) {
|
||||||
|
$declaration_end = $declaration_end_b;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$declaration_end = min($declaration_end_a, $declaration_end_b);
|
||||||
|
}
|
||||||
|
if ($declaration_end > $end) {
|
||||||
|
$declaration_end = $end;
|
||||||
|
}
|
||||||
|
// Add one in order to capture the } when we ge the full rule set.
|
||||||
|
$end++;
|
||||||
|
// Advance position for the next run of the while loop.
|
||||||
|
$property_position = $end;
|
||||||
|
|
||||||
|
// Get values assigned to this property.
|
||||||
|
$values_string = substr($css_string, $start_of_values + 1, $declaration_end - ($start_of_values + 1));
|
||||||
|
// Parse values string into an array of values.
|
||||||
|
$values_array = explode(',', $values_string);
|
||||||
|
if (empty($values_array)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values array, first element is a quoted string.
|
||||||
|
$dq = strpos($values_array[0], '"');
|
||||||
|
$sq = strpos($values_array[0], "'");
|
||||||
|
$quote_pos = ($sq !== FALSE) ? $sq : $dq;
|
||||||
|
// Skip if the first font is not quoted.
|
||||||
|
if ($quote_pos === FALSE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$values_array[0] = trim($values_array[0]);
|
||||||
|
// Skip if only one font is listed.
|
||||||
|
if (count($values_array) === 1) {
|
||||||
|
$fonts_with_no_replacements[$values_array[0]] = '';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the first value to a variable; starting at the quote.
|
||||||
|
$removed_value_original = substr($values_array[0], max($quote_pos - 1, 0));
|
||||||
|
|
||||||
|
// Resave first value.
|
||||||
|
if ($quote_pos > 1) {
|
||||||
|
$values_array[0] = trim(substr($values_array[0], 0, $quote_pos - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get value as a classname. Remove quotes, trim, lowercase, and replace
|
||||||
|
// spaces with dashes.
|
||||||
|
$removed_value_classname = strtolower(trim(str_replace(array('"', "'"), '', $removed_value_original)));
|
||||||
|
$removed_value_classname = str_replace(' ', '-', $removed_value_classname);
|
||||||
|
|
||||||
|
// Remove value if it contains a quote.
|
||||||
|
$values_array_copy = $values_array;
|
||||||
|
foreach ($values_array as $key => $value) {
|
||||||
|
if (strpos($value, '"') !== FALSE || strpos($value, "'") !== FALSE) {
|
||||||
|
unset($values_array[$key]);
|
||||||
|
}
|
||||||
|
elseif ($key !== 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($values_array)) {
|
||||||
|
// See if there's a "safe" fallback that is quoted.
|
||||||
|
$values_array = $values_array_copy;
|
||||||
|
foreach ($values_array as $key => $value) {
|
||||||
|
if (strpos($value, '"') !== FALSE || strpos($value, "'") !== FALSE) {
|
||||||
|
if ($key !== 0) {
|
||||||
|
$lower_key = trim(trim(strtolower(trim($value)), '"'), "'");
|
||||||
|
if (!empty($safe_fonts_list[$lower_key])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($values_array[$key]);
|
||||||
|
}
|
||||||
|
elseif ($key !== 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($values_array)) {
|
||||||
|
// No unquoted values left; do not modify the css.
|
||||||
|
$key = array_shift($values_array_copy);
|
||||||
|
$fonts_with_no_replacements[$key] = implode(',', $values_array_copy);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$extra = '';
|
||||||
|
if (isset($values_array[0])) {
|
||||||
|
$extra = $values_array[0] . ' ';
|
||||||
|
unset($values_array[0]);
|
||||||
|
}
|
||||||
|
// Rezero the keys.
|
||||||
|
$values_array = array_values($values_array);
|
||||||
|
// Save next value.
|
||||||
|
$next_value_original = trim($values_array[0]);
|
||||||
|
// Create the values string.
|
||||||
|
$new_values_string = $extra . implode(',', $values_array);
|
||||||
|
|
||||||
|
// Get all selectors.
|
||||||
|
$end_of_selectors = strpos($css_string, '{', $start);
|
||||||
|
$selectors = substr($css_string, $start, $end_of_selectors - $start);
|
||||||
|
// Ensure selectors is not a media query.
|
||||||
|
if (stripos($selectors, "@media") !== FALSE) {
|
||||||
|
// Move the start to the end of the media query.
|
||||||
|
$start = $end_of_selectors + 1;
|
||||||
|
// Get the selectors again.
|
||||||
|
$end_of_selectors = strpos($css_string, '{', $start);
|
||||||
|
$selectors = substr($css_string, $start, $end_of_selectors - $start);
|
||||||
|
}
|
||||||
|
|
||||||
|
// From advagg_load_stylesheet_content().
|
||||||
|
// Perform some safe CSS optimizations.
|
||||||
|
// Regexp to match comment blocks.
|
||||||
|
// Regexp to match double quoted strings.
|
||||||
|
// Regexp to match single quoted strings.
|
||||||
|
$comment = '/\*[^*]*\*+(?:[^/*][^*]*\*+)*/';
|
||||||
|
$double_quot = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
|
||||||
|
$single_quot = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'";
|
||||||
|
// Strip all comment blocks, but keep double/single quoted strings.
|
||||||
|
$selectors_stripped = preg_replace(
|
||||||
|
"<($double_quot|$single_quot)|$comment>Ss",
|
||||||
|
"$1",
|
||||||
|
$selectors
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add css class to all the selectors.
|
||||||
|
$selectors_array = explode(',', $selectors_stripped);
|
||||||
|
foreach ($selectors_array as &$selector) {
|
||||||
|
// Remove extra whitespace.
|
||||||
|
$selector = trim($selector);
|
||||||
|
$selector = " .{$removed_value_classname} {$selector}";
|
||||||
|
}
|
||||||
|
$new_selectors = implode(',', $selectors_array);
|
||||||
|
|
||||||
|
// Get full rule set.
|
||||||
|
$full_rule_set = substr($css_string, $start, $end - $start);
|
||||||
|
// Replace values.
|
||||||
|
$new_values_full_rule_set = str_replace($values_string, $new_values_string, $full_rule_set);
|
||||||
|
// Add in old rule set with new selectors.
|
||||||
|
$new_selectors_full_rule_set = $new_selectors . '{' . $property_in_loop . ': ' . $values_string . ';}';
|
||||||
|
|
||||||
|
// Record info.
|
||||||
|
$replacements[] = array(
|
||||||
|
$full_rule_set,
|
||||||
|
$new_values_full_rule_set,
|
||||||
|
$new_selectors_full_rule_set,
|
||||||
|
$removed_value_original,
|
||||||
|
$removed_value_classname,
|
||||||
|
$next_value_original,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return array($replacements, $fonts_with_no_replacements);
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
name = AdvAgg CDN Javascript
|
||||||
|
description = Use a shared CDN for javascript libraries, Google Libraries API currently.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles Advanced Aggregation installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_requirements().
|
||||||
|
*/
|
||||||
|
function advagg_js_cdn_requirements($phase) {
|
||||||
|
$requirements = array();
|
||||||
|
// Ensure translations don't break at install time.
|
||||||
|
$t = get_t();
|
||||||
|
|
||||||
|
// If not at runtime, return here.
|
||||||
|
if ($phase !== 'runtime') {
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module_exists('jquery_update')) {
|
||||||
|
$jquery_cdn = variable_get('jquery_update_jquery_cdn', 'none');
|
||||||
|
if ($jquery_cdn === 'none') {
|
||||||
|
$jquery_description = $t('The jquery update module has support for CDNs. Go to the <a href="@settings">jquery update settings page</a> and select a CDN instead of using this module.', array(
|
||||||
|
'@settings' => url('admin/config/development/jquery_update', array(
|
||||||
|
'fragment' => 'edit-jquery-update-jquery-cdn',
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$jquery_description = $t('The jquery update module is already configured to use the external CDN "@cdn".', array('@cdn' => $jquery_cdn));
|
||||||
|
}
|
||||||
|
|
||||||
|
$requirements['advagg_js_cdn_jquery_update'] = array(
|
||||||
|
'title' => $t('Adv JS CDN - jquery update'),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('Duplicate Functionality: Use jquery update instead of the advagg_js_cdn sub module.'),
|
||||||
|
'description' => $jquery_description . ' ' . $t('You should go to the <a href="@modules">modules page</a> and disable the "AdvAgg CDN JS" module.', array(
|
||||||
|
'@modules' => url('admin/modules', array(
|
||||||
|
'fragment' => 'edit-modules-advanced-cssjs-aggregation',
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($requirements)) {
|
||||||
|
$description = '';
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) {
|
||||||
|
$description = $t('Will be using the unminified version due to AdvAgg being in Development mode.');
|
||||||
|
}
|
||||||
|
$requirements['advagg_js_cdn'] = array(
|
||||||
|
'title' => $t('Adv JS CDN'),
|
||||||
|
'severity' => REQUIREMENT_OK,
|
||||||
|
'value' => $t('OK'),
|
||||||
|
'description' => $t('jQuery and jQuery UI JS should be coming from a CDN.') . ' ' . $description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
@ -0,0 +1,162 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation js cdn module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup default_variables
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to see if jquery should be grabbed from the Google CDN.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_CDN_JQUERY', TRUE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to see if jquery-ui should be grabbed from the Google CDN.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_CDN_JQUERY_UI', TRUE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default jquery version.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_CDN_JQUERY_VERSION', '1.4.4');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default jquery ui version.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_CDN_JQUERY_UI_VERSION', '1.8.7');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default minification.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_CDN_COMPRESSION', TRUE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup default_variables".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_js_alter().
|
||||||
|
*/
|
||||||
|
function advagg_js_cdn_js_alter(&$javascript) {
|
||||||
|
// Only modify if jquery_update is not enabled.
|
||||||
|
if (module_exists('jquery_update')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup variables.
|
||||||
|
// JQuery.
|
||||||
|
$jquery_version = variable_get('advagg_js_cdn_jquery_version', ADVAGG_JS_CDN_JQUERY_VERSION);
|
||||||
|
$min = variable_get('advagg_js_cdn_compression', ADVAGG_JS_CDN_COMPRESSION) ? '.min' : '';
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) {
|
||||||
|
$min = '';
|
||||||
|
}
|
||||||
|
// JQuery ui.
|
||||||
|
$jquery_ui_version = variable_get('advagg_js_cdn_jquery_ui_version', ADVAGG_JS_CDN_JQUERY_UI_VERSION);
|
||||||
|
$ui_mapping = advagg_js_cdn_get_ui_mapping();
|
||||||
|
$add_in_ui = FALSE;
|
||||||
|
|
||||||
|
foreach ($javascript as $name => $values) {
|
||||||
|
// Only modify if
|
||||||
|
// advagg_js_cdn_jquery is enabled,
|
||||||
|
// name is misc/jquery.js,
|
||||||
|
// and type is file.
|
||||||
|
if (variable_get('advagg_js_cdn_jquery', ADVAGG_JS_CDN_JQUERY)
|
||||||
|
&& $name === 'misc/jquery.js'
|
||||||
|
&& $javascript[$name]['type'] === 'file'
|
||||||
|
) {
|
||||||
|
// Add in backup.
|
||||||
|
$values['weight'] += 0.00001;
|
||||||
|
$values['data'] = 'window.jQuery || document.write("<script src=\'' . base_path() . $values['data'] . '\'>\x3C/script>")';
|
||||||
|
$values['type'] = 'inline';
|
||||||
|
$javascript[$name . 'backup.js'] = $values;
|
||||||
|
|
||||||
|
// Change core to external.
|
||||||
|
$javascript[$name]['data'] = '//ajax.googleapis.com/ajax/libs/jquery/' . $jquery_version . '/jquery' . $min . '.js';
|
||||||
|
$javascript[$name]['type'] = 'external';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only modify if
|
||||||
|
// advagg_js_cdn_jquery_ui is enabled,
|
||||||
|
// name is in the $ui_mapping array.
|
||||||
|
// and type is file.
|
||||||
|
if (variable_get('advagg_js_cdn_jquery_ui', ADVAGG_JS_CDN_JQUERY_UI)
|
||||||
|
&& array_key_exists($name, $ui_mapping)
|
||||||
|
&& $javascript[$name]['type'] === 'file'
|
||||||
|
) {
|
||||||
|
// We only need one file.
|
||||||
|
if (!$add_in_ui) {
|
||||||
|
// Add in backup.
|
||||||
|
$values['weight'] += 0.00001;
|
||||||
|
$values['data'] = 'window.jQuery.ui || document.write("<script src=\'' . base_path() . drupal_get_path('module', 'advagg_js_cdn') . '/js/jquery-ui.js' . '\'>\x3C/script>")';
|
||||||
|
$values['type'] = 'inline';
|
||||||
|
$javascript[$name . 'backup.js'] = $values;
|
||||||
|
|
||||||
|
// Change core to external.
|
||||||
|
$add_in_ui = TRUE;
|
||||||
|
$javascript[$name]['data'] = '//ajax.googleapis.com/ajax/libs/jqueryui/' . $jquery_ui_version . '/jquery-ui' . $min . '.js';
|
||||||
|
$javascript[$name]['type'] = 'external';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unset($javascript[$name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of jquery ui files.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* array of jQuery UI file locations.
|
||||||
|
*/
|
||||||
|
function advagg_js_cdn_get_ui_mapping() {
|
||||||
|
// Replace jQuery UI's JavaScript, beginning by defining the mapping.
|
||||||
|
$ui_mapping = array(
|
||||||
|
'misc/ui/jquery.effects.blind.min.js' => 'effects.blind',
|
||||||
|
'misc/ui/jquery.effects.bounce.min.js' => 'effects.bounce',
|
||||||
|
'misc/ui/jquery.effects.clip.min.js' => 'effects.clip',
|
||||||
|
'misc/ui/jquery.effects.core.min.js' => 'effects.core',
|
||||||
|
'misc/ui/jquery.effects.drop.min.js' => 'effects.drop',
|
||||||
|
'misc/ui/jquery.effects.explode.min.js' => 'effects.explode',
|
||||||
|
'misc/ui/jquery.effects.fade.min.js' => 'effects.fade',
|
||||||
|
'misc/ui/jquery.effects.fold.min.js' => 'effects.fold',
|
||||||
|
'misc/ui/jquery.effects.highlight.min.js' => 'effects.highlight',
|
||||||
|
'misc/ui/jquery.effects.pulsate.min.js' => 'effects.pulsate',
|
||||||
|
'misc/ui/jquery.effects.scale.min.js' => 'effects.scale',
|
||||||
|
'misc/ui/jquery.effects.shake.min.js' => 'effects.shake',
|
||||||
|
'misc/ui/jquery.effects.slide.min.js' => 'effects.slide',
|
||||||
|
'misc/ui/jquery.effects.transfer.min.js' => 'effects.transfer',
|
||||||
|
'misc/ui/jquery.ui.accordion.min.js' => 'ui.accordion',
|
||||||
|
'misc/ui/jquery.ui.autocomplete.min.js' => 'ui.autocomplete',
|
||||||
|
'misc/ui/jquery.ui.button.min.js' => 'ui.button',
|
||||||
|
'misc/ui/jquery.ui.core.min.js' => 'ui.core',
|
||||||
|
'misc/ui/jquery.ui.datepicker.min.js' => 'ui.datepicker',
|
||||||
|
'misc/ui/jquery.ui.dialog.min.js' => 'ui.dialog',
|
||||||
|
'misc/ui/jquery.ui.draggable.min.js' => 'ui.draggable',
|
||||||
|
'misc/ui/jquery.ui.droppable.min.js' => 'ui.droppable',
|
||||||
|
'misc/ui/jquery.ui.mouse.min.js' => 'ui.mouse',
|
||||||
|
'misc/ui/jquery.ui.position.min.js' => 'ui.position',
|
||||||
|
'misc/ui/jquery.ui.progressbar.min.js' => 'ui.progressbar',
|
||||||
|
'misc/ui/jquery.ui.resizable.min.js' => 'ui.resizable',
|
||||||
|
'misc/ui/jquery.ui.selectable.min.js' => 'ui.selectable',
|
||||||
|
'misc/ui/jquery.ui.slider.min.js' => 'ui.slider',
|
||||||
|
'misc/ui/jquery.ui.sortable.min.js' => 'ui.sortable',
|
||||||
|
'misc/ui/jquery.ui.tabs.min.js' => 'ui.tabs',
|
||||||
|
'misc/ui/jquery.ui.widget.min.js' => 'ui.widget',
|
||||||
|
);
|
||||||
|
return $ui_mapping;
|
||||||
|
}
|
11517
frontend/drupal/sites/all/modules/advagg/advagg_js_cdn/js/jquery-ui.js
vendored
Normal file
11517
frontend/drupal/sites/all/modules/advagg/advagg_js_cdn/js/jquery-ui.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,196 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the advagg JS compression module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; Configure advagg settings.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*
|
||||||
|
* @see system_settings_form()
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_admin_settings_form($form, $form_state) {
|
||||||
|
drupal_set_title(t('AdvAgg: JS Compression'));
|
||||||
|
$requirements = advagg_js_compress_check_cache_bin();
|
||||||
|
advagg_display_message_if_requirements_not_met($requirements);
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
$form = array();
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) {
|
||||||
|
$form['advagg_devel_msg'] = array(
|
||||||
|
'#markup' => '<p>' . t('The settings below will not have any effect because AdvAgg is currently in <a href="@devel">development mode</a>. Once the cache settings have been set to normal or aggressive, JS minification will take place.', array('@devel' => url($config_path . '/advagg', array('fragment' => 'edit-advagg-cache-level')))) . '</p>',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
list($list, $redo_list) = advagg_js_compress_all_js_files_list();
|
||||||
|
|
||||||
|
if (!empty($redo_list)) {
|
||||||
|
$form['advagg_js_compressor_batch'] = array(
|
||||||
|
'#markup' => '<p>' . t('There are %count js files that need to be minified. <a href="@batch">Click this batch compress link to process these files.</a>', array(
|
||||||
|
'@batch' => url($config_path . '/advagg/js-compress/batch'),
|
||||||
|
'%count' => count($redo_list),
|
||||||
|
)) . '</p>',
|
||||||
|
);
|
||||||
|
$form['advagg_js_compressor_redo_list'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('List of files that need to be recompressed'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
);
|
||||||
|
$form['advagg_js_compressor_redo_list']['list'] = array(
|
||||||
|
'#markup' => '<p><pre><tt>' . (print_r($redo_list, TRUE)) . '</tt></pre></p>',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
list($options, $description) = advagg_js_compress_configuration();
|
||||||
|
$form['advagg_js_compressor'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('File Compression: Select a Compressor'),
|
||||||
|
'#default_value' => variable_get('advagg_js_compressor', ADVAGG_JS_COMPRESSOR),
|
||||||
|
'#options' => $options,
|
||||||
|
'#description' => filter_xss($description),
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['advagg_js_compress_inline'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Inline Compression: Select a Compressor'),
|
||||||
|
'#default_value' => variable_get('advagg_js_compress_inline', ADVAGG_JS_COMPRESS_INLINE),
|
||||||
|
'#options' => $options,
|
||||||
|
'#description' => filter_xss($description),
|
||||||
|
);
|
||||||
|
$form['advagg_js_compress_inline_if_not_cacheable'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Inline Compression: Use even if this page is not cacheable'),
|
||||||
|
'#default_value' => variable_get('advagg_js_compress_inline_if_not_cacheable', ADVAGG_JS_COMPRESS_INLINE_IF_NOT_CACHEABLE),
|
||||||
|
'#description' => t('By checking this box, all Inline JavaScript will be compressed regardless of the state of <a href="@link">drupal_page_is_cacheable()</a>. If the C complied version of JSMin is enabled, this option should not slow down page generation that much; if you are using JSMin+ I recommend keeping this disabled.', array('@link' => 'http://api.drupal.org/api/drupal/includes!bootstrap.inc/function/drupal_page_is_cacheable/7')),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
':input[name="advagg_js_compress_inline"]' => array('value' => "0"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (variable_get('advagg_gzip', ADVAGG_GZIP) || variable_get('advagg_brotli', ADVAGG_BROTLI)) {
|
||||||
|
$form['advagg_js_compress_packer'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Here there be dragons! Use Packer on non gz/br JS Aggregates.'),
|
||||||
|
'#default_value' => variable_get('advagg_js_compress_packer', ADVAGG_JS_COMPRESS_PACKER),
|
||||||
|
'#description' => t('If enabled the non gzip/brotli version of JS files will be compressed using the JS Packer. Packer works similar to gz/br, thus using packer on a gzip/brotli file does not give a big improvement in terms of bytes transferred over the wire. WARNING: This has a high chance of breaking your JS. Only Enable on production after testing the non gzip/brotli version locally.'),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
':input[name="advagg_js_compressor"]' => array('value' => "0"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$form['advagg_js_compress_add_license'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Licensing comments'),
|
||||||
|
'#default_value' => variable_get('advagg_js_compress_add_license', ADVAGG_JS_COMPRESS_ADD_LICENSE),
|
||||||
|
'#description' => t("Stripping everything will produce somewhat better scores in
|
||||||
|
some automated scans but otherwise should not affect your site. Providing a link to the original source and keeping important comments is enabled by default in order to better follow the spirit of the GPL by linking to the original unminified javascript source files."),
|
||||||
|
'#options' => array(
|
||||||
|
0 => t('Strip everything (smallest files)'),
|
||||||
|
1 => t('Provide a link to the original source as a comment'),
|
||||||
|
2 => t('Keep important comments if the minifier supports it'),
|
||||||
|
3 => t('Do both; try to keep important comments and provide a link'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$options[-1] = t('Default');
|
||||||
|
ksort($options);
|
||||||
|
$form['per_file_settings'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Per File Settings'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
);
|
||||||
|
$file_settings = variable_get('advagg_js_compressor_file_settings', array());
|
||||||
|
foreach ($list as $row) {
|
||||||
|
$dir = dirname($row['data']);
|
||||||
|
if (!isset($form['per_file_settings'][$dir])) {
|
||||||
|
$form['per_file_settings'][$dir] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => check_plain($dir),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$options_copy = $options;
|
||||||
|
if (!empty($row['advagg_js_compress'])) {
|
||||||
|
foreach ($row['advagg_js_compress'] as $key => $values) {
|
||||||
|
if (!isset($options_copy[$key])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($values['code'] != 1) {
|
||||||
|
unset($options_copy[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$options_copy[$key] .= " Ratio: {$values['ratio']}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$form_api_filename = str_replace(array('/', '.'), array('__', '--'), $row['data']);
|
||||||
|
$form['per_file_settings'][$dir]['advagg_js_compressor_file_settings_' . $form_api_filename] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('%filename: Select a Compressor', array('%filename' => $row['data'])),
|
||||||
|
'#default_value' => isset($file_settings[$form_api_filename]) ? $file_settings[$form_api_filename] : ADVAGG_JS_COMPRESSOR_FILE_SETTINGS,
|
||||||
|
'#options' => $options_copy,
|
||||||
|
);
|
||||||
|
if ($form['per_file_settings'][$dir]['advagg_js_compressor_file_settings_' . $form_api_filename]['#default_value'] != ADVAGG_JS_COMPRESSOR_FILE_SETTINGS) {
|
||||||
|
$form['per_file_settings'][$dir]['#collapsed'] = FALSE;
|
||||||
|
$form['per_file_settings']['#collapsed'] = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No js files are found.
|
||||||
|
if (empty($list)) {
|
||||||
|
$form['per_file_settings']['#description'] = t('No JS files have been aggregated. You need to enable aggregation. No js files where found in the advagg_files table.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the cache bins on submit.
|
||||||
|
$form['#submit'][] = 'advagg_js_compress_admin_settings_form_submit';
|
||||||
|
|
||||||
|
return system_settings_form($form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback, clear out the advagg cache bin.
|
||||||
|
*
|
||||||
|
* Also remove default settings inside of the per_file_settings fieldgroup.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms_callback
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_admin_settings_form_submit($form, &$form_state) {
|
||||||
|
advagg_cache_clear_admin_submit();
|
||||||
|
|
||||||
|
// Get current defaults.
|
||||||
|
$file_settings = variable_get('advagg_js_compressor_file_settings', array());
|
||||||
|
|
||||||
|
// Save per file settings.
|
||||||
|
$new_settings = array();
|
||||||
|
foreach ($form_state['values'] as $key => $value) {
|
||||||
|
// Skip if not advagg_js_compressor_file_settings.
|
||||||
|
if (strpos($key, 'advagg_js_compressor_file_settings_') === FALSE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Do not process default settings.
|
||||||
|
if ($value == ADVAGG_JS_COMPRESSOR_FILE_SETTINGS) {
|
||||||
|
unset($form_state['values'][$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$new_settings[substr($key, 35)] = $value;
|
||||||
|
|
||||||
|
// Do not save this field into its own variable.
|
||||||
|
unset($form_state['values'][$key]);
|
||||||
|
}
|
||||||
|
if (!empty($new_settings) || !empty($file_settings)) {
|
||||||
|
if (empty($new_settings)) {
|
||||||
|
variable_del('advagg_js_compressor_file_settings');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
variable_set('advagg_js_compressor_file_settings', $new_settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,921 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced CSS/JS aggregation js compression module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (defined('PHP_VERSION_ID') && constant('PHP_VERSION_ID') >= 50300) {
|
||||||
|
// Include functions that use namespaces.
|
||||||
|
module_load_include('inc', 'advagg_js_compress', 'advagg_js_compress.php53');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_get_info_on_files_alter().
|
||||||
|
*
|
||||||
|
* Used to make sure the info is up to date in the cache.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_advagg_get_info_on_files_alter(&$return, $cached_data, $bypass_cache) {
|
||||||
|
$compressor_list = advagg_js_compress_get_enabled_compressors(array(), -1);
|
||||||
|
|
||||||
|
// Get cache ids.
|
||||||
|
$cache_ids = array();
|
||||||
|
foreach ($return as $filename => &$info) {
|
||||||
|
if (empty($info['fileext']) || $info['fileext'] !== 'js') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Check the cache.
|
||||||
|
$cache_id = 'advagg:js_compress:info:' . $info['filename_hash'];
|
||||||
|
$cache_id .= !empty($info['content_hash']) ? ':' . $info['content_hash'] : '';
|
||||||
|
$cache_ids[$filename] = $cache_id;
|
||||||
|
|
||||||
|
// Verify current data.
|
||||||
|
$advagg_js_compress = array();
|
||||||
|
if (!empty($info['advagg_js_compress'])) {
|
||||||
|
foreach ($info['advagg_js_compress'] as $values) {
|
||||||
|
$array_key = array_search($values['name'], $compressor_list);
|
||||||
|
if ($array_key !== FALSE) {
|
||||||
|
$cache_hits_data[$array_key] = $values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ksort($advagg_js_compress);
|
||||||
|
$info['advagg_js_compress'] = $advagg_js_compress;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($info);
|
||||||
|
// If no cache ids are found bail out.
|
||||||
|
if (empty($cache_ids)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get cached values.
|
||||||
|
$values = array_values($cache_ids);
|
||||||
|
$cache_hits = cache_get_multiple($values, 'cache_advagg_info');
|
||||||
|
$compressors = advagg_js_compress_get_enabled_compressors();
|
||||||
|
$advagg_get_info_on_file_cached_data = drupal_static('advagg_get_info_on_file');
|
||||||
|
|
||||||
|
// Add cached values into $return.
|
||||||
|
$filenames_info = array();
|
||||||
|
foreach ($cache_ids as $filename => $cache_id) {
|
||||||
|
$info = &$return[$filename];
|
||||||
|
|
||||||
|
// Add in cached values.
|
||||||
|
if (!empty($cache_hits[$cache_id])) {
|
||||||
|
// Verify cache data.
|
||||||
|
$cache_hits_data = array();
|
||||||
|
foreach ($cache_hits[$cache_id]->data as $values) {
|
||||||
|
$array_key = array_search($values['name'], $compressor_list);
|
||||||
|
if ($array_key !== FALSE) {
|
||||||
|
$cache_hits_data[$array_key] = $values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ksort($cache_hits_data);
|
||||||
|
|
||||||
|
$info['advagg_js_compress'] = array_replace($info['advagg_js_compress'], $cache_hits_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate missing values if needed.
|
||||||
|
foreach ($compressors as $id => $name) {
|
||||||
|
if (empty($info['advagg_js_compress'][$id])) {
|
||||||
|
$filenames_info[$filename] = $info;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Generate values if bypass cache is set and hashes do not match.
|
||||||
|
if ($bypass_cache
|
||||||
|
&& (empty($advagg_get_info_on_file_cached_data[$info['cache_id']]['content_hash'])
|
||||||
|
|| $info['content_hash'] !== $advagg_get_info_on_file_cached_data[$info['cache_id']]['content_hash']
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$filenames_info[$filename] = $info;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing if compressors are disabled or cache level does not equal 0.
|
||||||
|
if (empty($compressors) || variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($filenames_info)) {
|
||||||
|
$results = advagg_js_compress_run_mutiple_tests($filenames_info, $compressors);
|
||||||
|
foreach ($results as $filename => $data) {
|
||||||
|
$info = &$return[$filename];
|
||||||
|
if (!empty($info['advagg_js_compress'])) {
|
||||||
|
$data += $info['advagg_js_compress'];
|
||||||
|
}
|
||||||
|
$info['advagg_js_compress'] = $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_get_js_file_contents_alter().
|
||||||
|
*
|
||||||
|
* Used to compress a js file.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_advagg_get_js_file_contents_alter(&$contents, $filename, $aggregate_settings) {
|
||||||
|
// Get per file settings.
|
||||||
|
if (!empty($aggregate_settings['variables']['advagg_js_compressor_file_settings'])) {
|
||||||
|
$form_api_filename = str_replace(array('/', '.'), array('__', '--'), $filename);
|
||||||
|
if (isset($aggregate_settings['variables']['advagg_js_compressor_file_settings'][$form_api_filename])) {
|
||||||
|
$aggregate_settings['variables']['advagg_js_compressor'] = $aggregate_settings['variables']['advagg_js_compressor_file_settings'][$form_api_filename];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing if js file compression is disabled.
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_js_compressor'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure this file has been tested.
|
||||||
|
$compressor = $aggregate_settings['variables']['advagg_js_compressor'];
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
$info = advagg_get_info_on_file($filename);
|
||||||
|
if (!isset($info['advagg_js_compress'][$compressor]['code'])) {
|
||||||
|
// Test file here on the spot.
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) == 0
|
||||||
|
|| variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) == 1
|
||||||
|
) {
|
||||||
|
$compressors_to_test = advagg_js_compress_get_enabled_compressors($aggregate_settings);
|
||||||
|
$info['advagg_js_compress'] = advagg_js_compress_run_test($filename, $info, $compressors_to_test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compress it if it passes the test.
|
||||||
|
if (!empty($info['advagg_js_compress'][$compressor]['code'])
|
||||||
|
&& $info['advagg_js_compress'][$compressor]['code'] == 1
|
||||||
|
) {
|
||||||
|
advagg_js_compress_prep($contents, $filename, $aggregate_settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_save_aggregate_alter().
|
||||||
|
*
|
||||||
|
* Used to add in a .gz file if none exits and use packer on non gzip file.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_advagg_save_aggregate_alter(&$files_to_save, $aggregate_settings, $other_parameters) {
|
||||||
|
list($files, $type) = $other_parameters;
|
||||||
|
|
||||||
|
// Return if gzip and brotli are disabled.
|
||||||
|
// Return if packer is disabled.
|
||||||
|
// Return if type is not js.
|
||||||
|
if ((empty($aggregate_settings['variables']['advagg_gzip']) && empty($aggregate_settings['variables']['advagg_brotli']))
|
||||||
|
|| empty($aggregate_settings['variables']['advagg_js_compress_packer'])
|
||||||
|
|| $type !== 'js'
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the first file in the array.
|
||||||
|
$data = reset($files_to_save);
|
||||||
|
$uri = key($files_to_save);
|
||||||
|
// Use packer on non gzip/brotli js files.
|
||||||
|
$compressor = 2;
|
||||||
|
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
// Make sure all files in this aggregate are compatible with packer.
|
||||||
|
foreach ($files as $file => $settings) {
|
||||||
|
$info = advagg_get_info_on_file($file);
|
||||||
|
if (!isset($info['advagg_js_compress'][$compressor]['code'])) {
|
||||||
|
// Add in selected compressor.
|
||||||
|
$compressors = advagg_js_compress_get_enabled_compressors(array(), $compressor);
|
||||||
|
// Test file here on the spot.
|
||||||
|
$info['advagg_js_compress'] = advagg_js_compress_run_test($file, $info, $compressors);
|
||||||
|
}
|
||||||
|
// If this file causes php to bomb or the ratio is way too good then do not
|
||||||
|
// use packer on this aggregate.
|
||||||
|
if (!isset($info['advagg_js_compress'][$compressor]['code'])
|
||||||
|
|| $info['advagg_js_compress'][$compressor]['code'] == -1
|
||||||
|
|| $info['advagg_js_compress'][$compressor]['code'] == -3
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use packer on non gzip/brotli JS data.
|
||||||
|
$aggregate_settings['variables']['advagg_js_compressor'] = $compressor;
|
||||||
|
advagg_js_compress_prep($data, $uri, $aggregate_settings, FALSE);
|
||||||
|
$files_to_save[$uri] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of enabled compressors.
|
||||||
|
*
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* (Optional) aggregate_settings array.
|
||||||
|
* @param int $compressor
|
||||||
|
* (Optional) get info about a particular compressor.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_get_enabled_compressors(array $aggregate_settings = array(), $compressor = 0) {
|
||||||
|
// Create array.
|
||||||
|
list(, , $compressors) = advagg_js_compress_configuration();
|
||||||
|
if ($compressor == -1) {
|
||||||
|
return $compressors;
|
||||||
|
}
|
||||||
|
|
||||||
|
$return_compressors = array();
|
||||||
|
if (!empty($compressor)) {
|
||||||
|
$return_compressors = array($compressor => $compressors[$compressor]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Get variables.
|
||||||
|
if (isset($aggregate_settings['variables']['advagg_js_compressor'])) {
|
||||||
|
$file = $aggregate_settings['variables']['advagg_js_compressor'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$file = variable_get('advagg_js_compressor', ADVAGG_JS_COMPRESSOR);
|
||||||
|
}
|
||||||
|
if (isset($aggregate_settings['variables']['advagg_js_compress_packer'])) {
|
||||||
|
$packer = $aggregate_settings['variables']['advagg_js_compress_packer'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$packer = variable_get('advagg_js_compress_packer', ADVAGG_JS_COMPRESS_PACKER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($compressors[$file])) {
|
||||||
|
$return_compressors[$file] = $compressors[$file];
|
||||||
|
}
|
||||||
|
if ($packer) {
|
||||||
|
$return_compressors[2] = $compressors[2];
|
||||||
|
}
|
||||||
|
if ($file == 3) {
|
||||||
|
// Jsmin check.
|
||||||
|
if (isset($compressors[3])) {
|
||||||
|
$return_compressors[3] = $compressors[3];
|
||||||
|
}
|
||||||
|
// Fallback to jsqueeze.
|
||||||
|
elseif (isset($compressors[5])) {
|
||||||
|
$return_compressors[5] = $compressors[5];
|
||||||
|
}
|
||||||
|
// Fallback to jsmin+.
|
||||||
|
else {
|
||||||
|
$return_compressors[1] = $compressors[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return_compressors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a JS string.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* Javascript string.
|
||||||
|
* @param array $info
|
||||||
|
* Info about the js file.
|
||||||
|
* @param int $compressor
|
||||||
|
* Use a particular compressor.
|
||||||
|
* @param bool $force_run
|
||||||
|
* TRUE to skip cache and force the compression test.
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* The aggregate_settings array.
|
||||||
|
* @param bool $add_licensing
|
||||||
|
* FALSE to remove Source and licensing information comment.
|
||||||
|
* @param bool $log_errors
|
||||||
|
* FALSE to disable logging to watchdog on failure.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* FALSE if there was an error.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_do_it(&$contents, array $info, $compressor, $force_run, array $aggregate_settings, $log_errors) {
|
||||||
|
$no_errors = TRUE;
|
||||||
|
|
||||||
|
// Try cache.
|
||||||
|
$content_hash = !empty($info['content_hash']) ? ":{$info['content_hash']}" : '';
|
||||||
|
$cache_id = "advagg:js_compress:{$compressor}:{$info['filename_hash']}:{$content_hash}";
|
||||||
|
|
||||||
|
$cache = cache_get($cache_id, 'cache_advagg_aggregates');
|
||||||
|
$force_run = variable_get('advagg_js_compress_force_run', $force_run);
|
||||||
|
if (!$force_run && !empty($cache->data)) {
|
||||||
|
$contents = $cache->data;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Strip Byte Order Marks (BOM's), preg_* cannot parse these well.
|
||||||
|
$contents = str_replace(pack("CCC", 0xef, 0xbb, 0xbf), "", $contents);
|
||||||
|
// Jsmin may have errors (incorrectly determining EOLs) with mixed tabs
|
||||||
|
// and spaces. An example: jQuery.Cycle 3.0.3 - http://jquery.malsup.com/
|
||||||
|
if ($compressor == 3) {
|
||||||
|
$contents = str_replace("\t", " ", $contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add end string to get true end of file.
|
||||||
|
// Generate random variable contents and add to end of js string.
|
||||||
|
$random = dechex(mt_rand());
|
||||||
|
$end_string = "var advagg_end=\"$random\";";
|
||||||
|
$contents .= "\n$end_string";
|
||||||
|
|
||||||
|
// Use the compressor.
|
||||||
|
list(, , , $functions) = advagg_js_compress_configuration();
|
||||||
|
if (isset($functions[$compressor])) {
|
||||||
|
$run = $functions[$compressor];
|
||||||
|
if (function_exists($run)) {
|
||||||
|
$no_errors = $run($contents, $log_errors, $aggregate_settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get location of random variable.
|
||||||
|
$end_string = substr($end_string, 0, -1);
|
||||||
|
$pos = strrpos($contents, $end_string);
|
||||||
|
if ($pos === FALSE) {
|
||||||
|
$end_string = str_replace('"', "'", $end_string);
|
||||||
|
$pos = strrpos($contents, $end_string);
|
||||||
|
}
|
||||||
|
if ($pos !== FALSE) {
|
||||||
|
// Cut everything after random variable out of string.
|
||||||
|
$contents = substr($contents, 0, $pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Under some unknown/rare circumstances, JSMin and JSqueeze can add 2-3
|
||||||
|
// extraneous/wrong chars at the end of the string. This work-around will
|
||||||
|
// remove these chars if necessary. See https://www.drupal.org/node/2627468.
|
||||||
|
// Also see https://github.com/sqmk/pecl-jsmin/issues/46.
|
||||||
|
if ($compressor == 3 || $compressor == 5) {
|
||||||
|
$a = strrpos($contents, ';');
|
||||||
|
$b = strrpos($contents, '}');
|
||||||
|
$c = strrpos($contents, ')');
|
||||||
|
|
||||||
|
// Simple scripts can just end, check to make sure there's a
|
||||||
|
// match before cutting.
|
||||||
|
if ($a !== FALSE || $b !== FALSE || $c !== FALSE) {
|
||||||
|
$contents = substr($contents, 0, 1 + max($a, $b, $c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that $contents ends with ; or }.
|
||||||
|
if (strpbrk(substr(trim($contents), -1), ';}') === FALSE) {
|
||||||
|
// ; or } not found, add in ; to the end of $contents.
|
||||||
|
$contents = trim($contents) . ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($info['#no_cache'])) {
|
||||||
|
// Cache minified data for 1 week.
|
||||||
|
cache_set($cache_id, $contents, 'cache_advagg_aggregates', REQUEST_TIME + (86400 * 7));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Cache minified inline data for 1 hour.
|
||||||
|
cache_set($cache_id, $contents, 'cache_advagg_aggregates', REQUEST_TIME + 3600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $no_errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a JS string.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* Javascript string.
|
||||||
|
* @param string $filename
|
||||||
|
* Filename.
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* The aggregate_settings array.
|
||||||
|
* @param bool $add_licensing
|
||||||
|
* FALSE to remove Source and licensing information comment.
|
||||||
|
* @param bool $log_errors
|
||||||
|
* FALSE to disable logging to watchdog on failure.
|
||||||
|
* @param bool $test_ratios
|
||||||
|
* FALSE to disable compression ratio testing.
|
||||||
|
* @param bool $force_run
|
||||||
|
* TRUE to skip cache and force the compression test.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* FALSE if there was an error.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_prep(&$contents, $filename, array $aggregate_settings, $add_licensing = TRUE, $log_errors = TRUE, $test_ratios = TRUE, $force_run = FALSE) {
|
||||||
|
$no_errors = TRUE;
|
||||||
|
// Get the info on this file.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
$compressor = $aggregate_settings['variables']['advagg_js_compressor'];
|
||||||
|
|
||||||
|
if ($compressor == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Do nothing if the file is already minified.
|
||||||
|
$url = file_create_url($filename);
|
||||||
|
$semicolon_count = substr_count($contents, ';');
|
||||||
|
if ($compressor != 2
|
||||||
|
&& $semicolon_count > 10
|
||||||
|
&& $semicolon_count > (substr_count($contents, "\n", strpos($contents, ';')) * 5)
|
||||||
|
&& !$force_run
|
||||||
|
) {
|
||||||
|
$add_license_setting
|
||||||
|
= isset($aggregate_settings['variables']['advagg_js_compress_add_license']) ?
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_add_license'] :
|
||||||
|
variable_get('advagg_js_compress_add_license', ADVAGG_JS_COMPRESS_ADD_LICENSE);
|
||||||
|
if ($add_licensing && ($add_license_setting == 1 || $add_license_setting == 3)) {
|
||||||
|
$contents = "/* Source and licensing information for the line(s) below can be found at $url. */\n" . $contents . ";\n/* Source and licensing information for the above line(s) can be found at $url. */\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the JS string length before the compression operation.
|
||||||
|
$contents_before = $contents;
|
||||||
|
$before = strlen($contents);
|
||||||
|
|
||||||
|
// Do not use jsmin() if the function can not be called.
|
||||||
|
if ($compressor == 3 && !function_exists('jsmin')) {
|
||||||
|
if (defined('PHP_VERSION_ID') && constant('PHP_VERSION_ID') >= 50300) {
|
||||||
|
$compressor = 5;
|
||||||
|
watchdog('advagg_js_compress', 'The jsmin function does not exist. Using JSqueeze.');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$compressor = 1;
|
||||||
|
watchdog('advagg_js_compress', 'The jsmin function does not exist. Using JSmin+.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jsmin doesn't handle multi-byte characters before version 2, fall back to
|
||||||
|
// different compressor if jsmin version < 2 and $contents contains multi-
|
||||||
|
// byte characters.
|
||||||
|
if ($compressor == 3 && (version_compare(phpversion('jsmin'), '2.0.0', '<') && advagg_js_compress_string_contains_multibyte_characters($contents))) {
|
||||||
|
if (defined('PHP_VERSION_ID') && constant('PHP_VERSION_ID') >= 50300) {
|
||||||
|
$compressor = 5;
|
||||||
|
watchdog('advagg_js_compress', 'The currently installed jsmin version does not handle multibyte characters, you may consider to upgrade the jsmin extension. Using JSqueeze fallback.');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$compressor = 1;
|
||||||
|
watchdog('advagg_js_compress', 'The currently installed jsmin version does not handle multibyte characters, you may consider to upgrade the jsmin extension. Using JSmin+ fallback.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$info = advagg_get_info_on_files(array($filename), FALSE, FALSE);
|
||||||
|
$info = $info[$filename];
|
||||||
|
|
||||||
|
$no_errors = advagg_js_compress_do_it($contents, $info, $compressor, $force_run, $aggregate_settings, $log_errors, $url);
|
||||||
|
|
||||||
|
// Make sure compression ratios are good.
|
||||||
|
$after = strlen($contents);
|
||||||
|
$ratio = 0;
|
||||||
|
if ($before != 0) {
|
||||||
|
$ratio = ($before - $after) / $before;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ratios settings.
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_max_ratio']
|
||||||
|
= isset($aggregate_settings['variables']['advagg_js_compress_max_ratio']) ?
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_max_ratio'] :
|
||||||
|
variable_get('advagg_js_compress_max_ratio', ADVAGG_JS_COMPRESS_MAX_RATIO);
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_ratio']
|
||||||
|
= isset($aggregate_settings['variables']['advagg_js_compress_ratio']) ?
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_ratio'] :
|
||||||
|
variable_get('advagg_js_compress_ratio', ADVAGG_JS_COMPRESS_RATIO);
|
||||||
|
|
||||||
|
// Get license settings.
|
||||||
|
$add_license_setting
|
||||||
|
= isset($aggregate_settings['variables']['advagg_js_compress_add_license']) ?
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_add_license'] :
|
||||||
|
variable_get('advagg_js_compress_add_license', ADVAGG_JS_COMPRESS_ADD_LICENSE);
|
||||||
|
|
||||||
|
// Make sure the returned string is not empty or has a VERY high
|
||||||
|
// compression ratio.
|
||||||
|
if (empty($contents)
|
||||||
|
|| empty($ratio)
|
||||||
|
|| $ratio < $aggregate_settings['variables']['advagg_js_compress_ratio']
|
||||||
|
|| $ratio > $aggregate_settings['variables']['advagg_js_compress_max_ratio']
|
||||||
|
) {
|
||||||
|
$contents = $contents_before;
|
||||||
|
if ($compressor !== 1) {
|
||||||
|
// Try again using jsmin+.
|
||||||
|
$no_errors = advagg_js_compress_do_it($contents, $info, 1, $force_run, $aggregate_settings, $log_errors, $url);
|
||||||
|
// Make sure compression ratios are good.
|
||||||
|
$after = strlen($contents);
|
||||||
|
$ratio = 0;
|
||||||
|
if ($before != 0) {
|
||||||
|
$ratio = ($before - $after) / $before;
|
||||||
|
}
|
||||||
|
if (empty($contents)
|
||||||
|
|| empty($ratio)
|
||||||
|
|| $ratio < $aggregate_settings['variables']['advagg_js_compress_ratio']
|
||||||
|
|| $ratio > $aggregate_settings['variables']['advagg_js_compress_max_ratio']
|
||||||
|
) {
|
||||||
|
$contents = $contents_before;
|
||||||
|
$add_licensing = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($add_licensing && ($add_license_setting == 1 || $add_license_setting == 3)) {
|
||||||
|
$contents = "/* Source and licensing information for the line(s) below can be found at $url. */\n" . $contents . ";\n/* Source and licensing information for the above line(s) can be found at $url. */\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset if cache settings are set to Development.
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0 && !$force_run) {
|
||||||
|
$contents = $contents_before;
|
||||||
|
}
|
||||||
|
return $no_errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if string contains multibyte characters.
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* String to check.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE if string contains multibyte character.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_string_contains_multibyte_characters($string) {
|
||||||
|
// Check if there are multy-byte characters: If the UTF-8 encoded string has
|
||||||
|
// multybytes strlen() will return a byte-count greater than the actual
|
||||||
|
// character count, returned by drupal_strlen().
|
||||||
|
if (strlen($string) == drupal_strlen($string)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a JS string using jsmin+.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* Javascript string.
|
||||||
|
* @param bool $log_errors
|
||||||
|
* FALSE to disable logging to watchdog on failure.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_jsminplus(&$contents, $log_errors = TRUE) {
|
||||||
|
$no_errors = TRUE;
|
||||||
|
$contents_before = $contents;
|
||||||
|
$old_error = error_get_last();
|
||||||
|
|
||||||
|
// Set max nesting level.
|
||||||
|
if (!class_exists('JSMinPlus')) {
|
||||||
|
$nesting_level = ini_get('xdebug.max_nesting_level');
|
||||||
|
if (!empty($nesting_level) && $nesting_level < 200) {
|
||||||
|
ini_set('xdebug.max_nesting_level', 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try libraries for jsminplus.
|
||||||
|
if (is_callable('libraries_load')) {
|
||||||
|
libraries_load('jsminplus');
|
||||||
|
}
|
||||||
|
// Only include jsminplus.inc if the JSMinPlus class doesn't exist.
|
||||||
|
if (!class_exists('JSMinPlus')) {
|
||||||
|
include drupal_get_path('module', 'advagg_js_compress') . '/jsminplus.inc';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
try {
|
||||||
|
// JSMin+ the contents of the aggregated file.
|
||||||
|
$contents = @JSMinPlus::minify($contents);
|
||||||
|
|
||||||
|
// Capture any output from JSMinPlus.
|
||||||
|
$error = trim(ob_get_contents());
|
||||||
|
if (!empty($error)) {
|
||||||
|
$no_errors = FALSE;
|
||||||
|
throw new Exception($error);
|
||||||
|
}
|
||||||
|
$recent_error = error_get_last();
|
||||||
|
if (!empty($recent_error) && serialize($recent_error) !== serialize($old_error)) {
|
||||||
|
$no_errors = FALSE;
|
||||||
|
$error = print_r($recent_error, TRUE);
|
||||||
|
throw new Exception($error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
$no_errors = FALSE;
|
||||||
|
// Log the exception thrown by JSMin+ and roll back to uncompressed content.
|
||||||
|
if ($log_errors) {
|
||||||
|
watchdog('advagg_js_compress', '@message <pre> @contents </pre>', array(
|
||||||
|
'@message' => $e->getMessage(),
|
||||||
|
'@contents' => $contents_before,
|
||||||
|
), WATCHDOG_WARNING);
|
||||||
|
}
|
||||||
|
$contents = $contents_before;
|
||||||
|
}
|
||||||
|
ob_end_clean();
|
||||||
|
return $no_errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a JS string using packer.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* Javascript string.
|
||||||
|
* @param bool $log_errors
|
||||||
|
* FALSE to disable logging to watchdog on failure.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_jspacker(&$contents, $log_errors = TRUE) {
|
||||||
|
$no_errors = TRUE;
|
||||||
|
$contents_before = $contents;
|
||||||
|
|
||||||
|
// Try libraries for jspacker.
|
||||||
|
if (is_callable('libraries_load')) {
|
||||||
|
libraries_load('jspacker');
|
||||||
|
}
|
||||||
|
if (!class_exists('JavaScriptPacker')) {
|
||||||
|
include drupal_get_path('module', 'advagg_js_compress') . '/jspacker.inc';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add semicolons to the end of lines if missing.
|
||||||
|
$contents = str_replace("}\n", "};\n", $contents);
|
||||||
|
$contents = str_replace("\nfunction", ";\nfunction", $contents);
|
||||||
|
|
||||||
|
// Use Packer on the contents of the aggregated file.
|
||||||
|
try {
|
||||||
|
$packer = new JavaScriptPacker($contents, 62, TRUE, FALSE);
|
||||||
|
$contents = $packer->pack();
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
$no_errors = FALSE;
|
||||||
|
// Log the exception thrown by JSMin+ and roll back to uncompressed content.
|
||||||
|
if ($log_errors) {
|
||||||
|
watchdog('advagg_js_compress', '@message <pre> @contents </pre>', array(
|
||||||
|
'@message' => $e->getMessage(),
|
||||||
|
'@contents' => $contents_before,
|
||||||
|
), WATCHDOG_WARNING);
|
||||||
|
}
|
||||||
|
$contents = $contents_before;
|
||||||
|
}
|
||||||
|
return $no_errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a JS string using jsmin.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* Javascript string.
|
||||||
|
* @param bool $log_errors
|
||||||
|
* FALSE to disable logging to watchdog on failure.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_jsmin(&$contents, $log_errors = TRUE) {
|
||||||
|
$no_errors = TRUE;
|
||||||
|
$contents_before = $contents;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$contents = jsmin($contents);
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
$no_errors = FALSE;
|
||||||
|
// Log the exception thrown by JSMin+ and roll back to uncompressed content.
|
||||||
|
if ($log_errors) {
|
||||||
|
watchdog('advagg_js_compress', '@message <pre> @contents </pre>', array(
|
||||||
|
'@message' => $e->getMessage(),
|
||||||
|
'@contents' => $contents_before,
|
||||||
|
), WATCHDOG_WARNING);
|
||||||
|
}
|
||||||
|
$contents = $contents_before;
|
||||||
|
}
|
||||||
|
return $no_errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test a JS file to see if it compresses well.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* Path and filename of JS file.
|
||||||
|
* @param array $info
|
||||||
|
* (Optional) advagg_get_info_on_file().
|
||||||
|
* @param array $compressors
|
||||||
|
* (Optional) List of compressors to test.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* info about the file.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_run_test($filename, array $info = array(), array $compressors = array()) {
|
||||||
|
// Get the info on this file.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
if (empty($info)) {
|
||||||
|
$info = advagg_get_info_on_file($filename, FALSE, FALSE);
|
||||||
|
}
|
||||||
|
$cache_id = 'advagg:js_compress:info:' . $info['filename_hash'];
|
||||||
|
$cache_id .= !empty($info['content_hash']) ? ':' . $info['content_hash'] : '';
|
||||||
|
|
||||||
|
$compressor_list = advagg_js_compress_get_enabled_compressors(array(), -1);
|
||||||
|
// Build list of compressors.
|
||||||
|
if (empty($compressors)) {
|
||||||
|
$compressors = $compressor_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set to 0 if file doesn't exist.
|
||||||
|
if (empty($info['content_hash'])) {
|
||||||
|
foreach ($compressor_list as $key => $name) {
|
||||||
|
$results[$key] = array('code' => 0, 'ratio' => 0, 'name' => $name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Set to "-1" so if php bombs, the file will be marked as bad.
|
||||||
|
foreach ($compressor_list as $key => $name) {
|
||||||
|
$results[$key] = array('code' => -1, 'ratio' => 0, 'name' => $name);
|
||||||
|
}
|
||||||
|
$run_locally = TRUE;
|
||||||
|
// Run this via httprl if possible.
|
||||||
|
if (module_exists('httprl') && httprl_is_background_callback_capable()) {
|
||||||
|
$run_locally = FALSE;
|
||||||
|
// Setup callback options array.
|
||||||
|
$callback_options = array(
|
||||||
|
array(
|
||||||
|
'function' => 'advagg_js_compress_test_file',
|
||||||
|
'return' => &$results,
|
||||||
|
),
|
||||||
|
$filename, $compressors, $cache_id,
|
||||||
|
);
|
||||||
|
// Queue up the request.
|
||||||
|
httprl_queue_background_callback($callback_options);
|
||||||
|
// Execute request.
|
||||||
|
httprl_send_request();
|
||||||
|
|
||||||
|
// If php bombs out, try each compressor individually.
|
||||||
|
foreach ($results as $key => $value) {
|
||||||
|
if ($value['code'] == -1) {
|
||||||
|
$sub_result = array();
|
||||||
|
$sub_result[$key] = $value;
|
||||||
|
// Setup callback options array.
|
||||||
|
$callback_options = array(
|
||||||
|
array(
|
||||||
|
'function' => 'advagg_js_compress_test_file',
|
||||||
|
'return' => &$sub_result,
|
||||||
|
),
|
||||||
|
$filename, array($key => $value['name']), $cache_id,
|
||||||
|
);
|
||||||
|
// Queue up the request.
|
||||||
|
httprl_queue_background_callback($callback_options);
|
||||||
|
// Execute request.
|
||||||
|
httprl_send_request();
|
||||||
|
|
||||||
|
$results[$key] = $sub_result[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try locally if all return back -1 (something wrong with httprl).
|
||||||
|
foreach ($results as $key => $value) {
|
||||||
|
if ($value['code'] != -1) {
|
||||||
|
$run_locally = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($run_locally) {
|
||||||
|
// Save results, so if PHP bombs, this file is marked as bad.
|
||||||
|
// CACHE_PERMANENT isn't good here. Use 2 weeks from now + 0-45 days.
|
||||||
|
// The random 0 to 45 day addition is to prevent a cache stampeed.
|
||||||
|
cache_set($cache_id, $results, 'cache_advagg_info', round(REQUEST_TIME + 1209600 + mt_rand(0, 3888000), -3));
|
||||||
|
|
||||||
|
// Test the file.
|
||||||
|
$results = advagg_js_compress_test_file($filename, $compressors, $cache_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save and return results.
|
||||||
|
// Save results, so if PHP bombs, this file is marked as bad.
|
||||||
|
// CACHE_PERMANENT isn't good here. Use 2 weeks from now + 0-45 days.
|
||||||
|
// The random 0 to 45 day addition is to prevent a cache stampeed.
|
||||||
|
cache_set($cache_id, $results, 'cache_advagg_info', round(REQUEST_TIME + 1209600 + mt_rand(0, 3888000), -3));
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test a JS file to see if it compresses well.
|
||||||
|
*
|
||||||
|
* @param array $filenames_info
|
||||||
|
* Array of filenames and info from advagg_get_info_on_file().
|
||||||
|
* @param array $compressors
|
||||||
|
* (Optional) List of compressors to test.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Info about the file.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_run_mutiple_tests(array $filenames_info, array $compressors = array()) {
|
||||||
|
// Get the info on this file.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
|
||||||
|
$compressor_list = advagg_js_compress_get_enabled_compressors(array(), -1);
|
||||||
|
// Build list of compressors.
|
||||||
|
if (empty($compressors)) {
|
||||||
|
$compressors = $compressor_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent PHP from running out of time if working with a lot of files.
|
||||||
|
if (count($filenames_info) > 5) {
|
||||||
|
// Set to max time if running from the command line.
|
||||||
|
if (drupal_is_cli()) {
|
||||||
|
drupal_set_time_limit(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$max_execution_time = ini_get('max_execution_time');
|
||||||
|
if ($max_execution_time != 0) {
|
||||||
|
$current_time = 5;
|
||||||
|
if (is_callable('getrusage')) {
|
||||||
|
$dat = getrusage();
|
||||||
|
$current_time = $dat["ru_utime.tv_sec"];
|
||||||
|
}
|
||||||
|
$max_time = max(30, ini_get('max_execution_time'));
|
||||||
|
$time_left = $max_time - $current_time;
|
||||||
|
// Give every file 3 seconds.
|
||||||
|
drupal_set_time_limit(count($filenames_info) * 3 + $time_left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$return = array();
|
||||||
|
foreach ($filenames_info as $filename => $info) {
|
||||||
|
if (!module_exists('httprl') || !httprl_is_background_callback_capable()) {
|
||||||
|
$return[$filename] = advagg_js_compress_run_test($filename, $info, $compressors);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup this run.
|
||||||
|
$results = array();
|
||||||
|
if (empty($info)) {
|
||||||
|
$info = advagg_get_info_on_file($filename, FALSE, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set to 0 if file doesn't exist.
|
||||||
|
if (empty($info['content_hash'])) {
|
||||||
|
foreach ($compressor_list as $key => $name) {
|
||||||
|
$results[$key] = array('code' => 0, 'ratio' => 0, 'name' => $name);
|
||||||
|
}
|
||||||
|
$return[$filename] = $results;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = &$return[$filename];
|
||||||
|
// Set to "-1" so if php bombs, the file will be marked as bad.
|
||||||
|
foreach ($compressor_list as $key => $name) {
|
||||||
|
$results[$key] = array('code' => -1, 'ratio' => 0, 'name' => $name);
|
||||||
|
}
|
||||||
|
// Get cache id.
|
||||||
|
$cache_id = 'advagg:js_compress:info:' . $info['filename_hash'];
|
||||||
|
$cache_id .= !empty($info['content_hash']) ? ':' . $info['content_hash'] : '';
|
||||||
|
|
||||||
|
// Setup callback options array.
|
||||||
|
$callback_options = array(
|
||||||
|
array(
|
||||||
|
'function' => 'advagg_js_compress_test_file',
|
||||||
|
'return' => &$results,
|
||||||
|
),
|
||||||
|
$filename, $compressors, $cache_id,
|
||||||
|
);
|
||||||
|
// Queue up the request.
|
||||||
|
httprl_queue_background_callback($callback_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module_exists('httprl') && httprl_is_background_callback_capable()) {
|
||||||
|
// Execute request.
|
||||||
|
httprl_send_request();
|
||||||
|
|
||||||
|
foreach ($return as $filename => &$results) {
|
||||||
|
if (!empty($results)) {
|
||||||
|
// If we have one bad set, we need to retest all.
|
||||||
|
foreach ($results as $key => $value) {
|
||||||
|
if ($value['code'] == -1) {
|
||||||
|
unset($results);
|
||||||
|
$results = array();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If php bombs out, try each compressor individually.
|
||||||
|
if (empty($results)) {
|
||||||
|
foreach ($compressor_list as $key => $name) {
|
||||||
|
$info = $filenames_info[$filename];
|
||||||
|
$cache_id = 'advagg:js_compress:info:' . $info['filename_hash'];
|
||||||
|
$cache_id .= !empty($info['content_hash']) ? ':' . $info['content_hash'] : '';
|
||||||
|
|
||||||
|
$sub_result = array();
|
||||||
|
$sub_result[$key] = '';
|
||||||
|
// Setup callback options array.
|
||||||
|
$callback_options = array(
|
||||||
|
array(
|
||||||
|
'function' => 'advagg_js_compress_test_file',
|
||||||
|
'return' => &$sub_result,
|
||||||
|
),
|
||||||
|
$filename, array($key => $name), $cache_id,
|
||||||
|
);
|
||||||
|
// Queue up the request.
|
||||||
|
httprl_queue_background_callback($callback_options);
|
||||||
|
// Execute request.
|
||||||
|
httprl_send_request();
|
||||||
|
|
||||||
|
if (!empty($sub_result[$key])) {
|
||||||
|
$results[$key] = $sub_result[$key];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Set to "-1" as php bombed, the file will be marked as bad.
|
||||||
|
$results[$key] = array('code' => -1, 'ratio' => 0, 'name' => $name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($results);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Drush commands for AdvAgg JS minification.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup 3rd_party_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_drush_help().
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_drush_help($command) {
|
||||||
|
switch ($command) {
|
||||||
|
case 'drush:advagg-js-compress':
|
||||||
|
return dt('Run js minification for all js files.');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_drush_command().
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_drush_command() {
|
||||||
|
$items = array();
|
||||||
|
$items['advagg-js-compress'] = array(
|
||||||
|
'callback' => 'drush_advagg_js_compress',
|
||||||
|
'description' => dt('Run js minification.'),
|
||||||
|
'core' => array('7+'),
|
||||||
|
'arguments' => array(
|
||||||
|
'filename' => 'all will do all files, or specify the filename to target that file.',
|
||||||
|
),
|
||||||
|
'examples' => array(
|
||||||
|
'drush advagg-js-compress' => dt('Minify only the files that need to be done.'),
|
||||||
|
'drush advagg-js-compress all' => dt('Minify all js files again.'),
|
||||||
|
'drush advagg-js-compress misc/jquery.once.js' => dt('Minify the misc/jquery.once.js file.'),
|
||||||
|
),
|
||||||
|
'aliases' => array(
|
||||||
|
'advagg-jsc',
|
||||||
|
'advagg-jsmin',
|
||||||
|
'',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup 3rd_party_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for drush advagg-js-compress.
|
||||||
|
*
|
||||||
|
* Callback is called by using drush_hook_command() where
|
||||||
|
* hook is the name of the module (advagg) and command is the name of
|
||||||
|
* the Drush command with all "-" characters converted to "_" characters.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* The filename to compress or all to redo all files.
|
||||||
|
*/
|
||||||
|
function drush_advagg_js_compress($filename = '') {
|
||||||
|
// Get the redo list.
|
||||||
|
list($list, $redo_list) = advagg_js_compress_all_js_files_list();
|
||||||
|
|
||||||
|
// Handle special use cases.
|
||||||
|
if (!empty($filename)) {
|
||||||
|
// Do all.
|
||||||
|
if (strtolower($filename) === 'all') {
|
||||||
|
$redo_list = $list;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Do a single file, search for it in the $list.
|
||||||
|
$redo_list = array();
|
||||||
|
foreach ($list as $values) {
|
||||||
|
if ($values['data'] === $filename) {
|
||||||
|
$redo_list = array($values);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let user know if that file was not found.
|
||||||
|
if (empty($redo_list)) {
|
||||||
|
drush_log(dt('The file @filename was not found.', array(
|
||||||
|
'@filename' => $filename,
|
||||||
|
)), 'notice');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if nothing to do.
|
||||||
|
if (empty($redo_list)) {
|
||||||
|
drush_log(dt('All of @total js files are already minified.', array(
|
||||||
|
'@total' => count($list),
|
||||||
|
)), 'ok');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let user know what will happen.
|
||||||
|
drush_log(dt('A total of @redo out of @total js files will be minified.', array(
|
||||||
|
'@redo' => count($redo_list),
|
||||||
|
'@total' => count($list),
|
||||||
|
)), 'ok');
|
||||||
|
|
||||||
|
// Compress js files and cache.
|
||||||
|
advagg_js_compress_redo_files($redo_list, 0, TRUE);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
name = AdvAgg Compress Javascript
|
||||||
|
description = Compress Javascript with a 3rd party compressor; JSMin+, JSMin c ext, JShrink, and JSqueeze currently.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
recommends[] = libraries
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg/js-compress
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,151 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles AdvAgg JS compress installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_requirements().
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_requirements($phase) {
|
||||||
|
$requirements = array();
|
||||||
|
// Ensure translations don't break at install time.
|
||||||
|
$t = get_t();
|
||||||
|
|
||||||
|
// If not at runtime, return here.
|
||||||
|
if ($phase !== 'runtime') {
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure a compressor is being used.
|
||||||
|
if (variable_get('advagg_js_compressor', ADVAGG_JS_COMPRESSOR) == 0
|
||||||
|
&& variable_get('advagg_js_compress_inline', ADVAGG_JS_COMPRESS_INLINE) == 0
|
||||||
|
) {
|
||||||
|
// Check all files.
|
||||||
|
$file_settings = variable_get('advagg_js_compressor_file_settings', array());
|
||||||
|
$compression_used = FALSE;
|
||||||
|
foreach ($file_settings as $setting) {
|
||||||
|
if (!empty($setting)) {
|
||||||
|
$compression_used = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$compression_used) {
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
$requirements['advagg_js_compress_not_on'] = array(
|
||||||
|
'title' => $t('AdvAgg JS Compressor'),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('AdvAgg JS Compression is disabled.'),
|
||||||
|
'description' => $t('Go to the <a href="@settings">advagg js compress settings page</a> and select a compressor, or go to the <a href="@modules">modules page</a> and disable the "AdvAgg Compress Javascript" module.', array(
|
||||||
|
'@settings' => url($config_path . '/advagg/js-compress'),
|
||||||
|
'@modules' => url('admin/modules', array(
|
||||||
|
'fragment' => 'edit-modules-advanced-cssjs-aggregation',
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$requirements += advagg_js_compress_check_cache_bin();
|
||||||
|
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade AdvAgg JS Compress versions (6.x-1.x and 7.x-1.x) to 7.x-2.x.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_update_7200(&$sandbox) {
|
||||||
|
// Bail if old DB Table does not exist.
|
||||||
|
if (!db_table_exists('cache_advagg_js_compress_inline') && !db_table_exists('cache_advagg_js_compress_file')) {
|
||||||
|
return t('Nothing needed to happen in AdvAgg JS Compress.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all old advagg css compress variables.
|
||||||
|
db_delete('variable')
|
||||||
|
->condition('name', 'advagg_js%compress%', 'LIKE')
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
// Remove old schema.
|
||||||
|
if (db_table_exists('cache_advagg_js_compress_inline')) {
|
||||||
|
db_drop_table('cache_advagg_js_compress_inline');
|
||||||
|
}
|
||||||
|
if (db_table_exists('cache_advagg_js_compress_file')) {
|
||||||
|
db_drop_table('cache_advagg_js_compress_file');
|
||||||
|
}
|
||||||
|
|
||||||
|
return t('Upgraded AdvAgg JS Compress to 7.x-2.x.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the cache_advagg_info cache.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_update_7201(&$sandbox) {
|
||||||
|
cache_clear_all('advagg:js_compress:', 'cache_advagg_info', TRUE);
|
||||||
|
return t('Cleared the cache_advagg_info cache of js_compress entries.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change variable names so they are prefixed with the modules name.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_update_7202(&$sandbox) {
|
||||||
|
// Rename advagg_js_inline_compressor to advagg_js_compress_inline.
|
||||||
|
$old = variable_get('advagg_js_inline_compressor', NULL);
|
||||||
|
if (!is_null($old)) {
|
||||||
|
variable_del('advagg_js_inline_compressor');
|
||||||
|
}
|
||||||
|
if ($old !== ADVAGG_JS_COMPRESS_INLINE) {
|
||||||
|
variable_set('advagg_js_compress_inline', $old);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename advagg_js_inline_compress_if_not_cacheable to
|
||||||
|
// advagg_js_compress_inline_if_not_cacheable.
|
||||||
|
$old = variable_get('advagg_js_inline_compress_if_not_cacheable', NULL);
|
||||||
|
if (!is_null($old)) {
|
||||||
|
variable_del('advagg_js_inline_compress_if_not_cacheable');
|
||||||
|
}
|
||||||
|
if ($old !== ADVAGG_JS_COMPRESS_INLINE_IF_NOT_CACHEABLE) {
|
||||||
|
variable_set('advagg_js_compress_inline_if_not_cacheable', $old);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename advagg_js_inline_compress_if_not_cacheable to
|
||||||
|
// advagg_js_compress_inline_if_not_cacheable.
|
||||||
|
$old = variable_get('advagg_js_inline_compress_if_not_cacheable', NULL);
|
||||||
|
if (!is_null($old)) {
|
||||||
|
variable_del('advagg_js_inline_compress_if_not_cacheable');
|
||||||
|
}
|
||||||
|
if ($old !== ADVAGG_JS_COMPRESS_INLINE_IF_NOT_CACHEABLE) {
|
||||||
|
variable_set('advagg_js_compress_inline_if_not_cacheable', $old);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename advagg_js_max_compress_ratio to advagg_js_compress_max_ratio.
|
||||||
|
$old = variable_get('advagg_js_max_compress_ratio', NULL);
|
||||||
|
if (!is_null($old)) {
|
||||||
|
variable_del('advagg_js_max_compress_ratio');
|
||||||
|
}
|
||||||
|
if ($old !== ADVAGG_JS_COMPRESS_MAX_RATIO) {
|
||||||
|
variable_set('advagg_js_compress_max_ratio', $old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove unused variables from the variable table.
|
||||||
|
*
|
||||||
|
* Implements hook_update_N().
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_update_7203(&$sandbox) {
|
||||||
|
// Remove all old advagg css compress variables.
|
||||||
|
db_delete('variable')
|
||||||
|
->condition('name', 'advagg_js_compressor_file_settings_%', 'LIKE')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
@ -0,0 +1,776 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced CSS/JS aggregation js compression module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup default_variables
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to see packer is enabled.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_COMPRESS_PACKER', FALSE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to see what compressor to use. 0 is Disabled.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_COMPRESSOR', 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to see what compressor to use. 0 is Disabled.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_COMPRESS_INLINE', 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to if inline compression is used if page is not cacheable.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_COMPRESS_INLINE_IF_NOT_CACHEABLE', FALSE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for the compression ratio test.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_COMPRESS_RATIO', 0.1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for the compression ratio test.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_COMPRESS_MAX_RATIO', 0.9);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for per file compression settings.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_COMPRESSOR_FILE_SETTINGS', -1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to if inline compression is used if page is not cacheable.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_COMPRESS_ADD_LICENSE', 3);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to refresh the data in the cache 1 week before the TTL expires.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_JS_COMPRESS_REFRESH_BEFORE_CACHE_TTL', 604800);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup default_variables".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_cron().
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_cron() {
|
||||||
|
// Get the redo list.
|
||||||
|
list(, $redo_list) = advagg_js_compress_all_js_files_list();
|
||||||
|
|
||||||
|
// Return if nothing to do.
|
||||||
|
if (empty($redo_list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compress js files and cache.
|
||||||
|
advagg_js_compress_redo_files($redo_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu().
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_menu() {
|
||||||
|
$file_path = drupal_get_path('module', 'advagg_js_compress');
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
$items[$config_path . '/advagg/js-compress'] = array(
|
||||||
|
'title' => 'JS Compression',
|
||||||
|
'description' => 'Adjust JS Compression settings.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('advagg_js_compress_admin_settings_form'),
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file path' => $file_path,
|
||||||
|
'file' => 'advagg_js_compress.admin.inc',
|
||||||
|
'weight' => 10,
|
||||||
|
);
|
||||||
|
$items[$config_path . '/advagg/js-compress/batch'] = array(
|
||||||
|
'title' => 'Batch Generate',
|
||||||
|
'page callback' => 'advagg_js_compress_batch_callback',
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_module_implements_alter().
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_module_implements_alter(&$implementations, $hook) {
|
||||||
|
// Move advagg_js_compress below advagg.
|
||||||
|
if ($hook === 'advagg_save_aggregate_alter' && array_key_exists('advagg_js_compress', $implementations)) {
|
||||||
|
$advagg_key = '';
|
||||||
|
$advagg_js_compress_key = '';
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($implementations as $key => $value) {
|
||||||
|
if ($key == 'advagg') {
|
||||||
|
$advagg_key = $counter;
|
||||||
|
}
|
||||||
|
if ($key == 'advagg_js_compress') {
|
||||||
|
$advagg_js_compress_key = $counter;
|
||||||
|
}
|
||||||
|
$counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($advagg_js_compress_key > $advagg_key) {
|
||||||
|
// Move advagg_js_compress to the top.
|
||||||
|
$item = array('advagg_js_compress' => $implementations['advagg_js_compress']);
|
||||||
|
unset($implementations['advagg_js_compress']);
|
||||||
|
$implementations = array_merge($item, $implementations);
|
||||||
|
|
||||||
|
// Move advagg to the very top.
|
||||||
|
$item = array('advagg' => $implementations['advagg']);
|
||||||
|
unset($implementations['advagg']);
|
||||||
|
$implementations = array_merge($item, $implementations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_current_hooks_hash_array_alter().
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_advagg_current_hooks_hash_array_alter(&$aggregate_settings) {
|
||||||
|
$aggregate_settings['variables']['advagg_js_compressor'] = variable_get('advagg_js_compressor', ADVAGG_JS_COMPRESSOR);
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_packer'] = variable_get('advagg_js_compress_packer', ADVAGG_JS_COMPRESS_PACKER);
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_ratio'] = variable_get('advagg_js_compress_ratio', ADVAGG_JS_COMPRESS_RATIO);
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_max_ratio'] = variable_get('advagg_js_compress_max_ratio', ADVAGG_JS_COMPRESS_MAX_RATIO);
|
||||||
|
$aggregate_settings['variables']['advagg_js_compressor_file_settings'] = variable_get('advagg_js_compressor_file_settings', array());
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_add_license'] = variable_get('advagg_js_compress_add_license', ADVAGG_JS_COMPRESS_ADD_LICENSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_modify_js_pre_render_alter().
|
||||||
|
*
|
||||||
|
* Used compress inline js.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_advagg_modify_js_pre_render_alter(&$children, &$elements) {
|
||||||
|
// Get variables.
|
||||||
|
$aggregate_settings['variables']['advagg_js_compressor'] = variable_get('advagg_js_compress_inline', ADVAGG_JS_COMPRESS_INLINE);
|
||||||
|
|
||||||
|
// Do nothing if the compressor is disabled.
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_js_compressor'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing if the page is not cacheable and inline compress if not
|
||||||
|
// cacheable is not checked.
|
||||||
|
if (!variable_get('advagg_js_compress_inline_if_not_cacheable', ADVAGG_JS_COMPRESS_INLINE_IF_NOT_CACHEABLE) && !drupal_page_is_cacheable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compress any inline JS.
|
||||||
|
module_load_include('inc', 'advagg_js_compress', 'advagg_js_compress.advagg');
|
||||||
|
foreach ($children as &$values) {
|
||||||
|
// Compress onload.
|
||||||
|
if (!empty($values['#attributes']['onload'])) {
|
||||||
|
$contents = $values['#attributes']['onload'];
|
||||||
|
$filename = drupal_hash_base64($contents);
|
||||||
|
advagg_js_compress_prep($contents, $filename, $aggregate_settings, FALSE);
|
||||||
|
$values['#attributes']['onload'] = $contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compress onerror.
|
||||||
|
if (!empty($values['#attributes']['onerror'])) {
|
||||||
|
$contents = $values['#attributes']['onerror'];
|
||||||
|
$filename = drupal_hash_base64($contents);
|
||||||
|
advagg_js_compress_prep($contents, $filename, $aggregate_settings, FALSE);
|
||||||
|
$values['#attributes']['onerror'] = $contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compress inline.
|
||||||
|
if (!empty($values['#value'])) {
|
||||||
|
$contents = $values['#value'];
|
||||||
|
$filename = drupal_hash_base64($contents);
|
||||||
|
advagg_js_compress_prep($contents, $filename, $aggregate_settings, FALSE);
|
||||||
|
$values['#value'] = $contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup 3rd_party_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_libraries_info().
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_libraries_info() {
|
||||||
|
$libraries['JShrink'] = array(
|
||||||
|
'name' => 'JShrink',
|
||||||
|
'vendor url' => 'https://github.com/tedious/JShrink',
|
||||||
|
'download url' => 'https://github.com/tedious/JShrink/archive/master.zip',
|
||||||
|
'local version' => '1.2.0',
|
||||||
|
'version' => '1.2.0',
|
||||||
|
'files' => array(
|
||||||
|
'php' => array(
|
||||||
|
'src/JShrink/Minifier.php',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$libraries['jsqueeze'] = array(
|
||||||
|
'name' => 'JSqueeze',
|
||||||
|
'vendor url' => 'https://github.com/tchwork/jsqueeze',
|
||||||
|
'download url' => 'https://github.com/tchwork/jsqueeze/archive/master.zip',
|
||||||
|
'local version' => '2.0.5',
|
||||||
|
'version' => '2.0.5',
|
||||||
|
'files' => array(
|
||||||
|
'php' => array(
|
||||||
|
'src/JSqueeze.php',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$libraries['jsminplus'] = array(
|
||||||
|
'vendor url' => 'https://github.com/JSMinPlus/JSMinPlus',
|
||||||
|
'download url' => 'https://github.com/JSMinPlus/JSMinPlus/archive/master.zip',
|
||||||
|
'name' => 'JSMinPlus',
|
||||||
|
'local version' => '1.4',
|
||||||
|
'version arguments' => array(
|
||||||
|
'file' => 'jsminplus.php',
|
||||||
|
'pattern' => '/JSMinPlus\s+version\s+([0-9a-zA-Z\.-]+)/',
|
||||||
|
'lines' => 10,
|
||||||
|
'cols' => 40,
|
||||||
|
),
|
||||||
|
'files' => array(
|
||||||
|
'php' => array(
|
||||||
|
'jsminplus.php',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$libraries['jspacker'] = array(
|
||||||
|
'vendor url' => 'https://github.com/tholu/php-packer',
|
||||||
|
'download url' => 'https://github.com/tholu/php-packer/archive/master.zip',
|
||||||
|
'name' => 'JSPacker',
|
||||||
|
'local version' => '1.1',
|
||||||
|
'version arguments' => array(
|
||||||
|
'file' => 'src/Packer.php',
|
||||||
|
'pattern' => '/\.\s+version\s+([0-9a-zA-Z\.-]+)/',
|
||||||
|
'lines' => 4,
|
||||||
|
'cols' => 40,
|
||||||
|
),
|
||||||
|
'files' => array(
|
||||||
|
'php' => array(
|
||||||
|
'src/Packer.php',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup 3rd_party_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test a file, making sure it is compressible.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* Path and filename of the js file to test.
|
||||||
|
* @param array $compressors
|
||||||
|
* List of compressors to test.
|
||||||
|
* @param string $cache_id
|
||||||
|
* The cache ID for this file.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array showing the results of the compression tests.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_test_file($filename, array $compressors, $cache_id) {
|
||||||
|
$contents = (string) @advagg_file_get_contents($filename);
|
||||||
|
// Get the JS string length before the compression operation.
|
||||||
|
$contents_before = $contents;
|
||||||
|
$before = strlen($contents);
|
||||||
|
|
||||||
|
module_load_include('inc', 'advagg_js_compress', 'advagg_js_compress.advagg');
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
foreach ($compressors as $key => $name) {
|
||||||
|
$contents = $contents_before;
|
||||||
|
$aggregate_settings['variables']['advagg_js_compressor'] = $key;
|
||||||
|
|
||||||
|
// Compress it.
|
||||||
|
$no_errors = advagg_js_compress_prep($contents, $filename, $aggregate_settings, FALSE, FALSE, FALSE, TRUE);
|
||||||
|
$after = strlen($contents);
|
||||||
|
|
||||||
|
$ratio = 0;
|
||||||
|
if ($before != 0) {
|
||||||
|
$ratio = ($before - $after) / $before;
|
||||||
|
}
|
||||||
|
// Set to "-1" if the compressor threw an error.
|
||||||
|
if ($no_errors === FALSE) {
|
||||||
|
$results[$key] = array(
|
||||||
|
'code' => -1,
|
||||||
|
'ratio' => round($ratio, 5),
|
||||||
|
'name' => $name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Set to "-2" if compression ratio sucks (it's already compressed).
|
||||||
|
elseif ($ratio < 0.001) {
|
||||||
|
$results[$key] = array(
|
||||||
|
'code' => -2,
|
||||||
|
'ratio' => round($ratio, 5),
|
||||||
|
'name' => $name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Set to "-3" if the compression ratio is way too good (bad js output).
|
||||||
|
elseif ($ratio > 0.999) {
|
||||||
|
$results[$key] = array(
|
||||||
|
'code' => -3,
|
||||||
|
'ratio' => round($ratio, 5),
|
||||||
|
'name' => $name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Set to "1". Everything worked, mark this file as compressible.
|
||||||
|
else {
|
||||||
|
$results[$key] = array(
|
||||||
|
'code' => 1,
|
||||||
|
'ratio' => round($ratio, 5),
|
||||||
|
'name' => $name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache = cache_get($cache_id, 'cache_advagg_info');
|
||||||
|
// Merge in old cached data.
|
||||||
|
if (!empty($cache->data)) {
|
||||||
|
// Do not merge in -1 code.
|
||||||
|
foreach ($cache->data as $key => $value) {
|
||||||
|
if ($value['code'] == -1) {
|
||||||
|
unset($cache->data[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$results += $cache->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CACHE_PERMANENT isn't good here. Use 2 weeks from now + 0-45 days.
|
||||||
|
// The random 0 to 45 day addition is to prevent a cache stampeed.
|
||||||
|
cache_set($cache_id, $results, 'cache_advagg_info', round(REQUEST_TIME + 1209600 + mt_rand(0, 3888000), -3));
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the js compress configuration.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array($options, $description, $compressors, $functions).
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_configuration() {
|
||||||
|
// Set the defaults.
|
||||||
|
$description = '';
|
||||||
|
$options = array(
|
||||||
|
0 => t('Disabled'),
|
||||||
|
1 => t('JSMin+ ~1300ms'),
|
||||||
|
// 2 => t('Packer ~500ms'),
|
||||||
|
// 3 is JSMin c extension.
|
||||||
|
// 4 is JShrink.
|
||||||
|
// 5 is JSqueeze.
|
||||||
|
);
|
||||||
|
if (function_exists('jsmin')) {
|
||||||
|
$options[3] = t('JSMin ~2ms');
|
||||||
|
$description .= t('JSMin is the very fast C complied version. Recommend using it.');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!defined('PHP_VERSION_ID') || constant('PHP_VERSION_ID') < 50310) {
|
||||||
|
$link = 'http://www.ypass.net/software/php_jsmin/';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$link = 'https://github.com/sqmk/pecl-jsmin/';
|
||||||
|
}
|
||||||
|
$description .= t('You can use the much faster C version of JSMin (~2ms) by installing the <a href="@php_jsmin">JSMin PHP Extension</a> on this server.', array('@php_jsmin' => $link));
|
||||||
|
}
|
||||||
|
// Add in JShrink and JSqueeze if using php 5.3 or higher.
|
||||||
|
if (defined('PHP_VERSION_ID') && constant('PHP_VERSION_ID') >= 50300) {
|
||||||
|
$options += array(
|
||||||
|
4 => t('JShrink ~1000ms'),
|
||||||
|
5 => t('JSqueeze ~600ms'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$compressors = array(
|
||||||
|
1 => 'jsminplus',
|
||||||
|
2 => 'packer',
|
||||||
|
);
|
||||||
|
if (function_exists('jsmin')) {
|
||||||
|
$compressors[3] = 'jsmin';
|
||||||
|
}
|
||||||
|
if (defined('PHP_VERSION_ID') && constant('PHP_VERSION_ID') >= 50300) {
|
||||||
|
$compressors += array(
|
||||||
|
4 => 'jshrink',
|
||||||
|
5 => 'jsqueeze',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$functions = array(
|
||||||
|
1 => 'advagg_js_compress_jsminplus',
|
||||||
|
2 => 'advagg_js_compress_jspacker',
|
||||||
|
3 => 'advagg_js_compress_jsmin',
|
||||||
|
4 => 'advagg_js_compress_jshrink',
|
||||||
|
5 => 'advagg_js_compress_jsqueeze',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Allow for other modules to alter this list.
|
||||||
|
$options_desc = array($options, $description);
|
||||||
|
// Call hook_advagg_js_compress_configuration_alter().
|
||||||
|
drupal_alter('advagg_js_compress_configuration', $options_desc, $compressors, $functions);
|
||||||
|
list($options, $description) = $options_desc;
|
||||||
|
|
||||||
|
return array($options, $description, $compressors, $functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all js files and js files that are not compressed.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array($list, $redo_list).
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_all_js_files_list() {
|
||||||
|
// Get all files stored in the database.
|
||||||
|
$result = db_select('advagg_files', 'af')
|
||||||
|
->fields('af')
|
||||||
|
->condition('filetype', 'js')
|
||||||
|
->orderBy('filename', 'ASC')
|
||||||
|
->execute();
|
||||||
|
if (empty($result)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
module_load_include('inc', 'advagg_js_compress', 'advagg_js_compress.advagg');
|
||||||
|
$compressor_list = advagg_js_compress_get_enabled_compressors(array(), -1);
|
||||||
|
$compressor_list_count = count($compressor_list);
|
||||||
|
|
||||||
|
// Check if files have been compressed.
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
$redo_list = array();
|
||||||
|
$failed_redo_list = array();
|
||||||
|
$list = array();
|
||||||
|
$cache_ids = array();
|
||||||
|
foreach ($result as $row) {
|
||||||
|
$row = (array) $row;
|
||||||
|
// Check cache for jsmin info.
|
||||||
|
$info = advagg_get_info_on_file($row['filename']);
|
||||||
|
if ($info['filesize'] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$list[$row['filename']] = $info;
|
||||||
|
|
||||||
|
// Get the cache id as well.
|
||||||
|
$cache_id = 'advagg:js_compress:info:' . $info['filename_hash'];
|
||||||
|
$cache_id .= !empty($info['content_hash']) ? ':' . $info['content_hash'] : '';
|
||||||
|
$cache_ids[$cache_id] = $row['filename'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for soon to expire cache ids.
|
||||||
|
$values = array_keys($cache_ids);
|
||||||
|
$cache_hits = cache_get_multiple($values, 'cache_advagg_info');
|
||||||
|
$ttl = variable_get('advagg_js_compress_refresh_before_cache_ttl', ADVAGG_JS_COMPRESS_REFRESH_BEFORE_CACHE_TTL);
|
||||||
|
foreach ($cache_hits as $cache) {
|
||||||
|
if (!empty($cache->expire) && $cache->expire - $ttl < REQUEST_TIME) {
|
||||||
|
$info = $list[$cache_ids[$cache->cid]];
|
||||||
|
$redo_list[$info['data']] = $info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($list as $info) {
|
||||||
|
// No jsmin info or incomplete data => rerun compression tests.
|
||||||
|
if (empty($info['advagg_js_compress']) || count($info['advagg_js_compress']) !== $compressor_list_count) {
|
||||||
|
$redo_list[$info['data']] = $info;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$empty_ratio_count = 0;
|
||||||
|
$bad_compression_count = 0;
|
||||||
|
foreach ($info['advagg_js_compress'] as $values) {
|
||||||
|
if (empty($values['ratio'])) {
|
||||||
|
if ($values['code'] != -1) {
|
||||||
|
$empty_ratio_count++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$bad_compression_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// More than one compressor has an empty ratio.
|
||||||
|
if ($empty_ratio_count > 1) {
|
||||||
|
$failed_redo_list[$info['data']] = $info;
|
||||||
|
}
|
||||||
|
// All failed; try again.
|
||||||
|
if ($bad_compression_count == count($info['advagg_js_compress'])) {
|
||||||
|
$failed_redo_list[$info['data']] = $info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$redo_list = array_merge($redo_list, $failed_redo_list);
|
||||||
|
$reversed_needle = strrev('.min.js');
|
||||||
|
$advagg_js_compressor_file_settings = variable_get('advagg_js_compressor_file_settings', []);
|
||||||
|
foreach ($redo_list as $key => $info) {
|
||||||
|
// Filter out file if the compressor is disabled.
|
||||||
|
$filename = str_replace(['/', '.'], ['__', '--'], $key);
|
||||||
|
if (isset($advagg_js_compressor_file_settings[$filename]) && $advagg_js_compressor_file_settings[$filename] == 0) {
|
||||||
|
unset($redo_list[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out .min.js if they have already been ran.
|
||||||
|
if (stripos(strrev($info['data']), $reversed_needle) === 0
|
||||||
|
&& !empty($info['advagg_js_compress'][2]['ratio'])
|
||||||
|
) {
|
||||||
|
unset($redo_list[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out file if it is empty.
|
||||||
|
$data = file_get_contents($info['data']);
|
||||||
|
if (empty($data)) {
|
||||||
|
unset($redo_list[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out file if it only contains a small amount of whitespace.
|
||||||
|
$count_ws = strlen($data);
|
||||||
|
$count = strlen(preg_replace('/\s/', '', $data));
|
||||||
|
if ($count / $count_ws > 0.97) {
|
||||||
|
unset($redo_list[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffle($redo_list);
|
||||||
|
return array($list, $redo_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all js files and js files that are not compressed.
|
||||||
|
*
|
||||||
|
* @param array $redo_list
|
||||||
|
* JS files that need to be compressed.
|
||||||
|
* @param int $max_time
|
||||||
|
* Max amount of time to spend on compressing.
|
||||||
|
* @param bool $drush
|
||||||
|
* Set to TRUE to output drush info when running.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array($list, $redo_list).
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_redo_files(array $redo_list, $max_time = 30, $drush = FALSE) {
|
||||||
|
// Get the compressor list and start the clock.
|
||||||
|
module_load_include('inc', 'advagg_js_compress', 'advagg_js_compress.advagg');
|
||||||
|
$compressor_list = advagg_js_compress_get_enabled_compressors(array(), -1);
|
||||||
|
shuffle($redo_list);
|
||||||
|
$time_start = microtime(TRUE);
|
||||||
|
if ($drush && (!is_callable('drush_log') || !is_callable('dt'))) {
|
||||||
|
$drush = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change settings for testing.
|
||||||
|
if (isset($GLOBALS['conf']['advagg_js_compress_force_run'])) {
|
||||||
|
$advagg_js_compress_force_run = $GLOBALS['conf']['advagg_js_compress_force_run'];
|
||||||
|
}
|
||||||
|
if (isset($GLOBALS['conf']['advagg_js_compress_add_license'])) {
|
||||||
|
$advagg_js_compress_add_license = $GLOBALS['conf']['advagg_js_compress_add_license'];
|
||||||
|
}
|
||||||
|
if (isset($GLOBALS['conf']['httprl_background_callback'])) {
|
||||||
|
$httprl_background_callback = $GLOBALS['conf']['httprl_background_callback'];
|
||||||
|
}
|
||||||
|
$GLOBALS['conf']['advagg_js_compress_force_run'] = TRUE;
|
||||||
|
$GLOBALS['conf']['advagg_js_compress_add_license'] = 0;
|
||||||
|
$GLOBALS['conf']['httprl_background_callback'] = FALSE;
|
||||||
|
|
||||||
|
$counter = array();
|
||||||
|
foreach ($redo_list as $key => $values) {
|
||||||
|
// Test the files for up to 30 seconds.
|
||||||
|
$filenames_info = array();
|
||||||
|
$filenames_info[$values['data']] = $values;
|
||||||
|
$compressors = $compressor_list;
|
||||||
|
if (isset($values['compressors'])) {
|
||||||
|
$compressors = $values['compressors'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($drush) {
|
||||||
|
drush_log(dt('Compressing @data.', array(
|
||||||
|
'@data' => $values['data'],
|
||||||
|
)), 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove jsqueeze if compression failed.
|
||||||
|
if (!empty($values['advagg_js_compress'])) {
|
||||||
|
$neg_one_counter = 0;
|
||||||
|
foreach ($values['advagg_js_compress'] as $compressor_data) {
|
||||||
|
if ($compressor_data['code'] == -1) {
|
||||||
|
$neg_one_counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count($values['advagg_js_compress']) === $neg_one_counter) {
|
||||||
|
$compressor_key = array_search('jsqueeze', $compressors);
|
||||||
|
if ($compressor_key !== FALSE) {
|
||||||
|
unset($compressors[$compressor_key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prime cache.
|
||||||
|
advagg_js_compress_run_mutiple_tests($filenames_info, $compressors);
|
||||||
|
// Add to cache.
|
||||||
|
advagg_get_info_on_file($values['data'], TRUE, TRUE);
|
||||||
|
|
||||||
|
$counter[$key] = $values;
|
||||||
|
|
||||||
|
// Stop after 30 seconds of processing.
|
||||||
|
$time_end = microtime(TRUE);
|
||||||
|
$time = $time_end - $time_start;
|
||||||
|
if (!empty($max_time) && $time > $max_time) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put them back to normal.
|
||||||
|
if (isset($advagg_js_compress_force_run)) {
|
||||||
|
$GLOBALS['conf']['advagg_js_compress_force_run'] = $advagg_js_compress_force_run;
|
||||||
|
}
|
||||||
|
if (isset($advagg_js_compress_add_license)) {
|
||||||
|
$GLOBALS['conf']['advagg_js_compress_add_license'] = $advagg_js_compress_add_license;
|
||||||
|
}
|
||||||
|
if (isset($httprl_background_callback)) {
|
||||||
|
$GLOBALS['conf']['httprl_background_callback'] = $httprl_background_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The batch callback.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_batch_callback() {
|
||||||
|
$batch = array(
|
||||||
|
'operations' => array(),
|
||||||
|
'finished' => 'advagg_js_compress_batch_done',
|
||||||
|
'title' => t('Batch JS Minification'),
|
||||||
|
'init_message' => t('Starting'),
|
||||||
|
'progress_message' => t('Processed @current out of @total.'),
|
||||||
|
'error_message' => t('JS minification has encountered an error.'),
|
||||||
|
);
|
||||||
|
|
||||||
|
list($list, $redo_list) = advagg_js_compress_all_js_files_list();
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
if (empty($redo_list)) {
|
||||||
|
$redo_list = $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($redo_list as $redo) {
|
||||||
|
$batch['operations'][] = array('advagg_js_compress_batch_process', array($redo));
|
||||||
|
}
|
||||||
|
batch_set($batch);
|
||||||
|
// The path to redirect to when done.
|
||||||
|
batch_process($config_path . '/advagg/js-compress');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The batch processor.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_batch_process($redo, &$context) {
|
||||||
|
// Give it up to 3 minutes.
|
||||||
|
$max_time = ini_get('max_execution_time');
|
||||||
|
if ($max_time != 0 && $max_time < 180) {
|
||||||
|
set_time_limit(180);
|
||||||
|
}
|
||||||
|
ignore_user_abort(TRUE);
|
||||||
|
|
||||||
|
// Display a progress message...
|
||||||
|
$context['message'] = t("Now processing @filename...", array('@filename' => $redo['data']));
|
||||||
|
// Do heavy lifting here...
|
||||||
|
advagg_js_compress_redo_files(array($redo));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The batch finish handler.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_batch_done($success, $results, $operations) {
|
||||||
|
if ($success) {
|
||||||
|
drupal_set_message(t('Done!'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$error_operation = reset($operations);
|
||||||
|
$message = t('An error occurred while processing %error_operation with arguments: @arguments', array(
|
||||||
|
'%error_operation' => $error_operation[0],
|
||||||
|
'@arguments' => print_r($error_operation[1], TRUE),
|
||||||
|
));
|
||||||
|
drupal_set_message($message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the cache_advagg_info cache bin; make sure it works.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Single section of the requirements array.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_check_cache_bin() {
|
||||||
|
$t = get_t();
|
||||||
|
|
||||||
|
$requirements = array();
|
||||||
|
// Make sure cache table is working.
|
||||||
|
$cid = 'advagg_js_compres:install:cache_test';
|
||||||
|
$bin = 'cache_advagg_info';
|
||||||
|
cache_set($cid, TRUE, $bin);
|
||||||
|
$cache = cache_get($cid, $bin);
|
||||||
|
$working = FALSE;
|
||||||
|
if (!empty($cache->data)) {
|
||||||
|
cache_clear_all($cid, $bin);
|
||||||
|
$cache = cache_get($cid, $bin);
|
||||||
|
if (empty($cache->data)) {
|
||||||
|
$working = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($working)) {
|
||||||
|
$broken_name = get_class(_cache_get_object($bin));
|
||||||
|
|
||||||
|
if ($broken_name === 'DrupalFakeCache') {
|
||||||
|
$working_name = get_class(_cache_get_object('cache_form'));
|
||||||
|
if ($working_name === 'DrupalFakeCache') {
|
||||||
|
$working_name = 'DrupalDatabaseCache';
|
||||||
|
}
|
||||||
|
$extra_description = t('Please add this to the bottom of your settings.php file. <p><code>@string</code></p>', array('@string' => "\$conf['cache_class_cache_advagg_info'] = '{$working_name}';"));
|
||||||
|
}
|
||||||
|
$requirements['advagg_js_compres_cache_bin'] = array(
|
||||||
|
'title' => $t('AdvAgg JS Compressor - The %bin cache table does not work', array('%bin' => $bin)),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('The %class_name cache bin appears to be broken.', array('%class_name' => $broken_name)),
|
||||||
|
'description' => $t('You need to adjust your settings.php file so that the %bin bin is working correctly.', array(
|
||||||
|
'%bin' => $bin,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
$requirements['advagg_js_compres_cache_bin']['description'] .= " {$extra_description}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $requirements;
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced CSS/JS aggregation js compression php 5.3+ functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use JShrink\Minifier;
|
||||||
|
use Patchwork\JSqueeze;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a JS string using jshrink.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* Javascript string.
|
||||||
|
* @param bool $log_errors
|
||||||
|
* FALSE to disable logging to watchdog on failure.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_jshrink(&$contents, $log_errors = TRUE) {
|
||||||
|
$no_errors = TRUE;
|
||||||
|
$contents_before = $contents;
|
||||||
|
|
||||||
|
// Set max nesting level.
|
||||||
|
if (!class_exists('JShrink\Minifier')) {
|
||||||
|
$nesting_level = ini_get('xdebug.max_nesting_level');
|
||||||
|
if (!empty($nesting_level) && $nesting_level < 200) {
|
||||||
|
ini_set('xdebug.max_nesting_level', 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try libraries for JShrink.
|
||||||
|
if (is_callable('libraries_load')) {
|
||||||
|
libraries_load('JShrink');
|
||||||
|
}
|
||||||
|
// Only include jshrink.inc if the JShrink\Minifier class doesn't exist.
|
||||||
|
if (!class_exists('JShrink\Minifier')) {
|
||||||
|
include drupal_get_path('module', 'advagg_js_compress') . '/jshrink.inc';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
try {
|
||||||
|
// JShrink the contents of the aggregated file.
|
||||||
|
$contents = Minifier::minify($contents, array('flaggedComments' => FALSE));
|
||||||
|
|
||||||
|
// Capture any output from JShrink.
|
||||||
|
$error = trim(ob_get_contents());
|
||||||
|
if (!empty($error)) {
|
||||||
|
$no_errors = FALSE;
|
||||||
|
throw new Exception($error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
$no_errors = FALSE;
|
||||||
|
// Log the JShrink exception and rollback to uncompressed content.
|
||||||
|
if ($log_errors) {
|
||||||
|
watchdog('advagg_js_compress', '@message <pre> @contents </pre>', array(
|
||||||
|
'@message' => $e->getMessage(),
|
||||||
|
'@contents' => $contents_before,
|
||||||
|
), WATCHDOG_WARNING);
|
||||||
|
}
|
||||||
|
$contents = $contents_before;
|
||||||
|
}
|
||||||
|
ob_end_clean();
|
||||||
|
return $no_errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a JS string using jsqueeze.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* Javascript string.
|
||||||
|
* @param bool $log_errors
|
||||||
|
* FALSE to disable logging to watchdog on failure.
|
||||||
|
* @param array $aggregate_settings
|
||||||
|
* The aggregate_settings array.
|
||||||
|
*/
|
||||||
|
function advagg_js_compress_jsqueeze(&$contents, $log_errors = TRUE, array $aggregate_settings = array()) {
|
||||||
|
$no_errors = TRUE;
|
||||||
|
$contents_before = $contents;
|
||||||
|
|
||||||
|
// Set max nesting level.
|
||||||
|
if (!class_exists('Patchwork\JSqueeze')) {
|
||||||
|
$nesting_level = ini_get('xdebug.max_nesting_level');
|
||||||
|
if (!empty($nesting_level) && $nesting_level < 200) {
|
||||||
|
ini_set('xdebug.max_nesting_level', 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try libraries for jsqueeze.
|
||||||
|
if (is_callable('libraries_load')) {
|
||||||
|
libraries_load('jsqueeze');
|
||||||
|
}
|
||||||
|
if (!class_exists('Patchwork\JSqueeze')) {
|
||||||
|
// Only include jshrink.inc if the Patchwork\JSqueeze class doesn't exist.
|
||||||
|
include drupal_get_path('module', 'advagg_js_compress') . '/jsqueeze.inc';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
try {
|
||||||
|
$add_license_setting
|
||||||
|
= isset($aggregate_settings['variables']['advagg_js_compress_add_license']) ?
|
||||||
|
$aggregate_settings['variables']['advagg_js_compress_add_license'] :
|
||||||
|
variable_get('advagg_js_compress_add_license', ADVAGG_JS_COMPRESS_ADD_LICENSE);
|
||||||
|
|
||||||
|
$keep_important_comments = FALSE;
|
||||||
|
if ($add_license_setting == 2 || $add_license_setting == 3) {
|
||||||
|
$keep_important_comments = TRUE;
|
||||||
|
}
|
||||||
|
// Minify the contents of the aggregated file.
|
||||||
|
$jz = new JSqueeze();
|
||||||
|
$contents = $jz->squeeze(
|
||||||
|
$contents,
|
||||||
|
TRUE,
|
||||||
|
$keep_important_comments,
|
||||||
|
FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
// Capture any output from JSqueeze.
|
||||||
|
$error = trim(ob_get_contents());
|
||||||
|
if (!empty($error)) {
|
||||||
|
$no_errors = FALSE;
|
||||||
|
throw new Exception($error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
$no_errors = FALSE;
|
||||||
|
// Log the JSqueeze exception and rollback to uncompressed content.
|
||||||
|
if ($log_errors) {
|
||||||
|
watchdog('advagg_js_compress', '@message <pre> @contents </pre>', array(
|
||||||
|
'@message' => $e->getMessage(),
|
||||||
|
'@contents' => $contents_before,
|
||||||
|
), WATCHDOG_WARNING);
|
||||||
|
}
|
||||||
|
$contents = $contents_before;
|
||||||
|
}
|
||||||
|
ob_end_clean();
|
||||||
|
return $no_errors;
|
||||||
|
}
|
@ -0,0 +1,638 @@
|
|||||||
|
<?php
|
||||||
|
// @codingStandardsIgnoreFile
|
||||||
|
// @ignore comment_docblock_file:file
|
||||||
|
// @ignore style_curly_braces:file
|
||||||
|
// @ignore style_string_spacing:file
|
||||||
|
// @ignore style_else_spacing:file
|
||||||
|
// @ignore comment_comment_docblock_missing:file
|
||||||
|
// @ignore comment_comment_eg:file
|
||||||
|
// @ignore production_code:file
|
||||||
|
// @ignore druplart_unary:file
|
||||||
|
// @ignore style_uppercase_constants:file
|
||||||
|
// @ignore comment_comment_space:file
|
||||||
|
// @ignore druplart_conditional_assignment:file
|
||||||
|
// @ignore style_paren_spacing:file
|
||||||
|
// @ignore style_no_tabs:file
|
||||||
|
// @ignore comment_docblock_comment:file
|
||||||
|
// @ignore style_control_spacing:file
|
||||||
|
// @ignore comment_comment_indent:file
|
||||||
|
// @ignore :file
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the JShrink package.
|
||||||
|
*
|
||||||
|
* (c) Robert Hafner <tedivm@tedivm.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2009, Robert Hafner
|
||||||
|
All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the Stash Project nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL Robert Hafner BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JShrink
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @package JShrink
|
||||||
|
* @author Robert Hafner <tedivm@tedivm.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace JShrink;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minifier
|
||||||
|
*
|
||||||
|
* Usage - Minifier::minify($js);
|
||||||
|
* Usage - Minifier::minify($js, $options);
|
||||||
|
* Usage - Minifier::minify($js, array('flaggedComments' => false));
|
||||||
|
*
|
||||||
|
* @package JShrink
|
||||||
|
* @author Robert Hafner <tedivm@tedivm.com>
|
||||||
|
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||||
|
*/
|
||||||
|
class Minifier
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The input javascript to be minified.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location of the character (in the input string) that is next to be
|
||||||
|
* processed.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $index = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The first of the characters currently being looked at.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $a = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The next character being looked at (after a);
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $b = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This character is only active when certain look ahead actions take place.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $c;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the options for the current minification process.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These characters are used to define strings.
|
||||||
|
*/
|
||||||
|
protected $stringDelimiters = array("'", '"', '`');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the default options for minification. This array is merged with
|
||||||
|
* the one passed in by the user to create the request specific set of
|
||||||
|
* options (stored in the $options attribute).
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $defaultOptions = array('flaggedComments' => true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains lock ids which are used to replace certain code patterns and
|
||||||
|
* prevent them from being minified
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $locks = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a string containing javascript and removes unneeded characters in
|
||||||
|
* order to shrink the code without altering it's functionality.
|
||||||
|
*
|
||||||
|
* @param string $js The raw javascript to be minified
|
||||||
|
* @param array $options Various runtime options in an associative array
|
||||||
|
* @throws \Exception
|
||||||
|
* @return bool|string
|
||||||
|
*/
|
||||||
|
public static function minify($js, $options = array())
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
$jshrink = new Minifier();
|
||||||
|
$js = $jshrink->lock($js);
|
||||||
|
$jshrink->minifyDirectToOutput($js, $options);
|
||||||
|
|
||||||
|
// Sometimes there's a leading new line, so we trim that out here.
|
||||||
|
$js = ltrim(ob_get_clean());
|
||||||
|
$js = $jshrink->unlock($js);
|
||||||
|
unset($jshrink);
|
||||||
|
|
||||||
|
return $js;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
if (isset($jshrink)) {
|
||||||
|
// Since the breakdownScript function probably wasn't finished
|
||||||
|
// we clean it out before discarding it.
|
||||||
|
$jshrink->clean();
|
||||||
|
unset($jshrink);
|
||||||
|
}
|
||||||
|
|
||||||
|
// without this call things get weird, with partially outputted js.
|
||||||
|
ob_end_clean();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a javascript string and outputs only the required characters,
|
||||||
|
* stripping out all unneeded characters.
|
||||||
|
*
|
||||||
|
* @param string $js The raw javascript to be minified
|
||||||
|
* @param array $options Various runtime options in an associative array
|
||||||
|
*/
|
||||||
|
protected function minifyDirectToOutput($js, $options)
|
||||||
|
{
|
||||||
|
$this->initialize($js, $options);
|
||||||
|
$this->loop();
|
||||||
|
$this->clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes internal variables, normalizes new lines,
|
||||||
|
*
|
||||||
|
* @param string $js The raw javascript to be minified
|
||||||
|
* @param array $options Various runtime options in an associative array
|
||||||
|
*/
|
||||||
|
protected function initialize($js, $options)
|
||||||
|
{
|
||||||
|
$this->options = array_merge(static::$defaultOptions, $options);
|
||||||
|
$js = str_replace("\r\n", "\n", $js);
|
||||||
|
$js = str_replace('/**/', '', $js);
|
||||||
|
$this->input = str_replace("\r", "\n", $js);
|
||||||
|
|
||||||
|
// We add a newline to the end of the script to make it easier to deal
|
||||||
|
// with comments at the bottom of the script- this prevents the unclosed
|
||||||
|
// comment error that can otherwise occur.
|
||||||
|
$this->input .= PHP_EOL;
|
||||||
|
|
||||||
|
// Populate "a" with a new line, "b" with the first character, before
|
||||||
|
// entering the loop
|
||||||
|
$this->a = "\n";
|
||||||
|
$this->b = $this->getReal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The primary action occurs here. This function loops through the input string,
|
||||||
|
* outputting anything that's relevant and discarding anything that is not.
|
||||||
|
*/
|
||||||
|
protected function loop()
|
||||||
|
{
|
||||||
|
while ($this->a !== false && !is_null($this->a) && $this->a !== '') {
|
||||||
|
switch ($this->a) {
|
||||||
|
// new lines
|
||||||
|
case "\n":
|
||||||
|
// if the next line is something that can't stand alone preserve the newline
|
||||||
|
if ($this->b && strpos('(-+[@', $this->b) !== false) {
|
||||||
|
echo $this->a;
|
||||||
|
$this->saveString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if B is a space we skip the rest of the switch block and go down to the
|
||||||
|
// string/regex check below, resetting $this->b with getReal
|
||||||
|
if ($this->b === ' ') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise we treat the newline like a space
|
||||||
|
|
||||||
|
// no break
|
||||||
|
case ' ':
|
||||||
|
if (static::isAlphaNumeric($this->b)) {
|
||||||
|
echo $this->a;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->saveString();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
switch ($this->b) {
|
||||||
|
case "\n":
|
||||||
|
if (strpos('}])+-"\'', $this->a) !== false) {
|
||||||
|
echo $this->a;
|
||||||
|
$this->saveString();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (static::isAlphaNumeric($this->a)) {
|
||||||
|
echo $this->a;
|
||||||
|
$this->saveString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ' ':
|
||||||
|
if (!static::isAlphaNumeric($this->a)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no break
|
||||||
|
default:
|
||||||
|
// check for some regex that breaks stuff
|
||||||
|
if ($this->a === '/' && ($this->b === '\'' || $this->b === '"')) {
|
||||||
|
$this->saveRegex();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $this->a;
|
||||||
|
$this->saveString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do reg check of doom
|
||||||
|
$this->b = $this->getReal();
|
||||||
|
|
||||||
|
if (($this->b == '/' && strpos('(,=:[!&|?', $this->a) !== false)) {
|
||||||
|
$this->saveRegex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets attributes that do not need to be stored between requests so that
|
||||||
|
* the next request is ready to go. Another reason for this is to make sure
|
||||||
|
* the variables are cleared and are not taking up memory.
|
||||||
|
*/
|
||||||
|
protected function clean()
|
||||||
|
{
|
||||||
|
unset($this->input);
|
||||||
|
$this->index = 0;
|
||||||
|
$this->a = $this->b = '';
|
||||||
|
unset($this->c);
|
||||||
|
unset($this->options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next string for processing based off of the current index.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getChar()
|
||||||
|
{
|
||||||
|
// Check to see if we had anything in the look ahead buffer and use that.
|
||||||
|
if (isset($this->c)) {
|
||||||
|
$char = $this->c;
|
||||||
|
unset($this->c);
|
||||||
|
|
||||||
|
// Otherwise we start pulling from the input.
|
||||||
|
} else {
|
||||||
|
$char = substr($this->input, $this->index, 1);
|
||||||
|
|
||||||
|
// If the next character doesn't exist return false.
|
||||||
|
if (isset($char) && $char === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise increment the pointer and use this char.
|
||||||
|
$this->index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize all whitespace except for the newline character into a
|
||||||
|
// standard space.
|
||||||
|
if ($char !== "\n" && ord($char) < 32) {
|
||||||
|
return ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $char;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function gets the next "real" character. It is essentially a wrapper
|
||||||
|
* around the getChar function that skips comments. This has significant
|
||||||
|
* performance benefits as the skipping is done using native functions (ie,
|
||||||
|
* c code) rather than in script php.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return string Next 'real' character to be processed.
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*/
|
||||||
|
protected function getReal()
|
||||||
|
{
|
||||||
|
$startIndex = $this->index;
|
||||||
|
$char = $this->getChar();
|
||||||
|
|
||||||
|
// Check to see if we're potentially in a comment
|
||||||
|
if ($char !== '/') {
|
||||||
|
return $char;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->c = $this->getChar();
|
||||||
|
|
||||||
|
if ($this->c === '/') {
|
||||||
|
$this->processOneLineComments($startIndex);
|
||||||
|
|
||||||
|
return $this->getReal();
|
||||||
|
} elseif ($this->c === '*') {
|
||||||
|
$this->processMultiLineComments($startIndex);
|
||||||
|
|
||||||
|
return $this->getReal();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $char;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removed one line comments, with the exception of some very specific types of
|
||||||
|
* conditional comments.
|
||||||
|
*
|
||||||
|
* @param int $startIndex The index point where "getReal" function started
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function processOneLineComments($startIndex)
|
||||||
|
{
|
||||||
|
$thirdCommentString = substr($this->input, $this->index, 1);
|
||||||
|
|
||||||
|
// kill rest of line
|
||||||
|
$this->getNext("\n");
|
||||||
|
|
||||||
|
unset($this->c);
|
||||||
|
|
||||||
|
if ($thirdCommentString == '@') {
|
||||||
|
$endPoint = $this->index - $startIndex;
|
||||||
|
$this->c = "\n" . substr($this->input, $startIndex, $endPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skips multiline comments where appropriate, and includes them where needed.
|
||||||
|
* Conditional comments and "license" style blocks are preserved.
|
||||||
|
*
|
||||||
|
* @param int $startIndex The index point where "getReal" function started
|
||||||
|
* @return void
|
||||||
|
* @throws \RuntimeException Unclosed comments will throw an error
|
||||||
|
*/
|
||||||
|
protected function processMultiLineComments($startIndex)
|
||||||
|
{
|
||||||
|
$this->getChar(); // current C
|
||||||
|
$thirdCommentString = $this->getChar();
|
||||||
|
|
||||||
|
// kill everything up to the next */ if it's there
|
||||||
|
if ($this->getNext('*/')) {
|
||||||
|
$this->getChar(); // get *
|
||||||
|
$this->getChar(); // get /
|
||||||
|
$char = $this->getChar(); // get next real character
|
||||||
|
|
||||||
|
// Now we reinsert conditional comments and YUI-style licensing comments
|
||||||
|
if (($this->options['flaggedComments'] && $thirdCommentString === '!')
|
||||||
|
|| ($thirdCommentString === '@')) {
|
||||||
|
|
||||||
|
// If conditional comments or flagged comments are not the first thing in the script
|
||||||
|
// we need to echo a and fill it with a space before moving on.
|
||||||
|
if ($startIndex > 0) {
|
||||||
|
echo $this->a;
|
||||||
|
$this->a = " ";
|
||||||
|
|
||||||
|
// If the comment started on a new line we let it stay on the new line
|
||||||
|
if ($this->input[($startIndex - 1)] === "\n") {
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$endPoint = ($this->index - 1) - $startIndex;
|
||||||
|
echo substr($this->input, $startIndex, $endPoint);
|
||||||
|
|
||||||
|
$this->c = $char;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$char = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($char === false) {
|
||||||
|
throw new \RuntimeException('Unclosed multiline comment at position: ' . ($this->index - 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're here c is part of the comment and therefore tossed
|
||||||
|
$this->c = $char;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes the index ahead to the next instance of the supplied string. If it
|
||||||
|
* is found the first character of the string is returned and the index is set
|
||||||
|
* to it's position.
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return string|false Returns the first character of the string or false.
|
||||||
|
*/
|
||||||
|
protected function getNext($string)
|
||||||
|
{
|
||||||
|
// Find the next occurrence of "string" after the current position.
|
||||||
|
$pos = strpos($this->input, $string, $this->index);
|
||||||
|
|
||||||
|
// If it's not there return false.
|
||||||
|
if ($pos === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust position of index to jump ahead to the asked for string
|
||||||
|
$this->index = $pos;
|
||||||
|
|
||||||
|
// Return the first character of that string.
|
||||||
|
return substr($this->input, $this->index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a javascript string is detected this function crawls for the end of
|
||||||
|
* it and saves the whole string.
|
||||||
|
*
|
||||||
|
* @throws \RuntimeException Unclosed strings will throw an error
|
||||||
|
*/
|
||||||
|
protected function saveString()
|
||||||
|
{
|
||||||
|
$startpos = $this->index;
|
||||||
|
|
||||||
|
// saveString is always called after a gets cleared, so we push b into
|
||||||
|
// that spot.
|
||||||
|
$this->a = $this->b;
|
||||||
|
|
||||||
|
// If this isn't a string we don't need to do anything.
|
||||||
|
if (!in_array($this->a, $this->stringDelimiters)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// String type is the quote used, " or '
|
||||||
|
$stringType = $this->a;
|
||||||
|
|
||||||
|
// Echo out that starting quote
|
||||||
|
echo $this->a;
|
||||||
|
|
||||||
|
// Loop until the string is done
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
// Grab the very next character and load it into a
|
||||||
|
$this->a = $this->getChar();
|
||||||
|
|
||||||
|
switch ($this->a) {
|
||||||
|
|
||||||
|
// If the string opener (single or double quote) is used
|
||||||
|
// output it and break out of the while loop-
|
||||||
|
// The string is finished!
|
||||||
|
case $stringType:
|
||||||
|
break 2;
|
||||||
|
|
||||||
|
// New lines in strings without line delimiters are bad- actual
|
||||||
|
// new lines will be represented by the string \n and not the actual
|
||||||
|
// character, so those will be treated just fine using the switch
|
||||||
|
// block below.
|
||||||
|
case "\n":
|
||||||
|
if ($stringType === '`') {
|
||||||
|
echo $this->a;
|
||||||
|
} else {
|
||||||
|
throw new \RuntimeException('Unclosed string at position: ' . $startpos);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Escaped characters get picked up here. If it's an escaped new line it's not really needed
|
||||||
|
case '\\':
|
||||||
|
|
||||||
|
// a is a slash. We want to keep it, and the next character,
|
||||||
|
// unless it's a new line. New lines as actual strings will be
|
||||||
|
// preserved, but escaped new lines should be reduced.
|
||||||
|
$this->b = $this->getChar();
|
||||||
|
|
||||||
|
// If b is a new line we discard a and b and restart the loop.
|
||||||
|
if ($this->b === "\n") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// echo out the escaped character and restart the loop.
|
||||||
|
echo $this->a . $this->b;
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
// Since we're not dealing with any special cases we simply
|
||||||
|
// output the character and continue our loop.
|
||||||
|
default:
|
||||||
|
echo $this->a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a regular expression is detected this function crawls for the end of
|
||||||
|
* it and saves the whole regex.
|
||||||
|
*
|
||||||
|
* @throws \RuntimeException Unclosed regex will throw an error
|
||||||
|
*/
|
||||||
|
protected function saveRegex()
|
||||||
|
{
|
||||||
|
echo $this->a . $this->b;
|
||||||
|
|
||||||
|
while (($this->a = $this->getChar()) !== false) {
|
||||||
|
if ($this->a === '/') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->a === '\\') {
|
||||||
|
echo $this->a;
|
||||||
|
$this->a = $this->getChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->a === "\n") {
|
||||||
|
throw new \RuntimeException('Unclosed regex pattern at position: ' . $this->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $this->a;
|
||||||
|
}
|
||||||
|
$this->b = $this->getReal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if a character is alphanumeric.
|
||||||
|
*
|
||||||
|
* @param string $char Just one character
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected static function isAlphaNumeric($char)
|
||||||
|
{
|
||||||
|
return preg_match('/^[\w\$\pL]$/', $char) === 1 || $char == '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace patterns in the given string and store the replacement
|
||||||
|
*
|
||||||
|
* @param string $js The string to lock
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function lock($js)
|
||||||
|
{
|
||||||
|
/* lock things like <code>"asd" + ++x;</code> */
|
||||||
|
$lock = '"LOCK---' . crc32(time()) . '"';
|
||||||
|
|
||||||
|
$matches = array();
|
||||||
|
preg_match('/([+-])(\s+)([+-])/S', $js, $matches);
|
||||||
|
if (empty($matches)) {
|
||||||
|
return $js;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->locks[$lock] = $matches[2];
|
||||||
|
|
||||||
|
$js = preg_replace('/([+-])\s+([+-])/S', "$1{$lock}$2", $js);
|
||||||
|
/* -- */
|
||||||
|
|
||||||
|
return $js;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace "locks" with the original characters
|
||||||
|
*
|
||||||
|
* @param string $js The string to unlock
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function unlock($js)
|
||||||
|
{
|
||||||
|
if (empty($this->locks)) {
|
||||||
|
return $js;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->locks as $lock => $replacement) {
|
||||||
|
$js = str_replace($lock, $replacement, $js);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $js;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,793 @@
|
|||||||
|
<?php
|
||||||
|
// @codingStandardsIgnoreFile
|
||||||
|
// @ignore comment_docblock_file:file
|
||||||
|
// @ignore style_curly_braces:file
|
||||||
|
// @ignore style_string_spacing:file
|
||||||
|
// @ignore style_else_spacing:file
|
||||||
|
// @ignore comment_comment_docblock_missing:file
|
||||||
|
// @ignore comment_comment_eg:file
|
||||||
|
// @ignore production_code:file
|
||||||
|
// @ignore druplart_unary:file
|
||||||
|
// @ignore style_uppercase_constants:file
|
||||||
|
// @ignore comment_comment_space:file
|
||||||
|
// @ignore druplart_conditional_assignment:file
|
||||||
|
// @ignore style_paren_spacing:file
|
||||||
|
// @ignore style_no_tabs:file
|
||||||
|
// @ignore :file
|
||||||
|
|
||||||
|
/* 9 April 2008. version 1.1
|
||||||
|
*
|
||||||
|
* This is the php version of the Dean Edwards JavaScript's Packer,
|
||||||
|
* Based on :
|
||||||
|
*
|
||||||
|
* ParseMaster, version 1.0.2 (2005-08-19) Copyright 2005, Dean Edwards
|
||||||
|
* a multi-pattern parser.
|
||||||
|
* KNOWN BUG: erroneous behavior when using escapeChar with a replacement
|
||||||
|
* value that is a function
|
||||||
|
*
|
||||||
|
* packer, version 2.0.2 (2005-08-19) Copyright 2004-2005, Dean Edwards
|
||||||
|
*
|
||||||
|
* License: http://creativecommons.org/licenses/LGPL/2.1/
|
||||||
|
*
|
||||||
|
* Ported to PHP by Nicolas Martin.
|
||||||
|
*
|
||||||
|
* ----------------------------------------------------------------------
|
||||||
|
* changelog:
|
||||||
|
* 1.1 : correct a bug, '\0' packed then unpacked becomes '\'.
|
||||||
|
* ----------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* examples of usage :
|
||||||
|
* $myPacker = new JavaScriptPacker($script, 62, true, false);
|
||||||
|
* $packed = $myPacker->pack();
|
||||||
|
*
|
||||||
|
* or
|
||||||
|
*
|
||||||
|
* $myPacker = new JavaScriptPacker($script, 'Normal', true, false);
|
||||||
|
* $packed = $myPacker->pack();
|
||||||
|
*
|
||||||
|
* or (default values)
|
||||||
|
*
|
||||||
|
* $myPacker = new JavaScriptPacker($script);
|
||||||
|
* $packed = $myPacker->pack();
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* params of the constructor :
|
||||||
|
* $script: the JavaScript to pack, string.
|
||||||
|
* $encoding: level of encoding, int or string :
|
||||||
|
* 0,10,62,95 or 'None', 'Numeric', 'Normal', 'High ASCII'.
|
||||||
|
* default: 62.
|
||||||
|
* $fastDecode: include the fast decoder in the packed result, boolean.
|
||||||
|
* default : true.
|
||||||
|
* $specialChars: if you are flagged your private and local variables
|
||||||
|
* in the script, boolean.
|
||||||
|
* default: false.
|
||||||
|
*
|
||||||
|
* The pack() method return the compressed JavasScript, as a string.
|
||||||
|
*
|
||||||
|
* see http://dean.edwards.name/packer/usage/ for more information.
|
||||||
|
*
|
||||||
|
* Notes :
|
||||||
|
* # need PHP 5 . Tested with PHP 5.1.2, 5.1.3, 5.1.4, 5.2.3
|
||||||
|
*
|
||||||
|
* # The packed result may be different than with the Dean Edwards
|
||||||
|
* version, but with the same length. The reason is that the PHP
|
||||||
|
* function usort to sort array don't necessarily preserve the
|
||||||
|
* original order of two equal member. The Javascript sort function
|
||||||
|
* in fact preserve this order (but that's not require by the
|
||||||
|
* ECMAScript standard). So the encoded keywords order can be
|
||||||
|
* different in the two results.
|
||||||
|
*
|
||||||
|
* # Be careful with the 'High ASCII' Level encoding if you use
|
||||||
|
* UTF-8 in your files...
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class JavaScriptPacker {
|
||||||
|
// constants
|
||||||
|
const IGNORE = '$1';
|
||||||
|
|
||||||
|
// validate parameters
|
||||||
|
private $_script = '';
|
||||||
|
private $_encoding = 62;
|
||||||
|
private $_fastDecode = true;
|
||||||
|
private $_specialChars = false;
|
||||||
|
|
||||||
|
private $LITERAL_ENCODING = array(
|
||||||
|
'None' => 0,
|
||||||
|
'Numeric' => 10,
|
||||||
|
'Normal' => 62,
|
||||||
|
'High ASCII' => 95,
|
||||||
|
);
|
||||||
|
|
||||||
|
public function __construct($_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false) {
|
||||||
|
$this->_script = $_script . "\n";
|
||||||
|
if (array_key_exists($_encoding, $this->LITERAL_ENCODING)) {
|
||||||
|
$_encoding = $this->LITERAL_ENCODING[$_encoding];
|
||||||
|
}
|
||||||
|
$this->_encoding = min((int) $_encoding, 95);
|
||||||
|
$this->_fastDecode = $_fastDecode;
|
||||||
|
$this->_specialChars = $_specialChars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pack() {
|
||||||
|
$this->_addParser('_basicCompression');
|
||||||
|
if ($this->_specialChars) {
|
||||||
|
$this->_addParser('_encodeSpecialChars');
|
||||||
|
}
|
||||||
|
if ($this->_encoding) {
|
||||||
|
$this->_addParser('_encodeKeywords');
|
||||||
|
}
|
||||||
|
|
||||||
|
// go!
|
||||||
|
return $this->_pack($this->_script);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply all parsing routines
|
||||||
|
private function _pack($script) {
|
||||||
|
for ($i = 0; isset($this->_parsers[$i]); $i++) {
|
||||||
|
$script = call_user_func(array(&$this, $this->_parsers[$i]), $script);
|
||||||
|
}
|
||||||
|
return $script;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep a list of parsing functions, they'll be executed all at once
|
||||||
|
private $_parsers = array();
|
||||||
|
private function _addParser($parser) {
|
||||||
|
$this->_parsers[] = $parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero encoding - just removal of white space and comments
|
||||||
|
private function _basicCompression($script) {
|
||||||
|
$parser = new ParseMaster();
|
||||||
|
// make safe
|
||||||
|
$parser->escapeChar = '\\';
|
||||||
|
// protect strings
|
||||||
|
$parser->add('/\'[^\'\\n\\r]*\'/', self::IGNORE);
|
||||||
|
$parser->add('/"[^"\\n\\r]*"/', self::IGNORE);
|
||||||
|
// remove comments
|
||||||
|
$parser->add('/\\/\\/[^\\n\\r]*[\\n\\r]/', ' ');
|
||||||
|
$parser->add('/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' ');
|
||||||
|
// protect regular expressions
|
||||||
|
$parser->add('/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2'); // IGNORE
|
||||||
|
$parser->add('/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', self::IGNORE);
|
||||||
|
// remove: ;;; doSomething();
|
||||||
|
if ($this->_specialChars) {
|
||||||
|
$parser->add('/;;;[^\\n\\r]+[\\n\\r]/');
|
||||||
|
}
|
||||||
|
// remove redundant semi-colons
|
||||||
|
$parser->add('/\\(;;\\)/', self::IGNORE); // protect for (;;) loops
|
||||||
|
$parser->add('/;+\\s*([};])/', '$2');
|
||||||
|
// apply the above
|
||||||
|
$script = $parser->exec($script);
|
||||||
|
|
||||||
|
// remove white-space
|
||||||
|
$parser->add('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3');
|
||||||
|
$parser->add('/([+\\-])\\s+([+\\-])/', '$2 $3');
|
||||||
|
$parser->add('/\\s+/', '');
|
||||||
|
// done
|
||||||
|
return $parser->exec($script);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _encodeSpecialChars($script) {
|
||||||
|
$parser = new ParseMaster();
|
||||||
|
// replace: $name -> n, $$name -> na
|
||||||
|
$parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/',
|
||||||
|
array('fn' => '_replace_name')
|
||||||
|
);
|
||||||
|
// replace: _name -> _0, double-underscore (__name) is ignored
|
||||||
|
$regexp = '/\\b_[A-Za-z\\d]\\w*/';
|
||||||
|
// build the word list
|
||||||
|
$keywords = $this->_analyze($script, $regexp, '_encodePrivate');
|
||||||
|
// quick ref
|
||||||
|
$encoded = $keywords['encoded'];
|
||||||
|
|
||||||
|
$parser->add($regexp,
|
||||||
|
array(
|
||||||
|
'fn' => '_replace_encoded',
|
||||||
|
'data' => $encoded,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return $parser->exec($script);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _encodeKeywords($script) {
|
||||||
|
// escape high-ascii values already in the script (i.e. in strings)
|
||||||
|
if ($this->_encoding > 62) {
|
||||||
|
$script = $this->_escape95($script);
|
||||||
|
}
|
||||||
|
// create the parser
|
||||||
|
$parser = new ParseMaster();
|
||||||
|
$encode = $this->_getEncoder($this->_encoding);
|
||||||
|
// for high-ascii, don't encode single character low-ascii
|
||||||
|
$regexp = ($this->_encoding > 62) ? '/\\w\\w+/' : '/\\w+/';
|
||||||
|
// build the word list
|
||||||
|
$keywords = $this->_analyze($script, $regexp, $encode);
|
||||||
|
$encoded = $keywords['encoded'];
|
||||||
|
|
||||||
|
// encode
|
||||||
|
$parser->add($regexp,
|
||||||
|
array(
|
||||||
|
'fn' => '_replace_encoded',
|
||||||
|
'data' => $encoded,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (empty($script)) {
|
||||||
|
return $script;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//$res = $parser->exec($script);
|
||||||
|
//$res = $this->_bootStrap($res, $keywords);
|
||||||
|
//return $res;
|
||||||
|
return $this->_bootStrap($parser->exec($script), $keywords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _analyze($script, $regexp, $encode) {
|
||||||
|
// analyse
|
||||||
|
// retrieve all words in the script
|
||||||
|
$all = array();
|
||||||
|
preg_match_all($regexp, $script, $all);
|
||||||
|
$_sorted = array(); // list of words sorted by frequency
|
||||||
|
$_encoded = array(); // dictionary of word->encoding
|
||||||
|
$_protected = array(); // instances of "protected" words
|
||||||
|
$all = $all[0]; // simulate the javascript comportement of global match
|
||||||
|
if (!empty($all)) {
|
||||||
|
$unsorted = array(); // same list, not sorted
|
||||||
|
$protected = array(); // "protected" words (dictionary of word->"word")
|
||||||
|
$value = array(); // dictionary of charCode->encoding (eg. 256->ff)
|
||||||
|
$this->_count = array(); // word->count
|
||||||
|
$i = count($all);
|
||||||
|
$j = 0; //$word = null;
|
||||||
|
// count the occurrences - used for sorting later
|
||||||
|
do {
|
||||||
|
--$i;
|
||||||
|
$word = '$' . $all[$i];
|
||||||
|
if (!isset($this->_count[$word])) {
|
||||||
|
$this->_count[$word] = 0;
|
||||||
|
$unsorted[$j] = $word;
|
||||||
|
// make a dictionary of all of the protected words in this script
|
||||||
|
// these are words that might be mistaken for encoding
|
||||||
|
//if (is_string($encode) && method_exists($this, $encode))
|
||||||
|
$values[$j] = call_user_func(array(&$this, $encode), $j);
|
||||||
|
$protected['$' . $values[$j]] = $j++;
|
||||||
|
}
|
||||||
|
// increment the word counter
|
||||||
|
$this->_count[$word]++;
|
||||||
|
} while ($i > 0);
|
||||||
|
// prepare to sort the word list, first we must protect
|
||||||
|
// words that are also used as codes. we assign them a code
|
||||||
|
// equivalent to the word itself.
|
||||||
|
// e.g. if "do" falls within our encoding range
|
||||||
|
// then we store keywords["do"] = "do";
|
||||||
|
// this avoids problems when decoding
|
||||||
|
$i = count($unsorted);
|
||||||
|
do {
|
||||||
|
$word = $unsorted[--$i];
|
||||||
|
if (isset($protected[$word]) /*!= null*/) {
|
||||||
|
$_sorted[$protected[$word]] = substr($word, 1);
|
||||||
|
$_protected[$protected[$word]] = true;
|
||||||
|
$this->_count[$word] = 0;
|
||||||
|
}
|
||||||
|
} while ($i);
|
||||||
|
|
||||||
|
// sort the words by frequency
|
||||||
|
// Note: the javascript and php version of sort can be different :
|
||||||
|
// in php manual, usort :
|
||||||
|
// " If two members compare as equal,
|
||||||
|
// their order in the sorted array is undefined."
|
||||||
|
// so the final packed script is different of the Dean's javascript version
|
||||||
|
// but equivalent.
|
||||||
|
// the ECMAscript standard does not guarantee this behaviour,
|
||||||
|
// and thus not all browsers (e.g. Mozilla versions dating back to at
|
||||||
|
// least 2003) respect this.
|
||||||
|
usort($unsorted, array(&$this, '_sortWords'));
|
||||||
|
$j = 0;
|
||||||
|
// because there are "protected" words in the list
|
||||||
|
// we must add the sorted words around them
|
||||||
|
do {
|
||||||
|
if (!isset($_sorted[$i])) {
|
||||||
|
$_sorted[$i] = substr($unsorted[$j++], 1);
|
||||||
|
}
|
||||||
|
$_encoded[$_sorted[$i]] = $values[$i];
|
||||||
|
} while (++$i < count($unsorted));
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'sorted' => $_sorted,
|
||||||
|
'encoded' => $_encoded,
|
||||||
|
'protected' => $_protected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private $_count = array();
|
||||||
|
private function _sortWords($match1, $match2) {
|
||||||
|
return $this->_count[$match2] - $this->_count[$match1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the boot function used for loading and decoding
|
||||||
|
private function _bootStrap($packed, $keywords) {
|
||||||
|
$ENCODE = $this->_safeRegExp('$encode\\($count\\)');
|
||||||
|
|
||||||
|
// $packed: the packed script
|
||||||
|
$packed = "'" . $this->_escape($packed) . "'";
|
||||||
|
|
||||||
|
// $ascii: base for encoding
|
||||||
|
$ascii = min(count($keywords['sorted']), $this->_encoding);
|
||||||
|
if ($ascii == 0) {
|
||||||
|
$ascii = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// $count: number of words contained in the script
|
||||||
|
$count = count($keywords['sorted']);
|
||||||
|
|
||||||
|
// $keywords: list of words contained in the script
|
||||||
|
foreach ($keywords['protected'] as $i => $value) {
|
||||||
|
$keywords['sorted'][$i] = '';
|
||||||
|
}
|
||||||
|
// convert from a string to an array
|
||||||
|
ksort($keywords['sorted']);
|
||||||
|
$keywords = "'" . implode('|', $keywords['sorted']) . "'.split('|')";
|
||||||
|
|
||||||
|
$encode = ($this->_encoding > 62) ? '_encode95' : $this->_getEncoder($ascii);
|
||||||
|
$encode = $this->_getJSFunction($encode);
|
||||||
|
$encode = preg_replace('/_encoding/', '$ascii', $encode);
|
||||||
|
$encode = preg_replace('/arguments\\.callee/', '$encode', $encode);
|
||||||
|
$inline = '\\$count' . ($ascii > 10 ? '.toString(\\$ascii)' : '');
|
||||||
|
|
||||||
|
// $decode: code snippet to speed up decoding
|
||||||
|
if ($this->_fastDecode) {
|
||||||
|
// create the decoder
|
||||||
|
$decode = $this->_getJSFunction('_decodeBody');
|
||||||
|
if ($this->_encoding > 62) {
|
||||||
|
$decode = preg_replace('/\\\\w/', '[\\xa1-\\xff]', $decode);
|
||||||
|
}
|
||||||
|
// perform the encoding inline for lower ascii values
|
||||||
|
elseif ($ascii < 36) {
|
||||||
|
$decode = preg_replace($ENCODE, $inline, $decode);
|
||||||
|
}
|
||||||
|
// special case: when $count==0 there are no keywords. I want to keep
|
||||||
|
// the basic shape of the unpacking function so i'll frig the code...
|
||||||
|
if ($count == 0) {
|
||||||
|
$decode = preg_replace($this->_safeRegExp('($count)\\s*=\\s*1'), '$1=0', $decode, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// boot function
|
||||||
|
$unpack = $this->_getJSFunction('_unpack');
|
||||||
|
if ($this->_fastDecode) {
|
||||||
|
// insert the decoder
|
||||||
|
$this->buffer = $decode;
|
||||||
|
$unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastDecode'), $unpack, 1);
|
||||||
|
}
|
||||||
|
$unpack = preg_replace('/"/', "'", $unpack);
|
||||||
|
if ($this->_encoding > 62) { // high-ascii
|
||||||
|
// get rid of the word-boundaries for regexp matches
|
||||||
|
$unpack = preg_replace('/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack);
|
||||||
|
}
|
||||||
|
if ($ascii > 36 || $this->_encoding > 62 || $this->_fastDecode) {
|
||||||
|
// insert the encode function
|
||||||
|
$this->buffer = $encode;
|
||||||
|
$unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastEncode'), $unpack, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// perform the encoding inline
|
||||||
|
$unpack = preg_replace($ENCODE, $inline, $unpack);
|
||||||
|
}
|
||||||
|
// pack the boot function too
|
||||||
|
$unpackPacker = new JavaScriptPacker($unpack, 0, false, true);
|
||||||
|
$unpack = $unpackPacker->pack();
|
||||||
|
|
||||||
|
// arguments
|
||||||
|
$params = array($packed, $ascii, $count, $keywords);
|
||||||
|
if ($this->_fastDecode) {
|
||||||
|
$params[] = 0;
|
||||||
|
$params[] = '{}';
|
||||||
|
}
|
||||||
|
$params = implode(',', $params);
|
||||||
|
|
||||||
|
// the whole thing
|
||||||
|
return 'eval(' . $unpack . '(' . $params . "))\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private $buffer;
|
||||||
|
private function _insertFastDecode($match) {
|
||||||
|
return '{' . $this->buffer . ';';
|
||||||
|
}
|
||||||
|
private function _insertFastEncode($match) {
|
||||||
|
return '{$encode=' . $this->buffer . ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
// mmm.. ..which one do i need ??
|
||||||
|
private function _getEncoder($ascii) {
|
||||||
|
return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ?
|
||||||
|
'_encode95' : '_encode62' : '_encode36' : '_encode10';
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero encoding
|
||||||
|
// characters: 0123456789
|
||||||
|
private function _encode10($charCode) {
|
||||||
|
return $charCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inherent base36 support
|
||||||
|
// characters: 0123456789abcdefghijklmnopqrstuvwxyz
|
||||||
|
private function _encode36($charCode) {
|
||||||
|
return base_convert($charCode, 10, 36);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hitch a ride on base36 and add the upper case alpha characters
|
||||||
|
// characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||||
|
private function _encode62($charCode) {
|
||||||
|
$res = '';
|
||||||
|
if ($charCode >= $this->_encoding) {
|
||||||
|
$res = $this->_encode62((int) ($charCode / $this->_encoding));
|
||||||
|
}
|
||||||
|
$charCode = $charCode % $this->_encoding;
|
||||||
|
|
||||||
|
if ($charCode > 35) {
|
||||||
|
return $res . chr($charCode + 29);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return $res . base_convert($charCode, 10, 36);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use high-ascii values
|
||||||
|
// characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ
|
||||||
|
private function _encode95($charCode) {
|
||||||
|
$res = '';
|
||||||
|
if ($charCode >= $this->_encoding) {
|
||||||
|
$res = $this->_encode95($charCode / $this->_encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res . chr(($charCode % $this->_encoding) + 161);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _safeRegExp($string) {
|
||||||
|
return '/' . preg_replace('/\$/', '\\\$', $string) . '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _encodePrivate($charCode) {
|
||||||
|
return "_" . $charCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// protect characters used by the parser
|
||||||
|
private function _escape($script) {
|
||||||
|
return preg_replace('/([\\\\\'])/', '\\\$1', $script);
|
||||||
|
}
|
||||||
|
|
||||||
|
// protect high-ascii characters already in the script
|
||||||
|
private function _escape95($script) {
|
||||||
|
return preg_replace_callback(
|
||||||
|
'/[\\xa1-\\xff]/',
|
||||||
|
array(&$this, '_escape95Bis'),
|
||||||
|
$script
|
||||||
|
);
|
||||||
|
}
|
||||||
|
private function _escape95Bis($match) {
|
||||||
|
return '\x' . ((string) dechex(ord($match)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function _getJSFunction($aName) {
|
||||||
|
if (defined('self::JSFUNCTION' . $aName)) {
|
||||||
|
return constant('self::JSFUNCTION' . $aName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JavaScript Functions used.
|
||||||
|
// Note : In Dean's version, these functions are converted
|
||||||
|
// with 'String(aFunctionName);'.
|
||||||
|
// This internal conversion complete the original code, ex :
|
||||||
|
// 'while (aBool) anAction();' is converted to
|
||||||
|
// 'while (aBool) { anAction(); }'.
|
||||||
|
// The JavaScript functions below are corrected.
|
||||||
|
|
||||||
|
// unpacking function - this is the boot strap function
|
||||||
|
// data extracted from this packing routine is passed to
|
||||||
|
// this function when decoded in the target
|
||||||
|
// NOTE ! : without the ';' final.
|
||||||
|
const JSFUNCTION_unpack =
|
||||||
|
|
||||||
|
'function($packed, $ascii, $count, $keywords, $encode, $decode) {
|
||||||
|
while ($count--) {
|
||||||
|
if ($keywords[$count]) {
|
||||||
|
$packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $packed;
|
||||||
|
}';
|
||||||
|
/*
|
||||||
|
'function($packed, $ascii, $count, $keywords, $encode, $decode) {
|
||||||
|
while ($count--)
|
||||||
|
if ($keywords[$count])
|
||||||
|
$packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
|
||||||
|
return $packed;
|
||||||
|
}';
|
||||||
|
*/
|
||||||
|
|
||||||
|
// code-snippet inserted into the unpacker to speed up decoding
|
||||||
|
const JSFUNCTION_decodeBody =
|
||||||
|
//_decode = function() {
|
||||||
|
// does the browser support String.replace where the
|
||||||
|
// replacement value is a function?
|
||||||
|
|
||||||
|
' if (!\'\'.replace(/^/, String)) {
|
||||||
|
// decode all the values we need
|
||||||
|
while ($count--) {
|
||||||
|
$decode[$encode($count)] = $keywords[$count] || $encode($count);
|
||||||
|
}
|
||||||
|
// global replacement function
|
||||||
|
$keywords = [function ($encoded) {return $decode[$encoded]}];
|
||||||
|
// generic match
|
||||||
|
$encode = function () {return \'\\\\w+\'};
|
||||||
|
// reset the loop counter - we are now doing a global replace
|
||||||
|
$count = 1;
|
||||||
|
}
|
||||||
|
';
|
||||||
|
//};
|
||||||
|
/*
|
||||||
|
' if (!\'\'.replace(/^/, String)) {
|
||||||
|
// decode all the values we need
|
||||||
|
while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count);
|
||||||
|
// global replacement function
|
||||||
|
$keywords = [function ($encoded) {return $decode[$encoded]}];
|
||||||
|
// generic match
|
||||||
|
$encode = function () {return\'\\\\w+\'};
|
||||||
|
// reset the loop counter - we are now doing a global replace
|
||||||
|
$count = 1;
|
||||||
|
}';
|
||||||
|
*/
|
||||||
|
|
||||||
|
// zero encoding
|
||||||
|
// characters: 0123456789
|
||||||
|
const JSFUNCTION_encode10 =
|
||||||
|
'function($charCode) {
|
||||||
|
return $charCode;
|
||||||
|
}'; //;';
|
||||||
|
|
||||||
|
// inherent base36 support
|
||||||
|
// characters: 0123456789abcdefghijklmnopqrstuvwxyz
|
||||||
|
const JSFUNCTION_encode36 =
|
||||||
|
'function($charCode) {
|
||||||
|
return $charCode.toString(36);
|
||||||
|
}'; //;';
|
||||||
|
|
||||||
|
// hitch a ride on base36 and add the upper case alpha characters
|
||||||
|
// characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||||
|
const JSFUNCTION_encode62 =
|
||||||
|
'function($charCode) {
|
||||||
|
return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) +
|
||||||
|
(($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));
|
||||||
|
}';
|
||||||
|
|
||||||
|
// use high-ascii values
|
||||||
|
// characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ
|
||||||
|
const JSFUNCTION_encode95 =
|
||||||
|
'function($charCode) {
|
||||||
|
return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) +
|
||||||
|
String.fromCharCode($charCode % _encoding + 161);
|
||||||
|
}';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ParseMaster {
|
||||||
|
public $ignoreCase = false;
|
||||||
|
public $escapeChar = '';
|
||||||
|
|
||||||
|
// constants
|
||||||
|
const EXPRESSION = 0;
|
||||||
|
const REPLACEMENT = 1;
|
||||||
|
const LENGTH = 2;
|
||||||
|
|
||||||
|
// used to determine nesting levels
|
||||||
|
private $GROUPS = '/\\(/'; //g
|
||||||
|
private $SUB_REPLACE = '/\\$\\d/';
|
||||||
|
private $INDEXED = '/^\\$\\d+$/';
|
||||||
|
private $TRIM = '/([\'"])\\1\\.(.*)\\.\\1\\1$/';
|
||||||
|
private $ESCAPE = '/\\\./'; //g
|
||||||
|
private $QUOTE = '/\'/';
|
||||||
|
private $DELETED = '/\\x01[^\\x01]*\\x01/'; //g
|
||||||
|
|
||||||
|
public function add($expression, $replacement = '') {
|
||||||
|
// count the number of sub-expressions
|
||||||
|
// - add one because each pattern is itself a sub-expression
|
||||||
|
$length = 1 + preg_match_all($this->GROUPS, $this->_internalEscape((string) $expression), $out);
|
||||||
|
|
||||||
|
// treat only strings $replacement
|
||||||
|
if (is_string($replacement)) {
|
||||||
|
// does the pattern deal with sub-expressions?
|
||||||
|
if (preg_match($this->SUB_REPLACE, $replacement)) {
|
||||||
|
// a simple lookup? (e.g. "$2")
|
||||||
|
if (preg_match($this->INDEXED, $replacement)) {
|
||||||
|
// store the index (used for fast retrieval of matched strings)
|
||||||
|
$replacement = (int) (substr($replacement, 1)) - 1;
|
||||||
|
}
|
||||||
|
else { // a complicated lookup (e.g. "Hello $2 $1")
|
||||||
|
// build a function to do the lookup
|
||||||
|
$quote = preg_match($this->QUOTE, $this->_internalEscape($replacement))
|
||||||
|
? '"' : "'";
|
||||||
|
$replacement = array(
|
||||||
|
'fn' => '_backReferences',
|
||||||
|
'data' => array(
|
||||||
|
'replacement' => $replacement,
|
||||||
|
'length' => $length,
|
||||||
|
'quote' => $quote,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// pass the modified arguments
|
||||||
|
if (!empty($expression)) {
|
||||||
|
$this->_add($expression, $replacement, $length);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->_add('/^$/', $replacement, $length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exec($string) {
|
||||||
|
// execute the global replacement
|
||||||
|
$this->_escaped = array();
|
||||||
|
|
||||||
|
// simulate the _patterns.toSTring of Dean
|
||||||
|
$regexp = '/';
|
||||||
|
foreach ($this->_patterns as $reg) {
|
||||||
|
$regexp .= '(' . substr($reg[self::EXPRESSION], 1, -1) . ')|';
|
||||||
|
}
|
||||||
|
$regexp = substr($regexp, 0, -1) . '/';
|
||||||
|
$regexp .= ($this->ignoreCase) ? 'i' : '';
|
||||||
|
|
||||||
|
$string = $this->_escape($string, $this->escapeChar);
|
||||||
|
$string = preg_replace_callback(
|
||||||
|
$regexp,
|
||||||
|
array(
|
||||||
|
&$this,
|
||||||
|
'_replacement',
|
||||||
|
),
|
||||||
|
$string
|
||||||
|
);
|
||||||
|
$string = $this->_unescape($string, $this->escapeChar);
|
||||||
|
|
||||||
|
return preg_replace($this->DELETED, '', $string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reset() {
|
||||||
|
// clear the patterns collection so that this object may be re-used
|
||||||
|
$this->_patterns = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
private $_escaped = array(); // escaped characters
|
||||||
|
private $_patterns = array(); // patterns stored by index
|
||||||
|
|
||||||
|
// create and add a new pattern to the patterns collection
|
||||||
|
private function _add() {
|
||||||
|
$arguments = func_get_args();
|
||||||
|
$this->_patterns[] = $arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the global replace function (it's quite complicated)
|
||||||
|
private function _replacement($arguments) {
|
||||||
|
if (empty($arguments)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$i = 1;
|
||||||
|
$j = 0;
|
||||||
|
// loop through the patterns
|
||||||
|
while (isset($this->_patterns[$j])) {
|
||||||
|
$pattern = $this->_patterns[$j++];
|
||||||
|
// do we have a result?
|
||||||
|
if (isset($arguments[$i]) && ($arguments[$i] != '')) {
|
||||||
|
$replacement = $pattern[self::REPLACEMENT];
|
||||||
|
|
||||||
|
if (is_array($replacement) && isset($replacement['fn'])) {
|
||||||
|
|
||||||
|
if (isset($replacement['data'])) {
|
||||||
|
$this->buffer = $replacement['data'];
|
||||||
|
}
|
||||||
|
return call_user_func(array(&$this, $replacement['fn']), $arguments, $i);
|
||||||
|
|
||||||
|
}
|
||||||
|
elseif (is_int($replacement)) {
|
||||||
|
return $arguments[$replacement + $i];
|
||||||
|
|
||||||
|
}
|
||||||
|
$delete = ($this->escapeChar == '' ||
|
||||||
|
strpos($arguments[$i], $this->escapeChar) === false)
|
||||||
|
? '' : "\x01" . $arguments[$i] . "\x01";
|
||||||
|
return $delete . $replacement;
|
||||||
|
|
||||||
|
// skip over references to sub-expressions
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$i += $pattern[self::LENGTH];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _backReferences($match, $offset) {
|
||||||
|
$replacement = $this->buffer['replacement'];
|
||||||
|
$quote = $this->buffer['quote'];
|
||||||
|
$i = $this->buffer['length'];
|
||||||
|
while ($i) {
|
||||||
|
$replacement = str_replace('$' . $i--, $match[$offset + $i], $replacement);
|
||||||
|
}
|
||||||
|
return $replacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _replace_name($match, $offset) {
|
||||||
|
$length = strlen($match[$offset + 2]);
|
||||||
|
$start = $length - max($length - strlen($match[$offset + 3]), 0);
|
||||||
|
return substr($match[$offset + 1], $start, $length) . $match[$offset + 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _replace_encoded($match, $offset) {
|
||||||
|
return $this->buffer[$match[$offset]];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// php : we cannot pass additional data to preg_replace_callback,
|
||||||
|
// and we cannot use &$this in create_function, so let's go to lower level
|
||||||
|
private $buffer;
|
||||||
|
|
||||||
|
// encode escaped characters
|
||||||
|
private function _escape($string, $escapeChar) {
|
||||||
|
if ($escapeChar) {
|
||||||
|
$this->buffer = $escapeChar;
|
||||||
|
return preg_replace_callback(
|
||||||
|
'/\\' . $escapeChar . '(.)' . '/',
|
||||||
|
array(&$this, '_escapeBis'),
|
||||||
|
$string
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function _escapeBis($match) {
|
||||||
|
$this->_escaped[] = $match[1];
|
||||||
|
return $this->buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode escaped characters
|
||||||
|
private function _unescape($string, $escapeChar) {
|
||||||
|
if ($escapeChar) {
|
||||||
|
$regexp = '/' . '\\' . $escapeChar . '/';
|
||||||
|
$this->buffer = array(
|
||||||
|
'escapeChar' => $escapeChar,
|
||||||
|
'i' => 0,
|
||||||
|
);
|
||||||
|
return preg_replace_callback(
|
||||||
|
$regexp,
|
||||||
|
array(&$this, '_unescapeBis'),
|
||||||
|
$string
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function _unescapeBis() {
|
||||||
|
if (isset($this->_escaped[$this->buffer['i']])
|
||||||
|
&& $this->_escaped[$this->buffer['i']] != '') {
|
||||||
|
$temp = $this->_escaped[$this->buffer['i']];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$temp = '';
|
||||||
|
}
|
||||||
|
$this->buffer['i']++;
|
||||||
|
return $this->buffer['escapeChar'] . $temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _internalEscape($string) {
|
||||||
|
return preg_replace($this->ESCAPE, '', $string);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,811 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the advagg bundler module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; Configure advagg settings.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*
|
||||||
|
* @see system_settings_form()
|
||||||
|
*/
|
||||||
|
function advagg_mod_admin_settings_form() {
|
||||||
|
drupal_set_title(t('AdvAgg: Modifications'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
$form = array();
|
||||||
|
|
||||||
|
$options = array(
|
||||||
|
0 => t('Use default (safe) settings'),
|
||||||
|
2 => t('Use recommended (optimized) settings'),
|
||||||
|
4 => t('Use customized settings'),
|
||||||
|
);
|
||||||
|
$form['advagg_mod_admin_mode'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('AdvAgg Settings'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_admin_mode', ADVAGG_MOD_ADMIN_MODE),
|
||||||
|
'#options' => $options,
|
||||||
|
'#description' => t("Default settings will mirror core as closely as possible. <br>Recommended settings are optimized for speed."),
|
||||||
|
);
|
||||||
|
$form['global_container'] = array(
|
||||||
|
'#type' => 'container',
|
||||||
|
'#hidden' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'visible' => array(
|
||||||
|
':input[name="advagg_mod_admin_mode"]' => array('value' => '4'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Tell user to update library if a new version is available.
|
||||||
|
$library_name = 'loadCSS';
|
||||||
|
$module_name = 'advagg_mod';
|
||||||
|
list($description) = advagg_get_version_description($library_name, $module_name);
|
||||||
|
if (!empty($description)) {
|
||||||
|
$form['global_container']['advagg_version_msg'] = array(
|
||||||
|
'#markup' => "<p>{$description}</p>",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$form['global_container']['js'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('JS'),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_mod_js_preprocess'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Enable preprocess on all JS (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_preprocess', ADVAGG_MOD_JS_PREPROCESS),
|
||||||
|
'#description' => t('Force all JavaScript to have the preprocess attribute be set to TRUE. All JavaScript files will be aggregated if enabled.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_mod_js_remove_unused'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Remove unused JavaScript if possible'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_remove_unused', ADVAGG_MOD_JS_REMOVE_UNUSED),
|
||||||
|
'#description' => t('This will scan all included JS files for references to jquery and drupal. If none are found then the core JavaScript (jquery.js, drupal.js, Drupal.settings) is removed and not loaded on that page. If you have a site that does not use a lot of Javascript this might be helpful as it could prevent unused JavaScript from being executed, thus speeding up your sites frontend performance. Enabling this usually has negative backend performance impact.'),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_mod_js_no_ajaxpagestate'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Remove ajaxPageState CSS and JS data if ajax.js is not used on this page (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_no_ajaxpagestate', ADVAGG_MOD_JS_NO_AJAXPAGESTATE),
|
||||||
|
'#description' => t('This assumes that the only thing that uses Drupal.settings.ajaxPageState.css and Drupal.settings.ajaxPageState.js is ajax.js.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_mod_js_inline_resource_hints'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Resource hint src attributes found in the HTML content (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_inline_resource_hints', ADVAGG_MOD_JS_INLINE_RESOURCE_HINTS),
|
||||||
|
'#description' => t('Scan any JavaScript that was added to the content part of the page incorrectly for a src attribute; if found use the advagg <a href="@url">resource hints settings</a> for faster loading.', array('@url' => url($config_path . '/advagg', array('fragment' => 'edit-resource-hints')))),
|
||||||
|
'#disabled' => !variable_get('advagg_resource_hints_dns_prefetch', ADVAGG_RESOURCE_HINTS_DNS_PREFETCH) && !variable_get('advagg_resource_hints_preconnect', ADVAGG_RESOURCE_HINTS_PRECONNECT) && !variable_get('advagg_resource_hints_preload', ADVAGG_RESOURCE_HINTS_PRELOAD),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
if ($form['global_container']['js']['advagg_mod_js_inline_resource_hints']['#disabled']) {
|
||||||
|
$form['global_container']['js']['advagg_mod_js_inline_resource_hints']['#description'] .= ' ' . t('Currently disabled; to enable check the desired checkboxes under Resource Hints on the <a href="@url">configuration page</a>', array('@url' => url($config_path . '/advagg', array('fragment' => 'edit-resource-hints'))));
|
||||||
|
}
|
||||||
|
// Optimize JavaScript Ordering.
|
||||||
|
$form['global_container']['js']['adjust_sort'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Optimize JavaScript Ordering'),
|
||||||
|
'#description' => t('The settings in here might change the order in which the JavaScript is loaded. It will move the scripts around so that more optimal aggregates are built. In most cases enabling these checkboxes will cause no negative side effects.'),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['adjust_sort']['advagg_mod_js_head_extract'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move JavaScript added by drupal_add_html_head() into drupal_add_js() (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_head_extract', ADVAGG_MOD_JS_HEAD_EXTRACT),
|
||||||
|
'#description' => t('This will move JavaScript added incorrectly to Drupal into the top of the drupal_add_js() queue.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['adjust_sort']['advagg_mod_js_adjust_sort_external'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move all external scripts to the top of the execution order (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_adjust_sort_external', ADVAGG_MOD_JS_ADJUST_SORT_EXTERNAL),
|
||||||
|
'#description' => t('This will group all external JavaScript files to be above all other JavaScript.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['adjust_sort']['advagg_mod_js_adjust_sort_inline'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move all inline scripts to the bottom of the execution order (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_adjust_sort_inline', ADVAGG_MOD_JS_ADJUST_SORT_INLINE),
|
||||||
|
'#description' => t('This will group all inline JavaScript to be below all other JavaScript.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['adjust_sort']['advagg_mod_js_adjust_sort_browsers'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move all browser conditional JavaScript to the bottom of the group (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_adjust_sort_browsers', ADVAGG_MOD_JS_ADJUST_SORT_BROWSERS),
|
||||||
|
'#description' => t('This will group all browser conditional JavaScript to be in the lowest group of that conditional rule.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
if (module_exists('googleanalytics')
|
||||||
|
&& is_callable('googleanalytics_api')
|
||||||
|
&& is_callable('_googleanalytics_cache')
|
||||||
|
) {
|
||||||
|
$form['global_container']['js']['adjust_sort']['expert'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Experimental Settings'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => !variable_get('advagg_mod_ga_inline_to_file', ADVAGG_MOD_GA_INLINE_TO_FILE),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['adjust_sort']['expert']['advagg_mod_ga_inline_to_file'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move Google Analytics analytics.js code from inline to be a file'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_ga_inline_to_file', ADVAGG_MOD_GA_INLINE_TO_FILE),
|
||||||
|
'#description' => t('The AdvAgg Relocate module is more capable. Recommend using it if you are using this functionality. This setting will soon be deprecated.'),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['adjust_sort']['expert']['advagg_mod_prefetch'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Prefetch stats.g.doubleclick.net/robots.txt'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_prefetch', ADVAGG_MOD_PREFETCH),
|
||||||
|
'#description' => t('Opens a connection to stats.g.doubleclick.net in order to speed up the round trip time. If the browser supports preconnect and under Resource Hints on the <a href="@url">configuration page</a>, preconnect is enabled (currently %state), then it will use preconnect instead of this robots.txt hack.', array(
|
||||||
|
'@url' => url($config_path . '/advagg', array('fragment' => 'edit-resource-hints')),
|
||||||
|
'%state' => variable_get('advagg_resource_hints_preconnect', ADVAGG_RESOURCE_HINTS_PRECONNECT) ? t('enabled') : t('disabled'),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust javascript location and execution.
|
||||||
|
$form['global_container']['js']['placement'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Adjust javascript location and execution'),
|
||||||
|
'#description' => t('Most of the time adjusting the settings are safe but in some rare cases adjusting these can cause serious JavaScript issues with your site.'),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['placement']['advagg_mod_js_footer'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Move JS to the footer'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_footer', ADVAGG_MOD_JS_FOOTER),
|
||||||
|
'#options' => array(
|
||||||
|
0 => t('Disabled'),
|
||||||
|
1 => t('All but JavaScript Libraries'),
|
||||||
|
3 => t('All but what is in the $all_in_footer_list (recommended)'),
|
||||||
|
2 => t('All (might break things)'),
|
||||||
|
),
|
||||||
|
'#description' => t("If you have JavaScript inline in the body of your document, such as if you are displaying ads, you may need to keep Drupal JS Libraries in the head instead of moving them to the footer. This will keep all JS added with the JS_LIBRARY group in the head while still moving all other JavaScript to the footer."),
|
||||||
|
'#recommended_value' => 3,
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['placement']['advagg_mod_js_defer'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Deferred JavaScript Execution: Add The defer Tag To All Script Tags'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER),
|
||||||
|
'#options' => array(
|
||||||
|
0 => t('Disabled'),
|
||||||
|
2 => t('All but external scripts (recommended)'),
|
||||||
|
1 => t('All (might break things)'),
|
||||||
|
),
|
||||||
|
'#description' => t('This will delay the script execution until the HTML parser has finished. This will have a similar effect to moving all JavaScript to the footer. This might break javascript (especially inline); only use after extensive testing! <a href="@link">More Info</a>', array(
|
||||||
|
'@link' => 'http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/',
|
||||||
|
)),
|
||||||
|
'#recommended_value' => 2,
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['placement']['advagg_mod_js_async_in_header'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Asynchronous JavaScript Execution: Group together in the header (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_async_in_header', ADVAGG_MOD_JS_ASYNC_IN_HEADER),
|
||||||
|
'#description' => t('This will move all async JavaScript code to the header in the same group.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['placement']['advagg_mod_js_footer_inline_alter'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Put a wrapper around inline JS if it was added in the content section incorrectly (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_footer_inline_alter', ADVAGG_MOD_JS_FOOTER_INLINE_ALTER),
|
||||||
|
'#description' => t('This will put a wrapper around any inline JavaScript that was added to the content part of the page incorrectly. The wrapper will check every 250ms until window.Drupal.settings and window.jQuery are defined; at that point the inlined code will then run.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
':input[name="advagg_mod_js_footer"]' => array('value' => '0'),
|
||||||
|
':input[name="advagg_mod_js_defer"]' => array('value' => '0'),
|
||||||
|
':input[name="advagg_mod_js_async"]' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$advagg_mod_wrap_inline_js_skip_list = trim(variable_get('advagg_mod_wrap_inline_js_skip_list', ADVAGG_MOD_WRAP_INLINE_JS_SKIP_LIST));
|
||||||
|
$advagg_mod_wrap_inline_js_xpath = variable_get('advagg_mod_wrap_inline_js_xpath', ADVAGG_MOD_WRAP_INLINE_JS_XPATH);
|
||||||
|
$form['global_container']['js']['placement']['advagg_mod_wrap_inline'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Inline Wrapper Settings'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => empty($advagg_mod_wrap_inline_js_skip_list) && empty($advagg_mod_wrap_inline_js_xpath),
|
||||||
|
'#states' => array(
|
||||||
|
'visible' => array(
|
||||||
|
':input[name="advagg_mod_js_footer_inline_alter"]' => array('checked' => TRUE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['placement']['advagg_mod_wrap_inline']['advagg_mod_wrap_inline_js_skip_list'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('Inline skip list for wrapper code'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_wrap_inline_js_skip_list', ADVAGG_MOD_WRAP_INLINE_JS_SKIP_LIST),
|
||||||
|
'#description' => t("If the inline JavaScript matches a given string then the whole inline script will not be wrapped. Enter one per line. Useful for things like conversion.js from the Google tracking code, as this is not async friendly (conversion.js needs global variables that were previously defined). In that case enter <code>@code</code> or equivalent above, where you replace %replace with the actual ID. Ideally you'd want to fix the code by using <a href='@url'>async friendly JS</a> but this is not always possible with 3rd party code.", array(
|
||||||
|
'@code' => 'var google_conversion_id = XXX;',
|
||||||
|
'%replace' => 'XXX',
|
||||||
|
'@url' => 'https://developers.google.com/adwords-remarketing-tag/asynchronous/',
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['placement']['advagg_mod_wrap_inline']['advagg_mod_wrap_inline_js_xpath'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Use XPath instead of regex when searching for inline scripts'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_wrap_inline_js_xpath', ADVAGG_MOD_WRAP_INLINE_JS_XPATH),
|
||||||
|
'#description' => t('In general this should be disabled due to the unpredictable nature of parsing html snippets using DOMDocument loadHTML(). Only enable if you have script tags inside a textarea that have not been ran through htmlentities().'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['placement']['advagg_mod_js_defer_inline_alter'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Deferred inline JavaScript Execution: Put a wrapper around inline JS so it runs from a setTimeout call (recommended).'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_defer_inline_alter', ADVAGG_MOD_JS_DEFER_INLINE_ALTER),
|
||||||
|
'#description' => t('This will put a wrapper around any inline JavaScript.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$advagg_mod_defer_inline_js_skip_list = trim(variable_get('advagg_mod_defer_inline_js_skip_list', ADVAGG_MOD_DEFER_INLINE_JS_SKIP_LIST));
|
||||||
|
$form['global_container']['js']['placement']['advagg_mod_defer_inline'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Deferred Inline Settings'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => empty($advagg_mod_defer_inline_js_skip_list),
|
||||||
|
'#states' => array(
|
||||||
|
'visible' => array(
|
||||||
|
':input[name="advagg_mod_js_defer_inline_alter"]' => array('checked' => TRUE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['placement']['advagg_mod_defer_inline']['advagg_mod_defer_inline_js_skip_list'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('Inline skip list for wrapper code'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_defer_inline_js_skip_list', ADVAGG_MOD_DEFER_INLINE_JS_SKIP_LIST),
|
||||||
|
'#description' => t("If the inline JavaScript matches a given string then the whole inline script will not be wrapped. Enter one per line."),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['placement']['expert'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Experimental Settings'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => !variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['placement']['expert']['advagg_mod_js_async'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Here there be dragons! Asynchronous JavaScript Execution: Add The async Tag To All Script Tags'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC),
|
||||||
|
'#description' => t('This will cause the script to be downloaded in the background and executed out of order. This will break most javascript as most code is not async safe; only use after extensive testing! <a href="@link">More Info</a>', array(
|
||||||
|
'@link' => 'http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/',
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
// Outdated settings.
|
||||||
|
$form['global_container']['js']['old'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Outdated settings that should not be used'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => !variable_get('advagg_mod_js_async_shim', ADVAGG_MOD_JS_ASYNC_SHIM),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['old']['advagg_mod_js_async_shim'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Rewrite asynchronous script tags to inline, old-browser-compatible scripts.'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_js_async_shim', ADVAGG_MOD_JS_ASYNC_SHIM),
|
||||||
|
'#description' => t('Rewrites all scripts in the page with an "async" attribute to an inline JavaScript loading the script asynchronously in an old browser compatible way. List of <a href="@link">supported browsers</a>. Once all commonly used browsers support the "async" attribute you can happily disable this checkbox.', array('@link' => 'http://caniuse.com/script-async')),
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['global_container']['css'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('CSS'),
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['advagg_mod_css_preprocess'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Enable preprocess on all CSS (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_css_preprocess', ADVAGG_MOD_CSS_PREPROCESS),
|
||||||
|
'#description' => t('Force all CSS to have the preprocess attribute be set to TRUE. All CSS files will be aggregated if enabled.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['advagg_mod_css_preprocess']['#description'] .= module_exists('advagg_bundler') ? ' ' . t('You might want to increase the <a href="@link">CSS Bundles Per Page</a> if this is checked.', array('@link' => url($config_path . '/advagg/bundler'))) : '';
|
||||||
|
if (is_callable('omega_extension_enabled')
|
||||||
|
&& is_callable('omega_theme_get_setting')
|
||||||
|
&& omega_extension_enabled('development')
|
||||||
|
&& omega_theme_get_setting('omega_livereload', TRUE)
|
||||||
|
) {
|
||||||
|
$form['global_container']['css']['advagg_mod_css_preprocess']['#description'] .= ' ' . t('The omega theme is in development mode and livereload is also enabled. This setting combination disables CSS aggregation. If you wish to have CSS aggregation turned on, go to the <a href="@url">theme settings page</a>, select the Omega theme/subtheme and turn off LiveReload and/or turn off the Development extension.', array('@url' => url('admin/appearance/settings')));
|
||||||
|
}
|
||||||
|
// Only test the translate option if
|
||||||
|
// the locale function is defined OR
|
||||||
|
// the locale_custom_strings variable is not empty.
|
||||||
|
$locale_custom_strings = variable_get('locale_custom_strings_' . isset($GLOBALS['language']->language) ? $GLOBALS['language']->language : 'en', array());
|
||||||
|
if (function_exists('locale') || !empty($locale_custom_strings)) {
|
||||||
|
// Only show option if something comes back translated.
|
||||||
|
$files = advagg_mod_admin_test_css_files('css');
|
||||||
|
if (!empty($files)) {
|
||||||
|
$form['global_container']['css']['advagg_mod_css_translate'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Translate CSS content strings'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_css_translate', ADVAGG_MOD_CSS_TRANSLATE),
|
||||||
|
'#description' => t('Run strings inside of quotes of the content attribute through the <a href="@t">t() function</a>. An alternative to this can be found in this <a href="@post">blog post</a>.', array(
|
||||||
|
'@t' => 'https://api.drupal.org/api/drupal/includes!bootstrap.inc/function/t/7',
|
||||||
|
'@post' => 'http://fourkitchens.com/blog/2013/08/15/multilingual-css-generated-content-drupal',
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Optimize CSS Ordering.
|
||||||
|
$form['global_container']['css']['adjust_sort'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Optimize CSS Ordering'),
|
||||||
|
'#description' => t('The settings in here might change the order in which the CSS is loaded. It will move the CSS around so that more optimal aggregates are built. In most cases enabling these checkboxes will cause no negative side effects.'),
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['adjust_sort']['advagg_mod_css_head_extract'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move CSS added by drupal_add_html_head() into drupal_add_css() (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_css_head_extract', ADVAGG_MOD_CSS_HEAD_EXTRACT),
|
||||||
|
'#description' => t('This will move CSS added incorrectly to Drupal into the top of the drupal_add_css() queue.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['adjust_sort']['advagg_mod_css_adjust_sort_external'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move all external CSS to the top of the execution order (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_css_adjust_sort_external', ADVAGG_MOD_CSS_ADJUST_SORT_EXTERNAL),
|
||||||
|
'#description' => t('This will group all external CSS files to be above all other CSS.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['adjust_sort']['advagg_mod_css_adjust_sort_inline'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move all inline CSS to the bottom of the execution order (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_css_adjust_sort_inline', ADVAGG_MOD_CSS_ADJUST_SORT_INLINE),
|
||||||
|
'#description' => t('This will group all inline CSS to be below all other CSS.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['adjust_sort']['advagg_mod_css_adjust_sort_browsers'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move all browser conditional CSS to the bottom of the group (recommended)'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_css_adjust_sort_browsers', ADVAGG_MOD_CSS_ADJUST_SORT_BROWSERS),
|
||||||
|
'#description' => t('This will group all browser conditional CSS to be in the lowest group of that conditional rule.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
// Adjust CSS location and execution.
|
||||||
|
$form['global_container']['css']['placement'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Adjust CSS location and execution'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => !variable_get('advagg_mod_css_defer', ADVAGG_MOD_CSS_DEFER),
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['placement']['advagg_mod_css_defer'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Deferred CSS Execution: Use JS to load CSS'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_css_defer', ADVAGG_MOD_CSS_DEFER),
|
||||||
|
'#options' => array(
|
||||||
|
0 => t('Disabled'),
|
||||||
|
1 => t('All in head, above js'),
|
||||||
|
3 => t('All in head'),
|
||||||
|
4 => t('All in head, use link rel="preload" (recommended)'),
|
||||||
|
5 => t('All in footer except for JS loading code'),
|
||||||
|
7 => t('All in footer'),
|
||||||
|
),
|
||||||
|
'#description' => t('This will try to optimize CSS delivery by using JavaScript to load the CSS. This might break CSS on different browsers and will cause a flash of unstyled content (FOUC). Only enable after extensive testing! <a href="@link">More Info</a>', array(
|
||||||
|
'@link' => 'http://stackoverflow.com/questions/19374843/css-delivery-optimization-how-to-defer-css-loading',
|
||||||
|
)),
|
||||||
|
'#recommended_value' => 4,
|
||||||
|
);
|
||||||
|
// Taken from block_admin_configure().
|
||||||
|
$access = user_access('use PHP for settings');
|
||||||
|
$css_defer_pages = variable_get('advagg_mod_css_defer_pages', '');
|
||||||
|
$visibility_defer = variable_get('advagg_mod_css_defer_visibility', ADVAGG_MOD_VISIBILITY_LISTED);
|
||||||
|
$options = array(
|
||||||
|
ADVAGG_MOD_VISIBILITY_NOTLISTED => t('All pages except those listed (blacklist)'),
|
||||||
|
ADVAGG_MOD_VISIBILITY_LISTED => t('Only the listed pages (whitelist)'),
|
||||||
|
);
|
||||||
|
$description = t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array(
|
||||||
|
'%blog' => 'blog',
|
||||||
|
'%blog-wildcard' => 'blog/*',
|
||||||
|
'%front' => '<front>',
|
||||||
|
));
|
||||||
|
|
||||||
|
if (module_exists('php') && $access) {
|
||||||
|
$options += array(ADVAGG_MOD_VISIBILITY_PHP => t('Pages on which this PHP code returns <code>TRUE</code> (experts only)'));
|
||||||
|
$title = t('Pages or PHP code');
|
||||||
|
$description .= ' ' . t('If the PHP option is chosen, enter PHP code between %php. Note that executing incorrect PHP code can break your Drupal site.', array('%php' => '<?php ?>'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$title = t('Pages');
|
||||||
|
}
|
||||||
|
$options += array(ADVAGG_MOD_VISIBILITY_FILE_CONTROLLED => t('File Controlled (Recommended).'));
|
||||||
|
|
||||||
|
if ($visibility_defer == ADVAGG_MOD_VISIBILITY_PHP && !$access) {
|
||||||
|
$form['global_container']['css']['placement']['visibility_all'] = array(
|
||||||
|
'#type' => 'value',
|
||||||
|
'#value' => $visibility_defer,
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['placement']['pages_all'] = array(
|
||||||
|
'#type' => 'value',
|
||||||
|
'#value' => $css_defer_pages,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$form['global_container']['css']['placement']['advagg_mod_css_defer_visibility'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Defer CSS only on specific pages'),
|
||||||
|
'#options' => $options,
|
||||||
|
'#default_value' => $visibility_defer,
|
||||||
|
'#description' => t('Apply the above CSS setting only on specific pages. On other pages it will act as being Disabled. File Controlled will only use loadCSS() if a critical css file has been found and can be inlined.'),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
':input[name="advagg_mod_css_defer"]' => array('value' => '0'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'#recommended_value' => ADVAGG_MOD_VISIBILITY_FILE_CONTROLLED,
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['placement']['advagg_mod_css_defer_pages'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => '<span class="element-invisible">' . $title . '</span>',
|
||||||
|
'#default_value' => $css_defer_pages,
|
||||||
|
'#description' => $description,
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
':input[name="advagg_mod_css_defer"]' => array('value' => '0'),
|
||||||
|
),
|
||||||
|
'invisible' => array(
|
||||||
|
':input[name="advagg_mod_css_defer_visibility"]' => array('value' => '3'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
list(, $params) = advagg_mod_find_critical_css_file();
|
||||||
|
list($dirs) = $params;
|
||||||
|
$theme_path = drupal_get_path('theme', variable_get('theme_default', NULL)) . '/';
|
||||||
|
$form['global_container']['css']['placement']['advagg_mod_css_defer_visibility_file_explained'] = array(
|
||||||
|
'#type' => 'item',
|
||||||
|
'#markup' => t('Valid example file locations for this page: <p><code>@line1</code><br><code>@line2</code><br><code>@line3</code></p> valid example file locations for the "front" page: <p><code>@line4</code><br><code>@line5</code><br><code>@line6</code></p> valid example file locations for "node/1" (<a href="@url">current_path()</a>) page: <p><code>@line7</code><br><code>@line8</code><br><code>@line9</code></p> valid example file locations for the node type of "page": <p><code>@line10</code><br><code>@line11</code><br><code>@line12</code></p>', array(
|
||||||
|
'@url' => 'https://api.drupal.org/api/drupal/includes%21path.inc/function/current_path/7.x',
|
||||||
|
'@line1' => "{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[4]}{$dirs[6]}.css",
|
||||||
|
'@line2' => "{$dirs[0]}{$dirs[1]}anonymous/{$dirs[4]}{$dirs[6]}.css",
|
||||||
|
'@line3' => "{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[4]}{$dirs[6]}.css",
|
||||||
|
'@line4' => "{$theme_path}{$dirs[1]}{$dirs[2]}{$dirs[4]}front.css",
|
||||||
|
'@line5' => "{$theme_path}{$dirs[1]}anonymous/{$dirs[4]}front.css",
|
||||||
|
'@line6' => "{$theme_path}{$dirs[1]}{$dirs[3]}{$dirs[4]}front.css",
|
||||||
|
'@line7' => "{$theme_path}{$dirs[1]}{$dirs[2]}{$dirs[4]}node/1.css",
|
||||||
|
'@line8' => "{$theme_path}{$dirs[1]}anonymous/{$dirs[4]}node/1.css",
|
||||||
|
'@line9' => "{$theme_path}{$dirs[1]}{$dirs[3]}{$dirs[4]}node/1.css",
|
||||||
|
'@line10' => "{$theme_path}{$dirs[1]}{$dirs[2]}{$dirs[5]}page.css",
|
||||||
|
'@line11' => "{$theme_path}{$dirs[1]}anonymous/{$dirs[5]}page.css",
|
||||||
|
'@line12' => "{$theme_path}{$dirs[1]}{$dirs[3]}{$dirs[5]}page.css",
|
||||||
|
)),
|
||||||
|
'#states' => array(
|
||||||
|
'visible' => array(
|
||||||
|
':input[name="advagg_mod_css_defer_visibility"]' => array('value' => '3'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$form['global_container']['css']['placement']['advagg_mod_css_defer_skip_first_file'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Do not defer the first css file'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_css_defer_skip_first_file', ADVAGG_MOD_CSS_DEFER_SKIP_FIRST_FILE),
|
||||||
|
'#description' => t('Link Stylesheet will make the first css file be blocking. Inline CSS will inline up to @size of the first CSS files.', array('@size' => advagg_mod_admin_byte2human(variable_get('advagg_mod_css_defer_inline_size_limit', ADVAGG_MOD_CSS_DEFER_INLINE_SIZE_LIMIT)))),
|
||||||
|
'#options' => array(
|
||||||
|
0 => t('Disabled'),
|
||||||
|
2 => t('Link Stylesheet'),
|
||||||
|
4 => t('Inline CSS (no more than @size)', array('@size' => advagg_mod_admin_byte2human(variable_get('advagg_mod_css_defer_inline_size_limit', ADVAGG_MOD_CSS_DEFER_INLINE_SIZE_LIMIT)))),
|
||||||
|
),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
':input[name="advagg_mod_css_defer"]' => array('value' => '0'),
|
||||||
|
),
|
||||||
|
'invisible' => array(
|
||||||
|
':input[name="advagg_mod_css_defer_visibility"]' => array('value' => '3'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['placement']['advagg_mod_css_defer_js_code'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('How to include the JS loading code'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_css_defer_js_code', ADVAGG_MOD_CSS_DEFER_JS_CODE),
|
||||||
|
'#options' => array(
|
||||||
|
0 => t('Inline javascript loader library (recommended)'),
|
||||||
|
2 => t('Local file included in aggregate'),
|
||||||
|
4 => t('Externally load the latest from github'),
|
||||||
|
),
|
||||||
|
'#description' => t('The <a href="@link">loadCSS</a> library can be included in various ways.', array(
|
||||||
|
'@link' => 'https://github.com/filamentgroup/loadCSS',
|
||||||
|
)),
|
||||||
|
'#recommended_value' => 0,
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
':input[name="advagg_mod_css_defer"]' => array('value' => '0'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['placement']['advagg_mod_css_defer_admin'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Use JS to load CSS in the admin theme'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_css_defer_admin', ADVAGG_MOD_CSS_DEFER_ADMIN),
|
||||||
|
'#description' => t('This will optimize CSS delivery with JavaScript when viewing the admin theme'),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
':input[name="advagg_mod_css_defer"]' => array('value' => '0'),
|
||||||
|
),
|
||||||
|
'invisible' => array(
|
||||||
|
':input[name="advagg_mod_css_defer_visibility"]' => array('value' => '3'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$pages_all = variable_get('advagg_mod_inline_pages', '');
|
||||||
|
$pages_css = variable_get('advagg_mod_inline_css_pages', '');
|
||||||
|
$pages_js = variable_get('advagg_mod_inline_js_pages', '');
|
||||||
|
unset($options[ADVAGG_MOD_VISIBILITY_NOTLISTED], $options[ADVAGG_MOD_VISIBILITY_FILE_CONTROLLED]);
|
||||||
|
$form['global_container']['landing_page'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Inline CSS/JS on specific pages'),
|
||||||
|
'#description' => t('For most people these are settings that should not be used. This will prevent all local CSS and/or JavaScript files from being downloaded; instead the contents of them will all be inlined. This will cause the raw HTML downloaded to be a lot bigger but it will cause less connections to your webserver from being created. This can sometimes be useful for certain landing pages.'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => ($pages_all || $pages_css || $pages_js) ? FALSE : TRUE,
|
||||||
|
);
|
||||||
|
$visibility_all = variable_get('advagg_mod_inline_visibility', ADVAGG_MOD_VISIBILITY_LISTED);
|
||||||
|
if ($visibility_all == ADVAGG_MOD_VISIBILITY_PHP && !$access) {
|
||||||
|
$form['global_container']['landing_page']['path']['visibility_all'] = array(
|
||||||
|
'#type' => 'value',
|
||||||
|
'#value' => $visibility_all,
|
||||||
|
);
|
||||||
|
$form['global_container']['landing_page']['path']['pages_all'] = array(
|
||||||
|
'#type' => 'value',
|
||||||
|
'#value' => $pages_all,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$form['global_container']['landing_page']['path']['advagg_mod_inline_visibility'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Inline CSS and JS on specific pages'),
|
||||||
|
'#options' => $options,
|
||||||
|
'#default_value' => $visibility_all,
|
||||||
|
);
|
||||||
|
$form['global_container']['landing_page']['path']['advagg_mod_inline_pages'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => '<span class="element-invisible">' . $title . '</span>',
|
||||||
|
'#default_value' => $pages_all,
|
||||||
|
'#description' => $description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$visibility_css = variable_get('advagg_mod_inline_css_visibility', ADVAGG_MOD_VISIBILITY_LISTED);
|
||||||
|
if ($visibility_css == ADVAGG_MOD_VISIBILITY_PHP && !$access) {
|
||||||
|
$form['global_container']['landing_page']['path']['visibility_all'] = array(
|
||||||
|
'#type' => 'value',
|
||||||
|
'#value' => $visibility_css,
|
||||||
|
);
|
||||||
|
$form['global_container']['landing_page']['path']['pages_all'] = array(
|
||||||
|
'#type' => 'value',
|
||||||
|
'#value' => $pages_css,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$form['global_container']['landing_page']['path']['advagg_mod_inline_css_visibility'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Inline CSS on specific pages'),
|
||||||
|
'#options' => $options,
|
||||||
|
'#default_value' => $visibility_css,
|
||||||
|
);
|
||||||
|
$form['global_container']['landing_page']['path']['advagg_mod_inline_css_pages'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => '<span class="element-invisible">' . $title . '</span>',
|
||||||
|
'#default_value' => $pages_css,
|
||||||
|
'#description' => $description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$visibility_js = variable_get('advagg_mod_inline_js_visibility', ADVAGG_MOD_VISIBILITY_LISTED);
|
||||||
|
if ($visibility_js == ADVAGG_MOD_VISIBILITY_PHP && !$access) {
|
||||||
|
$form['global_container']['landing_page']['path']['visibility_all'] = array(
|
||||||
|
'#type' => 'value',
|
||||||
|
'#value' => $visibility_js,
|
||||||
|
);
|
||||||
|
$form['global_container']['landing_page']['path']['pages_all'] = array(
|
||||||
|
'#type' => 'value',
|
||||||
|
'#value' => $pages_js,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$form['global_container']['landing_page']['path']['advagg_mod_inline_js_visibility'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Inline JS on specific pages'),
|
||||||
|
'#options' => $options,
|
||||||
|
'#default_value' => $visibility_js,
|
||||||
|
);
|
||||||
|
$form['global_container']['landing_page']['path']['advagg_mod_inline_js_pages'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => '<span class="element-invisible">' . $title . '</span>',
|
||||||
|
'#default_value' => $pages_js,
|
||||||
|
'#description' => $description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form['global_container']['unified_multisite'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Unified Multisite'),
|
||||||
|
'#description' => t('For most people this is a setting that should not be used.'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['unified_multisite']['advagg_mod_unified_multisite_dir'] = array(
|
||||||
|
'#type' => 'textfield',
|
||||||
|
'#title' => t('Shared Directory'),
|
||||||
|
'#default_value' => variable_get('advagg_mod_unified_multisite_dir', ''),
|
||||||
|
'#size' => 60,
|
||||||
|
'#maxlength' => 128,
|
||||||
|
'#description' => t('This allows you to have a shared directory for all CSS/JS aggregates if this install of drupal is used as a <a href="@multisite">multisite</a>. If this servers multisites share a similar CSS/JS structure then a lot of resources can be saved by not rebuilding the same aggregates in each site of the multisite. Make sure that you use the same directory and advagg settings in each multisite for this setting to work efficiently. Current <a href="@info">hash value</a> of settings on this site: %value. If this value is different across the servers multisites then this will not save server resources as a different file will be built due to AdvAgg having different settings. Note that $base_path is used in the hash value so in some multisite cases it will be impossible to use this setting.', array(
|
||||||
|
'@multisite' => 'https://drupal.org/documentation/install/multi-site',
|
||||||
|
'@info' => url($config_path . '/advagg/info'),
|
||||||
|
'%value' => advagg_get_current_hooks_hash(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make sure the multisite_dir is writable.
|
||||||
|
$form['#validate'][] = 'advagg_mod_admin_settings_form_validate';
|
||||||
|
|
||||||
|
// Clear the cache bins on submit.
|
||||||
|
$form['#submit'][] = 'advagg_mod_admin_settings_form_submit';
|
||||||
|
|
||||||
|
return system_settings_form($form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate callback, check that the unified multisite directory was created.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms_callback
|
||||||
|
*/
|
||||||
|
function advagg_mod_admin_settings_form_validate($form, &$form_state) {
|
||||||
|
// Unified Mmultisite.
|
||||||
|
$multisite_dir = rtrim($form_state['values']['advagg_mod_unified_multisite_dir'], '/');
|
||||||
|
if (!empty($multisite_dir)) {
|
||||||
|
// Prepare directory.
|
||||||
|
$css_dir = $multisite_dir . '/advagg_css';
|
||||||
|
$js_dir = $multisite_dir . '/advagg_js';
|
||||||
|
if (!file_prepare_directory($css_dir, FILE_CREATE_DIRECTORY)
|
||||||
|
|| !file_prepare_directory($js_dir, FILE_CREATE_DIRECTORY)
|
||||||
|
) {
|
||||||
|
if (!is_dir($multisite_dir) || !is_writable($multisite_dir)) {
|
||||||
|
form_set_error('advagg_mod_unified_multisite_dir', t('%dir is not a directory or can not be written to. The shared directory needs to have the same permissions as the "Public file system path" found on the <a href="@file_system_link">File System configuration page</a>.', array(
|
||||||
|
'%dir' => $multisite_dir,
|
||||||
|
'@file_system_link' => url('admin/config/media/file-system'),
|
||||||
|
)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use JS to load CSS.
|
||||||
|
if ($form_state['values']['advagg_mod_css_defer']) {
|
||||||
|
if ($form_state['values']['advagg_mod_css_defer_visibility'] == ADVAGG_MOD_VISIBILITY_LISTED
|
||||||
|
&& empty($form_state['values']['advagg_mod_css_defer_pages'])
|
||||||
|
) {
|
||||||
|
form_set_error('advagg_mod_css_defer_pages', t('The Deferred CSS Execution setting will run only on specific pages. On other pages it will act as being Disabled. Please input what pages you wish to have the CSS be deferred. Currently none are selected.'));
|
||||||
|
}
|
||||||
|
if ($form_state['values']['advagg_mod_css_defer_visibility'] == ADVAGG_MOD_VISIBILITY_NOTLISTED
|
||||||
|
&& $form_state['values']['advagg_mod_css_defer_pages'] === '*'
|
||||||
|
) {
|
||||||
|
form_set_error('advagg_mod_css_defer_pages', t('The Deferred CSS Execution setting will run only on specific pages. On other pages it will act as being Disabled. Please input what pages you wish to have the CSS not be deferred. Currently all pages are disabled (*).'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback, clear out the advagg cache bin.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms_callback
|
||||||
|
*/
|
||||||
|
function advagg_mod_admin_settings_form_submit($form, &$form_state) {
|
||||||
|
// Clear caches.
|
||||||
|
advagg_cache_clear_admin_submit();
|
||||||
|
|
||||||
|
// Reset this form to defaults or recommended values; also show what changed.
|
||||||
|
advagg_set_admin_form_defaults_recommended($form_state, 'advagg_mod_admin_mode');
|
||||||
|
|
||||||
|
// If file controlled, turn off skip first file turn on admin defer.
|
||||||
|
if ($form_state['values']['advagg_mod_css_defer_visibility'] == 3) {
|
||||||
|
$form_state['values']['advagg_mod_css_defer_skip_first_file'] = 0;
|
||||||
|
$form_state['values']['advagg_mod_css_defer_admin'] = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If unified_multisite_dir has changed, flush menu router at the end of the
|
||||||
|
// request.
|
||||||
|
$multisite_dir = rtrim($form_state['values']['advagg_mod_unified_multisite_dir'], '/');
|
||||||
|
$dir = rtrim(variable_get('advagg_mod_unified_multisite_dir', ''), '/');
|
||||||
|
if ($multisite_dir != $dir) {
|
||||||
|
register_shutdown_function('advagg_get_root_files_dir', TRUE);
|
||||||
|
register_shutdown_function('menu_rebuild');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($form_state['values']['advagg_mod_js_defer_inline_alter']) && !empty($form_state['values']['advagg_mod_js_defer_jquery'])) {
|
||||||
|
$form_state['values']['advagg_mod_js_defer_jquery'] = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test all CSS files seeing if any string translations do anything.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array with the filename key and the before => after translation value.
|
||||||
|
*/
|
||||||
|
function advagg_mod_admin_test_css_files() {
|
||||||
|
$output = array();
|
||||||
|
// Get list of files.
|
||||||
|
$query_files = db_select('advagg_files', 'af')
|
||||||
|
->fields('af', array('filename_hash', 'filename'))
|
||||||
|
->condition('filetype', 'css')
|
||||||
|
->orderBy('filename', 'ASC')
|
||||||
|
->execute()
|
||||||
|
->fetchAllKeyed();
|
||||||
|
$files = array_values($query_files);
|
||||||
|
// Exit if no files were found.
|
||||||
|
if (empty($files)) {
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($files as $filename) {
|
||||||
|
// Skip missing files.
|
||||||
|
if (!file_exists($filename)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Load CSS file.
|
||||||
|
$file_contents = advagg_load_stylesheet_content((string) @advagg_file_get_contents($filename), TRUE);
|
||||||
|
|
||||||
|
// Code taken from drupal_load_stylesheet_content().
|
||||||
|
// Regexp to match double quoted strings.
|
||||||
|
$double_quot = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
|
||||||
|
// Regexp to match single quoted strings.
|
||||||
|
$single_quot = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'";
|
||||||
|
// Extract all content inside of quotes.
|
||||||
|
$css_content_pattern = "/content:.*?($double_quot|$single_quot|(\\;|\\})).*?(?:\\;|\\})/";
|
||||||
|
|
||||||
|
// Get strings inside of quotes of the content attribute.
|
||||||
|
preg_match_all($css_content_pattern, $file_contents, $matches);
|
||||||
|
// Skip if no matches.
|
||||||
|
if (empty($matches[1])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($matches[1] as $value) {
|
||||||
|
// Skip if equal to ; or }.
|
||||||
|
if ($value === ';' || $value === '}') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Remove quotes for t function.
|
||||||
|
$before = substr($value, 1, -1);
|
||||||
|
// Only test if it contains A-Za-z.
|
||||||
|
if (!preg_match('/[A-Za-z]/', $before)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Only test if it contains characters other than unicode.
|
||||||
|
$css_unicode_pattern = '/\\\\[0-9a-fA-F]{1,6}(?:\\r\\n|[ \\t\\r\\n\\f])?/';
|
||||||
|
$unicode_removed = preg_replace($css_unicode_pattern, '', $before);
|
||||||
|
if (empty($unicode_removed)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Run t function.
|
||||||
|
// @codingStandardsIgnoreLine
|
||||||
|
$after = t($before);
|
||||||
|
|
||||||
|
// Only include it if strings are different.
|
||||||
|
if ($before !== $after) {
|
||||||
|
if (!isset($output[$filename])) {
|
||||||
|
$output[$filename] = '';
|
||||||
|
}
|
||||||
|
$output[$filename] .= $before . ' => ' . $after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a number of bytes into human readable format.
|
||||||
|
*
|
||||||
|
* @param string $bytes
|
||||||
|
* Number to convert into a more human readable format.
|
||||||
|
* @param int $precision
|
||||||
|
* Number of decimals to output.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Human readable format of the bytes.
|
||||||
|
*/
|
||||||
|
function advagg_mod_admin_byte2human($bytes, $precision = 0) {
|
||||||
|
$units = array('', 'K', 'M', 'G', 'T');
|
||||||
|
|
||||||
|
$bytes = max($bytes, 0);
|
||||||
|
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||||
|
$pow = min($pow, count($units) - 1);
|
||||||
|
$bytes /= (1 << (10 * $pow));
|
||||||
|
|
||||||
|
$output = ceil(round($bytes, $precision + 2) * 10) / 10;
|
||||||
|
return $output . '' . $units[$pow];
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced CSS/JS aggregation modifier module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_get_info_on_files_alter().
|
||||||
|
*
|
||||||
|
* Used to make sure the info is up to date in the cache.
|
||||||
|
*/
|
||||||
|
function advagg_mod_advagg_get_info_on_files_alter(&$return, $cached_data, $bypass_cache) {
|
||||||
|
foreach ($return as &$info) {
|
||||||
|
if (empty($info['fileext']) || $info['fileext'] !== 'js') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// New or updated data or no advagg_js_compress data.
|
||||||
|
if (empty($cached_data[$info['cache_id']])
|
||||||
|
|| empty($info['advagg_mod'])
|
||||||
|
|| $info['content_hash'] != $cached_data[$info['cache_id']]['content_hash']
|
||||||
|
) {
|
||||||
|
// Check the cache.
|
||||||
|
$info['advagg_mod'] = advagg_mod_js_contains_jquery_drupal($info['data']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_get_js_file_contents_alter().
|
||||||
|
*/
|
||||||
|
function advagg_mod_advagg_get_js_file_contents_alter(&$contents, $filename, $aggregate_settings) {
|
||||||
|
// Fix adminimal_admin_menu js issues with admin menu.
|
||||||
|
if (module_exists('adminimal_admin_menu')) {
|
||||||
|
advagg_mod_fix_js_adminimal_admin_menu($contents, $filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_get_css_file_contents_alter().
|
||||||
|
*
|
||||||
|
* Used to run strings inside of quotes of the content attribute through the t
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* @see drupal_load_stylesheet_content()
|
||||||
|
*/
|
||||||
|
function advagg_mod_advagg_get_css_file_contents_alter(&$contents, $filename, $aggregate_settings) {
|
||||||
|
if (empty($aggregate_settings['variables']['advagg_mod_css_translate'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code taken from drupal_load_stylesheet_content().
|
||||||
|
// Regexp to match double quoted strings.
|
||||||
|
$double_quot = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
|
||||||
|
// Regexp to match single quoted strings.
|
||||||
|
$single_quot = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'";
|
||||||
|
// Extract all content inside of quotes.
|
||||||
|
$css_content_pattern = "/content:.*?($double_quot|$single_quot|(?:\\;|\\})).*?(?:\\;|\\})/";
|
||||||
|
|
||||||
|
// Run strings inside of quotes of the content attribute through the t
|
||||||
|
// function.
|
||||||
|
$contents = preg_replace_callback($css_content_pattern, 'advagg_mod_advagg_css_content_t_replace_callback', $contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run preg matches through the t() function.
|
||||||
|
*
|
||||||
|
* @param array $matches
|
||||||
|
* Array of matches from preg_replace_callback().
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Replaced String.
|
||||||
|
*/
|
||||||
|
function advagg_mod_advagg_css_content_t_replace_callback(array $matches) {
|
||||||
|
// Skip if equal to ; or }.
|
||||||
|
if ($matches[1] === ';' || $matches[1] === '}') {
|
||||||
|
return $matches[0];
|
||||||
|
}
|
||||||
|
// Remove quotes for t function.
|
||||||
|
$before = substr($matches[1], 1, -1);
|
||||||
|
// Only run if it contains A-Za-z.
|
||||||
|
if (!preg_match('/[A-Za-z]/', $before)) {
|
||||||
|
return $matches[0];
|
||||||
|
}
|
||||||
|
// Only run if it contains characters other than unicode.
|
||||||
|
$css_unicode_pattern = '/\\\\[0-9a-fA-F]{1,6}(?:\\r\\n|[ \\t\\r\\n\\f])?/';
|
||||||
|
$unicode_removed = preg_replace($css_unicode_pattern, '', $before);
|
||||||
|
if (empty($unicode_removed)) {
|
||||||
|
return $matches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run t function.
|
||||||
|
// @codingStandardsIgnoreLine
|
||||||
|
$after = t($before);
|
||||||
|
// Put back.
|
||||||
|
return str_replace($matches[1], str_replace($before, $after, $matches[1]), $matches[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix admin menu.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* The contents of the js file.
|
||||||
|
* @param string $filename
|
||||||
|
* The filename.
|
||||||
|
*/
|
||||||
|
function advagg_mod_fix_js_adminimal_admin_menu(&$contents, $filename) {
|
||||||
|
// Get adminimal_admin_menu path.
|
||||||
|
$adminimal_admin_menu_path = drupal_get_path('module', 'adminimal_admin_menu');
|
||||||
|
// Only match slicknav js.
|
||||||
|
if ($filename !== "$adminimal_admin_menu_path/js/slicknav/jquery.slicknav.js"
|
||||||
|
&& $filename !== "$adminimal_admin_menu_path/js/slicknav/jquery-no-conflict.slicknav.js"
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lines to look for.
|
||||||
|
$inserted_string_1 = "Drupal.admin = Drupal.admin || {};\n";
|
||||||
|
$inserted_string_2 = "Drupal.admin.behaviors = Drupal.admin.behaviors || {};\n";
|
||||||
|
if (strpos($contents, $inserted_string_1) !== FALSE
|
||||||
|
&& strpos($contents, $inserted_string_2) !== FALSE
|
||||||
|
) {
|
||||||
|
// Do nothing if the lines already exist.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get length of slicknav javascript.
|
||||||
|
$strlen = strlen($contents);
|
||||||
|
// Get the first occurrence of the problem string.
|
||||||
|
$end = strpos($contents, 'Drupal.admin.behaviors.responsivemenu');
|
||||||
|
if ($end === FALSE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get the start of this code block.
|
||||||
|
$start = strrpos($contents, '(function($)', $end - $strlen);
|
||||||
|
if ($start === FALSE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get the lcoation of {.
|
||||||
|
$middle = strpos($contents, "{\n", $start);
|
||||||
|
if ($middle === FALSE) {
|
||||||
|
$middle = strpos($contents, "{", $start);
|
||||||
|
if ($middle === FALSE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$middle += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$middle += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert new js code.
|
||||||
|
$contents = substr_replace($contents, $inserted_string_1 . $inserted_string_2, $middle, 0);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
name = AdvAgg Modifier
|
||||||
|
description = Allows one to alter the CSS and JS array.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
recommends[] = libraries
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg/mod
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles AdvAgg mod installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_install().
|
||||||
|
*/
|
||||||
|
function advagg_mod_install() {
|
||||||
|
// New install gets a locked admin section.
|
||||||
|
variable_set('advagg_mod_admin_mode', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge advagg_mod_css_defer_rel_preload into advagg_mod_css_defer.
|
||||||
|
*/
|
||||||
|
function advagg_mod_update_7201(&$sandbox) {
|
||||||
|
// Merge advagg_mod_css_defer_rel_preload into advagg_mod_css_defer.
|
||||||
|
$advagg_mod_css_defer = variable_get('advagg_mod_css_defer', ADVAGG_MOD_CSS_DEFER);
|
||||||
|
$advagg_mod_css_defer_rel_preload = variable_get('advagg_mod_css_defer_rel_preload', ADVAGG_MOD_CSS_DEFER_REL_PRELOAD);
|
||||||
|
variable_del('advagg_mod_css_defer_rel_preload');
|
||||||
|
if (!empty($advagg_mod_css_defer) && !empty($advagg_mod_css_defer_rel_preload)) {
|
||||||
|
variable_set('advagg_mod_css_defer', 4);
|
||||||
|
return t('The advagg_mod_css_defer_rel_preload variable has been deleted. The advagg_mod_css_defer variable has been set to a value of 4 (All in head, use link rel="preload"). You can adjust this value on the <a href="@url">AdvAgg Modifications page</a>', array('@url' => url('admin/config/development/performance/advagg/mod', array('fragment' => 'edit-advagg-mod-css-defer'))));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return t('The advagg_mod_css_defer_rel_preload variable has been deleted.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move advagg_mod_js_get_external_dns to advagg_mod_js_inline_resource_hints.
|
||||||
|
*/
|
||||||
|
function advagg_mod_update_7202(&$sandbox) {
|
||||||
|
$advagg_mod_js_inline_resource_hints = variable_get('advagg_mod_js_inline_resource_hints', NULL);
|
||||||
|
$advagg_mod_js_get_external_dns = variable_get('advagg_mod_js_get_external_dns', NULL);
|
||||||
|
|
||||||
|
if (is_null($advagg_mod_js_get_external_dns)) {
|
||||||
|
return t('Nothing needed to happen.');
|
||||||
|
}
|
||||||
|
variable_del('advagg_mod_js_get_external_dns');
|
||||||
|
if (!is_null($advagg_mod_js_inline_resource_hints) || empty($advagg_mod_js_inline_resource_hints)) {
|
||||||
|
return t('Nothing needed to happen.');
|
||||||
|
}
|
||||||
|
variable_set('advagg_mod_js_inline_resource_hints', $advagg_mod_js_inline_resource_hints);
|
||||||
|
return t('Variable has been moved.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_requirements().
|
||||||
|
*/
|
||||||
|
function advagg_mod_requirements($phase) {
|
||||||
|
$requirements = array();
|
||||||
|
// Ensure translations don't break at install time.
|
||||||
|
$t = get_t();
|
||||||
|
|
||||||
|
// If not at runtime, return here.
|
||||||
|
if ($phase !== 'runtime') {
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check version.
|
||||||
|
$lib_name = 'loadCSS';
|
||||||
|
$module_name = 'advagg_mod';
|
||||||
|
list($description, $info) = advagg_get_version_description($lib_name, $module_name);
|
||||||
|
if (!empty($description)) {
|
||||||
|
$requirements["{$module_name}_{$lib_name}_updates"] = array(
|
||||||
|
'title' => $t('@module_name', array('@module_name' => $info['name'])),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('The @name library needs to be updated.', array('@name' => $lib_name)),
|
||||||
|
'description' => $description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $requirements;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Used to load CSS via JS so css doesn't block the browser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a css file, load it using JavaScript.
|
||||||
|
*
|
||||||
|
* @param {string} src
|
||||||
|
* URL of the css file to load.
|
||||||
|
*/
|
||||||
|
function advagg_mod_loadStyleSheet(src) {
|
||||||
|
'use strict';
|
||||||
|
if (document.createStyleSheet) {
|
||||||
|
document.createStyleSheet(src);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var stylesheet = document.createElement('link');
|
||||||
|
stylesheet.href = src;
|
||||||
|
stylesheet.rel = 'stylesheet';
|
||||||
|
stylesheet.type = 'text/css';
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(stylesheet);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Used to load CSS via JS so css doesn't block the browser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
/*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
|
||||||
|
/* This file is meant as a standalone workflow for
|
||||||
|
- testing support for link[rel=preload]
|
||||||
|
- enabling async CSS loading in browsers that do not support rel=preload
|
||||||
|
- applying rel preload css once loaded, whether supported or not.
|
||||||
|
*/
|
||||||
|
(function( w ){
|
||||||
|
"use strict";
|
||||||
|
// rel=preload support test
|
||||||
|
if( !w.loadCSS ){
|
||||||
|
w.loadCSS = function(){};
|
||||||
|
}
|
||||||
|
// define on the loadCSS obj
|
||||||
|
var rp = loadCSS.relpreload = {};
|
||||||
|
// rel=preload feature support test
|
||||||
|
// runs once and returns a function for compat purposes
|
||||||
|
rp.support = (function(){
|
||||||
|
var ret;
|
||||||
|
try {
|
||||||
|
ret = w.document.createElement( "link" ).relList.supports( "preload" );
|
||||||
|
} catch (e) {
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
return function(){
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
// if preload isn't supported, get an asynchronous load by using a non-matching media attribute
|
||||||
|
// then change that media back to its intended value on load
|
||||||
|
rp.bindMediaToggle = function( link ){
|
||||||
|
// remember existing media attr for ultimate state, or default to 'all'
|
||||||
|
var finalMedia = link.media || "all";
|
||||||
|
|
||||||
|
function enableStylesheet(){
|
||||||
|
link.media = finalMedia;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind load handlers to enable media
|
||||||
|
if( link.addEventListener ){
|
||||||
|
link.addEventListener( "load", enableStylesheet );
|
||||||
|
} else if( link.attachEvent ){
|
||||||
|
link.attachEvent( "onload", enableStylesheet );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set rel and non-applicable media type to start an async request
|
||||||
|
// note: timeout allows this to happen async to let rendering continue in IE
|
||||||
|
setTimeout(function(){
|
||||||
|
link.rel = "stylesheet";
|
||||||
|
link.media = "only x";
|
||||||
|
});
|
||||||
|
// also enable media after 3 seconds,
|
||||||
|
// which will catch very old browsers (android 2.x, old firefox) that don't support onload on link
|
||||||
|
setTimeout( enableStylesheet, 3000 );
|
||||||
|
};
|
||||||
|
|
||||||
|
// loop through link elements in DOM
|
||||||
|
rp.poly = function(){
|
||||||
|
// double check this to prevent external calls from running
|
||||||
|
if( rp.support() ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var links = w.document.getElementsByTagName( "link" );
|
||||||
|
for( var i = 0; i < links.length; i++ ){
|
||||||
|
var link = links[ i ];
|
||||||
|
// qualify links to those with rel=preload and as=style attrs
|
||||||
|
if( link.rel === "preload" && link.getAttribute( "as" ) === "style" && !link.getAttribute( "data-loadcss" ) ){
|
||||||
|
// prevent rerunning on link
|
||||||
|
link.setAttribute( "data-loadcss", true );
|
||||||
|
// bind listeners to toggle media back
|
||||||
|
rp.bindMediaToggle( link );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// if unsupported, run the polyfill
|
||||||
|
if( !rp.support() ){
|
||||||
|
// run once at least
|
||||||
|
rp.poly();
|
||||||
|
|
||||||
|
// rerun poly on an interval until onload
|
||||||
|
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 );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// commonjs
|
||||||
|
if( typeof exports !== "undefined" ){
|
||||||
|
exports.loadCSS = loadCSS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
w.loadCSS = loadCSS;
|
||||||
|
}
|
||||||
|
}( typeof global !== "undefined" ? global : this ) );
|
3
frontend/drupal/sites/all/modules/advagg/advagg_mod/cssrelpreload.min.js
vendored
Normal file
3
frontend/drupal/sites/all/modules/advagg/advagg_mod/cssrelpreload.min.js
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/*eslint-disable */
|
||||||
|
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
|
||||||
|
!function(t){"use strict";t.loadCSS||(t.loadCSS=function(){});var e=loadCSS.relpreload={};if(e.support=function(){var e;try{e=t.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),e.bindMediaToggle=function(t){function e(){t.media=a}var a=t.media||"all";t.addEventListener?t.addEventListener("load",e):t.attachEvent&&t.attachEvent("onload",e),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(e,3e3)},e.poly=function(){if(!e.support())for(var a=t.document.getElementsByTagName("link"),n=0;n<a.length;n++){var o=a[n];"preload"!==o.rel||"style"!==o.getAttribute("as")||o.getAttribute("data-loadcss")||(o.setAttribute("data-loadcss",!0),e.bindMediaToggle(o))}},!e.support()){e.poly();var a=t.setInterval(e.poly,500);t.addEventListener?t.addEventListener("load",function(){e.poly(),t.clearInterval(a)}):t.attachEvent&&t.attachEvent("onload",function(){e.poly(),t.clearInterval(a)})}"undefined"!=typeof exports?exports.loadCSS=loadCSS:t.loadCSS=loadCSS}("undefined"!=typeof global?global:this);
|
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Used to load CSS via JS so css doesn't block the browser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
/*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
|
||||||
|
(function(w){
|
||||||
|
"use strict";
|
||||||
|
/* exported loadCSS */
|
||||||
|
var loadCSS = function( href, before, media, crossorigin ){
|
||||||
|
// Arguments explained:
|
||||||
|
// `href` [REQUIRED] is the URL for your CSS file.
|
||||||
|
// `before` [OPTIONAL] is the element the script should use as a reference for injecting our stylesheet <link> before
|
||||||
|
// By default, loadCSS attempts to inject the link after the last stylesheet or script in the DOM. However, you might desire a more specific location in your document.
|
||||||
|
// `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all'
|
||||||
|
var doc = w.document;
|
||||||
|
var ss = doc.createElement( "link" );
|
||||||
|
var ref;
|
||||||
|
if( before ){
|
||||||
|
ref = before;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes;
|
||||||
|
ref = refs[ refs.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var sheets = doc.styleSheets;
|
||||||
|
ss.rel = "stylesheet";
|
||||||
|
ss.href = href;
|
||||||
|
if( crossorigin ){
|
||||||
|
ss.setAttribute("crossorigin", crossorigin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// temporarily set media to something inapplicable to ensure it'll fetch without blocking render
|
||||||
|
ss.media = "only x";
|
||||||
|
|
||||||
|
// wait until body is defined before injecting link. This ensures a non-blocking load in IE11.
|
||||||
|
function ready( cb ){
|
||||||
|
if( doc.body ){
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
setTimeout(function(){
|
||||||
|
ready( cb );
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Inject link
|
||||||
|
// Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs
|
||||||
|
// Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/
|
||||||
|
ready( function(){
|
||||||
|
ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) );
|
||||||
|
});
|
||||||
|
// A method (exposed on return object for external use) that mimics onload by polling document.styleSheets until it includes the new sheet.
|
||||||
|
var onloadcssdefined = function( cb ){
|
||||||
|
var resolvedHref = ss.href;
|
||||||
|
var i = sheets.length;
|
||||||
|
while( i-- ){
|
||||||
|
if( sheets[ i ].href === resolvedHref ){
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(function() {
|
||||||
|
onloadcssdefined( cb );
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function loadCB(){
|
||||||
|
if( ss.addEventListener ){
|
||||||
|
ss.removeEventListener( "load", loadCB );
|
||||||
|
}
|
||||||
|
ss.media = media || "all";
|
||||||
|
}
|
||||||
|
|
||||||
|
// once loaded, set link's media back to `all` so that the stylesheet applies once it loads
|
||||||
|
if( ss.addEventListener ){
|
||||||
|
ss.addEventListener( "load", loadCB);
|
||||||
|
}
|
||||||
|
ss.onloadcssdefined = onloadcssdefined;
|
||||||
|
onloadcssdefined( loadCB );
|
||||||
|
return ss;
|
||||||
|
};
|
||||||
|
// commonjs
|
||||||
|
if( typeof exports !== "undefined" ){
|
||||||
|
exports.loadCSS = loadCSS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
w.loadCSS = loadCSS;
|
||||||
|
}
|
||||||
|
}( typeof global !== "undefined" ? global : this ));
|
3
frontend/drupal/sites/all/modules/advagg/advagg_mod/loadCSS.min.js
vendored
Normal file
3
frontend/drupal/sites/all/modules/advagg/advagg_mod/loadCSS.min.js
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/*eslint-disable */
|
||||||
|
/*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
|
||||||
|
!function(a){"use strict";var b=function(b,c,d,e){function k(a){return f.body?a():void setTimeout(function(){k(a)})}function m(){g.addEventListener&&g.removeEventListener("load",m),g.media=d||"all"}var h,f=a.document,g=f.createElement("link");if(c)h=c;else{var i=(f.body||f.getElementsByTagName("head")[0]).childNodes;h=i[i.length-1]}var j=f.styleSheets;g.rel="stylesheet",g.href=b,e&&g.setAttribute("crossorigin",e),g.media="only x",k(function(){h.parentNode.insertBefore(g,c?h:h.nextSibling)});var l=function(a){for(var b=g.href,c=j.length;c--;)if(j[c].href===b)return a();setTimeout(function(){l(a)})};return g.addEventListener&&g.addEventListener("load",m),g.onloadcssdefined=l,l(m),g};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b}("undefined"!=typeof global?global:this);
|
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Used to run js after the CSS has been loaded.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
/*! onloadCSS. (onload callback for loadCSS) [c]2017 Filament Group, Inc. MIT License */
|
||||||
|
/* global navigator */
|
||||||
|
/* exported onloadCSS */
|
||||||
|
function onloadCSS( ss, callback ) {
|
||||||
|
var called;
|
||||||
|
function newcb(){
|
||||||
|
if( !called && callback ){
|
||||||
|
called = true;
|
||||||
|
callback.call( ss );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( ss.addEventListener ){
|
||||||
|
ss.addEventListener( "load", newcb );
|
||||||
|
}
|
||||||
|
if( ss.attachEvent ){
|
||||||
|
ss.attachEvent( "onload", newcb );
|
||||||
|
}
|
||||||
|
|
||||||
|
// This code is for browsers that don’t support onload
|
||||||
|
// No support for onload (it'll bind but never fire):
|
||||||
|
// * Android 4.3 (Samsung Galaxy S4, Browserstack)
|
||||||
|
// * Android 4.2 Browser (Samsung Galaxy SIII Mini GT-I8200L)
|
||||||
|
// * Android 2.3 (Pantech Burst P9070)
|
||||||
|
|
||||||
|
// Weak inference targets Android < 4.4
|
||||||
|
if( "isApplicationInstalled" in navigator && "onloadcssdefined" in ss ) {
|
||||||
|
ss.onloadcssdefined( newcb );
|
||||||
|
}
|
||||||
|
}
|
3
frontend/drupal/sites/all/modules/advagg/advagg_mod/onloadCSS.min.js
vendored
Normal file
3
frontend/drupal/sites/all/modules/advagg/advagg_mod/onloadCSS.min.js
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/*eslint-disable */
|
||||||
|
/*! onloadCSS. (onload callback for loadCSS) [c]2017 Filament Group, Inc. MIT License */
|
||||||
|
function onloadCSS(n,a){function t(){!d&&a&&(d=!0,a.call(n))}var d;n.addEventListener&&n.addEventListener("load",t),n.attachEvent&&n.attachEvent("onload",t),"isApplicationInstalled"in navigator&&"onloadcssdefined"in n&&n.onloadcssdefined(t)}
|
@ -0,0 +1,532 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the advagg relocate module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; Configure advagg settings.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*
|
||||||
|
* @see system_settings_form()
|
||||||
|
*/
|
||||||
|
function advagg_relocate_admin_settings_form() {
|
||||||
|
drupal_set_title(t('AdvAgg: Relocate'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
|
||||||
|
$form = array();
|
||||||
|
$times[0] = 'No Limit';
|
||||||
|
// @codingStandardsIgnoreStart
|
||||||
|
$times += drupal_map_assoc(array(
|
||||||
|
60 * 60 * 12, // 12 hours.
|
||||||
|
60 * 60 * 24, // 1 day.
|
||||||
|
60 * 60 * 24 * 4, // 4 days.
|
||||||
|
60 * 60 * 24 * 7, // 1 week.
|
||||||
|
60 * 60 * 24 * 7 * 2, // 2 weeks.
|
||||||
|
60 * 60 * 24 * 7 * 3, // 3 weeks.
|
||||||
|
60 * 60 * 24 * 30, // 1 month.
|
||||||
|
), 'format_interval');
|
||||||
|
// @codingStandardsIgnoreEnd
|
||||||
|
|
||||||
|
$options = array(
|
||||||
|
0 => t('Use default (safe) settings'),
|
||||||
|
2 => t('Use recommended (optimized) settings'),
|
||||||
|
4 => t('Use customized settings'),
|
||||||
|
);
|
||||||
|
$form['advagg_relocate_admin_mode'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('AdvAgg Settings'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_admin_mode', ADVAGG_RELOCATE_ADMIN_MODE),
|
||||||
|
'#options' => $options,
|
||||||
|
'#description' => t("Default settings will mirror core as closely as possible. <br>Recommended settings are optimized for speed."),
|
||||||
|
);
|
||||||
|
$form['global_container'] = array(
|
||||||
|
'#type' => 'container',
|
||||||
|
'#hidden' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'visible' => array(
|
||||||
|
':input[name="advagg_relocate_admin_mode"]' => array('value' => '4'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['global_container']['js'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('JS Settings'),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move external JS files to a local JS file'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js', ADVAGG_RELOCATE_JS),
|
||||||
|
'#description' => t('External JS will be moved to a JS file that will be included in the aggregated JS output.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_ttl'] = array(
|
||||||
|
'#type' => 'select',
|
||||||
|
'#title' => t('Only cache external JavaScript files if the browser cache TTL is under this amount.'),
|
||||||
|
'#options' => $times,
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js_ttl', ADVAGG_RELOCATE_JS_TTL),
|
||||||
|
'#description' => t('As an example the Minimum needed to not get penalized by Google PageSpeed Insights is <a href="@url">1 week</a>.', array('@url' => 'https://developers.google.com/speed/docs/insights/LeverageBrowserCaching')),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_ga_local'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move inline google analytics inline analytics.js loader code to drupal_add_js'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js_ga_local', ADVAGG_RELOCATE_JS_GA_LOCAL),
|
||||||
|
'#description' => t('If the inline loader code is included in the drupal_add_js array it can then be loaded locally if the above checkbox is checked.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_piwik_local'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move inline piwik.js loader code to drupal_add_js'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js_piwik_local', ADVAGG_RELOCATE_JS_PIWIK_LOCAL),
|
||||||
|
'#description' => t('If the inline loader code is included in the drupal_add_js array it can then be loaded locally if the above checkbox is checked.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_gtm_local'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move inline google tag manager inline gtm.js loader code to drupal_add_js'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js_gtm_local', ADVAGG_RELOCATE_JS_GTM_LOCAL),
|
||||||
|
'#description' => t('If the inline loader code is included in the drupal_add_js array it can then be loaded locally if the above checkbox is checked.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_perfectaudience_local'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move inline perfectaudience loader code to drupal_add_js'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js_perfectaudience_local', ADVAGG_RELOCATE_JS_PERFECTAUDIENCE_LOCAL),
|
||||||
|
'#description' => t('If the inline loader code is included in the drupal_add_js array it can then be loaded locally if the above checkbox is checked.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_twitter_uwt_local'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move inline twitter loader code to drupal_add_js'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js_twitter_uwt_local', ADVAGG_RELOCATE_JS_TWITTER_UWT_LOCAL),
|
||||||
|
'#description' => t('If the inline loader code is included in the drupal_add_js array it can then be loaded locally if the above checkbox is checked.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_linkedin_insight_local'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move inline linkedin insight loader code to drupal_add_js'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js_linkedin_insight_local', ADVAGG_RELOCATE_JS_LINKEDIN_INSIGHT_LOCAL),
|
||||||
|
'#description' => t('If the inline loader code is included in the drupal_add_js array it can then be loaded locally if the above checkbox is checked.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_fbds_local'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move inline fbds.js loader code to drupal_add_js'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js_fbds_local', ADVAGG_RELOCATE_JS_FBDS_LOCAL),
|
||||||
|
'#description' => t('If the inline loader code is included in the drupal_add_js array it can then be loaded locally if the above checkbox is checked.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_fbevents_local'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move inline fbevents.js loader code to drupal_add_js'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js_fbevents_local', ADVAGG_RELOCATE_JS_FBEVENTS_LOCAL),
|
||||||
|
'#description' => t('If the inline loader code is included in the drupal_add_js array it can then be loaded locally if the above checkbox is checked.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_fbevents_local_ids'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('List of Facebook Pixel IDs that will be used.'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_js_fbevents_local_ids', ADVAGG_RELOCATE_JS_FBEVENTS_LOCAL_IDS),
|
||||||
|
'#description' => t('New line separated.'),
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js-fbevents-local' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$advagg_relocate_js_files_blacklist = variable_get('advagg_relocate_js_files_blacklist', ADVAGG_RELOCATE_JS_FILES_BLACKLIST);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_files_blacklist'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('External JS files blacklist'),
|
||||||
|
'#default_value' => $advagg_relocate_js_files_blacklist,
|
||||||
|
'#description' => t('New line separated; hostname, path, and filename. Example: <br><code>ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js</code><br><code>ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js</code>'),
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$advagg_relocate_js_domains_blacklist = variable_get('advagg_relocate_js_domains_blacklist', ADVAGG_RELOCATE_JS_DOMAINS_BLACKLIST);
|
||||||
|
$form['global_container']['js']['advagg_relocate_js_domains_blacklist'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('External JS domains blacklist'),
|
||||||
|
'#default_value' => $advagg_relocate_js_domains_blacklist,
|
||||||
|
'#description' => t('New line separated; hostname only. Example:<br><code>code.jquery.com</code><br><code>maxcdn.bootstrapcdn.com</code>'),
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-js' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['global_container']['css'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('CSS Settings'),
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['advagg_relocate_css'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move external CSS files to a local CSS file'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_css', ADVAGG_RELOCATE_CSS),
|
||||||
|
'#description' => t('External CSS will be moved to a CSS file that will be included in the aggregated CSS output.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['advagg_relocate_css_ttl'] = array(
|
||||||
|
'#type' => 'select',
|
||||||
|
'#title' => t('Only cache external CSS files if the browser cache TTL is under this amount.'),
|
||||||
|
'#options' => $times,
|
||||||
|
'#default_value' => variable_get('advagg_relocate_css_ttl', ADVAGG_RELOCATE_CSS_TTL),
|
||||||
|
'#description' => t('As an example the Minimum needed to not get penalized by Google PageSpeed Insights is <a href="@url">1 week</a>.', array('@url' => 'https://developers.google.com/speed/docs/insights/LeverageBrowserCaching')),
|
||||||
|
);
|
||||||
|
$advagg_relocate_css_files_blacklist = variable_get('advagg_relocate_css_files_blacklist', ADVAGG_RELOCATE_CSS_FILES_BLACKLIST);
|
||||||
|
$form['global_container']['css']['advagg_relocate_css_files_blacklist'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('External CSS files blacklist'),
|
||||||
|
'#default_value' => $advagg_relocate_css_files_blacklist,
|
||||||
|
'#description' => t('New line separated; hostname, path, and filename. Example: <br><code>ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css</code><br><code>ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/themes/smoothness/jquery-ui.css</code>'),
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-css' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$advagg_relocate_css_domains_blacklist = variable_get('advagg_relocate_css_domains_blacklist', ADVAGG_RELOCATE_CSS_DOMAINS_BLACKLIST);
|
||||||
|
$form['global_container']['css']['advagg_relocate_css_domains_blacklist'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('External CSS domains blacklist'),
|
||||||
|
'#default_value' => $advagg_relocate_css_domains_blacklist,
|
||||||
|
'#description' => t('New line separated; hostname only. Example:<br><code>code.jquery.com</code><br><code>maxcdn.bootstrapcdn.com</code>'),
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-css' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['global_container']['css']['fonts'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('Font CSS Settings'),
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['fonts']['advagg_relocate_css_inline_import'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Move external CSS font files to inline CSS'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_css_inline_import', ADVAGG_RELOCATE_CSS_INLINE_IMPORT),
|
||||||
|
'#description' => t('External font CSS will be moved to inline style tags.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['fonts']['advagg_relocate_css_inline_external'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Inline @import CSS font files in local .css files'),
|
||||||
|
'#default_value' => variable_get('advagg_relocate_css_inline_external', ADVAGG_RELOCATE_CSS_INLINE_EXTERNAL),
|
||||||
|
'#description' => t('External font CSS will be put in the aggregated CSS output.'),
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
);
|
||||||
|
|
||||||
|
$options = array(
|
||||||
|
'woff2' => t('<a href="@url">woff2</a> - Edge 14+, FF 39+, Chrome 36+, Opera 23+, iOS 10.1+, Android 5+', array('@url' => 'http://caniuse.com/#feat=woff2')),
|
||||||
|
'woff' => t('<a href="@url">woff</a> - IE 9+, FF 3.6+, Chrome 5+, Safari 5.1+, Opera 11.5+, iOS 5.1+, Android 4.4+', array('@url' => 'http://caniuse.com/#feat=woff')),
|
||||||
|
'ttf' => t('<a href="@url">ttf</a> - Older Safari, Android, iOS.', array('@url' => 'http://caniuse.com/#feat=ttf')),
|
||||||
|
'eot' => t('<a href="@url">eot</a> - IE6, IE7, IE8 and IE9 in compatibility mode.', array('@url' => 'http://caniuse.com/#feat=eot')),
|
||||||
|
'svg' => t('<a href="@url">svg</a> - Legacy iOS (4.0 and lower).', array('@url' => 'http://caniuse.com/#feat=svg')),
|
||||||
|
);
|
||||||
|
$defaults = array(
|
||||||
|
'woff2' => 'woff2',
|
||||||
|
'woff' => 'woff',
|
||||||
|
'ttf' => 'ttf',
|
||||||
|
'eot' => 0,
|
||||||
|
'svg' => 0,
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['fonts']['advagg_relocate_css_inline_import_browsers'] = array(
|
||||||
|
'#type' => 'checkboxes',
|
||||||
|
'#title' => t('Browser support for fonts'),
|
||||||
|
'#options' => $options,
|
||||||
|
'#default_value' => variable_get('advagg_relocate_css_inline_import_browsers', $defaults),
|
||||||
|
'#description' => t('<a href="@url">Read more</a>.', array('@url' => 'https://css-tricks.com/snippets/css/using-font-face/')),
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-css-inline-import' => array('checked' => FALSE),
|
||||||
|
'#edit-advagg-relocate-css-inline-external' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$form['global_container']['css']['fonts']['per_file_settings'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('What import statements should be inlined?'),
|
||||||
|
'#states' => array(
|
||||||
|
'invisible' => array(
|
||||||
|
'#edit-advagg-relocate-css-inline-import' => array('checked' => FALSE),
|
||||||
|
'#edit-advagg-relocate-css-inline-external' => array('checked' => FALSE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$advagg_relocate_css_font_domains = variable_get('advagg_relocate_css_font_domains', ADVAGG_RELOCATE_CSS_FONT_DOMAINS);
|
||||||
|
$form['global_container']['css']['fonts']['per_file_settings']['advagg_relocate_css_font_domains'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('What external domains are supported'),
|
||||||
|
'#default_value' => $advagg_relocate_css_font_domains,
|
||||||
|
'#description' => t('Currently Only fonts.googleapis.com has been tested. Please <a href="@url">open an issue</a> if you want to inline import statements for other domains.', array('@url' => 'https://www.drupal.org/project/issues/advagg')),
|
||||||
|
'#disabled' => TRUE,
|
||||||
|
);
|
||||||
|
|
||||||
|
$advagg_relocate_css_file_settings = variable_get('advagg_relocate_css_file_settings', array());
|
||||||
|
$files_with_import = advagg_relocate_admin_list_files_with_import();
|
||||||
|
|
||||||
|
if (!empty($files_with_import)) {
|
||||||
|
$domains = explode("\n", $advagg_relocate_css_font_domains);
|
||||||
|
foreach ($files_with_import as $filename => $references) {
|
||||||
|
// Convert filename to form api friendly name.
|
||||||
|
$form_api_filename = str_replace(
|
||||||
|
array('/', '.', '=', '&', ' '),
|
||||||
|
array('__', '--', '_', '-', '-'),
|
||||||
|
$filename
|
||||||
|
);
|
||||||
|
|
||||||
|
// Settings for the all in filename checkbox.
|
||||||
|
$default_value = TRUE;
|
||||||
|
if (isset($advagg_relocate_css_file_settings['all:' . $form_api_filename])) {
|
||||||
|
$default_value = $advagg_relocate_css_file_settings['all:' . $form_api_filename];
|
||||||
|
}
|
||||||
|
$form['global_container']['css']['fonts']['per_file_settings']['advagg_relocate_css_file_settings_all_' . $form_api_filename] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#default_value' => $default_value,
|
||||||
|
'#recommended_value' => TRUE,
|
||||||
|
'#title' => t('All in @filename', array('@filename' => $filename)),
|
||||||
|
);
|
||||||
|
$form_api_html_filename = str_replace(array('__', '--', '_'), '-', $form_api_filename);
|
||||||
|
|
||||||
|
// Fieldset for the checkboxes.
|
||||||
|
$form['global_container']['css']['fonts']['per_file_settings'][$form_api_filename] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => t('@filename', array('@filename' => $filename)),
|
||||||
|
'#states' => array(
|
||||||
|
'disabled' => array(
|
||||||
|
'#edit-advagg-relocate-css-file-settings-all-' . $form_api_html_filename => array('checked' => TRUE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build options and the default enabled settings.
|
||||||
|
$default_values = array();
|
||||||
|
$options = array();
|
||||||
|
foreach ($references as $key => $value) {
|
||||||
|
$include = FALSE;
|
||||||
|
foreach ($domains as $domain) {
|
||||||
|
if (strpos($value, $domain) !== FALSE) {
|
||||||
|
$include = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($include) {
|
||||||
|
$options[$key] = $value;
|
||||||
|
$default_values[$key] = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply saved settings to the checkboxes.
|
||||||
|
if (isset($advagg_relocate_css_file_settings[$form_api_filename])
|
||||||
|
&& is_array($advagg_relocate_css_file_settings[$form_api_filename])
|
||||||
|
) {
|
||||||
|
$default_values = $advagg_relocate_css_file_settings[$form_api_filename];
|
||||||
|
}
|
||||||
|
$form['global_container']['css']['fonts']['per_file_settings'][$form_api_filename]['advagg_relocate_css_file_settings_' . $form_api_filename] = array(
|
||||||
|
'#type' => 'checkboxes',
|
||||||
|
'#options' => $options,
|
||||||
|
'#default_value' => $default_values,
|
||||||
|
);
|
||||||
|
|
||||||
|
// No viable options for this file; remove fieldset and checkboxes.
|
||||||
|
if (empty($options)) {
|
||||||
|
unset($form['global_container']['css']['fonts']['per_file_settings'][$form_api_filename]);
|
||||||
|
unset($form['global_container']['css']['fonts']['per_file_settings']['advagg_relocate_css_file_settings_all_' . $form_api_filename]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the cache bins on submit.
|
||||||
|
$form['#submit'][] = 'advagg_relocate_admin_settings_form_submit';
|
||||||
|
|
||||||
|
return system_settings_form($form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback, clear out the advagg cache bin.
|
||||||
|
*
|
||||||
|
* Also process the advagg_relocate_css_file_settings variable.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms_callback
|
||||||
|
*/
|
||||||
|
function advagg_relocate_admin_settings_form_submit($form, &$form_state) {
|
||||||
|
// Work around PHP bug with $_POST not containing all the data.
|
||||||
|
$alt_post = array();
|
||||||
|
$input = rawurldecode(file_get_contents('php://input'));
|
||||||
|
parse_str($input, $alt_post);
|
||||||
|
$alt_post = drupal_json_decode(str_replace(' ', '+', drupal_json_encode($alt_post)));
|
||||||
|
$diff = advagg_diff_multi($_POST, $alt_post);
|
||||||
|
if (!empty($diff)) {
|
||||||
|
foreach ($diff as $k => $v) {
|
||||||
|
if (isset($form_state['values'][$k])) {
|
||||||
|
$form_state['values'][$k] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset this form to defaults or recommended values; also show what changed.
|
||||||
|
advagg_set_admin_form_defaults_recommended($form_state, 'advagg_relocate_admin_mode');
|
||||||
|
|
||||||
|
// Get settings that start with advagg_relocate_css_file_settings.
|
||||||
|
if (defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 50600) {
|
||||||
|
$vars = array_filter($form_state['values'], '_advagg_relocate_admin_is_css_file_settings', ARRAY_FILTER_USE_KEY);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$vars = array_intersect_key($form_state['values'], array_flip(array_filter(array_keys($form_state['values']), '_advagg_relocate_admin_is_css_file_settings')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset found values.
|
||||||
|
foreach ($vars as $key => $value) {
|
||||||
|
if (isset($form_state['values'][$key])) {
|
||||||
|
unset($form_state['values'][$key]);
|
||||||
|
}
|
||||||
|
// If all is set, remove sub options for that file.
|
||||||
|
if (strpos($key, 'advagg_relocate_css_file_settings_all_') === 0 && !empty($value)) {
|
||||||
|
$sub_key = str_replace('advagg_relocate_css_file_settings_all_', 'advagg_relocate_css_file_settings_', $key);
|
||||||
|
if (isset($vars[$sub_key])) {
|
||||||
|
unset($vars[$sub_key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save under one variable.
|
||||||
|
$saved_values = array();
|
||||||
|
foreach ($vars as $key => $value) {
|
||||||
|
$key = str_replace('advagg_relocate_css_file_settings_all_', 'all:', $key);
|
||||||
|
$key = str_replace('advagg_relocate_css_file_settings_', '', $key);
|
||||||
|
$saved_values[$key] = $value;
|
||||||
|
}
|
||||||
|
$form_state['values']['advagg_relocate_css_file_settings'] = $saved_values;
|
||||||
|
|
||||||
|
// Clear caches.
|
||||||
|
advagg_cache_clear_admin_submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See if the key starts with 'advagg_relocate_css_file_settings'.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* They array key as a string.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE if the input string starts with 'advagg_relocate_css_file_settings'.
|
||||||
|
*/
|
||||||
|
function _advagg_relocate_admin_is_css_file_settings($key) {
|
||||||
|
return strpos($key, 'advagg_relocate_css_file_settings') === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check all CSS files, see if any contains an @import that is external.
|
||||||
|
*
|
||||||
|
* @param array $files
|
||||||
|
* An array of filenames to check.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array of filenames that contain an @import that is external.
|
||||||
|
*/
|
||||||
|
function advagg_relocate_admin_list_files_with_import(array $files = array()) {
|
||||||
|
if (empty($files)) {
|
||||||
|
// Get filename.
|
||||||
|
$results = db_select('advagg_files', 'af')
|
||||||
|
->fields('af', array('filename'))
|
||||||
|
->condition('filetype', 'css')
|
||||||
|
->orderBy('filename', 'ASC')
|
||||||
|
->execute();
|
||||||
|
while ($row = $results->fetchAssoc()) {
|
||||||
|
$files[] = $row['filename'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$files_with_import_statements = array();
|
||||||
|
if (empty($files)) {
|
||||||
|
return $files_with_import_statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_load_include('inc', 'advagg', 'advagg');
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if (!file_exists($file)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Get the file contents.
|
||||||
|
$file_contents = advagg_load_css_stylesheet($file, FALSE);
|
||||||
|
if (strpos($file_contents, '@import') !== FALSE) {
|
||||||
|
$matches = array();
|
||||||
|
preg_match_all('%@import\s*+(?:url\(\s*+)?+[\'"]?+((?:http:\/\/|https:\/\/|\/\/)(?:[^\'"()\s]++))[\'"]?+\s*+\)?+\s*+;%i', $file_contents, $matches);
|
||||||
|
if (!empty($matches[0])) {
|
||||||
|
$output = array();
|
||||||
|
foreach ($matches[1] as $k => $v) {
|
||||||
|
$v = str_replace(array('=', '&', ' '), array('_', '-', '-'), $v);
|
||||||
|
$output[$v] = $matches[0][$k];
|
||||||
|
}
|
||||||
|
|
||||||
|
$files_with_import_statements[$file] = $output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $files_with_import_statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a cache clear button to the advagg operations form.
|
||||||
|
*/
|
||||||
|
function advagg_relocate_admin_form_advagg_admin_operations_form(&$form, &$form_state) {
|
||||||
|
$form['drastic_measures']['flush_relocate_cache'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
'#title' => t('Clear the advagg relocate cache'),
|
||||||
|
'#description' => t('Rarely needed but useful if a download was corrupted.'),
|
||||||
|
);
|
||||||
|
$form['drastic_measures']['flush_relocate_cache']['advagg_flush'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Flush AdvAgg Relocate Cache'),
|
||||||
|
'#submit' => array('advagg_relocate_flush_cache_button'),
|
||||||
|
);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@
|
|||||||
|
name = AdvAgg Relocate
|
||||||
|
description = Inline css import rules; also make any external file local.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg/relocate
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles AdvAgg Relocate installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_install().
|
||||||
|
*/
|
||||||
|
function advagg_relocate_install() {
|
||||||
|
// New install gets a locked admin section.
|
||||||
|
variable_set('advagg_relocate_admin_mode', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the advagg sri module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; Configure advagg settings.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*
|
||||||
|
* @see system_settings_form()
|
||||||
|
*/
|
||||||
|
function advagg_sri_admin_settings_form() {
|
||||||
|
drupal_set_title(t('AdvAgg: Subresource Integrity'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
|
||||||
|
$form = array();
|
||||||
|
$form['advagg_sri'] = array(
|
||||||
|
'#type' => 'radios',
|
||||||
|
'#title' => t('Subresource Integrity Level'),
|
||||||
|
'#default_value' => variable_get('advagg_sri', ADVAGG_SRI),
|
||||||
|
'#options' => array(
|
||||||
|
0 => t('Disabled'),
|
||||||
|
1 => t('SHA-256'),
|
||||||
|
2 => t('SHA-384'),
|
||||||
|
3 => t('SHA-512'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (module_exists('httprl')) {
|
||||||
|
$form['advagg_sri_file_generation'] = array(
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#title' => t('Always output the page with the subresource integrity attribute.'),
|
||||||
|
'#default_value' => variable_get('advagg_sri_file_generation', ADVAGG_SRI_FILE_GENERATION),
|
||||||
|
'#description' => t('If checked - background processes will not usually be used when generating aggregated files; sometimes resulting in a slower page load. Noted though that the page cache is disabled if the all the aggregates do not have the integrity attribute.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the cache bins on submit.
|
||||||
|
$form['#submit'][] = 'advagg_sri_admin_settings_form_submit';
|
||||||
|
|
||||||
|
return system_settings_form($form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback, clears out the advagg cache bin.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms_callback
|
||||||
|
*/
|
||||||
|
function advagg_sri_admin_settings_form_submit($form, &$form_state) {
|
||||||
|
// Clear caches.
|
||||||
|
advagg_cache_clear_admin_submit();
|
||||||
|
}
|
@ -0,0 +1,240 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation sri module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_save_aggregate_alter().
|
||||||
|
*
|
||||||
|
* Save the hash of the file.
|
||||||
|
*/
|
||||||
|
function advagg_sri_advagg_save_aggregate_alter(array &$files_to_save, array $aggregate_settings, array $other_parameters) {
|
||||||
|
// * @param array $files_to_save
|
||||||
|
// * Array($uri => $contents).
|
||||||
|
// * @param array $aggregate_settings
|
||||||
|
// * Array of settings.
|
||||||
|
// * @param array $other_parameters
|
||||||
|
// * Array of containing $files and $type.
|
||||||
|
foreach ($files_to_save as $uri => $contents) {
|
||||||
|
// Skip gzip/brotli files.
|
||||||
|
$ext = strtolower(pathinfo($uri, PATHINFO_EXTENSION));
|
||||||
|
if ($ext === 'gz' || $ext === 'br') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hashes = advagg_sri_compute_hashes($contents);
|
||||||
|
|
||||||
|
// Save to the database.
|
||||||
|
$filename = basename($uri);
|
||||||
|
advagg_sri_set_filename_hashes($filename, $hashes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_build_aggregate_plans_post_alter().
|
||||||
|
*/
|
||||||
|
function advagg_sri_advagg_build_aggregate_plans_post_alter(array &$plans) {
|
||||||
|
// * @param array $plans
|
||||||
|
// * Array of aggregate files.
|
||||||
|
$advagg_sri = variable_get('advagg_sri', ADVAGG_SRI);
|
||||||
|
if (empty($advagg_sri)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($advagg_sri == 1) {
|
||||||
|
$sha_bits = 'sha256';
|
||||||
|
}
|
||||||
|
if ($advagg_sri == 2) {
|
||||||
|
$sha_bits = 'sha384';
|
||||||
|
}
|
||||||
|
if ($advagg_sri == 3) {
|
||||||
|
$sha_bits = 'sha512';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all aggregates.
|
||||||
|
$files = array();
|
||||||
|
$filenames = array();
|
||||||
|
foreach ($plans as $key => $values) {
|
||||||
|
if ($values['type'] !== 'file' || empty($values['cache'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$files[$values['filename']] = $key;
|
||||||
|
$filenames[$values['filepath']] = $values['filename'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup hashes.
|
||||||
|
$hashes = array();
|
||||||
|
if (!empty($filenames)) {
|
||||||
|
$hashes = advagg_sri_get_filenames_hashes($filenames);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set attributes.
|
||||||
|
foreach ($hashes as $filename => $hash) {
|
||||||
|
if (isset($files[$filename]) && isset($plans[$files[$filename]])) {
|
||||||
|
$plans[$files[$filename]]['attributes']['integrity'] = $sha_bits . '-' . $hash[$sha_bits];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_build_aggregate_plans_alter().
|
||||||
|
*/
|
||||||
|
function advagg_sri_advagg_build_aggregate_plans_alter() {
|
||||||
|
if (module_exists('httprl') && variable_get('advagg_use_httprl', ADVAGG_USE_HTTPRL) && variable_get('advagg_sri_file_generation', ADVAGG_SRI_FILE_GENERATION)) {
|
||||||
|
$GLOBALS['conf']['advagg_use_httprl'] = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_missing_root_file().
|
||||||
|
*/
|
||||||
|
function advagg_sri_advagg_missing_root_file($aggregate_filename, $filename, $cache) {
|
||||||
|
// Remove entries from the DB.
|
||||||
|
if (!empty($aggregate_filename) && !empty($cache)) {
|
||||||
|
advagg_sri_del_filename_hashes($aggregate_filename);
|
||||||
|
}
|
||||||
|
// Remove entries from the Cache.
|
||||||
|
$ext = strtolower(pathinfo($aggregate_filename, PATHINFO_EXTENSION));
|
||||||
|
$cid = "advagg:$ext:";
|
||||||
|
cache_clear_all($cid, 'cache_advagg_aggregates', TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_removed_aggregates().
|
||||||
|
*/
|
||||||
|
function advagg_sri_advagg_removed_aggregates($kill_list) {
|
||||||
|
foreach ($kill_list as $uri) {
|
||||||
|
if (is_object($uri) && property_exists($uri, 'uri')) {
|
||||||
|
$temp = $uri->uri;
|
||||||
|
unset($uri);
|
||||||
|
$uri = $temp;
|
||||||
|
}
|
||||||
|
// Skip gzip/brotli files.
|
||||||
|
$ext = strtolower(pathinfo($uri, PATHINFO_EXTENSION));
|
||||||
|
if ($ext === 'gz' || $ext === 'br') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$filename = basename($uri);
|
||||||
|
advagg_sri_del_filename_hashes($filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store settings associated with hash.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* The name of the aggregated filename.
|
||||||
|
* @param array $hashes
|
||||||
|
* An array of SHA hashes of the contents of this filename.
|
||||||
|
*
|
||||||
|
* @return MergeQuery
|
||||||
|
* value from db_merge
|
||||||
|
*/
|
||||||
|
function advagg_sri_set_filename_hashes($filename, array $hashes) {
|
||||||
|
return db_merge('advagg_sri')
|
||||||
|
->key(array('filename' => $filename))
|
||||||
|
->fields(array(
|
||||||
|
'filename' => $filename,
|
||||||
|
'hashes' => serialize($hashes),
|
||||||
|
))
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store settings associated with hash.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* The name of the aggregated filename.
|
||||||
|
*
|
||||||
|
* @return DeleteQuery
|
||||||
|
* value from db_delete
|
||||||
|
*/
|
||||||
|
function advagg_sri_del_filename_hashes($filename) {
|
||||||
|
return db_delete('advagg_sri')
|
||||||
|
->condition('filename', $filename)
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hashes settings.
|
||||||
|
*
|
||||||
|
* @param array $filenames
|
||||||
|
* An array of filenames.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* The hashes for the given filenames.
|
||||||
|
*/
|
||||||
|
function advagg_sri_get_filenames_hashes(array $filenames) {
|
||||||
|
$hashes = array();
|
||||||
|
// Do not use the DB if in development mode.
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) {
|
||||||
|
$rows = db_select('advagg_sri', 'advagg_sri')
|
||||||
|
->fields('advagg_sri', array('filename', 'hashes'))
|
||||||
|
->condition('filename', $filenames, 'IN')
|
||||||
|
->execute();
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$hashes[$row->filename] = unserialize($row->hashes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the hash is not in the database, generate it on demand.
|
||||||
|
$db_filenames = array_keys($hashes);
|
||||||
|
$not_in_db = array_diff($filenames, $db_filenames);
|
||||||
|
foreach ($not_in_db as $file) {
|
||||||
|
$filepath = array_search($file, $filenames);
|
||||||
|
if (is_readable($filepath)) {
|
||||||
|
// Do not use advagg_file_get_contents here.
|
||||||
|
$contents = (string) @file_get_contents($filepath);
|
||||||
|
if (!empty($contents)) {
|
||||||
|
$hashes[$file] = advagg_sri_compute_hashes($contents);
|
||||||
|
advagg_sri_set_filename_hashes($file, $hashes[$file]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to make sure we have all hashes.
|
||||||
|
$all_hashes = array_keys($hashes);
|
||||||
|
$not_hashed = array_diff($filenames, $all_hashes);
|
||||||
|
if (!empty($not_hashed)) {
|
||||||
|
drupal_page_is_cacheable(FALSE);
|
||||||
|
// Disable saving to the cache if a sri is missing.
|
||||||
|
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) > 1) {
|
||||||
|
$GLOBALS['conf']['advagg_cache_level'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!module_exists('httprl') || !variable_get('advagg_use_httprl', ADVAGG_USE_HTTPRL)) {
|
||||||
|
watchdog('advagg_sri', 'The subresource integrity hashes could not be generated for these files: %files', array('%files' => print_r($not_hashed, TRUE)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of hashes.
|
||||||
|
*
|
||||||
|
* @param string $contents
|
||||||
|
* The string to hash.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* The hashes for the given string.
|
||||||
|
*/
|
||||||
|
function advagg_sri_compute_hashes($contents) {
|
||||||
|
// Generate hashes.
|
||||||
|
$hashes = array(
|
||||||
|
'sha512' => base64_encode(hash('sha512', $contents, TRUE)),
|
||||||
|
'sha384' => base64_encode(hash('sha384', $contents, TRUE)),
|
||||||
|
'sha256' => base64_encode(hash('sha256', $contents, TRUE)),
|
||||||
|
);
|
||||||
|
return $hashes;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
name = AdvAgg Subresource Integrity
|
||||||
|
description = Provide the integrity attribute for CSS and JS files.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg/sri
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles Advanced Aggregation installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_install().
|
||||||
|
*/
|
||||||
|
function advagg_sri_install() {
|
||||||
|
module_load_include('install', 'advagg', 'advagg');
|
||||||
|
$tables = array(
|
||||||
|
'advagg_sri' => array(
|
||||||
|
'filename',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$schema = advagg_sri_schema();
|
||||||
|
foreach ($tables as $table => $fields) {
|
||||||
|
// Change utf8_bin to ascii_bin.
|
||||||
|
advagg_install_change_table_collation($table, $fields, 'ascii_bin', $schema[$table]['fields']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_uninstall().
|
||||||
|
*/
|
||||||
|
function advagg_sri_uninstall() {
|
||||||
|
// Remove variables.
|
||||||
|
db_delete('variable')
|
||||||
|
->condition('name', 'advagg_sri%', 'LIKE')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_schema().
|
||||||
|
*/
|
||||||
|
function advagg_sri_schema() {
|
||||||
|
$schema = array();
|
||||||
|
|
||||||
|
// Copy the variable table and change a couple of things.
|
||||||
|
$schema['advagg_sri'] = drupal_get_schema_unprocessed('system', 'variable');
|
||||||
|
|
||||||
|
// Create the filename field.
|
||||||
|
$schema['advagg_sri']['fields']['filename'] = $schema['advagg_sri']['fields']['name'];
|
||||||
|
$schema['advagg_sri']['fields']['filename']['length'] = 143;
|
||||||
|
$schema['advagg_sri']['fields']['filename']['description'] = 'The name of the aggregate.';
|
||||||
|
$schema['advagg_sri']['fields']['filename']['binary'] = TRUE;
|
||||||
|
$schema['advagg_sri']['fields']['filename']['collation'] = 'ascii_bin';
|
||||||
|
$schema['advagg_sri']['fields']['filename']['charset'] = 'ascii';
|
||||||
|
$schema['advagg_sri']['fields']['filename']['mysql_character_set'] = 'ascii';
|
||||||
|
|
||||||
|
// Create the hashes field.
|
||||||
|
$schema['advagg_sri']['fields']['hashes'] = $schema['advagg_sri']['fields']['value'];
|
||||||
|
$schema['advagg_sri']['fields']['hashes']['description'] = 'The hashes associated with this filename.';
|
||||||
|
|
||||||
|
// Set primary key and table description.
|
||||||
|
$schema['advagg_sri']['description'] = 'Stores sha hashes of this file.';
|
||||||
|
$schema['advagg_sri']['primary key'][0] = 'filename';
|
||||||
|
|
||||||
|
// Remove the name and value fileds.
|
||||||
|
unset($schema['advagg_sri']['fields']['name'], $schema['advagg_sri']['fields']['value']);
|
||||||
|
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation sri module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup default_variables
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value of the SHA hash level.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_SRI', 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to force SRI to always be generated.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_SRI_FILE_GENERATION', FALSE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup default_variables".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu().
|
||||||
|
*/
|
||||||
|
function advagg_sri_menu() {
|
||||||
|
$file_path = drupal_get_path('module', 'advagg_sri');
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
$items[$config_path . '/advagg/sri'] = array(
|
||||||
|
'title' => 'Subresource Integrity',
|
||||||
|
'description' => 'Hash aggregated files.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('advagg_sri_admin_settings_form'),
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file path' => $file_path,
|
||||||
|
'file' => 'advagg_sri.admin.inc',
|
||||||
|
'weight' => 10,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_module_implements_alter().
|
||||||
|
*/
|
||||||
|
function advagg_sri_module_implements_alter(&$implementations, $hook) {
|
||||||
|
// Move advagg_sri to the bottom.
|
||||||
|
if ($hook === 'advagg_save_aggregate_alter' && array_key_exists('advagg_sri', $implementations)) {
|
||||||
|
$item = $implementations['advagg_sri'];
|
||||||
|
unset($implementations['advagg_sri']);
|
||||||
|
$implementations['advagg_sri'] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_form_FORM_ID_alter().
|
||||||
|
*/
|
||||||
|
function advagg_sri_form_advagg_admin_settings_form_alter(&$form, $form_state) {
|
||||||
|
// Disable httprl if SRI is set to always on.
|
||||||
|
if (variable_get('advagg_sri_file_generation', ADVAGG_SRI_FILE_GENERATION)) {
|
||||||
|
$form['global']['advagg_use_httprl']['#default_value'] = FALSE;
|
||||||
|
$form['global']['advagg_use_httprl']['#disabled'] = TRUE;
|
||||||
|
$form['global']['advagg_use_httprl']['#description'] = t('The Subresource Integrity submodule has disabled httprl usage. This is the "Always output the page with the subresource integrity attribute" checkbox.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
@ -0,0 +1,569 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the advagg validator module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup default_variables
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup default_variables".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; CSS validator via w3.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* form array.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_css_w3_form() {
|
||||||
|
drupal_set_title(t('AdvAgg: CSS Validator using w3.org'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
|
||||||
|
$form = advagg_validator_admin_form_generator('css', FALSE);
|
||||||
|
|
||||||
|
// Add notice that using this form will submit files to a remote server.
|
||||||
|
$form['notice'] = array(
|
||||||
|
'#markup' => '<div>' . t('Notice: The form below will submit files to the <a href="http://jigsaw.w3.org/css-validator/">http://jigsaw.w3.org/css-validator/</a> service if used.') . '</div>',
|
||||||
|
'#weight' => -1,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; CSS validator via csslint.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* form array.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_css_lint_form() {
|
||||||
|
drupal_set_title(t('AdvAgg: CSS Validator using CSSLINT'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
|
||||||
|
// Tell user to update library if a new version is available.
|
||||||
|
$module_name = 'advagg_validator';
|
||||||
|
$lib_name = 'csslint';
|
||||||
|
list($description) = advagg_get_version_description($lib_name, $module_name, TRUE);
|
||||||
|
$form = array();
|
||||||
|
if (!empty($description)) {
|
||||||
|
$form['advagg_version_msg'] = array(
|
||||||
|
'#markup' => "<p>{$description}</p>",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form += advagg_validator_admin_form_generator('css', TRUE);
|
||||||
|
$library = advagg_get_library('csslint', 'advagg_validator');
|
||||||
|
if (!empty($library['installed'])) {
|
||||||
|
$form['#attached']['libraries_load'][] = array('csslint');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$form['#attached']['js'] = $library['variants']['external']['files']['js'];
|
||||||
|
}
|
||||||
|
$form['#attached']['js'][] = array(
|
||||||
|
'data' => drupal_get_path('module', 'advagg_validator') . '/advagg_validator.js',
|
||||||
|
'type' => 'file',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Comma separated code.
|
||||||
|
// https://github.com/CSSLint/csslint/wiki/Command-line-interface#--ignore
|
||||||
|
$ignore_list = variable_get('advagg_validator_csslint_ignore', ADVAGG_VALIDATOR_CSSLINT_IGNORE);
|
||||||
|
if (is_array($ignore_list)) {
|
||||||
|
$ignore_list = implode(',', $ignore_list);
|
||||||
|
}
|
||||||
|
$form['#attached']['js'][] = array(
|
||||||
|
'data' => array('csslint' => array('ignore' => $ignore_list)),
|
||||||
|
'type' => 'setting',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form builder; CSS validator via jshint.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* form array.
|
||||||
|
*
|
||||||
|
* @ingroup advagg_forms
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_js_hint_form() {
|
||||||
|
drupal_set_title(t('AdvAgg: JavaScript Validator using JSHINT'));
|
||||||
|
advagg_display_message_if_requirements_not_met();
|
||||||
|
|
||||||
|
// Tell user to update library if a new version is available.
|
||||||
|
$module_name = 'advagg_validator';
|
||||||
|
$lib_name = 'jshint';
|
||||||
|
$form = array();
|
||||||
|
list($description) = advagg_get_version_description($lib_name, $module_name, TRUE);
|
||||||
|
if (!empty($description)) {
|
||||||
|
$form['advagg_version_msg'] = array(
|
||||||
|
'#markup' => "<p>{$description}</p>",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = advagg_validator_admin_form_generator('js', TRUE);
|
||||||
|
$library = advagg_get_library('jshint', 'advagg_validator');
|
||||||
|
|
||||||
|
if (!empty($library['installed'])) {
|
||||||
|
libraries_load('jshint');
|
||||||
|
$form['#attached']['libraries_load'][] = array('jshint');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$form['#attached']['js'] = $library['variants']['external']['files']['js'];
|
||||||
|
}
|
||||||
|
$form['#attached']['js'][] = array(
|
||||||
|
'data' => drupal_get_path('module', 'advagg_validator') . '/advagg_validator.js',
|
||||||
|
'type' => 'file',
|
||||||
|
);
|
||||||
|
// Define JSHINT options from https://drupal.org/node/1955232
|
||||||
|
$settings = array(
|
||||||
|
'browser' => TRUE,
|
||||||
|
'curly' => TRUE,
|
||||||
|
'eqeqeq' => TRUE,
|
||||||
|
'forin' => TRUE,
|
||||||
|
'latedef' => TRUE,
|
||||||
|
'newcap' => TRUE,
|
||||||
|
'noarg' => TRUE,
|
||||||
|
'strict' => TRUE,
|
||||||
|
'trailing' => TRUE,
|
||||||
|
'undef' => TRUE,
|
||||||
|
'unused' => TRUE,
|
||||||
|
'predef' => array(
|
||||||
|
'Drupal' => FALSE,
|
||||||
|
'drupalSettings' => FALSE,
|
||||||
|
'domready' => FALSE,
|
||||||
|
'jQuery' => FALSE,
|
||||||
|
'_' => FALSE,
|
||||||
|
'matchMedia' => FALSE,
|
||||||
|
'Backbone' => FALSE,
|
||||||
|
'Modernizr' => FALSE,
|
||||||
|
'VIE' => FALSE,
|
||||||
|
'CKEDITOR' => FALSE,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Comma separated code.
|
||||||
|
// https://jslinterrors.com/
|
||||||
|
$ignore_list = variable_get('advagg_validator_jshint_ignore', ADVAGG_VALIDATOR_JSHINT_IGNORE);
|
||||||
|
if (is_array($ignore_list)) {
|
||||||
|
$ignore_list = implode(',', $ignore_list);
|
||||||
|
}
|
||||||
|
$settings['ignore'] = $ignore_list;
|
||||||
|
|
||||||
|
$form['#attached']['js'][] = array(
|
||||||
|
'data' => array('jshint' => $settings),
|
||||||
|
'type' => 'setting',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan the file system and build a form tree from it.
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* String: css or js.
|
||||||
|
* @param bool $run_client_side
|
||||||
|
* Set to TRUE to run the checking code client side.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* form array.
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_form_generator($type, $run_client_side) {
|
||||||
|
$form = array();
|
||||||
|
module_load_include('inc', 'advagg_validator', 'advagg_validator');
|
||||||
|
|
||||||
|
$files = advagg_validator_scan_all_dirs($type);
|
||||||
|
rsort($files);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$dir = dirname($file);
|
||||||
|
|
||||||
|
// Build the direcotry structure.
|
||||||
|
$levels = explode('/', $dir === '.' ? '{ROOT}' : $dir);
|
||||||
|
$point = &$form;
|
||||||
|
$built = array();
|
||||||
|
foreach ($levels as $key => $value) {
|
||||||
|
// Build direcotry structure.
|
||||||
|
$built[] = $value;
|
||||||
|
$point = &$point[$value];
|
||||||
|
if (!is_array($point)) {
|
||||||
|
$form_api_dirname = str_replace(array('/', '.'), array('__', '--'), $dir);
|
||||||
|
$wrapper = 'advagg-validator-' . $type . '-validator-ajax' . $form_api_dirname;
|
||||||
|
|
||||||
|
$point = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => check_plain($value),
|
||||||
|
'#description' => '<strong>' . t('Directory:') . ' </strong>' . check_plain(implode('/', $built)),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => TRUE,
|
||||||
|
'#weight' => 100,
|
||||||
|
);
|
||||||
|
if (!isset($point['check_all_levels']) && $value !== '{ROOT}') {
|
||||||
|
$point['check_all_levels'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Check this Directory and all Subdirectories: @dirname', array('@dirname' => implode('/', $built))),
|
||||||
|
);
|
||||||
|
if (!$run_client_side) {
|
||||||
|
$point['check_all_levels'] += array(
|
||||||
|
'#submit' => array('advagg_validator_admin_test_advagg_' . $type . '_subdirectory_submit'),
|
||||||
|
'#ajax' => array(
|
||||||
|
'callback' => 'advagg_validator_admin_test_advagg_' . $type . '_subdirectory_callback',
|
||||||
|
'wrapper' => $wrapper,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$point['check_all_levels'] += array(
|
||||||
|
'#attributes' => array(
|
||||||
|
'class' => array('advagg_validator_recursive_' . $type),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$point['break'] = array(
|
||||||
|
'#markup' => '<div></div>',
|
||||||
|
);
|
||||||
|
|
||||||
|
$point['wrapper'] = array(
|
||||||
|
'#markup' => "<div id='" . $wrapper . "' class='results'></div>",
|
||||||
|
'#weight' => 90,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop in button and info if we reached the point where a file lives.
|
||||||
|
if (count($levels) == $key + 1) {
|
||||||
|
$form_api_filename = str_replace(array('/', '.'), array('__', '--'), $file);
|
||||||
|
|
||||||
|
if (!isset($point['check_this_level'])) {
|
||||||
|
$point['check_this_level'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Check this Directory: @dirname', array('@dirname' => $dir)),
|
||||||
|
);
|
||||||
|
if (!$run_client_side) {
|
||||||
|
$point['check_this_level'] += array(
|
||||||
|
'#submit' => array('advagg_validator_admin_test_advagg_' . $type . '_directory_submit'),
|
||||||
|
'#ajax' => array(
|
||||||
|
'callback' => 'advagg_validator_admin_test_advagg_' . $type . '_directory_callback',
|
||||||
|
'wrapper' => $wrapper,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$point['check_this_level'] += array(
|
||||||
|
'#attributes' => array(
|
||||||
|
'class' => array('advagg_validator_' . $type),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isset($point['start'])) {
|
||||||
|
$point['start'] = array(
|
||||||
|
'#markup' => '<br /><strong>' . t('File:') . ' </strong><div class="filenames">',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$point['start'] = array(
|
||||||
|
'#markup' => '<br /><strong>' . t('Files:') . ' </strong><div class="filenames">',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$point[$form_api_filename] = array(
|
||||||
|
'#markup' => check_plain($file) . " </br>\n",
|
||||||
|
);
|
||||||
|
if (!isset($point['end'])) {
|
||||||
|
$point['end'] = array(
|
||||||
|
'#markup' => '</div>',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$point['hidden_' . $form_api_filename] = array(
|
||||||
|
'#type' => 'hidden',
|
||||||
|
'#value' => $file,
|
||||||
|
'#attributes' => array(
|
||||||
|
'class' => array('filenames'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_forms_callback
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback, display file info in a drupal message.
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_test_advagg_css_submit($form, &$form_state) {
|
||||||
|
module_load_include('inc', 'advagg_validator', 'advagg_validator');
|
||||||
|
|
||||||
|
// @codingStandardsIgnoreLine
|
||||||
|
if (!empty($form_state['input']['ajax_page_state'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$info = advagg_validator_test_advagg_css();
|
||||||
|
$info = advagg_validator_admin_hide_good_files($info);
|
||||||
|
if (module_exists('httprl')) {
|
||||||
|
$output = httprl_pr($info);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$output = '<pre>' . check_plain(print_r($info, TRUE)) . '</pre>';
|
||||||
|
}
|
||||||
|
// @ignore security_dsm
|
||||||
|
drupal_set_message($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display file info via ajax callback.
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_test_advagg_css_callback($form, &$form_state) {
|
||||||
|
module_load_include('inc', 'advagg_validator', 'advagg_validator');
|
||||||
|
|
||||||
|
if (!empty($form_state['values']['error'])) {
|
||||||
|
return '<div id="advagg-file-info-ajax"></div>';
|
||||||
|
}
|
||||||
|
$info = advagg_validator_test_advagg_css();
|
||||||
|
if (empty($info)) {
|
||||||
|
return '<div id="advagg-file-info-ajax"></div>';
|
||||||
|
}
|
||||||
|
$info = advagg_validator_admin_hide_good_files($info);
|
||||||
|
if (module_exists('httprl')) {
|
||||||
|
$output = httprl_pr($info);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$output = '<pre>' . print_r($info, TRUE) . '</pre>';
|
||||||
|
}
|
||||||
|
return '<div id="advagg-validator-css-validator-ajax">' . $output . '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display file info in a drupal message.
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_test_advagg_css_directory_submit($form, &$form_state) {
|
||||||
|
// @codingStandardsIgnoreLine
|
||||||
|
if (!empty($form_state['input']['ajax_page_state']) || empty($form_state['values']['op']) || strpos($form_state['values']['op'], t('Check this Directory:')) !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
module_load_include('inc', 'advagg_validator', 'advagg_validator');
|
||||||
|
|
||||||
|
$dir = trim(substr($form_state['values']['op'], strpos($form_state['values']['op'], ':') + 1));
|
||||||
|
$slash_count = substr_count('/' . $dir, '/');
|
||||||
|
|
||||||
|
// Get list of files.
|
||||||
|
$files = array();
|
||||||
|
foreach ($form_state['values'] as $key => $value) {
|
||||||
|
if (strpos($key, 'hidden') === FALSE || strpos($value, $dir) === FALSE || substr_count($value, '/') > $slash_count || ($dir === '.' && substr_count($value, '/') > 0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$files[] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check list.
|
||||||
|
$info = advagg_validator_test_css_files($files);
|
||||||
|
$info = advagg_validator_admin_hide_good_files($info);
|
||||||
|
if (module_exists('httprl')) {
|
||||||
|
$output = httprl_pr($info);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$output = '<pre>' . check_plain(print_r($info, TRUE)) . '</pre>';
|
||||||
|
}
|
||||||
|
// @ignore security_dsm
|
||||||
|
drupal_set_message($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display file info via ajax callback.
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_test_advagg_css_directory_callback($form, &$form_state) {
|
||||||
|
if (empty($form_state['values']['op']) || strpos($form_state['values']['op'], t('Check this Directory:')) !== 0) {
|
||||||
|
return '<div></div>';
|
||||||
|
}
|
||||||
|
module_load_include('inc', 'advagg_validator', 'advagg_validator');
|
||||||
|
|
||||||
|
$dir = trim(substr($form_state['values']['op'], strpos($form_state['values']['op'], ':') + 1));
|
||||||
|
$slash_count = substr_count('/' . $dir, '/');
|
||||||
|
|
||||||
|
// Get list of files.
|
||||||
|
$files = array();
|
||||||
|
foreach ($form_state['values'] as $key => $value) {
|
||||||
|
if (strpos($key, 'hidden') === FALSE || strpos($value, $dir) === FALSE || substr_count($value, '/') > $slash_count || ($dir === '.' && substr_count($value, '/') > 0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$files[] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check list.
|
||||||
|
$info = advagg_validator_test_css_files($files);
|
||||||
|
$wrapper = '';
|
||||||
|
if (empty($info)) {
|
||||||
|
return '<div id="' . $wrapper . '" class="results"></div>';
|
||||||
|
}
|
||||||
|
$info = advagg_validator_admin_hide_good_files($info);
|
||||||
|
if (module_exists('httprl')) {
|
||||||
|
$output = httprl_pr($info);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$output = '<pre>' . print_r($info, TRUE) . '</pre>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$form_api_dirname = str_replace(array('/', '.'), array('__', '--'), $dir);
|
||||||
|
$wrapper = 'advagg-validator-css-validator-ajax' . $form_api_dirname;
|
||||||
|
return '<div id="' . $wrapper . '" class="results">' . $output . '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display file info in a drupal message.
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_test_advagg_css_subdirectory_submit($form, &$form_state) {
|
||||||
|
// @codingStandardsIgnoreLine
|
||||||
|
if (!empty($form_state['input']['ajax_page_state']) || empty($form_state['values']['op']) || strpos($form_state['values']['op'], t('Check this Directory and all Subdirectories:')) !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
module_load_include('inc', 'advagg_validator', 'advagg_validator');
|
||||||
|
|
||||||
|
$dir = trim(substr($form_state['values']['op'], strpos($form_state['values']['op'], ':') + 1));
|
||||||
|
|
||||||
|
// Get list of files.
|
||||||
|
$files = array();
|
||||||
|
foreach ($form_state['values'] as $key => $value) {
|
||||||
|
if (strpos($key, 'hidden') === FALSE || strpos($value, $dir) === FALSE || ($dir === '.' && substr_count($value, '/') > 0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$files[] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check list.
|
||||||
|
$info = advagg_validator_test_css_files($files);
|
||||||
|
$info = advagg_validator_admin_hide_good_files($info);
|
||||||
|
if (module_exists('httprl')) {
|
||||||
|
$output = httprl_pr($info);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$output = '<pre>' . check_plain(print_r($info, TRUE)) . '</pre>';
|
||||||
|
}
|
||||||
|
// @ignore security_dsm
|
||||||
|
drupal_set_message($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display file info via ajax callback.
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_test_advagg_css_subdirectory_callback($form, &$form_state) {
|
||||||
|
if (empty($form_state['values']['op']) || strpos($form_state['values']['op'], t('Check this Directory and all Subdirectories:')) !== 0) {
|
||||||
|
return '<div></div>';
|
||||||
|
}
|
||||||
|
module_load_include('inc', 'advagg_validator', 'advagg_validator');
|
||||||
|
|
||||||
|
$dir = trim(substr($form_state['values']['op'], strpos($form_state['values']['op'], ':') + 1));
|
||||||
|
|
||||||
|
// Get list of files.
|
||||||
|
$files = array();
|
||||||
|
foreach ($form_state['values'] as $key => $value) {
|
||||||
|
if (strpos($key, 'hidden') === FALSE || strpos($value, $dir) === FALSE || ($dir === '.' && substr_count($value, '/') > 0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$files[] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check list.
|
||||||
|
$info = advagg_validator_test_css_files($files);
|
||||||
|
$wrapper = '';
|
||||||
|
if (empty($info)) {
|
||||||
|
return '<div id="' . $wrapper . '" class="results"></div>';
|
||||||
|
}
|
||||||
|
$info = advagg_validator_admin_hide_good_files($info);
|
||||||
|
if (module_exists('httprl')) {
|
||||||
|
$output = httprl_pr($info);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$output = '<pre>' . print_r($info, TRUE) . '</pre>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$form_api_dirname = str_replace(array('/', '.'), array('__', '--'), $dir);
|
||||||
|
$wrapper = 'advagg-validator-css-validator-ajax' . $form_api_dirname;
|
||||||
|
return '<div id="' . $wrapper . '" class="results">' . $output . '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display file info in a drupal message.
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_test_all_css_submit($form, &$form_state) {
|
||||||
|
module_load_include('inc', 'advagg_validator', 'advagg_validator');
|
||||||
|
|
||||||
|
// @codingStandardsIgnoreLine
|
||||||
|
if (!empty($form_state['input']['ajax_page_state'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$info = advagg_validator_test_all_css();
|
||||||
|
$info = advagg_validator_admin_hide_good_files($info);
|
||||||
|
if (module_exists('httprl')) {
|
||||||
|
$output = httprl_pr($info);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$output = '<pre>' . check_plain(print_r($info, TRUE)) . '</pre>';
|
||||||
|
}
|
||||||
|
// @ignore security_dsm
|
||||||
|
drupal_set_message($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display file info via ajax callback.
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_test_all_css_callback($form, &$form_state) {
|
||||||
|
module_load_include('inc', 'advagg_validator', 'advagg_validator');
|
||||||
|
|
||||||
|
if (!empty($form_state['values']['error'])) {
|
||||||
|
return '<div id="advagg-file-info-ajax"></div>';
|
||||||
|
}
|
||||||
|
$info = advagg_validator_test_all_css();
|
||||||
|
if (empty($info)) {
|
||||||
|
return '<div id="advagg-file-info-ajax"></div>';
|
||||||
|
}
|
||||||
|
$info = advagg_validator_admin_hide_good_files($info);
|
||||||
|
if (module_exists('httprl')) {
|
||||||
|
$output = httprl_pr($info);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$output = '<pre>' . print_r($info, TRUE) . '</pre>';
|
||||||
|
}
|
||||||
|
return '<div id="advagg-validator-css-validator-ajax">' . $output . '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_forms_callback".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not display info on a file if it is valid.
|
||||||
|
*
|
||||||
|
* @param array $info
|
||||||
|
* Array containing info about validators and if the file is valid.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* $info array minus data if that file is valid.
|
||||||
|
*/
|
||||||
|
function advagg_validator_admin_hide_good_files(array $info) {
|
||||||
|
$output = array();
|
||||||
|
foreach ($info as $filename => $validators) {
|
||||||
|
foreach ($validators as $v_name => $v_data) {
|
||||||
|
if (!empty($v_data['validity'])) {
|
||||||
|
$output[$filename][$v_name] = 'valid';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$output[$filename][$v_name] = $v_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced CSS/JS aggregation validator module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup advagg_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_advagg_update_github_versions_alter().
|
||||||
|
*/
|
||||||
|
function advagg_validator_advagg_update_github_versions_alter(&$projects) {
|
||||||
|
$projects['csslint'] = array(
|
||||||
|
'callback' => 'advagg_get_github_version',
|
||||||
|
'url' => 'https://cdn.jsdelivr.net/gh/CSSLint/csslint@master/package.json',
|
||||||
|
'variable_name' => 'advagg_validator_github_version_csslint',
|
||||||
|
);
|
||||||
|
$projects['jshint'] = array(
|
||||||
|
'callback' => 'advagg_get_github_version',
|
||||||
|
'url' => 'https://cdn.jsdelivr.net/gh/jshint/jshint@master/package.json',
|
||||||
|
'variable_name' => 'advagg_validator_github_version_jshint',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup advagg_hooks".
|
||||||
|
*/
|
@ -0,0 +1,345 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation validation module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test all css files used by AdvAgg.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array of files with the result from the w3c validator.
|
||||||
|
*/
|
||||||
|
function advagg_validator_test_advagg_css($options = array()) {
|
||||||
|
// Get list of files.
|
||||||
|
$query_files = db_select('advagg_files', 'af')
|
||||||
|
->fields('af', array('filename_hash', 'filename'))
|
||||||
|
->condition('af.filetype', 'css')
|
||||||
|
->orderBy('filename', 'ASC')
|
||||||
|
->execute()
|
||||||
|
->fetchAllKeyed();
|
||||||
|
$files = array_values($query_files);
|
||||||
|
|
||||||
|
// Test and return list.
|
||||||
|
return advagg_validator_test_css_files($files, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively scan the drupal webroot and test all css files.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array of files with the result from the w3c validator.
|
||||||
|
*/
|
||||||
|
function advagg_validator_test_all_css($options = array()) {
|
||||||
|
// Get list of files.
|
||||||
|
$files = advagg_validator_scan_all_dirs('css');
|
||||||
|
|
||||||
|
// Test and return list.
|
||||||
|
return advagg_validator_test_css_files($files, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively scan the drupal webroot for files matching the given extension.
|
||||||
|
*
|
||||||
|
* @param string $ext
|
||||||
|
* Usually css or js.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array of files.
|
||||||
|
*/
|
||||||
|
function advagg_validator_scan_all_dirs($ext) {
|
||||||
|
$options = array(
|
||||||
|
'nodirmask' => '/(\.git|.*\/files*)/',
|
||||||
|
);
|
||||||
|
$output = advagg_validator_file_scan_directory(DRUPAL_ROOT, '/.*\.' . $ext . '$/', $options);
|
||||||
|
$files = array();
|
||||||
|
foreach ($output as $values) {
|
||||||
|
$files[] = str_replace(DRUPAL_ROOT . '/', '', $values->uri);
|
||||||
|
}
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test all given files with the w3c validator.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array of files with the result from the w3c validator.
|
||||||
|
*/
|
||||||
|
function advagg_validator_test_css_files($files, $options = array()) {
|
||||||
|
$output = array();
|
||||||
|
foreach ($files as $filename) {
|
||||||
|
// Skip missing files.
|
||||||
|
if (!file_exists($filename)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file_contents = (string) @advagg_file_get_contents($filename);
|
||||||
|
$lines = file($filename);
|
||||||
|
$filename_hash = drupal_hash_base64($filename);
|
||||||
|
$content_hash = drupal_hash_base64($file_contents);
|
||||||
|
|
||||||
|
// See if this file needs to be scanned.
|
||||||
|
$file_ok = db_select('advagg_validator', 'av')
|
||||||
|
->fields('av', array('content_hash', 'data'))
|
||||||
|
->condition('filename_hash', $filename_hash)
|
||||||
|
->condition('filetype', 'css')
|
||||||
|
->execute()
|
||||||
|
->fetchAllKeyed();
|
||||||
|
if (!empty($file_ok)) {
|
||||||
|
foreach ($file_ok as $content_hash_db => $serialized_data) {
|
||||||
|
if ($content_hash_db == $content_hash) {
|
||||||
|
$output[$filename] = unserialize($serialized_data);
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run jigsaw.w3.org validator.
|
||||||
|
$output[$filename]['jigsaw.w3.org'] = advagg_validator_test_css_file_w3c($filename, $options);
|
||||||
|
|
||||||
|
// Get extra context for errors.
|
||||||
|
if (!empty($output[$filename]['jigsaw.w3.org']['errors'])) {
|
||||||
|
foreach ($output[$filename]['jigsaw.w3.org']['errors'] as &$value) {
|
||||||
|
if (isset($value['line'])) {
|
||||||
|
$value['linedata'] = $lines[($value['line'] - 1)];
|
||||||
|
if (strlen($value['linedata']) > 512) {
|
||||||
|
unset($value['linedata']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($value);
|
||||||
|
}
|
||||||
|
if (!empty($output[$filename]['jigsaw.w3.org']['warnings'])) {
|
||||||
|
foreach ($output[$filename]['jigsaw.w3.org']['warnings'] as &$value) {
|
||||||
|
if (isset($value['line'])) {
|
||||||
|
$value['linedata'] = $lines[$value['line'] - 1];
|
||||||
|
if (strlen($value['linedata']) > 512) {
|
||||||
|
unset($value['linedata']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save data.
|
||||||
|
if (empty($output[$filename]['jigsaw.w3.org']['error'])) {
|
||||||
|
$record = array(
|
||||||
|
'filename' => $filename,
|
||||||
|
'filename_hash' => $filename_hash,
|
||||||
|
'content_hash' => $content_hash,
|
||||||
|
'filetype' => 'css',
|
||||||
|
'data' => serialize($output[$filename]),
|
||||||
|
);
|
||||||
|
db_merge('advagg_validator')
|
||||||
|
->key(array(
|
||||||
|
'filename_hash' => $record['filename_hash'],
|
||||||
|
))
|
||||||
|
->fields($record)
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a CSS file, test to make sure it is valid CSS.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* The name of the file.
|
||||||
|
* @param array $validator_options
|
||||||
|
* List of options to pass along to the CSS Validator.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Info from the w3c server.
|
||||||
|
*/
|
||||||
|
function advagg_validator_test_css_file_w3c($filename, array &$validator_options = array()) {
|
||||||
|
// Get CSS files contents.
|
||||||
|
$validator_options['text'] = (string) @advagg_file_get_contents($filename);
|
||||||
|
|
||||||
|
// Add in defaults.
|
||||||
|
$validator_options += array(
|
||||||
|
'output' => 'soap12',
|
||||||
|
'warning' => '1',
|
||||||
|
'profile' => 'css3',
|
||||||
|
'usermedium' => 'all',
|
||||||
|
'lang' => 'en',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build request URL.
|
||||||
|
// API Documentation http://jigsaw.w3.org/css-validator/api.html
|
||||||
|
$request_url = 'http://jigsaw.w3.org/css-validator/validator';
|
||||||
|
$query = http_build_query($validator_options, '', '&');
|
||||||
|
$url = $request_url . '?' . $query;
|
||||||
|
|
||||||
|
// Send request.
|
||||||
|
$result = drupal_http_request($url);
|
||||||
|
if (!empty($result->data) && $result->code == 200) {
|
||||||
|
// Parse XML and return info.
|
||||||
|
$return = advagg_validator_parse_soap_response_w3c($result->data);
|
||||||
|
$return['filename'] = $filename;
|
||||||
|
if (isset($validator_options['text'])) {
|
||||||
|
unset($validator_options['text']);
|
||||||
|
}
|
||||||
|
$return['options'] = $validator_options;
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array('error' => t('W3C Server did not return a 200 or request data was empty.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an XML response from the validator.
|
||||||
|
*
|
||||||
|
* This function parses a SOAP 1.2 response XML string from the validator.
|
||||||
|
*
|
||||||
|
* @param string $xml
|
||||||
|
* The raw SOAP 1.2 XML response from the validator.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Info from the w3c server.
|
||||||
|
*/
|
||||||
|
function advagg_validator_parse_soap_response_w3c($xml) {
|
||||||
|
$doc = new DOMDocument();
|
||||||
|
$response = array();
|
||||||
|
|
||||||
|
// Try to load soap 1.2 XML response, and suppress warning reports if any.
|
||||||
|
if (!@$doc->loadXML($xml)) {
|
||||||
|
// Could not load the XML document.
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the standard CDATA elements.
|
||||||
|
$cdata = array('uri', 'checkedby', 'csslevel', 'date');
|
||||||
|
foreach ($cdata as $var) {
|
||||||
|
$element = $doc->getElementsByTagName($var);
|
||||||
|
if ($element->length) {
|
||||||
|
$response[$var] = $element->item(0)->nodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the element validity and get errors if not valid.
|
||||||
|
$element = $doc->getElementsByTagName('validity');
|
||||||
|
if ($element->length && $element->item(0)->nodeValue === 'true') {
|
||||||
|
$response['validity'] = TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$response['validity'] = FALSE;
|
||||||
|
$errors = $doc->getElementsByTagName('error');
|
||||||
|
foreach ($errors as $error) {
|
||||||
|
$response['errors'][] = advagg_validator_dom_extractor($error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get warnings.
|
||||||
|
$warnings = $doc->getElementsByTagName('warning');
|
||||||
|
foreach ($warnings as $warning) {
|
||||||
|
$response['warnings'][] = advagg_validator_dom_extractor($warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return response array.
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract info from the DOMNode Object.
|
||||||
|
*
|
||||||
|
* @param object $dom
|
||||||
|
* DOMNode Class.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Key Value pair from the DOM Node.
|
||||||
|
*/
|
||||||
|
function advagg_validator_dom_extractor($dom) {
|
||||||
|
$node = $dom->firstChild;
|
||||||
|
$output = array();
|
||||||
|
do {
|
||||||
|
$text = trim($node->nodeValue);
|
||||||
|
if (!empty($text)) {
|
||||||
|
$key = str_replace('m:', '', $node->nodeName);
|
||||||
|
$output[$key] = $text;
|
||||||
|
}
|
||||||
|
} while ($node = $node->nextSibling);
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all files that match a given mask in a given directory.
|
||||||
|
*
|
||||||
|
* Directories and files beginning with a period are excluded; this
|
||||||
|
* prevents hidden files and directories (such as SVN working directories)
|
||||||
|
* from being scanned.
|
||||||
|
*
|
||||||
|
* @param string $dir
|
||||||
|
* The base directory or URI to scan, without trailing slash.
|
||||||
|
* @param string $mask
|
||||||
|
* The preg_match() regular expression of the files to find.
|
||||||
|
* @param array $options
|
||||||
|
* An associative array of additional options, with the following elements:
|
||||||
|
* - 'nomask': The preg_match() regular expression of the files to ignore.
|
||||||
|
* Defaults to '/(\.\.?|CVS)$/'.
|
||||||
|
* - 'nomask': The preg_match() regular expression of the dirs to ignore.
|
||||||
|
* Defaults to '/(\.git)/'.
|
||||||
|
* - 'callback': The callback function to call for each match. There is no
|
||||||
|
* default callback.
|
||||||
|
* - 'recurse': When TRUE, the directory scan will recurse the entire tree
|
||||||
|
* starting at the provided directory. Defaults to TRUE.
|
||||||
|
* - 'key': The key to be used for the returned associative array of files.
|
||||||
|
* Possible values are 'uri', for the file's URI; 'filename', for the
|
||||||
|
* basename of the file; and 'name' for the name of the file without the
|
||||||
|
* extension. Defaults to 'uri'.
|
||||||
|
* - 'min_depth': Minimum depth of directories to return files from. Defaults
|
||||||
|
* to 0.
|
||||||
|
* @param int $depth
|
||||||
|
* Current depth of recursion. This parameter is only used internally and
|
||||||
|
* should not be passed in.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An associative array (keyed on the chosen key) of objects with 'uri',
|
||||||
|
* 'filename', and 'name' members corresponding to the matching files.
|
||||||
|
*/
|
||||||
|
function advagg_validator_file_scan_directory($dir, $mask, array $options = array(), $depth = 0) {
|
||||||
|
// Merge in defaults.
|
||||||
|
$options += array(
|
||||||
|
'nomask' => '/(\.\.?|CVS)$/',
|
||||||
|
'nodirmask' => '/(\.git)/',
|
||||||
|
'callback' => 0,
|
||||||
|
'recurse' => TRUE,
|
||||||
|
'key' => 'uri',
|
||||||
|
'min_depth' => 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
$options['key'] = in_array($options['key'], array('uri', 'filename', 'name')) ? $options['key'] : 'uri';
|
||||||
|
$files = array();
|
||||||
|
// @ignore druplart_andor_assignment
|
||||||
|
if (is_dir($dir) && $handle = opendir($dir)) {
|
||||||
|
while (FALSE !== ($filename = readdir($handle))) {
|
||||||
|
if (!preg_match($options['nomask'], $filename) && $filename[0] !== '.') {
|
||||||
|
$uri = "$dir/$filename";
|
||||||
|
$uri = file_stream_wrapper_uri_normalize($uri);
|
||||||
|
if (is_dir($uri) && $options['recurse'] && !preg_match($options['nodirmask'], $uri)) {
|
||||||
|
// Give priority to files in this folder by merging them in after any
|
||||||
|
// subdirectory files.
|
||||||
|
$files = array_merge(advagg_validator_file_scan_directory($uri, $mask, $options, $depth + 1), $files);
|
||||||
|
}
|
||||||
|
elseif ($depth >= $options['min_depth'] && preg_match($mask, $filename)) {
|
||||||
|
// Always use this match over anything already set in $files with the
|
||||||
|
// same $$options['key'].
|
||||||
|
$file = new stdClass();
|
||||||
|
$file->uri = $uri;
|
||||||
|
$file->filename = $filename;
|
||||||
|
$file->name = pathinfo($filename, PATHINFO_FILENAME);
|
||||||
|
$key = $options['key'];
|
||||||
|
$files[$file->$key] = $file;
|
||||||
|
if ($options['callback']) {
|
||||||
|
$options['callback']($uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir($handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $files;
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
name = AdvAgg CSS/JS Validator
|
||||||
|
description = Validate the CSS and JS files used in Aggregation for syntax errors.
|
||||||
|
package = Advanced CSS/JS Aggregation
|
||||||
|
core = 7.x
|
||||||
|
dependencies[] = advagg
|
||||||
|
recommends[] = libraries
|
||||||
|
|
||||||
|
configure = admin/config/development/performance/advagg/validator
|
||||||
|
|
||||||
|
; Information added by Drupal.org packaging script on 2020-11-19
|
||||||
|
version = "7.x-2.35"
|
||||||
|
core = "7.x"
|
||||||
|
project = "advagg"
|
||||||
|
datestamp = "1605792717"
|
@ -0,0 +1,180 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Handles AdvAgg Validator installation and upgrade tasks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_install().
|
||||||
|
*/
|
||||||
|
function advagg_validator_install() {
|
||||||
|
module_load_include('install', 'advagg', 'advagg');
|
||||||
|
$tables = array(
|
||||||
|
'advagg_validator' => array(
|
||||||
|
'filename_hash',
|
||||||
|
'content_hash',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$schema = advagg_validator_schema();
|
||||||
|
foreach ($tables as $table => $fields) {
|
||||||
|
// Change utf8_bin to ascii_bin.
|
||||||
|
advagg_install_change_table_collation($table, $fields, 'ascii_bin', $schema[$table]['fields']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_requirements().
|
||||||
|
*/
|
||||||
|
function advagg_validator_requirements($phase) {
|
||||||
|
$requirements = array();
|
||||||
|
// Ensure translations don't break at install time.
|
||||||
|
$t = get_t();
|
||||||
|
|
||||||
|
// If not at runtime, return here.
|
||||||
|
if ($phase !== 'runtime') {
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check version.
|
||||||
|
$module_name = 'advagg_validator';
|
||||||
|
$lib_name = 'csslint';
|
||||||
|
list($description, $info) = advagg_get_version_description($lib_name, $module_name, TRUE);
|
||||||
|
if (!empty($description)) {
|
||||||
|
$requirements["{$module_name}_{$lib_name}_updates"] = array(
|
||||||
|
'title' => $t('@module_name', array('@module_name' => $info['name'])),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('The @name library needs to be updated.', array('@name' => $lib_name)),
|
||||||
|
'description' => $description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$lib_name = 'jshint';
|
||||||
|
list($description, $info) = advagg_get_version_description($lib_name, $module_name, TRUE);
|
||||||
|
if (!empty($description)) {
|
||||||
|
$requirements["{$module_name}_{$lib_name}_updates"] = array(
|
||||||
|
'title' => $t('@module_name', array('@module_name' => $info['name'])),
|
||||||
|
'severity' => REQUIREMENT_WARNING,
|
||||||
|
'value' => $t('The @name library needs to be updated.', array('@name' => $lib_name)),
|
||||||
|
'description' => $description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_schema().
|
||||||
|
*/
|
||||||
|
function advagg_validator_schema() {
|
||||||
|
// Create database table.
|
||||||
|
$schema['advagg_validator'] = array(
|
||||||
|
'description' => 'CSS/JS files that have been ran through a validator, checking syntax etc.',
|
||||||
|
'fields' => array(
|
||||||
|
'filename' => array(
|
||||||
|
'description' => 'Path and filename of the file relative to Drupal webroot.',
|
||||||
|
'type' => 'text',
|
||||||
|
'size' => 'normal',
|
||||||
|
'not null' => TRUE,
|
||||||
|
),
|
||||||
|
'filename_hash' => array(
|
||||||
|
'description' => 'Hash of path and filename. Used to join tables and for lookup.',
|
||||||
|
'type' => 'char',
|
||||||
|
'length' => 43,
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => '',
|
||||||
|
'binary' => TRUE,
|
||||||
|
'collation' => 'ascii_bin',
|
||||||
|
'charset' => 'ascii',
|
||||||
|
),
|
||||||
|
'content_hash' => array(
|
||||||
|
'description' => 'Hash of the file content. Used to see if the file has changed.',
|
||||||
|
'type' => 'char',
|
||||||
|
'length' => 43,
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => '',
|
||||||
|
'binary' => TRUE,
|
||||||
|
'collation' => 'ascii_bin',
|
||||||
|
'charset' => 'ascii',
|
||||||
|
),
|
||||||
|
'filetype' => array(
|
||||||
|
'description' => 'Filetype.',
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => 8,
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => '',
|
||||||
|
'binary' => TRUE,
|
||||||
|
),
|
||||||
|
'data' => array(
|
||||||
|
'description' => 'Validation data about this file.',
|
||||||
|
'type' => 'blob',
|
||||||
|
'not null' => TRUE,
|
||||||
|
'size' => 'big',
|
||||||
|
'translatable' => TRUE,
|
||||||
|
'serialize' => TRUE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'indexes' => array(
|
||||||
|
'content_hash' => array('content_hash'),
|
||||||
|
'filetype' => array('filetype'),
|
||||||
|
),
|
||||||
|
'primary key' => array('filename_hash'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the schema making the varchar columns utf8_bin in MySQL.
|
||||||
|
*/
|
||||||
|
function advagg_validator_update_7201(&$sandbox) {
|
||||||
|
$db_type = Database::getConnection()->databaseType();
|
||||||
|
$tables_altered = array();
|
||||||
|
if ($db_type === 'mysql') {
|
||||||
|
$results = db_query("SHOW FULL FIELDS FROM {advagg_validator}")->fetchAllAssoc('Field');
|
||||||
|
foreach ($results as $row) {
|
||||||
|
if (stripos($row->Type, 'varchar') !== FALSE && $row->Collation !== 'utf8_bin') {
|
||||||
|
db_query("ALTER TABLE {advagg_validator} MODIFY {$row->Field} {$row->Type} CHARACTER SET utf8 COLLATE utf8_bin");
|
||||||
|
$tables_altered['advagg_validator'][] = $row->Field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($tables_altered)) {
|
||||||
|
return t('Nothing needed to happen in AdvAgg Validator.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return t('The following columns inside of these database tables where converted to utf8_bin: <br />@data', array('@data' => print_r($tables_altered, TRUE)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update schema making the varchar columns char. Change utf8_bin to ascii_bin.
|
||||||
|
*/
|
||||||
|
function advagg_validator_update_7202(&$sandbox) {
|
||||||
|
module_load_include('install', 'advagg', 'advagg');
|
||||||
|
$tables = array(
|
||||||
|
'advagg_validator' => array(
|
||||||
|
'filename_hash',
|
||||||
|
'content_hash',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$schema = advagg_validator_schema();
|
||||||
|
foreach ($tables as $table => $fields) {
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
// Change varchar to char.
|
||||||
|
db_change_field($table, $field, $field, $schema[$table]['fields'][$field]);
|
||||||
|
}
|
||||||
|
// Change utf8_bin to ascii_bin.
|
||||||
|
advagg_install_change_table_collation($table, $fields, 'ascii_bin', $schema[$table]['fields']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
@ -0,0 +1,199 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Run JSHINT in the browser against the servers JS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global jQuery:false */
|
||||||
|
/* global Drupal:false */
|
||||||
|
/* global JSHINT:false */
|
||||||
|
/* global CSSLint:false */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Have clicks to advagg_validator_js classes run JSHINT clientside.
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
'use strict';
|
||||||
|
Drupal.behaviors.advagg_validator_js_simple = {
|
||||||
|
attach: function (context, settings) {
|
||||||
|
$('.advagg_validator_js', context).click(function (context) {
|
||||||
|
// Get Results Div.
|
||||||
|
var results = $(this).siblings('.results');
|
||||||
|
// Clear out the results.
|
||||||
|
$(results).html('');
|
||||||
|
// Loop over each filename.
|
||||||
|
$.each($(this).siblings('.filenames'), function () {
|
||||||
|
var filename = $(this).val();
|
||||||
|
if (filename) {
|
||||||
|
try {
|
||||||
|
var t = new Date().getTime();
|
||||||
|
var x = jQuery.ajax({
|
||||||
|
url: settings.basePath + filename + '?t=' + t,
|
||||||
|
dataType: 'text',
|
||||||
|
async: false
|
||||||
|
});
|
||||||
|
if (!JSHINT(x.responseText, Drupal.settings.jshint, Drupal.settings.jshint.predef)) {
|
||||||
|
$(results).append('<p><h4>' + filename + '</h4><ul>');
|
||||||
|
for (var i = 0; i < JSHINT.errors.length; i++) {
|
||||||
|
var ignore = (Drupal.settings.jshint && Drupal.settings.jshint.ignore) ? Drupal.settings.jshint.ignore.split(',') : [];
|
||||||
|
if (ignore.indexOf(JSHINT.errors[i].code) === -1) {
|
||||||
|
var w = JSHINT.errors[i].reason + ' (line ' + JSHINT.errors[i].line + ', col ' + JSHINT.errors[i].character + ', rule ' + JSHINT.errors[i].code + ')';
|
||||||
|
$(results).append('<li class="' + JSHINT.errors[i].id.replace(/[()]/g, '') + '">' + w.replace(/ /g, ' ') + '</li>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$(results).append('</ul></p>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
$(results).append(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}(jQuery));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Have clicks to advagg_validator_recursive_js classes run JSHINT clientside.
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
'use strict';
|
||||||
|
Drupal.behaviors.advagg_validator_js_recursive = {
|
||||||
|
attach: function (context, settings) {
|
||||||
|
$('.advagg_validator_recursive_js', context).click(function (context) {
|
||||||
|
// Get Results Div.
|
||||||
|
var results = $(this).siblings('.results');
|
||||||
|
// Clear out the results.
|
||||||
|
$(results).html('');
|
||||||
|
// Loop over each filename.
|
||||||
|
$.each($(this).parent().find('.filenames'), function () {
|
||||||
|
var filename = $(this).val();
|
||||||
|
if (filename) {
|
||||||
|
try {
|
||||||
|
var t = new Date().getTime();
|
||||||
|
var x = jQuery.ajax({
|
||||||
|
url: settings.basePath + filename + '?t=' + t,
|
||||||
|
dataType: 'text',
|
||||||
|
async: false
|
||||||
|
});
|
||||||
|
if (!JSHINT(x.responseText, Drupal.settings.jshint, Drupal.settings.jshint.predef)) {
|
||||||
|
$(results).append('<p><h4>' + filename + '</h4><ul>');
|
||||||
|
for (var i = 0; i < JSHINT.errors.length; i++) {
|
||||||
|
var ignore = (Drupal.settings.jshint && Drupal.settings.jshint.ignore) ? Drupal.settings.jshint.ignore.split(',') : [];
|
||||||
|
if (ignore.indexOf(JSHINT.errors[i].code) === -1) {
|
||||||
|
var w = JSHINT.errors[i].reason + ' (line ' + JSHINT.errors[i].line + ', col ' + JSHINT.errors[i].character + ', rule ' + JSHINT.errors[i].code + ')';
|
||||||
|
$(results).append('<li class="' + JSHINT.errors[i].id.replace(/[()]/g, '') + '">' + w.replace(/ /g, ' ') + '</li>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$(results).append('</ul></p>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
$(results).append(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}(jQuery));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Have clicks to advagg_validator_css classes run CSSLint clientside.
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
'use strict';
|
||||||
|
Drupal.behaviors.advagg_validator_css_simple = {
|
||||||
|
attach: function (context, settings) {
|
||||||
|
$('.advagg_validator_css', context).click(function (context) {
|
||||||
|
// Get Results Div.
|
||||||
|
var results = $(this).siblings('.results');
|
||||||
|
// Clear out the results.
|
||||||
|
$(results).html('');
|
||||||
|
// Loop over each filename.
|
||||||
|
$.each($(this).siblings('.filenames'), function () {
|
||||||
|
var filename = $(this).val();
|
||||||
|
if (filename) {
|
||||||
|
try {
|
||||||
|
var t = new Date().getTime();
|
||||||
|
var x = jQuery.ajax({
|
||||||
|
url: settings.basePath + filename + '?t=' + t,
|
||||||
|
dataType: 'text',
|
||||||
|
async: false
|
||||||
|
});
|
||||||
|
|
||||||
|
var y = CSSLint.verify(x.responseText);
|
||||||
|
var z = y.messages;
|
||||||
|
$(results).append('<p><h4>' + filename + '</h4><ul>');
|
||||||
|
for (var i = 0, len = z.length; i < len; i++) {
|
||||||
|
var ignore = (Drupal.settings.csslint && Drupal.settings.csslint.ignore) ? Drupal.settings.csslint.ignore.split(',') : [];
|
||||||
|
if (ignore.indexOf(z[i].rule.id) === -1) {
|
||||||
|
var w = z[i].message + ' (line ' + z[i].line + ', col ' + z[i].col + ', rule ' + z[i].rule.id + ')';
|
||||||
|
$(results).append('<li class="' + z[i].type + '">' + w.replace(/ /g, ' ') + '</li>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$(results).append('</ul></p>');
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
$(results).append(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}(jQuery));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Have clicks to advagg_validator_recursive_css classes run CSSLint clientside.
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
'use strict';
|
||||||
|
Drupal.behaviors.advagg_validator_css_recursive = {
|
||||||
|
attach: function (context, settings) {
|
||||||
|
$('.advagg_validator_recursive_css', context).click(function (context) {
|
||||||
|
// Get Results Div.
|
||||||
|
var results = $(this).siblings('.results');
|
||||||
|
// Clear out the results.
|
||||||
|
$(results).html('');
|
||||||
|
// Loop over each filename.
|
||||||
|
$.each($(this).parent().find('.filenames'), function () {
|
||||||
|
var filename = $(this).val();
|
||||||
|
if (filename) {
|
||||||
|
try {
|
||||||
|
var t = new Date().getTime();
|
||||||
|
var x = jQuery.ajax({
|
||||||
|
url: settings.basePath + filename + '?t=' + t,
|
||||||
|
dataType: 'text',
|
||||||
|
async: false
|
||||||
|
});
|
||||||
|
|
||||||
|
var y = CSSLint.verify(x.responseText);
|
||||||
|
var z = y.messages;
|
||||||
|
$(results).append('<p><h4>' + filename + '</h4><ul>');
|
||||||
|
for (var i = 0, len = z.length; i < len; i++) {
|
||||||
|
var ignore = (Drupal.settings.csslint && Drupal.settings.csslint.ignore) ? Drupal.settings.csslint.ignore.split(',') : [];
|
||||||
|
if (ignore.indexOf(z[i].rule.id) === -1) {
|
||||||
|
var w = z[i].message + ' (line ' + z[i].line + ', col ' + z[i].col + ', rule ' + z[i].rule.id + ')';
|
||||||
|
$(results).append('<li class="' + z[i].type + '">' + w.replace(/ /g, ' ') + '</li>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$(results).append('</ul></p>');
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
$(results).append(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}(jQuery));
|
@ -0,0 +1,204 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Advanced aggregation validation module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup default_variables
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default css lint ignore settings.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_VALIDATOR_CSSLINT_IGNORE', '');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default js hint ignore settings.
|
||||||
|
*/
|
||||||
|
define('ADVAGG_VALIDATOR_JSHINT_IGNORE', '');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup default_variables".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu().
|
||||||
|
*/
|
||||||
|
function advagg_validator_menu() {
|
||||||
|
// Make sure the advagg_admin_config_root_path() function is available.
|
||||||
|
drupal_load('module', 'advagg');
|
||||||
|
|
||||||
|
$file_path = drupal_get_path('module', 'advagg_validator');
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
$items = array();
|
||||||
|
|
||||||
|
$items[$config_path . '/advagg/validate_css_w3'] = array(
|
||||||
|
'title' => 'Validate CSS via W3',
|
||||||
|
'description' => 'Validate CSS syntax using w3.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('advagg_validator_admin_css_w3_form'),
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file path' => $file_path,
|
||||||
|
'file' => 'advagg_validator.admin.inc',
|
||||||
|
'weight' => 10,
|
||||||
|
);
|
||||||
|
$items[$config_path . '/advagg/validate_css_lint'] = array(
|
||||||
|
'title' => 'Validate CSS via Lint',
|
||||||
|
'description' => 'Validate CSS syntax using CSS Lint.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('advagg_validator_admin_css_lint_form'),
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file path' => $file_path,
|
||||||
|
'file' => 'advagg_validator.admin.inc',
|
||||||
|
'weight' => 11,
|
||||||
|
);
|
||||||
|
$items[$config_path . '/advagg/validate_js_hint'] = array(
|
||||||
|
'title' => 'Validate JS via Hint',
|
||||||
|
'description' => 'Validate JavaScript syntax using JSHint.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('advagg_validator_admin_js_hint_form'),
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file path' => $file_path,
|
||||||
|
'file' => 'advagg_validator.admin.inc',
|
||||||
|
'weight' => 12,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu_alter().
|
||||||
|
*/
|
||||||
|
function advagg_validator_menu_alter(&$items) {
|
||||||
|
// Make sure the advagg_admin_config_root_path() function is available.
|
||||||
|
drupal_load('module', 'advagg');
|
||||||
|
$config_path = advagg_admin_config_root_path();
|
||||||
|
|
||||||
|
if (!isset($items[$config_path . '/advagg'])) {
|
||||||
|
// If the advagg module is not enabled, redirect the /advagg path to
|
||||||
|
// /advagg/validate_css_w3.
|
||||||
|
$items[$config_path . '/advagg'] = array(
|
||||||
|
'title' => 'Advanced CSS/JS Aggregation',
|
||||||
|
'type' => MENU_LOCAL_TASK,
|
||||||
|
'weight' => 1,
|
||||||
|
'description' => $items[$config_path . '/advagg/validate_css_w3']['description'],
|
||||||
|
'page callback' => 'drupal_goto',
|
||||||
|
'page arguments' => array($config_path . '/advagg/validate_css_w3'),
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!isset($items[$config_path . '/default'])) {
|
||||||
|
// Make sure the performance page has a default path.
|
||||||
|
$items[$config_path . '/default'] = array(
|
||||||
|
'title' => 'Performance',
|
||||||
|
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||||
|
'file path' => drupal_get_path('module', 'system'),
|
||||||
|
'weight' => -10,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup hooks".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup 3rd_party_hooks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_libraries_info().
|
||||||
|
*/
|
||||||
|
function advagg_validator_libraries_info() {
|
||||||
|
$libraries['csslint'] = array(
|
||||||
|
'name' => 'csslint',
|
||||||
|
'vendor url' => 'https://github.com/CSSLint/csslint',
|
||||||
|
'download url' => 'https://github.com/CSSLint/csslint/archive/master.zip',
|
||||||
|
'version arguments' => array(
|
||||||
|
'file' => 'package.json',
|
||||||
|
'pattern' => '/"version":\\s+"([0-9\.]+)"/',
|
||||||
|
'lines' => 10,
|
||||||
|
),
|
||||||
|
'remote' => array(
|
||||||
|
'callback' => 'advagg_get_github_version_json',
|
||||||
|
'url' => 'https://cdn.jsdelivr.net/gh/CSSLint/csslint@master/package.json',
|
||||||
|
),
|
||||||
|
'files' => array(
|
||||||
|
'js' => array(
|
||||||
|
'dist/csslint.js' => array(
|
||||||
|
'type' => 'file',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'variants' => array(),
|
||||||
|
);
|
||||||
|
// Get the latest tagged version for external file loading.
|
||||||
|
$version = advagg_get_remote_libraries_version('csslint', $libraries['csslint']);
|
||||||
|
$libraries['csslint']['variants'] += array(
|
||||||
|
'external' => array(
|
||||||
|
'files' => array(
|
||||||
|
'js' => array(
|
||||||
|
"https://cdn.jsdelivr.net/gh/CSSLint/csslint@v{$version}/dist/csslint.js" => array(
|
||||||
|
'type' => 'external',
|
||||||
|
'data' => "https://cdn.jsdelivr.net/gh/CSSLint/csslint@v{$version}/dist/csslint.js",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$libraries['jshint'] = array(
|
||||||
|
'name' => 'jshint',
|
||||||
|
'vendor url' => 'https://github.com/jshint/jshint',
|
||||||
|
'download url' => 'https://github.com/jshint/jshint/archive/master.zip',
|
||||||
|
'version arguments' => array(
|
||||||
|
'file' => 'package.json',
|
||||||
|
'pattern' => '/"version":\\s+"([0-9\.]+)"/',
|
||||||
|
'lines' => 10,
|
||||||
|
),
|
||||||
|
'remote' => array(
|
||||||
|
'callback' => 'advagg_get_github_version_json',
|
||||||
|
'url' => 'https://cdn.jsdelivr.net/gh/jshint/jshint@master/package.json',
|
||||||
|
),
|
||||||
|
'files' => array(
|
||||||
|
'js' => array(
|
||||||
|
'dist/jshint.js' => array(
|
||||||
|
'type' => 'file',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'variants' => array(),
|
||||||
|
);
|
||||||
|
// Get the latest tagged version for external file loading.
|
||||||
|
$version = advagg_get_remote_libraries_version('jshint', $libraries['jshint']);
|
||||||
|
$libraries['jshint']['variants'] += array(
|
||||||
|
'external' => array(
|
||||||
|
'files' => array(
|
||||||
|
'js' => array(
|
||||||
|
"https://cdn.jsdelivr.net/gh/jshint/jshint@{$version}/dist/jshint.js" => array(
|
||||||
|
'type' => 'external',
|
||||||
|
'data' => "https://cdn.jsdelivr.net/gh/jshint/jshint@{$version}/dist/jshint.js",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "addtogroup 3rd_party_hooks".
|
||||||
|
*/
|
19
frontend/drupal/sites/all/modules/advagg/composer.json
Normal file
19
frontend/drupal/sites/all/modules/advagg/composer.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "drupal/advagg",
|
||||||
|
"description": "Improved aggregation of CSS/JS files to speed up page load times and prevent 404s.",
|
||||||
|
"type": "drupal-module",
|
||||||
|
"license": "GPL-2.0+",
|
||||||
|
"homepage": "https://drupal.org/project/advagg",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Mike Carper (mikeytown2)",
|
||||||
|
"homepage": "https://www.drupal.org/u/mikeytown2",
|
||||||
|
"role": "Creator, Maintainer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://drupal.org/project/issues/advagg",
|
||||||
|
"irc": "irc://irc.freenode.org/drupal-contribute",
|
||||||
|
"source": "https://cgit.drupalcode.org/advagg"
|
||||||
|
}
|
||||||
|
}
|
1721
frontend/drupal/sites/all/modules/advagg/tests/advagg.test
Normal file
1721
frontend/drupal/sites/all/modules/advagg/tests/advagg.test
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,89 @@
|
|||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
/* https://www.drupal.org/node/1514182#comment-9532431 */
|
||||||
|
home {
|
||||||
|
background: url('geo-md-webfont.eot?#iefix');
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url( 'geo-md-webfont.eot?#iefix' );
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url("geo-md-webfont.eot?#iefix");
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url(geo-md-webfont.eot?#iefix);
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url( geo-md-webfont.eot?#iefix );
|
||||||
|
}
|
||||||
|
/* https://www.drupal.org/node/2685177 */
|
||||||
|
home {
|
||||||
|
background: url('fostbook-webfont.svg#Foundry Sterling Book');
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url( 'fostbook-webfont.svg#Foundry Sterling Book' );
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url("fostbook-webfont.svg#Foundry Sterling Book");
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url(fostbook-webfont.svg#Foundry Sterling Book);
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url( fostbook-webfont.svg#Foundry Sterling Book );
|
||||||
|
}
|
||||||
|
/* https://www.drupal.org/node/2362643 */
|
||||||
|
home {
|
||||||
|
background: url(#SVGID_1_);
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url(#a);
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url('#a');
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url("#a");
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url( "#a" );
|
||||||
|
}
|
||||||
|
home {
|
||||||
|
background: url( #a );
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%222000%22%20height%3D%2221%22%20viewBox%3D%22-423.5%200%202000%2021%22%20enable-background%3D%22new%20-423.5%200%202000%2021%22%3E%3ClinearGradient%20id%3D%22SVGID_1_%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%221393.788%22%20y1%3D%22404.834%22%20x2%3D%221395.121%22%20y2%3D%22404.834%22%20gradientTransform%3D%22matrix(1500%200%200%20-538%20-2091105%20217811)%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23fff%22%2F%3E%3Cstop%20offset%3D%22.55%22%20stop-color%3D%22%23fff%22%2F%3E%3Cstop%20offset%3D%22.8%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3C%2FlinearGradient%3E%3Cpath%20fill%3D%22url(%23SVGID_1_)%22%20d%3D%22M-423.
|
||||||
|
5%200h2000v21h-2000V0z%22%2F%3E%3C%2Fsvg%3E');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Example from https://www.w3.org/TR/CSS2/syndata.html#rule-sets */
|
||||||
|
q[example="public class foo\
|
||||||
|
{\
|
||||||
|
private int x;\
|
||||||
|
\
|
||||||
|
foo(int x) {\
|
||||||
|
this.x = x;\
|
||||||
|
}\
|
||||||
|
\
|
||||||
|
}"] { color: red }
|
||||||
|
|
||||||
|
/* A pseudo selector with essential whitespace wrapped in quotes. */
|
||||||
|
q[style*="quotes: none"] {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
q[style*='quotes: none'] {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
q:after {
|
||||||
|
content: ": colon & escaped double \" quotes \".";
|
||||||
|
}
|
||||||
|
|
||||||
|
q:after {
|
||||||
|
content: ' (brackets & escaped single \' quotes \') ';
|
||||||
|
}
|
||||||
|
|
||||||
|
q:after {
|
||||||
|
content: "I'm Quote";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
home{background:url(tests/css_test_files/geo-md-webfont.eot?#iefix);}home{background:url(tests/css_test_files/geo-md-webfont.eot?#iefix);}home{background:url(tests/css_test_files/geo-md-webfont.eot?#iefix);}home{background:url(tests/css_test_files/geo-md-webfont.eot?#iefix);}home{background:url(tests/css_test_files/geo-md-webfont.eot?#iefix);}home{background:url(tests/css_test_files/fostbook-webfont.svg#Foundry Sterling Book);}home{background:url(tests/css_test_files/fostbook-webfont.svg#Foundry Sterling Book);}home{background:url(tests/css_test_files/fostbook-webfont.svg#Foundry Sterling Book);}home{background:url(tests/css_test_files/fostbook-webfont.svg#Foundry Sterling Book);}home{background:url(tests/css_test_files/fostbook-webfont.svg#Foundry Sterling Book);}home{background:url(#SVGID_1_);}home{background:url(#a);}home{background:url('#a');}home{background:url("#a");}home{background:url("#a");}home{background:url(#a);}html{background-image:url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%222000%22%20height%3D%2221%22%20viewBox%3D%22-423.5%200%202000%2021%22%20enable-background%3D%22new%20-423.5%200%202000%2021%22%3E%3ClinearGradient%20id%3D%22SVGID_1_%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%221393.788%22%20y1%3D%22404.834%22%20x2%3D%221395.121%22%20y2%3D%22404.834%22%20gradientTransform%3D%22matrix(1500%200%200%20-538%20-2091105%20217811)%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23fff%22%2F%3E%3Cstop%20offset%3D%22.55%22%20stop-color%3D%22%23fff%22%2F%3E%3Cstop%20offset%3D%22.8%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%220%22%2F%3E%3C%2FlinearGradient%3E%3Cpath%20fill%3D%22url(%23SVGID_1_)%22%20d%3D%22M-423.
|
||||||
|
5%200h2000v21h-2000V0z%22%2F%3E%3C%2Fsvg%3E');}q[example="public class foo\
|
||||||
|
{\
|
||||||
|
private int x;\
|
||||||
|
\
|
||||||
|
foo(int x) {\
|
||||||
|
this.x = x;\
|
||||||
|
}\
|
||||||
|
\
|
||||||
|
}"]{color:red}q[style*="quotes: none"]{quotes:none;}q[style*='quotes: none']{quotes:none;}q:after{content:": colon & escaped double \" quotes \".";}q:after{content:' (brackets & escaped single \' quotes \') ';}q:after{content:"I'm Quote";}
|
@ -0,0 +1,7 @@
|
|||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
@charset "UTF-8";html{font-family:"sans-serif";}
|
||||||
|
|
||||||
|
/* Test data with multi-byte characters */
|
||||||
|
.copyright-©, a-with-tilde-ã, plus-minus-± {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
html{font-family:"sans-serif";}.copyright-©,a-with-tilde-ã,plus-minus-±{font-weight:bold;}
|
@ -0,0 +1,3 @@
|
|||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
@charset 'UTF-8';
|
||||||
|
html{font-family:"sans-serif";}
|
@ -0,0 +1,2 @@
|
|||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
html{font-family:"sans-serif";}
|
@ -0,0 +1,2 @@
|
|||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
@charset "UTF-8";html{font-family:"sans-serif";}
|
@ -0,0 +1,2 @@
|
|||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
html{font-family:"sans-serif";}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,2 @@
|
|||||||
|
/* @codingStandardsIgnoreFile */
|
||||||
|
.test1{display:block;}img[style*="float: right"]{padding-left:5px;}html .clear-block{height:1%;}.clear-block{display:block;font:italic bold 12px/30px Georgia,serif;}.test2{display:block;}.bkslshv1{background-color:#c00;}.test3{display:block;}.test4{display:block;}.comment-in-double-quotes:before{content:"/* ";}.this_rule_must_stay{color:#f00;background-color:#fff;}.comment-in-double-quotes:after{content:" */";}.comment-in-single-quotes:before{content:'/*';}.this_rule_must_stay{color:#f00;background-color:#fff;}.comment-in-single-quotes:after{content:'*/';}.comment-in-mixed-quotes:before{content:'"/*"';}.this_rule_must_stay{color:#f00;background-color:#fff;}.comment-in-mixed-quotes:after{content:"'*/'";}.comment-in-quotes-with-escaped:before{content:'/* \" \' */';}.this_rule_must_stay{color:#f00;background-color:#fff;}.comment-in-quotes-with-escaped:after{content:"*/ \" \ '";}
|
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user