Plugin Directory

Changeset 3721511


Ignore:
Timestamp:
09/30/2026 02:54:33 PM (3 weeks ago)
Author:
DaanvandenBergh
Message:

Update to version 6.3.12 from GitHub

Location:
host-webfonts-local
Files:
44 edited
1 copied

Legend:

Unmodified
Added
Removed
  • host-webfonts-local/tags/6.3.12/assets/css/omgf-admin.css

    r3489128 r3721511  
    563563        width: 96px;
    564564}
     565
     566/* Font files which couldn't be downloaded correctly. */
     567.omgf form .omgf-task-manager #omgf-download-failures-notice ul {
     568        margin-left: 0;
     569}
     570
     571.omgf form .omgf-task-manager #omgf-download-failures-notice ul li {
     572        text-indent: 0;
     573        margin-bottom: 0.5em;
     574}
  • host-webfonts-local/tags/6.3.12/assets/css/omgf-admin.min.css

    r3489128 r3721511  
    1 @import url(omgf-admin-google-fonts.min.css);#wpwrap{background-color:rgb(248 250 252 / 1)}.omgf{font-family:"Public Sans",Arial,sans-serif;max-width:1350px}.omgf h1 .pro{background-color:#00adda;padding:0 5px 0 6px;border-radius:5px;text-transform:uppercase;color:#fff}.omgf h1{font-family:Poppins,Verdana,sans-serif;font-weight:800}.omgf h2,.omgf h3,.omgf h4,.omgf h5,.omgf h6{font-family:Poppins,Verdana,sans-serif;font-weight:500}.omgf .button{font-family:Poppins,Verdana,sans-serif;border-radius:10px;font-weight:500}.omgf .button-cancel{color:#a00;text-decoration:none;border-color:transparent;box-shadow:none;background:0 0;white-space:nowrap}.omgf .button-cancel.omgf-refresh{color:#ff851b}.omgf .button-cancel.omgf-empty,.omgf .button-cancel.omgf-refresh{padding:1px 10px!important}.omgf .button-cancel:hover{cursor:pointer;background:#d54e21;color:#fff;border-color:#d54e21}.omgf .button-cancel:hover#omgf-refresh{color:#fff;background:#ff851b;border-color:#ff851b}.omgf #submit{background:#00adda;border:0;padding:7px 20px}.omgf #submit:hover{background-color:#09c}.omgf h2.omgf-nav{border-bottom:0;padding-bottom:3px}.omgf h2.omgf-nav a{border:0;background-color:transparent;font-weight:600}.omgf h2.omgf-nav .nav-tab{color:#000}.omgf h2.omgf-nav .nav-tab:last-child{float:right}.omgf h2.omgf-nav .nav-tab-active,.omgf h2.omgf-nav .nav-tab:hover{color:#00adda}.omgf-nav .dashicons-before:before{margin-right:.25em;line-height:1.15em}.omgf form{max-width:1350px}.omgf form .dashicons{line-height:15px}.omgf form .omgf-task-manager{padding:5px 25px;margin-bottom:30px;box-shadow:0 10px 20px 1px #ccc;border:0;border-top:4px solid #00adda;border-radius:10px}.omgf form .omgf-task-manager .form-table{margin-bottom:1em}.omgf form .omgf-task-manager th.omgf-align-row-header{padding:.5em 0}.omgf form .omgf-task-manager .task-manager-row{padding:0;line-height:1.33}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice,.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{padding:18px 12px;border:1px solid #ececec;border-left:4px solid #ececec;border-radius:3px;margin-bottom:1em}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{padding:10px 18px 2px}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend .omgf-cache-legend-item{display:grid;grid-template-columns:1.5rem 9rem auto}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend .omgf-cache-legend-item .omgf-cache-legend-item-title{font-weight:700}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice ol{margin-left:1.5em;margin-bottom:0;overflow-wrap:anywhere}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.warning,.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{border:1px solid #ffdc00;border-left:4px solid #ffdc00;background-color:#fffbdf}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.success{border:1px solid #2ecc40;border-left:4px solid #2ecc40;background-color:#ddf7e0}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.info{border:1px solid #00adda;border-left:4px solid #00adda;background-color:#e0edf3}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.alert{border:1px solid #ff4136;border-left:4px solid #ff4136;background-color:#ff41362B}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice h4{margin-top:0}.omgf form .omgf-task-manager ul{margin-top:5px;list-style:none;margin-left:.5em;padding-left:0}.omgf form .omgf-task-manager ul li{padding-left:0;text-indent:-.5em;line-height:1.5em}.omgf form .omgf-task-manager ul li:before{font-family:dashicons,sans-serif;font-size:16px;vertical-align:-3px}.omgf form .omgf-task-manager ul li.stale:before{content:"\f534";color:#ff851b}.omgf form .omgf-task-manager ul li.found:before{content:"\f15e";color:#2ecc40}.omgf form .omgf-task-manager ul li.not-found:before{content:"\f223";color:#00adda}.omgf form .omgf-task-manager ul li.stale-cache-notice:before{content:"\f348";color:#00adda}.omgf form .omgf-task-manager ul li.unloaded:before{content:"\f460";color:#0074d9}.omgf form .advanced-processing-notice,.omgf form .used-subsets-notice{display:block;padding:18px 12px;margin-bottom:.5em;border:1px solid #ececec;border-left:4px solid #ececec}.omgf form .advanced-processing-notice.info,.omgf form .used-subsets-notice.info{border:1px solid #00adda;border-left:4px solid #00adda;background-color:#e0edf3}.omgf-optimize-fonts-container{padding:5px 25px 50px;margin:2.5em auto 2em;overflow:visible;border:0;border-top:4px solid #00adda;border-radius:10px;box-shadow:0 10px 20px 1px #ccc}.omgf-optimize-fonts-container .form-table tr{position:relative}.omgf-optimize-fonts-container .form-table tr td{padding:20px 0}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage{margin-top:1.5em;text-align:left}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage #omgf-optimize-preload-warning{position:fixed;top:50%;left:33%;width:33%;padding:20px 40px;background-color:#fff5b5;border:2px solid #ffdc00;border-radius:4px;box-shadow:1px 1px 333px #333;z-index:1000}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage #omgf-optimize-preload-warning .omgf-optimize-preload-warning-close{float:right;font-size:2em;line-height:.45;cursor:pointer}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table{width:100%;border-collapse:collapse}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table thead th{width:13%;padding:4px 0 9px}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody{border-top:4px solid #00adda}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody th{font-family:Poppins,sans-serif;font-weight:600;padding:9px 0 6px;width:40%}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td{padding-bottom:5px;width:13%}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .unload-mass-action{font-size:.9em}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .unload-mass-action:hover{cursor:pointer}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .family{font-weight:600}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody tr:last-child td{padding-bottom:10px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros{margin-right:10px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-cons,.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros{text-align:left;width:48%;display:inline-block;vertical-align:top}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-cons span:before,.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros span:before{vertical-align:-4px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description label{font-size:18px;font-weight:700}.omgf-optimize-fonts-container .omgf-optimize-fonts-tooltip{margin:0 0 30px}.omgf-optimize-fonts-container .omgf-optimize-fonts-tooltip ul li{line-height:1.5em}.omgf-help-section .about,.omgf-optimize-fonts-container p,.omgf-optimize-fonts-container ul,.omgf-task-manager p{text-align:justify;font-size:14px}.tooltip{position:relative;font-size:16px;line-height:16px;height:12px}.tooltip.omgf-save-optimize{vertical-align:middle}.tooltip .tooltip-text{visibility:hidden;position:absolute;top:100%;left:100%;z-index:10000;background-color:rgba(0,0,0,.75);font-family:"Public Sans",Arial,sans-serif;font-size:.8em;color:#fff;line-height:1.5;width:250px;padding:20px;margin-left:-125px;border-radius:10px}.tooltip .tooltip-text a{color:#fff}.tooltip .tooltip-text .illustration{margin-top:10px}.tooltip:hover .tooltip-text{visibility:visible}.omgf-warning{color:red;font-weight:600}#omgf-advanced-settings-form{background-color:#fff;border-radius:10px;padding:0 20px 20px;border-top:4px solid #00adda;box-shadow:0 10px 20px 1px #ccc}#omgf-help-form .postbox{padding:5px 25px 10px;border:0;border-top:4px solid #00adda;border-radius:10px;box-shadow:0 10px 20px 1px #ccc}#omgf-help-form .postbox .column-container{display:flex;flex-flow:row wrap;justify-content:space-between}#omgf-help-form .postbox .column-container .column ul li{margin-bottom:10px}#omgf-help-form .postbox .column-container .column ul li:last-child{margin-bottom:0}#omgf-help-form .postbox .column-container .column ul li a{text-decoration:none;line-height:1.45}#omgf-help-form .postbox .column-container .column ul li a i.dashicons{margin-right:5px;color:#646970}#omgf-help-form .postbox .column-container .column.last{margin-right:45px}#omgf-help-form .postbox .column-container .column.last .signature{text-align:center}#omgf-help-form .postbox .column-container .column.last .signature .dashicons-heart{color:#ff4136}#omgf-help-form .postbox .column-container .column.last .signature .signature-image{width:185px}@media only screen and (max-width:782px){.omgf-optimize-fonts-container{margin:20px 0}.omgf-optimize-fonts-container .option-title{position:relative;left:initial;top:initial}}.omgf-loading{position:fixed;background-color:rgba(255,255,255,.6);width:100%;height:100%;top:0;text-align:center}.omgf-loading .spinner{top:50%;left:50%;position:absolute;-ms-transform:translateX(-50%);transform:translateX(-50%)}#wpfooter{font-family:"Public Sans",Arial,sans-serif}#footer-thankyou a{vertical-align:-.65em}#footer-thankyou .dashicons.ffwp-heart{vertical-align:middle}#footer-thankyou .dashicons.ffwp-heart:before{color:#ff4136}#footer-thankyou .signature-image{width:96px}
     1@import url(omgf-admin-google-fonts.min.css);#wpwrap{background-color:rgb(248 250 252 / 1)}.omgf{font-family:"Public Sans",Arial,sans-serif;max-width:1350px}.omgf h1 .pro{background-color:#00adda;padding:0 5px 0 6px;border-radius:5px;text-transform:uppercase;color:#fff}.omgf h1{font-family:Poppins,Verdana,sans-serif;font-weight:800}.omgf h2,.omgf h3,.omgf h4,.omgf h5,.omgf h6{font-family:Poppins,Verdana,sans-serif;font-weight:500}.omgf .button{font-family:Poppins,Verdana,sans-serif;border-radius:10px;font-weight:500}.omgf .button-cancel{color:#a00;text-decoration:none;border-color:transparent;box-shadow:none;background:0 0;white-space:nowrap}.omgf .button-cancel.omgf-refresh{color:#ff851b}.omgf .button-cancel.omgf-empty,.omgf .button-cancel.omgf-refresh{padding:1px 10px!important}.omgf .button-cancel:hover{cursor:pointer;background:#d54e21;color:#fff;border-color:#d54e21}.omgf .button-cancel:hover#omgf-refresh{color:#fff;background:#ff851b;border-color:#ff851b}.omgf #submit{background:#00adda;border:0;padding:7px 20px}.omgf #submit:hover{background-color:#09c}.omgf h2.omgf-nav{border-bottom:0;padding-bottom:3px}.omgf h2.omgf-nav a{border:0;background-color:transparent;font-weight:600}.omgf h2.omgf-nav .nav-tab{color:#000}.omgf h2.omgf-nav .nav-tab:last-child{float:right}.omgf h2.omgf-nav .nav-tab-active,.omgf h2.omgf-nav .nav-tab:hover{color:#00adda}.omgf-nav .dashicons-before:before{margin-right:.25em;line-height:1.15em}.omgf form{max-width:1350px}.omgf form .dashicons{line-height:15px}.omgf form .omgf-task-manager{padding:5px 25px;margin-bottom:30px;box-shadow:0 10px 20px 1px #ccc;border:0;border-top:4px solid #00adda;border-radius:10px}.omgf form .omgf-task-manager .form-table{margin-bottom:1em}.omgf form .omgf-task-manager th.omgf-align-row-header{padding:.5em 0}.omgf form .omgf-task-manager .task-manager-row{padding:0;line-height:1.33}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice,.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{padding:18px 12px;border:1px solid #ececec;border-left:4px solid #ececec;border-radius:3px;margin-bottom:1em}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{padding:10px 18px 2px}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend .omgf-cache-legend-item{display:grid;grid-template-columns:1.5rem 9rem auto}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend .omgf-cache-legend-item .omgf-cache-legend-item-title{font-weight:700}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice ol{margin-left:1.5em;margin-bottom:0;overflow-wrap:anywhere}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.warning,.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{border:1px solid #ffdc00;border-left:4px solid #ffdc00;background-color:#fffbdf}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.success{border:1px solid #2ecc40;border-left:4px solid #2ecc40;background-color:#ddf7e0}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.info{border:1px solid #00adda;border-left:4px solid #00adda;background-color:#e0edf3}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.alert{border:1px solid #ff4136;border-left:4px solid #ff4136;background-color:#ff41362B}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice h4{margin-top:0}.omgf form .omgf-task-manager ul{margin-top:5px;list-style:none;margin-left:.5em;padding-left:0}.omgf form .omgf-task-manager ul li{padding-left:0;text-indent:-.5em;line-height:1.5em}.omgf form .omgf-task-manager ul li:before{font-family:dashicons,sans-serif;font-size:16px;vertical-align:-3px}.omgf form .omgf-task-manager ul li.stale:before{content:"\f534";color:#ff851b}.omgf form .omgf-task-manager ul li.found:before{content:"\f15e";color:#2ecc40}.omgf form .omgf-task-manager ul li.not-found:before{content:"\f223";color:#00adda}.omgf form .omgf-task-manager ul li.stale-cache-notice:before{content:"\f348";color:#00adda}.omgf form .omgf-task-manager ul li.unloaded:before{content:"\f460";color:#0074d9}.omgf form .advanced-processing-notice,.omgf form .used-subsets-notice{display:block;padding:18px 12px;margin-bottom:.5em;border:1px solid #ececec;border-left:4px solid #ececec}.omgf form .advanced-processing-notice.info,.omgf form .used-subsets-notice.info{border:1px solid #00adda;border-left:4px solid #00adda;background-color:#e0edf3}.omgf-optimize-fonts-container{padding:5px 25px 50px;margin:2.5em auto 2em;overflow:visible;border:0;border-top:4px solid #00adda;border-radius:10px;box-shadow:0 10px 20px 1px #ccc}.omgf-optimize-fonts-container .form-table tr{position:relative}.omgf-optimize-fonts-container .form-table tr td{padding:20px 0}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage{margin-top:1.5em;text-align:left}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage #omgf-optimize-preload-warning{position:fixed;top:50%;left:33%;width:33%;padding:20px 40px;background-color:#fff5b5;border:2px solid #ffdc00;border-radius:4px;box-shadow:1px 1px 333px #333;z-index:1000}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage #omgf-optimize-preload-warning .omgf-optimize-preload-warning-close{float:right;font-size:2em;line-height:.45;cursor:pointer}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table{width:100%;border-collapse:collapse}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table thead th{width:13%;padding:4px 0 9px}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody{border-top:4px solid #00adda}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody th{font-family:Poppins,sans-serif;font-weight:600;padding:9px 0 6px;width:40%}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td{padding-bottom:5px;width:13%}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .unload-mass-action{font-size:.9em}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .unload-mass-action:hover{cursor:pointer}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .family{font-weight:600}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody tr:last-child td{padding-bottom:10px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros{margin-right:10px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-cons,.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros{text-align:left;width:48%;display:inline-block;vertical-align:top}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-cons span:before,.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros span:before{vertical-align:-4px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description label{font-size:18px;font-weight:700}.omgf-optimize-fonts-container .omgf-optimize-fonts-tooltip{margin:0 0 30px}.omgf-optimize-fonts-container .omgf-optimize-fonts-tooltip ul li{line-height:1.5em}.omgf-help-section .about,.omgf-optimize-fonts-container p,.omgf-optimize-fonts-container ul,.omgf-task-manager p{text-align:justify;font-size:14px}.tooltip{position:relative;font-size:16px;line-height:16px;height:12px}.tooltip.omgf-save-optimize{vertical-align:middle}.tooltip .tooltip-text{visibility:hidden;position:absolute;top:100%;left:100%;z-index:10000;background-color:rgba(0,0,0,.75);font-family:"Public Sans",Arial,sans-serif;font-size:.8em;color:#fff;line-height:1.5;width:250px;padding:20px;margin-left:-125px;border-radius:10px}.tooltip .tooltip-text a{color:#fff}.tooltip .tooltip-text .illustration{margin-top:10px}.tooltip:hover .tooltip-text{visibility:visible}.omgf-warning{color:red;font-weight:600}#omgf-advanced-settings-form{background-color:#fff;border-radius:10px;padding:0 20px 20px;border-top:4px solid #00adda;box-shadow:0 10px 20px 1px #ccc}#omgf-help-form .postbox{padding:5px 25px 10px;border:0;border-top:4px solid #00adda;border-radius:10px;box-shadow:0 10px 20px 1px #ccc}#omgf-help-form .postbox .column-container{display:flex;flex-flow:row wrap;justify-content:space-between}#omgf-help-form .postbox .column-container .column ul li{margin-bottom:10px}#omgf-help-form .postbox .column-container .column ul li:last-child{margin-bottom:0}#omgf-help-form .postbox .column-container .column ul li a{text-decoration:none;line-height:1.45}#omgf-help-form .postbox .column-container .column ul li a i.dashicons{margin-right:5px;color:#646970}#omgf-help-form .postbox .column-container .column.last{margin-right:45px}#omgf-help-form .postbox .column-container .column.last .signature{text-align:center}#omgf-help-form .postbox .column-container .column.last .signature .dashicons-heart{color:#ff4136}#omgf-help-form .postbox .column-container .column.last .signature .signature-image{width:185px}@media only screen and (max-width:782px){.omgf-optimize-fonts-container{margin:20px 0}.omgf-optimize-fonts-container .option-title{position:relative;left:initial;top:initial}}.omgf-loading{position:fixed;background-color:rgba(255,255,255,.6);width:100%;height:100%;top:0;text-align:center}.omgf-loading .spinner{top:50%;left:50%;position:absolute;-ms-transform:translateX(-50%);transform:translateX(-50%)}#wpfooter{font-family:"Public Sans",Arial,sans-serif}#footer-thankyou a{vertical-align:-.65em}#footer-thankyou .dashicons.ffwp-heart{vertical-align:middle}#footer-thankyou .dashicons.ffwp-heart:before{color:#ff4136}#footer-thankyou .signature-image{width:96px}.omgf form .omgf-task-manager #omgf-download-failures-notice ul{margin-left:0}.omgf form .omgf-task-manager #omgf-download-failures-notice ul li{text-indent:0;margin-bottom:.5em}
  • host-webfonts-local/tags/6.3.12/assets/css/omgf-frontend.css

    r3489128 r3721511  
    132132        color: white !important;
    133133}
     134
     135#wp-admin-bar-omgf .alert {
     136        background-color: #FF4136 !important;
     137}
     138
     139#wp-admin-bar-omgf .alert .ab-item {
     140        color: white !important;
     141}
  • host-webfonts-local/tags/6.3.12/assets/css/omgf-frontend.min.css

    r3489128 r3721511  
    1 #wp-admin-bar-omgf.dot>.ab-item:after{content:"";width:10px;height:10px;margin-left:5px;border-radius:50%;background-color:grey;display:inline-block;-webkit-animation:2.5s infinite waiting;-moz-animation:2.5s infinite waiting;-o-animation:2.5s infinite waiting;animation:2.5s infinite waiting}@keyframes waiting{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:grey}}#wp-admin-bar-omgf-info>.ab-item{color:#fff!important}#wp-admin-bar-omgf.dot.success>.ab-item:after{background-color:#2ecc40;animation:none}#wp-admin-bar-omgf.dot.alert>.ab-item:after{background-color:#ff4136;-webkit-animation:2.5s blinking-alert;-moz-animation:2.5s blinking-alert;-o-animation:2.5s blinking-alert;animation:2.5s blinking-alert}@keyframes blinking-alert{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ff4136}}#wp-admin-bar-omgf.dot.warning>.ab-item:after{background-color:#ff851b;-webkit-animation:2.5s blinking-warning;-moz-animation:2.5s blinking-warning;-o-animation:2.5s blinking-warning;animation:2.5s blinking-warning}@keyframes blinking-warning{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ff851b}}#wp-admin-bar-omgf.dot.notice>.ab-item:after{background-color:#ffdc00;-webkit-animation:2.5s blinking-notice;-moz-animation:2.5s blinking-notice;-o-animation:2.5s blinking-notice;animation:2.5s blinking-notice}@keyframes blinking-notice{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ffdc00}}#wp-admin-bar-omgf.dot.info>.ab-item:after{background-color:#0074d9;-webkit-animation:2.5s blinking-info;-moz-animation:2.5s blinking-info;-o-animation:2.5s blinking-info;animation:2.5s blinking-info}@keyframes blinking-info{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#0074d9}}#wp-admin-bar-omgf.alert #wp-admin-bar-omgf-info{background-color:#ff4136!important}#wp-admin-bar-omgf.notice #wp-admin-bar-omgf-info{background-color:#ffdc00!important}#wp-admin-bar-omgf.info #wp-admin-bar-omgf-first-run,#wp-admin-bar-omgf.info #wp-admin-bar-omgf-info{background-color:#0074d9!important}#wp-admin-bar-omgf.notice #wp-admin-bar-omgf-info .ab-item{color:#000!important}#wp-admin-bar-omgf .info{background-color:#0074d9!important}#wp-admin-bar-omgf .info .ab-item{color:#fff!important}
     1#wp-admin-bar-omgf.dot>.ab-item:after{content:"";width:10px;height:10px;margin-left:5px;border-radius:50%;background-color:grey;display:inline-block;-webkit-animation:2.5s infinite waiting;-moz-animation:2.5s infinite waiting;-o-animation:2.5s infinite waiting;animation:2.5s infinite waiting}@keyframes waiting{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:grey}}#wp-admin-bar-omgf-info>.ab-item{color:#fff!important}#wp-admin-bar-omgf.dot.success>.ab-item:after{background-color:#2ecc40;animation:none}#wp-admin-bar-omgf.dot.alert>.ab-item:after{background-color:#ff4136;-webkit-animation:2.5s blinking-alert;-moz-animation:2.5s blinking-alert;-o-animation:2.5s blinking-alert;animation:2.5s blinking-alert}@keyframes blinking-alert{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ff4136}}#wp-admin-bar-omgf.dot.warning>.ab-item:after{background-color:#ff851b;-webkit-animation:2.5s blinking-warning;-moz-animation:2.5s blinking-warning;-o-animation:2.5s blinking-warning;animation:2.5s blinking-warning}@keyframes blinking-warning{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ff851b}}#wp-admin-bar-omgf.dot.notice>.ab-item:after{background-color:#ffdc00;-webkit-animation:2.5s blinking-notice;-moz-animation:2.5s blinking-notice;-o-animation:2.5s blinking-notice;animation:2.5s blinking-notice}@keyframes blinking-notice{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ffdc00}}#wp-admin-bar-omgf.dot.info>.ab-item:after{background-color:#0074d9;-webkit-animation:2.5s blinking-info;-moz-animation:2.5s blinking-info;-o-animation:2.5s blinking-info;animation:2.5s blinking-info}@keyframes blinking-info{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#0074d9}}#wp-admin-bar-omgf.alert #wp-admin-bar-omgf-info{background-color:#ff4136!important}#wp-admin-bar-omgf.notice #wp-admin-bar-omgf-info{background-color:#ffdc00!important}#wp-admin-bar-omgf.info #wp-admin-bar-omgf-first-run,#wp-admin-bar-omgf.info #wp-admin-bar-omgf-info{background-color:#0074d9!important}#wp-admin-bar-omgf.notice #wp-admin-bar-omgf-info .ab-item{color:#000!important}#wp-admin-bar-omgf .info{background-color:#0074d9!important}#wp-admin-bar-omgf .info .ab-item{color:#fff!important}#wp-admin-bar-omgf .alert{background-color:#ff4136!important}#wp-admin-bar-omgf .alert .ab-item{color:#fff!important}
  • host-webfonts-local/tags/6.3.12/assets/js/omgf-admin.js

    r3503345 r3721511  
    5454                        $('.omgf-optimize-forbidden').on('click', this.wait_for_page_reload);
    5555                        $(document).on('click', '#omgf-dismiss-performance-checker-notice', this.dismiss_performance_checker_notice);
     56                        $(document).on('click', '#omgf-dismiss-download-failures', this.dismiss_download_failures);
    5657                        // Ticker
    5758                        setInterval(this.loop_ticker_items, 4000);
    … …  
    480481
    481482                /**
     483                 * Dismiss the font files which couldn't be downloaded correctly.
     484                 */
     485                dismiss_download_failures: function (e) {
     486                        e.preventDefault();
     487
     488                        $.ajax({
     489                                type: 'POST',
     490                                url: omgf_admin_i18n.rest_url + '/dashboard/dismiss-download-failures',
     491                                beforeSend: function (xhr) {
     492                                        xhr.setRequestHeader('X-WP-Nonce', omgf_admin_i18n.nonce);
     493                                        omgf_admin.show_loader();
     494                                },
     495                                complete: function (result) {
     496                                        if (result.responseJSON !== undefined && result.responseJSON.success) {
     497                                                $('#omgf-download-failures-notice').fadeOut(300, function () {
     498                                                        $(this).remove();
     499                                                });
     500                                        }
     501
     502                                        omgf_admin.hide_loader();
     503                                }
     504                        });
     505                },
     506
     507                /**
    482508                 *
    483509                 */
  • host-webfonts-local/tags/6.3.12/assets/js/omgf-admin.min.js

    r3503345 r3721511  
    1 jQuery(document).ready(function(d){var r={ticker_items:document.querySelectorAll(".ticker-item"),ticker_index:0,empty_cache_directory_xhr:!1,optimize_xhr:!1,cache_prefix:"-mod-",flush_action_init:d(".omgf-empty").data("init"),empty_action_nonce:d(".omgf-empty").data("nonce"),delete_log_xhr:!1,delete_action_nonce:d("#omgf-delete-log").data("nonce"),init:function(){d(".omgf-subsets").on("change",this.maybe_select_latin),d(".omgf-optimize-fonts-manage .unload").on("change",this.maybe_unload_stylesheets),d(".omgf-optimize-fonts-manage .unload, .omgf-optimize-fonts-manage .fallback-font-stack select").on("change",this.generate_cache_key),d(".omgf-optimize-fonts-manage .unload").on("change",this.toggle_preload),d(".omgf-optimize-fonts-manage .preload").on("change",this.toggle_unload),d(".omgf-optimize-fonts-manage .preload").on("change",this.maybe_show_preload_warning),d(".omgf-optimize-fonts-manage .unload-italics").on("click",this.unload_italics),d(".omgf-optimize-fonts-manage .unload-all").on("click",this.unload_all),d(".omgf-optimize-fonts-manage .load-all").on("click",this.load_all),d("#omgf-save-optimize, #omgf-optimize-again").on("click",function(){d("#omgf-optimize-settings-form #submit").click()}),d(document).on("click","a[id^=omgf-hide-notice-]",this.hide_notice),d(".omgf-remove-stylesheet").on("click",this.remove_stylesheet_from_db),d(".omgf-refresh, #omgf-cache-refresh").on("click",this.refresh_cache),d(".omgf-empty, #omgf-cache-flush").on("click",this.empty_cache_directory),d("#omgf-optimize-settings-form").on("submit",this.show_loader_before_submit),d("#omgf-delete-log").on("click",this.delete_log),d(".omgf-optimize-preload-warning-close").on("click",this.hide_preload_warning),d(".omgf-optimize-forbidden").on("click",this.wait_for_page_reload),d(document).on("click","#omgf-dismiss-performance-checker-notice",this.dismiss_performance_checker_notice),setInterval(this.loop_ticker_items,4e3)},maybe_select_latin:function(e){let o=this.value,t=e.target,n=t.className,a=["latin","latin-ext"];"latin-ext"===o&&a.forEach(function(e){document.querySelector("."+n+" option[value="+e+"]").selected=!0}),a=["latin","vietnamese"],"vietnamese"===o&&a.forEach(function(e){document.querySelector("."+n+" option[value="+e+"]").selected=!0})},loop_ticker_items:function(){r.ticker_items.forEach(function(e,o){o==r.ticker_index?d(e).fadeIn(500):d(e).hide(0)}),r.ticker_index++,r.ticker_index===r.ticker_items.length&&(r.ticker_index=0)},hide_notice:function(){var e=d(this).data("warning-id"),o=d(this).data("nonce");d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_hide_notice",warning_id:e,nonce:o},beforeSend:function(){r.show_loader()},complete:function(e){void 0!==e.responseJSON&&void 0!==e.responseJSON.data&&d("#task-manager-notice-row").replaceWith(e.responseJSON.data),r.hide_loader()}})},remove_stylesheet_from_db:function(){var e=d(this).data("handle");d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_remove_stylesheet_from_db",handle:e,nonce:r.empty_action_nonce},beforeSend:function(){r.show_loader()},complete:function(){location.reload()}})},refresh_cache:function(){d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_refresh_cache",nonce:r.empty_action_nonce},beforeSend:function(){d(".omgf-optimize-fonts-manage table tbody").each(function(e,o){r.generate_cache_key(o)})},complete:function(){d("#submit").click()}})},maybe_unload_stylesheets:function(){var e=d(this).closest("tbody")[0].id;d("tbody#"+e+" input.unload:checked").length===d("tbody#"+e+" input.unload").length?r.unload_stylesheet(e):r.load_stylesheet(e)},unload_stylesheet:function(e){var o=d("#unload_stylesheets"),t=o.val().split(",");-1===t.indexOf(e)&&t.push(e),t.join(),o.val(t)},load_stylesheet:function(e){var o=d("#unload_stylesheets"),t=o.val().split(",");~(position=t.indexOf(e))&&t.splice(position,1),o.val(t)},generate_cache_key:function(e=null){t=void 0===e.target?d(e).attr("id"):d(this).data("handle");var t,o,e=d("#cache_keys"),n=e.val().split(","),a=d("#"+t+" input.unload:checked").length,i=d("#"+t+" input.unload").length,c=n.findIndex((e,o)=>{if(-1!==e.indexOf(t))return!0}),l=!1;"SELECT"===this.nodeName||0!==a&&a!==i||(n[c]=t,l=!0),!0!==l&&-1!==c&&(a=n[c],i=r.cache_prefix+Math.random().toString(36).substring(2,7),-1!==a.indexOf(r.cache_prefix)&&((l=a.split(r.cache_prefix))[o=r.get_last_element_index(l)]=Math.random().toString(36).substring(2,7),a=l[0],i=r.cache_prefix+l[o]),n[c]=a+i),e.val(n.join())},toggle_preload:function(){r.toggle(this,"preload")},toggle_unload:function(){r.toggle(this,"unload")},maybe_show_preload_warning:function(){5===d("."+this.className+":checked").length&&d("#omgf-optimize-preload-warning").fadeIn("300")},hide_preload_warning:function(){d("#omgf-optimize-preload-warning").fadeOut("100")},wait_for_page_reload:function(e){e.preventDefault(),r.show_loader();var o=window.setInterval(function(){document.hasFocus()&&(window.location.reload(),window.clearInterval(o))},500);window.open(this.href,"_blank")},unload_italics:function(e){e.preventDefault();var t=d(this).parents(".font-family").data("id");d(".unload").each(function(e,o){o.value.includes("italic")&&o.dataset.fontId===t&&!1===o.checked&&o.click()})},unload_all:function(e,o=this){o=d(o).parents(".font-family").data("id");d('input.unload[data-font-id="'+o+'"]').each(function(e,o){!1===o.checked&&o.click()})},load_all:function(){var e=d(this).parents(".font-family").data("id");d('input.unload[data-font-id="'+e+'"]').each(function(e,o){!0===o.checked&&o.click()})},toggle:function(e,o){var t=d(e),t=d("."+o+"-"+t.data("font-id")+"-"+t.val()+" ."+o);e.checked?t.attr("disabled",!0):t.attr("disabled",!1)},get_last_element_index:function(e){return e.length-1},empty_cache_directory:function(){r.empty_cache_directory_xhr&&r.empty_cache_directory_xhr.abort(),r.empty_cache_directory_xhr=d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_empty_dir",nonce:r.empty_action_nonce,init:r.flush_action_init},beforeSend:function(){r.show_loader()},complete:function(){location.reload()}})},delete_log:function(){r.delete_log_xhr&&r.delete_log_xhr.abort(),r.delete_log_xhr=d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_delete_log",nonce:r.delete_action_nonce},beforeSend:function(){r.show_loader()},complete:function(){location.reload()}})},show_loader_before_submit:function(){r.show_loader()},hide_loader:function(){d(".omgf-loading").fadeOut(300,function(){d(".omgf-loading").remove()})},dismiss_performance_checker_notice:function(e){e.preventDefault(),d.ajax({type:"POST",url:omgf_admin_i18n.rest_url+"/dashboard/dismiss-notice",beforeSend:function(e){e.setRequestHeader("X-WP-Nonce",omgf_admin_i18n.nonce),r.show_loader()},complete:function(e){void 0!==e.responseJSON&&e.responseJSON.success&&d("#omgf-performance-checker-notice").fadeOut(300,function(){d(this).remove()}),r.hide_loader()}})},show_loader:function(){d("#wpwrap").append('<div class="omgf-loading"><span class="spinner is-active"></span></div>')}};omgf_show_loader=r.show_loader,omgf_hide_loader=r.hide_loader,omgf_unload_all=r.unload_all,omgf_load_all=r.load_all,omgf_unload_stylesheet=r.unload_stylesheet,omgf_load_stylesheet=r.load_stylesheet,r.init()});
     1jQuery(document).ready(function(d){var s={ticker_items:document.querySelectorAll(".ticker-item"),ticker_index:0,empty_cache_directory_xhr:!1,optimize_xhr:!1,cache_prefix:"-mod-",flush_action_init:d(".omgf-empty").data("init"),empty_action_nonce:d(".omgf-empty").data("nonce"),delete_log_xhr:!1,delete_action_nonce:d("#omgf-delete-log").data("nonce"),init:function(){d(".omgf-subsets").on("change",this.maybe_select_latin),d(".omgf-optimize-fonts-manage .unload").on("change",this.maybe_unload_stylesheets),d(".omgf-optimize-fonts-manage .unload, .omgf-optimize-fonts-manage .fallback-font-stack select").on("change",this.generate_cache_key),d(".omgf-optimize-fonts-manage .unload").on("change",this.toggle_preload),d(".omgf-optimize-fonts-manage .preload").on("change",this.toggle_unload),d(".omgf-optimize-fonts-manage .preload").on("change",this.maybe_show_preload_warning),d(".omgf-optimize-fonts-manage .unload-italics").on("click",this.unload_italics),d(".omgf-optimize-fonts-manage .unload-all").on("click",this.unload_all),d(".omgf-optimize-fonts-manage .load-all").on("click",this.load_all),d("#omgf-save-optimize, #omgf-optimize-again").on("click",function(){d("#omgf-optimize-settings-form #submit").click()}),d(document).on("click","a[id^=omgf-hide-notice-]",this.hide_notice),d(".omgf-remove-stylesheet").on("click",this.remove_stylesheet_from_db),d(".omgf-refresh, #omgf-cache-refresh").on("click",this.refresh_cache),d(".omgf-empty, #omgf-cache-flush").on("click",this.empty_cache_directory),d("#omgf-optimize-settings-form").on("submit",this.show_loader_before_submit),d("#omgf-delete-log").on("click",this.delete_log),d(".omgf-optimize-preload-warning-close").on("click",this.hide_preload_warning),d(".omgf-optimize-forbidden").on("click",this.wait_for_page_reload),d(document).on("click","#omgf-dismiss-performance-checker-notice",this.dismiss_performance_checker_notice),d(document).on("click","#omgf-dismiss-download-failures",this.dismiss_download_failures),setInterval(this.loop_ticker_items,4e3)},maybe_select_latin:function(e){let o=this.value,t=e.target,n=t.className,a=["latin","latin-ext"];"latin-ext"===o&&a.forEach(function(e){document.querySelector("."+n+" option[value="+e+"]").selected=!0}),a=["latin","vietnamese"],"vietnamese"===o&&a.forEach(function(e){document.querySelector("."+n+" option[value="+e+"]").selected=!0})},loop_ticker_items:function(){s.ticker_items.forEach(function(e,o){o==s.ticker_index?d(e).fadeIn(500):d(e).hide(0)}),s.ticker_index++,s.ticker_index===s.ticker_items.length&&(s.ticker_index=0)},hide_notice:function(){var e=d(this).data("warning-id"),o=d(this).data("nonce");d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_hide_notice",warning_id:e,nonce:o},beforeSend:function(){s.show_loader()},complete:function(e){void 0!==e.responseJSON&&void 0!==e.responseJSON.data&&d("#task-manager-notice-row").replaceWith(e.responseJSON.data),s.hide_loader()}})},remove_stylesheet_from_db:function(){var e=d(this).data("handle");d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_remove_stylesheet_from_db",handle:e,nonce:s.empty_action_nonce},beforeSend:function(){s.show_loader()},complete:function(){location.reload()}})},refresh_cache:function(){d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_refresh_cache",nonce:s.empty_action_nonce},beforeSend:function(){d(".omgf-optimize-fonts-manage table tbody").each(function(e,o){s.generate_cache_key(o)})},complete:function(){d("#submit").click()}})},maybe_unload_stylesheets:function(){var e=d(this).closest("tbody")[0].id;d("tbody#"+e+" input.unload:checked").length===d("tbody#"+e+" input.unload").length?s.unload_stylesheet(e):s.load_stylesheet(e)},unload_stylesheet:function(e){var o=d("#unload_stylesheets"),t=o.val().split(",");-1===t.indexOf(e)&&t.push(e),t.join(),o.val(t)},load_stylesheet:function(e){var o=d("#unload_stylesheets"),t=o.val().split(",");~(position=t.indexOf(e))&&t.splice(position,1),o.val(t)},generate_cache_key:function(e=null){t=void 0===e.target?d(e).attr("id"):d(this).data("handle");var t,o,e=d("#cache_keys"),n=e.val().split(","),a=d("#"+t+" input.unload:checked").length,i=d("#"+t+" input.unload").length,c=n.findIndex((e,o)=>{if(-1!==e.indexOf(t))return!0}),l=!1;"SELECT"===this.nodeName||0!==a&&a!==i||(n[c]=t,l=!0),!0!==l&&-1!==c&&(a=n[c],i=s.cache_prefix+Math.random().toString(36).substring(2,7),-1!==a.indexOf(s.cache_prefix)&&((l=a.split(s.cache_prefix))[o=s.get_last_element_index(l)]=Math.random().toString(36).substring(2,7),a=l[0],i=s.cache_prefix+l[o]),n[c]=a+i),e.val(n.join())},toggle_preload:function(){s.toggle(this,"preload")},toggle_unload:function(){s.toggle(this,"unload")},maybe_show_preload_warning:function(){5===d("."+this.className+":checked").length&&d("#omgf-optimize-preload-warning").fadeIn("300")},hide_preload_warning:function(){d("#omgf-optimize-preload-warning").fadeOut("100")},wait_for_page_reload:function(e){e.preventDefault(),s.show_loader();var o=window.setInterval(function(){document.hasFocus()&&(window.location.reload(),window.clearInterval(o))},500);window.open(this.href,"_blank")},unload_italics:function(e){e.preventDefault();var t=d(this).parents(".font-family").data("id");d(".unload").each(function(e,o){o.value.includes("italic")&&o.dataset.fontId===t&&!1===o.checked&&o.click()})},unload_all:function(e,o=this){o=d(o).parents(".font-family").data("id");d('input.unload[data-font-id="'+o+'"]').each(function(e,o){!1===o.checked&&o.click()})},load_all:function(){var e=d(this).parents(".font-family").data("id");d('input.unload[data-font-id="'+e+'"]').each(function(e,o){!0===o.checked&&o.click()})},toggle:function(e,o){var t=d(e),t=d("."+o+"-"+t.data("font-id")+"-"+t.val()+" ."+o);e.checked?t.attr("disabled",!0):t.attr("disabled",!1)},get_last_element_index:function(e){return e.length-1},empty_cache_directory:function(){s.empty_cache_directory_xhr&&s.empty_cache_directory_xhr.abort(),s.empty_cache_directory_xhr=d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_empty_dir",nonce:s.empty_action_nonce,init:s.flush_action_init},beforeSend:function(){s.show_loader()},complete:function(){location.reload()}})},delete_log:function(){s.delete_log_xhr&&s.delete_log_xhr.abort(),s.delete_log_xhr=d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_delete_log",nonce:s.delete_action_nonce},beforeSend:function(){s.show_loader()},complete:function(){location.reload()}})},show_loader_before_submit:function(){s.show_loader()},hide_loader:function(){d(".omgf-loading").fadeOut(300,function(){d(".omgf-loading").remove()})},dismiss_performance_checker_notice:function(e){e.preventDefault(),d.ajax({type:"POST",url:omgf_admin_i18n.rest_url+"/dashboard/dismiss-notice",beforeSend:function(e){e.setRequestHeader("X-WP-Nonce",omgf_admin_i18n.nonce),s.show_loader()},complete:function(e){void 0!==e.responseJSON&&e.responseJSON.success&&d("#omgf-performance-checker-notice").fadeOut(300,function(){d(this).remove()}),s.hide_loader()}})},dismiss_download_failures:function(e){e.preventDefault(),d.ajax({type:"POST",url:omgf_admin_i18n.rest_url+"/dashboard/dismiss-download-failures",beforeSend:function(e){e.setRequestHeader("X-WP-Nonce",omgf_admin_i18n.nonce),s.show_loader()},complete:function(e){void 0!==e.responseJSON&&e.responseJSON.success&&d("#omgf-download-failures-notice").fadeOut(300,function(){d(this).remove()}),s.hide_loader()}})},show_loader:function(){d("#wpwrap").append('<div class="omgf-loading"><span class="spinner is-active"></span></div>')}};omgf_show_loader=s.show_loader,omgf_hide_loader=s.hide_loader,omgf_unload_all=s.unload_all,omgf_load_all=s.load_all,omgf_unload_stylesheet=s.unload_stylesheet,omgf_load_stylesheet=s.load_stylesheet,s.init()});
  • host-webfonts-local/tags/6.3.12/assets/js/omgf-frontend.js

    r3641590 r3721511  
    8787                                }
    8888
    89                                 if ((status !== 'success' && status !== 'warning')) {
     89                                // If the alert is only caused by failed downloads, the Google Fonts Checker's alert text doesn't apply.
     90                                let download_failures_only = status === 'alert' && response.download_failures && !response.google_fonts_checker_alert;
     91
     92                                if (status !== 'success' && status !== 'warning' && !download_failures_only) {
    9093                                        this.addInfoBox(status);
     94                                }
     95
     96                                if (response.download_failures) {
     97                                        this.addInfoBox('download_failures', response);
    9198                                }
    9299
    … …  
    188195
    189196                /**
     197                 * Normalizes a font-weight to the numeric value computed styles use, e.g. 'normal' => '400' and 'bold' => '700'.
     198                 * Ranges (e.g. '100 900' for variable fonts) are returned as-is.
     199                 *
     200                 * @param {string} weight
     201                 * @returns {string}
     202                 */
     203                normalizeFontWeight: function (weight) {
     204                        weight = String(weight || '400').trim().toLowerCase();
     205
     206                        if (weight === 'normal') {
     207                                return '400';
     208                        }
     209
     210                        if (weight === 'bold') {
     211                                return '700';
     212                        }
     213
     214                        return weight;
     215                },
     216
     217                /**
     218                 * Checks if a font face is in a set of face IDs collected from computed styles (which always have a single,
     219                 * numeric weight). A font face with a weight range (i.e. a variable font) is used if any weight within the range is.
     220                 *
     221                 * @param {Set} face_ids
     222                 * @param {string} family
     223                 * @param {string} weight Normalized font-weight.
     224                 * @param {string} style
     225                 * @returns {boolean}
     226                 */
     227                isFaceUsed: function (face_ids, family, weight, style) {
     228                        if (face_ids.has(`${family}-${weight}-${style}`.toLowerCase())) {
     229                                return true;
     230                        }
     231
     232                        let range = weight.split(/\s+/).map(Number);
     233
     234                        if (range.length !== 2 || range.some(isNaN)) {
     235                                return false;
     236                        }
     237
     238                        for (let face_id of face_ids) {
     239                                let prefix = `${family}-`.toLowerCase();
     240                                let suffix = `-${style}`.toLowerCase();
     241
     242                                if (!face_id.startsWith(prefix) || !face_id.endsWith(suffix)) {
     243                                        continue;
     244                                }
     245
     246                                let used_weight = Number(face_id.slice(prefix.length, face_id.length - suffix.length));
     247
     248                                if (used_weight >= range[0] && used_weight <= range[1]) {
     249                                        return true;
     250                                }
     251                        }
     252
     253                        return false;
     254                },
     255
     256                /**
     257                 * The rules of cross-origin stylesheets can't be read, unless they were loaded with the crossorigin attribute. Most
     258                 * font CDNs send CORS headers though, so these stylesheets are fetched again (usually from the browser's cache)
     259                 * and parsed separately to find their @font-face rules.
     260                 *
     261                 * @param {string[]} hrefs
     262                 * @param {Map} font_face_url_map
     263                 * @param {Set} loaded_font_urls
     264                 * @returns {Promise<void>}
     265                 */
     266                extractCrossOriginFontFaceRules: async function (hrefs, font_face_url_map, loaded_font_urls) {
     267                        if (typeof CSSStyleSheet === 'undefined' || typeof CSSStyleSheet.prototype.replaceSync !== 'function') {
     268                                return;
     269                        }
     270
     271                        // Keep the analysis bounded.
     272                        const max_sheets = 10;
     273                        const timeout_ms = 3000;
     274
     275                        let unique_hrefs = [...new Set(hrefs)].slice(0, max_sheets);
     276
     277                        await Promise.all(unique_hrefs.map(async (href) => {
     278                                let controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
     279                                let timeout = controller ? setTimeout(() => controller.abort(), timeout_ms) : null;
     280
     281                                try {
     282                                        let response = await fetch(href, {
     283                                                mode: 'cors',
     284                                                credentials: 'omit',
     285                                                signal: controller ? controller.signal : undefined
     286                                        });
     287
     288                                        if (!response.ok) {
     289                                                return;
     290                                        }
     291
     292                                        let sheet = new CSSStyleSheet();
     293
     294                                        // @import rules aren't supported (and ignored) in constructed stylesheets.
     295                                        sheet.replaceSync(await response.text());
     296
     297                                        this.extractFontFaceRules(sheet.cssRules, { href: href }, font_face_url_map, loaded_font_urls);
     298                                } catch (e) {
     299                                        // No CORS headers, a network error or a timeout: nothing we can do.
     300                                } finally {
     301                                        if (timeout) {
     302                                                clearTimeout(timeout);
     303                                        }
     304                                }
     305                        }));
     306                },
     307
     308                /**
    190309                 * Helper to get property value from a CSSRule, with fallback for Firefox.
    191310                 *
    … …  
    230349                                        let rule_style = this.getFontFaceProperty(rule, 'font-style') || 'normal';
    231350
    232                                         if (rule_weight === 'normal') rule_weight = '400';
    233                                         if (rule_weight === 'bold') rule_weight = '700';
     351                                        rule_weight = this.normalizeFontWeight(rule_weight);
    234352
    235353                                        let src = rule.style.getPropertyValue('src') || rule.style.src;
    … …  
    356474                                let font_face_url_map = new Map();
    357475
     476                                let cross_origin_sheets = [];
     477
    358478                                for (let i = 0; i < document.styleSheets.length; i++) {
     479                                        let sheet = document.styleSheets[i];
     480
    359481                                        try {
    360                                                 let sheet = document.styleSheets[i];
    361482                                                let rules = sheet.cssRules || sheet.rules;
    362483                                                if (!rules) continue;
    … …  
    364485                                                this.extractFontFaceRules(rules, sheet, font_face_url_map, loaded_font_urls);
    365486                                        } catch (e) {
    366                                                 // Ignore cross-origin stylesheet errors.
    367                                         }
    368                                 }
     487                                                // The rules of cross-origin stylesheets (loaded without the crossorigin attribute) can't be read.
     488                                                if (sheet.href) {
     489                                                        cross_origin_sheets.push(sheet.href);
     490                                                }
     491                                        }
     492                                }
     493
     494                                await this.extractCrossOriginFontFaceRules(cross_origin_sheets, font_face_url_map, loaded_font_urls);
    369495
    370496                                document.fonts.forEach((font) => {
    371497                                        let family = font.family.replace(/["']/g, '');
    372                                         let weight = font.weight;
     498                                        // Font faces often define keywords (e.g. icon fonts use 'normal'), while computed styles are always numeric.
     499                                        let weight = this.normalizeFontWeight(font.weight);
    373500                                        let style = font.style;
    374501                                        let face_id = `${family}-${weight}-${style}`.toLowerCase();
    … …  
    381508                                         * Check if any loaded fonts that are used above the fold are not preloaded.
    382509                                         */
    383                                         if (font.status === 'loaded' && font_url && used_faces_above_the_fold.has(face_id)) {
     510                                        if (font.status === 'loaded' && font_url && this.isFaceUsed(used_faces_above_the_fold, family, weight, style)) {
    384511                                                let is_preloaded = preloaded_fonts.some((url) => {
    385512                                                        // If we have the actual font URL, use it for exact matching.
    … …  
    416543                                         * elements that aren't rendered yet (e.g. tabs, accordions and modals) would be unloaded.
    417544                                         */
    418                                         if (font.status === 'unloaded' && !used_faces_not_rendered.has(face_id) && font_url) {
     545                                        if (font.status === 'unloaded' && !this.isFaceUsed(used_faces_not_rendered, family, weight, style) && font_url) {
    419546                                                unused_fonts.push({
    420547                                                        family: family,
    … …  
    711838                                let text = omgf_frontend.sprintf(omgf_frontend_i18n.info_box_preload_text, count || 0, data.potential_delay_ms || 0, data.impact || omgf_frontend_i18n.info_box_impact_low);
    712839                                info_box.innerHTML = `<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${text}</a>`;
     840                        }
     841
     842                        if (status === 'download_failures') {
     843                                info_box.id = 'wp-admin-bar-omgf-download-failures';
     844                                info_box.classList.add('alert');
     845
     846                                let link = document.createElement('a');
     847                                link.className = 'ab-item';
     848                                link.href = omgf_frontend_i18n.info_box_admin_url;
     849                                link.textContent = data.download_failures_text || '';
     850                                info_box.appendChild(link);
    713851                        }
    714852
  • host-webfonts-local/tags/6.3.12/assets/js/omgf-frontend.min.js

    r3641590 r3721511  
    1 let omgf_font_cls=0,omgf_fonts_ready=!1,omgf_dom_content_loaded="loading"!==document.readyState,omgf_dom_content_loaded_time=omgf_dom_content_loaded?performance.now():0,omgf_cls_supported=!1;omgf_dom_content_loaded||document.addEventListener("DOMContentLoaded",()=>{omgf_dom_content_loaded=!0,omgf_dom_content_loaded_time=performance.now()});try{let e=new PerformanceObserver(()=>{});e.observe({type:"layout-shift",buffered:!1}),e.disconnect(),omgf_cls_supported=!0}catch(e){}if(omgf_cls_supported&&void 0!==document.fonts&&void 0!==document.fonts.ready)try{let e=new PerformanceObserver(e=>{e.getEntries().forEach(e=>{!e.hadRecentInput&&omgf_dom_content_loaded&&!omgf_fonts_ready&&e.startTime>=omgf_dom_content_loaded_time&&(omgf_font_cls+=e.value)})});e.observe({type:"layout-shift",buffered:!0}),document.fonts.ready.then(()=>{omgf_fonts_ready=!0,e.disconnect()})}catch(e){}window.addEventListener("load",()=>{let _={menu_item:document.getElementById("wp-admin-bar-omgf"),sub_menu:document.getElementById("wp-admin-bar-omgf-default"),skip_results:omgf_frontend_results.skip||!1,init:async function(){let e=[],t=[],n=null;try{var o,a=this.filterGoogleFonts(),i=(o=await this.getStatus(a)).unused_fonts_analysis||{},r=(t=o.unused_fonts||[],o.preload_analysis||{}),s=(e=o.missing_preloads||[],o.cls_analysis||{});if(n=o.status||null,null!==this.menu_item&&(this.menu_item.classList.add("dot"),n)&&this.menu_item.classList.add(n),"success"!==n&&"warning"!==n&&this.addInfoBox(n),!_.skip_results){if(omgf_frontend_i18n.multilang_plugin_used){var l=omgf_frontend_i18n.subsets_count;if(1<l){let e=omgf_frontend_i18n.info_box_impact_low;3===l?e=omgf_frontend_i18n.info_box_impact_medium:3<l&&(e=omgf_frontend_i18n.info_box_impact_high);var f={name:omgf_frontend_i18n.multilang_plugin_name,subsets_count:l,impact:e};this.addInfoBox("multilang_plugin",f)}}i&&i.count&&this.addInfoBox("unload_notice",i),r&&0<r.potential_delay_ms&&0<e.length&&this.addInfoBox("preload_notice",r,e.length),s&&.01<=s.cls&&this.addInfoBox("cls_notice",s)}}catch(e){console.error("OMGF - Error running Google Fonts Checker:",e)}finally{document.dispatchEvent(new CustomEvent("omgf_frontend_loaded",{detail:{missing_preloads:e,unused_fonts:t,status:n}}))}},filterGoogleFonts:()=>{var e=window.performance.getEntries().filter(e=>{var t,n=0<e.name.indexOf("/fonts.googleapis.com/css")||0<e.name.indexOf("/fonts.googleapis.com/icon");let o=0<e.name.indexOf("/fonts.gstatic.com/");return o&&(t=/\.(woff2|woff|ttf|otf)(\?.*)?$/i.test(e.name),e=!e.initiatorType||["css","link","other"].includes(e.initiatorType),o=t&&e),n||o});if(0===e.length)return[];console.log("OMGF has found the following Google Fonts API calls:");let t=[];return e.forEach(e=>{t.push(e.name),console.log(" - "+e.name)}),t},getFontFaceProperty:function(e,t){var n=e.style.getPropertyValue(t);return n?n.replace(/["']/g,"").trim():(n=e.cssText.match(new RegExp(t+"\\s*:\\s*([^;]+)","i")))?n[1].replace(/["']/g,"").trim():null},extractFontFaceRules:function(t,o,a,i){for(let e=0;e<t.length;e++){var r=t[e];if(r.type===CSSRule.IMPORT_RULE&&r.styleSheet&&(s=r.styleSheet.cssRules||r.styleSheet.rules)&&this.extractFontFaceRules(s,r.styleSheet,a,i),"CSSFontFaceRule"===r.constructor.name||r.type===CSSRule.FONT_FACE_RULE){var s=this.getFontFaceProperty(r,"font-family");let e=this.getFontFaceProperty(r,"font-weight")||"400";var l,f=this.getFontFaceProperty(r,"font-style")||"normal";"bold"===(e="normal"===e?"400":e)&&(e="700");let t=r.style.getPropertyValue("src")||r.style.src,n=(t=t||this.getFontFaceProperty(r,"src"))?t.match(/url\(["']?([^"')]+\.woff2[^"')]*?)["']?\)/):null;(n=n||(t?t.match(/url\(["']?([^"')]+)["']?\)/):null))&&(l=new URL(n[1],o.href||document.baseURI).href,r=r.style.getPropertyValue("unicode-range")||"",f=(`${s}-${e}-`+f+(r?"-"+r:"")).toLowerCase(),a.has(f)||a.set(f,l),i.has(l))&&a.set(f,l)}}},getStatus:async function(e){var t=new URLSearchParams(window.location.search),t=Object.fromEntries(t.entries());let d=[],m=[];if(void 0!==document.fonts&&void 0!==document.fonts.ready){await document.fonts.ready;let s=[];document.head.querySelectorAll('link[rel="preload"][as="font"]').forEach(e=>{s.push(e.href)});let l=new Set,f=new Set;var o=document.querySelectorAll("body *:not(#wpadminbar):not(#wpadminbar *)");for(let e=0;e<o.length&&e<1500;e++){let a=o[e];var c=a.getBoundingClientRect(),u=window.getComputedStyle(a),g=u.fontFamily.replace(/["']/g,"");let t=u.fontWeight,n=u.fontStyle,i=c.top<window.innerHeight&&0<c.bottom&&c.left<window.innerWidth&&0<c.right,r=0<c.width||0<c.height;g.split(",").forEach(e=>{e=(`${e=e.trim()}-${t}-`+n).toLowerCase();i&&l.add(e),r||f.add(e)}),["::before","::after"].forEach(e=>{var e=window.getComputedStyle(a,e),t=e.fontFamily.replace(/["']/g,"");let n=e.fontWeight,o=e.fontStyle;t.split(",").forEach(e=>{e=(`${e=e.trim()}-${n}-`+o).toLowerCase();i&&l.add(e),r||f.add(e)})})}var n=new Set(window.performance.getEntriesByType("resource").filter(e=>e.name.match(/\.(woff2?|ttf|otf)(\?.*)?$/i)).map(e=>e.name));let _=new Map;for(let e=0;e<document.styleSheets.length;e++)try{var a=document.styleSheets[e],i=a.cssRules||a.rules;i&&this.extractFontFaceRules(i,a,_,n)}catch(e){}document.fonts.forEach(e=>{let n=e.family.replace(/["']/g,"");var t=e.weight,o=e.style,a=(n+`-${t}-`+o).toLowerCase(),i=(a+"-"+(e.unicodeRange||"")).toLowerCase();let r=_.get(i)||_.get(a);"loaded"===e.status&&r&&l.has(a)&&(s.some(e=>{var t;return!(!r||e!==r)||(t=n.toLowerCase().replace(/\s/g,"-"),e=e.toLowerCase(),new RegExp("(^|[/_-])"+t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"([./_-]|$)","i").test(e))})||d.some(e=>e.family===n&&e.url===r)||d.push({family:n,weight:t,style:o,url:r})),"unloaded"===e.status&&!f.has(a)&&r&&m.push({family:n,weight:t,style:o,url:r,unicode_range:e.unicodeRange})})}let r=this.analyzeUnusedFonts(m),s=await this.analyzePreloadImpact(d),l=this.analyzeCLS(omgf_font_cls);var f=new FormData;return f.append("path",document.location.pathname),f.append("urls",JSON.stringify(e)),f.append("params",JSON.stringify(t)),f.append("unused_fonts_analysis",JSON.stringify(r)),f.append("preload_analysis",JSON.stringify(s)),f.append("cls_analysis",JSON.stringify(l)),_.ajax(f).then(e=>(e&&(e.unused_fonts_analysis=r,e.unused_fonts=m,e.preload_analysis=s,e.missing_preloads=d,e.cls_analysis=l),e))},forEachMatchingResources:function(e,n,o){e.forEach(i=>{let t=i.family,r=i.url,s=t.toLowerCase().replace(/\s/g,"-");n.filter(e=>{var t=e.name.toLowerCase();if(r&&e.name===r)return!0;if(!new RegExp("(^|[/_-])"+s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"([./_-]|\\.|$)","i").test(t))return!1;if(void 0===i.weight)return!0;let n=i.weight;var e=i.style,o=("bold"===(n="normal"===n?"400":n)&&(n="700"),new RegExp(`[./_-]${n}([./_-]|\\.|$)`,"i"));let a=!0;return"italic"===e?a=t.includes("italic")||/[._-]i\.(woff|woff2|ttf|otf)/.test(t)||/\d{3}i\.(woff|woff2|ttf|otf)/.test(t):"normal"===e&&(a=!t.includes("italic")),o.test(t)&&a}).forEach(e=>{o(e,t)})})},getFontResources:function(){return window.performance.getEntriesByType("resource").filter(e=>e.name.match(/\.(woff|woff2|ttf|otf)(\?.*)?$/i))},analyzeUnusedFonts:function(e){if(0===e.length)return{};e=e.length;let t=omgf_frontend_i18n.info_box_impact_low;return 6<e?t=omgf_frontend_i18n.info_box_impact_high:3<=e&&(t=omgf_frontend_i18n.info_box_impact_medium),{count:e,impact:t}},analyzeCLS:function(e){let t=omgf_frontend_i18n.info_box_impact_low;e=Math.round(1e3*e)/1e3;return e<.01?{}:(.1<e&&(t=omgf_frontend_i18n.info_box_impact_medium),{cls:e,impact:t=.25<e?omgf_frontend_i18n.info_box_impact_high:t})},analyzePreloadImpact:async function(n){if(0===n.length)return{};let o={affects_lcp:!1,potential_delay_ms:0,impact:omgf_frontend_i18n.info_box_impact_low};try{var a=await new Promise(t=>{let n=new PerformanceObserver(e=>{e=e.getEntries(),e=e[e.length-1];n.disconnect(),t(e)});n.observe({type:"largest-contentful-paint",buffered:!0}),setTimeout(()=>{n.disconnect(),t(null)},2e3)});if(!a)return o;var i=a.element;let e=!1;if(!(e=i?"string"==typeof i.innerText&&0<i.innerText.trim().length||"string"==typeof i.textContent&&0<i.textContent.trim().length:e))return o;var r=this.getFontResources();let t=0;if(this.forEachMatchingResources(n,r,e=>{e=e.responseEnd-e.startTime;0<e&&(o.affects_lcp=!0,e>t)&&(t=e)}),o.potential_delay_ms=Math.round(t),o.potential_delay_ms<=5)return{};100<o.potential_delay_ms?o.impact=omgf_frontend_i18n.info_box_impact_high:20<o.potential_delay_ms&&(o.impact=omgf_frontend_i18n.info_box_impact_medium)}catch(e){return console.warn("OMGF - Error analyzing preload impact:",e),{}}return o},ajax:function(e){return fetch(omgf_frontend_i18n.api_url,{method:"POST",credentials:"same-origin",headers:{"X-WP-Nonce":omgf_frontend_i18n.nonce},body:e}).then(e=>!!e.ok&&e.json())},addInfoBox:function(e,t,n){var o,a=document.createElement("li");a.id="wp-admin-bar-omgf-info","alert"===e&&(a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${omgf_frontend_i18n.info_box_alert_text}</a>`),"notice"===e&&(a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${omgf_frontend_i18n.info_box_notice_text}</a>`),"multilang_plugin"===e&&(a.id="wp-admin-bar-omgf-multilang-info",o=_.sprintf(omgf_frontend_i18n.info_box_multilang_plugin_text,omgf_frontend_i18n.multilang_plugin_name,t.subsets_count,t.impact),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"unload_notice"===e&&(a.id="wp-admin-bar-omgf-unload-info",o=_.sprintf(omgf_frontend_i18n.info_box_unload_text,t.count||0,t.impact||omgf_frontend_i18n.info_box_impact_low),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"preload_notice"===e&&(a.id="wp-admin-bar-omgf-preload-info",o=_.sprintf(omgf_frontend_i18n.info_box_preload_text,n||0,t.potential_delay_ms||0,t.impact||omgf_frontend_i18n.info_box_impact_low),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"cls_notice"===e&&(a.id="wp-admin-bar-omgf-cls-info",n=_.sprintf(omgf_frontend_i18n.info_box_cls_text,t.cls,t.impact),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${n}</a>`),"cls_notice"!==e&&"unload_notice"!==e&&"preload_notice"!==e&&"multilang_plugin"!==e||a.classList.add("info"),"info"===e&&omgf_frontend_i18n.first_run&&document.getElementById("wp-admin-bar-omgf-optimize-this")?.classList.add("info"),_.sub_menu&&_.sub_menu.prepend(a)},sprintf:function(e){let a=arguments,i=1;return e.replace(/%(s|d|0\d+d)/g,function(e,t){var n=a[i++];switch(t){case"s":return n;case"d":return parseInt(n,10);default:var n=String(parseInt(n,10)),o=Number(t.slice(1,-1));return"0".repeat(o).slice(n.length)+n}})}},e=window.performance.getEntries(),t=0,n=setInterval(()=>{t++,((e=e.length<window.performance.getEntries().length?window.performance.getEntries():e).length===window.performance.getEntries().length||20<=t)&&(clearInterval(n),_.init())},500)});
     1let omgf_font_cls=0,omgf_fonts_ready=!1,omgf_dom_content_loaded="loading"!==document.readyState,omgf_dom_content_loaded_time=omgf_dom_content_loaded?performance.now():0,omgf_cls_supported=!1;omgf_dom_content_loaded||document.addEventListener("DOMContentLoaded",()=>{omgf_dom_content_loaded=!0,omgf_dom_content_loaded_time=performance.now()});try{let e=new PerformanceObserver(()=>{});e.observe({type:"layout-shift",buffered:!1}),e.disconnect(),omgf_cls_supported=!0}catch(e){}if(omgf_cls_supported&&void 0!==document.fonts&&void 0!==document.fonts.ready)try{let e=new PerformanceObserver(e=>{e.getEntries().forEach(e=>{!e.hadRecentInput&&omgf_dom_content_loaded&&!omgf_fonts_ready&&e.startTime>=omgf_dom_content_loaded_time&&(omgf_font_cls+=e.value)})});e.observe({type:"layout-shift",buffered:!0}),document.fonts.ready.then(()=>{omgf_fonts_ready=!0,e.disconnect()})}catch(e){}window.addEventListener("load",()=>{let p={menu_item:document.getElementById("wp-admin-bar-omgf"),sub_menu:document.getElementById("wp-admin-bar-omgf-default"),skip_results:omgf_frontend_results.skip||!1,init:async function(){let e=[],t=[],n=null;try{var o,a=this.filterGoogleFonts(),i=(o=await this.getStatus(a)).unused_fonts_analysis||{},r=(t=o.unused_fonts||[],o.preload_analysis||{}),s=(e=o.missing_preloads||[],o.cls_analysis||{}),l=(n=o.status||null,null!==this.menu_item&&(this.menu_item.classList.add("dot"),n)&&this.menu_item.classList.add(n),"alert"===n&&o.download_failures&&!o.google_fonts_checker_alert);if("success"===n||"warning"===n||l||this.addInfoBox(n),o.download_failures&&this.addInfoBox("download_failures",o),!p.skip_results){if(omgf_frontend_i18n.multilang_plugin_used){var f=omgf_frontend_i18n.subsets_count;if(1<f){let e=omgf_frontend_i18n.info_box_impact_low;3===f?e=omgf_frontend_i18n.info_box_impact_medium:3<f&&(e=omgf_frontend_i18n.info_box_impact_high);var d={name:omgf_frontend_i18n.multilang_plugin_name,subsets_count:f,impact:e};this.addInfoBox("multilang_plugin",d)}}i&&i.count&&this.addInfoBox("unload_notice",i),r&&0<r.potential_delay_ms&&0<e.length&&this.addInfoBox("preload_notice",r,e.length),s&&.01<=s.cls&&this.addInfoBox("cls_notice",s)}}catch(e){console.error("OMGF - Error running Google Fonts Checker:",e)}finally{document.dispatchEvent(new CustomEvent("omgf_frontend_loaded",{detail:{missing_preloads:e,unused_fonts:t,status:n}}))}},filterGoogleFonts:()=>{var e=window.performance.getEntries().filter(e=>{var t,n=0<e.name.indexOf("/fonts.googleapis.com/css")||0<e.name.indexOf("/fonts.googleapis.com/icon");let o=0<e.name.indexOf("/fonts.gstatic.com/");return o&&(t=/\.(woff2|woff|ttf|otf)(\?.*)?$/i.test(e.name),e=!e.initiatorType||["css","link","other"].includes(e.initiatorType),o=t&&e),n||o});if(0===e.length)return[];console.log("OMGF has found the following Google Fonts API calls:");let t=[];return e.forEach(e=>{t.push(e.name),console.log(" - "+e.name)}),t},normalizeFontWeight:function(e){return"normal"===(e=String(e||"400").trim().toLowerCase())?"400":"bold"===e?"700":e},isFaceUsed:function(e,t,n,o){if(e.has((t+`-${n}-`+o).toLowerCase()))return!0;var a=n.split(/\s+/).map(Number);if(2===a.length&&!a.some(isNaN))for(var i of e){var r=(t+"-").toLowerCase(),s=("-"+o).toLowerCase();if(i.startsWith(r)&&i.endsWith(s)){r=Number(i.slice(r.length,i.length-s.length));if(r>=a[0]&&r<=a[1])return!0}}return!1},extractCrossOriginFontFaceRules:async function(e,i,r){if("undefined"!=typeof CSSStyleSheet&&"function"==typeof CSSStyleSheet.prototype.replaceSync){e=[...new Set(e)].slice(0,10);await Promise.all(e.map(async e=>{let t="undefined"!=typeof AbortController?new AbortController:null;var n=t?setTimeout(()=>t.abort(),3e3):null;try{var o,a=await fetch(e,{mode:"cors",credentials:"omit",signal:t?t.signal:void 0});a.ok&&((o=new CSSStyleSheet).replaceSync(await a.text()),this.extractFontFaceRules(o.cssRules,{href:e},i,r))}catch(e){}finally{n&&clearTimeout(n)}}))}},getFontFaceProperty:function(e,t){var n=e.style.getPropertyValue(t);return n?n.replace(/["']/g,"").trim():(n=e.cssText.match(new RegExp(t+"\\s*:\\s*([^;]+)","i")))?n[1].replace(/["']/g,"").trim():null},extractFontFaceRules:function(t,n,o,a){for(let e=0;e<t.length;e++){var i=t[e];if(i.type===CSSRule.IMPORT_RULE&&i.styleSheet&&(s=i.styleSheet.cssRules||i.styleSheet.rules)&&this.extractFontFaceRules(s,i.styleSheet,o,a),"CSSFontFaceRule"===i.constructor.name||i.type===CSSRule.FONT_FACE_RULE){var r,s=this.getFontFaceProperty(i,"font-family"),l=this.getFontFaceProperty(i,"font-weight")||"400",f=this.getFontFaceProperty(i,"font-style")||"normal",l=this.normalizeFontWeight(l);let e=i.style.getPropertyValue("src")||i.style.src,t=(e=e||this.getFontFaceProperty(i,"src"))?e.match(/url\(["']?([^"')]+\.woff2[^"')]*?)["']?\)/):null;(t=t||(e?e.match(/url\(["']?([^"')]+)["']?\)/):null))&&(r=new URL(t[1],n.href||document.baseURI).href,f=(s+`-${l}-`+f+((l=i.style.getPropertyValue("unicode-range")||"")?"-"+l:"")).toLowerCase(),o.has(f)||o.set(f,r),a.has(r))&&o.set(f,r)}}},getStatus:async function(e){var t=new URLSearchParams(window.location.search),t=Object.fromEntries(t.entries());let _=[],c=[];if(void 0!==document.fonts&&void 0!==document.fonts.ready){await document.fonts.ready;let s=[];document.head.querySelectorAll('link[rel="preload"][as="font"]').forEach(e=>{s.push(e.href)});let l=new Set,f=new Set;var o=document.querySelectorAll("body *:not(#wpadminbar):not(#wpadminbar *)");for(let e=0;e<o.length&&e<1500;e++){let a=o[e];var m=a.getBoundingClientRect(),u=window.getComputedStyle(a),g=u.fontFamily.replace(/["']/g,"");let t=u.fontWeight,n=u.fontStyle,i=m.top<window.innerHeight&&0<m.bottom&&m.left<window.innerWidth&&0<m.right,r=0<m.width||0<m.height;g.split(",").forEach(e=>{e=(`${e=e.trim()}-${t}-`+n).toLowerCase();i&&l.add(e),r||f.add(e)}),["::before","::after"].forEach(e=>{var e=window.getComputedStyle(a,e),t=e.fontFamily.replace(/["']/g,"");let n=e.fontWeight,o=e.fontStyle;t.split(",").forEach(e=>{e=(`${e=e.trim()}-${n}-`+o).toLowerCase();i&&l.add(e),r||f.add(e)})})}var n=new Set(window.performance.getEntriesByType("resource").filter(e=>e.name.match(/\.(woff2?|ttf|otf)(\?.*)?$/i)).map(e=>e.name));let d=new Map;var a=[];for(let e=0;e<document.styleSheets.length;e++){var i=document.styleSheets[e];try{var r=i.cssRules||i.rules;r&&this.extractFontFaceRules(r,i,d,n)}catch(e){i.href&&a.push(i.href)}}await this.extractCrossOriginFontFaceRules(a,d,n),document.fonts.forEach(e=>{let n=e.family.replace(/["']/g,"");var t=this.normalizeFontWeight(e.weight),o=e.style,a=(n+`-${t}-`+o).toLowerCase(),i=(a+"-"+(e.unicodeRange||"")).toLowerCase();let r=d.get(i)||d.get(a);"loaded"===e.status&&r&&this.isFaceUsed(l,n,t,o)&&(s.some(e=>{var t;return!(!r||e!==r)||(t=n.toLowerCase().replace(/\s/g,"-"),e=e.toLowerCase(),new RegExp("(^|[/_-])"+t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"([./_-]|$)","i").test(e))})||_.some(e=>e.family===n&&e.url===r)||_.push({family:n,weight:t,style:o,url:r})),"unloaded"===e.status&&!this.isFaceUsed(f,n,t,o)&&r&&c.push({family:n,weight:t,style:o,url:r,unicode_range:e.unicodeRange})})}let s=this.analyzeUnusedFonts(c),l=await this.analyzePreloadImpact(_),f=this.analyzeCLS(omgf_font_cls);var d=new FormData;return d.append("path",document.location.pathname),d.append("urls",JSON.stringify(e)),d.append("params",JSON.stringify(t)),d.append("unused_fonts_analysis",JSON.stringify(s)),d.append("preload_analysis",JSON.stringify(l)),d.append("cls_analysis",JSON.stringify(f)),p.ajax(d).then(e=>(e&&(e.unused_fonts_analysis=s,e.unused_fonts=c,e.preload_analysis=l,e.missing_preloads=_,e.cls_analysis=f),e))},forEachMatchingResources:function(e,n,o){e.forEach(i=>{let t=i.family,r=i.url,s=t.toLowerCase().replace(/\s/g,"-");n.filter(e=>{var t=e.name.toLowerCase();if(r&&e.name===r)return!0;if(!new RegExp("(^|[/_-])"+s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"([./_-]|\\.|$)","i").test(t))return!1;if(void 0===i.weight)return!0;let n=i.weight;var e=i.style,o=("bold"===(n="normal"===n?"400":n)&&(n="700"),new RegExp(`[./_-]${n}([./_-]|\\.|$)`,"i"));let a=!0;return"italic"===e?a=t.includes("italic")||/[._-]i\.(woff|woff2|ttf|otf)/.test(t)||/\d{3}i\.(woff|woff2|ttf|otf)/.test(t):"normal"===e&&(a=!t.includes("italic")),o.test(t)&&a}).forEach(e=>{o(e,t)})})},getFontResources:function(){return window.performance.getEntriesByType("resource").filter(e=>e.name.match(/\.(woff|woff2|ttf|otf)(\?.*)?$/i))},analyzeUnusedFonts:function(e){if(0===e.length)return{};e=e.length;let t=omgf_frontend_i18n.info_box_impact_low;return 6<e?t=omgf_frontend_i18n.info_box_impact_high:3<=e&&(t=omgf_frontend_i18n.info_box_impact_medium),{count:e,impact:t}},analyzeCLS:function(e){let t=omgf_frontend_i18n.info_box_impact_low;e=Math.round(1e3*e)/1e3;return e<.01?{}:(.1<e&&(t=omgf_frontend_i18n.info_box_impact_medium),{cls:e,impact:t=.25<e?omgf_frontend_i18n.info_box_impact_high:t})},analyzePreloadImpact:async function(n){if(0===n.length)return{};let o={affects_lcp:!1,potential_delay_ms:0,impact:omgf_frontend_i18n.info_box_impact_low};try{var a=await new Promise(t=>{let n=new PerformanceObserver(e=>{e=e.getEntries(),e=e[e.length-1];n.disconnect(),t(e)});n.observe({type:"largest-contentful-paint",buffered:!0}),setTimeout(()=>{n.disconnect(),t(null)},2e3)});if(!a)return o;var i=a.element;let e=!1;if(!(e=i?"string"==typeof i.innerText&&0<i.innerText.trim().length||"string"==typeof i.textContent&&0<i.textContent.trim().length:e))return o;var r=this.getFontResources();let t=0;if(this.forEachMatchingResources(n,r,e=>{e=e.responseEnd-e.startTime;0<e&&(o.affects_lcp=!0,e>t)&&(t=e)}),o.potential_delay_ms=Math.round(t),o.potential_delay_ms<=5)return{};100<o.potential_delay_ms?o.impact=omgf_frontend_i18n.info_box_impact_high:20<o.potential_delay_ms&&(o.impact=omgf_frontend_i18n.info_box_impact_medium)}catch(e){return console.warn("OMGF - Error analyzing preload impact:",e),{}}return o},ajax:function(e){return fetch(omgf_frontend_i18n.api_url,{method:"POST",credentials:"same-origin",headers:{"X-WP-Nonce":omgf_frontend_i18n.nonce},body:e}).then(e=>!!e.ok&&e.json())},addInfoBox:function(e,t,n){var o,a=document.createElement("li");a.id="wp-admin-bar-omgf-info","alert"===e&&(a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${omgf_frontend_i18n.info_box_alert_text}</a>`),"notice"===e&&(a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${omgf_frontend_i18n.info_box_notice_text}</a>`),"multilang_plugin"===e&&(a.id="wp-admin-bar-omgf-multilang-info",o=p.sprintf(omgf_frontend_i18n.info_box_multilang_plugin_text,omgf_frontend_i18n.multilang_plugin_name,t.subsets_count,t.impact),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"unload_notice"===e&&(a.id="wp-admin-bar-omgf-unload-info",o=p.sprintf(omgf_frontend_i18n.info_box_unload_text,t.count||0,t.impact||omgf_frontend_i18n.info_box_impact_low),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"preload_notice"===e&&(a.id="wp-admin-bar-omgf-preload-info",o=p.sprintf(omgf_frontend_i18n.info_box_preload_text,n||0,t.potential_delay_ms||0,t.impact||omgf_frontend_i18n.info_box_impact_low),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"download_failures"===e&&(a.id="wp-admin-bar-omgf-download-failures",a.classList.add("alert"),(n=document.createElement("a")).className="ab-item",n.href=omgf_frontend_i18n.info_box_admin_url,n.textContent=t.download_failures_text||"",a.appendChild(n)),"cls_notice"===e&&(a.id="wp-admin-bar-omgf-cls-info",o=p.sprintf(omgf_frontend_i18n.info_box_cls_text,t.cls,t.impact),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"cls_notice"!==e&&"unload_notice"!==e&&"preload_notice"!==e&&"multilang_plugin"!==e||a.classList.add("info"),"info"===e&&omgf_frontend_i18n.first_run&&document.getElementById("wp-admin-bar-omgf-optimize-this")?.classList.add("info"),p.sub_menu&&p.sub_menu.prepend(a)},sprintf:function(e){let a=arguments,i=1;return e.replace(/%(s|d|0\d+d)/g,function(e,t){var n=a[i++];switch(t){case"s":return n;case"d":return parseInt(n,10);default:var n=String(parseInt(n,10)),o=Number(t.slice(1,-1));return"0".repeat(o).slice(n.length)+n}})}},e=window.performance.getEntries(),t=0,n=setInterval(()=>{t++,((e=e.length<window.performance.getEntries().length?window.performance.getEntries():e).length===window.performance.getEntries().length||20<=t)&&(clearInterval(n),p.init())},500)});
  • host-webfonts-local/tags/6.3.12/host-webfonts-local.php

    r3707539 r3721511  
    44 * Plugin URI: https://daan.dev/wordpress/omgf/
    55 * Description: Increase GDPR/DSGVO compliance and leverage browser cache by automatically self-hosting Google Fonts.
    6  * Version: 6.3.11
     6 * Version: 6.3.12
    77 * Author: Daan from Daan.dev
    88 * Author URI: https://daan.dev
  • host-webfonts-local/tags/6.3.12/readme.txt

    r3707539 r3721511  
    33Tags: google, fonts, gdpr, dsgvo, cache
    44Requires at least: 5.9
    5 Tested up to: 7.0
    6 Stable tag: 6.3.11
     5Tested up to: 7.1
     6Stable tag: 6.3.12
    77Requires PHP: 7.3
    88License: GPLv2 or later
    … …  
    8787
    8888== Changelog ==
     89
     90= 6.3.12 =
     91* Security: hardened the handling of Google Fonts stylesheet requests.
     92* Security: hardened the handling of downloaded font files.
     93* Security: admin notices are escaped on output.
     94* Security: the redirect after saving settings is restricted to the site's own host.
     95* Security: the stylesheet handle is escaped on the Optimize Fonts screen.
     96* Improved: removed a legacy, hidden option from the settings form.
     97* Security: only administrators can store results or trigger processing through the Admin Bar Menu's status endpoint, even when its permission is widened by a filter. URLs are validated before they're processed.
     98* Security: downloaded font files are now validated before they're stored.
     99* Fixed: the Font Performance Checker didn't detect fonts which should be preloaded if their @font-face statement used a keyword (e.g. font-weight: normal, which most icon fonts use) or a weight range (i.e. variable fonts).
     100* Improved: the Font Performance Checker now also detects fonts which should be preloaded in cross-origin stylesheets loaded without the crossorigin attribute, if the stylesheet's host allows it (most font CDNs do).
     101* Tested with WP 7.1.
    89102
    90103= 6.3.11 =
  • host-webfonts-local/tags/6.3.12/src/API/AdminbarMenu.php

    r3503345 r3721511  
    1919use OMGF\Admin\Dashboard;
    2020use OMGF\Admin\Settings;
     21use OMGF\Download;
    2122use OMGF\Helper as OMGF;
    2223
    … …  
    5556        public function get_admin_bar_status( $request ) {
    5657                $params                = $this->clean( $request->get_params() );
    57                 $stored_results        = $this->update_google_fonts_checker_results( $params );
    5858                $unused_fonts_analysis = $this->decode_json_array( $params['unused_fonts_analysis'] ?? [] );
    5959                $preload_analysis      = $this->decode_json_array( $params['preload_analysis'] ?? [] );
    6060                $cls_analysis          = $this->decode_json_array( $params['cls_analysis'] ?? [] );
    6161
    62                 $this->update_perf_metrics( $params, $unused_fonts_analysis, $preload_analysis, $cls_analysis );
     62                /**
     63                 * @since v6.3.12 The permission for this endpoint can be widened with the omgf_api_adminbar_menu_permission
     64                 *                filter (e.g., to allow visitors to request the status), but only administrators may store
     65                 *                results or trigger any processing through it. Everyone else gets a read-only status.
     66                 */
     67                if ( current_user_can( 'manage_options' ) ) {
     68                        $stored_results = $this->update_google_fonts_checker_results( $params );
     69
     70                        $this->update_perf_metrics( $params, $unused_fonts_analysis, $preload_analysis, $cls_analysis );
     71                } else {
     72                        $stored_results = get_option( Settings::OMGF_DB_GOOGLE_FONTS_CHECKER_RESULTS, [] );
     73                        $stored_results = is_array( $stored_results ) ? $stored_results : [];
     74                }
    6375
    6476                $status = $this->calculate_status( $stored_results, $unused_fonts_analysis, $preload_analysis, $cls_analysis );
    6577
    66                 return [ 'status' => apply_filters( 'omgf_ajax_admin_bar_status', $status ) ];
     78                /**
     79                 * @since v6.3.12 Font files which couldn't be downloaded correctly are reported to administrators only, because
     80                 *                visitors requesting the status (e.g. for Smart Optimize) need the regular status.
     81                 */
     82                $download_failures = current_user_can( 'manage_options' ) ? count( Download::get_failures() ) : 0;
     83
     84                if ( $download_failures > 0 ) {
     85                        $status = 'alert';
     86                }
     87
     88                $response = [ 'status' => apply_filters( 'omgf_ajax_admin_bar_status', $status ) ];
     89
     90                if ( $download_failures > 0 ) {
     91                        $response['download_failures']          = $download_failures;
     92                        $response['download_failures_text']     = sprintf(
     93                        /* translators: %d: number of font files */
     94                                _n(
     95                                        '%d font file couldn\'t be downloaded correctly',
     96                                        '%d font files couldn\'t be downloaded correctly',
     97                                        $download_failures,
     98                                        'host-webfonts-local'
     99                                ),
     100                                $download_failures
     101                        );
     102                        $response['google_fonts_checker_alert'] = ! empty( $stored_results );
     103                }
     104
     105                return $response;
    67106        }
    68107
    … …  
    135174                        $urls = [];
    136175                }
     176
     177                /**
     178                 * @since v6.3.12 Validate the URLs before they're passed to any filter.
     179                 */
     180                $urls = array_filter(
     181                        $urls,
     182                        function ( $url ) {
     183                                return is_string( $url ) && filter_var( $url, FILTER_VALIDATE_URL );
     184                        }
     185                );
    137186
    138187                $urls        = apply_filters( 'omgf_ajax_results', $urls, $params, $path );
  • host-webfonts-local/tags/6.3.12/src/API/Dashboard.php

    r3489128 r3721511  
    1818
    1919use OMGF\Admin\Settings;
     20use OMGF\Download;
    2021
    2122class Dashboard {
    … …  
    6768                        ]
    6869                );
     70
     71                register_rest_route(
     72                        $this->namespace,
     73                        '/' . $this->base . '/dismiss-download-failures',
     74                        [
     75                                [
     76                                        'methods'             => 'POST',
     77                                        'callback'            => [ $this, 'dismiss_download_failures' ],
     78                                        'permission_callback' => [ $this, 'get_permission' ],
     79                                ],
     80                                'schema' => null,
     81                        ]
     82                );
    6983        }
    7084
    … …  
    90104                return new \WP_REST_Response( [ 'success' => true ] );
    91105        }
     106
     107        /**
     108         * Dismiss the font files which couldn't be downloaded correctly.
     109         *
     110         * @since v6.3.12
     111         *
     112         * @return \WP_REST_Response
     113         */
     114        public function dismiss_download_failures() {
     115                Download::clear_failures();
     116
     117                return new \WP_REST_Response( [ 'success' => true ] );
     118        }
    92119}
  • host-webfonts-local/tags/6.3.12/src/Admin/Actions.php

    r3707539 r3721511  
    202202                );
    203203
     204                /**
     205                 * @since v6.3.12 These options are generated by OMGF and should never be updated using the settings form.
     206                 */
     207                $read_only_options = [
     208                        Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS,
     209                        Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS_FRONTEND,
     210                ];
     211
    204212                foreach ( $updated_settings as $option_name => $option_value ) {
    205213                        if ( ! str_starts_with( $option_name, 'omgf_' ) || ( empty( $option_value ) && $option_value !== '0' ) ) {
     214                                continue;
     215                        }
     216
     217                        if ( in_array( $option_name, $read_only_options, true ) ) {
    206218                                continue;
    207219                        }
    … …  
    258270
    259271                if ( ! defined( 'DAAN_DOING_TESTS' ) ) {
    260                         wp_redirect( $goback ); // @codeCoverageIgnore
     272                        wp_safe_redirect( $goback ); // @codeCoverageIgnore
    261273                        exit; // @codeCoverageIgnore
    262274                }
  • host-webfonts-local/tags/6.3.12/src/Admin/Dashboard.php

    r3628684 r3721511  
    1717namespace OMGF\Admin;
    1818
     19use OMGF\Download;
    1920use OMGF\Helper as OMGF;
    2021
    … …  
    101102
    102103                return ob_get_clean();
     104        }
     105
     106        /**
     107         * Renders the font files which couldn't be downloaded correctly.
     108         *
     109         * @since v6.3.12
     110         *
     111         * @return void
     112         */
     113        public static function render_download_failures() {
     114                $failures = Download::get_failures();
     115
     116                if ( empty( $failures ) ) {
     117                        return;
     118                }
     119                ?>
     120                <div class="task-manager-notice alert" id="omgf-download-failures-notice">
     121                        <h4><?php echo wp_kses_post(
     122                                        sprintf(
     123                                        /* translators: %1$s: plugin name, %2$d: number of font files */
     124                                                _n(
     125                                                        '%1$s couldn\'t download %2$d font file correctly.',
     126                                                        '%1$s couldn\'t download %2$d font files correctly.',
     127                                                        count( $failures ),
     128                                                        'host-webfonts-local'
     129                                                ),
     130                                                apply_filters( 'omgf_settings_page_title', 'OMGF' ),
     131                                                count( $failures )
     132                                        )
     133                                ); ?></h4>
     134                        <ul>
     135                                <?php foreach ( $failures as $url => $failure ) : ?>
     136                                        <li>
     137                                                <strong><?php echo esc_html( $url ); ?></strong><br/>
     138                                                <?php echo esc_html( Download::get_failure_message( $failure['reason'] ) ); ?>
     139                                        </li>
     140                                <?php endforeach; ?>
     141                        </ul>
     142                        <p>
     143                                [ <a href="#" id="omgf-dismiss-download-failures"><?php echo esc_html__( 'Dismiss', 'host-webfonts-local' ); ?></a> ]
     144                        </p>
     145                </div>
     146                <?php
    103147        }
    104148
    … …  
    141185                                        unset( $warnings['google_fonts_checker'] );
    142186                                }
     187
     188                                self::render_download_failures();
    143189                                ?>
    144190                                <?php if ( ! empty( $google_fonts_checker_results ) ): ?>
  • host-webfonts-local/tags/6.3.12/src/Admin/Notice.php

    r3494741 r3721511  
    4141                                foreach ( $notice as $type => $message ) {
    4242                                        ?>
    43                                         <div id="message" class="notice notice-<?php echo $type; ?> is-dismissible">
     43                                        <div id="message" class="notice notice-<?php echo esc_attr( $type ); ?> is-dismissible">
    4444                                                <?php foreach ( $message as $line ) : ?>
    45                                                         <p><strong><?php echo $line; ?></strong></p>
     45                                                        <p><strong><?php echo wp_kses_post( $line ); ?></strong></p>
    4646                                                <?php endforeach; ?>
    4747                                        </div>
  • host-webfonts-local/tags/6.3.12/src/Admin/Settings.php

    r3503345 r3721511  
    3535
    3636        const OMGF_DB_PERF_CHECK = 'omgf_perf_check';
     37
     38        const OMGF_DB_DOWNLOAD_FAILURES = 'omgf_download_failures';
    3739
    3840        const OMGF_DB_CACHE_TIMESTAMP = 'omgf_cache_timestamp';
  • host-webfonts-local/tags/6.3.12/src/Admin/Settings/Optimize.php

    r3531267 r3721511  
    269269                                        }
    270270                                        ?>
    271                                         <tbody class="stylesheet" id="<?php echo $handle; ?>">
     271                                        <tbody class="stylesheet" id="<?php echo esc_attr( $handle ); ?>">
    272272                                        <tr>
    273273                                                <th colspan="6"><?php echo sprintf(
    274274                                                                __( 'Stylesheet handle: %s', 'host-webfonts-local' ),
    275                                                                 $handle
     275                                                                esc_html( $handle )
    276276                                                        ); ?></th>
    277277                                        </tr>
    … …  
    390390                                <?php endforeach; ?>
    391391                        </table>
    392                         <input type="hidden" name="<?php echo Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS; ?>"
    393                                    value="<?php echo base64_encode( serialize( $this->optimized_fonts ) ); ?>"/>
    394392                        <input id="<?php echo Settings::OMGF_OPTIMIZE_SETTING_UNLOAD_STYLESHEETS; ?>" type="hidden"
    395393                                   name="omgf_settings[<?php echo Settings::OMGF_OPTIMIZE_SETTING_UNLOAD_STYLESHEETS; ?>]"
  • host-webfonts-local/tags/6.3.12/src/Download.php

    r3707539 r3721511  
    1919use OMGF\Helper as OMGF;
    2020use OMGF\Admin\Notice;
     21use OMGF\Admin\Settings;
    2122
    2223class Download {
    … …  
    3940        ];
    4041
     42        /**
     43         * Default maximum size (in bytes) of a downloaded font file.
     44         *
     45         * @since v6.3.12
     46         */
     47        const MAX_FILE_SIZE = 25 * MB_IN_BYTES;
     48
     49        /**
     50         * Maximum number of failed downloads reported on the Dashboard.
     51         *
     52         * @since v6.3.12
     53         */
     54        const MAX_FAILURES = 20;
     55
     56        /**
     57         * Reasons why a downloaded file was discarded.
     58         */
     59        const FAILURE_EMPTY = 'empty';
     60
     61        const FAILURE_TOO_LARGE = 'too_large';
     62
     63        const FAILURE_INVALID = 'invalid';
     64
    4165
    4266        /** @var string $url */
    … …  
    5781                string $path
    5882        ) {
    59                 $this->url      = $url;
    60                 $this->filename = $filename;
     83                $this->url = $url;
     84                /**
     85                 * @since v6.3.12 Sanitize the filename so a font family (which it's partly built from) can't contain a
     86                 *                path separator and write the downloaded file outside of $path.
     87                 */
     88                $this->filename = sanitize_file_name( $filename );
    6189                $this->path     = $path;
    6290        }
    … …  
    82110                $temp_filename = $this->path . '/' . $this->filename . '-' . wp_generate_password( 12, false ) . '.tmp';
    83111
    84                 $response = wp_safe_remote_get(
     112                $max_file_size = self::get_max_file_size();
     113                $response      = wp_safe_remote_get(
    85114                        $this->url,
    86115                        [
    87                                 'timeout'  => 300,
    88                                 'stream'   => true,
    89                                 'filename' => $temp_filename,
     116                                'timeout'             => 300,
     117                                'stream'              => true,
     118                                'filename'            => $temp_filename,
     119                                /**
     120                                 * @since v6.3.12 Stop downloading once the file exceeds the maximum size. One extra byte is requested, so
     121                                 *                a file which was cut off can be told apart from a file of exactly the maximum size.
     122                                 */
     123                                'limit_response_size' => $max_file_size + 1,
    90124                        ]
    91125                );
    … …  
    161195                }
    162196
     197                /**
     198                 * @since v6.3.12 Validate the downloaded file before it's stored.
     199                 */
     200                $reason = $this->validate_file( $temp_filename, $max_file_size );
     201
     202                if ( $reason ) {
     203                        $this->delete_temp_file( $temp_filename );
     204
     205                        /**
     206                         * Reported on the Dashboard (and in the Admin Bar), because downloads often run during a visitor's request.
     207                         *
     208                         * @see \OMGF\Admin\Dashboard::render_download_failures()
     209                         */
     210                        self::add_failure( $this->url, $reason );
     211
     212                        return '';
     213                }
     214
    163215                if ( file_exists( $temp_filename ) ) {
    164216                        $final_path = $this->path . '/' . $this->filename . '.' . $extension;
    … …  
    179231                }
    180232
     233                self::remove_failure( $this->url );
     234
    181235                return OMGF_UPLOAD_URL . str_replace( OMGF_UPLOAD_DIR, '', $this->path ) . '/' . $this->filename . '.' . $extension;
     236        }
     237
     238        /**
     239         * Maximum size (in bytes) of a downloaded font file.
     240         *
     241         * @since  v6.3.12
     242         * @filter omgf_download_max_file_size
     243         *
     244         * @return int
     245         */
     246        public static function get_max_file_size() {
     247                return max( 1, (int) apply_filters( 'omgf_download_max_file_size', self::MAX_FILE_SIZE ) );
     248        }
     249
     250        /**
     251         * Checks if $file is a complete font file, i.e. not empty, not cut off because it exceeds the maximum size, and
     252         * starting with the signature of a font format.
     253         *
     254         * @since v6.3.12
     255         *
     256         * @param string $file
     257         * @param int    $max_file_size
     258         *
     259         * @return string An error message if the file is invalid, an empty string if it's valid.
     260         */
     261        private function validate_file( $file, $max_file_size ) {
     262                $size = file_exists( $file ) ? filesize( $file ) : 0;
     263
     264                if ( ! $size ) {
     265                        return self::FAILURE_EMPTY;
     266                }
     267
     268                if ( $size > $max_file_size ) {
     269                        return self::FAILURE_TOO_LARGE;
     270                }
     271
     272                if ( ! self::is_font_file( $file ) ) {
     273                        return self::FAILURE_INVALID;
     274                }
     275
     276                return '';
     277        }
     278
     279        /**
     280         * Failed downloads, most recent first.
     281         *
     282         * @since v6.3.12
     283         *
     284         * @return array [ url => [ 'reason' => string, 'time' => int ] ]
     285         */
     286        public static function get_failures() {
     287                return self::normalize_failures( get_option( Settings::OMGF_DB_DOWNLOAD_FAILURES, [] ) );
     288        }
     289
     290        /**
     291         * Removes invalid entries and sorts the failed downloads, most recent first.
     292         *
     293         * @since v6.3.12
     294         *
     295         * @param mixed $failures
     296         *
     297         * @return array
     298         */
     299        private static function normalize_failures( $failures ) {
     300                if ( ! is_array( $failures ) ) {
     301                        return []; // @codeCoverageIgnore
     302                }
     303
     304                $failures = array_filter(
     305                        $failures,
     306                        function ( $failure, $url ) {
     307                                return is_string( $url ) && is_array( $failure ) && in_array( $failure['reason'] ?? '', self::get_failure_reasons(), true );
     308                        },
     309                        ARRAY_FILTER_USE_BOTH
     310                );
     311
     312                uasort(
     313                        $failures,
     314                        function ( $a, $b ) {
     315                                return ( $b['time'] ?? 0 ) <=> ( $a['time'] ?? 0 );
     316                        }
     317                );
     318
     319                return $failures;
     320        }
     321
     322        /**
     323         * Stores a failed download, keeping at most MAX_FAILURES (the most recent ones).
     324         *
     325         * @since v6.3.12
     326         *
     327         * @param string $url
     328         * @param string $reason One of the FAILURE_* constants.
     329         *
     330         * @return void
     331         */
     332        public static function add_failure( $url, $reason ) {
     333                self::update_failures(
     334                        function ( $failures ) use ( $url, $reason ) {
     335                                unset( $failures[ $url ] );
     336
     337                                return array_slice( [ $url => [ 'reason' => $reason, 'time' => time() ] ] + $failures, 0, self::MAX_FAILURES, true );
     338                        }
     339                );
     340        }
     341
     342        /**
     343         * Removes a failed download, e.g. once it succeeds.
     344         *
     345         * @since v6.3.12
     346         *
     347         * @param string $url
     348         *
     349         * @return void
     350         */
     351        public static function remove_failure( $url ) {
     352                /**
     353                 * This runs after every successful download, so only lock when there's something to remove. The check reads the
     354                 * database, because a cached copy might not contain a failure stored by another request.
     355                 */
     356                if ( ! isset( self::read_stored_failures()[ $url ] ) ) {
     357                        return;
     358                }
     359
     360                self::update_failures(
     361                        function ( $failures ) use ( $url ) {
     362                                unset( $failures[ $url ] );
     363
     364                                return $failures;
     365                        }
     366                );
     367        }
     368
     369        /**
     370         * Removes all failed downloads, i.e. when they're dismissed.
     371         *
     372         * @since v6.3.12
     373         *
     374         * @return void
     375         */
     376        public static function clear_failures() {
     377                self::update_failures(
     378                        function () {
     379                                return [];
     380                        }
     381                );
     382        }
     383
     384        /**
     385         * Reads the failed downloads from the database, instead of a (possibly outdated) cached copy.
     386         *
     387         * @since v6.3.12
     388         *
     389         * @return array
     390         */
     391        private static function read_stored_failures() {
     392                global $wpdb;
     393
     394                $stored = $wpdb->get_var(
     395                        $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s", Settings::OMGF_DB_DOWNLOAD_FAILURES )
     396                );
     397
     398                return self::normalize_failures( $stored === null ? [] : maybe_unserialize( $stored ) );
     399        }
     400
     401        /**
     402         * Updates the failed downloads while holding a (MySQL) lock, based on the value currently stored in the database,
     403         * so concurrent downloads can't overwrite each other's changes.
     404         *
     405         * @since v6.3.12
     406         *
     407         * @param callable $callback Receives the current failed downloads and returns the new ones.
     408         *
     409         * @return void
     410         */
     411        private static function update_failures( $callback ) {
     412                global $wpdb;
     413
     414                $lock_name = $wpdb->prefix . Settings::OMGF_DB_DOWNLOAD_FAILURES;
     415                // Databases which don't support locks (e.g. SQLite) return null, in which case we proceed without one.
     416                $locked = (bool) $wpdb->get_var( $wpdb->prepare( 'SELECT GET_LOCK(%s, %d)', $lock_name, 5 ) );
     417
     418                try {
     419                        $failures = $callback( self::read_stored_failures() );
     420
     421                        wp_cache_delete( Settings::OMGF_DB_DOWNLOAD_FAILURES, 'options' );
     422                        wp_cache_delete( 'notoptions', 'options' );
     423
     424                        if ( empty( $failures ) ) {
     425                                delete_option( Settings::OMGF_DB_DOWNLOAD_FAILURES );
     426                        } else {
     427                                update_option( Settings::OMGF_DB_DOWNLOAD_FAILURES, $failures, false );
     428                        }
     429                } finally {
     430                        if ( $locked ) {
     431                                $wpdb->query( $wpdb->prepare( 'SELECT RELEASE_LOCK(%s)', $lock_name ) );
     432                        }
     433                }
     434        }
     435
     436        /**
     437         * @since v6.3.12
     438         *
     439         * @return string[]
     440         */
     441        private static function get_failure_reasons() {
     442                return [ self::FAILURE_EMPTY, self::FAILURE_TOO_LARGE, self::FAILURE_INVALID ];
     443        }
     444
     445        /**
     446         * A human-readable description of why a download failed, and what the user can do about it.
     447         *
     448         * @since v6.3.12
     449         *
     450         * @param string $reason One of the FAILURE_* constants.
     451         *
     452         * @return string
     453         */
     454        public static function get_failure_message( $reason ) {
     455                switch ( $reason ) {
     456                        case self::FAILURE_TOO_LARGE:
     457                                return sprintf(
     458                                /* translators: %s: maximum file size, e.g. 25 MB */
     459                                        __( 'The file exceeds the maximum file size of %s, so it isn\'t used and a fallback font is shown instead.', 'host-webfonts-local' ),
     460                                        size_format( self::get_max_file_size() )
     461                                );
     462                        case self::FAILURE_INVALID:
     463                                return __( 'The downloaded file isn\'t a font file (e.g. an error page), so a fallback font is shown until it\'s downloaded successfully. Click Save & Optimize to try again.', 'host-webfonts-local' );
     464                        default:
     465                                return __( 'The downloaded file was empty, so a fallback font is shown until it\'s downloaded successfully. Click Save & Optimize to try again.', 'host-webfonts-local' );
     466                }
     467        }
     468
     469        /**
     470         * Checks if $file starts with the signature (magic bytes) of a WOFF2, WOFF, TrueType, OpenType or EOT file.
     471         *
     472         * @since v6.3.12
     473         *
     474         * @param string $file
     475         *
     476         * @return bool
     477         */
     478        public static function is_font_file( $file ) {
     479                $handle = @fopen( $file, 'rb' ); // phpcs:ignore
     480
     481                if ( ! $handle ) {
     482                        return false; // @codeCoverageIgnore
     483                }
     484
     485                $header = (string) fread( $handle, 36 );
     486
     487                fclose( $handle );
     488
     489                // WOFF2, WOFF, OpenType (CFF), TrueType, TrueType (Apple), TrueType Collection.
     490                if ( in_array( substr( $header, 0, 4 ), [ 'wOF2', 'wOFF', 'OTTO', "\x00\x01\x00\x00", 'true', 'ttcf' ], true ) ) {
     491                        return true;
     492                }
     493
     494                // EOT: magic number 0x504C at offset 34.
     495                return strlen( $header ) >= 36 && substr( $header, 34, 2 ) === 'LP';
    182496        }
    183497
  • host-webfonts-local/tags/6.3.12/src/Filters.php

    r3531267 r3721511  
    2828                add_filter( 'home_url', [ $this, 'force_ssl' ], 1000, 2 );
    2929                add_filter( 'omgf_optimize_user_agent', [ $this, 'maybe_do_legacy_mode' ] );
    30                 add_filter( 'pre_update_option_omgf_optimized_fonts', [ $this, 'base64_decode_optimized_fonts' ] );
    3130                add_filter( 'omgf_do_not_load_frontend_css', [ $this, 'maybe_load_frontend_assets' ] );
    32         }
    33 
    34         /**
    35          * @since v5.0.5 omgf_optimized_fonts is base64_encoded in the frontend, to bypass firewall restrictions on
    36          * some servers.
    37          *
    38          * @param $old_value
    39          * @param $value
    40          *
    41          * @return bool|array
    42          */
    43         public function base64_decode_optimized_fonts( $value ) {
    44                 if ( is_string( $value ) && base64_encode( base64_decode( $value, true ) ) === $value ) {
    45                         return base64_decode( $value );
    46                 }
    47 
    48                 return $value;
    4931        }
    5032
  • host-webfonts-local/tags/6.3.12/src/Frontend/Process.php

    r3707539 r3721511  
    629629                         *                just fine. Using a negated character class (instead of adding the /s modifier) makes
    630630                         *                sure the match can never run past a quote character.
    631                          */
    632                         preg_match( '/href=([\'"])(?P<href>[^\'"]*)\1/', $link, $href );
     631                         * @since v6.3.12 Only match the href attribute when it starts a new attribute (after whitespace or the
     632                         *                tag opener), so an attribute whose name ends in "href" (e.g. data-href, x:href,
     633                         *                x.href) isn't captured as the href.
     634                         */
     635                        preg_match( '/(?:^|[\s<])href=([\'"])(?P<href>[^\'"]*)\1/', $link, $href );
    633636
    634637                        /**
    … …  
    734737
    735738                        /**
     739                         * @since v6.3.12 Only process (and request) stylesheets whose URL is actually hosted on the Google
     740                         *                Fonts API (or a compatible endpoint). The element was kept because one of its
     741                         *                attribute values points at the API (see get_element_urls()), but the href used
     742                         *                here could point at a different — e.g. internal — host. Checking here, before the
     743                         *                branches below, leaves a non-API link untouched (not removed, not swapped for a
     744                         *                cached file) and makes sure it's never requested, which prevents SSRF.
     745                         */
     746                        if ( ! $this->is_font_api_url( $url ) ) {
     747                                continue;
     748                        }
     749
     750                        /**
    736751                         * If the stylesheet with $handle is completely marked for unloading, just remove the element
    737752                         * to prevent it from loading.
    … …  
    795810
    796811                        $search[ $key ]  = $stack['href'];
    797                         $replace[ $key ] = $cached_url ? $cached_url . '?ver=' . $this->timestamp : '';
     812                        /**
     813                         * @since v6.3.12 If optimization produced no URL (e.g. the request was blocked or redirected, the
     814                         *                fetch failed, or the response wasn't a stylesheet), leave the original link
     815                         *                untouched instead of removing it, so the fonts keep loading.
     816                         */
     817                        $replace[ $key ] = $cached_url ? $cached_url . '?ver=' . $this->timestamp : $stack['href'];
    798818                }
    799819
  • host-webfonts-local/tags/6.3.12/src/Helper.php

    r3707539 r3721511  
    133133
    134134        /**
     135         * Unserializes $value if it's serialized, like maybe_unserialize(), but only allows stdClass objects, because
     136         * OMGF never stores any other objects.
     137         *
     138         * @since v6.3.12
     139         *
     140         * @param mixed $value
     141         *
     142         * @return mixed
     143         */
     144        public static function maybe_unserialize( $value ) {
     145                if ( ! is_string( $value ) || ! is_serialized( $value ) ) {
     146                        return $value;
     147                }
     148
     149                return @unserialize( trim( $value ), [ 'allowed_classes' => [ 'stdClass' ] ] ); // phpcs:ignore
     150        }
     151
     152        /**
    135153         * Method to retrieve OMGF's settings from database.
    136154         * WARNING: DO NOT ATTEMPT TO RETRIEVE WP CORE SETTINGS USING THIS METHOD. IT WILL FAIL.
    … …  
    151169                        // get_option() should take care of this, but sometimes it doesn't.
    152170                        if ( is_string( $value ) ) {
    153                                 $value = maybe_unserialize( $value );
     171                                $value = self::maybe_unserialize( $value );
    154172                        }
    155173
    … …  
    292310                }
    293311
     312                /**
     313                 * @since v6.3.12 A font family is used to build the filename its files are stored under, so reject any
     314                 *                path separator to make sure a crafted name can't traverse out of the fonts directory.
     315                 *                Dots are left alone: a font family can legitimately contain them, and without a
     316                 *                separator they can't traverse (the download filename is sanitized regardless).
     317                 */
     318                if ( preg_match( '#[/\\\\]#', $family ) ) {
     319                        return false; // @codeCoverageIgnore
     320                }
     321
    294322                // A CSS custom property, e.g. --wp--preset--font-family--foo.
    295323                if ( str_starts_with( $family, '--' ) ) {
    … …  
    534562                 */
    535563                if ( is_string( self::$admin_optimized_fonts ) && self::$admin_optimized_fonts !== '' ) {
    536                         self::$admin_optimized_fonts = maybe_unserialize( self::$admin_optimized_fonts ); // @codeCoverageIgnore
     564                        self::$admin_optimized_fonts = self::maybe_unserialize( self::$admin_optimized_fonts ); // @codeCoverageIgnore
    537565                }
    538566
    … …  
    698726                 */
    699727                if ( is_string( self::$optimized_fonts ) && self::$optimized_fonts !== '' ) {
    700                         self::$optimized_fonts = maybe_unserialize( self::$optimized_fonts ); // @codeCoverageIgnore
     728                        self::$optimized_fonts = self::maybe_unserialize( self::$optimized_fonts ); // @codeCoverageIgnore
    701729                }
    702730
  • host-webfonts-local/tags/6.3.12/src/Optimize.php

    r3707539 r3721511  
    298298                /** @codeCoverageIgnoreEnd */
    299299
    300                 $response = wp_remote_get(
     300                /**
     301                 * @since v6.3.12 Use wp_safe_remote_get() so internal/private hosts are rejected even if the host
     302                 *                allow-list is ever bypassed. Belt-and-braces against SSRF.
     303                 */
     304                $response = wp_safe_remote_get(
    301305                        $url,
    302306                        [
     307                                /**
     308                                 * @since v6.3.12 Never follow redirects: the Google Fonts API (and compatible CDNs) serve the
     309                                 *                stylesheet directly, so a redirect would only point the request somewhere else.
     310                                 */
     311                                'redirection' => 0,
    303312                                /**
    304313                                 * Allow WP devs to use a different User-Agent, e.g. for compatibility with older browsers/OSes.
  • host-webfonts-local/trunk/assets/css/omgf-admin.css

    r3489128 r3721511  
    563563        width: 96px;
    564564}
     565
     566/* Font files which couldn't be downloaded correctly. */
     567.omgf form .omgf-task-manager #omgf-download-failures-notice ul {
     568        margin-left: 0;
     569}
     570
     571.omgf form .omgf-task-manager #omgf-download-failures-notice ul li {
     572        text-indent: 0;
     573        margin-bottom: 0.5em;
     574}
  • host-webfonts-local/trunk/assets/css/omgf-admin.min.css

    r3489128 r3721511  
    1 @import url(omgf-admin-google-fonts.min.css);#wpwrap{background-color:rgb(248 250 252 / 1)}.omgf{font-family:"Public Sans",Arial,sans-serif;max-width:1350px}.omgf h1 .pro{background-color:#00adda;padding:0 5px 0 6px;border-radius:5px;text-transform:uppercase;color:#fff}.omgf h1{font-family:Poppins,Verdana,sans-serif;font-weight:800}.omgf h2,.omgf h3,.omgf h4,.omgf h5,.omgf h6{font-family:Poppins,Verdana,sans-serif;font-weight:500}.omgf .button{font-family:Poppins,Verdana,sans-serif;border-radius:10px;font-weight:500}.omgf .button-cancel{color:#a00;text-decoration:none;border-color:transparent;box-shadow:none;background:0 0;white-space:nowrap}.omgf .button-cancel.omgf-refresh{color:#ff851b}.omgf .button-cancel.omgf-empty,.omgf .button-cancel.omgf-refresh{padding:1px 10px!important}.omgf .button-cancel:hover{cursor:pointer;background:#d54e21;color:#fff;border-color:#d54e21}.omgf .button-cancel:hover#omgf-refresh{color:#fff;background:#ff851b;border-color:#ff851b}.omgf #submit{background:#00adda;border:0;padding:7px 20px}.omgf #submit:hover{background-color:#09c}.omgf h2.omgf-nav{border-bottom:0;padding-bottom:3px}.omgf h2.omgf-nav a{border:0;background-color:transparent;font-weight:600}.omgf h2.omgf-nav .nav-tab{color:#000}.omgf h2.omgf-nav .nav-tab:last-child{float:right}.omgf h2.omgf-nav .nav-tab-active,.omgf h2.omgf-nav .nav-tab:hover{color:#00adda}.omgf-nav .dashicons-before:before{margin-right:.25em;line-height:1.15em}.omgf form{max-width:1350px}.omgf form .dashicons{line-height:15px}.omgf form .omgf-task-manager{padding:5px 25px;margin-bottom:30px;box-shadow:0 10px 20px 1px #ccc;border:0;border-top:4px solid #00adda;border-radius:10px}.omgf form .omgf-task-manager .form-table{margin-bottom:1em}.omgf form .omgf-task-manager th.omgf-align-row-header{padding:.5em 0}.omgf form .omgf-task-manager .task-manager-row{padding:0;line-height:1.33}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice,.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{padding:18px 12px;border:1px solid #ececec;border-left:4px solid #ececec;border-radius:3px;margin-bottom:1em}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{padding:10px 18px 2px}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend .omgf-cache-legend-item{display:grid;grid-template-columns:1.5rem 9rem auto}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend .omgf-cache-legend-item .omgf-cache-legend-item-title{font-weight:700}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice ol{margin-left:1.5em;margin-bottom:0;overflow-wrap:anywhere}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.warning,.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{border:1px solid #ffdc00;border-left:4px solid #ffdc00;background-color:#fffbdf}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.success{border:1px solid #2ecc40;border-left:4px solid #2ecc40;background-color:#ddf7e0}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.info{border:1px solid #00adda;border-left:4px solid #00adda;background-color:#e0edf3}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.alert{border:1px solid #ff4136;border-left:4px solid #ff4136;background-color:#ff41362B}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice h4{margin-top:0}.omgf form .omgf-task-manager ul{margin-top:5px;list-style:none;margin-left:.5em;padding-left:0}.omgf form .omgf-task-manager ul li{padding-left:0;text-indent:-.5em;line-height:1.5em}.omgf form .omgf-task-manager ul li:before{font-family:dashicons,sans-serif;font-size:16px;vertical-align:-3px}.omgf form .omgf-task-manager ul li.stale:before{content:"\f534";color:#ff851b}.omgf form .omgf-task-manager ul li.found:before{content:"\f15e";color:#2ecc40}.omgf form .omgf-task-manager ul li.not-found:before{content:"\f223";color:#00adda}.omgf form .omgf-task-manager ul li.stale-cache-notice:before{content:"\f348";color:#00adda}.omgf form .omgf-task-manager ul li.unloaded:before{content:"\f460";color:#0074d9}.omgf form .advanced-processing-notice,.omgf form .used-subsets-notice{display:block;padding:18px 12px;margin-bottom:.5em;border:1px solid #ececec;border-left:4px solid #ececec}.omgf form .advanced-processing-notice.info,.omgf form .used-subsets-notice.info{border:1px solid #00adda;border-left:4px solid #00adda;background-color:#e0edf3}.omgf-optimize-fonts-container{padding:5px 25px 50px;margin:2.5em auto 2em;overflow:visible;border:0;border-top:4px solid #00adda;border-radius:10px;box-shadow:0 10px 20px 1px #ccc}.omgf-optimize-fonts-container .form-table tr{position:relative}.omgf-optimize-fonts-container .form-table tr td{padding:20px 0}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage{margin-top:1.5em;text-align:left}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage #omgf-optimize-preload-warning{position:fixed;top:50%;left:33%;width:33%;padding:20px 40px;background-color:#fff5b5;border:2px solid #ffdc00;border-radius:4px;box-shadow:1px 1px 333px #333;z-index:1000}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage #omgf-optimize-preload-warning .omgf-optimize-preload-warning-close{float:right;font-size:2em;line-height:.45;cursor:pointer}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table{width:100%;border-collapse:collapse}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table thead th{width:13%;padding:4px 0 9px}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody{border-top:4px solid #00adda}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody th{font-family:Poppins,sans-serif;font-weight:600;padding:9px 0 6px;width:40%}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td{padding-bottom:5px;width:13%}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .unload-mass-action{font-size:.9em}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .unload-mass-action:hover{cursor:pointer}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .family{font-weight:600}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody tr:last-child td{padding-bottom:10px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros{margin-right:10px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-cons,.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros{text-align:left;width:48%;display:inline-block;vertical-align:top}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-cons span:before,.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros span:before{vertical-align:-4px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description label{font-size:18px;font-weight:700}.omgf-optimize-fonts-container .omgf-optimize-fonts-tooltip{margin:0 0 30px}.omgf-optimize-fonts-container .omgf-optimize-fonts-tooltip ul li{line-height:1.5em}.omgf-help-section .about,.omgf-optimize-fonts-container p,.omgf-optimize-fonts-container ul,.omgf-task-manager p{text-align:justify;font-size:14px}.tooltip{position:relative;font-size:16px;line-height:16px;height:12px}.tooltip.omgf-save-optimize{vertical-align:middle}.tooltip .tooltip-text{visibility:hidden;position:absolute;top:100%;left:100%;z-index:10000;background-color:rgba(0,0,0,.75);font-family:"Public Sans",Arial,sans-serif;font-size:.8em;color:#fff;line-height:1.5;width:250px;padding:20px;margin-left:-125px;border-radius:10px}.tooltip .tooltip-text a{color:#fff}.tooltip .tooltip-text .illustration{margin-top:10px}.tooltip:hover .tooltip-text{visibility:visible}.omgf-warning{color:red;font-weight:600}#omgf-advanced-settings-form{background-color:#fff;border-radius:10px;padding:0 20px 20px;border-top:4px solid #00adda;box-shadow:0 10px 20px 1px #ccc}#omgf-help-form .postbox{padding:5px 25px 10px;border:0;border-top:4px solid #00adda;border-radius:10px;box-shadow:0 10px 20px 1px #ccc}#omgf-help-form .postbox .column-container{display:flex;flex-flow:row wrap;justify-content:space-between}#omgf-help-form .postbox .column-container .column ul li{margin-bottom:10px}#omgf-help-form .postbox .column-container .column ul li:last-child{margin-bottom:0}#omgf-help-form .postbox .column-container .column ul li a{text-decoration:none;line-height:1.45}#omgf-help-form .postbox .column-container .column ul li a i.dashicons{margin-right:5px;color:#646970}#omgf-help-form .postbox .column-container .column.last{margin-right:45px}#omgf-help-form .postbox .column-container .column.last .signature{text-align:center}#omgf-help-form .postbox .column-container .column.last .signature .dashicons-heart{color:#ff4136}#omgf-help-form .postbox .column-container .column.last .signature .signature-image{width:185px}@media only screen and (max-width:782px){.omgf-optimize-fonts-container{margin:20px 0}.omgf-optimize-fonts-container .option-title{position:relative;left:initial;top:initial}}.omgf-loading{position:fixed;background-color:rgba(255,255,255,.6);width:100%;height:100%;top:0;text-align:center}.omgf-loading .spinner{top:50%;left:50%;position:absolute;-ms-transform:translateX(-50%);transform:translateX(-50%)}#wpfooter{font-family:"Public Sans",Arial,sans-serif}#footer-thankyou a{vertical-align:-.65em}#footer-thankyou .dashicons.ffwp-heart{vertical-align:middle}#footer-thankyou .dashicons.ffwp-heart:before{color:#ff4136}#footer-thankyou .signature-image{width:96px}
     1@import url(omgf-admin-google-fonts.min.css);#wpwrap{background-color:rgb(248 250 252 / 1)}.omgf{font-family:"Public Sans",Arial,sans-serif;max-width:1350px}.omgf h1 .pro{background-color:#00adda;padding:0 5px 0 6px;border-radius:5px;text-transform:uppercase;color:#fff}.omgf h1{font-family:Poppins,Verdana,sans-serif;font-weight:800}.omgf h2,.omgf h3,.omgf h4,.omgf h5,.omgf h6{font-family:Poppins,Verdana,sans-serif;font-weight:500}.omgf .button{font-family:Poppins,Verdana,sans-serif;border-radius:10px;font-weight:500}.omgf .button-cancel{color:#a00;text-decoration:none;border-color:transparent;box-shadow:none;background:0 0;white-space:nowrap}.omgf .button-cancel.omgf-refresh{color:#ff851b}.omgf .button-cancel.omgf-empty,.omgf .button-cancel.omgf-refresh{padding:1px 10px!important}.omgf .button-cancel:hover{cursor:pointer;background:#d54e21;color:#fff;border-color:#d54e21}.omgf .button-cancel:hover#omgf-refresh{color:#fff;background:#ff851b;border-color:#ff851b}.omgf #submit{background:#00adda;border:0;padding:7px 20px}.omgf #submit:hover{background-color:#09c}.omgf h2.omgf-nav{border-bottom:0;padding-bottom:3px}.omgf h2.omgf-nav a{border:0;background-color:transparent;font-weight:600}.omgf h2.omgf-nav .nav-tab{color:#000}.omgf h2.omgf-nav .nav-tab:last-child{float:right}.omgf h2.omgf-nav .nav-tab-active,.omgf h2.omgf-nav .nav-tab:hover{color:#00adda}.omgf-nav .dashicons-before:before{margin-right:.25em;line-height:1.15em}.omgf form{max-width:1350px}.omgf form .dashicons{line-height:15px}.omgf form .omgf-task-manager{padding:5px 25px;margin-bottom:30px;box-shadow:0 10px 20px 1px #ccc;border:0;border-top:4px solid #00adda;border-radius:10px}.omgf form .omgf-task-manager .form-table{margin-bottom:1em}.omgf form .omgf-task-manager th.omgf-align-row-header{padding:.5em 0}.omgf form .omgf-task-manager .task-manager-row{padding:0;line-height:1.33}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice,.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{padding:18px 12px;border:1px solid #ececec;border-left:4px solid #ececec;border-radius:3px;margin-bottom:1em}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{padding:10px 18px 2px}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend .omgf-cache-legend-item{display:grid;grid-template-columns:1.5rem 9rem auto}.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend .omgf-cache-legend-item .omgf-cache-legend-item-title{font-weight:700}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice ol{margin-left:1.5em;margin-bottom:0;overflow-wrap:anywhere}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.warning,.omgf form .omgf-task-manager .task-manager-row.omgf-cache-legend{border:1px solid #ffdc00;border-left:4px solid #ffdc00;background-color:#fffbdf}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.success{border:1px solid #2ecc40;border-left:4px solid #2ecc40;background-color:#ddf7e0}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.info{border:1px solid #00adda;border-left:4px solid #00adda;background-color:#e0edf3}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice.alert{border:1px solid #ff4136;border-left:4px solid #ff4136;background-color:#ff41362B}.omgf form .omgf-task-manager .task-manager-row .task-manager-notice h4{margin-top:0}.omgf form .omgf-task-manager ul{margin-top:5px;list-style:none;margin-left:.5em;padding-left:0}.omgf form .omgf-task-manager ul li{padding-left:0;text-indent:-.5em;line-height:1.5em}.omgf form .omgf-task-manager ul li:before{font-family:dashicons,sans-serif;font-size:16px;vertical-align:-3px}.omgf form .omgf-task-manager ul li.stale:before{content:"\f534";color:#ff851b}.omgf form .omgf-task-manager ul li.found:before{content:"\f15e";color:#2ecc40}.omgf form .omgf-task-manager ul li.not-found:before{content:"\f223";color:#00adda}.omgf form .omgf-task-manager ul li.stale-cache-notice:before{content:"\f348";color:#00adda}.omgf form .omgf-task-manager ul li.unloaded:before{content:"\f460";color:#0074d9}.omgf form .advanced-processing-notice,.omgf form .used-subsets-notice{display:block;padding:18px 12px;margin-bottom:.5em;border:1px solid #ececec;border-left:4px solid #ececec}.omgf form .advanced-processing-notice.info,.omgf form .used-subsets-notice.info{border:1px solid #00adda;border-left:4px solid #00adda;background-color:#e0edf3}.omgf-optimize-fonts-container{padding:5px 25px 50px;margin:2.5em auto 2em;overflow:visible;border:0;border-top:4px solid #00adda;border-radius:10px;box-shadow:0 10px 20px 1px #ccc}.omgf-optimize-fonts-container .form-table tr{position:relative}.omgf-optimize-fonts-container .form-table tr td{padding:20px 0}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage{margin-top:1.5em;text-align:left}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage #omgf-optimize-preload-warning{position:fixed;top:50%;left:33%;width:33%;padding:20px 40px;background-color:#fff5b5;border:2px solid #ffdc00;border-radius:4px;box-shadow:1px 1px 333px #333;z-index:1000}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage #omgf-optimize-preload-warning .omgf-optimize-preload-warning-close{float:right;font-size:2em;line-height:.45;cursor:pointer}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table{width:100%;border-collapse:collapse}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table thead th{width:13%;padding:4px 0 9px}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody{border-top:4px solid #00adda}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody th{font-family:Poppins,sans-serif;font-weight:600;padding:9px 0 6px;width:40%}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td{padding-bottom:5px;width:13%}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .unload-mass-action{font-size:.9em}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .unload-mass-action:hover{cursor:pointer}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody td .family{font-weight:600}.omgf-optimize-fonts-container .omgf-optimize-fonts-manage table tbody tr:last-child td{padding-bottom:10px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros{margin-right:10px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-cons,.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros{text-align:left;width:48%;display:inline-block;vertical-align:top}.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-cons span:before,.omgf-optimize-fonts-container .omgf-optimize-fonts-description .omgf-optimize-fonts-pros span:before{vertical-align:-4px}.omgf-optimize-fonts-container .omgf-optimize-fonts-description label{font-size:18px;font-weight:700}.omgf-optimize-fonts-container .omgf-optimize-fonts-tooltip{margin:0 0 30px}.omgf-optimize-fonts-container .omgf-optimize-fonts-tooltip ul li{line-height:1.5em}.omgf-help-section .about,.omgf-optimize-fonts-container p,.omgf-optimize-fonts-container ul,.omgf-task-manager p{text-align:justify;font-size:14px}.tooltip{position:relative;font-size:16px;line-height:16px;height:12px}.tooltip.omgf-save-optimize{vertical-align:middle}.tooltip .tooltip-text{visibility:hidden;position:absolute;top:100%;left:100%;z-index:10000;background-color:rgba(0,0,0,.75);font-family:"Public Sans",Arial,sans-serif;font-size:.8em;color:#fff;line-height:1.5;width:250px;padding:20px;margin-left:-125px;border-radius:10px}.tooltip .tooltip-text a{color:#fff}.tooltip .tooltip-text .illustration{margin-top:10px}.tooltip:hover .tooltip-text{visibility:visible}.omgf-warning{color:red;font-weight:600}#omgf-advanced-settings-form{background-color:#fff;border-radius:10px;padding:0 20px 20px;border-top:4px solid #00adda;box-shadow:0 10px 20px 1px #ccc}#omgf-help-form .postbox{padding:5px 25px 10px;border:0;border-top:4px solid #00adda;border-radius:10px;box-shadow:0 10px 20px 1px #ccc}#omgf-help-form .postbox .column-container{display:flex;flex-flow:row wrap;justify-content:space-between}#omgf-help-form .postbox .column-container .column ul li{margin-bottom:10px}#omgf-help-form .postbox .column-container .column ul li:last-child{margin-bottom:0}#omgf-help-form .postbox .column-container .column ul li a{text-decoration:none;line-height:1.45}#omgf-help-form .postbox .column-container .column ul li a i.dashicons{margin-right:5px;color:#646970}#omgf-help-form .postbox .column-container .column.last{margin-right:45px}#omgf-help-form .postbox .column-container .column.last .signature{text-align:center}#omgf-help-form .postbox .column-container .column.last .signature .dashicons-heart{color:#ff4136}#omgf-help-form .postbox .column-container .column.last .signature .signature-image{width:185px}@media only screen and (max-width:782px){.omgf-optimize-fonts-container{margin:20px 0}.omgf-optimize-fonts-container .option-title{position:relative;left:initial;top:initial}}.omgf-loading{position:fixed;background-color:rgba(255,255,255,.6);width:100%;height:100%;top:0;text-align:center}.omgf-loading .spinner{top:50%;left:50%;position:absolute;-ms-transform:translateX(-50%);transform:translateX(-50%)}#wpfooter{font-family:"Public Sans",Arial,sans-serif}#footer-thankyou a{vertical-align:-.65em}#footer-thankyou .dashicons.ffwp-heart{vertical-align:middle}#footer-thankyou .dashicons.ffwp-heart:before{color:#ff4136}#footer-thankyou .signature-image{width:96px}.omgf form .omgf-task-manager #omgf-download-failures-notice ul{margin-left:0}.omgf form .omgf-task-manager #omgf-download-failures-notice ul li{text-indent:0;margin-bottom:.5em}
  • host-webfonts-local/trunk/assets/css/omgf-frontend.css

    r3489128 r3721511  
    132132        color: white !important;
    133133}
     134
     135#wp-admin-bar-omgf .alert {
     136        background-color: #FF4136 !important;
     137}
     138
     139#wp-admin-bar-omgf .alert .ab-item {
     140        color: white !important;
     141}
  • host-webfonts-local/trunk/assets/css/omgf-frontend.min.css

    r3489128 r3721511  
    1 #wp-admin-bar-omgf.dot>.ab-item:after{content:"";width:10px;height:10px;margin-left:5px;border-radius:50%;background-color:grey;display:inline-block;-webkit-animation:2.5s infinite waiting;-moz-animation:2.5s infinite waiting;-o-animation:2.5s infinite waiting;animation:2.5s infinite waiting}@keyframes waiting{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:grey}}#wp-admin-bar-omgf-info>.ab-item{color:#fff!important}#wp-admin-bar-omgf.dot.success>.ab-item:after{background-color:#2ecc40;animation:none}#wp-admin-bar-omgf.dot.alert>.ab-item:after{background-color:#ff4136;-webkit-animation:2.5s blinking-alert;-moz-animation:2.5s blinking-alert;-o-animation:2.5s blinking-alert;animation:2.5s blinking-alert}@keyframes blinking-alert{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ff4136}}#wp-admin-bar-omgf.dot.warning>.ab-item:after{background-color:#ff851b;-webkit-animation:2.5s blinking-warning;-moz-animation:2.5s blinking-warning;-o-animation:2.5s blinking-warning;animation:2.5s blinking-warning}@keyframes blinking-warning{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ff851b}}#wp-admin-bar-omgf.dot.notice>.ab-item:after{background-color:#ffdc00;-webkit-animation:2.5s blinking-notice;-moz-animation:2.5s blinking-notice;-o-animation:2.5s blinking-notice;animation:2.5s blinking-notice}@keyframes blinking-notice{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ffdc00}}#wp-admin-bar-omgf.dot.info>.ab-item:after{background-color:#0074d9;-webkit-animation:2.5s blinking-info;-moz-animation:2.5s blinking-info;-o-animation:2.5s blinking-info;animation:2.5s blinking-info}@keyframes blinking-info{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#0074d9}}#wp-admin-bar-omgf.alert #wp-admin-bar-omgf-info{background-color:#ff4136!important}#wp-admin-bar-omgf.notice #wp-admin-bar-omgf-info{background-color:#ffdc00!important}#wp-admin-bar-omgf.info #wp-admin-bar-omgf-first-run,#wp-admin-bar-omgf.info #wp-admin-bar-omgf-info{background-color:#0074d9!important}#wp-admin-bar-omgf.notice #wp-admin-bar-omgf-info .ab-item{color:#000!important}#wp-admin-bar-omgf .info{background-color:#0074d9!important}#wp-admin-bar-omgf .info .ab-item{color:#fff!important}
     1#wp-admin-bar-omgf.dot>.ab-item:after{content:"";width:10px;height:10px;margin-left:5px;border-radius:50%;background-color:grey;display:inline-block;-webkit-animation:2.5s infinite waiting;-moz-animation:2.5s infinite waiting;-o-animation:2.5s infinite waiting;animation:2.5s infinite waiting}@keyframes waiting{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:grey}}#wp-admin-bar-omgf-info>.ab-item{color:#fff!important}#wp-admin-bar-omgf.dot.success>.ab-item:after{background-color:#2ecc40;animation:none}#wp-admin-bar-omgf.dot.alert>.ab-item:after{background-color:#ff4136;-webkit-animation:2.5s blinking-alert;-moz-animation:2.5s blinking-alert;-o-animation:2.5s blinking-alert;animation:2.5s blinking-alert}@keyframes blinking-alert{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ff4136}}#wp-admin-bar-omgf.dot.warning>.ab-item:after{background-color:#ff851b;-webkit-animation:2.5s blinking-warning;-moz-animation:2.5s blinking-warning;-o-animation:2.5s blinking-warning;animation:2.5s blinking-warning}@keyframes blinking-warning{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ff851b}}#wp-admin-bar-omgf.dot.notice>.ab-item:after{background-color:#ffdc00;-webkit-animation:2.5s blinking-notice;-moz-animation:2.5s blinking-notice;-o-animation:2.5s blinking-notice;animation:2.5s blinking-notice}@keyframes blinking-notice{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#ffdc00}}#wp-admin-bar-omgf.dot.info>.ab-item:after{background-color:#0074d9;-webkit-animation:2.5s blinking-info;-moz-animation:2.5s blinking-info;-o-animation:2.5s blinking-info;animation:2.5s blinking-info}@keyframes blinking-info{12.5%,37.5%,67.5%,87.5%{background-color:transparent}0%,100%,25%,50%,75%{background-color:#0074d9}}#wp-admin-bar-omgf.alert #wp-admin-bar-omgf-info{background-color:#ff4136!important}#wp-admin-bar-omgf.notice #wp-admin-bar-omgf-info{background-color:#ffdc00!important}#wp-admin-bar-omgf.info #wp-admin-bar-omgf-first-run,#wp-admin-bar-omgf.info #wp-admin-bar-omgf-info{background-color:#0074d9!important}#wp-admin-bar-omgf.notice #wp-admin-bar-omgf-info .ab-item{color:#000!important}#wp-admin-bar-omgf .info{background-color:#0074d9!important}#wp-admin-bar-omgf .info .ab-item{color:#fff!important}#wp-admin-bar-omgf .alert{background-color:#ff4136!important}#wp-admin-bar-omgf .alert .ab-item{color:#fff!important}
  • host-webfonts-local/trunk/assets/js/omgf-admin.js

    r3503345 r3721511  
    5454                        $('.omgf-optimize-forbidden').on('click', this.wait_for_page_reload);
    5555                        $(document).on('click', '#omgf-dismiss-performance-checker-notice', this.dismiss_performance_checker_notice);
     56                        $(document).on('click', '#omgf-dismiss-download-failures', this.dismiss_download_failures);
    5657                        // Ticker
    5758                        setInterval(this.loop_ticker_items, 4000);
    … …  
    480481
    481482                /**
     483                 * Dismiss the font files which couldn't be downloaded correctly.
     484                 */
     485                dismiss_download_failures: function (e) {
     486                        e.preventDefault();
     487
     488                        $.ajax({
     489                                type: 'POST',
     490                                url: omgf_admin_i18n.rest_url + '/dashboard/dismiss-download-failures',
     491                                beforeSend: function (xhr) {
     492                                        xhr.setRequestHeader('X-WP-Nonce', omgf_admin_i18n.nonce);
     493                                        omgf_admin.show_loader();
     494                                },
     495                                complete: function (result) {
     496                                        if (result.responseJSON !== undefined && result.responseJSON.success) {
     497                                                $('#omgf-download-failures-notice').fadeOut(300, function () {
     498                                                        $(this).remove();
     499                                                });
     500                                        }
     501
     502                                        omgf_admin.hide_loader();
     503                                }
     504                        });
     505                },
     506
     507                /**
    482508                 *
    483509                 */
  • host-webfonts-local/trunk/assets/js/omgf-admin.min.js

    r3503345 r3721511  
    1 jQuery(document).ready(function(d){var r={ticker_items:document.querySelectorAll(".ticker-item"),ticker_index:0,empty_cache_directory_xhr:!1,optimize_xhr:!1,cache_prefix:"-mod-",flush_action_init:d(".omgf-empty").data("init"),empty_action_nonce:d(".omgf-empty").data("nonce"),delete_log_xhr:!1,delete_action_nonce:d("#omgf-delete-log").data("nonce"),init:function(){d(".omgf-subsets").on("change",this.maybe_select_latin),d(".omgf-optimize-fonts-manage .unload").on("change",this.maybe_unload_stylesheets),d(".omgf-optimize-fonts-manage .unload, .omgf-optimize-fonts-manage .fallback-font-stack select").on("change",this.generate_cache_key),d(".omgf-optimize-fonts-manage .unload").on("change",this.toggle_preload),d(".omgf-optimize-fonts-manage .preload").on("change",this.toggle_unload),d(".omgf-optimize-fonts-manage .preload").on("change",this.maybe_show_preload_warning),d(".omgf-optimize-fonts-manage .unload-italics").on("click",this.unload_italics),d(".omgf-optimize-fonts-manage .unload-all").on("click",this.unload_all),d(".omgf-optimize-fonts-manage .load-all").on("click",this.load_all),d("#omgf-save-optimize, #omgf-optimize-again").on("click",function(){d("#omgf-optimize-settings-form #submit").click()}),d(document).on("click","a[id^=omgf-hide-notice-]",this.hide_notice),d(".omgf-remove-stylesheet").on("click",this.remove_stylesheet_from_db),d(".omgf-refresh, #omgf-cache-refresh").on("click",this.refresh_cache),d(".omgf-empty, #omgf-cache-flush").on("click",this.empty_cache_directory),d("#omgf-optimize-settings-form").on("submit",this.show_loader_before_submit),d("#omgf-delete-log").on("click",this.delete_log),d(".omgf-optimize-preload-warning-close").on("click",this.hide_preload_warning),d(".omgf-optimize-forbidden").on("click",this.wait_for_page_reload),d(document).on("click","#omgf-dismiss-performance-checker-notice",this.dismiss_performance_checker_notice),setInterval(this.loop_ticker_items,4e3)},maybe_select_latin:function(e){let o=this.value,t=e.target,n=t.className,a=["latin","latin-ext"];"latin-ext"===o&&a.forEach(function(e){document.querySelector("."+n+" option[value="+e+"]").selected=!0}),a=["latin","vietnamese"],"vietnamese"===o&&a.forEach(function(e){document.querySelector("."+n+" option[value="+e+"]").selected=!0})},loop_ticker_items:function(){r.ticker_items.forEach(function(e,o){o==r.ticker_index?d(e).fadeIn(500):d(e).hide(0)}),r.ticker_index++,r.ticker_index===r.ticker_items.length&&(r.ticker_index=0)},hide_notice:function(){var e=d(this).data("warning-id"),o=d(this).data("nonce");d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_hide_notice",warning_id:e,nonce:o},beforeSend:function(){r.show_loader()},complete:function(e){void 0!==e.responseJSON&&void 0!==e.responseJSON.data&&d("#task-manager-notice-row").replaceWith(e.responseJSON.data),r.hide_loader()}})},remove_stylesheet_from_db:function(){var e=d(this).data("handle");d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_remove_stylesheet_from_db",handle:e,nonce:r.empty_action_nonce},beforeSend:function(){r.show_loader()},complete:function(){location.reload()}})},refresh_cache:function(){d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_refresh_cache",nonce:r.empty_action_nonce},beforeSend:function(){d(".omgf-optimize-fonts-manage table tbody").each(function(e,o){r.generate_cache_key(o)})},complete:function(){d("#submit").click()}})},maybe_unload_stylesheets:function(){var e=d(this).closest("tbody")[0].id;d("tbody#"+e+" input.unload:checked").length===d("tbody#"+e+" input.unload").length?r.unload_stylesheet(e):r.load_stylesheet(e)},unload_stylesheet:function(e){var o=d("#unload_stylesheets"),t=o.val().split(",");-1===t.indexOf(e)&&t.push(e),t.join(),o.val(t)},load_stylesheet:function(e){var o=d("#unload_stylesheets"),t=o.val().split(",");~(position=t.indexOf(e))&&t.splice(position,1),o.val(t)},generate_cache_key:function(e=null){t=void 0===e.target?d(e).attr("id"):d(this).data("handle");var t,o,e=d("#cache_keys"),n=e.val().split(","),a=d("#"+t+" input.unload:checked").length,i=d("#"+t+" input.unload").length,c=n.findIndex((e,o)=>{if(-1!==e.indexOf(t))return!0}),l=!1;"SELECT"===this.nodeName||0!==a&&a!==i||(n[c]=t,l=!0),!0!==l&&-1!==c&&(a=n[c],i=r.cache_prefix+Math.random().toString(36).substring(2,7),-1!==a.indexOf(r.cache_prefix)&&((l=a.split(r.cache_prefix))[o=r.get_last_element_index(l)]=Math.random().toString(36).substring(2,7),a=l[0],i=r.cache_prefix+l[o]),n[c]=a+i),e.val(n.join())},toggle_preload:function(){r.toggle(this,"preload")},toggle_unload:function(){r.toggle(this,"unload")},maybe_show_preload_warning:function(){5===d("."+this.className+":checked").length&&d("#omgf-optimize-preload-warning").fadeIn("300")},hide_preload_warning:function(){d("#omgf-optimize-preload-warning").fadeOut("100")},wait_for_page_reload:function(e){e.preventDefault(),r.show_loader();var o=window.setInterval(function(){document.hasFocus()&&(window.location.reload(),window.clearInterval(o))},500);window.open(this.href,"_blank")},unload_italics:function(e){e.preventDefault();var t=d(this).parents(".font-family").data("id");d(".unload").each(function(e,o){o.value.includes("italic")&&o.dataset.fontId===t&&!1===o.checked&&o.click()})},unload_all:function(e,o=this){o=d(o).parents(".font-family").data("id");d('input.unload[data-font-id="'+o+'"]').each(function(e,o){!1===o.checked&&o.click()})},load_all:function(){var e=d(this).parents(".font-family").data("id");d('input.unload[data-font-id="'+e+'"]').each(function(e,o){!0===o.checked&&o.click()})},toggle:function(e,o){var t=d(e),t=d("."+o+"-"+t.data("font-id")+"-"+t.val()+" ."+o);e.checked?t.attr("disabled",!0):t.attr("disabled",!1)},get_last_element_index:function(e){return e.length-1},empty_cache_directory:function(){r.empty_cache_directory_xhr&&r.empty_cache_directory_xhr.abort(),r.empty_cache_directory_xhr=d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_empty_dir",nonce:r.empty_action_nonce,init:r.flush_action_init},beforeSend:function(){r.show_loader()},complete:function(){location.reload()}})},delete_log:function(){r.delete_log_xhr&&r.delete_log_xhr.abort(),r.delete_log_xhr=d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_delete_log",nonce:r.delete_action_nonce},beforeSend:function(){r.show_loader()},complete:function(){location.reload()}})},show_loader_before_submit:function(){r.show_loader()},hide_loader:function(){d(".omgf-loading").fadeOut(300,function(){d(".omgf-loading").remove()})},dismiss_performance_checker_notice:function(e){e.preventDefault(),d.ajax({type:"POST",url:omgf_admin_i18n.rest_url+"/dashboard/dismiss-notice",beforeSend:function(e){e.setRequestHeader("X-WP-Nonce",omgf_admin_i18n.nonce),r.show_loader()},complete:function(e){void 0!==e.responseJSON&&e.responseJSON.success&&d("#omgf-performance-checker-notice").fadeOut(300,function(){d(this).remove()}),r.hide_loader()}})},show_loader:function(){d("#wpwrap").append('<div class="omgf-loading"><span class="spinner is-active"></span></div>')}};omgf_show_loader=r.show_loader,omgf_hide_loader=r.hide_loader,omgf_unload_all=r.unload_all,omgf_load_all=r.load_all,omgf_unload_stylesheet=r.unload_stylesheet,omgf_load_stylesheet=r.load_stylesheet,r.init()});
     1jQuery(document).ready(function(d){var s={ticker_items:document.querySelectorAll(".ticker-item"),ticker_index:0,empty_cache_directory_xhr:!1,optimize_xhr:!1,cache_prefix:"-mod-",flush_action_init:d(".omgf-empty").data("init"),empty_action_nonce:d(".omgf-empty").data("nonce"),delete_log_xhr:!1,delete_action_nonce:d("#omgf-delete-log").data("nonce"),init:function(){d(".omgf-subsets").on("change",this.maybe_select_latin),d(".omgf-optimize-fonts-manage .unload").on("change",this.maybe_unload_stylesheets),d(".omgf-optimize-fonts-manage .unload, .omgf-optimize-fonts-manage .fallback-font-stack select").on("change",this.generate_cache_key),d(".omgf-optimize-fonts-manage .unload").on("change",this.toggle_preload),d(".omgf-optimize-fonts-manage .preload").on("change",this.toggle_unload),d(".omgf-optimize-fonts-manage .preload").on("change",this.maybe_show_preload_warning),d(".omgf-optimize-fonts-manage .unload-italics").on("click",this.unload_italics),d(".omgf-optimize-fonts-manage .unload-all").on("click",this.unload_all),d(".omgf-optimize-fonts-manage .load-all").on("click",this.load_all),d("#omgf-save-optimize, #omgf-optimize-again").on("click",function(){d("#omgf-optimize-settings-form #submit").click()}),d(document).on("click","a[id^=omgf-hide-notice-]",this.hide_notice),d(".omgf-remove-stylesheet").on("click",this.remove_stylesheet_from_db),d(".omgf-refresh, #omgf-cache-refresh").on("click",this.refresh_cache),d(".omgf-empty, #omgf-cache-flush").on("click",this.empty_cache_directory),d("#omgf-optimize-settings-form").on("submit",this.show_loader_before_submit),d("#omgf-delete-log").on("click",this.delete_log),d(".omgf-optimize-preload-warning-close").on("click",this.hide_preload_warning),d(".omgf-optimize-forbidden").on("click",this.wait_for_page_reload),d(document).on("click","#omgf-dismiss-performance-checker-notice",this.dismiss_performance_checker_notice),d(document).on("click","#omgf-dismiss-download-failures",this.dismiss_download_failures),setInterval(this.loop_ticker_items,4e3)},maybe_select_latin:function(e){let o=this.value,t=e.target,n=t.className,a=["latin","latin-ext"];"latin-ext"===o&&a.forEach(function(e){document.querySelector("."+n+" option[value="+e+"]").selected=!0}),a=["latin","vietnamese"],"vietnamese"===o&&a.forEach(function(e){document.querySelector("."+n+" option[value="+e+"]").selected=!0})},loop_ticker_items:function(){s.ticker_items.forEach(function(e,o){o==s.ticker_index?d(e).fadeIn(500):d(e).hide(0)}),s.ticker_index++,s.ticker_index===s.ticker_items.length&&(s.ticker_index=0)},hide_notice:function(){var e=d(this).data("warning-id"),o=d(this).data("nonce");d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_hide_notice",warning_id:e,nonce:o},beforeSend:function(){s.show_loader()},complete:function(e){void 0!==e.responseJSON&&void 0!==e.responseJSON.data&&d("#task-manager-notice-row").replaceWith(e.responseJSON.data),s.hide_loader()}})},remove_stylesheet_from_db:function(){var e=d(this).data("handle");d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_remove_stylesheet_from_db",handle:e,nonce:s.empty_action_nonce},beforeSend:function(){s.show_loader()},complete:function(){location.reload()}})},refresh_cache:function(){d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_refresh_cache",nonce:s.empty_action_nonce},beforeSend:function(){d(".omgf-optimize-fonts-manage table tbody").each(function(e,o){s.generate_cache_key(o)})},complete:function(){d("#submit").click()}})},maybe_unload_stylesheets:function(){var e=d(this).closest("tbody")[0].id;d("tbody#"+e+" input.unload:checked").length===d("tbody#"+e+" input.unload").length?s.unload_stylesheet(e):s.load_stylesheet(e)},unload_stylesheet:function(e){var o=d("#unload_stylesheets"),t=o.val().split(",");-1===t.indexOf(e)&&t.push(e),t.join(),o.val(t)},load_stylesheet:function(e){var o=d("#unload_stylesheets"),t=o.val().split(",");~(position=t.indexOf(e))&&t.splice(position,1),o.val(t)},generate_cache_key:function(e=null){t=void 0===e.target?d(e).attr("id"):d(this).data("handle");var t,o,e=d("#cache_keys"),n=e.val().split(","),a=d("#"+t+" input.unload:checked").length,i=d("#"+t+" input.unload").length,c=n.findIndex((e,o)=>{if(-1!==e.indexOf(t))return!0}),l=!1;"SELECT"===this.nodeName||0!==a&&a!==i||(n[c]=t,l=!0),!0!==l&&-1!==c&&(a=n[c],i=s.cache_prefix+Math.random().toString(36).substring(2,7),-1!==a.indexOf(s.cache_prefix)&&((l=a.split(s.cache_prefix))[o=s.get_last_element_index(l)]=Math.random().toString(36).substring(2,7),a=l[0],i=s.cache_prefix+l[o]),n[c]=a+i),e.val(n.join())},toggle_preload:function(){s.toggle(this,"preload")},toggle_unload:function(){s.toggle(this,"unload")},maybe_show_preload_warning:function(){5===d("."+this.className+":checked").length&&d("#omgf-optimize-preload-warning").fadeIn("300")},hide_preload_warning:function(){d("#omgf-optimize-preload-warning").fadeOut("100")},wait_for_page_reload:function(e){e.preventDefault(),s.show_loader();var o=window.setInterval(function(){document.hasFocus()&&(window.location.reload(),window.clearInterval(o))},500);window.open(this.href,"_blank")},unload_italics:function(e){e.preventDefault();var t=d(this).parents(".font-family").data("id");d(".unload").each(function(e,o){o.value.includes("italic")&&o.dataset.fontId===t&&!1===o.checked&&o.click()})},unload_all:function(e,o=this){o=d(o).parents(".font-family").data("id");d('input.unload[data-font-id="'+o+'"]').each(function(e,o){!1===o.checked&&o.click()})},load_all:function(){var e=d(this).parents(".font-family").data("id");d('input.unload[data-font-id="'+e+'"]').each(function(e,o){!0===o.checked&&o.click()})},toggle:function(e,o){var t=d(e),t=d("."+o+"-"+t.data("font-id")+"-"+t.val()+" ."+o);e.checked?t.attr("disabled",!0):t.attr("disabled",!1)},get_last_element_index:function(e){return e.length-1},empty_cache_directory:function(){s.empty_cache_directory_xhr&&s.empty_cache_directory_xhr.abort(),s.empty_cache_directory_xhr=d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_empty_dir",nonce:s.empty_action_nonce,init:s.flush_action_init},beforeSend:function(){s.show_loader()},complete:function(){location.reload()}})},delete_log:function(){s.delete_log_xhr&&s.delete_log_xhr.abort(),s.delete_log_xhr=d.ajax({type:"POST",url:ajaxurl,data:{action:"omgf_delete_log",nonce:s.delete_action_nonce},beforeSend:function(){s.show_loader()},complete:function(){location.reload()}})},show_loader_before_submit:function(){s.show_loader()},hide_loader:function(){d(".omgf-loading").fadeOut(300,function(){d(".omgf-loading").remove()})},dismiss_performance_checker_notice:function(e){e.preventDefault(),d.ajax({type:"POST",url:omgf_admin_i18n.rest_url+"/dashboard/dismiss-notice",beforeSend:function(e){e.setRequestHeader("X-WP-Nonce",omgf_admin_i18n.nonce),s.show_loader()},complete:function(e){void 0!==e.responseJSON&&e.responseJSON.success&&d("#omgf-performance-checker-notice").fadeOut(300,function(){d(this).remove()}),s.hide_loader()}})},dismiss_download_failures:function(e){e.preventDefault(),d.ajax({type:"POST",url:omgf_admin_i18n.rest_url+"/dashboard/dismiss-download-failures",beforeSend:function(e){e.setRequestHeader("X-WP-Nonce",omgf_admin_i18n.nonce),s.show_loader()},complete:function(e){void 0!==e.responseJSON&&e.responseJSON.success&&d("#omgf-download-failures-notice").fadeOut(300,function(){d(this).remove()}),s.hide_loader()}})},show_loader:function(){d("#wpwrap").append('<div class="omgf-loading"><span class="spinner is-active"></span></div>')}};omgf_show_loader=s.show_loader,omgf_hide_loader=s.hide_loader,omgf_unload_all=s.unload_all,omgf_load_all=s.load_all,omgf_unload_stylesheet=s.unload_stylesheet,omgf_load_stylesheet=s.load_stylesheet,s.init()});
  • host-webfonts-local/trunk/assets/js/omgf-frontend.js

    r3641590 r3721511  
    8787                                }
    8888
    89                                 if ((status !== 'success' && status !== 'warning')) {
     89                                // If the alert is only caused by failed downloads, the Google Fonts Checker's alert text doesn't apply.
     90                                let download_failures_only = status === 'alert' && response.download_failures && !response.google_fonts_checker_alert;
     91
     92                                if (status !== 'success' && status !== 'warning' && !download_failures_only) {
    9093                                        this.addInfoBox(status);
     94                                }
     95
     96                                if (response.download_failures) {
     97                                        this.addInfoBox('download_failures', response);
    9198                                }
    9299
    … …  
    188195
    189196                /**
     197                 * Normalizes a font-weight to the numeric value computed styles use, e.g. 'normal' => '400' and 'bold' => '700'.
     198                 * Ranges (e.g. '100 900' for variable fonts) are returned as-is.
     199                 *
     200                 * @param {string} weight
     201                 * @returns {string}
     202                 */
     203                normalizeFontWeight: function (weight) {
     204                        weight = String(weight || '400').trim().toLowerCase();
     205
     206                        if (weight === 'normal') {
     207                                return '400';
     208                        }
     209
     210                        if (weight === 'bold') {
     211                                return '700';
     212                        }
     213
     214                        return weight;
     215                },
     216
     217                /**
     218                 * Checks if a font face is in a set of face IDs collected from computed styles (which always have a single,
     219                 * numeric weight). A font face with a weight range (i.e. a variable font) is used if any weight within the range is.
     220                 *
     221                 * @param {Set} face_ids
     222                 * @param {string} family
     223                 * @param {string} weight Normalized font-weight.
     224                 * @param {string} style
     225                 * @returns {boolean}
     226                 */
     227                isFaceUsed: function (face_ids, family, weight, style) {
     228                        if (face_ids.has(`${family}-${weight}-${style}`.toLowerCase())) {
     229                                return true;
     230                        }
     231
     232                        let range = weight.split(/\s+/).map(Number);
     233
     234                        if (range.length !== 2 || range.some(isNaN)) {
     235                                return false;
     236                        }
     237
     238                        for (let face_id of face_ids) {
     239                                let prefix = `${family}-`.toLowerCase();
     240                                let suffix = `-${style}`.toLowerCase();
     241
     242                                if (!face_id.startsWith(prefix) || !face_id.endsWith(suffix)) {
     243                                        continue;
     244                                }
     245
     246                                let used_weight = Number(face_id.slice(prefix.length, face_id.length - suffix.length));
     247
     248                                if (used_weight >= range[0] && used_weight <= range[1]) {
     249                                        return true;
     250                                }
     251                        }
     252
     253                        return false;
     254                },
     255
     256                /**
     257                 * The rules of cross-origin stylesheets can't be read, unless they were loaded with the crossorigin attribute. Most
     258                 * font CDNs send CORS headers though, so these stylesheets are fetched again (usually from the browser's cache)
     259                 * and parsed separately to find their @font-face rules.
     260                 *
     261                 * @param {string[]} hrefs
     262                 * @param {Map} font_face_url_map
     263                 * @param {Set} loaded_font_urls
     264                 * @returns {Promise<void>}
     265                 */
     266                extractCrossOriginFontFaceRules: async function (hrefs, font_face_url_map, loaded_font_urls) {
     267                        if (typeof CSSStyleSheet === 'undefined' || typeof CSSStyleSheet.prototype.replaceSync !== 'function') {
     268                                return;
     269                        }
     270
     271                        // Keep the analysis bounded.
     272                        const max_sheets = 10;
     273                        const timeout_ms = 3000;
     274
     275                        let unique_hrefs = [...new Set(hrefs)].slice(0, max_sheets);
     276
     277                        await Promise.all(unique_hrefs.map(async (href) => {
     278                                let controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
     279                                let timeout = controller ? setTimeout(() => controller.abort(), timeout_ms) : null;
     280
     281                                try {
     282                                        let response = await fetch(href, {
     283                                                mode: 'cors',
     284                                                credentials: 'omit',
     285                                                signal: controller ? controller.signal : undefined
     286                                        });
     287
     288                                        if (!response.ok) {
     289                                                return;
     290                                        }
     291
     292                                        let sheet = new CSSStyleSheet();
     293
     294                                        // @import rules aren't supported (and ignored) in constructed stylesheets.
     295                                        sheet.replaceSync(await response.text());
     296
     297                                        this.extractFontFaceRules(sheet.cssRules, { href: href }, font_face_url_map, loaded_font_urls);
     298                                } catch (e) {
     299                                        // No CORS headers, a network error or a timeout: nothing we can do.
     300                                } finally {
     301                                        if (timeout) {
     302                                                clearTimeout(timeout);
     303                                        }
     304                                }
     305                        }));
     306                },
     307
     308                /**
    190309                 * Helper to get property value from a CSSRule, with fallback for Firefox.
    191310                 *
    … …  
    230349                                        let rule_style = this.getFontFaceProperty(rule, 'font-style') || 'normal';
    231350
    232                                         if (rule_weight === 'normal') rule_weight = '400';
    233                                         if (rule_weight === 'bold') rule_weight = '700';
     351                                        rule_weight = this.normalizeFontWeight(rule_weight);
    234352
    235353                                        let src = rule.style.getPropertyValue('src') || rule.style.src;
    … …  
    356474                                let font_face_url_map = new Map();
    357475
     476                                let cross_origin_sheets = [];
     477
    358478                                for (let i = 0; i < document.styleSheets.length; i++) {
     479                                        let sheet = document.styleSheets[i];
     480
    359481                                        try {
    360                                                 let sheet = document.styleSheets[i];
    361482                                                let rules = sheet.cssRules || sheet.rules;
    362483                                                if (!rules) continue;
    … …  
    364485                                                this.extractFontFaceRules(rules, sheet, font_face_url_map, loaded_font_urls);
    365486                                        } catch (e) {
    366                                                 // Ignore cross-origin stylesheet errors.
    367                                         }
    368                                 }
     487                                                // The rules of cross-origin stylesheets (loaded without the crossorigin attribute) can't be read.
     488                                                if (sheet.href) {
     489                                                        cross_origin_sheets.push(sheet.href);
     490                                                }
     491                                        }
     492                                }
     493
     494                                await this.extractCrossOriginFontFaceRules(cross_origin_sheets, font_face_url_map, loaded_font_urls);
    369495
    370496                                document.fonts.forEach((font) => {
    371497                                        let family = font.family.replace(/["']/g, '');
    372                                         let weight = font.weight;
     498                                        // Font faces often define keywords (e.g. icon fonts use 'normal'), while computed styles are always numeric.
     499                                        let weight = this.normalizeFontWeight(font.weight);
    373500                                        let style = font.style;
    374501                                        let face_id = `${family}-${weight}-${style}`.toLowerCase();
    … …  
    381508                                         * Check if any loaded fonts that are used above the fold are not preloaded.
    382509                                         */
    383                                         if (font.status === 'loaded' && font_url && used_faces_above_the_fold.has(face_id)) {
     510                                        if (font.status === 'loaded' && font_url && this.isFaceUsed(used_faces_above_the_fold, family, weight, style)) {
    384511                                                let is_preloaded = preloaded_fonts.some((url) => {
    385512                                                        // If we have the actual font URL, use it for exact matching.
    … …  
    416543                                         * elements that aren't rendered yet (e.g. tabs, accordions and modals) would be unloaded.
    417544                                         */
    418                                         if (font.status === 'unloaded' && !used_faces_not_rendered.has(face_id) && font_url) {
     545                                        if (font.status === 'unloaded' && !this.isFaceUsed(used_faces_not_rendered, family, weight, style) && font_url) {
    419546                                                unused_fonts.push({
    420547                                                        family: family,
    … …  
    711838                                let text = omgf_frontend.sprintf(omgf_frontend_i18n.info_box_preload_text, count || 0, data.potential_delay_ms || 0, data.impact || omgf_frontend_i18n.info_box_impact_low);
    712839                                info_box.innerHTML = `<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${text}</a>`;
     840                        }
     841
     842                        if (status === 'download_failures') {
     843                                info_box.id = 'wp-admin-bar-omgf-download-failures';
     844                                info_box.classList.add('alert');
     845
     846                                let link = document.createElement('a');
     847                                link.className = 'ab-item';
     848                                link.href = omgf_frontend_i18n.info_box_admin_url;
     849                                link.textContent = data.download_failures_text || '';
     850                                info_box.appendChild(link);
    713851                        }
    714852
  • host-webfonts-local/trunk/assets/js/omgf-frontend.min.js

    r3641590 r3721511  
    1 let omgf_font_cls=0,omgf_fonts_ready=!1,omgf_dom_content_loaded="loading"!==document.readyState,omgf_dom_content_loaded_time=omgf_dom_content_loaded?performance.now():0,omgf_cls_supported=!1;omgf_dom_content_loaded||document.addEventListener("DOMContentLoaded",()=>{omgf_dom_content_loaded=!0,omgf_dom_content_loaded_time=performance.now()});try{let e=new PerformanceObserver(()=>{});e.observe({type:"layout-shift",buffered:!1}),e.disconnect(),omgf_cls_supported=!0}catch(e){}if(omgf_cls_supported&&void 0!==document.fonts&&void 0!==document.fonts.ready)try{let e=new PerformanceObserver(e=>{e.getEntries().forEach(e=>{!e.hadRecentInput&&omgf_dom_content_loaded&&!omgf_fonts_ready&&e.startTime>=omgf_dom_content_loaded_time&&(omgf_font_cls+=e.value)})});e.observe({type:"layout-shift",buffered:!0}),document.fonts.ready.then(()=>{omgf_fonts_ready=!0,e.disconnect()})}catch(e){}window.addEventListener("load",()=>{let _={menu_item:document.getElementById("wp-admin-bar-omgf"),sub_menu:document.getElementById("wp-admin-bar-omgf-default"),skip_results:omgf_frontend_results.skip||!1,init:async function(){let e=[],t=[],n=null;try{var o,a=this.filterGoogleFonts(),i=(o=await this.getStatus(a)).unused_fonts_analysis||{},r=(t=o.unused_fonts||[],o.preload_analysis||{}),s=(e=o.missing_preloads||[],o.cls_analysis||{});if(n=o.status||null,null!==this.menu_item&&(this.menu_item.classList.add("dot"),n)&&this.menu_item.classList.add(n),"success"!==n&&"warning"!==n&&this.addInfoBox(n),!_.skip_results){if(omgf_frontend_i18n.multilang_plugin_used){var l=omgf_frontend_i18n.subsets_count;if(1<l){let e=omgf_frontend_i18n.info_box_impact_low;3===l?e=omgf_frontend_i18n.info_box_impact_medium:3<l&&(e=omgf_frontend_i18n.info_box_impact_high);var f={name:omgf_frontend_i18n.multilang_plugin_name,subsets_count:l,impact:e};this.addInfoBox("multilang_plugin",f)}}i&&i.count&&this.addInfoBox("unload_notice",i),r&&0<r.potential_delay_ms&&0<e.length&&this.addInfoBox("preload_notice",r,e.length),s&&.01<=s.cls&&this.addInfoBox("cls_notice",s)}}catch(e){console.error("OMGF - Error running Google Fonts Checker:",e)}finally{document.dispatchEvent(new CustomEvent("omgf_frontend_loaded",{detail:{missing_preloads:e,unused_fonts:t,status:n}}))}},filterGoogleFonts:()=>{var e=window.performance.getEntries().filter(e=>{var t,n=0<e.name.indexOf("/fonts.googleapis.com/css")||0<e.name.indexOf("/fonts.googleapis.com/icon");let o=0<e.name.indexOf("/fonts.gstatic.com/");return o&&(t=/\.(woff2|woff|ttf|otf)(\?.*)?$/i.test(e.name),e=!e.initiatorType||["css","link","other"].includes(e.initiatorType),o=t&&e),n||o});if(0===e.length)return[];console.log("OMGF has found the following Google Fonts API calls:");let t=[];return e.forEach(e=>{t.push(e.name),console.log(" - "+e.name)}),t},getFontFaceProperty:function(e,t){var n=e.style.getPropertyValue(t);return n?n.replace(/["']/g,"").trim():(n=e.cssText.match(new RegExp(t+"\\s*:\\s*([^;]+)","i")))?n[1].replace(/["']/g,"").trim():null},extractFontFaceRules:function(t,o,a,i){for(let e=0;e<t.length;e++){var r=t[e];if(r.type===CSSRule.IMPORT_RULE&&r.styleSheet&&(s=r.styleSheet.cssRules||r.styleSheet.rules)&&this.extractFontFaceRules(s,r.styleSheet,a,i),"CSSFontFaceRule"===r.constructor.name||r.type===CSSRule.FONT_FACE_RULE){var s=this.getFontFaceProperty(r,"font-family");let e=this.getFontFaceProperty(r,"font-weight")||"400";var l,f=this.getFontFaceProperty(r,"font-style")||"normal";"bold"===(e="normal"===e?"400":e)&&(e="700");let t=r.style.getPropertyValue("src")||r.style.src,n=(t=t||this.getFontFaceProperty(r,"src"))?t.match(/url\(["']?([^"')]+\.woff2[^"')]*?)["']?\)/):null;(n=n||(t?t.match(/url\(["']?([^"')]+)["']?\)/):null))&&(l=new URL(n[1],o.href||document.baseURI).href,r=r.style.getPropertyValue("unicode-range")||"",f=(`${s}-${e}-`+f+(r?"-"+r:"")).toLowerCase(),a.has(f)||a.set(f,l),i.has(l))&&a.set(f,l)}}},getStatus:async function(e){var t=new URLSearchParams(window.location.search),t=Object.fromEntries(t.entries());let d=[],m=[];if(void 0!==document.fonts&&void 0!==document.fonts.ready){await document.fonts.ready;let s=[];document.head.querySelectorAll('link[rel="preload"][as="font"]').forEach(e=>{s.push(e.href)});let l=new Set,f=new Set;var o=document.querySelectorAll("body *:not(#wpadminbar):not(#wpadminbar *)");for(let e=0;e<o.length&&e<1500;e++){let a=o[e];var c=a.getBoundingClientRect(),u=window.getComputedStyle(a),g=u.fontFamily.replace(/["']/g,"");let t=u.fontWeight,n=u.fontStyle,i=c.top<window.innerHeight&&0<c.bottom&&c.left<window.innerWidth&&0<c.right,r=0<c.width||0<c.height;g.split(",").forEach(e=>{e=(`${e=e.trim()}-${t}-`+n).toLowerCase();i&&l.add(e),r||f.add(e)}),["::before","::after"].forEach(e=>{var e=window.getComputedStyle(a,e),t=e.fontFamily.replace(/["']/g,"");let n=e.fontWeight,o=e.fontStyle;t.split(",").forEach(e=>{e=(`${e=e.trim()}-${n}-`+o).toLowerCase();i&&l.add(e),r||f.add(e)})})}var n=new Set(window.performance.getEntriesByType("resource").filter(e=>e.name.match(/\.(woff2?|ttf|otf)(\?.*)?$/i)).map(e=>e.name));let _=new Map;for(let e=0;e<document.styleSheets.length;e++)try{var a=document.styleSheets[e],i=a.cssRules||a.rules;i&&this.extractFontFaceRules(i,a,_,n)}catch(e){}document.fonts.forEach(e=>{let n=e.family.replace(/["']/g,"");var t=e.weight,o=e.style,a=(n+`-${t}-`+o).toLowerCase(),i=(a+"-"+(e.unicodeRange||"")).toLowerCase();let r=_.get(i)||_.get(a);"loaded"===e.status&&r&&l.has(a)&&(s.some(e=>{var t;return!(!r||e!==r)||(t=n.toLowerCase().replace(/\s/g,"-"),e=e.toLowerCase(),new RegExp("(^|[/_-])"+t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"([./_-]|$)","i").test(e))})||d.some(e=>e.family===n&&e.url===r)||d.push({family:n,weight:t,style:o,url:r})),"unloaded"===e.status&&!f.has(a)&&r&&m.push({family:n,weight:t,style:o,url:r,unicode_range:e.unicodeRange})})}let r=this.analyzeUnusedFonts(m),s=await this.analyzePreloadImpact(d),l=this.analyzeCLS(omgf_font_cls);var f=new FormData;return f.append("path",document.location.pathname),f.append("urls",JSON.stringify(e)),f.append("params",JSON.stringify(t)),f.append("unused_fonts_analysis",JSON.stringify(r)),f.append("preload_analysis",JSON.stringify(s)),f.append("cls_analysis",JSON.stringify(l)),_.ajax(f).then(e=>(e&&(e.unused_fonts_analysis=r,e.unused_fonts=m,e.preload_analysis=s,e.missing_preloads=d,e.cls_analysis=l),e))},forEachMatchingResources:function(e,n,o){e.forEach(i=>{let t=i.family,r=i.url,s=t.toLowerCase().replace(/\s/g,"-");n.filter(e=>{var t=e.name.toLowerCase();if(r&&e.name===r)return!0;if(!new RegExp("(^|[/_-])"+s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"([./_-]|\\.|$)","i").test(t))return!1;if(void 0===i.weight)return!0;let n=i.weight;var e=i.style,o=("bold"===(n="normal"===n?"400":n)&&(n="700"),new RegExp(`[./_-]${n}([./_-]|\\.|$)`,"i"));let a=!0;return"italic"===e?a=t.includes("italic")||/[._-]i\.(woff|woff2|ttf|otf)/.test(t)||/\d{3}i\.(woff|woff2|ttf|otf)/.test(t):"normal"===e&&(a=!t.includes("italic")),o.test(t)&&a}).forEach(e=>{o(e,t)})})},getFontResources:function(){return window.performance.getEntriesByType("resource").filter(e=>e.name.match(/\.(woff|woff2|ttf|otf)(\?.*)?$/i))},analyzeUnusedFonts:function(e){if(0===e.length)return{};e=e.length;let t=omgf_frontend_i18n.info_box_impact_low;return 6<e?t=omgf_frontend_i18n.info_box_impact_high:3<=e&&(t=omgf_frontend_i18n.info_box_impact_medium),{count:e,impact:t}},analyzeCLS:function(e){let t=omgf_frontend_i18n.info_box_impact_low;e=Math.round(1e3*e)/1e3;return e<.01?{}:(.1<e&&(t=omgf_frontend_i18n.info_box_impact_medium),{cls:e,impact:t=.25<e?omgf_frontend_i18n.info_box_impact_high:t})},analyzePreloadImpact:async function(n){if(0===n.length)return{};let o={affects_lcp:!1,potential_delay_ms:0,impact:omgf_frontend_i18n.info_box_impact_low};try{var a=await new Promise(t=>{let n=new PerformanceObserver(e=>{e=e.getEntries(),e=e[e.length-1];n.disconnect(),t(e)});n.observe({type:"largest-contentful-paint",buffered:!0}),setTimeout(()=>{n.disconnect(),t(null)},2e3)});if(!a)return o;var i=a.element;let e=!1;if(!(e=i?"string"==typeof i.innerText&&0<i.innerText.trim().length||"string"==typeof i.textContent&&0<i.textContent.trim().length:e))return o;var r=this.getFontResources();let t=0;if(this.forEachMatchingResources(n,r,e=>{e=e.responseEnd-e.startTime;0<e&&(o.affects_lcp=!0,e>t)&&(t=e)}),o.potential_delay_ms=Math.round(t),o.potential_delay_ms<=5)return{};100<o.potential_delay_ms?o.impact=omgf_frontend_i18n.info_box_impact_high:20<o.potential_delay_ms&&(o.impact=omgf_frontend_i18n.info_box_impact_medium)}catch(e){return console.warn("OMGF - Error analyzing preload impact:",e),{}}return o},ajax:function(e){return fetch(omgf_frontend_i18n.api_url,{method:"POST",credentials:"same-origin",headers:{"X-WP-Nonce":omgf_frontend_i18n.nonce},body:e}).then(e=>!!e.ok&&e.json())},addInfoBox:function(e,t,n){var o,a=document.createElement("li");a.id="wp-admin-bar-omgf-info","alert"===e&&(a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${omgf_frontend_i18n.info_box_alert_text}</a>`),"notice"===e&&(a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${omgf_frontend_i18n.info_box_notice_text}</a>`),"multilang_plugin"===e&&(a.id="wp-admin-bar-omgf-multilang-info",o=_.sprintf(omgf_frontend_i18n.info_box_multilang_plugin_text,omgf_frontend_i18n.multilang_plugin_name,t.subsets_count,t.impact),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"unload_notice"===e&&(a.id="wp-admin-bar-omgf-unload-info",o=_.sprintf(omgf_frontend_i18n.info_box_unload_text,t.count||0,t.impact||omgf_frontend_i18n.info_box_impact_low),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"preload_notice"===e&&(a.id="wp-admin-bar-omgf-preload-info",o=_.sprintf(omgf_frontend_i18n.info_box_preload_text,n||0,t.potential_delay_ms||0,t.impact||omgf_frontend_i18n.info_box_impact_low),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"cls_notice"===e&&(a.id="wp-admin-bar-omgf-cls-info",n=_.sprintf(omgf_frontend_i18n.info_box_cls_text,t.cls,t.impact),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${n}</a>`),"cls_notice"!==e&&"unload_notice"!==e&&"preload_notice"!==e&&"multilang_plugin"!==e||a.classList.add("info"),"info"===e&&omgf_frontend_i18n.first_run&&document.getElementById("wp-admin-bar-omgf-optimize-this")?.classList.add("info"),_.sub_menu&&_.sub_menu.prepend(a)},sprintf:function(e){let a=arguments,i=1;return e.replace(/%(s|d|0\d+d)/g,function(e,t){var n=a[i++];switch(t){case"s":return n;case"d":return parseInt(n,10);default:var n=String(parseInt(n,10)),o=Number(t.slice(1,-1));return"0".repeat(o).slice(n.length)+n}})}},e=window.performance.getEntries(),t=0,n=setInterval(()=>{t++,((e=e.length<window.performance.getEntries().length?window.performance.getEntries():e).length===window.performance.getEntries().length||20<=t)&&(clearInterval(n),_.init())},500)});
     1let omgf_font_cls=0,omgf_fonts_ready=!1,omgf_dom_content_loaded="loading"!==document.readyState,omgf_dom_content_loaded_time=omgf_dom_content_loaded?performance.now():0,omgf_cls_supported=!1;omgf_dom_content_loaded||document.addEventListener("DOMContentLoaded",()=>{omgf_dom_content_loaded=!0,omgf_dom_content_loaded_time=performance.now()});try{let e=new PerformanceObserver(()=>{});e.observe({type:"layout-shift",buffered:!1}),e.disconnect(),omgf_cls_supported=!0}catch(e){}if(omgf_cls_supported&&void 0!==document.fonts&&void 0!==document.fonts.ready)try{let e=new PerformanceObserver(e=>{e.getEntries().forEach(e=>{!e.hadRecentInput&&omgf_dom_content_loaded&&!omgf_fonts_ready&&e.startTime>=omgf_dom_content_loaded_time&&(omgf_font_cls+=e.value)})});e.observe({type:"layout-shift",buffered:!0}),document.fonts.ready.then(()=>{omgf_fonts_ready=!0,e.disconnect()})}catch(e){}window.addEventListener("load",()=>{let p={menu_item:document.getElementById("wp-admin-bar-omgf"),sub_menu:document.getElementById("wp-admin-bar-omgf-default"),skip_results:omgf_frontend_results.skip||!1,init:async function(){let e=[],t=[],n=null;try{var o,a=this.filterGoogleFonts(),i=(o=await this.getStatus(a)).unused_fonts_analysis||{},r=(t=o.unused_fonts||[],o.preload_analysis||{}),s=(e=o.missing_preloads||[],o.cls_analysis||{}),l=(n=o.status||null,null!==this.menu_item&&(this.menu_item.classList.add("dot"),n)&&this.menu_item.classList.add(n),"alert"===n&&o.download_failures&&!o.google_fonts_checker_alert);if("success"===n||"warning"===n||l||this.addInfoBox(n),o.download_failures&&this.addInfoBox("download_failures",o),!p.skip_results){if(omgf_frontend_i18n.multilang_plugin_used){var f=omgf_frontend_i18n.subsets_count;if(1<f){let e=omgf_frontend_i18n.info_box_impact_low;3===f?e=omgf_frontend_i18n.info_box_impact_medium:3<f&&(e=omgf_frontend_i18n.info_box_impact_high);var d={name:omgf_frontend_i18n.multilang_plugin_name,subsets_count:f,impact:e};this.addInfoBox("multilang_plugin",d)}}i&&i.count&&this.addInfoBox("unload_notice",i),r&&0<r.potential_delay_ms&&0<e.length&&this.addInfoBox("preload_notice",r,e.length),s&&.01<=s.cls&&this.addInfoBox("cls_notice",s)}}catch(e){console.error("OMGF - Error running Google Fonts Checker:",e)}finally{document.dispatchEvent(new CustomEvent("omgf_frontend_loaded",{detail:{missing_preloads:e,unused_fonts:t,status:n}}))}},filterGoogleFonts:()=>{var e=window.performance.getEntries().filter(e=>{var t,n=0<e.name.indexOf("/fonts.googleapis.com/css")||0<e.name.indexOf("/fonts.googleapis.com/icon");let o=0<e.name.indexOf("/fonts.gstatic.com/");return o&&(t=/\.(woff2|woff|ttf|otf)(\?.*)?$/i.test(e.name),e=!e.initiatorType||["css","link","other"].includes(e.initiatorType),o=t&&e),n||o});if(0===e.length)return[];console.log("OMGF has found the following Google Fonts API calls:");let t=[];return e.forEach(e=>{t.push(e.name),console.log(" - "+e.name)}),t},normalizeFontWeight:function(e){return"normal"===(e=String(e||"400").trim().toLowerCase())?"400":"bold"===e?"700":e},isFaceUsed:function(e,t,n,o){if(e.has((t+`-${n}-`+o).toLowerCase()))return!0;var a=n.split(/\s+/).map(Number);if(2===a.length&&!a.some(isNaN))for(var i of e){var r=(t+"-").toLowerCase(),s=("-"+o).toLowerCase();if(i.startsWith(r)&&i.endsWith(s)){r=Number(i.slice(r.length,i.length-s.length));if(r>=a[0]&&r<=a[1])return!0}}return!1},extractCrossOriginFontFaceRules:async function(e,i,r){if("undefined"!=typeof CSSStyleSheet&&"function"==typeof CSSStyleSheet.prototype.replaceSync){e=[...new Set(e)].slice(0,10);await Promise.all(e.map(async e=>{let t="undefined"!=typeof AbortController?new AbortController:null;var n=t?setTimeout(()=>t.abort(),3e3):null;try{var o,a=await fetch(e,{mode:"cors",credentials:"omit",signal:t?t.signal:void 0});a.ok&&((o=new CSSStyleSheet).replaceSync(await a.text()),this.extractFontFaceRules(o.cssRules,{href:e},i,r))}catch(e){}finally{n&&clearTimeout(n)}}))}},getFontFaceProperty:function(e,t){var n=e.style.getPropertyValue(t);return n?n.replace(/["']/g,"").trim():(n=e.cssText.match(new RegExp(t+"\\s*:\\s*([^;]+)","i")))?n[1].replace(/["']/g,"").trim():null},extractFontFaceRules:function(t,n,o,a){for(let e=0;e<t.length;e++){var i=t[e];if(i.type===CSSRule.IMPORT_RULE&&i.styleSheet&&(s=i.styleSheet.cssRules||i.styleSheet.rules)&&this.extractFontFaceRules(s,i.styleSheet,o,a),"CSSFontFaceRule"===i.constructor.name||i.type===CSSRule.FONT_FACE_RULE){var r,s=this.getFontFaceProperty(i,"font-family"),l=this.getFontFaceProperty(i,"font-weight")||"400",f=this.getFontFaceProperty(i,"font-style")||"normal",l=this.normalizeFontWeight(l);let e=i.style.getPropertyValue("src")||i.style.src,t=(e=e||this.getFontFaceProperty(i,"src"))?e.match(/url\(["']?([^"')]+\.woff2[^"')]*?)["']?\)/):null;(t=t||(e?e.match(/url\(["']?([^"')]+)["']?\)/):null))&&(r=new URL(t[1],n.href||document.baseURI).href,f=(s+`-${l}-`+f+((l=i.style.getPropertyValue("unicode-range")||"")?"-"+l:"")).toLowerCase(),o.has(f)||o.set(f,r),a.has(r))&&o.set(f,r)}}},getStatus:async function(e){var t=new URLSearchParams(window.location.search),t=Object.fromEntries(t.entries());let _=[],c=[];if(void 0!==document.fonts&&void 0!==document.fonts.ready){await document.fonts.ready;let s=[];document.head.querySelectorAll('link[rel="preload"][as="font"]').forEach(e=>{s.push(e.href)});let l=new Set,f=new Set;var o=document.querySelectorAll("body *:not(#wpadminbar):not(#wpadminbar *)");for(let e=0;e<o.length&&e<1500;e++){let a=o[e];var m=a.getBoundingClientRect(),u=window.getComputedStyle(a),g=u.fontFamily.replace(/["']/g,"");let t=u.fontWeight,n=u.fontStyle,i=m.top<window.innerHeight&&0<m.bottom&&m.left<window.innerWidth&&0<m.right,r=0<m.width||0<m.height;g.split(",").forEach(e=>{e=(`${e=e.trim()}-${t}-`+n).toLowerCase();i&&l.add(e),r||f.add(e)}),["::before","::after"].forEach(e=>{var e=window.getComputedStyle(a,e),t=e.fontFamily.replace(/["']/g,"");let n=e.fontWeight,o=e.fontStyle;t.split(",").forEach(e=>{e=(`${e=e.trim()}-${n}-`+o).toLowerCase();i&&l.add(e),r||f.add(e)})})}var n=new Set(window.performance.getEntriesByType("resource").filter(e=>e.name.match(/\.(woff2?|ttf|otf)(\?.*)?$/i)).map(e=>e.name));let d=new Map;var a=[];for(let e=0;e<document.styleSheets.length;e++){var i=document.styleSheets[e];try{var r=i.cssRules||i.rules;r&&this.extractFontFaceRules(r,i,d,n)}catch(e){i.href&&a.push(i.href)}}await this.extractCrossOriginFontFaceRules(a,d,n),document.fonts.forEach(e=>{let n=e.family.replace(/["']/g,"");var t=this.normalizeFontWeight(e.weight),o=e.style,a=(n+`-${t}-`+o).toLowerCase(),i=(a+"-"+(e.unicodeRange||"")).toLowerCase();let r=d.get(i)||d.get(a);"loaded"===e.status&&r&&this.isFaceUsed(l,n,t,o)&&(s.some(e=>{var t;return!(!r||e!==r)||(t=n.toLowerCase().replace(/\s/g,"-"),e=e.toLowerCase(),new RegExp("(^|[/_-])"+t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"([./_-]|$)","i").test(e))})||_.some(e=>e.family===n&&e.url===r)||_.push({family:n,weight:t,style:o,url:r})),"unloaded"===e.status&&!this.isFaceUsed(f,n,t,o)&&r&&c.push({family:n,weight:t,style:o,url:r,unicode_range:e.unicodeRange})})}let s=this.analyzeUnusedFonts(c),l=await this.analyzePreloadImpact(_),f=this.analyzeCLS(omgf_font_cls);var d=new FormData;return d.append("path",document.location.pathname),d.append("urls",JSON.stringify(e)),d.append("params",JSON.stringify(t)),d.append("unused_fonts_analysis",JSON.stringify(s)),d.append("preload_analysis",JSON.stringify(l)),d.append("cls_analysis",JSON.stringify(f)),p.ajax(d).then(e=>(e&&(e.unused_fonts_analysis=s,e.unused_fonts=c,e.preload_analysis=l,e.missing_preloads=_,e.cls_analysis=f),e))},forEachMatchingResources:function(e,n,o){e.forEach(i=>{let t=i.family,r=i.url,s=t.toLowerCase().replace(/\s/g,"-");n.filter(e=>{var t=e.name.toLowerCase();if(r&&e.name===r)return!0;if(!new RegExp("(^|[/_-])"+s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"([./_-]|\\.|$)","i").test(t))return!1;if(void 0===i.weight)return!0;let n=i.weight;var e=i.style,o=("bold"===(n="normal"===n?"400":n)&&(n="700"),new RegExp(`[./_-]${n}([./_-]|\\.|$)`,"i"));let a=!0;return"italic"===e?a=t.includes("italic")||/[._-]i\.(woff|woff2|ttf|otf)/.test(t)||/\d{3}i\.(woff|woff2|ttf|otf)/.test(t):"normal"===e&&(a=!t.includes("italic")),o.test(t)&&a}).forEach(e=>{o(e,t)})})},getFontResources:function(){return window.performance.getEntriesByType("resource").filter(e=>e.name.match(/\.(woff|woff2|ttf|otf)(\?.*)?$/i))},analyzeUnusedFonts:function(e){if(0===e.length)return{};e=e.length;let t=omgf_frontend_i18n.info_box_impact_low;return 6<e?t=omgf_frontend_i18n.info_box_impact_high:3<=e&&(t=omgf_frontend_i18n.info_box_impact_medium),{count:e,impact:t}},analyzeCLS:function(e){let t=omgf_frontend_i18n.info_box_impact_low;e=Math.round(1e3*e)/1e3;return e<.01?{}:(.1<e&&(t=omgf_frontend_i18n.info_box_impact_medium),{cls:e,impact:t=.25<e?omgf_frontend_i18n.info_box_impact_high:t})},analyzePreloadImpact:async function(n){if(0===n.length)return{};let o={affects_lcp:!1,potential_delay_ms:0,impact:omgf_frontend_i18n.info_box_impact_low};try{var a=await new Promise(t=>{let n=new PerformanceObserver(e=>{e=e.getEntries(),e=e[e.length-1];n.disconnect(),t(e)});n.observe({type:"largest-contentful-paint",buffered:!0}),setTimeout(()=>{n.disconnect(),t(null)},2e3)});if(!a)return o;var i=a.element;let e=!1;if(!(e=i?"string"==typeof i.innerText&&0<i.innerText.trim().length||"string"==typeof i.textContent&&0<i.textContent.trim().length:e))return o;var r=this.getFontResources();let t=0;if(this.forEachMatchingResources(n,r,e=>{e=e.responseEnd-e.startTime;0<e&&(o.affects_lcp=!0,e>t)&&(t=e)}),o.potential_delay_ms=Math.round(t),o.potential_delay_ms<=5)return{};100<o.potential_delay_ms?o.impact=omgf_frontend_i18n.info_box_impact_high:20<o.potential_delay_ms&&(o.impact=omgf_frontend_i18n.info_box_impact_medium)}catch(e){return console.warn("OMGF - Error analyzing preload impact:",e),{}}return o},ajax:function(e){return fetch(omgf_frontend_i18n.api_url,{method:"POST",credentials:"same-origin",headers:{"X-WP-Nonce":omgf_frontend_i18n.nonce},body:e}).then(e=>!!e.ok&&e.json())},addInfoBox:function(e,t,n){var o,a=document.createElement("li");a.id="wp-admin-bar-omgf-info","alert"===e&&(a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${omgf_frontend_i18n.info_box_alert_text}</a>`),"notice"===e&&(a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${omgf_frontend_i18n.info_box_notice_text}</a>`),"multilang_plugin"===e&&(a.id="wp-admin-bar-omgf-multilang-info",o=p.sprintf(omgf_frontend_i18n.info_box_multilang_plugin_text,omgf_frontend_i18n.multilang_plugin_name,t.subsets_count,t.impact),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"unload_notice"===e&&(a.id="wp-admin-bar-omgf-unload-info",o=p.sprintf(omgf_frontend_i18n.info_box_unload_text,t.count||0,t.impact||omgf_frontend_i18n.info_box_impact_low),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"preload_notice"===e&&(a.id="wp-admin-bar-omgf-preload-info",o=p.sprintf(omgf_frontend_i18n.info_box_preload_text,n||0,t.potential_delay_ms||0,t.impact||omgf_frontend_i18n.info_box_impact_low),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"download_failures"===e&&(a.id="wp-admin-bar-omgf-download-failures",a.classList.add("alert"),(n=document.createElement("a")).className="ab-item",n.href=omgf_frontend_i18n.info_box_admin_url,n.textContent=t.download_failures_text||"",a.appendChild(n)),"cls_notice"===e&&(a.id="wp-admin-bar-omgf-cls-info",o=p.sprintf(omgf_frontend_i18n.info_box_cls_text,t.cls,t.impact),a.innerHTML=`<a class="ab-item" href="${omgf_frontend_i18n.info_box_admin_url}">${o}</a>`),"cls_notice"!==e&&"unload_notice"!==e&&"preload_notice"!==e&&"multilang_plugin"!==e||a.classList.add("info"),"info"===e&&omgf_frontend_i18n.first_run&&document.getElementById("wp-admin-bar-omgf-optimize-this")?.classList.add("info"),p.sub_menu&&p.sub_menu.prepend(a)},sprintf:function(e){let a=arguments,i=1;return e.replace(/%(s|d|0\d+d)/g,function(e,t){var n=a[i++];switch(t){case"s":return n;case"d":return parseInt(n,10);default:var n=String(parseInt(n,10)),o=Number(t.slice(1,-1));return"0".repeat(o).slice(n.length)+n}})}},e=window.performance.getEntries(),t=0,n=setInterval(()=>{t++,((e=e.length<window.performance.getEntries().length?window.performance.getEntries():e).length===window.performance.getEntries().length||20<=t)&&(clearInterval(n),p.init())},500)});
  • host-webfonts-local/trunk/host-webfonts-local.php

    r3707539 r3721511  
    44 * Plugin URI: https://daan.dev/wordpress/omgf/
    55 * Description: Increase GDPR/DSGVO compliance and leverage browser cache by automatically self-hosting Google Fonts.
    6  * Version: 6.3.11
     6 * Version: 6.3.12
    77 * Author: Daan from Daan.dev
    88 * Author URI: https://daan.dev
  • host-webfonts-local/trunk/readme.txt

    r3707539 r3721511  
    33Tags: google, fonts, gdpr, dsgvo, cache
    44Requires at least: 5.9
    5 Tested up to: 7.0
    6 Stable tag: 6.3.11
     5Tested up to: 7.1
     6Stable tag: 6.3.12
    77Requires PHP: 7.3
    88License: GPLv2 or later
    … …  
    8787
    8888== Changelog ==
     89
     90= 6.3.12 =
     91* Security: hardened the handling of Google Fonts stylesheet requests.
     92* Security: hardened the handling of downloaded font files.
     93* Security: admin notices are escaped on output.
     94* Security: the redirect after saving settings is restricted to the site's own host.
     95* Security: the stylesheet handle is escaped on the Optimize Fonts screen.
     96* Improved: removed a legacy, hidden option from the settings form.
     97* Security: only administrators can store results or trigger processing through the Admin Bar Menu's status endpoint, even when its permission is widened by a filter. URLs are validated before they're processed.
     98* Security: downloaded font files are now validated before they're stored.
     99* Fixed: the Font Performance Checker didn't detect fonts which should be preloaded if their @font-face statement used a keyword (e.g. font-weight: normal, which most icon fonts use) or a weight range (i.e. variable fonts).
     100* Improved: the Font Performance Checker now also detects fonts which should be preloaded in cross-origin stylesheets loaded without the crossorigin attribute, if the stylesheet's host allows it (most font CDNs do).
     101* Tested with WP 7.1.
    89102
    90103= 6.3.11 =
  • host-webfonts-local/trunk/src/API/AdminbarMenu.php

    r3503345 r3721511  
    1919use OMGF\Admin\Dashboard;
    2020use OMGF\Admin\Settings;
     21use OMGF\Download;
    2122use OMGF\Helper as OMGF;
    2223
    … …  
    5556        public function get_admin_bar_status( $request ) {
    5657                $params                = $this->clean( $request->get_params() );
    57                 $stored_results        = $this->update_google_fonts_checker_results( $params );
    5858                $unused_fonts_analysis = $this->decode_json_array( $params['unused_fonts_analysis'] ?? [] );
    5959                $preload_analysis      = $this->decode_json_array( $params['preload_analysis'] ?? [] );
    6060                $cls_analysis          = $this->decode_json_array( $params['cls_analysis'] ?? [] );
    6161
    62                 $this->update_perf_metrics( $params, $unused_fonts_analysis, $preload_analysis, $cls_analysis );
     62                /**
     63                 * @since v6.3.12 The permission for this endpoint can be widened with the omgf_api_adminbar_menu_permission
     64                 *                filter (e.g., to allow visitors to request the status), but only administrators may store
     65                 *                results or trigger any processing through it. Everyone else gets a read-only status.
     66                 */
     67                if ( current_user_can( 'manage_options' ) ) {
     68                        $stored_results = $this->update_google_fonts_checker_results( $params );
     69
     70                        $this->update_perf_metrics( $params, $unused_fonts_analysis, $preload_analysis, $cls_analysis );
     71                } else {
     72                        $stored_results = get_option( Settings::OMGF_DB_GOOGLE_FONTS_CHECKER_RESULTS, [] );
     73                        $stored_results = is_array( $stored_results ) ? $stored_results : [];
     74                }
    6375
    6476                $status = $this->calculate_status( $stored_results, $unused_fonts_analysis, $preload_analysis, $cls_analysis );
    6577
    66                 return [ 'status' => apply_filters( 'omgf_ajax_admin_bar_status', $status ) ];
     78                /**
     79                 * @since v6.3.12 Font files which couldn't be downloaded correctly are reported to administrators only, because
     80                 *                visitors requesting the status (e.g. for Smart Optimize) need the regular status.
     81                 */
     82                $download_failures = current_user_can( 'manage_options' ) ? count( Download::get_failures() ) : 0;
     83
     84                if ( $download_failures > 0 ) {
     85                        $status = 'alert';
     86                }
     87
     88                $response = [ 'status' => apply_filters( 'omgf_ajax_admin_bar_status', $status ) ];
     89
     90                if ( $download_failures > 0 ) {
     91                        $response['download_failures']          = $download_failures;
     92                        $response['download_failures_text']     = sprintf(
     93                        /* translators: %d: number of font files */
     94                                _n(
     95                                        '%d font file couldn\'t be downloaded correctly',
     96                                        '%d font files couldn\'t be downloaded correctly',
     97                                        $download_failures,
     98                                        'host-webfonts-local'
     99                                ),
     100                                $download_failures
     101                        );
     102                        $response['google_fonts_checker_alert'] = ! empty( $stored_results );
     103                }
     104
     105                return $response;
    67106        }
    68107
    … …  
    135174                        $urls = [];
    136175                }
     176
     177                /**
     178                 * @since v6.3.12 Validate the URLs before they're passed to any filter.
     179                 */
     180                $urls = array_filter(
     181                        $urls,
     182                        function ( $url ) {
     183                                return is_string( $url ) && filter_var( $url, FILTER_VALIDATE_URL );
     184                        }
     185                );
    137186
    138187                $urls        = apply_filters( 'omgf_ajax_results', $urls, $params, $path );
  • host-webfonts-local/trunk/src/API/Dashboard.php

    r3489128 r3721511  
    1818
    1919use OMGF\Admin\Settings;
     20use OMGF\Download;
    2021
    2122class Dashboard {
    … …  
    6768                        ]
    6869                );
     70
     71                register_rest_route(
     72                        $this->namespace,
     73                        '/' . $this->base . '/dismiss-download-failures',
     74                        [
     75                                [
     76                                        'methods'             => 'POST',
     77                                        'callback'            => [ $this, 'dismiss_download_failures' ],
     78                                        'permission_callback' => [ $this, 'get_permission' ],
     79                                ],
     80                                'schema' => null,
     81                        ]
     82                );
    6983        }
    7084
    … …  
    90104                return new \WP_REST_Response( [ 'success' => true ] );
    91105        }
     106
     107        /**
     108         * Dismiss the font files which couldn't be downloaded correctly.
     109         *
     110         * @since v6.3.12
     111         *
     112         * @return \WP_REST_Response
     113         */
     114        public function dismiss_download_failures() {
     115                Download::clear_failures();
     116
     117                return new \WP_REST_Response( [ 'success' => true ] );
     118        }
    92119}
  • host-webfonts-local/trunk/src/Admin/Actions.php

    r3707539 r3721511  
    202202                );
    203203
     204                /**
     205                 * @since v6.3.12 These options are generated by OMGF and should never be updated using the settings form.
     206                 */
     207                $read_only_options = [
     208                        Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS,
     209                        Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS_FRONTEND,
     210                ];
     211
    204212                foreach ( $updated_settings as $option_name => $option_value ) {
    205213                        if ( ! str_starts_with( $option_name, 'omgf_' ) || ( empty( $option_value ) && $option_value !== '0' ) ) {
     214                                continue;
     215                        }
     216
     217                        if ( in_array( $option_name, $read_only_options, true ) ) {
    206218                                continue;
    207219                        }
    … …  
    258270
    259271                if ( ! defined( 'DAAN_DOING_TESTS' ) ) {
    260                         wp_redirect( $goback ); // @codeCoverageIgnore
     272                        wp_safe_redirect( $goback ); // @codeCoverageIgnore
    261273                        exit; // @codeCoverageIgnore
    262274                }
  • host-webfonts-local/trunk/src/Admin/Dashboard.php

    r3628684 r3721511  
    1717namespace OMGF\Admin;
    1818
     19use OMGF\Download;
    1920use OMGF\Helper as OMGF;
    2021
    … …  
    101102
    102103                return ob_get_clean();
     104        }
     105
     106        /**
     107         * Renders the font files which couldn't be downloaded correctly.
     108         *
     109         * @since v6.3.12
     110         *
     111         * @return void
     112         */
     113        public static function render_download_failures() {
     114                $failures = Download::get_failures();
     115
     116                if ( empty( $failures ) ) {
     117                        return;
     118                }
     119                ?>
     120                <div class="task-manager-notice alert" id="omgf-download-failures-notice">
     121                        <h4><?php echo wp_kses_post(
     122                                        sprintf(
     123                                        /* translators: %1$s: plugin name, %2$d: number of font files */
     124                                                _n(
     125                                                        '%1$s couldn\'t download %2$d font file correctly.',
     126                                                        '%1$s couldn\'t download %2$d font files correctly.',
     127                                                        count( $failures ),
     128                                                        'host-webfonts-local'
     129                                                ),
     130                                                apply_filters( 'omgf_settings_page_title', 'OMGF' ),
     131                                                count( $failures )
     132                                        )
     133                                ); ?></h4>
     134                        <ul>
     135                                <?php foreach ( $failures as $url => $failure ) : ?>
     136                                        <li>
     137                                                <strong><?php echo esc_html( $url ); ?></strong><br/>
     138                                                <?php echo esc_html( Download::get_failure_message( $failure['reason'] ) ); ?>
     139                                        </li>
     140                                <?php endforeach; ?>
     141                        </ul>
     142                        <p>
     143                                [ <a href="#" id="omgf-dismiss-download-failures"><?php echo esc_html__( 'Dismiss', 'host-webfonts-local' ); ?></a> ]
     144                        </p>
     145                </div>
     146                <?php
    103147        }
    104148
    … …  
    141185                                        unset( $warnings['google_fonts_checker'] );
    142186                                }
     187
     188                                self::render_download_failures();
    143189                                ?>
    144190                                <?php if ( ! empty( $google_fonts_checker_results ) ): ?>
  • host-webfonts-local/trunk/src/Admin/Notice.php

    r3494741 r3721511  
    4141                                foreach ( $notice as $type => $message ) {
    4242                                        ?>
    43                                         <div id="message" class="notice notice-<?php echo $type; ?> is-dismissible">
     43                                        <div id="message" class="notice notice-<?php echo esc_attr( $type ); ?> is-dismissible">
    4444                                                <?php foreach ( $message as $line ) : ?>
    45                                                         <p><strong><?php echo $line; ?></strong></p>
     45                                                        <p><strong><?php echo wp_kses_post( $line ); ?></strong></p>
    4646                                                <?php endforeach; ?>
    4747                                        </div>
  • host-webfonts-local/trunk/src/Admin/Settings.php

    r3503345 r3721511  
    3535
    3636        const OMGF_DB_PERF_CHECK = 'omgf_perf_check';
     37
     38        const OMGF_DB_DOWNLOAD_FAILURES = 'omgf_download_failures';
    3739
    3840        const OMGF_DB_CACHE_TIMESTAMP = 'omgf_cache_timestamp';
  • host-webfonts-local/trunk/src/Admin/Settings/Optimize.php

    r3531267 r3721511  
    269269                                        }
    270270                                        ?>
    271                                         <tbody class="stylesheet" id="<?php echo $handle; ?>">
     271                                        <tbody class="stylesheet" id="<?php echo esc_attr( $handle ); ?>">
    272272                                        <tr>
    273273                                                <th colspan="6"><?php echo sprintf(
    274274                                                                __( 'Stylesheet handle: %s', 'host-webfonts-local' ),
    275                                                                 $handle
     275                                                                esc_html( $handle )
    276276                                                        ); ?></th>
    277277                                        </tr>
    … …  
    390390                                <?php endforeach; ?>
    391391                        </table>
    392                         <input type="hidden" name="<?php echo Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS; ?>"
    393                                    value="<?php echo base64_encode( serialize( $this->optimized_fonts ) ); ?>"/>
    394392                        <input id="<?php echo Settings::OMGF_OPTIMIZE_SETTING_UNLOAD_STYLESHEETS; ?>" type="hidden"
    395393                                   name="omgf_settings[<?php echo Settings::OMGF_OPTIMIZE_SETTING_UNLOAD_STYLESHEETS; ?>]"
  • host-webfonts-local/trunk/src/Download.php

    r3707539 r3721511  
    1919use OMGF\Helper as OMGF;
    2020use OMGF\Admin\Notice;
     21use OMGF\Admin\Settings;
    2122
    2223class Download {
    … …  
    3940        ];
    4041
     42        /**
     43         * Default maximum size (in bytes) of a downloaded font file.
     44         *
     45         * @since v6.3.12
     46         */
     47        const MAX_FILE_SIZE = 25 * MB_IN_BYTES;
     48
     49        /**
     50         * Maximum number of failed downloads reported on the Dashboard.
     51         *
     52         * @since v6.3.12
     53         */
     54        const MAX_FAILURES = 20;
     55
     56        /**
     57         * Reasons why a downloaded file was discarded.
     58         */
     59        const FAILURE_EMPTY = 'empty';
     60
     61        const FAILURE_TOO_LARGE = 'too_large';
     62
     63        const FAILURE_INVALID = 'invalid';
     64
    4165
    4266        /** @var string $url */
    … …  
    5781                string $path
    5882        ) {
    59                 $this->url      = $url;
    60                 $this->filename = $filename;
     83                $this->url = $url;
     84                /**
     85                 * @since v6.3.12 Sanitize the filename so a font family (which it's partly built from) can't contain a
     86                 *                path separator and write the downloaded file outside of $path.
     87                 */
     88                $this->filename = sanitize_file_name( $filename );
    6189                $this->path     = $path;
    6290        }
    … …  
    82110                $temp_filename = $this->path . '/' . $this->filename . '-' . wp_generate_password( 12, false ) . '.tmp';
    83111
    84                 $response = wp_safe_remote_get(
     112                $max_file_size = self::get_max_file_size();
     113                $response      = wp_safe_remote_get(
    85114                        $this->url,
    86115                        [
    87                                 'timeout'  => 300,
    88                                 'stream'   => true,
    89                                 'filename' => $temp_filename,
     116                                'timeout'             => 300,
     117                                'stream'              => true,
     118                                'filename'            => $temp_filename,
     119                                /**
     120                                 * @since v6.3.12 Stop downloading once the file exceeds the maximum size. One extra byte is requested, so
     121                                 *                a file which was cut off can be told apart from a file of exactly the maximum size.
     122                                 */
     123                                'limit_response_size' => $max_file_size + 1,
    90124                        ]
    91125                );
    … …  
    161195                }
    162196
     197                /**
     198                 * @since v6.3.12 Validate the downloaded file before it's stored.
     199                 */
     200                $reason = $this->validate_file( $temp_filename, $max_file_size );
     201
     202                if ( $reason ) {
     203                        $this->delete_temp_file( $temp_filename );
     204
     205                        /**
     206                         * Reported on the Dashboard (and in the Admin Bar), because downloads often run during a visitor's request.
     207                         *
     208                         * @see \OMGF\Admin\Dashboard::render_download_failures()
     209                         */
     210                        self::add_failure( $this->url, $reason );
     211
     212                        return '';
     213                }
     214
    163215                if ( file_exists( $temp_filename ) ) {
    164216                        $final_path = $this->path . '/' . $this->filename . '.' . $extension;
    … …  
    179231                }
    180232
     233                self::remove_failure( $this->url );
     234
    181235                return OMGF_UPLOAD_URL . str_replace( OMGF_UPLOAD_DIR, '', $this->path ) . '/' . $this->filename . '.' . $extension;
     236        }
     237
     238        /**
     239         * Maximum size (in bytes) of a downloaded font file.
     240         *
     241         * @since  v6.3.12
     242         * @filter omgf_download_max_file_size
     243         *
     244         * @return int
     245         */
     246        public static function get_max_file_size() {
     247                return max( 1, (int) apply_filters( 'omgf_download_max_file_size', self::MAX_FILE_SIZE ) );
     248        }
     249
     250        /**
     251         * Checks if $file is a complete font file, i.e. not empty, not cut off because it exceeds the maximum size, and
     252         * starting with the signature of a font format.
     253         *
     254         * @since v6.3.12
     255         *
     256         * @param string $file
     257         * @param int    $max_file_size
     258         *
     259         * @return string An error message if the file is invalid, an empty string if it's valid.
     260         */
     261        private function validate_file( $file, $max_file_size ) {
     262                $size = file_exists( $file ) ? filesize( $file ) : 0;
     263
     264                if ( ! $size ) {
     265                        return self::FAILURE_EMPTY;
     266                }
     267
     268                if ( $size > $max_file_size ) {
     269                        return self::FAILURE_TOO_LARGE;
     270                }
     271
     272                if ( ! self::is_font_file( $file ) ) {
     273                        return self::FAILURE_INVALID;
     274                }
     275
     276                return '';
     277        }
     278
     279        /**
     280         * Failed downloads, most recent first.
     281         *
     282         * @since v6.3.12
     283         *
     284         * @return array [ url => [ 'reason' => string, 'time' => int ] ]
     285         */
     286        public static function get_failures() {
     287                return self::normalize_failures( get_option( Settings::OMGF_DB_DOWNLOAD_FAILURES, [] ) );
     288        }
     289
     290        /**
     291         * Removes invalid entries and sorts the failed downloads, most recent first.
     292         *
     293         * @since v6.3.12
     294         *
     295         * @param mixed $failures
     296         *
     297         * @return array
     298         */
     299        private static function normalize_failures( $failures ) {
     300                if ( ! is_array( $failures ) ) {
     301                        return []; // @codeCoverageIgnore
     302                }
     303
     304                $failures = array_filter(
     305                        $failures,
     306                        function ( $failure, $url ) {
     307                                return is_string( $url ) && is_array( $failure ) && in_array( $failure['reason'] ?? '', self::get_failure_reasons(), true );
     308                        },
     309                        ARRAY_FILTER_USE_BOTH
     310                );
     311
     312                uasort(
     313                        $failures,
     314                        function ( $a, $b ) {
     315                                return ( $b['time'] ?? 0 ) <=> ( $a['time'] ?? 0 );
     316                        }
     317                );
     318
     319                return $failures;
     320        }
     321
     322        /**
     323         * Stores a failed download, keeping at most MAX_FAILURES (the most recent ones).
     324         *
     325         * @since v6.3.12
     326         *
     327         * @param string $url
     328         * @param string $reason One of the FAILURE_* constants.
     329         *
     330         * @return void
     331         */
     332        public static function add_failure( $url, $reason ) {
     333                self::update_failures(
     334                        function ( $failures ) use ( $url, $reason ) {
     335                                unset( $failures[ $url ] );
     336
     337                                return array_slice( [ $url => [ 'reason' => $reason, 'time' => time() ] ] + $failures, 0, self::MAX_FAILURES, true );
     338                        }
     339                );
     340        }
     341
     342        /**
     343         * Removes a failed download, e.g. once it succeeds.
     344         *
     345         * @since v6.3.12
     346         *
     347         * @param string $url
     348         *
     349         * @return void
     350         */
     351        public static function remove_failure( $url ) {
     352                /**
     353                 * This runs after every successful download, so only lock when there's something to remove. The check reads the
     354                 * database, because a cached copy might not contain a failure stored by another request.
     355                 */
     356                if ( ! isset( self::read_stored_failures()[ $url ] ) ) {
     357                        return;
     358                }
     359
     360                self::update_failures(
     361                        function ( $failures ) use ( $url ) {
     362                                unset( $failures[ $url ] );
     363
     364                                return $failures;
     365                        }
     366                );
     367        }
     368
     369        /**
     370         * Removes all failed downloads, i.e. when they're dismissed.
     371         *
     372         * @since v6.3.12
     373         *
     374         * @return void
     375         */
     376        public static function clear_failures() {
     377                self::update_failures(
     378                        function () {
     379                                return [];
     380                        }
     381                );
     382        }
     383
     384        /**
     385         * Reads the failed downloads from the database, instead of a (possibly outdated) cached copy.
     386         *
     387         * @since v6.3.12
     388         *
     389         * @return array
     390         */
     391        private static function read_stored_failures() {
     392                global $wpdb;
     393
     394                $stored = $wpdb->get_var(
     395                        $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s", Settings::OMGF_DB_DOWNLOAD_FAILURES )
     396                );
     397
     398                return self::normalize_failures( $stored === null ? [] : maybe_unserialize( $stored ) );
     399        }
     400
     401        /**
     402         * Updates the failed downloads while holding a (MySQL) lock, based on the value currently stored in the database,
     403         * so concurrent downloads can't overwrite each other's changes.
     404         *
     405         * @since v6.3.12
     406         *
     407         * @param callable $callback Receives the current failed downloads and returns the new ones.
     408         *
     409         * @return void
     410         */
     411        private static function update_failures( $callback ) {
     412                global $wpdb;
     413
     414                $lock_name = $wpdb->prefix . Settings::OMGF_DB_DOWNLOAD_FAILURES;
     415                // Databases which don't support locks (e.g. SQLite) return null, in which case we proceed without one.
     416                $locked = (bool) $wpdb->get_var( $wpdb->prepare( 'SELECT GET_LOCK(%s, %d)', $lock_name, 5 ) );
     417
     418                try {
     419                        $failures = $callback( self::read_stored_failures() );
     420
     421                        wp_cache_delete( Settings::OMGF_DB_DOWNLOAD_FAILURES, 'options' );
     422                        wp_cache_delete( 'notoptions', 'options' );
     423
     424                        if ( empty( $failures ) ) {
     425                                delete_option( Settings::OMGF_DB_DOWNLOAD_FAILURES );
     426                        } else {
     427                                update_option( Settings::OMGF_DB_DOWNLOAD_FAILURES, $failures, false );
     428                        }
     429                } finally {
     430                        if ( $locked ) {
     431                                $wpdb->query( $wpdb->prepare( 'SELECT RELEASE_LOCK(%s)', $lock_name ) );
     432                        }
     433                }
     434        }
     435
     436        /**
     437         * @since v6.3.12
     438         *
     439         * @return string[]
     440         */
     441        private static function get_failure_reasons() {
     442                return [ self::FAILURE_EMPTY, self::FAILURE_TOO_LARGE, self::FAILURE_INVALID ];
     443        }
     444
     445        /**
     446         * A human-readable description of why a download failed, and what the user can do about it.
     447         *
     448         * @since v6.3.12
     449         *
     450         * @param string $reason One of the FAILURE_* constants.
     451         *
     452         * @return string
     453         */
     454        public static function get_failure_message( $reason ) {
     455                switch ( $reason ) {
     456                        case self::FAILURE_TOO_LARGE:
     457                                return sprintf(
     458                                /* translators: %s: maximum file size, e.g. 25 MB */
     459                                        __( 'The file exceeds the maximum file size of %s, so it isn\'t used and a fallback font is shown instead.', 'host-webfonts-local' ),
     460                                        size_format( self::get_max_file_size() )
     461                                );
     462                        case self::FAILURE_INVALID:
     463                                return __( 'The downloaded file isn\'t a font file (e.g. an error page), so a fallback font is shown until it\'s downloaded successfully. Click Save & Optimize to try again.', 'host-webfonts-local' );
     464                        default:
     465                                return __( 'The downloaded file was empty, so a fallback font is shown until it\'s downloaded successfully. Click Save & Optimize to try again.', 'host-webfonts-local' );
     466                }
     467        }
     468
     469        /**
     470         * Checks if $file starts with the signature (magic bytes) of a WOFF2, WOFF, TrueType, OpenType or EOT file.
     471         *
     472         * @since v6.3.12
     473         *
     474         * @param string $file
     475         *
     476         * @return bool
     477         */
     478        public static function is_font_file( $file ) {
     479                $handle = @fopen( $file, 'rb' ); // phpcs:ignore
     480
     481                if ( ! $handle ) {
     482                        return false; // @codeCoverageIgnore
     483                }
     484
     485                $header = (string) fread( $handle, 36 );
     486
     487                fclose( $handle );
     488
     489                // WOFF2, WOFF, OpenType (CFF), TrueType, TrueType (Apple), TrueType Collection.
     490                if ( in_array( substr( $header, 0, 4 ), [ 'wOF2', 'wOFF', 'OTTO', "\x00\x01\x00\x00", 'true', 'ttcf' ], true ) ) {
     491                        return true;
     492                }
     493
     494                // EOT: magic number 0x504C at offset 34.
     495                return strlen( $header ) >= 36 && substr( $header, 34, 2 ) === 'LP';
    182496        }
    183497
  • host-webfonts-local/trunk/src/Filters.php

    r3531267 r3721511  
    2828                add_filter( 'home_url', [ $this, 'force_ssl' ], 1000, 2 );
    2929                add_filter( 'omgf_optimize_user_agent', [ $this, 'maybe_do_legacy_mode' ] );
    30                 add_filter( 'pre_update_option_omgf_optimized_fonts', [ $this, 'base64_decode_optimized_fonts' ] );
    3130                add_filter( 'omgf_do_not_load_frontend_css', [ $this, 'maybe_load_frontend_assets' ] );
    32         }
    33 
    34         /**
    35          * @since v5.0.5 omgf_optimized_fonts is base64_encoded in the frontend, to bypass firewall restrictions on
    36          * some servers.
    37          *
    38          * @param $old_value
    39          * @param $value
    40          *
    41          * @return bool|array
    42          */
    43         public function base64_decode_optimized_fonts( $value ) {
    44                 if ( is_string( $value ) && base64_encode( base64_decode( $value, true ) ) === $value ) {
    45                         return base64_decode( $value );
    46                 }
    47 
    48                 return $value;
    4931        }
    5032
  • host-webfonts-local/trunk/src/Frontend/Process.php

    r3707539 r3721511  
    629629                         *                just fine. Using a negated character class (instead of adding the /s modifier) makes
    630630                         *                sure the match can never run past a quote character.
    631                          */
    632                         preg_match( '/href=([\'"])(?P<href>[^\'"]*)\1/', $link, $href );
     631                         * @since v6.3.12 Only match the href attribute when it starts a new attribute (after whitespace or the
     632                         *                tag opener), so an attribute whose name ends in "href" (e.g. data-href, x:href,
     633                         *                x.href) isn't captured as the href.
     634                         */
     635                        preg_match( '/(?:^|[\s<])href=([\'"])(?P<href>[^\'"]*)\1/', $link, $href );
    633636
    634637                        /**
    … …  
    734737
    735738                        /**
     739                         * @since v6.3.12 Only process (and request) stylesheets whose URL is actually hosted on the Google
     740                         *                Fonts API (or a compatible endpoint). The element was kept because one of its
     741                         *                attribute values points at the API (see get_element_urls()), but the href used
     742                         *                here could point at a different — e.g. internal — host. Checking here, before the
     743                         *                branches below, leaves a non-API link untouched (not removed, not swapped for a
     744                         *                cached file) and makes sure it's never requested, which prevents SSRF.
     745                         */
     746                        if ( ! $this->is_font_api_url( $url ) ) {
     747                                continue;
     748                        }
     749
     750                        /**
    736751                         * If the stylesheet with $handle is completely marked for unloading, just remove the element
    737752                         * to prevent it from loading.
    … …  
    795810
    796811                        $search[ $key ]  = $stack['href'];
    797                         $replace[ $key ] = $cached_url ? $cached_url . '?ver=' . $this->timestamp : '';
     812                        /**
     813                         * @since v6.3.12 If optimization produced no URL (e.g. the request was blocked or redirected, the
     814                         *                fetch failed, or the response wasn't a stylesheet), leave the original link
     815                         *                untouched instead of removing it, so the fonts keep loading.
     816                         */
     817                        $replace[ $key ] = $cached_url ? $cached_url . '?ver=' . $this->timestamp : $stack['href'];
    798818                }
    799819
  • host-webfonts-local/trunk/src/Helper.php

    r3707539 r3721511  
    133133
    134134        /**
     135         * Unserializes $value if it's serialized, like maybe_unserialize(), but only allows stdClass objects, because
     136         * OMGF never stores any other objects.
     137         *
     138         * @since v6.3.12
     139         *
     140         * @param mixed $value
     141         *
     142         * @return mixed
     143         */
     144        public static function maybe_unserialize( $value ) {
     145                if ( ! is_string( $value ) || ! is_serialized( $value ) ) {
     146                        return $value;
     147                }
     148
     149                return @unserialize( trim( $value ), [ 'allowed_classes' => [ 'stdClass' ] ] ); // phpcs:ignore
     150        }
     151
     152        /**
    135153         * Method to retrieve OMGF's settings from database.
    136154         * WARNING: DO NOT ATTEMPT TO RETRIEVE WP CORE SETTINGS USING THIS METHOD. IT WILL FAIL.
    … …  
    151169                        // get_option() should take care of this, but sometimes it doesn't.
    152170                        if ( is_string( $value ) ) {
    153                                 $value = maybe_unserialize( $value );
     171                                $value = self::maybe_unserialize( $value );
    154172                        }
    155173
    … …  
    292310                }
    293311
     312                /**
     313                 * @since v6.3.12 A font family is used to build the filename its files are stored under, so reject any
     314                 *                path separator to make sure a crafted name can't traverse out of the fonts directory.
     315                 *                Dots are left alone: a font family can legitimately contain them, and without a
     316                 *                separator they can't traverse (the download filename is sanitized regardless).
     317                 */
     318                if ( preg_match( '#[/\\\\]#', $family ) ) {
     319                        return false; // @codeCoverageIgnore
     320                }
     321
    294322                // A CSS custom property, e.g. --wp--preset--font-family--foo.
    295323                if ( str_starts_with( $family, '--' ) ) {
    … …  
    534562                 */
    535563                if ( is_string( self::$admin_optimized_fonts ) && self::$admin_optimized_fonts !== '' ) {
    536                         self::$admin_optimized_fonts = maybe_unserialize( self::$admin_optimized_fonts ); // @codeCoverageIgnore
     564                        self::$admin_optimized_fonts = self::maybe_unserialize( self::$admin_optimized_fonts ); // @codeCoverageIgnore
    537565                }
    538566
    … …  
    698726                 */
    699727                if ( is_string( self::$optimized_fonts ) && self::$optimized_fonts !== '' ) {
    700                         self::$optimized_fonts = maybe_unserialize( self::$optimized_fonts ); // @codeCoverageIgnore
     728                        self::$optimized_fonts = self::maybe_unserialize( self::$optimized_fonts ); // @codeCoverageIgnore
    701729                }
    702730
  • host-webfonts-local/trunk/src/Optimize.php

    r3707539 r3721511  
    298298                /** @codeCoverageIgnoreEnd */
    299299
    300                 $response = wp_remote_get(
     300                /**
     301                 * @since v6.3.12 Use wp_safe_remote_get() so internal/private hosts are rejected even if the host
     302                 *                allow-list is ever bypassed. Belt-and-braces against SSRF.
     303                 */
     304                $response = wp_safe_remote_get(
    301305                        $url,
    302306                        [
     307                                /**
     308                                 * @since v6.3.12 Never follow redirects: the Google Fonts API (and compatible CDNs) serve the
     309                                 *                stylesheet directly, so a redirect would only point the request somewhere else.
     310                                 */
     311                                'redirection' => 0,
    303312                                /**
    304313                                 * Allow WP devs to use a different User-Agent, e.g. for compatibility with older browsers/OSes.
Note: See TracChangeset for help on using the changeset viewer.