├── .github └── workflows │ └── nodejs.yml ├── assets ├── css │ ├── admin.css │ ├── admin.css.map │ ├── admin.scss │ ├── calendar.css │ ├── elements.css │ ├── elements.css.map │ ├── elements.scss │ ├── popup-builder.css │ ├── popup-builder.css.map │ ├── popup-builder.scss │ ├── popups.css │ ├── popups.css.map │ └── popups.scss ├── img │ ├── default │ │ ├── cafe.png │ │ ├── city-day.png │ │ ├── city-night.png │ │ ├── clothes.png │ │ ├── man-blue.png │ │ ├── man-thumbs-up.png │ │ ├── man-working.png │ │ ├── shopping.png │ │ ├── woman-shopping.png │ │ ├── woman-working.png │ │ ├── woman-yellow.png │ │ └── yellow-gradient.png │ ├── groundhogg-banner.png │ └── template-library-coming-soon.png └── js │ ├── baremetrics-calendar.js │ ├── baremetrics-calendar.min.js │ ├── chart.min.js │ ├── elements.js │ ├── elements.min.js │ ├── lib │ ├── morphdom.js │ └── morphdom.min.js │ ├── make-el.js │ ├── make-el.min.js │ ├── popup-builder.js │ ├── popup-builder.min.js │ ├── popups.js │ ├── popups.min.js │ ├── reports.js │ ├── reports.min.js │ ├── settings.js │ └── settings.min.js ├── holler-box.php ├── includes ├── Holler_EDD_SL_Plugin_Updater.php ├── class-holler-admin.php ├── class-holler-api.php ├── class-holler-frontend.php ├── class-holler-integrations.php ├── class-holler-lead.php ├── class-holler-licensing.php ├── class-holler-popup.php ├── class-holler-reporting.php ├── class-holler-settings.php ├── class-holler-telemetry.php └── class-holler-updater.php ├── languages ├── holler-box.mo └── holler-box.po ├── readme.txt └── uninstall.php /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to WordPress.org 2 | on: 3 | push: 4 | tags-ignore: 5 | - "*.dev" 6 | - "*.dev.*" 7 | - "*.beta" 8 | - "*.beta.*" 9 | - "*.alpha" 10 | - "*.alpha.*" 11 | - "*.rc.*" 12 | jobs: 13 | tag: 14 | name: New tag 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@master 18 | - name: Install Subversion 19 | run: sudo apt-get install subversion 20 | - name: WordPress Plugin Deploy 21 | with: 22 | generate-zip: true 23 | uses: 10up/action-wordpress-plugin-deploy@stable 24 | env: 25 | SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} 26 | SVN_USERNAME: ${{ secrets.SVN_USERNAME }} 27 | SLUG: holler-box 28 | - name: Create GitHub release 29 | uses: softprops/action-gh-release@v1 30 | with: 31 | files: ${{github.workspace}}/${{ github.event.repository.name }}.zip 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /assets/css/admin.css: -------------------------------------------------------------------------------- 1 | .holler-spinner { 2 | display: block; 3 | height: 15px; 4 | width: 15px; 5 | border: 5px solid rgba(255, 255, 255, 0.4); 6 | border-bottom-color: #ffffff; 7 | border-radius: 50%; 8 | position: relative; 9 | animation: holler-rotation 1s linear infinite; 10 | } 11 | 12 | .widefat th.column-impressions, .widefat th.column-conversions, .widefat th.column-cvr, .widefat td.column-impressions, .widefat td.column-conversions, .widefat td.column-cvr { 13 | text-align: center; 14 | width: 100px; 15 | } 16 | 17 | .holler-header { 18 | padding-left: 30px; 19 | padding-right: 30px; 20 | } 21 | .holler-header #logo svg { 22 | width: 200px; 23 | } 24 | 25 | body.hollerbox_page_hollerbox_reports #wpcontent, 26 | body.hollerbox_page_hollerbox #wpcontent { 27 | padding: 0; 28 | } 29 | 30 | body.hollerbox_page_hollerbox .holler-header { 31 | padding-left: calc( (100% - 800px) / 2 ); 32 | padding-right: calc( (100% - 800px) / 2 ); 33 | } 34 | body.hollerbox_page_hollerbox #page { 35 | max-width: 800px; 36 | margin: 30px auto; 37 | gap: 30px; 38 | } 39 | body.hollerbox_page_hollerbox #page #settings { 40 | display: flex; 41 | flex-direction: column; 42 | gap: 30px; 43 | width: 100%; 44 | } 45 | body.hollerbox_page_hollerbox #page #settings .disable-credit.disable-changes { 46 | pointer-events: none; 47 | opacity: 0.5; 48 | } 49 | body.hollerbox_page_hollerbox #page #settings .credit { 50 | background: rgba(16, 38, 64, 0.05); 51 | padding: 5px; 52 | border-radius: 5px; 53 | } 54 | body.hollerbox_page_hollerbox #page #settings .danger-zone { 55 | overflow: hidden; 56 | } 57 | body.hollerbox_page_hollerbox #page #settings .danger-zone .holler-panel-header { 58 | background: rgba(233, 31, 79, 0.1); 59 | } 60 | body.hollerbox_page_hollerbox #page #settings .danger-zone .holler-panel-header h2 { 61 | color: #940e2e; 62 | } 63 | body.hollerbox_page_hollerbox #page #right { 64 | width: 260px; 65 | flex-shrink: 0; 66 | display: flex; 67 | flex-direction: column; 68 | gap: 30px; 69 | } 70 | body.hollerbox_page_hollerbox #page #right .holler-menu a { 71 | padding: 16px; 72 | font-size: 14px; 73 | display: flex; 74 | gap: 10px; 75 | } 76 | body.hollerbox_page_hollerbox #page #right .holler-menu a:hover { 77 | background: rgba(16, 38, 64, 0.05); 78 | text-decoration: none; 79 | } 80 | body.hollerbox_page_hollerbox #page #right .holler-menu a svg { 81 | width: 20px; 82 | } 83 | body.hollerbox_page_hollerbox .setup-page { 84 | max-width: 600px; 85 | margin: auto; 86 | } 87 | body.hollerbox_page_hollerbox .setup-page .page-inner > .holler-panel > .inside { 88 | padding: 30px; 89 | padding-bottom: 50px; 90 | } 91 | body.hollerbox_page_hollerbox .setup-page .page-inner > .holler-panel input[type=text], 92 | body.hollerbox_page_hollerbox .setup-page .page-inner > .holler-panel input[type=email] { 93 | width: 100%; 94 | font-size: 16px; 95 | padding: 4px 12px; 96 | } 97 | body.hollerbox_page_hollerbox .setup-page .page-inner .plugin .plugin-actions { 98 | border-top: 1px solid rgba(16, 38, 64, 0.1); 99 | background: rgba(16, 38, 64, 0.02); 100 | } 101 | body.hollerbox_page_hollerbox .setup-page .page-inner .plugin h2 { 102 | margin: 0; 103 | font-size: 22px; 104 | } 105 | body.hollerbox_page_hollerbox .setup-page .page-inner .plugin .icon { 106 | min-width: 80px; 107 | } 108 | body.hollerbox_page_hollerbox .setup-page label, 109 | body.hollerbox_page_hollerbox .setup-page li, 110 | body.hollerbox_page_hollerbox .setup-page p { 111 | font-size: 16px; 112 | } 113 | body.hollerbox_page_hollerbox .setup-page ul { 114 | list-style: disc; 115 | padding-left: 20px; 116 | } 117 | body.hollerbox_page_hollerbox .setup-page #logo { 118 | display: flex; 119 | justify-content: center; 120 | } 121 | body.hollerbox_page_hollerbox .setup-page #logo svg { 122 | width: 300px; 123 | margin: 60px auto; 124 | } 125 | 126 | body.hollerbox_page_hollerbox_reports .popup-links { 127 | font-size: 16px; 128 | } 129 | body.hollerbox_page_hollerbox_reports #holler-app #reports-here { 130 | padding: 30px; 131 | display: grid; 132 | grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; 133 | gap: 30px; 134 | } 135 | body.hollerbox_page_hollerbox_reports #holler-app .span-full { 136 | grid-column: span 6; 137 | } 138 | body.hollerbox_page_hollerbox_reports #holler-app .span-half { 139 | grid-column: span 3; 140 | } 141 | body.hollerbox_page_hollerbox_reports #holler-app .span-third { 142 | grid-column: span 2; 143 | } 144 | body.hollerbox_page_hollerbox_reports #holler-app canvas { 145 | height: 500px; 146 | } 147 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel .holler-panel-header h2 { 148 | font-size: 18px; 149 | padding: 16px; 150 | } 151 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel .holler-panel-header h2 .dashicons { 152 | font-size: 20px; 153 | height: 20px; 154 | width: 20px; 155 | } 156 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel .holler-big-number { 157 | font-size: 42px; 158 | font-weight: 500; 159 | margin: 10px 0; 160 | } 161 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel table { 162 | width: 100%; 163 | border-collapse: collapse; 164 | } 165 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel table thead th { 166 | font-weight: 500; 167 | text-align: left; 168 | } 169 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel table th, body.hollerbox_page_hollerbox_reports #holler-app .holler-panel table td { 170 | padding: 12px 16px; 171 | font-size: 16px; 172 | } 173 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel table th:not(:first-child), body.hollerbox_page_hollerbox_reports #holler-app .holler-panel table td:not(:first-child) { 174 | text-align: center; 175 | } 176 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel table th:first-child, body.hollerbox_page_hollerbox_reports #holler-app .holler-panel table td:first-child { 177 | width: 500px; 178 | } 179 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel table tbody tr:nth-child(odd) { 180 | background-color: rgba(0, 117, 255, 0.02); 181 | } 182 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel .pagination { 183 | padding: 12px 16px; 184 | justify-content: flex-end; 185 | } 186 | body.hollerbox_page_hollerbox_reports #holler-app .holler-panel .pagination .ellipsis { 187 | font-size: 20px; 188 | } 189 | 190 | /*# sourceMappingURL=admin.css.map */ 191 | -------------------------------------------------------------------------------- /assets/css/admin.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["admin.scss"],"names":[],"mappings":"AA6BA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAKE;EAGE;EACA;;;AAKN;EACE;EACA;;AAEA;EACE;;;AAMF;AAAA;EACE;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGE;EACE;EACA;;AAIJ;EACE,YAjFS;EAkFT;EACA;;AAGF;EACE;;AAEA;EACE,YAtFK;;AAuFL;EACE;;AAOR;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;EACA;EACA;EACA;;AAEA;EACE,YAlHK;EAmHL;;AAGF;EACE;;AAQV;EACE;EACA;;AAII;EACE;EACA;;AAGF;AAAA;EAGE;EACA;EACA;;AAMF;EACE;EACA,YAtJO;;AAyJT;EACE;EACA;;AAGF;EACE;;AAKN;AAAA;AAAA;EAGE;;AAGF;EACE;EACA;;AAGF;EAEE;EACA;;AAEA;EACE;EACA;;;AAQN;EACE;;AAKA;EACE;EAEA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAWE;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAKN;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGE;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE,kBAtRO;;AA0RX;EACE;EACA;;AAEA;EACE","file":"admin.css"} -------------------------------------------------------------------------------- /assets/css/admin.scss: -------------------------------------------------------------------------------- 1 | $primary-blue: rgb(0, 117, 255); 2 | $primary-blue-60: rgba(0, 117, 255, 0.60); 3 | $primary-blue-50: rgba(0, 117, 255, 0.50); 4 | $primary-blue-20: rgba(0, 117, 255, 0.20); 5 | $primary-blue-10: rgba(0, 117, 255, 0.10); 6 | $primary-blue-7: rgba(0, 117, 255, 0.07); 7 | $primary-blue-2: rgba(0, 117, 255, 0.02); 8 | $primary-dark: rgb(16, 38, 64); 9 | $primary-dark-50: rgba(16, 38, 64, 0.5); 10 | $primary-dark-30: rgba(16, 38, 64, 0.3); 11 | $primary-dark-15: rgba(16, 38, 64, 0.15); 12 | $primary-dark-10: rgba(16, 38, 64, 0.10); 13 | $primary-dark-5: rgba(16, 38, 64, 0.05); 14 | $primary-dark-1: rgba(16, 38, 64, 0.02); 15 | $error-red: rgb(233, 31, 79); 16 | $error-red-50: rgba(233, 31, 79, 0.5); 17 | $error-red-10: rgba(233, 31, 79, 0.10); 18 | $secondary-purple: rgb(108, 25, 173); 19 | $alert-yellow-50: rgba(255, 238, 88, 0.50); 20 | $alert-yellow: rgb(255, 238, 88); 21 | $holler-yellow: rgb(232, 173, 11); 22 | $benchmark-orange: rgb(245, 129, 21); 23 | $benchmark-orange-50: rgba(245, 129, 21, 0.50); 24 | $benchmark-orange-25: rgba(245, 129, 21, 0.25); 25 | $action-green: rgb(158, 206, 56); 26 | $action-green-50: rgba(158, 206, 56, 0.5); 27 | $box-shadow: 5px 5px 30px 0 rgba(24, 45, 70, 0.05); 28 | $background-color: #F6F9FB; 29 | 30 | .holler-spinner { 31 | display: block; 32 | height: 15px; 33 | width: 15px; 34 | border: 5px solid rgba(255, 255, 255, 0.4); 35 | border-bottom-color: #ffffff; 36 | border-radius: 50%; 37 | position: relative; 38 | animation: holler-rotation 1s linear infinite; 39 | } 40 | 41 | .widefat { 42 | th, td { 43 | &.column-impressions, 44 | &.column-conversions, 45 | &.column-cvr { 46 | text-align: center; 47 | width: 100px; 48 | } 49 | } 50 | } 51 | 52 | .holler-header { 53 | padding-left: 30px; 54 | padding-right: 30px; 55 | 56 | #logo svg { 57 | width: 200px; 58 | } 59 | } 60 | 61 | body.hollerbox_page_hollerbox_reports, 62 | body.hollerbox_page_hollerbox { 63 | #wpcontent { 64 | padding: 0; 65 | } 66 | } 67 | 68 | body.hollerbox_page_hollerbox { 69 | 70 | .holler-header { 71 | padding-left: calc( (100% - 800px) / 2 ); 72 | padding-right: calc( (100% - 800px) / 2 ); 73 | } 74 | 75 | #page { 76 | max-width: 800px; 77 | margin: 30px auto; 78 | gap: 30px; 79 | 80 | #settings { 81 | display: flex; 82 | flex-direction: column; 83 | gap: 30px; 84 | width: 100%; 85 | 86 | .disable-credit { 87 | &.disable-changes { 88 | pointer-events: none; 89 | opacity: 0.5; 90 | } 91 | } 92 | 93 | .credit { 94 | background: $primary-dark-5; 95 | padding: 5px; 96 | border-radius: 5px; 97 | } 98 | 99 | .danger-zone { 100 | overflow: hidden; 101 | 102 | .holler-panel-header{ 103 | background: $error-red-10; 104 | h2 { 105 | color: darken( $error-red, 20 ); 106 | } 107 | } 108 | 109 | } 110 | } 111 | 112 | #right { 113 | width: 260px; 114 | flex-shrink: 0; 115 | display: flex; 116 | flex-direction: column; 117 | gap: 30px; 118 | 119 | .holler-menu { 120 | a { 121 | padding: 16px; 122 | font-size: 14px; 123 | display: flex; 124 | gap: 10px; 125 | 126 | &:hover{ 127 | background: $primary-dark-5; 128 | text-decoration: none; 129 | } 130 | 131 | svg { 132 | width: 20px; 133 | } 134 | } 135 | } 136 | } 137 | 138 | } 139 | 140 | .setup-page { 141 | max-width: 600px; 142 | margin: auto; 143 | 144 | .page-inner { 145 | > .holler-panel { 146 | > .inside { 147 | padding: 30px; 148 | padding-bottom: 50px; 149 | } 150 | 151 | input[type="text"], 152 | input[type="email"] 153 | { 154 | width: 100%; 155 | font-size: 16px; 156 | padding: 4px 12px; 157 | } 158 | } 159 | 160 | .plugin { 161 | 162 | .plugin-actions { 163 | border-top: 1px solid $primary-dark-10; 164 | background: $primary-dark-1; 165 | } 166 | 167 | h2 { 168 | margin: 0; 169 | font-size: 22px; 170 | } 171 | 172 | .icon { 173 | min-width: 80px; 174 | } 175 | } 176 | } 177 | 178 | label, 179 | li, 180 | p { 181 | font-size: 16px; 182 | } 183 | 184 | ul { 185 | list-style: disc; 186 | padding-left: 20px; 187 | } 188 | 189 | #logo { 190 | 191 | display: flex; 192 | justify-content: center; 193 | 194 | svg { 195 | width: 300px; 196 | margin: 60px auto; 197 | } 198 | } 199 | } 200 | } 201 | 202 | body.hollerbox_page_hollerbox_reports { 203 | 204 | .popup-links { 205 | font-size: 16px; 206 | } 207 | 208 | #holler-app { 209 | 210 | #reports-here { 211 | padding: 30px; 212 | 213 | display: grid; 214 | grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; 215 | gap: 30px; 216 | } 217 | 218 | .span-full { 219 | grid-column: span 6; 220 | } 221 | 222 | .span-half { 223 | grid-column: span 3; 224 | } 225 | 226 | .span-third { 227 | grid-column: span 2; 228 | } 229 | 230 | canvas { 231 | height: 500px; 232 | } 233 | 234 | .holler-panel { 235 | 236 | &.pie .inside{ 237 | //padding-top: 0; 238 | //padding-bottom: 0; 239 | } 240 | 241 | .holler-panel-header { 242 | h2 { 243 | font-size: 18px; 244 | padding: 16px; 245 | 246 | .dashicons { 247 | font-size: 20px; 248 | height: 20px; 249 | width: 20px; 250 | } 251 | } 252 | } 253 | 254 | .holler-big-number { 255 | font-size: 42px; 256 | font-weight: 500; 257 | margin: 10px 0; 258 | } 259 | 260 | table { 261 | width: 100%; 262 | border-collapse: collapse; 263 | 264 | thead { 265 | th { 266 | font-weight: 500; 267 | text-align: left; 268 | } 269 | } 270 | 271 | th, td { 272 | padding: 12px 16px; 273 | font-size: 16px; 274 | 275 | &:not(:first-child) { 276 | text-align: center; 277 | } 278 | 279 | &:first-child { 280 | width: 500px; 281 | } 282 | } 283 | 284 | tbody tr:nth-child(odd) { 285 | background-color: $primary-blue-2; 286 | } 287 | } 288 | 289 | .pagination { 290 | padding: 12px 16px; 291 | justify-content: flex-end; 292 | 293 | .ellipsis { 294 | font-size: 20px; 295 | } 296 | } 297 | } 298 | 299 | } 300 | 301 | 302 | } 303 | -------------------------------------------------------------------------------- /assets/css/calendar.css: -------------------------------------------------------------------------------- 1 | .daterange{position:relative}.daterange *{box-sizing:border-box}.daterange div,.daterange li,.daterange span,.daterange ul{margin:0;padding:0;border:0}.daterange ul{list-style:none}.daterange.dr-active{z-index:10}.daterange .dr-input{display:-webkit-box;display:-ms-flexbox;display:flex;border:1px solid #C3CACD;border-radius:5px;background-color:#FFF;position:relative;z-index:5;overflow:hidden;height:40px}.daterange .dr-input:hover{border-color:#2693D5}.daterange .dr-input.dr-active{box-shadow:0 0 0 3px rgba(38,147,213,.4);border-color:#2693D5}.daterange .dr-input .dr-dates{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;padding:0 1.5rem 0 .75rem;min-width:calc(100% - 35px)}.daterange .dr-input .dr-dates .dr-date{font-size:.9375rem;padding:.65625rem 0;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;outline:0}.daterange .dr-input .dr-dates .dr-date.dr-active,.daterange .dr-input .dr-dates .dr-date:focus,.daterange .dr-input .dr-dates .dr-date:hover{color:#2693D5}.daterange .dr-input .dr-dates .dr-date:empty:after{content:attr(placeholder);color:#9BA3A7}.daterange .dr-input .dr-dates .dr-dates-dash{color:#9BA3A7;padding:0 10px;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;font-weight:600}.daterange .dr-input .dr-presets{width:2.1875rem;border-left:1px solid #C3CACD;-ms-flex-negative:0;flex-shrink:0;cursor:pointer;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.daterange .dr-input .dr-presets.dr-active,.daterange .dr-input .dr-presets:hover{border-color:#2693D5;box-shadow:inset 0 2px 3px #EBF1F4}.daterange .dr-input .dr-presets.dr-active .dr-preset-bar,.daterange .dr-input .dr-presets:hover .dr-preset-bar{background-color:#2693D5}.daterange .dr-input .dr-presets .dr-preset-bar{height:2px;background-color:#C3CACD;margin:1px 0 1px 25%}.daterange .dr-input .dr-presets .dr-preset-bar:nth-child(1){width:50%}.daterange .dr-input .dr-presets .dr-preset-bar:nth-child(2){width:40%}.daterange .dr-input .dr-presets .dr-preset-bar:nth-child(3){width:30%}.daterange .dr-selections{position:absolute}.daterange .dr-selections .dr-calendar{background-color:#FFF;font-size:.9375rem;box-shadow:0 0 5px #C3CACD;border-radius:5px;position:relative;overflow:hidden;z-index:4;padding-top:5px;top:-5px;left:4px;-webkit-transition:width .2s;transition:width .2s;min-width:210px}.daterange .dr-selections .dr-calendar .dr-range-switcher{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:.375rem .5rem;font-size:.875rem}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid rgba(195,202,205,.5);border-radius:5px;height:1.5625rem}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher i{color:#C3CACD;position:relative;top:-1px;cursor:pointer;font-size:.75rem;height:100%;width:20px}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher i:hover:after,.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher i:hover:before{background-color:#2693D5}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher i.dr-disabled{pointer-events:none;opacity:0}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher i:after,.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher i:before{content:"";position:absolute;width:7px;height:2px;background-color:#C3CACD;border-radius:1px;left:50%}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher i.dr-left:before{top:calc(50% - 2px);-webkit-transform:translate(-50%,-50%) rotate(-45deg);transform:translate(-50%,-50%) rotate(-45deg)}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher i.dr-left:after{top:calc(50% + 2px);-webkit-transform:translate(-50%,-50%) rotate(45deg);transform:translate(-50%,-50%) rotate(45deg)}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher i.dr-right:before{top:calc(50% - 2px);-webkit-transform:translate(-50%,-50%) rotate(45deg);transform:translate(-50%,-50%) rotate(45deg)}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-switcher i.dr-right:after{top:calc(50% + 2px);-webkit-transform:translate(-50%,-50%) rotate(-45deg);transform:translate(-50%,-50%) rotate(-45deg)}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-month-switcher{width:100%;margin-right:.375rem}.daterange .dr-selections .dr-calendar .dr-range-switcher .dr-year-switcher{min-width:80px}.daterange .dr-selections .dr-calendar .dr-days-of-week-list{display:-webkit-box;display:-ms-flexbox;display:flex;background-color:#EBF1F4;font-size:.625rem;color:#9BA3A7;padding:.3125rem 0;border:1px solid rgba(195,202,205,.5);border-left:none;border-right:none}.daterange .dr-selections .dr-calendar .dr-days-of-week-list .dr-day-of-week{width:calc(100% / 7);text-align:center}.daterange .dr-selections .dr-calendar .dr-day-list{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;font-size:.9375rem}.daterange .dr-selections .dr-calendar .dr-day-list .dr-day{padding:.3125rem;text-align:center;width:calc(100% / 7);cursor:pointer;color:#4f565c}.daterange .dr-selections .dr-calendar .dr-day-list .dr-day.dr-hover:not(.dr-current){background-color:#EBF1F4!important}.daterange .dr-selections .dr-calendar .dr-day-list .dr-day.dr-hover-before{border-left:2px solid #2693D5!important;border-radius:2px 0 0 2px;padding-left:.1875rem!important}.daterange .dr-selections .dr-calendar .dr-day-list .dr-day.dr-hover-after{border-right:2px solid #2693D5!important;border-radius:0 2px 2px 0;padding-right:.1875rem!important}.daterange .dr-selections .dr-calendar .dr-day-list .dr-end,.daterange .dr-selections .dr-calendar .dr-day-list .dr-selected,.daterange .dr-selections .dr-calendar .dr-day-list .dr-start{background-color:#EBF1F4}.daterange .dr-selections .dr-calendar .dr-day-list .dr-maybe{background-color:#EBF1F4!important}.daterange .dr-selections .dr-calendar .dr-day-list .dr-fade{color:#C3CACD}.daterange .dr-selections .dr-calendar .dr-day-list .dr-start{border-left:2px solid #2693D5;border-radius:2px 0 0 2px;padding-left:.1875rem}.daterange .dr-selections .dr-calendar .dr-day-list .dr-end{border-right:2px solid #2693D5;border-radius:0 2px 2px 0;padding-right:.1875rem}.daterange .dr-selections .dr-calendar .dr-day-list .dr-current{color:#2693D5!important;background-color:rgba(38,147,213,.2)!important}.daterange .dr-selections .dr-calendar .dr-day-list .dr-outside{pointer-events:none;cursor:default;color:rgba(195,202,205,.5)}.daterange .dr-selections .dr-preset-list{background-color:#FFF;color:#2693D5;font-size:.9375rem;box-shadow:0 0 5px #C3CACD;border-radius:5px;position:relative;overflow:hidden;z-index:4;padding-top:5px;top:-5px;left:4px;width:100%}.daterange .dr-selections .dr-list-item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end;padding:.75rem .625rem;border-bottom:1px solid #EBF1F4;cursor:pointer;white-space:nowrap}.daterange .dr-selections .dr-list-item:hover{background-color:#EBF1F4}.daterange .dr-selections .dr-list-item .dr-item-aside{color:#9BA3A7;font-size:.75rem;margin-left:.75rem;position:relative;top:1px}.daterange--single .dr-input{cursor:text}.daterange--single .dr-input .dr-dates{padding:0;min-width:160px;width:100%}.daterange--single .dr-input .dr-dates .dr-date{width:100%;padding:.65625rem .75rem;text-align:left;color:#4f565c} -------------------------------------------------------------------------------- /assets/css/elements.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["elements.scss"],"names":[],"mappings":";AA2BA;EACE;;;AAIA;EACE;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EAEE;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAKJ;EAEE;;AAIE;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAKF;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EAME;;AAJA;EACE;;AAKF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;;AAMR;EAEE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE,kBA/Ma;;AAkNf;EACE;;AAEA;EACE,kBA5NW;;;AAiOjB;EAEE;EACA;;AAEA;EACE;EACA;EAEA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EAEA;EAUA;EACA;EACA;EACA;EACA;;AAZA;EACE,kBArPW;;AAwPb;EACE;;;AAaJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA,OA1SY;EA2SZ;EACA;;AAKA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAOJ;AAAA;EACE;EACA;EACA;;;AAMJ;EAEE,cA1UQ;;AA4UR;EACE,cA7UM;EA8UN;EACA;;;AAMJ;EACE;;AAIA;EACE;;;AAKN;EACE;EACA;EACA,YAxVW;EAyVX;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAKA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;EAEE;EACA;;AAGF;EACE;;AAGF;EACE,OA1YU;;;AAgZlB;EACE;EACA;EACA;;;AAGF;EACE,YA3Ze;EA4Zf;EACA;EACA;EACA,OA7Za;;AA+Zb;EACE,YAtZc;EAuZd;;AAGF;EACE,YAvZkB;EAwZlB;;AAGF;EACE,YA/ac;;AAkbhB;EACE,YA9Zc;EA+Zd;;AAGF;EACE,YA5aW;EA6aX;;AAGF;EACE;;;AAOF;AAAA;AAAA;EACE,OA1bQ;;AA6bV;AAAA;AAAA;EACE,OAxbe;;AA4bjB;AAAA;AAAA;EACE,OA1bW;;AA6bb;AAAA;AAAA;EACE;;AAGF;AAAA;AAAA;EACE;;AAGF;AAAA;AAAA;EACE;;;AAKF;EACE;EACA;;AAEA;EACE;;;AAKN;EACE;EACA;AACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAIJ;EACE;;AAGF;EAEE;EACA;EACA;;AAEA;EACE;EASA;EACA;EACA;;AATA;EACE;EACA;EACA;EACA;;AAQJ;AAAA;AAAA;EAGE;EACA;;AAEA;AAAA;AAAA;EACE,YAvhBS;;AA0hBX;AAAA;AAAA;EACE;EACA;;AAGF;AAAA;AAAA;EACE;EACA;EACA;EACA;EACA;EACA;;AAMF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAOJ;EAEE;;AAEA;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;;AAOJ;EACE;;AAME;EACE;EACA;EACA;;;AAMR;EACE;EACA;;;AAGF;EACE;IACE;;EAEF;IACE;;;AAIJ;AAAA;AAAA;AAAA;EAIE;;AAEA;AAAA;AAAA;AAAA;EACE;EACA;EACA;;AAEA;AAAA;AAAA;AAAA;EACE;;AAGF;AAAA;AAAA;AAAA;EACE;EACA;;AAIJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAEE;EACA;EAMA;EACA;EACA;EACA;EACA;EACA;;AATA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;;AAUF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAEE;EACA;EACA;EAEA;EACA;EACA,OA/qBS;EAgrBT;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE,YAnrBS;;AAqrBT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;;AAIJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAEE,YAltBS;;AAotBT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA,OAttBO;;AAytBT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE,kBAztBQ;EA0tBR,OA9tBK;;AAmuBX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;EACA,OAtuBS;EAuuBT;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;EACA,OAruBO;;AAwuBT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA,cAjvBO;EAkvBP,kBA5uBS;;AA8uBT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA,YA3uBQ;EA4uBR,OAhvBK;;AAqvBX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE,YAhvBM;;AAkvBN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE,OAnvBI;EAovBJ;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE,OA3vBE;EA4vBF,kBA1vBK;;AA+vBX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA,OAzwBS;EA0wBT;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE,YA1wBU;;AA6wBZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE,kBA9wBU;;AAkxBd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAGE;EACA;EACA;;AAGA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;;AAIA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;;AASN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;;AAIJ;AAAA;AAAA;AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EAEE;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;;AAMJ;EACE;;AAEA;EACE;EACA;EAOA;EACA;EACA;EAEA;EACA;EACA;EACA;;AAZA;EACE;EACA;;AAYF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;;AAGE;EACE;;AASJ;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EAEE,YAt7BK;EAu7BL;;;AAOV;EACE;EACA;EACA;AACA;EACA;EACA;EACA;EACA,YA96BW;EA+6BX;EACA;;AAEA;EACE;EACA,kBAt7BW;;AAy7Bb;EACE;EACA,kBAp8BQ;;AAu8BV;EACE;EACA,kBAn8Be;;AAs8BjB;EACE;EACA,kBAp9BW;;;AAw9Bf;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAIA;EACE;EACA;;AAMR;EACE;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;;AAGF;EACE;EACA,YA/gCc;EAghCd;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;;AAKF;EACE;EACA;EACA;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;;AAKN;EACE;EACA;EACA;EACA;;AAGF;EAOE;EAEA;EACA;EACA;;AAKF;EACE;EACA;;;AAIJ;EACE;;;AAMF;EACE;EACA;;;AAGF;AAAA;EAEE;EACA;;AAEA;AAAA;AAAA;AAAA;EAEE;;AAEA;AAAA;AAAA;AAAA;EACE;;AAIJ;AAAA;EAQE;EACA;EACA;;AARA;AAAA;EACE;EACA;EACA;;AAQJ;AAAA;EACE;EACA;;;AAIJ;AAAA;EAEE,OAzqCU;;;AA6qCV;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EAEE;;AAEA;EACE;;AAEA;AAAA;EAEE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAEA;AAAA;EAEE;EACA;;AAGF;EAEE;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAIA;AAAA;EAEE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAKF;EACE;;AAKF;AAAA;EAEE;;AAKN;EACE;;AAGA;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;;AAKN;EAEE;EACA;EACA;EACA;EACA;EACA;EACA,YAhyCW;EAiyCX;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EAEA;;AAEA;EACE,kBAl0CY;EAm0CZ;;;AAKN;EAEE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIA;EACE,OA73CO;;AAg4CT;EACE;EACA,kBAl4CO;EAm4CP,cAn4CO;EAo4CP;;;AAOR;EACE;EACA;EACA;;AAEA;EAEE;EACA;EAUA;EACA;EACA;EACA;;AAXA;EACE,kBA14CW;;AA64Cb;EACE;;AASJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE,YA/5CS;EAg6CT;;AAGF;EACE,YA35CY;EA45CZ;;AAGF;EACE;EACA;EACA;;AAEA;EACE,kBAh7CS;;;AAu7CjB;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAIJ;AAAA;EAEE;EACA;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;EACA;;;AAMN;EACE;EACA;EACA;EACA;;;AAKA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;EAEA,kBAr/CgB;EAs/ChB;EACA;EACA;;AAKA;EACE;EAKA;EACA,kBAxgDW;EAygDX;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE,cA//CW;;AAigDX;EACE,kBAlgDS;;;AAugDf;EAEE;EACA;;AAGE;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,kBAtiDW;EAuiDX;EACA,YAvhDS;EAwhDT;EACA;EACA;EACA;EACA;EACA;;AAGA;EACE;EACA;EAEA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;;AAIJ;EACE;EACA;EAEA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAMR;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAIJ;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAOR;EAEE;EACA;EACA;;AAEA;EACE;EACA;;AAIA;EACE,OAtrDa;;AA2rDf;EACE,OAzrDS;;AA6rDb;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;;AAGE;EACE;;AAIJ;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AACA;EACE;IACE;;;EAEF;IACE;;;EAEF;IACE;;;EAEF;IACE;;;EAEF;IACE;;;EAEF;IACE;;;EAEF;IACE;;;EAEF;IACE;;;AAKJ;AACA;EACE;EACA;EACA;;;AAIA;EAEE;EACA;EACA;;AAEA;EACE,cAh4DS;EAi4DT,YA93DY;;AAk4DhB;EACE;;;AAKF;EACE;;;AAIJ;EACE;;AAEA;EACE;EAEA;EACA;EACA;;;AAKJ;EACE;EACA;EACA;;;AAGF;EACE;IACE;;;AAIJ;EACE;IACE;;;AAIJ;EACE;IACE;IACA;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACE;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;;AAGF;EACE;;AAEA;EACE;;AAIJ;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAKJ;EACE;;AAGF;EACE;;;AAKJ;EAWE;EACA;EACA;;AAXA;EACE;EACA;;AAEA;EACE;;AAQJ;EACE;EAEA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA,YA1iEY;EA2iEZ;EACA;EACA;EACA;;AAEA;EACE,YA7iEO;EA8iEP;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE,OAhkEK;EAikEL;;AAMR;EACE;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE,OAvmEO;EAwmEP,YA1mES;;;AAinEjB;EAEE;EACA;EACA,YAtnEgB;EAunEhB,OApnEa;EAqnEb;;AAGA;EACE;EACA;EACA","file":"elements.css"} -------------------------------------------------------------------------------- /assets/css/popup-builder.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | input#post-title { 3 | font-size: 16px; 4 | min-width: 500px; 5 | } 6 | 7 | .holler-pro-ad { 8 | padding: 20px; 9 | font-size: 18px; 10 | background: #e8ad0b; 11 | color: #fff; 12 | text-align: center; 13 | margin: 20px 30px 0 30px; 14 | border-radius: 5px; 15 | line-height: 1.3; 16 | } 17 | .holler-pro-ad a { 18 | color: #fff; 19 | } 20 | 21 | #triggers .holler-pro-ad { 22 | margin: 0; 23 | } 24 | 25 | .integration-group .holler-pro-ad { 26 | margin: 0; 27 | grid-column: span 3; 28 | } 29 | 30 | .promote-groundhogg img { 31 | max-width: 100%; 32 | border-radius: 5px; 33 | } 34 | .promote-groundhogg h1 { 35 | font-size: 42px; 36 | margin-bottom: 40px; 37 | font-weight: 300; 38 | line-height: 1.1; 39 | } 40 | .promote-groundhogg p, .promote-groundhogg ul, .promote-groundhogg li { 41 | font-size: 18px; 42 | } 43 | 44 | .pro-feature { 45 | max-width: 400px; 46 | } 47 | .pro-feature h1 { 48 | font-size: 42px; 49 | margin-bottom: 40px; 50 | font-weight: 300; 51 | } 52 | .pro-feature p, .pro-feature ul, .pro-feature li { 53 | font-size: 18px; 54 | } 55 | 56 | #wpcontent { 57 | padding: 0; 58 | } 59 | 60 | #wpfooter { 61 | display: none; 62 | } 63 | 64 | @keyframes holler-grow { 65 | from { 66 | max-height: 0; 67 | } 68 | to { 69 | max-height: 100vh; 70 | } 71 | } 72 | table.display-conditions-grid { 73 | box-sizing: border-box; 74 | border-collapse: collapse; 75 | width: 100%; 76 | } 77 | table.display-conditions-grid .picker-here { 78 | margin-top: 10px; 79 | } 80 | table.display-conditions-grid .rules { 81 | display: flex; 82 | flex-direction: column; 83 | gap: 10px; 84 | margin-bottom: 10px; 85 | } 86 | table.display-conditions-grid .rules .rule { 87 | padding-bottom: 10px; 88 | border-bottom: 1px solid rgba(16, 38, 64, 0.15); 89 | } 90 | table.display-conditions-grid .rules select { 91 | max-width: 100%; 92 | } 93 | table.display-conditions-grid th { 94 | text-align: left; 95 | font-size: 16px; 96 | padding: 30px; 97 | border-right: 1px solid rgba(16, 38, 64, 0.15); 98 | } 99 | table.display-conditions-grid td { 100 | width: 100%; 101 | box-sizing: border-box; 102 | padding: 30px; 103 | } 104 | table.display-conditions-grid tr:not(:last-child) td, table.display-conditions-grid tr:not(:last-child) th { 105 | border-bottom: 1px solid rgba(16, 38, 64, 0.15); 106 | } 107 | 108 | #triggers { 109 | display: flex; 110 | flex-direction: column; 111 | gap: 10px; 112 | } 113 | #triggers .trigger { 114 | display: flex; 115 | align-items: center; 116 | justify-content: space-between; 117 | border: 1px solid rgba(16, 38, 64, 0.15); 118 | border-radius: 5px; 119 | padding: 10px 10px 10px 15px; 120 | min-height: 30px; 121 | } 122 | #triggers .trigger input[type=number] { 123 | width: 60px; 124 | } 125 | #triggers .trigger .controls { 126 | display: none; 127 | } 128 | #triggers .trigger.enabled .controls { 129 | display: block; 130 | } 131 | #triggers .trigger.enabled .name { 132 | opacity: 1; 133 | } 134 | #triggers .trigger:hover { 135 | box-shadow: 5px 5px 30px 0 rgba(24, 45, 70, 0.05); 136 | } 137 | #triggers .trigger .name { 138 | font-size: 16px; 139 | font-weight: 500; 140 | opacity: 0.6; 141 | } 142 | #triggers .trigger button { 143 | justify-self: flex-end; 144 | margin-left: auto; 145 | } 146 | 147 | #integrations-here { 148 | padding: 30px; 149 | } 150 | #integrations-here h2 { 151 | font-size: 24px; 152 | } 153 | #integrations-here .integration-group { 154 | margin: 20px 0 40px 0; 155 | box-sizing: content-box; 156 | width: 100%; 157 | display: grid; 158 | grid-template-columns: 1fr 1fr 1fr 1fr 1fr; 159 | gap: 20px; 160 | } 161 | #integrations-here .integration-group:last-child { 162 | margin-bottom: 0; 163 | } 164 | #integrations-here .integration { 165 | position: relative; 166 | min-width: calc(100% / 6); 167 | cursor: pointer; 168 | border-radius: 5px; 169 | background: #ffffff; 170 | border: 1px solid rgba(16, 38, 64, 0.15); 171 | } 172 | #integrations-here .integration:hover { 173 | box-shadow: 5px 5px 30px 0 rgba(24, 45, 70, 0.05); 174 | } 175 | #integrations-here .integration:hover .integration-name { 176 | font-weight: bold; 177 | color: #0075ff; 178 | } 179 | #integrations-here .integration:hover svg { 180 | transform: scale(1.1); 181 | } 182 | #integrations-here .integration .integration-name { 183 | text-align: center; 184 | } 185 | #integrations-here .integration .icon { 186 | border-bottom: 1px solid rgba(16, 38, 64, 0.15); 187 | display: flex; 188 | align-items: center; 189 | justify-content: center; 190 | padding: 0 20px; 191 | height: 110px; 192 | } 193 | #integrations-here .integration .icon svg { 194 | transition: 0.4s; 195 | width: 60px; 196 | height: 60px; 197 | } 198 | 199 | .edit-integration #edit-here { 200 | padding: 30px; 201 | } 202 | 203 | @keyframes lds-facebook { 204 | 0% { 205 | top: 8px; 206 | height: 64px; 207 | } 208 | 50%, 100% { 209 | top: 24px; 210 | height: 32px; 211 | } 212 | } 213 | .lds-facebook { 214 | display: block; 215 | margin: auto; 216 | position: relative; 217 | width: 80px; 218 | height: 80px; 219 | } 220 | .lds-facebook div { 221 | display: inline-block; 222 | position: absolute; 223 | left: 8px; 224 | width: 16px; 225 | background: rgba(16, 38, 64, 0.5); 226 | animation: lds-facebook 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite; 227 | } 228 | .lds-facebook div:nth-child(1) { 229 | left: 8px; 230 | animation-delay: -0.24s; 231 | } 232 | .lds-facebook div:nth-child(2) { 233 | left: 32px; 234 | animation-delay: -0.12s; 235 | } 236 | .lds-facebook div:nth-child(3) { 237 | left: 56px; 238 | animation-delay: 0s; 239 | } 240 | 241 | #templates .template-filters { 242 | padding-top: 30px; 243 | padding-left: 30px; 244 | } 245 | #templates .template-grid { 246 | box-sizing: border-box; 247 | width: 100%; 248 | display: grid; 249 | grid-template-columns: 1fr 1fr 1fr; 250 | gap: 30px 30px; 251 | grid-auto-flow: row; 252 | padding: 30px; 253 | } 254 | #templates .template-grid.library { 255 | grid-template-columns: 1fr 1fr; 256 | } 257 | #templates .template-grid.library .template .preview-wrap { 258 | height: 300px; 259 | } 260 | #templates .template-grid .template { 261 | box-sizing: content-box; 262 | cursor: pointer; 263 | border-radius: 5px; 264 | border: 1px solid rgba(16, 38, 64, 0.15); 265 | overflow: hidden; 266 | position: relative; 267 | } 268 | #templates .template-grid .template span.pro-template { 269 | position: absolute; 270 | top: 10px; 271 | right: 10px; 272 | background: #e8ad0b; 273 | padding: 4px 6px; 274 | border-radius: 5px; 275 | color: #ffffff; 276 | z-index: 9999; 277 | } 278 | #templates .template-grid .template span.pro-template svg { 279 | height: 14px; 280 | width: 14px; 281 | } 282 | #templates .template-grid .template .template-name { 283 | text-align: center; 284 | } 285 | #templates .template-grid .template .preview-wrap { 286 | overflow: hidden; 287 | height: 250px; 288 | background: rgba(0, 117, 255, 0.02); 289 | border-bottom: 1px solid rgba(16, 38, 64, 0.15); 290 | } 291 | #templates .template-grid .template .preview-wrap .preview { 292 | box-sizing: content-box; 293 | width: 100%; 294 | height: 100%; 295 | position: relative; 296 | zoom: 40%; 297 | } 298 | #templates .template-grid .template .preview-wrap .preview .holler-box .holler-box-overlay, 299 | #templates .template-grid .template .preview-wrap .preview .holler-box .holler-box-overlay::after, 300 | #templates .template-grid .template .preview-wrap .preview .holler-box .holler-box-overlay::before { 301 | position: absolute; 302 | } 303 | #templates .template-grid .template .preview-wrap .preview .holler-box .positioner { 304 | position: absolute; 305 | } 306 | #templates .template-grid .template .preview-wrap .preview .holler-box .holler-box-modal { 307 | transition: 0.3s; 308 | } 309 | #templates .template-grid .template .preview-wrap .preview:not(.show-overlay) .holler-box .holler-box-overlay, 310 | #templates .template-grid .template .preview-wrap .preview:not(.show-overlay) .holler-box .holler-box-overlay::after, 311 | #templates .template-grid .template .preview-wrap .preview:not(.show-overlay) .holler-box .holler-box-overlay::before { 312 | display: none; 313 | } 314 | #templates .template-grid .template:hover { 315 | box-shadow: 5px 5px 30px 0 rgba(24, 45, 70, 0.05); 316 | transition: 0.4s; 317 | } 318 | #templates .template-grid .template:hover .template-name { 319 | font-weight: bold; 320 | color: #0075ff; 321 | } 322 | #templates .template-grid .template:hover .preview-wrap .preview .holler-box-modal { 323 | transform: scale(1.1); 324 | } 325 | 326 | .holler-header:has(.tab-select) { 327 | padding: 0; 328 | min-height: 0; 329 | } 330 | .holler-header:has(.tab-select) .holler-button { 331 | margin-right: 10px; 332 | } 333 | 334 | menu.tab-select { 335 | display: flex; 336 | padding: 0; 337 | margin: 0; 338 | } 339 | menu.tab-select li.tab { 340 | list-style: none; 341 | padding: 20px; 342 | box-sizing: content-box; 343 | font-size: 20px; 344 | font-weight: 500; 345 | cursor: pointer; 346 | margin: 0; 347 | } 348 | menu.tab-select li.tab.active { 349 | color: #0075ff; 350 | border-bottom: #0075ff 3px solid; 351 | } 352 | menu.tab-select li.tab:hover { 353 | background-color: rgba(16, 38, 64, 0.05); 354 | } 355 | 356 | #import-popup { 357 | padding: 100px; 358 | display: flex; 359 | flex-direction: column; 360 | align-items: center; 361 | gap: 10px; 362 | } 363 | 364 | #holler-app { 365 | position: fixed; 366 | top: 32px; 367 | right: 0; 368 | bottom: 0; 369 | left: 160px; 370 | display: flex; 371 | flex-direction: column; 372 | } 373 | #holler-app #header { 374 | background: #ffffff; 375 | display: flex; 376 | gap: 20px; 377 | border-bottom: 1px solid rgba(16, 38, 64, 0.1); 378 | align-items: center; 379 | } 380 | #holler-app #header .holler { 381 | padding: 0 10px; 382 | border-right: 1px solid rgba(16, 38, 64, 0.1); 383 | height: 100%; 384 | display: flex; 385 | align-items: center; 386 | } 387 | #holler-app #header .holler svg { 388 | height: 30px; 389 | width: 30px; 390 | } 391 | #holler-app #header .inside-header { 392 | width: 100%; 393 | display: flex; 394 | justify-content: space-between; 395 | padding: 10px; 396 | align-items: center; 397 | } 398 | #holler-app #header .inside-header h1.holler-title { 399 | margin: 0; 400 | font-size: 20px; 401 | font-weight: 400; 402 | } 403 | #holler-app #header .inside-header h1.holler-title:hover b { 404 | color: #0075ff; 405 | } 406 | #holler-app #editor { 407 | height: 100%; 408 | overflow: auto; 409 | display: flex; 410 | align-items: stretch; 411 | } 412 | #holler-app #editor #frame { 413 | margin: 20px; 414 | width: 100%; 415 | position: relative; 416 | box-sizing: border-box; 417 | overflow: hidden; 418 | border-radius: 5px; 419 | transition: 0.4s; 420 | } 421 | #holler-app #editor #frame.mobile { 422 | width: 480px; 423 | margin-left: auto; 424 | margin-right: auto; 425 | height: 853px; 426 | max-height: calc( 100vh - 32px - 56px - 40px ); 427 | } 428 | #holler-app #editor #frame iframe { 429 | overflow: hidden; 430 | height: 100%; 431 | width: 100%; 432 | } 433 | #holler-app #editor #frame #preview.suppress-animations .animation { 434 | animation: none !important; 435 | } 436 | #holler-app #editor #frame #preview .holler-box .holler-box-overlay, 437 | #holler-app #editor #frame #preview .holler-box .holler-box-overlay::after, 438 | #holler-app #editor #frame #preview .holler-box .holler-box-overlay::before { 439 | position: absolute; 440 | border-radius: 5px; 441 | } 442 | #holler-app #editor #frame #preview .holler-box .positioner { 443 | position: absolute; 444 | } 445 | #holler-app #editor .control-wrap { 446 | flex-shrink: 0; 447 | width: 360px; 448 | background: #ffffff; 449 | border-right: 1px solid rgba(16, 38, 64, 0.1); 450 | overflow: auto; 451 | } 452 | #holler-app #editor .control-wrap #controls { 453 | display: flex; 454 | flex-direction: column; 455 | } 456 | #holler-app #editor .control-wrap #controls .control-button { 457 | border: none; 458 | border-bottom: 1px solid rgba(16, 38, 64, 0.1); 459 | background: #FFFFFF; 460 | display: flex; 461 | justify-content: space-between; 462 | cursor: pointer; 463 | padding: 12px 10px 12px 20px; 464 | font-size: 14px; 465 | font-weight: 500; 466 | } 467 | #holler-app #editor .control-wrap #controls .control-button:hover { 468 | background-color: rgba(16, 38, 64, 0.05); 469 | } 470 | #holler-app #editor .control-wrap #controls .control-group { 471 | border-bottom: 1px solid rgba(16, 38, 64, 0.1); 472 | } 473 | #holler-app #editor .control-wrap #controls .control-group hr { 474 | width: calc( 100% + 40px ); 475 | margin: 10px -20px; 476 | } 477 | #holler-app #editor .control-wrap #controls .control-group:has(.CodeMirror) .controls { 478 | padding: 0; 479 | } 480 | #holler-app #editor .control-wrap #controls .control-group:has(.CodeMirror) .controls > .CodeMirror { 481 | border: solid rgba(16, 38, 64, 0.1); 482 | border-width: 1px 0 1px 0; 483 | } 484 | #holler-app #editor .control-wrap #controls .control-group:has(.CodeMirror) .controls p { 485 | padding: 0 20px; 486 | } 487 | #holler-app #editor .control-wrap #controls .control-group .control-group-header { 488 | display: flex; 489 | justify-content: space-between; 490 | cursor: pointer; 491 | } 492 | #holler-app #editor .control-wrap #controls .control-group .control-group-header:hover { 493 | background-color: rgba(16, 38, 64, 0.05); 494 | } 495 | #holler-app #editor .control-wrap #controls .control-group .control-group-header .control-group-name { 496 | padding: 12px 0 12px 20px; 497 | font-size: 14px; 498 | font-weight: 500; 499 | } 500 | #holler-app #editor .control-wrap #controls .control-group .control-group-header > button.toggle-indicator { 501 | background: transparent; 502 | border: none; 503 | } 504 | #holler-app #editor .control-wrap #controls .control-group .control-group-header > button.toggle-indicator:hover { 505 | background: rgba(16, 38, 64, 0.05); 506 | } 507 | #holler-app #editor .control-wrap #controls .control-group .control-group-header > button.toggle-indicator:focus { 508 | box-shadow: 0 0 0 1px #0075ff, 0 0 2px 1px rgba(0, 117, 255, 0.6); 509 | outline: 1px solid transparent; 510 | } 511 | #holler-app #editor .control-wrap #controls .control-group .control-group-header > button.toggle-indicator::before { 512 | display: inline-block; 513 | font: normal 20px/1 dashicons; 514 | speak: never; 515 | -webkit-font-smoothing: antialiased; 516 | -moz-osx-font-smoothing: grayscale; 517 | text-decoration: none; 518 | } 519 | #holler-app #editor .control-wrap #controls .control-group .control-group-header > button.toggle-indicator::before { 520 | content: ""; 521 | } 522 | #holler-app #editor .control-wrap #controls .control-group.open .controls { 523 | display: flex; 524 | animation: holler-grow 0.5s linear; 525 | overflow: hidden; 526 | } 527 | #holler-app #editor .control-wrap #controls .control-group.open .control-group-header > button.toggle-indicator::before { 528 | content: ""; 529 | } 530 | #holler-app #editor .control-wrap #controls .control-group .controls { 531 | display: none; 532 | padding: 20px; 533 | flex-direction: column; 534 | gap: 10px; 535 | } 536 | #holler-app #editor .control-wrap #controls .control-group .controls label { 537 | font-size: 14px; 538 | } 539 | #holler-app #editor .control-wrap #controls .control-group .controls .control { 540 | display: flex; 541 | justify-content: space-between; 542 | align-items: center; 543 | gap: 10px; 544 | } 545 | #holler-app #editor .control-wrap #controls .control-group .controls .control.hidden { 546 | display: none; 547 | } 548 | #holler-app #editor .control-wrap #controls .control-group .controls .control.stacked { 549 | flex-direction: column; 550 | align-items: stretch; 551 | } 552 | #holler-app #editor .control-wrap #controls .control-group .controls .control input[type=number].input { 553 | max-width: 80px; 554 | padding-right: 0; 555 | } 556 | #holler-app #editor .control-wrap #controls .control-group .controls .control .width-control { 557 | display: flex; 558 | } 559 | #holler-app #editor .control-wrap #controls .control-group .controls .control .width-control input.width-number { 560 | max-width: 60px; 561 | padding-right: 0; 562 | } 563 | #holler-app #editor .control-wrap #controls .control-group .controls #integrations { 564 | display: flex; 565 | flex-direction: column; 566 | gap: 10px; 567 | } 568 | #holler-app #editor .control-wrap #controls .control-group .controls #integrations .integration { 569 | cursor: pointer; 570 | display: flex; 571 | align-items: center; 572 | justify-content: flex-start; 573 | gap: 10px; 574 | border: 1px solid rgba(16, 38, 64, 0.15); 575 | border-radius: 5px; 576 | padding: 5px 5px 5px 15px; 577 | } 578 | #holler-app #editor .control-wrap #controls .control-group .controls #integrations .integration:hover { 579 | box-shadow: 5px 5px 30px 0 rgba(24, 45, 70, 0.05); 580 | } 581 | #holler-app #editor .control-wrap #controls .control-group .controls #integrations .integration > .icon { 582 | display: flex; 583 | align-items: center; 584 | } 585 | #holler-app #editor .control-wrap #controls .control-group .controls #integrations .integration > .icon > svg { 586 | height: 20px; 587 | width: 20px; 588 | } 589 | #holler-app #editor .control-wrap #controls .control-group .controls #integrations .integration .name { 590 | font-size: 16px; 591 | } 592 | #holler-app #editor .control-wrap #controls .control-group .controls #integrations .integration button { 593 | justify-self: flex-end; 594 | margin-left: auto; 595 | } 596 | 597 | /*# sourceMappingURL=popup-builder.css.map */ 598 | -------------------------------------------------------------------------------- /assets/css/popup-builder.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["popup-builder.scss"],"names":[],"mappings":";AA4BA;EACE;EACA;;;AAGF;EACE;EACA;EACA,YAjBc;EAkBd;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAKF;EACE;;;AAKF;EACE;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EAEA;EACA;EACA;;AAGF;EACE;;;AAIJ;EAEE;;AAEA;EACE;EAEA;EACA;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;IACE;;EAGF;IACE;;;AAQJ;EAEE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAIA;EACE;;;AAKN;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE,YA7KO;;AAgLT;EACE;EACA;EACA;;AAGF;EACE;EACA;;;AAKN;EAEE;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EAEE;EACA;EACA;EACA;EACA;EACA;;AAEA;EAME,YAjOO;;AA4NP;EACE;EACA,OAvPO;;AA4PT;EACE;;AAKJ;EACE;;AAGF;EACE;EAEA;EACA;EACA;EACA;EAEA;;AAEA;EACE;EACA;EACA;;;AASN;EACE;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA,YAhTc;EAiTd;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAOJ;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA,YA7VQ;EA8VR;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;;AAGF;EACE;EACA;EACA,YA7XS;EA8XT;;AAEA;EACE;EAEA;EACA;EAEA;EACA;;AAGE;AAAA;AAAA;EAIE;;AAGF;EACE;;AAGF;EACE;;AAMA;AAAA;AAAA;EAIE;;AAOV;EAEE,YAtZK;EAuZL;;AAEA;EACE;EACA,OApbK;;AAwbL;EACE;;;AAQZ;EACE;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE,OAzdS;EA0dT;;AAGF;EACE,kBAldW;;;AAudjB;EACE;EACA;EACA;EACA;EACA;;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGE;EACE,OAvhBG;;AA8hBb;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAME;EACE;;AAKF;AAAA;AAAA;EAIE;EACA;;AAGF;EACE;;AAMR;EACE;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE,kBA/lBK;;AAqmBT;EACE;;AAEA;EACE;EACA;;AAIA;EACE;;AAEA;EACE;EACA;;AAGF;EAEE;;AAKN;EACE;EACA;EACA;;AAEA;EACE,kBAnoBG;;AAsoBL;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE,YAjpBC;;AAopBH;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAKF;EACE;;AAMJ;EACE;EACA;EACA;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAEA;EACE;EACA;;AAMN;EAEE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE,YAnuBL;;AAsuBG;EAEE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;;AAGF;EACE;EACA","file":"popup-builder.css"} -------------------------------------------------------------------------------- /assets/css/popup-builder.scss: -------------------------------------------------------------------------------- 1 | $primary-blue: rgb(0, 117, 255); 2 | $primary-blue-60: rgba(0, 117, 255, 0.60); 3 | $primary-blue-50: rgba(0, 117, 255, 0.50); 4 | $primary-blue-20: rgba(0, 117, 255, 0.20); 5 | $primary-blue-10: rgba(0, 117, 255, 0.10); 6 | $primary-blue-7: rgba(0, 117, 255, 0.07); 7 | $primary-blue-2: rgba(0, 117, 255, 0.02); 8 | $primary-dark: rgb(16, 38, 64); 9 | $primary-dark-50: rgba(16, 38, 64, 0.5); 10 | $primary-dark-30: rgba(16, 38, 64, 0.3); 11 | $primary-dark-15: rgba(16, 38, 64, 0.15); 12 | $primary-dark-10: rgba(16, 38, 64, 0.10); 13 | $primary-dark-5: rgba(16, 38, 64, 0.05); 14 | $error-red: rgb(233, 31, 79); 15 | $error-red-50: rgba(233, 31, 79, 0.5); 16 | $error-red-10: rgba(233, 31, 79, 0.10); 17 | $secondary-purple: rgb(108, 25, 173); 18 | $alert-yellow-50: rgba(255, 238, 88, 0.50); 19 | $alert-yellow: rgb(255, 238, 88); 20 | $holler-yellow: rgb(232, 173, 11); 21 | $benchmark-orange: rgb(245, 129, 21); 22 | $benchmark-orange-50: rgba(245, 129, 21, 0.50); 23 | $benchmark-orange-25: rgba(245, 129, 21, 0.25); 24 | $action-green: rgb(158, 206, 56); 25 | $action-green-50: rgba(158, 206, 56, 0.5); 26 | $box-shadow: 5px 5px 30px 0 rgba(24, 45, 70, 0.05); 27 | $background-color: #F6F9FB; 28 | 29 | input#post-title{ 30 | font-size: 16px; 31 | min-width: 500px; 32 | } 33 | 34 | .holler-pro-ad { 35 | padding: 20px; 36 | font-size: 18px; 37 | background: $holler-yellow; 38 | color: #fff; 39 | text-align: center; 40 | margin: 20px 30px 0 30px; 41 | border-radius: 5px; 42 | line-height: 1.3; 43 | 44 | a { 45 | color: #fff; 46 | } 47 | } 48 | 49 | #triggers { 50 | .holler-pro-ad { 51 | margin: 0; 52 | } 53 | } 54 | 55 | .integration-group { 56 | .holler-pro-ad { 57 | margin: 0; 58 | grid-column: span 3; 59 | } 60 | } 61 | 62 | .promote-groundhogg { 63 | 64 | img { 65 | max-width: 100%; 66 | border-radius: 5px; 67 | } 68 | 69 | h1 { 70 | font-size: 42px; 71 | //text-align: center; 72 | margin-bottom: 40px; 73 | font-weight: 300; 74 | line-height: 1.1; 75 | } 76 | 77 | p, ul, li { 78 | font-size: 18px; 79 | } 80 | } 81 | 82 | .pro-feature { 83 | 84 | max-width: 400px; 85 | 86 | h1 { 87 | font-size: 42px; 88 | //text-align: center; 89 | margin-bottom: 40px; 90 | font-weight: 300; 91 | } 92 | 93 | p, ul, li { 94 | font-size: 18px; 95 | } 96 | } 97 | 98 | #wpcontent { 99 | padding: 0; 100 | } 101 | 102 | #wpfooter { 103 | display: none; 104 | } 105 | 106 | @keyframes holler-grow { 107 | from { 108 | max-height: 0; 109 | } 110 | 111 | to { 112 | max-height: 100vh; 113 | } 114 | } 115 | 116 | #text-content { 117 | 118 | } 119 | 120 | table.display-conditions-grid { 121 | 122 | box-sizing: border-box; 123 | border-collapse: collapse; 124 | width: 100%; 125 | 126 | .picker-here { 127 | margin-top: 10px; 128 | } 129 | 130 | .rules { 131 | display: flex; 132 | flex-direction: column; 133 | gap: 10px; 134 | margin-bottom: 10px; 135 | 136 | .rule { 137 | padding-bottom: 10px; 138 | border-bottom: 1px solid $primary-dark-15; 139 | } 140 | 141 | select { 142 | max-width: 100%; 143 | } 144 | } 145 | 146 | th { 147 | text-align: left; 148 | font-size: 16px; 149 | padding: 30px; 150 | border-right: 1px solid $primary-dark-15; 151 | } 152 | 153 | td { 154 | width: 100%; 155 | box-sizing: border-box; 156 | padding: 30px; 157 | } 158 | 159 | tr:not(:last-child) { 160 | td, th { 161 | border-bottom: 1px solid $primary-dark-15; 162 | } 163 | } 164 | } 165 | 166 | #triggers { 167 | display: flex; 168 | flex-direction: column; 169 | gap: 10px; 170 | 171 | .trigger { 172 | display: flex; 173 | align-items: center; 174 | justify-content: space-between; 175 | border: 1px solid $primary-dark-15; 176 | border-radius: 5px; 177 | padding: 10px 10px 10px 15px; 178 | min-height: 30px; 179 | 180 | input[type=number] { 181 | width: 60px; 182 | } 183 | 184 | .controls { 185 | display: none; 186 | } 187 | 188 | &.enabled { 189 | .controls { 190 | display: block; 191 | } 192 | 193 | .name { 194 | opacity: 1; 195 | } 196 | } 197 | 198 | &:hover { 199 | box-shadow: $box-shadow; 200 | } 201 | 202 | .name { 203 | font-size: 16px; 204 | font-weight: 500; 205 | opacity: 0.6; 206 | } 207 | 208 | button { 209 | justify-self: flex-end; 210 | margin-left: auto; 211 | } 212 | } 213 | } 214 | 215 | #integrations-here { 216 | 217 | padding: 30px; 218 | 219 | h2 { 220 | font-size: 24px; 221 | } 222 | 223 | .integration-group { 224 | margin: 20px 0 40px 0; 225 | box-sizing: content-box; 226 | width: 100%; 227 | display: grid; 228 | grid-template-columns: 1fr 1fr 1fr 1fr 1fr; 229 | gap: 20px; 230 | 231 | &:last-child { 232 | margin-bottom: 0; 233 | } 234 | } 235 | 236 | .integration { 237 | 238 | position: relative; 239 | min-width: calc(100% / 6); 240 | cursor: pointer; 241 | border-radius: 5px; 242 | background: #ffffff; 243 | border: 1px solid $primary-dark-15; 244 | 245 | &:hover { 246 | .integration-name { 247 | font-weight: bold; 248 | color: $primary-blue; 249 | } 250 | 251 | box-shadow: $box-shadow; 252 | 253 | svg { 254 | transform: scale(1.1); 255 | } 256 | 257 | } 258 | 259 | .integration-name { 260 | text-align: center; 261 | } 262 | 263 | .icon { 264 | border-bottom: 1px solid $primary-dark-15; 265 | 266 | display: flex; 267 | align-items: center; 268 | justify-content: center; 269 | padding: 0 20px; 270 | 271 | height: 110px; 272 | 273 | svg { 274 | transition: 0.4s; 275 | width: 60px; 276 | height: 60px; 277 | } 278 | 279 | } 280 | 281 | } 282 | } 283 | 284 | .edit-integration { 285 | #edit-here { 286 | padding: 30px; 287 | } 288 | } 289 | 290 | @keyframes lds-facebook { 291 | 0% { 292 | top: 8px; 293 | height: 64px; 294 | } 295 | 50%, 100% { 296 | top: 24px; 297 | height: 32px; 298 | } 299 | } 300 | 301 | .lds-facebook { 302 | display: block; 303 | margin: auto; 304 | position: relative; 305 | width: 80px; 306 | height: 80px; 307 | 308 | div { 309 | display: inline-block; 310 | position: absolute; 311 | left: 8px; 312 | width: 16px; 313 | background: $primary-dark-50; 314 | animation: lds-facebook 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite; 315 | 316 | &:nth-child(1) { 317 | left: 8px; 318 | animation-delay: -0.24s; 319 | } 320 | 321 | &:nth-child(2) { 322 | left: 32px; 323 | animation-delay: -0.12s; 324 | } 325 | 326 | &:nth-child(3) { 327 | left: 56px; 328 | animation-delay: 0s; 329 | } 330 | } 331 | } 332 | 333 | #templates { 334 | 335 | .template-filters{ 336 | padding-top: 30px; 337 | padding-left: 30px; 338 | } 339 | 340 | .template-grid { 341 | box-sizing: border-box; 342 | width: 100%; 343 | display: grid; 344 | grid-template-columns: 1fr 1fr 1fr; 345 | gap: 30px 30px; 346 | grid-auto-flow: row; 347 | padding: 30px; 348 | 349 | &.library { 350 | grid-template-columns: 1fr 1fr; 351 | 352 | .template .preview-wrap{ 353 | height: 300px; 354 | } 355 | } 356 | 357 | .template { 358 | box-sizing: content-box; 359 | cursor: pointer; 360 | border-radius: 5px; 361 | border: 1px solid $primary-dark-15; 362 | overflow: hidden; 363 | position: relative; 364 | 365 | span.pro-template { 366 | position: absolute; 367 | top: 10px; 368 | right: 10px; 369 | background: $holler-yellow; 370 | padding: 4px 6px; 371 | border-radius: 5px; 372 | color: #ffffff; 373 | z-index: 9999; 374 | 375 | svg { 376 | height: 14px; 377 | width: 14px; 378 | } 379 | } 380 | 381 | .template-name { 382 | text-align: center; 383 | } 384 | 385 | .preview-wrap { 386 | overflow: hidden; 387 | height: 250px; 388 | background: $primary-blue-2; 389 | border-bottom: 1px solid $primary-dark-15; 390 | 391 | .preview { 392 | box-sizing: content-box; 393 | 394 | width: 100%; 395 | height: 100%; 396 | 397 | position: relative; 398 | zoom: 40%; 399 | 400 | .holler-box { 401 | .holler-box-overlay, 402 | .holler-box-overlay::after, 403 | .holler-box-overlay::before, 404 | { 405 | position: absolute; 406 | } 407 | 408 | .positioner { 409 | position: absolute; 410 | } 411 | 412 | .holler-box-modal { 413 | transition: .3s; 414 | } 415 | } 416 | 417 | &:not(.show-overlay) { 418 | .holler-box { 419 | .holler-box-overlay, 420 | .holler-box-overlay::after, 421 | .holler-box-overlay::before, 422 | { 423 | display: none; 424 | } 425 | } 426 | } 427 | } 428 | } 429 | 430 | &:hover { 431 | 432 | box-shadow: $box-shadow; 433 | transition: 0.4s; 434 | 435 | .template-name { 436 | font-weight: bold; 437 | color: $primary-blue; 438 | } 439 | 440 | .preview-wrap { 441 | .preview .holler-box-modal { 442 | transform: scale(1.1); 443 | } 444 | } 445 | } 446 | } 447 | } 448 | } 449 | 450 | .holler-header:has(.tab-select){ 451 | padding: 0; 452 | min-height: 0; 453 | 454 | .holler-button { 455 | margin-right: 10px; 456 | } 457 | } 458 | 459 | menu.tab-select{ 460 | display: flex; 461 | padding: 0; 462 | margin: 0; 463 | 464 | li.tab { 465 | list-style: none; 466 | padding: 20px; 467 | box-sizing: content-box; 468 | font-size: 20px; 469 | font-weight: 500; 470 | cursor: pointer; 471 | margin: 0; 472 | 473 | &.active { 474 | color: $primary-blue; 475 | border-bottom: $primary-blue 3px solid; 476 | } 477 | 478 | &:hover{ 479 | background-color: $primary-dark-5; 480 | } 481 | } 482 | } 483 | 484 | #import-popup{ 485 | padding: 100px; 486 | display: flex; 487 | flex-direction: column; 488 | align-items: center; 489 | gap: 10px; 490 | } 491 | 492 | #holler-app { 493 | 494 | position: fixed; 495 | top: 32px; 496 | right: 0; 497 | bottom: 0; 498 | left: 160px; 499 | display: flex; 500 | flex-direction: column; 501 | 502 | #header { 503 | background: #ffffff; 504 | display: flex; 505 | gap: 20px; 506 | border-bottom: 1px solid $primary-dark-10; 507 | align-items: center; 508 | 509 | .holler { 510 | padding: 0 10px; 511 | border-right: 1px solid $primary-dark-10; 512 | height: 100%; 513 | display: flex; 514 | align-items: center; 515 | 516 | svg { 517 | height: 30px; 518 | width: 30px; 519 | } 520 | } 521 | 522 | .inside-header { 523 | width: 100%; 524 | display: flex; 525 | justify-content: space-between; 526 | padding: 10px; 527 | align-items: center; 528 | 529 | h1.holler-title { 530 | margin: 0; 531 | font-size: 20px; 532 | font-weight: 400; 533 | 534 | &:hover { 535 | b { 536 | color: $primary-blue; 537 | } 538 | } 539 | } 540 | } 541 | } 542 | 543 | #editor { 544 | height: 100%; 545 | overflow: auto; 546 | display: flex; 547 | align-items: stretch; 548 | 549 | #frame { 550 | margin: 20px; 551 | width: 100%; 552 | position: relative; 553 | box-sizing: border-box; 554 | overflow: hidden; 555 | border-radius: 5px; 556 | transition: 0.4s; 557 | 558 | &.mobile { 559 | width: 480px; 560 | margin-left: auto; 561 | margin-right: auto; 562 | height: 853px; 563 | max-height: calc( 100vh - 32px - 56px - 40px ); 564 | } 565 | 566 | iframe { 567 | overflow: hidden; 568 | height: 100%; 569 | width: 100%; 570 | } 571 | 572 | #preview { 573 | 574 | &.suppress-animations { 575 | .animation { 576 | animation: none !important; 577 | } 578 | } 579 | 580 | .holler-box { 581 | .holler-box-overlay, 582 | .holler-box-overlay::after, 583 | .holler-box-overlay::before 584 | { 585 | position: absolute; 586 | border-radius: 5px; 587 | } 588 | 589 | .positioner { 590 | position: absolute; 591 | } 592 | } 593 | } 594 | } 595 | 596 | .control-wrap { 597 | flex-shrink: 0; 598 | width: 360px; 599 | background: #ffffff; 600 | border-right: 1px solid $primary-dark-10; 601 | overflow: auto; 602 | 603 | #controls { 604 | 605 | display: flex; 606 | flex-direction: column; 607 | 608 | .control-button { 609 | border: none; 610 | border-bottom: 1px solid $primary-dark-10; 611 | background: #FFFFFF; 612 | display: flex; 613 | justify-content: space-between; 614 | cursor: pointer; 615 | padding: 12px 10px 12px 20px; 616 | font-size: 14px; 617 | font-weight: 500; 618 | 619 | &:hover { 620 | background-color: $primary-dark-5; 621 | } 622 | 623 | 624 | } 625 | 626 | .control-group { 627 | border-bottom: 1px solid $primary-dark-10; 628 | 629 | hr { 630 | width: calc( 100% + 40px ); 631 | margin: 10px -20px; 632 | } 633 | 634 | &:has(.CodeMirror) { 635 | .controls { 636 | padding: 0; 637 | 638 | > .CodeMirror { 639 | border: solid $primary-dark-10; 640 | border-width: 1px 0 1px 0; 641 | } 642 | 643 | p { 644 | //text-align: center; 645 | padding: 0 20px; 646 | } 647 | } 648 | } 649 | 650 | .control-group-header { 651 | display: flex; 652 | justify-content: space-between; 653 | cursor: pointer; 654 | 655 | &:hover { 656 | background-color: $primary-dark-5; 657 | } 658 | 659 | .control-group-name { 660 | padding: 12px 0 12px 20px; 661 | font-size: 14px; 662 | font-weight: 500; 663 | } 664 | 665 | > button.toggle-indicator { 666 | background: transparent; 667 | border: none; 668 | 669 | &:hover { 670 | background: $primary-dark-5; 671 | } 672 | 673 | &:focus { 674 | box-shadow: 0 0 0 1px $primary-blue, 0 0 2px 1px $primary-blue-60; 675 | outline: 1px solid transparent; 676 | } 677 | 678 | &::before { 679 | display: inline-block; 680 | font: normal 20px/1 dashicons; 681 | speak: never; 682 | -webkit-font-smoothing: antialiased; 683 | -moz-osx-font-smoothing: grayscale; 684 | text-decoration: none; 685 | } 686 | } 687 | 688 | > button { 689 | &.toggle-indicator::before { 690 | content: "\f140"; 691 | } 692 | } 693 | } 694 | 695 | &.open { 696 | .controls { 697 | display: flex; 698 | animation: holler-grow 0.5s linear; 699 | overflow: hidden; 700 | } 701 | 702 | .control-group-header > button.toggle-indicator::before { 703 | content: "\f142"; 704 | } 705 | } 706 | 707 | .controls { 708 | display: none; 709 | padding: 20px; 710 | flex-direction: column; 711 | gap: 10px; 712 | 713 | label { 714 | font-size: 14px; 715 | } 716 | 717 | .control { 718 | display: flex; 719 | justify-content: space-between; 720 | align-items: center; 721 | gap: 10px; 722 | 723 | &.hidden{ 724 | display: none; 725 | } 726 | 727 | &.stacked{ 728 | flex-direction: column; 729 | align-items: stretch; 730 | } 731 | 732 | input[type=number].input{ 733 | max-width: 80px; 734 | padding-right: 0; 735 | } 736 | 737 | .width-control{ 738 | display: flex; 739 | 740 | input.width-number { 741 | max-width: 60px; 742 | padding-right: 0; 743 | } 744 | } 745 | } 746 | 747 | 748 | #integrations { 749 | 750 | display: flex; 751 | flex-direction: column; 752 | gap: 10px; 753 | 754 | .integration { 755 | cursor: pointer; 756 | display: flex; 757 | align-items: center; 758 | justify-content: flex-start; 759 | gap: 10px; 760 | border: 1px solid $primary-dark-15; 761 | border-radius: 5px; 762 | padding: 5px 5px 5px 15px; 763 | 764 | &:hover { 765 | box-shadow: $box-shadow; 766 | } 767 | 768 | > .icon { 769 | 770 | display: flex; 771 | align-items: center; 772 | 773 | > svg { 774 | height: 20px; 775 | width: 20px; 776 | } 777 | } 778 | 779 | .name { 780 | font-size: 16px; 781 | } 782 | 783 | button { 784 | justify-self: flex-end; 785 | margin-left: auto; 786 | } 787 | } 788 | } 789 | } 790 | } 791 | } 792 | } 793 | } 794 | } 795 | -------------------------------------------------------------------------------- /assets/css/popups.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["popups.scss"],"names":[],"mappings":"AA6BA;EACE;;;AAIA;EACE;;AAIA;AAAA;EAEE;;AAIJ;EACE;EACA;EACA;EACA;;AAGF;EACE;;;AAMF;EACE;;AAMI;EACE;;AAGF;EACE;;AAGF;EACE;;AAKN;AAAA;EAEE;;;AAKN;EACE;IACE;;EAEF;IACE;;;AAIJ;EACE;IACE;;EAEF;IACE;;;AAIJ;EACE;IACE;;;AAIJ;EACE;IACE;IACA;;EAGF;IACE;IACA;;EAGF;IACE;;;AAIJ;EACE;IACE;IACA;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;;;AAKJ;EACE;IACE;;EAEF;IACE;IACA;;;AAKJ;EACE;IACE;;EAEF;IACE;IACA;;;AAIJ;EACE;IACE;;EAEF;IACE;IACA;;;AAKJ;EACE;IACE;;EAEF;IACE;IACA;;;AAIJ;EACE;;AAEA;EACE;EACA,OAjNQ;EAkNR;EACA;;AAEA;EACE;;AAGF;EACE;;AAKF;AAAA;AAAA;AAAA;EAIE;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EAEE;;AAEA;EAEE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAQJ;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAON;EAEE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAMR;EAQE;EACA;;AAPA;EACE,OAxYU;EAyYV;EACA;;AAOJ;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EAaA;;AAXA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAKF;EACE;;AAIJ;EACE;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA,YA7bS;;AA+bT;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YAheS;EAieT,YAzcO;EA0cP;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAUJ;EAEE;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OA9hBS;;AAgiBT;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AASN;EAQE;EACA;;AAPA;EACE,OAhkBQ;EAikBR;EACA;;AAOJ;AAME;AAKA;AAMA;;AAfA;EACE;;AAIF;EACE,YAhmBS;;AAomBX;EACE,YAxmBU;EAymBV;;AAIF;EACE,YA/mBU;;AAknBZ;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA,OA/nBQ;EAgoBR;EACA;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA,YArpBK;EAspBL;EACA;EAGA;EACA;EACA;;AAGA;EACE,YAhqBG;EAiqBH;EACA;EACA;EACA;;AAKN;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;;AAEA;EACE;EACA,YAvrBK;EAwrBL;EACA;EACA;EACA;;AAKN;EACE;EACA;EACA;EACA;;AAEA;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAUA;;AARA;AAAA;EACE;;AAGF;AAAA;EACE;;AAMJ;EAEE;EACA;EACA,YAvuBK;EAwuBL;EACA;EACA;;AAEA;EAEE,YA7uBM;;AAgvBR;EACE;EACA;;AAQN;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAOJ;EACE;;AAEA;EACE;;AAGF;EACE;EACA,YAtwBO;EAuwBP;EACA;EACA;;AAGF;EACE;EACA;;AAMN;EACE;EACA;EACA,YA3wBO;EA4wBP;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA,kBA/yBS;EAgzBT;;AAEA;EACE;EAEA;EACA;;AAEA;EACE;;AAGF;EACE;;AAQR;EACE;;AAKF;EACE;;AAMF;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAOJ;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAOJ;EACE;;AAEA;EACE;;AAIJ;EACE;EAEA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA,YAv6BW;;AAg7BX;EAEE;EACA;EACA;;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;AAEA;EACE;EACA;;AAMR;EAME;EACA;EACA;;AANA;EACE;;AAOF;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAQJ;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA,kBA5gCS;;AAohCb;EACE;EACA;;AAEA;EACE;;AAUN;AAAA;AAAA;AAAA;AAAA;EAME;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;EACE;;AAGF;AAAA;AAAA;AAAA;AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;AAAA;AAAA;AAAA;AAAA;EACE;EACA;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;;AAOJ;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAKA;EACE;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EACE;;AAKF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAUA;;AARA;EACE;;AAGF;EACE;;;AAUR;EAGI;IACE;IACA;;;EAOA;IACE;IACA;;EAGF;IAGE;IACA;;EAEA;IACE;;EAIJ;IAME;IACA;IACA;;EAEA;IACE;;EAKJ;IAME;;EAEA;IACE;;EAIJ;IAGE;;EAEA;IACE;IACA;;EAIJ;IACE;;EAEA;IACE;IACA;;;EASN;IAME;IACA;;EALA;IACE;;EAMF;IACE;;EAEA;IACE;IACA;;EAGF;IACE;IACA;;EAMF;IACE;IACA;IACA;;EAGF;IACE;;EAOJ;IACE","file":"popups.css"} -------------------------------------------------------------------------------- /assets/img/default/cafe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/cafe.png -------------------------------------------------------------------------------- /assets/img/default/city-day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/city-day.png -------------------------------------------------------------------------------- /assets/img/default/city-night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/city-night.png -------------------------------------------------------------------------------- /assets/img/default/clothes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/clothes.png -------------------------------------------------------------------------------- /assets/img/default/man-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/man-blue.png -------------------------------------------------------------------------------- /assets/img/default/man-thumbs-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/man-thumbs-up.png -------------------------------------------------------------------------------- /assets/img/default/man-working.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/man-working.png -------------------------------------------------------------------------------- /assets/img/default/shopping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/shopping.png -------------------------------------------------------------------------------- /assets/img/default/woman-shopping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/woman-shopping.png -------------------------------------------------------------------------------- /assets/img/default/woman-working.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/woman-working.png -------------------------------------------------------------------------------- /assets/img/default/woman-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/woman-yellow.png -------------------------------------------------------------------------------- /assets/img/default/yellow-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/default/yellow-gradient.png -------------------------------------------------------------------------------- /assets/img/groundhogg-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/groundhogg-banner.png -------------------------------------------------------------------------------- /assets/img/template-library-coming-soon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/assets/img/template-library-coming-soon.png -------------------------------------------------------------------------------- /assets/js/baremetrics-calendar.min.js: -------------------------------------------------------------------------------- 1 | "use strict";(function(root,factory){if(typeof define==="function"&&define.amd){define(["jquery","moment"],factory)}else if(typeof exports==="object"){module.exports=factory(require("jquery"),require("moment"))}else{root.Calendar=factory(jQuery,moment)}})(this,function($,moment){function Calendar(settings){var self=this;this.settings=settings;this.calIsOpen=false;this.presetIsOpen=false;this.sameDayRange=settings.same_day_range||false;this.element=settings.element||$(".daterange");this.selected=null;this.type=this.element.hasClass("daterange--single")?"single":"double";this.required=settings.required==false?false:true;this.format=settings.format||{};this.format.input=settings.format&&settings.format.input||"MMMM D, YYYY";this.format.preset=settings.format&&settings.format.preset||"ll";this.format.jump_month=settings.format&&settings.format.jump_month||"MMMM";this.format.jump_year=settings.format&&settings.format.jump_year||"YYYY";this.placeholder=settings.placeholder||this.format.input;this.days_array=settings.days_array&&settings.days_array.length==7?settings.days_array:moment.weekdaysMin();this.orig_start_date=null;this.orig_end_date=null;this.orig_current_date=null;this.earliest_date=settings.earliest_date?moment(settings.earliest_date):moment("1900-01-01","YYYY-MM-DD");this.latest_date=settings.latest_date?moment(settings.latest_date):moment("2900-12-31","YYYY-MM-DD");this.end_date=settings.end_date?moment(settings.end_date):this.type=="double"?moment():null;this.start_date=settings.start_date?moment(settings.start_date):this.type=="double"?this.end_date.clone().subtract(1,"month"):null;this.current_date=settings.current_date?moment(settings.current_date):this.type=="single"?moment():null;this.presets=settings.presets==false||this.type=="single"?false:true;this.callback=settings.callback||this.calendarSetDates;this.calendarHTML(this.type);$(".dr-presets",this.element).click(function(){self.presetToggle()});$(".dr-list-item",this.element).click(function(){var start=$(".dr-item-aside",this).data("start");var end=$(".dr-item-aside",this).data("end");self.start_date=self.calendarCheckDate(start);self.end_date=self.calendarCheckDate(end);self.calendarSetDates();self.presetToggle();self.calendarSaveDates()});$(".dr-date",this.element).on({click:function(){self.calendarOpen(this)},keyup:function(event){if(event.keyCode==9&&!self.calIsOpen&&!self.start_date&&!self.end_date)self.calendarOpen(this)},keydown:function(event){switch(event.keyCode){case 9:if($(self.selected).hasClass("dr-date-start")){event.preventDefault();self.calendarCheckDates();self.calendarSetDates();$(".dr-date-end",self.element).trigger("click")}else{self.calendarCheckDates();self.calendarSetDates();self.calendarSaveDates();self.calendarClose("force")}break;case 13:event.preventDefault();self.calendarCheckDates();self.calendarSetDates();self.calendarSaveDates();self.calendarClose("force");break;case 27:self.calendarSetDates();self.calendarClose("force");break;case 38:event.preventDefault();var timeframe="day";if(event.shiftKey)timeframe="week";if(event.metaKey)timeframe="month";var back=moment(self.current_date).subtract(1,timeframe);$(this).html(back.format(self.format.input));self.current_date=back;break;case 40:event.preventDefault();var timeframe="day";if(event.shiftKey)timeframe="week";if(event.metaKey)timeframe="month";var forward=moment(self.current_date).add(1,timeframe);$(this).html(forward.format(self.format.input));self.current_date=forward;break}}});$(".dr-month-switcher i",this.element).click(function(){var m=$(".dr-month-switcher span",self.element).data("month");var y=$(".dr-year-switcher span",self.element).data("year");var this_moment=moment([y,m,1]);var back=this_moment.clone().subtract(1,"month");var forward=this_moment.clone().add(1,"month").startOf("day");if($(this).hasClass("dr-left")){self.calendarOpen(self.selected,back)}else if($(this).hasClass("dr-right")){self.calendarOpen(self.selected,forward)}});$(".dr-year-switcher i",this.element).click(function(){var m=$(".dr-month-switcher span",self.element).data("month");var y=$(".dr-year-switcher span",self.element).data("year");var this_moment=moment([y,m,1]);var back=this_moment.clone().subtract(1,"year");var forward=this_moment.clone().add(1,"year").startOf("day");if($(this).hasClass("dr-left")){self.calendarOpen(self.selected,back)}else if($(this).hasClass("dr-right")){self.calendarOpen(self.selected,forward)}});$(".dr-dates-dash",this.element).click(function(){$(".dr-date-start",self.element).trigger("click")});var clickHandler=function(event){var contains=$(event.target).parent().closest(self.element);if(!contains.length){document.removeEventListener("click",clickHandler,false);if(self.presetIsOpen)self.presetToggle();if(self.calIsOpen){if($(self.selected).hasClass("dr-date-end"))self.calendarSaveDates();self.calendarSetDates();self.calendarClose("force")}}};this.element.on("click",function(){document.addEventListener("click",clickHandler,false)})}Calendar.prototype.presetToggle=function(){if(this.presetIsOpen==false){this.orig_start_date=this.start_date;this.orig_end_date=this.end_date;this.orig_current_date=this.current_date;this.presetIsOpen=true}else if(this.presetIsOpen){this.presetIsOpen=false}if(this.calIsOpen==true)this.calendarClose();$(".dr-preset-list",this.element).slideToggle(200);$(".dr-input",this.element).toggleClass("dr-active");$(".dr-presets",this.element).toggleClass("dr-active");this.element.toggleClass("dr-active")};Calendar.prototype.presetCreate=function(){var self=this;var ul_presets=$('');var presets=typeof self.settings.presets==="object"?self.settings.presets:[{label:"Last 30 days",start:moment(self.latest_date).subtract(29,"days"),end:self.latest_date},{label:"Last month",start:moment(self.latest_date).subtract(1,"month").startOf("month"),end:moment(self.latest_date).subtract(1,"month").endOf("month")},{label:"Last 3 months",start:moment(self.latest_date).subtract(3,"month").startOf("month"),end:moment(self.latest_date).subtract(1,"month").endOf("month")},{label:"Last 6 months",start:moment(self.latest_date).subtract(6,"month").startOf("month"),end:moment(self.latest_date).subtract(1,"month").endOf("month")},{label:"Last year",start:moment(self.latest_date).subtract(1,"year").startOf("year"),end:moment(self.latest_date).subtract(1,"year").endOf("year")},{label:"All time",start:self.earliest_date,end:self.latest_date}];if(moment(self.latest_date).diff(moment(self.latest_date).startOf("month"),"days")>=6&&typeof self.settings.presets!=="object"){presets.splice(1,0,{label:"This month",start:moment(self.latest_date).startOf("month"),end:self.latest_date})}$.each(presets,function(i,d){if(moment(d.start).isBefore(self.earliest_date)){d.start=self.earliest_date}if(moment(d.start).isAfter(self.latest_date)){d.start=self.latest_date}if(moment(d.end).isBefore(self.earliest_date)){d.end=self.earliest_date}if(moment(d.end).isAfter(self.latest_date)){d.end=self.latest_date}var startISO=moment(d.start).toISOString();var endISO=moment(d.end).toISOString();var string=moment(d.start).format(self.format.preset)+" – "+moment(d.end).format(self.format.preset);if($(".dr-preset-list",self.element).length){var item=$(".dr-preset-list .dr-list-item:nth-of-type("+(i+1)+") .dr-item-aside",self.element);item.data("start",startISO);item.data("end",endISO);item.html(string)}else{ul_presets.append('
  • '+d.label+''+string+""+"
  • ")}});return ul_presets};Calendar.prototype.calendarSetDates=function(){$(".dr-date-start",this.element).html(moment(this.start_date).format(this.format.input));$(".dr-date-end",this.element).html(moment(this.end_date).format(this.format.input));if(!this.start_date&&!this.end_date){var old_date=$(".dr-date",this.element).html();var new_date=moment(this.current_date).format(this.format.input);if(old_date.length===0&&!this.required)new_date="";if(old_date!=new_date)$(".dr-date",this.element).html(new_date)}};Calendar.prototype.calendarSaveDates=function(){if(this.type==="double"){if(!moment(this.orig_end_date).isSame(this.end_date)||!moment(this.orig_start_date).isSame(this.start_date))return this.callback()}else{if(!this.required||!moment(this.orig_current_date).isSame(this.current_date))return this.callback()}};Calendar.prototype.calendarCheckDate=function(d){if(d==="today"||d==="now")return moment().isAfter(this.latest_date)?this.latest_date:moment().isBefore(this.earliest_date)?this.earliest_date:moment();if(d==="earliest")return this.earliest_date;if(d==="latest")return this.latest_date;if(d&&(/\bago\b/.test(d)||/\bahead\b/.test(d)))return this.stringToDate(d);var regex=/(?:\d)((?:st|nd|rd|th)?,?)/;var d_array=d?d.replace(regex,"").split(" "):[];if(d_array.length==2){d_array.push(moment().format(this.format.jump_year));d=d_array.join(" ")}var parsed_d=this.parseDate(d);if(!parsed_d.isValid())return moment(d);return parsed_d};Calendar.prototype.calendarCheckDates=function(){var startTxt=$(".dr-date-start",this.element).html();var endTxt=$(".dr-date-end",this.element).html();var c=this.calendarCheckDate($(this.selected).html());var s;var e;if(startTxt==="ytd"||endTxt==="ytd"){s=moment().startOf("year");e=moment().endOf("year")}else{s=this.calendarCheckDate(startTxt);e=this.calendarCheckDate(endTxt)}if(c.isBefore(this.earliest_date))c=this.earliest_date;if(s.isBefore(this.earliest_date))s=this.earliest_date;if(e.isBefore(this.earliest_date)||e.isBefore(s))e=s.clone().add(6,"day");if(c.isAfter(this.latest_date))c=this.latest_date;if(e.isAfter(this.latest_date))e=this.latest_date;if(s.isAfter(this.latest_date)||s.isAfter(e))s=e.clone().subtract(6,"day");if(this.type==="double"){if(s.isSame(e)&&!this.sameDayRange)return this.calendarSetDates();this.start_date=s.isValid()?s:this.start_date;this.end_date=e.isValid()?e:this.end_date}this.current_date=c.isValid()?c:this.current_date};Calendar.prototype.stringToDate=function(str){var date_arr=str.split(" ");if(date_arr[2]==="ago"){return moment(this.current_date).subtract(date_arr[0],date_arr[1])}else if(date_arr[2]==="ahead"){return moment(this.current_date).add(date_arr[0],date_arr[1])}return this.current_date};Calendar.prototype.calendarOpen=function(selected,switcher){var self=this;var other;var cal_width=$(".dr-dates",this.element).innerWidth()-8;this.selected=selected||this.selected;if(this.presetIsOpen==true)this.presetToggle();if(this.calIsOpen==true){this.calendarClose(switcher?"switcher":undefined)}else if($(this.selected).html().length){this.orig_start_date=this.start_date;this.orig_end_date=this.end_date;this.orig_current_date=this.current_date}this.calendarCheckDates();this.calendarCreate(switcher);this.calendarSetDates();var next_month=moment(switcher||this.current_date).add(1,"month").startOf("month").startOf("day");var past_month=moment(switcher||this.current_date).subtract(1,"month").endOf("month");var next_year=moment(switcher||this.current_date).add(1,"year").startOf("month").startOf("day");var past_year=moment(switcher||this.current_date).subtract(1,"year").endOf("month");var this_moment=moment(switcher||this.current_date);$(".dr-month-switcher span",this.element).data("month",this_moment.month()).html(this_moment.format(this.format.jump_month));$(".dr-year-switcher span",this.element).data("year",this_moment.year()).html(this_moment.format(this.format.jump_year));$(".dr-switcher i",this.element).removeClass("dr-disabled");if(next_month.isAfter(this.latest_date))$(".dr-month-switcher .dr-right",this.element).addClass("dr-disabled");if(past_month.isBefore(this.earliest_date))$(".dr-month-switcher .dr-left",this.element).addClass("dr-disabled");if(next_year.isAfter(this.latest_date))$(".dr-year-switcher .dr-right",this.element).addClass("dr-disabled");if(past_year.isBefore(this.earliest_date))$(".dr-year-switcher .dr-left",this.element).addClass("dr-disabled");$(".dr-day",this.element).on({mouseenter:function(){var selected=$(this);var start_date=moment(self.start_date);var end_date=moment(self.end_date);var current_date=moment(self.current_date);if($(self.selected).hasClass("dr-date-start")){selected.addClass("dr-hover dr-hover-before");$(".dr-start",self.element).css({border:"none","padding-left":"0.3125rem"});setMaybeRange("start")}if($(self.selected).hasClass("dr-date-end")){selected.addClass("dr-hover dr-hover-after");$(".dr-end",self.element).css({border:"none","padding-right":"0.3125rem"});setMaybeRange("end")}if(!self.start_date&&!self.end_date)selected.addClass("dr-maybe");$(".dr-selected",self.element).css("background-color","transparent");function setMaybeRange(type){other=undefined;self.range(6*7).forEach(function(i){var next=selected.next().data("date");var prev=selected.prev().data("date");var curr=selected.data("date");if(!curr)return false;if(!prev)prev=curr;if(!next)next=curr;if(type=="start"){if(moment(next).isSame(self.end_date)||self.sameDayRange&&moment(curr).isSame(self.end_date))return false;if(moment(curr).isAfter(self.end_date)){other=other||moment(curr).add(6,"day").startOf("day");if(i>5||(next?moment(next).isAfter(self.latest_date):false)){$(selected).addClass("dr-end");other=moment(curr);return false}}selected=selected.next().addClass("dr-maybe")}else if(type=="end"){if(moment(prev).isSame(self.start_date)||self.sameDayRange&&moment(curr).isSame(self.start_date))return false;if(moment(curr).isBefore(self.start_date)){other=other||moment(curr).subtract(6,"day");if(i>5||(prev?moment(prev).isBefore(self.earliest_date):false)){$(selected).addClass("dr-start");other=moment(curr);return false}}selected=selected.prev().addClass("dr-maybe")}})}},mouseleave:function(){if($(this).hasClass("dr-hover-before dr-end"))$(this).removeClass("dr-end");if($(this).hasClass("dr-hover-after dr-start"))$(this).removeClass("dr-start");$(this).removeClass("dr-hover dr-hover-before dr-hover-after");$(".dr-start, .dr-end",self.element).css({border:"",padding:""});$(".dr-maybe:not(.dr-current)",self.element).removeClass("dr-start dr-end");$(".dr-day",self.element).removeClass("dr-maybe");$(".dr-selected",self.element).css("background-color","")}});if(/iPad|iPhone|iPod/.test(navigator.userAgent)&&!window.MSStream){$(".dr-day",this.element).on({touchstart:function(){self.selectOneDate(other,self,$(this).data("date"))}});$("div[contenteditable]",this.element).removeAttr("contenteditable")}else{$(".dr-day",this.element).on({mousedown:function(){self.selectOneDate(other,self,$(this).data("date"))}})}$(".dr-calendar",this.element).css("width",cal_width).slideDown(200);$(".dr-input",this.element).addClass("dr-active");$(selected).addClass("dr-active").focus();this.element.addClass("dr-active");this.calIsOpen=true};Calendar.prototype.calendarClose=function(type){var self=this;if(!this.calIsOpen||this.presetIsOpen||type=="force"){$(".dr-calendar",this.element).slideUp(200,function(){$(".dr-day",self.element).remove()})}else{$(".dr-day",this.element).remove()}if(type=="switcher"){return false}$(".dr-input, .dr-date",this.element).removeClass("dr-active");this.element.removeClass("dr-active");this.calIsOpen=false};Calendar.prototype.calendarCreate=function(switcher){var self=this;var array=this.calendarArray(this.start_date,this.end_date,this.current_date,switcher);array.forEach(function(d,i){var classString="dr-day";if(d.fade)classString+=" dr-fade";if(d.start)classString+=" dr-start";if(d.end)classString+=" dr-end";if(d.current)classString+=" dr-current";if(d.selected)classString+=" dr-selected";if(d.outside)classString+=" dr-outside";$(".dr-day-list",self.element).append('
  • '+d.str+"
  • ")})};Calendar.prototype.calendarArray=function(start,end,current,switcher){var self=this;current=moment(current||start||end).startOf("day");var reference=switcher||current||start||end;var startRange=moment(reference).startOf("month").startOf("week");var endRange=moment(startRange).add(6*7-1,"days").endOf("day");var daysInRange=[];var d=moment(startRange);while(d.isBefore(endRange)){daysInRange.push({str:+d.format("D"),start:start&&d.isSame(start,"day"),end:end&&d.isSame(end,"day"),current:current&&d.isSame(current,"day"),selected:start&&end&&d.isBetween(start,end),date:d.toISOString(),outside:d.isBefore(self.earliest_date)||d.isAfter(self.latest_date),fade:!d.isSame(reference,"month")});d.add(1,"d")}return daysInRange};Calendar.prototype.calendarHTML=function(type){var ul_days_of_the_week=$('');var days=this.days_array.splice(moment.localeData().firstDayOfWeek()).concat(this.days_array.splice(0,moment.localeData().firstDayOfWeek()));$.each(days,function(i,elem){ul_days_of_the_week.append('
  • '+elem+"
  • ")});if(type=="double")return this.element.append('
    '+'
    '+'
    '+moment(this.start_date).format(this.format.input)+"
    "+''+'
    '+moment(this.end_date).format(this.format.input)+"
    "+"
    "+(this.presets?'
    '+''+''+''+"
    ":"")+"
    "+'
    '+'"+(this.presets?this.presetCreate()[0].outerHTML:"")+"
    ");return this.element.append('
    '+'
    '+'
    '+(this.settings.current_date?moment(this.current_date).format(this.format.input):"")+"
    "+"
    "+"
    "+'
    '+'"+"
    ")};Calendar.prototype.parseDate=function(d){if(moment.defaultZone!==null&&moment.hasOwnProperty("tz")){return moment.tz(d,this.format.input,moment.defaultZone.name)}else{return moment(d,this.format.input)}};Calendar.prototype.range=function(length){var range=new Array(length);for(var idx=0;idx=0;i--){attr=toNodeAttrs[i];attrName=attr.name;attrNamespaceURI=attr.namespaceURI;attrValue=attr.value;if(attrNamespaceURI){attrName=attr.localName||attrName;fromValue=fromNode.getAttributeNS(attrNamespaceURI,attrName);if(fromValue!==attrValue){if(attr.prefix==="xmlns"){attrName=attr.name}fromNode.setAttributeNS(attrNamespaceURI,attrName,attrValue)}}else{fromValue=fromNode.getAttribute(attrName);if(fromValue!==attrValue){fromNode.setAttribute(attrName,attrValue)}}}var fromNodeAttrs=fromNode.attributes;for(var d=fromNodeAttrs.length-1;d>=0;d--){attr=fromNodeAttrs[d];attrName=attr.name;attrNamespaceURI=attr.namespaceURI;if(attrNamespaceURI){attrName=attr.localName||attrName;if(!toNode.hasAttributeNS(attrNamespaceURI,attrName)){fromNode.removeAttributeNS(attrNamespaceURI,attrName)}}else{if(!toNode.hasAttribute(attrName)){fromNode.removeAttribute(attrName)}}}}var range;var NS_XHTML="http://www.w3.org/1999/xhtml";var doc=typeof document==="undefined"?undefined:document;var HAS_TEMPLATE_SUPPORT=!!doc&&"content"in doc.createElement("template");var HAS_RANGE_SUPPORT=!!doc&&doc.createRange&&"createContextualFragment"in doc.createRange();function createFragmentFromTemplate(str){var template=doc.createElement("template");template.innerHTML=str;return template.content.childNodes[0]}function createFragmentFromRange(str){if(!range){range=doc.createRange();range.selectNode(doc.body)}var fragment=range.createContextualFragment(str);return fragment.childNodes[0]}function createFragmentFromWrap(str){var fragment=doc.createElement("body");fragment.innerHTML=str;return fragment.childNodes[0]}function toElement(str){str=str.trim();if(HAS_TEMPLATE_SUPPORT){return createFragmentFromTemplate(str)}else if(HAS_RANGE_SUPPORT){return createFragmentFromRange(str)}return createFragmentFromWrap(str)}function compareNodeNames(fromEl,toEl){var fromNodeName=fromEl.nodeName;var toNodeName=toEl.nodeName;var fromCodeStart,toCodeStart;if(fromNodeName===toNodeName){return true}fromCodeStart=fromNodeName.charCodeAt(0);toCodeStart=toNodeName.charCodeAt(0);if(fromCodeStart<=90&&toCodeStart>=97){return fromNodeName===toNodeName.toUpperCase()}else if(toCodeStart<=90&&fromCodeStart>=97){return toNodeName===fromNodeName.toUpperCase()}else{return false}}function createElementNS(name,namespaceURI){return!namespaceURI||namespaceURI===NS_XHTML?doc.createElement(name):doc.createElementNS(namespaceURI,name)}function moveChildren(fromEl,toEl){var curChild=fromEl.firstChild;while(curChild){var nextChild=curChild.nextSibling;toEl.appendChild(curChild);curChild=nextChild}return toEl}function syncBooleanAttrProp(fromEl,toEl,name){if(fromEl[name]!==toEl[name]){fromEl[name]=toEl[name];if(fromEl[name]){fromEl.setAttribute(name,"")}else{fromEl.removeAttribute(name)}}}var specialElHandlers={OPTION:function(fromEl,toEl){var parentNode=fromEl.parentNode;if(parentNode){var parentName=parentNode.nodeName.toUpperCase();if(parentName==="OPTGROUP"){parentNode=parentNode.parentNode;parentName=parentNode&&parentNode.nodeName.toUpperCase()}if(parentName==="SELECT"&&!parentNode.hasAttribute("multiple")){if(fromEl.hasAttribute("selected")&&!toEl.selected){fromEl.setAttribute("selected","selected");fromEl.removeAttribute("selected")}parentNode.selectedIndex=-1}}syncBooleanAttrProp(fromEl,toEl,"selected")},INPUT:function(fromEl,toEl){syncBooleanAttrProp(fromEl,toEl,"checked");syncBooleanAttrProp(fromEl,toEl,"disabled");if(fromEl.value!==toEl.value){fromEl.value=toEl.value}if(!toEl.hasAttribute("value")){fromEl.removeAttribute("value")}},TEXTAREA:function(fromEl,toEl){var newValue=toEl.value;if(fromEl.value!==newValue){fromEl.value=newValue}var firstChild=fromEl.firstChild;if(firstChild){var oldValue=firstChild.nodeValue;if(oldValue==newValue||!newValue&&oldValue==fromEl.placeholder){return}firstChild.nodeValue=newValue}},SELECT:function(fromEl,toEl){if(!toEl.hasAttribute("multiple")){var selectedIndex=-1;var i=0;var curChild=fromEl.firstChild;var optgroup;var nodeName;while(curChild){nodeName=curChild.nodeName&&curChild.nodeName.toUpperCase();if(nodeName==="OPTGROUP"){optgroup=curChild;curChild=optgroup.firstChild}else{if(nodeName==="OPTION"){if(curChild.hasAttribute("selected")){selectedIndex=i;break}i++}curChild=curChild.nextSibling;if(!curChild&&optgroup){curChild=optgroup.nextSibling;optgroup=null}}}fromEl.selectedIndex=selectedIndex}}};var ELEMENT_NODE=1;var DOCUMENT_FRAGMENT_NODE$1=11;var TEXT_NODE=3;var COMMENT_NODE=8;function noop(){}function defaultGetNodeKey(node){if(node){return node.getAttribute&&node.getAttribute("id")||node.id}}function morphdomFactory(morphAttrs){return function morphdom(fromNode,toNode,options){if(!options){options={}}if(typeof toNode==="string"){if(fromNode.nodeName==="#document"||fromNode.nodeName==="HTML"||fromNode.nodeName==="BODY"){var toNodeHtml=toNode;toNode=doc.createElement("html");toNode.innerHTML=toNodeHtml}else{toNode=toElement(toNode)}}else if(toNode.nodeType===DOCUMENT_FRAGMENT_NODE$1){toNode=toNode.firstElementChild}var getNodeKey=options.getNodeKey||defaultGetNodeKey;var onBeforeNodeAdded=options.onBeforeNodeAdded||noop;var onNodeAdded=options.onNodeAdded||noop;var onBeforeElUpdated=options.onBeforeElUpdated||noop;var onElUpdated=options.onElUpdated||noop;var onBeforeNodeDiscarded=options.onBeforeNodeDiscarded||noop;var onNodeDiscarded=options.onNodeDiscarded||noop;var onBeforeElChildrenUpdated=options.onBeforeElChildrenUpdated||noop;var skipFromChildren=options.skipFromChildren||noop;var addChild=options.addChild||function(parent,child){return parent.appendChild(child)};var childrenOnly=options.childrenOnly===true;var fromNodesLookup=Object.create(null);var keyedRemovalList=[];function addKeyedRemoval(key){keyedRemovalList.push(key)}function walkDiscardedChildNodes(node,skipKeyedNodes){if(node.nodeType===ELEMENT_NODE){var curChild=node.firstChild;while(curChild){var key=undefined;if(skipKeyedNodes&&(key=getNodeKey(curChild))){addKeyedRemoval(key)}else{onNodeDiscarded(curChild);if(curChild.firstChild){walkDiscardedChildNodes(curChild,skipKeyedNodes)}}curChild=curChild.nextSibling}}}function removeNode(node,parentNode,skipKeyedNodes){if(onBeforeNodeDiscarded(node)===false){return}if(parentNode){parentNode.removeChild(node)}onNodeDiscarded(node);walkDiscardedChildNodes(node,skipKeyedNodes)}function indexTree(node){if(node.nodeType===ELEMENT_NODE||node.nodeType===DOCUMENT_FRAGMENT_NODE$1){var curChild=node.firstChild;while(curChild){var key=getNodeKey(curChild);if(key){fromNodesLookup[key]=curChild}indexTree(curChild);curChild=curChild.nextSibling}}}indexTree(fromNode);function handleNodeAdded(el){onNodeAdded(el);var curChild=el.firstChild;while(curChild){var nextSibling=curChild.nextSibling;var key=getNodeKey(curChild);if(key){var unmatchedFromEl=fromNodesLookup[key];if(unmatchedFromEl&&compareNodeNames(curChild,unmatchedFromEl)){curChild.parentNode.replaceChild(unmatchedFromEl,curChild);morphEl(unmatchedFromEl,curChild)}else{handleNodeAdded(curChild)}}else{handleNodeAdded(curChild)}curChild=nextSibling}}function cleanupFromEl(fromEl,curFromNodeChild,curFromNodeKey){while(curFromNodeChild){var fromNextSibling=curFromNodeChild.nextSibling;if(curFromNodeKey=getNodeKey(curFromNodeChild)){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=fromNextSibling}}function morphEl(fromEl,toEl,childrenOnly){var toElKey=getNodeKey(toEl);if(toElKey){delete fromNodesLookup[toElKey]}if(!childrenOnly){if(onBeforeElUpdated(fromEl,toEl)===false){return}morphAttrs(fromEl,toEl);onElUpdated(fromEl);if(onBeforeElChildrenUpdated(fromEl,toEl)===false){return}}if(fromEl.nodeName!=="TEXTAREA"){morphChildren(fromEl,toEl)}else{specialElHandlers.TEXTAREA(fromEl,toEl)}}function morphChildren(fromEl,toEl){var skipFrom=skipFromChildren(fromEl);var curToNodeChild=toEl.firstChild;var curFromNodeChild=fromEl.firstChild;var curToNodeKey;var curFromNodeKey;var fromNextSibling;var toNextSibling;var matchingFromEl;outer:while(curToNodeChild){toNextSibling=curToNodeChild.nextSibling;curToNodeKey=getNodeKey(curToNodeChild);while(!skipFrom&&curFromNodeChild){fromNextSibling=curFromNodeChild.nextSibling;if(curToNodeChild.isSameNode&&curToNodeChild.isSameNode(curFromNodeChild)){curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling;continue outer}curFromNodeKey=getNodeKey(curFromNodeChild);var curFromNodeType=curFromNodeChild.nodeType;var isCompatible=undefined;if(curFromNodeType===curToNodeChild.nodeType){if(curFromNodeType===ELEMENT_NODE){if(curToNodeKey){if(curToNodeKey!==curFromNodeKey){if(matchingFromEl=fromNodesLookup[curToNodeKey]){if(fromNextSibling===matchingFromEl){isCompatible=false}else{fromEl.insertBefore(matchingFromEl,curFromNodeChild);if(curFromNodeKey){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=matchingFromEl}}else{isCompatible=false}}}else if(curFromNodeKey){isCompatible=false}isCompatible=isCompatible!==false&&compareNodeNames(curFromNodeChild,curToNodeChild);if(isCompatible){morphEl(curFromNodeChild,curToNodeChild)}}else if(curFromNodeType===TEXT_NODE||curFromNodeType==COMMENT_NODE){isCompatible=true;if(curFromNodeChild.nodeValue!==curToNodeChild.nodeValue){curFromNodeChild.nodeValue=curToNodeChild.nodeValue}}}if(isCompatible){curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling;continue outer}if(curFromNodeKey){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=fromNextSibling}if(curToNodeKey&&(matchingFromEl=fromNodesLookup[curToNodeKey])&&compareNodeNames(matchingFromEl,curToNodeChild)){if(!skipFrom){addChild(fromEl,matchingFromEl)}morphEl(matchingFromEl,curToNodeChild)}else{var onBeforeNodeAddedResult=onBeforeNodeAdded(curToNodeChild);if(onBeforeNodeAddedResult!==false){if(onBeforeNodeAddedResult){curToNodeChild=onBeforeNodeAddedResult}if(curToNodeChild.actualize){curToNodeChild=curToNodeChild.actualize(fromEl.ownerDocument||doc)}addChild(fromEl,curToNodeChild);handleNodeAdded(curToNodeChild)}}curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling}cleanupFromEl(fromEl,curFromNodeChild,curFromNodeKey);var specialElHandler=specialElHandlers[fromEl.nodeName];if(specialElHandler){specialElHandler(fromEl,toEl)}}var morphedNode=fromNode;var morphedNodeType=morphedNode.nodeType;var toNodeType=toNode.nodeType;if(!childrenOnly){if(morphedNodeType===ELEMENT_NODE){if(toNodeType===ELEMENT_NODE){if(!compareNodeNames(fromNode,toNode)){onNodeDiscarded(fromNode);morphedNode=moveChildren(fromNode,createElementNS(toNode.nodeName,toNode.namespaceURI))}}else{morphedNode=toNode}}else if(morphedNodeType===TEXT_NODE||morphedNodeType===COMMENT_NODE){if(toNodeType===morphedNodeType){if(morphedNode.nodeValue!==toNode.nodeValue){morphedNode.nodeValue=toNode.nodeValue}return morphedNode}else{morphedNode=toNode}}}if(morphedNode===toNode){onNodeDiscarded(fromNode)}else{if(toNode.isSameNode&&toNode.isSameNode(morphedNode)){return}morphEl(morphedNode,toNode,childrenOnly);if(keyedRemovalList){for(var i=0,len=keyedRemovalList.length;i { 2 | 3 | /** 4 | * 5 | * @param string 6 | * @return {boolean} 7 | */ 8 | function isString (string) { 9 | return typeof string === 'string' 10 | } 11 | 12 | const AttributeHandlers = { 13 | value: ( el, value ) => { 14 | el.value = value 15 | }, 16 | className: (el, attribute) => { 17 | if (isString(attribute)) { 18 | attribute = attribute.split(' ').map( c => c.trim() ).filter( c => c ) 19 | } 20 | 21 | el.classList.add(...attribute) 22 | }, 23 | eventHandlers: (el, events) => { 24 | for (let event in events) { 25 | el.addEventListener(event, events[event]) 26 | } 27 | }, 28 | onInput: (el, func) => AttributeHandlers.eventHandlers(el, { input: func }), 29 | onChange: (el, func) => AttributeHandlers.eventHandlers(el, { change: func }), 30 | onFocus: (el, func) => AttributeHandlers.eventHandlers(el, { focus: func }), 31 | onClick: (el, func) => AttributeHandlers.eventHandlers(el, { click: func }), 32 | style: (el, style) => { 33 | 34 | if (isString(style)) { 35 | el.style = style 36 | return 37 | } 38 | 39 | for (let attribute in style) { 40 | el.style[attribute] = style[attribute] 41 | } 42 | }, 43 | onCreate: (el, func) => func(el), 44 | 45 | } 46 | 47 | /** 48 | * 49 | * @param html 50 | * @return {ChildNode} 51 | */ 52 | function htmlToElement (html) { 53 | var template = document.createElement('template') 54 | html = html.trim() // Never return a text node of whitespace as the result 55 | template.innerHTML = html 56 | return template.content.firstChild 57 | } 58 | 59 | /** 60 | * 61 | * @param html 62 | * @return {NodeListOf} 63 | */ 64 | function htmlToElements (html) { 65 | var template = document.createElement('template') 66 | template.innerHTML = html 67 | return template.content.childNodes 68 | } 69 | 70 | /** 71 | * 72 | * @param tagName 73 | * @param attributes 74 | * @param children 75 | * @return {*} 76 | */ 77 | const makeEl = (tagName, attributes, children = null) => { 78 | 79 | let el = tagName === 'fragment' ? document.createDocumentFragment() : document.createElement(tagName) 80 | 81 | for (let attributeName in attributes) { 82 | 83 | if (attributes[attributeName] === false) { 84 | continue 85 | } 86 | 87 | if (AttributeHandlers.hasOwnProperty(attributeName)) { 88 | AttributeHandlers[attributeName](el, attributes[attributeName]) 89 | continue 90 | } 91 | 92 | if (attributeName.startsWith('data')) { 93 | let dataName = attributeName.replace(/^data(.+)/, '$1') 94 | dataName = dataName.charAt(0).toLowerCase() + dataName.slice(1) 95 | 96 | el.dataset[dataName] = attributes[attributeName] 97 | continue 98 | } 99 | 100 | el.setAttribute(attributeName, attributes[attributeName]) 101 | } 102 | 103 | if (children === null) { 104 | return el 105 | } 106 | 107 | if (!Array.isArray(children)) { 108 | children = [children] 109 | } 110 | 111 | children.forEach(child => { 112 | 113 | if (! child) { 114 | return 115 | } 116 | 117 | // Template literals 118 | if (isString(child)) { 119 | let _children = htmlToElements(child) 120 | while (_children.length) { 121 | el.appendChild(_children[0]) 122 | } 123 | return 124 | } 125 | 126 | el.appendChild(child) 127 | }) 128 | 129 | return el 130 | } 131 | 132 | const Input = (attributes) => { 133 | return makeEl('input', { 134 | type: 'text', 135 | ...attributes, 136 | }) 137 | } 138 | 139 | const Textarea = (attributes) => { 140 | return makeEl('textarea', { 141 | ...attributes, 142 | }) 143 | } 144 | 145 | const Select = (attributes) => { 146 | 147 | let { 148 | options = {}, 149 | selected = '', 150 | onChange = e => {}, 151 | ...rest 152 | } = attributes 153 | 154 | if (!Array.isArray(options)) { 155 | options = Object.keys(options).map(key => ({ value: key, text: options[key] })) 156 | } 157 | 158 | if (!Array.isArray(selected)) { 159 | selected = [selected] 160 | } 161 | 162 | options = options.map(opt => typeof opt === 'string' ? { value: opt, text: opt } : opt). 163 | map(({ value, text }) => makeEl('option', { 164 | value, 165 | selected: selected.includes(value), 166 | }, text)) 167 | 168 | return makeEl('select', { 169 | ...rest, 170 | onChange: (e) => { 171 | if (rest.multiple) { 172 | e.target.values = e.target.querySelectorAll('option:checked').map(el => el.value) 173 | } 174 | 175 | onChange(e) 176 | }, 177 | }, options) 178 | } 179 | 180 | const Button = (attributes, children) => { 181 | return makeEl('button', { 182 | ...attributes, 183 | }, children) 184 | } 185 | 186 | const Toggle = ({ 187 | onLabel = 'On', 188 | offLabel = 'Off', 189 | ...atts 190 | }) => { 191 | 192 | return makeEl('label', { 193 | className: 'holler-switch', 194 | }, [ 195 | Input({ 196 | ...atts, 197 | type: 'checkbox', 198 | }), 199 | //language=HTML 200 | ` 201 | ${onLabel} 202 | ${offLabel}`, 203 | ]) 204 | } 205 | 206 | const Div = (attributes = {}, children = []) => { 207 | return makeEl('div', attributes, children) 208 | } 209 | 210 | const Dashicon = (icon) => { 211 | return makeEl('span', { 212 | className: `dashicons dashicons-${icon}`, 213 | }) 214 | } 215 | 216 | const Fragment = ( children ) => { 217 | return makeEl( 'fragment', {}, children ) 218 | } 219 | 220 | const Span = (attributes = {}, children = []) => { 221 | return makeEl('span', attributes, children ) 222 | } 223 | 224 | const Label = (attributes = {}, children = []) => { 225 | return makeEl('label', attributes, children ) 226 | } 227 | 228 | const InputRepeater = ({ 229 | onChange = () => {}, 230 | rows = [], 231 | cells = [], 232 | sortable = false, 233 | fillRow = () => Array(cells.length).fill(''), 234 | }) => { 235 | 236 | const changeEvent = () => new CustomEvent() 237 | 238 | const removeRow = (rowIndex) => { 239 | rows.splice(rowIndex, 1) 240 | onChange(rows) 241 | } 242 | 243 | const addRow = () => { 244 | rows.push(fillRow()) 245 | onChange(rows) 246 | } 247 | 248 | const onCellChange = (rowIndex, cellIndex, value) => { 249 | rows[rowIndex][cellIndex] = value 250 | onChange(rows) 251 | } 252 | 253 | const RepeaterRow = (row, rowIndex) => Div({ 254 | className: 'holler-input-repeater-row', 255 | dataRow: rowIndex, 256 | }, [ 257 | // Cells 258 | ...cells.map((cellCallback, cellIndex) => cellCallback({ 259 | value: row[cellIndex] ?? '', 260 | dataRow: rowIndex, 261 | dataCell: cellIndex, 262 | onChange: e => onCellChange(rowIndex, cellIndex, e.target.value), 263 | }, row)), 264 | // Sortable Handle 265 | sortable ? makeEl('span', { 266 | className: 'handle', 267 | dataRow: rowIndex, 268 | }, Dashicon('move')) : null, 269 | // Remove Row Button 270 | Button({ 271 | className: 'holler-button dashicon remove-row', 272 | dataRow: rowIndex, 273 | onClick: e => removeRow(rowIndex), 274 | }, Dashicon('no-alt')), 275 | ]) 276 | 277 | let repeater = Div({ 278 | className: 'holler-input-repeater', 279 | onCreate: el => { 280 | 281 | if (!sortable) { 282 | return 283 | } 284 | 285 | $(el).sortable({ 286 | handle: '.handle', 287 | update: (e, ui) => { 288 | 289 | let $row = $(ui.item) 290 | let oldIndex = parseInt($row.data('row')) 291 | let curIndex = $row.index() 292 | 293 | let row = rows[oldIndex] 294 | 295 | rows.splice(oldIndex, 1) 296 | rows.splice(curIndex, 0, row) 297 | onChange(rows, repeater) 298 | }, 299 | }) 300 | }, 301 | }, [ 302 | ...rows.map((row, i) => RepeaterRow(row, i)), 303 | Div({ 304 | className: 'holler-input-repeater-row-add', 305 | }, [ 306 | `
    `, 307 | // Add Row Button 308 | Button({ 309 | className: 'add-row holler-button dashicon', 310 | onClick: e => addRow(), 311 | }, Dashicon('plus-alt2')), 312 | ]), 313 | ]) 314 | 315 | return repeater 316 | } 317 | 318 | window.MakeEl = { 319 | makeEl, 320 | Input, 321 | Textarea, 322 | Select, 323 | Button, 324 | Toggle, 325 | Div, 326 | Span, 327 | Label, 328 | InputRepeater, 329 | Fragment 330 | } 331 | })(jQuery) -------------------------------------------------------------------------------- /assets/js/make-el.min.js: -------------------------------------------------------------------------------- 1 | ($=>{function isString(string){return typeof string==="string"}const AttributeHandlers={value:(el,value)=>{el.value=value},className:(el,attribute)=>{if(isString(attribute)){attribute=attribute.split(" ").map(c=>c.trim()).filter(c=>c)}el.classList.add(...attribute)},eventHandlers:(el,events)=>{for(let event in events){el.addEventListener(event,events[event])}},onInput:(el,func)=>AttributeHandlers.eventHandlers(el,{input:func}),onChange:(el,func)=>AttributeHandlers.eventHandlers(el,{change:func}),onFocus:(el,func)=>AttributeHandlers.eventHandlers(el,{focus:func}),onClick:(el,func)=>AttributeHandlers.eventHandlers(el,{click:func}),style:(el,style)=>{if(isString(style)){el.style=style;return}for(let attribute in style){el.style[attribute]=style[attribute]}},onCreate:(el,func)=>func(el)};function htmlToElement(html){var template=document.createElement("template");html=html.trim();template.innerHTML=html;return template.content.firstChild}function htmlToElements(html){var template=document.createElement("template");template.innerHTML=html;return template.content.childNodes}const makeEl=(tagName,attributes,children=null)=>{let el=tagName==="fragment"?document.createDocumentFragment():document.createElement(tagName);for(let attributeName in attributes){if(attributes[attributeName]===false){continue}if(AttributeHandlers.hasOwnProperty(attributeName)){AttributeHandlers[attributeName](el,attributes[attributeName]);continue}if(attributeName.startsWith("data")){let dataName=attributeName.replace(/^data(.+)/,"$1");dataName=dataName.charAt(0).toLowerCase()+dataName.slice(1);el.dataset[dataName]=attributes[attributeName];continue}el.setAttribute(attributeName,attributes[attributeName])}if(children===null){return el}if(!Array.isArray(children)){children=[children]}children.forEach(child=>{if(!child){return}if(isString(child)){let _children=htmlToElements(child);while(_children.length){el.appendChild(_children[0])}return}el.appendChild(child)});return el};const Input=attributes=>{return makeEl("input",{type:"text",...attributes})};const Textarea=attributes=>{return makeEl("textarea",{...attributes})};const Select=attributes=>{let{options:options={},selected:selected="",onChange:onChange=e=>{},...rest}=attributes;if(!Array.isArray(options)){options=Object.keys(options).map(key=>({value:key,text:options[key]}))}if(!Array.isArray(selected)){selected=[selected]}options=options.map(opt=>typeof opt==="string"?{value:opt,text:opt}:opt).map(({value,text})=>makeEl("option",{value:value,selected:selected.includes(value)},text));return makeEl("select",{...rest,onChange:e=>{if(rest.multiple){e.target.values=e.target.querySelectorAll("option:checked").map(el=>el.value)}onChange(e)}},options)};const Button=(attributes,children)=>{return makeEl("button",{...attributes},children)};const Toggle=({onLabel:onLabel="On",offLabel:offLabel="Off",...atts})=>{return makeEl("label",{className:"holler-switch"},[Input({...atts,type:"checkbox"}),` 2 | ${onLabel} 3 | ${offLabel}`])};const Div=(attributes={},children=[])=>{return makeEl("div",attributes,children)};const Dashicon=icon=>{return makeEl("span",{className:`dashicons dashicons-${icon}`})};const Fragment=children=>{return makeEl("fragment",{},children)};const Span=(attributes={},children=[])=>{return makeEl("span",attributes,children)};const Label=(attributes={},children=[])=>{return makeEl("label",attributes,children)};const InputRepeater=({onChange:onChange=()=>{},rows:rows=[],cells:cells=[],sortable:sortable=false,fillRow:fillRow=()=>Array(cells.length).fill("")})=>{const changeEvent=()=>new CustomEvent;const removeRow=rowIndex=>{rows.splice(rowIndex,1);onChange(rows)};const addRow=()=>{rows.push(fillRow());onChange(rows)};const onCellChange=(rowIndex,cellIndex,value)=>{rows[rowIndex][cellIndex]=value;onChange(rows)};const RepeaterRow=(row,rowIndex)=>Div({className:"holler-input-repeater-row",dataRow:rowIndex},[...cells.map((cellCallback,cellIndex)=>cellCallback({value:row[cellIndex]??"",dataRow:rowIndex,dataCell:cellIndex,onChange:e=>onCellChange(rowIndex,cellIndex,e.target.value)},row)),sortable?makeEl("span",{className:"handle",dataRow:rowIndex},Dashicon("move")):null,Button({className:"holler-button dashicon remove-row",dataRow:rowIndex,onClick:e=>removeRow(rowIndex)},Dashicon("no-alt"))]);let repeater=Div({className:"holler-input-repeater",onCreate:el=>{if(!sortable){return}$(el).sortable({handle:".handle",update:(e,ui)=>{let $row=$(ui.item);let oldIndex=parseInt($row.data("row"));let curIndex=$row.index();let row=rows[oldIndex];rows.splice(oldIndex,1);rows.splice(curIndex,0,row);onChange(rows,repeater)}})}},[...rows.map((row,i)=>RepeaterRow(row,i)),Div({className:"holler-input-repeater-row-add"},[`
    `,Button({className:"add-row holler-button dashicon",onClick:e=>addRow()},Dashicon("plus-alt2"))])]);return repeater};window.MakeEl={makeEl:makeEl,Input:Input,Textarea:Textarea,Select:Select,Button:Button,Toggle:Toggle,Div:Div,Span:Span,Label:Label,InputRepeater:InputRepeater,Fragment:Fragment}})(jQuery); -------------------------------------------------------------------------------- /assets/js/reports.min.js: -------------------------------------------------------------------------------- 1 | ($=>{const{report_data:report_data=[]}=HollerBox;const{icons,confirmationModal,tooltipIcon,tooltip}=HollerBox.elements;const{__}=wp.i18n;let after,before;after=moment().subtract(6,"days");before=moment();const __YMD="YYYY-MM-DD";function ApiError(message){this.name="ApiError";this.message=message}ApiError.prototype=Error.prototype;function darkenRGB(rgb,percentage){const[r,g,b]=rgb.match(/\d+/g).map(Number);const validPercentage=Math.max(0,Math.min(100,percentage));const factor=1-validPercentage/100;const newR=Math.round(r*factor);const newG=Math.round(g*factor);const newB=Math.round(b*factor);return`rgb(${newR}, ${newG}, ${newB})`}async function apiGet(route,params={},opts={}){let __params=new URLSearchParams;Object.keys(params).forEach(k=>{__params.append(k,params[k])});const response=await fetch(route+"?"+__params,{headers:{"X-WP-Nonce":HollerBox.nonces._wprest},...opts});let json=await response.json();if(!response.ok){console.log(json);throw new ApiError(json.message)}return json}function getDatesInRange(after,before){let date=moment(after);const dates=[];while(date<=before){dates.push(moment(date));date.add(1,"day")}return dates}const arrayUnique=items=>{let set=new Set(items);return[...set]};const ReportData={report_data:{},popups:{},cache:{},fetch(){return apiGet(HollerBox.routes.report,{before:before.format(__YMD),after:after.format(__YMD)}).then(r=>{this.report_data=r.data;this.popups=r.popups})},getPopup(id){return this.popups.find(p=>p.ID==id)},getPopups(){return arrayUnique(this.report_data.map(({popup_id})=>parseInt(popup_id))).map(id=>this.popups.find(p=>p.ID==id))},getPopupIds(){return arrayUnique(this.report_data.map(({popup_id})=>parseInt(popup_id)))},getPages(){return arrayUnique(this.report_data.map(({location})=>location))},getContents({popup_id:_popup_id=false}){return arrayUnique(this.report_data.filter(({popup_id,s_type})=>popup_id==_popup_id&&s_type==="conversion").map(({content})=>content))},sumCount(type,{id:id=false,date:date=false,location:_location=false,content:_content=false}){return this.report_data.filter(({popup_id,s_type,s_date,location:location="",content:content=""})=>(id?popup_id==id:true)&&(date?s_date===date.format(__YMD):true)&&(_location?_location===location:true)&&(_content?_content===content:true)&&s_type===type).reduce((total,{s_count})=>total+parseInt(s_count),0)},sumConversions(query){return this.sumCount("conversion",query)},sumImpressions(query){return this.sumCount("impression",query)},sumContent(query){return this.sumCount("conversion",query)}};const{Div,Input,Select,Button}=MakeEl;const Pagination=({rows:rows=[],itemsPerPage:itemsPerPage=10,currentPage:currentPage=1,onPageChange:onPageChange=page=>{}})=>{let numPages=Math.ceil(rows.length/itemsPerPage);const PrevButton=()=>Button({className:"holler-button secondary prev-button",onClick:()=>onPageChange(currentPage-1)},"Prev");const NextButton=()=>Button({className:"holler-button secondary next-button",onClick:()=>onPageChange(currentPage+1)},"Next");const PageButton=page=>Button({className:`holler-button ${currentPage===page?"primary":"secondary"} paginate page-${page}`,onClick:()=>onPageChange(page)},`${page}`);let pageButtons=[];let start=Math.max(1,currentPage-2);let end=Math.min(numPages,start+3);if(start>1){pageButtons.push(PageButton(1));if(start>2){pageButtons.push('')}}for(let i=start;i<=end;i++){pageButtons.push(PageButton(i))}if(end…')}pageButtons.push(PageButton(numPages))}return Div({className:"pagination display-flex gap-10"},[currentPage>1?PrevButton():null,...pageButtons,currentPage{}})=>{const $el=$(selector);let itemsPerPage=10;let currentPage=1;const renderTable=()=>{let offSet=(currentPage-1)*itemsPerPage;return` 2 | 3 | 4 | ${headers.map(h=>``).join("")} 5 | 6 | 7 | ${rows.slice(offSet,offSet+itemsPerPage).map(row=>`${row.map(item=>``).join("")}`).join("")} 8 | 9 |
    ${h}
    ${item}
    10 |
    11 | `};const mount=()=>{$el.html(renderTable());if(rows.length>itemsPerPage){morphdom(document.querySelector(`${selector} .table-pagination`),Pagination({rows:rows,itemsPerPage:itemsPerPage,currentPage:currentPage,onPageChange:page=>{currentPage=page;mount()}}))}onMount()};mount()};const lineChart=(id,query={})=>{const dates=getDatesInRange(after,before);const labels=dates.map(m=>m.format("MMM D"));const data={labels:labels,datasets:[{label:"impressions",data:dates.map(date=>ReportData.sumImpressions({date:date,...query})),tension:.1,fill:true,backgroundColor:"rgba(0, 117, 255, 0.20)",borderColor:"rgba(0, 117, 255, 0.25)"},{label:"conversions",data:dates.map(date=>ReportData.sumConversions({date:date,...query})),tension:.1,fill:true,backgroundColor:"rgba(0, 117, 255, 0.70)",borderColor:"rgba(0, 117, 255, 0.80)"}]};const config={type:"line",data:data,options:{maintainAspectRatio:false}};const myChart=new Chart(document.getElementById(id),config)};const pages=[{slug:/popup\/[0-9]+/,render:()=>{return` 12 |
    13 |

    14 | 15 |
    16 |
    17 |
    18 |

    All Activity

    19 |
    20 |
    21 | 22 |
    23 |
    24 |
    25 |
    26 |

    Impressions

    27 |
    28 |
    29 |
    30 |
    31 |
    32 |
    33 |
    34 |

    Conversions

    35 |
    36 |
    37 |
    38 |
    39 |
    40 |
    41 |
    42 |

    Conversion Rate

    43 |
    44 |
    45 |
    46 |
    47 |
    48 |
    49 |
    50 |

    Pages

    51 |
    52 |
    53 |
    54 |
    55 |
    56 |
    57 |

    Conversion Content ${tooltipIcon("conversion-content")}

    58 |
    59 |
    60 | 61 |
    62 |
    63 | `},onMount:([name,popup_id],setPage)=>{tooltip("#conversion-content",{content:"The content a user interacted with when a conversion was recorded."});let popup=ReportData.getPopup(popup_id);if(!popup){confirmationModal({alert:`

    ${__("There is no data for this popup yet. Wait a few days and check again.")}

    `,onConfirm:()=>{setPage("/")}});return}$(".popup-title").html(popup.post_title);$("#edit-popup").on("click",e=>{e.preventDefault();window.open(`${HollerBox.admin_url}/post.php?post=${popup.ID}&action=edit`,"_self")});lineChart("main-graph",{id:popup.ID});let contents=ReportData.getContents({popup_id:popup.ID});let rgb="rgb(0,120,255)";new Chart(document.getElementById("content-graph"),{type:"doughnut",data:{labels:contents.map(c=>c?c:"Clicked"),datasets:[{label:"Conversion Content",data:contents.map(content=>ReportData.sumContent({content:content,id:popup.ID})),backgroundColor:contents.map((c,i)=>{if(i>0){rgb=darkenRGB(rgb,20)}return rgb})}]},options:{plugins:{legend:{position:"right"}},aspectRatio:2}});let impressions=ReportData.sumImpressions({id:popup.ID});let conversions=ReportData.sumConversions({id:popup.ID});$("#impressions").html(impressions);$("#conversions").html(conversions);$("#conversion-rate").html(Math.floor(conversions/Math.max(impressions,1)*100)+"%");Table("#pages-table",{headers:["Page","Imp.","Conv.","CVR."],rows:ReportData.getPages().map(page=>{let impressions=ReportData.sumImpressions({location:page,id:popup.ID});let conversions=ReportData.sumConversions({location:page,id:popup.ID});return[`${page}`,impressions,conversions,Math.floor(conversions/Math.max(impressions,1)*100)+"%"]}).filter(([link,impressions])=>impressions>0).sort(([la,ia,ca],[lb,ib,cb])=>{if(ca===cb){return ib-ia}return cb-ca})})}},{slug:"",render:()=>{return` 64 |
    65 |
    66 |

    All Activity

    67 |
    68 |
    69 | 70 |
    71 |
    72 |
    73 |
    74 |

    Impressions

    75 |
    76 |
    77 |
    78 |
    79 |
    80 |
    81 |
    82 |

    Conversions

    83 |
    84 |
    85 |
    86 |
    87 |
    88 |
    89 |
    90 |

    Conversion Rate

    91 |
    92 |
    93 |
    94 |
    95 |
    96 |
    97 |
    98 |

    Popups

    99 |
    100 |
    101 |
    102 |
    103 |
    104 |
    105 |

    Pages

    106 |
    107 |
    108 |
    109 |
    110 | `},onMount:(params,setPage)=>{lineChart("main-graph",{});let impressions=ReportData.sumImpressions({});let conversions=ReportData.sumConversions({});$("#impressions").html(impressions);$("#conversions").html(conversions);$("#conversion-rate").html(Math.floor(conversions/Math.max(impressions,1)*100)+"%");Table("#pages-table",{headers:["Page","Imp.","Conv.","CVR."],rows:ReportData.getPages().map(page=>{let impressions=ReportData.sumImpressions({location:page});let conversions=ReportData.sumConversions({location:page});return[`${page}`,impressions,conversions,Math.floor(conversions/impressions*100)+"%"]}).sort(([la,ia,ca],[lb,ib,cb])=>{if(ca===cb){return ib-ia}return cb-ca})});Table("#popups-table",{onMount:()=>{$(".popup-link").on("click",e=>{e.preventDefault();setPage(`/popup/${e.target.dataset.id}/`)})},headers:["Popup","Imp.","Conv.","CVR."],rows:ReportData.getPopups().map(p=>{let impressions=ReportData.sumImpressions({id:p.ID});let conversions=ReportData.sumConversions({id:p.ID});return[`${p.post_title}`,impressions,conversions,Math.floor(conversions/impressions*100)+"%"]}).sort(([la,ia,ca],[lb,ib,cb])=>{if(ca===cb){return ib-ia}return cb-ca})})}}];const Page={slug:"",currentPage:pages[0],params:[],getCurSlug(){return window.location.hash.substring(1)},initFromSlug(){this.slug=this.getCurSlug();this.params=this.getCurSlug().split("/").filter(p=>p);this.mount();window.dispatchEvent(new Event("resize"))},init(){if(window.location.hash){this.initFromSlug()}else{history.pushState({},"",`#/`);this.initFromSlug()}window.addEventListener("popstate",e=>{this.initFromSlug()})},renderReports(){return` 111 |
    112 | 115 |
    116 |
    117 |
    118 | ${this.currentPage.render()} 119 |
    `},mount(){this.currentPage=pages.find(p=>this.slug.match(p.slug));const setPage=slug=>{history.pushState({},"",`#${slug}`);this.initFromSlug()};$("#holler-app").html(this.renderReports());const datePicker=new Calendar({element:$("#holler-datepicker"),presets:[{label:"Last 30 days",start:moment().subtract(29,"days"),end:moment()},{label:"Last 14 days",start:moment().subtract(13,"days"),end:moment()},{label:"Last 7 days",start:moment().subtract(6,"days"),end:moment()},{label:"Today",start:moment(),end:moment()},{label:"Yesterday",start:moment().subtract(1,"day"),end:moment().subtract(1,"day")},{label:"This month",start:moment().startOf("month"),end:moment().endOf("month")},{label:"Last month",start:moment().subtract(1,"month").startOf("month"),end:moment().subtract(1,"month").endOf("month")}],format:{preset:"MMM D"},earliest_date:"January 1, 2017",latest_date:moment(),start_date:after,end_date:before,callback:function(){after=this.start_date;before=this.end_date;ReportData.fetch().then(()=>Page.mount())}});this.currentPage.onMount(this.params,setPage)}};$(()=>ReportData.fetch().then(()=>Page.init()))})(jQuery); -------------------------------------------------------------------------------- /holler-box.php: -------------------------------------------------------------------------------- 1 | setup_constants(); 60 | $this->includes(); 61 | $this->load_textdomain(); 62 | $this->hooks(); 63 | 64 | Holler_Admin::instance(); 65 | new Holler_Api(); 66 | new Holler_Frontend(); 67 | new Holler_Telemetry(); 68 | new Holler_Updater(); 69 | 70 | Holler_Licensing::instance(); 71 | } 72 | 73 | /** 74 | * Setup plugin constants 75 | * 76 | * @access private 77 | * @since 0.1.0 78 | * @return void 79 | */ 80 | private function setup_constants() { 81 | 82 | // Plugin version 83 | define( 'Holler_Box_VER', HOLLERBOX_VERSION ); 84 | 85 | // Plugin path 86 | define( 'Holler_Box_DIR', plugin_dir_path( __FILE__ ) ); 87 | 88 | // Plugin URL 89 | define( 'Holler_Box_URL', plugin_dir_url( __FILE__ ) ); 90 | } 91 | 92 | 93 | /** 94 | * Include necessary files 95 | * 96 | * @access private 97 | * @since 0.1.0 98 | * @return void 99 | */ 100 | private function includes() { 101 | require_once __DIR__ . '/includes/class-holler-api.php'; 102 | require_once __DIR__ . '/includes/class-holler-admin.php'; 103 | require_once __DIR__ . '/includes/class-holler-popup.php'; 104 | require_once __DIR__ . '/includes/class-holler-frontend.php'; 105 | require_once __DIR__ . '/includes/class-holler-lead.php'; 106 | require_once __DIR__ . '/includes/class-holler-integrations.php'; 107 | require_once __DIR__ . '/includes/class-holler-reporting.php'; 108 | require_once __DIR__ . '/includes/class-holler-settings.php'; 109 | require_once __DIR__ . '/includes/class-holler-licensing.php'; 110 | require_once __DIR__ . '/includes/class-holler-telemetry.php'; 111 | require_once __DIR__ . '/includes/class-holler-updater.php'; 112 | require_once __DIR__ . '/includes/Holler_EDD_SL_Plugin_Updater.php'; 113 | } 114 | 115 | 116 | /** 117 | * Run action and filter hooks 118 | * 119 | * @access private 120 | * @since 0.1.0 121 | * @return void 122 | * 123 | * 124 | */ 125 | private function hooks() { 126 | } 127 | 128 | /** 129 | * Internationalization 130 | * 131 | * @access public 132 | * @since 0.1.0 133 | * @return void 134 | */ 135 | public function load_textdomain() { 136 | 137 | load_plugin_textdomain( 'holler-box' ); 138 | 139 | } 140 | 141 | } 142 | } // End if class_exists check 143 | 144 | 145 | /** 146 | * The main function responsible for returning the one true EDD_Metrics 147 | * instance to functions everywhere 148 | * 149 | * @since 0.1.0 150 | * @return \Holler_Box The one true Holler_Box 151 | * 152 | */ 153 | function holler_box_load() { 154 | Holler_Box::instance(); 155 | 156 | do_action( 'hollerbox/loaded' ); 157 | } 158 | 159 | add_action( 'plugins_loaded', 'holler_box_load' ); 160 | 161 | /** 162 | * The activation hook is called outside of the singleton because WordPress doesn't 163 | * register the call from within the class, since we are preferring the plugins_loaded 164 | * hook for compatibility, we also can't reference a function inside the plugin class 165 | * for the activation function. If you need an activation function, put it here. 166 | * 167 | * @since 0.1.0 168 | * @return void 169 | */ 170 | function holler_box_activation() { 171 | /* Activation functions here */ 172 | 173 | holler_box_load(); 174 | 175 | Holler_Reporting::instance()->create_table(); 176 | } 177 | 178 | register_activation_hook( __FILE__, 'holler_box_activation' ); 179 | -------------------------------------------------------------------------------- /includes/class-holler-api.php: -------------------------------------------------------------------------------- 1 | \d+)', [ 18 | [ 19 | 'methods' => WP_REST_Server::READABLE, 20 | 'callback' => [ $this, 'read' ], 21 | 'permission_callback' => [ $this, 'permission_callback' ] 22 | ], 23 | [ 24 | 'methods' => WP_REST_Server::EDITABLE, 25 | 'callback' => [ $this, 'update' ], 26 | 'permission_callback' => [ $this, 'permission_callback' ] 27 | ], 28 | [ 29 | 'methods' => WP_REST_Server::DELETABLE, 30 | 'callback' => [ $this, 'delete' ], 31 | 'permission_callback' => [ $this, 'permission_callback' ] 32 | ], 33 | ] ); 34 | 35 | register_rest_route( 'hollerbox', 'submit/(?P\d+)', [ 36 | [ 37 | 'methods' => WP_REST_Server::CREATABLE, 38 | 'callback' => [ $this, 'submit' ], 39 | 'permission_callback' => '__return_true' 40 | ] 41 | ] ); 42 | 43 | register_rest_route( 'hollerbox', '/report', [ 44 | [ 45 | 'methods' => WP_REST_Server::READABLE, 46 | 'callback' => [ $this, 'read_report' ], 47 | 'permission_callback' => [ $this, 'permission_callback' ] 48 | ] 49 | ] ); 50 | 51 | register_rest_route( 'hollerbox', 'options', [ 52 | [ 53 | 'methods' => WP_REST_Server::READABLE, 54 | 'callback' => [ $this, 'read_options' ], 55 | 'permission_callback' => [ $this, 'permission_callback' ] 56 | ] 57 | ] ); 58 | 59 | register_rest_route( 'hollerbox', 'closed', [ 60 | [ 61 | 'methods' => WP_REST_Server::CREATABLE, 62 | 'callback' => [ $this, 'track_popup_closed' ], 63 | 'permission_callback' => 'is_user_logged_in' 64 | ] 65 | ] ); 66 | 67 | register_rest_route( 'hollerbox', 'conversion', [ 68 | [ 69 | 'methods' => WP_REST_Server::CREATABLE, 70 | 'callback' => [ $this, 'track_conversion' ], 71 | 'permission_callback' => '__return_true' 72 | ] 73 | ] ); 74 | 75 | register_rest_route( 'hollerbox', 'impression', [ 76 | [ 77 | 'methods' => WP_REST_Server::CREATABLE, 78 | 'callback' => [ $this, 'track_impression' ], 79 | 'permission_callback' => '__return_true' 80 | ] 81 | ] ); 82 | 83 | register_rest_route( 'hollerbox', 'install', [ 84 | [ 85 | 'methods' => WP_REST_Server::CREATABLE, 86 | 'callback' => [ $this, 'install_plugin' ], 87 | 'permission_callback' => [ $this, 'plugins_permission_callback' ] 88 | ] 89 | ] ); 90 | 91 | register_rest_route( 'hollerbox', 'settings', [ 92 | [ 93 | 'methods' => WP_REST_Server::EDITABLE, 94 | 'callback' => [ $this, 'update_settings' ], 95 | 'permission_callback' => [ $this, 'options_permission_callback' ] 96 | ] 97 | ] ); 98 | 99 | register_rest_route( 'hollerbox', 'library', [ 100 | [ 101 | 'methods' => WP_REST_Server::READABLE, 102 | 'callback' => [ $this, 'library' ], 103 | 'permission_callback' => [ $this, 'permission_callback' ] 104 | ] 105 | ] ); 106 | } 107 | 108 | /** 109 | * Do library stuff 110 | * 111 | * @return WP_Error|WP_REST_Response 112 | */ 113 | public function library(){ 114 | 115 | $response = wp_remote_get( 'https://library.groundhogg.io/wp-json/hollerbox/list/' ); 116 | 117 | if ( is_wp_error( $response ) ){ 118 | return $response; 119 | } 120 | 121 | $body = wp_remote_retrieve_body( $response ); 122 | $json = json_decode( $body ); 123 | 124 | return rest_ensure_response( $json ); 125 | } 126 | 127 | /** 128 | * Get all report data for specific time rage 129 | * 130 | * @param WP_REST_Request $request 131 | * 132 | * @return WP_Error|WP_HTTP_Response|WP_REST_Response 133 | */ 134 | public function read_report( WP_REST_Request $request ) { 135 | 136 | $fallback = new DateTime( '30 days ago', wp_timezone() ); 137 | 138 | $before = sanitize_text_field( $request->get_param( 'before' ) ?: current_time( 'Y-m-d' ) ); 139 | $after = sanitize_text_field( $request->get_param( 'after' ) ?: $fallback->format( 'Y-m-d' ) ); 140 | 141 | $data = Holler_Reporting::instance()->get_report_data( [ 142 | 'before' => $before, 143 | 'after' => $after 144 | ] ); 145 | 146 | $popups = array_unique( wp_parse_id_list( wp_list_pluck( $data, 'popup_id' ) ) ); 147 | $popups = array_map( function ( $id ) { 148 | return new Holler_Popup( $id ); 149 | }, $popups ); 150 | 151 | return rest_ensure_response( [ 152 | 'data' => $data, 153 | 'popups' => array_values( $popups ), 154 | ] ); 155 | } 156 | 157 | /** 158 | * When a popup is closed 159 | * 160 | * @param WP_REST_Request $request 161 | * 162 | * @return WP_Error|WP_HTTP_Response|WP_REST_Response 163 | */ 164 | public function track_popup_closed( WP_REST_Request $request ) { 165 | 166 | $id = absint( $request->get_param( 'popup_id' ) ); 167 | 168 | $popup = new Holler_Popup( $id ); 169 | 170 | if ( ! $popup->exists() ) { 171 | return self::ERROR_404(); 172 | } 173 | 174 | if ( ! is_user_logged_in() ){ 175 | return self::ERROR_401(); 176 | } 177 | 178 | $closed_popups = wp_parse_id_list( get_user_meta( get_current_user_id(), 'hollerbox_closed_popups', true ) ); 179 | $closed_popups[] = $popup->ID; 180 | update_user_meta( get_current_user_id(), 'hollerbox_closed_popups', implode( ',', array_unique( $closed_popups ) ) ); 181 | 182 | return rest_ensure_response( [ 183 | 'success' => true 184 | ] ); 185 | } 186 | 187 | /** 188 | * Track a popup conversion 189 | * 190 | * @param WP_REST_Request $request 191 | * 192 | * @return WP_Error|WP_HTTP_Response|WP_REST_Response 193 | */ 194 | public function track_conversion( WP_REST_Request $request ) { 195 | 196 | $id = absint( $request->get_param( 'popup_id' ) ); 197 | 198 | $popup = new Holler_Popup( $id ); 199 | 200 | if ( ! $popup->exists() ) { 201 | return self::ERROR_404(); 202 | } 203 | 204 | // Parse the location 205 | $location = parse_url( sanitize_text_field( $request->get_param( 'location' ) ), PHP_URL_PATH ); 206 | $content = sanitize_text_field( $request->get_param( 'content' ) ); 207 | 208 | Holler_Reporting::instance()->add_conversion( $popup, $location, $content ); 209 | 210 | if ( is_user_logged_in() ) { 211 | $conversions = wp_parse_id_list( get_user_meta( get_current_user_id(), 'hollerbox_popup_conversions', true ) ); 212 | $conversions[] = $popup->ID; 213 | update_user_meta( get_current_user_id(), 'hollerbox_popup_conversions', implode( ',', array_unique( $conversions ) ) ); 214 | } 215 | 216 | return rest_ensure_response( [ 217 | 'success' => true 218 | ] ); 219 | } 220 | 221 | /** 222 | * Track an impression 223 | * 224 | * @param WP_REST_Request $request 225 | * 226 | * @return WP_Error|WP_HTTP_Response|WP_REST_Response 227 | */ 228 | public function track_impression( WP_REST_Request $request ) { 229 | 230 | $id = absint( $request->get_param( 'popup_id' ) ); 231 | 232 | $popup = new Holler_Popup( $id ); 233 | 234 | if ( ! $popup->exists() ) { 235 | return self::ERROR_404(); 236 | } 237 | 238 | // Parse the location 239 | $location = parse_url( sanitize_text_field( $request->get_param( 'location' ) ), PHP_URL_PATH ); 240 | 241 | Holler_Reporting::instance()->add_impression( $popup, $location ); 242 | 243 | return rest_ensure_response( [ 244 | 'success' => true 245 | ] ); 246 | } 247 | 248 | /** 249 | * Standard 404 message 250 | * 251 | * @return WP_Error 252 | */ 253 | public static function ERROR_404() { 254 | return new WP_Error( 'missing', 'Popup not found.', [ 'status' => 404 ] ); 255 | } 256 | 257 | /** 258 | * Standard 401 message 259 | * 260 | * @return WP_Error 261 | */ 262 | public static function ERROR_401() { 263 | return new WP_Error( 'access_denied', 'Insufficient permissions.', [ 'status' => 401 ] ); 264 | } 265 | 266 | /** 267 | * Install either MailHawk or Groundhogg remotely 268 | * 269 | * @param WP_REST_Request $request 270 | * 271 | * @return bool|void|WP_Error 272 | */ 273 | public function install_plugin( WP_REST_Request $request ) { 274 | 275 | $slug = sanitize_text_field( $request->get_param( 'slug' ) ); 276 | 277 | if ( ! in_array( $slug, [ 'groundhogg', 'mailhawk' ] ) ) { 278 | return new WP_Error( 'invalid_slug', 'Not a valid slug.', [ 'status' => 401 ] ); 279 | } 280 | 281 | $installed = $this->_install_plugin( $slug ); 282 | 283 | if ( is_wp_error( $installed ) ) { 284 | return $installed; 285 | } 286 | 287 | if ( ! $installed ) { 288 | return new WP_Error( 'not_installed', 'Unable to install.', [ 'status' => 500 ] ); 289 | } 290 | 291 | $response = []; 292 | 293 | switch ( $slug ) { 294 | case 'mailhawk': 295 | 296 | if ( ! defined( 'MAILHAWK_VERSION' ) ) { 297 | return new WP_Error( 'mailhawk_missing', 'Unable to find MailHawk.', [ 'status' => 500 ] ); 298 | } 299 | 300 | $response = [ 301 | 'partner_id' => '', // todo change this 302 | 'register_url' => esc_url( trailingslashit( MAILHAWK_LICENSE_SERVER_URL ) ), 303 | 'redirect_uri' => \MailHawk\get_admin_mailhawk_uri(), 304 | 'client_state' => esc_attr( \MailHawk\Keys::instance()->state() ), 305 | ]; 306 | 307 | break; 308 | case 'groundhogg': 309 | $response = []; 310 | break; 311 | } 312 | 313 | 314 | return rest_ensure_response( wp_parse_args( $response, [ 315 | 'success' => true 316 | ] ) ); 317 | 318 | } 319 | 320 | /** 321 | * Pre-process the post content 322 | * 323 | * @param $slug string 324 | * 325 | * @return WP_Error|bool 326 | */ 327 | public function _install_plugin( $slug ) { 328 | 329 | include_once ABSPATH . 'wp-admin/includes/plugin.php'; 330 | 331 | foreach ( get_plugins() as $path => $details ) { 332 | 333 | if ( false === strpos( $path, sprintf( '/%s.php', $slug ) ) ) { 334 | continue; 335 | } 336 | 337 | $activate = activate_plugin( $path ); 338 | 339 | if ( is_wp_error( $activate ) ) { 340 | return $activate; 341 | } 342 | 343 | return true; 344 | } 345 | 346 | include_once ABSPATH . 'wp-admin/includes/plugin-install.php'; 347 | include_once ABSPATH . 'wp-admin/includes/file.php'; 348 | include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; 349 | 350 | // Use the WordPress Plugins API to get the plugin download link. 351 | $api = plugins_api( 352 | 'plugin_information', 353 | array( 354 | 'slug' => $slug, 355 | ) 356 | ); 357 | 358 | if ( is_wp_error( $api ) ) { 359 | return $api; 360 | } 361 | 362 | // Use the AJAX upgrader skin to quietly install the plugin. 363 | $upgrader = new \Plugin_Upgrader( new \WP_Ajax_Upgrader_Skin() ); 364 | $install = $upgrader->install( $api->download_link ); 365 | 366 | if ( is_wp_error( $install ) ) { 367 | return $install; 368 | } 369 | 370 | $activate = activate_plugin( $upgrader->plugin_info() ); 371 | 372 | if ( is_wp_error( $activate ) ) { 373 | return $activate; 374 | } 375 | 376 | return true; 377 | } 378 | 379 | /** 380 | * Submit a form for a specific popup 381 | * 382 | * @param WP_REST_Request $request 383 | * 384 | * @return WP_REST_Response|WP_Error 385 | */ 386 | public function submit( WP_REST_Request $request ) { 387 | 388 | $id = absint( $request->get_param( 'popup_id' ) ); 389 | 390 | $popup = new Holler_Popup( $id ); 391 | 392 | if ( ! $popup->exists() ) { 393 | return self::ERROR_404(); 394 | } 395 | 396 | $lead = new Holler_Lead( $request ); 397 | 398 | $response = $popup->submit( $lead ); 399 | 400 | if ( is_wp_error( $response ) ) { 401 | return rest_ensure_response( $response ); 402 | } 403 | 404 | if ( $response['status'] === 'success' ) { 405 | $this->track_conversion( $request ); 406 | } 407 | 408 | return rest_ensure_response( $response ); 409 | } 410 | 411 | /** 412 | * Update a popup with some specific settings 413 | * 414 | * @param WP_REST_Request $request 415 | * 416 | * @return WP_REST_Response|WP_Error 417 | */ 418 | public function read( WP_REST_Request $request ) { 419 | 420 | $id = absint( $request->get_param( 'popup_id' ) ); 421 | 422 | $popup = new Holler_Popup( $id ); 423 | 424 | if ( ! $popup->exists() ) { 425 | return self::ERROR_404(); 426 | } 427 | 428 | return rest_ensure_response( $popup ); 429 | } 430 | 431 | /** 432 | * Update a popup with some specific settings 433 | * 434 | * @param WP_REST_Request $request 435 | * 436 | * @return WP_REST_Response|WP_Error 437 | */ 438 | public function update( WP_REST_Request $request ) { 439 | 440 | $id = absint( $request->get_param( 'popup_id' ) ); 441 | 442 | $popup = new Holler_Popup( $id ); 443 | 444 | if ( ! $popup->exists() ) { 445 | return self::ERROR_404(); 446 | } 447 | 448 | $new_settings = $request->get_json_params(); 449 | 450 | $result = $popup->update( $new_settings ); 451 | 452 | if ( is_wp_error( $result ) ) { 453 | return $result; 454 | } 455 | 456 | return rest_ensure_response( $popup ); 457 | } 458 | 459 | /** 460 | * Delete a popup 461 | * 462 | * @param WP_REST_Request $request 463 | * 464 | * @return WP_Error|WP_REST_Response 465 | */ 466 | public function delete( WP_REST_Request $request ) { 467 | $id = absint( $request->get_param( 'popup_id' ) ); 468 | 469 | $popup = new Holler_Popup( $id ); 470 | 471 | if ( ! $popup->exists() ) { 472 | return self::ERROR_404(); 473 | } 474 | 475 | $popup->delete(); 476 | 477 | return rest_ensure_response( [ 'success' => true ] ); 478 | } 479 | 480 | /** 481 | * Delete a popup 482 | * 483 | * @param WP_REST_Request $request 484 | * 485 | * @return WP_Error|WP_REST_Response 486 | */ 487 | public function update_settings( WP_REST_Request $request ) { 488 | $settings = $request->get_param( 'settings' ); 489 | 490 | foreach ( $settings as $option_name => $option_value ) { 491 | Holler_Settings::instance()->update( $option_name, $option_value, false ); 492 | } 493 | 494 | Holler_Settings::instance()->commit(); 495 | 496 | return rest_ensure_response( [ 'success' => true ] ); 497 | } 498 | 499 | /** 500 | * Get options for the display condition pickers 501 | * 502 | * @param WP_REST_Request $request 503 | * 504 | * @return WP_Error|WP_HTTP_Response|WP_REST_Response 505 | */ 506 | public function read_options( WP_REST_Request $request ) { 507 | 508 | $search = $request->get_param( 'search' ); 509 | $options = []; 510 | 511 | if ( $post_type = $request->get_param( 'post_type' ) ) { 512 | 513 | $posts = get_posts( [ 514 | 'numberposts' => 30, 515 | 'post_type' => $post_type, 516 | 's' => $search, 517 | ] ); 518 | 519 | $options = array_map( function ( $post ) { 520 | return [ 'id' => $post->ID, 'text' => $post->post_title ]; 521 | }, $posts ); 522 | } 523 | 524 | if ( $taxonomy = $request->get_param( 'taxonomy' ) ) { 525 | 526 | $terms = get_terms( [ 527 | 'taxonomy' => $taxonomy, 528 | 'hide_empty' => false, 529 | 'search' => $search, 530 | 'number' => 30 531 | ] ); 532 | 533 | $options = array_map( function ( $term ) { 534 | return [ 'id' => $term->term_id, 'text' => $term->name ]; 535 | }, $terms ); 536 | } 537 | 538 | return rest_ensure_response( $options ); 539 | 540 | } 541 | 542 | public function permission_callback() { 543 | return current_user_can( 'edit_popups' ); 544 | } 545 | 546 | public function options_permission_callback() { 547 | return current_user_can( 'manage_options' ); 548 | } 549 | 550 | public function plugins_permission_callback() { 551 | return current_user_can( 'install_plugins' ); 552 | } 553 | 554 | } 555 | -------------------------------------------------------------------------------- /includes/class-holler-frontend.php: -------------------------------------------------------------------------------- 1 | is_builder_preview() ): 37 | ?> 38 | 39 | 43 | 56 | 61 |
    62 | active as $popup ): 63 | 64 | $popup->maybe_output_content(); 65 | 66 | endforeach; ?> 67 |
    68 | get( 'script_debug_mode' ) ? '' : '.min'; 76 | 77 | wp_enqueue_style( 'hollerbox-popups', Holler_Box_URL . 'assets/css/popups.css', [], HOLLERBOX_VERSION ); 78 | wp_enqueue_script( 'hollerbox-popups', Holler_Box_URL . 'assets/js/popups' . $dot_min . '.js', [], HOLLERBOX_VERSION, true ); 79 | 80 | $cookie_lifetime = strtotime( '+' . Holler_Settings::instance()->get( 'cookie_lifetime', 1 ) . ' ' . Holler_Settings::instance()->get( 'cookie_lifetime_period', 'months' ), 0 ); 81 | 82 | $l10n = [ 83 | 'cookie_lifetime' => $cookie_lifetime, 84 | 'active' => array_map( function ( $popup ) { 85 | $popup = $popup->jsonSerialize(); 86 | 87 | // Remove secret properties 88 | unset( $popup['integrations'] ); 89 | 90 | return $popup; 91 | }, $this->active ), 92 | 'home_url' => home_url(), 93 | 'nav' => [ 94 | 'home' => home_url(), 95 | 'login' => wp_login_url(), 96 | 'lost_password' => wp_lostpassword_url(), 97 | 'register' => wp_registration_url(), 98 | 'privacy_page' => get_privacy_policy_url(), 99 | ], 100 | 'is_preview' => is_preview(), 101 | 'is_frontend' => ! is_admin(), 102 | 'is_builder_preview' => $this->is_builder_preview(), 103 | 'is_user_logged_in' => is_user_logged_in(), 104 | 'routes' => [ 105 | 'root' => rest_url( 'hollerbox' ), 106 | 'conversion' => rest_url( 'hollerbox/conversion' ), 107 | 'impression' => rest_url( 'hollerbox/impression' ), 108 | 'submit' => rest_url( 'hollerbox/submit' ), 109 | 'closed' => rest_url( 'hollerbox/closed' ), 110 | ], 111 | 'nonces' => [ 112 | '_wprest' => wp_create_nonce( 'wp_rest' ) 113 | ], 114 | 'settings' => Holler_Settings::instance()->get( [ 115 | 'credit_disabled', 116 | 'gdpr_enabled', 117 | 'gdpr_text', 118 | 'cookie_compliance', 119 | 'cookie_name', 120 | 'cookie_value', 121 | 'script_debug_mode', 122 | 'stacked_delay' 123 | ], [ 124 | 'credit_disabled' => false, 125 | 'gdpr_enabled' => false, 126 | 'gdpr_text' => '', 127 | 'cookie_compliance' => false, 128 | 'cookie_name' => 'viewed_cookie_policy', 129 | 'cookie_value' => 'yes', 130 | 'stacked_delay' => 5, 131 | ] ) 132 | ]; 133 | 134 | if ( defined( 'GROUNDHOGG_VERSION' ) ){ 135 | $l10n['is_current_contact'] = \Groundhogg\is_a_contact( \Groundhogg\get_current_contact() ); 136 | } 137 | 138 | do_action( 'hollerbox/scripts', $this ); 139 | do_action( 'hollerbox/frontend/scripts', $this ); 140 | 141 | wp_add_inline_script( 'hollerbox-popups', "HollerBox = " . wp_json_encode( $l10n ), 'before' ); 142 | 143 | } 144 | 145 | /** 146 | * Add popup classes to the main body 147 | * 148 | * @param $classes 149 | * 150 | * @return array|mixed 151 | */ 152 | public function body_classes( $classes ) { 153 | 154 | foreach ( $this->active as $popup ) { 155 | $classes = array_merge( $classes, $popup->get_body_classes() ); 156 | } 157 | 158 | return $classes; 159 | } 160 | 161 | /** 162 | * @param $popup Holler_Popup 163 | */ 164 | protected function add_active( $popup ) { 165 | $this->active[] = $popup; 166 | } 167 | 168 | /** 169 | * There are active popups on this page 170 | * 171 | * @return bool 172 | */ 173 | public function has_active() { 174 | return ! empty( $this->active ); 175 | } 176 | 177 | /** 178 | * Find which popups are active for the current request 179 | * 180 | * @param $query 181 | */ 182 | public function get_active_popups() { 183 | 184 | // Do not run in admin 185 | if ( is_admin() ) { 186 | return; 187 | } 188 | 189 | if ( $this->is_builder_preview() ) { 190 | 191 | // hide admin bar 192 | add_filter( 'show_admin_bar', '__return_false' ); 193 | 194 | // prevent query monitor output in preview 195 | add_filter( 'qm/process', '__return_false' ); 196 | 197 | // Suppress popups from being displayed 198 | return; 199 | } 200 | 201 | $args = [ 'post_type' => 'hollerbox', 'posts_per_page' => - 1, 'post_status' => 'publish' ]; 202 | 203 | // The Query 204 | $the_query = new WP_Query( $args ); 205 | $popups = []; 206 | 207 | // The Loop 208 | if ( $the_query->have_posts() ) { 209 | 210 | while ( $the_query->have_posts() ) { 211 | $the_query->the_post(); 212 | $id = get_the_id(); 213 | 214 | $popup = new Holler_Popup( $id ); 215 | 216 | $popups[] = $popup; 217 | } 218 | 219 | /* Restore original Post Data */ 220 | wp_reset_postdata(); 221 | } 222 | 223 | // only show active popups for the current query 224 | foreach ( $popups as $popup ) { 225 | if ( $popup->can_show() ) { 226 | $this->add_active( $popup ); 227 | } 228 | } 229 | 230 | if ( $this->has_active() && ! $this->is_builder_preview() ) { 231 | add_action( 'admin_bar_menu', [ $this, 'holler_box_menu_item' ], 99 ); 232 | } 233 | 234 | } 235 | 236 | public static function HollerIcon( $props = [] ) { 237 | 238 | $props = wp_parse_args( $props, [ 239 | 'width' => '20px', 240 | 'height' => '20px', 241 | ] ); 242 | 243 | return ' 244 | 245 | 246 | 247 | 249 | '; 250 | } 251 | 252 | /** 253 | * Add HollerBox menu item to admin bar 254 | * 255 | * @param $wp_admin_bar WP_Admin_Bar 256 | * 257 | * @return void 258 | */ 259 | public function holler_box_menu_item( $wp_admin_bar ) { 260 | 261 | if ( ! $this->has_active() ) { 262 | return; 263 | } 264 | 265 | $wp_admin_bar->add_node( [ 266 | 'id' => 'manage-hollerbox', 267 | 'title' => self::HollerIcon( [ 268 | 'style' => [ 269 | 'padding-top' => '5px' 270 | ] 271 | ] ) 272 | ] ); 273 | 274 | foreach ( $this->active as $popup ) { 275 | 276 | $wp_admin_bar->add_node( [ 277 | 'parent' => 'manage-hollerbox', 278 | 'id' => 'holler-' . $popup->ID, 279 | 'title' => $popup->post_title, 280 | 'href' => get_edit_post_link( $popup->ID ) 281 | ] ); 282 | 283 | } 284 | 285 | } 286 | 287 | /** 288 | * Convert array to HTML tag attributes 289 | * 290 | * @param $atts 291 | * 292 | * @return string 293 | */ 294 | static function array_to_atts( $atts ) { 295 | $tag = ''; 296 | 297 | if ( ! is_array( $atts ) ) { 298 | return ''; 299 | } 300 | 301 | foreach ( $atts as $key => $value ) { 302 | 303 | if ( empty( $value ) ) { 304 | continue; 305 | } 306 | 307 | $key = strtolower( $key ); 308 | 309 | switch ( $key ) { 310 | case 'style': 311 | $value = self::array_to_css( $value ); 312 | break; 313 | case 'href': 314 | case 'action': 315 | case 'src': 316 | $value = strpos( $value, 'data:image/png;base64,' ) === false ? esc_url( $value ) : $value; 317 | break; 318 | default: 319 | if ( is_array( $value ) ) { 320 | $value = implode( ' ', $value ); 321 | } 322 | 323 | $value = esc_attr( $value ); 324 | break; 325 | 326 | } 327 | 328 | $tag .= sanitize_key( $key ) . '="' . $value . '" '; 329 | } 330 | 331 | return $tag; 332 | } 333 | 334 | /** 335 | * Convert array to CSS style attributes 336 | * 337 | * @param $atts 338 | * 339 | * @return string 340 | */ 341 | static function array_to_css( $atts ) { 342 | 343 | if ( ! is_array( $atts ) ) { 344 | return $atts; 345 | } 346 | 347 | $css = ''; 348 | foreach ( $atts as $key => $value ) { 349 | 350 | if ( is_array( $value ) ) { 351 | $value = implode( ' ', $value ); 352 | } 353 | 354 | $css .= sanitize_key( $key ) . ':' . esc_attr( $value ) . ';'; 355 | } 356 | 357 | return $css; 358 | } 359 | 360 | 361 | } 362 | -------------------------------------------------------------------------------- /includes/class-holler-integrations.php: -------------------------------------------------------------------------------- 1 | init(); 18 | } 19 | } 20 | 21 | /** 22 | * Registry of callbacks for specific integrations 23 | * 24 | * @var callable[] 25 | */ 26 | private static $integrations = []; 27 | 28 | /** 29 | * Register an integration handler 30 | * 31 | * @param $id string 32 | * @param $callback callable 33 | */ 34 | public static function register( $id, $callback ) { 35 | 36 | if ( ! is_callable( $callback ) ) { 37 | return; 38 | } 39 | 40 | self::$integrations[ $id ] = $callback; 41 | } 42 | 43 | /** 44 | * Do an integration 45 | * 46 | * @param $integration array 47 | * @param $lead Holler_Lead 48 | * @param $popup Holler_Popup 49 | * 50 | * @return true|WP_Error false if the provided integration is not registered 51 | */ 52 | public static function _do( $integration, $lead, $popup ) { 53 | 54 | // Setup the instance and init integrations 55 | if ( empty( self::$integrations ) ) { 56 | self::instance(); 57 | } 58 | 59 | $type = $integration['type']; 60 | 61 | if ( ! isset( self::$integrations[ $type ] ) ) { 62 | return new WP_Error( 'unknown_integration', 'Unknown integration type ' . $type ); 63 | } 64 | 65 | /** 66 | * Action before the integration 67 | * 68 | * @param $lead Holler_Lead 69 | * @param $popup Holler_Popup 70 | */ 71 | do_action( "hollerbox/integrations/$type/before", $lead, $popup ); 72 | 73 | // Do the integration 74 | $result = call_user_func( self::$integrations[ $type ], $integration, $lead, $popup ); 75 | 76 | /** 77 | * Action after the integration 78 | * 79 | * @param $lead Holler_Lead 80 | * @param $popup Holler_Popup 81 | */ 82 | do_action( "hollerbox/integrations/$type/after", $lead, $popup ); 83 | 84 | return $result; 85 | } 86 | 87 | /** 88 | * Register the basic integration types 89 | */ 90 | public function init() { 91 | 92 | self::register( 'email', [ $this, 'email' ] ); 93 | self::register( 'groundhogg', [ $this, 'groundhogg' ] ); 94 | self::register( 'webhook', [ $this, 'webhook' ] ); 95 | self::register( 'zapier', [ $this, 'zapier' ] ); 96 | 97 | do_action( 'hollerbox/register_integrations' ); 98 | } 99 | 100 | /** 101 | * @var WP_Error 102 | */ 103 | protected $wp_mail_error; 104 | 105 | /** 106 | * Catch email failed error 107 | * 108 | * @param $wp_error 109 | * 110 | * @return void 111 | */ 112 | public function catch_mail_failed( $wp_error ) { 113 | $this->wp_mail_error = $wp_error; 114 | } 115 | 116 | /** 117 | * Process the email integration 118 | * 119 | * @param $props array 120 | * @param $lead Holler_Lead 121 | * 122 | * @return bool|mixed|void 123 | */ 124 | public function email( $props, $lead ) { 125 | 126 | $props = wp_parse_args( $props, [ 127 | 'to' => [], 128 | 'from' => '', 129 | 'reply_to' => '', 130 | 'subject' => '', 131 | 'content' => '', 132 | ] ); 133 | 134 | $to = array_map( function ( $email ) use ( $lead ) { 135 | return $email === '{{email}}' ? $lead->get_email() : $email; 136 | }, $props['to'] ); 137 | 138 | $to = array_filter( $to, function ( $email ) { 139 | return is_email( $email ); 140 | } ); 141 | 142 | $replacements = [ 143 | '{{email}}' => $lead->email, 144 | '{{name}}' => $lead->name, 145 | '{{full_name}}' => $lead->name, 146 | '{{first_name}}' => $lead->first_name, 147 | '{{last_name}}' => $lead->last_name, 148 | '{{phone}}' => $lead->phone, 149 | '{{location}}' => $lead->location, 150 | '{{referrer}}' => $lead->referrer, 151 | '{{ip_address}}' => $lead->get_ip(), 152 | '{{message}}' => $lead->get_message_formatted(), 153 | ]; 154 | 155 | /** 156 | * Add additional replacements 157 | * 158 | * @param $replacements array 159 | * @param $props array 160 | * @param $lead Holler_Lead 161 | */ 162 | $replacements = apply_filters( 'hollerbox/integrations/email/replacements', $replacements, $props, $lead ); 163 | 164 | $message = wp_kses_post( $props['content'] ); 165 | $message = str_replace( array_keys( $replacements ), array_values( $replacements ), $message ); 166 | 167 | $subject = sanitize_text_field( $props['subject'] ); 168 | $subject = str_replace( array_keys( $replacements ), array_values( $replacements ), $subject ); 169 | 170 | $headers = [ 171 | 'Content-Type: text/html', 172 | ]; 173 | 174 | $reply_to = str_replace( '{{email}}', $lead->email, $props['reply_to'] ); 175 | 176 | if ( is_email( $reply_to ) ) { 177 | $headers[] = sprintf( 'Reply-to: %s', $reply_to ); 178 | } 179 | 180 | if ( is_email( $props['from'] ) ) { 181 | $headers[] = sprintf( 'From: %s', $props['from'] ); 182 | } 183 | 184 | add_action( 'wp_mail_failed', [ $this, 'catch_mail_failed' ] ); 185 | 186 | $result = wp_mail( 187 | $to, 188 | $subject, 189 | $message, 190 | $headers 191 | ); 192 | 193 | remove_action( 'wp_mail_failed', [ $this, 'catch_mail_failed' ] ); 194 | 195 | return $result ?: $this->wp_mail_error; 196 | } 197 | 198 | /** 199 | * Process the webhook integration 200 | * 201 | * @param $props array 202 | * @param $lead Holler_Lead 203 | * @param $popup Holler_Popup 204 | * 205 | * @return bool|mixed|void 206 | */ 207 | public function webhook( $props, $lead, $popup ) { 208 | 209 | $props = wp_parse_args( $props, [ 210 | 'url' => '', 211 | 'method' => 'post', 212 | 'payload' => 'json', 213 | ] ); 214 | 215 | $headers = []; 216 | 217 | $body = [ 218 | 'full_name' => $lead->get_name(), 219 | 'first_name' => $lead->get_first_name(), 220 | 'last_name' => $lead->get_last_name(), 221 | 'phone' => $lead->get_phone(), 222 | 'email' => $lead->get_email(), 223 | 'ip4' => $lead->get_ip(), 224 | 'gdpr_consent' => $lead->gdpr_consent, 225 | ]; 226 | 227 | if ( $lead->message ) { 228 | $body['message'] = $lead->message; 229 | } 230 | 231 | /** 232 | * Filter the outgoing webhook body 233 | * 234 | * @param $body array 235 | * @param $props array 236 | * @param $lead Holler_Lead 237 | * @param $popup Holler_Popup 238 | */ 239 | $body = apply_filters( 'hollerbox/integrations/webhook/body', $body, $props, $lead, $popup ); 240 | 241 | if ( $props['method'] === 'get' ) { 242 | return wp_remote_get( add_query_arg( $body, $props['url'] ), [ 243 | 'sslverify' => is_ssl(), 244 | 'user-agent' => 'HollerBox/' . HOLLERBOX_VERSION . '; ' . home_url() 245 | ] ); 246 | } 247 | 248 | if ( $props['payload'] === 'json' ) { 249 | $body = wp_json_encode( $body ); 250 | $headers['Content-type'] = sprintf( 'application/json; charset=%s', get_bloginfo( 'charset' ) ); 251 | } 252 | 253 | $response = wp_remote_request( $props['url'], [ 254 | 'method' => strtoupper( $props['method'] ), 255 | 'body' => $body, 256 | 'headers' => $headers, 257 | 'data_format' => 'body', 258 | 'sslverify' => is_ssl(), 259 | 'user-agent' => 'HollerBox/' . HOLLERBOX_VERSION . '; ' . home_url() 260 | ] ); 261 | 262 | if ( is_wp_error( $response ) ) { 263 | return $response; 264 | } 265 | 266 | return true; 267 | } 268 | 269 | /** 270 | * Process the Zapier integration 271 | * 272 | * @param $props array 273 | * @param $lead Holler_Lead 274 | * @param $popup Holler_Popup 275 | * 276 | * @return bool|mixed|void 277 | */ 278 | public function zapier( $props, $lead, $popup ) { 279 | 280 | $props = wp_parse_args( $props, [ 281 | 'url' => '', 282 | ] ); 283 | 284 | $props['method'] = 'POST'; 285 | $props['payload'] = 'json'; 286 | 287 | return $this->webhook( $props, $lead, $popup ); 288 | } 289 | 290 | /** 291 | * Process the groundhogg integration 292 | * 293 | * @param $props array 294 | * @param $lead Holler_Lead 295 | * 296 | * @return bool|mixed|void 297 | */ 298 | public function groundhogg( $props, $lead ) { 299 | 300 | if ( ! defined( 'GROUNDHOGG_VERSION' ) ) { 301 | return false; 302 | } 303 | 304 | $contact = new \Groundhogg\Contact( $lead->email ); 305 | 306 | if ( ! $contact->exists() ) { 307 | $contact->create( [ 308 | 'first_name' => $lead->first_name, 309 | 'last_name' => $lead->last_name, 310 | 'email' => $lead->email, 311 | ] ); 312 | } else { 313 | $contact->update( array_filter( [ 314 | 'first_name' => $lead->first_name, 315 | 'last_name' => $lead->last_name, 316 | ] ) ); 317 | } 318 | 319 | $contact->add_tag( map_deep( $props['tags'], 'sanitize_text_field' ) ); 320 | 321 | // Gave consent 322 | if ( $lead->gdpr_consent ) { 323 | $contact->set_marketing_consent(); 324 | $contact->set_gdpr_consent(); 325 | $contact->set_terms_agreement(); 326 | } 327 | 328 | if ( $lead->message ) { 329 | $contact->update_meta( 'hollerbox_chat_message', $lead->message ); 330 | } 331 | 332 | if ( $lead->phone ) { 333 | $contact->update_meta( 'primary_phone', $lead->phone ); 334 | } 335 | 336 | \Groundhogg\after_form_submit_handler( $contact ); 337 | 338 | $contact->update_meta( 'source_page', $lead->location ); 339 | 340 | return true; 341 | } 342 | 343 | /** 344 | * Create a new user integration 345 | * 346 | * @param $props 347 | * @param $lead 348 | * 349 | * @return bool 350 | */ 351 | public function user( $props, $lead ) { 352 | 353 | $props = wp_parse_args( $props, [ 354 | 'role' => 'subscriber', 355 | 'format' => 'email', 356 | 'login' => false 357 | ] ); 358 | 359 | $email_address = $lead->get_email(); 360 | 361 | $password = wp_generate_password(); 362 | 363 | $role = $props['role']; 364 | 365 | // Email already exists... 366 | if ( email_exists( $email_address ) ) { 367 | return false; 368 | } 369 | 370 | switch ( $props['format'] ) { 371 | default: 372 | case 'email_address': 373 | $username = $lead->get_email(); 374 | break; 375 | case 'first_last': 376 | $username = strtolower( sprintf( "%s_%s", $lead->get_first_name(), $lead->get_last_name() ) ); 377 | break; 378 | case 'last_first': 379 | $username = strtolower( sprintf( "%s_%s", $lead->get_last_name(), $lead->get_first_name() ) ); 380 | break; 381 | } 382 | 383 | // More or less guaranteed unique at this point. 384 | $username = $this->generate_unique_username( $username ); 385 | 386 | $user_id = wp_create_user( $username, $password, $email_address ); 387 | $user = new \WP_User( $user_id ); 388 | $user->set_role( $role ); 389 | 390 | $user->first_name = $lead->get_first_name(); 391 | $user->last_name = $lead->get_last_name(); 392 | 393 | wp_update_user( $user ); 394 | 395 | wp_new_user_notification( $user_id, null, 'user' ); 396 | 397 | return true; 398 | 399 | } 400 | 401 | /** 402 | * Ensure a username is unique by checking if it is already taken, and if it is adding a unique string after it. 403 | * 404 | * @param string $username 405 | * @param bool $known_exists to avoid double-checking the same username during every recursion 406 | * 407 | * @return string 408 | */ 409 | private function generate_unique_username( $username, $known_exists = false ) { 410 | 411 | $username = sanitize_user( $username ); 412 | 413 | if ( ! $known_exists && ! username_exists( $username ) ) { 414 | return $username; 415 | } 416 | 417 | $new_username = uniqid( $username . '_' ); 418 | 419 | if ( ! username_exists( $new_username ) ) { 420 | return $new_username; 421 | } else { 422 | return $this->generate_unique_username( $username, true ); 423 | } 424 | } 425 | 426 | } 427 | 428 | -------------------------------------------------------------------------------- /includes/class-holler-lead.php: -------------------------------------------------------------------------------- 1 | name = sanitize_text_field( $request->get_param( 'name' ) ); 26 | 27 | $parts = explode( ' ', $this->name ); 28 | $this->first_name = trim( $parts[0] ); 29 | 30 | if ( isset( $parts[1] ) ) { 31 | $this->last_name = trim( $parts[1] ); 32 | } 33 | 34 | $this->email = sanitize_email( strtolower( $request->get_param( 'email' ) ) ); 35 | 36 | if ( is_user_logged_in() ) { 37 | 38 | $user = wp_get_current_user(); 39 | 40 | if ( ! $this->email ) { 41 | $this->email = $user->user_email; 42 | } 43 | 44 | if ( ! $this->first_name ) { 45 | $this->first_name = $user->first_name; 46 | } 47 | 48 | if ( ! $this->last_name ) { 49 | $this->last_name = $user->last_name; 50 | } 51 | } 52 | 53 | if ( defined( 'GROUNDHOGG_VERSION' ) && \Groundhogg\is_a_contact( \Groundhogg\get_current_contact() ) ) { 54 | 55 | $contact = \Groundhogg\get_current_contact(); 56 | 57 | if ( ! $this->email ) { 58 | $this->email = $contact->get_email(); 59 | } 60 | 61 | if ( ! $this->first_name ) { 62 | $this->first_name = $contact->get_first_name(); 63 | } 64 | 65 | if ( ! $this->last_name ) { 66 | $this->last_name = $contact->get_last_name(); 67 | } 68 | } 69 | 70 | $this->phone = sanitize_text_field( $request->get_param( 'phone' ) ); 71 | $this->location = sanitize_text_field( $request->get_param( 'location' ) ); 72 | $this->referrer = sanitize_text_field( $request->get_param( 'referer' ) ); 73 | $this->gdpr_consent = $request->get_param( 'gdpr_consent' ) === 'yes'; 74 | $this->message = sanitize_textarea_field( $request->get_param( 'message' ) ); 75 | $this->request = $request; 76 | 77 | /** 78 | * When a lead is initialized 79 | * 80 | * @param $lead Holler_Lead 81 | * @param $request WP_REST_Request 82 | */ 83 | do_action( 'hollerbox/lead_init', $this, $request ); 84 | } 85 | 86 | /** 87 | * Get the lead's email address 88 | * 89 | * @return string 90 | */ 91 | public function get_email() { 92 | return $this->email; 93 | } 94 | 95 | /** 96 | * Get the lead's name 97 | * 98 | * @return string 99 | */ 100 | public function get_name() { 101 | return $this->name; 102 | } 103 | 104 | /** 105 | * Get the first name 106 | * 107 | * @return string 108 | */ 109 | public function get_first_name() { 110 | return $this->first_name; 111 | } 112 | 113 | /** 114 | * Get the last name 115 | * 116 | * @return string 117 | */ 118 | public function get_last_name() { 119 | return $this->last_name; 120 | } 121 | 122 | /** 123 | * Get the phone number 124 | * 125 | * @return string 126 | */ 127 | public function get_phone() { 128 | return $this->phone; 129 | } 130 | 131 | /** 132 | * Get the chat message 133 | * 134 | * @return string 135 | */ 136 | public function get_message_formatted() { 137 | return wpautop( $this->message ); 138 | } 139 | 140 | /** 141 | * Get the leads IP address 142 | * 143 | * @return string 144 | */ 145 | public function get_ip() { 146 | if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) //check ip from share internet 147 | { 148 | $ip = $_SERVER['HTTP_CLIENT_IP']; 149 | } else if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) //to check ip is pass from proxy 150 | { 151 | $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; 152 | } else { 153 | $ip = $_SERVER['REMOTE_ADDR']; 154 | } 155 | 156 | return $ip; 157 | } 158 | 159 | #[ReturnTypeWillChange] 160 | public function offsetExists( $offset ) { 161 | return isset( $this->$offset ) || isset( $this->data[ $offset ] ); 162 | } 163 | 164 | #[ReturnTypeWillChange] 165 | public function offsetGet( $offset ) { 166 | if ( property_exists( $this, $offset ) ) { 167 | return $this->$offset; 168 | } 169 | 170 | return $this->data[ $offset ]; 171 | } 172 | 173 | #[ReturnTypeWillChange] 174 | public function offsetSet( $offset, $value ) { 175 | if ( property_exists( $this, $offset ) ) { 176 | $this->$offset = $value; 177 | } 178 | 179 | $this->data[ $offset ] = $value; 180 | } 181 | 182 | #[ReturnTypeWillChange] 183 | public function offsetUnset( $offset ) { 184 | if ( property_exists( $this, $offset ) ) { 185 | return; 186 | } 187 | 188 | unset( $this->data[ $offset ] ); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /includes/class-holler-licensing.php: -------------------------------------------------------------------------------- 1 | WP_REST_Server::CREATABLE, 28 | 'callback' => [ $this, 'activate' ], 29 | 'permission_callback' => [ $this, 'permission_callback' ] 30 | ], 31 | [ 32 | 'methods' => WP_REST_Server::DELETABLE, 33 | 'callback' => [ $this, 'deactivate' ], 34 | 'permission_callback' => [ $this, 'permission_callback' ] 35 | ], 36 | ] ); 37 | 38 | } 39 | 40 | /** 41 | * @since 0.1.0 42 | * @var Holler_Licensing $instance 43 | */ 44 | private static $instance; 45 | 46 | /** 47 | * Get active instance 48 | * 49 | * @access public 50 | * @since 0.1.0 51 | * @return self The one true Holler_Box 52 | */ 53 | public static function instance() { 54 | if ( ! self::$instance ) { 55 | self::$instance = new Holler_Licensing(); 56 | } 57 | 58 | return self::$instance; 59 | } 60 | 61 | /** 62 | * Create an EDD updater for the pro version 63 | * 64 | * @param $file 65 | * @param $version 66 | * 67 | * @return Holler_EDD_SL_Plugin_Updater 68 | */ 69 | public function edd_updater( $file, $version ){ 70 | 71 | // EDD Stuff 72 | return new \Holler_EDD_SL_Plugin_Updater( self::STORE_URL, $file, [ 73 | 'version' => $version, 74 | 'license' => Holler_Settings::instance()->get( 'license' ), 75 | 'item_id' => $this->get_pro_item_id(), 76 | 'url' => home_url(), 77 | 'author' => 'Groundhogg Inc.' 78 | ] ); 79 | } 80 | 81 | /** 82 | * Get the ID of the item to license in the store 83 | * 84 | * @return mixed|void 85 | */ 86 | public function get_pro_item_id(){ 87 | return apply_filters( 'hollerbox/pro_item_id', self::PRO_ITEM_ID ); 88 | } 89 | 90 | /** 91 | * Get the error message for a given error. 92 | * 93 | * @param $error 94 | * @param false $expiry 95 | * 96 | * @return string 97 | */ 98 | protected function get_license_error_message( $error, $expiry = false ) { 99 | 100 | switch ( $error ) { 101 | case 'expired' : 102 | $message = sprintf( 103 | _x( 'Your license key expired on %s.', 'notice', 'groundhogg' ), 104 | date_i18n( get_option( 'date_format' ), strtotime( $expiry, current_time( 'timestamp' ) ) ) 105 | ); 106 | break; 107 | case 'invalid' : 108 | case 'disabled' : 109 | $message = _x( 'Your license key has been disabled.', 'notice', 'groundhogg' ); 110 | break; 111 | case 'site_inactive' : 112 | $message = _x( 'Your license is not active for this URL.', 'notice', 'groundhogg' ); 113 | break; 114 | case 'key_mismatch' : 115 | case 'invalid_item_id' : 116 | case 'item_name_mismatch' : 117 | $message = sprintf( _x( 'The extension you are licensing is unrecognized.', 'notice', 'groundhogg' ) ); 118 | break; 119 | case 'missing_url' : 120 | case 'missing' : 121 | $message = sprintf( _x( 'This appears to be an invalid license key.', 'notice', 'groundhogg' ) ); 122 | break; 123 | case 'no_activations_left': 124 | $message = _x( 'Your license key has reached its activation limit.', 'notice', 'groundhogg' ); 125 | break; 126 | default : 127 | $message = _x( 'An error occurred, please try again.', 'notice', 'groundhogg' ); 128 | break; 129 | } 130 | 131 | return $message; 132 | } 133 | 134 | /** 135 | * Activate a new license key 136 | * 137 | * @return array|bool|WP_Error 138 | */ 139 | protected function _activate( $license ) { 140 | 141 | $existing_license = Holler_Settings::instance()->get( 'license' ); 142 | 143 | // Exiting license is the same as is being added, return true 144 | if ( $existing_license && $license === $existing_license ) { 145 | return true; 146 | } 147 | 148 | // A new license is being activated, let's deactivate the old one 149 | if ( $existing_license ) { 150 | $res = $this->_deactivate(); 151 | 152 | if ( is_wp_error( $res ) ) { 153 | return $res; 154 | } 155 | } 156 | 157 | $response = wp_remote_post( self::STORE_URL, [ 158 | 'body' => [ 159 | 'edd_action' => 'activate_license', 160 | 'item_id' => $this->get_pro_item_id(), 161 | 'license' => $license, 162 | 'url' => home_url(), 163 | ] 164 | ] ); 165 | 166 | if ( is_wp_error( $response ) ) { 167 | return $response; 168 | } 169 | 170 | $license_data = json_decode( wp_remote_retrieve_body( $response ) ); 171 | 172 | if ( false === $license_data->success ) { 173 | $message = self::get_license_error_message( $license_data->error, $license_data->expires ); 174 | 175 | return new WP_Error( $license_data->error, $message ); 176 | } 177 | 178 | $expiry = $license_data->expires; 179 | 180 | Holler_Settings::instance()->update( 'license', $license ); 181 | Holler_Settings::instance()->update( 'license_expiry', $expiry ); 182 | Holler_Settings::instance()->update( 'is_licensed', true ); 183 | 184 | return true; 185 | } 186 | 187 | /** 188 | * Deactivate the current license key 189 | * 190 | * @return array|bool|WP_Error 191 | */ 192 | protected function _deactivate() { 193 | 194 | $existing_license = Holler_Settings::instance()->get( 'license' ); 195 | 196 | if ( ! $existing_license ) { 197 | return true; 198 | } 199 | 200 | $response = wp_remote_post( self::STORE_URL, [ 201 | 'body' => [ 202 | 'edd_action' => 'deactivate_license', 203 | 'item_id' => $this->get_pro_item_id(), 204 | 'license' => $existing_license, 205 | 'url' => home_url(), 206 | ] 207 | ] ); 208 | 209 | if ( is_wp_error( $response ) ) { 210 | return $response; 211 | } 212 | 213 | Holler_Settings::instance()->update( 'is_licensed', false ); 214 | Holler_Settings::instance()->update( 'license', '' ); 215 | Holler_Settings::instance()->update( 'license_expiry', '' ); 216 | 217 | return true; 218 | } 219 | 220 | /** 221 | * Activate a license for the first time 222 | * 223 | * @param WP_REST_Request $request 224 | * 225 | * @return WP_REST_Response|WP_Error 226 | */ 227 | public function activate( WP_REST_Request $request ) { 228 | 229 | $license = sanitize_text_field( $request->get_param( 'license' ) ); 230 | 231 | $result = $this->_activate( $license ); 232 | 233 | if ( is_wp_error( $result ) ) { 234 | return $result; 235 | } 236 | 237 | return rest_ensure_response( [ 238 | 'success' => true, 239 | 'license_data' => [ 240 | 'is_licensed' => true, 241 | 'license' => Holler_Settings::instance()->get( 'license' ), 242 | 'license_expiry' => Holler_Settings::instance()->get( 'license_expiry' ), 243 | ] 244 | ] ); 245 | } 246 | 247 | /** 248 | * Deactivate the current license 249 | * 250 | * @param WP_REST_Request $request 251 | * 252 | * @return array|bool|WP_Error|WP_HTTP_Response|WP_REST_Response 253 | */ 254 | public function deactivate( WP_REST_Request $request ) { 255 | 256 | $result = $this->_deactivate(); 257 | 258 | if ( is_wp_error( $result ) ) { 259 | return $result; 260 | } 261 | 262 | return rest_ensure_response( [ 263 | 'success' => true, 264 | ] ); 265 | } 266 | 267 | 268 | public function permission_callback() { 269 | return current_user_can( 'manage_options' ); 270 | } 271 | 272 | } 273 | -------------------------------------------------------------------------------- /includes/class-holler-reporting.php: -------------------------------------------------------------------------------- 1 | table_name = $wpdb->prefix . 'hollerbox_stats'; 35 | 36 | add_action( 'deleted_post', [ $this, 'maybe_delete_stats' ], 10, 2 ); 37 | } 38 | 39 | /** 40 | * Delete associated stats from the reporting because we no longer need them 41 | * 42 | * @param $post_id 43 | * @param $post WP_Post 44 | * 45 | * @return void 46 | */ 47 | public function maybe_delete_stats( $post_id, $post ) { 48 | 49 | if ( $post->post_type !== 'hollerbox' ) { 50 | return; 51 | } 52 | 53 | global $wpdb; 54 | 55 | $wpdb->query( "DELETE FROM $this->table_name WHERE popup_id = $post_id" ); 56 | } 57 | 58 | /** 59 | * Create or upgrade the table depending on the table version 60 | * 61 | * @return void 62 | */ 63 | public function maybe_create_table() { 64 | $this->create_table(); 65 | } 66 | 67 | /** 68 | * Check if the stats table exists 69 | * 70 | * @since 2.4 71 | * 72 | * @param string $table The table name 73 | * 74 | * @return bool If the table name exists 75 | */ 76 | public function table_exists() { 77 | global $wpdb; 78 | 79 | return $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE '%s'", $this->table_name ) ) === $this->table_name; 80 | } 81 | 82 | /** 83 | * Drops the table 84 | */ 85 | public function drop() { 86 | 87 | if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) { 88 | exit; 89 | } 90 | 91 | delete_option( $this->table_name . '_table_version' ); 92 | 93 | global $wpdb; 94 | 95 | $wpdb->query( "DROP TABLE IF EXISTS " . $this->table_name ); 96 | } 97 | 98 | /** 99 | * Creates the table 100 | * 101 | * @return void 102 | */ 103 | public function create_table() { 104 | 105 | global $wpdb; 106 | 107 | require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); 108 | 109 | $charset_collate = $wpdb->get_charset_collate(); 110 | 111 | $max_index_length = $this->max_key_length(); 112 | 113 | $sql = "CREATE TABLE " . $this->table_name . " ( 114 | s_type varchar(10) NOT NULL, 115 | s_date date NOT NULL, 116 | s_count INT unsigned NOT NULL, 117 | popup_id bigint(20) unsigned NOT NULL, 118 | location varchar($max_index_length) NOT NULL, 119 | content varchar($max_index_length) NOT NULL, 120 | PRIMARY KEY (popup_id, s_type, s_date, location, content), 121 | KEY s_date (s_date), 122 | KEY s_type (s_type), 123 | KEY popup_id (popup_id), 124 | KEY content (content) 125 | ) $charset_collate ENGINE=InnoDB;"; 126 | 127 | dbDelta( $sql ); 128 | 129 | update_option( $this->table_name . '_table_version', self::TABLE_VERSION ); 130 | } 131 | 132 | /** 133 | * @throws Exception 134 | * 135 | * @param Holler_Popup $popup 136 | * @param string $location 137 | * @param string $content 138 | * 139 | * @param string $type 140 | * 141 | * @return void 142 | */ 143 | protected function increment_stat( string $type, Holler_Popup $popup, $location, $content = '' ) { 144 | global $wpdb; 145 | 146 | $date = new DateTime( 'now', wp_timezone() ); 147 | 148 | $wpdb->query( $wpdb->prepare( 149 | "INSERT INTO $this->table_name (s_type, s_count, popup_id, location, content, s_date) 150 | VALUES (%s, %d, %d, %s, %s, %s) 151 | ON DUPLICATE KEY UPDATE s_count = s_count + 1", 152 | $type, 1, $popup->ID, $location, $content, $date->format( 'Y-m-d' ) ) ); 153 | } 154 | 155 | /** 156 | * Increment the daily impression count for the given popup 157 | * 158 | * @param $popup Holler_Popup 159 | * @param $location string 160 | * 161 | * @return void 162 | */ 163 | public function add_impression( Holler_Popup $popup, string $location ) { 164 | $this->increment_stat( 'impression', $popup, $location ); 165 | } 166 | 167 | /** 168 | * Increment the daily conversion count for the given popup 169 | * 170 | * @param $popup Holler_Popup 171 | * @param $location string 172 | * @param $content string 173 | * 174 | * @return void 175 | */ 176 | public function add_conversion( Holler_Popup $popup, string $location, string $content ) { 177 | $this->increment_stat( 'conversion', $popup, $location, $content ); 178 | } 179 | 180 | /** 181 | * Get the impression count for a given interval 182 | * 183 | * @param $type string 184 | * @param $popup Holler_Popup 185 | * @param $interval DateInterval 186 | * 187 | * @return int 188 | */ 189 | public function get_total_popup_count_for_interval( string $type, Holler_Popup $popup, DateInterval $interval ) { 190 | 191 | $date = new DateTime( 'now', wp_timezone() ); 192 | $today = $date->format( 'Y-m-d' ); 193 | $date->sub( $interval ); 194 | $prev = $date->format( 'Y-m-d' ); 195 | 196 | return $this->_get_total_count_for_interval( [ 197 | 'popup_id' => $popup->ID, 198 | 'after' => $prev, 199 | 'before' => $today, 200 | 's_type' => $type 201 | ] ); 202 | } 203 | 204 | /** 205 | * Get the impression count for a given interval 206 | * 207 | * @param $query 208 | * 209 | * @return int 210 | */ 211 | public function _get_total_count_for_interval( $query = [] ) { 212 | 213 | global $wpdb; 214 | 215 | $cache_key = md5( wp_json_encode( $query ) ); 216 | 217 | $count = wp_cache_get( $cache_key, 'hollerbox:counts', null, $found ); 218 | 219 | if ( $found ) { 220 | return $count; 221 | } 222 | 223 | $default_date = new DateTime( '30 days ago', wp_timezone() ); 224 | 225 | $query = wp_parse_args( $query, [ 226 | 'before' => current_time( 'Y-m-d' ), 227 | 'after' => $default_date->format( 'Y-m-d' ) 228 | ] ); 229 | 230 | $clauses = []; 231 | 232 | foreach ( $query as $column => $value ) { 233 | 234 | if ( ! $this->is_valid_column( $column ) ) { 235 | continue; 236 | } 237 | 238 | switch ( $column ) { 239 | case 'before': 240 | $clauses[] = $wpdb->prepare( "s_date <= %s", $value ); 241 | break; 242 | case 'after': 243 | $clauses[] = $wpdb->prepare( "s_date >= %s", $value ); 244 | break; 245 | case 's_type': 246 | case 'location': 247 | case 'content': 248 | case 's_date': 249 | $clauses[] = $wpdb->prepare( "$column = %s", $value ); 250 | break; 251 | default: 252 | $clauses[] = $wpdb->prepare( "$column = %d", $value ); 253 | break; 254 | } 255 | } 256 | 257 | $where = implode( ' AND ', $clauses ); 258 | 259 | $query = "SELECT SUM(s_count) FROM $this->table_name WHERE $where"; 260 | 261 | $count = intval( $wpdb->get_var( $query ) ); 262 | 263 | wp_cache_set( $cache_key, $count, 'hollerbox:counts' ); 264 | 265 | return $count; 266 | } 267 | 268 | /** 269 | * Get all impressions for all popups for the last 30 days 270 | * 271 | * @return int 272 | */ 273 | public function get_total_impressions_last_30() { 274 | $after = new DateTime( '30 days ago' ); 275 | 276 | return $this->_get_total_count_for_interval( [ 277 | 's_type' => 'impression', 278 | 'after' => $after->format( 'Y-m-d' ) 279 | ] ); 280 | } 281 | 282 | /** 283 | * Get all conversions for all popups for the last 30 days 284 | * 285 | * @return int 286 | */ 287 | public function get_total_conversions_last_30() { 288 | $after = new DateTime( '30 days ago' ); 289 | 290 | return $this->_get_total_count_for_interval( [ 291 | 's_type' => 'conversion', 292 | 'after' => $after->format( 'Y-m-d' ) 293 | ] ); 294 | } 295 | 296 | /** 297 | * Get the impression count for a given interval 298 | * 299 | * @param $popup Holler_Popup 300 | * @param $interval DateInterval 301 | * 302 | * @return int 303 | */ 304 | public function get_total_impressions_for_interval( Holler_Popup $popup, DateInterval $interval ) { 305 | return $this->get_total_popup_count_for_interval( 'impression', $popup, $interval ); 306 | } 307 | 308 | /** 309 | * Get the submission count for a given interval 310 | * 311 | * @param $popup Holler_Popup 312 | * @param $interval DateInterval 313 | * 314 | * @return int 315 | */ 316 | public function get_total_conversions_for_interval( Holler_Popup $popup, DateInterval $interval ) { 317 | return $this->get_total_popup_count_for_interval( 'conversion', $popup, $interval ); 318 | } 319 | 320 | /** 321 | * Ensures that a column is valid for SQL queries 322 | * 323 | * @param $column 324 | * 325 | * @return bool 326 | */ 327 | public function is_valid_column( $column ) { 328 | return in_array( $column, [ 329 | 's_type', 330 | 's_count', 331 | 'popup_id', 332 | 'location', 333 | 's_date', 334 | 'content' 335 | ] ); 336 | } 337 | 338 | /** 339 | * Get rows of report data for the given query 340 | * 341 | * @param array $query 342 | * 343 | * @return array 344 | */ 345 | public function get_report_data( array $query = [] ) { 346 | 347 | global $wpdb; 348 | 349 | $default_date = new DateTime( '30 days ago', wp_timezone() ); 350 | 351 | $query = wp_parse_args( $query, [ 352 | 'before' => current_time( 'Y-m-d' ), 353 | 'after' => $default_date->format( 'Y-m-d' ) 354 | ] ); 355 | 356 | $clauses = []; 357 | 358 | foreach ( $query as $column => $value ) { 359 | switch ( $column ) { 360 | case 'before': 361 | $clauses[] = $wpdb->prepare( 's_date <= %s', $value ); 362 | break; 363 | case 'after': 364 | $clauses[] = $wpdb->prepare( 's_date >= %s', $value ); 365 | break; 366 | case 's_type': 367 | case 'location': 368 | case 'content': 369 | case 's_date': 370 | $clauses[] = $wpdb->prepare( "$column = %s", $value ); 371 | break; 372 | default: 373 | if ( $this->is_valid_column( $column ) ) { 374 | if ( is_numeric( $column ) ) { 375 | $clauses[] = $wpdb->prepare( "$column = %d", $value ); 376 | } else { 377 | $clauses[] = $wpdb->prepare( "$column = %s", $value ); 378 | } 379 | } 380 | break; 381 | } 382 | } 383 | 384 | $where = implode( ' AND ', $clauses ); 385 | 386 | $query = "SELECT * FROM $this->table_name WHERE $where"; 387 | 388 | return $wpdb->get_results( $query ); 389 | } 390 | 391 | /** 392 | * Max key length 393 | * 394 | * @return int 395 | */ 396 | public function max_key_length() { 397 | global $wpdb; 398 | 399 | return $wpdb->charset === 'utf8mb4' ? 191 : 255; 400 | } 401 | 402 | /** 403 | * Change location to varchar 255 404 | * Update primary key to include location 405 | * 406 | * @return void 407 | */ 408 | public function update_2_1_2() { 409 | global $wpdb; 410 | 411 | $max_index_length = $this->max_key_length(); 412 | 413 | $wpdb->query( "ALTER TABLE {$this->table_name} MODIFY COLUMN location varchar($max_index_length) NOT NULL, DROP PRIMARY KEY, ADD PRIMARY KEY (popup_id, s_type, s_date, location);" ); 414 | } 415 | 416 | /** 417 | * Add the new column 418 | * 419 | * @return void 420 | */ 421 | public function update_2_2() { 422 | global $wpdb; 423 | 424 | $max_index_length = $this->max_key_length(); 425 | 426 | $commands = [ 427 | // Change the engine to InnoDB 428 | "ALTER TABLE {$this->table_name} ENGINE=InnoDB;", 429 | // Add the content columns 430 | "ALTER TABLE {$this->table_name} ADD content varchar($max_index_length) NOT NULL;", 431 | // Add index 432 | "CREATE INDEX content ON {$this->table_name} (content);", 433 | // Update the primary key 434 | "ALTER TABLE {$this->table_name} DROP PRIMARY KEY, ADD PRIMARY KEY (popup_id, s_type, s_date, location, content);" 435 | ]; 436 | 437 | foreach ( $commands as $command ) { 438 | $wpdb->query( $command ); 439 | } 440 | } 441 | 442 | } 443 | -------------------------------------------------------------------------------- /includes/class-holler-settings.php: -------------------------------------------------------------------------------- 1 | settings = get_option( self::OPTION_NAME, [] ); 34 | } 35 | 36 | /** 37 | * Get a setting 38 | * 39 | * @param string|array $setting 40 | * @param mixed $default 41 | * 42 | * @return mixed 43 | */ 44 | public function get( $setting, $default = false ) { 45 | 46 | if ( is_array( $setting ) ) { 47 | 48 | $settings = []; 49 | 50 | foreach ( $setting as $key ) { 51 | $settings[ $key ] = $this->settings[ $key ] ?? ( is_array( $default ) && isset( $default[ $key ] ) ? $default[ $key ] : false ); 52 | } 53 | 54 | return $settings; 55 | } 56 | 57 | return $this->settings[ $setting ] ?? $default; 58 | } 59 | 60 | /** 61 | * Update a setting 62 | * 63 | * @param $name string 64 | * @param $value mixed 65 | * @param $commit bool 66 | * 67 | * @return void 68 | */ 69 | public function update( string $name, $value, bool $commit = true ) { 70 | $this->settings[ $name ] = $value; 71 | 72 | switch ( $name ) { 73 | case 'stacked_delay': 74 | $value = absint( $value ); 75 | break; 76 | case 'cookie_compliance': 77 | case 'gdpr_enabled': 78 | case 'credit_disabled': 79 | case 'disable_all': 80 | case 'script_debug_mode': 81 | case 'delete_all_data': 82 | case 'telemetry_subscribed': 83 | case 'is_licensed': 84 | case 'is_legacy_user': 85 | case 'legacy_user_agreed': 86 | $value = boolval( $value ); 87 | break; 88 | case 'gdpr_text': 89 | $value = wp_kses_post( $value ); 90 | break; 91 | case 'cookie_name': 92 | case 'cookie_value': 93 | default: 94 | $value = sanitize_text_field( $value ); 95 | break; 96 | } 97 | 98 | $this->settings[ $name ] = $value; 99 | 100 | if ( $commit ) { 101 | $this->commit(); 102 | } 103 | } 104 | 105 | /** 106 | * Commit the settings as they are in the instance 107 | * 108 | * @return bool 109 | */ 110 | public function commit() { 111 | return update_option( self::OPTION_NAME, $this->settings ); 112 | } 113 | 114 | /** 115 | * Drop the option name 116 | * @return void 117 | */ 118 | public function drop() { 119 | delete_option( self::OPTION_NAME ); 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /includes/class-holler-telemetry.php: -------------------------------------------------------------------------------- 1 | WP_REST_Server::CREATABLE, 38 | 'callback' => [ $this, 'optin' ], 39 | 'permission_callback' => [ $this, 'permission_callback' ] 40 | ], 41 | ] ); 42 | 43 | register_rest_route( 'hollerbox', '/telemetry/legacy', [ 44 | [ 45 | 'methods' => WP_REST_Server::CREATABLE, 46 | 'callback' => [ $this, 'optin_legacy' ], 47 | 'permission_callback' => [ $this, 'permission_callback' ] 48 | ], 49 | ] ); 50 | 51 | } 52 | 53 | /** 54 | * Send telemetry weekly 55 | * 56 | * @return void 57 | */ 58 | public function send_telemetry() { 59 | 60 | // Opted out of telemetry 61 | if ( ! Holler_Settings::instance()->get( 'telemetry_subscribed' ) ) { 62 | return; 63 | } 64 | 65 | $request = [ 66 | 'email' => Holler_Settings::instance()->get( 'telemetry_email' ), 67 | 'date' => current_time( 'mysql' ), 68 | 'system_info' => [ 69 | 'php_version' => PHP_VERSION, 70 | 'wp_version' => get_bloginfo( 'version' ), 71 | 'hb_version' => HOLLERBOX_VERSION, 72 | 'site_lang' => get_bloginfo( 'language' ), 73 | ], 74 | 'usage' => [ 75 | 'conversions' => Holler_Reporting::instance()->get_total_conversions_last_30(), 76 | 'impressions' => Holler_Reporting::instance()->get_total_impressions_last_30() 77 | ] 78 | ]; 79 | 80 | wp_remote_post( 'https://hollerwp.com/wp-json/gh/v3/webhook-listener?auth_token=JVq8f3u&step_id=36', [ 81 | 'body' => wp_json_encode( $request ), 82 | 'headers' => [ 83 | 'Content-Type' => 'application/json' 84 | ], 85 | ] ); 86 | 87 | } 88 | 89 | /** 90 | * Optin top telemetry 91 | * 92 | * @param WP_REST_Request $request 93 | * 94 | * @return WP_Error|WP_REST_Response 95 | */ 96 | public function optin( WP_REST_Request $request ) { 97 | 98 | $telemetry = $request->get_param( 'telemetry_subscribed' ) ?: false; 99 | $marketing = $request->get_param( 'marketing_subscribed' ) ?: false; 100 | $email = sanitize_email( $request->get_param( 'email' ) ); 101 | 102 | $user = wp_get_current_user(); 103 | $name = $user->display_name; 104 | 105 | if ( ! $email ) { 106 | $email = $user->user_email; 107 | } 108 | 109 | if ( ! empty( $user->first_name ) ) { 110 | $name = $user->first_name . ' ' . $user->last_name; 111 | } 112 | 113 | if ( ! $telemetry && ! $marketing ) { 114 | return rest_ensure_response( [ 'success' => true ] ); 115 | } 116 | 117 | $request = [ 118 | 'email' => $email, 119 | 'name' => $name, 120 | 'role' => sanitize_text_field( $request->get_param( 'role' ) ), 121 | 'business' => sanitize_text_field( $request->get_param( 'business' ) ), 122 | 'marketing' => $marketing ? 'yes' : 'no', 123 | 'telemetry' => $telemetry ? 'yes' : 'no' 124 | ]; 125 | 126 | if ( $telemetry ) { 127 | // Remember telemetry optin for later 128 | Holler_Settings::instance()->update( 'telemetry_subscribed', true ); 129 | Holler_Settings::instance()->update( 'telemetry_email', $email ); 130 | 131 | $request['system_info'] = [ 132 | 'php_version' => PHP_VERSION, 133 | 'wp_version' => get_bloginfo( 'version' ), 134 | 'hb_version' => HOLLERBOX_VERSION, 135 | 'site_lang' => get_bloginfo( 'language' ), 136 | ]; 137 | } 138 | 139 | wp_remote_post( 'https://hollerwp.com/wp-json/gh/v3/webhook-listener?auth_token=JVq8f3u&step_id=34', [ 140 | 'body' => wp_json_encode( $request ), 141 | 'headers' => [ 142 | 'Content-Type' => 'application/json' 143 | ], 144 | ] ); 145 | 146 | return rest_ensure_response( [ 'success' => true ] ); 147 | } 148 | 149 | /** 150 | * Optin top telemetry 151 | * 152 | * @param WP_REST_Request $request 153 | * 154 | * @return WP_Error|WP_REST_Response 155 | */ 156 | public function optin_legacy( WP_REST_Request $request ) { 157 | 158 | $email = sanitize_email( $request->get_param( 'email' ) ); 159 | $name = sanitize_text_field( $request->get_param( 'name' ) ); 160 | 161 | $request = [ 162 | 'email' => $email, 163 | 'name' => $name, 164 | ]; 165 | 166 | wp_remote_post( 'https://hollerwp.com/wp-json/gh/v3/webhook-listener?auth_token=NdZ6FeU&step_id=44', [ 167 | 'body' => wp_json_encode( $request ), 168 | 'headers' => [ 169 | 'Content-Type' => 'application/json' 170 | ], 171 | ] ); 172 | 173 | return rest_ensure_response( [ 'success' => true ] ); 174 | } 175 | 176 | public function permission_callback() { 177 | return current_user_can( 'manage_options' ); 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /includes/class-holler-updater.php: -------------------------------------------------------------------------------- 1 | [ $this, 'v_2_0' ], 12 | '2.1.2' => [ $this, 'v_2_1_2' ], 13 | '2.2' => [ $this, 'v_2_2' ], 14 | ]; 15 | } 16 | 17 | public function v_2_2() { 18 | Holler_Reporting::instance()->update_2_2(); 19 | } 20 | 21 | /** 22 | * Updated the stats table 23 | * 24 | * @return void 25 | */ 26 | public function v_2_1_2() { 27 | Holler_Reporting::instance()->update_2_1_2(); 28 | } 29 | 30 | /** 31 | * Upate to 2.0 32 | * 33 | * @return void 34 | */ 35 | public function v_2_0() { 36 | 37 | // Migrate some settings over 38 | Holler_Settings::instance()->update( 'license', get_option( 'hwp_pro_edd_license' ), false ); 39 | Holler_Settings::instance()->update( 'is_licensed', get_option( 'hwp_pro_edd_license_status' ) === 'valid', false ); 40 | Holler_Settings::instance()->update( 'credit_disabled', get_option( 'hwp_powered_by' ), false ); 41 | 42 | $options = [ 43 | 'hwp_ac_api_key', 44 | 'hwp_ck_api_key', 45 | 'hwp_mc_api_key', 46 | 'hwp_powered_by', 47 | ]; 48 | 49 | // If any of the settings were set and are not empty, this person is a legacy user 50 | foreach ( $options as $option ) { 51 | $val = get_option( $option ); 52 | if ( ! empty( $val ) ) { 53 | Holler_Settings::instance()->update( 'is_legacy_user', true, false ); 54 | break; 55 | } 56 | } 57 | 58 | // Commit the settings 59 | Holler_Settings::instance()->commit(); 60 | 61 | // Create the reports table 62 | Holler_Reporting::instance()->create_table(); 63 | } 64 | 65 | /** 66 | * Array of updates which have already been performed 67 | * 68 | * @return false|mixed|void 69 | */ 70 | public function get_previous_updates() { 71 | return get_option( 'holler_previous_updates', [] ); 72 | } 73 | 74 | /** 75 | * Whether an update for a specific version was performed 76 | * 77 | * @param $version 78 | * 79 | * @return bool 80 | */ 81 | public function did_update( $version ) { 82 | return in_array( $version, $this->get_previous_updates() ); 83 | } 84 | 85 | /** 86 | * Remember that we did this update 87 | * 88 | * @param $version 89 | * 90 | * @return void 91 | */ 92 | public function update_complete( $version ) { 93 | $updates = $this->get_previous_updates(); 94 | $updates[] = $version; 95 | update_option( 'holler_previous_updates', $updates ); 96 | } 97 | 98 | /** 99 | * Maybe perform updates 100 | * 101 | * @return void 102 | */ 103 | public function maybe_upgrade() { 104 | foreach ( $this->get_updates() as $update => $callback ) { 105 | if ( $this->did_update( $update ) ) { 106 | continue; 107 | } 108 | 109 | if ( is_callable( $callback ) ) { 110 | call_user_func( $callback ); 111 | 112 | $this->update_complete( $update ); 113 | } 114 | } 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /languages/holler-box.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groundhoggwp/holler-box/bff676c0eddee90de2adf6852b411e5e08148e7b/languages/holler-box.mo -------------------------------------------------------------------------------- /languages/holler-box.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: Holler Box\n" 4 | "POT-Creation-Date: 2018-09-15 13:14-0800\n" 5 | "PO-Revision-Date: 2018-09-15 13:14-0800\n" 6 | "Last-Translator: \n" 7 | "Language-Team: \n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "X-Generator: Poedit 1.5.7\n" 12 | "X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e\n" 13 | "X-Poedit-Basepath: .\n" 14 | "X-Poedit-SearchPath-0: /Users/scottopolis/Documents/Websites/Flywheel Local/" 15 | "app/public/wp-content/plugins/holler-box\n" 16 | 17 | #: /Users/scottopolis/Documents/Websites/Flywheel 18 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-functions.php:130 19 | msgid "Please enter a valid email address." 20 | msgstr "" 21 | 22 | #: /Users/scottopolis/Documents/Websites/Flywheel 23 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-functions.php:324 24 | msgid "Type your message" 25 | msgstr "" 26 | 27 | #: /Users/scottopolis/Documents/Websites/Flywheel 28 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-functions.php:399 29 | msgid "50% Complete" 30 | msgstr "" 31 | 32 | #: /Users/scottopolis/Documents/Websites/Flywheel 33 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-functions.php:582 34 | msgid "Enter email" 35 | msgstr "" 36 | 37 | #: /Users/scottopolis/Documents/Websites/Flywheel 38 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:202 39 | msgid "Settings" 40 | msgstr "" 41 | 42 | #: /Users/scottopolis/Documents/Websites/Flywheel 43 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:252 44 | msgid "Email Settings" 45 | msgstr "" 46 | 47 | #: /Users/scottopolis/Documents/Websites/Flywheel 48 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:254 49 | msgid "Email title (only used with \"send to email\" setting)" 50 | msgstr "" 51 | 52 | #: /Users/scottopolis/Documents/Websites/Flywheel 53 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:258 54 | msgid "" 55 | "If you are using ConvertKit, entery your API key. It can be found on your account info page." 58 | msgstr "" 59 | 60 | #: /Users/scottopolis/Documents/Websites/Flywheel 61 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:262 62 | msgid "" 63 | "If you are using Active Campaign, enter your url and API key. It can be " 64 | "found under My Settings -> Developer." 65 | msgstr "" 66 | 67 | #: /Users/scottopolis/Documents/Websites/Flywheel 68 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:268 69 | msgid "" 70 | "If you are using MailChimp, enter your API key. It can be found under " 71 | "Account -> Extras -> API Keys." 72 | msgstr "" 73 | 74 | #: /Users/scottopolis/Documents/Websites/Flywheel 75 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:274 76 | msgid "" 77 | "Disable MailChimp double-opt in? Check to subscribe users to your list " 78 | "without confirmation. If checked, MailChimp will not send a final welcome " 79 | "email." 80 | msgstr "" 81 | 82 | #: /Users/scottopolis/Documents/Websites/Flywheel 83 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:277 84 | msgid "Miscellaneous" 85 | msgstr "" 86 | 87 | #: /Users/scottopolis/Documents/Websites/Flywheel 88 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:281 89 | msgid "Hide attribution links" 90 | msgstr "" 91 | 92 | #: /Users/scottopolis/Documents/Websites/Flywheel 93 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:365 94 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:366 95 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:367 96 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:368 97 | msgid "Holler Box" 98 | msgstr "" 99 | 100 | #: /Users/scottopolis/Documents/Websites/Flywheel 101 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:369 102 | msgid "Add New" 103 | msgstr "" 104 | 105 | #: /Users/scottopolis/Documents/Websites/Flywheel 106 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:370 107 | msgid "Add New Box" 108 | msgstr "" 109 | 110 | #: /Users/scottopolis/Documents/Websites/Flywheel 111 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:371 112 | msgid "New Box" 113 | msgstr "" 114 | 115 | #: /Users/scottopolis/Documents/Websites/Flywheel 116 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:372 117 | msgid "Edit Box" 118 | msgstr "" 119 | 120 | #: /Users/scottopolis/Documents/Websites/Flywheel 121 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:373 122 | msgid "View Box" 123 | msgstr "" 124 | 125 | #: /Users/scottopolis/Documents/Websites/Flywheel 126 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:374 127 | msgid "All Boxes" 128 | msgstr "" 129 | 130 | #: /Users/scottopolis/Documents/Websites/Flywheel 131 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:375 132 | msgid "Search Boxes" 133 | msgstr "" 134 | 135 | #: /Users/scottopolis/Documents/Websites/Flywheel 136 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:376 137 | msgid "Parent Boxes:" 138 | msgstr "" 139 | 140 | #: /Users/scottopolis/Documents/Websites/Flywheel 141 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:377 142 | msgid "No Boxes found." 143 | msgstr "" 144 | 145 | #: /Users/scottopolis/Documents/Websites/Flywheel 146 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:378 147 | msgid "No Boxes found in Trash." 148 | msgstr "" 149 | 150 | #: /Users/scottopolis/Documents/Websites/Flywheel 151 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:413 152 | msgid "Display" 153 | msgstr "" 154 | 155 | #: /Users/scottopolis/Documents/Websites/Flywheel 156 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:422 157 | msgid "Advanced Settings" 158 | msgstr "" 159 | 160 | #: /Users/scottopolis/Documents/Websites/Flywheel 161 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:444 162 | msgid "Choose a Holler Box Type" 163 | msgstr "" 164 | 165 | #: /Users/scottopolis/Documents/Websites/Flywheel 166 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:541 167 | msgid "Position" 168 | msgstr "" 169 | 170 | #: /Users/scottopolis/Documents/Websites/Flywheel 171 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:564 172 | msgid "Choose a Template" 173 | msgstr "" 174 | 175 | #: /Users/scottopolis/Documents/Websites/Flywheel 176 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:604 177 | msgid "Popup Options" 178 | msgstr "" 179 | 180 | #: /Users/scottopolis/Documents/Websites/Flywheel 181 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:608 182 | msgid "Upload a Custom Image" 183 | msgstr "" 184 | 185 | #: /Users/scottopolis/Documents/Websites/Flywheel 186 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:621 187 | msgid "Accent color" 188 | msgstr "" 189 | 190 | #: /Users/scottopolis/Documents/Websites/Flywheel 191 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:625 192 | msgid "Background color" 193 | msgstr "" 194 | 195 | #: /Users/scottopolis/Documents/Websites/Flywheel 196 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:628 197 | msgid "Text color" 198 | msgstr "" 199 | 200 | #: /Users/scottopolis/Documents/Websites/Flywheel 201 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:637 202 | msgid "Show email opt-in" 203 | msgstr "" 204 | 205 | #: /Users/scottopolis/Documents/Websites/Flywheel 206 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:645 207 | msgid "Send to email address" 208 | msgstr "" 209 | 210 | #: /Users/scottopolis/Documents/Websites/Flywheel 211 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:649 212 | msgid "ConvertKit" 213 | msgstr "" 214 | 215 | #: /Users/scottopolis/Documents/Websites/Flywheel 216 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:653 217 | msgid "MailChimp" 218 | msgstr "" 219 | 220 | #: /Users/scottopolis/Documents/Websites/Flywheel 221 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:657 222 | msgid "Active Campaign" 223 | msgstr "" 224 | 225 | #: /Users/scottopolis/Documents/Websites/Flywheel 226 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:661 227 | msgid "Drip" 228 | msgstr "" 229 | 230 | #: /Users/scottopolis/Documents/Websites/Flywheel 231 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:667 232 | msgid "MailPoet" 233 | msgstr "" 234 | 235 | #: /Users/scottopolis/Documents/Websites/Flywheel 236 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:673 237 | msgid "Custom" 238 | msgstr "" 239 | 240 | #: /Users/scottopolis/Documents/Websites/Flywheel 241 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:681 242 | msgid "" 243 | "ConvertKit List ID, click for help. " 246 | "*required" 247 | msgstr "" 248 | 249 | #: /Users/scottopolis/Documents/Websites/Flywheel 250 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:687 251 | msgid "MailChimp List *required" 252 | msgstr "" 253 | 254 | #: /Users/scottopolis/Documents/Websites/Flywheel 255 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:720 256 | msgid "Active Campaign List *required" 257 | msgstr "" 258 | 259 | #: /Users/scottopolis/Documents/Websites/Flywheel 260 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:752 261 | msgid "MailPoet List *required" 262 | msgstr "" 263 | 264 | #: /Users/scottopolis/Documents/Websites/Flywheel 265 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:784 266 | msgid "Send to email *required" 267 | msgstr "" 268 | 269 | #: /Users/scottopolis/Documents/Websites/Flywheel 270 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:791 271 | msgid "Insert HTML form code here" 272 | msgstr "" 273 | 274 | #: /Users/scottopolis/Documents/Websites/Flywheel 275 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:801 276 | msgid "Name Field Placeholder" 277 | msgstr "" 278 | 279 | #: /Users/scottopolis/Documents/Websites/Flywheel 280 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:807 281 | msgid "Don't show first name field" 282 | msgstr "" 283 | 284 | #: /Users/scottopolis/Documents/Websites/Flywheel 285 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:813 286 | msgid "Small text above email field" 287 | msgstr "" 288 | 289 | #: /Users/scottopolis/Documents/Websites/Flywheel 290 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:818 291 | msgid "Placeholder" 292 | msgstr "" 293 | 294 | #: /Users/scottopolis/Documents/Websites/Flywheel 295 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:823 296 | msgid "Confirmation Message" 297 | msgstr "" 298 | 299 | #: /Users/scottopolis/Documents/Websites/Flywheel 300 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:828 301 | msgid "Submit Button Text" 302 | msgstr "" 303 | 304 | #: /Users/scottopolis/Documents/Websites/Flywheel 305 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:959 306 | msgid "What pages?" 307 | msgstr "" 308 | 309 | #: /Users/scottopolis/Documents/Websites/Flywheel 310 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:966 311 | msgid "Show on pages" 312 | msgstr "" 313 | 314 | #: /Users/scottopolis/Documents/Websites/Flywheel 315 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:978 316 | msgid "Show to these visitors" 317 | msgstr "" 318 | 319 | #: /Users/scottopolis/Documents/Websites/Flywheel 320 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:981 321 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:992 322 | msgid "All visitors" 323 | msgstr "" 324 | 325 | #: /Users/scottopolis/Documents/Websites/Flywheel 326 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:982 327 | msgid "Logged in only" 328 | msgstr "" 329 | 330 | #: /Users/scottopolis/Documents/Websites/Flywheel 331 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:983 332 | msgid "Logged out only" 333 | msgstr "" 334 | 335 | #: /Users/scottopolis/Documents/Websites/Flywheel 336 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:989 337 | msgid "New or returning" 338 | msgstr "" 339 | 340 | #: /Users/scottopolis/Documents/Websites/Flywheel 341 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:993 342 | msgid "New visitors only" 343 | msgstr "" 344 | 345 | #: /Users/scottopolis/Documents/Websites/Flywheel 346 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:994 347 | msgid "Returning visitors only" 348 | msgstr "" 349 | 350 | #: /Users/scottopolis/Documents/Websites/Flywheel 351 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1001 352 | msgid "When should we show it?" 353 | msgstr "" 354 | 355 | #: /Users/scottopolis/Documents/Websites/Flywheel 356 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1005 357 | msgid "Immediately" 358 | msgstr "" 359 | 360 | #: /Users/scottopolis/Documents/Websites/Flywheel 361 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1006 362 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1023 363 | msgid "Delay of" 364 | msgstr "" 365 | 366 | #: /Users/scottopolis/Documents/Websites/Flywheel 367 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1006 368 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1023 369 | msgid "seconds" 370 | msgstr "" 371 | 372 | #: /Users/scottopolis/Documents/Websites/Flywheel 373 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1007 374 | msgid "User scrolls halfway down the page" 375 | msgstr "" 376 | 377 | #: /Users/scottopolis/Documents/Websites/Flywheel 378 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1008 379 | msgid "Exit Detection" 380 | msgstr "" 381 | 382 | #: /Users/scottopolis/Documents/Websites/Flywheel 383 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1018 384 | msgid "After it displays, when should it disappear?" 385 | msgstr "" 386 | 387 | #: /Users/scottopolis/Documents/Websites/Flywheel 388 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1022 389 | msgid "When user clicks hide" 390 | msgstr "" 391 | 392 | #: /Users/scottopolis/Documents/Websites/Flywheel 393 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1031 394 | msgid "How often should we show it to each visitor?" 395 | msgstr "" 396 | 397 | #: /Users/scottopolis/Documents/Websites/Flywheel 398 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1035 399 | msgid "Hide after user interacts (Close or email submit)" 400 | msgstr "" 401 | 402 | #: /Users/scottopolis/Documents/Websites/Flywheel 403 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1036 404 | msgid "Every page load" 405 | msgstr "" 406 | 407 | #: /Users/scottopolis/Documents/Websites/Flywheel 408 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1037 409 | msgid "Show, then hide for" 410 | msgstr "" 411 | 412 | #: /Users/scottopolis/Documents/Websites/Flywheel 413 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1037 414 | msgid "days" 415 | msgstr "" 416 | 417 | #: /Users/scottopolis/Documents/Websites/Flywheel 418 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1044 419 | msgid "Show on Devices" 420 | msgstr "" 421 | 422 | #: /Users/scottopolis/Documents/Websites/Flywheel 423 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1048 424 | msgid "All devices" 425 | msgstr "" 426 | 427 | #: /Users/scottopolis/Documents/Websites/Flywheel 428 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1049 429 | msgid "Desktop only" 430 | msgstr "" 431 | 432 | #: /Users/scottopolis/Documents/Websites/Flywheel 433 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1050 434 | msgid "Mobile only" 435 | msgstr "" 436 | 437 | #: /Users/scottopolis/Documents/Websites/Flywheel 438 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1059 439 | msgid "Hide the floating button? (Appears when box is hidden.)" 440 | msgstr "" 441 | 442 | #: /Users/scottopolis/Documents/Websites/Flywheel 443 | #: Local/app/public/wp-content/plugins/holler-box/includes/class-holler-admin.php:1068 444 | msgid "Gravatar Email" 445 | msgstr "" 446 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | get( 'delete_all_data' ) ) { 36 | 37 | \Holler_Reporting::instance()->drop(); 38 | 39 | // Delete all the posts 40 | $wpdb->query( "DELETE p,tr,pm 41 | FROM $wpdb->posts p 42 | LEFT JOIN $wpdb->term_relationships tr 43 | ON (p.ID = tr.object_id) 44 | LEFT JOIN $wpdb->postmeta pm 45 | ON (p.ID = pm.post_id) 46 | WHERE p.post_type = 'hollerbox';" ); 47 | 48 | // Delete cached user meta 49 | $wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key in ('hollerbox_popup_conversions','hollerbox_closed_popups');" ); 50 | 51 | \Holler_Settings::instance()->drop(); 52 | 53 | wp_clear_scheduled_hook( 'hollerbox/telemetry' ); 54 | 55 | } 56 | --------------------------------------------------------------------------------