FooGallery – Image Gallery WordPress Plugin - Version 1.4.25

Version Description

  • New : Retina support for albums!
  • New : Default crop position setting for attachments
  • New : Speed up gallery previews in wp-admin
  • New : Caption support for Responsive Lightbox by dFactory
  • Fix : Extension loading issues on certain installs
  • Fix : Shortcode copy-to-clipboard metabox works again
  • Fix : Bugs fixes for paging, filtering, FooBox and more
  • Fix : Ensure jquery-ui-sortable is loaded on edit page for some installs
  • Fix : All-In-One Stack Album layout bugs
  • Fix : Reworked extensions listing page logic
  • Update : FooGallery client side 1.0.23
  • Update : Freemius SDK 1.2.4
Download this release

Release Info

Developer bradvin
Plugin Icon 128x128 FooGallery – Image Gallery WordPress Plugin
Version 1.4.25
Comparing to
See all releases

Code changes from version 1.4.15 to 1.4.25

Files changed (82) hide show
  1. README.md +6 -358
  2. README.txt +23 -8
  3. css/admin-foogallery.css +33 -1
  4. extensions/albums/admin/class-metaboxes.php +2 -2
  5. extensions/albums/album-default.php +2 -0
  6. extensions/albums/album-stack.php +7 -4
  7. extensions/albums/js/album-stack.js +54 -47
  8. extensions/default-templates/default/class-default-gallery-template.php +1 -0
  9. extensions/default-templates/justified/class-justified-gallery-template.php +1 -0
  10. extensions/default-templates/masonry/class-masonry-gallery-template.php +1 -0
  11. extensions/default-templates/shared/css/foogallery.css +2 -2
  12. extensions/default-templates/shared/css/foogallery.min.css +1 -1
  13. extensions/default-templates/shared/js/foogallery.js +789 -538
  14. extensions/default-templates/shared/js/foogallery.min.js +3 -3
  15. extensions/default-templates/simple-portfolio/class-simple-portfolio-gallery-template.php +1 -0
  16. extensions/media-categories/class-media-categories-extension.php +0 -237
  17. foogallery.php +46 -6
  18. freemius/.codeclimate.yml +19 -0
  19. freemius/.github/ISSUE_TEMPLATE.md +29 -0
  20. freemius/.travis.yml +11 -0
  21. freemius/LICENSE.txt +673 -673
  22. freemius/assets/css/admin/account.css +1 -1
  23. freemius/assets/css/admin/affiliation.css +1 -1
  24. freemius/assets/css/admin/common.css +2 -2
  25. freemius/assets/css/admin/connect.css +1 -1
  26. freemius/assets/css/admin/index.php +3 -0
  27. freemius/assets/css/customizer.css +1 -1
  28. freemius/assets/css/index.php +3 -0
  29. freemius/assets/img/foogallery.png +0 -0
  30. freemius/assets/img/index.php +3 -0
  31. freemius/assets/js/index.php +3 -0
  32. freemius/assets/js/nojquery.ba-postmessage.js +139 -139
  33. freemius/assets/js/nojquery.ba-postmessage.min.js +11 -11
  34. freemius/assets/js/postmessage.js +134 -134
  35. freemius/assets/scss/_mixins.scss +269 -269
  36. freemius/assets/scss/admin/_themes.scss +20 -20
  37. freemius/assets/scss/admin/account.scss +255 -255
  38. freemius/assets/scss/admin/add-ons.scss +448 -448
  39. freemius/assets/scss/admin/affiliation.scss +96 -96
  40. freemius/assets/scss/admin/index.php +3 -0
  41. freemius/assets/scss/customizer.scss +124 -124
  42. freemius/assets/scss/index.php +3 -0
  43. freemius/composer.json +10 -10
  44. freemius/config.php +3 -0
  45. freemius/gulpfile.js +32 -4
  46. freemius/includes/class-freemius-abstract.php +596 -596
  47. freemius/includes/class-freemius.php +572 -330
  48. freemius/includes/class-fs-logger.php +687 -687
  49. freemius/includes/class-fs-plugin-updater.php +12 -14
  50. freemius/includes/class-fs-security.php +85 -85
  51. freemius/includes/customizer/class-fs-customizer-support-section.php +3 -3
  52. freemius/includes/customizer/class-fs-customizer-upsell-control.php +2 -2
  53. freemius/includes/customizer/index.php +3 -0
  54. freemius/includes/debug/class-fs-debug-bar-panel.php +1 -1
  55. freemius/includes/debug/debug-bar-start.php +2 -2
  56. freemius/includes/entities/class-fs-affiliate-terms.php +127 -127
  57. freemius/includes/entities/class-fs-affiliate.php +83 -83
  58. freemius/includes/entities/class-fs-billing.php +94 -94
  59. freemius/includes/entities/class-fs-entity.php +148 -148
  60. freemius/includes/entities/class-fs-payment.php +93 -93
  61. freemius/includes/entities/class-fs-plugin-info.php +33 -33
  62. freemius/includes/entities/class-fs-plugin-license.php +224 -224
  63. freemius/includes/entities/class-fs-plugin-plan.php +144 -144
  64. freemius/includes/entities/class-fs-plugin-tag.php +23 -23
  65. freemius/includes/entities/class-fs-plugin.php +116 -116
  66. freemius/includes/entities/class-fs-pricing.php +140 -140
  67. freemius/includes/entities/class-fs-scope-entity.php +28 -28
  68. freemius/includes/entities/class-fs-site.php +147 -147
  69. freemius/includes/entities/class-fs-subscription.php +124 -124
  70. freemius/includes/entities/class-fs-user.php +61 -61
  71. freemius/includes/fs-core-functions.php +1097 -665
  72. freemius/includes/fs-essential-functions.php +3 -0
  73. freemius/includes/fs-plugin-info-dialog.php +98 -56
  74. freemius/includes/i18n.php +3 -1
  75. freemius/includes/l10n.php +0 -220
  76. freemius/includes/managers/class-fs-admin-menu-manager.php +849 -849
  77. freemius/includes/managers/class-fs-cache-manager.php +280 -280
  78. freemius/includes/managers/class-fs-key-value-storage.php +327 -327
  79. freemius/includes/managers/class-fs-license-manager.php +103 -103
  80. freemius/includes/managers/class-fs-option-manager.php +352 -352
  81. freemius/includes/managers/class-fs-plan-manager.php +161 -161
  82. freemius/includes/managers/class-fs-plugin-manager.php +0 -163
README.md CHANGED
@@ -1,363 +1,11 @@
1
- ![Plugin Banner](https://s3.amazonaws.com/foogallery/banner-772x250.jpg)
2
 
3
- [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fooplugins/foogallery/badges/quality-score.png?b=develop)](https://scrutinizer-ci.com/g/fooplugins/foogallery/?branch=develop)
4
- # FooGallery - Image Gallery WordPress Plugin #
5
- **Contributors:** bradvin, steveush, fooplugins
6
-
7
- **Donate link:** http://fooplugins.com
8
-
9
- **Tags:** gallery, image gallery, photo gallery, responsive, album, media gallery, masonry gallery, portfolio, justified image gallery, video gallery, photography, photographer, retina
10
-
11
- **Requires at least:** 3.9
12
-
13
- **Tested up to:** 4.8.3
14
-
15
- **Stable tag:** trunk
16
-
17
- **License:** GPLv2 or later
18
-
19
- **License URI:** http://www.gnu.org/licenses/gpl-2.0.html
20
-
21
- FooGallery is the best image gallery plugin for WordPress. Why? Stunning gallery layouts, responsive, retina-ready, lightning fast, easy to use.
22
-
23
- ## Description ##
24
-
25
- FooGallery is the best image gallery plugin for WordPress. Why? Stunning gallery layouts, responsive, retina-ready, lightning fast, easy to use. Built to be highly configurable and extensible for developers or freelancers.
26
-
27
- [View the FooGallery Homepage & DEMO](http://foo.gallery/)
28
-
29
- **Major changes in 1.4.1!**
30
-
31
- There have been major updates to the gallery templates in v1.4.1. If you have updated, please test your galleries.
32
-
33
- **Retina Support**
34
-
35
- FooGallery now comes with built-in support for higher quality thumbnails on retina-enabled displays. No more blurry thumbnails, just crisp thumbnails that look amazing on all devices.
36
-
37
- **Video Support**
38
-
39
- FooGallery now supports the creation of video galleries with the [FooVideo premium extension](http://fooplugins.com/plugins/foovideo/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)! Create galleries with both images and videos mixed!
40
-
41
- **Albums Built In**
42
-
43
- Albums are now built in as an extension. Simply head over to the extensions page and activate the albums extension. A new menu item will appear that allows you to add albums just as easily as galleries.
44
-
45
- **Built For Developers**
46
-
47
- FooGallery was designed to be the most developer-friendly gallery plugin available for WordPress. It was also built on top of a solid extension framework, which means different functionality is separated out into different areas in the codebase. It also means the core plugin is lightweight, but still allowing for the most flexibility.
48
-
49
- **Features**
50
-
51
- * Retina thumbnail support
52
- * Add videos to galleries (via the [FooVideo premium extension](http://fooplugins.com/plugins/foovideo/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo))
53
- * Albums built-in! (Activate the albums extension)
54
- * Gallery custom post type
55
- * Use built-in media library to manage images
56
- * Drag n Drop reordering of images and galleries
57
- * Built-in gallery templates
58
- * Built-in support for FooBox
59
- * Built-in Extensions Store
60
- * Built on an extension framework
61
- * Custom CSS for both galleries and albums
62
- * Copy to clipboard shortcodes
63
- * Visual shortcodes in rich text editor
64
- * Gallery picker to insert shortcode
65
- * Page usage metabox with one-click gallery page creation
66
- * NextGen importer tool (albums and galleries)
67
- * Multisite Support
68
- * plus many, many more
69
-
70
- **Gallery Demos**
71
-
72
- * Responsive Image Gallery - [demo](http://foo.gallery/demos/responsive-image-gallery/)
73
- * FooGrid Image Gallery - [light demo](http://foo.gallery/demos/foogrid-light/) - [dark demo](http://foo.gallery/demos/foogrid-dark/)
74
- * Image Viewer Gallery - [light demo](http://foo.gallery/demos/image-viewer-gallery/) - [dark demo](http://foo.gallery/demos/image-viewer-gallery-dark/)
75
- * Masonry Gallery - [demo](http://foo.gallery/demos/masonry-image-gallery/)
76
- * Simple Portfolio - [demo](http://foo.gallery/demos/simple-portfolio/)
77
- * Justified Gallery ([http://miromannino.github.io/Justified-Gallery](http://miromannino.github.io/Justified-Gallery/)) - [demo](http://foo.gallery/demos/justified-gallery/)
78
- * Single Thumbnail Gallery - [demo](http://foo.gallery/demos/single-thumbnail-gallery/)
79
- * Polaroid Gallery - [demo](http://foo.gallery/demos/polaroid-image-gallery/)
80
- * Cube Gallery - [demo](http://foo.gallery/demos/cube-gallery/) - [Buy Now!](http://fooplugins.com/plugins/cube-gallery-template/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
81
- * Mixed (Images + Videos) - [demo](http://foo.gallery/demos/mixed/)
82
-
83
- **Caption Demos**
84
-
85
- * Simple Hover Captions - [demo](http://foo.gallery/demos/captions-simple-hover/)
86
- * Always Visible Captions - [demo](http://foo.gallery/demos/responsive-image-gallery-captions/)
87
- * Drop Captions - [demo](http://foo.gallery/demos/captions-drop/)
88
- * Fade Captions - [demo](http://foo.gallery/demos/captions-fade/)
89
- * Push Captions - [demo](http://foo.gallery/demos/captions-push/)
90
-
91
- **Album Demos**
92
-
93
- * Responsive Album Layout - [demo](http://foo.gallery/demos/responsive-album-layout/)
94
- * All-in-one Stack Album - [demo](http://foo.gallery/demos/all-in-one-stack-album/)
95
-
96
- **Videos Demos**
97
-
98
- * Vertical Video Slider - [demo](http://foo.gallery/demos/video-slider-vertical/) - [full-width demo](http://foo.gallery/demos/video-slider-vertical-full-width/)
99
- * Horizontal Video Slider - [demo](http://foo.gallery/demos/video-slider-horizontal/) - [custom colors](http://foo.gallery/demos/video-slider-custom-theme/)
100
-
101
- **Extensions**
102
-
103
- * [FooVideo](http://fooplugins.com/plugins/foovideo/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
104
- * Albums (bundled)
105
- * Default Gallery Templates (bundled)
106
- * NextGen Gallery and Album Importer (bundled)
107
- * [FooGrid Gallery](http://fooplugins.com/plugins/foogrid/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
108
- * [Cube Gallery](http://fooplugins.com/plugins/cube-gallery-template/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
109
- * [Custom Branding](http://fooplugins.com/plugins/foogallery-branding/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
110
- * [FooBox PRO Lightbox](http://fooplugins.com/plugins/foobox/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
111
- * [FooBox FREE Lightbox](http://wordpress.org/plugins/foobox-image-lightbox)
112
- * [Owl Carousel Template](http://wordpress.org/plugins/foogallery-owl-carousel-template/)
113
- * [ZOOM Template](http://wordpress.org/plugins/foogallery-zoom-template/)
114
- * [Thirsty Affiliates](http://wordpress.org/plugins/thirstyaffiliates-for-foogallery-extension/)
115
- * [Polaroid Template](https://github.com/fooplugins/foogallery-polaroid-template)
116
- * [Use Media Menu](https://github.com/fooplugins/foogallery-media-menu)
117
- * Build your own!!!
118
-
119
- **Documentation**
120
-
121
- * [FooGallery 101](http://docs.fooplugins.com/foogallery/foogallery-101/)
122
- * [Developer 101](http://docs.fooplugins.com/foogallery/foogallery-developers-101/)
123
- * [Actions and Filters](http://docs.fooplugins.com/foogallery/actions-filters/)
124
-
125
- **Contribute**
126
-
127
- FooGallery is hosted on [GitHub](https://github.com/fooplugins/foogallery). If you find a bug, please [create an issue](https://github.com/fooplugins/foogallery/issues).
128
-
129
- ## Installation ##
130
-
131
- 1. Upload `foogallery` folder to the `/wp-content/plugins/` directory
132
- 2. Activate the plugin through the 'Plugins' menu in WordPress
133
- 3. You will be redirected to the FooGallery Help page to get your started
134
-
135
- ## Frequently Asked Questions ##
136
-
137
- ### Why are my thumbnails so blurry? ###
138
-
139
- Have you enabled retina support for your galleries? Displays with retina support can show better quality thumbnails when retina support is enabled. To enable retina support, edit the gallery and locate the Retina Support metabox.
140
-
141
- ### Can I add videos to my galleries? ###
142
-
143
- Yes, you need to use the [FooVideo premium extension](http://fooplugins.com/plugins/foovideo/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
144
-
145
- ### How do I get albums working? ###
146
-
147
- Simply go to the FooGallery extensions page and activate the Albums extension. If you do not see a button to activate the albums extension, reload the extensions list by clicking the reload button.
148
-
149
- ### Can I build an extension? ###
150
-
151
- Hell, yes! Check out our [developer page](http://foo.gallery/developers/)
152
 
153
- ## Screenshots ##
154
 
155
- [View screenshots](https://wordpress.org/plugins/foogallery/screenshots/)
156
 
157
- ## Upgrade Notice ##
158
 
159
- Update now to support FooVideo and many other improvements!
160
-
161
- ## Changelog ##
162
-
163
- ### 1.4.5 ###
164
-
165
- * Fix : Lazy loading - scrolling galleries in certain scenarios were not loading thumbs
166
- * Fix : Galleries that were hidden on page load were not displaying correctly when shown
167
- * Fix : Default gallery settings were not being applied to new galleries
168
- * New : shortcode arguments applied to gallery for common fields
169
- * New : HTML caching is disabled by default now!
170
- * Updated to latest client side JS and CSS
171
-
172
- ### 1.4.4 ###
173
-
174
- * Fix : upgrade was calling underfined function
175
-
176
- ### 1.4.3 ###
177
-
178
- * Fix : Justify gallery template issues
179
- * Fix : Masonry gallery template issues
180
- * Fix : Caption description not hidden when supposed to
181
- * Fix : Complete rework of thumbnail dimension logic!
182
- * Fix : Redirection bug on activation
183
- * Fix : Added checks for galleries causing PHP warnings
184
- * New : Last Row setting in Justify gallery template
185
- * New : Alignment setting in Simple Portfolio gallery template
186
- * New : Added more checks after load to ensure gallery layout is correct
187
- * Updated to Freemius SDK 1.2.2.10
188
- * Updated to latest client side JS and CSS
189
-
190
- ### 1.3.28 ###
191
-
192
- * Complete rewrite of the built-in gallery templates
193
- * New : lazy loading
194
- * New : simple pagination
195
- * New : Live Previews when editing a gallery
196
- * 260+ updates, changes and bug fixes
197
-
198
- ### 1.3.8 ###
199
-
200
- * New : Built in support for FooBox, fixing a lot of issues where FooBox option is not available
201
- * Fix : More reliable extension active status on extensions listing
202
- * Fix : More obvious wording for 3rd party plugins when they are not installed
203
-
204
- ### 1.3.7 ###
205
-
206
- * Fix : Activation redirect bug showing "Sorry, you are not allowed to access this page."
207
-
208
- ### 1.3.6 ###
209
-
210
- * New : Freemius integration!
211
- * New : Added support for the Responsive Lightbox by dFactory
212
- * New : New custom class field for an attachment
213
- * New : Added more system info for better debugging when there are server issues
214
- * Fix : Visual editor FooGallery edit button
215
- * Fix : Image Viewer hover effect none now works as expected
216
- * Fix : Disable HTML caching for randomly ordered galleries
217
-
218
- ### 1.2.20 ###
219
-
220
- * New : Force Use Original thumb setting on gallery edit page
221
- * Fix : PHP warning from thumbnail class since 1.2.19
222
-
223
- ### 1.2.19 ###
224
-
225
- * New : Gallery output caching! Saves database requests improving load time
226
- * New : Gallery usage column in admin gallery listing
227
- * New : Better support for animated gifs
228
- * New : Hover icons retina support
229
- * New : Uninstall button on settings
230
- * New : Save thumb dimensions per attachment. (needed in future versions)
231
- * Fix : Extensions refactor and many issues resolved
232
- * Fix : Better retina support for all templates
233
- * Fix : Colorize / Greyscale CSS filters
234
- * Fix : Even better wpthumb compatibility
235
-
236
- ### 1.2.18 ###
237
-
238
- * Fix : Handle no settings in retina metabox
239
-
240
- ### 1.2.17 ###
241
-
242
- * New : Retina support - metabox per gallery and default settings
243
- * New : Attachment datasources - backend changes for how images are used in a gallery. (This will allow for new external sources in the future)
244
- * New : Caption color settings in Simple portfolio gallery template
245
- * New : Updated to latest Justified Gallery
246
- * Fix : Better wpthumb compatibility
247
-
248
- ### 1.2.16 ###
249
-
250
- * Fix : Yoast SEO Sitemaps fatal error with deleted galleries
251
- * Fix : Updating pages with deleted galleries throws php warnings
252
-
253
- ### 1.2.15 ###
254
-
255
- * Fix : Album admin CSS issues in WP 4.6
256
- * Fix : Masonry layout issues in WP 4.6
257
- * Fix : Media attachment fields not updating
258
- * Fix : Better support for IE10
259
- * New : Thumbnail generation test admin notice and settings
260
-
261
- ### 1.2.13 ###
262
-
263
- * Fix : Shortcode replacing content in visual editor
264
- * Fix : Gallery hover effect of None being ignored
265
- * New : ImageViewer language settings for 'Prev', 'Next' & 'of'
266
- * New : Setting to use original thumbnails if available
267
-
268
- ### 1.2.12 ###
269
-
270
- * Fix : Simple Portfolio missing captions fix
271
-
272
- ### 1.2.11 ###
273
-
274
- * Fix : Simple Portfolio undefined function fix
275
-
276
- ### 1.2.10 ###
277
-
278
- * New : support for multiple admin JS and CSS assets for gallery templates
279
- * New : Added setting to choose Caption Description source
280
- * New : Crop position can be chosen for attachments
281
- * New : Albums gallery details modal for setting a gallery URL
282
- * New : Better shortcode preview in editor
283
- * New : Editor button now supported if multiple editors exist
284
- * Fix : Better No-Link support for gallery templates
285
- * Fix : Compatible with Unyson plugin
286
- * Fix : Compatible with Advanced Custom Fields
287
- * Fix : Simple Portfolio fixes and tweaks
288
- * Fix : ImageViewer fixes and better browser compatibility
289
- * Fix : Changed assets enqueue version to rather use extension version
290
- * Fix : Album URL fix for permalinks with no trailing slashes
291
-
292
- ### 1.2.9 ###
293
- * New : Added Image Viewer gallery template
294
- * New : Caption support for default template
295
- * New : Yoast SEO gallery image support!
296
- * New : Responsive options for Masonry gallery
297
- * New : change gallery URL slug for albums
298
- * New : setting to turn off loading animation in default gallery
299
- * New : French translation
300
- * Fix : Support for WP 4.4
301
- * Fix : All templates - moved all jQuery ready events to vanilla JS
302
- * Fix : Many gallery template tweaks
303
- * Fix : allow no default to be chosen in settings
304
-
305
- ### 1.2.8 ###
306
- * Works now with Polylang translation plugin
307
- * CSS Updates & enhancements to all gallery templates
308
- * Password protected galleries now work as expected
309
- * Ability to hide WYSIWYG editor button
310
- * Updated WPThumb
311
- * Multiple bug fixes and improvements
312
-
313
- ### 1.2.7 ###
314
- * Bug fixes for 1.2.6 release
315
- * Added 2 new settings to Justified Gallery template (maxRowHeight + Caption Source)
316
-
317
- ### 1.2.6 ###
318
- * CSS load optimizations
319
- * Updates and tweaks on all built-in gallery templates
320
- * More robust extension loading
321
- * More robust upgrades to FooBox PRO
322
- * Improved copy-to-clipboard
323
- * Added more hover effects
324
- * Support for FooVideo
325
-
326
- ### 1.2.5 ###
327
- * Fix for extensions being empty
328
- * Added support for Multi-site
329
- * Added esc_url to all places where url is rendered
330
- * Updated to latest Justified Gallery v3.5.4
331
-
332
- ### 1.2.4 ###
333
- * Many album template updates, enhancements and fixes
334
- * Many gallery template tweaks and fixes
335
- * Sort order settings for galleries and albums
336
- * Added new Single Thumbnail Gallery template
337
-
338
- ### 1.2.1 ###
339
- * Added setting to choose default gallery to copy settings from
340
- * Fixed bug #45 - gallery fields not showing onload
341
- * replaced minicolors with spectrum colorpicker
342
- * Allow gallery fields to have a suffix
343
- * Added function to render galleries "foogallery_render_gallery( $gallery_id )"
344
-
345
- ### 1.2.0 ###
346
- * Added albums extension
347
- * Added custom CSS metaboxes
348
- * Updated Nextgen importer
349
- * Fixed many bugs
350
-
351
- ### 1.1.8.2 ###
352
- * Fixed "edit gallery" CSS with WP 4.0
353
-
354
- ### 1.1.8.1 ###
355
- * Fixed "insert gallery" CSS with WP 4.0
356
-
357
- ### 1.1.8 ###
358
- * Added 2 new gallery templates
359
- * Added 10+ actions and filters for more customization
360
- * Countless bug fixes and enhancements
361
-
362
- ### 1.1.7 ###
363
- * first version!
1
+ ![Plugin Banner](https://s3.amazonaws.com/foogallery/foogallery-large.png)
2
 
3
+ Why choose FooGallery? Stunning gallery layouts, responsive, retina-ready, lightning fast, easy to use.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
+ [FooGallery Homepage](http://foo.gallery/)
6
 
7
+ [FooGallery Demos](http://foo.gallery/demos)
8
 
9
+ [FooGallery on wordpress.org](https://wordpress.org/plugins/foogallery/)
10
 
11
+ [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fooplugins/foogallery/badges/quality-score.png?b=develop)](https://scrutinizer-ci.com/g/fooplugins/foogallery/?branch=develop)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.txt CHANGED
@@ -3,16 +3,16 @@ Contributors: bradvin, steveush, fooplugins, freemius
3
  Donate link: http://fooplugins.com
4
  Tags: gallery, image gallery, photo gallery, responsive, album, media gallery, masonry gallery, portfolio, justified image gallery, video gallery, photography, photographer, retina
5
  Requires at least: 3.9
6
- Tested up to: 4.9.1
7
  Stable tag: trunk
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
- FooGallery is the best image gallery plugin for WordPress. Why? Stunning gallery layouts, responsive, retina-ready, lightning fast, easy to use.
12
 
13
  == Description ==
14
 
15
- FooGallery is the best image gallery plugin for WordPress. Why? Stunning gallery layouts, responsive, retina-ready, lightning fast, easy to use. Built to be highly configurable and extensible for developers or freelancers.
16
 
17
  [View the FooGallery Homepage & DEMO](http://foo.gallery/)
18
 
@@ -61,6 +61,8 @@ FooGallery was designed to be the most developer-friendly gallery plugin availab
61
  **PRO Features**
62
 
63
  * 11 Beautiful Hover Effect Presets
 
 
64
  * Polaroid PRO Gallery Template
65
  * Grid PRO Gallery Template
66
  * Advanced Pagination
@@ -98,8 +100,6 @@ FooGallery was designed to be the most developer-friendly gallery plugin availab
98
  * [Custom Branding](http://fooplugins.com/plugins/foogallery-branding/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
99
  * [FooBox PRO Lightbox](http://fooplugins.com/plugins/foobox/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
100
  * [FooBox FREE Lightbox](http://wordpress.org/plugins/foobox-image-lightbox)
101
- * [ZOOM Template](http://wordpress.org/plugins/foogallery-zoom-template/)
102
- * [Use Media Menu](https://github.com/fooplugins/foogallery-media-menu)
103
  * Build your own!!!
104
 
105
  **Documentation**
@@ -122,7 +122,7 @@ FooGallery is hosted on [GitHub](https://github.com/fooplugins/foogallery). If y
122
 
123
  = Why are my thumbnails so blurry? =
124
 
125
- Have you enabled retina support for your galleries? Displays with retina support can show better quality thumbnails when retina support is enabled. To enable retina support, edit the gallery and locate the Retina Support metabox.
126
 
127
  = Can I add videos to my galleries? =
128
 
@@ -146,10 +146,25 @@ Hell, yes! Check out our [developer page](http://foo.gallery/developers/)
146
 
147
  == Upgrade Notice ==
148
 
149
- Update now to support FooVideo and many other improvements!
150
 
151
  == Changelog ==
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  = 1.4.15 =
154
 
155
  * Fix : All-In-One stack album fatal error when 2 albums on same page
@@ -159,7 +174,7 @@ Update now to support FooVideo and many other improvements!
159
  * Fix : All-In-One stack album now uses the gallery featured image
160
  * Added more position options for Single Thumbnail Gallery
161
  * Added paging output setting
162
- * Update to latest client side JS 1.0.19
163
 
164
  = 1.4.12 =
165
 
3
  Donate link: http://fooplugins.com
4
  Tags: gallery, image gallery, photo gallery, responsive, album, media gallery, masonry gallery, portfolio, justified image gallery, video gallery, photography, photographer, retina
5
  Requires at least: 3.9
6
+ Tested up to: 4.9.4
7
  Stable tag: trunk
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
+ Why choose FooGallery? Stunning gallery layouts, responsive, retina-ready, lightning fast, easy to use.
12
 
13
  == Description ==
14
 
15
+ Why choose FooGallery? Stunning gallery layouts, responsive, retina-ready, lightning fast, easy to use. Built to be highly configurable and extensible for developers or freelancers.
16
 
17
  [View the FooGallery Homepage & DEMO](http://foo.gallery/)
18
 
61
  **PRO Features**
62
 
63
  * 11 Beautiful Hover Effect Presets
64
+ * Media Tags and Categories
65
+ * Gallery Filtering using Media Tags or Categories
66
  * Polaroid PRO Gallery Template
67
  * Grid PRO Gallery Template
68
  * Advanced Pagination
100
  * [Custom Branding](http://fooplugins.com/plugins/foogallery-branding/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
101
  * [FooBox PRO Lightbox](http://fooplugins.com/plugins/foobox/?utm_source=foogalleryplugin&utm_medium=foogallerylink&utm_campaign=foogallery_wprepo)
102
  * [FooBox FREE Lightbox](http://wordpress.org/plugins/foobox-image-lightbox)
 
 
103
  * Build your own!!!
104
 
105
  **Documentation**
122
 
123
  = Why are my thumbnails so blurry? =
124
 
125
+ Have you enabled retina support for your galleries? To enable retina support, edit the gallery and locate the Retina Support metabox.
126
 
127
  = Can I add videos to my galleries? =
128
 
146
 
147
  == Upgrade Notice ==
148
 
149
+ Update now to get all the latest features, bug fixes and improvements!
150
 
151
  == Changelog ==
152
 
153
+ = 1.4.25 =
154
+
155
+ * New : Retina support for albums!
156
+ * New : Default crop position setting for attachments
157
+ * New : Speed up gallery previews in wp-admin
158
+ * New : Caption support for Responsive Lightbox by dFactory
159
+ * Fix : Extension loading issues on certain installs
160
+ * Fix : Shortcode copy-to-clipboard metabox works again
161
+ * Fix : Bugs fixes for paging, filtering, FooBox and more
162
+ * Fix : Ensure jquery-ui-sortable is loaded on edit page for some installs
163
+ * Fix : All-In-One Stack Album layout bugs
164
+ * Fix : Reworked extensions listing page logic
165
+ * Update : FooGallery client side 1.0.23
166
+ * Update : Freemius SDK 1.2.4
167
+
168
  = 1.4.15 =
169
 
170
  * Fix : All-In-One stack album fatal error when 2 albums on same page
174
  * Fix : All-In-One stack album now uses the gallery featured image
175
  * Added more position options for Single Thumbnail Gallery
176
  * Added paging output setting
177
+ * Update to latest client side JS 1.0.20
178
 
179
  = 1.4.12 =
180
 
css/admin-foogallery.css CHANGED
@@ -459,4 +459,36 @@ button[data-balloon]{overflow:visible}[data-balloon]{position:relative}[data-bal
459
 
460
  .foogallery_template_field_type-thumb_size_no_crop label+input {
461
  margin-left: 5px;
462
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
459
 
460
  .foogallery_template_field_type-thumb_size_no_crop label+input {
461
  margin-left: 5px;
462
+ }
463
+
464
+ /* changes to the media modal */
465
+ .compat-item .compat-attachment-fields th.label label span {
466
+ text-align: left;
467
+ }
468
+
469
+ .compat-item .compat-attachment-fields td.field {
470
+ width: 100%;
471
+ margin: 0;
472
+ }
473
+
474
+ .media-modal .attachments-browser .media-toolbar {
475
+ right: 500px;
476
+ }
477
+ .media-modal .attachments {
478
+ right: 500px;
479
+ }
480
+ .media-modal .media-sidebar {
481
+ width: 467px;
482
+ }
483
+
484
+ @media (max-width: 900px) {
485
+ .media-modal .attachments-browser .media-toolbar {
486
+ right: 300px;
487
+ }
488
+ .media-modal .attachments {
489
+ right: 300px;
490
+ }
491
+ .media-modal .media-sidebar {
492
+ width: 267px;
493
+ }
494
+ }
extensions/albums/admin/class-metaboxes.php CHANGED
@@ -241,14 +241,14 @@ if ( ! class_exists( 'FooGallery_Admin_Album_MetaBoxes' ) ) {
241
  $shortcode = $album->shortcode();
242
  ?>
243
  <p class="foogallery-shortcode">
244
- <input type="text" id="foogallery-copy-shortcode" size="<?php echo strlen( $shortcode ); ?>" value="<?php echo htmlspecialchars( $shortcode ); ?>" readonly="readonly" />
245
  </p>
246
  <p>
247
  <?php _e( 'Paste the above shortcode into a post or page to show the album.', 'foogallery' ); ?>
248
  </p>
249
  <script>
250
  jQuery(function($) {
251
- var shortcodeInput = document.querySelector('#foogallery-copy-shortcode');
252
  shortcodeInput.addEventListener('click', function () {
253
  try {
254
  // select the contents
241
  $shortcode = $album->shortcode();
242
  ?>
243
  <p class="foogallery-shortcode">
244
+ <input type="text" id="foogallery_copy_shortcode" size="<?php echo strlen( $shortcode ); ?>" value="<?php echo htmlspecialchars( $shortcode ); ?>" readonly="readonly" />
245
  </p>
246
  <p>
247
  <?php _e( 'Paste the above shortcode into a post or page to show the album.', 'foogallery' ); ?>
248
  </p>
249
  <script>
250
  jQuery(function($) {
251
+ var shortcodeInput = document.querySelector('#foogallery_copy_shortcode');
252
  shortcodeInput.addEventListener('click', function () {
253
  try {
254
  // select the contents
extensions/albums/album-default.php CHANGED
@@ -4,6 +4,7 @@
4
  */
5
  global $current_foogallery_album;
6
  global $current_foogallery_album_arguments;
 
7
  $gallery = foogallery_album_get_current_gallery();
8
  $alignment = foogallery_album_template_setting( 'alignment', 'alignment-left' );
9
  $foogallery = false;
@@ -44,6 +45,7 @@ if ( false !== $foogallery ) {
44
  <ul class="foogallery-album-gallery-list <?php echo $alignment; ?>">
45
  <?php
46
  foreach ( $current_foogallery_album->galleries() as $gallery ) {
 
47
  if (!empty($gallery->attachment_ids)) {
48
  $attachment = $gallery->featured_attachment();
49
 
4
  */
5
  global $current_foogallery_album;
6
  global $current_foogallery_album_arguments;
7
+ global $current_foogallery;
8
  $gallery = foogallery_album_get_current_gallery();
9
  $alignment = foogallery_album_template_setting( 'alignment', 'alignment-left' );
10
  $foogallery = false;
45
  <ul class="foogallery-album-gallery-list <?php echo $alignment; ?>">
46
  <?php
47
  foreach ( $current_foogallery_album->galleries() as $gallery ) {
48
+ $current_foogallery = $gallery;
49
  if (!empty($gallery->attachment_ids)) {
50
  $attachment = $gallery->featured_attachment();
51
 
extensions/albums/album-stack.php CHANGED
@@ -4,6 +4,7 @@
4
  */
5
  global $current_foogallery_album;
6
  global $current_foogallery_album_arguments;
 
7
  $args = foogallery_album_template_setting( 'thumbnail_dimensions', array() );
8
  $lightbox = foogallery_album_template_setting( 'lightbox', 'unknown' );
9
  $random_angle = foogallery_album_template_setting( 'random_angle', 'false' );
@@ -33,15 +34,17 @@ if ( !function_exists( 'foogallery_album_all_in_one_stack_render_gallery_attachm
33
  <ul id="foogallery-stack-album-<?php echo $current_foogallery_album->ID; ?>" class="tp-grid">
34
  <?php
35
  foreach ( $current_foogallery_album->galleries() as $gallery ) {
36
- $featured_image = $gallery->featured_attachment();
 
 
 
37
 
38
  foreach ( $gallery->attachments() as $attachment ) {
39
- if ( $featured_image->ID !== $attachment->ID ) {
40
- //force the featured image to be last!
41
  foogallery_album_all_in_one_stack_render_gallery_attachment( $gallery, $attachment, $args, $lightbox );
42
  }
43
  }
44
- foogallery_album_all_in_one_stack_render_gallery_attachment( $gallery, $featured_image, $args, $lightbox );
45
  }
46
  ?>
47
  </ul>
4
  */
5
  global $current_foogallery_album;
6
  global $current_foogallery_album_arguments;
7
+ global $current_foogallery;
8
  $args = foogallery_album_template_setting( 'thumbnail_dimensions', array() );
9
  $lightbox = foogallery_album_template_setting( 'lightbox', 'unknown' );
10
  $random_angle = foogallery_album_template_setting( 'random_angle', 'false' );
34
  <ul id="foogallery-stack-album-<?php echo $current_foogallery_album->ID; ?>" class="tp-grid">
35
  <?php
36
  foreach ( $current_foogallery_album->galleries() as $gallery ) {
37
+ $current_foogallery = $gallery;
38
+ $featured_attachment = $gallery->featured_attachment();
39
+ //render the featured attachment first
40
+ foogallery_album_all_in_one_stack_render_gallery_attachment( $gallery, $featured_attachment, $args, $lightbox );
41
 
42
  foreach ( $gallery->attachments() as $attachment ) {
43
+ if ( $featured_attachment->ID !== $attachment->ID ) {
44
+ //render all but the featured attachment
45
  foogallery_album_all_in_one_stack_render_gallery_attachment( $gallery, $attachment, $args, $lightbox );
46
  }
47
  }
 
48
  }
49
  ?>
50
  </ul>
extensions/albums/js/album-stack.js CHANGED
@@ -63,7 +63,9 @@ if (!window.FooGalleryStackAlbumModernizr) {
63
  // ======================= imagesLoaded Plugin ===============================
64
  // https://github.com/desandro/imagesloaded
65
 
66
- // $('#my-container').imagesLoaded(myFunction)
 
 
67
  // execute a callback when all images have loaded.
68
  // needed because .load() doesn't work on cached images
69
 
@@ -76,7 +78,7 @@ if (!window.FooGalleryStackAlbumModernizr) {
76
  // blank image data-uri bypasses webkit log warning (thx doug jones)
77
  var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
78
 
79
- $.fn.imagesLoaded = function( callback ) {
80
  var $this = this,
81
  deferred = $.isFunction($.Deferred) ? $.Deferred() : 0,
82
  hasNotify = $.isFunction(deferred.notify),
@@ -130,7 +132,7 @@ if (!window.FooGalleryStackAlbumModernizr) {
130
  }
131
 
132
  // cache image and its state for future calls
133
- $.data( img, 'imagesLoaded', { isBroken: isBroken, src: img.src } );
134
 
135
  // trigger deferred progress method if present
136
  if ( hasNotify ) {
@@ -140,7 +142,7 @@ if (!window.FooGalleryStackAlbumModernizr) {
140
  // call doneLoading and clean listeners if all images are loaded
141
  if ( $images.length === loaded.length ){
142
  setTimeout( doneLoading );
143
- $images.unbind( '.imagesLoaded' );
144
  }
145
  }
146
 
@@ -148,7 +150,7 @@ if (!window.FooGalleryStackAlbumModernizr) {
148
  if ( !$images.length ) {
149
  doneLoading();
150
  } else {
151
- $images.bind( 'load.imagesLoaded error.imagesLoaded', function( event ){
152
  // trigger imgLoaded
153
  imgLoaded( event.target, event.type === 'error' );
154
  }).each( function( i, el ) {
@@ -156,7 +158,7 @@ if (!window.FooGalleryStackAlbumModernizr) {
156
 
157
  // find out if this image has been already checked for status
158
  // if it was, and src has not changed, call imgLoaded on it
159
- var cached = $.data( el, 'imagesLoaded' );
160
  if ( cached && cached.src === src ) {
161
  imgLoaded( el, cached.isBroken );
162
  return;
@@ -237,7 +239,7 @@ if (!window.FooGalleryStackAlbumModernizr) {
237
 
238
  // preload images
239
  var self = this;
240
- this.el.imagesLoaded( function() {
241
  self.options.onLoad();
242
  self._layout();
243
  self._initEvents();
@@ -408,35 +410,41 @@ if (!window.FooGalleryStackAlbumModernizr) {
408
  _setInitialStyle : function() {
409
  for (var j =0, pile_len = this.pilesArr.length; j < pile_len; j++){
410
 
411
- var p = this.pilesArr[j];
 
 
 
 
 
 
412
 
413
  for( var i = 0, len = p.elements.length; i < len; ++i ) {
414
 
415
- var $el = $( p.elements[i].el ),
416
- styleCSS = { transform : 'rotate(0deg)' };
417
 
418
  this._applyInitialTransition( $el );
419
 
420
- if( i === len - 2 ) {
 
 
 
421
  styleCSS = { transform : 'rotate(' + this.options.pileAngles + 'deg)' };
422
  }
423
  else if( i === len - 3 ) {
424
  styleCSS = { transform : 'rotate(-' + this.options.pileAngles + 'deg)' };
425
  }
426
- else if( i !== len - 1 ) {
427
  var extraStyle = { visibility : 'hidden' };
428
  $el.css( extraStyle ).data( 'extraStyle', extraStyle );
429
  }
430
- else if( p.name.substr( 0, 6 ) !== 'nopile' ) {
431
- $el.data( 'front', true ).append( '<div class="tp-title-cover"><div class="tp-title"><span>' + p.name + '</span><span>' + len + '</span></div></div>' );
432
- }
433
 
434
  $el.css( styleCSS ).data( {
435
  initialStyle : styleCSS,
436
  pileName : p.name,
437
  pileCount : len,
438
  shadow : $el.css( 'box-shadow' ),
439
- isPile : p.name.substr( 0, 6 ) === 'nopile' ? false : true
440
  } );
441
 
442
  }
@@ -461,9 +469,9 @@ if (!window.FooGalleryStackAlbumModernizr) {
461
 
462
  var p = this.pilesArr[j],
463
 
464
- //for( var pile in this.piles ) {
465
- //
466
- // var p = this.piles[pile],
467
  stepW = this.itemSize.width + this.options.gutter,
468
 
469
  accumIL = 0, accumIT = 0, il, it;
@@ -540,8 +548,9 @@ if (!window.FooGalleryStackAlbumModernizr) {
540
  // the position of the items will influence the final margin left value and height for the ul
541
  // center the ul
542
  lastItemTop = this.spread ? lastItemTop : accumT;
 
543
  this.el.css( {
544
- marginLeft : ml,
545
  height : lastItemTop + this.itemSize.height
546
  } );
547
 
@@ -553,23 +562,17 @@ if (!window.FooGalleryStackAlbumModernizr) {
553
  }
554
 
555
  // final style
556
- var fs;
557
 
558
  for (var j =0, pile_len = this.pilesArr.length; j < pile_len; j++){
559
 
560
  var p = this.pilesArr[j],
561
-
562
- //for( var pile in this.piles ) {
563
- //
564
- // var p = this.piles[ pile ],
565
-
566
  cnt = 0;
567
 
568
  for( var i = 0, len = p.elements.length; i < len; ++i ) {
569
 
570
  var elem = p.elements[i],
571
  $item = $( elem.el ),
572
- $img = $item.find( 'img' ),
573
  styleCSS = p.name === this.pileName ? {
574
  zIndex : 9999,
575
  visibility : 'visible',
@@ -581,12 +584,13 @@ if (!window.FooGalleryStackAlbumModernizr) {
581
 
582
  if( p.name === this.pileName ) {
583
 
584
- if( $item.data( 'front' ) ) {
585
- $item.find( 'div.tp-title' ).hide();
 
586
  }
587
 
588
- if( i < len - 1 ) {
589
- $img.css( 'visibility', 'visible' );
590
  }
591
 
592
  fs = elem.finalPosition;
@@ -602,9 +606,6 @@ if (!window.FooGalleryStackAlbumModernizr) {
602
  }
603
 
604
  }
605
- else if( i < len - 1 ) {
606
- $img.css( 'visibility', 'hidden' );
607
- }
608
 
609
  $item.css( styleCSS );
610
 
@@ -645,7 +646,11 @@ if (!window.FooGalleryStackAlbumModernizr) {
645
 
646
  }
647
 
648
- this.el.css( 'height', fs.top + this.itemSize.height );
 
 
 
 
649
 
650
  },
651
  _closePile : function() {
@@ -666,11 +671,6 @@ if (!window.FooGalleryStackAlbumModernizr) {
666
  for (var j =0, pile_len = this.pilesArr.length; j < pile_len; j++){
667
 
668
  var p = this.pilesArr[j],
669
-
670
- //for( var pile in this.piles ) {
671
- //
672
- // var p = this.piles[ pile ],
673
-
674
  cnt = 0;
675
 
676
  for( var i = 0, len = p.elements.length; i < len; ++i ) {
@@ -704,7 +704,7 @@ if (!window.FooGalleryStackAlbumModernizr) {
704
  return;
705
  }
706
 
707
- var $el = $( this ), extraStyle = $el.data( 'extraStyle' );
708
 
709
  // hack: remove box-shadow while animating to prevent the shadow stack effect
710
  $el.css( 'box-shadow', $el.data( 'shadow' ) );
@@ -714,7 +714,7 @@ if (!window.FooGalleryStackAlbumModernizr) {
714
  self._applyInitialTransition( $el );
715
  }
716
  else {
717
- $el.css( $el.data( 'initialStyle' ) );
718
  }
719
 
720
  if( extraStyle ) {
@@ -725,6 +725,8 @@ if (!window.FooGalleryStackAlbumModernizr) {
725
 
726
  if( $el.data( 'front' ) ) {
727
  $el.find( 'div.tp-title' ).show();
 
 
728
  }
729
 
730
  if( cnt === $el.data( 'pileCount' ) ) {
@@ -739,9 +741,10 @@ if (!window.FooGalleryStackAlbumModernizr) {
739
  }
740
 
741
  var $el = $( this );
742
-
743
- if( $el.index() < len - 1 ) {
744
- $el.find( 'img' ).css( 'visibility', 'visible' );
 
745
  }
746
 
747
  if( self.support ) {
@@ -759,7 +762,11 @@ if (!window.FooGalleryStackAlbumModernizr) {
759
  this.pileName = '';
760
 
761
  // update ul height
762
- this.el.css( 'height', fs.top + this.itemSize.height );
 
 
 
 
763
 
764
  }
765
 
@@ -820,7 +827,7 @@ if (!window.FooGalleryStackAlbumModernizr) {
820
  if ( !instance ) {
821
 
822
  logError( "cannot call methods on stapel prior to initialization; " +
823
- "attempted to call method '" + options + "'" );
824
  return;
825
 
826
  }
63
  // ======================= imagesLoaded Plugin ===============================
64
  // https://github.com/desandro/imagesloaded
65
 
66
+ // renamed from imagesLoaded to foogalleryImagesLoaded to avoid conflicts
67
+
68
+ // $('#my-container').foogalleryImagesLoaded(myFunction)
69
  // execute a callback when all images have loaded.
70
  // needed because .load() doesn't work on cached images
71
 
78
  // blank image data-uri bypasses webkit log warning (thx doug jones)
79
  var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
80
 
81
+ $.fn.foogalleryImagesLoaded = function( callback ) {
82
  var $this = this,
83
  deferred = $.isFunction($.Deferred) ? $.Deferred() : 0,
84
  hasNotify = $.isFunction(deferred.notify),
132
  }
133
 
134
  // cache image and its state for future calls
135
+ $.data( img, 'foogalleryImagesLoaded', { isBroken: isBroken, src: img.src } );
136
 
137
  // trigger deferred progress method if present
138
  if ( hasNotify ) {
142
  // call doneLoading and clean listeners if all images are loaded
143
  if ( $images.length === loaded.length ){
144
  setTimeout( doneLoading );
145
+ $images.unbind( '.foogalleryImagesLoaded' );
146
  }
147
  }
148
 
150
  if ( !$images.length ) {
151
  doneLoading();
152
  } else {
153
+ $images.bind( 'load.foogalleryImagesLoaded error.foogalleryImagesLoaded', function( event ){
154
  // trigger imgLoaded
155
  imgLoaded( event.target, event.type === 'error' );
156
  }).each( function( i, el ) {
158
 
159
  // find out if this image has been already checked for status
160
  // if it was, and src has not changed, call imgLoaded on it
161
+ var cached = $.data( el, 'foogalleryImagesLoaded' );
162
  if ( cached && cached.src === src ) {
163
  imgLoaded( el, cached.isBroken );
164
  return;
239
 
240
  // preload images
241
  var self = this;
242
+ this.el.foogalleryImagesLoaded( function() {
243
  self.options.onLoad();
244
  self._layout();
245
  self._initEvents();
410
  _setInitialStyle : function() {
411
  for (var j =0, pile_len = this.pilesArr.length; j < pile_len; j++){
412
 
413
+ var p = this.pilesArr[j], featured = false, $el, styleCSS;
414
+ for (var k = 0, kl = p.elements.length; k < kl; k++){
415
+ if ( $( p.elements[k].el ).data('featured') ){
416
+ featured = true;
417
+ break;
418
+ }
419
+ }
420
 
421
  for( var i = 0, len = p.elements.length; i < len; ++i ) {
422
 
423
+ $el = $( p.elements[i].el );
424
+ styleCSS = { transform : 'rotate(0deg)' };
425
 
426
  this._applyInitialTransition( $el );
427
 
428
+ if ( p.name.substr( 0, 6 ) !== 'nopile' && ( $el.data('featured') || ( !featured && i === 0 ) ) ){
429
+ $el.css({ zIndex: 9999 }).data( 'front', true ).append( '<div class="tp-title-cover"><div class="tp-title"><span>' + p.name + '</span><span>' + len + '</span></div></div>' );
430
+ }
431
+ else if( i === len - 2 ) {
432
  styleCSS = { transform : 'rotate(' + this.options.pileAngles + 'deg)' };
433
  }
434
  else if( i === len - 3 ) {
435
  styleCSS = { transform : 'rotate(-' + this.options.pileAngles + 'deg)' };
436
  }
437
+ else {
438
  var extraStyle = { visibility : 'hidden' };
439
  $el.css( extraStyle ).data( 'extraStyle', extraStyle );
440
  }
 
 
 
441
 
442
  $el.css( styleCSS ).data( {
443
  initialStyle : styleCSS,
444
  pileName : p.name,
445
  pileCount : len,
446
  shadow : $el.css( 'box-shadow' ),
447
+ isPile : p.name.substr(0, 6) !== 'nopile'
448
  } );
449
 
450
  }
469
 
470
  var p = this.pilesArr[j],
471
 
472
+ //for( var pile in this.piles ) {
473
+ //
474
+ // var p = this.piles[pile],
475
  stepW = this.itemSize.width + this.options.gutter,
476
 
477
  accumIL = 0, accumIT = 0, il, it;
548
  // the position of the items will influence the final margin left value and height for the ul
549
  // center the ul
550
  lastItemTop = this.spread ? lastItemTop : accumT;
551
+ this.marginLeft = ml;
552
  this.el.css( {
553
+ marginLeft : this.marginLeft,
554
  height : lastItemTop + this.itemSize.height
555
  } );
556
 
562
  }
563
 
564
  // final style
565
+ var fs, rowWidth = 0;
566
 
567
  for (var j =0, pile_len = this.pilesArr.length; j < pile_len; j++){
568
 
569
  var p = this.pilesArr[j],
 
 
 
 
 
570
  cnt = 0;
571
 
572
  for( var i = 0, len = p.elements.length; i < len; ++i ) {
573
 
574
  var elem = p.elements[i],
575
  $item = $( elem.el ),
 
576
  styleCSS = p.name === this.pileName ? {
577
  zIndex : 9999,
578
  visibility : 'visible',
584
 
585
  if( p.name === this.pileName ) {
586
 
587
+ if (elem.finalPosition.top === 0){
588
+ if (rowWidth > 0) rowWidth += this.options.gutter;
589
+ rowWidth += this.itemSize.width;
590
  }
591
 
592
+ if( $item.data( 'front' ) ) {
593
+ $item.find( 'div.tp-title' ).hide();
594
  }
595
 
596
  fs = elem.finalPosition;
606
  }
607
 
608
  }
 
 
 
609
 
610
  $item.css( styleCSS );
611
 
646
 
647
  }
648
 
649
+ //this.el.css( 'height', fs.top + this.itemSize.height );
650
+ this.el.css({
651
+ marginLeft: Math.ceil((this.elWidth - rowWidth) / 2),
652
+ height: fs.top + this.itemSize.height
653
+ });
654
 
655
  },
656
  _closePile : function() {
671
  for (var j =0, pile_len = this.pilesArr.length; j < pile_len; j++){
672
 
673
  var p = this.pilesArr[j],
 
 
 
 
 
674
  cnt = 0;
675
 
676
  for( var i = 0, len = p.elements.length; i < len; ++i ) {
704
  return;
705
  }
706
 
707
+ var $el = $( this ), extraStyle = $el.data( 'extraStyle' ), initialStyle = $el.data( 'initialStyle' );
708
 
709
  // hack: remove box-shadow while animating to prevent the shadow stack effect
710
  $el.css( 'box-shadow', $el.data( 'shadow' ) );
714
  self._applyInitialTransition( $el );
715
  }
716
  else {
717
+ $el.css( initialStyle );
718
  }
719
 
720
  if( extraStyle ) {
725
 
726
  if( $el.data( 'front' ) ) {
727
  $el.find( 'div.tp-title' ).show();
728
+ } else {
729
+ $el.css({visibility: 'hidden', zIndex: 1});
730
  }
731
 
732
  if( cnt === $el.data( 'pileCount' ) ) {
741
  }
742
 
743
  var $el = $( this );
744
+ if( $el.data( 'front' ) ) {
745
+ $el.find( 'div.tp-title' ).show();
746
+ } else {
747
+ $el.css({visibility: 'hidden', zIndex: 1});
748
  }
749
 
750
  if( self.support ) {
762
  this.pileName = '';
763
 
764
  // update ul height
765
+ //this.el.css( 'height', fs.top + this.itemSize.height );
766
+ this.el.css({
767
+ marginLeft: this.marginLeft,
768
+ height: fs.top + this.itemSize.height
769
+ });
770
 
771
  }
772
 
827
  if ( !instance ) {
828
 
829
  logError( "cannot call methods on stapel prior to initialization; " +
830
+ "attempted to call method '" + options + "'" );
831
  return;
832
 
833
  }
extensions/default-templates/default/class-default-gallery-template.php CHANGED
@@ -55,6 +55,7 @@ if ( !class_exists( 'FooGallery_Default_Gallery_Template' ) ) {
55
  'lazyload_support' => true,
56
  'mandatory_classes' => 'fg-default',
57
  'thumbnail_dimensions' => true,
 
58
  'fields' => array(
59
  array(
60
  'id' => 'thumbnail_dimensions',
55
  'lazyload_support' => true,
56
  'mandatory_classes' => 'fg-default',
57
  'thumbnail_dimensions' => true,
58
+ 'filtering_support' => true,
59
  'fields' => array(
60
  array(
61
  'id' => 'thumbnail_dimensions',
extensions/default-templates/justified/class-justified-gallery-template.php CHANGED
@@ -55,6 +55,7 @@ if ( !class_exists( 'FooGallery_Justified_Gallery_Template' ) ) {
55
  'paging_support' => true,
56
  'mandatory_classes' => 'fg-justified',
57
  'thumbnail_dimensions' => true,
 
58
  'fields' => array(
59
  array(
60
  'id' => 'thumb_height',
55
  'paging_support' => true,
56
  'mandatory_classes' => 'fg-justified',
57
  'thumbnail_dimensions' => true,
58
+ 'filtering_support' => true,
59
  'fields' => array(
60
  array(
61
  'id' => 'thumb_height',
extensions/default-templates/masonry/class-masonry-gallery-template.php CHANGED
@@ -64,6 +64,7 @@ if ( !class_exists( 'FooGallery_Masonry_Gallery_Template' ) ) {
64
  'paging_support' => true,
65
  'mandatory_classes' => 'fg-masonry',
66
  'thumbnail_dimensions' => true,
 
67
  'fields' => array(
68
  array(
69
  'id' => 'thumbnail_width',
64
  'paging_support' => true,
65
  'mandatory_classes' => 'fg-masonry',
66
  'thumbnail_dimensions' => true,
67
+ 'filtering_support' => true,
68
  'fields' => array(
69
  array(
70
  'id' => 'thumbnail_width',
extensions/default-templates/shared/css/foogallery.css CHANGED
@@ -1847,7 +1847,7 @@ only screen and (min-resolution: 2.25dppx) {
1847
  text-align: left;
1848
  }
1849
  .fg-simple_portfolio .fg-caption-desc {
1850
- text-align: justify;
1851
  }
1852
  .fg-simple_portfolio.fg-light .fg-caption,
1853
  .fg-simple_portfolio.fg-dark .fg-caption {
@@ -1885,7 +1885,7 @@ only screen and (min-resolution: 2.25dppx) {
1885
 
1886
  /* Handle Border Sizing */
1887
  .fg-simple_portfolio .fg-caption {
1888
- border-width: 10px;
1889
  }
1890
  .fg-simple_portfolio .fg-caption-title+.fg-caption-desc {
1891
  margin-top: 5px;
1847
  text-align: left;
1848
  }
1849
  .fg-simple_portfolio .fg-caption-desc {
1850
+ text-align: left;
1851
  }
1852
  .fg-simple_portfolio.fg-light .fg-caption,
1853
  .fg-simple_portfolio.fg-dark .fg-caption {
1885
 
1886
  /* Handle Border Sizing */
1887
  .fg-simple_portfolio .fg-caption {
1888
+ border-width: 0;
1889
  }
1890
  .fg-simple_portfolio .fg-caption-title+.fg-caption-desc {
1891
  margin-top: 5px;
extensions/default-templates/shared/css/foogallery.min.css CHANGED
@@ -1 +1 @@
1
- .foogallery,.foogallery *{box-sizing:border-box}.foogallery{display:block;z-index:1;font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;position:relative;line-height:0;font-size:0;width:100%;max-width:100%}.foogallery .fg-item{display:inline-block;position:relative;background-color:transparent;z-index:2;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.foogallery .fg-item-inner{display:block;position:relative;visibility:hidden;overflow:hidden;opacity:0;z-index:3;margin:0;border:solid 0 transparent}.foogallery .fg-item.fg-error,.foogallery .fg-item.fg-loading{background:no-repeat center}.foogallery .fg-item.fg-error{background-image:url(../img/image.png)}@media only screen and (-o-min-device-pixel-ratio:5/4),only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-device-pixel-ratio:1.25),only screen and (min-resolution:1.25dppx){.foogallery .fg-item.fg-error{background-image:url(../img/image@2x.png)}}@media only screen and (-o-min-device-pixel-ratio:9/4),only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-device-pixel-ratio:2.25),only screen and (min-resolution:2.25dppx){.foogallery .fg-item.fg-error{background-image:url(../img/image@3x.png)}}.foogallery .fg-item.fg-loaded{z-index:4}.foogallery .fg-loaded .fg-item-inner{visibility:visible;opacity:1;z-index:5}.foogallery .fg-error .fg-item-inner{pointer-events:none;cursor:default}.foogallery .fg-thumb{display:block;position:relative;border:none;outline:0;text-decoration:none;z-index:4}.foogallery .fg-image{display:block;position:relative;border:none;outline:0;text-decoration:none;z-index:5;max-width:none;height:auto;margin:0}.foogallery .fg-loaded .fg-thumb{z-index:6}.foogallery .fg-loaded .fg-image{z-index:7}.foogallery.fg-light .fg-item-inner{background-color:#fff;color:#333;border-color:#fff}.foogallery.fg-dark .fg-item-inner{background-color:#333;color:#fff;border-color:#333}.foogallery.fg-light .fg-item.fg-error,.foogallery.fg-light .fg-item.fg-idle,.foogallery.fg-light .fg-item.fg-loading{background-color:#eee;box-shadow:inset 0 0 0 1px #ddd}.foogallery.fg-dark .fg-item.fg-error,.foogallery.fg-dark .fg-item.fg-idle,.foogallery.fg-dark .fg-item.fg-loading{background-color:#444;box-shadow:inset 0 0 0 1px #333}.foogallery.fg-border-thin .fg-item-inner{border-width:4px}.foogallery.fg-border-medium .fg-item-inner{border-width:10px}.foogallery.fg-border-thick .fg-item-inner{border-width:16px}.foogallery.fg-light.fg-shadow-outline .fg-item-inner{box-shadow:0 0 0 1px #ddd}.foogallery.fg-dark.fg-shadow-outline .fg-item-inner{box-shadow:0 0 0 1px #222}.foogallery.fg-dark.fg-shadow-small .fg-item-inner,.foogallery.fg-light.fg-shadow-small .fg-item-inner{box-shadow:0 1px 4px 0 rgba(0,0,0,.5)}.foogallery.fg-dark.fg-shadow-medium .fg-item-inner,.foogallery.fg-light.fg-shadow-medium .fg-item-inner{box-shadow:0 1px 10px 0 rgba(0,0,0,.5)}.foogallery.fg-dark.fg-shadow-large .fg-item-inner,.foogallery.fg-light.fg-shadow-large .fg-item-inner{box-shadow:0 1px 16px 0 rgba(0,0,0,.5)}.foogallery.fg-shadow-inset-large .fg-thumb:after,.foogallery.fg-shadow-inset-medium .fg-thumb:after,.foogallery.fg-shadow-inset-small .fg-thumb:after{display:block;content:"";position:absolute;top:0;left:0;right:0;bottom:0;z-index:7}.foogallery.fg-dark.fg-shadow-inset-small .fg-thumb:after,.foogallery.fg-light.fg-shadow-inset-small .fg-thumb:after{box-shadow:inset 0 1px 4px 0 rgba(0,0,0,.3)}.foogallery.fg-dark.fg-shadow-inset-medium .fg-thumb:after,.foogallery.fg-light.fg-shadow-inset-medium .fg-thumb:after{box-shadow:inset 0 1px 10px 0 rgba(0,0,0,.3)}.foogallery.fg-dark.fg-shadow-inset-large .fg-thumb:after,.foogallery.fg-light.fg-shadow-inset-large .fg-thumb:after{box-shadow:inset 0 1px 16px 0 rgba(0,0,0,.3)}.foogallery.fg-round-full.fg-shadow-inset-large .fg-thumb:after,.foogallery.fg-round-full.fg-shadow-inset-medium .fg-thumb:after,.foogallery.fg-round-full.fg-shadow-inset-small .fg-thumb:after{border-radius:50%}.foogallery.fg-round-small .fg-item,.foogallery.fg-round-small .fg-item-inner{border-radius:5px}.foogallery.fg-round-medium .fg-item,.foogallery.fg-round-medium .fg-item-inner{border-radius:10px}.foogallery.fg-round-large .fg-item,.foogallery.fg-round-large .fg-item-inner{border-radius:15px}.foogallery.fg-round-full .fg-item,.foogallery.fg-round-full .fg-item-inner{border-radius:50%}.foogallery .fg-loader{position:absolute;top:50%;left:50%;transform:translateX(-50%) translateY(-50%);width:1em;height:1em;font-size:5px;visibility:hidden;opacity:0}.foogallery .fg-loading .fg-loader{visibility:visible;opacity:1}.fg-loading-default .fg-loader{border-radius:50%;text-indent:-9999em;-webkit-animation:loading-default 1.1s infinite ease;animation:loading-default 1.1s infinite ease}@-webkit-keyframes loading-default{0%,100%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,1),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.5),-1.8em -1.8em 0 0 rgba(130,130,130,.7)}12.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.7),1.8em -1.8em 0 0 rgba(130,130,130,1),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.5)}25%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.5),1.8em -1.8em 0 0 rgba(130,130,130,.7),2.5em 0 0 0 rgba(130,130,130,1),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}37.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.5),2.5em 0 0 0 rgba(130,130,130,.7),1.75em 1.75em 0 0 rgba(130,130,130,1),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}50%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.5),1.75em 1.75em 0 0 rgba(130,130,130,.7),0 2.5em 0 0 rgba(130,130,130,1),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}62.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.5),0 2.5em 0 0 rgba(130,130,130,.7),-1.8em 1.8em 0 0 rgba(130,130,130,1),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}75%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.5),-1.8em 1.8em 0 0 rgba(130,130,130,.7),-2.6em 0 0 0 rgba(130,130,130,1),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}87.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.5),-2.6em 0 0 0 rgba(130,130,130,.7),-1.8em -1.8em 0 0 rgba(130,130,130,1)}}@keyframes loading-default{0%,100%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,1),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.5),-1.8em -1.8em 0 0 rgba(130,130,130,.7)}12.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.7),1.8em -1.8em 0 0 rgba(130,130,130,1),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.5)}25%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.5),1.8em -1.8em 0 0 rgba(130,130,130,.7),2.5em 0 0 0 rgba(130,130,130,1),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}37.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.5),2.5em 0 0 0 rgba(130,130,130,.7),1.75em 1.75em 0 0 rgba(130,130,130,1),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}50%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.5),1.75em 1.75em 0 0 rgba(130,130,130,.7),0 2.5em 0 0 rgba(130,130,130,1),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}62.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.5),0 2.5em 0 0 rgba(130,130,130,.7),-1.8em 1.8em 0 0 rgba(130,130,130,1),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}75%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.5),-1.8em 1.8em 0 0 rgba(130,130,130,.7),-2.6em 0 0 0 rgba(130,130,130,1),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}87.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.5),-2.6em 0 0 0 rgba(130,130,130,.7),-1.8em -1.8em 0 0 rgba(130,130,130,1)}}.fg-loading-bars .fg-loader,.fg-loading-bars .fg-loader:after,.fg-loading-bars .fg-loader:before{background:rgba(130,130,130,1);-webkit-animation:loading-bars 1s infinite ease-in-out;animation:loading-bars 1s infinite ease-in-out;width:1em;height:4em}.fg-loading-bars .fg-loader{color:rgba(130,130,130,1);text-indent:-9999em;font-size:4px;-webkit-animation-delay:-.16s;animation-delay:-.16s}.fg-loading-bars .fg-loader:after,.fg-loading-bars .fg-loader:before{position:absolute;top:0;content:''}.fg-loading-bars .fg-loader:before{left:-1.5em;-webkit-animation-delay:-.32s;animation-delay:-.32s}.fg-loading-bars .fg-loader:after{left:1.5em}@-webkit-keyframes loading-bars{0%,100%,80%{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}@keyframes loading-bars{0%,100%,80%{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}.fg-loading-trail .fg-loader{color:#828282;font-size:20px;text-indent:-9999em;overflow:hidden;border-radius:50%;-webkit-animation:loading-trail-1 1.7s infinite ease,loading-trail-2 1.7s infinite ease;animation:loading-trail-1 1.7s infinite ease,loading-trail-2 1.7s infinite ease}@-webkit-keyframes loading-trail-1{0%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}5%,95%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}10%,59%{box-shadow:0 -.83em 0 -.4em,-.087em -.825em 0 -.42em,-.173em -.812em 0 -.44em,-.256em -.789em 0 -.46em,-.297em -.775em 0 -.477em}20%{box-shadow:0 -.83em 0 -.4em,-.338em -.758em 0 -.42em,-.555em -.617em 0 -.44em,-.671em -.488em 0 -.46em,-.749em -.34em 0 -.477em}38%{box-shadow:0 -.83em 0 -.4em,-.377em -.74em 0 -.42em,-.645em -.522em 0 -.44em,-.775em -.297em 0 -.46em,-.82em -.09em 0 -.477em}100%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}}@keyframes loading-trail-1{0%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}5%,95%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}10%,59%{box-shadow:0 -.83em 0 -.4em,-.087em -.825em 0 -.42em,-.173em -.812em 0 -.44em,-.256em -.789em 0 -.46em,-.297em -.775em 0 -.477em}20%{box-shadow:0 -.83em 0 -.4em,-.338em -.758em 0 -.42em,-.555em -.617em 0 -.44em,-.671em -.488em 0 -.46em,-.749em -.34em 0 -.477em}38%{box-shadow:0 -.83em 0 -.4em,-.377em -.74em 0 -.42em,-.645em -.522em 0 -.44em,-.775em -.297em 0 -.46em,-.82em -.09em 0 -.477em}100%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}}@-webkit-keyframes loading-trail-2{0%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(0);transform:translateX(-50%) translateY(-50%) rotate(0)}100%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(360deg);transform:translateX(-50%) translateY(-50%) rotate(360deg)}}@keyframes loading-trail-2{0%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(0);transform:translateX(-50%) translateY(-50%) rotate(0)}100%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(360deg);transform:translateX(-50%) translateY(-50%) rotate(360deg)}}.fg-loading-pulse .fg-loader,.fg-loading-pulse .fg-loader:after,.fg-loading-pulse .fg-loader:before{border-radius:50%;width:2.5em;height:2.5em;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation:loading-pulse 1.8s infinite ease-in-out;animation:loading-pulse 1.8s infinite ease-in-out}.fg-loading-pulse .fg-loader{color:#828282;font-size:4px;text-indent:-9999em;transform:translateX(-50%) translateY(-150%);-webkit-animation-delay:-.16s;animation-delay:-.16s}.fg-loading-pulse .fg-loader:after,.fg-loading-pulse .fg-loader:before{content:'';position:absolute;top:0}.fg-loading-pulse .fg-loader:before{left:-3.5em;-webkit-animation-delay:-.32s;animation-delay:-.32s}.fg-loading-pulse .fg-loader:after{left:3.5em}@-webkit-keyframes loading-pulse{0%,100%,80%{box-shadow:0 2.5em 0 -1.3em}40%{box-shadow:0 2.5em 0 0}}@keyframes loading-pulse{0%,100%,80%{box-shadow:0 2.5em 0 -1.3em}40%{box-shadow:0 2.5em 0 0}}.fg-loading-dots .fg-loader{color:#828282;font-size:5px;border-radius:50%;text-indent:-9999em;-webkit-animation:loading-dots 1.3s infinite linear;animation:loading-dots 1.3s infinite linear}@-webkit-keyframes loading-dots{0%,100%{box-shadow:0 -3em 0 .2em,2em -2em 0 0,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 0}12.5%{box-shadow:0 -3em 0 0,2em -2em 0 .2em,3em 0 0 0,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}25%{box-shadow:0 -3em 0 -.5em,2em -2em 0 0,3em 0 0 .2em,2em 2em 0 0,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}37.5%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 0,2em 2em 0 .2em,0 3em 0 0,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}50%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 0,0 3em 0 .2em,-2em 2em 0 0,-3em 0 0 -1em,-2em -2em 0 -1em}62.5%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 0,-2em 2em 0 .2em,-3em 0 0 0,-2em -2em 0 -1em}75%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 0,-3em 0 0 .2em,-2em -2em 0 0}87.5%{box-shadow:0 -3em 0 0,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 0,-3em 0 0 0,-2em -2em 0 .2em}}@keyframes loading-dots{0%,100%{box-shadow:0 -3em 0 .2em,2em -2em 0 0,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 0}12.5%{box-shadow:0 -3em 0 0,2em -2em 0 .2em,3em 0 0 0,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}25%{box-shadow:0 -3em 0 -.5em,2em -2em 0 0,3em 0 0 .2em,2em 2em 0 0,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}37.5%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 0,2em 2em 0 .2em,0 3em 0 0,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}50%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 0,0 3em 0 .2em,-2em 2em 0 0,-3em 0 0 -1em,-2em -2em 0 -1em}62.5%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 0,-2em 2em 0 .2em,-3em 0 0 0,-2em -2em 0 -1em}75%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 0,-3em 0 0 .2em,-2em -2em 0 0}87.5%{box-shadow:0 -3em 0 0,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 0,-3em 0 0 0,-2em -2em 0 .2em}}.fg-loading-partial .fg-loader,.fg-loading-partial .fg-loader:after{border-radius:50%;width:10em;height:10em}.fg-loading-partial .fg-loader{font-size:4px;text-indent:-9999em;border-top:1.1em solid rgba(130,130,130,.2);border-right:1.1em solid rgba(130,130,130,.2);border-bottom:1.1em solid rgba(130,130,130,.2);border-left:1.1em solid #828282;-webkit-animation:loading-partial 1.1s infinite linear;animation:loading-partial 1.1s infinite linear}@-webkit-keyframes loading-partial{0%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(0);transform:translateX(-50%) translateY(-50%) rotate(0)}100%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(360deg);transform:translateX(-50%) translateY(-50%) rotate(360deg)}}@keyframes loading-partial{0%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(0);transform:translateX(-50%) translateY(-50%) rotate(0)}100%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(360deg);transform:translateX(-50%) translateY(-50%) rotate(360deg)}}.foogallery.fg-loaded-drop .fg-item,.foogallery.fg-loaded-fade-in .fg-item,.foogallery.fg-loaded-flip .fg-item,.foogallery.fg-loaded-fly .fg-item,.foogallery.fg-loaded-scale-up .fg-item,.foogallery.fg-loaded-slide-down .fg-item,.foogallery.fg-loaded-slide-left .fg-item,.foogallery.fg-loaded-slide-right .fg-item,.foogallery.fg-loaded-slide-up .fg-item,.foogallery.fg-loaded-swing-down .fg-item{transition-timing-function:ease;transition-duration:650ms;transition-property:background-color,transform}.foogallery.fg-loaded-drop .fg-item-inner,.foogallery.fg-loaded-fade-in .fg-item-inner,.foogallery.fg-loaded-flip .fg-item-inner,.foogallery.fg-loaded-fly .fg-item-inner,.foogallery.fg-loaded-scale-up .fg-item-inner,.foogallery.fg-loaded-slide-down .fg-item-inner,.foogallery.fg-loaded-slide-left .fg-item-inner,.foogallery.fg-loaded-slide-right .fg-item-inner,.foogallery.fg-loaded-slide-up .fg-item-inner,.foogallery.fg-loaded-swing-down .fg-item-inner{transition-timing-function:ease;transition-duration:650ms}.foogallery.fg-loaded-drop .fg-item.fg-loaded,.foogallery.fg-loaded-flip .fg-item.fg-loaded,.foogallery.fg-loaded-fly .fg-item.fg-loaded,.foogallery.fg-loaded-swing-down .fg-item.fg-loaded{perspective:1300px}.foogallery.fg-loaded-fade-in .fg-item-inner{transition-property:visibility,opacity}.foogallery .fg-caption{visibility:hidden;opacity:0;background-color:rgba(0,0,0,.6);color:#fff;position:absolute;z-index:8;width:100%;max-height:100%;overflow:hidden;font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:13px;font-weight:400;line-height:1.3;border:none;text-align:center;cursor:pointer}.foogallery .fg-caption a{text-decoration:none;color:#fff;border-bottom:1px solid #fff}.foogallery .fg-caption a:hover{border-bottom:none}.foogallery .fg-caption-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:16px;font-weight:400;padding:5px}.foogallery .fg-caption-desc{padding:5px}.foogallery .fg-caption-title+.fg-caption-desc{padding-top:0}.foogallery.fg-caption-always .fg-caption .fg-caption-inner:before{display:none}.foogallery.fg-caption-always .fg-item.fg-loaded .fg-caption{left:0;bottom:0;transition-timing-function:ease;transition-duration:.3s;transition-property:visibility,opacity;visibility:visible;opacity:1;text-align:left}.foogallery.fg-caption-hover .fg-caption .fg-caption-inner{width:100%;max-height:100%;position:absolute;top:50%;left:0;transform:translateY(-50%)}.foogallery.fg-caption-hover .fg-item.fg-loaded .fg-thumb:before{display:none}.foogallery.fg-hover-circle-plus .fg-thumb:before,.foogallery.fg-hover-external .fg-thumb:before,.foogallery.fg-hover-eye .fg-thumb:before,.foogallery.fg-hover-plus .fg-thumb:before,.foogallery.fg-hover-tint .fg-thumb:before,.foogallery.fg-hover-zoom .fg-thumb:before,.foogallery.fg-hover-zoom2 .fg-thumb:before,.foogallery.fg-hover-zoom3 .fg-thumb:before{content:"";display:block;position:absolute;visibility:hidden;opacity:0;top:0;bottom:0;left:0;right:0;z-index:8;background:rgba(0,0,0,.5) no-repeat center center;background-size:32px 32px}.foogallery.fg-hover-circle-plus .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-circle-plus .fg-thumb:focus:before,.foogallery.fg-hover-external .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-external .fg-thumb:focus:before,.foogallery.fg-hover-eye .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-eye .fg-thumb:focus:before,.foogallery.fg-hover-plus .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-plus .fg-thumb:focus:before,.foogallery.fg-hover-tint .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-tint .fg-thumb:focus:before,.foogallery.fg-hover-zoom .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-zoom .fg-thumb:focus:before,.foogallery.fg-hover-zoom2 .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-zoom2 .fg-thumb:focus:before,.foogallery.fg-hover-zoom3 .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-zoom3 .fg-thumb:focus:before{visibility:visible;opacity:1}.foogallery.fg-hover-circle-plus .fg-caption-inner:before,.foogallery.fg-hover-external .fg-caption-inner:before,.foogallery.fg-hover-eye .fg-caption-inner:before,.foogallery.fg-hover-plus .fg-caption-inner:before,.foogallery.fg-hover-tint .fg-caption-inner:before,.foogallery.fg-hover-zoom .fg-caption-inner:before,.foogallery.fg-hover-zoom2 .fg-caption-inner:before,.foogallery.fg-hover-zoom3 .fg-caption-inner:before{content:"";display:inline-block;position:relative;width:32px;height:32px;margin:10px 0 5px 0;background:transparent no-repeat center center;background-size:32px 32px;vertical-align:middle}.foogallery.fg-hover-zoom .fg-caption-inner:before,.foogallery.fg-hover-zoom .fg-thumb:before{background-image:url(../img/zoom.png)}.foogallery.fg-hover-zoom2 .fg-caption-inner:before,.foogallery.fg-hover-zoom2 .fg-thumb:before{background-image:url(../img/zoom2.png)}.foogallery.fg-hover-zoom3 .fg-caption-inner:before,.foogallery.fg-hover-zoom3 .fg-thumb:before{background-image:url(../img/zoom3.png)}.foogallery.fg-hover-plus .fg-caption-inner:before,.foogallery.fg-hover-plus .fg-thumb:before{background-image:url(../img/plus.png)}.foogallery.fg-hover-circle-plus .fg-caption-inner:before,.foogallery.fg-hover-circle-plus .fg-thumb:before{background-image:url(../img/circle-plus.png)}.foogallery.fg-hover-eye .fg-caption-inner:before,.foogallery.fg-hover-eye .fg-thumb:before{background-image:url(../img/eye.png)}.foogallery.fg-hover-external .fg-caption-inner:before,.foogallery.fg-hover-external .fg-thumb:before{background-image:url(../img/external.png)}@media only screen and (-o-min-device-pixel-ratio:5/4),only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-device-pixel-ratio:1.25),only screen and (min-resolution:1.25dppx){.foogallery.fg-hover-zoom .fg-caption-inner:before,.foogallery.fg-hover-zoom .fg-thumb:before{background-image:url(../img/zoom@2x.png)}.foogallery.fg-hover-zoom2 .fg-caption-inner:before,.foogallery.fg-hover-zoom2 .fg-thumb:before{background-image:url(../img/zoom2@2x.png)}.foogallery.fg-hover-zoom3 .fg-caption-inner:before,.foogallery.fg-hover-zoom3 .fg-thumb:before{background-image:url(../img/zoom3@2x.png)}.foogallery.fg-hover-plus .fg-caption-inner:before,.foogallery.fg-hover-plus .fg-thumb:before{background-image:url(../img/plus@2x.png)}.foogallery.fg-hover-circle-plus .fg-caption-inner:before,.foogallery.fg-hover-circle-plus .fg-thumb:before{background-image:url(../img/circle-plus@2x.png)}.foogallery.fg-hover-eye .fg-caption-inner:before,.foogallery.fg-hover-eye .fg-thumb:before{background-image:url(../img/eye@2x.png)}.foogallery.fg-hover-external .fg-caption-inner:before,.foogallery.fg-hover-external .fg-thumb:before{background-image:url(../img/external@2x.png)}}@media only screen and (-o-min-device-pixel-ratio:9/4),only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-device-pixel-ratio:2.25),only screen and (min-resolution:2.25dppx){.foogallery.fg-hover-zoom .fg-caption-inner:before,.foogallery.fg-hover-zoom .fg-thumb:before{background-image:url(../img/zoom@3x.png)}.foogallery.fg-hover-zoom2 .fg-caption-inner:before,.foogallery.fg-hover-zoom2 .fg-thumb:before{background-image:url(../img/zoom2@3x.png)}.foogallery.fg-hover-zoom3 .fg-caption-inner:before,.foogallery.fg-hover-zoom3 .fg-thumb:before{background-image:url(../img/zoom3@3x.png)}.foogallery.fg-hover-plus .fg-caption-inner:before,.foogallery.fg-hover-plus .fg-thumb:before{background-image:url(../img/plus@3x.png)}.foogallery.fg-hover-circle-plus .fg-caption-inner:before,.foogallery.fg-hover-circle-plus .fg-thumb:before{background-image:url(../img/circle-plus@3x.png)}.foogallery.fg-hover-eye .fg-caption-inner:before,.foogallery.fg-hover-eye .fg-thumb:before{background-image:url(../img/eye@3x.png)}.foogallery.fg-hover-external .fg-caption-inner:before,.foogallery.fg-hover-external .fg-thumb:before{background-image:url(../img/external@3x.png)}}.foogallery.fg-caption-hover.fg-hover-colorize .fg-caption,.foogallery.fg-caption-hover.fg-hover-fade .fg-caption,.foogallery.fg-caption-hover.fg-hover-grayscale .fg-caption,.foogallery.fg-caption-hover.fg-hover-instant .fg-caption,.foogallery.fg-caption-hover.fg-hover-push .fg-caption,.foogallery.fg-caption-hover.fg-hover-scale .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-down .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-left .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-right .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-up .fg-caption,.foogallery.fg-hover-colorize .fg-image,.foogallery.fg-hover-colorize .fg-thumb:before,.foogallery.fg-hover-fade .fg-thumb:before,.foogallery.fg-hover-grayscale .fg-image,.foogallery.fg-hover-grayscale .fg-thumb:before,.foogallery.fg-hover-instant .fg-thumb:before,.foogallery.fg-hover-push .fg-thumb,.foogallery.fg-hover-scale .fg-item,.foogallery.fg-hover-scale .fg-thumb:before,.foogallery.fg-hover-slide-down .fg-thumb:before,.foogallery.fg-hover-slide-left .fg-thumb:before,.foogallery.fg-hover-slide-right .fg-thumb:before,.foogallery.fg-hover-slide-up .fg-thumb:before{transition-timing-function:ease;transition-duration:.3s}.foogallery.fg-hover-colorize .fg-image{filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'saturate\' values=\'0\'/></filter></svg>#grayscale");filter:gray;-webkit-filter:grayscale(100%);-webkit-transition-property:-webkit-filter;transition-property:filter}.foogallery.fg-hover-colorize .fg-item-inner:hover .fg-image{-webkit-filter:none;filter:none}.foogallery.fg-caption-hover.fg-hover-colorize .fg-caption,.foogallery.fg-hover-colorize .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:visibility,opacity}.foogallery.fg-caption-hover.fg-hover-colorize .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-colorize .fg-item-inner:hover .fg-thumb:before{visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-fade .fg-loaded .fg-caption,.foogallery.fg-hover-fade .fg-loaded .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:visibility,opacity}.foogallery.fg-caption-hover.fg-hover-fade .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-fade .fg-loaded .fg-item-inner:hover .fg-thumb:before{visibility:visible;opacity:1}.foogallery.fg-hover-grayscale .fg-image{-webkit-filter:none;filter:none;-webkit-transition-property:-webkit-filter;transition-property:filter}.foogallery.fg-hover-grayscale .fg-item-inner:hover .fg-image{-webkit-filter:grayscale(1);-webkit-filter:grayscale(100%);filter:grayscale(100%);filter:gray;opacity:1}.foogallery.fg-caption-hover.fg-hover-grayscale .fg-caption,.foogallery.fg-hover-grayscale .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:visibility,opacity}.foogallery.fg-caption-hover.fg-hover-grayscale .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-grayscale .fg-item-inner:hover .fg-thumb:before{visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-instant .fg-loaded .fg-caption,.foogallery.fg-hover-instant .fg-loaded .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:none}.foogallery.fg-caption-hover.fg-hover-instant .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-instant .fg-loaded .fg-item-inner:hover .fg-thumb:before{visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-push .fg-loaded .fg-caption,.foogallery.fg-hover-push .fg-loaded .fg-thumb:before{display:block;left:0;top:0;bottom:0;transform:translateX(100%);visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-push .fg-loaded .fg-caption,.foogallery.fg-hover-push .fg-loaded .fg-thumb{transition-property:transform}.foogallery.fg-caption-hover.fg-hover-push .fg-loaded .fg-item-inner:hover .fg-caption{transform:translateY(0)}.foogallery.fg-caption-hover.fg-hover-push .fg-loaded .fg-item-inner:hover .fg-thumb,.foogallery.fg-hover-push .fg-loaded .fg-item-inner:hover .fg-thumb{transform:translateX(-100%)}.foogallery.fg-hover-scale .fg-item{transition-property:transform;z-index:4}.foogallery.fg-hover-scale .fg-item:hover{transform:scale(1.048);z-index:10}.foogallery.fg-caption-hover.fg-hover-scale .fg-caption,.foogallery.fg-hover-scale .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:visibility,opacity}.foogallery.fg-caption-hover.fg-hover-scale .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-scale .fg-item-inner:hover .fg-thumb:before{visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-slide-down .fg-loaded .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-left .fg-loaded .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-right .fg-loaded .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-up .fg-loaded .fg-caption,.foogallery.fg-hover-slide-down .fg-loaded .fg-thumb:before,.foogallery.fg-hover-slide-left .fg-loaded .fg-thumb:before,.foogallery.fg-hover-slide-right .fg-loaded .fg-thumb:before,.foogallery.fg-hover-slide-up .fg-loaded .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:transform;visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-slide-down .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-left .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-right .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-up .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-slide-down .fg-loaded .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-slide-left .fg-loaded .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-slide-right .fg-loaded .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-slide-up .fg-loaded .fg-item-inner:hover .fg-thumb:before{transform:translateY(0) translateX(0)}.foogallery.fg-caption-hover.fg-hover-slide-up .fg-loaded .fg-caption,.foogallery.fg-hover-slide-up .fg-loaded .fg-thumb:before{transform:translateY(100%)}.foogallery.fg-caption-hover.fg-hover-slide-down .fg-loaded .fg-caption,.foogallery.fg-hover-slide-down .fg-loaded .fg-thumb:before{transform:translateY(-100%)}.foogallery.fg-caption-hover.fg-hover-slide-left .fg-loaded .fg-caption,.foogallery.fg-hover-slide-left .fg-loaded .fg-thumb:before{transform:translateX(100%)}.foogallery.fg-caption-hover.fg-hover-slide-right .fg-loaded .fg-caption,.foogallery.fg-hover-slide-right .fg-loaded .fg-thumb:before{transform:translateX(-100%)}.fg-paging-container,.fg-paging-container *,.fg-paging-container :after,.fg-paging-container :before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fg-paging-container{display:block;padding:15px;text-align:center;font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fg-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fg-paging-container .fg-dot-item,.fg-paging-container .fg-dots{display:inline-block;margin:0;padding:0;outline:0;list-style:none}.fg-paging-container .fg-dot-item .fg-dot-link{display:inline-block;margin:3px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;user-select:none;background-image:none;text-decoration:none;border:1px solid transparent;position:relative;border-radius:50%;padding:0;font-size:0;outline:0;color:transparent;box-shadow:none}.fg-paging-container .fg-dot-item .fg-dot-link:before{content:"";background-color:transparent;border:1px solid transparent;display:block;border-radius:50%;width:9px;height:9px;padding:0;margin:2px}.fg-paging-container .fg-dot-item .fg-dot-link:active,.fg-paging-container .fg-dot-item .fg-dot-link:focus,.fg-paging-container .fg-dot-item .fg-dot-link:hover{text-decoration:none;box-shadow:none;outline:0}.fg-paging-container .fg-dot-item.fg-disabled .fg-dot-link,.fg-paging-container .fg-dot-item.fg-selected .fg-dot-link{cursor:not-allowed;pointer-events:none}.fg-paging-container .fg-dot-item.fg-disabled .fg-dot-link{cursor:not-allowed;pointer-events:none;outline:0}.fg-paging-container.fg-light .fg-dot-item .fg-dot-link,.fg-paging-container.fg-light .fg-dot-item .fg-dot-link:before{transition-timing-function:ease-out;transition-duration:.3s;transition-property:color,border-color,background-color}.fg-paging-container.fg-light .fg-dot-item .fg-dot-link{background-color:#eee;border-color:#9e9e9e}.fg-paging-container.fg-light .fg-dot-item.fg-selected .fg-dot-link{border-color:#8a8a8a}.fg-paging-container.fg-light .fg-dot-item .fg-dot-link:focus:before,.fg-paging-container.fg-light .fg-dot-item .fg-dot-link:hover:before,.fg-paging-container.fg-light .fg-dot-item.fg-selected .fg-dot-link:before{background-color:#666;border-color:#8a8a8a}.fg-paging-container.fg-light .fg-dot-item.fg-disabled .fg-dot-link,.fg-paging-container.fg-light .fg-dot-item.fg-disabled .fg-dot-link:focus,.fg-paging-container.fg-light .fg-dot-item.fg-disabled .fg-dot-link:hover{background-color:#eee;border-color:#9e9e9e;opacity:.5}.fg-paging-container.fg-dark .fg-dot-item .fg-dot-link,.fg-paging-container.fg-dark .fg-dot-item .fg-dot-link:before{transition-timing-function:ease-out;transition-duration:.3s;transition-property:color,border-color,background-color}.fg-paging-container.fg-dark .fg-dot-item .fg-dot-link{background-color:#666;border-color:#333}.fg-paging-container.fg-dark .fg-dot-item.fg-selected .fg-dot-link{border-color:#444}.fg-paging-container.fg-dark .fg-dot-item .fg-dot-link:focus:before,.fg-paging-container.fg-dark .fg-dot-item .fg-dot-link:hover:before,.fg-paging-container.fg-dark .fg-dot-item.fg-selected .fg-dot-link:before{background-color:#333;border-color:#444}.fg-paging-container.fg-dark .fg-dot-item.fg-disabled .fg-dot-link,.fg-paging-container.fg-dark .fg-dot-item.fg-disabled .fg-dot-link:focus,.fg-paging-container.fg-dark .fg-dot-item.fg-disabled .fg-dot-link:hover{background-color:#666;border-color:#333;opacity:.5}.fg-default:after{content:'';display:block;clear:both}.fg-default .fg-item,.fg-default .fg-item-inner,.fg-default .fg-thumb{display:inline-block;vertical-align:top;max-width:100%}.fg-default .fg-image{border-radius:0;display:block;max-width:100%;height:auto;margin:0;padding:0}.fg-default .fg-image{vertical-align:top}.fg-default.fg-left{text-align:left}.fg-default.fg-center{text-align:center}.fg-default.fg-right{text-align:right}.fg-default.fg-gutter-5{padding-left:5px;margin-bottom:-5px}.fg-default.fg-gutter-5 .fg-item{margin-right:5px;margin-bottom:5px}.fg-default.fg-gutter-10{padding-left:10px;margin-bottom:-10px}.fg-default.fg-gutter-10 .fg-item{margin-right:10px;margin-bottom:10px}.fg-default.fg-gutter-15{padding-left:15px;margin-bottom:-15px}.fg-default.fg-gutter-15 .fg-item{margin-right:15px;margin-bottom:15px}.fg-default.fg-gutter-20{padding-left:20px;margin-bottom:-20px}.fg-default.fg-gutter-20 .fg-item{margin-right:20px;margin-bottom:20px}.fg-default.fg-gutter-25{padding-left:25px;margin-bottom:-25px}.fg-default.fg-gutter-25 .fg-item{margin-right:25px;margin-bottom:25px}.fg-masonry *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.foogallery.fg-masonry.fg-center{margin:0 auto}.fg-masonry .fg-thumb{display:block}.fg-masonry.fg-masonry-fixed .fg-thumb{display:inline-block}.fg-masonry.fg-masonry-fixed .fg-image{max-width:100%}.fg-masonry .fg-column-width{display:inline-block;visibility:hidden;height:0;border:solid 0 transparent}.fg-masonry.fg-masonry-2col .fg-image,.fg-masonry.fg-masonry-3col .fg-image,.fg-masonry.fg-masonry-4col .fg-image,.fg-masonry.fg-masonry-5col .fg-image{width:100%;height:auto;max-width:100%}.fg-masonry .fg-item{line-height:0;font-size:0}.fg-masonry.fg-masonry-2col .fg-item{margin-bottom:1%;width:49%}.fg-masonry.fg-masonry-2col .fg-column-width{width:49%}.fg-masonry.fg-masonry-2col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-item{margin-bottom:0;width:50%}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-column-width{width:50%}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-item{margin-bottom:3%;width:47%}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-column-width{width:47%}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-gutter-width{width:3%}.fg-masonry.fg-masonry-3col .fg-item{margin-bottom:1%;width:32%}.fg-masonry.fg-masonry-3col .fg-column-width{width:32%}.fg-masonry.fg-masonry-3col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-item{margin-bottom:0;width:33%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-column-width{width:33%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-item{margin-bottom:3%;width:30%}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-column-width{width:30%}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-gutter-width{width:3%}.fg-masonry.fg-masonry-4col .fg-item{margin-bottom:1%;width:24%}.fg-masonry.fg-masonry-4col .fg-column-width{width:24%}.fg-masonry.fg-masonry-4col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-item{margin-bottom:0;width:25%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-column-width{width:25%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-item{margin-bottom:3%;width:22%}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-column-width{width:22%}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-gutter-width{width:3%}.fg-masonry.fg-masonry-5col .fg-item{margin-bottom:1%;width:19%}.fg-masonry.fg-masonry-5col .fg-column-width{width:19%}.fg-masonry.fg-masonry-5col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-item{margin-bottom:0;width:20%}.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-column-width{width:20%}.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-item{margin-bottom:3%;width:17%}.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-column-width{width:17%}.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-gutter-width{width:3%}@media screen and (max-width:720px){.fg-masonry.fg-masonry-4col .fg-item,.fg-masonry.fg-masonry-5col .fg-item{margin-bottom:1%;width:32%}.fg-masonry.fg-masonry-4col .fg-column-width,.fg-masonry.fg-masonry-5col .fg-column-width{width:32%}.fg-masonry.fg-masonry-4col .fg-gutter-width,.fg-masonry.fg-masonry-5col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-item{margin-bottom:0;width:33%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-column-width{width:33%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-item{margin-bottom:3%;width:30%}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-column-width{width:30%}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-gutter-width{width:3%}}@media screen and (max-width:480px){.fg-masonry.fg-masonry-3col .fg-item,.fg-masonry.fg-masonry-4col .fg-item,.fg-masonry.fg-masonry-5col .fg-item{margin-bottom:1%;width:49%}.fg-masonry.fg-masonry-3col .fg-column-width,.fg-masonry.fg-masonry-4col .fg-column-width,.fg-masonry.fg-masonry-5col .fg-column-width{width:49%}.fg-masonry.fg-masonry-3col .fg-gutter-width,.fg-masonry.fg-masonry-4col .fg-gutter-width,.fg-masonry.fg-masonry-5col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-item{margin-bottom:0;width:50%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-column-width{width:50%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-item{margin-bottom:3%;width:47%}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-column-width{width:47%}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-gutter-width{width:3%}}@media screen and (max-width:320px){.fg-masonry.fg-masonry-2col .fg-item,.fg-masonry.fg-masonry-3col .fg-item,.fg-masonry.fg-masonry-4col .fg-item,.fg-masonry.fg-masonry-5col .fg-item{margin-bottom:1%;width:100%}.fg-masonry.fg-masonry-2col .fg-column-width,.fg-masonry.fg-masonry-3col .fg-column-width,.fg-masonry.fg-masonry-4col .fg-column-width,.fg-masonry.fg-masonry-5col .fg-column-width{width:100%}.fg-masonry.fg-masonry-2col .fg-gutter-width,.fg-masonry.fg-masonry-3col .fg-gutter-width,.fg-masonry.fg-masonry-4col .fg-gutter-width,.fg-masonry.fg-masonry-5col .fg-gutter-width{width:0}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-item{margin-bottom:0;width:100%}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-column-width{width:100%}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-item{margin-bottom:3%;width:100%}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-column-width{width:100%}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-gutter-width{width:0}}.foogallery.fg-border-thin .fg-column-width{border-width:5px}.foogallery.fg-border-medium .fg-column-width{border-width:10px}.foogallery.fg-border-thick .fg-column-width{border-width:15px}.foogallery.fg-masonry.fg-captions-bottom .fg-item-inner .fg-caption{visibility:visible;opacity:1;font-size:13px;position:relative;display:block;top:auto;bottom:auto;left:auto;right:auto;width:auto;height:auto;text-transform:none;transform:none;transition:none;background-color:transparent;border-style:solid;border-color:transparent}.foogallery.fg-masonry.fg-captions-bottom .fg-item-inner:hover .fg-caption{transform:none;transition:none}.foogallery.fg-masonry.fg-captions-bottom .fg-item-inner .fg-caption-inner{display:block;position:relative;max-height:none;top:auto;bottom:auto;left:auto;right:auto;width:auto;height:auto;border:none;transform:none;transition:none}.foogallery.fg-masonry.fg-captions-bottom .fg-item-inner .fg-caption-inner:before{display:none}.foogallery.fg-masonry.fg-captions-bottom.fg-caption-hover .fg-item-inner .fg-thumb:before{display:block}.foogallery.fg-masonry.fg-captions-bottom.fg-caption-always .fg-item-inner:hover .fg-caption{visibility:visible;opacity:1}.fg-masonry.fg-captions-bottom .fg-caption-desc,.fg-masonry.fg-captions-bottom .fg-caption-title{text-align:left}.fg-masonry.fg-captions-bottom.fg-dark .fg-caption,.fg-masonry.fg-captions-bottom.fg-light .fg-caption{color:#828282}.fg-masonry.fg-captions-bottom.fg-dark .fg-caption a,.fg-masonry.fg-captions-bottom.fg-light .fg-caption a{color:#828282;border-bottom:1px solid #828282}.fg-masonry.fg-captions-bottom.fg-dark .fg-caption a:hover,.fg-masonry.fg-captions-bottom.fg-light .fg-caption a:hover{border-bottom:none}.fg-masonry.fg-captions-bottom.fg-light .fg-caption-title,.fg-masonry.fg-captions-bottom.fg-light .fg-caption-title a{color:#222}.fg-masonry.fg-captions-bottom.fg-dark .fg-caption-title,.fg-masonry.fg-captions-bottom.fg-dark .fg-caption-title a{color:#fff}.fg-masonry.fg-captions-bottom.fg-light .fg-caption-title a{border-bottom:1px solid #222}.fg-masonry.fg-captions-bottom.fg-dark .fg-caption-title a{border-bottom:1px solid #fff}.fg-masonry.fg-captions-bottom .fg-caption{border-width:10px}.fg-masonry.fg-captions-bottom .fg-caption-title+.fg-caption-desc{margin-top:5px}.fg-masonry.fg-captions-bottom.fg-border-thin .fg-caption{border-width:10px 5px 5px 5px}.fg-masonry.fg-captions-bottom.fg-border-medium .fg-caption{border-width:10px 0 0 0}.fg-masonry.fg-captions-bottom.fg-border-thick .fg-caption{border-width:15px 0 0 0}.fg-masonry.fg-captions-bottom.fg-border-thick .fg-caption-title+.fg-caption-desc{margin-top:10px}.fg-justified{box-sizing:border-box;position:relative}.foogallery.fg-justified .fg-image,.foogallery.fg-justified .fg-item,.foogallery.fg-justified .fg-item-inner,.foogallery.fg-justified .fg-thumb{box-sizing:border-box;display:block;margin:0;padding:0}.fg-justified .fg-item{visibility:visible;position:absolute}.fg-justified .fg-item-inner{position:relative;width:100%;height:100%}.fg-justified .fg-thumb{position:relative;overflow:hidden}.fg-justified .fg-image{z-index:1}.fg-justified .fg-item.fg-positioned .fg-thumb{width:100%;height:100%}.fg-justified .fg-item.fg-positioned .fg-image{width:100%;height:auto;min-height:100%}.fg-simple_portfolio{box-sizing:border-box;position:relative;font-size:16px;margin:0 auto;padding:0;width:100%}.fg-simple_portfolio .fg-item{position:absolute;display:inline-block;margin:0;padding:0;outline:0}.fg-simple_portfolio .fg-image,.fg-simple_portfolio .fg-item-inner,.fg-simple_portfolio .fg-thumb{display:block;margin:0;padding:0;outline:0}.fg-simple_portfolio .fg-item-inner{position:relative;width:100%;height:100%}.fg-simple_portfolio .fg-thumb{box-sizing:border-box;display:block;margin:0;padding:0;border:none;outline:0;position:relative;overflow:hidden}.fg-simple_portfolio .fg-item.fg-positioned .fg-image{width:100%;height:auto}.fg-simple_portfolio .fg-image{z-index:1}.foogallery.fg-simple_portfolio .fg-item-inner .fg-caption{visibility:visible;opacity:1;font-size:13px;position:relative;display:block;top:auto;bottom:auto;left:auto;right:auto;width:auto;height:auto;text-transform:none;transform:none;transition:none;background-color:transparent;border-style:solid;border-color:transparent}.foogallery.fg-simple_portfolio .fg-item-inner:hover .fg-caption{transform:none;transition:none}.foogallery.fg-simple_portfolio .fg-item-inner .fg-caption-inner{display:block;top:auto;bottom:auto;left:auto;right:auto;width:auto;height:auto;border:none;transform:none;transition:none}.foogallery.fg-simple_portfolio .fg-item-inner .fg-caption-inner:before{display:none}.foogallery.fg-simple_portfolio.fg-caption-hover .fg-item-inner .fg-thumb:before{display:block}.foogallery.fg-simple_portfolio.fg-caption-always .fg-item-inner:hover .fg-caption{visibility:visible;opacity:1}.fg-simple_portfolio .fg-caption-title{text-align:left}.fg-simple_portfolio .fg-caption-desc{text-align:justify}.fg-simple_portfolio.fg-dark .fg-caption,.fg-simple_portfolio.fg-light .fg-caption{color:#828282}.fg-simple_portfolio.fg-dark .fg-caption a,.fg-simple_portfolio.fg-light .fg-caption a{color:#828282;border-bottom:1px solid #828282}.fg-simple_portfolio.fg-dark .fg-caption a:hover,.fg-simple_portfolio.fg-light .fg-caption a:hover{border-bottom:none}.fg-simple_portfolio.fg-light .fg-caption-title,.fg-simple_portfolio.fg-light .fg-caption-title a{color:#222}.fg-simple_portfolio.fg-dark .fg-caption-title,.fg-simple_portfolio.fg-dark .fg-caption-title a{color:#fff}.fg-simple_portfolio.fg-light .fg-caption-title a{border-bottom:1px solid #222}.fg-simple_portfolio.fg-dark .fg-caption-title a{border-bottom:1px solid #fff}.fg-simple_portfolio.fg-captions-top .fg-item.fg-positioned .fg-thumb{position:absolute;bottom:0;left:0}.fg-simple_portfolio .fg-caption{border-width:10px}.fg-simple_portfolio .fg-caption-title+.fg-caption-desc{margin-top:5px}.fg-simple_portfolio.fg-border-thin .fg-caption{border-width:10px 5px 5px 5px}.fg-simple_portfolio.fg-captions-top.fg-border-thin .fg-caption{border-width:5px 5px 10px 5px}.fg-simple_portfolio.fg-border-medium .fg-caption{border-width:10px 0 0 0}.fg-simple_portfolio.fg-captions-top.fg-border-medium .fg-caption{border-width:0 0 10px 0}.fg-simple_portfolio.fg-border-thick .fg-caption{border-width:15px 0 0 0}.fg-simple_portfolio.fg-captions-top.fg-border-thick .fg-caption{border-width:0 0 15px 0}.fg-simple_portfolio.fg-border-thick .fg-caption-title+.fg-caption-desc{margin-top:10px}.foogallery.fg-preset.fg-polaroid .fg-item{-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transition:-webkit-transform .35s,background-color .65s;transition:transform .35s,background-color .65s}.foogallery.fg-preset.fg-polaroid .fg-item:nth-child(2n+1){-webkit-transform:rotate(3deg);transform:rotate(3deg)}.foogallery.fg-preset.fg-polaroid .fg-item:nth-child(2n){-webkit-transform:rotate(-3deg);transform:rotate(-3deg)}.foogallery.fg-preset.fg-polaroid .fg-item:nth-child(3n){-webkit-transform:rotate(1deg);transform:rotate(1deg)}.foogallery.fg-preset.fg-polaroid .fg-item:nth-child(5n){-webkit-transform:rotate(-2deg);transform:rotate(-2deg)}.foogallery.fg-preset.fg-polaroid .fg-item:hover{-webkit-transform:rotate(0);transform:rotate(0)}.foogallery.fg-preset.fg-polaroid .fg-caption{position:relative;width:auto;font-family:"Segoe Print Regular",-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif}.foogallery.fg-preset.fg-polaroid .fg-caption-inner,.foogallery.fg-preset.fg-polaroid .fg-caption-title{position:relative;width:auto}.foogallery.fg-preset.fg-polaroid .fg-caption-title{text-align:center}.foogallery.fg-preset.fg-polaroid .fg-caption-desc{display:none}.foogallery.fg-light.fg-preset.fg-polaroid .fg-caption-title,.foogallery.fg-preset.fg-polaroid .fg-caption-title{color:#333}.foogallery.fg-dark.fg-preset.fg-polaroid .fg-caption-title{color:#fff}.foogallery.fg-preset.fg-polaroid .fg-caption{border-style:solid;border-color:transparent;border-width:10px}.foogallery.fg-preset.fg-polaroid .fg-caption-title+.fg-caption-desc{margin-top:5px}.foogallery.fg-preset.fg-polaroid.fg-border-thin .fg-caption{border-width:10px 5px 5px 5px}.foogallery.fg-preset.fg-polaroid.fg-captions-top.fg-border-thin .fg-caption{border-width:5px 5px 10px 5px}.foogallery.fg-preset.fg-polaroid.fg-border-medium .fg-caption{border-width:10px 0 0 0}.foogallery.fg-preset.fg-polaroid.fg-captions-top.fg-border-medium .fg-caption{border-width:0 0 10px 0}.foogallery.fg-preset.fg-polaroid.fg-border-thick .fg-caption{border-width:15px 0 0 0}.foogallery.fg-preset.fg-polaroid.fg-captions-top.fg-border-thick .fg-caption{border-width:0 0 15px 0}.foogallery.fg-preset.fg-polaroid.fg-border-thick .fg-caption-title+.fg-caption-desc{margin-top:10px}.fg-image-viewer{display:block;font-family:'Open Sans','Helvetica Neue',Arial,sans-serif}.fg-image-viewer.fg-left{text-align:left}.fg-image-viewer.fg-center{text-align:center}.fg-image-viewer.fg-right{text-align:right}.fiv-inner{position:relative;display:inline-block;max-width:100%;overflow:hidden;z-index:6}.fiv-inner .fiv-inner-container{position:relative;overflow:hidden;max-width:100%;border-style:solid;border-width:0;border-bottom-width:4px;z-index:5}.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item .fg-thumb,.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item .fg-thumb:active,.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item .fg-thumb:hover,.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item .fg-thumb:visited{position:relative;display:block;border:none;outline:0;text-decoration:none;box-shadow:none;max-width:100%}.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item{position:relative;visibility:visible;opacity:1;border:none;outline:0;text-decoration:none;box-shadow:none;max-width:100%}.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item .fg-thumb img{display:block;max-width:100%;height:auto;border:none;outline:0;text-decoration:none}.fg-image-viewer .fiv-inner .fiv-ctrls{display:block;text-align:center;font-size:14px;border-style:solid;line-height:34px}.fg-image-viewer .fiv-inner .fiv-ctrls:after{content:'';display:block;clear:both}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-count{display:inline-block;font-weight:400;margin:0}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next,.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:none;min-width:80px;position:relative;overflow:hidden;transition:background-color .3s}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next:before,.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev:before{display:block;position:absolute;font-size:24px;line-height:30px;top:0;left:0;width:100%;transform:translateY(0);transition:transform .3s}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next:hover:before,.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev:hover:before{transform:translateY(-100%)}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next span,.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev span{display:block;width:100%;transform:translateY(100%);transition:transform .3s}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next:hover span,.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev:hover span{transform:translateY(0)}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev{float:left}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev:before{content:'\2190'}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next{float:right}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next:before{content:'\2192'}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-count span{margin:0 4px}/*!* Theme - Default (Light) *!*//*!* Theme - Dark *!*/.foogallery.fg-image-viewer.fg-caption-always .fg-item-inner .fg-caption{padding:0;border:none;background:#000;background:-moz-linear-gradient(left,rgba(0,0,0,.8) 0,rgba(0,0,0,.8) 60%,rgba(0,0,0,0) 100%);background:-webkit-linear-gradient(left,rgba(0,0,0,.8) 0,rgba(0,0,0,.8) 60%,rgba(0,0,0,0) 100%);background:linear-gradient(to right,rgba(0,0,0,.8) 0,rgba(0,0,0,.8) 60%,rgba(0,0,0,0) 100%)}.foogallery.fg-image-viewer.fg-caption-always .fg-caption-title{padding:10px 10px 10px 10px}.foogallery.fg-image-viewer.fg-caption-always .fg-caption-desc{padding:10px 10px 10px 10px}.foogallery.fg-image-viewer.fg-caption-always .fg-caption-title+.fg-caption-desc{padding:0 10px 10px 10px}.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls,.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls .fiv-count,.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls .fiv-next,.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls .fiv-prev,.fg-image-viewer.fg-light .fiv-inner .fiv-inner-container{background-color:#fff;color:#333;border-color:#fff}.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls .fiv-next:hover,.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls .fiv-prev:hover{background-color:#f2f2f2}.fg-image-viewer.fg-light .fiv-next,.fg-image-viewer.fg-light .fiv-prev{box-shadow:inset 0 0 0 1px #ddd}.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls,.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls .fiv-count,.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls .fiv-next,.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls .fiv-prev,.fg-image-viewer.fg-dark .fiv-inner .fiv-inner-container{background-color:#333;color:#fff;border-color:#333}.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls .fiv-next:hover,.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls .fiv-prev:hover{background-color:#444}.fg-image-viewer.fg-dark .fiv-next,.fg-image-viewer.fg-dark .fiv-prev{box-shadow:inset 0 0 0 1px #222}.foogallery.fg-image-viewer.fg-border-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thick .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thin .fg-item-inner{border-width:0}.foogallery.fg-image-viewer .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-thin .fiv-inner-container{border-width:4px}.foogallery.fg-image-viewer.fg-border-medium .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-medium .fiv-inner-container{border-width:10px}.foogallery.fg-image-viewer.fg-border-thick .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-thick .fiv-inner-container{border-width:16px}.foogallery.fg-image-viewer .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-medium .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-thick .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-thin .fiv-ctrls{border-top-width:1px}.foogallery.fg-image-viewer.fg-round-small .fg-item,.foogallery.fg-image-viewer.fg-round-small .fg-item-inner,.foogallery.fg-image-viewer.fg-round-small .fiv-inner{border-radius:5px}.foogallery.fg-image-viewer.fg-round-small .fg-item,.foogallery.fg-image-viewer.fg-round-small .fg-item-inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.foogallery.fg-image-viewer.fg-round-small .fiv-next,.foogallery.fg-image-viewer.fg-round-small .fiv-prev{border-radius:3px}.foogallery.fg-image-viewer.fg-border-medium.fg-round-small .fg-item,.foogallery.fg-image-viewer.fg-border-medium.fg-round-small .fg-item-inner,.foogallery.fg-image-viewer.fg-border-medium.fg-round-small .fiv-next,.foogallery.fg-image-viewer.fg-border-medium.fg-round-small .fiv-prev,.foogallery.fg-image-viewer.fg-border-thick.fg-round-small .fg-item,.foogallery.fg-image-viewer.fg-border-thick.fg-round-small .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thick.fg-round-small .fiv-next,.foogallery.fg-image-viewer.fg-border-thick.fg-round-small .fiv-prev,.foogallery.fg-image-viewer.fg-border-thin.fg-round-small .fg-item,.foogallery.fg-image-viewer.fg-border-thin.fg-round-small .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thin.fg-round-small .fiv-next,.foogallery.fg-image-viewer.fg-border-thin.fg-round-small .fiv-prev{border-radius:3px}.foogallery.fg-image-viewer.fg-round-medium .fg-item,.foogallery.fg-image-viewer.fg-round-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-round-medium .fiv-inner{border-radius:10px}.foogallery.fg-image-viewer.fg-round-medium .fg-item,.foogallery.fg-image-viewer.fg-round-medium .fg-item-inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.foogallery.fg-image-viewer.fg-round-medium .fiv-next,.foogallery.fg-image-viewer.fg-round-medium .fiv-prev{border-radius:5px}.foogallery.fg-image-viewer.fg-border-thin.fg-round-medium .fg-item,.foogallery.fg-image-viewer.fg-border-thin.fg-round-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thin.fg-round-medium .fiv-next,.foogallery.fg-image-viewer.fg-border-thin.fg-round-medium .fiv-prev{border-radius:5px}.foogallery.fg-image-viewer.fg-border-medium.fg-round-medium .fg-item,.foogallery.fg-image-viewer.fg-border-medium.fg-round-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-border-medium.fg-round-medium .fiv-next,.foogallery.fg-image-viewer.fg-border-medium.fg-round-medium .fiv-prev,.foogallery.fg-image-viewer.fg-border-thick.fg-round-medium .fg-item,.foogallery.fg-image-viewer.fg-border-thick.fg-round-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thick.fg-round-medium .fiv-next,.foogallery.fg-image-viewer.fg-border-thick.fg-round-medium .fiv-prev{border-radius:3px}.foogallery.fg-image-viewer.fg-round-large .fg-item,.foogallery.fg-image-viewer.fg-round-large .fg-item-inner,.foogallery.fg-image-viewer.fg-round-large .fiv-inner{border-radius:15px}.foogallery.fg-image-viewer.fg-round-large .fg-item,.foogallery.fg-image-viewer.fg-round-large .fg-item-inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.foogallery.fg-image-viewer.fg-round-large .fiv-next,.foogallery.fg-image-viewer.fg-round-large .fiv-prev{border-radius:11px}.foogallery.fg-image-viewer.fg-border-thin.fg-round-large .fg-item,.foogallery.fg-image-viewer.fg-border-thin.fg-round-large .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thin.fg-round-large .fiv-next,.foogallery.fg-image-viewer.fg-border-thin.fg-round-large .fiv-prev{border-radius:11px}.foogallery.fg-image-viewer.fg-border-medium.fg-round-large .fg-item,.foogallery.fg-image-viewer.fg-border-medium.fg-round-large .fg-item-inner,.foogallery.fg-image-viewer.fg-border-medium.fg-round-large .fiv-next,.foogallery.fg-image-viewer.fg-border-medium.fg-round-large .fiv-prev{border-radius:5px}.foogallery.fg-image-viewer.fg-border-thick.fg-round-large .fg-item,.foogallery.fg-image-viewer.fg-border-thick.fg-round-large .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thick.fg-round-large .fiv-next,.foogallery.fg-image-viewer.fg-border-thick.fg-round-large .fiv-prev{border-radius:3px}.foogallery.fg-image-viewer.fg-round-full .fiv-inner,.foogallery.fg-image-viewer.fg-round-full .fiv-next,.foogallery.fg-image-viewer.fg-round-full .fiv-prev{border-radius:50%}.foogallery.fg-image-viewer.fg-dark.fg-shadow-large .fg-item-inner,.foogallery.fg-image-viewer.fg-dark.fg-shadow-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-dark.fg-shadow-outline .fg-item-inner,.foogallery.fg-image-viewer.fg-dark.fg-shadow-small .fg-item-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-large .fg-item-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-outline .fg-item-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-small .fg-item-inner{box-shadow:none}.foogallery.fg-image-viewer.fg-light.fg-shadow-outline .fiv-inner{box-shadow:0 0 0 1px #ddd}.foogallery.fg-image-viewer.fg-dark.fg-shadow-outline .fiv-inner{box-shadow:0 0 0 1px #222}.foogallery.fg-image-viewer.fg-dark.fg-shadow-small .fiv-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-small .fiv-inner{box-shadow:0 1px 4px 0 rgba(0,0,0,.5)}.foogallery.fg-image-viewer.fg-dark.fg-shadow-medium .fiv-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-medium .fiv-inner{box-shadow:0 1px 10px 0 rgba(0,0,0,.5)}.foogallery.fg-image-viewer.fg-dark.fg-shadow-large .fiv-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-large .fiv-inner{box-shadow:0 1px 16px 0 rgba(0,0,0,.5)}.foogallery.fg-thumbnail,.foogallery.fg-thumbnail.fg-center{text-align:center}.foogallery.fg-thumbnail.fg-left{text-align:left}.foogallery.fg-thumbnail.fg-right{text-align:right}.foogallery.fg-thumbnail.fg-float-left{float:left;width:auto}.foogallery.fg-thumbnail.fg-float-right{float:right;width:auto}.foogallery.fg-thumbnail .fg-item{display:inline-block;vertical-align:top;max-width:100%}.foogallery.fg-thumbnail .fg-image{max-width:100%}.foogallery.fg-thumbnail .fg-st-hidden{display:none}
1
+ .foogallery,.foogallery *{box-sizing:border-box}.foogallery{display:block;z-index:1;font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;position:relative;line-height:0;font-size:0;width:100%;max-width:100%}.foogallery .fg-item{display:inline-block;position:relative;background-color:transparent;z-index:2;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.foogallery .fg-item-inner{display:block;position:relative;visibility:hidden;overflow:hidden;opacity:0;z-index:3;margin:0;border:solid 0 transparent}.foogallery .fg-item.fg-error,.foogallery .fg-item.fg-loading{background:no-repeat center}.foogallery .fg-item.fg-error{background-image:url(../img/image.png)}@media only screen and (-o-min-device-pixel-ratio:5/4),only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-device-pixel-ratio:1.25),only screen and (min-resolution:1.25dppx){.foogallery .fg-item.fg-error{background-image:url(../img/image@2x.png)}}@media only screen and (-o-min-device-pixel-ratio:9/4),only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-device-pixel-ratio:2.25),only screen and (min-resolution:2.25dppx){.foogallery .fg-item.fg-error{background-image:url(../img/image@3x.png)}}.foogallery .fg-item.fg-loaded{z-index:4}.foogallery .fg-loaded .fg-item-inner{visibility:visible;opacity:1;z-index:5}.foogallery .fg-error .fg-item-inner{pointer-events:none;cursor:default}.foogallery .fg-thumb{display:block;position:relative;border:none;outline:0;text-decoration:none;z-index:4}.foogallery .fg-image{display:block;position:relative;border:none;outline:0;text-decoration:none;z-index:5;max-width:none;height:auto;margin:0}.foogallery .fg-loaded .fg-thumb{z-index:6}.foogallery .fg-loaded .fg-image{z-index:7}.foogallery.fg-light .fg-item-inner{background-color:#fff;color:#333;border-color:#fff}.foogallery.fg-dark .fg-item-inner{background-color:#333;color:#fff;border-color:#333}.foogallery.fg-light .fg-item.fg-error,.foogallery.fg-light .fg-item.fg-idle,.foogallery.fg-light .fg-item.fg-loading{background-color:#eee;box-shadow:inset 0 0 0 1px #ddd}.foogallery.fg-dark .fg-item.fg-error,.foogallery.fg-dark .fg-item.fg-idle,.foogallery.fg-dark .fg-item.fg-loading{background-color:#444;box-shadow:inset 0 0 0 1px #333}.foogallery.fg-border-thin .fg-item-inner{border-width:4px}.foogallery.fg-border-medium .fg-item-inner{border-width:10px}.foogallery.fg-border-thick .fg-item-inner{border-width:16px}.foogallery.fg-light.fg-shadow-outline .fg-item-inner{box-shadow:0 0 0 1px #ddd}.foogallery.fg-dark.fg-shadow-outline .fg-item-inner{box-shadow:0 0 0 1px #222}.foogallery.fg-dark.fg-shadow-small .fg-item-inner,.foogallery.fg-light.fg-shadow-small .fg-item-inner{box-shadow:0 1px 4px 0 rgba(0,0,0,.5)}.foogallery.fg-dark.fg-shadow-medium .fg-item-inner,.foogallery.fg-light.fg-shadow-medium .fg-item-inner{box-shadow:0 1px 10px 0 rgba(0,0,0,.5)}.foogallery.fg-dark.fg-shadow-large .fg-item-inner,.foogallery.fg-light.fg-shadow-large .fg-item-inner{box-shadow:0 1px 16px 0 rgba(0,0,0,.5)}.foogallery.fg-shadow-inset-large .fg-thumb:after,.foogallery.fg-shadow-inset-medium .fg-thumb:after,.foogallery.fg-shadow-inset-small .fg-thumb:after{display:block;content:"";position:absolute;top:0;left:0;right:0;bottom:0;z-index:7}.foogallery.fg-dark.fg-shadow-inset-small .fg-thumb:after,.foogallery.fg-light.fg-shadow-inset-small .fg-thumb:after{box-shadow:inset 0 1px 4px 0 rgba(0,0,0,.3)}.foogallery.fg-dark.fg-shadow-inset-medium .fg-thumb:after,.foogallery.fg-light.fg-shadow-inset-medium .fg-thumb:after{box-shadow:inset 0 1px 10px 0 rgba(0,0,0,.3)}.foogallery.fg-dark.fg-shadow-inset-large .fg-thumb:after,.foogallery.fg-light.fg-shadow-inset-large .fg-thumb:after{box-shadow:inset 0 1px 16px 0 rgba(0,0,0,.3)}.foogallery.fg-round-full.fg-shadow-inset-large .fg-thumb:after,.foogallery.fg-round-full.fg-shadow-inset-medium .fg-thumb:after,.foogallery.fg-round-full.fg-shadow-inset-small .fg-thumb:after{border-radius:50%}.foogallery.fg-round-small .fg-item,.foogallery.fg-round-small .fg-item-inner{border-radius:5px}.foogallery.fg-round-medium .fg-item,.foogallery.fg-round-medium .fg-item-inner{border-radius:10px}.foogallery.fg-round-large .fg-item,.foogallery.fg-round-large .fg-item-inner{border-radius:15px}.foogallery.fg-round-full .fg-item,.foogallery.fg-round-full .fg-item-inner{border-radius:50%}.foogallery .fg-loader{position:absolute;top:50%;left:50%;transform:translateX(-50%) translateY(-50%);width:1em;height:1em;font-size:5px;visibility:hidden;opacity:0}.foogallery .fg-loading .fg-loader{visibility:visible;opacity:1}.fg-loading-default .fg-loader{border-radius:50%;text-indent:-9999em;-webkit-animation:loading-default 1.1s infinite ease;animation:loading-default 1.1s infinite ease}@-webkit-keyframes loading-default{0%,100%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,1),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.5),-1.8em -1.8em 0 0 rgba(130,130,130,.7)}12.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.7),1.8em -1.8em 0 0 rgba(130,130,130,1),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.5)}25%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.5),1.8em -1.8em 0 0 rgba(130,130,130,.7),2.5em 0 0 0 rgba(130,130,130,1),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}37.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.5),2.5em 0 0 0 rgba(130,130,130,.7),1.75em 1.75em 0 0 rgba(130,130,130,1),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}50%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.5),1.75em 1.75em 0 0 rgba(130,130,130,.7),0 2.5em 0 0 rgba(130,130,130,1),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}62.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.5),0 2.5em 0 0 rgba(130,130,130,.7),-1.8em 1.8em 0 0 rgba(130,130,130,1),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}75%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.5),-1.8em 1.8em 0 0 rgba(130,130,130,.7),-2.6em 0 0 0 rgba(130,130,130,1),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}87.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.5),-2.6em 0 0 0 rgba(130,130,130,.7),-1.8em -1.8em 0 0 rgba(130,130,130,1)}}@keyframes loading-default{0%,100%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,1),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.5),-1.8em -1.8em 0 0 rgba(130,130,130,.7)}12.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.7),1.8em -1.8em 0 0 rgba(130,130,130,1),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.5)}25%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.5),1.8em -1.8em 0 0 rgba(130,130,130,.7),2.5em 0 0 0 rgba(130,130,130,1),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}37.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.5),2.5em 0 0 0 rgba(130,130,130,.7),1.75em 1.75em 0 0 rgba(130,130,130,1),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}50%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.5),1.75em 1.75em 0 0 rgba(130,130,130,.7),0 2.5em 0 0 rgba(130,130,130,1),-1.8em 1.8em 0 0 rgba(130,130,130,.2),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}62.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.5),0 2.5em 0 0 rgba(130,130,130,.7),-1.8em 1.8em 0 0 rgba(130,130,130,1),-2.6em 0 0 0 rgba(130,130,130,.2),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}75%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.5),-1.8em 1.8em 0 0 rgba(130,130,130,.7),-2.6em 0 0 0 rgba(130,130,130,1),-1.8em -1.8em 0 0 rgba(130,130,130,.2)}87.5%{box-shadow:0 -2.6em 0 0 rgba(130,130,130,.2),1.8em -1.8em 0 0 rgba(130,130,130,.2),2.5em 0 0 0 rgba(130,130,130,.2),1.75em 1.75em 0 0 rgba(130,130,130,.2),0 2.5em 0 0 rgba(130,130,130,.2),-1.8em 1.8em 0 0 rgba(130,130,130,.5),-2.6em 0 0 0 rgba(130,130,130,.7),-1.8em -1.8em 0 0 rgba(130,130,130,1)}}.fg-loading-bars .fg-loader,.fg-loading-bars .fg-loader:after,.fg-loading-bars .fg-loader:before{background:rgba(130,130,130,1);-webkit-animation:loading-bars 1s infinite ease-in-out;animation:loading-bars 1s infinite ease-in-out;width:1em;height:4em}.fg-loading-bars .fg-loader{color:rgba(130,130,130,1);text-indent:-9999em;font-size:4px;-webkit-animation-delay:-.16s;animation-delay:-.16s}.fg-loading-bars .fg-loader:after,.fg-loading-bars .fg-loader:before{position:absolute;top:0;content:''}.fg-loading-bars .fg-loader:before{left:-1.5em;-webkit-animation-delay:-.32s;animation-delay:-.32s}.fg-loading-bars .fg-loader:after{left:1.5em}@-webkit-keyframes loading-bars{0%,100%,80%{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}@keyframes loading-bars{0%,100%,80%{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}.fg-loading-trail .fg-loader{color:#828282;font-size:20px;text-indent:-9999em;overflow:hidden;border-radius:50%;-webkit-animation:loading-trail-1 1.7s infinite ease,loading-trail-2 1.7s infinite ease;animation:loading-trail-1 1.7s infinite ease,loading-trail-2 1.7s infinite ease}@-webkit-keyframes loading-trail-1{0%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}5%,95%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}10%,59%{box-shadow:0 -.83em 0 -.4em,-.087em -.825em 0 -.42em,-.173em -.812em 0 -.44em,-.256em -.789em 0 -.46em,-.297em -.775em 0 -.477em}20%{box-shadow:0 -.83em 0 -.4em,-.338em -.758em 0 -.42em,-.555em -.617em 0 -.44em,-.671em -.488em 0 -.46em,-.749em -.34em 0 -.477em}38%{box-shadow:0 -.83em 0 -.4em,-.377em -.74em 0 -.42em,-.645em -.522em 0 -.44em,-.775em -.297em 0 -.46em,-.82em -.09em 0 -.477em}100%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}}@keyframes loading-trail-1{0%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}5%,95%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}10%,59%{box-shadow:0 -.83em 0 -.4em,-.087em -.825em 0 -.42em,-.173em -.812em 0 -.44em,-.256em -.789em 0 -.46em,-.297em -.775em 0 -.477em}20%{box-shadow:0 -.83em 0 -.4em,-.338em -.758em 0 -.42em,-.555em -.617em 0 -.44em,-.671em -.488em 0 -.46em,-.749em -.34em 0 -.477em}38%{box-shadow:0 -.83em 0 -.4em,-.377em -.74em 0 -.42em,-.645em -.522em 0 -.44em,-.775em -.297em 0 -.46em,-.82em -.09em 0 -.477em}100%{box-shadow:0 -.83em 0 -.4em,0 -.83em 0 -.42em,0 -.83em 0 -.44em,0 -.83em 0 -.46em,0 -.83em 0 -.477em}}@-webkit-keyframes loading-trail-2{0%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(0);transform:translateX(-50%) translateY(-50%) rotate(0)}100%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(360deg);transform:translateX(-50%) translateY(-50%) rotate(360deg)}}@keyframes loading-trail-2{0%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(0);transform:translateX(-50%) translateY(-50%) rotate(0)}100%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(360deg);transform:translateX(-50%) translateY(-50%) rotate(360deg)}}.fg-loading-pulse .fg-loader,.fg-loading-pulse .fg-loader:after,.fg-loading-pulse .fg-loader:before{border-radius:50%;width:2.5em;height:2.5em;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation:loading-pulse 1.8s infinite ease-in-out;animation:loading-pulse 1.8s infinite ease-in-out}.fg-loading-pulse .fg-loader{color:#828282;font-size:4px;text-indent:-9999em;transform:translateX(-50%) translateY(-150%);-webkit-animation-delay:-.16s;animation-delay:-.16s}.fg-loading-pulse .fg-loader:after,.fg-loading-pulse .fg-loader:before{content:'';position:absolute;top:0}.fg-loading-pulse .fg-loader:before{left:-3.5em;-webkit-animation-delay:-.32s;animation-delay:-.32s}.fg-loading-pulse .fg-loader:after{left:3.5em}@-webkit-keyframes loading-pulse{0%,100%,80%{box-shadow:0 2.5em 0 -1.3em}40%{box-shadow:0 2.5em 0 0}}@keyframes loading-pulse{0%,100%,80%{box-shadow:0 2.5em 0 -1.3em}40%{box-shadow:0 2.5em 0 0}}.fg-loading-dots .fg-loader{color:#828282;font-size:5px;border-radius:50%;text-indent:-9999em;-webkit-animation:loading-dots 1.3s infinite linear;animation:loading-dots 1.3s infinite linear}@-webkit-keyframes loading-dots{0%,100%{box-shadow:0 -3em 0 .2em,2em -2em 0 0,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 0}12.5%{box-shadow:0 -3em 0 0,2em -2em 0 .2em,3em 0 0 0,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}25%{box-shadow:0 -3em 0 -.5em,2em -2em 0 0,3em 0 0 .2em,2em 2em 0 0,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}37.5%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 0,2em 2em 0 .2em,0 3em 0 0,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}50%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 0,0 3em 0 .2em,-2em 2em 0 0,-3em 0 0 -1em,-2em -2em 0 -1em}62.5%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 0,-2em 2em 0 .2em,-3em 0 0 0,-2em -2em 0 -1em}75%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 0,-3em 0 0 .2em,-2em -2em 0 0}87.5%{box-shadow:0 -3em 0 0,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 0,-3em 0 0 0,-2em -2em 0 .2em}}@keyframes loading-dots{0%,100%{box-shadow:0 -3em 0 .2em,2em -2em 0 0,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 0}12.5%{box-shadow:0 -3em 0 0,2em -2em 0 .2em,3em 0 0 0,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}25%{box-shadow:0 -3em 0 -.5em,2em -2em 0 0,3em 0 0 .2em,2em 2em 0 0,0 3em 0 -1em,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}37.5%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 0,2em 2em 0 .2em,0 3em 0 0,-2em 2em 0 -1em,-3em 0 0 -1em,-2em -2em 0 -1em}50%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 0,0 3em 0 .2em,-2em 2em 0 0,-3em 0 0 -1em,-2em -2em 0 -1em}62.5%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 0,-2em 2em 0 .2em,-3em 0 0 0,-2em -2em 0 -1em}75%{box-shadow:0 -3em 0 -1em,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 0,-3em 0 0 .2em,-2em -2em 0 0}87.5%{box-shadow:0 -3em 0 0,2em -2em 0 -1em,3em 0 0 -1em,2em 2em 0 -1em,0 3em 0 -1em,-2em 2em 0 0,-3em 0 0 0,-2em -2em 0 .2em}}.fg-loading-partial .fg-loader,.fg-loading-partial .fg-loader:after{border-radius:50%;width:10em;height:10em}.fg-loading-partial .fg-loader{font-size:4px;text-indent:-9999em;border-top:1.1em solid rgba(130,130,130,.2);border-right:1.1em solid rgba(130,130,130,.2);border-bottom:1.1em solid rgba(130,130,130,.2);border-left:1.1em solid #828282;-webkit-animation:loading-partial 1.1s infinite linear;animation:loading-partial 1.1s infinite linear}@-webkit-keyframes loading-partial{0%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(0);transform:translateX(-50%) translateY(-50%) rotate(0)}100%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(360deg);transform:translateX(-50%) translateY(-50%) rotate(360deg)}}@keyframes loading-partial{0%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(0);transform:translateX(-50%) translateY(-50%) rotate(0)}100%{-webkit-transform:translateX(-50%) translateY(-50%) rotate(360deg);transform:translateX(-50%) translateY(-50%) rotate(360deg)}}.foogallery.fg-loaded-drop .fg-item,.foogallery.fg-loaded-fade-in .fg-item,.foogallery.fg-loaded-flip .fg-item,.foogallery.fg-loaded-fly .fg-item,.foogallery.fg-loaded-scale-up .fg-item,.foogallery.fg-loaded-slide-down .fg-item,.foogallery.fg-loaded-slide-left .fg-item,.foogallery.fg-loaded-slide-right .fg-item,.foogallery.fg-loaded-slide-up .fg-item,.foogallery.fg-loaded-swing-down .fg-item{transition-timing-function:ease;transition-duration:650ms;transition-property:background-color,transform}.foogallery.fg-loaded-drop .fg-item-inner,.foogallery.fg-loaded-fade-in .fg-item-inner,.foogallery.fg-loaded-flip .fg-item-inner,.foogallery.fg-loaded-fly .fg-item-inner,.foogallery.fg-loaded-scale-up .fg-item-inner,.foogallery.fg-loaded-slide-down .fg-item-inner,.foogallery.fg-loaded-slide-left .fg-item-inner,.foogallery.fg-loaded-slide-right .fg-item-inner,.foogallery.fg-loaded-slide-up .fg-item-inner,.foogallery.fg-loaded-swing-down .fg-item-inner{transition-timing-function:ease;transition-duration:650ms}.foogallery.fg-loaded-drop .fg-item.fg-loaded,.foogallery.fg-loaded-flip .fg-item.fg-loaded,.foogallery.fg-loaded-fly .fg-item.fg-loaded,.foogallery.fg-loaded-swing-down .fg-item.fg-loaded{perspective:1300px}.foogallery.fg-loaded-fade-in .fg-item-inner{transition-property:visibility,opacity}.foogallery .fg-caption{visibility:hidden;opacity:0;background-color:rgba(0,0,0,.6);color:#fff;position:absolute;z-index:8;width:100%;max-height:100%;overflow:hidden;font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:13px;font-weight:400;line-height:1.3;border:none;text-align:center;cursor:pointer}.foogallery .fg-caption a{text-decoration:none;color:#fff;border-bottom:1px solid #fff}.foogallery .fg-caption a:hover{border-bottom:none}.foogallery .fg-caption-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:16px;font-weight:400;padding:5px}.foogallery .fg-caption-desc{padding:5px}.foogallery .fg-caption-title+.fg-caption-desc{padding-top:0}.foogallery.fg-caption-always .fg-caption .fg-caption-inner:before{display:none}.foogallery.fg-caption-always .fg-item.fg-loaded .fg-caption{left:0;bottom:0;transition-timing-function:ease;transition-duration:.3s;transition-property:visibility,opacity;visibility:visible;opacity:1;text-align:left}.foogallery.fg-caption-hover .fg-caption .fg-caption-inner{width:100%;max-height:100%;position:absolute;top:50%;left:0;transform:translateY(-50%)}.foogallery.fg-caption-hover .fg-item.fg-loaded .fg-thumb:before{display:none}.foogallery.fg-hover-circle-plus .fg-thumb:before,.foogallery.fg-hover-external .fg-thumb:before,.foogallery.fg-hover-eye .fg-thumb:before,.foogallery.fg-hover-plus .fg-thumb:before,.foogallery.fg-hover-tint .fg-thumb:before,.foogallery.fg-hover-zoom .fg-thumb:before,.foogallery.fg-hover-zoom2 .fg-thumb:before,.foogallery.fg-hover-zoom3 .fg-thumb:before{content:"";display:block;position:absolute;visibility:hidden;opacity:0;top:0;bottom:0;left:0;right:0;z-index:8;background:rgba(0,0,0,.5) no-repeat center center;background-size:32px 32px}.foogallery.fg-hover-circle-plus .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-circle-plus .fg-thumb:focus:before,.foogallery.fg-hover-external .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-external .fg-thumb:focus:before,.foogallery.fg-hover-eye .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-eye .fg-thumb:focus:before,.foogallery.fg-hover-plus .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-plus .fg-thumb:focus:before,.foogallery.fg-hover-tint .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-tint .fg-thumb:focus:before,.foogallery.fg-hover-zoom .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-zoom .fg-thumb:focus:before,.foogallery.fg-hover-zoom2 .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-zoom2 .fg-thumb:focus:before,.foogallery.fg-hover-zoom3 .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-zoom3 .fg-thumb:focus:before{visibility:visible;opacity:1}.foogallery.fg-hover-circle-plus .fg-caption-inner:before,.foogallery.fg-hover-external .fg-caption-inner:before,.foogallery.fg-hover-eye .fg-caption-inner:before,.foogallery.fg-hover-plus .fg-caption-inner:before,.foogallery.fg-hover-tint .fg-caption-inner:before,.foogallery.fg-hover-zoom .fg-caption-inner:before,.foogallery.fg-hover-zoom2 .fg-caption-inner:before,.foogallery.fg-hover-zoom3 .fg-caption-inner:before{content:"";display:inline-block;position:relative;width:32px;height:32px;margin:10px 0 5px 0;background:transparent no-repeat center center;background-size:32px 32px;vertical-align:middle}.foogallery.fg-hover-zoom .fg-caption-inner:before,.foogallery.fg-hover-zoom .fg-thumb:before{background-image:url(../img/zoom.png)}.foogallery.fg-hover-zoom2 .fg-caption-inner:before,.foogallery.fg-hover-zoom2 .fg-thumb:before{background-image:url(../img/zoom2.png)}.foogallery.fg-hover-zoom3 .fg-caption-inner:before,.foogallery.fg-hover-zoom3 .fg-thumb:before{background-image:url(../img/zoom3.png)}.foogallery.fg-hover-plus .fg-caption-inner:before,.foogallery.fg-hover-plus .fg-thumb:before{background-image:url(../img/plus.png)}.foogallery.fg-hover-circle-plus .fg-caption-inner:before,.foogallery.fg-hover-circle-plus .fg-thumb:before{background-image:url(../img/circle-plus.png)}.foogallery.fg-hover-eye .fg-caption-inner:before,.foogallery.fg-hover-eye .fg-thumb:before{background-image:url(../img/eye.png)}.foogallery.fg-hover-external .fg-caption-inner:before,.foogallery.fg-hover-external .fg-thumb:before{background-image:url(../img/external.png)}@media only screen and (-o-min-device-pixel-ratio:5/4),only screen and (-webkit-min-device-pixel-ratio:1.25),only screen and (min-device-pixel-ratio:1.25),only screen and (min-resolution:1.25dppx){.foogallery.fg-hover-zoom .fg-caption-inner:before,.foogallery.fg-hover-zoom .fg-thumb:before{background-image:url(../img/zoom@2x.png)}.foogallery.fg-hover-zoom2 .fg-caption-inner:before,.foogallery.fg-hover-zoom2 .fg-thumb:before{background-image:url(../img/zoom2@2x.png)}.foogallery.fg-hover-zoom3 .fg-caption-inner:before,.foogallery.fg-hover-zoom3 .fg-thumb:before{background-image:url(../img/zoom3@2x.png)}.foogallery.fg-hover-plus .fg-caption-inner:before,.foogallery.fg-hover-plus .fg-thumb:before{background-image:url(../img/plus@2x.png)}.foogallery.fg-hover-circle-plus .fg-caption-inner:before,.foogallery.fg-hover-circle-plus .fg-thumb:before{background-image:url(../img/circle-plus@2x.png)}.foogallery.fg-hover-eye .fg-caption-inner:before,.foogallery.fg-hover-eye .fg-thumb:before{background-image:url(../img/eye@2x.png)}.foogallery.fg-hover-external .fg-caption-inner:before,.foogallery.fg-hover-external .fg-thumb:before{background-image:url(../img/external@2x.png)}}@media only screen and (-o-min-device-pixel-ratio:9/4),only screen and (-webkit-min-device-pixel-ratio:2.25),only screen and (min-device-pixel-ratio:2.25),only screen and (min-resolution:2.25dppx){.foogallery.fg-hover-zoom .fg-caption-inner:before,.foogallery.fg-hover-zoom .fg-thumb:before{background-image:url(../img/zoom@3x.png)}.foogallery.fg-hover-zoom2 .fg-caption-inner:before,.foogallery.fg-hover-zoom2 .fg-thumb:before{background-image:url(../img/zoom2@3x.png)}.foogallery.fg-hover-zoom3 .fg-caption-inner:before,.foogallery.fg-hover-zoom3 .fg-thumb:before{background-image:url(../img/zoom3@3x.png)}.foogallery.fg-hover-plus .fg-caption-inner:before,.foogallery.fg-hover-plus .fg-thumb:before{background-image:url(../img/plus@3x.png)}.foogallery.fg-hover-circle-plus .fg-caption-inner:before,.foogallery.fg-hover-circle-plus .fg-thumb:before{background-image:url(../img/circle-plus@3x.png)}.foogallery.fg-hover-eye .fg-caption-inner:before,.foogallery.fg-hover-eye .fg-thumb:before{background-image:url(../img/eye@3x.png)}.foogallery.fg-hover-external .fg-caption-inner:before,.foogallery.fg-hover-external .fg-thumb:before{background-image:url(../img/external@3x.png)}}.foogallery.fg-caption-hover.fg-hover-colorize .fg-caption,.foogallery.fg-caption-hover.fg-hover-fade .fg-caption,.foogallery.fg-caption-hover.fg-hover-grayscale .fg-caption,.foogallery.fg-caption-hover.fg-hover-instant .fg-caption,.foogallery.fg-caption-hover.fg-hover-push .fg-caption,.foogallery.fg-caption-hover.fg-hover-scale .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-down .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-left .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-right .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-up .fg-caption,.foogallery.fg-hover-colorize .fg-image,.foogallery.fg-hover-colorize .fg-thumb:before,.foogallery.fg-hover-fade .fg-thumb:before,.foogallery.fg-hover-grayscale .fg-image,.foogallery.fg-hover-grayscale .fg-thumb:before,.foogallery.fg-hover-instant .fg-thumb:before,.foogallery.fg-hover-push .fg-thumb,.foogallery.fg-hover-scale .fg-item,.foogallery.fg-hover-scale .fg-thumb:before,.foogallery.fg-hover-slide-down .fg-thumb:before,.foogallery.fg-hover-slide-left .fg-thumb:before,.foogallery.fg-hover-slide-right .fg-thumb:before,.foogallery.fg-hover-slide-up .fg-thumb:before{transition-timing-function:ease;transition-duration:.3s}.foogallery.fg-hover-colorize .fg-image{filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'saturate\' values=\'0\'/></filter></svg>#grayscale");filter:gray;-webkit-filter:grayscale(100%);-webkit-transition-property:-webkit-filter;transition-property:filter}.foogallery.fg-hover-colorize .fg-item-inner:hover .fg-image{-webkit-filter:none;filter:none}.foogallery.fg-caption-hover.fg-hover-colorize .fg-caption,.foogallery.fg-hover-colorize .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:visibility,opacity}.foogallery.fg-caption-hover.fg-hover-colorize .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-colorize .fg-item-inner:hover .fg-thumb:before{visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-fade .fg-loaded .fg-caption,.foogallery.fg-hover-fade .fg-loaded .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:visibility,opacity}.foogallery.fg-caption-hover.fg-hover-fade .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-fade .fg-loaded .fg-item-inner:hover .fg-thumb:before{visibility:visible;opacity:1}.foogallery.fg-hover-grayscale .fg-image{-webkit-filter:none;filter:none;-webkit-transition-property:-webkit-filter;transition-property:filter}.foogallery.fg-hover-grayscale .fg-item-inner:hover .fg-image{-webkit-filter:grayscale(1);-webkit-filter:grayscale(100%);filter:grayscale(100%);filter:gray;opacity:1}.foogallery.fg-caption-hover.fg-hover-grayscale .fg-caption,.foogallery.fg-hover-grayscale .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:visibility,opacity}.foogallery.fg-caption-hover.fg-hover-grayscale .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-grayscale .fg-item-inner:hover .fg-thumb:before{visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-instant .fg-loaded .fg-caption,.foogallery.fg-hover-instant .fg-loaded .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:none}.foogallery.fg-caption-hover.fg-hover-instant .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-instant .fg-loaded .fg-item-inner:hover .fg-thumb:before{visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-push .fg-loaded .fg-caption,.foogallery.fg-hover-push .fg-loaded .fg-thumb:before{display:block;left:0;top:0;bottom:0;transform:translateX(100%);visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-push .fg-loaded .fg-caption,.foogallery.fg-hover-push .fg-loaded .fg-thumb{transition-property:transform}.foogallery.fg-caption-hover.fg-hover-push .fg-loaded .fg-item-inner:hover .fg-caption{transform:translateY(0)}.foogallery.fg-caption-hover.fg-hover-push .fg-loaded .fg-item-inner:hover .fg-thumb,.foogallery.fg-hover-push .fg-loaded .fg-item-inner:hover .fg-thumb{transform:translateX(-100%)}.foogallery.fg-hover-scale .fg-item{transition-property:transform;z-index:4}.foogallery.fg-hover-scale .fg-item:hover{transform:scale(1.048);z-index:10}.foogallery.fg-caption-hover.fg-hover-scale .fg-caption,.foogallery.fg-hover-scale .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:visibility,opacity}.foogallery.fg-caption-hover.fg-hover-scale .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-scale .fg-item-inner:hover .fg-thumb:before{visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-slide-down .fg-loaded .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-left .fg-loaded .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-right .fg-loaded .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-up .fg-loaded .fg-caption,.foogallery.fg-hover-slide-down .fg-loaded .fg-thumb:before,.foogallery.fg-hover-slide-left .fg-loaded .fg-thumb:before,.foogallery.fg-hover-slide-right .fg-loaded .fg-thumb:before,.foogallery.fg-hover-slide-up .fg-loaded .fg-thumb:before{display:block;left:0;top:0;bottom:0;transition-property:transform;visibility:visible;opacity:1}.foogallery.fg-caption-hover.fg-hover-slide-down .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-left .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-right .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-caption-hover.fg-hover-slide-up .fg-loaded .fg-item-inner:hover .fg-caption,.foogallery.fg-hover-slide-down .fg-loaded .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-slide-left .fg-loaded .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-slide-right .fg-loaded .fg-item-inner:hover .fg-thumb:before,.foogallery.fg-hover-slide-up .fg-loaded .fg-item-inner:hover .fg-thumb:before{transform:translateY(0) translateX(0)}.foogallery.fg-caption-hover.fg-hover-slide-up .fg-loaded .fg-caption,.foogallery.fg-hover-slide-up .fg-loaded .fg-thumb:before{transform:translateY(100%)}.foogallery.fg-caption-hover.fg-hover-slide-down .fg-loaded .fg-caption,.foogallery.fg-hover-slide-down .fg-loaded .fg-thumb:before{transform:translateY(-100%)}.foogallery.fg-caption-hover.fg-hover-slide-left .fg-loaded .fg-caption,.foogallery.fg-hover-slide-left .fg-loaded .fg-thumb:before{transform:translateX(100%)}.foogallery.fg-caption-hover.fg-hover-slide-right .fg-loaded .fg-caption,.foogallery.fg-hover-slide-right .fg-loaded .fg-thumb:before{transform:translateX(-100%)}.fg-paging-container,.fg-paging-container *,.fg-paging-container :after,.fg-paging-container :before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fg-paging-container{display:block;padding:15px;text-align:center;font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fg-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fg-paging-container .fg-dot-item,.fg-paging-container .fg-dots{display:inline-block;margin:0;padding:0;outline:0;list-style:none}.fg-paging-container .fg-dot-item .fg-dot-link{display:inline-block;margin:3px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;user-select:none;background-image:none;text-decoration:none;border:1px solid transparent;position:relative;border-radius:50%;padding:0;font-size:0;outline:0;color:transparent;box-shadow:none}.fg-paging-container .fg-dot-item .fg-dot-link:before{content:"";background-color:transparent;border:1px solid transparent;display:block;border-radius:50%;width:9px;height:9px;padding:0;margin:2px}.fg-paging-container .fg-dot-item .fg-dot-link:active,.fg-paging-container .fg-dot-item .fg-dot-link:focus,.fg-paging-container .fg-dot-item .fg-dot-link:hover{text-decoration:none;box-shadow:none;outline:0}.fg-paging-container .fg-dot-item.fg-disabled .fg-dot-link,.fg-paging-container .fg-dot-item.fg-selected .fg-dot-link{cursor:not-allowed;pointer-events:none}.fg-paging-container .fg-dot-item.fg-disabled .fg-dot-link{cursor:not-allowed;pointer-events:none;outline:0}.fg-paging-container.fg-light .fg-dot-item .fg-dot-link,.fg-paging-container.fg-light .fg-dot-item .fg-dot-link:before{transition-timing-function:ease-out;transition-duration:.3s;transition-property:color,border-color,background-color}.fg-paging-container.fg-light .fg-dot-item .fg-dot-link{background-color:#eee;border-color:#9e9e9e}.fg-paging-container.fg-light .fg-dot-item.fg-selected .fg-dot-link{border-color:#8a8a8a}.fg-paging-container.fg-light .fg-dot-item .fg-dot-link:focus:before,.fg-paging-container.fg-light .fg-dot-item .fg-dot-link:hover:before,.fg-paging-container.fg-light .fg-dot-item.fg-selected .fg-dot-link:before{background-color:#666;border-color:#8a8a8a}.fg-paging-container.fg-light .fg-dot-item.fg-disabled .fg-dot-link,.fg-paging-container.fg-light .fg-dot-item.fg-disabled .fg-dot-link:focus,.fg-paging-container.fg-light .fg-dot-item.fg-disabled .fg-dot-link:hover{background-color:#eee;border-color:#9e9e9e;opacity:.5}.fg-paging-container.fg-dark .fg-dot-item .fg-dot-link,.fg-paging-container.fg-dark .fg-dot-item .fg-dot-link:before{transition-timing-function:ease-out;transition-duration:.3s;transition-property:color,border-color,background-color}.fg-paging-container.fg-dark .fg-dot-item .fg-dot-link{background-color:#666;border-color:#333}.fg-paging-container.fg-dark .fg-dot-item.fg-selected .fg-dot-link{border-color:#444}.fg-paging-container.fg-dark .fg-dot-item .fg-dot-link:focus:before,.fg-paging-container.fg-dark .fg-dot-item .fg-dot-link:hover:before,.fg-paging-container.fg-dark .fg-dot-item.fg-selected .fg-dot-link:before{background-color:#333;border-color:#444}.fg-paging-container.fg-dark .fg-dot-item.fg-disabled .fg-dot-link,.fg-paging-container.fg-dark .fg-dot-item.fg-disabled .fg-dot-link:focus,.fg-paging-container.fg-dark .fg-dot-item.fg-disabled .fg-dot-link:hover{background-color:#666;border-color:#333;opacity:.5}.fg-default:after{content:'';display:block;clear:both}.fg-default .fg-item,.fg-default .fg-item-inner,.fg-default .fg-thumb{display:inline-block;vertical-align:top;max-width:100%}.fg-default .fg-image{border-radius:0;display:block;max-width:100%;height:auto;margin:0;padding:0}.fg-default .fg-image{vertical-align:top}.fg-default.fg-left{text-align:left}.fg-default.fg-center{text-align:center}.fg-default.fg-right{text-align:right}.fg-default.fg-gutter-5{padding-left:5px;margin-bottom:-5px}.fg-default.fg-gutter-5 .fg-item{margin-right:5px;margin-bottom:5px}.fg-default.fg-gutter-10{padding-left:10px;margin-bottom:-10px}.fg-default.fg-gutter-10 .fg-item{margin-right:10px;margin-bottom:10px}.fg-default.fg-gutter-15{padding-left:15px;margin-bottom:-15px}.fg-default.fg-gutter-15 .fg-item{margin-right:15px;margin-bottom:15px}.fg-default.fg-gutter-20{padding-left:20px;margin-bottom:-20px}.fg-default.fg-gutter-20 .fg-item{margin-right:20px;margin-bottom:20px}.fg-default.fg-gutter-25{padding-left:25px;margin-bottom:-25px}.fg-default.fg-gutter-25 .fg-item{margin-right:25px;margin-bottom:25px}.fg-masonry *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.foogallery.fg-masonry.fg-center{margin:0 auto}.fg-masonry .fg-thumb{display:block}.fg-masonry.fg-masonry-fixed .fg-thumb{display:inline-block}.fg-masonry.fg-masonry-fixed .fg-image{max-width:100%}.fg-masonry .fg-column-width{display:inline-block;visibility:hidden;height:0;border:solid 0 transparent}.fg-masonry.fg-masonry-2col .fg-image,.fg-masonry.fg-masonry-3col .fg-image,.fg-masonry.fg-masonry-4col .fg-image,.fg-masonry.fg-masonry-5col .fg-image{width:100%;height:auto;max-width:100%}.fg-masonry .fg-item{line-height:0;font-size:0}.fg-masonry.fg-masonry-2col .fg-item{margin-bottom:1%;width:49%}.fg-masonry.fg-masonry-2col .fg-column-width{width:49%}.fg-masonry.fg-masonry-2col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-item{margin-bottom:0;width:50%}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-column-width{width:50%}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-item{margin-bottom:3%;width:47%}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-column-width{width:47%}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-gutter-width{width:3%}.fg-masonry.fg-masonry-3col .fg-item{margin-bottom:1%;width:32%}.fg-masonry.fg-masonry-3col .fg-column-width{width:32%}.fg-masonry.fg-masonry-3col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-item{margin-bottom:0;width:33%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-column-width{width:33%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-item{margin-bottom:3%;width:30%}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-column-width{width:30%}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-gutter-width{width:3%}.fg-masonry.fg-masonry-4col .fg-item{margin-bottom:1%;width:24%}.fg-masonry.fg-masonry-4col .fg-column-width{width:24%}.fg-masonry.fg-masonry-4col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-item{margin-bottom:0;width:25%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-column-width{width:25%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-item{margin-bottom:3%;width:22%}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-column-width{width:22%}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-gutter-width{width:3%}.fg-masonry.fg-masonry-5col .fg-item{margin-bottom:1%;width:19%}.fg-masonry.fg-masonry-5col .fg-column-width{width:19%}.fg-masonry.fg-masonry-5col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-item{margin-bottom:0;width:20%}.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-column-width{width:20%}.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-item{margin-bottom:3%;width:17%}.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-column-width{width:17%}.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-gutter-width{width:3%}@media screen and (max-width:720px){.fg-masonry.fg-masonry-4col .fg-item,.fg-masonry.fg-masonry-5col .fg-item{margin-bottom:1%;width:32%}.fg-masonry.fg-masonry-4col .fg-column-width,.fg-masonry.fg-masonry-5col .fg-column-width{width:32%}.fg-masonry.fg-masonry-4col .fg-gutter-width,.fg-masonry.fg-masonry-5col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-item{margin-bottom:0;width:33%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-column-width{width:33%}.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-item{margin-bottom:3%;width:30%}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-column-width{width:30%}.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-gutter-width{width:3%}}@media screen and (max-width:480px){.fg-masonry.fg-masonry-3col .fg-item,.fg-masonry.fg-masonry-4col .fg-item,.fg-masonry.fg-masonry-5col .fg-item{margin-bottom:1%;width:49%}.fg-masonry.fg-masonry-3col .fg-column-width,.fg-masonry.fg-masonry-4col .fg-column-width,.fg-masonry.fg-masonry-5col .fg-column-width{width:49%}.fg-masonry.fg-masonry-3col .fg-gutter-width,.fg-masonry.fg-masonry-4col .fg-gutter-width,.fg-masonry.fg-masonry-5col .fg-gutter-width{width:1%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-item{margin-bottom:0;width:50%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-column-width{width:50%}.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-item{margin-bottom:3%;width:47%}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-column-width{width:47%}.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-gutter-width{width:3%}}@media screen and (max-width:320px){.fg-masonry.fg-masonry-2col .fg-item,.fg-masonry.fg-masonry-3col .fg-item,.fg-masonry.fg-masonry-4col .fg-item,.fg-masonry.fg-masonry-5col .fg-item{margin-bottom:1%;width:100%}.fg-masonry.fg-masonry-2col .fg-column-width,.fg-masonry.fg-masonry-3col .fg-column-width,.fg-masonry.fg-masonry-4col .fg-column-width,.fg-masonry.fg-masonry-5col .fg-column-width{width:100%}.fg-masonry.fg-masonry-2col .fg-gutter-width,.fg-masonry.fg-masonry-3col .fg-gutter-width,.fg-masonry.fg-masonry-4col .fg-gutter-width,.fg-masonry.fg-masonry-5col .fg-gutter-width{width:0}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-item{margin-bottom:0;width:100%}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-column-width{width:100%}.fg-masonry.fg-masonry-2col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-3col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-4col.fg-gutter-none .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-none .fg-gutter-width{width:0}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-item,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-item{margin-bottom:3%;width:100%}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-column-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-column-width{width:100%}.fg-masonry.fg-masonry-2col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-3col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-4col.fg-gutter-large .fg-gutter-width,.fg-masonry.fg-masonry-5col.fg-gutter-large .fg-gutter-width{width:0}}.foogallery.fg-border-thin .fg-column-width{border-width:5px}.foogallery.fg-border-medium .fg-column-width{border-width:10px}.foogallery.fg-border-thick .fg-column-width{border-width:15px}.foogallery.fg-masonry.fg-captions-bottom .fg-item-inner .fg-caption{visibility:visible;opacity:1;font-size:13px;position:relative;display:block;top:auto;bottom:auto;left:auto;right:auto;width:auto;height:auto;text-transform:none;transform:none;transition:none;background-color:transparent;border-style:solid;border-color:transparent}.foogallery.fg-masonry.fg-captions-bottom .fg-item-inner:hover .fg-caption{transform:none;transition:none}.foogallery.fg-masonry.fg-captions-bottom .fg-item-inner .fg-caption-inner{display:block;position:relative;max-height:none;top:auto;bottom:auto;left:auto;right:auto;width:auto;height:auto;border:none;transform:none;transition:none}.foogallery.fg-masonry.fg-captions-bottom .fg-item-inner .fg-caption-inner:before{display:none}.foogallery.fg-masonry.fg-captions-bottom.fg-caption-hover .fg-item-inner .fg-thumb:before{display:block}.foogallery.fg-masonry.fg-captions-bottom.fg-caption-always .fg-item-inner:hover .fg-caption{visibility:visible;opacity:1}.fg-masonry.fg-captions-bottom .fg-caption-desc,.fg-masonry.fg-captions-bottom .fg-caption-title{text-align:left}.fg-masonry.fg-captions-bottom.fg-dark .fg-caption,.fg-masonry.fg-captions-bottom.fg-light .fg-caption{color:#828282}.fg-masonry.fg-captions-bottom.fg-dark .fg-caption a,.fg-masonry.fg-captions-bottom.fg-light .fg-caption a{color:#828282;border-bottom:1px solid #828282}.fg-masonry.fg-captions-bottom.fg-dark .fg-caption a:hover,.fg-masonry.fg-captions-bottom.fg-light .fg-caption a:hover{border-bottom:none}.fg-masonry.fg-captions-bottom.fg-light .fg-caption-title,.fg-masonry.fg-captions-bottom.fg-light .fg-caption-title a{color:#222}.fg-masonry.fg-captions-bottom.fg-dark .fg-caption-title,.fg-masonry.fg-captions-bottom.fg-dark .fg-caption-title a{color:#fff}.fg-masonry.fg-captions-bottom.fg-light .fg-caption-title a{border-bottom:1px solid #222}.fg-masonry.fg-captions-bottom.fg-dark .fg-caption-title a{border-bottom:1px solid #fff}.fg-masonry.fg-captions-bottom .fg-caption{border-width:10px}.fg-masonry.fg-captions-bottom .fg-caption-title+.fg-caption-desc{margin-top:5px}.fg-masonry.fg-captions-bottom.fg-border-thin .fg-caption{border-width:10px 5px 5px 5px}.fg-masonry.fg-captions-bottom.fg-border-medium .fg-caption{border-width:10px 0 0 0}.fg-masonry.fg-captions-bottom.fg-border-thick .fg-caption{border-width:15px 0 0 0}.fg-masonry.fg-captions-bottom.fg-border-thick .fg-caption-title+.fg-caption-desc{margin-top:10px}.fg-justified{box-sizing:border-box;position:relative}.foogallery.fg-justified .fg-image,.foogallery.fg-justified .fg-item,.foogallery.fg-justified .fg-item-inner,.foogallery.fg-justified .fg-thumb{box-sizing:border-box;display:block;margin:0;padding:0}.fg-justified .fg-item{visibility:visible;position:absolute}.fg-justified .fg-item-inner{position:relative;width:100%;height:100%}.fg-justified .fg-thumb{position:relative;overflow:hidden}.fg-justified .fg-image{z-index:1}.fg-justified .fg-item.fg-positioned .fg-thumb{width:100%;height:100%}.fg-justified .fg-item.fg-positioned .fg-image{width:100%;height:auto;min-height:100%}.fg-simple_portfolio{box-sizing:border-box;position:relative;font-size:16px;margin:0 auto;padding:0;width:100%}.fg-simple_portfolio .fg-item{position:absolute;display:inline-block;margin:0;padding:0;outline:0}.fg-simple_portfolio .fg-image,.fg-simple_portfolio .fg-item-inner,.fg-simple_portfolio .fg-thumb{display:block;margin:0;padding:0;outline:0}.fg-simple_portfolio .fg-item-inner{position:relative;width:100%;height:100%}.fg-simple_portfolio .fg-thumb{box-sizing:border-box;display:block;margin:0;padding:0;border:none;outline:0;position:relative;overflow:hidden}.fg-simple_portfolio .fg-item.fg-positioned .fg-image{width:100%;height:auto}.fg-simple_portfolio .fg-image{z-index:1}.foogallery.fg-simple_portfolio .fg-item-inner .fg-caption{visibility:visible;opacity:1;font-size:13px;position:relative;display:block;top:auto;bottom:auto;left:auto;right:auto;width:auto;height:auto;text-transform:none;transform:none;transition:none;background-color:transparent;border-style:solid;border-color:transparent}.foogallery.fg-simple_portfolio .fg-item-inner:hover .fg-caption{transform:none;transition:none}.foogallery.fg-simple_portfolio .fg-item-inner .fg-caption-inner{display:block;top:auto;bottom:auto;left:auto;right:auto;width:auto;height:auto;border:none;transform:none;transition:none}.foogallery.fg-simple_portfolio .fg-item-inner .fg-caption-inner:before{display:none}.foogallery.fg-simple_portfolio.fg-caption-hover .fg-item-inner .fg-thumb:before{display:block}.foogallery.fg-simple_portfolio.fg-caption-always .fg-item-inner:hover .fg-caption{visibility:visible;opacity:1}.fg-simple_portfolio .fg-caption-title{text-align:left}.fg-simple_portfolio .fg-caption-desc{text-align:left}.fg-simple_portfolio.fg-dark .fg-caption,.fg-simple_portfolio.fg-light .fg-caption{color:#828282}.fg-simple_portfolio.fg-dark .fg-caption a,.fg-simple_portfolio.fg-light .fg-caption a{color:#828282;border-bottom:1px solid #828282}.fg-simple_portfolio.fg-dark .fg-caption a:hover,.fg-simple_portfolio.fg-light .fg-caption a:hover{border-bottom:none}.fg-simple_portfolio.fg-light .fg-caption-title,.fg-simple_portfolio.fg-light .fg-caption-title a{color:#222}.fg-simple_portfolio.fg-dark .fg-caption-title,.fg-simple_portfolio.fg-dark .fg-caption-title a{color:#fff}.fg-simple_portfolio.fg-light .fg-caption-title a{border-bottom:1px solid #222}.fg-simple_portfolio.fg-dark .fg-caption-title a{border-bottom:1px solid #fff}.fg-simple_portfolio.fg-captions-top .fg-item.fg-positioned .fg-thumb{position:absolute;bottom:0;left:0}.fg-simple_portfolio .fg-caption{border-width:0}.fg-simple_portfolio .fg-caption-title+.fg-caption-desc{margin-top:5px}.fg-simple_portfolio.fg-border-thin .fg-caption{border-width:10px 5px 5px 5px}.fg-simple_portfolio.fg-captions-top.fg-border-thin .fg-caption{border-width:5px 5px 10px 5px}.fg-simple_portfolio.fg-border-medium .fg-caption{border-width:10px 0 0 0}.fg-simple_portfolio.fg-captions-top.fg-border-medium .fg-caption{border-width:0 0 10px 0}.fg-simple_portfolio.fg-border-thick .fg-caption{border-width:15px 0 0 0}.fg-simple_portfolio.fg-captions-top.fg-border-thick .fg-caption{border-width:0 0 15px 0}.fg-simple_portfolio.fg-border-thick .fg-caption-title+.fg-caption-desc{margin-top:10px}.foogallery.fg-preset.fg-polaroid .fg-item{-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transition:-webkit-transform .35s,background-color .65s;transition:transform .35s,background-color .65s}.foogallery.fg-preset.fg-polaroid .fg-item:nth-child(2n+1){-webkit-transform:rotate(3deg);transform:rotate(3deg)}.foogallery.fg-preset.fg-polaroid .fg-item:nth-child(2n){-webkit-transform:rotate(-3deg);transform:rotate(-3deg)}.foogallery.fg-preset.fg-polaroid .fg-item:nth-child(3n){-webkit-transform:rotate(1deg);transform:rotate(1deg)}.foogallery.fg-preset.fg-polaroid .fg-item:nth-child(5n){-webkit-transform:rotate(-2deg);transform:rotate(-2deg)}.foogallery.fg-preset.fg-polaroid .fg-item:hover{-webkit-transform:rotate(0);transform:rotate(0)}.foogallery.fg-preset.fg-polaroid .fg-caption{position:relative;width:auto;font-family:"Segoe Print Regular",-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif}.foogallery.fg-preset.fg-polaroid .fg-caption-inner,.foogallery.fg-preset.fg-polaroid .fg-caption-title{position:relative;width:auto}.foogallery.fg-preset.fg-polaroid .fg-caption-title{text-align:center}.foogallery.fg-preset.fg-polaroid .fg-caption-desc{display:none}.foogallery.fg-light.fg-preset.fg-polaroid .fg-caption-title,.foogallery.fg-preset.fg-polaroid .fg-caption-title{color:#333}.foogallery.fg-dark.fg-preset.fg-polaroid .fg-caption-title{color:#fff}.foogallery.fg-preset.fg-polaroid .fg-caption{border-style:solid;border-color:transparent;border-width:10px}.foogallery.fg-preset.fg-polaroid .fg-caption-title+.fg-caption-desc{margin-top:5px}.foogallery.fg-preset.fg-polaroid.fg-border-thin .fg-caption{border-width:10px 5px 5px 5px}.foogallery.fg-preset.fg-polaroid.fg-captions-top.fg-border-thin .fg-caption{border-width:5px 5px 10px 5px}.foogallery.fg-preset.fg-polaroid.fg-border-medium .fg-caption{border-width:10px 0 0 0}.foogallery.fg-preset.fg-polaroid.fg-captions-top.fg-border-medium .fg-caption{border-width:0 0 10px 0}.foogallery.fg-preset.fg-polaroid.fg-border-thick .fg-caption{border-width:15px 0 0 0}.foogallery.fg-preset.fg-polaroid.fg-captions-top.fg-border-thick .fg-caption{border-width:0 0 15px 0}.foogallery.fg-preset.fg-polaroid.fg-border-thick .fg-caption-title+.fg-caption-desc{margin-top:10px}.fg-image-viewer{display:block;font-family:'Open Sans','Helvetica Neue',Arial,sans-serif}.fg-image-viewer.fg-left{text-align:left}.fg-image-viewer.fg-center{text-align:center}.fg-image-viewer.fg-right{text-align:right}.fiv-inner{position:relative;display:inline-block;max-width:100%;overflow:hidden;z-index:6}.fiv-inner .fiv-inner-container{position:relative;overflow:hidden;max-width:100%;border-style:solid;border-width:0;border-bottom-width:4px;z-index:5}.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item .fg-thumb,.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item .fg-thumb:active,.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item .fg-thumb:hover,.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item .fg-thumb:visited{position:relative;display:block;border:none;outline:0;text-decoration:none;box-shadow:none;max-width:100%}.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item{position:relative;visibility:visible;opacity:1;border:none;outline:0;text-decoration:none;box-shadow:none;max-width:100%}.fg-image-viewer .fiv-inner .fiv-inner-container .fg-item .fg-thumb img{display:block;max-width:100%;height:auto;border:none;outline:0;text-decoration:none}.fg-image-viewer .fiv-inner .fiv-ctrls{display:block;text-align:center;font-size:14px;border-style:solid;line-height:34px}.fg-image-viewer .fiv-inner .fiv-ctrls:after{content:'';display:block;clear:both}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-count{display:inline-block;font-weight:400;margin:0}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next,.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:none;min-width:80px;position:relative;overflow:hidden;transition:background-color .3s}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next:before,.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev:before{display:block;position:absolute;font-size:24px;line-height:30px;top:0;left:0;width:100%;transform:translateY(0);transition:transform .3s}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next:hover:before,.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev:hover:before{transform:translateY(-100%)}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next span,.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev span{display:block;width:100%;transform:translateY(100%);transition:transform .3s}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next:hover span,.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev:hover span{transform:translateY(0)}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev{float:left}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-prev:before{content:'\2190'}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next{float:right}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-next:before{content:'\2192'}.fg-image-viewer .fiv-inner .fiv-ctrls .fiv-count span{margin:0 4px}/*!* Theme - Default (Light) *!*//*!* Theme - Dark *!*/.foogallery.fg-image-viewer.fg-caption-always .fg-item-inner .fg-caption{padding:0;border:none;background:#000;background:-moz-linear-gradient(left,rgba(0,0,0,.8) 0,rgba(0,0,0,.8) 60%,rgba(0,0,0,0) 100%);background:-webkit-linear-gradient(left,rgba(0,0,0,.8) 0,rgba(0,0,0,.8) 60%,rgba(0,0,0,0) 100%);background:linear-gradient(to right,rgba(0,0,0,.8) 0,rgba(0,0,0,.8) 60%,rgba(0,0,0,0) 100%)}.foogallery.fg-image-viewer.fg-caption-always .fg-caption-title{padding:10px 10px 10px 10px}.foogallery.fg-image-viewer.fg-caption-always .fg-caption-desc{padding:10px 10px 10px 10px}.foogallery.fg-image-viewer.fg-caption-always .fg-caption-title+.fg-caption-desc{padding:0 10px 10px 10px}.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls,.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls .fiv-count,.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls .fiv-next,.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls .fiv-prev,.fg-image-viewer.fg-light .fiv-inner .fiv-inner-container{background-color:#fff;color:#333;border-color:#fff}.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls .fiv-next:hover,.fg-image-viewer.fg-light .fiv-inner .fiv-ctrls .fiv-prev:hover{background-color:#f2f2f2}.fg-image-viewer.fg-light .fiv-next,.fg-image-viewer.fg-light .fiv-prev{box-shadow:inset 0 0 0 1px #ddd}.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls,.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls .fiv-count,.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls .fiv-next,.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls .fiv-prev,.fg-image-viewer.fg-dark .fiv-inner .fiv-inner-container{background-color:#333;color:#fff;border-color:#333}.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls .fiv-next:hover,.fg-image-viewer.fg-dark .fiv-inner .fiv-ctrls .fiv-prev:hover{background-color:#444}.fg-image-viewer.fg-dark .fiv-next,.fg-image-viewer.fg-dark .fiv-prev{box-shadow:inset 0 0 0 1px #222}.foogallery.fg-image-viewer.fg-border-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thick .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thin .fg-item-inner{border-width:0}.foogallery.fg-image-viewer .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-thin .fiv-inner-container{border-width:4px}.foogallery.fg-image-viewer.fg-border-medium .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-medium .fiv-inner-container{border-width:10px}.foogallery.fg-image-viewer.fg-border-thick .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-thick .fiv-inner-container{border-width:16px}.foogallery.fg-image-viewer .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-medium .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-thick .fiv-ctrls,.foogallery.fg-image-viewer.fg-border-thin .fiv-ctrls{border-top-width:1px}.foogallery.fg-image-viewer.fg-round-small .fg-item,.foogallery.fg-image-viewer.fg-round-small .fg-item-inner,.foogallery.fg-image-viewer.fg-round-small .fiv-inner{border-radius:5px}.foogallery.fg-image-viewer.fg-round-small .fg-item,.foogallery.fg-image-viewer.fg-round-small .fg-item-inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.foogallery.fg-image-viewer.fg-round-small .fiv-next,.foogallery.fg-image-viewer.fg-round-small .fiv-prev{border-radius:3px}.foogallery.fg-image-viewer.fg-border-medium.fg-round-small .fg-item,.foogallery.fg-image-viewer.fg-border-medium.fg-round-small .fg-item-inner,.foogallery.fg-image-viewer.fg-border-medium.fg-round-small .fiv-next,.foogallery.fg-image-viewer.fg-border-medium.fg-round-small .fiv-prev,.foogallery.fg-image-viewer.fg-border-thick.fg-round-small .fg-item,.foogallery.fg-image-viewer.fg-border-thick.fg-round-small .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thick.fg-round-small .fiv-next,.foogallery.fg-image-viewer.fg-border-thick.fg-round-small .fiv-prev,.foogallery.fg-image-viewer.fg-border-thin.fg-round-small .fg-item,.foogallery.fg-image-viewer.fg-border-thin.fg-round-small .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thin.fg-round-small .fiv-next,.foogallery.fg-image-viewer.fg-border-thin.fg-round-small .fiv-prev{border-radius:3px}.foogallery.fg-image-viewer.fg-round-medium .fg-item,.foogallery.fg-image-viewer.fg-round-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-round-medium .fiv-inner{border-radius:10px}.foogallery.fg-image-viewer.fg-round-medium .fg-item,.foogallery.fg-image-viewer.fg-round-medium .fg-item-inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.foogallery.fg-image-viewer.fg-round-medium .fiv-next,.foogallery.fg-image-viewer.fg-round-medium .fiv-prev{border-radius:5px}.foogallery.fg-image-viewer.fg-border-thin.fg-round-medium .fg-item,.foogallery.fg-image-viewer.fg-border-thin.fg-round-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thin.fg-round-medium .fiv-next,.foogallery.fg-image-viewer.fg-border-thin.fg-round-medium .fiv-prev{border-radius:5px}.foogallery.fg-image-viewer.fg-border-medium.fg-round-medium .fg-item,.foogallery.fg-image-viewer.fg-border-medium.fg-round-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-border-medium.fg-round-medium .fiv-next,.foogallery.fg-image-viewer.fg-border-medium.fg-round-medium .fiv-prev,.foogallery.fg-image-viewer.fg-border-thick.fg-round-medium .fg-item,.foogallery.fg-image-viewer.fg-border-thick.fg-round-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thick.fg-round-medium .fiv-next,.foogallery.fg-image-viewer.fg-border-thick.fg-round-medium .fiv-prev{border-radius:3px}.foogallery.fg-image-viewer.fg-round-large .fg-item,.foogallery.fg-image-viewer.fg-round-large .fg-item-inner,.foogallery.fg-image-viewer.fg-round-large .fiv-inner{border-radius:15px}.foogallery.fg-image-viewer.fg-round-large .fg-item,.foogallery.fg-image-viewer.fg-round-large .fg-item-inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.foogallery.fg-image-viewer.fg-round-large .fiv-next,.foogallery.fg-image-viewer.fg-round-large .fiv-prev{border-radius:11px}.foogallery.fg-image-viewer.fg-border-thin.fg-round-large .fg-item,.foogallery.fg-image-viewer.fg-border-thin.fg-round-large .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thin.fg-round-large .fiv-next,.foogallery.fg-image-viewer.fg-border-thin.fg-round-large .fiv-prev{border-radius:11px}.foogallery.fg-image-viewer.fg-border-medium.fg-round-large .fg-item,.foogallery.fg-image-viewer.fg-border-medium.fg-round-large .fg-item-inner,.foogallery.fg-image-viewer.fg-border-medium.fg-round-large .fiv-next,.foogallery.fg-image-viewer.fg-border-medium.fg-round-large .fiv-prev{border-radius:5px}.foogallery.fg-image-viewer.fg-border-thick.fg-round-large .fg-item,.foogallery.fg-image-viewer.fg-border-thick.fg-round-large .fg-item-inner,.foogallery.fg-image-viewer.fg-border-thick.fg-round-large .fiv-next,.foogallery.fg-image-viewer.fg-border-thick.fg-round-large .fiv-prev{border-radius:3px}.foogallery.fg-image-viewer.fg-round-full .fiv-inner,.foogallery.fg-image-viewer.fg-round-full .fiv-next,.foogallery.fg-image-viewer.fg-round-full .fiv-prev{border-radius:50%}.foogallery.fg-image-viewer.fg-dark.fg-shadow-large .fg-item-inner,.foogallery.fg-image-viewer.fg-dark.fg-shadow-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-dark.fg-shadow-outline .fg-item-inner,.foogallery.fg-image-viewer.fg-dark.fg-shadow-small .fg-item-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-large .fg-item-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-medium .fg-item-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-outline .fg-item-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-small .fg-item-inner{box-shadow:none}.foogallery.fg-image-viewer.fg-light.fg-shadow-outline .fiv-inner{box-shadow:0 0 0 1px #ddd}.foogallery.fg-image-viewer.fg-dark.fg-shadow-outline .fiv-inner{box-shadow:0 0 0 1px #222}.foogallery.fg-image-viewer.fg-dark.fg-shadow-small .fiv-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-small .fiv-inner{box-shadow:0 1px 4px 0 rgba(0,0,0,.5)}.foogallery.fg-image-viewer.fg-dark.fg-shadow-medium .fiv-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-medium .fiv-inner{box-shadow:0 1px 10px 0 rgba(0,0,0,.5)}.foogallery.fg-image-viewer.fg-dark.fg-shadow-large .fiv-inner,.foogallery.fg-image-viewer.fg-light.fg-shadow-large .fiv-inner{box-shadow:0 1px 16px 0 rgba(0,0,0,.5)}.foogallery.fg-thumbnail,.foogallery.fg-thumbnail.fg-center{text-align:center}.foogallery.fg-thumbnail.fg-left{text-align:left}.foogallery.fg-thumbnail.fg-right{text-align:right}.foogallery.fg-thumbnail.fg-float-left{float:left;width:auto}.foogallery.fg-thumbnail.fg-float-right{float:right;width:auto}.foogallery.fg-thumbnail .fg-item{display:inline-block;vertical-align:top;max-width:100%}.foogallery.fg-thumbnail .fg-image{max-width:100%}.foogallery.fg-thumbnail .fg-st-hidden{display:none}
extensions/default-templates/shared/js/foogallery.js CHANGED
@@ -58,9 +58,9 @@
58
  );
59
  /*!
60
  * FooGallery.utils - Contains common utility methods and classes used in our plugins.
61
- * @version 0.0.6
62
  * @link https://github.com/steveush/foo-utils#readme
63
- * @copyright Steve Usher 2017
64
  * @license Released under the GPL-3.0 license.
65
  */
66
  /**
@@ -111,7 +111,7 @@
111
  * @name version
112
  * @type {string}
113
  */
114
- version: '0.0.6',
115
  };
116
 
117
  /**
@@ -207,7 +207,7 @@
207
  })(jQuery);
208
  (function ($, _){
209
  // only register methods if this version is the current version
210
- if (_.version !== '0.0.6') return;
211
 
212
  /**
213
  * @summary Contains common type checking utility methods.
@@ -561,7 +561,7 @@
561
  );
562
  (function($, _, _is){
563
  // only register methods if this version is the current version
564
- if (_.version !== '0.0.6') return;
565
 
566
  /**
567
  * @memberof FooGallery.utils
@@ -1096,7 +1096,7 @@
1096
  );
1097
  (function(_, _is){
1098
  // only register methods if this version is the current version
1099
- if (_.version !== '0.0.6') return;
1100
 
1101
  /**
1102
  * @summary Contains common url utility methods.
@@ -1182,7 +1182,7 @@
1182
  * console.log( _url.param( search, "v", "2" ) ); // => "?wmode=opaque&autoplay=1&v=2"
1183
  */
1184
  _.url.param = function(search, key, value){
1185
- if (!_is.string(search) || _is.empty(search) || !_is.string(key) || _is.empty(key)) return search;
1186
  var regex, match, result, param;
1187
  if (_is.undef(value)){
1188
  regex = new RegExp('[?|&]' + key + '=([^&;]+?)(&|#|;|$)'); // regex to match the key and it's value but only capture the value
@@ -1199,7 +1199,7 @@
1199
  result = search.replace(regex, '$1' + param); // replace any existing instance of the key with the new value
1200
  // If nothing was replaced, then add the new param to the end
1201
  if (result === search && !regex.test(result)) { // if no replacement occurred and the parameter is not currently in the result then add it
1202
- result += '&' + param;
1203
  }
1204
  }
1205
  return result;
@@ -1231,7 +1231,7 @@
1231
  );
1232
  (function (_, _is, _fn) {
1233
  // only register methods if this version is the current version
1234
- if (_.version !== '0.0.6') return;
1235
 
1236
  /**
1237
  * @summary Contains common string utility methods.
@@ -1546,7 +1546,7 @@
1546
  );
1547
  (function($, _, _is, _fn, _str){
1548
  // only register methods if this version is the current version
1549
- if (_.version !== '0.0.6') return;
1550
 
1551
  /**
1552
  * @summary Contains common object utility methods.
@@ -1629,12 +1629,12 @@
1629
  * console.log( _obj.merge( target, object ) ); // => {"name": "My Object", "enabled": true, "arr": [4,5,6], "something": 123}
1630
  */
1631
  _.obj.merge = function(target, object){
1632
- target = _is.object(target) ? target : {};
1633
- object = _is.object(object) ? object : {};
1634
  for (var prop in object) {
1635
  if (object.hasOwnProperty(prop)) {
1636
- if (_is.object(object[prop])) {
1637
- target[prop] = _is.object(target[prop]) ? target[prop] : {};
1638
  _.obj.merge(target[prop], object[prop]);
1639
  } else if (_is.array(object[prop])) {
1640
  target[prop] = object[prop].slice();
@@ -1878,7 +1878,7 @@
1878
  );
1879
  (function($, _, _is){
1880
  // only register methods if this version is the current version
1881
- if (_.version !== '0.0.6') return;
1882
 
1883
  // any methods that have dependencies but don't fall into a specific subset or namespace can be added here
1884
 
@@ -1977,7 +1977,7 @@
1977
  );
1978
  (function($, _, _is){
1979
  // only register methods if this version is the current version
1980
- if (_.version !== '0.0.6') return;
1981
 
1982
  /**
1983
  * @summary Contains common utility methods and members for the CSS transition property.
@@ -2072,12 +2072,19 @@
2072
  return def;
2073
  };
2074
 
 
 
 
 
 
 
 
2075
  /**
2076
  * @summary Start a transition by toggling the supplied `className` on the `$element`.
2077
  * @memberof FooGallery.utils.transition
2078
  * @function start
2079
  * @param {jQuery} $element - The jQuery element to start the transition on.
2080
- * @param {string} className - One or more class names (separated by spaces) to be toggled that starts the transition.
2081
  * @param {boolean} [state] - A Boolean (not just truthy/falsy) value to determine whether the class should be added or removed.
2082
  * @param {number} [timeout] - The maximum time, in milliseconds, to wait for the `transitionend` event to be raised. If not provided this will be automatically set to the elements `transition-duration` property plus an extra 50 milliseconds.
2083
  * @returns {Promise}
@@ -2086,7 +2093,7 @@
2086
  * The last parameter `timeout` is used to create a timer that behaves as a safety net in case the `transitionend` event is never raised and ensures the deferred returned by this method is resolved or rejected within a specified time.
2087
  * @see {@link https://developer.mozilla.org/en/docs/Web/CSS/transition-duration|transition-duration - CSS | MDN} for more information on the `transition-duration` CSS property.
2088
  */
2089
- _.transition.start = function($element, className, state, timeout){
2090
  var deferred = $.Deferred();
2091
 
2092
  $element = $element.first();
@@ -2121,7 +2128,11 @@
2121
 
2122
  setTimeout(function(){
2123
  // This is executed inside of a 20ms timeout to allow the binding of the event handler above to actually happen before the class is toggled
2124
- $element.toggleClass(className, state);
 
 
 
 
2125
  if (!_.transition.supported){
2126
  // If the browser doesn't support transitions then just resolve the deferred
2127
  deferred.resolve();
@@ -2139,7 +2150,7 @@
2139
  );
2140
  (function ($, _, _is, _obj, _fn) {
2141
  // only register methods if this version is the current version
2142
- if (_.version !== '0.0.6') return;
2143
 
2144
  /**
2145
  * @summary A base class providing some helper methods for prototypal inheritance.
@@ -2279,7 +2290,7 @@
2279
  );
2280
  (function($, _, _is){
2281
  // only register methods if this version is the current version
2282
- if (_.version !== '0.0.6') return;
2283
 
2284
  _.Bounds = _.Class.extend(/** @lends FooGallery.utils.Bounds */{
2285
  /**
@@ -2381,7 +2392,7 @@
2381
  );
2382
  (function($, _, _is, _fn){
2383
  // only register methods if this version is the current version
2384
- if (_.version !== '0.0.6') return;
2385
 
2386
  _.Factory = _.Class.extend(/** @lends FooGallery.utils.Factory */{
2387
  /**
@@ -2705,7 +2716,7 @@
2705
  );
2706
  (function(_, _fn, _str){
2707
  // only register methods if this version is the current version
2708
- if (_.version !== '0.0.6') return;
2709
 
2710
  // this is done to handle Content Security in Chrome and other browsers blocking access to the localStorage object under certain configurations.
2711
  // see: https://www.chromium.org/for-testers/bug-reporting-guidelines/uncaught-securityerror-failed-to-read-the-localstorage-property-from-window-access-is-denied-for-this-document
@@ -2812,7 +2823,7 @@
2812
  );
2813
  (function($, _, _is){
2814
  // only register methods if this version is the current version
2815
- if (_.version !== '0.0.6') return;
2816
 
2817
  _.Throttle = _.Class.extend(/** @lends FooGallery.utils.Throttle */{
2818
  /**
@@ -2924,7 +2935,7 @@
2924
  FooGallery.utils,
2925
  FooGallery.utils.is
2926
  );
2927
- (function($, _, _utils, _is, _fn){
2928
 
2929
  _.debug = new _utils.Debugger("__FooGallery__");
2930
 
@@ -2935,20 +2946,21 @@
2935
  * @param {(string|string[]|object)} classes - A single space delimited string of CSS class names to convert or an array of them with each item being included in the selector using the OR (`,`) syntax as a separator. If an object is supplied the result will be an object with the same property names but the values converted to selectors.
2936
  * @returns {(object|string)}
2937
  */
2938
- _utils.selectify = function(classes){
2939
- if (_is.hash(classes)){
 
2940
  var result = {}, selector;
2941
- for (var name in classes){
2942
  if (!classes.hasOwnProperty(name)) continue;
2943
- if (selector = _utils.selectify(classes[name])){
2944
  result[name] = selector;
2945
  }
2946
  }
2947
  return result;
2948
  }
2949
- if (_is.string(classes) || _is.array(classes)){
2950
  if (_is.string(classes)) classes = [classes];
2951
- return $.map(classes, function(str){
2952
  return _is.string(str) ? "." + str.split(/\s/g).join(".") : null;
2953
  }).join(",");
2954
  }
@@ -2982,17 +2994,17 @@
2982
  */
2983
  _.dataItem = "__FooGalleryItem__";
2984
 
2985
- _.init = function(options, element){
2986
  return _.template.make(options, element).initialize();
2987
  };
2988
 
2989
- _.initAll = function(options){
2990
- return _fn.when($(".foogallery").map(function(i, element){
2991
  return _.init(options, element);
2992
  }).get());
2993
  };
2994
 
2995
- _.parseSrc = function(src, srcWidth, srcHeight, srcset, renderWidth, renderHeight){
2996
  if (!_is.string(src)) return null;
2997
  // if there is no srcset just return the src
2998
  if (!_is.string(srcset)) return src;
@@ -3020,8 +3032,8 @@
3020
 
3021
  // get the current viewport info and use it to determine the correct src to load
3022
  var dpr = window.devicePixelRatio || 1,
3023
- area = {w: renderWidth * dpr, h: renderHeight * dpr, x: dpr},
3024
- property;
3025
 
3026
  // first check each of the viewport properties against the max values of the same properties in our src array
3027
  // only src's with a property greater than the viewport or equal to the max are kept
@@ -3068,15 +3080,15 @@
3068
  * <div class="fg-item-inner">
3069
  * <a class="fg-thumb" href="[item.href]">
3070
  * <img class="fg-image" width="[item.width]" height="[item.height]"
3071
- * title="[item.title]" alt="[item.description]"
3072
- * data-src="[item.src]"
3073
- * data-srcset="[item.srcset]" />
3074
  * <!-- Optional caption markup -->
3075
  * <div class="fg-caption">
3076
- * <div class="fg-caption-inner">
3077
- * <div class="fg-caption-title">[item.title]</div>
3078
- * <div class="fg-caption-desc">[item.description]</div>
3079
- * </div>
3080
  * </div>
3081
  * </a>
3082
  * </div>
@@ -3084,17 +3096,17 @@
3084
  * <!-- Any number of additional items -->
3085
  * </div>
3086
  * <script>
3087
- * jQuery(function($){
3088
  * $("#gallery-1").foogallery();
3089
  * });
3090
  * </script>
3091
  * @example {@caption Options can be supplied directly to the `.foogallery()` method or by supplying them using the `data-foogallery` attribute. If supplied using the attribute the value must follow [valid JSON syntax](http://en.wikipedia.org/wiki/JSON#Data_types.2C_syntax_and_example) including quoted property names. If the same option is supplied in both locations as it is below, the value from the attribute overrides the value supplied to the method, in this case `lazy` would be `true`.}{@lang html}
3092
  * <!-- Supplying the options using the attribute -->
3093
  * <div id="gallery-1" class="foogallery fg-responsive" data-foogallery='{"lazy": true}'>
3094
- * <!-- Items -->
3095
  * </div>
3096
  * <script>
3097
- * jQuery(function($){
3098
  * // Supply the options directly to the method
3099
  * $("#gallery-1").foogallery({
3100
  * lazy: false
@@ -3102,12 +3114,12 @@
3102
  * });
3103
  * </script>
3104
  */
3105
- $.fn.foogallery = function(options, ready){
3106
- return this.filter(".foogallery").each(function(i, element){
3107
- if (_is.string(options)){
3108
  var template = $.data(element, _.dataTemplate);
3109
- if (template instanceof _.Template){
3110
- switch (options){
3111
  case "layout":
3112
  template.layout();
3113
  return;
@@ -3117,8 +3129,8 @@
3117
  }
3118
  }
3119
  } else {
3120
- _.template.make(options, element).initialize().then(function(template){
3121
- if (_is.fn(ready)){
3122
  ready(template);
3123
  }
3124
  });
@@ -3139,13 +3151,13 @@
3139
  */
3140
 
3141
  })(
3142
- FooGallery.$,
3143
- FooGallery,
3144
- FooGallery.utils,
3145
- FooGallery.utils.is,
3146
- FooGallery.utils.fn
3147
  );
3148
- (function($, _, _utils, _is, _fn, _obj){
3149
 
3150
  _.TemplateFactory = _utils.Factory.extend(/** @lends FooGallery.TemplateFactory */{
3151
  /**
@@ -3157,7 +3169,7 @@
3157
  * @borrows FooGallery.utils.Class.extend as extend
3158
  * @borrows FooGallery.utils.Class.override as override
3159
  */
3160
- construct: function(){
3161
  /**
3162
  * @summary An object containing all registered galleries.
3163
  * @memberof FooGallery.TemplateFactory#
@@ -3195,9 +3207,9 @@
3195
  * @param {number} [priority=0] - This determines the index for the class when using either the {@link FooGallery.TemplateFactory#load|load} or {@link FooGallery.TemplateFactory#names|names} methods, a higher value equals a lower index.
3196
  * @returns {boolean} `true` if the `klass` was successfully registered.
3197
  */
3198
- register: function(name, template, options, classes, il8n, priority){
3199
  var self = this, result = self._super(name, template, priority);
3200
- if (result){
3201
  var reg = self.registered;
3202
  reg[name].opt = _is.hash(options) ? options : {};
3203
  reg[name].cls = _is.hash(classes) ? classes : {};
@@ -3213,7 +3225,7 @@
3213
  * @param {(jQuery|HTMLElement|string)} [element] - The jQuery object, HTMLElement or selector of the template element to create. If not supplied the {@link FooGallery~Options#type|type} options' value is used.
3214
  * @returns {FooGallery.Template}
3215
  */
3216
- make: function(options, element){
3217
  element = _is.jq(element) ? element : $(element);
3218
  options = _obj.extend({}, options, element.data("foogallery"));
3219
  var self = this, type = self.type(options, element);
@@ -3221,10 +3233,10 @@
3221
  options = self.options(type, options);
3222
  return self._super(type, options, element);
3223
  },
3224
- type: function(options, element){
3225
  element = _is.jq(element) ? element : $(element);
3226
  var self = this, type = _is.hash(options) && _is.hash(options) && _is.string(options.type) && self.contains(options.type) ? options.type : "core";
3227
- if (type === "core" && element.length > 0){
3228
  var reg = self.registered, names = self.names(true);
3229
  for (var i = 0, l = names.length; i < l; i++) {
3230
  if (!reg.hasOwnProperty(names[i])) continue;
@@ -3239,17 +3251,17 @@
3239
  }
3240
  return type;
3241
  },
3242
- configure: function(name, options, classes, il8n){
3243
  var self = this;
3244
- if (self.contains(name)){
3245
  var reg = self.registered;
3246
  _obj.extend(reg[name].opt, options);
3247
  _obj.extend(reg[name].cls, classes);
3248
  _obj.extend(reg[name].il8n, il8n);
3249
  }
3250
  },
3251
- options: function(name, options){
3252
- options = _obj.extend({}, options);
3253
  var self = this, reg = self.registered,
3254
  def = reg["core"].opt,
3255
  cls = reg["core"].cls,
@@ -3257,15 +3269,17 @@
3257
 
3258
  if (!_is.hash(options.cls)) options.cls = {};
3259
  if (!_is.hash(options.il8n)) options.il8n = {};
3260
- options = _.paging.merge(options);
3261
- if (name !== "core" && self.contains(name)){
 
 
3262
  options = _obj.extend({}, def, reg[name].opt, options);
3263
- options.cls = _obj.extend(options.cls, cls, reg[name].cls, options.cls);
3264
- options.il8n = _obj.extend(options.il8n, il8n, reg[name].il8n, options.il8n);
3265
  } else {
3266
  options = _obj.extend({}, def, options);
3267
- options.cls = _obj.extend(options.cls, cls, options.cls);
3268
- options.il8n = _obj.extend(options.il8n, il8n, options.il8n);
3269
  }
3270
  return options;
3271
  }
@@ -3361,8 +3375,8 @@
3361
  def_cls = reg["default"].cls,
3362
  def_il8n = reg["default"].il8n,
3363
  opt = _is.hash(options.paging) ? options.paging : {},
3364
- cls = _is.hash(options.cls) && _is.hash(options.cls.paging) ? options.cls.paging : {},
3365
- il8n = _is.hash(options.il8n) && _is.hash(options.il8n.paging) ? options.il8n.paging : {};
3366
 
3367
  if (!_is.hash(options.cls)) options.cls = {};
3368
  if (!_is.hash(options.il8n)) options.il8n = {};
@@ -3431,7 +3445,9 @@
3431
  FooGallery.utils.fn,
3432
  FooGallery.utils.obj
3433
  );
3434
- (function($, _, _utils, _is, _fn, _str){
 
 
3435
 
3436
  _.Template = _utils.Class.extend(/** @lends FooGallery.Template */{
3437
  /**
@@ -3444,8 +3460,15 @@
3444
  * @borrows FooGallery.utils.Class.extend as extend
3445
  * @borrows FooGallery.utils.Class.override as override
3446
  */
3447
- construct: function(options, element){
3448
  var self = this;
 
 
 
 
 
 
 
3449
  /**
3450
  * @summary The jQuery object for the template container.
3451
  * @memberof FooGallery.Template#
@@ -3474,13 +3497,6 @@
3474
  * @type {string}
3475
  */
3476
  self.id = self.$el.prop("id") || options.id;
3477
- /**
3478
- * @summary Whether or not the template created its' own container element.
3479
- * @memberof FooGallery.Template#
3480
- * @name createdSelf
3481
- * @type {boolean}
3482
- */
3483
- self.createdSelf = false;
3484
  /**
3485
  * @summary The CSS classes for the template.
3486
  * @memberof FooGallery.Template#
@@ -3515,7 +3531,14 @@
3515
  * @name pages
3516
  * @type {?FooGallery.Paging}
3517
  */
3518
- self.pages = _.paging.make(options.paging.type, self);
 
 
 
 
 
 
 
3519
  /**
3520
  * @summary The state manager for the template.
3521
  * @memberof FooGallery.Template#
@@ -3535,6 +3558,12 @@
3535
  self.initialized = false;
3536
  self.destroying = false;
3537
  self.destroyed = false;
 
 
 
 
 
 
3538
  },
3539
 
3540
  // ################
@@ -3553,39 +3582,56 @@
3553
  * @fires FooGallery.Template~"post-init.foogallery"
3554
  * @fires FooGallery.Template~"ready.foogallery"
3555
  */
3556
- initialize: function(parent){
3557
  var self = this;
3558
  if (_is.promise(self._initialize)) return self._initialize;
3559
  parent = _is.jq(parent) ? parent : $(parent);
3560
- return self._initialize = $.Deferred(function(def){
3561
  self.initializing = true;
3562
- if (parent.length === 0 && self.$el.parent().length === 0){
3563
  def.reject("A parent element is required.");
3564
  return;
3565
  }
3566
- if (self.$el.length === 0){
3567
  self.$el = self.create();
3568
- self.createdSelf = true;
3569
  }
3570
- if (parent.length > 0){
3571
  self.$el.appendTo(parent);
3572
  }
3573
  var queue = $.Deferred(), promise = queue.promise(), existing;
3574
- if (self.$el.length > 0 && (existing = self.$el.data(_.dataTemplate)) instanceof _.Template){
3575
- promise = promise.then(function(){
3576
- return existing.destroy().then(function(){
3577
  self.$el.data(_.dataTemplate, self);
3578
  });
3579
  });
3580
  } else {
3581
  self.$el.data(_.dataTemplate, self);
3582
  }
3583
- promise.then(function(){
3584
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3585
  // at this point we have our container element free of pre-existing instances so let's bind any event listeners supplied by the .on option
3586
- if (!_is.empty(self.opt.on)){
3587
  self.$el.on(self.opt.on);
3588
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3589
 
3590
  /**
3591
  * @summary Raised before the template is fully initialized.
@@ -3617,17 +3663,17 @@
3617
  */
3618
  var e = self.raise("pre-init");
3619
  if (e.isDefaultPrevented()) return _fn.rejectWith("pre-init default prevented");
3620
- }).then(function(){
3621
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3622
  // checks the delay option and if it is greater than 0 waits for that amount of time before continuing
3623
  if (self.opt.delay <= 0) return _fn.resolved;
3624
- return $.Deferred(function(wait){
3625
  self._delay = setTimeout(function () {
3626
  self._delay = null;
3627
  wait.resolve();
3628
  }, self.opt.delay);
3629
  }).promise();
3630
- }).then(function(){
3631
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3632
  /**
3633
  * @summary Raised before the template is initialized but after any pre-initialization work is complete.
@@ -3673,7 +3719,7 @@
3673
  var e = self.raise("init");
3674
  if (e.isDefaultPrevented()) return _fn.rejectWith("init default prevented");
3675
  return self.items.fetch();
3676
- }).then(function(){
3677
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3678
  /**
3679
  * @summary Raised after the template is initialized but before any post-initialization work is complete.
@@ -3720,9 +3766,9 @@
3720
  if (e.isDefaultPrevented()) return _fn.rejectWith("post-init default prevented");
3721
  var state = self.state.parse();
3722
  self.state.set(_is.empty(state) ? self.state.initial() : state);
3723
- $(window).on("scroll.foogallery", {self: self}, self.throttle(self.onWindowScroll, self.opt.throttle))
3724
- .on("popstate.foogallery", {self: self}, self.onWindowPopState);
3725
- }).then(function(){
3726
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3727
  /**
3728
  * @summary Raised after the template is fully initialized but before the first load occurs.
@@ -3742,7 +3788,7 @@
3742
  */
3743
  self.raise("first-load");
3744
  return self.loadAvailable();
3745
- }).then(function(){
3746
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3747
  self.initializing = false;
3748
  self.initialized = true;
@@ -3772,11 +3818,11 @@
3772
  */
3773
  self.raise("ready");
3774
  def.resolve(self);
3775
- }).fail(function(err){
3776
  def.reject(err);
3777
  });
3778
  queue.resolve();
3779
- }).promise().fail(function(err){
3780
  console.log("initialize failed", self, err);
3781
  self.destroy();
3782
  });
@@ -3791,10 +3837,20 @@
3791
  * <div id="{options.id}" class="{options.cls.container} {options.classes}">
3792
  * </div>
3793
  */
3794
- create: function(){
3795
  var self = this;
3796
  return $("<div/>", {"id": self.id, "class": self.cls.container}).addClass(self.opt.classes);
3797
  },
 
 
 
 
 
 
 
 
 
 
3798
 
3799
  // #############
3800
  // ## Destroy ##
@@ -3808,13 +3864,13 @@
3808
  * @description Once this method is called it can not be stopped and the template will be destroyed.
3809
  * @fires FooGallery.Template~"destroy.foogallery"
3810
  */
3811
- destroy: function(){
3812
  var self = this;
3813
  if (self.destroyed) return _fn.resolved;
3814
  self.destroying = true;
3815
- return $.Deferred(function(def){
3816
- if (self.initializing && _is.promise(self._initialize)){
3817
- self._initialize.always(function(){
3818
  self.destroying = false;
3819
  self._destroy();
3820
  def.resolve();
@@ -3832,7 +3888,7 @@
3832
  * @function _destroy
3833
  * @private
3834
  */
3835
- _destroy: function(){
3836
  var self = this;
3837
  if (self.destroyed) return;
3838
  /**
@@ -3851,12 +3907,12 @@
3851
  * });
3852
  */
3853
  self.raise("destroy");
3854
- $(window).off("popstate.foogallery", self.onWindowPopState)
3855
- .off("scroll.foogallery");
3856
  self.state.destroy();
 
3857
  if (self.pages) self.pages.destroy();
3858
  self.items.destroy();
3859
- if (!_is.empty(self.opt.on)){
3860
  self.$el.off(self.opt.on);
3861
  }
3862
  /**
@@ -3876,7 +3932,17 @@
3876
  */
3877
  self.raise("destroyed");
3878
  self.$el.removeData(_.dataTemplate);
3879
- if (self.createdSelf){
 
 
 
 
 
 
 
 
 
 
3880
  self.$el.remove();
3881
  }
3882
  self.$el = self.state = self.items = self.pages = null;
@@ -3889,20 +3955,25 @@
3889
  // ## Load Items ##
3890
  // ################
3891
 
 
 
 
 
 
 
 
 
 
 
 
3892
  /**
3893
  * @summary Check if any available items need to be loaded and loads them.
3894
  * @memberof FooGallery.Template#
3895
  * @function loadAvailable
3896
  * @returns {Promise<FooGallery.Item[]>} Resolves with an array of {@link FooGallery.Item|items} as the first argument. If no items are loaded this array is empty.
3897
  */
3898
- loadAvailable: function(){
3899
- var self = this, items;
3900
- if (self.pages){
3901
- items = self.pages.available();
3902
- } else {
3903
- items = self.items.available();
3904
- }
3905
- return self.items.load(items);
3906
  },
3907
 
3908
  /**
@@ -3911,11 +3982,11 @@
3911
  * @function _check
3912
  * @private
3913
  */
3914
- _check: function(delay){
3915
  delay = _is.number(delay) ? delay : 0;
3916
  var self = this;
3917
- setTimeout(function(){
3918
- if (self.initialized && (!self.destroying || !self.destroyed)){
3919
  self.loadAvailable();
3920
  }
3921
  }, delay);
@@ -3944,20 +4015,20 @@
3944
  if (!_is.string(eventName) || _is.empty(eventName)) return null;
3945
  args = _is.array(args) ? args : [];
3946
  var self = this,
3947
- name = eventName.split(".")[0],
3948
- listener = _str.camel("on-" + name),
3949
- event = $.Event(name + ".foogallery");
3950
  args.unshift(self); // add self
3951
  self.$el.trigger(event, args);
3952
  _.debug.logf("{id}|{name}:", {id: self.id, name: name}, args);
3953
- if (_is.fn(self[listener])){
3954
  args.unshift(event); // add event
3955
  self[listener].apply(self.$el.get(0), args);
3956
  }
3957
  return event;
3958
  },
3959
 
3960
- layout: function(){
3961
  var self = this;
3962
  if (self._initialize === null) return;
3963
  /**
@@ -3987,9 +4058,9 @@
3987
  * @param {number} wait - The number of milliseconds to wait before allowing execution.
3988
  * @returns {Function}
3989
  */
3990
- throttle: function(fn, wait){
3991
  var time = Date.now();
3992
- return function() {
3993
  if ((time + wait - Date.now()) < 0) {
3994
  var args = _fn.arg2arr(arguments);
3995
  fn.apply(this, args);
@@ -4009,9 +4080,9 @@
4009
  * @param {jQuery.Event} e - The jQuery.Event object for the event.
4010
  * @private
4011
  */
4012
- onWindowPopState: function(e){
4013
  var self = e.data.self, state = e.originalEvent.state;
4014
- if (!_is.empty(state) && state.id === self.id){
4015
  self.state.set(state);
4016
  self.loadAvailable();
4017
  }
@@ -4023,7 +4094,7 @@
4023
  * @param {jQuery.Event} e - The jQuery.Event object for the event.
4024
  * @private
4025
  */
4026
- onWindowScroll: function(e){
4027
  var self = e.data.self;
4028
  self.loadAvailable();
4029
  }
@@ -4045,9 +4116,7 @@
4045
  template: {}
4046
  }, {
4047
  container: "foogallery"
4048
- }, {
4049
-
4050
- }, -100);
4051
 
4052
  /**
4053
  * @summary An object containing all the core template options.
@@ -4094,15 +4163,13 @@
4094
  */
4095
 
4096
  })(
4097
- FooGallery.$,
4098
- FooGallery,
4099
- FooGallery.utils,
4100
- FooGallery.utils.is,
4101
- FooGallery.utils.fn,
4102
- FooGallery.utils.str
4103
  );
4104
-
4105
-
4106
  (function(_, _utils){
4107
 
4108
  _.Component = _utils.Class.extend(/** @lend FooGallery.Component */{
@@ -4365,6 +4432,7 @@
4365
  */
4366
  initial: function(){
4367
  var self = this, tmpl = self.tmpl, state = {};
 
4368
  if (tmpl.pages && tmpl.pages.current > 1) state.p = tmpl.pages.current;
4369
  return state;
4370
  },
@@ -4379,6 +4447,9 @@
4379
  get: function(item){
4380
  var self = this, tmpl = self.tmpl, state = {};
4381
  if (item instanceof _.Item) state.i = item.id;
 
 
 
4382
  if (tmpl.pages && tmpl.pages.isValid(tmpl.pages.current)){
4383
  state.p = tmpl.pages.current;
4384
  }
@@ -4396,6 +4467,11 @@
4396
  if (_is.hash(state)){
4397
  tmpl.items.reset();
4398
  var item = tmpl.items.get(state.i);
 
 
 
 
 
4399
  if (tmpl.pages){
4400
  tmpl.pages.rebuild();
4401
  var page = tmpl.pages.number(state.p);
@@ -4449,6 +4525,7 @@
4449
  * @summary An object used to store the state of a template.
4450
  * @typedef {object} FooGallery~State
4451
  * @property {number} [p] - The current page number.
 
4452
  * @property {?string} [i] - The currently selected item.
4453
  */
4454
 
@@ -4458,7 +4535,7 @@
4458
  FooGallery.utils.is,
4459
  FooGallery.utils.str
4460
  );
4461
- (function($, _, _utils, _is, _fn, _obj){
4462
 
4463
  _.Item = _.Component.extend(/** @lends FooGallery.Item */{
4464
  /**
@@ -4471,7 +4548,7 @@
4471
  * @borrows FooGallery.utils.Class.extend as extend
4472
  * @borrows FooGallery.utils.Class.override as override
4473
  */
4474
- construct: function(template, options){
4475
  var self = this;
4476
  /**
4477
  * @ignore
@@ -4482,6 +4559,8 @@
4482
  self.cls = template.cls.item;
4483
  self.il8n = template.il8n.item;
4484
  self.sel = template.sel.item;
 
 
4485
  /**
4486
  * @summary Whether or not the items' elements are appended to the template.
4487
  * @memberof FooGallery.Item#
@@ -4561,110 +4640,114 @@
4561
  */
4562
  self.$caption = null;
4563
 
4564
- options = _obj.extend({}, template.opt.item, options);
4565
-
 
 
 
 
4566
  /**
4567
  * @memberof FooGallery.Item#
4568
  * @name id
4569
  * @type {string}
4570
  */
4571
- self.id = options.id;
4572
  /**
4573
  * @memberof FooGallery.Item#
4574
  * @name href
4575
  * @type {string}
4576
  */
4577
- self.href = options.href;
4578
  /**
4579
  * @memberof FooGallery.Item#
4580
  * @name src
4581
  * @type {string}
4582
  */
4583
- self.src = options.src;
4584
  /**
4585
  * @memberof FooGallery.Item#
4586
  * @name srcset
4587
  * @type {string}
4588
  */
4589
- self.srcset = options.srcset;
4590
  /**
4591
  * @memberof FooGallery.Item#
4592
  * @name width
4593
  * @type {number}
4594
  */
4595
- self.width = options.width;
4596
  /**
4597
  * @memberof FooGallery.Item#
4598
  * @name height
4599
  * @type {number}
4600
  */
4601
- self.height = options.height;
4602
  /**
4603
  * @memberof FooGallery.Item#
4604
  * @name title
4605
  * @type {string}
4606
  */
4607
- self.title = options.title;
4608
  /**
4609
  * @memberof FooGallery.Item#
4610
  * @name alt
4611
  * @type {string}
4612
  */
4613
- self.alt = options.alt;
4614
  /**
4615
  * @memberof FooGallery.Item#
4616
  * @name caption
4617
  * @type {string}
4618
  */
4619
- self.caption = _is.empty(options.caption) ? self.title : options.caption;
4620
  /**
4621
  * @memberof FooGallery.Item#
4622
  * @name description
4623
  * @type {string}
4624
  */
4625
- self.description = _is.empty(options.description) ? self.alt : options.description;
4626
  /**
4627
  * @memberof FooGallery.Item#
4628
  * @name attrItem
4629
  * @type {FooGallery.Item~Attributes}
4630
  */
4631
- self.attr = options.attr;
4632
  /**
4633
  * @memberof FooGallery.Item#
4634
  * @name tags
4635
  * @type {string[]}
4636
  */
4637
- self.tags = options.tags;
4638
  /**
4639
  * @memberof FooGallery.Item#
4640
  * @name maxWidth
4641
  * @type {?FooGallery.Item~maxWidthCallback}
4642
  */
4643
- self.maxWidth = options.maxWidth;
4644
  /**
4645
  * @memberof FooGallery.Item#
4646
  * @name maxCaptionLength
4647
  * @type {number}
4648
  */
4649
- self.maxCaptionLength = options.maxCaptionLength;
4650
  /**
4651
  * @memberof FooGallery.Item#
4652
  * @name maxDescriptionLength
4653
  * @type {number}
4654
  */
4655
- self.maxDescriptionLength = options.maxDescriptionLength;
4656
  /**
4657
  * @memberof FooGallery.Item#
4658
  * @name showCaptionTitle
4659
  * @type {boolean}
4660
  */
4661
- self.showCaptionTitle = options.showCaptionTitle;
4662
  /**
4663
  * @memberof FooGallery.Item#
4664
  * @name showCaptionDescription
4665
  * @type {boolean}
4666
  */
4667
- self.showCaptionDescription = options.showCaptionDescription;
4668
  /**
4669
  * @summary The cached result of the last call to the {@link FooGallery.Item#getThumbUrl|getThumbUrl} method.
4670
  * @memberof FooGallery.Item#
@@ -4673,6 +4756,14 @@
4673
  * @private
4674
  */
4675
  self._thumbUrl = null;
 
 
 
 
 
 
 
 
4676
  /**
4677
  * @summary This property is used to store the promise created when loading an item for the first time.
4678
  * @memberof FooGallery.Item#
@@ -4681,13 +4772,26 @@
4681
  * @private
4682
  */
4683
  self._load = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
4684
  },
4685
  /**
4686
  * @summary Destroy the item preparing it for garbage collection.
4687
  * @memberof FooGallery.Item#
4688
  * @function destroy
4689
  */
4690
- destroy: function(){
4691
  var self = this;
4692
  /**
4693
  * @summary Raised when a template destroys an item.
@@ -4733,16 +4837,38 @@
4733
  * });
4734
  */
4735
  var e = self.tmpl.raise("destroy-item");
4736
- if (!e.isDefaultPrevented()){
4737
- if (self.isParsed && !self.isAttached){
4738
- self.append();
4739
- } else if (!self.isParsed && self.isAttached) {
4740
- self.detach();
4741
- }
4742
  self._super();
4743
  }
4744
  return self.tmpl === null;
4745
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4746
  /**
4747
  * @summary Parse the supplied element updating the current items' properties.
4748
  * @memberof FooGallery.Item#
@@ -4752,8 +4878,8 @@
4752
  * @fires FooGallery.Template~"parse-item.foogallery"
4753
  * @fires FooGallery.Template~"parsed-item.foogallery"
4754
  */
4755
- parse: function(element){
4756
- var self = this, o = self.tmpl.opt, cls = self.cls, sel = self.sel, $el = $(element);
4757
  /**
4758
  * @summary Raised when an item needs to parse properties from an element.
4759
  * @event FooGallery.Template~"parse-item.foogallery"
@@ -4797,53 +4923,12 @@
4797
  * });
4798
  */
4799
  var e = self.tmpl.raise("parse-item", [self, $el]);
4800
- if (!e.isDefaultPrevented() && (self.isCreated = $el.is(sel.elem))){
4801
- self.$el = $el.data(_.dataItem, self);
4802
- self.$inner = self.$el.find(sel.inner);
4803
- self.$anchor = self.$el.find(sel.anchor).on("click.foogallery", {self: self}, self.onAnchorClick);
4804
- self.$image = self.$anchor.find(sel.image);
4805
- self.$caption = self.$el.find(sel.caption.elem).on("click.foogallery", {self: self}, self.onCaptionClick);
4806
- self.isAttached = self.$el.parent().length > 0;
4807
- self.isLoading = self.$el.is(sel.loading);
4808
- self.isLoaded = self.$el.is(sel.loaded);
4809
- self.isError = self.$el.is(sel.error);
4810
- self.id = self.$anchor.data("id");
4811
- self.tags = self.$anchor.data("tags");
4812
- self.href = self.$anchor.attr("href");
4813
- self.src = self.$image.attr(o.src);
4814
- self.srcset = self.$image.attr(o.srcset);
4815
- self.width = parseInt(self.$image.attr("width"));
4816
- self.height = parseInt(self.$image.attr("height"));
4817
- self.title = self.$image.attr("title");
4818
- self.alt = self.$image.attr("alt");
4819
- self.caption = self.$anchor.data("title") || self.$anchor.data("captionTitle");
4820
- self.description = self.$anchor.data("description") || self.$anchor.data("captionDesc");
4821
- // if the caption or description are not provided set there values to the title and alt respectively
4822
- if (_is.empty(self.caption)) self.caption = self.title;
4823
- if (_is.empty(self.description)) self.description = self.alt;
4824
- // enforce the max lengths for the caption and description
4825
- if (_is.number(self.maxCaptionLength) && self.maxCaptionLength > 0 && !_is.empty(self.caption) && _is.string(self.caption) && self.caption.length > self.maxCaptionLength){
4826
- self.$caption.find(sel.caption.title).html(self.caption.substr(0, self.maxCaptionLength) + "&hellip;");
4827
- }
4828
- if (_is.number(self.maxDescriptionLength) && self.maxDescriptionLength > 0 && !_is.empty(self.description) && _is.string(self.description) && self.description.length > self.maxDescriptionLength){
4829
- self.$caption.find(sel.caption.description).html(self.description.substr(0, self.maxDescriptionLength) + "&hellip;");
4830
- }
4831
- // check if the item has a loader
4832
- if (self.$el.find(sel.loader).length === 0){
4833
- self.$el.append($("<div/>", {"class": cls.loader}));
4834
- }
4835
- // if the image has no src url then set the placeholder
4836
- if (_is.empty(self.$image.prop("src"))){
4837
- self.$image.prop("src", self.tmpl.items.placeholder(self.width, self.height));
4838
- }
4839
- if (self.isCreated && self.isAttached && !self.isLoading && !self.isLoaded && !self.isError){
4840
- self.$el.addClass(cls.idle);
4841
- }
4842
  self.fix();
4843
- self.isParsed = true;
4844
  // We don't load the attributes when parsing as they are only ever used to create an item and if you're parsing it's already created.
4845
  }
4846
- if (self.isParsed){
4847
  /**
4848
  * @summary Raised after an item has been parsed from an element.
4849
  * @event FooGallery.Template~"parsed-item.foogallery"
@@ -4865,6 +4950,65 @@
4865
  }
4866
  return self.isParsed;
4867
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4868
  /**
4869
  * @summary Create the items' DOM elements and populate the corresponding properties.
4870
  * @memberof FooGallery.Item#
@@ -4873,9 +5017,9 @@
4873
  * @fires FooGallery.Template~"create-item.foogallery"
4874
  * @fires FooGallery.Template~"created-item.foogallery"
4875
  */
4876
- create: function(){
4877
  var self = this;
4878
- if (!self.isCreated && _is.string(self.href) && _is.string(self.src) && _is.number(self.width) && _is.number(self.height)){
4879
  /**
4880
  * @summary Raised when an item needs to create its' elements.
4881
  * @event FooGallery.Template~"create-item.foogallery"
@@ -4918,74 +5062,10 @@
4918
  * });
4919
  */
4920
  var e = self.tmpl.raise("create-item", [self]);
4921
- if (!e.isDefaultPrevented()){
4922
- var o = self.tmpl.opt, cls = self.cls, attr = self.attr;
4923
- attr.elem["class"] = cls.elem + " " + cls.idle;
4924
-
4925
- attr.inner["class"] = cls.inner;
4926
-
4927
- attr.anchor["class"] = cls.anchor;
4928
- attr.anchor["href"] = self.href;
4929
- attr.anchor["data-id"] = self.id;
4930
- attr.anchor["data-title"] = self.caption;
4931
- attr.anchor["data-description"] = self.description;
4932
- if (!_is.empty(self.tags)){
4933
- attr.anchor["data-tags"] = JSON.stringify(self.tags);
4934
- }
4935
-
4936
- attr.image["class"] = cls.image;
4937
- attr.image["src"] = self.tmpl.items.placeholder(self.width, self.height);
4938
- attr.image[o.src] = self.src;
4939
- attr.image[o.srcset] = self.srcset;
4940
- attr.image["width"] = self.width;
4941
- attr.image["height"] = self.height;
4942
- attr.image["title"] = self.title;
4943
- attr.image["alt"] = self.alt;
4944
-
4945
- self.$el = $("<div/>").attr(attr.elem).data(_.dataItem, self);
4946
- self.$inner = $("<figure/>").attr(attr.inner).appendTo(self.$el);
4947
- self.$anchor = $("<a/>").attr(attr.anchor).appendTo(self.$inner).on("click.foogallery", {self: self}, self.onAnchorClick);
4948
- self.$image = $("<img/>").attr(attr.image).appendTo(self.$anchor);
4949
-
4950
- cls = self.cls.caption;
4951
- attr = self.attr.caption;
4952
- attr.elem["class"] = cls.elem;
4953
- self.$caption = $("<figcaption/>").attr(attr.elem).on("click.foogallery", {self: self}, self.onCaptionClick);
4954
- var hasTitle = !_is.empty(self.caption), hasDesc = !_is.empty(self.description);
4955
- if (hasTitle || hasDesc){
4956
- attr.inner["class"] = cls.inner;
4957
- attr.title["class"] = cls.title;
4958
- attr.description["class"] = cls.description;
4959
- var $inner = $("<div/>").attr(attr.inner).appendTo(self.$caption);
4960
- if (hasTitle){
4961
- var $title;
4962
- // enforce the max length for the caption
4963
- if (_is.number(self.maxCaptionLength) && self.maxCaptionLength > 0 && _is.string(self.caption) && self.caption.length > self.maxCaptionLength){
4964
- $title = $("<div/>").attr(attr.title).html(self.caption.substr(0, self.maxCaptionLength) + "&hellip;");
4965
- } else {
4966
- $title = $("<div/>").attr(attr.title).html(self.caption);
4967
- }
4968
- $inner.append($title);
4969
- }
4970
- if (hasDesc){
4971
- var $desc;
4972
- // enforce the max length for the description
4973
- if (_is.number(self.maxDescriptionLength) && self.maxDescriptionLength > 0 && _is.string(self.description) && self.description.length > self.maxDescriptionLength){
4974
- $desc = $("<div/>").attr(attr.description).html(self.description.substr(0, self.maxDescriptionLength) + "&hellip;");
4975
- } else {
4976
- $desc = $("<div/>").attr(attr.description).html(self.description);
4977
- }
4978
- $inner.append($desc);
4979
- }
4980
- }
4981
- self.$caption.appendTo(self.$inner);
4982
- // check if the item has a loader
4983
- if (self.$el.find(self.sel.loader).length === 0){
4984
- self.$el.append($("<div/>", {"class": self.cls.loader}));
4985
- }
4986
- self.isCreated = true;
4987
  }
4988
- if (self.isCreated){
4989
  /**
4990
  * @summary Raised after an items' elements have been created.
4991
  * @event FooGallery.Template~"created-item.foogallery"
@@ -5007,6 +5087,79 @@
5007
  }
5008
  return self.isCreated;
5009
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5010
  /**
5011
  * @summary Append the item to the current template.
5012
  * @memberof FooGallery.Item#
@@ -5015,9 +5168,9 @@
5015
  * @fires FooGallery.Template~"append-item.foogallery"
5016
  * @fires FooGallery.Template~"appended-item.foogallery"
5017
  */
5018
- append: function(){
5019
  var self = this;
5020
- if (self.isCreated && !self.isAttached){
5021
  /**
5022
  * @summary Raised when an item needs to append its elements to the template.
5023
  * @event FooGallery.Template~"append-item.foogallery"
@@ -5060,12 +5213,12 @@
5060
  * });
5061
  */
5062
  var e = self.tmpl.raise("append-item", [self]);
5063
- if (!e.isDefaultPrevented()){
5064
  self.tmpl.$el.append(self.$el);
5065
  self.fix();
5066
  self.isAttached = true;
5067
  }
5068
- if (self.isAttached){
5069
  /**
5070
  * @summary Raised after an item has appended its' elements to the template.
5071
  * @event FooGallery.Template~"appended-item.foogallery"
@@ -5093,9 +5246,9 @@
5093
  * @function detach
5094
  * @returns {boolean}
5095
  */
5096
- detach: function(){
5097
  var self = this;
5098
- if (self.isCreated && self.isAttached){
5099
  /**
5100
  * @summary Raised when an item needs to detach its' elements from the template.
5101
  * @event FooGallery.Template~"detach-item.foogallery"
@@ -5138,12 +5291,12 @@
5138
  * });
5139
  */
5140
  var e = self.tmpl.raise("detach-item", [self]);
5141
- if (!e.isDefaultPrevented()){
5142
  self.$el.detach();
5143
  self.unfix();
5144
  self.isAttached = false;
5145
  }
5146
- if (!self.isAttached){
5147
  /**
5148
  * @summary Raised after an item has detached its' elements from the template.
5149
  * @event FooGallery.Template~"detached-item.foogallery"
@@ -5171,7 +5324,7 @@
5171
  * @function load
5172
  * @returns {Promise.<FooGallery.Item>}
5173
  */
5174
- load: function(){
5175
  var self = this;
5176
  if (_is.promise(self._load)) return self._load;
5177
  if (!self.isCreated || !self.isAttached) return _fn.rejectWith("not created or attached");
@@ -5213,18 +5366,18 @@
5213
  * @function fix
5214
  * @returns {FooGallery.Item}
5215
  */
5216
- fix: function(){
5217
- var self = this;
5218
- if (self.isCreated && !self.isLoading && !self.isLoaded && !self.isError){
5219
  var w = self.width, h = self.height;
5220
  // if we have a base width and height to work with
5221
- if (!isNaN(w) && !isNaN(h)){
5222
  // figure out the max image width and calculate the height the image should be displayed as
5223
  var width = _is.fn(self.maxWidth) ? self.maxWidth(self) : self.$image.width();
5224
  if (width <= 0) width = w;
5225
  var ratio = width / w, height = h * ratio;
5226
  // actually set the inline css on the image
5227
- self.$image.css({width: width,height: height});
5228
  }
5229
  }
5230
  return self;
@@ -5235,9 +5388,9 @@
5235
  * @function unfix
5236
  * @returns {FooGallery.Item}
5237
  */
5238
- unfix: function(){
5239
- var self = this;
5240
- if (self.isCreated) self.$image.css({width: '',height: ''});
5241
  return self;
5242
  },
5243
  /**
@@ -5247,7 +5400,7 @@
5247
  * @param {boolean} [refresh=false] - Whether or not to force refreshing of the cached value.
5248
  * @returns {string}
5249
  */
5250
- getThumbUrl: function(refresh){
5251
  refresh = _is.boolean(refresh) ? refresh : false;
5252
  var self = this;
5253
  if (!refresh && _is.string(self._thumbUrl)) return self._thumbUrl;
@@ -5258,11 +5411,11 @@
5258
  * @memberof FooGallery.Item#
5259
  * @function scrollTo
5260
  */
5261
- scrollTo: function(align){
5262
  var self = this;
5263
- if (self.isAttached){
5264
  var ib = self.bounds(), vb = _utils.getViewportBounds();
5265
- switch(align){
5266
  case "top": // attempts to center the item horizontally but aligns the top with the middle of the viewport
5267
  ib.left += (ib.width / 2) - (vb.width / 2);
5268
  ib.top -= (vb.height / 5);
@@ -5282,7 +5435,7 @@
5282
  * @function bounds
5283
  * @returns {?FooGallery.utils.Bounds}
5284
  */
5285
- bounds: function(){
5286
  return this.isAttached ? _utils.getElementBounds(this.$el) : null;
5287
  },
5288
  /**
@@ -5292,7 +5445,7 @@
5292
  * @param {FooGallery.utils.Bounds} bounds - The bounds to check.
5293
  * @returns {boolean}
5294
  */
5295
- intersects: function(bounds){
5296
  return this.isAttached ? this.bounds().intersects(bounds) : false;
5297
  },
5298
  /**
@@ -5302,9 +5455,9 @@
5302
  * @param {jQuery.Event} e - The jQuery.Event object for the click event.
5303
  * @private
5304
  */
5305
- onAnchorClick: function(e){
5306
  var self = e.data.self,
5307
- state = self.tmpl.state.get(self);
5308
  self.tmpl.state.update(state);
5309
  },
5310
  /**
@@ -5314,9 +5467,9 @@
5314
  * @param {jQuery.Event} e - The jQuery.Event object for the click event.
5315
  * @private
5316
  */
5317
- onCaptionClick: function(e){
5318
  var self = e.data.self;
5319
- if ($(e.target).is(self.sel.caption.all) && self.$anchor.length > 0){
5320
  self.$anchor.get(0).click();
5321
  }
5322
  }
@@ -5338,6 +5491,7 @@
5338
  /**
5339
  * @summary A simple object containing an items' default values.
5340
  * @typedef {object} FooGallery.Item~Options
 
5341
  * @property {?string} [id=null] - The `data-id` attribute for the outer element.
5342
  * @property {?string} [href=null] - The `href` attribute for the anchor element.
5343
  * @property {?string} [src=null] - The `src` attribute for the image element.
@@ -5358,6 +5512,7 @@
5358
  */
5359
  _.template.configure("core", {
5360
  item: {
 
5361
  id: "",
5362
  href: "",
5363
  src: "",
@@ -5405,6 +5560,8 @@
5405
  description: "fg-caption-desc"
5406
  }
5407
  }
 
 
5408
  });
5409
 
5410
  _.components.register("item", _.Item);
@@ -5445,14 +5602,14 @@
5445
  */
5446
 
5447
  })(
5448
- FooGallery.$,
5449
- FooGallery,
5450
- FooGallery.utils,
5451
- FooGallery.utils.is,
5452
- FooGallery.utils.fn,
5453
- FooGallery.utils.obj
5454
  );
5455
- (function($, _, _utils, _is, _fn){
5456
 
5457
  _.Items = _.Component.extend(/** @lends FooGallery.Items */{
5458
  /**
@@ -5464,7 +5621,7 @@
5464
  * @borrows FooGallery.utils.Class.extend as extend
5465
  * @borrows FooGallery.utils.Class.override as override
5466
  */
5467
- construct: function(template){
5468
  var self = this;
5469
  /**
5470
  * @ignore
@@ -5481,7 +5638,7 @@
5481
  var cls = self.tmpl.cls.item.caption;
5482
  self.tmpl.sel.item.caption.all = _utils.selectify([cls.elem, cls.inner, cls.title, cls.description]);
5483
  },
5484
- destroy: function(){
5485
  var self = this, items = self.all(), destroyed = [];
5486
  if (items.length > 0) {
5487
  /**
@@ -5501,7 +5658,7 @@
5501
  * });
5502
  */
5503
  self.tmpl.raise("destroy-items", [items]);
5504
- destroyed = $.map(items, function(item){
5505
  return item.destroy() ? item : null;
5506
  });
5507
  /**
@@ -5529,24 +5686,24 @@
5529
  self._available = [];
5530
  self._super();
5531
  },
5532
- fetch: function(refresh){
5533
  var self = this;
5534
  if (!refresh && _is.promise(self._fetched)) return self._fetched;
5535
  var fg = self.tmpl, selectors = fg.sel,
5536
- option = fg.opt.items,
5537
- def = $.Deferred();
5538
 
5539
  var items = self.make(fg.$el.find(selectors.item.elem));
5540
 
5541
- if (!_is.empty(option)){
5542
- if (_is.array(option)){
5543
  items.push.apply(items, self.make(option));
5544
  def.resolve(items);
5545
- } else if (_is.string(option)){
5546
- $.get(option).then(function(response){
5547
  items.push.apply(items, self.make(response));
5548
  def.resolve(items);
5549
- }, function( jqXHR, textStatus, errorThrown ){
5550
  console.log("FooGallery: GET items error.", option, jqXHR, textStatus, errorThrown);
5551
  def.resolve(items);
5552
  });
@@ -5557,30 +5714,36 @@
5557
  items.push.apply(items, self.make(window[fg.id + "-items"]));
5558
  def.resolve(items);
5559
  }
5560
- def.then(function(items){
5561
- self._arr = items;
5562
- self.idMap = self.createIdMap(items);
5563
- self.setAvailable(self.all());
5564
  });
5565
  return self._fetched = def.promise();
5566
  },
5567
- all: function(){
5568
  return this._arr.slice();
5569
  },
5570
- available: function(){
 
 
 
5571
  return this._available.slice();
5572
  },
5573
- get: function(id){
5574
  return !_is.empty(id) && !!this.idMap[id] ? this.idMap[id] : null;
5575
  },
5576
- setAvailable: function(items){
 
 
 
 
 
5577
  this._available = _is.array(items) ? items : [];
5578
  },
5579
- reset: function(){
5580
  this.setAvailable(this.all());
5581
  },
5582
- placeholder: function(width, height){
5583
- if (this._canvas && this._canvas.toDataURL && _is.number(width) && _is.number(height)){
5584
  this._canvas.width = width;
5585
  this._canvas.height = height;
5586
  return this._canvas.toDataURL();
@@ -5594,14 +5757,14 @@
5594
  * @param {FooGallery.Item[]} items - The items to filter.
5595
  * @returns {FooGallery.Item[]}
5596
  */
5597
- loadable: function(items){
5598
  var self = this, opt = self.tmpl.opt, viewport;
5599
- if (opt.lazy){
5600
  viewport = _utils.getViewportBounds(opt.viewport);
5601
  }
5602
- return _is.array(items) ? $.map(items, function(item){
5603
- return item.isCreated && item.isAttached && !item.isLoading && !item.isLoaded && !item.isError && (!opt.lazy || (opt.lazy && item.intersects(viewport))) ? item : null;
5604
- }) : [];
5605
  },
5606
  /**
5607
  * @summary Filter the supplied `items` and return only those that can be created.
@@ -5610,10 +5773,10 @@
5610
  * @param {FooGallery.Item[]} items - The items to filter.
5611
  * @returns {FooGallery.Item[]}
5612
  */
5613
- creatable: function(items){
5614
- return _is.array(items) ? $.map(items, function(item){
5615
- return item instanceof _.Item && !item.isCreated ? item : null;
5616
- }) : [];
5617
  },
5618
  /**
5619
  * @summary Filter the supplied `items` and return only those that can be appended.
@@ -5622,10 +5785,10 @@
5622
  * @param {FooGallery.Item[]} items - The items to filter.
5623
  * @returns {FooGallery.Item[]}
5624
  */
5625
- appendable: function(items){
5626
- return _is.array(items) ? $.map(items, function(item){
5627
- return item instanceof _.Item && item.isCreated && !item.isAttached ? item : null;
5628
- }) : [];
5629
  },
5630
  /**
5631
  * @summary Filter the supplied `items` and return only those that can be detached.
@@ -5634,10 +5797,10 @@
5634
  * @param {FooGallery.Item[]} items - The items to filter.
5635
  * @returns {FooGallery.Item[]}
5636
  */
5637
- detachable: function(items){
5638
- return _is.array(items) ? $.map(items, function(item){
5639
- return item instanceof _.Item && item.isCreated && item.isAttached ? item : null;
5640
- }) : [];
5641
  },
5642
  /**
5643
  * @summary Get a single jQuery object containing all the supplied items' elements.
@@ -5646,7 +5809,7 @@
5646
  * @param {FooGallery.Item[]} items - The items to get a jQuery object for.
5647
  * @returns {jQuery}
5648
  */
5649
- jquerify: function(items){
5650
  return $($.map(items, function (item) {
5651
  return item.$el.get();
5652
  }));
@@ -5661,9 +5824,9 @@
5661
  * @fires FooGallery.Template~"made-items.foogallery"
5662
  * @fires FooGallery.Template~"parsed-items.foogallery"
5663
  */
5664
- make: function(items){
5665
  var self = this, made = [];
5666
- if (_is.jq(items) || _is.array(items)){
5667
  var parsed = [], arr = $.makeArray(items);
5668
  if (arr.length === 0) return made;
5669
  /**
@@ -5695,11 +5858,13 @@
5695
  * });
5696
  */
5697
  var e = self.tmpl.raise("make-items", [arr]);
5698
- if (!e.isDefaultPrevented()){
5699
- made = $.map(arr, function(obj){
5700
- var item = _.components.make("item", self.tmpl, _is.hash(obj) ? obj : {});
5701
- if (_is.element(obj)){
5702
- if (item.parse(obj)){
 
 
5703
  parsed.push(item);
5704
  return item;
5705
  }
@@ -5747,6 +5912,15 @@
5747
  }
5748
  return made;
5749
  },
 
 
 
 
 
 
 
 
 
5750
  /**
5751
  * @summary Create each of the supplied {@link FooGallery.Item|`items`} elements.
5752
  * @memberof FooGallery.Items#
@@ -5760,7 +5934,7 @@
5760
  * @fires FooGallery.Template~"append-items.foogallery"
5761
  * @fires FooGallery.Template~"appended-items.foogallery"
5762
  */
5763
- create: function(items, append){
5764
  var self = this, created = [], creatable = self.creatable(items);
5765
  if (creatable.length > 0) {
5766
  /**
@@ -5792,8 +5966,8 @@
5792
  * });
5793
  */
5794
  var e = self.tmpl.raise("create-items", [creatable]);
5795
- if (!e.isDefaultPrevented()){
5796
- created = $.map(creatable, function(item){
5797
  return item.create() ? item : null;
5798
  });
5799
  }
@@ -5827,7 +6001,7 @@
5827
  * @fires FooGallery.Template~"append-items.foogallery"
5828
  * @fires FooGallery.Template~"appended-items.foogallery"
5829
  */
5830
- append: function(items){
5831
  var self = this, appended = [], appendable = self.appendable(items);
5832
  if (appendable.length > 0) {
5833
  /**
@@ -5858,8 +6032,8 @@
5858
  * });
5859
  */
5860
  var e = self.tmpl.raise("append-items", [appendable]);
5861
- if (!e.isDefaultPrevented()){
5862
- appended = $.map(appendable, function(item){
5863
  return item.append() ? item : null;
5864
  });
5865
  }
@@ -5892,7 +6066,7 @@
5892
  * @fires FooGallery.Template~"detach-items.foogallery"
5893
  * @fires FooGallery.Template~"detached-items.foogallery"
5894
  */
5895
- detach: function(items){
5896
  var self = this, detached = [], detachable = self.detachable(items);
5897
  if (detachable.length > 0) {
5898
  /**
@@ -5923,8 +6097,8 @@
5923
  * });
5924
  */
5925
  var e = self.tmpl.raise("detach-items", [detachable]);
5926
- if (!e.isDefaultPrevented()){
5927
- detached = $.map(detachable, function(item){
5928
  return item.detach() ? item : null;
5929
  });
5930
  }
@@ -5957,10 +6131,10 @@
5957
  * @fires FooGallery.Template~"load-items.foogallery"
5958
  * @fires FooGallery.Template~"loaded-items.foogallery"
5959
  */
5960
- load: function(items){
5961
  var self = this;
5962
  items = self.loadable(items);
5963
- if (items.length > 0){
5964
  /**
5965
  * @summary Raised before the template loads any items.
5966
  * @event FooGallery.Template~"load-items.foogallery"
@@ -5989,11 +6163,11 @@
5989
  * });
5990
  */
5991
  var e = self.tmpl.raise("load-items", [items]);
5992
- if (!e.isDefaultPrevented()){
5993
- var loading = $.map(items, function(item){
5994
  return item.load();
5995
  });
5996
- return _fn.when(loading).done(function(loaded) {
5997
  /**
5998
  * @summary Raised after the template has loaded items.
5999
  * @event FooGallery.Template~"loaded-items.foogallery"
@@ -6016,9 +6190,9 @@
6016
  }
6017
  return _fn.resolveWith([]);
6018
  },
6019
- createIdMap: function(items){
6020
  var map = {};
6021
- $.each(items, function(i, item){
6022
  if (_is.empty(item.id)) item.id = "" + (i + 1);
6023
  map[item.id] = item;
6024
  });
@@ -6029,17 +6203,17 @@
6029
  _.components.register("items", _.Items);
6030
 
6031
  })(
6032
- FooGallery.$,
6033
- FooGallery,
6034
- FooGallery.utils,
6035
- FooGallery.utils.is,
6036
- FooGallery.utils.fn,
6037
- FooGallery.utils.str
6038
  );
6039
- (function($, _, _utils, _is){
6040
 
6041
  _.Paging = _.Component.extend({
6042
- construct: function(template){
6043
  var self = this;
6044
  /**
6045
  * @ignore
@@ -6062,119 +6236,111 @@
6062
  self.ctrls = [];
6063
  self._arr = [];
6064
  },
6065
- destroy: function(){
6066
  var self = this;
6067
  self._arr.splice(0, self._arr.length);
6068
- $.each(self.ctrls.splice(0, self.ctrls.length), function(i, control){
6069
  control.destroy();
6070
  });
6071
  self._super();
6072
  },
6073
- build: function(){
6074
  var self = this, items = self.tmpl.items.available();
6075
  self.total = self.size > 0 && items.length > 0 ? Math.ceil(items.length / self.size) : 1;
6076
- if (self.total <= 1){
6077
- self._arr.push(items);
6078
- self.tmpl.items.detach(self._arr[0]);
6079
- } else {
6080
- for (var i = 0; i < self.total; i++){
6081
- self._arr.push(items.splice(0, self.size));
6082
- self.tmpl.items.detach(self._arr[i]);
6083
- }
6084
  }
6085
- if (self.total > 1 && _.paging.hasCtrl(self.type)){
6086
  var pos = self.position, top, bottom;
6087
- if (pos === "both" || pos === "top"){
6088
  top = _.paging.makeCtrl(self.type, self.tmpl, self, "top");
6089
- if (top.create()){
6090
  top.append();
6091
  self.ctrls.push(top);
6092
  }
6093
  }
6094
- if (pos === "both" || pos === "bottom"){
6095
  bottom = _.paging.makeCtrl(self.type, self.tmpl, self, "bottom");
6096
- if (bottom.create()){
6097
  bottom.append();
6098
  self.ctrls.push(bottom);
6099
  }
6100
  }
6101
  }
6102
  },
6103
- rebuild: function(){
6104
  var self = this;
6105
  self.current = 0;
6106
  self.total = 0;
6107
  self._arr.splice(0, self._arr.length);
6108
- $.each(self.ctrls.splice(0, self.ctrls.length), function(i, control){
6109
  control.destroy();
6110
  });
6111
  self.build();
6112
  },
6113
- all: function(){
6114
  return this._arr.slice();
6115
  },
6116
- available: function(){
6117
  return this.get(this.current);
6118
  },
6119
- controls: function(pageNumber){
6120
  var self = this;
6121
- if (self.isValid(pageNumber)){
6122
- $.each(self.ctrls, function(i, control){
6123
  control.update(pageNumber);
6124
  });
6125
  }
6126
  },
6127
- isValid: function(pageNumber){
6128
  return _is.number(pageNumber) && pageNumber > 0 && pageNumber <= this.total;
6129
  },
6130
- number: function(value){
6131
  return this.isValid(value) ? value : (this.current === 0 ? 1 : this.current);
6132
  },
6133
- create: function(pageNumber){
6134
  var self = this;
6135
  pageNumber = self.number(pageNumber);
6136
  var index = pageNumber - 1;
 
6137
  self.tmpl.items.create(self._arr[index], true);
6138
- for (var i = 0, l = self._arr.length; i < l; i++) {
6139
- if (i === index) continue;
6140
- self.tmpl.items.detach(self._arr[i]);
6141
- }
6142
  self.current = pageNumber;
6143
  },
6144
- get: function(pageNumber){
6145
  var self = this;
6146
- if (self.isValid(pageNumber)){
6147
  pageNumber = self.number(pageNumber);
6148
  return self._arr[pageNumber - 1];
6149
  }
6150
  return [];
6151
  },
6152
- set: function(pageNumber, scroll, updateState){
6153
  var self = this;
6154
- if (self.isValid(pageNumber)){
6155
- self.controls(pageNumber);
6156
  var num = self.number(pageNumber), state;
6157
  if (num !== self.current) {
6158
- var prev = self.current, setPage = function(){
6159
  updateState = _is.boolean(updateState) ? updateState : true;
6160
- if (updateState && self.current === 1 && !self.tmpl.state.exists()){
 
6161
  state = self.tmpl.state.get();
6162
  self.tmpl.state.update(state, self.pushOrReplace);
6163
  }
6164
- self.create(num);
6165
- if (updateState){
 
6166
  state = self.tmpl.state.get();
6167
  self.tmpl.state.update(state, self.pushOrReplace);
6168
  }
6169
  if (self.scrollToTop && _is.boolean(scroll) ? scroll : false) {
6170
  var page = self.get(self.current);
6171
- if (page.length > 0){
6172
  page[0].scrollTo("top");
6173
  }
6174
  }
6175
- self.tmpl.raise("after-page-change", [self.current, prev]);
6176
  };
6177
- var e = self.tmpl.raise("before-page-change", [self.current, num, setPage]);
6178
  if (e.isDefaultPrevented()) return false;
6179
  setPage();
6180
  return true;
@@ -6182,7 +6348,7 @@
6182
  }
6183
  return false;
6184
  },
6185
- find: function(item){
6186
  var self = this;
6187
  for (var i = 0, l = self._arr.length; i < l; i++) {
6188
  if ($.inArray(item, self._arr[i]) !== -1) {
@@ -6191,57 +6357,58 @@
6191
  }
6192
  return 0;
6193
  },
6194
- contains: function(pageNumber, item){
6195
  var items = this.get(pageNumber);
6196
  return $.inArray(item, items) !== -1;
6197
  },
6198
- first: function(){
6199
  this.goto(1);
6200
  },
6201
- last: function(){
6202
  this.goto(this._arr.length);
6203
  },
6204
- prev: function(){
6205
  this.goto(this.current - 1);
6206
  },
6207
- next: function(){
6208
  this.goto(this.current + 1);
6209
  },
6210
- goto: function(pageNumber){
6211
  var self = this;
6212
- if (self.set(pageNumber, true)){
6213
  self.tmpl.loadAvailable();
6214
  }
6215
  }
6216
  });
6217
 
6218
  _.PagingControl = _.Component.extend({
6219
- construct: function(template, parent, position){
6220
  var self = this;
6221
  self._super(template);
6222
  self.pages = parent;
6223
  self.position = position;
6224
  self.$container = null;
6225
  },
6226
- create: function(){
6227
  var self = this;
6228
  self.$container = $("<nav/>", {"class": self.pages.cls.container}).addClass(self.pages.theme);
6229
  return true;
6230
  },
6231
- destroy: function(){
6232
  var self = this;
6233
  self.$container.remove();
6234
  self.$container = null;
6235
  },
6236
- append: function(){
6237
  var self = this;
6238
- if (self.position === "top"){
6239
  self.$container.insertBefore(self.tmpl.$el);
6240
  } else {
6241
  self.$container.insertAfter(self.tmpl.$el);
6242
  }
6243
  },
6244
- update: function(pageNumber){}
 
6245
  });
6246
 
6247
  _.paging.register("default", _.Paging, null, {
@@ -6256,10 +6423,10 @@
6256
  }, null, -100);
6257
 
6258
  })(
6259
- FooGallery.$,
6260
- FooGallery,
6261
- FooGallery.utils,
6262
- FooGallery.utils.is
6263
  );
6264
  (function($, _, _utils, _is){
6265
 
@@ -6560,6 +6727,16 @@
6560
  cls.layouts = $.map(cls.layout, function(value){
6561
  return value;
6562
  }).join(" ");
 
 
 
 
 
 
 
 
 
 
6563
  // check if the supplied layout is supported
6564
  if (!_is.string(cls.layout[self.template.layout])){
6565
  // if not set the default
@@ -6601,13 +6778,12 @@
6601
  rule = '#' + self.id + sel.container + ' ' + sel.item.elem + ' { margin-bottom: ' + self.template.gutter + 'px; }';
6602
  sheet.insertRule(rule , 0);
6603
  }
6604
-
6605
  self.masonry = new Masonry( self.$el.get(0), self.template );
6606
  },
6607
- onInit: function(event, self){
6608
  self.masonry.layout();
6609
  },
6610
- onPostInit: function(event, self){
6611
  self.masonry.layout();
6612
  },
6613
  onReady: function(event, self){
@@ -6616,13 +6792,15 @@
6616
  onDestroy: function(event, self){
6617
  self.$el.find(self.sel.columnWidth).remove();
6618
  self.$el.find(self.sel.gutterWidth).remove();
6619
- if (self.masonry instanceof Masonry){
6620
- self.masonry.destroy();
6621
- }
6622
  if (self.style && self.style.parentNode){
6623
  self.style.parentNode.removeChild(self.style);
6624
  }
6625
  },
 
 
 
 
 
6626
  onLayout: function(event, self){
6627
  self.masonry.layout();
6628
  },
@@ -6697,6 +6875,8 @@
6697
 
6698
  _.template.register("masonry", _.MasonryTemplate, {
6699
  template: {
 
 
6700
  layout: "col4"
6701
  }
6702
  }, {
@@ -6771,10 +6951,10 @@
6771
  */
6772
 
6773
  })(
6774
- FooGallery.$,
6775
- FooGallery,
6776
- FooGallery.utils,
6777
- FooGallery.utils.is
6778
  );
6779
  (function($, _, _utils, _is){
6780
 
@@ -6793,14 +6973,10 @@
6793
  self.options.maxRowHeight = parseInt(self.options.maxRowHeight);
6794
  }
6795
  }
6796
- self.layout(true);
6797
  $(window).on("resize.justified", {self: self}, self.onWindowResize);
6798
  },
6799
  destroy: function(){
6800
  $(window).off("resize.justified");
6801
- $.each(this._items, function(i, item){
6802
- item.$item.removeAttr("style").removeClass("fg-positioned");
6803
- });
6804
  this.$el.removeAttr("style");
6805
  },
6806
  parse: function(){
@@ -6856,9 +7032,9 @@
6856
  }
6857
 
6858
  var self = this,
6859
- containerWidth = self.getContainerWidth(),
6860
- rows = self.rows(containerWidth),
6861
- offsetTop = 0;
6862
 
6863
  for (var i = 0, l = rows.length, row; i < l; i++){
6864
  row = rows[i];
@@ -6922,9 +7098,9 @@
6922
  },
6923
  justify: function(row, containerWidth, offsetTop){
6924
  var self = this,
6925
- left = 0,
6926
- margins = self.options.margins * (row.items.length - 1),
6927
- ratio = (containerWidth - margins) / row.width;
6928
 
6929
  if (row.index > 0) offsetTop += self.options.margins;
6930
  row.top = offsetTop;
@@ -6971,10 +7147,10 @@
6971
  },
6972
  rows: function(containerWidth){
6973
  var self = this,
6974
- items = self.items(),
6975
- rows = [],
6976
- process = items.length > 0,
6977
- index = -1, offsetTop = 0;
6978
 
6979
  while (process){
6980
  index += 1;
@@ -7037,10 +7213,10 @@
7037
  };
7038
 
7039
  })(
7040
- FooGallery.$,
7041
- FooGallery,
7042
- FooGallery.utils,
7043
- FooGallery.utils.is
7044
  );
7045
  (function($, _, _utils){
7046
 
@@ -7051,11 +7227,11 @@
7051
  onInit: function(event, self){
7052
  self.justified.init();
7053
  },
7054
- onPostInit: function(event, self){
7055
  self.justified.layout( true );
7056
  },
7057
  onReady: function(event, self){
7058
- self.justified.layout( true );
7059
  },
7060
  onDestroy: function(event, self){
7061
  self.justified.destroy();
@@ -7079,9 +7255,9 @@
7079
  });
7080
 
7081
  })(
7082
- FooGallery.$,
7083
- FooGallery,
7084
- FooGallery.utils
7085
  );
7086
  (function($, _, _utils, _is){
7087
 
@@ -7094,13 +7270,9 @@
7094
  init: function(){
7095
  var self = this;
7096
  $(window).on("resize.portfolio", {self: self}, self.onWindowResize);
7097
- self.layout(true);
7098
  },
7099
  destroy: function(){
7100
  $(window).off("resize.portfolio");
7101
- $.each(this._items, function(i, item){
7102
- item.$item.removeAttr("style").removeClass("fg-positioned");
7103
- });
7104
  this.$el.removeAttr("style");
7105
  },
7106
  parse: function(){
@@ -7160,9 +7332,9 @@
7160
  }
7161
 
7162
  var self = this,
7163
- containerWidth = self.getContainerWidth(),
7164
- rows = self.rows(containerWidth),
7165
- offsetTop = 0;
7166
 
7167
  for (var i = 0, l = rows.length, row; i < l; i++){
7168
  row = rows[i];
@@ -7222,10 +7394,10 @@
7222
  },
7223
  rows: function(containerWidth){
7224
  var self = this,
7225
- items = self.items(),
7226
- rows = [],
7227
- process = items.length > 0,
7228
- index = -1, offsetTop = 0;
7229
 
7230
  while (process){
7231
  index += 1;
@@ -7290,10 +7462,10 @@
7290
  };
7291
 
7292
  })(
7293
- FooGallery.$,
7294
- FooGallery,
7295
- FooGallery.utils,
7296
- FooGallery.utils.is
7297
  );
7298
  (function($, _, _utils){
7299
 
@@ -7309,11 +7481,11 @@
7309
  onInit: function(event, self){
7310
  self.portfolio.init();
7311
  },
7312
- onPostInit: function(event, self){
7313
  self.portfolio.layout( true );
7314
  },
7315
  onReady: function(event, self){
7316
- self.portfolio.layout( true );
7317
  },
7318
  onDestroy: function(event, self){
7319
  self.portfolio.destroy();
@@ -7339,9 +7511,9 @@
7339
  });
7340
 
7341
  })(
7342
- FooGallery.$,
7343
- FooGallery,
7344
- FooGallery.utils
7345
  );
7346
  // (function(_){
7347
  //
@@ -7399,35 +7571,35 @@
7399
  * @name $inner
7400
  * @type {jQuery}
7401
  */
7402
- this.$inner = this.$el.find('.fiv-inner-container');
7403
  /**
7404
  * @summary The jQuery object that displays the current image count.
7405
  * @memberof FooGallery.ImageViewerTemplate#
7406
  * @name $current
7407
  * @type {jQuery}
7408
  */
7409
- this.$current = this.$el.find('.fiv-count-current');
7410
  /**
7411
  * @summary The jQuery object that displays the current image count.
7412
  * @memberof FooGallery.ImageViewerTemplate#
7413
  * @name $current
7414
  * @type {jQuery}
7415
  */
7416
- this.$total = this.$el.find('.fiv-count-total');
7417
  /**
7418
  * @summary The jQuery object for the previous button.
7419
  * @memberof FooGallery.ImageViewerTemplate#
7420
  * @name $prev
7421
  * @type {jQuery}
7422
  */
7423
- this.$prev = this.$el.find('.fiv-prev');
7424
  /**
7425
  * @summary The jQuery object for the next button.
7426
  * @memberof FooGallery.ImageViewerTemplate#
7427
  * @name $next
7428
  * @type {jQuery}
7429
  */
7430
- this.$next = this.$el.find('.fiv-next');
7431
  /**
7432
  * @summary The CSS classes for the Image Viewer template.
7433
  * @memberof FooGallery.ImageViewerTemplate#
@@ -7441,6 +7613,28 @@
7441
  * @type {FooGallery.ImageViewerTemplate~CSSSelectors}
7442
  */
7443
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7444
  onInit: function (event, self) {
7445
  if (self.template.attachFooBox) {
7446
  self.$el.on('foobox.previous', {self: self}, self.onFooBoxPrev)
@@ -7473,6 +7667,14 @@
7473
  item.fix();
7474
  item.isAttached = true;
7475
  },
 
 
 
 
 
 
 
 
7476
  update: function(){
7477
  if (this.pages){
7478
  this.$current.text(this.pages.current);
@@ -7487,7 +7689,11 @@
7487
  */
7488
  prev: function () {
7489
  if (this.pages){
7490
- this.pages.prev();
 
 
 
 
7491
  this.update();
7492
  }
7493
  },
@@ -7499,7 +7705,11 @@
7499
  */
7500
  next: function () {
7501
  if (this.pages){
7502
- this.pages.next();
 
 
 
 
7503
  this.update();
7504
  }
7505
  },
@@ -7547,10 +7757,23 @@
7547
 
7548
  _.template.register("image-viewer", _.ImageViewerTemplate, {
7549
  template: {
7550
- attachFooBox: false
 
7551
  }
7552
  }, {
7553
- container: "foogallery fg-image-viewer"
 
 
 
 
 
 
 
 
 
 
 
 
7554
  });
7555
 
7556
  })(
@@ -7564,15 +7787,40 @@
7564
  _.ThumbnailTemplate = _.Template.extend({
7565
  construct: function (options, element) {
7566
  this._super(_obj.extend({}, options, {
 
 
 
7567
  paging: {
7568
  type: "none"
7569
  }
7570
  }), element);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7571
  }
7572
  });
7573
 
7574
  _.template.register("thumbnail", _.ThumbnailTemplate, null, {
7575
- container: "foogallery fg-thumbnail"
 
7576
  });
7577
 
7578
  })(
@@ -7580,33 +7828,36 @@
7580
  FooGallery,
7581
  FooGallery.utils.obj
7582
  );
7583
- (function($, _utils, _is){
7584
 
7585
- function wp_integration(e, tmpl, current, prev){
7586
- if ((e.type === "after-page-change" && prev !== 0) || e.type === "ready"){
7587
  $("body").trigger("post-load");
7588
  }
7589
- }
 
 
 
 
 
 
 
 
 
 
7590
 
7591
  // this automatically initializes all templates on page load
7592
  $(function () {
7593
- $('[id^="foogallery-"]:not(.fg-ready)').foogallery({
7594
- on: {
7595
- "ready.foogallery after-page-change.foogallery": wp_integration
7596
- }
7597
- });
7598
  });
7599
 
7600
- _utils.ready(function(){
7601
- $('[id^="foogallery-"].fg-ready').foogallery({
7602
- on: {
7603
- "ready.foogallery after-page-change.foogallery": wp_integration
7604
- }
7605
- });
7606
  });
7607
 
7608
  })(
7609
  FooGallery.$,
 
7610
  FooGallery.utils,
7611
- FooGallery.utils.is
7612
  );
58
  );
59
  /*!
60
  * FooGallery.utils - Contains common utility methods and classes used in our plugins.
61
+ * @version 0.0.8
62
  * @link https://github.com/steveush/foo-utils#readme
63
+ * @copyright Steve Usher 2018
64
  * @license Released under the GPL-3.0 license.
65
  */
66
  /**
111
  * @name version
112
  * @type {string}
113
  */
114
+ version: '0.0.8',
115
  };
116
 
117
  /**
207
  })(jQuery);
208
  (function ($, _){
209
  // only register methods if this version is the current version
210
+ if (_.version !== '0.0.8') return;
211
 
212
  /**
213
  * @summary Contains common type checking utility methods.
561
  );
562
  (function($, _, _is){
563
  // only register methods if this version is the current version
564
+ if (_.version !== '0.0.8') return;
565
 
566
  /**
567
  * @memberof FooGallery.utils
1096
  );
1097
  (function(_, _is){
1098
  // only register methods if this version is the current version
1099
+ if (_.version !== '0.0.8') return;
1100
 
1101
  /**
1102
  * @summary Contains common url utility methods.
1182
  * console.log( _url.param( search, "v", "2" ) ); // => "?wmode=opaque&autoplay=1&v=2"
1183
  */
1184
  _.url.param = function(search, key, value){
1185
+ if (!_is.string(search) || !_is.string(key) || _is.empty(key)) return search;
1186
  var regex, match, result, param;
1187
  if (_is.undef(value)){
1188
  regex = new RegExp('[?|&]' + key + '=([^&;]+?)(&|#|;|$)'); // regex to match the key and it's value but only capture the value
1199
  result = search.replace(regex, '$1' + param); // replace any existing instance of the key with the new value
1200
  // If nothing was replaced, then add the new param to the end
1201
  if (result === search && !regex.test(result)) { // if no replacement occurred and the parameter is not currently in the result then add it
1202
+ result += (result.indexOf("?") !== -1 ? '&' : '?') + param;
1203
  }
1204
  }
1205
  return result;
1231
  );
1232
  (function (_, _is, _fn) {
1233
  // only register methods if this version is the current version
1234
+ if (_.version !== '0.0.8') return;
1235
 
1236
  /**
1237
  * @summary Contains common string utility methods.
1546
  );
1547
  (function($, _, _is, _fn, _str){
1548
  // only register methods if this version is the current version
1549
+ if (_.version !== '0.0.8') return;
1550
 
1551
  /**
1552
  * @summary Contains common object utility methods.
1629
  * console.log( _obj.merge( target, object ) ); // => {"name": "My Object", "enabled": true, "arr": [4,5,6], "something": 123}
1630
  */
1631
  _.obj.merge = function(target, object){
1632
+ target = _is.hash(target) ? target : {};
1633
+ object = _is.hash(object) ? object : {};
1634
  for (var prop in object) {
1635
  if (object.hasOwnProperty(prop)) {
1636
+ if (_is.hash(object[prop])) {
1637
+ target[prop] = _is.hash(target[prop]) ? target[prop] : {};
1638
  _.obj.merge(target[prop], object[prop]);
1639
  } else if (_is.array(object[prop])) {
1640
  target[prop] = object[prop].slice();
1878
  );
1879
  (function($, _, _is){
1880
  // only register methods if this version is the current version
1881
+ if (_.version !== '0.0.8') return;
1882
 
1883
  // any methods that have dependencies but don't fall into a specific subset or namespace can be added here
1884
 
1977
  );
1978
  (function($, _, _is){
1979
  // only register methods if this version is the current version
1980
+ if (_.version !== '0.0.8') return;
1981
 
1982
  /**
1983
  * @summary Contains common utility methods and members for the CSS transition property.
2072
  return def;
2073
  };
2074
 
2075
+ /**
2076
+ * @summary The callback function to execute when starting a transition.
2077
+ * @callback FooGallery.utils.transition~startCallback
2078
+ * @param {jQuery} $element - The element to start the transition on.
2079
+ * @this Element
2080
+ */
2081
+
2082
  /**
2083
  * @summary Start a transition by toggling the supplied `className` on the `$element`.
2084
  * @memberof FooGallery.utils.transition
2085
  * @function start
2086
  * @param {jQuery} $element - The jQuery element to start the transition on.
2087
+ * @param {(string|FooGallery.utils.transition~startCallback)} classNameOrFunc - One or more class names (separated by spaces) to be toggled or a function that performs the required actions to start the transition.
2088
  * @param {boolean} [state] - A Boolean (not just truthy/falsy) value to determine whether the class should be added or removed.
2089
  * @param {number} [timeout] - The maximum time, in milliseconds, to wait for the `transitionend` event to be raised. If not provided this will be automatically set to the elements `transition-duration` property plus an extra 50 milliseconds.
2090
  * @returns {Promise}
2093
  * The last parameter `timeout` is used to create a timer that behaves as a safety net in case the `transitionend` event is never raised and ensures the deferred returned by this method is resolved or rejected within a specified time.
2094
  * @see {@link https://developer.mozilla.org/en/docs/Web/CSS/transition-duration|transition-duration - CSS | MDN} for more information on the `transition-duration` CSS property.
2095
  */
2096
+ _.transition.start = function($element, classNameOrFunc, state, timeout){
2097
  var deferred = $.Deferred();
2098
 
2099
  $element = $element.first();
2128
 
2129
  setTimeout(function(){
2130
  // This is executed inside of a 20ms timeout to allow the binding of the event handler above to actually happen before the class is toggled
2131
+ if (_is.fn(classNameOrFunc)){
2132
+ classNameOrFunc.apply($element.get(0), [$element]);
2133
+ } else {
2134
+ $element.toggleClass(classNameOrFunc, state);
2135
+ }
2136
  if (!_.transition.supported){
2137
  // If the browser doesn't support transitions then just resolve the deferred
2138
  deferred.resolve();
2150
  );
2151
  (function ($, _, _is, _obj, _fn) {
2152
  // only register methods if this version is the current version
2153
+ if (_.version !== '0.0.8') return;
2154
 
2155
  /**
2156
  * @summary A base class providing some helper methods for prototypal inheritance.
2290
  );
2291
  (function($, _, _is){
2292
  // only register methods if this version is the current version
2293
+ if (_.version !== '0.0.8') return;
2294
 
2295
  _.Bounds = _.Class.extend(/** @lends FooGallery.utils.Bounds */{
2296
  /**
2392
  );
2393
  (function($, _, _is, _fn){
2394
  // only register methods if this version is the current version
2395
+ if (_.version !== '0.0.8') return;
2396
 
2397
  _.Factory = _.Class.extend(/** @lends FooGallery.utils.Factory */{
2398
  /**
2716
  );
2717
  (function(_, _fn, _str){
2718
  // only register methods if this version is the current version
2719
+ if (_.version !== '0.0.8') return;
2720
 
2721
  // this is done to handle Content Security in Chrome and other browsers blocking access to the localStorage object under certain configurations.
2722
  // see: https://www.chromium.org/for-testers/bug-reporting-guidelines/uncaught-securityerror-failed-to-read-the-localstorage-property-from-window-access-is-denied-for-this-document
2823
  );
2824
  (function($, _, _is){
2825
  // only register methods if this version is the current version
2826
+ if (_.version !== '0.0.8') return;
2827
 
2828
  _.Throttle = _.Class.extend(/** @lends FooGallery.utils.Throttle */{
2829
  /**
2935
  FooGallery.utils,
2936
  FooGallery.utils.is
2937
  );
2938
+ (function ($, _, _utils, _is, _fn) {
2939
 
2940
  _.debug = new _utils.Debugger("__FooGallery__");
2941
 
2946
  * @param {(string|string[]|object)} classes - A single space delimited string of CSS class names to convert or an array of them with each item being included in the selector using the OR (`,`) syntax as a separator. If an object is supplied the result will be an object with the same property names but the values converted to selectors.
2947
  * @returns {(object|string)}
2948
  */
2949
+ _utils.selectify = function (classes) {
2950
+ if (_is.empty(classes)) return null;
2951
+ if (_is.hash(classes)) {
2952
  var result = {}, selector;
2953
+ for (var name in classes) {
2954
  if (!classes.hasOwnProperty(name)) continue;
2955
+ if (selector = _utils.selectify(classes[name])) {
2956
  result[name] = selector;
2957
  }
2958
  }
2959
  return result;
2960
  }
2961
+ if (_is.string(classes) || _is.array(classes)) {
2962
  if (_is.string(classes)) classes = [classes];
2963
+ return $.map(classes, function (str) {
2964
  return _is.string(str) ? "." + str.split(/\s/g).join(".") : null;
2965
  }).join(",");
2966
  }
2994
  */
2995
  _.dataItem = "__FooGalleryItem__";
2996
 
2997
+ _.init = function (options, element) {
2998
  return _.template.make(options, element).initialize();
2999
  };
3000
 
3001
+ _.initAll = function (options) {
3002
+ return _fn.when($(".foogallery").map(function (i, element) {
3003
  return _.init(options, element);
3004
  }).get());
3005
  };
3006
 
3007
+ _.parseSrc = function (src, srcWidth, srcHeight, srcset, renderWidth, renderHeight) {
3008
  if (!_is.string(src)) return null;
3009
  // if there is no srcset just return the src
3010
  if (!_is.string(srcset)) return src;
3032
 
3033
  // get the current viewport info and use it to determine the correct src to load
3034
  var dpr = window.devicePixelRatio || 1,
3035
+ area = {w: renderWidth * dpr, h: renderHeight * dpr, x: dpr},
3036
+ property;
3037
 
3038
  // first check each of the viewport properties against the max values of the same properties in our src array
3039
  // only src's with a property greater than the viewport or equal to the max are kept
3080
  * <div class="fg-item-inner">
3081
  * <a class="fg-thumb" href="[item.href]">
3082
  * <img class="fg-image" width="[item.width]" height="[item.height]"
3083
+ * title="[item.title]" alt="[item.description]"
3084
+ * data-src="[item.src]"
3085
+ * data-srcset="[item.srcset]" />
3086
  * <!-- Optional caption markup -->
3087
  * <div class="fg-caption">
3088
+ * <div class="fg-caption-inner">
3089
+ * <div class="fg-caption-title">[item.title]</div>
3090
+ * <div class="fg-caption-desc">[item.description]</div>
3091
+ * </div>
3092
  * </div>
3093
  * </a>
3094
  * </div>
3096
  * <!-- Any number of additional items -->
3097
  * </div>
3098
  * <script>
3099
+ * jQuery(function($){
3100
  * $("#gallery-1").foogallery();
3101
  * });
3102
  * </script>
3103
  * @example {@caption Options can be supplied directly to the `.foogallery()` method or by supplying them using the `data-foogallery` attribute. If supplied using the attribute the value must follow [valid JSON syntax](http://en.wikipedia.org/wiki/JSON#Data_types.2C_syntax_and_example) including quoted property names. If the same option is supplied in both locations as it is below, the value from the attribute overrides the value supplied to the method, in this case `lazy` would be `true`.}{@lang html}
3104
  * <!-- Supplying the options using the attribute -->
3105
  * <div id="gallery-1" class="foogallery fg-responsive" data-foogallery='{"lazy": true}'>
3106
+ * <!-- Items -->
3107
  * </div>
3108
  * <script>
3109
+ * jQuery(function($){
3110
  * // Supply the options directly to the method
3111
  * $("#gallery-1").foogallery({
3112
  * lazy: false
3114
  * });
3115
  * </script>
3116
  */
3117
+ $.fn.foogallery = function (options, ready) {
3118
+ return this.each(function (i, element) {
3119
+ if (_is.string(options)) {
3120
  var template = $.data(element, _.dataTemplate);
3121
+ if (template instanceof _.Template) {
3122
+ switch (options) {
3123
  case "layout":
3124
  template.layout();
3125
  return;
3129
  }
3130
  }
3131
  } else {
3132
+ _.template.make(options, element).initialize().then(function (template) {
3133
+ if (_is.fn(ready)) {
3134
  ready(template);
3135
  }
3136
  });
3151
  */
3152
 
3153
  })(
3154
+ FooGallery.$,
3155
+ FooGallery,
3156
+ FooGallery.utils,
3157
+ FooGallery.utils.is,
3158
+ FooGallery.utils.fn
3159
  );
3160
+ (function ($, _, _utils, _is, _fn, _obj) {
3161
 
3162
  _.TemplateFactory = _utils.Factory.extend(/** @lends FooGallery.TemplateFactory */{
3163
  /**
3169
  * @borrows FooGallery.utils.Class.extend as extend
3170
  * @borrows FooGallery.utils.Class.override as override
3171
  */
3172
+ construct: function () {
3173
  /**
3174
  * @summary An object containing all registered galleries.
3175
  * @memberof FooGallery.TemplateFactory#
3207
  * @param {number} [priority=0] - This determines the index for the class when using either the {@link FooGallery.TemplateFactory#load|load} or {@link FooGallery.TemplateFactory#names|names} methods, a higher value equals a lower index.
3208
  * @returns {boolean} `true` if the `klass` was successfully registered.
3209
  */
3210
+ register: function (name, template, options, classes, il8n, priority) {
3211
  var self = this, result = self._super(name, template, priority);
3212
+ if (result) {
3213
  var reg = self.registered;
3214
  reg[name].opt = _is.hash(options) ? options : {};
3215
  reg[name].cls = _is.hash(classes) ? classes : {};
3225
  * @param {(jQuery|HTMLElement|string)} [element] - The jQuery object, HTMLElement or selector of the template element to create. If not supplied the {@link FooGallery~Options#type|type} options' value is used.
3226
  * @returns {FooGallery.Template}
3227
  */
3228
+ make: function (options, element) {
3229
  element = _is.jq(element) ? element : $(element);
3230
  options = _obj.extend({}, options, element.data("foogallery"));
3231
  var self = this, type = self.type(options, element);
3233
  options = self.options(type, options);
3234
  return self._super(type, options, element);
3235
  },
3236
+ type: function (options, element) {
3237
  element = _is.jq(element) ? element : $(element);
3238
  var self = this, type = _is.hash(options) && _is.hash(options) && _is.string(options.type) && self.contains(options.type) ? options.type : "core";
3239
+ if (type === "core" && element.length > 0) {
3240
  var reg = self.registered, names = self.names(true);
3241
  for (var i = 0, l = names.length; i < l; i++) {
3242
  if (!reg.hasOwnProperty(names[i])) continue;
3251
  }
3252
  return type;
3253
  },
3254
+ configure: function (name, options, classes, il8n) {
3255
  var self = this;
3256
+ if (self.contains(name)) {
3257
  var reg = self.registered;
3258
  _obj.extend(reg[name].opt, options);
3259
  _obj.extend(reg[name].cls, classes);
3260
  _obj.extend(reg[name].il8n, il8n);
3261
  }
3262
  },
3263
+ options: function (name, options) {
3264
+ options = _obj.extend({type: name}, options);
3265
  var self = this, reg = self.registered,
3266
  def = reg["core"].opt,
3267
  cls = reg["core"].cls,
3269
 
3270
  if (!_is.hash(options.cls)) options.cls = {};
3271
  if (!_is.hash(options.il8n)) options.il8n = {};
3272
+ if (!_is.undef(_.filtering)) options = _.filtering.merge(options);
3273
+ if (!_is.undef(_.paging)) options = _.paging.merge(options);
3274
+
3275
+ if (name !== "core" && self.contains(name)) {
3276
  options = _obj.extend({}, def, reg[name].opt, options);
3277
+ options.cls = _obj.extend({}, cls, reg[name].cls, options.cls);
3278
+ options.il8n = _obj.extend({}, il8n, reg[name].il8n, options.il8n);
3279
  } else {
3280
  options = _obj.extend({}, def, options);
3281
+ options.cls = _obj.extend({}, cls, options.cls);
3282
+ options.il8n = _obj.extend({}, il8n, options.il8n);
3283
  }
3284
  return options;
3285
  }
3375
  def_cls = reg["default"].cls,
3376
  def_il8n = reg["default"].il8n,
3377
  opt = _is.hash(options.paging) ? options.paging : {},
3378
+ cls = _is.hash(options.cls) && _is.hash(options.cls.paging) ? _obj.extend({}, options.cls.paging) : {},
3379
+ il8n = _is.hash(options.il8n) && _is.hash(options.il8n.paging) ? _obj.extend({}, options.il8n.paging) : {};
3380
 
3381
  if (!_is.hash(options.cls)) options.cls = {};
3382
  if (!_is.hash(options.il8n)) options.il8n = {};
3445
  FooGallery.utils.fn,
3446
  FooGallery.utils.obj
3447
  );
3448
+ (function ($, _, _utils, _is, _fn, _str) {
3449
+
3450
+ var instance = 0;
3451
 
3452
  _.Template = _utils.Class.extend(/** @lends FooGallery.Template */{
3453
  /**
3460
  * @borrows FooGallery.utils.Class.extend as extend
3461
  * @borrows FooGallery.utils.Class.override as override
3462
  */
3463
+ construct: function (options, element) {
3464
  var self = this;
3465
+ /**
3466
+ * @summary An instance specific namespace to use when binding events to global objects that could be shared across multiple galleries.
3467
+ * @memberof FooGallery.Template#
3468
+ * @name namespace
3469
+ * @type {string}
3470
+ */
3471
+ self.namespace = ".foogallery-" + (++instance);
3472
  /**
3473
  * @summary The jQuery object for the template container.
3474
  * @memberof FooGallery.Template#
3497
  * @type {string}
3498
  */
3499
  self.id = self.$el.prop("id") || options.id;
 
 
 
 
 
 
 
3500
  /**
3501
  * @summary The CSS classes for the template.
3502
  * @memberof FooGallery.Template#
3531
  * @name pages
3532
  * @type {?FooGallery.Paging}
3533
  */
3534
+ self.pages = !_is.undef(_.paging) ? _.paging.make(options.paging.type, self) : null;
3535
+ /**
3536
+ * @summary The page manager for the template.
3537
+ * @memberof FooGallery.Template#
3538
+ * @name filter
3539
+ * @type {?FooGallery.Filtering}
3540
+ */
3541
+ self.filter = !_is.undef(_.filtering) ? _.filtering.make(options.filtering.type, self) : null;
3542
  /**
3543
  * @summary The state manager for the template.
3544
  * @memberof FooGallery.Template#
3558
  self.initialized = false;
3559
  self.destroying = false;
3560
  self.destroyed = false;
3561
+ self._undo = {
3562
+ classes: "",
3563
+ style: "",
3564
+ create: false,
3565
+ children: false
3566
+ };
3567
  },
3568
 
3569
  // ################
3582
  * @fires FooGallery.Template~"post-init.foogallery"
3583
  * @fires FooGallery.Template~"ready.foogallery"
3584
  */
3585
+ initialize: function (parent) {
3586
  var self = this;
3587
  if (_is.promise(self._initialize)) return self._initialize;
3588
  parent = _is.jq(parent) ? parent : $(parent);
3589
+ return self._initialize = $.Deferred(function (def) {
3590
  self.initializing = true;
3591
+ if (parent.length === 0 && self.$el.parent().length === 0) {
3592
  def.reject("A parent element is required.");
3593
  return;
3594
  }
3595
+ if (self.$el.length === 0) {
3596
  self.$el = self.create();
3597
+ self._undo.create = true;
3598
  }
3599
+ if (parent.length > 0) {
3600
  self.$el.appendTo(parent);
3601
  }
3602
  var queue = $.Deferred(), promise = queue.promise(), existing;
3603
+ if (self.$el.length > 0 && (existing = self.$el.data(_.dataTemplate)) instanceof _.Template) {
3604
+ promise = promise.then(function () {
3605
+ return existing.destroy().then(function () {
3606
  self.$el.data(_.dataTemplate, self);
3607
  });
3608
  });
3609
  } else {
3610
  self.$el.data(_.dataTemplate, self);
3611
  }
3612
+ promise.then(function () {
3613
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3614
  // at this point we have our container element free of pre-existing instances so let's bind any event listeners supplied by the .on option
3615
+ if (!_is.empty(self.opt.on)) {
3616
  self.$el.on(self.opt.on);
3617
  }
3618
+ self._undo.classes = self.$el.attr("class");
3619
+ self._undo.style = self.$el.attr("style");
3620
+
3621
+ // ensure the container has it's required CSS classes
3622
+ if (!self.$el.is(self.sel.container)) {
3623
+ self.$el.addClass(self.cls.container);
3624
+ }
3625
+ var selector = _utils.selectify(self.opt.classes);
3626
+ if (selector != null && !self.$el.is(selector)) {
3627
+ self.$el.addClass(self.opt.classes);
3628
+ }
3629
+
3630
+ // if the container currently has no children make them
3631
+ if (self.$el.children().length == 0) {
3632
+ self.$el.append(self.createChildren());
3633
+ self._undo.children = true;
3634
+ }
3635
 
3636
  /**
3637
  * @summary Raised before the template is fully initialized.
3663
  */
3664
  var e = self.raise("pre-init");
3665
  if (e.isDefaultPrevented()) return _fn.rejectWith("pre-init default prevented");
3666
+ }).then(function () {
3667
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3668
  // checks the delay option and if it is greater than 0 waits for that amount of time before continuing
3669
  if (self.opt.delay <= 0) return _fn.resolved;
3670
+ return $.Deferred(function (wait) {
3671
  self._delay = setTimeout(function () {
3672
  self._delay = null;
3673
  wait.resolve();
3674
  }, self.opt.delay);
3675
  }).promise();
3676
+ }).then(function () {
3677
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3678
  /**
3679
  * @summary Raised before the template is initialized but after any pre-initialization work is complete.
3719
  var e = self.raise("init");
3720
  if (e.isDefaultPrevented()) return _fn.rejectWith("init default prevented");
3721
  return self.items.fetch();
3722
+ }).then(function () {
3723
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3724
  /**
3725
  * @summary Raised after the template is initialized but before any post-initialization work is complete.
3766
  if (e.isDefaultPrevented()) return _fn.rejectWith("post-init default prevented");
3767
  var state = self.state.parse();
3768
  self.state.set(_is.empty(state) ? self.state.initial() : state);
3769
+ $(window).on("scroll" + self.namespace, {self: self}, self.throttle(self.onWindowScroll, self.opt.throttle))
3770
+ .on("popstate" + self.namespace, {self: self}, self.onWindowPopState);
3771
+ }).then(function () {
3772
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3773
  /**
3774
  * @summary Raised after the template is fully initialized but before the first load occurs.
3788
  */
3789
  self.raise("first-load");
3790
  return self.loadAvailable();
3791
+ }).then(function () {
3792
  if (self.destroying) return _fn.rejectWith("destroy in progress");
3793
  self.initializing = false;
3794
  self.initialized = true;
3818
  */
3819
  self.raise("ready");
3820
  def.resolve(self);
3821
+ }).fail(function (err) {
3822
  def.reject(err);
3823
  });
3824
  queue.resolve();
3825
+ }).promise().fail(function (err) {
3826
  console.log("initialize failed", self, err);
3827
  self.destroy();
3828
  });
3837
  * <div id="{options.id}" class="{options.cls.container} {options.classes}">
3838
  * </div>
3839
  */
3840
+ create: function () {
3841
  var self = this;
3842
  return $("<div/>", {"id": self.id, "class": self.cls.container}).addClass(self.opt.classes);
3843
  },
3844
+ /**
3845
+ * @summary Create any container child elements for the template returning the jQuery object.
3846
+ * @memberof FooGallery.Template#
3847
+ * @function createChildren
3848
+ * @returns {(jQuery|jQuery[]|HTMLElement|HTMLElement[])} A jQuery object to use as the container for the template.
3849
+ * @description This method is called just prior to the {@link FooGallery.Template~"preinit.foogallery"|preinit} event if the container element has no children to allow templates to add any markup required.
3850
+ */
3851
+ createChildren: function () {
3852
+ return $();
3853
+ },
3854
 
3855
  // #############
3856
  // ## Destroy ##
3864
  * @description Once this method is called it can not be stopped and the template will be destroyed.
3865
  * @fires FooGallery.Template~"destroy.foogallery"
3866
  */
3867
+ destroy: function () {
3868
  var self = this;
3869
  if (self.destroyed) return _fn.resolved;
3870
  self.destroying = true;
3871
+ return $.Deferred(function (def) {
3872
+ if (self.initializing && _is.promise(self._initialize)) {
3873
+ self._initialize.always(function () {
3874
  self.destroying = false;
3875
  self._destroy();
3876
  def.resolve();
3888
  * @function _destroy
3889
  * @private
3890
  */
3891
+ _destroy: function () {
3892
  var self = this;
3893
  if (self.destroyed) return;
3894
  /**
3907
  * });
3908
  */
3909
  self.raise("destroy");
3910
+ $(window).off(self.namespace);
 
3911
  self.state.destroy();
3912
+ if (self.filter) self.filter.destroy();
3913
  if (self.pages) self.pages.destroy();
3914
  self.items.destroy();
3915
+ if (!_is.empty(self.opt.on)) {
3916
  self.$el.off(self.opt.on);
3917
  }
3918
  /**
3932
  */
3933
  self.raise("destroyed");
3934
  self.$el.removeData(_.dataTemplate);
3935
+
3936
+ if (_is.empty(self._undo.classes)) self.$el.removeAttr("class");
3937
+ else self.$el.attr("class", self._undo.classes);
3938
+
3939
+ if (_is.empty(self._undo.style)) self.$el.removeAttr("style");
3940
+ else self.$el.attr("style", self._undo.style);
3941
+
3942
+ if (self._undo.children) {
3943
+ self.$el.empty();
3944
+ }
3945
+ if (self._undo.create) {
3946
  self.$el.remove();
3947
  }
3948
  self.$el = self.state = self.items = self.pages = null;
3955
  // ## Load Items ##
3956
  // ################
3957
 
3958
+ /**
3959
+ * @summary Gets all available items.
3960
+ * @description This takes into account if paging is enabled and will return only the current pages' items.
3961
+ * @memberof FooGallery.Template#
3962
+ * @function getAvailable
3963
+ * @returns {FooGallery.Item[]} An array of {@link FooGallery.Item|items}.
3964
+ */
3965
+ getAvailable: function () {
3966
+ return this.pages ? this.pages.available() : this.items.available();
3967
+ },
3968
+
3969
  /**
3970
  * @summary Check if any available items need to be loaded and loads them.
3971
  * @memberof FooGallery.Template#
3972
  * @function loadAvailable
3973
  * @returns {Promise<FooGallery.Item[]>} Resolves with an array of {@link FooGallery.Item|items} as the first argument. If no items are loaded this array is empty.
3974
  */
3975
+ loadAvailable: function () {
3976
+ return this.items.load(this.getAvailable());
 
 
 
 
 
 
3977
  },
3978
 
3979
  /**
3982
  * @function _check
3983
  * @private
3984
  */
3985
+ _check: function (delay) {
3986
  delay = _is.number(delay) ? delay : 0;
3987
  var self = this;
3988
+ setTimeout(function () {
3989
+ if (self.initialized && (!self.destroying || !self.destroyed)) {
3990
  self.loadAvailable();
3991
  }
3992
  }, delay);
4015
  if (!_is.string(eventName) || _is.empty(eventName)) return null;
4016
  args = _is.array(args) ? args : [];
4017
  var self = this,
4018
+ name = eventName.split(".")[0],
4019
+ listener = _str.camel("on-" + name),
4020
+ event = $.Event(name + ".foogallery");
4021
  args.unshift(self); // add self
4022
  self.$el.trigger(event, args);
4023
  _.debug.logf("{id}|{name}:", {id: self.id, name: name}, args);
4024
+ if (_is.fn(self[listener])) {
4025
  args.unshift(event); // add event
4026
  self[listener].apply(self.$el.get(0), args);
4027
  }
4028
  return event;
4029
  },
4030
 
4031
+ layout: function () {
4032
  var self = this;
4033
  if (self._initialize === null) return;
4034
  /**
4058
  * @param {number} wait - The number of milliseconds to wait before allowing execution.
4059
  * @returns {Function}
4060
  */
4061
+ throttle: function (fn, wait) {
4062
  var time = Date.now();
4063
+ return function () {
4064
  if ((time + wait - Date.now()) < 0) {
4065
  var args = _fn.arg2arr(arguments);
4066
  fn.apply(this, args);
4080
  * @param {jQuery.Event} e - The jQuery.Event object for the event.
4081
  * @private
4082
  */
4083
+ onWindowPopState: function (e) {
4084
  var self = e.data.self, state = e.originalEvent.state;
4085
+ if (!_is.empty(state) && state.id === self.id) {
4086
  self.state.set(state);
4087
  self.loadAvailable();
4088
  }
4094
  * @param {jQuery.Event} e - The jQuery.Event object for the event.
4095
  * @private
4096
  */
4097
+ onWindowScroll: function (e) {
4098
  var self = e.data.self;
4099
  self.loadAvailable();
4100
  }
4116
  template: {}
4117
  }, {
4118
  container: "foogallery"
4119
+ }, {}, -100);
 
 
4120
 
4121
  /**
4122
  * @summary An object containing all the core template options.
4163
  */
4164
 
4165
  })(
4166
+ FooGallery.$,
4167
+ FooGallery,
4168
+ FooGallery.utils,
4169
+ FooGallery.utils.is,
4170
+ FooGallery.utils.fn,
4171
+ FooGallery.utils.str
4172
  );
 
 
4173
  (function(_, _utils){
4174
 
4175
  _.Component = _utils.Class.extend(/** @lend FooGallery.Component */{
4432
  */
4433
  initial: function(){
4434
  var self = this, tmpl = self.tmpl, state = {};
4435
+ if (tmpl.filter && !_is.empty(tmpl.filter.current)) state.f = tmpl.filter.current;
4436
  if (tmpl.pages && tmpl.pages.current > 1) state.p = tmpl.pages.current;
4437
  return state;
4438
  },
4447
  get: function(item){
4448
  var self = this, tmpl = self.tmpl, state = {};
4449
  if (item instanceof _.Item) state.i = item.id;
4450
+ if (tmpl.filter && !_is.empty(tmpl.filter.current)){
4451
+ state.f = tmpl.filter.current;
4452
+ }
4453
  if (tmpl.pages && tmpl.pages.isValid(tmpl.pages.current)){
4454
  state.p = tmpl.pages.current;
4455
  }
4467
  if (_is.hash(state)){
4468
  tmpl.items.reset();
4469
  var item = tmpl.items.get(state.i);
4470
+ if (tmpl.filter){
4471
+ tmpl.filter.rebuild();
4472
+ var tags = !_is.empty(state.f) ? state.f : [];
4473
+ tmpl.filter.set(tags, false);
4474
+ }
4475
  if (tmpl.pages){
4476
  tmpl.pages.rebuild();
4477
  var page = tmpl.pages.number(state.p);
4525
  * @summary An object used to store the state of a template.
4526
  * @typedef {object} FooGallery~State
4527
  * @property {number} [p] - The current page number.
4528
+ * @property {string[]} [f] - The current filter array.
4529
  * @property {?string} [i] - The currently selected item.
4530
  */
4531
 
4535
  FooGallery.utils.is,
4536
  FooGallery.utils.str
4537
  );
4538
+ (function ($, _, _utils, _is, _fn, _obj) {
4539
 
4540
  _.Item = _.Component.extend(/** @lends FooGallery.Item */{
4541
  /**
4548
  * @borrows FooGallery.utils.Class.extend as extend
4549
  * @borrows FooGallery.utils.Class.override as override
4550
  */
4551
+ construct: function (template, options) {
4552
  var self = this;
4553
  /**
4554
  * @ignore
4559
  self.cls = template.cls.item;
4560
  self.il8n = template.il8n.item;
4561
  self.sel = template.sel.item;
4562
+ self.opt = _obj.extend({}, template.opt.item, options);
4563
+
4564
  /**
4565
  * @summary Whether or not the items' elements are appended to the template.
4566
  * @memberof FooGallery.Item#
4640
  */
4641
  self.$caption = null;
4642
 
4643
+ /**
4644
+ * @memberof FooGallery.Item#
4645
+ * @name type
4646
+ * @type {string}
4647
+ */
4648
+ self.type = self.opt.type;
4649
  /**
4650
  * @memberof FooGallery.Item#
4651
  * @name id
4652
  * @type {string}
4653
  */
4654
+ self.id = self.opt.id;
4655
  /**
4656
  * @memberof FooGallery.Item#
4657
  * @name href
4658
  * @type {string}
4659
  */
4660
+ self.href = self.opt.href;
4661
  /**
4662
  * @memberof FooGallery.Item#
4663
  * @name src
4664
  * @type {string}
4665
  */
4666
+ self.src = self.opt.src;
4667
  /**
4668
  * @memberof FooGallery.Item#
4669
  * @name srcset
4670
  * @type {string}
4671
  */
4672
+ self.srcset = self.opt.srcset;
4673
  /**
4674
  * @memberof FooGallery.Item#
4675
  * @name width
4676
  * @type {number}
4677
  */
4678
+ self.width = self.opt.width;
4679
  /**
4680
  * @memberof FooGallery.Item#
4681
  * @name height
4682
  * @type {number}
4683
  */
4684
+ self.height = self.opt.height;
4685
  /**
4686
  * @memberof FooGallery.Item#
4687
  * @name title
4688
  * @type {string}
4689
  */
4690
+ self.title = self.opt.title;
4691
  /**
4692
  * @memberof FooGallery.Item#
4693
  * @name alt
4694
  * @type {string}
4695
  */
4696
+ self.alt = self.opt.alt;
4697
  /**
4698
  * @memberof FooGallery.Item#
4699
  * @name caption
4700
  * @type {string}
4701
  */
4702
+ self.caption = _is.empty(self.opt.caption) ? self.title : self.opt.caption;
4703
  /**
4704
  * @memberof FooGallery.Item#
4705
  * @name description
4706
  * @type {string}
4707
  */
4708
+ self.description = _is.empty(self.opt.description) ? self.alt : self.opt.description;
4709
  /**
4710
  * @memberof FooGallery.Item#
4711
  * @name attrItem
4712
  * @type {FooGallery.Item~Attributes}
4713
  */
4714
+ self.attr = self.opt.attr;
4715
  /**
4716
  * @memberof FooGallery.Item#
4717
  * @name tags
4718
  * @type {string[]}
4719
  */
4720
+ self.tags = self.opt.tags;
4721
  /**
4722
  * @memberof FooGallery.Item#
4723
  * @name maxWidth
4724
  * @type {?FooGallery.Item~maxWidthCallback}
4725
  */
4726
+ self.maxWidth = self.opt.maxWidth;
4727
  /**
4728
  * @memberof FooGallery.Item#
4729
  * @name maxCaptionLength
4730
  * @type {number}
4731
  */
4732
+ self.maxCaptionLength = self.opt.maxCaptionLength;
4733
  /**
4734
  * @memberof FooGallery.Item#
4735
  * @name maxDescriptionLength
4736
  * @type {number}
4737
  */
4738
+ self.maxDescriptionLength = self.opt.maxDescriptionLength;
4739
  /**
4740
  * @memberof FooGallery.Item#
4741
  * @name showCaptionTitle
4742
  * @type {boolean}
4743
  */
4744
+ self.showCaptionTitle = self.opt.showCaptionTitle;
4745
  /**
4746
  * @memberof FooGallery.Item#
4747
  * @name showCaptionDescription
4748
  * @type {boolean}
4749
  */
4750
+ self.showCaptionDescription = self.opt.showCaptionDescription;
4751
  /**
4752
  * @summary The cached result of the last call to the {@link FooGallery.Item#getThumbUrl|getThumbUrl} method.
4753
  * @memberof FooGallery.Item#
4756
  * @private
4757
  */
4758
  self._thumbUrl = null;
4759
+ /**
4760
+ * @summary The placeholder url for this item generated by calling the {@link FooGallery.Items#placeholder|placeholder} method.
4761
+ * @memberof FooGallery.Item#
4762
+ * @name _placeholder
4763
+ * @type {string}
4764
+ * @private
4765
+ */
4766
+ self._placeholder = null;
4767
  /**
4768
  * @summary This property is used to store the promise created when loading an item for the first time.
4769
  * @memberof FooGallery.Item#
4772
  * @private
4773
  */
4774
  self._load = null;
4775
+ /**
4776
+ * @summary This property is used to store the init state of an item the first time it is parsed and is used to reset state during destroy.
4777
+ * @memberof FooGallery.Item#
4778
+ * @name _undo
4779
+ * @type {object}
4780
+ * @private
4781
+ */
4782
+ self._undo = {
4783
+ classes: "",
4784
+ style: "",
4785
+ loader: false,
4786
+ placeholder: false
4787
+ };
4788
  },
4789
  /**
4790
  * @summary Destroy the item preparing it for garbage collection.
4791
  * @memberof FooGallery.Item#
4792
  * @function destroy
4793
  */
4794
+ destroy: function () {
4795
  var self = this;
4796
  /**
4797
  * @summary Raised when a template destroys an item.
4837
  * });
4838
  */
4839
  var e = self.tmpl.raise("destroy-item");
4840
+ if (!e.isDefaultPrevented()) {
4841
+ self.doDestroyItem();
 
 
 
 
4842
  self._super();
4843
  }
4844
  return self.tmpl === null;
4845
  },
4846
+ /**
4847
+ * @summary Performs the actual destroy logic for the item.
4848
+ * @memberof FooGallery.Item#
4849
+ * @function doDestroyItem
4850
+ */
4851
+ doDestroyItem: function () {
4852
+ var self = this;
4853
+ if (self.isParsed) {
4854
+ self.append();
4855
+ if (_is.empty(self._undo.classes)) self.$el.removeAttr("class");
4856
+ else self.$el.attr("class", self._undo.classes);
4857
+
4858
+ if (_is.empty(self._undo.style)) self.$el.removeAttr("style");
4859
+ else self.$el.attr("style", self._undo.style);
4860
+
4861
+ if (self._undo.loader) {
4862
+ self.$el.find(self.sel.loader).remove();
4863
+ }
4864
+ if (self._undo.placeholder && self.$image.prop("src") == self._placeholder) {
4865
+ self.$image.removeAttr("src");
4866
+ }
4867
+ } else if (self.isCreated) {
4868
+ self.detach();
4869
+ self.$el.remove();
4870
+ }
4871
+ },
4872
  /**
4873
  * @summary Parse the supplied element updating the current items' properties.
4874
  * @memberof FooGallery.Item#
4878
  * @fires FooGallery.Template~"parse-item.foogallery"
4879
  * @fires FooGallery.Template~"parsed-item.foogallery"
4880
  */
4881
+ parse: function (element) {
4882
+ var self = this, $el = $(element);
4883
  /**
4884
  * @summary Raised when an item needs to parse properties from an element.
4885
  * @event FooGallery.Template~"parse-item.foogallery"
4923
  * });
4924
  */
4925
  var e = self.tmpl.raise("parse-item", [self, $el]);
4926
+ if (!e.isDefaultPrevented() && (self.isCreated = $el.is(self.sel.elem))) {
4927
+ self.isParsed = self.doParseItem($el);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4928
  self.fix();
 
4929
  // We don't load the attributes when parsing as they are only ever used to create an item and if you're parsing it's already created.
4930
  }
4931
+ if (self.isParsed) {
4932
  /**
4933
  * @summary Raised after an item has been parsed from an element.
4934
  * @event FooGallery.Template~"parsed-item.foogallery"
4950
  }
4951
  return self.isParsed;
4952
  },
4953
+ /**
4954
+ * @summary Performs the actual parse logic for the item.
4955
+ * @memberof FooGallery.Item#
4956
+ * @function doParseItem
4957
+ * @param {jQuery} $el - The jQuery element to parse.
4958
+ * @returns {boolean}
4959
+ */
4960
+ doParseItem: function ($el) {
4961
+ var self = this, o = self.tmpl.opt, cls = self.cls, sel = self.sel;
4962
+
4963
+ self._undo.classes = $el.attr("class") || "";
4964
+ self._undo.style = $el.attr("style") || "";
4965
+
4966
+ self.$el = $el.data(_.dataItem, self);
4967
+ self.$inner = self.$el.find(sel.inner);
4968
+ self.$anchor = self.$el.find(sel.anchor).on("click.foogallery", {self: self}, self.onAnchorClick);
4969
+ self.$image = self.$anchor.find(sel.image);
4970
+ self.$caption = self.$el.find(sel.caption.elem).on("click.foogallery", {self: self}, self.onCaptionClick);
4971
+ self.isAttached = self.$el.parent().length > 0;
4972
+ self.isLoading = self.$el.is(sel.loading);
4973
+ self.isLoaded = self.$el.is(sel.loaded);
4974
+ self.isError = self.$el.is(sel.error);
4975
+ self.id = self.$anchor.data("id") || self.id;
4976
+ self.tags = self.$anchor.data("tags") || self.tags;
4977
+ self.href = self.$anchor.attr("href") || self.href;
4978
+ self.src = self.$image.attr(o.src) || self.src;
4979
+ self.srcset = self.$image.attr(o.srcset) || self.srcset;
4980
+ self.width = parseInt(self.$image.attr("width")) || self.width;
4981
+ self.height = parseInt(self.$image.attr("height")) || self.height;
4982
+ self.title = self.$image.attr("title") || self.title;
4983
+ self.alt = self.$image.attr("alt") || self.alt;
4984
+ self.caption = self.$anchor.data("title") || self.$anchor.data("captionTitle") || self.caption || self.title;
4985
+ self.description = self.$anchor.data("description") || self.$anchor.data("captionDesc") || self.description || self.alt;
4986
+ // if the caption or description are not set yet try fetching it from the html
4987
+ if (_is.empty(self.caption)) self.caption = $.trim(self.$caption.find(sel.caption.title).html());
4988
+ if (_is.empty(self.description)) self.description = $.trim(self.$caption.find(sel.caption.description).html());
4989
+ // enforce the max lengths for the caption and description
4990
+ if (_is.number(self.maxCaptionLength) && self.maxCaptionLength > 0 && !_is.empty(self.caption) && _is.string(self.caption) && self.caption.length > self.maxCaptionLength) {
4991
+ self.$caption.find(sel.caption.title).html(self.caption.substr(0, self.maxCaptionLength) + "&hellip;");
4992
+ }
4993
+ if (_is.number(self.maxDescriptionLength) && self.maxDescriptionLength > 0 && !_is.empty(self.description) && _is.string(self.description) && self.description.length > self.maxDescriptionLength) {
4994
+ self.$caption.find(sel.caption.description).html(self.description.substr(0, self.maxDescriptionLength) + "&hellip;");
4995
+ }
4996
+ // check if the item has a loader
4997
+ if (self.$el.find(sel.loader).length === 0) {
4998
+ self.$el.append($("<div/>", {"class": cls.loader}));
4999
+ self._undo.loader = true;
5000
+ }
5001
+ // if the image has no src url then set the placeholder
5002
+ if (_is.empty(self.$image.prop("src"))) {
5003
+ self._placeholder = self.tmpl.items.placeholder(self.width, self.height);
5004
+ self.$image.prop("src", self._placeholder);
5005
+ self._undo.placeholder = true;
5006
+ }
5007
+ if (self.isCreated && self.isAttached && !self.isLoading && !self.isLoaded && !self.isError) {
5008
+ self.$el.addClass(cls.idle);
5009
+ }
5010
+ return true;
5011
+ },
5012
  /**
5013
  * @summary Create the items' DOM elements and populate the corresponding properties.
5014
  * @memberof FooGallery.Item#
5017
  * @fires FooGallery.Template~"create-item.foogallery"
5018
  * @fires FooGallery.Template~"created-item.foogallery"
5019
  */
5020
+ create: function () {
5021
  var self = this;
5022
+ if (!self.isCreated && _is.string(self.href) && _is.string(self.src) && _is.number(self.width) && _is.number(self.height)) {
5023
  /**
5024
  * @summary Raised when an item needs to create its' elements.
5025
  * @event FooGallery.Template~"create-item.foogallery"
5062
  * });
5063
  */
5064
  var e = self.tmpl.raise("create-item", [self]);
5065
+ if (!e.isDefaultPrevented()) {
5066
+ self.isCreated = self.doCreateItem();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5067
  }
5068
+ if (self.isCreated) {
5069
  /**
5070
  * @summary Raised after an items' elements have been created.
5071
  * @event FooGallery.Template~"created-item.foogallery"
5087
  }
5088
  return self.isCreated;
5089
  },
5090
+ /**
5091
+ * @summary Performs the actual create logic for the item.
5092
+ * @memberof FooGallery.Item#
5093
+ * @function doCreateItem
5094
+ * @returns {boolean}
5095
+ */
5096
+ doCreateItem: function () {
5097
+ var self = this, o = self.tmpl.opt, cls = self.cls, attr = self.attr;
5098
+ attr.elem["class"] = cls.elem + " " + cls.idle;
5099
+
5100
+ attr.inner["class"] = cls.inner;
5101
+
5102
+ attr.anchor["class"] = cls.anchor;
5103
+ attr.anchor["href"] = self.href;
5104
+ attr.anchor["data-id"] = self.id;
5105
+ attr.anchor["data-title"] = self.caption;
5106
+ attr.anchor["data-description"] = self.description;
5107
+ if (!_is.empty(self.tags)) {
5108
+ attr.anchor["data-tags"] = JSON.stringify(self.tags);
5109
+ }
5110
+
5111
+ attr.image["class"] = cls.image;
5112
+ attr.image["src"] = self.tmpl.items.placeholder(self.width, self.height);
5113
+ attr.image[o.src] = self.src;
5114
+ attr.image[o.srcset] = self.srcset;
5115
+ attr.image["width"] = self.width;
5116
+ attr.image["height"] = self.height;
5117
+ attr.image["title"] = self.title;
5118
+ attr.image["alt"] = self.alt;
5119
+
5120
+ self.$el = $("<div/>").attr(attr.elem).data(_.dataItem, self);
5121
+ self.$inner = $("<figure/>").attr(attr.inner).appendTo(self.$el);
5122
+ self.$anchor = $("<a/>").attr(attr.anchor).appendTo(self.$inner).on("click.foogallery", {self: self}, self.onAnchorClick);
5123
+ self.$image = $("<img/>").attr(attr.image).appendTo(self.$anchor);
5124
+
5125
+ cls = self.cls.caption;
5126
+ attr = self.attr.caption;
5127
+ attr.elem["class"] = cls.elem;
5128
+ self.$caption = $("<figcaption/>").attr(attr.elem).on("click.foogallery", {self: self}, self.onCaptionClick);
5129
+ var hasTitle = !_is.empty(self.caption), hasDesc = !_is.empty(self.description);
5130
+ if (hasTitle || hasDesc) {
5131
+ attr.inner["class"] = cls.inner;
5132
+ attr.title["class"] = cls.title;
5133
+ attr.description["class"] = cls.description;
5134
+ var $inner = $("<div/>").attr(attr.inner).appendTo(self.$caption);
5135
+ if (hasTitle) {
5136
+ var $title;
5137
+ // enforce the max length for the caption
5138
+ if (_is.number(self.maxCaptionLength) && self.maxCaptionLength > 0 && _is.string(self.caption) && self.caption.length > self.maxCaptionLength) {
5139
+ $title = $("<div/>").attr(attr.title).html(self.caption.substr(0, self.maxCaptionLength) + "&hellip;");
5140
+ } else {
5141
+ $title = $("<div/>").attr(attr.title).html(self.caption);
5142
+ }
5143
+ $inner.append($title);
5144
+ }
5145
+ if (hasDesc) {
5146
+ var $desc;
5147
+ // enforce the max length for the description
5148
+ if (_is.number(self.maxDescriptionLength) && self.maxDescriptionLength > 0 && _is.string(self.description) && self.description.length > self.maxDescriptionLength) {
5149
+ $desc = $("<div/>").attr(attr.description).html(self.description.substr(0, self.maxDescriptionLength) + "&hellip;");
5150
+ } else {
5151
+ $desc = $("<div/>").attr(attr.description).html(self.description);
5152
+ }
5153
+ $inner.append($desc);
5154
+ }
5155
+ }
5156
+ self.$caption.appendTo(self.$inner);
5157
+ // check if the item has a loader
5158
+ if (self.$el.find(self.sel.loader).length === 0) {
5159
+ self.$el.append($("<div/>", {"class": self.cls.loader}));
5160
+ }
5161
+ return true;
5162
+ },
5163
  /**
5164
  * @summary Append the item to the current template.
5165
  * @memberof FooGallery.Item#
5168
  * @fires FooGallery.Template~"append-item.foogallery"
5169
  * @fires FooGallery.Template~"appended-item.foogallery"
5170
  */
5171
+ append: function () {
5172
  var self = this;
5173
+ if (self.isCreated && !self.isAttached) {
5174
  /**
5175
  * @summary Raised when an item needs to append its elements to the template.
5176
  * @event FooGallery.Template~"append-item.foogallery"
5213
  * });
5214
  */
5215
  var e = self.tmpl.raise("append-item", [self]);
5216
+ if (!e.isDefaultPrevented()) {
5217
  self.tmpl.$el.append(self.$el);
5218
  self.fix();
5219
  self.isAttached = true;
5220
  }
5221
+ if (self.isAttached) {
5222
  /**
5223
  * @summary Raised after an item has appended its' elements to the template.
5224
  * @event FooGallery.Template~"appended-item.foogallery"
5246
  * @function detach
5247
  * @returns {boolean}
5248
  */
5249
+ detach: function () {
5250
  var self = this;
5251
+ if (self.isCreated && self.isAttached) {
5252
  /**
5253
  * @summary Raised when an item needs to detach its' elements from the template.
5254
  * @event FooGallery.Template~"detach-item.foogallery"
5291
  * });
5292
  */
5293
  var e = self.tmpl.raise("detach-item", [self]);
5294
+ if (!e.isDefaultPrevented()) {
5295
  self.$el.detach();
5296
  self.unfix();
5297
  self.isAttached = false;
5298
  }
5299
+ if (!self.isAttached) {
5300
  /**
5301
  * @summary Raised after an item has detached its' elements from the template.
5302
  * @event FooGallery.Template~"detached-item.foogallery"
5324
  * @function load
5325
  * @returns {Promise.<FooGallery.Item>}
5326
  */
5327
+ load: function () {
5328
  var self = this;
5329
  if (_is.promise(self._load)) return self._load;
5330
  if (!self.isCreated || !self.isAttached) return _fn.rejectWith("not created or attached");
5366
  * @function fix
5367
  * @returns {FooGallery.Item}
5368
  */
5369
+ fix: function () {
5370
+ var self = this, e = self.tmpl.raise("fix-item", [self]);
5371
+ if (!e.isDefaultPrevented() && self.isCreated && !self.isLoading && !self.isLoaded && !self.isError) {
5372
  var w = self.width, h = self.height;
5373
  // if we have a base width and height to work with
5374
+ if (!isNaN(w) && !isNaN(h)) {
5375
  // figure out the max image width and calculate the height the image should be displayed as
5376
  var width = _is.fn(self.maxWidth) ? self.maxWidth(self) : self.$image.width();
5377
  if (width <= 0) width = w;
5378
  var ratio = width / w, height = h * ratio;
5379
  // actually set the inline css on the image
5380
+ self.$image.css({width: width, height: height});
5381
  }
5382
  }
5383
  return self;
5388
  * @function unfix
5389
  * @returns {FooGallery.Item}
5390
  */
5391
+ unfix: function () {
5392
+ var self = this, e = self.tmpl.raise("unfix-item", [self]);
5393
+ if (!e.isDefaultPrevented() && self.isCreated) self.$image.css({width: '', height: ''});
5394
  return self;
5395
  },
5396
  /**
5400
  * @param {boolean} [refresh=false] - Whether or not to force refreshing of the cached value.
5401
  * @returns {string}
5402
  */
5403
+ getThumbUrl: function (refresh) {
5404
  refresh = _is.boolean(refresh) ? refresh : false;
5405
  var self = this;
5406
  if (!refresh && _is.string(self._thumbUrl)) return self._thumbUrl;
5411
  * @memberof FooGallery.Item#
5412
  * @function scrollTo
5413
  */
5414
+ scrollTo: function (align) {
5415
  var self = this;
5416
+ if (self.isAttached) {
5417
  var ib = self.bounds(), vb = _utils.getViewportBounds();
5418
+ switch (align) {
5419
  case "top": // attempts to center the item horizontally but aligns the top with the middle of the viewport
5420
  ib.left += (ib.width / 2) - (vb.width / 2);
5421
  ib.top -= (vb.height / 5);
5435
  * @function bounds
5436
  * @returns {?FooGallery.utils.Bounds}
5437
  */
5438
+ bounds: function () {
5439
  return this.isAttached ? _utils.getElementBounds(this.$el) : null;
5440
  },
5441
  /**
5445
  * @param {FooGallery.utils.Bounds} bounds - The bounds to check.
5446
  * @returns {boolean}
5447
  */
5448
+ intersects: function (bounds) {
5449
  return this.isAttached ? this.bounds().intersects(bounds) : false;
5450
  },
5451
  /**
5455
  * @param {jQuery.Event} e - The jQuery.Event object for the click event.
5456
  * @private
5457
  */
5458
+ onAnchorClick: function (e) {
5459
  var self = e.data.self,
5460
+ state = self.tmpl.state.get(self);
5461
  self.tmpl.state.update(state);
5462
  },
5463
  /**
5467
  * @param {jQuery.Event} e - The jQuery.Event object for the click event.
5468
  * @private
5469
  */
5470
+ onCaptionClick: function (e) {
5471
  var self = e.data.self;
5472
+ if ($(e.target).is(self.sel.caption.all) && self.$anchor.length > 0) {
5473
  self.$anchor.get(0).click();
5474
  }
5475
  }
5491
  /**
5492
  * @summary A simple object containing an items' default values.
5493
  * @typedef {object} FooGallery.Item~Options
5494
+ * @property {?string} [type="item"] - The `data-type` attribute for the anchor element.
5495
  * @property {?string} [id=null] - The `data-id` attribute for the outer element.
5496
  * @property {?string} [href=null] - The `href` attribute for the anchor element.
5497
  * @property {?string} [src=null] - The `src` attribute for the image element.
5512
  */
5513
  _.template.configure("core", {
5514
  item: {
5515
+ type: "item",
5516
  id: "",
5517
  href: "",
5518
  src: "",
5560
  description: "fg-caption-desc"
5561
  }
5562
  }
5563
+ }, {
5564
+ item: {}
5565
  });
5566
 
5567
  _.components.register("item", _.Item);
5602
  */
5603
 
5604
  })(
5605
+ FooGallery.$,
5606
+ FooGallery,
5607
+ FooGallery.utils,
5608
+ FooGallery.utils.is,
5609
+ FooGallery.utils.fn,
5610
+ FooGallery.utils.obj
5611
  );
5612
+ (function ($, _, _utils, _is, _fn, _obj) {
5613
 
5614
  _.Items = _.Component.extend(/** @lends FooGallery.Items */{
5615
  /**
5621
  * @borrows FooGallery.utils.Class.extend as extend
5622
  * @borrows FooGallery.utils.Class.override as override
5623
  */
5624
+ construct: function (template) {
5625
  var self = this;
5626
  /**
5627
  * @ignore
5638
  var cls = self.tmpl.cls.item.caption;
5639
  self.tmpl.sel.item.caption.all = _utils.selectify([cls.elem, cls.inner, cls.title, cls.description]);
5640
  },
5641
+ destroy: function () {
5642
  var self = this, items = self.all(), destroyed = [];
5643
  if (items.length > 0) {
5644
  /**
5658
  * });
5659
  */
5660
  self.tmpl.raise("destroy-items", [items]);
5661
+ destroyed = $.map(items, function (item) {
5662
  return item.destroy() ? item : null;
5663
  });
5664
  /**
5686
  self._available = [];
5687
  self._super();
5688
  },
5689
+ fetch: function (refresh) {
5690
  var self = this;
5691
  if (!refresh && _is.promise(self._fetched)) return self._fetched;
5692
  var fg = self.tmpl, selectors = fg.sel,
5693
+ option = fg.opt.items,
5694
+ def = $.Deferred();
5695
 
5696
  var items = self.make(fg.$el.find(selectors.item.elem));
5697
 
5698
+ if (!_is.empty(option)) {
5699
+ if (_is.array(option)) {
5700
  items.push.apply(items, self.make(option));
5701
  def.resolve(items);
5702
+ } else if (_is.string(option)) {
5703
+ $.get(option).then(function (response) {
5704
  items.push.apply(items, self.make(response));
5705
  def.resolve(items);
5706
+ }, function (jqXHR, textStatus, errorThrown) {
5707
  console.log("FooGallery: GET items error.", option, jqXHR, textStatus, errorThrown);
5708
  def.resolve(items);
5709
  });
5714
  items.push.apply(items, self.make(window[fg.id + "-items"]));
5715
  def.resolve(items);
5716
  }
5717
+ def.then(function (items) {
5718
+ self.setAll(items);
 
 
5719
  });
5720
  return self._fetched = def.promise();
5721
  },
5722
+ all: function () {
5723
  return this._arr.slice();
5724
  },
5725
+ count: function (all) {
5726
+ return all ? this.all().length : this.available().length;
5727
+ },
5728
+ available: function () {
5729
  return this._available.slice();
5730
  },
5731
+ get: function (id) {
5732
  return !_is.empty(id) && !!this.idMap[id] ? this.idMap[id] : null;
5733
  },
5734
+ setAll: function (items) {
5735
+ this._arr = _is.array(items) ? items : [];
5736
+ this.idMap = this.createIdMap(items);
5737
+ this._available = this.all();
5738
+ },
5739
+ setAvailable: function (items) {
5740
  this._available = _is.array(items) ? items : [];
5741
  },
5742
+ reset: function () {
5743
  this.setAvailable(this.all());
5744
  },
5745
+ placeholder: function (width, height) {
5746
+ if (this._canvas && this._canvas.toDataURL && _is.number(width) && _is.number(height)) {
5747
  this._canvas.width = width;
5748
  this._canvas.height = height;
5749
  return this._canvas.toDataURL();
5757
  * @param {FooGallery.Item[]} items - The items to filter.
5758
  * @returns {FooGallery.Item[]}
5759
  */
5760
+ loadable: function (items) {
5761
  var self = this, opt = self.tmpl.opt, viewport;
5762
+ if (opt.lazy) {
5763
  viewport = _utils.getViewportBounds(opt.viewport);
5764
  }
5765
+ return _is.array(items) ? $.map(items, function (item) {
5766
+ return item.isCreated && item.isAttached && !item.isLoading && !item.isLoaded && !item.isError && (!opt.lazy || (opt.lazy && item.intersects(viewport))) ? item : null;
5767
+ }) : [];
5768
  },
5769
  /**
5770
  * @summary Filter the supplied `items` and return only those that can be created.
5773
  * @param {FooGallery.Item[]} items - The items to filter.
5774
  * @returns {FooGallery.Item[]}
5775
  */
5776
+ creatable: function (items) {
5777
+ return _is.array(items) ? $.map(items, function (item) {
5778
+ return item instanceof _.Item && !item.isCreated ? item : null;
5779
+ }) : [];
5780
  },
5781
  /**
5782
  * @summary Filter the supplied `items` and return only those that can be appended.
5785
  * @param {FooGallery.Item[]} items - The items to filter.
5786
  * @returns {FooGallery.Item[]}
5787
  */
5788
+ appendable: function (items) {
5789
+ return _is.array(items) ? $.map(items, function (item) {
5790
+ return item instanceof _.Item && item.isCreated && !item.isAttached ? item : null;
5791
+ }) : [];
5792
  },
5793
  /**
5794
  * @summary Filter the supplied `items` and return only those that can be detached.
5797
  * @param {FooGallery.Item[]} items - The items to filter.
5798
  * @returns {FooGallery.Item[]}
5799
  */
5800
+ detachable: function (items) {
5801
+ return _is.array(items) ? $.map(items, function (item) {
5802
+ return item instanceof _.Item && item.isCreated && item.isAttached ? item : null;
5803
+ }) : [];
5804
  },
5805
  /**
5806
  * @summary Get a single jQuery object containing all the supplied items' elements.
5809
  * @param {FooGallery.Item[]} items - The items to get a jQuery object for.
5810
  * @returns {jQuery}
5811
  */
5812
+ jquerify: function (items) {
5813
  return $($.map(items, function (item) {
5814
  return item.$el.get();
5815
  }));
5824
  * @fires FooGallery.Template~"made-items.foogallery"
5825
  * @fires FooGallery.Template~"parsed-items.foogallery"
5826
  */
5827
+ make: function (items) {
5828
  var self = this, made = [];
5829
+ if (_is.jq(items) || _is.array(items)) {
5830
  var parsed = [], arr = $.makeArray(items);
5831
  if (arr.length === 0) return made;
5832
  /**
5858
  * });
5859
  */
5860
  var e = self.tmpl.raise("make-items", [arr]);
5861
+ if (!e.isDefaultPrevented()) {
5862
+ made = $.map(arr, function (obj) {
5863
+ var type = self.type(obj),
5864
+ opt = _obj.extend(_is.hash(obj) ? obj : {}, {type: type});
5865
+ var item = _.components.make(type, self.tmpl, opt);
5866
+ if (_is.element(obj)) {
5867
+ if (item.parse(obj)) {
5868
  parsed.push(item);
5869
  return item;
5870
  }
5912
  }
5913
  return made;
5914
  },
5915
+ type: function (objOrElement) {
5916
+ var type;
5917
+ if (_is.hash(objOrElement)) {
5918
+ type = objOrElement.type;
5919
+ } else if (_is.element(objOrElement)) {
5920
+ type = $(objOrElement).find(this.tmpl.sel.item.anchor).data("type");
5921
+ }
5922
+ return _is.string(type) && _.components.contains(type) ? type : "item";
5923
+ },
5924
  /**
5925
  * @summary Create each of the supplied {@link FooGallery.Item|`items`} elements.
5926
  * @memberof FooGallery.Items#
5934
  * @fires FooGallery.Template~"append-items.foogallery"
5935
  * @fires FooGallery.Template~"appended-items.foogallery"
5936
  */
5937
+ create: function (items, append) {
5938
  var self = this, created = [], creatable = self.creatable(items);
5939
  if (creatable.length > 0) {
5940
  /**
5966
  * });
5967
  */
5968
  var e = self.tmpl.raise("create-items", [creatable]);
5969
+ if (!e.isDefaultPrevented()) {
5970
+ created = $.map(creatable, function (item) {
5971
  return item.create() ? item : null;
5972
  });
5973
  }
6001
  * @fires FooGallery.Template~"append-items.foogallery"
6002
  * @fires FooGallery.Template~"appended-items.foogallery"
6003
  */
6004
+ append: function (items) {
6005
  var self = this, appended = [], appendable = self.appendable(items);
6006
  if (appendable.length > 0) {
6007
  /**
6032
  * });
6033
  */
6034
  var e = self.tmpl.raise("append-items", [appendable]);
6035
+ if (!e.isDefaultPrevented()) {
6036
+ appended = $.map(appendable, function (item) {
6037
  return item.append() ? item : null;
6038
  });
6039
  }
6066
  * @fires FooGallery.Template~"detach-items.foogallery"
6067
  * @fires FooGallery.Template~"detached-items.foogallery"
6068
  */
6069
+ detach: function (items) {
6070
  var self = this, detached = [], detachable = self.detachable(items);
6071
  if (detachable.length > 0) {
6072
  /**
6097
  * });
6098
  */
6099
  var e = self.tmpl.raise("detach-items", [detachable]);
6100
+ if (!e.isDefaultPrevented()) {
6101
+ detached = $.map(detachable, function (item) {
6102
  return item.detach() ? item : null;
6103
  });
6104
  }
6131
  * @fires FooGallery.Template~"load-items.foogallery"
6132
  * @fires FooGallery.Template~"loaded-items.foogallery"
6133
  */
6134
+ load: function (items) {
6135
  var self = this;
6136
  items = self.loadable(items);
6137
+ if (items.length > 0) {
6138
  /**
6139
  * @summary Raised before the template loads any items.
6140
  * @event FooGallery.Template~"load-items.foogallery"
6163
  * });
6164
  */
6165
  var e = self.tmpl.raise("load-items", [items]);
6166
+ if (!e.isDefaultPrevented()) {
6167
+ var loading = $.map(items, function (item) {
6168
  return item.load();
6169
  });
6170
+ return _fn.when(loading).done(function (loaded) {
6171
  /**
6172
  * @summary Raised after the template has loaded items.
6173
  * @event FooGallery.Template~"loaded-items.foogallery"
6190
  }
6191
  return _fn.resolveWith([]);
6192
  },
6193
+ createIdMap: function (items) {
6194
  var map = {};
6195
+ $.each(items, function (i, item) {
6196
  if (_is.empty(item.id)) item.id = "" + (i + 1);
6197
  map[item.id] = item;
6198
  });
6203
  _.components.register("items", _.Items);
6204
 
6205
  })(
6206
+ FooGallery.$,
6207
+ FooGallery,
6208
+ FooGallery.utils,
6209
+ FooGallery.utils.is,
6210
+ FooGallery.utils.fn,
6211
+ FooGallery.utils.obj
6212
  );
6213
+ (function ($, _, _utils, _is) {
6214
 
6215
  _.Paging = _.Component.extend({
6216
+ construct: function (template) {
6217
  var self = this;
6218
  /**
6219
  * @ignore
6236
  self.ctrls = [];
6237
  self._arr = [];
6238
  },
6239
+ destroy: function () {
6240
  var self = this;
6241
  self._arr.splice(0, self._arr.length);
6242
+ $.each(self.ctrls.splice(0, self.ctrls.length), function (i, control) {
6243
  control.destroy();
6244
  });
6245
  self._super();
6246
  },
6247
+ build: function () {
6248
  var self = this, items = self.tmpl.items.available();
6249
  self.total = self.size > 0 && items.length > 0 ? Math.ceil(items.length / self.size) : 1;
6250
+ for (var i = 0; i < self.total; i++) {
6251
+ self._arr.push(items.splice(0, self.size));
 
 
 
 
 
 
6252
  }
6253
+ if (self.total > 1 && _.paging.hasCtrl(self.type)) {
6254
  var pos = self.position, top, bottom;
6255
+ if (pos === "both" || pos === "top") {
6256
  top = _.paging.makeCtrl(self.type, self.tmpl, self, "top");
6257
+ if (top.create()) {
6258
  top.append();
6259
  self.ctrls.push(top);
6260
  }
6261
  }
6262
+ if (pos === "both" || pos === "bottom") {
6263
  bottom = _.paging.makeCtrl(self.type, self.tmpl, self, "bottom");
6264
+ if (bottom.create()) {
6265
  bottom.append();
6266
  self.ctrls.push(bottom);
6267
  }
6268
  }
6269
  }
6270
  },
6271
+ rebuild: function () {
6272
  var self = this;
6273
  self.current = 0;
6274
  self.total = 0;
6275
  self._arr.splice(0, self._arr.length);
6276
+ $.each(self.ctrls.splice(0, self.ctrls.length), function (i, control) {
6277
  control.destroy();
6278
  });
6279
  self.build();
6280
  },
6281
+ all: function () {
6282
  return this._arr.slice();
6283
  },
6284
+ available: function () {
6285
  return this.get(this.current);
6286
  },
6287
+ controls: function (pageNumber) {
6288
  var self = this;
6289
+ if (self.isValid(pageNumber)) {
6290
+ $.each(self.ctrls, function (i, control) {
6291
  control.update(pageNumber);
6292
  });
6293
  }
6294
  },
6295
+ isValid: function (pageNumber) {
6296
  return _is.number(pageNumber) && pageNumber > 0 && pageNumber <= this.total;
6297
  },
6298
+ number: function (value) {
6299
  return this.isValid(value) ? value : (this.current === 0 ? 1 : this.current);
6300
  },
6301
+ create: function (pageNumber, isFilter) {
6302
  var self = this;
6303
  pageNumber = self.number(pageNumber);
6304
  var index = pageNumber - 1;
6305
+ self.tmpl.items.detach(self.tmpl.items.all());
6306
  self.tmpl.items.create(self._arr[index], true);
 
 
 
 
6307
  self.current = pageNumber;
6308
  },
6309
+ get: function (pageNumber) {
6310
  var self = this;
6311
+ if (self.isValid(pageNumber)) {
6312
  pageNumber = self.number(pageNumber);
6313
  return self._arr[pageNumber - 1];
6314
  }
6315
  return [];
6316
  },
6317
+ set: function (pageNumber, scroll, updateState, isFilter) {
6318
  var self = this;
6319
+ if (self.isValid(pageNumber)) {
 
6320
  var num = self.number(pageNumber), state;
6321
  if (num !== self.current) {
6322
+ var prev = self.current, setPage = function () {
6323
  updateState = _is.boolean(updateState) ? updateState : true;
6324
+ isFilter = _is.boolean(isFilter) ? isFilter : false;
6325
+ if (updateState && self.current === 1 && !self.tmpl.state.exists()) {
6326
  state = self.tmpl.state.get();
6327
  self.tmpl.state.update(state, self.pushOrReplace);
6328
  }
6329
+ self.controls(pageNumber);
6330
+ self.create(num, isFilter);
6331
+ if (updateState) {
6332
  state = self.tmpl.state.get();
6333
  self.tmpl.state.update(state, self.pushOrReplace);
6334
  }
6335
  if (self.scrollToTop && _is.boolean(scroll) ? scroll : false) {
6336
  var page = self.get(self.current);
6337
+ if (page.length > 0) {
6338
  page[0].scrollTo("top");
6339
  }
6340
  }
6341
+ self.tmpl.raise("after-page-change", [self.current, prev, isFilter]);
6342
  };
6343
+ var e = self.tmpl.raise("before-page-change", [self.current, num, setPage, isFilter]);
6344
  if (e.isDefaultPrevented()) return false;
6345
  setPage();
6346
  return true;
6348
  }
6349
  return false;
6350
  },
6351
+ find: function (item) {
6352
  var self = this;
6353
  for (var i = 0, l = self._arr.length; i < l; i++) {
6354
  if ($.inArray(item, self._arr[i]) !== -1) {
6357
  }
6358
  return 0;
6359
  },
6360
+ contains: function (pageNumber, item) {
6361
  var items = this.get(pageNumber);
6362
  return $.inArray(item, items) !== -1;
6363
  },
6364
+ first: function () {
6365
  this.goto(1);
6366
  },
6367
+ last: function () {
6368
  this.goto(this._arr.length);
6369
  },
6370
+ prev: function () {
6371
  this.goto(this.current - 1);
6372
  },
6373
+ next: function () {
6374
  this.goto(this.current + 1);
6375
  },
6376
+ goto: function (pageNumber) {
6377
  var self = this;
6378
+ if (self.set(pageNumber, true)) {
6379
  self.tmpl.loadAvailable();
6380
  }
6381
  }
6382
  });
6383
 
6384
  _.PagingControl = _.Component.extend({
6385
+ construct: function (template, parent, position) {
6386
  var self = this;
6387
  self._super(template);
6388
  self.pages = parent;
6389
  self.position = position;
6390
  self.$container = null;
6391
  },
6392
+ create: function () {
6393
  var self = this;
6394
  self.$container = $("<nav/>", {"class": self.pages.cls.container}).addClass(self.pages.theme);
6395
  return true;
6396
  },
6397
+ destroy: function () {
6398
  var self = this;
6399
  self.$container.remove();
6400
  self.$container = null;
6401
  },
6402
+ append: function () {
6403
  var self = this;
6404
+ if (self.position === "top") {
6405
  self.$container.insertBefore(self.tmpl.$el);
6406
  } else {
6407
  self.$container.insertAfter(self.tmpl.$el);
6408
  }
6409
  },
6410
+ update: function (pageNumber) {
6411
+ }
6412
  });
6413
 
6414
  _.paging.register("default", _.Paging, null, {
6423
  }, null, -100);
6424
 
6425
  })(
6426
+ FooGallery.$,
6427
+ FooGallery,
6428
+ FooGallery.utils,
6429
+ FooGallery.utils.is
6430
  );
6431
  (function($, _, _utils, _is){
6432
 
6727
  cls.layouts = $.map(cls.layout, function(value){
6728
  return value;
6729
  }).join(" ");
6730
+ // check if the layout is supplied as a CSS class
6731
+ var layouts = $.map(cls.layout, function(value, key){
6732
+ return {key: key, value: value};
6733
+ });
6734
+ for (var i =0, l = layouts.length; i < l; i++){
6735
+ if (self.$el.hasClass(layouts[i].value)){
6736
+ self.template.layout = layouts[i].key;
6737
+ break;
6738
+ }
6739
+ }
6740
  // check if the supplied layout is supported
6741
  if (!_is.string(cls.layout[self.template.layout])){
6742
  // if not set the default
6778
  rule = '#' + self.id + sel.container + ' ' + sel.item.elem + ' { margin-bottom: ' + self.template.gutter + 'px; }';
6779
  sheet.insertRule(rule , 0);
6780
  }
 
6781
  self.masonry = new Masonry( self.$el.get(0), self.template );
6782
  },
6783
+ onPostInit: function(event, self){
6784
  self.masonry.layout();
6785
  },
6786
+ onFirstLoad: function(event, self){
6787
  self.masonry.layout();
6788
  },
6789
  onReady: function(event, self){
6792
  onDestroy: function(event, self){
6793
  self.$el.find(self.sel.columnWidth).remove();
6794
  self.$el.find(self.sel.gutterWidth).remove();
 
 
 
6795
  if (self.style && self.style.parentNode){
6796
  self.style.parentNode.removeChild(self.style);
6797
  }
6798
  },
6799
+ onDestroyed: function(event, self){
6800
+ if (self.masonry instanceof Masonry){
6801
+ self.masonry.destroy();
6802
+ }
6803
+ },
6804
  onLayout: function(event, self){
6805
  self.masonry.layout();
6806
  },
6875
 
6876
  _.template.register("masonry", _.MasonryTemplate, {
6877
  template: {
6878
+ initLayout: false,
6879
+ isInitLayout: false,
6880
  layout: "col4"
6881
  }
6882
  }, {
6951
  */
6952
 
6953
  })(
6954
+ FooGallery.$,
6955
+ FooGallery,
6956
+ FooGallery.utils,
6957
+ FooGallery.utils.is
6958
  );
6959
  (function($, _, _utils, _is){
6960
 
6973
  self.options.maxRowHeight = parseInt(self.options.maxRowHeight);
6974
  }
6975
  }
 
6976
  $(window).on("resize.justified", {self: self}, self.onWindowResize);
6977
  },
6978
  destroy: function(){
6979
  $(window).off("resize.justified");
 
 
 
6980
  this.$el.removeAttr("style");
6981
  },
6982
  parse: function(){
7032
  }
7033
 
7034
  var self = this,
7035
+ containerWidth = self.getContainerWidth(),
7036
+ rows = self.rows(containerWidth),
7037
+ offsetTop = 0;
7038
 
7039
  for (var i = 0, l = rows.length, row; i < l; i++){
7040
  row = rows[i];
7098
  },
7099
  justify: function(row, containerWidth, offsetTop){
7100
  var self = this,
7101
+ left = 0,
7102
+ margins = self.options.margins * (row.items.length - 1),
7103
+ ratio = (containerWidth - margins) / row.width;
7104
 
7105
  if (row.index > 0) offsetTop += self.options.margins;
7106
  row.top = offsetTop;
7147
  },
7148
  rows: function(containerWidth){
7149
  var self = this,
7150
+ items = self.items(),
7151
+ rows = [],
7152
+ process = items.length > 0,
7153
+ index = -1, offsetTop = 0;
7154
 
7155
  while (process){
7156
  index += 1;
7213
  };
7214
 
7215
  })(
7216
+ FooGallery.$,
7217
+ FooGallery,
7218
+ FooGallery.utils,
7219
+ FooGallery.utils.is
7220
  );
7221
  (function($, _, _utils){
7222
 
7227
  onInit: function(event, self){
7228
  self.justified.init();
7229
  },
7230
+ onFirstLoad: function(event, self){
7231
  self.justified.layout( true );
7232
  },
7233
  onReady: function(event, self){
7234
+ self.justified.layout();
7235
  },
7236
  onDestroy: function(event, self){
7237
  self.justified.destroy();
7255
  });
7256
 
7257
  })(
7258
+ FooGallery.$,
7259
+ FooGallery,
7260
+ FooGallery.utils
7261
  );
7262
  (function($, _, _utils, _is){
7263
 
7270
  init: function(){
7271
  var self = this;
7272
  $(window).on("resize.portfolio", {self: self}, self.onWindowResize);
 
7273
  },
7274
  destroy: function(){
7275
  $(window).off("resize.portfolio");
 
 
 
7276
  this.$el.removeAttr("style");
7277
  },
7278
  parse: function(){
7332
  }
7333
 
7334
  var self = this,
7335
+ containerWidth = self.getContainerWidth(),
7336
+ rows = self.rows(containerWidth),
7337
+ offsetTop = 0;
7338
 
7339
  for (var i = 0, l = rows.length, row; i < l; i++){
7340
  row = rows[i];
7394
  },
7395
  rows: function(containerWidth){
7396
  var self = this,
7397
+ items = self.items(),
7398
+ rows = [],
7399
+ process = items.length > 0,
7400
+ index = -1, offsetTop = 0;
7401
 
7402
  while (process){
7403
  index += 1;
7462
  };
7463
 
7464
  })(
7465
+ FooGallery.$,
7466
+ FooGallery,
7467
+ FooGallery.utils,
7468
+ FooGallery.utils.is
7469
  );
7470
  (function($, _, _utils){
7471
 
7481
  onInit: function(event, self){
7482
  self.portfolio.init();
7483
  },
7484
+ onFirstLoad: function(event, self){
7485
  self.portfolio.layout( true );
7486
  },
7487
  onReady: function(event, self){
7488
+ self.portfolio.layout();
7489
  },
7490
  onDestroy: function(event, self){
7491
  self.portfolio.destroy();
7511
  });
7512
 
7513
  })(
7514
+ FooGallery.$,
7515
+ FooGallery,
7516
+ FooGallery.utils
7517
  );
7518
  // (function(_){
7519
  //
7571
  * @name $inner
7572
  * @type {jQuery}
7573
  */
7574
+ this.$inner = $();
7575
  /**
7576
  * @summary The jQuery object that displays the current image count.
7577
  * @memberof FooGallery.ImageViewerTemplate#
7578
  * @name $current
7579
  * @type {jQuery}
7580
  */
7581
+ this.$current = $();
7582
  /**
7583
  * @summary The jQuery object that displays the current image count.
7584
  * @memberof FooGallery.ImageViewerTemplate#
7585
  * @name $current
7586
  * @type {jQuery}
7587
  */
7588
+ this.$total = $();
7589
  /**
7590
  * @summary The jQuery object for the previous button.
7591
  * @memberof FooGallery.ImageViewerTemplate#
7592
  * @name $prev
7593
  * @type {jQuery}
7594
  */
7595
+ this.$prev = $();
7596
  /**
7597
  * @summary The jQuery object for the next button.
7598
  * @memberof FooGallery.ImageViewerTemplate#
7599
  * @name $next
7600
  * @type {jQuery}
7601
  */
7602
+ this.$next = $();
7603
  /**
7604
  * @summary The CSS classes for the Image Viewer template.
7605
  * @memberof FooGallery.ImageViewerTemplate#
7613
  * @type {FooGallery.ImageViewerTemplate~CSSSelectors}
7614
  */
7615
  },
7616
+ createChildren: function(){
7617
+ var self = this;
7618
+ return $("<div/>", {"class": self.cls.inner}).append(
7619
+ $("<div/>", {"class": self.cls.innerContainer}),
7620
+ $("<div/>", {"class": self.cls.controls}).append(
7621
+ $("<div/>", {"class": self.cls.prev})
7622
+ .append($("<span/>", {text: self.il8n.prev})),
7623
+ $("<label/>", {"class": self.cls.count, text: self.il8n.count})
7624
+ .prepend($("<span/>", {"class": self.cls.countCurrent, text: "0"}))
7625
+ .append($("<span/>", {"class": self.cls.countTotal, text: "0"})),
7626
+ $("<div/>", {"class": self.cls.next})
7627
+ .append($("<span/>", {text: self.il8n.next}))
7628
+ )
7629
+ );
7630
+ },
7631
+ onPreInit: function(event, self){
7632
+ self.$inner = self.$el.find(self.sel.innerContainer);
7633
+ self.$current = self.$el.find(self.sel.countCurrent);
7634
+ self.$total = self.$el.find(self.sel.countTotal);
7635
+ self.$prev = self.$el.find(self.sel.prev);
7636
+ self.$next = self.$el.find(self.sel.next);
7637
+ },
7638
  onInit: function (event, self) {
7639
  if (self.template.attachFooBox) {
7640
  self.$el.on('foobox.previous', {self: self}, self.onFooBoxPrev)
7667
  item.fix();
7668
  item.isAttached = true;
7669
  },
7670
+ onAfterPageChange: function(event, self, current, prev, isFilter){
7671
+ if (!isFilter){
7672
+ self.update();
7673
+ }
7674
+ },
7675
+ onAfterFilterChange: function(event, self){
7676
+ self.update();
7677
+ },
7678
  update: function(){
7679
  if (this.pages){
7680
  this.$current.text(this.pages.current);
7689
  */
7690
  prev: function () {
7691
  if (this.pages){
7692
+ if (this.template.loop && this.pages.current === 1){
7693
+ this.pages.last();
7694
+ } else {
7695
+ this.pages.prev();
7696
+ }
7697
  this.update();
7698
  }
7699
  },
7705
  */
7706
  next: function () {
7707
  if (this.pages){
7708
+ if (this.template.loop && this.pages.current === this.pages.total){
7709
+ this.pages.first();
7710
+ } else {
7711
+ this.pages.next();
7712
+ }
7713
  this.update();
7714
  }
7715
  },
7757
 
7758
  _.template.register("image-viewer", _.ImageViewerTemplate, {
7759
  template: {
7760
+ attachFooBox: false,
7761
+ loop: false
7762
  }
7763
  }, {
7764
+ container: "foogallery fg-image-viewer",
7765
+ inner: "fiv-inner",
7766
+ innerContainer: "fiv-inner-container",
7767
+ controls: "fiv-ctrls",
7768
+ prev: "fiv-prev",
7769
+ next: "fiv-next",
7770
+ count: "fiv-count",
7771
+ countCurrent: "fiv-count-current",
7772
+ countTotal: "fiv-count-total"
7773
+ }, {
7774
+ prev: "Prev",
7775
+ next: "Next",
7776
+ count: "of"
7777
  });
7778
 
7779
  })(
7787
  _.ThumbnailTemplate = _.Template.extend({
7788
  construct: function (options, element) {
7789
  this._super(_obj.extend({}, options, {
7790
+ filtering: {
7791
+ type: "none"
7792
+ },
7793
  paging: {
7794
  type: "none"
7795
  }
7796
  }), element);
7797
+ this.$hidden = $();
7798
+ },
7799
+ createChildren: function(){
7800
+ var self = this;
7801
+ return self.$hidden = $("<div/>", {"class": self.cls.hidden});
7802
+ },
7803
+ onPreInit: function(event, self){
7804
+ self.$hidden = self.$el.find(self.sel.hidden);
7805
+ },
7806
+ onPostInit: function(event, self){
7807
+ var hidden = self.items.all().slice(1);
7808
+ for (var i = 0, l = hidden.length, item; i < l; i++){
7809
+ item = hidden[i];
7810
+ self.$hidden.append(
7811
+ $("<a/>", {
7812
+ href: item.href,
7813
+ rel: "foobox[" + self.id + "]"
7814
+ }).attr(item.attr.anchor)
7815
+ );
7816
+ }
7817
+ self.items.setAll(self.items.all().slice(0,1));
7818
  }
7819
  });
7820
 
7821
  _.template.register("thumbnail", _.ThumbnailTemplate, null, {
7822
+ container: "foogallery fg-thumbnail",
7823
+ hidden: "fg-st-hidden"
7824
  });
7825
 
7826
  })(
7828
  FooGallery,
7829
  FooGallery.utils.obj
7830
  );
7831
+ (function ($, _, _utils, _obj) {
7832
 
7833
+ _.triggerPostLoad = function (e, tmpl, current, prev, isFilter) {
7834
+ if (e.type === "first-load" || (tmpl.initialized && ((e.type === "after-page-change" && !isFilter) || e.type === "after-filter-change"))) {
7835
  $("body").trigger("post-load");
7836
  }
7837
+ };
7838
+
7839
+ _.autoDefaults = {
7840
+ on: {
7841
+ "first-load.foogallery after-page-change.foogallery after-filter-change.foogallery": _.triggerPostLoad
7842
+ }
7843
+ };
7844
+
7845
+ _.auto = function (options) {
7846
+ _.autoDefaults = _obj.merge(_.autoDefaults, options);
7847
+ };
7848
 
7849
  // this automatically initializes all templates on page load
7850
  $(function () {
7851
+ $('[id^="foogallery-"]:not(.fg-ready)').foogallery(_.autoDefaults);
 
 
 
 
7852
  });
7853
 
7854
+ _utils.ready(function () {
7855
+ $('[id^="foogallery-"].fg-ready').foogallery(_.autoDefaults);
 
 
 
 
7856
  });
7857
 
7858
  })(
7859
  FooGallery.$,
7860
+ FooGallery,
7861
  FooGallery.utils,
7862
+ FooGallery.utils.obj
7863
  );
extensions/default-templates/shared/js/foogallery.min.js CHANGED
@@ -1,10 +1,10 @@
1
  /*
2
  * FooGallery - The Most Intuitive and Extensible Gallery Creation and Management Tool Ever Created for WordPress
3
- * @version 1.0.20
4
  * @link
5
  * @copyright Steven Usher & Brad Vincent 2015
6
  * @license Released under the GPLv3 license.
7
  */
8
 
9
- !function(a,b){b.$=a}(jQuery,window.FooGallery=window.FooGallery||{}),function(a){if(!a)return void console.warn("jQuery must be included in the page prior to the FooGallery.utils library.");var b={$:a,version:"0.0.6"};b.versionCompare=function(a,b){function c(a){for(var b=a.split("."),c=0,d=b.length;c<d;c++)b[c]=parseInt(b[c]),isNaN(b[c])&&(b[c]=0);return b}if(!/[\d.]/.test(a)||!/[\d.]/.test(b))return NaN;for(var d=c(a),e=c(b);d.length<e.length;)d.push(0);for(;e.length<d.length;)e.push(0);for(var f=0;f<d.length;++f){if(e.length==f)return 1;if(d[f]!=e[f])return d[f]>e[f]?1:-1}return d.length!=e.length?-1:0},!function(){try{return!!window.FooGallery.utils}catch(a){return!1}}()?window.FooGallery.utils=b:b.versionCompare(b.version,window.FooGallery.utils.version)>0?(console.warn("An older version of FooGallery.utils ("+window.FooGallery.utils.version+") already exists in the page, version "+b.version+" will override it."),window.FooGallery.utils=b):console.warn("A newer version of FooGallery.utils ("+window.FooGallery.utils.version+") already exists in the page, version "+b.version+" will not register itself.")}(jQuery),function(a,b){"0.0.6"===b.version&&(b.is={},b.is.array=function(a){return"[object Array]"===Object.prototype.toString.call(a)},b.is.boolean=function(a){return"[object Boolean]"===Object.prototype.toString.call(a)},b.is.element=function(a){return"object"==typeof HTMLElement?a instanceof HTMLElement:!!a&&"object"==typeof a&&null!==a&&1===a.nodeType&&"string"==typeof a.nodeName},b.is.empty=function(a){if(b.is.undef(a)||null===a)return!0;if(b.is.number(a)&&0==a)return!0;if(b.is.boolean(a)&&!1===a)return!0;if(b.is.string(a)&&0===a.length)return!0;if(b.is.array(a)&&0===a.length)return!0;if(b.is.jq(a)&&0===a.length)return!0;if(b.is.hash(a)){for(var c in a)if(a.hasOwnProperty(c))return!1;return!0}return!1},b.is.error=function(a){return"[object Error]"===Object.prototype.toString.call(a)},b.is.fn=function(a){return a===window.alert||"[object Function]"===Object.prototype.toString.call(a)},b.is.hash=function(a){return b.is.object(a)&&a.constructor===Object&&!a.nodeType&&!a.setInterval},b.is.jq=function(c){return!b.is.undef(a)&&c instanceof a},b.is.number=function(a){return"[object Number]"===Object.prototype.toString.call(a)&&!isNaN(a)},b.is.object=function(a){return"[object Object]"===Object.prototype.toString.call(a)&&!b.is.undef(a)&&null!==a},b.is.promise=function(a){return b.is.object(a)&&b.is.fn(a.then)&&b.is.fn(a.promise)},b.is.size=function(a){return!!(b.is.string(a)&&!b.is.empty(a)||b.is.number(a))&&/^(auto|none|(?:[\d\.]*)+?(?:%|px|mm|q|cm|in|pt|pc|em|ex|ch|rem|vh|vw|vmin|vmax)?)$/.test(a)},b.is.string=function(a){return"[object String]"===Object.prototype.toString.call(a)},b.is.undef=function(a){return void 0===a})}(FooGallery.utils.$,FooGallery.utils),function(a,b,c){if("0.0.6"===b.version){b.fn={};var d=Function.prototype.toString;b.fn.CONTAINS_SUPER=/xyz/.test(d.call(function(){xyz}))?/\b_super\b/:/.*/,b.fn.addOrOverride=function(a,e,f){if(c.object(a)&&c.string(e)&&!c.empty(e)&&c.fn(f)){var g=a[e],h=c.fn(g)&&b.fn.CONTAINS_SUPER.test(d.call(f));a[e]=h?function(a,b){return function(){var c=this._super;this._super=a;var d=b.apply(this,arguments);return this._super=c,d}}(g,f):f}},b.fn.apply=function(a,b){function d(){return a.apply(this,b)}return b=c.array(b)?b:[],d.prototype=a.prototype,new d},b.fn.arg2arr=function(a){return Array.prototype.slice.call(a)},b.fn.check=function(d,e,f,g){function h(a){return function(){return a.apply(d,arguments)}}return f=c.fn(f)?f:a.noop,d=c.object(d)?d:window,e=c.string(e)?b.fn.fetch(e,g):e,h(c.fn(e)?e:f)},b.fn.fetch=function(b,d){return!c.string(b)||c.empty(b)?null:(d=c.object(d)?d:window,a.each(b.split("."),function(a,b){if(!d[b])return!1;d=d[b]}),c.fn(d)?d:null)},b.fn.enqueue=function(d,e,f,g){function h(a,b){try{return n.push(a),b.apply(a,i)}catch(a){return j.reject(a,n),j}}var i=b.fn.arg2arr(arguments),j=a.Deferred(),k=a.Deferred(),l=k.promise(),m=[],n=[],o=!0;return d=i.shift(),e=i.shift(),a.each(d,function(a,d){c.fn(d[e])&&(l=l.then(function(){if(!o){var a=b.fn.arg2arr(arguments);m.push(a)}return o=!1,h(d,d[e])}))}),l.then(function(){if(!o){var a=b.fn.arg2arr(arguments);m.push(a)}o=!1,j.resolve(m)}),l.fail(function(){var a=b.fn.arg2arr(arguments);a.push(n),j.reject.apply(j,a)}),k.resolve(),j.promise()},b.fn.when=function(b){if(!c.array(b)||c.empty(b))return a.when();for(var d=a.Deferred(),e=[],f=b.length,g=0;g<b.length;g++)b[g].then(function(a){e.push(a)}).always(function(){--f||d.resolve(e)});return d.promise()},b.fn.rejectWith=function(c,d){var e=a.Deferred(),f=b.fn.arg2arr(arguments);return e.reject.apply(e,f).promise()},b.fn.resolveWith=function(c,d){var e=a.Deferred(),f=b.fn.arg2arr(arguments);return e.resolve.apply(e,f).promise()},b.fn.resolved=a.Deferred().resolve().promise(),b.fn.rejected=a.Deferred().reject().promise()}}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is),function(a,b){if("0.0.6"===a.version){a.url={};var c=document.createElement("a");a.url.parts=function(a){return c.href=a,{hash:c.hash,host:c.host,hostname:c.hostname,href:c.href,origin:c.origin,pathname:c.pathname,port:c.port,protocol:c.protocol,search:c.search}},a.url.full=function(a){return!b.string(a)||b.empty(a)?null:(c.href=a,c.href)},a.url.param=function(a,c,d){if(!b.string(a)||b.empty(a)||!b.string(c)||b.empty(c))return a;var e,f,g,h;return b.undef(d)?(e=new RegExp("[?|&]"+c+"=([^&;]+?)(&|#|;|$)"),f=e.exec(a)||[,""],g=f[1].replace(/\+/g,"%20"),b.string(g)&&!b.empty(g)?decodeURIComponent(g):null):(""===d||null===d?(e=new RegExp("^([^#]*?)(([^#]*)&)?"+c+"(=[^&#]*)?(&|#|$)"),g=a.replace(e,"$1$3$5").replace(/^([^#]*)((\?)&|\?(#|$))/,"$1$3$4")):(e=new RegExp("([?&])"+c+"[^&]*"),h=c+"="+encodeURIComponent(d),(g=a.replace(e,"$1"+h))!==a||e.test(g)||(g+="&"+h)),g)}}}(FooGallery.utils,FooGallery.utils.is),function(a,b,c){"0.0.6"===a.version&&(a.str={},a.str.camel=function(a){return b.empty(a)?a:a.toUpperCase()===a?a.toLowerCase():a.replace(/^([A-Z])|[-\s_]+(\w)/g,function(a,c,d){return b.string(d)?d.toUpperCase():c.toLowerCase()})},a.str.contains=function(a,c,d){return!(!b.string(a)||b.empty(a)||!b.string(c)||b.empty(c))&&(c.length<=a.length&&-1!==(d?a.toUpperCase().indexOf(c.toUpperCase()):a.indexOf(c)))},a.str.containsWord=function(a,c,d){if(!b.string(a)||b.empty(a)||!b.string(c)||b.empty(c)||a.length<c.length)return!1;for(var e=a.split(/\W/),f=0,g=e.length;f<g;f++)if(d?e[f].toUpperCase()==c.toUpperCase():e[f]==c)return!0;return!1},a.str.endsWith=function(a,c){return!b.string(a)||b.empty(a)||!b.string(c)||b.empty(c)?a==c:a.slice(a.length-c.length)==c},a.str.escapeRegExp=function(a){return b.empty(a)?a:a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")},a.str.fnv1a=function(a){if(!b.string(a)||b.empty(a))return null;var c,d,e=2166136261;for(c=0,d=a.length;c<d;c++)e^=a.charCodeAt(c),e+=(e<<1)+(e<<4)+(e<<7)+(e<<8)+(e<<24);return e>>>0},a.str.from=function(c,d){return!b.string(c)||b.empty(c)||!b.string(d)||b.empty(d)?null:a.str.contains(c,d)?c.substring(c.indexOf(d)+d.length):null},a.str.join=function(d,e,f){if(!b.string(d)||!b.string(e))return null;var g=c.arg2arr(arguments);d=g.shift();var h,i,j=g.shift();for(h=0,i=g.length;h<i;h++)e=g[h],b.empty(e)||(a.str.endsWith(j,d)&&(j=j.slice(0,j.length-d.length)),a.str.startsWith(e,d)&&(e=e.slice(d.length)),j+=d+e);return j},a.str.startsWith=function(a,c){return!b.empty(a)&&!b.empty(c)&&a.slice(0,c.length)==c},a.str.until=function(c,d){return b.empty(c)||b.empty(d)?c:a.str.contains(c,d)?c.substring(0,c.indexOf(d)):c},a.str.format=function(a,d,e){var f=c.arg2arr(arguments);if(a=f.shift(),b.empty(a)||b.empty(f))return a;1===f.length&&(b.array(f[0])||b.object(f[0]))&&(f=f[0]);for(var g in f)a=a.replace(new RegExp("\\{"+g+"\\}","gi"),f[g]);return a})}(FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn),function(a,b,c,d,e){if("0.0.6"===b.version){b.obj={};var f=function(){};b.obj.create=function(a){if(!c.object(a))throw TypeError("Argument must be an object");f.prototype=a;var b=new f;return f.prototype=null,b},b.obj.extend=function(e,f,g){e=c.object(e)?e:{};var h=d.arg2arr(arguments);return h.shift(),a.each(h,function(a,c){b.obj.merge(e,c)}),e},b.obj.merge=function(a,d){a=c.object(a)?a:{},d=c.object(d)?d:{};for(var e in d)d.hasOwnProperty(e)&&(c.object(d[e])?(a[e]=c.object(a[e])?a[e]:{},b.obj.merge(a[e],d[e])):c.array(d[e])?a[e]=d[e].slice():a[e]=d[e]);return a},b.obj.mergeValid=function(d,e,f,g){if(!c.hash(f)||!c.hash(e))return d;e=c.hash(e)?e:{},g=c.hash(g)?g:{};var h,i,j;for(h in e)e.hasOwnProperty(h)&&c.fn(e[h])&&(i=c.array(g[h])?g[h]:c.string(g[h])?[g[h]]:[h],a.each(i,function(a,g){if(j=b.obj.prop(f,g),!c.undef(j))return e[h](j)?(b.obj.prop(d,h,j),!1):void 0}));return d},b.obj.prop=function(b,d,f){if(c.object(b)&&!c.empty(d)){var g,h;if(c.undef(f))return e.contains(d,".")?(g=d.split("."),h=g.length-1,a.each(g,function(a,d){if(a===h)f=b[d];else{if(!c.hash(b[d]))return!1;b=b[d]}})):c.undef(b[d])||(f=b[d]),f;e.contains(d,".")?(g=d.split("."),h=g.length-1,a.each(g,function(a,d){a===h?b[d]=f:b=c.hash(b[d])?b[d]:b[d]={}})):c.undef(b[d])||(b[d]=f)}}}}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.str),function(a,b,c){if("0.0.6"===b.version){b.ready=function(a){function c(){try{a.call(window,b.$)}catch(a){console.error(a)}}(Function("/*@cc_on return true@*/")()?"complete"===document.readyState:"loading"!==document.readyState)?c():document.addEventListener("DOMContentLoaded",c,!1)};var d=0;b.uniqueId=function(a,b){var e=a.attr("id");return c.empty(e)&&(b=c.string(b)&&!c.empty(b)?b:"uid-",e=b+ ++d,a.attr("id",e).data("__uniqueId__",!0)),e},b.removeUniqueId=function(a){a.data("__uniqueId__")&&a.removeAttr("id").removeData("__uniqueId__")}}}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is),function(a,b,c){if("0.0.6"===b.version){b.transition={};var d=document.createElement("div");b.transition.supported=function(a){var b=a.style;return c.string(b.transition)||c.string(b.WebkitTransition)||c.string(b.MozTransition)||c.string(b.msTransition)||c.string(b.OTransition)}(d),b.transition.end=function(a){var b=a.style;return c.string(b.transition)?"transitionend":c.string(b.WebkitTransition)?"webkitTransitionEnd":c.string(b.MozTransition)?"transitionend":c.string(b.msTransition)?"msTransitionEnd":c.string(b.OTransition)?"oTransitionEnd":null}(d),b.transition.duration=function(a,b){if(b=c.number(b)?b:0,!c.jq(a))return b;var d=a.css("transition-duration");if(/^([\d\.]*)+?(ms|s)$/i.test(d)){var e=d.match(/^([\d\.]*)+?(ms|s)$/i),f=parseFloat(e[1]);return"s"===e[2].toLowerCase()&&(f*=1e3),f}return b},b.transition.start=function(d,e,f,g){var h=a.Deferred();if(d=d.first(),b.transition.supported){var i=d.data("transition_safety");c.hash(i)&&c.number(i.timer)&&(clearTimeout(i.timer),d.removeData("transition_safety").off(b.transition.end+".utils"),i.deferred.reject()),g=c.number(g)?g:b.transition.duration(d)+50,i={deferred:h,timer:setTimeout(function(){d.removeData("transition_safety").off(b.transition.end+".utils"),h.resolve()},g)},d.data("transition_safety",i),d.on(b.transition.end+".utils",function(a){d.is(a.target)&&(clearTimeout(i.timer),d.removeData("transition_safety").off(b.transition.end+".utils"),h.resolve())})}return setTimeout(function(){d.toggleClass(e,f),b.transition.supported||h.resolve()},20),h.promise()}}}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is),function(a,b,c,d,e){"0.0.6"===b.version&&(b.Class=function(){},b.Class.extend=function(a){function f(){if(!c.fn(this.construct))throw new SyntaxError('FooGallery.utils.Class objects must be constructed with the "new" keyword.');this.construct.apply(this,arguments)}a=c.hash(a)?a:{};var g=d.create(this.prototype);for(var h in a)a.hasOwnProperty(h)&&e.addOrOverride(g,h,a[h]);return g.construct=c.fn(g.construct)?g.construct:function(){},f.prototype=g,f.prototype.constructor=c.fn(g.__ctor__)?g.__ctor__:f,f.extend=b.Class.extend,f.override=b.Class.override,f},b.Class.override=function(a,b){e.addOrOverride(this.prototype,a,b)})}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.obj,FooGallery.utils.fn),function(a,b,c){if("0.0.6"===b.version){b.Bounds=b.Class.extend({construct:function(){var a=this;a.top=0,a.right=0,a.bottom=0,a.left=0,a.width=0,a.height=0},inflate:function(a){var b=this;return c.number(a)&&(b.top-=a,b.right+=a,b.bottom+=a,b.left-=a,b.width+=2*a,b.height+=2*a),b},intersects:function(a){var b=this;return b.left<=a.right&&a.left<=b.right&&b.top<=a.bottom&&a.top<=b.bottom}});var d;b.getViewportBounds=function(c){d||(d=a(window));var e=new b.Bounds;return e.top=d.scrollTop(),e.left=d.scrollLeft(),e.width=d.width(),e.height=d.height(),e.right=e.left+e.width,e.bottom=e.top+e.height,e.inflate(c),e},b.getElementBounds=function(d){c.jq(d)||(d=a(d));var e=new b.Bounds;if(0!==d.length){var f=d.offset();e.top=f.top,e.left=f.left,e.width=d.width(),e.height=d.height()}return e.right=e.left+e.width,e.bottom=e.top+e.height,e}}}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is),function(a,b,c,d){"0.0.6"===b.version&&(b.Factory=b.Class.extend({construct:function(){this.registered={}},contains:function(a){return!c.undef(this.registered[a])},load:function(b,e,f){var g,h,i=this,j=d.arg2arr(arguments),k=[],l=[];b=j.shift()||{};for(g in i.registered)if(i.registered.hasOwnProperty(g)){var m=i.registered[g];b.hasOwnProperty(g)&&(h=b[g],c.string(h)&&(h=d.fetch(b[g])),c.fn(h)&&(m={name:g,klass:h,priority:i.registered[g].priority})),k.push(m)}for(g in b)b.hasOwnProperty(g)&&!i.registered.hasOwnProperty(g)&&(h=b[g],c.string(h)&&(h=d.fetch(b[g])),c.fn(h)&&k.push({name:g,klass:h,priority:0}));return k.sort(function(a,b){return b.priority-a.priority}),a.each(k,function(a,b){c.fn(b.klass)&&l.push(d.apply(b.klass,j))}),l},make:function(a,b,e){var f,g=this,h=d.arg2arr(arguments);return a=h.shift(),f=g.registered[a],c.hash(f)&&c.fn(f.klass)?d.apply(f.klass,h):null},names:function(b){b=!!c.boolean(b)&&b;var d,e=[];if(b){var f=[];for(d in this.registered)this.registered.hasOwnProperty(d)&&f.push(this.registered[d]);f.sort(function(a,b){return b.priority-a.priority}),a.each(f,function(a,b){e.push(b.name)})}else for(d in this.registered)this.registered.hasOwnProperty(d)&&e.push(d);return e},register:function(a,b,d){if(!c.string(a)||c.empty(a)||!c.fn(b))return!1;d=c.number(d)?d:0;var e=this.registered[a];return this.registered[a]={name:a,klass:b,priority:c.undef(e)?d:e.priority},!0}}))}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn),function(a,b,c){if("0.0.6"===a.version){var d=!1;try{d=!!window.localStorage}catch(a){d=!1}a.Debugger=a.Class.extend({construct:function(a){this.key=a,this.enabled=!!d&&!!localStorage.getItem(this.key)},enable:function(){d&&(this.enabled=!0,localStorage.setItem(this.key,this.enabled))},disable:function(){d&&(this.enabled=!1,localStorage.removeItem(this.key))},log:function(a,c){this.enabled&&console.log.apply(console,b.arg2arr(arguments))},logf:function(a,d,e){if(this.enabled){var f=b.arg2arr(arguments);a=f.shift(),d=f.shift(),f.unshift(c.format(a,d)),this.log.apply(this,f)}}})}}(FooGallery.utils,FooGallery.utils.fn,FooGallery.utils.str),function(a,b,c){"0.0.6"===b.version&&(b.Throttle=b.Class.extend({construct:function(a){this.id=null,this.active=!1,this.idle=c.number(a)?a:0},limit:function(a){if(c.fn(a)){this.clear();var b=this;this.active=!0,this.id=setTimeout(function(){b.active=!1,b.id=null,a()},this.idle)}},clear:function(){c.number(this.id)&&(clearTimeout(this.id),this.active=!1,this.id=null)}}))}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is),function(a,b,c,d,e){b.debug=new c.Debugger("__FooGallery__"),c.selectify=function(b){if(d.hash(b)){var e,f={};for(var g in b)b.hasOwnProperty(g)&&(e=c.selectify(b[g]))&&(f[g]=e);return f}return d.string(b)||d.array(b)?(d.string(b)&&(b=[b]),a.map(b,function(a){return d.string(a)?"."+a.split(/\s/g).join("."):null}).join(",")):null},b.emptyImage="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",b.dataTemplate="__FooGallery__",b.dataItem="__FooGalleryItem__",b.init=function(a,c){return b.template.make(a,c).initialize()},b.initAll=function(c){return e.when(a(".foogallery").map(function(a,d){return b.init(c,d)}).get())},b.parseSrc=function(b,c,e,f,g,h){if(!d.string(b))return null;if(!d.string(f))return b;var i=a.map(f.replace(/(\s[\d.]+[whx]),/g,"$1 @,@ ").split(" @,@ "),function(a){return{url:/^\s*(\S*)/.exec(a)[1],w:parseFloat((/\S\s+(\d+)w/.exec(a)||[0,1/0])[1]),h:parseFloat((/\S\s+(\d+)h/.exec(a)||[0,1/0])[1]),x:parseFloat((/\S\s+([\d.]+)x/.exec(a)||[0,1])[1])}});if(!i.length)return b;i.unshift({url:b,w:i[0].w!==1/0&&i[0].h===1/0?c:1/0,h:i[0].h!==1/0&&i[0].w===1/0?e:1/0,x:1});var j,k=window.devicePixelRatio||1,l={w:g*k,h:h*k,x:k};for(j in l)l.hasOwnProperty(j)&&(i=a.grep(i,function(a,b){return function(c){return c[a]>=l[a]||c[a]===b}}(j,Math.max.apply(null,a.map(i,function(a){return a[j]})))));for(j in l)l.hasOwnProperty(j)&&(i=a.grep(i,function(a,b){return function(c){return c[a]===b}}(j,Math.min.apply(null,a.map(i,function(a){return a[j]})))));return i[0].url},a.fn.foogallery=function(c,e){return this.filter(".foogallery").each(function(f,g){if(d.string(c)){var h=a.data(g,b.dataTemplate);if(h instanceof b.Template)switch(c){case"layout":return void h.layout();case"destroy":return void h.destroy()}}else b.template.make(c,g).initialize().then(function(a){d.fn(e)&&e(a)})})}}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn),function(a,b,c,d,e,f){b.TemplateFactory=c.Factory.extend({construct:function(){this.registered={}},register:function(a,b,c,e,f,g){var h=this,i=h._super(a,b,g);if(i){var j=h.registered;j[a].opt=d.hash(c)?c:{},j[a].cls=d.hash(e)?e:{},j[a].il8n=d.hash(f)?f:{}}return i},make:function(b,c){c=d.jq(c)?c:a(c),b=f.extend({},b,c.data("foogallery"));var e=this,g=e.type(b,c);return e.contains(g)?(b=e.options(g,b),e._super(g,b,c)):null},type:function(b,e){e=d.jq(e)?e:a(e);var f=this,g=d.hash(b)&&d.hash(b)&&d.string(b.type)&&f.contains(b.type)?b.type:"core";if("core"===g&&e.length>0)for(var h=f.registered,i=f.names(!0),j=0,k=i.length;j<k;j++)if(h.hasOwnProperty(i[j])){var l=i[j],m=h[l].cls;if(d.string(m.container)){var n=c.selectify(m.container);if(e.is(n)){g=i[j];break}}}return g},configure:function(a,b,c,d){var e=this;if(e.contains(a)){var g=e.registered;f.extend(g[a].opt,b),f.extend(g[a].cls,c),f.extend(g[a].il8n,d)}},options:function(a,c){c=f.extend({},c);var e=this,g=e.registered,h=g.core.opt,i=g.core.cls,j=g.core.il8n;return d.hash(c.cls)||(c.cls={}),d.hash(c.il8n)||(c.il8n={}),c=b.paging.merge(c),"core"!==a&&e.contains(a)?(c=f.extend({},h,g[a].opt,c),c.cls=f.extend(c.cls,i,g[a].cls,c.cls),c.il8n=f.extend(c.il8n,j,g[a].il8n,c.il8n)):(c=f.extend({},h,c),c.cls=f.extend(c.cls,i,c.cls),c.il8n=f.extend(c.il8n,j,c.il8n)),c}}),b.template=new b.TemplateFactory}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.obj),function(a,b,c,d,e){a.PagingFactory=b.Factory.extend({construct:function(){this.registered={}},register:function(a,b,d,e,f,g,h){var i=this,j=i._super(a,b,h);if(j){var k=i.registered;k[a].ctrl=c.fn(d)?d:null,k[a].opt=c.hash(e)?e:{},k[a].cls=c.hash(f)?f:{},k[a].il8n=c.hash(g)?g:{}}return j},type:function(a){var b,d=this;return c.hash(a)&&c.hash(b=a.paging)&&c.string(b.type)&&d.contains(b.type)?b.type:null},merge:function(a){a=e.extend({},a);var b=this,d=b.type(a),f=b.registered,g=f.default.opt,h=f.default.cls,i=f.default.il8n,j=c.hash(a.paging)?a.paging:{},k=c.hash(a.cls)&&c.hash(a.cls.paging)?a.cls.paging:{},l=c.hash(a.il8n)&&c.hash(a.il8n.paging)?a.il8n.paging:{};return c.hash(a.cls)||(a.cls={}),c.hash(a.il8n)||(a.il8n={}),"default"!==d&&b.contains(d)?(a.paging=e.extend({},g,f[d].opt,j,{type:d}),a.cls=e.extend(a.cls,{paging:h},{paging:f[d].cls},{paging:k}),a.il8n=e.extend(a.il8n,{paging:i},{paging:f[d].il8n},{paging:l})):(a.paging=e.extend({},g,j,{type:d}),a.cls=e.extend(a.cls,{paging:h},{paging:k}),a.il8n=e.extend(a.il8n,{paging:i},{paging:l})),a},configure:function(a,b,c,d){var f=this;if(f.contains(a)){var g=f.registered;e.extend(g[a].opt,b),e.extend(g[a].cls,c),e.extend(g[a].il8n,d)}},hasCtrl:function(a){var b=this,d=b.registered[a];return c.hash(d)&&c.fn(d.ctrl)},makeCtrl:function(a,b,d,e){var f=this,g=f.registered[a];return c.hash(g)&&c.fn(g.ctrl)?new g.ctrl(b,d,e):null}}),a.paging=new a.PagingFactory}(FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.obj),function(a,b,c,d,e,f){b.Template=c.Class.extend({construct:function(e,f){var g=this;g.$el=d.jq(f)?f:a(f),g.opt=e,g.template=e.template,g.id=g.$el.prop("id")||e.id,g.createdSelf=!1,g.cls=e.cls,g.il8n=e.il8n,g.sel=c.selectify(g.cls),g.items=b.components.make("items",g),g.pages=b.paging.make(e.paging.type,g),g.state=b.components.make("state",g),g._initialize=null,g.initializing=!1,g.initialized=!1,g.destroying=!1,g.destroyed=!1},initialize:function(c){var f=this;return d.promise(f._initialize)?f._initialize:(c=d.jq(c)?c:a(c),f._initialize=a.Deferred(function(g){if(f.initializing=!0,0===c.length&&0===f.$el.parent().length)return void g.reject("A parent element is required.");0===f.$el.length&&(f.$el=f.create(),f.createdSelf=!0),c.length>0&&f.$el.appendTo(c);var h,i=a.Deferred(),j=i.promise();f.$el.length>0&&(h=f.$el.data(b.dataTemplate))instanceof b.Template?j=j.then(function(){return h.destroy().then(function(){f.$el.data(b.dataTemplate,f)})}):f.$el.data(b.dataTemplate,f),j.then(function(){return f.destroying?e.rejectWith("destroy in progress"):(d.empty(f.opt.on)||f.$el.on(f.opt.on),f.raise("pre-init").isDefaultPrevented()?e.rejectWith("pre-init default prevented"):void 0)}).then(function(){return f.destroying?e.rejectWith("destroy in progress"):f.opt.delay<=0?e.resolved:a.Deferred(function(a){f._delay=setTimeout(function(){f._delay=null,a.resolve()},f.opt.delay)}).promise()}).then(function(){return f.destroying?e.rejectWith("destroy in progress"):f.raise("init").isDefaultPrevented()?e.rejectWith("init default prevented"):f.items.fetch()}).then(function(){if(f.destroying)return e.rejectWith("destroy in progress");if(f.raise("post-init").isDefaultPrevented())return e.rejectWith("post-init default prevented");var b=f.state.parse();f.state.set(d.empty(b)?f.state.initial():b),a(window).on("scroll.foogallery",{self:f},f.throttle(f.onWindowScroll,f.opt.throttle)).on("popstate.foogallery",{self:f},f.onWindowPopState)}).then(function(){return f.destroying?e.rejectWith("destroy in progress"):(f.raise("first-load"),f.loadAvailable())}).then(function(){if(f.destroying)return e.rejectWith("destroy in progress");f.initializing=!1,f.initialized=!0,f._check(200),f._check(500),f._check(1e3),f._check(2e3),f._check(5e3),f.raise("ready"),g.resolve(f)}).fail(function(a){g.reject(a)}),i.resolve()}).promise().fail(function(a){console.log("initialize failed",f,a),f.destroy()}))},create:function(){var b=this;return a("<div/>",{id:b.id,class:b.cls.container}).addClass(b.opt.classes)},destroy:function(){var b=this;return b.destroyed?e.resolved:(b.destroying=!0,a.Deferred(function(a){b.initializing&&d.promise(b._initialize)?b._initialize.always(function(){b.destroying=!1,b._destroy(),a.resolve()}):(b.destroying=!1,b._destroy(),a.resolve())}).promise())},_destroy:function(){var c=this;c.destroyed||(c.raise("destroy"),a(window).off("popstate.foogallery",c.onWindowPopState).off("scroll.foogallery"),c.state.destroy(),c.pages&&c.pages.destroy(),c.items.destroy(),d.empty(c.opt.on)||c.$el.off(c.opt.on),c.raise("destroyed"),c.$el.removeData(b.dataTemplate),c.createdSelf&&c.$el.remove(),c.$el=c.state=c.items=c.pages=null,c.destroyed=!0,c.initializing=!1,c.initialized=!1)},loadAvailable:function(){var a,b=this;return a=b.pages?b.pages.available():b.items.available(),b.items.load(a)},_check:function(a){a=d.number(a)?a:0;var b=this;setTimeout(function(){!b.initialized||b.destroying&&b.destroyed||b.loadAvailable()},a)},raise:function(c,e){if(!d.string(c)||d.empty(c))return null;e=d.array(e)?e:[];var g=this,h=c.split(".")[0],i=f.camel("on-"+h),j=a.Event(h+".foogallery");return e.unshift(g),g.$el.trigger(j,e),b.debug.logf("{id}|{name}:",{id:g.id,name:h},e),d.fn(g[i])&&(e.unshift(j),g[i].apply(g.$el.get(0),e)),j},layout:function(){var a=this;null!==a._initialize&&a.raise("layout")},throttle:function(a,b){var c=Date.now();return function(){if(c+b-Date.now()<0){var d=e.arg2arr(arguments);a.apply(this,d),c=Date.now()}}},onWindowPopState:function(a){var b=a.data.self,c=a.originalEvent.state;d.empty(c)||c.id!==b.id||(b.state.set(c),b.loadAvailable())},onWindowScroll:function(a){a.data.self.loadAvailable()}}),b.template.register("core",b.Template,{id:null,type:"core",classes:"",on:{},lazy:!0,viewport:200,items:[],delay:100,throttle:50,timeout:6e4,srcset:"data-srcset",src:"data-src",template:{}},{container:"foogallery"},{},-100)}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.str),function(a,b){a.Component=b.Class.extend({construct:function(a){this.tmpl=a},destroy:function(){this.tmpl=null}}),a.components=new b.Factory}(FooGallery,FooGallery.utils),function(a,b,c,d){b.State=b.Component.extend({construct:function(a){var b=this;b._super(a),b.apiEnabled=!!window.history&&!!history.replaceState,b.opt=b.tmpl.opt.state,b.enabled=b.opt.enabled,b.pushOrReplace=b.isPushOrReplace(b.opt.pushOrReplace)?b.opt.pushOrReplace:"replace";var c=d.escapeRegExp(b.tmpl.id),e=d.escapeRegExp(b.opt.values),f=d.escapeRegExp(b.opt.pair);b.regex={exists:new RegExp("^#"+c+"\\"+e+".+?"),values:new RegExp("(\\w+)"+f+"([^"+e+"]+)","g")}},destroy:function(){var a=this;a.clear(),a.opt=a.regex={},a._super()},isPushOrReplace:function(b){return-1!==a.inArray(b,["push","replace"])},exists:function(){return this.regex.exists.test(location.hash)&&this.regex.values.test(location.hash)},parse:function(){var b=this,d={};if(b.exists())if(b.enabled){d.id=b.tmpl.id;var e=location.hash.match(b.regex.values);a.each(e,function(e,f){var g=f.split(b.opt.pair);2===g.length&&(d[g[0]]=-1===g[1].indexOf(b.opt.array)?decodeURIComponent(g[1]):a.map(g[1].split(b.opt.array),function(a){return decodeURIComponent(a)}),c.string(d[g[0]])&&!isNaN(d[g[0]])&&(d[g[0]]=parseInt(d[g[0]])))})}else b.apiEnabled?history.replaceState(null,"",location.pathname+location.search):location.hash="#";return d},hashify:function(b){var d=this;if(c.hash(b)){var e=[];return a.each(b,function(b,f){c.empty(f)||"id"===b||(f=c.array(f)?a.map(f,function(a){return encodeURIComponent(a)}).join("+"):encodeURIComponent(f),e.push(b+d.opt.pair+f))}),e.length>0&&e.unshift("#"+d.tmpl.id),e.join(d.opt.values)}return""},replace:function(a){var b=this;if(b.enabled&&b.apiEnabled){a.id=b.tmpl.id;var d=b.hashify(a),e=c.empty(d);history.replaceState(e?null:a,"",e?location.pathname+location.search:d)}},push:function(a){var b=this;if(b.enabled&&b.apiEnabled){a.id=b.tmpl.id;var d=b.hashify(a),e=c.empty(d);history.pushState(e?null:a,"",e?location.pathname+location.search:d)}},update:function(a,b){var c=this;c.enabled&&c.apiEnabled&&(b=c.isPushOrReplace(b)?b:c.pushOrReplace,c[b](a))},clear:function(){this.exists()&&this.replace({})},initial:function(){var a=this,b=a.tmpl,c={};return b.pages&&b.pages.current>1&&(c.p=b.pages.current),c},get:function(a){var c=this,d=c.tmpl,e={};return a instanceof b.Item&&(e.i=a.id),d.pages&&d.pages.isValid(d.pages.current)&&(e.p=d.pages.current),e},set:function(a){var b=this,d=b.tmpl;if(c.hash(a)){d.items.reset();var e=d.items.get(a.i);if(d.pages){d.pages.rebuild();var f=d.pages.number(a.p);e&&!d.pages.contains(f,e)&&(f=d.pages.find(e),f=0!==f?f:1),d.pages.set(f,!c.empty(a),!1),e&&d.pages.contains(f,e)&&e.scrollTo()}else d.items.detach(d.items.all()),d.items.create(d.items.available(),!0),e&&e.scrollTo();c.empty(a.i)||(a.i=null,b.replace(a))}}}),b.template.configure("core",{state:{enabled:!1,pushOrReplace:"replace",values:"/",pair:":",array:"+"}}),b.components.register("state",b.State)}(FooGallery.$,FooGallery,FooGallery.utils.is,FooGallery.utils.str),function(a,b,c,d,e,f){b.Item=b.Component.extend({construct:function(a,b){var c=this;c._super(a),c.cls=a.cls.item,c.il8n=a.il8n.item,c.sel=a.sel.item,c.isAttached=!1,c.isCreated=!1,c.isLoading=!1,c.isLoaded=!1,c.isError=!1,c.isParsed=!1,c.$el=null,c.$inner=null,c.$anchor=null,c.$image=null,c.$caption=null,b=f.extend({},a.opt.item,b),c.id=b.id,c.href=b.href,c.src=b.src,c.srcset=b.srcset,c.width=b.width,c.height=b.height,c.title=b.title,c.alt=b.alt,c.caption=d.empty(b.caption)?c.title:b.caption,c.description=d.empty(b.description)?c.alt:b.description,c.attr=b.attr,c.tags=b.tags,c.maxWidth=b.maxWidth,c.maxCaptionLength=b.maxCaptionLength,c.maxDescriptionLength=b.maxDescriptionLength,c.showCaptionTitle=b.showCaptionTitle,c.showCaptionDescription=b.showCaptionDescription,c._thumbUrl=null,c._load=null},destroy:function(){var a=this;return a.tmpl.raise("destroy-item").isDefaultPrevented()||(a.isParsed&&!a.isAttached?a.append():!a.isParsed&&a.isAttached&&a.detach(),a._super()),null===a.tmpl},parse:function(c){var e=this,f=e.tmpl.opt,g=e.cls,h=e.sel,i=a(c);return!e.tmpl.raise("parse-item",[e,i]).isDefaultPrevented()&&(e.isCreated=i.is(h.elem))&&(e.$el=i.data(b.dataItem,e),e.$inner=e.$el.find(h.inner),e.$anchor=e.$el.find(h.anchor).on("click.foogallery",{self:e},e.onAnchorClick),e.$image=e.$anchor.find(h.image),e.$caption=e.$el.find(h.caption.elem).on("click.foogallery",{self:e},e.onCaptionClick),e.isAttached=e.$el.parent().length>0,e.isLoading=e.$el.is(h.loading),e.isLoaded=e.$el.is(h.loaded),e.isError=e.$el.is(h.error),e.id=e.$anchor.data("id"),e.tags=e.$anchor.data("tags"),e.href=e.$anchor.attr("href"),e.src=e.$image.attr(f.src),e.srcset=e.$image.attr(f.srcset),e.width=parseInt(e.$image.attr("width")),e.height=parseInt(e.$image.attr("height")),e.title=e.$image.attr("title"),e.alt=e.$image.attr("alt"),e.caption=e.$anchor.data("title")||e.$anchor.data("captionTitle"),e.description=e.$anchor.data("description")||e.$anchor.data("captionDesc"),d.empty(e.caption)&&(e.caption=e.title),d.empty(e.description)&&(e.description=e.alt),d.number(e.maxCaptionLength)&&e.maxCaptionLength>0&&!d.empty(e.caption)&&d.string(e.caption)&&e.caption.length>e.maxCaptionLength&&e.$caption.find(h.caption.title).html(e.caption.substr(0,e.maxCaptionLength)+"&hellip;"),d.number(e.maxDescriptionLength)&&e.maxDescriptionLength>0&&!d.empty(e.description)&&d.string(e.description)&&e.description.length>e.maxDescriptionLength&&e.$caption.find(h.caption.description).html(e.description.substr(0,e.maxDescriptionLength)+"&hellip;"),0===e.$el.find(h.loader).length&&e.$el.append(a("<div/>",{class:g.loader})),d.empty(e.$image.prop("src"))&&e.$image.prop("src",e.tmpl.items.placeholder(e.width,e.height)),!e.isCreated||!e.isAttached||e.isLoading||e.isLoaded||e.isError||e.$el.addClass(g.idle),e.fix(),e.isParsed=!0),e.isParsed&&e.tmpl.raise("parsed-item",[e]),e.isParsed},create:function(){var c=this;if(!c.isCreated&&d.string(c.href)&&d.string(c.src)&&d.number(c.width)&&d.number(c.height)){if(!c.tmpl.raise("create-item",[c]).isDefaultPrevented()){var e=c.tmpl.opt,f=c.cls,g=c.attr;g.elem.class=f.elem+" "+f.idle,g.inner.class=f.inner,g.anchor.class=f.anchor,g.anchor.href=c.href,g.anchor["data-id"]=c.id,g.anchor["data-title"]=c.caption,g.anchor["data-description"]=c.description,d.empty(c.tags)||(g.anchor["data-tags"]=JSON.stringify(c.tags)),g.image.class=f.image,g.image.src=c.tmpl.items.placeholder(c.width,c.height),g.image[e.src]=c.src,g.image[e.srcset]=c.srcset,g.image.width=c.width,g.image.height=c.height,g.image.title=c.title,g.image.alt=c.alt,c.$el=a("<div/>").attr(g.elem).data(b.dataItem,c),c.$inner=a("<figure/>").attr(g.inner).appendTo(c.$el),c.$anchor=a("<a/>").attr(g.anchor).appendTo(c.$inner).on("click.foogallery",{self:c},c.onAnchorClick),c.$image=a("<img/>").attr(g.image).appendTo(c.$anchor),f=c.cls.caption,g=c.attr.caption,g.elem.class=f.elem,c.$caption=a("<figcaption/>").attr(g.elem).on("click.foogallery",{self:c},c.onCaptionClick)
10
- ;var h=!d.empty(c.caption),i=!d.empty(c.description);if(h||i){g.inner.class=f.inner,g.title.class=f.title,g.description.class=f.description;var j=a("<div/>").attr(g.inner).appendTo(c.$caption);if(h){var k;k=d.number(c.maxCaptionLength)&&c.maxCaptionLength>0&&d.string(c.caption)&&c.caption.length>c.maxCaptionLength?a("<div/>").attr(g.title).html(c.caption.substr(0,c.maxCaptionLength)+"&hellip;"):a("<div/>").attr(g.title).html(c.caption),j.append(k)}if(i){var l;l=d.number(c.maxDescriptionLength)&&c.maxDescriptionLength>0&&d.string(c.description)&&c.description.length>c.maxDescriptionLength?a("<div/>").attr(g.description).html(c.description.substr(0,c.maxDescriptionLength)+"&hellip;"):a("<div/>").attr(g.description).html(c.description),j.append(l)}}c.$caption.appendTo(c.$inner),0===c.$el.find(c.sel.loader).length&&c.$el.append(a("<div/>",{class:c.cls.loader})),c.isCreated=!0}c.isCreated&&c.tmpl.raise("created-item",[c])}return c.isCreated},append:function(){var a=this;if(a.isCreated&&!a.isAttached){a.tmpl.raise("append-item",[a]).isDefaultPrevented()||(a.tmpl.$el.append(a.$el),a.fix(),a.isAttached=!0),a.isAttached&&a.tmpl.raise("appended-item",[a])}return a.isAttached},detach:function(){var a=this;if(a.isCreated&&a.isAttached){a.tmpl.raise("detach-item",[a]).isDefaultPrevented()||(a.$el.detach(),a.unfix(),a.isAttached=!1),a.isAttached||a.tmpl.raise("detached-item",[a])}return!a.isAttached},load:function(){var b=this;if(d.promise(b._load))return b._load;if(!b.isCreated||!b.isAttached)return e.rejectWith("not created or attached");if(b.tmpl.raise("load-item",[b]).isDefaultPrevented())return e.rejectWith("default prevented");var c=b.cls,f=b.$image.get(0),g=f.src;return b.isLoading=!0,b.$el.removeClass(c.idle).removeClass(c.loaded).removeClass(c.error).addClass(c.loading),b._load=a.Deferred(function(a){d.undef(window.InstallTrigger)||(f.src=""),f.onload=function(){f.onload=f.onerror=null,b.isLoading=!1,b.isLoaded=!0,b.$el.removeClass(c.loading).addClass(c.loaded),b.unfix(),b.tmpl.raise("loaded-item",[b]),a.resolve(b)},f.onerror=function(){f.onload=f.onerror=null,b.isLoading=!1,b.isError=!0,b.$el.removeClass(c.loading).addClass(c.error),d.string(g)&&b.$image.prop("src",g),b.tmpl.raise("error-item",[b]),a.reject(b)},f.src=b.getThumbUrl()}).promise()},fix:function(){var a=this;if(a.isCreated&&!a.isLoading&&!a.isLoaded&&!a.isError){var b=a.width,c=a.height;if(!isNaN(b)&&!isNaN(c)){var e=d.fn(a.maxWidth)?a.maxWidth(a):a.$image.width();e<=0&&(e=b);var f=e/b,g=c*f;a.$image.css({width:e,height:g})}}return a},unfix:function(){var a=this;return a.isCreated&&a.$image.css({width:"",height:""}),a},getThumbUrl:function(a){a=!!d.boolean(a)&&a;var c=this;return!a&&d.string(c._thumbUrl)?c._thumbUrl:c._thumbUrl=b.parseSrc(c.src,c.width,c.height,c.srcset,c.$anchor.innerWidth(),c.$anchor.innerHeight())},scrollTo:function(a){var b=this;if(b.isAttached){var d=b.bounds(),e=c.getViewportBounds();switch(a){case"top":d.left+=d.width/2-e.width/2,d.top-=e.height/5;break;default:d.left+=d.width/2-e.width/2,d.top+=d.height/2-e.height/2}window.scrollTo(d.left,d.top)}return b},bounds:function(){return this.isAttached?c.getElementBounds(this.$el):null},intersects:function(a){return!!this.isAttached&&this.bounds().intersects(a)},onAnchorClick:function(a){var b=a.data.self,c=b.tmpl.state.get(b);b.tmpl.state.update(c)},onCaptionClick:function(b){var c=b.data.self;a(b.target).is(c.sel.caption.all)&&c.$anchor.length>0&&c.$anchor.get(0).click()}}),b.template.configure("core",{item:{id:"",href:"",src:"",srcset:"",width:0,height:0,title:"",alt:"",caption:"",description:"",tags:[],maxWidth:null,maxCaptionLength:0,maxDescriptionLength:0,showCaptionTitle:!0,showCaptionDescription:!0,attr:{elem:{},inner:{},anchor:{},image:{},caption:{elem:{},inner:{},title:{},description:{}}}}},{item:{elem:"fg-item",inner:"fg-item-inner",anchor:"fg-thumb",image:"fg-image",loader:"fg-loader",idle:"fg-idle",loading:"fg-loading",loaded:"fg-loaded",error:"fg-error",caption:{elem:"fg-caption",inner:"fg-caption-inner",title:"fg-caption-title",description:"fg-caption-desc"}}}),b.components.register("item",b.Item)}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.obj),function(a,b,c,d,e){b.Items=b.Component.extend({construct:function(a){var b=this;b._super(a),b.idMap={},b._fetched=null,b._arr=[],b._available=[],b._canvas=document.createElement("canvas");var d=b.tmpl.cls.item.caption;b.tmpl.sel.item.caption.all=c.selectify([d.elem,d.inner,d.title,d.description])},destroy:function(){var b=this,c=b.all(),d=[];c.length>0&&(b.tmpl.raise("destroy-items",[c]),d=a.map(c,function(a){return a.destroy()?a:null}),d.length>0&&b.tmpl.raise("destroyed-items",[d])),b.idMap={},b._canvas=b._fetched=null,b._arr=[],b._available=[],b._super()},fetch:function(b){var c=this;if(!b&&d.promise(c._fetched))return c._fetched;var e=c.tmpl,f=e.sel,g=e.opt.items,h=a.Deferred(),i=c.make(e.$el.find(f.item.elem));return d.empty(g)?(i.push.apply(i,c.make(window[e.id+"-items"])),h.resolve(i)):d.array(g)?(i.push.apply(i,c.make(g)),h.resolve(i)):d.string(g)?a.get(g).then(function(a){i.push.apply(i,c.make(a)),h.resolve(i)},function(a,b,c){console.log("FooGallery: GET items error.",g,a,b,c),h.resolve(i)}):h.resolve(i),h.then(function(a){c._arr=a,c.idMap=c.createIdMap(a),c.setAvailable(c.all())}),c._fetched=h.promise()},all:function(){return this._arr.slice()},available:function(){return this._available.slice()},get:function(a){return!d.empty(a)&&this.idMap[a]?this.idMap[a]:null},setAvailable:function(a){this._available=d.array(a)?a:[]},reset:function(){this.setAvailable(this.all())},placeholder:function(a,c){return this._canvas&&this._canvas.toDataURL&&d.number(a)&&d.number(c)?(this._canvas.width=a,this._canvas.height=c,this._canvas.toDataURL()):b.emptyImage},loadable:function(b){var e,f=this,g=f.tmpl.opt;return g.lazy&&(e=c.getViewportBounds(g.viewport)),d.array(b)?a.map(b,function(a){return a.isCreated&&a.isAttached&&!a.isLoading&&!a.isLoaded&&!a.isError&&(!g.lazy||g.lazy&&a.intersects(e))?a:null}):[]},creatable:function(c){return d.array(c)?a.map(c,function(a){return a instanceof b.Item&&!a.isCreated?a:null}):[]},appendable:function(c){return d.array(c)?a.map(c,function(a){return a instanceof b.Item&&a.isCreated&&!a.isAttached?a:null}):[]},detachable:function(c){return d.array(c)?a.map(c,function(a){return a instanceof b.Item&&a.isCreated&&a.isAttached?a:null}):[]},jquerify:function(b){return a(a.map(b,function(a){return a.$el.get()}))},make:function(c){var e=this,f=[];if(d.jq(c)||d.array(c)){var g=[],h=a.makeArray(c);if(0===h.length)return f;e.tmpl.raise("make-items",[h]).isDefaultPrevented()||(f=a.map(h,function(a){var c=b.components.make("item",e.tmpl,d.hash(a)?a:{});return d.element(a)?c.parse(a)?(g.push(c),c):null:c})),f.length>0&&e.tmpl.raise("made-items",[f]),g.length>0&&e.tmpl.raise("parsed-items",[g])}return f},create:function(b,c){var e=this,f=[],g=e.creatable(b);if(g.length>0){e.tmpl.raise("create-items",[g]).isDefaultPrevented()||(f=a.map(g,function(a){return a.create()?a:null})),f.length>0&&e.tmpl.raise("created-items",[f])}return d.boolean(c)&&c?e.append(b):f},append:function(b){var c=this,d=[],e=c.appendable(b);if(e.length>0){c.tmpl.raise("append-items",[e]).isDefaultPrevented()||(d=a.map(e,function(a){return a.append()?a:null})),d.length>0&&c.tmpl.raise("appended-items",[d])}return d},detach:function(b){var c=this,d=[],e=c.detachable(b);if(e.length>0){c.tmpl.raise("detach-items",[e]).isDefaultPrevented()||(d=a.map(e,function(a){return a.detach()?a:null})),d.length>0&&c.tmpl.raise("detached-items",[d])}return d},load:function(b){var c=this;if(b=c.loadable(b),b.length>0){if(!c.tmpl.raise("load-items",[b]).isDefaultPrevented()){var d=a.map(b,function(a){return a.load()});return e.when(d).done(function(a){c.tmpl.raise("loaded-items",[a])})}}return e.resolveWith([])},createIdMap:function(b){var c={};return a.each(b,function(a,b){d.empty(b.id)&&(b.id=""+(a+1)),c[b.id]=b}),c}}),b.components.register("items",b.Items)}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.str),function(a,b,c,d){b.Paging=b.Component.extend({construct:function(a){var b=this;b._super(a),b.opt=b.tmpl.opt.paging,b.cls=b.tmpl.cls.paging,b.il8n=b.tmpl.il8n.paging,b.sel=b.tmpl.sel.paging,b.pushOrReplace=b.opt.pushOrReplace,b.type=b.opt.type,b.theme=b.opt.theme,b.size=b.opt.size,b.position=b.opt.position,b.scrollToTop=b.opt.scrollToTop,b.current=0,b.total=0,b.ctrls=[],b._arr=[]},destroy:function(){var b=this;b._arr.splice(0,b._arr.length),a.each(b.ctrls.splice(0,b.ctrls.length),function(a,b){b.destroy()}),b._super()},build:function(){var a=this,c=a.tmpl.items.available();if(a.total=a.size>0&&c.length>0?Math.ceil(c.length/a.size):1,a.total<=1)a._arr.push(c),a.tmpl.items.detach(a._arr[0]);else for(var d=0;d<a.total;d++)a._arr.push(c.splice(0,a.size)),a.tmpl.items.detach(a._arr[d]);if(a.total>1&&b.paging.hasCtrl(a.type)){var e,f,g=a.position;"both"!==g&&"top"!==g||(e=b.paging.makeCtrl(a.type,a.tmpl,a,"top"),e.create()&&(e.append(),a.ctrls.push(e))),"both"!==g&&"bottom"!==g||(f=b.paging.makeCtrl(a.type,a.tmpl,a,"bottom"),f.create()&&(f.append(),a.ctrls.push(f)))}},rebuild:function(){var b=this;b.current=0,b.total=0,b._arr.splice(0,b._arr.length),a.each(b.ctrls.splice(0,b.ctrls.length),function(a,b){b.destroy()}),b.build()},all:function(){return this._arr.slice()},available:function(){return this.get(this.current)},controls:function(b){var c=this;c.isValid(b)&&a.each(c.ctrls,function(a,c){c.update(b)})},isValid:function(a){return d.number(a)&&a>0&&a<=this.total},number:function(a){return this.isValid(a)?a:0===this.current?1:this.current},create:function(a){var b=this;a=b.number(a);var c=a-1;b.tmpl.items.create(b._arr[c],!0);for(var d=0,e=b._arr.length;d<e;d++)d!==c&&b.tmpl.items.detach(b._arr[d]);b.current=a},get:function(a){var b=this;return b.isValid(a)?(a=b.number(a),b._arr[a-1]):[]},set:function(a,b,c){var e=this;if(e.isValid(a)){e.controls(a);var f,g=e.number(a);if(g!==e.current){var h=e.current,i=function(){if(c=!d.boolean(c)||c,c&&1===e.current&&!e.tmpl.state.exists()&&(f=e.tmpl.state.get(),e.tmpl.state.update(f,e.pushOrReplace)),e.create(g),c&&(f=e.tmpl.state.get(),e.tmpl.state.update(f,e.pushOrReplace)),e.scrollToTop&&d.boolean(b)&&b){var a=e.get(e.current);a.length>0&&a[0].scrollTo("top")}e.tmpl.raise("after-page-change",[e.current,h])};return!e.tmpl.raise("before-page-change",[e.current,g,i]).isDefaultPrevented()&&(i(),!0)}}return!1},find:function(b){for(var c=this,d=0,e=c._arr.length;d<e;d++)if(-1!==a.inArray(b,c._arr[d]))return d+1;return 0},contains:function(b,c){var d=this.get(b);return-1!==a.inArray(c,d)},first:function(){this.goto(1)},last:function(){this.goto(this._arr.length)},prev:function(){this.goto(this.current-1)},next:function(){this.goto(this.current+1)},goto:function(a){var b=this;b.set(a,!0)&&b.tmpl.loadAvailable()}}),b.PagingControl=b.Component.extend({construct:function(a,b,c){var d=this;d._super(a),d.pages=b,d.position=c,d.$container=null},create:function(){var b=this;return b.$container=a("<nav/>",{class:b.pages.cls.container}).addClass(b.pages.theme),!0},destroy:function(){var a=this;a.$container.remove(),a.$container=null},append:function(){var a=this;"top"===a.position?a.$container.insertBefore(a.tmpl.$el):a.$container.insertAfter(a.tmpl.$el)},update:function(a){}}),b.paging.register("default",b.Paging,null,{type:"none",theme:"fg-light",size:30,pushOrReplace:"push",position:"none",scrollToTop:!0},{container:"fg-paging-container"},null,-100)}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is),function(a,b,c,d){b.Dots=b.Paging.extend({}),b.DotsControl=b.PagingControl.extend({construct:function(b,c,d){this._super(b,c,d),this.$container=a(),this.$list=a(),this.$items=a()},create:function(){for(var b,c=this,d=c.pages.cls,e=c.pages.il8n,f=[],g=a("<ul/>",{class:d.list}),h=0,i=c.pages.total;h<i;h++)f.push(b=c.createItem(h+1,e.page)),g.append(b);return c.$list=g,c.$container=a("<nav/>",{class:d.container}).addClass(c.pages.theme).append(g),c.$items=a(a.map(f,function(a){return a.get()})),!0},append:function(){var a=this;"top"===a.position?a.$container.insertBefore(a.tmpl.$el):a.$container.insertAfter(a.tmpl.$el)},destroy:function(){var b=this,c=b.pages.sel;b.$list.find(c.link).off("click.foogallery",b.onLinkClick),b.$container.remove(),b.$container=a(),b.$list=a(),b.$items=a()},update:function(a){this.setSelected(a-1)},setSelected:function(b){var c=this,e=c.pages.cls,f=c.pages.il8n,g=c.pages.sel;c.$items.filter(g.selected).removeClass(e.selected).each(function(b,c){var e=a(c),f=e.data("label"),h=e.find(g.reader);d.string(f)&&0!==h.length&&h.html(f)}),c.$items.eq(b).addClass(e.selected).each(function(b,c){var e=a(c),h=e.find(g.reader),i=h.html();d.string(i)&&0!==h.length&&(e.data("label",i),h.html(f.current))})},createItem:function(b,c,e,f,g){e=d.string(e)?e:b,c=d.string(c)?c:"";var h=this,i=h.pages.opt,j=h.pages.cls,k=a("<a/>",{class:j.link,href:"#page-"+b}).html(e).on("click.foogallery",{self:h,page:b},h.onLinkClick);d.empty(c)||k.attr("title",c.replace(/\{PAGE}/g,b).replace(/\{LIMIT}/g,i.limit+"")),g=d.string(g)?g:c,d.empty(g)||k.prepend(a("<span/>",{class:j.reader,text:g.replace(/\{PAGE}/g,"").replace(/\{LIMIT}/g,i.limit+"")}));var l=a("<li/>",{class:j.item}).append(k);return f=d.string(f)?f:"",d.empty(f)||l.addClass(f),l},onLinkClick:function(b){b.preventDefault();var c=b.data.self,d=b.data.page,e=c.pages.sel;a(this).closest(e.item).is(e.disabled)||(c.pages.set(d,!0),c.tmpl.loadAvailable())}}),b.paging.register("dots",b.Dots,b.DotsControl,{type:"dots",position:"both",pushOrReplace:"push"},{list:"fg-dots",item:"fg-dot-item",link:"fg-dot-link",disabled:"fg-disabled",selected:"fg-selected",visible:"fg-visible",reader:"fg-sr-only"},{current:"Current page",page:"Page {PAGE}"})}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is),function(a,b,c){b.DefaultTemplate=b.Template.extend({}),b.template.register("default",b.DefaultTemplate,null,{container:"foogallery fg-default"})}(FooGallery.$,FooGallery,FooGallery.utils),function(a,b,c,d){b.MasonryTemplate=b.Template.extend({construct:function(a,b){this._super(a,b),this.masonry=null,this.style=null,this.$columnWidth=null},getStylesheet:function(){var a=this;return null===a.style&&(a.style=document.createElement("style"),a.style.appendChild(document.createTextNode("")),document.head.appendChild(a.style)),a.style.sheet},onPreInit:function(b,c){var e=c.sel,f=c.cls;f.layouts=a.map(f.layout,function(a){return a}).join(" "),d.string(f.layout[c.template.layout])||(c.template.layout="col4");var g,h,i="fixed"===c.template.layout;if(c.template.isFitWidth=i,c.template.percentPosition=!i,c.template.transitionDuration=0,c.template.itemSelector=e.item.elem,c.$el.removeClass(f.layouts).addClass(f.layout[c.template.layout]),i||(0===c.$el.find(e.gutterWidth).length&&c.$el.prepend(a("<div/>").addClass(f.gutterWidth)),c.template.gutter=e.gutterWidth),0===c.$el.find(e.columnWidth).length&&c.$el.prepend(a("<div/>").addClass(f.columnWidth)),i&&d.number(c.template.columnWidth)){var j=c.$el.find(e.columnWidth).width(c.template.columnWidth);g=c.getStylesheet(),h="#"+c.id+e.container+" "+e.item.elem+" { width: "+j.outerWidth()+"px; }",g.insertRule(h,0)}c.template.columnWidth=e.columnWidth,i&&d.number(c.template.gutter)&&(g=c.getStylesheet(),h="#"+c.id+e.container+" "+e.item.elem+" { margin-bottom: "+c.template.gutter+"px; }",g.insertRule(h,0)),c.masonry=new Masonry(c.$el.get(0),c.template)},onInit:function(a,b){b.masonry.layout()},onPostInit:function(a,b){b.masonry.layout()},onReady:function(a,b){b.masonry.layout()},onDestroy:function(a,b){b.$el.find(b.sel.columnWidth).remove(),b.$el.find(b.sel.gutterWidth).remove(),b.masonry instanceof Masonry&&b.masonry.destroy(),b.style&&b.style.parentNode&&b.style.parentNode.removeChild(b.style)},onLayout:function(a,b){b.masonry.layout()},onParsedItems:function(a,b,c){b.masonry.layout()},onAppendedItems:function(a,b,c){c=b.items.jquerify(c),c=b.masonry.addItems(c),b.masonry.layoutItems(c,!0)},onDetachItem:function(a,b,c){a.isDefaultPrevented()||(a.preventDefault(),b.masonry.remove(c.$el),c.isAttached=!1,c.unfix())},onDetachedItems:function(a,b,c){b.masonry.layout()},onLoadedItems:function(a,b,c){b.masonry.layout()}}),b.template.register("masonry",b.MasonryTemplate,{template:{layout:"col4"}},{container:"foogallery fg-masonry",columnWidth:"fg-column-width",gutterWidth:"fg-gutter-width",layout:{fixed:"fg-masonry-fixed",col2:"fg-masonry-2col",col3:"fg-masonry-3col",col4:"fg-masonry-4col",col5:"fg-masonry-5col"}})}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is),function(a,b,c,d){b.Justified=c.Class.extend({construct:function(c,d){this.$el=a(c),this.options=a.extend(!0,{},b.Justified.defaults,d),this._items=[]},init:function(){var b=this;d.string(b.options.maxRowHeight)&&(b.options.maxRowHeight.indexOf("%")?b.options.maxRowHeight=b.options.rowHeight*(parseInt(b.options.maxRowHeight)/100):b.options.maxRowHeight=parseInt(b.options.maxRowHeight)),b.layout(!0),a(window).on("resize.justified",{self:b},b.onWindowResize)},destroy:function(){a(window).off("resize.justified"),a.each(this._items,function(a,b){b.$item.removeAttr("style").removeClass("fg-positioned")}),this.$el.removeAttr("style")},parse:function(){var b=this,c=b.$el.is(":visible"),d=a("<div/>",{class:b.$el.attr("class")}).css({position:"absolute",top:0,left:-9999,visibility:"hidden"}).appendTo("body");return b._items=b.$el.find(b.options.itemSelector).removeAttr("style").removeClass("fg-positioned").map(function(e,f){var g,h=a(f),i=0,j=0;if(c)i=h.outerWidth(),j=h.outerHeight();else{var k=h.clone();k.appendTo(d),i=k.outerWidth(),j=k.outerHeight()}return g=b.options.rowHeight/j,{index:e,width:i*g,height:b.options.rowHeight,top:0,left:0,$item:h}}).get(),d.remove(),b._items},round:function(a){return Math.round(a)},getContainerWidth:function(){var a=this;return a.$el.is(":visible")?a.$el.width():a.$el.parents(":visible:first").width()},layout:function(a,b){a=!!d.boolean(a)&&a,b=!d.boolean(b)||b,(a||0===this._items.length)&&this.parse();for(var c,e=this,f=e.getContainerWidth(),g=e.rows(f),h=0,i=0,j=g.length;i<j;i++)c=g[i],h=i===j-1?e.lastRow(c,f,h):e.justify(c,f,h),e.render(c);e.$el.height(h),b&&e.getContainerWidth()<f&&e.layout(!1,!1)},render:function(a){for(var b,c=0,d=a.items.length;c<d;c++)b=a.items[c],a.visible?b.$item.css({width:b.width,height:b.height,top:b.top,left:b.left,display:"",maxHeight:this.options.maxRowHeight>0?this.options.maxRowHeight:""}).addClass("fg-positioned"):b.$item.css("display","none")},lastRow:function(a,b,c){var d=this;switch(d.options.lastRow){case"hide":a.visible=!1;break;case"justify":c=d.justify(a,b,c);break;case"nojustify":c=a.width/b>d.options.justifyThreshold?d.justify(a,b,c):d.position(a,b,c,"left");break;case"right":case"center":case"left":c=d.position(a,b,c,d.options.lastRow);break;default:c=d.position(a,b,c,"left")}return c},justify:function(a,b,c){var d=this,e=0,f=d.options.margins*(a.items.length-1),g=(b-f)/a.width;a.index>0&&(c+=d.options.margins),a.top=c,a.width=d.round(a.width*g),a.height=d.round(a.height*g);for(var h,i=0,j=a.items.length;i<j;i++)h=a.items[i],h.width=d.round(h.width*g),h.height=d.round(h.height*g),h.top=c,i>0&&(e+=d.options.margins),h.left=e,e+=h.width;return c+(a.height>d.options.maxRowHeight?d.options.maxRowHeight:a.height)},position:function(a,b,c,d){var e=this,f=a.items[a.items.length-1],g=b-(f.left+f.width);a.index>0&&(c+=e.options.margins),a.top=c;for(var h,i=0,j=a.items.length;i<j;i++)h=a.items[i],h.top=c,"center"===d?h.left+=g/2:"right"===d&&(h.left+=g);return c+a.height},items:function(){return a.map(this._items,function(a){return{index:a.index,width:a.width,height:a.height,$item:a.$item,top:a.top,left:a.left}})},rows:function(a){for(var b=this,c=b.items(),d=[],e=c.length>0,f=-1,g=0;e;){f+=1,f>0&&(g+=b.options.margins);for(var h,i,j,k={index:f,visible:!0,top:g,width:0,height:b.options.rowHeight,items:[]},l=[],m=0,n=0,o=c.length;n<o&&(i=c[n],!((h=k.width+i.width)>a&&n>0));n++)h>a&&0==n&&(h=a,j=a/i.width,i.width=b.round(i.width*j),i.height=b.round(i.height*j),k.height=i.height),i.top=k.top,n>0&&(m+=b.options.margins),i.left=m,m+=i.width,k.width=h,k.items.push(i),l.push(n);if(0===l.length){e=!1;break}l.sort(function(a,b){return b-a});for(var p=0,q=l.length;p<q;p++)c.splice(l[p],1);d.push(k),e=c.length>0}return d},onWindowResize:function(a){a.data.self.layout()}}),b.Justified.defaults={itemSelector:".fg-item",rowHeight:150,maxRowHeight:"200%",margins:0,lastRow:"center",justifyThreshold:.5}}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is),function(a,b,c){b.JustifiedTemplate=b.Template.extend({onPreInit:function(a,c){c.justified=new b.Justified(c.$el.get(0),c.template)},onInit:function(a,b){b.justified.init()},onPostInit:function(a,b){b.justified.layout(!0)},onReady:function(a,b){b.justified.layout(!0)},onDestroy:function(a,b){b.justified.destroy()},onLayout:function(a,b){b.justified.layout(!0)},onParsedItems:function(a,b,c){b.justified.layout(!0)},onAppendedItems:function(a,b,c){b.justified.layout(!0)},onDetachedItems:function(a,b,c){b.justified.layout(!0)}}),b.template.register("justified",b.JustifiedTemplate,null,{container:"foogallery fg-justified"})}(FooGallery.$,FooGallery,FooGallery.utils),function(a,b,c,d){b.Portfolio=c.Class.extend({construct:function(c,d){this.$el=a(c),this.options=a.extend(!0,{},b.Portfolio.defaults,d),this._items=[]},init:function(){var b=this;a(window).on("resize.portfolio",{self:b},b.onWindowResize),b.layout(!0)},destroy:function(){a(window).off("resize.portfolio"),a.each(this._items,function(a,b){b.$item.removeAttr("style").removeClass("fg-positioned")}),this.$el.removeAttr("style")},parse:function(){var b=this,c=b.$el.is(":visible"),d=a("<div/>",{class:b.$el.attr("class")}).css({position:"absolute",top:0,left:-9999,visibility:"hidden"}).appendTo("body");return b._items=b.$el.find(".fg-item").removeAttr("style").removeClass("fg-positioned").map(function(b,e){var f=a(e),g=f.find(".fg-thumb"),h=f.find(".fg-image"),i=0,j=0;if(f.find(".fg-caption").css("max-width",parseFloat(h.attr("width"))),h.css({width:h.attr("width"),height:h.attr("height")}),c)i=f.outerWidth(),j=f.outerHeight();else{var k=f.clone();k.appendTo(d),i=k.outerWidth(),j=k.outerHeight()}return h.css({width:"",height:""}),{index:b,width:i,height:j,top:0,left:0,$item:f,$thumb:g}}).get(),d.remove(),b._items},round:function(a){return Math.round(2*a)/2},getContainerWidth:function(){var a=this;return a.$el.is(":visible")?a.$el.width():a.$el.parents(":visible:first").width()},layout:function(a,b){a=!!d.boolean(a)&&a,b=!d.boolean(b)||b,(a||0===this._items.length)&&this.parse();for(var c,e=this,f=e.getContainerWidth(),g=e.rows(f),h=0,i=0,j=g.length;i<j;i++)c=g[i],h=e.position(c,f,h,e.options.align),e.render(c);e.$el.height(h),b&&e.getContainerWidth()<f&&e.layout(!1,!1)},render:function(a){for(var b,c=0,d=a.items.length;c<d;c++)b=a.items[c],a.visible?b.$item.css({width:b.width,height:a.height,top:b.top,left:b.left,display:""}).addClass("fg-positioned"):b.$item.css("display","none")},position:function(a,b,c,d){var e=this,f=a.items[a.items.length-1],g=b-(f.left+f.width);a.index>0&&(c+=e.options.gutter),a.top=c;for(var h,i=0,j=a.items.length;i<j;i++)h=a.items[i],h.top=c,"center"===d?h.left+=g/2:"right"===d&&(h.left+=g);return c+a.height},items:function(){return a.map(this._items,function(a){return{index:a.index,width:a.width,height:a.height,$item:a.$item,$thumb:a.$thumb,top:a.top,left:a.left}})},rows:function(a){for(var b=this,c=b.items(),d=[],e=c.length>0,f=-1,g=0;e;){f+=1,f>0&&(g+=b.options.gutter);for(var h,i,j,k={index:f,visible:!0,top:g,width:0,height:0,items:[]},l=[],m=0,n=0,o=c.length;n<o&&(i=c[n],!((h=k.width+i.width)>a&&n>0));n++)h>a&&0==n&&(h=a,j=a/i.width,i.width=b.round(i.width*j),i.height=b.round(i.height*j),k.height=i.height),i.top=k.top,n>0&&(m+=b.options.gutter),n!==o-1&&(h+=b.options.gutter),i.left=m,m+=i.width,i.height>k.height&&(k.height=i.height),k.width=h,k.items.push(i),l.push(n);if(0===l.length){e=!1;break}l.sort(function(a,b){return b-a});for(var p=0,q=l.length;p<q;p++)c.splice(l[p],1);d.push(k),e=c.length>0}return d},onWindowResize:function(a){a.data.self.layout()}}),b.Portfolio.defaults={gutter:40,align:"center"}}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is),function(a,b,c){b.PortfolioTemplate=b.Template.extend({construct:function(a,b){this._super(a,b),this.portfolio=null},onPreInit:function(a,c){c.portfolio=new b.Portfolio(c.$el.get(0),c.template)},onInit:function(a,b){b.portfolio.init()},onPostInit:function(a,b){b.portfolio.layout(!0)},onReady:function(a,b){b.portfolio.layout(!0)},onDestroy:function(a,b){b.portfolio.destroy()},onLayout:function(a,b){b.portfolio.layout(!0)},onParsedItems:function(a,b,c){b.portfolio.layout(!0)},onAppendedItems:function(a,b,c){b.portfolio.layout(!0)},onDetachedItems:function(a,b,c){b.portfolio.layout(!0)}}),b.template.register("simple_portfolio",b.PortfolioTemplate,{gutter:40},{container:"foogallery fg-simple_portfolio"})}(FooGallery.$,FooGallery,FooGallery.utils),function(a,b,c,d){b.ImageViewerTemplate=b.Template.extend({construct:function(a,b){this._super(d.extend({},a,{paging:{pushOrReplace:"replace",theme:"fg-light",type:"default",size:1,position:"none",scrollToTop:!1}}),b),this.$inner=this.$el.find(".fiv-inner-container"),this.$current=this.$el.find(".fiv-count-current"),this.$total=this.$el.find(".fiv-count-total"),this.$prev=this.$el.find(".fiv-prev"),this.$next=this.$el.find(".fiv-next")},onInit:function(a,b){b.template.attachFooBox&&b.$el.on("foobox.previous",{self:b},b.onFooBoxPrev).on("foobox.next",{self:b},b.onFooBoxNext),b.$prev.on("click",{self:b},b.onPrevClick),b.$next.on("click",{self:b},b.onNextClick)},onFirstLoad:function(a,b){b.update()},onDestroy:function(a,b){b.template.attachFooBox&&b.$el.off({"foobox.previous":b.onFooBoxPrev,"foobox.next":b.onFooBoxNext}),b.$prev.off("click",b.onPrevClick),b.$next.off("click",b.onNextClick)},onAppendItem:function(a,b,c){a.preventDefault(),b.$inner.append(c.$el),c.fix(),c.isAttached=!0},update:function(){this.pages&&(this.$current.text(this.pages.current),this.$total.text(this.pages.total))},prev:function(){this.pages&&(this.pages.prev(),this.update())},next:function(){this.pages&&(this.pages.next(),this.update())},onFooBoxPrev:function(a){a.data.self.prev()},onFooBoxNext:function(a){a.data.self.next()},onPrevClick:function(a){a.preventDefault(),a.stopPropagation(),a.data.self.prev()},onNextClick:function(a){a.preventDefault(),a.stopPropagation(),a.data.self.next()}}),b.template.register("image-viewer",b.ImageViewerTemplate,{template:{attachFooBox:!1}},{container:"foogallery fg-image-viewer"})}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.obj),function(a,b,c){b.ThumbnailTemplate=b.Template.extend({construct:function(a,b){this._super(c.extend({},a,{paging:{type:"none"}}),b)}}),b.template.register("thumbnail",b.ThumbnailTemplate,null,{container:"foogallery fg-thumbnail"})}(FooGallery.$,FooGallery,FooGallery.utils.obj),function(a,b,c){function d(b,c,d,e){("after-page-change"===b.type&&0!==e||"ready"===b.type)&&a("body").trigger("post-load")}a(function(){a('[id^="foogallery-"]:not(.fg-ready)').foogallery({on:{"ready.foogallery after-page-change.foogallery":d}})}),b.ready(function(){a('[id^="foogallery-"].fg-ready').foogallery({on:{"ready.foogallery after-page-change.foogallery":d}})})}(FooGallery.$,FooGallery.utils,FooGallery.utils.is);
1
  /*
2
  * FooGallery - The Most Intuitive and Extensible Gallery Creation and Management Tool Ever Created for WordPress
3
+ * @version 1.0.23
4
  * @link
5
  * @copyright Steven Usher & Brad Vincent 2015
6
  * @license Released under the GPLv3 license.
7
  */
8
 
9
+ !function(a,b){b.$=a}(jQuery,window.FooGallery=window.FooGallery||{}),function(a){if(!a)return void console.warn("jQuery must be included in the page prior to the FooGallery.utils library.");var b={$:a,version:"0.0.8"};b.versionCompare=function(a,b){function c(a){for(var b=a.split("."),c=0,d=b.length;c<d;c++)b[c]=parseInt(b[c]),isNaN(b[c])&&(b[c]=0);return b}if(!/[\d.]/.test(a)||!/[\d.]/.test(b))return NaN;for(var d=c(a),e=c(b);d.length<e.length;)d.push(0);for(;e.length<d.length;)e.push(0);for(var f=0;f<d.length;++f){if(e.length==f)return 1;if(d[f]!=e[f])return d[f]>e[f]?1:-1}return d.length!=e.length?-1:0},!function(){try{return!!window.FooGallery.utils}catch(a){return!1}}()?window.FooGallery.utils=b:b.versionCompare(b.version,window.FooGallery.utils.version)>0?(console.warn("An older version of FooGallery.utils ("+window.FooGallery.utils.version+") already exists in the page, version "+b.version+" will override it."),window.FooGallery.utils=b):console.warn("A newer version of FooGallery.utils ("+window.FooGallery.utils.version+") already exists in the page, version "+b.version+" will not register itself.")}(jQuery),function(a,b){"0.0.8"===b.version&&(b.is={},b.is.array=function(a){return"[object Array]"===Object.prototype.toString.call(a)},b.is.boolean=function(a){return"[object Boolean]"===Object.prototype.toString.call(a)},b.is.element=function(a){return"object"==typeof HTMLElement?a instanceof HTMLElement:!!a&&"object"==typeof a&&null!==a&&1===a.nodeType&&"string"==typeof a.nodeName},b.is.empty=function(a){if(b.is.undef(a)||null===a)return!0;if(b.is.number(a)&&0==a)return!0;if(b.is.boolean(a)&&!1===a)return!0;if(b.is.string(a)&&0===a.length)return!0;if(b.is.array(a)&&0===a.length)return!0;if(b.is.jq(a)&&0===a.length)return!0;if(b.is.hash(a)){for(var c in a)if(a.hasOwnProperty(c))return!1;return!0}return!1},b.is.error=function(a){return"[object Error]"===Object.prototype.toString.call(a)},b.is.fn=function(a){return a===window.alert||"[object Function]"===Object.prototype.toString.call(a)},b.is.hash=function(a){return b.is.object(a)&&a.constructor===Object&&!a.nodeType&&!a.setInterval},b.is.jq=function(c){return!b.is.undef(a)&&c instanceof a},b.is.number=function(a){return"[object Number]"===Object.prototype.toString.call(a)&&!isNaN(a)},b.is.object=function(a){return"[object Object]"===Object.prototype.toString.call(a)&&!b.is.undef(a)&&null!==a},b.is.promise=function(a){return b.is.object(a)&&b.is.fn(a.then)&&b.is.fn(a.promise)},b.is.size=function(a){return!!(b.is.string(a)&&!b.is.empty(a)||b.is.number(a))&&/^(auto|none|(?:[\d\.]*)+?(?:%|px|mm|q|cm|in|pt|pc|em|ex|ch|rem|vh|vw|vmin|vmax)?)$/.test(a)},b.is.string=function(a){return"[object String]"===Object.prototype.toString.call(a)},b.is.undef=function(a){return void 0===a})}(FooGallery.utils.$,FooGallery.utils),function(a,b,c){if("0.0.8"===b.version){b.fn={};var d=Function.prototype.toString;b.fn.CONTAINS_SUPER=/xyz/.test(d.call(function(){xyz}))?/\b_super\b/:/.*/,b.fn.addOrOverride=function(a,e,f){if(c.object(a)&&c.string(e)&&!c.empty(e)&&c.fn(f)){var g=a[e],h=c.fn(g)&&b.fn.CONTAINS_SUPER.test(d.call(f));a[e]=h?function(a,b){return function(){var c=this._super;this._super=a;var d=b.apply(this,arguments);return this._super=c,d}}(g,f):f}},b.fn.apply=function(a,b){function d(){return a.apply(this,b)}return b=c.array(b)?b:[],d.prototype=a.prototype,new d},b.fn.arg2arr=function(a){return Array.prototype.slice.call(a)},b.fn.check=function(d,e,f,g){function h(a){return function(){return a.apply(d,arguments)}}return f=c.fn(f)?f:a.noop,d=c.object(d)?d:window,e=c.string(e)?b.fn.fetch(e,g):e,h(c.fn(e)?e:f)},b.fn.fetch=function(b,d){return!c.string(b)||c.empty(b)?null:(d=c.object(d)?d:window,a.each(b.split("."),function(a,b){if(!d[b])return!1;d=d[b]}),c.fn(d)?d:null)},b.fn.enqueue=function(d,e,f,g){function h(a,b){try{return n.push(a),b.apply(a,i)}catch(a){return j.reject(a,n),j}}var i=b.fn.arg2arr(arguments),j=a.Deferred(),k=a.Deferred(),l=k.promise(),m=[],n=[],o=!0;return d=i.shift(),e=i.shift(),a.each(d,function(a,d){c.fn(d[e])&&(l=l.then(function(){if(!o){var a=b.fn.arg2arr(arguments);m.push(a)}return o=!1,h(d,d[e])}))}),l.then(function(){if(!o){var a=b.fn.arg2arr(arguments);m.push(a)}o=!1,j.resolve(m)}),l.fail(function(){var a=b.fn.arg2arr(arguments);a.push(n),j.reject.apply(j,a)}),k.resolve(),j.promise()},b.fn.when=function(b){if(!c.array(b)||c.empty(b))return a.when();for(var d=a.Deferred(),e=[],f=b.length,g=0;g<b.length;g++)b[g].then(function(a){e.push(a)}).always(function(){--f||d.resolve(e)});return d.promise()},b.fn.rejectWith=function(c,d){var e=a.Deferred(),f=b.fn.arg2arr(arguments);return e.reject.apply(e,f).promise()},b.fn.resolveWith=function(c,d){var e=a.Deferred(),f=b.fn.arg2arr(arguments);return e.resolve.apply(e,f).promise()},b.fn.resolved=a.Deferred().resolve().promise(),b.fn.rejected=a.Deferred().reject().promise()}}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is),function(a,b){if("0.0.8"===a.version){a.url={};var c=document.createElement("a");a.url.parts=function(a){return c.href=a,{hash:c.hash,host:c.host,hostname:c.hostname,href:c.href,origin:c.origin,pathname:c.pathname,port:c.port,protocol:c.protocol,search:c.search}},a.url.full=function(a){return!b.string(a)||b.empty(a)?null:(c.href=a,c.href)},a.url.param=function(a,c,d){if(!b.string(a)||!b.string(c)||b.empty(c))return a;var e,f,g,h;return b.undef(d)?(e=new RegExp("[?|&]"+c+"=([^&;]+?)(&|#|;|$)"),f=e.exec(a)||[,""],g=f[1].replace(/\+/g,"%20"),b.string(g)&&!b.empty(g)?decodeURIComponent(g):null):(""===d||null===d?(e=new RegExp("^([^#]*?)(([^#]*)&)?"+c+"(=[^&#]*)?(&|#|$)"),g=a.replace(e,"$1$3$5").replace(/^([^#]*)((\?)&|\?(#|$))/,"$1$3$4")):(e=new RegExp("([?&])"+c+"[^&]*"),h=c+"="+encodeURIComponent(d),(g=a.replace(e,"$1"+h))!==a||e.test(g)||(g+=(-1!==g.indexOf("?")?"&":"?")+h)),g)}}}(FooGallery.utils,FooGallery.utils.is),function(a,b,c){"0.0.8"===a.version&&(a.str={},a.str.camel=function(a){return b.empty(a)?a:a.toUpperCase()===a?a.toLowerCase():a.replace(/^([A-Z])|[-\s_]+(\w)/g,function(a,c,d){return b.string(d)?d.toUpperCase():c.toLowerCase()})},a.str.contains=function(a,c,d){return!(!b.string(a)||b.empty(a)||!b.string(c)||b.empty(c))&&(c.length<=a.length&&-1!==(d?a.toUpperCase().indexOf(c.toUpperCase()):a.indexOf(c)))},a.str.containsWord=function(a,c,d){if(!b.string(a)||b.empty(a)||!b.string(c)||b.empty(c)||a.length<c.length)return!1;for(var e=a.split(/\W/),f=0,g=e.length;f<g;f++)if(d?e[f].toUpperCase()==c.toUpperCase():e[f]==c)return!0;return!1},a.str.endsWith=function(a,c){return!b.string(a)||b.empty(a)||!b.string(c)||b.empty(c)?a==c:a.slice(a.length-c.length)==c},a.str.escapeRegExp=function(a){return b.empty(a)?a:a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")},a.str.fnv1a=function(a){if(!b.string(a)||b.empty(a))return null;var c,d,e=2166136261;for(c=0,d=a.length;c<d;c++)e^=a.charCodeAt(c),e+=(e<<1)+(e<<4)+(e<<7)+(e<<8)+(e<<24);return e>>>0},a.str.from=function(c,d){return!b.string(c)||b.empty(c)||!b.string(d)||b.empty(d)?null:a.str.contains(c,d)?c.substring(c.indexOf(d)+d.length):null},a.str.join=function(d,e,f){if(!b.string(d)||!b.string(e))return null;var g=c.arg2arr(arguments);d=g.shift();var h,i,j=g.shift();for(h=0,i=g.length;h<i;h++)e=g[h],b.empty(e)||(a.str.endsWith(j,d)&&(j=j.slice(0,j.length-d.length)),a.str.startsWith(e,d)&&(e=e.slice(d.length)),j+=d+e);return j},a.str.startsWith=function(a,c){return!b.empty(a)&&!b.empty(c)&&a.slice(0,c.length)==c},a.str.until=function(c,d){return b.empty(c)||b.empty(d)?c:a.str.contains(c,d)?c.substring(0,c.indexOf(d)):c},a.str.format=function(a,d,e){var f=c.arg2arr(arguments);if(a=f.shift(),b.empty(a)||b.empty(f))return a;1===f.length&&(b.array(f[0])||b.object(f[0]))&&(f=f[0]);for(var g in f)a=a.replace(new RegExp("\\{"+g+"\\}","gi"),f[g]);return a})}(FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn),function(a,b,c,d,e){if("0.0.8"===b.version){b.obj={};var f=function(){};b.obj.create=function(a){if(!c.object(a))throw TypeError("Argument must be an object");f.prototype=a;var b=new f;return f.prototype=null,b},b.obj.extend=function(e,f,g){e=c.object(e)?e:{};var h=d.arg2arr(arguments);return h.shift(),a.each(h,function(a,c){b.obj.merge(e,c)}),e},b.obj.merge=function(a,d){a=c.hash(a)?a:{},d=c.hash(d)?d:{};for(var e in d)d.hasOwnProperty(e)&&(c.hash(d[e])?(a[e]=c.hash(a[e])?a[e]:{},b.obj.merge(a[e],d[e])):c.array(d[e])?a[e]=d[e].slice():a[e]=d[e]);return a},b.obj.mergeValid=function(d,e,f,g){if(!c.hash(f)||!c.hash(e))return d;e=c.hash(e)?e:{},g=c.hash(g)?g:{};var h,i,j;for(h in e)e.hasOwnProperty(h)&&c.fn(e[h])&&(i=c.array(g[h])?g[h]:c.string(g[h])?[g[h]]:[h],a.each(i,function(a,g){if(j=b.obj.prop(f,g),!c.undef(j))return e[h](j)?(b.obj.prop(d,h,j),!1):void 0}));return d},b.obj.prop=function(b,d,f){if(c.object(b)&&!c.empty(d)){var g,h;if(c.undef(f))return e.contains(d,".")?(g=d.split("."),h=g.length-1,a.each(g,function(a,d){if(a===h)f=b[d];else{if(!c.hash(b[d]))return!1;b=b[d]}})):c.undef(b[d])||(f=b[d]),f;e.contains(d,".")?(g=d.split("."),h=g.length-1,a.each(g,function(a,d){a===h?b[d]=f:b=c.hash(b[d])?b[d]:b[d]={}})):c.undef(b[d])||(b[d]=f)}}}}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.str),function(a,b,c){if("0.0.8"===b.version){b.ready=function(a){function c(){try{a.call(window,b.$)}catch(a){console.error(a)}}(Function("/*@cc_on return true@*/")()?"complete"===document.readyState:"loading"!==document.readyState)?c():document.addEventListener("DOMContentLoaded",c,!1)};var d=0;b.uniqueId=function(a,b){var e=a.attr("id");return c.empty(e)&&(b=c.string(b)&&!c.empty(b)?b:"uid-",e=b+ ++d,a.attr("id",e).data("__uniqueId__",!0)),e},b.removeUniqueId=function(a){a.data("__uniqueId__")&&a.removeAttr("id").removeData("__uniqueId__")}}}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is),function(a,b,c){if("0.0.8"===b.version){b.transition={};var d=document.createElement("div");b.transition.supported=function(a){var b=a.style;return c.string(b.transition)||c.string(b.WebkitTransition)||c.string(b.MozTransition)||c.string(b.msTransition)||c.string(b.OTransition)}(d),b.transition.end=function(a){var b=a.style;return c.string(b.transition)?"transitionend":c.string(b.WebkitTransition)?"webkitTransitionEnd":c.string(b.MozTransition)?"transitionend":c.string(b.msTransition)?"msTransitionEnd":c.string(b.OTransition)?"oTransitionEnd":null}(d),b.transition.duration=function(a,b){if(b=c.number(b)?b:0,!c.jq(a))return b;var d=a.css("transition-duration");if(/^([\d\.]*)+?(ms|s)$/i.test(d)){var e=d.match(/^([\d\.]*)+?(ms|s)$/i),f=parseFloat(e[1]);return"s"===e[2].toLowerCase()&&(f*=1e3),f}return b},b.transition.start=function(d,e,f,g){var h=a.Deferred();if(d=d.first(),b.transition.supported){var i=d.data("transition_safety");c.hash(i)&&c.number(i.timer)&&(clearTimeout(i.timer),d.removeData("transition_safety").off(b.transition.end+".utils"),i.deferred.reject()),g=c.number(g)?g:b.transition.duration(d)+50,i={deferred:h,timer:setTimeout(function(){d.removeData("transition_safety").off(b.transition.end+".utils"),h.resolve()},g)},d.data("transition_safety",i),d.on(b.transition.end+".utils",function(a){d.is(a.target)&&(clearTimeout(i.timer),d.removeData("transition_safety").off(b.transition.end+".utils"),h.resolve())})}return setTimeout(function(){c.fn(e)?e.apply(d.get(0),[d]):d.toggleClass(e,f),b.transition.supported||h.resolve()},20),h.promise()}}}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is),function(a,b,c,d,e){"0.0.8"===b.version&&(b.Class=function(){},b.Class.extend=function(a){function f(){if(!c.fn(this.construct))throw new SyntaxError('FooGallery.utils.Class objects must be constructed with the "new" keyword.');this.construct.apply(this,arguments)}a=c.hash(a)?a:{};var g=d.create(this.prototype);for(var h in a)a.hasOwnProperty(h)&&e.addOrOverride(g,h,a[h]);return g.construct=c.fn(g.construct)?g.construct:function(){},f.prototype=g,f.prototype.constructor=c.fn(g.__ctor__)?g.__ctor__:f,f.extend=b.Class.extend,f.override=b.Class.override,f},b.Class.override=function(a,b){e.addOrOverride(this.prototype,a,b)})}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.obj,FooGallery.utils.fn),function(a,b,c){if("0.0.8"===b.version){b.Bounds=b.Class.extend({construct:function(){var a=this;a.top=0,a.right=0,a.bottom=0,a.left=0,a.width=0,a.height=0},inflate:function(a){var b=this;return c.number(a)&&(b.top-=a,b.right+=a,b.bottom+=a,b.left-=a,b.width+=2*a,b.height+=2*a),b},intersects:function(a){var b=this;return b.left<=a.right&&a.left<=b.right&&b.top<=a.bottom&&a.top<=b.bottom}});var d;b.getViewportBounds=function(c){d||(d=a(window));var e=new b.Bounds;return e.top=d.scrollTop(),e.left=d.scrollLeft(),e.width=d.width(),e.height=d.height(),e.right=e.left+e.width,e.bottom=e.top+e.height,e.inflate(c),e},b.getElementBounds=function(d){c.jq(d)||(d=a(d));var e=new b.Bounds;if(0!==d.length){var f=d.offset();e.top=f.top,e.left=f.left,e.width=d.width(),e.height=d.height()}return e.right=e.left+e.width,e.bottom=e.top+e.height,e}}}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is),function(a,b,c,d){"0.0.8"===b.version&&(b.Factory=b.Class.extend({construct:function(){this.registered={}},contains:function(a){return!c.undef(this.registered[a])},load:function(b,e,f){var g,h,i=this,j=d.arg2arr(arguments),k=[],l=[];b=j.shift()||{};for(g in i.registered)if(i.registered.hasOwnProperty(g)){var m=i.registered[g];b.hasOwnProperty(g)&&(h=b[g],c.string(h)&&(h=d.fetch(b[g])),c.fn(h)&&(m={name:g,klass:h,priority:i.registered[g].priority})),k.push(m)}for(g in b)b.hasOwnProperty(g)&&!i.registered.hasOwnProperty(g)&&(h=b[g],c.string(h)&&(h=d.fetch(b[g])),c.fn(h)&&k.push({name:g,klass:h,priority:0}));return k.sort(function(a,b){return b.priority-a.priority}),a.each(k,function(a,b){c.fn(b.klass)&&l.push(d.apply(b.klass,j))}),l},make:function(a,b,e){var f,g=this,h=d.arg2arr(arguments);return a=h.shift(),f=g.registered[a],c.hash(f)&&c.fn(f.klass)?d.apply(f.klass,h):null},names:function(b){b=!!c.boolean(b)&&b;var d,e=[];if(b){var f=[];for(d in this.registered)this.registered.hasOwnProperty(d)&&f.push(this.registered[d]);f.sort(function(a,b){return b.priority-a.priority}),a.each(f,function(a,b){e.push(b.name)})}else for(d in this.registered)this.registered.hasOwnProperty(d)&&e.push(d);return e},register:function(a,b,d){if(!c.string(a)||c.empty(a)||!c.fn(b))return!1;d=c.number(d)?d:0;var e=this.registered[a];return this.registered[a]={name:a,klass:b,priority:c.undef(e)?d:e.priority},!0}}))}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn),function(a,b,c){if("0.0.8"===a.version){var d=!1;try{d=!!window.localStorage}catch(a){d=!1}a.Debugger=a.Class.extend({construct:function(a){this.key=a,this.enabled=!!d&&!!localStorage.getItem(this.key)},enable:function(){d&&(this.enabled=!0,localStorage.setItem(this.key,this.enabled))},disable:function(){d&&(this.enabled=!1,localStorage.removeItem(this.key))},log:function(a,c){this.enabled&&console.log.apply(console,b.arg2arr(arguments))},logf:function(a,d,e){if(this.enabled){var f=b.arg2arr(arguments);a=f.shift(),d=f.shift(),f.unshift(c.format(a,d)),this.log.apply(this,f)}}})}}(FooGallery.utils,FooGallery.utils.fn,FooGallery.utils.str),function(a,b,c){"0.0.8"===b.version&&(b.Throttle=b.Class.extend({construct:function(a){this.id=null,this.active=!1,this.idle=c.number(a)?a:0},limit:function(a){if(c.fn(a)){this.clear();var b=this;this.active=!0,this.id=setTimeout(function(){b.active=!1,b.id=null,a()},this.idle)}},clear:function(){c.number(this.id)&&(clearTimeout(this.id),this.active=!1,this.id=null)}}))}(FooGallery.utils.$,FooGallery.utils,FooGallery.utils.is),function(a,b,c,d,e){b.debug=new c.Debugger("__FooGallery__"),c.selectify=function(b){if(d.empty(b))return null;if(d.hash(b)){var e,f={};for(var g in b)b.hasOwnProperty(g)&&(e=c.selectify(b[g]))&&(f[g]=e);return f}return d.string(b)||d.array(b)?(d.string(b)&&(b=[b]),a.map(b,function(a){return d.string(a)?"."+a.split(/\s/g).join("."):null}).join(",")):null},b.emptyImage="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",b.dataTemplate="__FooGallery__",b.dataItem="__FooGalleryItem__",b.init=function(a,c){return b.template.make(a,c).initialize()},b.initAll=function(c){return e.when(a(".foogallery").map(function(a,d){return b.init(c,d)}).get())},b.parseSrc=function(b,c,e,f,g,h){if(!d.string(b))return null;if(!d.string(f))return b;var i=a.map(f.replace(/(\s[\d.]+[whx]),/g,"$1 @,@ ").split(" @,@ "),function(a){return{url:/^\s*(\S*)/.exec(a)[1],w:parseFloat((/\S\s+(\d+)w/.exec(a)||[0,1/0])[1]),h:parseFloat((/\S\s+(\d+)h/.exec(a)||[0,1/0])[1]),x:parseFloat((/\S\s+([\d.]+)x/.exec(a)||[0,1])[1])}});if(!i.length)return b;i.unshift({url:b,w:i[0].w!==1/0&&i[0].h===1/0?c:1/0,h:i[0].h!==1/0&&i[0].w===1/0?e:1/0,x:1});var j,k=window.devicePixelRatio||1,l={w:g*k,h:h*k,x:k};for(j in l)l.hasOwnProperty(j)&&(i=a.grep(i,function(a,b){return function(c){return c[a]>=l[a]||c[a]===b}}(j,Math.max.apply(null,a.map(i,function(a){return a[j]})))));for(j in l)l.hasOwnProperty(j)&&(i=a.grep(i,function(a,b){return function(c){return c[a]===b}}(j,Math.min.apply(null,a.map(i,function(a){return a[j]})))));return i[0].url},a.fn.foogallery=function(c,e){return this.each(function(f,g){if(d.string(c)){var h=a.data(g,b.dataTemplate);if(h instanceof b.Template)switch(c){case"layout":return void h.layout();case"destroy":return void h.destroy()}}else b.template.make(c,g).initialize().then(function(a){d.fn(e)&&e(a)})})}}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn),function(a,b,c,d,e,f){b.TemplateFactory=c.Factory.extend({construct:function(){this.registered={}},register:function(a,b,c,e,f,g){var h=this,i=h._super(a,b,g);if(i){var j=h.registered;j[a].opt=d.hash(c)?c:{},j[a].cls=d.hash(e)?e:{},j[a].il8n=d.hash(f)?f:{}}return i},make:function(b,c){c=d.jq(c)?c:a(c),b=f.extend({},b,c.data("foogallery"));var e=this,g=e.type(b,c);return e.contains(g)?(b=e.options(g,b),e._super(g,b,c)):null},type:function(b,e){e=d.jq(e)?e:a(e);var f=this,g=d.hash(b)&&d.hash(b)&&d.string(b.type)&&f.contains(b.type)?b.type:"core";if("core"===g&&e.length>0)for(var h=f.registered,i=f.names(!0),j=0,k=i.length;j<k;j++)if(h.hasOwnProperty(i[j])){var l=i[j],m=h[l].cls;if(d.string(m.container)){var n=c.selectify(m.container);if(e.is(n)){g=i[j];break}}}return g},configure:function(a,b,c,d){var e=this;if(e.contains(a)){var g=e.registered;f.extend(g[a].opt,b),f.extend(g[a].cls,c),f.extend(g[a].il8n,d)}},options:function(a,c){c=f.extend({type:a},c);var e=this,g=e.registered,h=g.core.opt,i=g.core.cls,j=g.core.il8n;return d.hash(c.cls)||(c.cls={}),d.hash(c.il8n)||(c.il8n={}),d.undef(b.filtering)||(c=b.filtering.merge(c)),d.undef(b.paging)||(c=b.paging.merge(c)),"core"!==a&&e.contains(a)?(c=f.extend({},h,g[a].opt,c),c.cls=f.extend({},i,g[a].cls,c.cls),c.il8n=f.extend({},j,g[a].il8n,c.il8n)):(c=f.extend({},h,c),c.cls=f.extend({},i,c.cls),c.il8n=f.extend({},j,c.il8n)),c}}),b.template=new b.TemplateFactory}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.obj),function(a,b,c,d,e){a.PagingFactory=b.Factory.extend({construct:function(){this.registered={}},register:function(a,b,d,e,f,g,h){var i=this,j=i._super(a,b,h);if(j){var k=i.registered;k[a].ctrl=c.fn(d)?d:null,k[a].opt=c.hash(e)?e:{},k[a].cls=c.hash(f)?f:{},k[a].il8n=c.hash(g)?g:{}}return j},type:function(a){var b,d=this;return c.hash(a)&&c.hash(b=a.paging)&&c.string(b.type)&&d.contains(b.type)?b.type:null},merge:function(a){a=e.extend({},a);var b=this,d=b.type(a),f=b.registered,g=f.default.opt,h=f.default.cls,i=f.default.il8n,j=c.hash(a.paging)?a.paging:{},k=c.hash(a.cls)&&c.hash(a.cls.paging)?e.extend({},a.cls.paging):{},l=c.hash(a.il8n)&&c.hash(a.il8n.paging)?e.extend({},a.il8n.paging):{};return c.hash(a.cls)||(a.cls={}),c.hash(a.il8n)||(a.il8n={}),"default"!==d&&b.contains(d)?(a.paging=e.extend({},g,f[d].opt,j,{type:d}),a.cls=e.extend(a.cls,{paging:h},{paging:f[d].cls},{paging:k}),a.il8n=e.extend(a.il8n,{paging:i},{paging:f[d].il8n},{paging:l})):(a.paging=e.extend({},g,j,{type:d}),a.cls=e.extend(a.cls,{paging:h},{paging:k}),a.il8n=e.extend(a.il8n,{paging:i},{paging:l})),a},configure:function(a,b,c,d){var f=this;if(f.contains(a)){var g=f.registered;e.extend(g[a].opt,b),e.extend(g[a].cls,c),e.extend(g[a].il8n,d)}},hasCtrl:function(a){var b=this,d=b.registered[a];return c.hash(d)&&c.fn(d.ctrl)},makeCtrl:function(a,b,d,e){var f=this,g=f.registered[a];return c.hash(g)&&c.fn(g.ctrl)?new g.ctrl(b,d,e):null}}),a.paging=new a.PagingFactory}(FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.obj),function(a,b,c,d,e,f){var g=0;b.Template=c.Class.extend({construct:function(e,f){var h=this;h.namespace=".foogallery-"+ ++g,h.$el=d.jq(f)?f:a(f),h.opt=e,h.template=e.template,h.id=h.$el.prop("id")||e.id,h.cls=e.cls,h.il8n=e.il8n,h.sel=c.selectify(h.cls),h.items=b.components.make("items",h),h.pages=d.undef(b.paging)?null:b.paging.make(e.paging.type,h),h.filter=d.undef(b.filtering)?null:b.filtering.make(e.filtering.type,h),h.state=b.components.make("state",h),h._initialize=null,h.initializing=!1,h.initialized=!1,h.destroying=!1,h.destroyed=!1,h._undo={classes:"",style:"",create:!1,children:!1}},initialize:function(f){var g=this;return d.promise(g._initialize)?g._initialize:(f=d.jq(f)?f:a(f),g._initialize=a.Deferred(function(h){if(g.initializing=!0,0===f.length&&0===g.$el.parent().length)return void h.reject("A parent element is required.");0===g.$el.length&&(g.$el=g.create(),g._undo.create=!0),f.length>0&&g.$el.appendTo(f);var i,j=a.Deferred(),k=j.promise();g.$el.length>0&&(i=g.$el.data(b.dataTemplate))instanceof b.Template?k=k.then(function(){return i.destroy().then(function(){g.$el.data(b.dataTemplate,g)})}):g.$el.data(b.dataTemplate,g),k.then(function(){if(g.destroying)return e.rejectWith("destroy in progress");d.empty(g.opt.on)||g.$el.on(g.opt.on),g._undo.classes=g.$el.attr("class"),g._undo.style=g.$el.attr("style"),g.$el.is(g.sel.container)||g.$el.addClass(g.cls.container);var a=c.selectify(g.opt.classes);return null==a||g.$el.is(a)||g.$el.addClass(g.opt.classes),0==g.$el.children().length&&(g.$el.append(g.createChildren()),g._undo.children=!0),g.raise("pre-init").isDefaultPrevented()?e.rejectWith("pre-init default prevented"):void 0}).then(function(){return g.destroying?e.rejectWith("destroy in progress"):g.opt.delay<=0?e.resolved:a.Deferred(function(a){g._delay=setTimeout(function(){g._delay=null,a.resolve()},g.opt.delay)}).promise()}).then(function(){return g.destroying?e.rejectWith("destroy in progress"):g.raise("init").isDefaultPrevented()?e.rejectWith("init default prevented"):g.items.fetch()}).then(function(){if(g.destroying)return e.rejectWith("destroy in progress");if(g.raise("post-init").isDefaultPrevented())return e.rejectWith("post-init default prevented");var b=g.state.parse();g.state.set(d.empty(b)?g.state.initial():b),a(window).on("scroll"+g.namespace,{self:g},g.throttle(g.onWindowScroll,g.opt.throttle)).on("popstate"+g.namespace,{self:g},g.onWindowPopState)}).then(function(){return g.destroying?e.rejectWith("destroy in progress"):(g.raise("first-load"),g.loadAvailable())}).then(function(){if(g.destroying)return e.rejectWith("destroy in progress");g.initializing=!1,g.initialized=!0,g._check(200),g._check(500),g._check(1e3),g._check(2e3),g._check(5e3),g.raise("ready"),h.resolve(g)}).fail(function(a){h.reject(a)}),j.resolve()}).promise().fail(function(a){console.log("initialize failed",g,a),g.destroy()}))},create:function(){var b=this;return a("<div/>",{id:b.id,class:b.cls.container}).addClass(b.opt.classes)},createChildren:function(){return a()},destroy:function(){var b=this;return b.destroyed?e.resolved:(b.destroying=!0,a.Deferred(function(a){b.initializing&&d.promise(b._initialize)?b._initialize.always(function(){b.destroying=!1,b._destroy(),a.resolve()}):(b.destroying=!1,b._destroy(),a.resolve())}).promise())},_destroy:function(){var c=this;c.destroyed||(c.raise("destroy"),a(window).off(c.namespace),c.state.destroy(),c.filter&&c.filter.destroy(),c.pages&&c.pages.destroy(),c.items.destroy(),d.empty(c.opt.on)||c.$el.off(c.opt.on),c.raise("destroyed"),c.$el.removeData(b.dataTemplate),d.empty(c._undo.classes)?c.$el.removeAttr("class"):c.$el.attr("class",c._undo.classes),d.empty(c._undo.style)?c.$el.removeAttr("style"):c.$el.attr("style",c._undo.style),c._undo.children&&c.$el.empty(),c._undo.create&&c.$el.remove(),c.$el=c.state=c.items=c.pages=null,c.destroyed=!0,c.initializing=!1,c.initialized=!1)},getAvailable:function(){return this.pages?this.pages.available():this.items.available()},loadAvailable:function(){return this.items.load(this.getAvailable())},_check:function(a){a=d.number(a)?a:0;var b=this;setTimeout(function(){!b.initialized||b.destroying&&b.destroyed||b.loadAvailable()},a)},raise:function(c,e){if(!d.string(c)||d.empty(c))return null;e=d.array(e)?e:[];var g=this,h=c.split(".")[0],i=f.camel("on-"+h),j=a.Event(h+".foogallery");return e.unshift(g),g.$el.trigger(j,e),b.debug.logf("{id}|{name}:",{id:g.id,name:h},e),d.fn(g[i])&&(e.unshift(j),g[i].apply(g.$el.get(0),e)),j},layout:function(){var a=this;null!==a._initialize&&a.raise("layout")},throttle:function(a,b){var c=Date.now();return function(){if(c+b-Date.now()<0){var d=e.arg2arr(arguments);a.apply(this,d),c=Date.now()}}},onWindowPopState:function(a){var b=a.data.self,c=a.originalEvent.state;d.empty(c)||c.id!==b.id||(b.state.set(c),b.loadAvailable())},onWindowScroll:function(a){a.data.self.loadAvailable()}}),b.template.register("core",b.Template,{id:null,type:"core",classes:"",on:{},lazy:!0,viewport:200,items:[],delay:100,throttle:50,timeout:6e4,srcset:"data-srcset",src:"data-src",template:{}},{container:"foogallery"},{},-100)}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.str),function(a,b){a.Component=b.Class.extend({construct:function(a){this.tmpl=a},destroy:function(){this.tmpl=null}}),a.components=new b.Factory}(FooGallery,FooGallery.utils),function(a,b,c,d){b.State=b.Component.extend({construct:function(a){var b=this;b._super(a),b.apiEnabled=!!window.history&&!!history.replaceState,b.opt=b.tmpl.opt.state,b.enabled=b.opt.enabled,b.pushOrReplace=b.isPushOrReplace(b.opt.pushOrReplace)?b.opt.pushOrReplace:"replace";var c=d.escapeRegExp(b.tmpl.id),e=d.escapeRegExp(b.opt.values),f=d.escapeRegExp(b.opt.pair);b.regex={exists:new RegExp("^#"+c+"\\"+e+".+?"),values:new RegExp("(\\w+)"+f+"([^"+e+"]+)","g")}},destroy:function(){var a=this;a.clear(),a.opt=a.regex={},a._super()},isPushOrReplace:function(b){return-1!==a.inArray(b,["push","replace"])},exists:function(){return this.regex.exists.test(location.hash)&&this.regex.values.test(location.hash)},parse:function(){var b=this,d={};if(b.exists())if(b.enabled){d.id=b.tmpl.id;var e=location.hash.match(b.regex.values);a.each(e,function(e,f){var g=f.split(b.opt.pair);2===g.length&&(d[g[0]]=-1===g[1].indexOf(b.opt.array)?decodeURIComponent(g[1]):a.map(g[1].split(b.opt.array),function(a){return decodeURIComponent(a)}),c.string(d[g[0]])&&!isNaN(d[g[0]])&&(d[g[0]]=parseInt(d[g[0]])))})}else b.apiEnabled?history.replaceState(null,"",location.pathname+location.search):location.hash="#";return d},hashify:function(b){var d=this;if(c.hash(b)){var e=[];return a.each(b,function(b,f){c.empty(f)||"id"===b||(f=c.array(f)?a.map(f,function(a){return encodeURIComponent(a)}).join("+"):encodeURIComponent(f),e.push(b+d.opt.pair+f))}),e.length>0&&e.unshift("#"+d.tmpl.id),e.join(d.opt.values)}return""},replace:function(a){var b=this;if(b.enabled&&b.apiEnabled){a.id=b.tmpl.id;var d=b.hashify(a),e=c.empty(d);history.replaceState(e?null:a,"",e?location.pathname+location.search:d)}},push:function(a){var b=this;if(b.enabled&&b.apiEnabled){a.id=b.tmpl.id;var d=b.hashify(a),e=c.empty(d);history.pushState(e?null:a,"",e?location.pathname+location.search:d)}},update:function(a,b){var c=this;c.enabled&&c.apiEnabled&&(b=c.isPushOrReplace(b)?b:c.pushOrReplace,c[b](a))},clear:function(){this.exists()&&this.replace({})},initial:function(){var a=this,b=a.tmpl,d={};return b.filter&&!c.empty(b.filter.current)&&(d.f=b.filter.current),b.pages&&b.pages.current>1&&(d.p=b.pages.current),d},get:function(a){var d=this,e=d.tmpl,f={};return a instanceof b.Item&&(f.i=a.id),e.filter&&!c.empty(e.filter.current)&&(f.f=e.filter.current),e.pages&&e.pages.isValid(e.pages.current)&&(f.p=e.pages.current),f},set:function(a){var b=this,d=b.tmpl;if(c.hash(a)){d.items.reset();var e=d.items.get(a.i);if(d.filter){d.filter.rebuild();var f=c.empty(a.f)?[]:a.f;d.filter.set(f,!1)}if(d.pages){d.pages.rebuild();var g=d.pages.number(a.p);e&&!d.pages.contains(g,e)&&(g=d.pages.find(e),g=0!==g?g:1),d.pages.set(g,!c.empty(a),!1),e&&d.pages.contains(g,e)&&e.scrollTo()}else d.items.detach(d.items.all()),d.items.create(d.items.available(),!0),e&&e.scrollTo();c.empty(a.i)||(a.i=null,b.replace(a))}}}),b.template.configure("core",{state:{enabled:!1,pushOrReplace:"replace",values:"/",pair:":",array:"+"}}),b.components.register("state",b.State)}(FooGallery.$,FooGallery,FooGallery.utils.is,FooGallery.utils.str),function(a,b,c,d,e,f){b.Item=b.Component.extend({construct:function(a,b){var c=this;c._super(a),c.cls=a.cls.item,c.il8n=a.il8n.item,c.sel=a.sel.item,c.opt=f.extend({},a.opt.item,b),c.isAttached=!1,c.isCreated=!1,c.isLoading=!1,c.isLoaded=!1,c.isError=!1,c.isParsed=!1,c.$el=null,c.$inner=null,c.$anchor=null,c.$image=null,c.$caption=null,c.type=c.opt.type,c.id=c.opt.id,c.href=c.opt.href,c.src=c.opt.src,c.srcset=c.opt.srcset,c.width=c.opt.width,c.height=c.opt.height,c.title=c.opt.title,c.alt=c.opt.alt,c.caption=d.empty(c.opt.caption)?c.title:c.opt.caption,c.description=d.empty(c.opt.description)?c.alt:c.opt.description,c.attr=c.opt.attr,c.tags=c.opt.tags,c.maxWidth=c.opt.maxWidth,c.maxCaptionLength=c.opt.maxCaptionLength,c.maxDescriptionLength=c.opt.maxDescriptionLength,c.showCaptionTitle=c.opt.showCaptionTitle,c.showCaptionDescription=c.opt.showCaptionDescription,c._thumbUrl=null,c._placeholder=null,c._load=null,c._undo={classes:"",style:"",loader:!1,placeholder:!1}},destroy:function(){var a=this;return a.tmpl.raise("destroy-item").isDefaultPrevented()||(a.doDestroyItem(),a._super()),null===a.tmpl},doDestroyItem:function(){var a=this;a.isParsed?(a.append(),d.empty(a._undo.classes)?a.$el.removeAttr("class"):a.$el.attr("class",a._undo.classes),d.empty(a._undo.style)?a.$el.removeAttr("style"):a.$el.attr("style",a._undo.style),a._undo.loader&&a.$el.find(a.sel.loader).remove(),a._undo.placeholder&&a.$image.prop("src")==a._placeholder&&a.$image.removeAttr("src")):a.isCreated&&(a.detach(),a.$el.remove())},parse:function(b){var c=this,d=a(b);return!c.tmpl.raise("parse-item",[c,d]).isDefaultPrevented()&&(c.isCreated=d.is(c.sel.elem))&&(c.isParsed=c.doParseItem(d),c.fix()),c.isParsed&&c.tmpl.raise("parsed-item",[c]),c.isParsed},doParseItem:function(c){var e=this,f=e.tmpl.opt,g=e.cls,h=e.sel;return e._undo.classes=c.attr("class")||"",e._undo.style=c.attr("style")||"",e.$el=c.data(b.dataItem,e),e.$inner=e.$el.find(h.inner),e.$anchor=e.$el.find(h.anchor).on("click.foogallery",{self:e},e.onAnchorClick),e.$image=e.$anchor.find(h.image),e.$caption=e.$el.find(h.caption.elem).on("click.foogallery",{self:e},e.onCaptionClick),e.isAttached=e.$el.parent().length>0,e.isLoading=e.$el.is(h.loading),e.isLoaded=e.$el.is(h.loaded),e.isError=e.$el.is(h.error),e.id=e.$anchor.data("id")||e.id,e.tags=e.$anchor.data("tags")||e.tags,e.href=e.$anchor.attr("href")||e.href,e.src=e.$image.attr(f.src)||e.src,e.srcset=e.$image.attr(f.srcset)||e.srcset,e.width=parseInt(e.$image.attr("width"))||e.width,e.height=parseInt(e.$image.attr("height"))||e.height,e.title=e.$image.attr("title")||e.title,e.alt=e.$image.attr("alt")||e.alt,e.caption=e.$anchor.data("title")||e.$anchor.data("captionTitle")||e.caption||e.title,e.description=e.$anchor.data("description")||e.$anchor.data("captionDesc")||e.description||e.alt,d.empty(e.caption)&&(e.caption=a.trim(e.$caption.find(h.caption.title).html())),
10
+ d.empty(e.description)&&(e.description=a.trim(e.$caption.find(h.caption.description).html())),d.number(e.maxCaptionLength)&&e.maxCaptionLength>0&&!d.empty(e.caption)&&d.string(e.caption)&&e.caption.length>e.maxCaptionLength&&e.$caption.find(h.caption.title).html(e.caption.substr(0,e.maxCaptionLength)+"&hellip;"),d.number(e.maxDescriptionLength)&&e.maxDescriptionLength>0&&!d.empty(e.description)&&d.string(e.description)&&e.description.length>e.maxDescriptionLength&&e.$caption.find(h.caption.description).html(e.description.substr(0,e.maxDescriptionLength)+"&hellip;"),0===e.$el.find(h.loader).length&&(e.$el.append(a("<div/>",{class:g.loader})),e._undo.loader=!0),d.empty(e.$image.prop("src"))&&(e._placeholder=e.tmpl.items.placeholder(e.width,e.height),e.$image.prop("src",e._placeholder),e._undo.placeholder=!0),!e.isCreated||!e.isAttached||e.isLoading||e.isLoaded||e.isError||e.$el.addClass(g.idle),!0},create:function(){var a=this;if(!a.isCreated&&d.string(a.href)&&d.string(a.src)&&d.number(a.width)&&d.number(a.height)){a.tmpl.raise("create-item",[a]).isDefaultPrevented()||(a.isCreated=a.doCreateItem()),a.isCreated&&a.tmpl.raise("created-item",[a])}return a.isCreated},doCreateItem:function(){var c=this,e=c.tmpl.opt,f=c.cls,g=c.attr;g.elem.class=f.elem+" "+f.idle,g.inner.class=f.inner,g.anchor.class=f.anchor,g.anchor.href=c.href,g.anchor["data-id"]=c.id,g.anchor["data-title"]=c.caption,g.anchor["data-description"]=c.description,d.empty(c.tags)||(g.anchor["data-tags"]=JSON.stringify(c.tags)),g.image.class=f.image,g.image.src=c.tmpl.items.placeholder(c.width,c.height),g.image[e.src]=c.src,g.image[e.srcset]=c.srcset,g.image.width=c.width,g.image.height=c.height,g.image.title=c.title,g.image.alt=c.alt,c.$el=a("<div/>").attr(g.elem).data(b.dataItem,c),c.$inner=a("<figure/>").attr(g.inner).appendTo(c.$el),c.$anchor=a("<a/>").attr(g.anchor).appendTo(c.$inner).on("click.foogallery",{self:c},c.onAnchorClick),c.$image=a("<img/>").attr(g.image).appendTo(c.$anchor),f=c.cls.caption,g=c.attr.caption,g.elem.class=f.elem,c.$caption=a("<figcaption/>").attr(g.elem).on("click.foogallery",{self:c},c.onCaptionClick);var h=!d.empty(c.caption),i=!d.empty(c.description);if(h||i){g.inner.class=f.inner,g.title.class=f.title,g.description.class=f.description;var j=a("<div/>").attr(g.inner).appendTo(c.$caption);if(h){var k;k=d.number(c.maxCaptionLength)&&c.maxCaptionLength>0&&d.string(c.caption)&&c.caption.length>c.maxCaptionLength?a("<div/>").attr(g.title).html(c.caption.substr(0,c.maxCaptionLength)+"&hellip;"):a("<div/>").attr(g.title).html(c.caption),j.append(k)}if(i){var l;l=d.number(c.maxDescriptionLength)&&c.maxDescriptionLength>0&&d.string(c.description)&&c.description.length>c.maxDescriptionLength?a("<div/>").attr(g.description).html(c.description.substr(0,c.maxDescriptionLength)+"&hellip;"):a("<div/>").attr(g.description).html(c.description),j.append(l)}}return c.$caption.appendTo(c.$inner),0===c.$el.find(c.sel.loader).length&&c.$el.append(a("<div/>",{class:c.cls.loader})),!0},append:function(){var a=this;if(a.isCreated&&!a.isAttached){a.tmpl.raise("append-item",[a]).isDefaultPrevented()||(a.tmpl.$el.append(a.$el),a.fix(),a.isAttached=!0),a.isAttached&&a.tmpl.raise("appended-item",[a])}return a.isAttached},detach:function(){var a=this;if(a.isCreated&&a.isAttached){a.tmpl.raise("detach-item",[a]).isDefaultPrevented()||(a.$el.detach(),a.unfix(),a.isAttached=!1),a.isAttached||a.tmpl.raise("detached-item",[a])}return!a.isAttached},load:function(){var b=this;if(d.promise(b._load))return b._load;if(!b.isCreated||!b.isAttached)return e.rejectWith("not created or attached");if(b.tmpl.raise("load-item",[b]).isDefaultPrevented())return e.rejectWith("default prevented");var c=b.cls,f=b.$image.get(0),g=f.src;return b.isLoading=!0,b.$el.removeClass(c.idle).removeClass(c.loaded).removeClass(c.error).addClass(c.loading),b._load=a.Deferred(function(a){d.undef(window.InstallTrigger)||(f.src=""),f.onload=function(){f.onload=f.onerror=null,b.isLoading=!1,b.isLoaded=!0,b.$el.removeClass(c.loading).addClass(c.loaded),b.unfix(),b.tmpl.raise("loaded-item",[b]),a.resolve(b)},f.onerror=function(){f.onload=f.onerror=null,b.isLoading=!1,b.isError=!0,b.$el.removeClass(c.loading).addClass(c.error),d.string(g)&&b.$image.prop("src",g),b.tmpl.raise("error-item",[b]),a.reject(b)},f.src=b.getThumbUrl()}).promise()},fix:function(){var a=this;if(!a.tmpl.raise("fix-item",[a]).isDefaultPrevented()&&a.isCreated&&!a.isLoading&&!a.isLoaded&&!a.isError){var b=a.width,c=a.height;if(!isNaN(b)&&!isNaN(c)){var e=d.fn(a.maxWidth)?a.maxWidth(a):a.$image.width();e<=0&&(e=b);var f=e/b,g=c*f;a.$image.css({width:e,height:g})}}return a},unfix:function(){var a=this;return!a.tmpl.raise("unfix-item",[a]).isDefaultPrevented()&&a.isCreated&&a.$image.css({width:"",height:""}),a},getThumbUrl:function(a){a=!!d.boolean(a)&&a;var c=this;return!a&&d.string(c._thumbUrl)?c._thumbUrl:c._thumbUrl=b.parseSrc(c.src,c.width,c.height,c.srcset,c.$anchor.innerWidth(),c.$anchor.innerHeight())},scrollTo:function(a){var b=this;if(b.isAttached){var d=b.bounds(),e=c.getViewportBounds();switch(a){case"top":d.left+=d.width/2-e.width/2,d.top-=e.height/5;break;default:d.left+=d.width/2-e.width/2,d.top+=d.height/2-e.height/2}window.scrollTo(d.left,d.top)}return b},bounds:function(){return this.isAttached?c.getElementBounds(this.$el):null},intersects:function(a){return!!this.isAttached&&this.bounds().intersects(a)},onAnchorClick:function(a){var b=a.data.self,c=b.tmpl.state.get(b);b.tmpl.state.update(c)},onCaptionClick:function(b){var c=b.data.self;a(b.target).is(c.sel.caption.all)&&c.$anchor.length>0&&c.$anchor.get(0).click()}}),b.template.configure("core",{item:{type:"item",id:"",href:"",src:"",srcset:"",width:0,height:0,title:"",alt:"",caption:"",description:"",tags:[],maxWidth:null,maxCaptionLength:0,maxDescriptionLength:0,showCaptionTitle:!0,showCaptionDescription:!0,attr:{elem:{},inner:{},anchor:{},image:{},caption:{elem:{},inner:{},title:{},description:{}}}}},{item:{elem:"fg-item",inner:"fg-item-inner",anchor:"fg-thumb",image:"fg-image",loader:"fg-loader",idle:"fg-idle",loading:"fg-loading",loaded:"fg-loaded",error:"fg-error",caption:{elem:"fg-caption",inner:"fg-caption-inner",title:"fg-caption-title",description:"fg-caption-desc"}}},{item:{}}),b.components.register("item",b.Item)}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.obj),function(a,b,c,d,e,f){b.Items=b.Component.extend({construct:function(a){var b=this;b._super(a),b.idMap={},b._fetched=null,b._arr=[],b._available=[],b._canvas=document.createElement("canvas");var d=b.tmpl.cls.item.caption;b.tmpl.sel.item.caption.all=c.selectify([d.elem,d.inner,d.title,d.description])},destroy:function(){var b=this,c=b.all(),d=[];c.length>0&&(b.tmpl.raise("destroy-items",[c]),d=a.map(c,function(a){return a.destroy()?a:null}),d.length>0&&b.tmpl.raise("destroyed-items",[d])),b.idMap={},b._canvas=b._fetched=null,b._arr=[],b._available=[],b._super()},fetch:function(b){var c=this;if(!b&&d.promise(c._fetched))return c._fetched;var e=c.tmpl,f=e.sel,g=e.opt.items,h=a.Deferred(),i=c.make(e.$el.find(f.item.elem));return d.empty(g)?(i.push.apply(i,c.make(window[e.id+"-items"])),h.resolve(i)):d.array(g)?(i.push.apply(i,c.make(g)),h.resolve(i)):d.string(g)?a.get(g).then(function(a){i.push.apply(i,c.make(a)),h.resolve(i)},function(a,b,c){console.log("FooGallery: GET items error.",g,a,b,c),h.resolve(i)}):h.resolve(i),h.then(function(a){c.setAll(a)}),c._fetched=h.promise()},all:function(){return this._arr.slice()},count:function(a){return a?this.all().length:this.available().length},available:function(){return this._available.slice()},get:function(a){return!d.empty(a)&&this.idMap[a]?this.idMap[a]:null},setAll:function(a){this._arr=d.array(a)?a:[],this.idMap=this.createIdMap(a),this._available=this.all()},setAvailable:function(a){this._available=d.array(a)?a:[]},reset:function(){this.setAvailable(this.all())},placeholder:function(a,c){return this._canvas&&this._canvas.toDataURL&&d.number(a)&&d.number(c)?(this._canvas.width=a,this._canvas.height=c,this._canvas.toDataURL()):b.emptyImage},loadable:function(b){var e,f=this,g=f.tmpl.opt;return g.lazy&&(e=c.getViewportBounds(g.viewport)),d.array(b)?a.map(b,function(a){return a.isCreated&&a.isAttached&&!a.isLoading&&!a.isLoaded&&!a.isError&&(!g.lazy||g.lazy&&a.intersects(e))?a:null}):[]},creatable:function(c){return d.array(c)?a.map(c,function(a){return a instanceof b.Item&&!a.isCreated?a:null}):[]},appendable:function(c){return d.array(c)?a.map(c,function(a){return a instanceof b.Item&&a.isCreated&&!a.isAttached?a:null}):[]},detachable:function(c){return d.array(c)?a.map(c,function(a){return a instanceof b.Item&&a.isCreated&&a.isAttached?a:null}):[]},jquerify:function(b){return a(a.map(b,function(a){return a.$el.get()}))},make:function(c){var e=this,g=[];if(d.jq(c)||d.array(c)){var h=[],i=a.makeArray(c);if(0===i.length)return g;e.tmpl.raise("make-items",[i]).isDefaultPrevented()||(g=a.map(i,function(a){var c=e.type(a),g=f.extend(d.hash(a)?a:{},{type:c}),i=b.components.make(c,e.tmpl,g);return d.element(a)?i.parse(a)?(h.push(i),i):null:i})),g.length>0&&e.tmpl.raise("made-items",[g]),h.length>0&&e.tmpl.raise("parsed-items",[h])}return g},type:function(c){var e;return d.hash(c)?e=c.type:d.element(c)&&(e=a(c).find(this.tmpl.sel.item.anchor).data("type")),d.string(e)&&b.components.contains(e)?e:"item"},create:function(b,c){var e=this,f=[],g=e.creatable(b);if(g.length>0){e.tmpl.raise("create-items",[g]).isDefaultPrevented()||(f=a.map(g,function(a){return a.create()?a:null})),f.length>0&&e.tmpl.raise("created-items",[f])}return d.boolean(c)&&c?e.append(b):f},append:function(b){var c=this,d=[],e=c.appendable(b);if(e.length>0){c.tmpl.raise("append-items",[e]).isDefaultPrevented()||(d=a.map(e,function(a){return a.append()?a:null})),d.length>0&&c.tmpl.raise("appended-items",[d])}return d},detach:function(b){var c=this,d=[],e=c.detachable(b);if(e.length>0){c.tmpl.raise("detach-items",[e]).isDefaultPrevented()||(d=a.map(e,function(a){return a.detach()?a:null})),d.length>0&&c.tmpl.raise("detached-items",[d])}return d},load:function(b){var c=this;if(b=c.loadable(b),b.length>0){if(!c.tmpl.raise("load-items",[b]).isDefaultPrevented()){var d=a.map(b,function(a){return a.load()});return e.when(d).done(function(a){c.tmpl.raise("loaded-items",[a])})}}return e.resolveWith([])},createIdMap:function(b){var c={};return a.each(b,function(a,b){d.empty(b.id)&&(b.id=""+(a+1)),c[b.id]=b}),c}}),b.components.register("items",b.Items)}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is,FooGallery.utils.fn,FooGallery.utils.obj),function(a,b,c,d){b.Paging=b.Component.extend({construct:function(a){var b=this;b._super(a),b.opt=b.tmpl.opt.paging,b.cls=b.tmpl.cls.paging,b.il8n=b.tmpl.il8n.paging,b.sel=b.tmpl.sel.paging,b.pushOrReplace=b.opt.pushOrReplace,b.type=b.opt.type,b.theme=b.opt.theme,b.size=b.opt.size,b.position=b.opt.position,b.scrollToTop=b.opt.scrollToTop,b.current=0,b.total=0,b.ctrls=[],b._arr=[]},destroy:function(){var b=this;b._arr.splice(0,b._arr.length),a.each(b.ctrls.splice(0,b.ctrls.length),function(a,b){b.destroy()}),b._super()},build:function(){var a=this,c=a.tmpl.items.available();a.total=a.size>0&&c.length>0?Math.ceil(c.length/a.size):1;for(var d=0;d<a.total;d++)a._arr.push(c.splice(0,a.size));if(a.total>1&&b.paging.hasCtrl(a.type)){var e,f,g=a.position;"both"!==g&&"top"!==g||(e=b.paging.makeCtrl(a.type,a.tmpl,a,"top"),e.create()&&(e.append(),a.ctrls.push(e))),"both"!==g&&"bottom"!==g||(f=b.paging.makeCtrl(a.type,a.tmpl,a,"bottom"),f.create()&&(f.append(),a.ctrls.push(f)))}},rebuild:function(){var b=this;b.current=0,b.total=0,b._arr.splice(0,b._arr.length),a.each(b.ctrls.splice(0,b.ctrls.length),function(a,b){b.destroy()}),b.build()},all:function(){return this._arr.slice()},available:function(){return this.get(this.current)},controls:function(b){var c=this;c.isValid(b)&&a.each(c.ctrls,function(a,c){c.update(b)})},isValid:function(a){return d.number(a)&&a>0&&a<=this.total},number:function(a){return this.isValid(a)?a:0===this.current?1:this.current},create:function(a,b){var c=this;a=c.number(a);var d=a-1;c.tmpl.items.detach(c.tmpl.items.all()),c.tmpl.items.create(c._arr[d],!0),c.current=a},get:function(a){var b=this;return b.isValid(a)?(a=b.number(a),b._arr[a-1]):[]},set:function(a,b,c,e){var f=this;if(f.isValid(a)){var g,h=f.number(a);if(h!==f.current){var i=f.current,j=function(){if(c=!d.boolean(c)||c,e=!!d.boolean(e)&&e,c&&1===f.current&&!f.tmpl.state.exists()&&(g=f.tmpl.state.get(),f.tmpl.state.update(g,f.pushOrReplace)),f.controls(a),f.create(h,e),c&&(g=f.tmpl.state.get(),f.tmpl.state.update(g,f.pushOrReplace)),f.scrollToTop&&d.boolean(b)&&b){var j=f.get(f.current);j.length>0&&j[0].scrollTo("top")}f.tmpl.raise("after-page-change",[f.current,i,e])};return!f.tmpl.raise("before-page-change",[f.current,h,j,e]).isDefaultPrevented()&&(j(),!0)}}return!1},find:function(b){for(var c=this,d=0,e=c._arr.length;d<e;d++)if(-1!==a.inArray(b,c._arr[d]))return d+1;return 0},contains:function(b,c){var d=this.get(b);return-1!==a.inArray(c,d)},first:function(){this.goto(1)},last:function(){this.goto(this._arr.length)},prev:function(){this.goto(this.current-1)},next:function(){this.goto(this.current+1)},goto:function(a){var b=this;b.set(a,!0)&&b.tmpl.loadAvailable()}}),b.PagingControl=b.Component.extend({construct:function(a,b,c){var d=this;d._super(a),d.pages=b,d.position=c,d.$container=null},create:function(){var b=this;return b.$container=a("<nav/>",{class:b.pages.cls.container}).addClass(b.pages.theme),!0},destroy:function(){var a=this;a.$container.remove(),a.$container=null},append:function(){var a=this;"top"===a.position?a.$container.insertBefore(a.tmpl.$el):a.$container.insertAfter(a.tmpl.$el)},update:function(a){}}),b.paging.register("default",b.Paging,null,{type:"none",theme:"fg-light",size:30,pushOrReplace:"push",position:"none",scrollToTop:!0},{container:"fg-paging-container"},null,-100)}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is),function(a,b,c,d){b.Dots=b.Paging.extend({}),b.DotsControl=b.PagingControl.extend({construct:function(b,c,d){this._super(b,c,d),this.$container=a(),this.$list=a(),this.$items=a()},create:function(){for(var b,c=this,d=c.pages.cls,e=c.pages.il8n,f=[],g=a("<ul/>",{class:d.list}),h=0,i=c.pages.total;h<i;h++)f.push(b=c.createItem(h+1,e.page)),g.append(b);return c.$list=g,c.$container=a("<nav/>",{class:d.container}).addClass(c.pages.theme).append(g),c.$items=a(a.map(f,function(a){return a.get()})),!0},append:function(){var a=this;"top"===a.position?a.$container.insertBefore(a.tmpl.$el):a.$container.insertAfter(a.tmpl.$el)},destroy:function(){var b=this,c=b.pages.sel;b.$list.find(c.link).off("click.foogallery",b.onLinkClick),b.$container.remove(),b.$container=a(),b.$list=a(),b.$items=a()},update:function(a){this.setSelected(a-1)},setSelected:function(b){var c=this,e=c.pages.cls,f=c.pages.il8n,g=c.pages.sel;c.$items.filter(g.selected).removeClass(e.selected).each(function(b,c){var e=a(c),f=e.data("label"),h=e.find(g.reader);d.string(f)&&0!==h.length&&h.html(f)}),c.$items.eq(b).addClass(e.selected).each(function(b,c){var e=a(c),h=e.find(g.reader),i=h.html();d.string(i)&&0!==h.length&&(e.data("label",i),h.html(f.current))})},createItem:function(b,c,e,f,g){e=d.string(e)?e:b,c=d.string(c)?c:"";var h=this,i=h.pages.opt,j=h.pages.cls,k=a("<a/>",{class:j.link,href:"#page-"+b}).html(e).on("click.foogallery",{self:h,page:b},h.onLinkClick);d.empty(c)||k.attr("title",c.replace(/\{PAGE}/g,b).replace(/\{LIMIT}/g,i.limit+"")),g=d.string(g)?g:c,d.empty(g)||k.prepend(a("<span/>",{class:j.reader,text:g.replace(/\{PAGE}/g,"").replace(/\{LIMIT}/g,i.limit+"")}));var l=a("<li/>",{class:j.item}).append(k);return f=d.string(f)?f:"",d.empty(f)||l.addClass(f),l},onLinkClick:function(b){b.preventDefault();var c=b.data.self,d=b.data.page,e=c.pages.sel;a(this).closest(e.item).is(e.disabled)||(c.pages.set(d,!0),c.tmpl.loadAvailable())}}),b.paging.register("dots",b.Dots,b.DotsControl,{type:"dots",position:"both",pushOrReplace:"push"},{list:"fg-dots",item:"fg-dot-item",link:"fg-dot-link",disabled:"fg-disabled",selected:"fg-selected",visible:"fg-visible",reader:"fg-sr-only"},{current:"Current page",page:"Page {PAGE}"})}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is),function(a,b,c){b.DefaultTemplate=b.Template.extend({}),b.template.register("default",b.DefaultTemplate,null,{container:"foogallery fg-default"})}(FooGallery.$,FooGallery,FooGallery.utils),function(a,b,c,d){b.MasonryTemplate=b.Template.extend({construct:function(a,b){this._super(a,b),this.masonry=null,this.style=null,this.$columnWidth=null},getStylesheet:function(){var a=this;return null===a.style&&(a.style=document.createElement("style"),a.style.appendChild(document.createTextNode("")),document.head.appendChild(a.style)),a.style.sheet},onPreInit:function(b,c){var e=c.sel,f=c.cls;f.layouts=a.map(f.layout,function(a){return a}).join(" ");for(var g=a.map(f.layout,function(a,b){return{key:b,value:a}}),h=0,i=g.length;h<i;h++)if(c.$el.hasClass(g[h].value)){c.template.layout=g[h].key;break}d.string(f.layout[c.template.layout])||(c.template.layout="col4");var j,k,l="fixed"===c.template.layout;if(c.template.isFitWidth=l,c.template.percentPosition=!l,c.template.transitionDuration=0,c.template.itemSelector=e.item.elem,c.$el.removeClass(f.layouts).addClass(f.layout[c.template.layout]),l||(0===c.$el.find(e.gutterWidth).length&&c.$el.prepend(a("<div/>").addClass(f.gutterWidth)),c.template.gutter=e.gutterWidth),0===c.$el.find(e.columnWidth).length&&c.$el.prepend(a("<div/>").addClass(f.columnWidth)),l&&d.number(c.template.columnWidth)){var m=c.$el.find(e.columnWidth).width(c.template.columnWidth);j=c.getStylesheet(),k="#"+c.id+e.container+" "+e.item.elem+" { width: "+m.outerWidth()+"px; }",j.insertRule(k,0)}c.template.columnWidth=e.columnWidth,l&&d.number(c.template.gutter)&&(j=c.getStylesheet(),k="#"+c.id+e.container+" "+e.item.elem+" { margin-bottom: "+c.template.gutter+"px; }",j.insertRule(k,0)),c.masonry=new Masonry(c.$el.get(0),c.template)},onPostInit:function(a,b){b.masonry.layout()},onFirstLoad:function(a,b){b.masonry.layout()},onReady:function(a,b){b.masonry.layout()},onDestroy:function(a,b){b.$el.find(b.sel.columnWidth).remove(),b.$el.find(b.sel.gutterWidth).remove(),b.style&&b.style.parentNode&&b.style.parentNode.removeChild(b.style)},onDestroyed:function(a,b){b.masonry instanceof Masonry&&b.masonry.destroy()},onLayout:function(a,b){b.masonry.layout()},onParsedItems:function(a,b,c){b.masonry.layout()},onAppendedItems:function(a,b,c){c=b.items.jquerify(c),c=b.masonry.addItems(c),b.masonry.layoutItems(c,!0)},onDetachItem:function(a,b,c){a.isDefaultPrevented()||(a.preventDefault(),b.masonry.remove(c.$el),c.isAttached=!1,c.unfix())},onDetachedItems:function(a,b,c){b.masonry.layout()},onLoadedItems:function(a,b,c){b.masonry.layout()}}),b.template.register("masonry",b.MasonryTemplate,{template:{initLayout:!1,isInitLayout:!1,layout:"col4"}},{container:"foogallery fg-masonry",columnWidth:"fg-column-width",gutterWidth:"fg-gutter-width",layout:{fixed:"fg-masonry-fixed",col2:"fg-masonry-2col",col3:"fg-masonry-3col",col4:"fg-masonry-4col",col5:"fg-masonry-5col"}})}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is),function(a,b,c,d){b.Justified=c.Class.extend({construct:function(c,d){this.$el=a(c),this.options=a.extend(!0,{},b.Justified.defaults,d),this._items=[]},init:function(){var b=this;d.string(b.options.maxRowHeight)&&(b.options.maxRowHeight.indexOf("%")?b.options.maxRowHeight=b.options.rowHeight*(parseInt(b.options.maxRowHeight)/100):b.options.maxRowHeight=parseInt(b.options.maxRowHeight)),a(window).on("resize.justified",{self:b},b.onWindowResize)},destroy:function(){a(window).off("resize.justified"),this.$el.removeAttr("style")},parse:function(){var b=this,c=b.$el.is(":visible"),d=a("<div/>",{class:b.$el.attr("class")}).css({position:"absolute",top:0,left:-9999,visibility:"hidden"}).appendTo("body");return b._items=b.$el.find(b.options.itemSelector).removeAttr("style").removeClass("fg-positioned").map(function(e,f){var g,h=a(f),i=0,j=0;if(c)i=h.outerWidth(),j=h.outerHeight();else{var k=h.clone();k.appendTo(d),i=k.outerWidth(),j=k.outerHeight()}return g=b.options.rowHeight/j,{index:e,width:i*g,height:b.options.rowHeight,top:0,left:0,$item:h}}).get(),d.remove(),b._items},round:function(a){return Math.round(a)},getContainerWidth:function(){var a=this;return a.$el.is(":visible")?a.$el.width():a.$el.parents(":visible:first").width()},layout:function(a,b){a=!!d.boolean(a)&&a,b=!d.boolean(b)||b,(a||0===this._items.length)&&this.parse();for(var c,e=this,f=e.getContainerWidth(),g=e.rows(f),h=0,i=0,j=g.length;i<j;i++)c=g[i],h=i===j-1?e.lastRow(c,f,h):e.justify(c,f,h),e.render(c);e.$el.height(h),b&&e.getContainerWidth()<f&&e.layout(!1,!1)},render:function(a){for(var b,c=0,d=a.items.length;c<d;c++)b=a.items[c],a.visible?b.$item.css({width:b.width,height:b.height,top:b.top,left:b.left,display:"",maxHeight:this.options.maxRowHeight>0?this.options.maxRowHeight:""}).addClass("fg-positioned"):b.$item.css("display","none")},lastRow:function(a,b,c){var d=this;switch(d.options.lastRow){case"hide":a.visible=!1;break;case"justify":c=d.justify(a,b,c);break;case"nojustify":c=a.width/b>d.options.justifyThreshold?d.justify(a,b,c):d.position(a,b,c,"left");break;case"right":case"center":case"left":c=d.position(a,b,c,d.options.lastRow);break;default:c=d.position(a,b,c,"left")}return c},justify:function(a,b,c){var d=this,e=0,f=d.options.margins*(a.items.length-1),g=(b-f)/a.width;a.index>0&&(c+=d.options.margins),a.top=c,a.width=d.round(a.width*g),a.height=d.round(a.height*g);for(var h,i=0,j=a.items.length;i<j;i++)h=a.items[i],h.width=d.round(h.width*g),h.height=d.round(h.height*g),h.top=c,i>0&&(e+=d.options.margins),h.left=e,e+=h.width;return c+(a.height>d.options.maxRowHeight?d.options.maxRowHeight:a.height)},position:function(a,b,c,d){var e=this,f=a.items[a.items.length-1],g=b-(f.left+f.width);a.index>0&&(c+=e.options.margins),a.top=c;for(var h,i=0,j=a.items.length;i<j;i++)h=a.items[i],h.top=c,"center"===d?h.left+=g/2:"right"===d&&(h.left+=g);return c+a.height},items:function(){return a.map(this._items,function(a){return{index:a.index,width:a.width,height:a.height,$item:a.$item,top:a.top,left:a.left}})},rows:function(a){for(var b=this,c=b.items(),d=[],e=c.length>0,f=-1,g=0;e;){f+=1,f>0&&(g+=b.options.margins);for(var h,i,j,k={index:f,visible:!0,top:g,width:0,height:b.options.rowHeight,items:[]},l=[],m=0,n=0,o=c.length;n<o&&(i=c[n],!((h=k.width+i.width)>a&&n>0));n++)h>a&&0==n&&(h=a,j=a/i.width,i.width=b.round(i.width*j),i.height=b.round(i.height*j),k.height=i.height),i.top=k.top,n>0&&(m+=b.options.margins),i.left=m,m+=i.width,k.width=h,k.items.push(i),l.push(n);if(0===l.length){e=!1;break}l.sort(function(a,b){return b-a});for(var p=0,q=l.length;p<q;p++)c.splice(l[p],1);d.push(k),e=c.length>0}return d},onWindowResize:function(a){a.data.self.layout()}}),b.Justified.defaults={itemSelector:".fg-item",rowHeight:150,maxRowHeight:"200%",margins:0,lastRow:"center",justifyThreshold:.5}}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is),function(a,b,c){b.JustifiedTemplate=b.Template.extend({onPreInit:function(a,c){c.justified=new b.Justified(c.$el.get(0),c.template)},onInit:function(a,b){b.justified.init()},onFirstLoad:function(a,b){b.justified.layout(!0)},onReady:function(a,b){b.justified.layout()},onDestroy:function(a,b){b.justified.destroy()},onLayout:function(a,b){b.justified.layout(!0)},onParsedItems:function(a,b,c){b.justified.layout(!0)},onAppendedItems:function(a,b,c){b.justified.layout(!0)},onDetachedItems:function(a,b,c){b.justified.layout(!0)}}),b.template.register("justified",b.JustifiedTemplate,null,{container:"foogallery fg-justified"})}(FooGallery.$,FooGallery,FooGallery.utils),function(a,b,c,d){b.Portfolio=c.Class.extend({construct:function(c,d){this.$el=a(c),this.options=a.extend(!0,{},b.Portfolio.defaults,d),this._items=[]},init:function(){var b=this;a(window).on("resize.portfolio",{self:b},b.onWindowResize)},destroy:function(){a(window).off("resize.portfolio"),this.$el.removeAttr("style")},parse:function(){var b=this,c=b.$el.is(":visible"),d=a("<div/>",{class:b.$el.attr("class")}).css({position:"absolute",top:0,left:-9999,visibility:"hidden"}).appendTo("body");return b._items=b.$el.find(".fg-item").removeAttr("style").removeClass("fg-positioned").map(function(b,e){var f=a(e),g=f.find(".fg-thumb"),h=f.find(".fg-image"),i=0,j=0;if(f.find(".fg-caption").css("max-width",parseFloat(h.attr("width"))),h.css({width:h.attr("width"),height:h.attr("height")}),c)i=f.outerWidth(),j=f.outerHeight();else{var k=f.clone();k.appendTo(d),i=k.outerWidth(),j=k.outerHeight()}return h.css({width:"",height:""}),{index:b,width:i,height:j,top:0,left:0,$item:f,$thumb:g}}).get(),d.remove(),b._items},round:function(a){return Math.round(2*a)/2},getContainerWidth:function(){var a=this;return a.$el.is(":visible")?a.$el.width():a.$el.parents(":visible:first").width()},layout:function(a,b){a=!!d.boolean(a)&&a,b=!d.boolean(b)||b,(a||0===this._items.length)&&this.parse();for(var c,e=this,f=e.getContainerWidth(),g=e.rows(f),h=0,i=0,j=g.length;i<j;i++)c=g[i],h=e.position(c,f,h,e.options.align),e.render(c);e.$el.height(h),b&&e.getContainerWidth()<f&&e.layout(!1,!1)},render:function(a){for(var b,c=0,d=a.items.length;c<d;c++)b=a.items[c],a.visible?b.$item.css({width:b.width,height:a.height,top:b.top,left:b.left,display:""}).addClass("fg-positioned"):b.$item.css("display","none")},position:function(a,b,c,d){var e=this,f=a.items[a.items.length-1],g=b-(f.left+f.width);a.index>0&&(c+=e.options.gutter),a.top=c;for(var h,i=0,j=a.items.length;i<j;i++)h=a.items[i],h.top=c,"center"===d?h.left+=g/2:"right"===d&&(h.left+=g);return c+a.height},items:function(){return a.map(this._items,function(a){return{index:a.index,width:a.width,height:a.height,$item:a.$item,$thumb:a.$thumb,top:a.top,left:a.left}})},rows:function(a){for(var b=this,c=b.items(),d=[],e=c.length>0,f=-1,g=0;e;){f+=1,f>0&&(g+=b.options.gutter);for(var h,i,j,k={index:f,visible:!0,top:g,width:0,height:0,items:[]},l=[],m=0,n=0,o=c.length;n<o&&(i=c[n],!((h=k.width+i.width)>a&&n>0));n++)h>a&&0==n&&(h=a,j=a/i.width,i.width=b.round(i.width*j),i.height=b.round(i.height*j),k.height=i.height),i.top=k.top,n>0&&(m+=b.options.gutter),n!==o-1&&(h+=b.options.gutter),i.left=m,m+=i.width,i.height>k.height&&(k.height=i.height),k.width=h,k.items.push(i),l.push(n);if(0===l.length){e=!1;break}l.sort(function(a,b){return b-a});for(var p=0,q=l.length;p<q;p++)c.splice(l[p],1);d.push(k),e=c.length>0}return d},onWindowResize:function(a){a.data.self.layout()}}),b.Portfolio.defaults={gutter:40,align:"center"}}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.is),function(a,b,c){b.PortfolioTemplate=b.Template.extend({construct:function(a,b){this._super(a,b),this.portfolio=null},onPreInit:function(a,c){c.portfolio=new b.Portfolio(c.$el.get(0),c.template)},onInit:function(a,b){b.portfolio.init()},onFirstLoad:function(a,b){b.portfolio.layout(!0)},onReady:function(a,b){b.portfolio.layout()},onDestroy:function(a,b){b.portfolio.destroy()},onLayout:function(a,b){b.portfolio.layout(!0)},onParsedItems:function(a,b,c){b.portfolio.layout(!0)},onAppendedItems:function(a,b,c){b.portfolio.layout(!0)},onDetachedItems:function(a,b,c){b.portfolio.layout(!0)}}),b.template.register("simple_portfolio",b.PortfolioTemplate,{gutter:40},{container:"foogallery fg-simple_portfolio"})}(FooGallery.$,FooGallery,FooGallery.utils),function(a,b,c,d){b.ImageViewerTemplate=b.Template.extend({construct:function(b,c){this._super(d.extend({},b,{paging:{pushOrReplace:"replace",theme:"fg-light",type:"default",size:1,position:"none",scrollToTop:!1}}),c),this.$inner=a(),this.$current=a(),this.$total=a(),this.$prev=a(),this.$next=a()},createChildren:function(){var b=this;return a("<div/>",{class:b.cls.inner}).append(a("<div/>",{class:b.cls.innerContainer}),a("<div/>",{class:b.cls.controls}).append(a("<div/>",{class:b.cls.prev}).append(a("<span/>",{text:b.il8n.prev})),a("<label/>",{class:b.cls.count,text:b.il8n.count}).prepend(a("<span/>",{class:b.cls.countCurrent,text:"0"})).append(a("<span/>",{class:b.cls.countTotal,text:"0"})),a("<div/>",{class:b.cls.next}).append(a("<span/>",{text:b.il8n.next}))))},onPreInit:function(a,b){b.$inner=b.$el.find(b.sel.innerContainer),b.$current=b.$el.find(b.sel.countCurrent),b.$total=b.$el.find(b.sel.countTotal),b.$prev=b.$el.find(b.sel.prev),b.$next=b.$el.find(b.sel.next)},onInit:function(a,b){b.template.attachFooBox&&b.$el.on("foobox.previous",{self:b},b.onFooBoxPrev).on("foobox.next",{self:b},b.onFooBoxNext),b.$prev.on("click",{self:b},b.onPrevClick),b.$next.on("click",{self:b},b.onNextClick)},onFirstLoad:function(a,b){b.update()},onDestroy:function(a,b){b.template.attachFooBox&&b.$el.off({"foobox.previous":b.onFooBoxPrev,"foobox.next":b.onFooBoxNext}),b.$prev.off("click",b.onPrevClick),b.$next.off("click",b.onNextClick)},onAppendItem:function(a,b,c){a.preventDefault(),b.$inner.append(c.$el),c.fix(),c.isAttached=!0},onAfterPageChange:function(a,b,c,d,e){e||b.update()},onAfterFilterChange:function(a,b){b.update()},update:function(){this.pages&&(this.$current.text(this.pages.current),this.$total.text(this.pages.total))},prev:function(){this.pages&&(this.template.loop&&1===this.pages.current?this.pages.last():this.pages.prev(),this.update())},next:function(){this.pages&&(this.template.loop&&this.pages.current===this.pages.total?this.pages.first():this.pages.next(),this.update())},onFooBoxPrev:function(a){a.data.self.prev()},onFooBoxNext:function(a){a.data.self.next()},onPrevClick:function(a){a.preventDefault(),a.stopPropagation(),a.data.self.prev()},onNextClick:function(a){a.preventDefault(),a.stopPropagation(),a.data.self.next()}}),b.template.register("image-viewer",b.ImageViewerTemplate,{template:{attachFooBox:!1,loop:!1}},{container:"foogallery fg-image-viewer",inner:"fiv-inner",innerContainer:"fiv-inner-container",controls:"fiv-ctrls",prev:"fiv-prev",next:"fiv-next",count:"fiv-count",countCurrent:"fiv-count-current",countTotal:"fiv-count-total"},{prev:"Prev",next:"Next",count:"of"})}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.obj),function(a,b,c){b.ThumbnailTemplate=b.Template.extend({construct:function(b,d){this._super(c.extend({},b,{filtering:{type:"none"},paging:{type:"none"}}),d),this.$hidden=a()},createChildren:function(){var b=this;return b.$hidden=a("<div/>",{class:b.cls.hidden})},onPreInit:function(a,b){b.$hidden=b.$el.find(b.sel.hidden)},onPostInit:function(b,c){for(var d,e=c.items.all().slice(1),f=0,g=e.length;f<g;f++)d=e[f],c.$hidden.append(a("<a/>",{href:d.href,rel:"foobox["+c.id+"]"}).attr(d.attr.anchor));c.items.setAll(c.items.all().slice(0,1))}}),b.template.register("thumbnail",b.ThumbnailTemplate,null,{container:"foogallery fg-thumbnail",hidden:"fg-st-hidden"})}(FooGallery.$,FooGallery,FooGallery.utils.obj),function(a,b,c,d){b.triggerPostLoad=function(b,c,d,e,f){("first-load"===b.type||c.initialized&&("after-page-change"===b.type&&!f||"after-filter-change"===b.type))&&a("body").trigger("post-load")},b.autoDefaults={on:{"first-load.foogallery after-page-change.foogallery after-filter-change.foogallery":b.triggerPostLoad}},b.auto=function(a){b.autoDefaults=d.merge(b.autoDefaults,a)},a(function(){a('[id^="foogallery-"]:not(.fg-ready)').foogallery(b.autoDefaults)}),c.ready(function(){a('[id^="foogallery-"].fg-ready').foogallery(b.autoDefaults)})}(FooGallery.$,FooGallery,FooGallery.utils,FooGallery.utils.obj);
extensions/default-templates/simple-portfolio/class-simple-portfolio-gallery-template.php CHANGED
@@ -63,6 +63,7 @@ if ( !class_exists( 'FooGallery_Simple_Portfolio_Gallery_Template' ) ) {
63
  'paging_support' => true,
64
  'mandatory_classes' => 'fg-simple_portfolio',
65
  'thumbnail_dimensions' => true,
 
66
  'fields' => array(
67
  array(
68
  'id' => 'help',
63
  'paging_support' => true,
64
  'mandatory_classes' => 'fg-simple_portfolio',
65
  'thumbnail_dimensions' => true,
66
+ 'filtering_support' => true,
67
  'fields' => array(
68
  array(
69
  'id' => 'help',
extensions/media-categories/class-media-categories-extension.php DELETED
@@ -1,237 +0,0 @@
1
- <?php
2
- if ( ! class_exists( 'FooGallery_Media_Categories_Extension' ) ) {
3
-
4
- define( 'FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_TAXONOMY', 'foogallery_media_category' );
5
- define( 'FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_SLUG', 'foogallery_media_categories' );
6
- define( 'FOOGALLERY_MEDIA_CATEGORIES_INPUT_TYPE', 'category_checkboxlist' );
7
-
8
- class FooGallery_Media_Categories_Extension {
9
-
10
- /**
11
- * Class Constructor
12
- */
13
- function __construct() {
14
- add_action( 'init', array( $this, 'add_categories_to_attachments' ) );
15
- if ( is_admin() ) {
16
- add_filter( 'foogallery_attachment_custom_fields', array( $this, 'add_media_category_field' ) );
17
- add_filter( 'foogallery_attachment_add_fields', array( $this, 'remove_taxonomy_media_category_field') );
18
- add_filter( 'foogallery_attachment_field_' . FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_SLUG, array( $this, 'customize_media_category_field'), 10, 2 );
19
- add_filter( 'foogallery_attachment_save_field_' . FOOGALLERY_MEDIA_CATEGORIES_INPUT_TYPE, array( $this, 'save_media_category_field' ), 10, 4 );
20
- add_action( 'restrict_manage_posts', array( $this, 'add_category_filter' ) );
21
- }
22
- }
23
-
24
- /**
25
- * Register the category taxonomy for attachments
26
- */
27
- function add_categories_to_attachments() {
28
- $labels = array(
29
- 'name' => __( 'Categories', 'foogallery' ),
30
- 'singular_name' => __( 'Category', 'foogallery' ),
31
- 'search_items' => __( 'Search Categories', 'foogallery' ),
32
- 'all_items' => __( 'All Categories', 'foogallery' ),
33
- 'parent_item' => __( 'Parent Category', 'foogallery' ),
34
- 'parent_item_colon' => __( 'Parent Category:', 'foogallery' ),
35
- 'edit_item' => __( 'Edit Category', 'foogallery' ),
36
- 'update_item' => __( 'Update Category', 'foogallery' ),
37
- 'add_new_item' => __( 'Add New Category', 'foogallery' ),
38
- 'new_item_name' => __( 'New Category Name', 'foogallery' ),
39
- 'menu_name' => __( 'Categories', 'foogallery' )
40
- );
41
-
42
- $args = array(
43
- 'labels' => $labels,
44
- 'hierarchical' => true,
45
- 'query_var' => true,
46
- 'rewrite' => false,
47
- 'show_admin_column' => 'true'
48
- );
49
-
50
- register_taxonomy( FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_TAXONOMY, 'attachment', $args );
51
- }
52
-
53
- /**
54
- * Add a new category field to the attachments
55
- *
56
- * @param $fields array All fields that will be added to the media modal
57
- *
58
- * @return mixed
59
- */
60
- function add_media_category_field( $fields ) {
61
- $args = array(
62
- 'orderby' => 'name',
63
- 'order' => 'ASC',
64
- 'hide_empty' => false
65
- );
66
-
67
- //pull all terms
68
- $terms = get_terms( FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_TAXONOMY, $args );
69
-
70
- $media_categories = array();
71
-
72
- foreach ( $terms as $term ) {
73
- $media_categories[ $term->term_id ] = $term->name;
74
- }
75
-
76
- $fields[FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_SLUG] = array(
77
- 'label' => __( 'Categories', 'foogallery' ),
78
- 'input' => FOOGALLERY_MEDIA_CATEGORIES_INPUT_TYPE,
79
- 'helps' => __( 'Categorize your attachments', 'foogallery' ),
80
- 'options' => $media_categories,
81
- 'exclusions' => array()
82
- );
83
-
84
- return $fields;
85
- }
86
-
87
- /**
88
- * Remove the automatically added media category field
89
- * @param $fields
90
- *
91
- * @return mixed
92
- */
93
- function remove_taxonomy_media_category_field( $fields ) {
94
- if ( array_key_exists( FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_TAXONOMY, $fields ) ) {
95
- unset( $fields[FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_TAXONOMY] );
96
- }
97
-
98
- return $fields;
99
- }
100
-
101
- /**
102
- * Customize the media category field to make sure we output a checkboxlist
103
- * @param $field_values
104
- *
105
- * @return mixed
106
- */
107
- function customize_media_category_field( $field_values, $post_id ) {
108
-
109
- $media_categories = array();
110
-
111
- //get the terms linked to the attachment
112
- $terms = get_the_terms( $post_id, FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_TAXONOMY );
113
- if ( $terms && ! is_wp_error( $terms ) ) {
114
- foreach ( $terms as $term ) {
115
- $media_categories[ $term->term_id ] = $term->name;
116
- }
117
- }
118
-
119
- //set to html
120
- $field_values['input'] = 'html';
121
-
122
- $html = '';
123
- $i = 0;
124
-
125
- if ( ! empty( $field_values['options'] ) ) {
126
-
127
- foreach ( $field_values['options'] as $k => $v ) {
128
- if ( array_key_exists( $k, $media_categories ) ) {
129
- $checked = ' checked="checked"';
130
- } else {
131
- $checked = '';
132
- }
133
-
134
- $html .= '<input' . $checked . ' value="' . $k . '" type="checkbox" name="attachments[' . $post_id . '][' . FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_SLUG . '][' . $k . ']" id="' . sanitize_key( FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_SLUG . '_' . $post_id . '_' . $i ) . '" /> <label for="' . sanitize_key( FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_SLUG . '_' . $post_id . '_' . $i ) . '">' . $v . '</label> ';
135
- $i++;
136
- }
137
- }
138
-
139
- if ( 0 === $i ) {
140
- $html .= __( 'No Categories Available!', 'foogallery' );
141
- }
142
-
143
- $html .= '<style>.compat-field-foogallery_media_categories .field input {margin-right: 0px;} .compat-field-foogallery_media_categories .field label {vertical-align: bottom; margin-right: 10px;}</style>';
144
-
145
- $html .= '<br /><a target="_blank" href="' . admin_url( 'edit-tags.php?taxonomy=' . FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_TAXONOMY . '&post_type=attachment' ) . '">' . __( 'Manage Categories', 'foogallery' ) . '</a>';
146
-
147
- $field_values['value'] = '';
148
- $field_values['html'] = $html;
149
-
150
- return $field_values;
151
- }
152
-
153
- /**
154
- * Save the categories for the attachment
155
- *
156
- * @param $field
157
- * @param $values
158
- * @param $post
159
- * @param $attachment
160
- */
161
- function save_media_category_field($field, $values, $post, $attachment) {
162
- $post_id = $post['ID'];
163
-
164
- //first clear any categories for the post
165
- wp_delete_object_term_relationships( $post_id, FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_TAXONOMY );
166
-
167
- $category_ids = $attachment[ $field ];
168
-
169
- if ( !empty( $category_ids ) ) {
170
- //clean category ids
171
- $category_ids = array_keys( $category_ids );
172
- $category_ids = array_map( 'intval', $category_ids );
173
- $category_ids = array_unique( $category_ids );
174
-
175
- $term_taxonomy_ids = wp_set_object_terms( $post_id, $category_ids, FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_TAXONOMY );
176
-
177
- if ( is_wp_error( $term_taxonomy_ids ) ) {
178
- // There was an error somewhere and the terms couldn't be set.
179
- $post['errors'][ $field ]['errors'][] = __( 'Error saving the categories for the attachment!', 'foogallery' );
180
- }
181
- }
182
- }
183
-
184
-
185
- /***
186
- *
187
- * Add a category filter
188
- */
189
- function add_category_filter() {
190
- global $pagenow;
191
- if ( 'upload.php' == $pagenow ) {
192
-
193
- $dropdown_options = array(
194
- 'taxonomy' => FOOGALLERY_MEDIA_CATEGORIES_EXTENSION_TAXONOMY,
195
- 'show_option_all' => __( 'View all categories' ),
196
- 'hide_empty' => false,
197
- 'hierarchical' => true,
198
- 'orderby' => 'name',
199
- 'show_count' => true,
200
- //'walker' => new foogallery_walker_category_dropdown(),
201
- 'value' => 'slug'
202
- );
203
-
204
- wp_dropdown_categories( $dropdown_options );
205
- }
206
- }
207
- }
208
- }
209
-
210
- /** Custom walker for wp_dropdown_categories, based on https://gist.github.com/stephenh1988/2902509 */
211
- class foogallery_walker_category_dropdown extends Walker_CategoryDropdown{
212
-
213
- function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {
214
- $pad = str_repeat( '&nbsp;', $depth * 3 );
215
- $cat_name = apply_filters( 'list_cats', $category->name, $category );
216
-
217
- if( ! isset( $args['value'] ) ) {
218
- $args['value'] = ( $category->taxonomy != 'category' ? 'slug' : 'id' );
219
- }
220
-
221
- $value = ( $args['value']=='slug' ? $category->slug : $category->term_id );
222
- if ( 0 == $args['selected'] && isset( $_GET['category_media'] ) && '' != $_GET['category_media'] ) {
223
- $args['selected'] = $_GET['category_media'];
224
- }
225
-
226
- $output .= '<option class="level-' . $depth . '" value="' . $value . '"';
227
- if ( $value === (string) $args['selected'] ) {
228
- $output .= ' selected="selected"';
229
- }
230
- $output .= '>';
231
- $output .= $pad . $cat_name;
232
- if ( $args['show_count'] )
233
- $output .= '&nbsp;&nbsp;(' . $category->count . ')';
234
-
235
- $output .= "</option>\n";
236
- }
237
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
foogallery.php CHANGED
@@ -3,7 +3,7 @@
3
  /*
4
  Plugin Name: FooGallery
5
  Description: FooGallery is the most intuitive and extensible gallery management tool ever created for WordPress
6
- Version: 1.4.15
7
  Author: FooPlugins
8
  Plugin URI: https://foo.gallery
9
  Author URI: http://fooplugins.com
@@ -23,7 +23,7 @@ if ( !class_exists( 'FooGallery_Plugin' ) ) {
23
  define( 'FOOGALLERY_PATH', plugin_dir_path( __FILE__ ) );
24
  define( 'FOOGALLERY_URL', plugin_dir_url( __FILE__ ) );
25
  define( 'FOOGALLERY_FILE', __FILE__ );
26
- define( 'FOOGALLERY_VERSION', '1.4.15' );
27
  define( 'FOOGALLERY_SETTINGS_VERSION', '2' );
28
  require_once FOOGALLERY_PATH . 'includes/constants.php';
29
  // Create a helper function for easy SDK access.
@@ -40,7 +40,6 @@ if ( !class_exists( 'FooGallery_Plugin' ) ) {
40
  'type' => 'plugin',
41
  'public_key' => 'pk_d87616455a835af1d0658699d0192',
42
  'is_premium' => false,
43
- 'has_addons' => false,
44
  'has_paid_plans' => true,
45
  'trial' => array(
46
  'days' => 7,
@@ -117,13 +116,19 @@ if ( !class_exists( 'FooGallery_Plugin' ) ) {
117
  10,
118
  6
119
  );
 
 
 
 
 
 
 
120
  add_action( 'foogallery_admin_menu_before', array( $this, 'add_freemius_activation_menu' ) );
121
  } else {
122
  new FooGallery_Public();
123
  }
124
 
125
  new FooGallery_Thumbnails();
126
- new FooGallery_Polylang_Compatibility();
127
  new FooGallery_Attachment_Filters();
128
  new FooGallery_Retina();
129
  new FooGallery_WPThumb_Enhancements();
@@ -133,16 +138,42 @@ if ( !class_exists( 'FooGallery_Plugin' ) ) {
133
  new FooGallery_LazyLoad();
134
  new FooGallery_Paging();
135
  new FooGallery_Thumbnail_Dimensions();
136
- new FooGallery_FooBox_Support();
137
- new FooGallery_Responsive_Lightbox_dFactory_Support();
138
  new FooGallery_Attachment_Custom_Class();
139
  new FooGallery_Upgrade();
 
140
  new FooGallery_Extensions_Compatibility();
 
141
  $checker = new FooGallery_Version_Check();
142
  $checker->wire_up_checker();
143
  new FooGallery_Widget_Init();
144
  //include the default templates no matter what!
145
  new FooGallery_Default_Templates();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  }
147
 
148
  /**
@@ -194,6 +225,15 @@ if ( !class_exists( 'FooGallery_Plugin' ) ) {
194
  }
195
  }
196
 
 
 
 
 
 
 
 
 
 
197
  /**
198
  * Set default extensions when a new site is created in multisite and FooGallery is network activated
199
  *
3
  /*
4
  Plugin Name: FooGallery
5
  Description: FooGallery is the most intuitive and extensible gallery management tool ever created for WordPress
6
+ Version: 1.4.25
7
  Author: FooPlugins
8
  Plugin URI: https://foo.gallery
9
  Author URI: http://fooplugins.com
23
  define( 'FOOGALLERY_PATH', plugin_dir_path( __FILE__ ) );
24
  define( 'FOOGALLERY_URL', plugin_dir_url( __FILE__ ) );
25
  define( 'FOOGALLERY_FILE', __FILE__ );
26
+ define( 'FOOGALLERY_VERSION', '1.4.25' );
27
  define( 'FOOGALLERY_SETTINGS_VERSION', '2' );
28
  require_once FOOGALLERY_PATH . 'includes/constants.php';
29
  // Create a helper function for easy SDK access.
40
  'type' => 'plugin',
41
  'public_key' => 'pk_d87616455a835af1d0658699d0192',
42
  'is_premium' => false,
 
43
  'has_paid_plans' => true,
44
  'trial' => array(
45
  'days' => 7,
116
  10,
117
  6
118
  );
119
+ foogallery_fs()->add_filter(
120
+ 'is_submenu_visible',
121
+ array( $this, 'is_submenu_visible' ),
122
+ 10,
123
+ 2
124
+ );
125
+ foogallery_fs()->add_filter( 'hide_account_tabs', '__return_true' );
126
  add_action( 'foogallery_admin_menu_before', array( $this, 'add_freemius_activation_menu' ) );
127
  } else {
128
  new FooGallery_Public();
129
  }
130
 
131
  new FooGallery_Thumbnails();
 
132
  new FooGallery_Attachment_Filters();
133
  new FooGallery_Retina();
134
  new FooGallery_WPThumb_Enhancements();
138
  new FooGallery_LazyLoad();
139
  new FooGallery_Paging();
140
  new FooGallery_Thumbnail_Dimensions();
 
 
141
  new FooGallery_Attachment_Custom_Class();
142
  new FooGallery_Upgrade();
143
+ new FooGallery_Compatibility();
144
  new FooGallery_Extensions_Compatibility();
145
+ new FooGallery_Default_Crop_Position();
146
  $checker = new FooGallery_Version_Check();
147
  $checker->wire_up_checker();
148
  new FooGallery_Widget_Init();
149
  //include the default templates no matter what!
150
  new FooGallery_Default_Templates();
151
+ add_filter( 'foogallery_extensions_for_view', array( $this, 'add_foogallery_pro_extension' ) );
152
+ }
153
+
154
+ function add_foogallery_pro_extension( $extensions )
155
+ {
156
+ $extension = array(
157
+ 'slug' => 'foogallery-pro',
158
+ 'class' => 'FooGallery_Pro',
159
+ 'categories' => array( 'Featured', 'Premium' ),
160
+ 'title' => 'FooGallery Pro',
161
+ 'description' => 'The best gallery plugin for WordPress just got even better!',
162
+ 'price' => '$49',
163
+ 'author' => 'FooPlugins',
164
+ 'author_url' => 'http://fooplugins.com',
165
+ 'thumbnail' => 'https://s3.amazonaws.com/foogallery/extensions/foogallerypro.png',
166
+ 'tags' => array( 'premium' ),
167
+ 'source' => 'fooplugins',
168
+ "download_button" => array(
169
+ "text" => "Start FREE Trial",
170
+ "target" => "_self",
171
+ "href" => foogallery_fs()->checkout_url( WP_FS__PERIOD_ANNUALLY, true ),
172
+ "confirm" => false,
173
+ ),
174
+ );
175
+ array_unshift( $extensions, $extension );
176
+ return $extensions;
177
  }
178
 
179
  /**
225
  }
226
  }
227
 
228
+ function is_submenu_visible( $visible, $id )
229
+ {
230
+ if ( 'addons' === $id ) {
231
+ //hide addons submenu for now
232
+ $visible = false;
233
+ }
234
+ return $visible;
235
+ }
236
+
237
  /**
238
  * Set default extensions when a new site is created in multisite and FooGallery is network activated
239
  *
freemius/.codeclimate.yml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ engines:
2
+ phpcodesniffer:
3
+ enabled: true
4
+ config:
5
+ standard: "WordPress"
6
+ checks:
7
+ Squiz Commenting InlineComment InvalidEndChar:
8
+ enabled: false
9
+ Squiz Commenting InlineComment SpacingBefore:
10
+ enabled: false
11
+ Squiz Commenting InlineComment WrongStyle:
12
+ enabled: false
13
+ Generic Commenting DocComment MissingShort:
14
+ enabled: false
15
+ Generic WhiteSpace ScopeIndent IncorrectExact:
16
+ enabled: false
17
+ ratings:
18
+ paths:
19
+ - "**.php"
freemius/.github/ISSUE_TEMPLATE.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ **Actual Behavior**:
2
+ - `What is the issue? (*)`
3
+ - `What is the expected behavior?`
4
+
5
+ **Versions**: (*)
6
+ - `Freemius SDK Version:`
7
+ - `WordPress Version:`
8
+ - `PHP Version:`
9
+
10
+ **Plugin / Theme**: (*)
11
+ - `Name:`
12
+ - `Slug:`
13
+ - `Freemius ID:`
14
+
15
+ **Additional Information**:
16
+ - `Browser Type: (*)`
17
+ - `Browser Version: (*)`
18
+ - `OS: (*)`
19
+ - `Stack Traces:`
20
+
21
+ ----
22
+ **Note:** `(*)` indicates required information. Without this information, your issue may be auto-closed.
23
+
24
+ > You can find your Freemius SDK version at `/freemius/start.php`
25
+
26
+ > You can find your Freemius product ID in the Freemius dashboard: `SETTINGS -> INTEGRATION`.
27
+
28
+ > Do not modify the titles or questions. Simply add your responses to the ends of the questions.
29
+ Add more lines if needed.
freemius/.travis.yml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ sudo: false
2
+
3
+ language: php
4
+
5
+ php:
6
+ - 5.3
7
+ - 5.4
8
+ - 5.5
9
+ - 5.6
10
+ - 7.0
11
+ - hhvm
freemius/LICENSE.txt CHANGED
@@ -1,674 +1,674 @@
1
- GNU GENERAL PUBLIC LICENSE
2
- Version 3, 29 June 2007
3
-
4
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
- Everyone is permitted to copy and distribute verbatim copies
6
- of this license document, but changing it is not allowed.
7
-
8
- Preamble
9
-
10
- The GNU General Public License is a free, copyleft license for
11
- software and other kinds of works.
12
-
13
- The licenses for most software and other practical works are designed
14
- to take away your freedom to share and change the works. By contrast,
15
- the GNU General Public License is intended to guarantee your freedom to
16
- share and change all versions of a program--to make sure it remains free
17
- software for all its users. We, the Free Software Foundation, use the
18
- GNU General Public License for most of our software; it applies also to
19
- any other work released this way by its authors. You can apply it to
20
- your programs, too.
21
-
22
- When we speak of free software, we are referring to freedom, not
23
- price. Our General Public Licenses are designed to make sure that you
24
- have the freedom to distribute copies of free software (and charge for
25
- them if you wish), that you receive source code or can get it if you
26
- want it, that you can change the software or use pieces of it in new
27
- free programs, and that you know you can do these things.
28
-
29
- To protect your rights, we need to prevent others from denying you
30
- these rights or asking you to surrender the rights. Therefore, you have
31
- certain responsibilities if you distribute copies of the software, or if
32
- you modify it: responsibilities to respect the freedom of others.
33
-
34
- For example, if you distribute copies of such a program, whether
35
- gratis or for a fee, you must pass on to the recipients the same
36
- freedoms that you received. You must make sure that they, too, receive
37
- or can get the source code. And you must show them these terms so they
38
- know their rights.
39
-
40
- Developers that use the GNU GPL protect your rights with two steps:
41
- (1) assert copyright on the software, and (2) offer you this License
42
- giving you legal permission to copy, distribute and/or modify it.
43
-
44
- For the developers' and authors' protection, the GPL clearly explains
45
- that there is no warranty for this free software. For both users' and
46
- authors' sake, the GPL requires that modified versions be marked as
47
- changed, so that their problems will not be attributed erroneously to
48
- authors of previous versions.
49
-
50
- Some devices are designed to deny users access to install or run
51
- modified versions of the software inside them, although the manufacturer
52
- can do so. This is fundamentally incompatible with the aim of
53
- protecting users' freedom to change the software. The systematic
54
- pattern of such abuse occurs in the area of products for individuals to
55
- use, which is precisely where it is most unacceptable. Therefore, we
56
- have designed this version of the GPL to prohibit the practice for those
57
- products. If such problems arise substantially in other domains, we
58
- stand ready to extend this provision to those domains in future versions
59
- of the GPL, as needed to protect the freedom of users.
60
-
61
- Finally, every program is threatened constantly by software patents.
62
- States should not allow patents to restrict development and use of
63
- software on general-purpose computers, but in those that do, we wish to
64
- avoid the special danger that patents applied to a free program could
65
- make it effectively proprietary. To prevent this, the GPL assures that
66
- patents cannot be used to render the program non-free.
67
-
68
- The precise terms and conditions for copying, distribution and
69
- modification follow.
70
-
71
- TERMS AND CONDITIONS
72
-
73
- 0. Definitions.
74
-
75
- "This License" refers to version 3 of the GNU General Public License.
76
-
77
- "Copyright" also means copyright-like laws that apply to other kinds of
78
- works, such as semiconductor masks.
79
-
80
- "The Program" refers to any copyrightable work licensed under this
81
- License. Each licensee is addressed as "you". "Licensees" and
82
- "recipients" may be individuals or organizations.
83
-
84
- To "modify" a work means to copy from or adapt all or part of the work
85
- in a fashion requiring copyright permission, other than the making of an
86
- exact copy. The resulting work is called a "modified version" of the
87
- earlier work or a work "based on" the earlier work.
88
-
89
- A "covered work" means either the unmodified Program or a work based
90
- on the Program.
91
-
92
- To "propagate" a work means to do anything with it that, without
93
- permission, would make you directly or secondarily liable for
94
- infringement under applicable copyright law, except executing it on a
95
- computer or modifying a private copy. Propagation includes copying,
96
- distribution (with or without modification), making available to the
97
- public, and in some countries other activities as well.
98
-
99
- To "convey" a work means any kind of propagation that enables other
100
- parties to make or receive copies. Mere interaction with a user through
101
- a computer network, with no transfer of a copy, is not conveying.
102
-
103
- An interactive user interface displays "Appropriate Legal Notices"
104
- to the extent that it includes a convenient and prominently visible
105
- feature that (1) displays an appropriate copyright notice, and (2)
106
- tells the user that there is no warranty for the work (except to the
107
- extent that warranties are provided), that licensees may convey the
108
- work under this License, and how to view a copy of this License. If
109
- the interface presents a list of user commands or options, such as a
110
- menu, a prominent item in the list meets this criterion.
111
-
112
- 1. Source Code.
113
-
114
- The "source code" for a work means the preferred form of the work
115
- for making modifications to it. "Object code" means any non-source
116
- form of a work.
117
-
118
- A "Standard Interface" means an interface that either is an official
119
- standard defined by a recognized standards body, or, in the case of
120
- interfaces specified for a particular programming language, one that
121
- is widely used among developers working in that language.
122
-
123
- The "System Libraries" of an executable work include anything, other
124
- than the work as a whole, that (a) is included in the normal form of
125
- packaging a Major Component, but which is not part of that Major
126
- Component, and (b) serves only to enable use of the work with that
127
- Major Component, or to implement a Standard Interface for which an
128
- implementation is available to the public in source code form. A
129
- "Major Component", in this context, means a major essential component
130
- (kernel, window system, and so on) of the specific operating system
131
- (if any) on which the executable work runs, or a compiler used to
132
- produce the work, or an object code interpreter used to run it.
133
-
134
- The "Corresponding Source" for a work in object code form means all
135
- the source code needed to generate, install, and (for an executable
136
- work) run the object code and to modify the work, including scripts to
137
- control those activities. However, it does not include the work's
138
- System Libraries, or general-purpose tools or generally available free
139
- programs which are used unmodified in performing those activities but
140
- which are not part of the work. For example, Corresponding Source
141
- includes interface definition files associated with source files for
142
- the work, and the source code for shared libraries and dynamically
143
- linked subprograms that the work is specifically designed to require,
144
- such as by intimate data communication or control flow between those
145
- subprograms and other parts of the work.
146
-
147
- The Corresponding Source need not include anything that users
148
- can regenerate automatically from other parts of the Corresponding
149
- Source.
150
-
151
- The Corresponding Source for a work in source code form is that
152
- same work.
153
-
154
- 2. Basic Permissions.
155
-
156
- All rights granted under this License are granted for the term of
157
- copyright on the Program, and are irrevocable provided the stated
158
- conditions are met. This License explicitly affirms your unlimited
159
- permission to run the unmodified Program. The output from running a
160
- covered work is covered by this License only if the output, given its
161
- content, constitutes a covered work. This License acknowledges your
162
- rights of fair use or other equivalent, as provided by copyright law.
163
-
164
- You may make, run and propagate covered works that you do not
165
- convey, without conditions so long as your license otherwise remains
166
- in force. You may convey covered works to others for the sole purpose
167
- of having them make modifications exclusively for you, or provide you
168
- with facilities for running those works, provided that you comply with
169
- the terms of this License in conveying all material for which you do
170
- not control copyright. Those thus making or running the covered works
171
- for you must do so exclusively on your behalf, under your direction
172
- and control, on terms that prohibit them from making any copies of
173
- your copyrighted material outside their relationship with you.
174
-
175
- Conveying under any other circumstances is permitted solely under
176
- the conditions stated below. Sublicensing is not allowed; section 10
177
- makes it unnecessary.
178
-
179
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
-
181
- No covered work shall be deemed part of an effective technological
182
- measure under any applicable law fulfilling obligations under article
183
- 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
- similar laws prohibiting or restricting circumvention of such
185
- measures.
186
-
187
- When you convey a covered work, you waive any legal power to forbid
188
- circumvention of technological measures to the extent such circumvention
189
- is effected by exercising rights under this License with respect to
190
- the covered work, and you disclaim any intention to limit operation or
191
- modification of the work as a means of enforcing, against the work's
192
- users, your or third parties' legal rights to forbid circumvention of
193
- technological measures.
194
-
195
- 4. Conveying Verbatim Copies.
196
-
197
- You may convey verbatim copies of the Program's source code as you
198
- receive it, in any medium, provided that you conspicuously and
199
- appropriately publish on each copy an appropriate copyright notice;
200
- keep intact all notices stating that this License and any
201
- non-permissive terms added in accord with section 7 apply to the code;
202
- keep intact all notices of the absence of any warranty; and give all
203
- recipients a copy of this License along with the Program.
204
-
205
- You may charge any price or no price for each copy that you convey,
206
- and you may offer support or warranty protection for a fee.
207
-
208
- 5. Conveying Modified Source Versions.
209
-
210
- You may convey a work based on the Program, or the modifications to
211
- produce it from the Program, in the form of source code under the
212
- terms of section 4, provided that you also meet all of these conditions:
213
-
214
- a) The work must carry prominent notices stating that you modified
215
- it, and giving a relevant date.
216
-
217
- b) The work must carry prominent notices stating that it is
218
- released under this License and any conditions added under section
219
- 7. This requirement modifies the requirement in section 4 to
220
- "keep intact all notices".
221
-
222
- c) You must license the entire work, as a whole, under this
223
- License to anyone who comes into possession of a copy. This
224
- License will therefore apply, along with any applicable section 7
225
- additional terms, to the whole of the work, and all its parts,
226
- regardless of how they are packaged. This License gives no
227
- permission to license the work in any other way, but it does not
228
- invalidate such permission if you have separately received it.
229
-
230
- d) If the work has interactive user interfaces, each must display
231
- Appropriate Legal Notices; however, if the Program has interactive
232
- interfaces that do not display Appropriate Legal Notices, your
233
- work need not make them do so.
234
-
235
- A compilation of a covered work with other separate and independent
236
- works, which are not by their nature extensions of the covered work,
237
- and which are not combined with it such as to form a larger program,
238
- in or on a volume of a storage or distribution medium, is called an
239
- "aggregate" if the compilation and its resulting copyright are not
240
- used to limit the access or legal rights of the compilation's users
241
- beyond what the individual works permit. Inclusion of a covered work
242
- in an aggregate does not cause this License to apply to the other
243
- parts of the aggregate.
244
-
245
- 6. Conveying Non-Source Forms.
246
-
247
- You may convey a covered work in object code form under the terms
248
- of sections 4 and 5, provided that you also convey the
249
- machine-readable Corresponding Source under the terms of this License,
250
- in one of these ways:
251
-
252
- a) Convey the object code in, or embodied in, a physical product
253
- (including a physical distribution medium), accompanied by the
254
- Corresponding Source fixed on a durable physical medium
255
- customarily used for software interchange.
256
-
257
- b) Convey the object code in, or embodied in, a physical product
258
- (including a physical distribution medium), accompanied by a
259
- written offer, valid for at least three years and valid for as
260
- long as you offer spare parts or customer support for that product
261
- model, to give anyone who possesses the object code either (1) a
262
- copy of the Corresponding Source for all the software in the
263
- product that is covered by this License, on a durable physical
264
- medium customarily used for software interchange, for a price no
265
- more than your reasonable cost of physically performing this
266
- conveying of source, or (2) access to copy the
267
- Corresponding Source from a network server at no charge.
268
-
269
- c) Convey individual copies of the object code with a copy of the
270
- written offer to provide the Corresponding Source. This
271
- alternative is allowed only occasionally and noncommercially, and
272
- only if you received the object code with such an offer, in accord
273
- with subsection 6b.
274
-
275
- d) Convey the object code by offering access from a designated
276
- place (gratis or for a charge), and offer equivalent access to the
277
- Corresponding Source in the same way through the same place at no
278
- further charge. You need not require recipients to copy the
279
- Corresponding Source along with the object code. If the place to
280
- copy the object code is a network server, the Corresponding Source
281
- may be on a different server (operated by you or a third party)
282
- that supports equivalent copying facilities, provided you maintain
283
- clear directions next to the object code saying where to find the
284
- Corresponding Source. Regardless of what server hosts the
285
- Corresponding Source, you remain obligated to ensure that it is
286
- available for as long as needed to satisfy these requirements.
287
-
288
- e) Convey the object code using peer-to-peer transmission, provided
289
- you inform other peers where the object code and Corresponding
290
- Source of the work are being offered to the general public at no
291
- charge under subsection 6d.
292
-
293
- A separable portion of the object code, whose source code is excluded
294
- from the Corresponding Source as a System Library, need not be
295
- included in conveying the object code work.
296
-
297
- A "User Product" is either (1) a "consumer product", which means any
298
- tangible personal property which is normally used for personal, family,
299
- or household purposes, or (2) anything designed or sold for incorporation
300
- into a dwelling. In determining whether a product is a consumer product,
301
- doubtful cases shall be resolved in favor of coverage. For a particular
302
- product received by a particular user, "normally used" refers to a
303
- typical or common use of that class of product, regardless of the status
304
- of the particular user or of the way in which the particular user
305
- actually uses, or expects or is expected to use, the product. A product
306
- is a consumer product regardless of whether the product has substantial
307
- commercial, industrial or non-consumer uses, unless such uses represent
308
- the only significant mode of use of the product.
309
-
310
- "Installation Information" for a User Product means any methods,
311
- procedures, authorization keys, or other information required to install
312
- and execute modified versions of a covered work in that User Product from
313
- a modified version of its Corresponding Source. The information must
314
- suffice to ensure that the continued functioning of the modified object
315
- code is in no case prevented or interfered with solely because
316
- modification has been made.
317
-
318
- If you convey an object code work under this section in, or with, or
319
- specifically for use in, a User Product, and the conveying occurs as
320
- part of a transaction in which the right of possession and use of the
321
- User Product is transferred to the recipient in perpetuity or for a
322
- fixed term (regardless of how the transaction is characterized), the
323
- Corresponding Source conveyed under this section must be accompanied
324
- by the Installation Information. But this requirement does not apply
325
- if neither you nor any third party retains the ability to install
326
- modified object code on the User Product (for example, the work has
327
- been installed in ROM).
328
-
329
- The requirement to provide Installation Information does not include a
330
- requirement to continue to provide support service, warranty, or updates
331
- for a work that has been modified or installed by the recipient, or for
332
- the User Product in which it has been modified or installed. Access to a
333
- network may be denied when the modification itself materially and
334
- adversely affects the operation of the network or violates the rules and
335
- protocols for communication across the network.
336
-
337
- Corresponding Source conveyed, and Installation Information provided,
338
- in accord with this section must be in a format that is publicly
339
- documented (and with an implementation available to the public in
340
- source code form), and must require no special password or key for
341
- unpacking, reading or copying.
342
-
343
- 7. Additional Terms.
344
-
345
- "Additional permissions" are terms that supplement the terms of this
346
- License by making exceptions from one or more of its conditions.
347
- Additional permissions that are applicable to the entire Program shall
348
- be treated as though they were included in this License, to the extent
349
- that they are valid under applicable law. If additional permissions
350
- apply only to part of the Program, that part may be used separately
351
- under those permissions, but the entire Program remains governed by
352
- this License without regard to the additional permissions.
353
-
354
- When you convey a copy of a covered work, you may at your option
355
- remove any additional permissions from that copy, or from any part of
356
- it. (Additional permissions may be written to require their own
357
- removal in certain cases when you modify the work.) You may place
358
- additional permissions on material, added by you to a covered work,
359
- for which you have or can give appropriate copyright permission.
360
-
361
- Notwithstanding any other provision of this License, for material you
362
- add to a covered work, you may (if authorized by the copyright holders of
363
- that material) supplement the terms of this License with terms:
364
-
365
- a) Disclaiming warranty or limiting liability differently from the
366
- terms of sections 15 and 16 of this License; or
367
-
368
- b) Requiring preservation of specified reasonable legal notices or
369
- author attributions in that material or in the Appropriate Legal
370
- Notices displayed by works containing it; or
371
-
372
- c) Prohibiting misrepresentation of the origin of that material, or
373
- requiring that modified versions of such material be marked in
374
- reasonable ways as different from the original version; or
375
-
376
- d) Limiting the use for publicity purposes of names of licensors or
377
- authors of the material; or
378
-
379
- e) Declining to grant rights under trademark law for use of some
380
- trade names, trademarks, or service marks; or
381
-
382
- f) Requiring indemnification of licensors and authors of that
383
- material by anyone who conveys the material (or modified versions of
384
- it) with contractual assumptions of liability to the recipient, for
385
- any liability that these contractual assumptions directly impose on
386
- those licensors and authors.
387
-
388
- All other non-permissive additional terms are considered "further
389
- restrictions" within the meaning of section 10. If the Program as you
390
- received it, or any part of it, contains a notice stating that it is
391
- governed by this License along with a term that is a further
392
- restriction, you may remove that term. If a license document contains
393
- a further restriction but permits relicensing or conveying under this
394
- License, you may add to a covered work material governed by the terms
395
- of that license document, provided that the further restriction does
396
- not survive such relicensing or conveying.
397
-
398
- If you add terms to a covered work in accord with this section, you
399
- must place, in the relevant source files, a statement of the
400
- additional terms that apply to those files, or a notice indicating
401
- where to find the applicable terms.
402
-
403
- Additional terms, permissive or non-permissive, may be stated in the
404
- form of a separately written license, or stated as exceptions;
405
- the above requirements apply either way.
406
-
407
- 8. Termination.
408
-
409
- You may not propagate or modify a covered work except as expressly
410
- provided under this License. Any attempt otherwise to propagate or
411
- modify it is void, and will automatically terminate your rights under
412
- this License (including any patent licenses granted under the third
413
- paragraph of section 11).
414
-
415
- However, if you cease all violation of this License, then your
416
- license from a particular copyright holder is reinstated (a)
417
- provisionally, unless and until the copyright holder explicitly and
418
- finally terminates your license, and (b) permanently, if the copyright
419
- holder fails to notify you of the violation by some reasonable means
420
- prior to 60 days after the cessation.
421
-
422
- Moreover, your license from a particular copyright holder is
423
- reinstated permanently if the copyright holder notifies you of the
424
- violation by some reasonable means, this is the first time you have
425
- received notice of violation of this License (for any work) from that
426
- copyright holder, and you cure the violation prior to 30 days after
427
- your receipt of the notice.
428
-
429
- Termination of your rights under this section does not terminate the
430
- licenses of parties who have received copies or rights from you under
431
- this License. If your rights have been terminated and not permanently
432
- reinstated, you do not qualify to receive new licenses for the same
433
- material under section 10.
434
-
435
- 9. Acceptance Not Required for Having Copies.
436
-
437
- You are not required to accept this License in order to receive or
438
- run a copy of the Program. Ancillary propagation of a covered work
439
- occurring solely as a consequence of using peer-to-peer transmission
440
- to receive a copy likewise does not require acceptance. However,
441
- nothing other than this License grants you permission to propagate or
442
- modify any covered work. These actions infringe copyright if you do
443
- not accept this License. Therefore, by modifying or propagating a
444
- covered work, you indicate your acceptance of this License to do so.
445
-
446
- 10. Automatic Licensing of Downstream Recipients.
447
-
448
- Each time you convey a covered work, the recipient automatically
449
- receives a license from the original licensors, to run, modify and
450
- propagate that work, subject to this License. You are not responsible
451
- for enforcing compliance by third parties with this License.
452
-
453
- An "entity transaction" is a transaction transferring control of an
454
- organization, or substantially all assets of one, or subdividing an
455
- organization, or merging organizations. If propagation of a covered
456
- work results from an entity transaction, each party to that
457
- transaction who receives a copy of the work also receives whatever
458
- licenses to the work the party's predecessor in interest had or could
459
- give under the previous paragraph, plus a right to possession of the
460
- Corresponding Source of the work from the predecessor in interest, if
461
- the predecessor has it or can get it with reasonable efforts.
462
-
463
- You may not impose any further restrictions on the exercise of the
464
- rights granted or affirmed under this License. For example, you may
465
- not impose a license fee, royalty, or other charge for exercise of
466
- rights granted under this License, and you may not initiate litigation
467
- (including a cross-claim or counterclaim in a lawsuit) alleging that
468
- any patent claim is infringed by making, using, selling, offering for
469
- sale, or importing the Program or any portion of it.
470
-
471
- 11. Patents.
472
-
473
- A "contributor" is a copyright holder who authorizes use under this
474
- License of the Program or a work on which the Program is based. The
475
- work thus licensed is called the contributor's "contributor version".
476
-
477
- A contributor's "essential patent claims" are all patent claims
478
- owned or controlled by the contributor, whether already acquired or
479
- hereafter acquired, that would be infringed by some manner, permitted
480
- by this License, of making, using, or selling its contributor version,
481
- but do not include claims that would be infringed only as a
482
- consequence of further modification of the contributor version. For
483
- purposes of this definition, "control" includes the right to grant
484
- patent sublicenses in a manner consistent with the requirements of
485
- this License.
486
-
487
- Each contributor grants you a non-exclusive, worldwide, royalty-free
488
- patent license under the contributor's essential patent claims, to
489
- make, use, sell, offer for sale, import and otherwise run, modify and
490
- propagate the contents of its contributor version.
491
-
492
- In the following three paragraphs, a "patent license" is any express
493
- agreement or commitment, however denominated, not to enforce a patent
494
- (such as an express permission to practice a patent or covenant not to
495
- sue for patent infringement). To "grant" such a patent license to a
496
- party means to make such an agreement or commitment not to enforce a
497
- patent against the party.
498
-
499
- If you convey a covered work, knowingly relying on a patent license,
500
- and the Corresponding Source of the work is not available for anyone
501
- to copy, free of charge and under the terms of this License, through a
502
- publicly available network server or other readily accessible means,
503
- then you must either (1) cause the Corresponding Source to be so
504
- available, or (2) arrange to deprive yourself of the benefit of the
505
- patent license for this particular work, or (3) arrange, in a manner
506
- consistent with the requirements of this License, to extend the patent
507
- license to downstream recipients. "Knowingly relying" means you have
508
- actual knowledge that, but for the patent license, your conveying the
509
- covered work in a country, or your recipient's use of the covered work
510
- in a country, would infringe one or more identifiable patents in that
511
- country that you have reason to believe are valid.
512
-
513
- If, pursuant to or in connection with a single transaction or
514
- arrangement, you convey, or propagate by procuring conveyance of, a
515
- covered work, and grant a patent license to some of the parties
516
- receiving the covered work authorizing them to use, propagate, modify
517
- or convey a specific copy of the covered work, then the patent license
518
- you grant is automatically extended to all recipients of the covered
519
- work and works based on it.
520
-
521
- A patent license is "discriminatory" if it does not include within
522
- the scope of its coverage, prohibits the exercise of, or is
523
- conditioned on the non-exercise of one or more of the rights that are
524
- specifically granted under this License. You may not convey a covered
525
- work if you are a party to an arrangement with a third party that is
526
- in the business of distributing software, under which you make payment
527
- to the third party based on the extent of your activity of conveying
528
- the work, and under which the third party grants, to any of the
529
- parties who would receive the covered work from you, a discriminatory
530
- patent license (a) in connection with copies of the covered work
531
- conveyed by you (or copies made from those copies), or (b) primarily
532
- for and in connection with specific products or compilations that
533
- contain the covered work, unless you entered into that arrangement,
534
- or that patent license was granted, prior to 28 March 2007.
535
-
536
- Nothing in this License shall be construed as excluding or limiting
537
- any implied license or other defenses to infringement that may
538
- otherwise be available to you under applicable patent law.
539
-
540
- 12. No Surrender of Others' Freedom.
541
-
542
- If conditions are imposed on you (whether by court order, agreement or
543
- otherwise) that contradict the conditions of this License, they do not
544
- excuse you from the conditions of this License. If you cannot convey a
545
- covered work so as to satisfy simultaneously your obligations under this
546
- License and any other pertinent obligations, then as a consequence you may
547
- not convey it at all. For example, if you agree to terms that obligate you
548
- to collect a royalty for further conveying from those to whom you convey
549
- the Program, the only way you could satisfy both those terms and this
550
- License would be to refrain entirely from conveying the Program.
551
-
552
- 13. Use with the GNU Affero General Public License.
553
-
554
- Notwithstanding any other provision of this License, you have
555
- permission to link or combine any covered work with a work licensed
556
- under version 3 of the GNU Affero General Public License into a single
557
- combined work, and to convey the resulting work. The terms of this
558
- License will continue to apply to the part which is the covered work,
559
- but the special requirements of the GNU Affero General Public License,
560
- section 13, concerning interaction through a network will apply to the
561
- combination as such.
562
-
563
- 14. Revised Versions of this License.
564
-
565
- The Free Software Foundation may publish revised and/or new versions of
566
- the GNU General Public License from time to time. Such new versions will
567
- be similar in spirit to the present version, but may differ in detail to
568
- address new problems or concerns.
569
-
570
- Each version is given a distinguishing version number. If the
571
- Program specifies that a certain numbered version of the GNU General
572
- Public License "or any later version" applies to it, you have the
573
- option of following the terms and conditions either of that numbered
574
- version or of any later version published by the Free Software
575
- Foundation. If the Program does not specify a version number of the
576
- GNU General Public License, you may choose any version ever published
577
- by the Free Software Foundation.
578
-
579
- If the Program specifies that a proxy can decide which future
580
- versions of the GNU General Public License can be used, that proxy's
581
- public statement of acceptance of a version permanently authorizes you
582
- to choose that version for the Program.
583
-
584
- Later license versions may give you additional or different
585
- permissions. However, no additional obligations are imposed on any
586
- author or copyright holder as a result of your choosing to follow a
587
- later version.
588
-
589
- 15. Disclaimer of Warranty.
590
-
591
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
- APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
- HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
- OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
- PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
- IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
- ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
-
600
- 16. Limitation of Liability.
601
-
602
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
- WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
- THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
- GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
- USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
- DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
- PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
- EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
- SUCH DAMAGES.
611
-
612
- 17. Interpretation of Sections 15 and 16.
613
-
614
- If the disclaimer of warranty and limitation of liability provided
615
- above cannot be given local legal effect according to their terms,
616
- reviewing courts shall apply local law that most closely approximates
617
- an absolute waiver of all civil liability in connection with the
618
- Program, unless a warranty or assumption of liability accompanies a
619
- copy of the Program in return for a fee.
620
-
621
- END OF TERMS AND CONDITIONS
622
-
623
- How to Apply These Terms to Your New Programs
624
-
625
- If you develop a new program, and you want it to be of the greatest
626
- possible use to the public, the best way to achieve this is to make it
627
- free software which everyone can redistribute and change under these terms.
628
-
629
- To do so, attach the following notices to the program. It is safest
630
- to attach them to the start of each source file to most effectively
631
- state the exclusion of warranty; and each file should have at least
632
- the "copyright" line and a pointer to where the full notice is found.
633
-
634
- {one line to give the program's name and a brief idea of what it does.}
635
- Copyright (C) {year} {name of author}
636
-
637
- This program is free software: you can redistribute it and/or modify
638
- it under the terms of the GNU General Public License as published by
639
- the Free Software Foundation, either version 3 of the License, or
640
- (at your option) any later version.
641
-
642
- This program is distributed in the hope that it will be useful,
643
- but WITHOUT ANY WARRANTY; without even the implied warranty of
644
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645
- GNU General Public License for more details.
646
-
647
- You should have received a copy of the GNU General Public License
648
- along with this program. If not, see <http://www.gnu.org/licenses/>.
649
-
650
- Also add information on how to contact you by electronic and paper mail.
651
-
652
- If the program does terminal interaction, make it output a short
653
- notice like this when it starts in an interactive mode:
654
-
655
- {project} Copyright (C) {year} {fullname}
656
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
- This is free software, and you are welcome to redistribute it
658
- under certain conditions; type `show c' for details.
659
-
660
- The hypothetical commands `show w' and `show c' should show the appropriate
661
- parts of the General Public License. Of course, your program's commands
662
- might be different; for a GUI interface, you would use an "about box".
663
-
664
- You should also get your employer (if you work as a programmer) or school,
665
- if any, to sign a "copyright disclaimer" for the program, if necessary.
666
- For more information on this, and how to apply and follow the GNU GPL, see
667
- <http://www.gnu.org/licenses/>.
668
-
669
- The GNU General Public License does not permit incorporating your program
670
- into proprietary programs. If your program is a subroutine library, you
671
- may consider it more useful to permit linking proprietary applications with
672
- the library. If this is what you want to do, use the GNU Lesser General
673
- Public License instead of this License. But first, please read
674
  <http://www.gnu.org/philosophy/why-not-lgpl.html>.
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU General Public License is a free, copyleft license for
11
+ software and other kinds of works.
12
+
13
+ The licenses for most software and other practical works are designed
14
+ to take away your freedom to share and change the works. By contrast,
15
+ the GNU General Public License is intended to guarantee your freedom to
16
+ share and change all versions of a program--to make sure it remains free
17
+ software for all its users. We, the Free Software Foundation, use the
18
+ GNU General Public License for most of our software; it applies also to
19
+ any other work released this way by its authors. You can apply it to
20
+ your programs, too.
21
+
22
+ When we speak of free software, we are referring to freedom, not
23
+ price. Our General Public Licenses are designed to make sure that you
24
+ have the freedom to distribute copies of free software (and charge for
25
+ them if you wish), that you receive source code or can get it if you
26
+ want it, that you can change the software or use pieces of it in new
27
+ free programs, and that you know you can do these things.
28
+
29
+ To protect your rights, we need to prevent others from denying you
30
+ these rights or asking you to surrender the rights. Therefore, you have
31
+ certain responsibilities if you distribute copies of the software, or if
32
+ you modify it: responsibilities to respect the freedom of others.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must pass on to the recipients the same
36
+ freedoms that you received. You must make sure that they, too, receive
37
+ or can get the source code. And you must show them these terms so they
38
+ know their rights.
39
+
40
+ Developers that use the GNU GPL protect your rights with two steps:
41
+ (1) assert copyright on the software, and (2) offer you this License
42
+ giving you legal permission to copy, distribute and/or modify it.
43
+
44
+ For the developers' and authors' protection, the GPL clearly explains
45
+ that there is no warranty for this free software. For both users' and
46
+ authors' sake, the GPL requires that modified versions be marked as
47
+ changed, so that their problems will not be attributed erroneously to
48
+ authors of previous versions.
49
+
50
+ Some devices are designed to deny users access to install or run
51
+ modified versions of the software inside them, although the manufacturer
52
+ can do so. This is fundamentally incompatible with the aim of
53
+ protecting users' freedom to change the software. The systematic
54
+ pattern of such abuse occurs in the area of products for individuals to
55
+ use, which is precisely where it is most unacceptable. Therefore, we
56
+ have designed this version of the GPL to prohibit the practice for those
57
+ products. If such problems arise substantially in other domains, we
58
+ stand ready to extend this provision to those domains in future versions
59
+ of the GPL, as needed to protect the freedom of users.
60
+
61
+ Finally, every program is threatened constantly by software patents.
62
+ States should not allow patents to restrict development and use of
63
+ software on general-purpose computers, but in those that do, we wish to
64
+ avoid the special danger that patents applied to a free program could
65
+ make it effectively proprietary. To prevent this, the GPL assures that
66
+ patents cannot be used to render the program non-free.
67
+
68
+ The precise terms and conditions for copying, distribution and
69
+ modification follow.
70
+
71
+ TERMS AND CONDITIONS
72
+
73
+ 0. Definitions.
74
+
75
+ "This License" refers to version 3 of the GNU General Public License.
76
+
77
+ "Copyright" also means copyright-like laws that apply to other kinds of
78
+ works, such as semiconductor masks.
79
+
80
+ "The Program" refers to any copyrightable work licensed under this
81
+ License. Each licensee is addressed as "you". "Licensees" and
82
+ "recipients" may be individuals or organizations.
83
+
84
+ To "modify" a work means to copy from or adapt all or part of the work
85
+ in a fashion requiring copyright permission, other than the making of an
86
+ exact copy. The resulting work is called a "modified version" of the
87
+ earlier work or a work "based on" the earlier work.
88
+
89
+ A "covered work" means either the unmodified Program or a work based
90
+ on the Program.
91
+
92
+ To "propagate" a work means to do anything with it that, without
93
+ permission, would make you directly or secondarily liable for
94
+ infringement under applicable copyright law, except executing it on a
95
+ computer or modifying a private copy. Propagation includes copying,
96
+ distribution (with or without modification), making available to the
97
+ public, and in some countries other activities as well.
98
+
99
+ To "convey" a work means any kind of propagation that enables other
100
+ parties to make or receive copies. Mere interaction with a user through
101
+ a computer network, with no transfer of a copy, is not conveying.
102
+
103
+ An interactive user interface displays "Appropriate Legal Notices"
104
+ to the extent that it includes a convenient and prominently visible
105
+ feature that (1) displays an appropriate copyright notice, and (2)
106
+ tells the user that there is no warranty for the work (except to the
107
+ extent that warranties are provided), that licensees may convey the
108
+ work under this License, and how to view a copy of this License. If
109
+ the interface presents a list of user commands or options, such as a
110
+ menu, a prominent item in the list meets this criterion.
111
+
112
+ 1. Source Code.
113
+
114
+ The "source code" for a work means the preferred form of the work
115
+ for making modifications to it. "Object code" means any non-source
116
+ form of a work.
117
+
118
+ A "Standard Interface" means an interface that either is an official
119
+ standard defined by a recognized standards body, or, in the case of
120
+ interfaces specified for a particular programming language, one that
121
+ is widely used among developers working in that language.
122
+
123
+ The "System Libraries" of an executable work include anything, other
124
+ than the work as a whole, that (a) is included in the normal form of
125
+ packaging a Major Component, but which is not part of that Major
126
+ Component, and (b) serves only to enable use of the work with that
127
+ Major Component, or to implement a Standard Interface for which an
128
+ implementation is available to the public in source code form. A
129
+ "Major Component", in this context, means a major essential component
130
+ (kernel, window system, and so on) of the specific operating system
131
+ (if any) on which the executable work runs, or a compiler used to
132
+ produce the work, or an object code interpreter used to run it.
133
+
134
+ The "Corresponding Source" for a work in object code form means all
135
+ the source code needed to generate, install, and (for an executable
136
+ work) run the object code and to modify the work, including scripts to
137
+ control those activities. However, it does not include the work's
138
+ System Libraries, or general-purpose tools or generally available free
139
+ programs which are used unmodified in performing those activities but
140
+ which are not part of the work. For example, Corresponding Source
141
+ includes interface definition files associated with source files for
142
+ the work, and the source code for shared libraries and dynamically
143
+ linked subprograms that the work is specifically designed to require,
144
+ such as by intimate data communication or control flow between those
145
+ subprograms and other parts of the work.
146
+
147
+ The Corresponding Source need not include anything that users
148
+ can regenerate automatically from other parts of the Corresponding
149
+ Source.
150
+
151
+ The Corresponding Source for a work in source code form is that
152
+ same work.
153
+
154
+ 2. Basic Permissions.
155
+
156
+ All rights granted under this License are granted for the term of
157
+ copyright on the Program, and are irrevocable provided the stated
158
+ conditions are met. This License explicitly affirms your unlimited
159
+ permission to run the unmodified Program. The output from running a
160
+ covered work is covered by this License only if the output, given its
161
+ content, constitutes a covered work. This License acknowledges your
162
+ rights of fair use or other equivalent, as provided by copyright law.
163
+
164
+ You may make, run and propagate covered works that you do not
165
+ convey, without conditions so long as your license otherwise remains
166
+ in force. You may convey covered works to others for the sole purpose
167
+ of having them make modifications exclusively for you, or provide you
168
+ with facilities for running those works, provided that you comply with
169
+ the terms of this License in conveying all material for which you do
170
+ not control copyright. Those thus making or running the covered works
171
+ for you must do so exclusively on your behalf, under your direction
172
+ and control, on terms that prohibit them from making any copies of
173
+ your copyrighted material outside their relationship with you.
174
+
175
+ Conveying under any other circumstances is permitted solely under
176
+ the conditions stated below. Sublicensing is not allowed; section 10
177
+ makes it unnecessary.
178
+
179
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
+
181
+ No covered work shall be deemed part of an effective technological
182
+ measure under any applicable law fulfilling obligations under article
183
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
+ similar laws prohibiting or restricting circumvention of such
185
+ measures.
186
+
187
+ When you convey a covered work, you waive any legal power to forbid
188
+ circumvention of technological measures to the extent such circumvention
189
+ is effected by exercising rights under this License with respect to
190
+ the covered work, and you disclaim any intention to limit operation or
191
+ modification of the work as a means of enforcing, against the work's
192
+ users, your or third parties' legal rights to forbid circumvention of
193
+ technological measures.
194
+
195
+ 4. Conveying Verbatim Copies.
196
+
197
+ You may convey verbatim copies of the Program's source code as you
198
+ receive it, in any medium, provided that you conspicuously and
199
+ appropriately publish on each copy an appropriate copyright notice;
200
+ keep intact all notices stating that this License and any
201
+ non-permissive terms added in accord with section 7 apply to the code;
202
+ keep intact all notices of the absence of any warranty; and give all
203
+ recipients a copy of this License along with the Program.
204
+
205
+ You may charge any price or no price for each copy that you convey,
206
+ and you may offer support or warranty protection for a fee.
207
+
208
+ 5. Conveying Modified Source Versions.
209
+
210
+ You may convey a work based on the Program, or the modifications to
211
+ produce it from the Program, in the form of source code under the
212
+ terms of section 4, provided that you also meet all of these conditions:
213
+
214
+ a) The work must carry prominent notices stating that you modified
215
+ it, and giving a relevant date.
216
+
217
+ b) The work must carry prominent notices stating that it is
218
+ released under this License and any conditions added under section
219
+ 7. This requirement modifies the requirement in section 4 to
220
+ "keep intact all notices".
221
+
222
+ c) You must license the entire work, as a whole, under this
223
+ License to anyone who comes into possession of a copy. This
224
+ License will therefore apply, along with any applicable section 7
225
+ additional terms, to the whole of the work, and all its parts,
226
+ regardless of how they are packaged. This License gives no
227
+ permission to license the work in any other way, but it does not
228
+ invalidate such permission if you have separately received it.
229
+
230
+ d) If the work has interactive user interfaces, each must display
231
+ Appropriate Legal Notices; however, if the Program has interactive
232
+ interfaces that do not display Appropriate Legal Notices, your
233
+ work need not make them do so.
234
+
235
+ A compilation of a covered work with other separate and independent
236
+ works, which are not by their nature extensions of the covered work,
237
+ and which are not combined with it such as to form a larger program,
238
+ in or on a volume of a storage or distribution medium, is called an
239
+ "aggregate" if the compilation and its resulting copyright are not
240
+ used to limit the access or legal rights of the compilation's users
241
+ beyond what the individual works permit. Inclusion of a covered work
242
+ in an aggregate does not cause this License to apply to the other
243
+ parts of the aggregate.
244
+
245
+ 6. Conveying Non-Source Forms.
246
+
247
+ You may convey a covered work in object code form under the terms
248
+ of sections 4 and 5, provided that you also convey the
249
+ machine-readable Corresponding Source under the terms of this License,
250
+ in one of these ways:
251
+
252
+ a) Convey the object code in, or embodied in, a physical product
253
+ (including a physical distribution medium), accompanied by the
254
+ Corresponding Source fixed on a durable physical medium
255
+ customarily used for software interchange.
256
+
257
+ b) Convey the object code in, or embodied in, a physical product
258
+ (including a physical distribution medium), accompanied by a
259
+ written offer, valid for at least three years and valid for as
260
+ long as you offer spare parts or customer support for that product
261
+ model, to give anyone who possesses the object code either (1) a
262
+ copy of the Corresponding Source for all the software in the
263
+ product that is covered by this License, on a durable physical
264
+ medium customarily used for software interchange, for a price no
265
+ more than your reasonable cost of physically performing this
266
+ conveying of source, or (2) access to copy the
267
+ Corresponding Source from a network server at no charge.
268
+
269
+ c) Convey individual copies of the object code with a copy of the
270
+ written offer to provide the Corresponding Source. This
271
+ alternative is allowed only occasionally and noncommercially, and
272
+ only if you received the object code with such an offer, in accord
273
+ with subsection 6b.
274
+
275
+ d) Convey the object code by offering access from a designated
276
+ place (gratis or for a charge), and offer equivalent access to the
277
+ Corresponding Source in the same way through the same place at no
278
+ further charge. You need not require recipients to copy the
279
+ Corresponding Source along with the object code. If the place to
280
+ copy the object code is a network server, the Corresponding Source
281
+ may be on a different server (operated by you or a third party)
282
+ that supports equivalent copying facilities, provided you maintain
283
+ clear directions next to the object code saying where to find the
284
+ Corresponding Source. Regardless of what server hosts the
285
+ Corresponding Source, you remain obligated to ensure that it is
286
+ available for as long as needed to satisfy these requirements.
287
+
288
+ e) Convey the object code using peer-to-peer transmission, provided
289
+ you inform other peers where the object code and Corresponding
290
+ Source of the work are being offered to the general public at no
291
+ charge under subsection 6d.
292
+
293
+ A separable portion of the object code, whose source code is excluded
294
+ from the Corresponding Source as a System Library, need not be
295
+ included in conveying the object code work.
296
+
297
+ A "User Product" is either (1) a "consumer product", which means any
298
+ tangible personal property which is normally used for personal, family,
299
+ or household purposes, or (2) anything designed or sold for incorporation
300
+ into a dwelling. In determining whether a product is a consumer product,
301
+ doubtful cases shall be resolved in favor of coverage. For a particular
302
+ product received by a particular user, "normally used" refers to a
303
+ typical or common use of that class of product, regardless of the status
304
+ of the particular user or of the way in which the particular user
305
+ actually uses, or expects or is expected to use, the product. A product
306
+ is a consumer product regardless of whether the product has substantial
307
+ commercial, industrial or non-consumer uses, unless such uses represent
308
+ the only significant mode of use of the product.
309
+
310
+ "Installation Information" for a User Product means any methods,
311
+ procedures, authorization keys, or other information required to install
312
+ and execute modified versions of a covered work in that User Product from
313
+ a modified version of its Corresponding Source. The information must
314
+ suffice to ensure that the continued functioning of the modified object
315
+ code is in no case prevented or interfered with solely because
316
+ modification has been made.
317
+
318
+ If you convey an object code work under this section in, or with, or
319
+ specifically for use in, a User Product, and the conveying occurs as
320
+ part of a transaction in which the right of possession and use of the
321
+ User Product is transferred to the recipient in perpetuity or for a
322
+ fixed term (regardless of how the transaction is characterized), the
323
+ Corresponding Source conveyed under this section must be accompanied
324
+ by the Installation Information. But this requirement does not apply
325
+ if neither you nor any third party retains the ability to install
326
+ modified object code on the User Product (for example, the work has
327
+ been installed in ROM).
328
+
329
+ The requirement to provide Installation Information does not include a
330
+ requirement to continue to provide support service, warranty, or updates
331
+ for a work that has been modified or installed by the recipient, or for
332
+ the User Product in which it has been modified or installed. Access to a
333
+ network may be denied when the modification itself materially and
334
+ adversely affects the operation of the network or violates the rules and
335
+ protocols for communication across the network.
336
+
337
+ Corresponding Source conveyed, and Installation Information provided,
338
+ in accord with this section must be in a format that is publicly
339
+ documented (and with an implementation available to the public in
340
+ source code form), and must require no special password or key for
341
+ unpacking, reading or copying.
342
+
343
+ 7. Additional Terms.
344
+
345
+ "Additional permissions" are terms that supplement the terms of this
346
+ License by making exceptions from one or more of its conditions.
347
+ Additional permissions that are applicable to the entire Program shall
348
+ be treated as though they were included in this License, to the extent
349
+ that they are valid under applicable law. If additional permissions
350
+ apply only to part of the Program, that part may be used separately
351
+ under those permissions, but the entire Program remains governed by
352
+ this License without regard to the additional permissions.
353
+
354
+ When you convey a copy of a covered work, you may at your option
355
+ remove any additional permissions from that copy, or from any part of
356
+ it. (Additional permissions may be written to require their own
357
+ removal in certain cases when you modify the work.) You may place
358
+ additional permissions on material, added by you to a covered work,
359
+ for which you have or can give appropriate copyright permission.
360
+
361
+ Notwithstanding any other provision of this License, for material you
362
+ add to a covered work, you may (if authorized by the copyright holders of
363
+ that material) supplement the terms of this License with terms:
364
+
365
+ a) Disclaiming warranty or limiting liability differently from the
366
+ terms of sections 15 and 16 of this License; or
367
+
368
+ b) Requiring preservation of specified reasonable legal notices or
369
+ author attributions in that material or in the Appropriate Legal
370
+ Notices displayed by works containing it; or
371
+
372
+ c) Prohibiting misrepresentation of the origin of that material, or
373
+ requiring that modified versions of such material be marked in
374
+ reasonable ways as different from the original version; or
375
+
376
+ d) Limiting the use for publicity purposes of names of licensors or
377
+ authors of the material; or
378
+
379
+ e) Declining to grant rights under trademark law for use of some
380
+ trade names, trademarks, or service marks; or
381
+
382
+ f) Requiring indemnification of licensors and authors of that
383
+ material by anyone who conveys the material (or modified versions of
384
+ it) with contractual assumptions of liability to the recipient, for
385
+ any liability that these contractual assumptions directly impose on
386
+ those licensors and authors.
387
+
388
+ All other non-permissive additional terms are considered "further
389
+ restrictions" within the meaning of section 10. If the Program as you
390
+ received it, or any part of it, contains a notice stating that it is
391
+ governed by this License along with a term that is a further
392
+ restriction, you may remove that term. If a license document contains
393
+ a further restriction but permits relicensing or conveying under this
394
+ License, you may add to a covered work material governed by the terms
395
+ of that license document, provided that the further restriction does
396
+ not survive such relicensing or conveying.
397
+
398
+ If you add terms to a covered work in accord with this section, you
399
+ must place, in the relevant source files, a statement of the
400
+ additional terms that apply to those files, or a notice indicating
401
+ where to find the applicable terms.
402
+
403
+ Additional terms, permissive or non-permissive, may be stated in the
404
+ form of a separately written license, or stated as exceptions;
405
+ the above requirements apply either way.
406
+
407
+ 8. Termination.
408
+
409
+ You may not propagate or modify a covered work except as expressly
410
+ provided under this License. Any attempt otherwise to propagate or
411
+ modify it is void, and will automatically terminate your rights under
412
+ this License (including any patent licenses granted under the third
413
+ paragraph of section 11).
414
+
415
+ However, if you cease all violation of this License, then your
416
+ license from a particular copyright holder is reinstated (a)
417
+ provisionally, unless and until the copyright holder explicitly and
418
+ finally terminates your license, and (b) permanently, if the copyright
419
+ holder fails to notify you of the violation by some reasonable means
420
+ prior to 60 days after the cessation.
421
+
422
+ Moreover, your license from a particular copyright holder is
423
+ reinstated permanently if the copyright holder notifies you of the
424
+ violation by some reasonable means, this is the first time you have
425
+ received notice of violation of this License (for any work) from that
426
+ copyright holder, and you cure the violation prior to 30 days after
427
+ your receipt of the notice.
428
+
429
+ Termination of your rights under this section does not terminate the
430
+ licenses of parties who have received copies or rights from you under
431
+ this License. If your rights have been terminated and not permanently
432
+ reinstated, you do not qualify to receive new licenses for the same
433
+ material under section 10.
434
+
435
+ 9. Acceptance Not Required for Having Copies.
436
+
437
+ You are not required to accept this License in order to receive or
438
+ run a copy of the Program. Ancillary propagation of a covered work
439
+ occurring solely as a consequence of using peer-to-peer transmission
440
+ to receive a copy likewise does not require acceptance. However,
441
+ nothing other than this License grants you permission to propagate or
442
+ modify any covered work. These actions infringe copyright if you do
443
+ not accept this License. Therefore, by modifying or propagating a
444
+ covered work, you indicate your acceptance of this License to do so.
445
+
446
+ 10. Automatic Licensing of Downstream Recipients.
447
+
448
+ Each time you convey a covered work, the recipient automatically
449
+ receives a license from the original licensors, to run, modify and
450
+ propagate that work, subject to this License. You are not responsible
451
+ for enforcing compliance by third parties with this License.
452
+
453
+ An "entity transaction" is a transaction transferring control of an
454
+ organization, or substantially all assets of one, or subdividing an
455
+ organization, or merging organizations. If propagation of a covered
456
+ work results from an entity transaction, each party to that
457
+ transaction who receives a copy of the work also receives whatever
458
+ licenses to the work the party's predecessor in interest had or could
459
+ give under the previous paragraph, plus a right to possession of the
460
+ Corresponding Source of the work from the predecessor in interest, if
461
+ the predecessor has it or can get it with reasonable efforts.
462
+
463
+ You may not impose any further restrictions on the exercise of the
464
+ rights granted or affirmed under this License. For example, you may
465
+ not impose a license fee, royalty, or other charge for exercise of
466
+ rights granted under this License, and you may not initiate litigation
467
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
468
+ any patent claim is infringed by making, using, selling, offering for
469
+ sale, or importing the Program or any portion of it.
470
+
471
+ 11. Patents.
472
+
473
+ A "contributor" is a copyright holder who authorizes use under this
474
+ License of the Program or a work on which the Program is based. The
475
+ work thus licensed is called the contributor's "contributor version".
476
+
477
+ A contributor's "essential patent claims" are all patent claims
478
+ owned or controlled by the contributor, whether already acquired or
479
+ hereafter acquired, that would be infringed by some manner, permitted
480
+ by this License, of making, using, or selling its contributor version,
481
+ but do not include claims that would be infringed only as a
482
+ consequence of further modification of the contributor version. For
483
+ purposes of this definition, "control" includes the right to grant
484
+ patent sublicenses in a manner consistent with the requirements of
485
+ this License.
486
+
487
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
488
+ patent license under the contributor's essential patent claims, to
489
+ make, use, sell, offer for sale, import and otherwise run, modify and
490
+ propagate the contents of its contributor version.
491
+
492
+ In the following three paragraphs, a "patent license" is any express
493
+ agreement or commitment, however denominated, not to enforce a patent
494
+ (such as an express permission to practice a patent or covenant not to
495
+ sue for patent infringement). To "grant" such a patent license to a
496
+ party means to make such an agreement or commitment not to enforce a
497
+ patent against the party.
498
+
499
+ If you convey a covered work, knowingly relying on a patent license,
500
+ and the Corresponding Source of the work is not available for anyone
501
+ to copy, free of charge and under the terms of this License, through a
502
+ publicly available network server or other readily accessible means,
503
+ then you must either (1) cause the Corresponding Source to be so
504
+ available, or (2) arrange to deprive yourself of the benefit of the
505
+ patent license for this particular work, or (3) arrange, in a manner
506
+ consistent with the requirements of this License, to extend the patent
507
+ license to downstream recipients. "Knowingly relying" means you have
508
+ actual knowledge that, but for the patent license, your conveying the
509
+ covered work in a country, or your recipient's use of the covered work
510
+ in a country, would infringe one or more identifiable patents in that
511
+ country that you have reason to believe are valid.
512
+
513
+ If, pursuant to or in connection with a single transaction or
514
+ arrangement, you convey, or propagate by procuring conveyance of, a
515
+ covered work, and grant a patent license to some of the parties
516
+ receiving the covered work authorizing them to use, propagate, modify
517
+ or convey a specific copy of the covered work, then the patent license
518
+ you grant is automatically extended to all recipients of the covered
519
+ work and works based on it.
520
+
521
+ A patent license is "discriminatory" if it does not include within
522
+ the scope of its coverage, prohibits the exercise of, or is
523
+ conditioned on the non-exercise of one or more of the rights that are
524
+ specifically granted under this License. You may not convey a covered
525
+ work if you are a party to an arrangement with a third party that is
526
+ in the business of distributing software, under which you make payment
527
+ to the third party based on the extent of your activity of conveying
528
+ the work, and under which the third party grants, to any of the
529
+ parties who would receive the covered work from you, a discriminatory
530
+ patent license (a) in connection with copies of the covered work
531
+ conveyed by you (or copies made from those copies), or (b) primarily
532
+ for and in connection with specific products or compilations that
533
+ contain the covered work, unless you entered into that arrangement,
534
+ or that patent license was granted, prior to 28 March 2007.
535
+
536
+ Nothing in this License shall be construed as excluding or limiting
537
+ any implied license or other defenses to infringement that may
538
+ otherwise be available to you under applicable patent law.
539
+
540
+ 12. No Surrender of Others' Freedom.
541
+
542
+ If conditions are imposed on you (whether by court order, agreement or
543
+ otherwise) that contradict the conditions of this License, they do not
544
+ excuse you from the conditions of this License. If you cannot convey a
545
+ covered work so as to satisfy simultaneously your obligations under this
546
+ License and any other pertinent obligations, then as a consequence you may
547
+ not convey it at all. For example, if you agree to terms that obligate you
548
+ to collect a royalty for further conveying from those to whom you convey
549
+ the Program, the only way you could satisfy both those terms and this
550
+ License would be to refrain entirely from conveying the Program.
551
+
552
+ 13. Use with the GNU Affero General Public License.
553
+
554
+ Notwithstanding any other provision of this License, you have
555
+ permission to link or combine any covered work with a work licensed
556
+ under version 3 of the GNU Affero General Public License into a single
557
+ combined work, and to convey the resulting work. The terms of this
558
+ License will continue to apply to the part which is the covered work,
559
+ but the special requirements of the GNU Affero General Public License,
560
+ section 13, concerning interaction through a network will apply to the
561
+ combination as such.
562
+
563
+ 14. Revised Versions of this License.
564
+
565
+ The Free Software Foundation may publish revised and/or new versions of
566
+ the GNU General Public License from time to time. Such new versions will
567
+ be similar in spirit to the present version, but may differ in detail to
568
+ address new problems or concerns.
569
+
570
+ Each version is given a distinguishing version number. If the
571
+ Program specifies that a certain numbered version of the GNU General
572
+ Public License "or any later version" applies to it, you have the
573
+ option of following the terms and conditions either of that numbered
574
+ version or of any later version published by the Free Software
575
+ Foundation. If the Program does not specify a version number of the
576
+ GNU General Public License, you may choose any version ever published
577
+ by the Free Software Foundation.
578
+
579
+ If the Program specifies that a proxy can decide which future
580
+ versions of the GNU General Public License can be used, that proxy's
581
+ public statement of acceptance of a version permanently authorizes you
582
+ to choose that version for the Program.
583
+
584
+ Later license versions may give you additional or different
585
+ permissions. However, no additional obligations are imposed on any
586
+ author or copyright holder as a result of your choosing to follow a
587
+ later version.
588
+
589
+ 15. Disclaimer of Warranty.
590
+
591
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
+
600
+ 16. Limitation of Liability.
601
+
602
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
+ SUCH DAMAGES.
611
+
612
+ 17. Interpretation of Sections 15 and 16.
613
+
614
+ If the disclaimer of warranty and limitation of liability provided
615
+ above cannot be given local legal effect according to their terms,
616
+ reviewing courts shall apply local law that most closely approximates
617
+ an absolute waiver of all civil liability in connection with the
618
+ Program, unless a warranty or assumption of liability accompanies a
619
+ copy of the Program in return for a fee.
620
+
621
+ END OF TERMS AND CONDITIONS
622
+
623
+ How to Apply These Terms to Your New Programs
624
+
625
+ If you develop a new program, and you want it to be of the greatest
626
+ possible use to the public, the best way to achieve this is to make it
627
+ free software which everyone can redistribute and change under these terms.
628
+
629
+ To do so, attach the following notices to the program. It is safest
630
+ to attach them to the start of each source file to most effectively
631
+ state the exclusion of warranty; and each file should have at least
632
+ the "copyright" line and a pointer to where the full notice is found.
633
+
634
+ {one line to give the program's name and a brief idea of what it does.}
635
+ Copyright (C) {year} {name of author}
636
+
637
+ This program is free software: you can redistribute it and/or modify
638
+ it under the terms of the GNU General Public License as published by
639
+ the Free Software Foundation, either version 3 of the License, or
640
+ (at your option) any later version.
641
+
642
+ This program is distributed in the hope that it will be useful,
643
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
644
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645
+ GNU General Public License for more details.
646
+
647
+ You should have received a copy of the GNU General Public License
648
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
649
+
650
+ Also add information on how to contact you by electronic and paper mail.
651
+
652
+ If the program does terminal interaction, make it output a short
653
+ notice like this when it starts in an interactive mode:
654
+
655
+ {project} Copyright (C) {year} {fullname}
656
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
+ This is free software, and you are welcome to redistribute it
658
+ under certain conditions; type `show c' for details.
659
+
660
+ The hypothetical commands `show w' and `show c' should show the appropriate
661
+ parts of the General Public License. Of course, your program's commands
662
+ might be different; for a GUI interface, you would use an "about box".
663
+
664
+ You should also get your employer (if you work as a programmer) or school,
665
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
666
+ For more information on this, and how to apply and follow the GNU GPL, see
667
+ <http://www.gnu.org/licenses/>.
668
+
669
+ The GNU General Public License does not permit incorporating your program
670
+ into proprietary programs. If your program is a subroutine library, you
671
+ may consider it more useful to permit linking proprietary applications with
672
+ the library. If this is what you want to do, use the GNU Lesser General
673
+ Public License instead of this License. But first, please read
674
  <http://www.gnu.org/philosophy/why-not-lgpl.html>.
freemius/assets/css/admin/account.css CHANGED
@@ -1 +1 @@
1
- #fs_account .postbox,#fs_account .widefat{max-width:700px}#fs_account h3{font-size:1.3em;padding:12px 15px;margin:0 0 12px 0;line-height:1.4;border-bottom:1px solid #F1F1F1}#fs_account h3 .dashicons{width:26px;height:26px;font-size:1.3em}#fs_account i.dashicons{font-size:1.2em;height:1.2em;width:1.2em}#fs_account .button i.dashicons{vertical-align:middle}#fs_account .fs-header-actions{position:absolute;top:17px;right:15px;font-size:0.9em}#fs_account .fs-header-actions ul{margin:0}#fs_account .fs-header-actions li{float:left}#fs_account .fs-header-actions li form{display:inline-block}#fs_account .fs-header-actions li a{text-decoration:none}#fs_account_details .button-group{float:right}.rtl #fs_account .fs-header-actions{left:15px;right:auto}.fs-key-value-table{width:100%}.fs-key-value-table form{display:inline-block}.fs-key-value-table tr td:first-child{text-align:right}.fs-key-value-table tr td:first-child nobr{font-weight:bold}.fs-key-value-table tr td:first-child form{display:block}.fs-key-value-table tr td.fs-right{text-align:right}.fs-key-value-table tr.fs-odd{background:#ebebeb}.fs-key-value-table td,.fs-key-value-table th{padding:10px}.fs-key-value-table code{line-height:28px}.fs-key-value-table var,.fs-key-value-table code,.fs-key-value-table input[type="text"]{color:#0073AA;font-size:16px;background:none}.fs-key-value-table input[type="text"]{width:100%;font-weight:bold}label.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error{background:#dc3232}#fs_addons h3{border:none;margin-bottom:0;padding:4px 5px}#fs_addons td{vertical-align:middle}#fs_addons thead{white-space:nowrap}#fs_addons td:first-child,#fs_addons th:first-child{text-align:left;font-weight:bold}#fs_addons td:last-child,#fs_addons th:last-child{text-align:right}#fs_addons th{font-weight:bold}#fs_billing_address{width:100%}#fs_billing_address tr td{width:50%;padding:5px}#fs_billing_address tr:first-of-type td{padding-top:0}#fs_billing_address span{font-weight:bold}#fs_billing_address input,#fs_billing_address select{display:block;width:100%;margin-top:5px}#fs_billing_address input::-moz-placeholder,#fs_billing_address select::-moz-placeholder{color:transparent;opacity:1}#fs_billing_address input:-ms-input-placeholder,#fs_billing_address select:-ms-input-placeholder{color:transparent}#fs_billing_address input::-webkit-input-placeholder,#fs_billing_address select::-webkit-input-placeholder{color:transparent}#fs_billing_address input.fs-read-mode,#fs_billing_address select.fs-read-mode{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode td span{display:none}#fs_billing_address.fs-read-mode input,#fs_billing_address.fs-read-mode select{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode input::-moz-placeholder,#fs_billing_address.fs-read-mode select::-moz-placeholder{color:#ccc;opacity:1}#fs_billing_address.fs-read-mode input:-ms-input-placeholder,#fs_billing_address.fs-read-mode select:-ms-input-placeholder{color:#ccc}#fs_billing_address.fs-read-mode input::-webkit-input-placeholder,#fs_billing_address.fs-read-mode select::-webkit-input-placeholder{color:#ccc}#fs_billing_address button{display:block;width:100%}
1
+ #fs_account .postbox,#fs_account .widefat{max-width:700px}#fs_account h3{font-size:1.3em;padding:12px 15px;margin:0 0 12px 0;line-height:1.4;border-bottom:1px solid #F1F1F1}#fs_account h3 .dashicons{width:26px;height:26px;font-size:1.3em}#fs_account i.dashicons{font-size:1.2em;height:1.2em;width:1.2em}#fs_account .button i.dashicons{vertical-align:middle}#fs_account .fs-header-actions{position:absolute;top:17px;right:15px;font-size:0.9em}#fs_account .fs-header-actions ul{margin:0}#fs_account .fs-header-actions li{float:left}#fs_account .fs-header-actions li form{display:inline-block}#fs_account .fs-header-actions li a{text-decoration:none}#fs_account_details .button-group{float:right}.rtl #fs_account .fs-header-actions{left:15px;right:auto}.fs-key-value-table{width:100%}.fs-key-value-table form{display:inline-block}.fs-key-value-table tr td:first-child{text-align:right}.fs-key-value-table tr td:first-child nobr{font-weight:bold}.fs-key-value-table tr td:first-child form{display:block}.fs-key-value-table tr td.fs-right{text-align:right}.fs-key-value-table tr.fs-odd{background:#ebebeb}.fs-key-value-table td,.fs-key-value-table th{padding:10px}.fs-key-value-table code{line-height:28px}.fs-key-value-table var,.fs-key-value-table code,.fs-key-value-table input[type="text"]{color:#0073AA;font-size:16px;background:none}.fs-key-value-table input[type="text"]{width:100%;font-weight:bold}label.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error{background:#dc3232}#fs_addons h3{border:none;margin-bottom:0;padding:4px 5px}#fs_addons td{vertical-align:middle}#fs_addons thead{white-space:nowrap}#fs_addons td:first-child,#fs_addons th:first-child{text-align:left;font-weight:bold}#fs_addons td:last-child,#fs_addons th:last-child{text-align:right}#fs_addons th{font-weight:bold}#fs_billing_address{width:100%}#fs_billing_address tr td{width:50%;padding:5px}#fs_billing_address tr:first-of-type td{padding-top:0}#fs_billing_address span{font-weight:bold}#fs_billing_address input,#fs_billing_address select{display:block;width:100%;margin-top:5px}#fs_billing_address input::-moz-placeholder,#fs_billing_address select::-moz-placeholder{color:transparent;opacity:1}#fs_billing_address input:-ms-input-placeholder,#fs_billing_address select:-ms-input-placeholder{color:transparent}#fs_billing_address input::-webkit-input-placeholder,#fs_billing_address select::-webkit-input-placeholder{color:transparent}#fs_billing_address input.fs-read-mode,#fs_billing_address select.fs-read-mode{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode td span{display:none}#fs_billing_address.fs-read-mode input,#fs_billing_address.fs-read-mode select{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode input::-moz-placeholder,#fs_billing_address.fs-read-mode select::-moz-placeholder{color:#ccc;opacity:1}#fs_billing_address.fs-read-mode input:-ms-input-placeholder,#fs_billing_address.fs-read-mode select:-ms-input-placeholder{color:#ccc}#fs_billing_address.fs-read-mode input::-webkit-input-placeholder,#fs_billing_address.fs-read-mode select::-webkit-input-placeholder{color:#ccc}#fs_billing_address button{display:block;width:100%}
freemius/assets/css/admin/affiliation.css CHANGED
@@ -1 +1 @@
1
- @charset "UTF-8";#fs_affiliation_content_wrapper #messages{margin-top:25px}#fs_affiliation_content_wrapper h3{font-size:24px;padding:0;margin-left:0}#fs_affiliation_content_wrapper ul li{box-sizing:border-box;list-style-type:none}#fs_affiliation_content_wrapper ul li:before{content:'✓';margin-right:10px;font-weight:bold}#fs_affiliation_content_wrapper p:not(.description),#fs_affiliation_content_wrapper li,#fs_affiliation_content_wrapper label{font-size:16px !important;line-height:26px !important}#fs_affiliation_content_wrapper .button{margin-top:20px;margin-bottom:7px;line-height:35px;height:40px;font-size:16px}#fs_affiliation_content_wrapper .button#cancel_button{margin-right:5px}#fs_affiliation_content_wrapper form .input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form .input-container .input-label{font-weight:bold;display:block;width:100%}#fs_affiliation_content_wrapper form .input-container.input-container-text label,#fs_affiliation_content_wrapper form .input-container.input-container-text input,#fs_affiliation_content_wrapper form .input-container.input-container-text textarea{display:block}#fs_affiliation_content_wrapper form .input-container #add_domain,#fs_affiliation_content_wrapper form .input-container .remove-domain{text-decoration:none;display:inline-block;margin-top:3px}#fs_affiliation_content_wrapper form .input-container #add_domain:focus,#fs_affiliation_content_wrapper form .input-container .remove-domain:focus{box-shadow:none}#fs_affiliation_content_wrapper form .input-container #add_domain.disabled,#fs_affiliation_content_wrapper form .input-container .remove-domain.disabled{color:#aaa;cursor:default}#fs_affiliation_content_wrapper form #extra_domains_container .description{margin-top:0;position:relative;top:-4px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain{display:inline-block;margin-right:5px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain:last-of-type{margin-bottom:0}
1
+ @charset "UTF-8";#fs_affiliation_content_wrapper #messages{margin-top:25px}#fs_affiliation_content_wrapper h3{font-size:24px;padding:0;margin-left:0}#fs_affiliation_content_wrapper ul li{box-sizing:border-box;list-style-type:none}#fs_affiliation_content_wrapper ul li:before{content:'✓';margin-right:10px;font-weight:bold}#fs_affiliation_content_wrapper p:not(.description),#fs_affiliation_content_wrapper li,#fs_affiliation_content_wrapper label{font-size:16px !important;line-height:26px !important}#fs_affiliation_content_wrapper .button{margin-top:20px;margin-bottom:7px;line-height:35px;height:40px;font-size:16px}#fs_affiliation_content_wrapper .button#cancel_button{margin-right:5px}#fs_affiliation_content_wrapper form .input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form .input-container .input-label{font-weight:bold;display:block;width:100%}#fs_affiliation_content_wrapper form .input-container.input-container-text label,#fs_affiliation_content_wrapper form .input-container.input-container-text input,#fs_affiliation_content_wrapper form .input-container.input-container-text textarea{display:block}#fs_affiliation_content_wrapper form .input-container #add_domain,#fs_affiliation_content_wrapper form .input-container .remove-domain{text-decoration:none;display:inline-block;margin-top:3px}#fs_affiliation_content_wrapper form .input-container #add_domain:focus,#fs_affiliation_content_wrapper form .input-container .remove-domain:focus{box-shadow:none}#fs_affiliation_content_wrapper form .input-container #add_domain.disabled,#fs_affiliation_content_wrapper form .input-container .remove-domain.disabled{color:#aaa;cursor:default}#fs_affiliation_content_wrapper form #extra_domains_container .description{margin-top:0;position:relative;top:-4px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain{display:inline-block;margin-right:5px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain:last-of-type{margin-bottom:0}
freemius/assets/css/admin/common.css CHANGED
@@ -1,2 +1,2 @@
1
- .theme-browser .theme .fs-premium-theme-badge{position:absolute;top:10px;right:0;background:#71ae00;color:#fff;text-transform:uppercase;padding:5px 10px;-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;font-weight:bold;border-right:0;-moz-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);font-size:1.1em}#iframe{line-height:0;font-size:0}.fs-full-size-wrapper{margin:40px 0 -65px -20px}@media (max-width: 600px){.fs-full-size-wrapper{margin:0 0 -65px -10px}}
2
- .fs-notice{position:relative}.fs-notice.fs-has-title{margin-bottom:30px !important}.fs-notice.success{color:green}.fs-notice.promotion{border-color:#00a0d2 !important;background-color:#f2fcff !important}.fs-notice .fs-notice-body{margin:.5em 0;padding:2px}.fs-notice .fs-close{cursor:pointer;color:#aaa;float:right}.fs-notice .fs-close:hover{color:#666}.fs-notice .fs-close>*{margin-top:7px;display:inline-block}.fs-notice label.fs-plugin-title{background:rgba(0,0,0,0.3);color:#fff;padding:2px 10px;position:absolute;top:100%;bottom:auto;right:auto;-moz-border-radius:0 0 3px 3px;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;left:10px;font-size:12px;font-weight:bold;cursor:auto}div.fs-notice.updated,div.fs-notice.success,div.fs-notice.promotion{display:block !important}.rtl .fs-notice .fs-close{float:left}.fs-secure-notice{position:fixed;top:32px;left:160px;right:0;background:#ebfdeb;padding:10px 20px;color:green;z-index:9999;-moz-box-shadow:0 2px 2px rgba(6,113,6,0.3);-webkit-box-shadow:0 2px 2px rgba(6,113,6,0.3);box-shadow:0 2px 2px rgba(6,113,6,0.3);opacity:0.95;filter:alpha(opacity=95)}.fs-secure-notice:hover{opacity:1;filter:alpha(opacity=100)}.fs-secure-notice a.fs-security-proof{color:green;text-decoration:none}@media screen and (max-width: 960px){.fs-secure-notice{left:36px}}@media screen and (max-width: 600px){.fs-secure-notice{display:none}}@media screen and (max-width: 500px){#fs_promo_tab{display:none}}@media screen and (max-width: 782px){.fs-secure-notice{left:0;top:46px;text-align:center}}span.fs-submenu-item.fs-sub:before{content:'\21B3';padding:0 5px}.rtl span.fs-submenu-item.fs-sub:before{content:'\21B2'}.fs-submenu-item.pricing.upgrade-mode{color:greenyellow}.fs-submenu-item.pricing.trial-mode{color:#83e2ff}#adminmenu .update-plugins.fs-trial{background-color:#00b9eb}.fs-ajax-spinner{border:0;width:20px;height:20px;margin-right:5px;vertical-align:sub;display:inline-block;background:url("../../../../../../../wp-admin/images/wpspin_light-2x.gif");background-size:contain}.wrap.fs-section h2{text-align:left}
1
+ .theme-browser .theme .fs-premium-theme-badge{position:absolute;top:10px;right:0;background:#71ae00;color:#fff;text-transform:uppercase;padding:5px 10px;-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;font-weight:bold;border-right:0;-moz-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);font-size:1.1em}#iframe{line-height:0;font-size:0}.fs-full-size-wrapper{margin:40px 0 -65px -20px}@media (max-width: 600px){.fs-full-size-wrapper{margin:0 0 -65px -10px}}
2
+ .fs-notice{position:relative}.fs-notice.fs-has-title{margin-bottom:30px !important}.fs-notice.success{color:green}.fs-notice.promotion{border-color:#00a0d2 !important;background-color:#f2fcff !important}.fs-notice .fs-notice-body{margin:.5em 0;padding:2px}.fs-notice .fs-close{cursor:pointer;color:#aaa;float:right}.fs-notice .fs-close:hover{color:#666}.fs-notice .fs-close>*{margin-top:7px;display:inline-block}.fs-notice label.fs-plugin-title{background:rgba(0,0,0,0.3);color:#fff;padding:2px 10px;position:absolute;top:100%;bottom:auto;right:auto;-moz-border-radius:0 0 3px 3px;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;left:10px;font-size:12px;font-weight:bold;cursor:auto}div.fs-notice.updated,div.fs-notice.success,div.fs-notice.promotion{display:block !important}.rtl .fs-notice .fs-close{float:left}.fs-secure-notice{position:fixed;top:32px;left:160px;right:0;background:#ebfdeb;padding:10px 20px;color:green;z-index:9999;-moz-box-shadow:0 2px 2px rgba(6,113,6,0.3);-webkit-box-shadow:0 2px 2px rgba(6,113,6,0.3);box-shadow:0 2px 2px rgba(6,113,6,0.3);opacity:0.95;filter:alpha(opacity=95)}.fs-secure-notice:hover{opacity:1;filter:alpha(opacity=100)}.fs-secure-notice a.fs-security-proof{color:green;text-decoration:none}@media screen and (max-width: 960px){.fs-secure-notice{left:36px}}@media screen and (max-width: 600px){.fs-secure-notice{display:none}}@media screen and (max-width: 500px){#fs_promo_tab{display:none}}@media screen and (max-width: 782px){.fs-secure-notice{left:0;top:46px;text-align:center}}span.fs-submenu-item.fs-sub:before{content:'\21B3';padding:0 5px}.rtl span.fs-submenu-item.fs-sub:before{content:'\21B2'}.fs-submenu-item.pricing.upgrade-mode{color:greenyellow}.fs-submenu-item.pricing.trial-mode{color:#83e2ff}#adminmenu .update-plugins.fs-trial{background-color:#00b9eb}.fs-ajax-spinner{border:0;width:20px;height:20px;margin-right:5px;vertical-align:sub;display:inline-block;background:url("../../../../../../../wp-admin/images/wpspin_light-2x.gif");background-size:contain}.wrap.fs-section h2{text-align:left}
freemius/assets/css/admin/connect.css CHANGED
@@ -1 +1 @@
1
- #fs_connect{width:480px;-moz-box-shadow:0px 1px 2px rgba(0,0,0,0.3);-webkit-box-shadow:0px 1px 2px rgba(0,0,0,0.3);box-shadow:0px 1px 2px rgba(0,0,0,0.3);margin:20px 0}@media screen and (max-width: 479px){#fs_connect{-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none;width:auto;margin:0 0 0 -10px}}#fs_connect .fs-content{background:#fff;padding:15px 20px}#fs_connect .fs-content .fs-error{background:snow;color:#d3135a;border:1px solid #d3135a;-moz-box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);text-align:center;padding:5px;margin-bottom:10px}#fs_connect .fs-content p{margin:0;padding:0;font-size:1.2em}#fs_connect .fs-license-key-container{position:relative;width:280px;margin:10px auto 0 auto}#fs_connect .fs-license-key-container input{width:100%}#fs_connect .fs-license-key-container .dashicons{position:absolute;top:5px;right:5px}#fs_connect .fs-actions{padding:10px 20px;background:#C0C7CA}#fs_connect .fs-actions .button{padding:0 10px 1px;line-height:35px;height:37px;font-size:16px;margin-bottom:0}#fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}#fs_connect .fs-actions .button.button-primary{padding-right:15px;padding-left:15px}#fs_connect .fs-actions .button.button-primary:after{content:' \279C'}#fs_connect .fs-actions .button.button-primary.fs-loading:after{content:''}#fs_connect .fs-actions .button.button-secondary{float:right}#fs_connect.fs-anonymous-disabled .fs-actions .button.button-primary{width:100%}#fs_connect .fs-permissions{padding:10px 20px;background:#FEFEFE;-moz-transition:background 0.5s ease;-o-transition:background 0.5s ease;-ms-transition:background 0.5s ease;-webkit-transition:background 0.5s ease;transition:background 0.5s ease}#fs_connect .fs-permissions .fs-license-sync-disclaimer{text-align:center;margin-top:0}#fs_connect .fs-permissions .fs-trigger{font-size:0.9em;text-decoration:none;text-align:center;display:block}#fs_connect .fs-permissions ul{height:0;overflow:hidden;margin:0}#fs_connect .fs-permissions ul li{margin-bottom:12px}#fs_connect .fs-permissions ul li:last-child{margin-bottom:0}#fs_connect .fs-permissions ul li i.dashicons{float:left;font-size:40px;width:40px;height:40px}#fs_connect .fs-permissions ul li div{margin-left:55px}#fs_connect .fs-permissions ul li div span{font-weight:bold;text-transform:uppercase;color:#23282d}#fs_connect .fs-permissions ul li div p{margin:2px 0 0 0}#fs_connect .fs-permissions.fs-open{background:#fff}#fs_connect .fs-permissions.fs-open ul{height:auto;margin:20px 20px 10px 20px}@media screen and (max-width: 479px){#fs_connect .fs-permissions{background:#fff}#fs_connect .fs-permissions .fs-trigger{display:none}#fs_connect .fs-permissions ul{height:auto;margin:20px}}#fs_connect .fs-freemium-licensing{padding:8px;background:#777;color:#fff}#fs_connect .fs-freemium-licensing p{text-align:center;display:block;margin:0;padding:0}#fs_connect .fs-freemium-licensing a{color:#C2EEFF;text-decoration:underline}#fs_connect .fs-visual{padding:12px;line-height:0;background:#fafafa;height:80px;position:relative}#fs_connect .fs-visual .fs-site-icon{position:absolute;left:20px;top:10px}#fs_connect .fs-visual .fs-connect-logo{position:absolute;right:20px;top:10px}#fs_connect .fs-visual .fs-plugin-icon{position:absolute;top:10px;left:50%;margin-left:-40px}#fs_connect .fs-visual .fs-plugin-icon,#fs_connect .fs-visual .fs-site-icon,#fs_connect .fs-visual img,#fs_connect .fs-visual object{width:80px;height:80px}#fs_connect .fs-visual .dashicons-wordpress{font-size:64px;background:#01749a;color:#fff;width:64px;height:64px;padding:8px}#fs_connect .fs-visual .dashicons-plus{position:absolute;top:50%;font-size:30px;margin-top:-10px;color:#bbb}#fs_connect .fs-visual .dashicons-plus.fs-first{left:28%}#fs_connect .fs-visual .dashicons-plus.fs-second{left:65%}#fs_connect .fs-visual .fs-plugin-icon,#fs_connect .fs-visual .fs-connect-logo,#fs_connect .fs-visual .fs-site-icon{border:1px solid #ccc;padding:1px;background:#fff}#fs_connect .fs-terms{text-align:center;font-size:0.85em;padding:5px;background:rgba(0,0,0,0.05)}#fs_connect .fs-terms,#fs_connect .fs-terms a{color:#999}#fs_connect .fs-terms a{text-decoration:none}.rtl #fs_connect .fs-actions{padding:10px 20px;background:#C0C7CA}.rtl #fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}.rtl #fs_connect .fs-actions .button.button-primary:after{content:' \000bb'}.rtl #fs_connect .fs-actions .button.button-primary.fs-loading:after{content:''}.rtl #fs_connect .fs-actions .button.button-secondary{float:left}.rtl #fs_connect .fs-permissions ul li div{margin-right:55px;margin-left:0}.rtl #fs_connect .fs-permissions ul li i.dashicons{float:right}.rtl #fs_connect .fs-visual .fs-site-icon{right:20px;left:auto}.rtl #fs_connect .fs-visual .fs-connect-logo{right:auto;left:20px}#fs_theme_connect_wrapper{position:fixed;top:0;height:100%;width:100%;z-index:99990;background:rgba(0,0,0,0.75);text-align:center;overflow-y:auto}#fs_theme_connect_wrapper:before{content:"";display:inline-block;vertical-align:middle;height:100%}#fs_theme_connect_wrapper>button.close{color:white;cursor:pointer;height:40px;width:40px;position:absolute;right:0;border:0;background-color:transparent;top:32px}#fs_theme_connect_wrapper #fs_connect{top:0;text-align:left;display:inline-block;vertical-align:middle;margin-top:52px;margin-bottom:20px}#fs_theme_connect_wrapper #fs_connect .fs-terms{background:rgba(140,140,140,0.64)}#fs_theme_connect_wrapper #fs_connect .fs-terms,#fs_theme_connect_wrapper #fs_connect .fs-terms a{color:#c5c5c5}.wp-pointer-content #fs_connect{margin:0;-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none}.fs-opt-in-pointer .wp-pointer-content{padding:0}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow{border-bottom-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow-inner{border-bottom-color:#fafafa}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow{border-top-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow-inner{border-top-color:#fafafa}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow{border-right-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow-inner{border-right-color:#fafafa}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow{border-left-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow-inner{border-left-color:#fafafa}
1
+ #fs_connect{width:480px;-moz-box-shadow:0px 1px 2px rgba(0,0,0,0.3);-webkit-box-shadow:0px 1px 2px rgba(0,0,0,0.3);box-shadow:0px 1px 2px rgba(0,0,0,0.3);margin:20px 0}@media screen and (max-width: 479px){#fs_connect{-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none;width:auto;margin:0 0 0 -10px}}#fs_connect .fs-content{background:#fff;padding:15px 20px}#fs_connect .fs-content .fs-error{background:snow;color:#d3135a;border:1px solid #d3135a;-moz-box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);text-align:center;padding:5px;margin-bottom:10px}#fs_connect .fs-content p{margin:0;padding:0;font-size:1.2em}#fs_connect .fs-license-key-container{position:relative;width:280px;margin:10px auto 0 auto}#fs_connect .fs-license-key-container input{width:100%}#fs_connect .fs-license-key-container .dashicons{position:absolute;top:5px;right:5px}#fs_connect .fs-actions{padding:10px 20px;background:#C0C7CA}#fs_connect .fs-actions .button{padding:0 10px 1px;line-height:35px;height:37px;font-size:16px;margin-bottom:0}#fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}#fs_connect .fs-actions .button.button-primary{padding-right:15px;padding-left:15px}#fs_connect .fs-actions .button.button-primary:after{content:' \279C'}#fs_connect .fs-actions .button.button-primary.fs-loading:after{content:''}#fs_connect .fs-actions .button.button-secondary{float:right}#fs_connect.fs-anonymous-disabled .fs-actions .button.button-primary{width:100%}#fs_connect .fs-permissions{padding:10px 20px;background:#FEFEFE;-moz-transition:background 0.5s ease;-o-transition:background 0.5s ease;-ms-transition:background 0.5s ease;-webkit-transition:background 0.5s ease;transition:background 0.5s ease}#fs_connect .fs-permissions .fs-license-sync-disclaimer{text-align:center;margin-top:0}#fs_connect .fs-permissions .fs-trigger{font-size:0.9em;text-decoration:none;text-align:center;display:block}#fs_connect .fs-permissions ul{height:0;overflow:hidden;margin:0}#fs_connect .fs-permissions ul li{margin-bottom:12px}#fs_connect .fs-permissions ul li:last-child{margin-bottom:0}#fs_connect .fs-permissions ul li i.dashicons{float:left;font-size:40px;width:40px;height:40px}#fs_connect .fs-permissions ul li div{margin-left:55px}#fs_connect .fs-permissions ul li div span{font-weight:bold;text-transform:uppercase;color:#23282d}#fs_connect .fs-permissions ul li div p{margin:2px 0 0 0}#fs_connect .fs-permissions.fs-open{background:#fff}#fs_connect .fs-permissions.fs-open ul{height:auto;margin:20px 20px 10px 20px}@media screen and (max-width: 479px){#fs_connect .fs-permissions{background:#fff}#fs_connect .fs-permissions .fs-trigger{display:none}#fs_connect .fs-permissions ul{height:auto;margin:20px}}#fs_connect .fs-freemium-licensing{padding:8px;background:#777;color:#fff}#fs_connect .fs-freemium-licensing p{text-align:center;display:block;margin:0;padding:0}#fs_connect .fs-freemium-licensing a{color:#C2EEFF;text-decoration:underline}#fs_connect .fs-visual{padding:12px;line-height:0;background:#fafafa;height:80px;position:relative}#fs_connect .fs-visual .fs-site-icon{position:absolute;left:20px;top:10px}#fs_connect .fs-visual .fs-connect-logo{position:absolute;right:20px;top:10px}#fs_connect .fs-visual .fs-plugin-icon{position:absolute;top:10px;left:50%;margin-left:-40px}#fs_connect .fs-visual .fs-plugin-icon,#fs_connect .fs-visual .fs-site-icon,#fs_connect .fs-visual img,#fs_connect .fs-visual object{width:80px;height:80px}#fs_connect .fs-visual .dashicons-wordpress{font-size:64px;background:#01749a;color:#fff;width:64px;height:64px;padding:8px}#fs_connect .fs-visual .dashicons-plus{position:absolute;top:50%;font-size:30px;margin-top:-10px;color:#bbb}#fs_connect .fs-visual .dashicons-plus.fs-first{left:28%}#fs_connect .fs-visual .dashicons-plus.fs-second{left:65%}#fs_connect .fs-visual .fs-plugin-icon,#fs_connect .fs-visual .fs-connect-logo,#fs_connect .fs-visual .fs-site-icon{border:1px solid #ccc;padding:1px;background:#fff}#fs_connect .fs-terms{text-align:center;font-size:0.85em;padding:5px;background:rgba(0,0,0,0.05)}#fs_connect .fs-terms,#fs_connect .fs-terms a{color:#999}#fs_connect .fs-terms a{text-decoration:none}.rtl #fs_connect .fs-actions{padding:10px 20px;background:#C0C7CA}.rtl #fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}.rtl #fs_connect .fs-actions .button.button-primary:after{content:' \000bb'}.rtl #fs_connect .fs-actions .button.button-primary.fs-loading:after{content:''}.rtl #fs_connect .fs-actions .button.button-secondary{float:left}.rtl #fs_connect .fs-permissions ul li div{margin-right:55px;margin-left:0}.rtl #fs_connect .fs-permissions ul li i.dashicons{float:right}.rtl #fs_connect .fs-visual .fs-site-icon{right:20px;left:auto}.rtl #fs_connect .fs-visual .fs-connect-logo{right:auto;left:20px}#fs_theme_connect_wrapper{position:fixed;top:0;height:100%;width:100%;z-index:99990;background:rgba(0,0,0,0.75);text-align:center;overflow-y:auto}#fs_theme_connect_wrapper:before{content:"";display:inline-block;vertical-align:middle;height:100%}#fs_theme_connect_wrapper>button.close{color:white;cursor:pointer;height:40px;width:40px;position:absolute;right:0;border:0;background-color:transparent;top:32px}#fs_theme_connect_wrapper #fs_connect{top:0;text-align:left;display:inline-block;vertical-align:middle;margin-top:52px;margin-bottom:20px}#fs_theme_connect_wrapper #fs_connect .fs-terms{background:rgba(140,140,140,0.64)}#fs_theme_connect_wrapper #fs_connect .fs-terms,#fs_theme_connect_wrapper #fs_connect .fs-terms a{color:#c5c5c5}.wp-pointer-content #fs_connect{margin:0;-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none}.fs-opt-in-pointer .wp-pointer-content{padding:0}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow{border-bottom-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow-inner{border-bottom-color:#fafafa}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow{border-top-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow-inner{border-top-color:#fafafa}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow{border-right-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow-inner{border-right-color:#fafafa}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow{border-left-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow-inner{border-left-color:#fafafa}
freemius/assets/css/admin/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden.
3
+ // Hide file structure from users on unprotected servers.
freemius/assets/css/customizer.css CHANGED
@@ -1 +1 @@
1
- #fs_customizer_upsell .fs-customizer-plan{padding:10px 20px 20px 20px;border-radius:3px;background:#fff}#fs_customizer_upsell .fs-customizer-plan h2{position:relative;margin:0;line-height:2em;text-transform:uppercase}#fs_customizer_upsell .fs-customizer-plan h2 .button-link{top:-2px}#fs_customizer_upsell .fs-feature{position:relative}#fs_customizer_upsell .dashicons-yes{color:#0085ba;font-size:2em;vertical-align:bottom;margin-left:-7px;margin-right:10px}.rtl #fs_customizer_upsell .dashicons-yes{margin-left:10px;margin-right:-7px}#fs_customizer_upsell .dashicons-editor-help{color:#bbb;cursor:help}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{opacity:0;visibility:hidden;-moz-transition:opacity 0.3s ease-in-out;-o-transition:opacity 0.3s ease-in-out;-ms-transition:opacity 0.3s ease-in-out;-webkit-transition:opacity 0.3s ease-in-out;transition:opacity 0.3s ease-in-out;position:absolute;background:#000;color:#fff;font-family:'arial', serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:0;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.2);box-shadow:1px 1px 1px rgba(0,0,0,0.2);line-height:1.3em;font-weight:bold;text-align:left}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{text-align:right}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{content:' ';display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:#000 transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{right:21px;left:auto}#fs_customizer_upsell .dashicons-editor-help:hover .fs-feature-desc{visibility:visible;opacity:1}#fs_customizer_upsell .button-primary{display:block;text-align:center;margin-top:10px}#fs_customizer_support{display:block !important}#fs_customizer_support .button{float:right}#fs_customizer_support .button-group{width:100%;display:block;margin-top:10px}#fs_customizer_support .button-group .button{float:none;width:50%;text-align:center}
1
+ #fs_customizer_upsell .fs-customizer-plan{padding:10px 20px 20px 20px;border-radius:3px;background:#fff}#fs_customizer_upsell .fs-customizer-plan h2{position:relative;margin:0;line-height:2em;text-transform:uppercase}#fs_customizer_upsell .fs-customizer-plan h2 .button-link{top:-2px}#fs_customizer_upsell .fs-feature{position:relative}#fs_customizer_upsell .dashicons-yes{color:#0085ba;font-size:2em;vertical-align:bottom;margin-left:-7px;margin-right:10px}.rtl #fs_customizer_upsell .dashicons-yes{margin-left:10px;margin-right:-7px}#fs_customizer_upsell .dashicons-editor-help{color:#bbb;cursor:help}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{opacity:0;visibility:hidden;-moz-transition:opacity 0.3s ease-in-out;-o-transition:opacity 0.3s ease-in-out;-ms-transition:opacity 0.3s ease-in-out;-webkit-transition:opacity 0.3s ease-in-out;transition:opacity 0.3s ease-in-out;position:absolute;background:#000;color:#fff;font-family:'arial', serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:0;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.2);box-shadow:1px 1px 1px rgba(0,0,0,0.2);line-height:1.3em;font-weight:bold;text-align:left}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{text-align:right}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{content:' ';display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:#000 transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{right:21px;left:auto}#fs_customizer_upsell .dashicons-editor-help:hover .fs-feature-desc{visibility:visible;opacity:1}#fs_customizer_upsell .button-primary{display:block;text-align:center;margin-top:10px}#fs_customizer_support{display:block !important}#fs_customizer_support .button{float:right}#fs_customizer_support .button-group{width:100%;display:block;margin-top:10px}#fs_customizer_support .button-group .button{float:none;width:50%;text-align:center}
freemius/assets/css/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden.
3
+ // Hide file structure from users on unprotected servers.
freemius/assets/img/foogallery.png ADDED
Binary file
freemius/assets/img/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden.
3
+ // Hide file structure from users on unprotected servers.
freemius/assets/js/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden.
3
+ // Hide file structure from users on unprotected servers.
freemius/assets/js/nojquery.ba-postmessage.js CHANGED
@@ -1,140 +1,140 @@
1
- /*!
2
- * jQuery postMessage - v0.5 - 9/11/2009
3
- * http://benalman.com/projects/jquery-postmessage-plugin/
4
- *
5
- * Copyright (c) 2009 "Cowboy" Ben Alman
6
- * Dual licensed under the MIT and GPL licenses.
7
- * http://benalman.com/about/license/
8
- *
9
- * Non-jQuery fork by Jeff Lee
10
- *
11
- * This fork consists of the following changes:
12
- * 1. Basic code cleanup and restructuring, for legibility.
13
- * 2. The `postMessage` and `receiveMessage` functions can be bound arbitrarily,
14
- * in terms of both function names and object scope. Scope is specified by
15
- * the the "this" context of NoJQueryPostMessageMixin();
16
- * 3. I've removed the check for Opera 9.64, which used `$.browser`. There were
17
- * at least three different GitHub users requesting the removal of this
18
- * "Opera sniff" on the original project's Issues page, so I figured this
19
- * would be a relatively safe change.
20
- * 4. `postMessage` no longer uses `$.param` to serialize messages that are not
21
- * strings. I actually prefer this structure anyway. `receiveMessage` does
22
- * not implement a corresponding deserialization step, and as such it seems
23
- * cleaner and more symmetric to leave both data serialization and
24
- * deserialization to the client.
25
- * 5. The use of `$.isFunction` is replaced by a functionally-identical check.
26
- * 6. The `$:nomunge` YUI option is no longer necessary.
27
- */
28
-
29
- function NoJQueryPostMessageMixin(postBinding, receiveBinding) {
30
-
31
- var setMessageCallback, unsetMessageCallback, currentMsgCallback,
32
- intervalId, lastHash, cacheBust = 1;
33
-
34
- if (window.postMessage) {
35
-
36
- if (window.addEventListener) {
37
- setMessageCallback = function(callback) {
38
- window.addEventListener('message', callback, false);
39
- }
40
-
41
- unsetMessageCallback = function(callback) {
42
- window.removeEventListener('message', callback, false);
43
- }
44
- } else {
45
- setMessageCallback = function(callback) {
46
- window.attachEvent('onmessage', callback);
47
- }
48
-
49
- unsetMessageCallback = function(callback) {
50
- window.detachEvent('onmessage', callback);
51
- }
52
- }
53
-
54
- this[postBinding] = function(message, targetUrl, target) {
55
- if (!targetUrl) {
56
- return;
57
- }
58
-
59
- // The browser supports window.postMessage, so call it with a targetOrigin
60
- // set appropriately, based on the targetUrl parameter.
61
- target.postMessage( message, targetUrl.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) );
62
- }
63
-
64
- // Since the browser supports window.postMessage, the callback will be
65
- // bound to the actual event associated with window.postMessage.
66
- this[receiveBinding] = function(callback, sourceOrigin, delay) {
67
- // Unbind an existing callback if it exists.
68
- if (currentMsgCallback) {
69
- unsetMessageCallback(currentMsgCallback);
70
- currentMsgCallback = null;
71
- }
72
-
73
- if (!callback) {
74
- return false;
75
- }
76
-
77
- // Bind the callback. A reference to the callback is stored for ease of
78
- // unbinding.
79
- currentMsgCallback = setMessageCallback(function(e) {
80
- switch(Object.prototype.toString.call(sourceOrigin)) {
81
- case '[object String]':
82
- if (sourceOrigin !== e.origin) {
83
- return false;
84
- }
85
- break;
86
- case '[object Function]':
87
- if (sourceOrigin(e.origin)) {
88
- return false;
89
- }
90
- break;
91
- }
92
-
93
- callback(e);
94
- });
95
- };
96
-
97
- } else {
98
-
99
- this[postBinding] = function(message, targetUrl, target) {
100
- if (!targetUrl) {
101
- return;
102
- }
103
-
104
- // The browser does not support window.postMessage, so set the location
105
- // of the target to targetUrl#message. A bit ugly, but it works! A cache
106
- // bust parameter is added to ensure that repeat messages trigger the
107
- // callback.
108
- target.location = targetUrl.replace( /#.*$/, '' ) + '#' + (+new Date) + (cacheBust++) + '&' + message;
109
- }
110
-
111
- // Since the browser sucks, a polling loop will be started, and the
112
- // callback will be called whenever the location.hash changes.
113
- this[receiveBinding] = function(callback, sourceOrigin, delay) {
114
- if (intervalId) {
115
- clearInterval(intervalId);
116
- intervalId = null;
117
- }
118
-
119
- if (callback) {
120
- delay = typeof sourceOrigin === 'number'
121
- ? sourceOrigin
122
- : typeof delay === 'number'
123
- ? delay
124
- : 100;
125
-
126
- intervalId = setInterval(function(){
127
- var hash = document.location.hash,
128
- re = /^#?\d+&/;
129
- if ( hash !== lastHash && re.test( hash ) ) {
130
- lastHash = hash;
131
- callback({ data: hash.replace( re, '' ) });
132
- }
133
- }, delay );
134
- }
135
- };
136
-
137
- }
138
-
139
- return this;
140
  }
1
+ /*!
2
+ * jQuery postMessage - v0.5 - 9/11/2009
3
+ * http://benalman.com/projects/jquery-postmessage-plugin/
4
+ *
5
+ * Copyright (c) 2009 "Cowboy" Ben Alman
6
+ * Dual licensed under the MIT and GPL licenses.
7
+ * http://benalman.com/about/license/
8
+ *
9
+ * Non-jQuery fork by Jeff Lee
10
+ *
11
+ * This fork consists of the following changes:
12
+ * 1. Basic code cleanup and restructuring, for legibility.
13
+ * 2. The `postMessage` and `receiveMessage` functions can be bound arbitrarily,
14
+ * in terms of both function names and object scope. Scope is specified by
15
+ * the the "this" context of NoJQueryPostMessageMixin();
16
+ * 3. I've removed the check for Opera 9.64, which used `$.browser`. There were
17
+ * at least three different GitHub users requesting the removal of this
18
+ * "Opera sniff" on the original project's Issues page, so I figured this
19
+ * would be a relatively safe change.
20
+ * 4. `postMessage` no longer uses `$.param` to serialize messages that are not
21
+ * strings. I actually prefer this structure anyway. `receiveMessage` does
22
+ * not implement a corresponding deserialization step, and as such it seems
23
+ * cleaner and more symmetric to leave both data serialization and
24
+ * deserialization to the client.
25
+ * 5. The use of `$.isFunction` is replaced by a functionally-identical check.
26
+ * 6. The `$:nomunge` YUI option is no longer necessary.
27
+ */
28
+
29
+ function NoJQueryPostMessageMixin(postBinding, receiveBinding) {
30
+
31
+ var setMessageCallback, unsetMessageCallback, currentMsgCallback,
32
+ intervalId, lastHash, cacheBust = 1;
33
+
34
+ if (window.postMessage) {
35
+
36
+ if (window.addEventListener) {
37
+ setMessageCallback = function(callback) {
38
+ window.addEventListener('message', callback, false);
39
+ }
40
+
41
+ unsetMessageCallback = function(callback) {
42
+ window.removeEventListener('message', callback, false);
43
+ }
44
+ } else {
45
+ setMessageCallback = function(callback) {
46
+ window.attachEvent('onmessage', callback);
47
+ }
48
+
49
+ unsetMessageCallback = function(callback) {
50
+ window.detachEvent('onmessage', callback);
51
+ }
52
+ }
53
+
54
+ this[postBinding] = function(message, targetUrl, target) {
55
+ if (!targetUrl) {
56
+ return;
57
+ }
58
+
59
+ // The browser supports window.postMessage, so call it with a targetOrigin
60
+ // set appropriately, based on the targetUrl parameter.
61
+ target.postMessage( message, targetUrl.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) );
62
+ }
63
+
64
+ // Since the browser supports window.postMessage, the callback will be
65
+ // bound to the actual event associated with window.postMessage.
66
+ this[receiveBinding] = function(callback, sourceOrigin, delay) {
67
+ // Unbind an existing callback if it exists.
68
+ if (currentMsgCallback) {
69
+ unsetMessageCallback(currentMsgCallback);
70
+ currentMsgCallback = null;
71
+ }
72
+
73
+ if (!callback) {
74
+ return false;
75
+ }
76
+
77
+ // Bind the callback. A reference to the callback is stored for ease of
78
+ // unbinding.
79
+ currentMsgCallback = setMessageCallback(function(e) {
80
+ switch(Object.prototype.toString.call(sourceOrigin)) {
81
+ case '[object String]':
82
+ if (sourceOrigin !== e.origin) {
83
+ return false;
84
+ }
85
+ break;
86
+ case '[object Function]':
87
+ if (sourceOrigin(e.origin)) {
88
+ return false;
89
+ }
90
+ break;
91
+ }
92
+
93
+ callback(e);
94
+ });
95
+ };
96
+
97
+ } else {
98
+
99
+ this[postBinding] = function(message, targetUrl, target) {
100
+ if (!targetUrl) {
101
+ return;
102
+ }
103
+
104
+ // The browser does not support window.postMessage, so set the location
105
+ // of the target to targetUrl#message. A bit ugly, but it works! A cache
106
+ // bust parameter is added to ensure that repeat messages trigger the
107
+ // callback.
108
+ target.location = targetUrl.replace( /#.*$/, '' ) + '#' + (+new Date) + (cacheBust++) + '&' + message;
109
+ }
110
+
111
+ // Since the browser sucks, a polling loop will be started, and the
112
+ // callback will be called whenever the location.hash changes.
113
+ this[receiveBinding] = function(callback, sourceOrigin, delay) {
114
+ if (intervalId) {
115
+ clearInterval(intervalId);
116
+ intervalId = null;
117
+ }
118
+
119
+ if (callback) {
120
+ delay = typeof sourceOrigin === 'number'
121
+ ? sourceOrigin
122
+ : typeof delay === 'number'
123
+ ? delay
124
+ : 100;
125
+
126
+ intervalId = setInterval(function(){
127
+ var hash = document.location.hash,
128
+ re = /^#?\d+&/;
129
+ if ( hash !== lastHash && re.test( hash ) ) {
130
+ lastHash = hash;
131
+ callback({ data: hash.replace( re, '' ) });
132
+ }
133
+ }, delay );
134
+ }
135
+ };
136
+
137
+ }
138
+
139
+ return this;
140
  }
freemius/assets/js/nojquery.ba-postmessage.min.js CHANGED
@@ -1,12 +1,12 @@
1
- /*
2
- * nojquery-postmessage by Jeff Lee
3
- * a non-jQuery fork of:
4
- *
5
- * jQuery postMessage - v0.5 - 9/11/2009
6
- * http://benalman.com/projects/jquery-postmessage-plugin/
7
- *
8
- * Copyright (c) 2009 "Cowboy" Ben Alman
9
- * Dual licensed under the MIT and GPL licenses.
10
- * http://benalman.com/about/license/
11
- */
12
  function NoJQueryPostMessageMixin(g,a){var b,h,e,d,f,c=1;if(window.postMessage){if(window.addEventListener){b=function(i){window.addEventListener("message",i,false)};h=function(i){window.removeEventListener("message",i,false)}}else{b=function(i){window.attachEvent("onmessage",i)};h=function(i){window.detachEvent("onmessage",i)}}this[g]=function(i,k,j){if(!k){return}j.postMessage(i,k.replace(/([^:]+:\/\/[^\/]+).*/,"$1"))};this[a]=function(k,j,i){if(e){h(e);e=null}if(!k){return false}e=b(function(l){switch(Object.prototype.toString.call(j)){case"[object String]":if(j!==l.origin){return false}break;case"[object Function]":if(j(l.origin)){return false}break}k(l)})}}else{this[g]=function(i,k,j){if(!k){return}j.location=k.replace(/#.*$/,"")+"#"+(+new Date)+(c++)+"&"+i};this[a]=function(k,j,i){if(d){clearInterval(d);d=null}if(k){i=typeof j==="number"?j:typeof i==="number"?i:100;d=setInterval(function(){var m=document.location.hash,l=/^#?\d+&/;if(m!==f&&l.test(m)){f=m;k({data:m.replace(l,"")})}},i)}}}return this};
1
+ /*
2
+ * nojquery-postmessage by Jeff Lee
3
+ * a non-jQuery fork of:
4
+ *
5
+ * jQuery postMessage - v0.5 - 9/11/2009
6
+ * http://benalman.com/projects/jquery-postmessage-plugin/
7
+ *
8
+ * Copyright (c) 2009 "Cowboy" Ben Alman
9
+ * Dual licensed under the MIT and GPL licenses.
10
+ * http://benalman.com/about/license/
11
+ */
12
  function NoJQueryPostMessageMixin(g,a){var b,h,e,d,f,c=1;if(window.postMessage){if(window.addEventListener){b=function(i){window.addEventListener("message",i,false)};h=function(i){window.removeEventListener("message",i,false)}}else{b=function(i){window.attachEvent("onmessage",i)};h=function(i){window.detachEvent("onmessage",i)}}this[g]=function(i,k,j){if(!k){return}j.postMessage(i,k.replace(/([^:]+:\/\/[^\/]+).*/,"$1"))};this[a]=function(k,j,i){if(e){h(e);e=null}if(!k){return false}e=b(function(l){switch(Object.prototype.toString.call(j)){case"[object String]":if(j!==l.origin){return false}break;case"[object Function]":if(j(l.origin)){return false}break}k(l)})}}else{this[g]=function(i,k,j){if(!k){return}j.location=k.replace(/#.*$/,"")+"#"+(+new Date)+(c++)+"&"+i};this[a]=function(k,j,i){if(d){clearInterval(d);d=null}if(k){i=typeof j==="number"?j:typeof i==="number"?i:100;d=setInterval(function(){var m=document.location.hash,l=/^#?\d+&/;if(m!==f&&l.test(m)){f=m;k({data:m.replace(l,"")})}},i)}}}return this};
freemius/assets/js/postmessage.js CHANGED
@@ -1,135 +1,135 @@
1
- (function ($, undef) {
2
- var global = this;
3
-
4
- // Namespace.
5
- global.FS = global.FS || {};
6
-
7
- global.FS.PostMessage = function ()
8
- {
9
- var
10
- _is_child = false,
11
- _postman = new NoJQueryPostMessageMixin('postMessage', 'receiveMessage'),
12
- _callbacks = {},
13
- _base_url,
14
- _parent_url = decodeURIComponent(document.location.hash.replace(/^#/, '')),
15
- _parent_subdomain = _parent_url.substring(0, _parent_url.indexOf('/', ('https://' === _parent_url.substring(0, ('https://').length)) ? 8 : 7)),
16
- _init = function () {
17
- _postman.receiveMessage(function (e) {
18
- var data = JSON.parse(e.data);
19
-
20
- if (_callbacks[data.type]) {
21
- for (var i = 0; i < _callbacks[data.type].length; i++) {
22
- // Execute type callbacks.
23
- _callbacks[data.type][i](data.data);
24
- }
25
- }
26
- }, _base_url);
27
- },
28
- _hasParent = ('' !== _parent_url),
29
- $window = $(window),
30
- $html = $('html');
31
-
32
- return {
33
- init : function (url, iframes)
34
- {
35
- _base_url = url;
36
- _init();
37
-
38
- // Automatically receive forward messages.
39
- FS.PostMessage.receiveOnce('forward', function (data){
40
- window.location = data.url;
41
- });
42
-
43
- iframes = iframes || [];
44
-
45
- if (iframes.length > 0) {
46
- $window.on('scroll', function () {
47
- for (var i = 0; i < iframes.length; i++) {
48
- FS.PostMessage.postScroll(iframes[i]);
49
- }
50
- });
51
- }
52
- },
53
- init_child : function ()
54
- {
55
- this.init(_parent_subdomain);
56
-
57
- _is_child = true;
58
-
59
- // Post height of a child right after window is loaded.
60
- $(window).bind('load', function () {
61
- FS.PostMessage.postHeight();
62
-
63
- // Post message that window was loaded.
64
- FS.PostMessage.post('loaded');
65
- });
66
- },
67
- hasParent : function ()
68
- {
69
- return _hasParent;
70
- },
71
- postHeight : function (diff, wrapper) {
72
- diff = diff || 0;
73
- wrapper = wrapper || '#wrap_section';
74
- this.post('height', {
75
- height: diff + $(wrapper).outerHeight(true)
76
- });
77
- },
78
- postScroll : function (iframe) {
79
- this.post('scroll', {
80
- top: $window.scrollTop(),
81
- height: ($window.height() - parseFloat($html.css('paddingTop')) - parseFloat($html.css('marginTop')))
82
- }, iframe);
83
- },
84
- post : function (type, data, iframe)
85
- {
86
- console.debug('PostMessage.post', type);
87
-
88
- if (iframe)
89
- {
90
- // Post to iframe.
91
- _postman.postMessage(JSON.stringify({
92
- type: type,
93
- data: data
94
- }), iframe.src, iframe.contentWindow);
95
- }
96
- else {
97
- // Post to parent.
98
- _postman.postMessage(JSON.stringify({
99
- type: type,
100
- data: data
101
- }), _parent_url, window.parent);
102
- }
103
- },
104
- receive: function (type, callback)
105
- {
106
- console.debug('PostMessage.receive', type);
107
-
108
- if (undef === _callbacks[type])
109
- _callbacks[type] = [];
110
-
111
- _callbacks[type].push(callback);
112
- },
113
- receiveOnce: function (type, callback)
114
- {
115
- if (this.is_set(type))
116
- return;
117
-
118
- this.receive(type, callback);
119
- },
120
- // Check if any callbacks assigned to a specified message type.
121
- is_set: function (type)
122
- {
123
- return (undef != _callbacks[type]);
124
- },
125
- parent_url: function ()
126
- {
127
- return _parent_url;
128
- },
129
- parent_subdomain: function ()
130
- {
131
- return _parent_subdomain;
132
- }
133
- };
134
- }();
135
  })(jQuery);
1
+ (function ($, undef) {
2
+ var global = this;
3
+
4
+ // Namespace.
5
+ global.FS = global.FS || {};
6
+
7
+ global.FS.PostMessage = function ()
8
+ {
9
+ var
10
+ _is_child = false,
11
+ _postman = new NoJQueryPostMessageMixin('postMessage', 'receiveMessage'),
12
+ _callbacks = {},
13
+ _base_url,
14
+ _parent_url = decodeURIComponent(document.location.hash.replace(/^#/, '')),
15
+ _parent_subdomain = _parent_url.substring(0, _parent_url.indexOf('/', ('https://' === _parent_url.substring(0, ('https://').length)) ? 8 : 7)),
16
+ _init = function () {
17
+ _postman.receiveMessage(function (e) {
18
+ var data = JSON.parse(e.data);
19
+
20
+ if (_callbacks[data.type]) {
21
+ for (var i = 0; i < _callbacks[data.type].length; i++) {
22
+ // Execute type callbacks.
23
+ _callbacks[data.type][i](data.data);
24
+ }
25
+ }
26
+ }, _base_url);
27
+ },
28
+ _hasParent = ('' !== _parent_url),
29
+ $window = $(window),
30
+ $html = $('html');
31
+
32
+ return {
33
+ init : function (url, iframes)
34
+ {
35
+ _base_url = url;
36
+ _init();
37
+
38
+ // Automatically receive forward messages.
39
+ FS.PostMessage.receiveOnce('forward', function (data){
40
+ window.location = data.url;
41
+ });
42
+
43
+ iframes = iframes || [];
44
+
45
+ if (iframes.length > 0) {
46
+ $window.on('scroll', function () {
47
+ for (var i = 0; i < iframes.length; i++) {
48
+ FS.PostMessage.postScroll(iframes[i]);
49
+ }
50
+ });
51
+ }
52
+ },
53
+ init_child : function ()
54
+ {
55
+ this.init(_parent_subdomain);
56
+
57
+ _is_child = true;
58
+
59
+ // Post height of a child right after window is loaded.
60
+ $(window).bind('load', function () {
61
+ FS.PostMessage.postHeight();
62
+
63
+ // Post message that window was loaded.
64
+ FS.PostMessage.post('loaded');
65
+ });
66
+ },
67
+ hasParent : function ()
68
+ {
69
+ return _hasParent;
70
+ },
71
+ postHeight : function (diff, wrapper) {
72
+ diff = diff || 0;
73
+ wrapper = wrapper || '#wrap_section';
74
+ this.post('height', {
75
+ height: diff + $(wrapper).outerHeight(true)
76
+ });
77
+ },
78
+ postScroll : function (iframe) {
79
+ this.post('scroll', {
80
+ top: $window.scrollTop(),
81
+ height: ($window.height() - parseFloat($html.css('paddingTop')) - parseFloat($html.css('marginTop')))
82
+ }, iframe);
83
+ },
84
+ post : function (type, data, iframe)
85
+ {
86
+ console.debug('PostMessage.post', type);
87
+
88
+ if (iframe)
89
+ {
90
+ // Post to iframe.
91
+ _postman.postMessage(JSON.stringify({
92
+ type: type,
93
+ data: data
94
+ }), iframe.src, iframe.contentWindow);
95
+ }
96
+ else {
97
+ // Post to parent.
98
+ _postman.postMessage(JSON.stringify({
99
+ type: type,
100
+ data: data
101
+ }), _parent_url, window.parent);
102
+ }
103
+ },
104
+ receive: function (type, callback)
105
+ {
106
+ console.debug('PostMessage.receive', type);
107
+
108
+ if (undef === _callbacks[type])
109
+ _callbacks[type] = [];
110
+
111
+ _callbacks[type].push(callback);
112
+ },
113
+ receiveOnce: function (type, callback)
114
+ {
115
+ if (this.is_set(type))
116
+ return;
117
+
118
+ this.receive(type, callback);
119
+ },
120
+ // Check if any callbacks assigned to a specified message type.
121
+ is_set: function (type)
122
+ {
123
+ return (undef != _callbacks[type]);
124
+ },
125
+ parent_url: function ()
126
+ {
127
+ return _parent_url;
128
+ },
129
+ parent_subdomain: function ()
130
+ {
131
+ return _parent_subdomain;
132
+ }
133
+ };
134
+ }();
135
  })(jQuery);
freemius/assets/scss/_mixins.scss CHANGED
@@ -1,270 +1,270 @@
1
- // ---- CSS3 SASS MIXINS ----
2
- // https://github.com/madr/css3-sass-mixins
3
- //
4
- // Copyright (C) 2011 by Anders Ytterström
5
- //
6
- // Permission is hereby granted, free of charge, to any person obtaining a copy
7
- // of this software and associated documentation files (the "Software"), to deal
8
- // in the Software without restriction, including without limitation the rights
9
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- // copies of the Software, and to permit persons to whom the Software is
11
- // furnished to do so, subject to the following conditions:
12
- //
13
- // The above copyright notice and this permission notice shall be included in
14
- // all copies or substantial portions of the Software.
15
- //
16
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
- // THE SOFTWARE.
23
- //
24
-
25
- // ---- LEGACY IE SUPPORT USING FILTERS ----
26
- // Should IE filters be used or not?
27
- // PROS: gradients, drop shadows etc will be handled by css.
28
- // CONS: will harm the site performance badly,
29
- // especially on sites with heavy rendering and scripting.
30
- $useIEFilters: 0;
31
- // might be 0 or 1. disabled by default.
32
- // ---- /LEGACY IE SUPPORT USING FILTERS ----
33
-
34
-
35
- @mixin background-size ($value) {
36
- -webkit-background-size: $value;
37
- background-size: $value;
38
- }
39
-
40
- @mixin border-image ($path, $offsets, $repeats) {
41
- -moz-border-image: $path $offsets $repeats;
42
- -o-border-image: $path $offsets $repeats;
43
- -webkit-border-image: $path $offsets $repeats;
44
- border-image: $path $offsets $repeats;
45
- }
46
-
47
- @mixin border-radius ($values...) {
48
- -moz-border-radius: $values;
49
- -webkit-border-radius: $values;
50
- border-radius: $values;
51
- /*-moz-background-clip: padding;
52
- -webkit-background-clip: padding-box;
53
- background-clip: padding-box;*/
54
- }
55
-
56
- @mixin box-shadow ($values...) {
57
- -moz-box-shadow: $values;
58
- -webkit-box-shadow: $values;
59
- box-shadow: $values;
60
- }
61
-
62
- //@mixin box-shadow ($x, $y, $offset, $hex, $ie: $useIEFilters, $inset: null, $spread:null) {
63
- // -moz-box-shadow: $x $y $offset $spread $hex $inset;
64
- // -webkit-box-shadow: $x $y $offset $spread $hex $inset;
65
- // box-shadow: $x $y $offset $spread $hex $inset;
66
- //
67
- // @if $ie == 1 {
68
- // $iecolor: '#' + red($hex) + green($hex) + blue($hex);
69
- // filter: progid:DXImageTransform.Microsoft.dropshadow(OffX=#{$x}, OffY=#{$y}, Color='#{$iecolor}');
70
- // -ms-filter: quote(progid:DXImageTransform.Microsoft.dropshadow(OffX=#{$x}, OffY=#{$y}, Color='#{$iecolor}'));
71
- // }
72
- //}
73
-
74
- @mixin box-sizing($value) {
75
- -moz-box-sizing: $value;
76
- -webkit-box-sizing: $value;
77
- box-sizing: $value;
78
- }
79
-
80
- // requires sass 3.2
81
- @mixin keyframes($name){
82
- @-moz-keyframes #{$name} { @content; }
83
- @-ms-keyframes #{$name} { @content; }
84
- @-o-keyframes #{$name} { @content; }
85
- @-webkit-keyframes #{$name} { @content; }
86
- @keyframes #{$name} { @content; }
87
- }
88
-
89
- @mixin linear-gradient($from, $to, $ie: $useIEFilters) {
90
- @if $ie != 1 { background-color: $to; }
91
-
92
- background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, $from),color-stop(1, $to));
93
- background-image: -webkit-linear-gradient(top, $from, $to);
94
- background-image: -moz-linear-gradient(top, $from, $to);
95
- background-image: -ms-linear-gradient(top, $from, $to);
96
- background-image: -o-linear-gradient(top, $from, $to);
97
- background-image: linear-gradient(top, bottom, $from, $to);
98
-
99
- @if $ie == 1 {
100
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#{$from}', endColorstr='#{$to}');
101
- }
102
- }
103
-
104
- @mixin horizontal-gradient($startColor: #555, $endColor: #333, $ie: $useIEFilters) {
105
- @if $ie != 1 { background-color: $endColor; }
106
-
107
- background-color: $endColor;
108
- background-image: -webkit-gradient(linear, 0 0, 100% 0, from($startColor), to($endColor)); // Safari 4+, Chrome 2+
109
- background-image: -webkit-linear-gradient(left, $startColor, $endColor); // Safari 5.1+, Chrome 10+
110
- background-image: -moz-linear-gradient(left, $startColor, $endColor); // FF 3.6+
111
- background-image: -o-linear-gradient(left, $startColor, $endColor); // Opera 11.10
112
- background-image: linear-gradient(to right, $startColor, $endColor); // Standard, IE10
113
- background-repeat: repeat-x;
114
- @if $ie == 1 {
115
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#{$startColor}', endColorstr='#{$endColor}', GradientType=1);
116
- }
117
- }
118
-
119
- @mixin radial-gradient($from, $to, $ie: $useIEFilters) {
120
- @if $ie != 1 { background-color: $to; }
121
-
122
- background: -moz-radial-gradient(center, circle cover, $from 0%, $to 100%);
123
- background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, $from), color-stop(100%, $to));
124
- background: -webkit-radial-gradient(center, circle cover, $from 0%, $to 100%);
125
- background: -o-radial-gradient(center, circle cover, $from 0%, $to 100%);
126
- background: -ms-radial-gradient(center, circle cover, $from 0%, $to 100%);
127
- background: radial-gradient(center, circle cover, $from 0%, $to 100%);
128
- background-color: $from;
129
-
130
- @if $ie == 1 {
131
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#{$from}', endColorstr='#{$to}', GradientType=1); /* IE6-9 fallback on horizontal gradient */
132
- }
133
- }
134
-
135
- @mixin perspective($perspective) {
136
- -moz-perspective: $perspective;
137
- -ms-perspective: $perspective;
138
- -webkit-perspective: $perspective;
139
- perspective: $perspective;
140
- -moz-transform-style: preserve-3d;
141
- -ms-transform-style: preserve-3d;
142
- -webkit-transform-style: preserve-3d;
143
- transform-style: preserve-3d;
144
- }
145
-
146
- @mixin transform ($transforms) {
147
- -moz-transform: $transforms;
148
- -o-transform: $transforms;
149
- -ms-transform: $transforms;
150
- -webkit-transform: $transforms;
151
- transform: $transforms;
152
- }
153
-
154
- @mixin matrix ($a, $b, $c, $d, $e, $f) {
155
- -moz-transform: matrix($a, $b, $c, $d, #{$e}px, #{$f}px);
156
- -o-transform: matrix($a, $b, $c, $d, $e, $f);
157
- -ms-transform: matrix($a, $b, $c, $d, $e, $f);
158
- -webkit-transform: matrix($a, $b, $c, $d, $e, $f);
159
- transform: matrix($a, $b, $c, $d, $e, $f);
160
- }
161
-
162
- @mixin rotate ($deg) {
163
- @include transform(rotate(#{$deg}deg));
164
- }
165
-
166
- @mixin scale ($size) {
167
- @include transform(scale(#{$size}));
168
- }
169
-
170
- @mixin translate ($x, $y) {
171
- @include transform(translate($x, $y));
172
- }
173
-
174
- @mixin transition ($value...) {
175
- -moz-transition: $value;
176
- -o-transition: $value;
177
- -ms-transition: $value;
178
- -webkit-transition: $value;
179
- transition: $value;
180
- }
181
-
182
- @mixin animation($str) {
183
- -webkit-animation: #{$str};
184
- -moz-animation: #{$str};
185
- -ms-animation: #{$str};
186
- -o-animation: #{$str};
187
- animation: #{$str};
188
- }
189
-
190
- @mixin animation-name($str) {
191
- -webkit-animation-name: #{$str};
192
- -moz-animation-name: #{$str};
193
- -ms-animation-name: #{$str};
194
- -o-animation-name: #{$str};
195
- animation-name: #{$str};
196
- }
197
-
198
- @mixin animation-duration($str) {
199
- -webkit-animation-duration: #{$str};
200
- -moz-animation-duration: #{$str};
201
- -ms-animation-duration: #{$str};
202
- -o-animation-duration: #{$str};
203
- animation-duration: #{$str};
204
- }
205
-
206
- @mixin animation-direction($str) {
207
- -webkit-animation-direction: #{$str};
208
- -moz-animation-direction: #{$str};
209
- -ms-animation-direction: #{$str};
210
- -o-animation-direction: #{$str};
211
- animation-direction: #{$str};
212
- }
213
-
214
- @mixin animation-delay($str) {
215
- animation-delay:#{$str};
216
- -o-animation-delay:#{$str};
217
- -ms-animation-delay:#{$str};
218
- -webkit-animation-delay:#{$str};
219
- -moz-animation-delay:#{$str};
220
- }
221
-
222
- @mixin animation-iteration-count($str) {
223
- animation-iteration-count:#{$str};
224
- -o-animation-iteration-count:#{$str};
225
- -ms-animation-iteration-count:#{$str};
226
- -webkit-animation-iteration-count:#{$str};
227
- -moz-animation-iteration-count:#{$str};
228
- }
229
-
230
- @mixin animation-timing-function($str) {
231
- -webkit-animation-timing-function: #{$str};
232
- -moz-animation-timing-function: #{$str};
233
- -ms-animation-timing-function: #{$str};
234
- -o-animation-timing-function: #{$str};
235
- animation-timing-function: #{$str};
236
- }
237
-
238
- // ==== /CSS3 SASS MIXINS ====
239
-
240
- @mixin opacity($opacity) {
241
- opacity: $opacity;
242
- $opacity-ie: $opacity * 100;
243
- filter: alpha(opacity=$opacity-ie); //IE8
244
- }
245
-
246
- @mixin size($width, $height: $width)
247
- {
248
- width: $width;
249
- height: $height;
250
- }
251
-
252
- @mixin clearfix
253
- {
254
- &:after {
255
- content: "";
256
- display: table;
257
- clear: both;
258
- }
259
- }
260
-
261
- // Placeholder text
262
- @mixin placeholder($color: $input-color-placeholder) {
263
- // Firefox
264
- &::-moz-placeholder {
265
- color: $color;
266
- opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526
267
- }
268
- &:-ms-input-placeholder { color: $color; } // Internet Explorer 10+
269
- &::-webkit-input-placeholder { color: $color; } // Safari and Chrome
270
  }
1
+ // ---- CSS3 SASS MIXINS ----
2
+ // https://github.com/madr/css3-sass-mixins
3
+ //
4
+ // Copyright (C) 2011 by Anders Ytterström
5
+ //
6
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ // of this software and associated documentation files (the "Software"), to deal
8
+ // in the Software without restriction, including without limitation the rights
9
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ // copies of the Software, and to permit persons to whom the Software is
11
+ // furnished to do so, subject to the following conditions:
12
+ //
13
+ // The above copyright notice and this permission notice shall be included in
14
+ // all copies or substantial portions of the Software.
15
+ //
16
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ // THE SOFTWARE.
23
+ //
24
+
25
+ // ---- LEGACY IE SUPPORT USING FILTERS ----
26
+ // Should IE filters be used or not?
27
+ // PROS: gradients, drop shadows etc will be handled by css.
28
+ // CONS: will harm the site performance badly,
29
+ // especially on sites with heavy rendering and scripting.
30
+ $useIEFilters: 0;
31
+ // might be 0 or 1. disabled by default.
32
+ // ---- /LEGACY IE SUPPORT USING FILTERS ----
33
+
34
+
35
+ @mixin background-size ($value) {
36
+ -webkit-background-size: $value;
37
+ background-size: $value;
38
+ }
39
+
40
+ @mixin border-image ($path, $offsets, $repeats) {
41
+ -moz-border-image: $path $offsets $repeats;
42
+ -o-border-image: $path $offsets $repeats;
43
+ -webkit-border-image: $path $offsets $repeats;
44
+ border-image: $path $offsets $repeats;
45
+ }
46
+
47
+ @mixin border-radius ($values...) {
48
+ -moz-border-radius: $values;
49
+ -webkit-border-radius: $values;
50
+ border-radius: $values;
51
+ /*-moz-background-clip: padding;
52
+ -webkit-background-clip: padding-box;
53
+ background-clip: padding-box;*/
54
+ }
55
+
56
+ @mixin box-shadow ($values...) {
57
+ -moz-box-shadow: $values;
58
+ -webkit-box-shadow: $values;
59
+ box-shadow: $values;
60
+ }
61
+
62
+ //@mixin box-shadow ($x, $y, $offset, $hex, $ie: $useIEFilters, $inset: null, $spread:null) {
63
+ // -moz-box-shadow: $x $y $offset $spread $hex $inset;
64
+ // -webkit-box-shadow: $x $y $offset $spread $hex $inset;
65
+ // box-shadow: $x $y $offset $spread $hex $inset;
66
+ //
67
+ // @if $ie == 1 {
68
+ // $iecolor: '#' + red($hex) + green($hex) + blue($hex);
69
+ // filter: progid:DXImageTransform.Microsoft.dropshadow(OffX=#{$x}, OffY=#{$y}, Color='#{$iecolor}');
70
+ // -ms-filter: quote(progid:DXImageTransform.Microsoft.dropshadow(OffX=#{$x}, OffY=#{$y}, Color='#{$iecolor}'));
71
+ // }
72
+ //}
73
+
74
+ @mixin box-sizing($value) {
75
+ -moz-box-sizing: $value;
76
+ -webkit-box-sizing: $value;
77
+ box-sizing: $value;
78
+ }
79
+
80
+ // requires sass 3.2
81
+ @mixin keyframes($name){
82
+ @-moz-keyframes #{$name} { @content; }
83
+ @-ms-keyframes #{$name} { @content; }
84
+ @-o-keyframes #{$name} { @content; }
85
+ @-webkit-keyframes #{$name} { @content; }
86
+ @keyframes #{$name} { @content; }
87
+ }
88
+
89
+ @mixin linear-gradient($from, $to, $ie: $useIEFilters) {
90
+ @if $ie != 1 { background-color: $to; }
91
+
92
+ background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, $from),color-stop(1, $to));
93
+ background-image: -webkit-linear-gradient(top, $from, $to);
94
+ background-image: -moz-linear-gradient(top, $from, $to);
95
+ background-image: -ms-linear-gradient(top, $from, $to);
96
+ background-image: -o-linear-gradient(top, $from, $to);
97
+ background-image: linear-gradient(top, bottom, $from, $to);
98
+
99
+ @if $ie == 1 {
100
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#{$from}', endColorstr='#{$to}');
101
+ }
102
+ }
103
+
104
+ @mixin horizontal-gradient($startColor: #555, $endColor: #333, $ie: $useIEFilters) {
105
+ @if $ie != 1 { background-color: $endColor; }
106
+
107
+ background-color: $endColor;
108
+ background-image: -webkit-gradient(linear, 0 0, 100% 0, from($startColor), to($endColor)); // Safari 4+, Chrome 2+
109
+ background-image: -webkit-linear-gradient(left, $startColor, $endColor); // Safari 5.1+, Chrome 10+
110
+ background-image: -moz-linear-gradient(left, $startColor, $endColor); // FF 3.6+
111
+ background-image: -o-linear-gradient(left, $startColor, $endColor); // Opera 11.10
112
+ background-image: linear-gradient(to right, $startColor, $endColor); // Standard, IE10
113
+ background-repeat: repeat-x;
114
+ @if $ie == 1 {
115
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#{$startColor}', endColorstr='#{$endColor}', GradientType=1);
116
+ }
117
+ }
118
+
119
+ @mixin radial-gradient($from, $to, $ie: $useIEFilters) {
120
+ @if $ie != 1 { background-color: $to; }
121
+
122
+ background: -moz-radial-gradient(center, circle cover, $from 0%, $to 100%);
123
+ background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, $from), color-stop(100%, $to));
124
+ background: -webkit-radial-gradient(center, circle cover, $from 0%, $to 100%);
125
+ background: -o-radial-gradient(center, circle cover, $from 0%, $to 100%);
126
+ background: -ms-radial-gradient(center, circle cover, $from 0%, $to 100%);
127
+ background: radial-gradient(center, circle cover, $from 0%, $to 100%);
128
+ background-color: $from;
129
+
130
+ @if $ie == 1 {
131
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#{$from}', endColorstr='#{$to}', GradientType=1); /* IE6-9 fallback on horizontal gradient */
132
+ }
133
+ }
134
+
135
+ @mixin perspective($perspective) {
136
+ -moz-perspective: $perspective;
137
+ -ms-perspective: $perspective;
138
+ -webkit-perspective: $perspective;
139
+ perspective: $perspective;
140
+ -moz-transform-style: preserve-3d;
141
+ -ms-transform-style: preserve-3d;
142
+ -webkit-transform-style: preserve-3d;
143
+ transform-style: preserve-3d;
144
+ }
145
+
146
+ @mixin transform ($transforms) {
147
+ -moz-transform: $transforms;
148
+ -o-transform: $transforms;
149
+ -ms-transform: $transforms;
150
+ -webkit-transform: $transforms;
151
+ transform: $transforms;
152
+ }
153
+
154
+ @mixin matrix ($a, $b, $c, $d, $e, $f) {
155
+ -moz-transform: matrix($a, $b, $c, $d, #{$e}px, #{$f}px);
156
+ -o-transform: matrix($a, $b, $c, $d, $e, $f);
157
+ -ms-transform: matrix($a, $b, $c, $d, $e, $f);
158
+ -webkit-transform: matrix($a, $b, $c, $d, $e, $f);
159
+ transform: matrix($a, $b, $c, $d, $e, $f);
160
+ }
161
+
162
+ @mixin rotate ($deg) {
163
+ @include transform(rotate(#{$deg}deg));
164
+ }
165
+
166
+ @mixin scale ($size) {
167
+ @include transform(scale(#{$size}));
168
+ }
169
+
170
+ @mixin translate ($x, $y) {
171
+ @include transform(translate($x, $y));
172
+ }
173
+
174
+ @mixin transition ($value...) {
175
+ -moz-transition: $value;
176
+ -o-transition: $value;
177
+ -ms-transition: $value;
178
+ -webkit-transition: $value;
179
+ transition: $value;
180
+ }
181
+
182
+ @mixin animation($str) {
183
+ -webkit-animation: #{$str};
184
+ -moz-animation: #{$str};
185
+ -ms-animation: #{$str};
186
+ -o-animation: #{$str};
187
+ animation: #{$str};
188
+ }
189
+
190
+ @mixin animation-name($str) {
191
+ -webkit-animation-name: #{$str};
192
+ -moz-animation-name: #{$str};
193
+ -ms-animation-name: #{$str};
194
+ -o-animation-name: #{$str};
195
+ animation-name: #{$str};
196
+ }
197
+
198
+ @mixin animation-duration($str) {
199
+ -webkit-animation-duration: #{$str};
200
+ -moz-animation-duration: #{$str};
201
+ -ms-animation-duration: #{$str};
202
+ -o-animation-duration: #{$str};
203
+ animation-duration: #{$str};
204
+ }
205
+
206
+ @mixin animation-direction($str) {
207
+ -webkit-animation-direction: #{$str};
208
+ -moz-animation-direction: #{$str};
209
+ -ms-animation-direction: #{$str};
210
+ -o-animation-direction: #{$str};
211
+ animation-direction: #{$str};
212
+ }
213
+
214
+ @mixin animation-delay($str) {
215
+ animation-delay:#{$str};
216
+ -o-animation-delay:#{$str};
217
+ -ms-animation-delay:#{$str};
218
+ -webkit-animation-delay:#{$str};
219
+ -moz-animation-delay:#{$str};
220
+ }
221
+
222
+ @mixin animation-iteration-count($str) {
223
+ animation-iteration-count:#{$str};
224
+ -o-animation-iteration-count:#{$str};
225
+ -ms-animation-iteration-count:#{$str};
226
+ -webkit-animation-iteration-count:#{$str};
227
+ -moz-animation-iteration-count:#{$str};
228
+ }
229
+
230
+ @mixin animation-timing-function($str) {
231
+ -webkit-animation-timing-function: #{$str};
232
+ -moz-animation-timing-function: #{$str};
233
+ -ms-animation-timing-function: #{$str};
234
+ -o-animation-timing-function: #{$str};
235
+ animation-timing-function: #{$str};
236
+ }
237
+
238
+ // ==== /CSS3 SASS MIXINS ====
239
+
240
+ @mixin opacity($opacity) {
241
+ opacity: $opacity;
242
+ $opacity-ie: $opacity * 100;
243
+ filter: alpha(opacity=$opacity-ie); //IE8
244
+ }
245
+
246
+ @mixin size($width, $height: $width)
247
+ {
248
+ width: $width;
249
+ height: $height;
250
+ }
251
+
252
+ @mixin clearfix
253
+ {
254
+ &:after {
255
+ content: "";
256
+ display: table;
257
+ clear: both;
258
+ }
259
+ }
260
+
261
+ // Placeholder text
262
+ @mixin placeholder($color: $input-color-placeholder) {
263
+ // Firefox
264
+ &::-moz-placeholder {
265
+ color: $color;
266
+ opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526
267
+ }
268
+ &:-ms-input-placeholder { color: $color; } // Internet Explorer 10+
269
+ &::-webkit-input-placeholder { color: $color; } // Safari and Chrome
270
  }
freemius/assets/scss/admin/_themes.scss CHANGED
@@ -1,21 +1,21 @@
1
- .theme-browser
2
- {
3
- .theme
4
- {
5
- .fs-premium-theme-badge
6
- {
7
- position: absolute;
8
- top: 10px;
9
- right: 0;
10
- background: $fs-logo-green-color;
11
- color: #fff;
12
- text-transform: uppercase;
13
- padding: 5px 10px;
14
- @include border-radius(3px 0 0 3px);
15
- font-weight: bold;
16
- border-right: 0;
17
- @include box-shadow(0 2px 1px -1px rgba(0, 0, 0, .3));
18
- font-size: 1.1em;
19
- }
20
- }
21
  }
1
+ .theme-browser
2
+ {
3
+ .theme
4
+ {
5
+ .fs-premium-theme-badge
6
+ {
7
+ position: absolute;
8
+ top: 10px;
9
+ right: 0;
10
+ background: $fs-logo-green-color;
11
+ color: #fff;
12
+ text-transform: uppercase;
13
+ padding: 5px 10px;
14
+ @include border-radius(3px 0 0 3px);
15
+ font-weight: bold;
16
+ border-right: 0;
17
+ @include box-shadow(0 2px 1px -1px rgba(0, 0, 0, .3));
18
+ font-size: 1.1em;
19
+ }
20
+ }
21
  }
freemius/assets/scss/admin/account.scss CHANGED
@@ -1,256 +1,256 @@
1
- @import "../start";
2
-
3
- #fs_account
4
- {
5
- .postbox,
6
- .widefat
7
- {
8
- max-width: 700px;
9
- }
10
-
11
- h3
12
- {
13
- font-size: 1.3em;
14
- padding: 12px 15px;
15
- margin: 0 0 12px 0;
16
- line-height: 1.4;
17
- border-bottom: 1px solid #F1F1F1;
18
-
19
- .dashicons {
20
- width: 26px;
21
- height: 26px;
22
- font-size: 1.3em;
23
- }
24
- }
25
-
26
- i.dashicons
27
- {
28
- font-size: 1.2em;
29
- height: 1.2em;
30
- width: 1.2em;
31
- }
32
-
33
- .button
34
- {
35
- i.dashicons
36
- {
37
- vertical-align: middle;
38
- }
39
- }
40
-
41
- .fs-header-actions
42
- {
43
- position: absolute;
44
- top: 17px;
45
- right: 15px;
46
- font-size: 0.9em;
47
-
48
- ul
49
- {
50
- margin: 0;
51
- }
52
-
53
- li
54
- {
55
- form
56
- {
57
- display: inline-block;
58
- }
59
-
60
- float: left;
61
- a
62
- {
63
- text-decoration: none;
64
- }
65
- }
66
- }
67
- }
68
-
69
- #fs_account_details .button-group {
70
- float: right;
71
- }
72
-
73
- .rtl #fs_account .fs-header-actions
74
- {
75
- left: 15px;
76
- right: auto;
77
- }
78
-
79
- .fs-key-value-table
80
- {
81
- width: 100%;
82
-
83
- form
84
- {
85
- display: inline-block;
86
- }
87
-
88
- tr
89
- {
90
- td:first-child
91
- {
92
- nobr
93
- {
94
- font-weight: bold;
95
- }
96
-
97
- text-align: right;
98
-
99
- form
100
- {
101
- display: block;
102
- }
103
- }
104
-
105
- td.fs-right
106
- {
107
- text-align: right;
108
- }
109
-
110
- &.fs-odd
111
- {
112
- background: #ebebeb;
113
- }
114
- }
115
-
116
- td, th
117
- {
118
- padding: 10px;
119
- }
120
-
121
- code {
122
- line-height: 28px;
123
- }
124
-
125
- var, code, input[type="text"]
126
- {
127
- color: #0073AA;
128
- font-size: 16px;
129
- background: none;
130
- }
131
-
132
- input[type="text"] {
133
- width: 100%;
134
- font-weight: bold;
135
- }
136
- }
137
-
138
- label.fs-tag
139
- {
140
- background: #ffba00;
141
- color: #fff;
142
- display: inline-block;
143
- border-radius: 3px;
144
- padding: 5px;
145
- font-size: 11px;
146
- line-height: 11px;
147
- vertical-align: baseline;
148
-
149
- &.fs-warn
150
- {
151
- background: #ffba00;
152
- }
153
- &.fs-success
154
- {
155
- background: #46b450;
156
- }
157
- &.fs-error
158
- {
159
- background: #dc3232;
160
- }
161
- }
162
-
163
- #fs_addons
164
- {
165
- h3
166
- {
167
- border: none;
168
- margin-bottom: 0;
169
- padding: 4px 5px;
170
- }
171
-
172
- td
173
- {
174
- vertical-align: middle;
175
- }
176
-
177
- thead {
178
- white-space: nowrap;
179
- }
180
-
181
- td:first-child,
182
- th:first-child
183
- {
184
- text-align: left;
185
- font-weight: bold;
186
- }
187
- td:last-child,
188
- th:last-child
189
- {
190
- text-align: right;
191
- }
192
- th
193
- {
194
- font-weight: bold;
195
- }
196
- }
197
-
198
- #fs_billing_address {
199
- width: 100%;
200
-
201
- tr {
202
- td {
203
- width: 50%;
204
- padding: 5px;
205
- }
206
-
207
- &:first-of-type {
208
- td {
209
- padding-top: 0;
210
- }
211
- }
212
- }
213
-
214
- @mixin read-mode {
215
- border-color: transparent;
216
- color: #777;
217
- border-bottom: 1px dashed #ccc;
218
- padding-left: 0;
219
- background: none;
220
- }
221
-
222
- span {
223
- font-weight: bold;
224
- }
225
-
226
- input, select {
227
- @include placeholder(transparent);
228
-
229
- display: block;
230
- width: 100%;
231
- margin-top: 5px;
232
-
233
- &.fs-read-mode {
234
- @include read-mode();
235
- }
236
- }
237
-
238
-
239
- &.fs-read-mode {
240
- td span {
241
- display: none;
242
- }
243
-
244
- input, select
245
- {
246
- @include read-mode();
247
- @include placeholder(#ccc);
248
- }
249
- }
250
-
251
-
252
- button {
253
- display: block;
254
- width: 100%;
255
- }
256
  }
1
+ @import "../start";
2
+
3
+ #fs_account
4
+ {
5
+ .postbox,
6
+ .widefat
7
+ {
8
+ max-width: 700px;
9
+ }
10
+
11
+ h3
12
+ {
13
+ font-size: 1.3em;
14
+ padding: 12px 15px;
15
+ margin: 0 0 12px 0;
16
+ line-height: 1.4;
17
+ border-bottom: 1px solid #F1F1F1;
18
+
19
+ .dashicons {
20
+ width: 26px;
21
+ height: 26px;
22
+ font-size: 1.3em;
23
+ }
24
+ }
25
+
26
+ i.dashicons
27
+ {
28
+ font-size: 1.2em;
29
+ height: 1.2em;
30
+ width: 1.2em;
31
+ }
32
+
33
+ .button
34
+ {
35
+ i.dashicons
36
+ {
37
+ vertical-align: middle;
38
+ }
39
+ }
40
+
41
+ .fs-header-actions
42
+ {
43
+ position: absolute;
44
+ top: 17px;
45
+ right: 15px;
46
+ font-size: 0.9em;
47
+
48
+ ul
49
+ {
50
+ margin: 0;
51
+ }
52
+
53
+ li
54
+ {
55
+ form
56
+ {
57
+ display: inline-block;
58
+ }
59
+
60
+ float: left;
61
+ a
62
+ {
63
+ text-decoration: none;
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ #fs_account_details .button-group {
70
+ float: right;
71
+ }
72
+
73
+ .rtl #fs_account .fs-header-actions
74
+ {
75
+ left: 15px;
76
+ right: auto;
77
+ }
78
+
79
+ .fs-key-value-table
80
+ {
81
+ width: 100%;
82
+
83
+ form
84
+ {
85
+ display: inline-block;
86
+ }
87
+
88
+ tr
89
+ {
90
+ td:first-child
91
+ {
92
+ nobr
93
+ {
94
+ font-weight: bold;
95
+ }
96
+
97
+ text-align: right;
98
+
99
+ form
100
+ {
101
+ display: block;
102
+ }
103
+ }
104
+
105
+ td.fs-right
106
+ {
107
+ text-align: right;
108
+ }
109
+
110
+ &.fs-odd
111
+ {
112
+ background: #ebebeb;
113
+ }
114
+ }
115
+
116
+ td, th
117
+ {
118
+ padding: 10px;
119
+ }
120
+
121
+ code {
122
+ line-height: 28px;
123
+ }
124
+
125
+ var, code, input[type="text"]
126
+ {
127
+ color: #0073AA;
128
+ font-size: 16px;
129
+ background: none;
130
+ }
131
+
132
+ input[type="text"] {
133
+ width: 100%;
134
+ font-weight: bold;
135
+ }
136
+ }
137
+
138
+ label.fs-tag
139
+ {
140
+ background: #ffba00;
141
+ color: #fff;
142
+ display: inline-block;
143
+ border-radius: 3px;
144
+ padding: 5px;
145
+ font-size: 11px;
146
+ line-height: 11px;
147
+ vertical-align: baseline;
148
+
149
+ &.fs-warn
150
+ {
151
+ background: #ffba00;
152
+ }
153
+ &.fs-success
154
+ {
155
+ background: #46b450;
156
+ }
157
+ &.fs-error
158
+ {
159
+ background: #dc3232;
160
+ }
161
+ }
162
+
163
+ #fs_addons
164
+ {
165
+ h3
166
+ {
167
+ border: none;
168
+ margin-bottom: 0;
169
+ padding: 4px 5px;
170
+ }
171
+
172
+ td
173
+ {
174
+ vertical-align: middle;
175
+ }
176
+
177
+ thead {
178
+ white-space: nowrap;
179
+ }
180
+
181
+ td:first-child,
182
+ th:first-child
183
+ {
184
+ text-align: left;
185
+ font-weight: bold;
186
+ }
187
+ td:last-child,
188
+ th:last-child
189
+ {
190
+ text-align: right;
191
+ }
192
+ th
193
+ {
194
+ font-weight: bold;
195
+ }
196
+ }
197
+
198
+ #fs_billing_address {
199
+ width: 100%;
200
+
201
+ tr {
202
+ td {
203
+ width: 50%;
204
+ padding: 5px;
205
+ }
206
+
207
+ &:first-of-type {
208
+ td {
209
+ padding-top: 0;
210
+ }
211
+ }
212
+ }
213
+
214
+ @mixin read-mode {
215
+ border-color: transparent;
216
+ color: #777;
217
+ border-bottom: 1px dashed #ccc;
218
+ padding-left: 0;
219
+ background: none;
220
+ }
221
+
222
+ span {
223
+ font-weight: bold;
224
+ }
225
+
226
+ input, select {
227
+ @include placeholder(transparent);
228
+
229
+ display: block;
230
+ width: 100%;
231
+ margin-top: 5px;
232
+
233
+ &.fs-read-mode {
234
+ @include read-mode();
235
+ }
236
+ }
237
+
238
+
239
+ &.fs-read-mode {
240
+ td span {
241
+ display: none;
242
+ }
243
+
244
+ input, select
245
+ {
246
+ @include read-mode();
247
+ @include placeholder(#ccc);
248
+ }
249
+ }
250
+
251
+
252
+ button {
253
+ display: block;
254
+ width: 100%;
255
+ }
256
  }
freemius/assets/scss/admin/add-ons.scss CHANGED
@@ -1,449 +1,449 @@
1
- @import "../start";
2
-
3
- #fs_addons
4
- {
5
- .fs-cards-list
6
- {
7
- list-style: none;
8
-
9
- .fs-card
10
- {
11
- float: left;
12
- // height: 185px; // With reviews/ratings
13
- height: 152px;
14
- width: 310px;
15
- padding: 0;
16
- margin: 0 0 30px 30px;
17
- font-size: 14px;
18
- list-style: none;
19
- border: 1px solid #ddd;
20
- cursor: pointer;
21
- position: relative;
22
-
23
- .fs-overlay
24
- {
25
- position: absolute;
26
- left: 0;
27
- right: 0;
28
- bottom: 0;
29
- top: 0;
30
- z-index: 9;
31
- }
32
-
33
- .fs-inner
34
- {
35
- background-color: #fff;
36
- overflow: hidden;
37
- height: 100%;
38
- position: relative;
39
-
40
- ul
41
- {
42
- @include transition(all, 0.15s);
43
- left: 0;
44
- right: 0;
45
- top: 0;
46
- position: absolute;
47
- }
48
-
49
- li
50
- {
51
- list-style: none;
52
- line-height: 18px;
53
- padding: 0 15px;
54
- width: 100%;
55
- display: block;
56
- @include box-sizing(border-box);
57
- }
58
-
59
- .fs-card-banner
60
- {
61
- padding: 0;
62
- margin: 0;
63
- line-height: 0;
64
- display: block;
65
- height: 100px;
66
- background-repeat: repeat-x;
67
- background-size: 100% 100%;
68
- @include transition(all, 0.15s);
69
- }
70
-
71
- .fs-title
72
- {
73
- margin: 10px 0 0 0;
74
- height: 18px;
75
- overflow: hidden;
76
- color: #000;
77
- white-space: nowrap;
78
- text-overflow: ellipsis;
79
- font-weight: bold;
80
- }
81
-
82
- .fs-offer
83
- {
84
- font-size: 0.9em;
85
- }
86
-
87
- .fs-description
88
- {
89
- background-color: #f9f9f9;
90
- padding: 10px 15px 100px 15px;
91
- border-top: 1px solid #eee;
92
- margin: 0 0 10px 0;
93
- color: #777;
94
- }
95
-
96
- .fs-tag
97
- {
98
- position: absolute;
99
- top: 10px;
100
- right: 0px;
101
- background: greenyellow;
102
- display: block;
103
- padding: 2px 10px;
104
- @include box-shadow(1px 1px 1px rgba(0,0,0,0.3));
105
- text-transform: uppercase;
106
- font-size: 0.9em;
107
- font-weight: bold;
108
- }
109
-
110
- .fs-cta
111
- {
112
- .button
113
- {
114
- position: absolute;
115
- top: 112px;
116
- right: 10px;
117
- }
118
- }
119
- }
120
-
121
- @media screen and (min-width: 960px) {
122
- &:hover
123
- {
124
- .fs-overlay
125
- {
126
- border: 2px solid $fms-link-color;
127
- margin-left: -1px;
128
- margin-top: -1px;
129
- }
130
-
131
- .fs-inner
132
- {
133
- ul
134
- {
135
- top: -100px;
136
- }
137
-
138
- .fs-card-banner
139
- {
140
- // background-position: 50% -100px;
141
- }
142
-
143
- .fs-title,
144
- .fs-offer
145
- {
146
- color: $fms-link-color;
147
- }
148
- }
149
- }
150
- }
151
- }
152
- }
153
- }
154
-
155
- #TB_window
156
- {
157
- &, iframe
158
- {
159
- width: 772px !important;
160
- }
161
- }
162
-
163
- #plugin-information
164
- {
165
- #section-description
166
- {
167
- h2, h3, p, b, i, blockquote, li, ul, ol
168
- {
169
- clear: none;
170
- }
171
-
172
- .fs-selling-points
173
- {
174
- padding-bottom: 10px;
175
- border-bottom: 1px solid #ddd;
176
-
177
- ul
178
- {
179
- margin: 0;
180
-
181
- li
182
- {
183
- padding: 0;
184
- list-style: none outside none;
185
-
186
- i.dashicons
187
- {
188
- color: $fs-logo-green-color;
189
- font-size: 3em;
190
- vertical-align: middle;
191
- line-height: 30px;
192
- float: left;
193
- margin: 0 0 0 -15px;
194
- }
195
-
196
- h3
197
- {
198
- margin: 1em 30px !important;
199
- }
200
- }
201
- }
202
- }
203
-
204
- .fs-screenshots
205
- {
206
- @include clearfix();
207
- ul
208
- {
209
- list-style: none;
210
- margin: 0;
211
-
212
- li
213
- {
214
- width: 225px;
215
- height: 225px;
216
- float: left;
217
- margin-bottom: 20px;
218
- @include box-sizing(content-box);
219
-
220
- a
221
- {
222
- display: block;
223
- width: 100%;
224
- height: 100%;
225
- border: 1px solid;
226
- @include box-shadow(1px 1px 1px rgba(0, 0, 0, 0.2));
227
- background-size: cover;
228
- }
229
-
230
- &.odd
231
- {
232
- margin-right: 20px;
233
- }
234
- }
235
- }
236
- }
237
- }
238
-
239
- .plugin-information-pricing
240
- {
241
- $pricing_color: #FFFEEC;
242
- $borders_color: #DDD;
243
- margin: -16px;
244
- // padding: 20px;
245
- border-bottom: 1px solid $borders_color;
246
-
247
- .fs-plan
248
- {
249
-
250
- h3
251
- {
252
- margin-top: 0;
253
- padding: 20px;
254
- font-size: 16px;
255
- }
256
-
257
- .nav-tab-wrapper
258
- {
259
- border-bottom: 1px solid $borders_color;
260
-
261
- .nav-tab
262
- {
263
- cursor: pointer;
264
- position: relative;
265
- padding: 0 10px;
266
- font-size: 0.9em;
267
-
268
- label
269
- {
270
- text-transform: uppercase;
271
- color: green;
272
- background: greenyellow;
273
- position: absolute;
274
- left: -1px;
275
- right: -1px;
276
- bottom: 100%;
277
- border: 1px solid darkgreen;
278
- padding: 2px;
279
- text-align: center;
280
- font-size: 0.9em;
281
- line-height: 1em;
282
- }
283
-
284
- &.nav-tab-active
285
- {
286
- cursor: default;
287
- background: $pricing_color;
288
- border-bottom-color: $pricing_color;
289
- }
290
- }
291
- }
292
-
293
- &.fs-single-cycle
294
- {
295
- h3
296
- {
297
- background: $pricing_color;
298
- margin: 0;
299
- padding-bottom: 0;
300
- color: #0073aa;
301
- }
302
-
303
- .nav-tab-wrapper,
304
- .fs-billing-frequency
305
- {
306
- display: none;
307
- }
308
- }
309
-
310
- .fs-pricing-body
311
- {
312
- background: $pricing_color;
313
- padding: 20px;
314
- }
315
-
316
- .button
317
- {
318
- width: 100%;
319
- text-align: center;
320
- font-weight: bold;
321
- text-transform: uppercase;
322
- font-size: 1.1em;
323
- }
324
-
325
- label
326
- {
327
- white-space: nowrap;
328
- }
329
-
330
- var {
331
- font-style: normal;
332
- }
333
-
334
- .fs-billing-frequency,
335
- .fs-annual-discount
336
- {
337
- text-align: center;
338
- display: block;
339
- font-weight: bold;
340
- margin-bottom: 10px;
341
- text-transform: uppercase;
342
- background: #F3F3F3;
343
- padding: 2px;
344
- border: 1px solid #ccc;
345
- }
346
-
347
- .fs-annual-discount
348
- {
349
- text-transform: none;
350
- color: green;
351
- background: greenyellow;
352
- }
353
-
354
- ul.fs-trial-terms
355
- {
356
- font-size: 0.9em;
357
-
358
- i
359
- {
360
- float: left;
361
- margin: 0 0 0 -15px;
362
- }
363
-
364
- li
365
- {
366
- margin: 10px 0 0 0;
367
- }
368
- }
369
- }
370
- }
371
-
372
- #section-features
373
- {
374
- .fs-features
375
- {
376
- margin: -20px -26px;
377
- }
378
-
379
- table
380
- {
381
- width: 100%;
382
- border-spacing: 0;
383
- border-collapse: separate;
384
-
385
- thead
386
- {
387
- th
388
- {
389
- padding: 10px 0;
390
- }
391
-
392
- .fs-price
393
- {
394
- color: $fs-logo-green-color;
395
- font-weight: normal;
396
- display: block;
397
- text-align: center;
398
- }
399
- }
400
-
401
- tbody
402
- {
403
- td
404
- {
405
- border-top: 1px solid #ccc;
406
- padding: 10px 0;
407
- text-align: center;
408
- width: 100px;
409
- color: $fs-logo-green-color;
410
-
411
- &:first-child
412
- {
413
- text-align: left;
414
- width: auto;
415
- color: inherit;
416
- padding-left: 26px;
417
- }
418
- }
419
- tr.fs-odd
420
- {
421
- td
422
- {
423
- background: #fefefe;
424
- }
425
- }
426
- }
427
- }
428
-
429
- .dashicons-yes
430
- {
431
- width: 30px;
432
- height: 30px;
433
- font-size: 30px;
434
- }
435
- }
436
- }
437
-
438
- @media screen and (max-width: 961px) {
439
- #fs_addons
440
- {
441
- .fs-cards-list
442
- {
443
- .fs-card
444
- {
445
- height: 265px;
446
- }
447
- }
448
- }
449
  }
1
+ @import "../start";
2
+
3
+ #fs_addons
4
+ {
5
+ .fs-cards-list
6
+ {
7
+ list-style: none;
8
+
9
+ .fs-card
10
+ {
11
+ float: left;
12
+ // height: 185px; // With reviews/ratings
13
+ height: 152px;
14
+ width: 310px;
15
+ padding: 0;
16
+ margin: 0 0 30px 30px;
17
+ font-size: 14px;
18
+ list-style: none;
19
+ border: 1px solid #ddd;
20
+ cursor: pointer;
21
+ position: relative;
22
+
23
+ .fs-overlay
24
+ {
25
+ position: absolute;
26
+ left: 0;
27
+ right: 0;
28
+ bottom: 0;
29
+ top: 0;
30
+ z-index: 9;
31
+ }
32
+
33
+ .fs-inner
34
+ {
35
+ background-color: #fff;
36
+ overflow: hidden;
37
+ height: 100%;
38
+ position: relative;
39
+
40
+ ul
41
+ {
42
+ @include transition(all, 0.15s);
43
+ left: 0;
44
+ right: 0;
45
+ top: 0;
46
+ position: absolute;
47
+ }
48
+
49
+ li
50
+ {
51
+ list-style: none;
52
+ line-height: 18px;
53
+ padding: 0 15px;
54
+ width: 100%;
55
+ display: block;
56
+ @include box-sizing(border-box);
57
+ }
58
+
59
+ .fs-card-banner
60
+ {
61
+ padding: 0;
62
+ margin: 0;
63
+ line-height: 0;
64
+ display: block;
65
+ height: 100px;
66
+ background-repeat: repeat-x;
67
+ background-size: 100% 100%;
68
+ @include transition(all, 0.15s);
69
+ }
70
+
71
+ .fs-title
72
+ {
73
+ margin: 10px 0 0 0;
74
+ height: 18px;
75
+ overflow: hidden;
76
+ color: #000;
77
+ white-space: nowrap;
78
+ text-overflow: ellipsis;
79
+ font-weight: bold;
80
+ }
81
+
82
+ .fs-offer
83
+ {
84
+ font-size: 0.9em;
85
+ }
86
+
87
+ .fs-description
88
+ {
89
+ background-color: #f9f9f9;
90
+ padding: 10px 15px 100px 15px;
91
+ border-top: 1px solid #eee;
92
+ margin: 0 0 10px 0;
93
+ color: #777;
94
+ }
95
+
96
+ .fs-tag
97
+ {
98
+ position: absolute;
99
+ top: 10px;
100
+ right: 0px;
101
+ background: greenyellow;
102
+ display: block;
103
+ padding: 2px 10px;
104
+ @include box-shadow(1px 1px 1px rgba(0,0,0,0.3));
105
+ text-transform: uppercase;
106
+ font-size: 0.9em;
107
+ font-weight: bold;
108
+ }
109
+
110
+ .fs-cta
111
+ {
112
+ .button
113
+ {
114
+ position: absolute;
115
+ top: 112px;
116
+ right: 10px;
117
+ }
118
+ }
119
+ }
120
+
121
+ @media screen and (min-width: 960px) {
122
+ &:hover
123
+ {
124
+ .fs-overlay
125
+ {
126
+ border: 2px solid $fms-link-color;
127
+ margin-left: -1px;
128
+ margin-top: -1px;
129
+ }
130
+
131
+ .fs-inner
132
+ {
133
+ ul
134
+ {
135
+ top: -100px;
136
+ }
137
+
138
+ .fs-card-banner
139
+ {
140
+ // background-position: 50% -100px;
141
+ }
142
+
143
+ .fs-title,
144
+ .fs-offer
145
+ {
146
+ color: $fms-link-color;
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ #TB_window
156
+ {
157
+ &, iframe
158
+ {
159
+ width: 772px !important;
160
+ }
161
+ }
162
+
163
+ #plugin-information
164
+ {
165
+ #section-description
166
+ {
167
+ h2, h3, p, b, i, blockquote, li, ul, ol
168
+ {
169
+ clear: none;
170
+ }
171
+
172
+ .fs-selling-points
173
+ {
174
+ padding-bottom: 10px;
175
+ border-bottom: 1px solid #ddd;
176
+
177
+ ul
178
+ {
179
+ margin: 0;
180
+
181
+ li
182
+ {
183
+ padding: 0;
184
+ list-style: none outside none;
185
+
186
+ i.dashicons
187
+ {
188
+ color: $fs-logo-green-color;
189
+ font-size: 3em;
190
+ vertical-align: middle;
191
+ line-height: 30px;
192
+ float: left;
193
+ margin: 0 0 0 -15px;
194
+ }
195
+
196
+ h3
197
+ {
198
+ margin: 1em 30px !important;
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ .fs-screenshots
205
+ {
206
+ @include clearfix();
207
+ ul
208
+ {
209
+ list-style: none;
210
+ margin: 0;
211
+
212
+ li
213
+ {
214
+ width: 225px;
215
+ height: 225px;
216
+ float: left;
217
+ margin-bottom: 20px;
218
+ @include box-sizing(content-box);
219
+
220
+ a
221
+ {
222
+ display: block;
223
+ width: 100%;
224
+ height: 100%;
225
+ border: 1px solid;
226
+ @include box-shadow(1px 1px 1px rgba(0, 0, 0, 0.2));
227
+ background-size: cover;
228
+ }
229
+
230
+ &.odd
231
+ {
232
+ margin-right: 20px;
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ .plugin-information-pricing
240
+ {
241
+ $pricing_color: #FFFEEC;
242
+ $borders_color: #DDD;
243
+ margin: -16px;
244
+ // padding: 20px;
245
+ border-bottom: 1px solid $borders_color;
246
+
247
+ .fs-plan
248
+ {
249
+
250
+ h3
251
+ {
252
+ margin-top: 0;
253
+ padding: 20px;
254
+ font-size: 16px;
255
+ }
256
+
257
+ .nav-tab-wrapper
258
+ {
259
+ border-bottom: 1px solid $borders_color;
260
+
261
+ .nav-tab
262
+ {
263
+ cursor: pointer;
264
+ position: relative;
265
+ padding: 0 10px;
266
+ font-size: 0.9em;
267
+
268
+ label
269
+ {
270
+ text-transform: uppercase;
271
+ color: green;
272
+ background: greenyellow;
273
+ position: absolute;
274
+ left: -1px;
275
+ right: -1px;
276
+ bottom: 100%;
277
+ border: 1px solid darkgreen;
278
+ padding: 2px;
279
+ text-align: center;
280
+ font-size: 0.9em;
281
+ line-height: 1em;
282
+ }
283
+
284
+ &.nav-tab-active
285
+ {
286
+ cursor: default;
287
+ background: $pricing_color;
288
+ border-bottom-color: $pricing_color;
289
+ }
290
+ }
291
+ }
292
+
293
+ &.fs-single-cycle
294
+ {
295
+ h3
296
+ {
297
+ background: $pricing_color;
298
+ margin: 0;
299
+ padding-bottom: 0;
300
+ color: #0073aa;
301
+ }
302
+
303
+ .nav-tab-wrapper,
304
+ .fs-billing-frequency
305
+ {
306
+ display: none;
307
+ }
308
+ }
309
+
310
+ .fs-pricing-body
311
+ {
312
+ background: $pricing_color;
313
+ padding: 20px;
314
+ }
315
+
316
+ .button
317
+ {
318
+ width: 100%;
319
+ text-align: center;
320
+ font-weight: bold;
321
+ text-transform: uppercase;
322
+ font-size: 1.1em;
323
+ }
324
+
325
+ label
326
+ {
327
+ white-space: nowrap;
328
+ }
329
+
330
+ var {
331
+ font-style: normal;
332
+ }
333
+
334
+ .fs-billing-frequency,
335
+ .fs-annual-discount
336
+ {
337
+ text-align: center;
338
+ display: block;
339
+ font-weight: bold;
340
+ margin-bottom: 10px;
341
+ text-transform: uppercase;
342
+ background: #F3F3F3;
343
+ padding: 2px;
344
+ border: 1px solid #ccc;
345
+ }
346
+
347
+ .fs-annual-discount
348
+ {
349
+ text-transform: none;
350
+ color: green;
351
+ background: greenyellow;
352
+ }
353
+
354
+ ul.fs-trial-terms
355
+ {
356
+ font-size: 0.9em;
357
+
358
+ i
359
+ {
360
+ float: left;
361
+ margin: 0 0 0 -15px;
362
+ }
363
+
364
+ li
365
+ {
366
+ margin: 10px 0 0 0;
367
+ }
368
+ }
369
+ }
370
+ }
371
+
372
+ #section-features
373
+ {
374
+ .fs-features
375
+ {
376
+ margin: -20px -26px;
377
+ }
378
+
379
+ table
380
+ {
381
+ width: 100%;
382
+ border-spacing: 0;
383
+ border-collapse: separate;
384
+
385
+ thead
386
+ {
387
+ th
388
+ {
389
+ padding: 10px 0;
390
+ }
391
+
392
+ .fs-price
393
+ {
394
+ color: $fs-logo-green-color;
395
+ font-weight: normal;
396
+ display: block;
397
+ text-align: center;
398
+ }
399
+ }
400
+
401
+ tbody
402
+ {
403
+ td
404
+ {
405
+ border-top: 1px solid #ccc;
406
+ padding: 10px 0;
407
+ text-align: center;
408
+ width: 100px;
409
+ color: $fs-logo-green-color;
410
+
411
+ &:first-child
412
+ {
413
+ text-align: left;
414
+ width: auto;
415
+ color: inherit;
416
+ padding-left: 26px;
417
+ }
418
+ }
419
+ tr.fs-odd
420
+ {
421
+ td
422
+ {
423
+ background: #fefefe;
424
+ }
425
+ }
426
+ }
427
+ }
428
+
429
+ .dashicons-yes
430
+ {
431
+ width: 30px;
432
+ height: 30px;
433
+ font-size: 30px;
434
+ }
435
+ }
436
+ }
437
+
438
+ @media screen and (max-width: 961px) {
439
+ #fs_addons
440
+ {
441
+ .fs-cards-list
442
+ {
443
+ .fs-card
444
+ {
445
+ height: 265px;
446
+ }
447
+ }
448
+ }
449
  }
freemius/assets/scss/admin/affiliation.scss CHANGED
@@ -1,97 +1,97 @@
1
- @import "../start";
2
-
3
- #fs_affiliation_content_wrapper {
4
- #messages {
5
- margin-top: 25px;
6
- }
7
-
8
- h3 {
9
- font-size: 24px;
10
- padding: 0;
11
- margin-left: 0;
12
- }
13
-
14
- ul {
15
- li {
16
- @include box-sizing(border-box);
17
- list-style-type: none;
18
-
19
- &:before {
20
- content: '✓';
21
- margin-right: 10px;
22
- font-weight: bold;
23
- }
24
- }
25
- }
26
-
27
- p:not(.description), li, label {
28
- font-size: 16px !important;
29
- line-height: 26px !important;
30
- }
31
-
32
- .button {
33
- margin-top: 20px;
34
- margin-bottom: 7px;
35
- line-height: 35px;
36
- height: 40px;
37
- font-size: 16px;
38
-
39
- &#cancel_button {
40
- margin-right: 5px;
41
- }
42
- }
43
-
44
- form {
45
- .input-container {
46
- .input-label {
47
- font-weight: bold;
48
- display: block;
49
- width: 100%;
50
- }
51
-
52
- &.input-container-text {
53
- label, input, textarea {
54
- display: block;
55
- }
56
- }
57
-
58
- margin-bottom: 15px;
59
-
60
- #add_domain, .remove-domain {
61
- text-decoration: none;
62
- display: inline-block;
63
- margin-top: 3px;
64
-
65
- &:focus {
66
- box-shadow: none;
67
- }
68
-
69
- &.disabled {
70
- color: #aaa;
71
- cursor: default;
72
- }
73
- }
74
- }
75
-
76
- #extra_domains_container {
77
- .description {
78
- margin-top: 0;
79
- position: relative;
80
- top: -4px;
81
- }
82
-
83
- .extra-domain-input-container {
84
- margin-bottom: 15px;
85
-
86
- .domain {
87
- display: inline-block;
88
- margin-right: 5px;
89
-
90
- &:last-of-type {
91
- margin-bottom: 0;
92
- }
93
- }
94
- }
95
- }
96
- }
97
  }
1
+ @import "../start";
2
+
3
+ #fs_affiliation_content_wrapper {
4
+ #messages {
5
+ margin-top: 25px;
6
+ }
7
+
8
+ h3 {
9
+ font-size: 24px;
10
+ padding: 0;
11
+ margin-left: 0;
12
+ }
13
+
14
+ ul {
15
+ li {
16
+ @include box-sizing(border-box);
17
+ list-style-type: none;
18
+
19
+ &:before {
20
+ content: '✓';
21
+ margin-right: 10px;
22
+ font-weight: bold;
23
+ }
24
+ }
25
+ }
26
+
27
+ p:not(.description), li, label {
28
+ font-size: 16px !important;
29
+ line-height: 26px !important;
30
+ }
31
+
32
+ .button {
33
+ margin-top: 20px;
34
+ margin-bottom: 7px;
35
+ line-height: 35px;
36
+ height: 40px;
37
+ font-size: 16px;
38
+
39
+ &#cancel_button {
40
+ margin-right: 5px;
41
+ }
42
+ }
43
+
44
+ form {
45
+ .input-container {
46
+ .input-label {
47
+ font-weight: bold;
48
+ display: block;
49
+ width: 100%;
50
+ }
51
+
52
+ &.input-container-text {
53
+ label, input, textarea {
54
+ display: block;
55
+ }
56
+ }
57
+
58
+ margin-bottom: 15px;
59
+
60
+ #add_domain, .remove-domain {
61
+ text-decoration: none;
62
+ display: inline-block;
63
+ margin-top: 3px;
64
+
65
+ &:focus {
66
+ box-shadow: none;
67
+ }
68
+
69
+ &.disabled {
70
+ color: #aaa;
71
+ cursor: default;
72
+ }
73
+ }
74
+ }
75
+
76
+ #extra_domains_container {
77
+ .description {
78
+ margin-top: 0;
79
+ position: relative;
80
+ top: -4px;
81
+ }
82
+
83
+ .extra-domain-input-container {
84
+ margin-bottom: 15px;
85
+
86
+ .domain {
87
+ display: inline-block;
88
+ margin-right: 5px;
89
+
90
+ &:last-of-type {
91
+ margin-bottom: 0;
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
  }
freemius/assets/scss/admin/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden.
3
+ // Hide file structure from users on unprotected servers.
freemius/assets/scss/customizer.scss CHANGED
@@ -1,125 +1,125 @@
1
- @import "start";
2
-
3
- #fs_customizer_upsell {
4
- .fs-customizer-plan {
5
- padding: 10px 20px 20px 20px;
6
- border-radius: 3px;
7
- background: #fff;
8
-
9
- h2 {
10
- position: relative;
11
- margin: 0;
12
- line-height: 2em;
13
- text-transform: uppercase;
14
-
15
- .button-link {
16
- top: -2px;
17
- }
18
- }
19
- }
20
-
21
- .fs-feature {
22
- position: relative;
23
- }
24
-
25
- .dashicons-yes {
26
- color: #0085ba;
27
- font-size: 2em;
28
- vertical-align: bottom;
29
- margin-left: -7px;
30
- margin-right: 10px;
31
-
32
- .rtl & {
33
- margin-left: 10px;
34
- margin-right: -7px;
35
- }
36
- }
37
-
38
- .dashicons-editor-help
39
- {
40
- color: #bbb;
41
- cursor: help;
42
-
43
- $tooltip-color: #000;
44
-
45
- .fs-feature-desc {
46
- opacity: 0;
47
- visibility: hidden;
48
- @include transition(opacity 0.3s ease-in-out);
49
-
50
- position: absolute;
51
- background: $tooltip-color;
52
- color: #fff;
53
- font-family: 'arial', serif;
54
- font-size: 12px;
55
- padding: 10px;
56
- z-index: 999999;
57
- bottom: 100%;
58
- margin-bottom: 5px;
59
- left: 0;
60
- right: 0;
61
- @include border-radius(5px);
62
- @include box-shadow(1px 1px 1px rgba(0,0,0,0.2));
63
- line-height: 1.3em;
64
- font-weight: bold;
65
- text-align: left;
66
-
67
- .rtl &
68
- {
69
- text-align: right;
70
- }
71
-
72
- &::after {
73
- content: ' ';
74
- display: block;
75
- width: 0;
76
- height: 0;
77
- border-style: solid;
78
- border-width: 5px 5px 0 5px;
79
- border-color: $tooltip-color transparent transparent transparent;
80
- position: absolute;
81
- top: 100%;
82
- left: 21px;
83
-
84
- .rtl & {
85
- right: 21px;
86
- left: auto;
87
- }
88
- }
89
- }
90
-
91
- &:hover {
92
- .fs-feature-desc {
93
- visibility: visible;
94
- opacity: 1;
95
- }
96
- }
97
- }
98
-
99
- .button-primary {
100
- display: block;
101
- text-align: center;
102
- margin-top: 10px;
103
- }
104
- }
105
-
106
- #fs_customizer_support
107
- {
108
- display: block !important;
109
-
110
- .button {
111
- float: right;
112
- }
113
-
114
- .button-group {
115
- width: 100%;
116
- display: block;
117
- margin-top: 10px;
118
-
119
- .button {
120
- float: none;
121
- width: 50%;
122
- text-align: center;
123
- }
124
- }
125
  }
1
+ @import "start";
2
+
3
+ #fs_customizer_upsell {
4
+ .fs-customizer-plan {
5
+ padding: 10px 20px 20px 20px;
6
+ border-radius: 3px;
7
+ background: #fff;
8
+
9
+ h2 {
10
+ position: relative;
11
+ margin: 0;
12
+ line-height: 2em;
13
+ text-transform: uppercase;
14
+
15
+ .button-link {
16
+ top: -2px;
17
+ }
18
+ }
19
+ }
20
+
21
+ .fs-feature {
22
+ position: relative;
23
+ }
24
+
25
+ .dashicons-yes {
26
+ color: #0085ba;
27
+ font-size: 2em;
28
+ vertical-align: bottom;
29
+ margin-left: -7px;
30
+ margin-right: 10px;
31
+
32
+ .rtl & {
33
+ margin-left: 10px;
34
+ margin-right: -7px;
35
+ }
36
+ }
37
+
38
+ .dashicons-editor-help
39
+ {
40
+ color: #bbb;
41
+ cursor: help;
42
+
43
+ $tooltip-color: #000;
44
+
45
+ .fs-feature-desc {
46
+ opacity: 0;
47
+ visibility: hidden;
48
+ @include transition(opacity 0.3s ease-in-out);
49
+
50
+ position: absolute;
51
+ background: $tooltip-color;
52
+ color: #fff;
53
+ font-family: 'arial', serif;
54
+ font-size: 12px;
55
+ padding: 10px;
56
+ z-index: 999999;
57
+ bottom: 100%;
58
+ margin-bottom: 5px;
59
+ left: 0;
60
+ right: 0;
61
+ @include border-radius(5px);
62
+ @include box-shadow(1px 1px 1px rgba(0,0,0,0.2));
63
+ line-height: 1.3em;
64
+ font-weight: bold;
65
+ text-align: left;
66
+
67
+ .rtl &
68
+ {
69
+ text-align: right;
70
+ }
71
+
72
+ &::after {
73
+ content: ' ';
74
+ display: block;
75
+ width: 0;
76
+ height: 0;
77
+ border-style: solid;
78
+ border-width: 5px 5px 0 5px;
79
+ border-color: $tooltip-color transparent transparent transparent;
80
+ position: absolute;
81
+ top: 100%;
82
+ left: 21px;
83
+
84
+ .rtl & {
85
+ right: 21px;
86
+ left: auto;
87
+ }
88
+ }
89
+ }
90
+
91
+ &:hover {
92
+ .fs-feature-desc {
93
+ visibility: visible;
94
+ opacity: 1;
95
+ }
96
+ }
97
+ }
98
+
99
+ .button-primary {
100
+ display: block;
101
+ text-align: center;
102
+ margin-top: 10px;
103
+ }
104
+ }
105
+
106
+ #fs_customizer_support
107
+ {
108
+ display: block !important;
109
+
110
+ .button {
111
+ float: right;
112
+ }
113
+
114
+ .button-group {
115
+ width: 100%;
116
+ display: block;
117
+ margin-top: 10px;
118
+
119
+ .button {
120
+ float: none;
121
+ width: 50%;
122
+ text-align: center;
123
+ }
124
+ }
125
  }
freemius/assets/scss/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden.
3
+ // Hide file structure from users on unprotected servers.
freemius/composer.json CHANGED
@@ -1,10 +1,10 @@
1
- {
2
- "name": "freemius/wordpress-sdk",
3
- "description": "Freemius WordPress SDK",
4
- "keywords": ["freemius", "wordpress", "plugin", "sdk"],
5
- "homepage": "https://freemius.com",
6
- "license": "GPL-2.0+",
7
- "require": {
8
- "php": ">=5.2"
9
- }
10
- }
1
+ {
2
+ "name": "freemius/wordpress-sdk",
3
+ "description": "Freemius WordPress SDK",
4
+ "keywords": ["freemius", "wordpress", "plugin", "sdk"],
5
+ "homepage": "https://freemius.com",
6
+ "license": "GPL-3.0",
7
+ "require": {
8
+ "php": ">=5.2"
9
+ }
10
+ }
freemius/config.php CHANGED
@@ -285,6 +285,9 @@
285
  if ( ! defined( 'WP_FS__TIME_24_HOURS_IN_SEC' ) ) {
286
  define( 'WP_FS__TIME_24_HOURS_IN_SEC', 86400 );
287
  }
 
 
 
288
 
289
  #--------------------------------------------------------------------------------
290
  #region Debugging
285
  if ( ! defined( 'WP_FS__TIME_24_HOURS_IN_SEC' ) ) {
286
  define( 'WP_FS__TIME_24_HOURS_IN_SEC', 86400 );
287
  }
288
+ if ( ! defined( 'WP_FS__TIME_WEEK_IN_SEC' ) ) {
289
+ define( 'WP_FS__TIME_WEEK_IN_SEC', 7 * WP_FS__TIME_24_HOURS_IN_SEC );
290
+ }
291
 
292
  #--------------------------------------------------------------------------------
293
  #region Debugging
freemius/gulpfile.js CHANGED
@@ -10,7 +10,7 @@ var clean = require('gulp-clean');
10
 
11
  var languagesFolder = './languages/';
12
 
13
- var options = require('./transifex-config.json')
14
 
15
  function getFolders(dir) {
16
  return filesystem.readdirSync(dir)
@@ -23,7 +23,7 @@ var transifex = require('gulp-transifex').createClient(options);
23
 
24
  // Create POT out of i18n.php.
25
  gulp.task('prepare-source', function () {
26
- gulp.src('includes/i18n.php')
27
  .pipe(sort())
28
  .pipe(wpPot({
29
  destFile : 'freemius.pot',
@@ -31,21 +31,49 @@ gulp.task('prepare-source', function () {
31
  bugReport : 'https://github.com/Freemius/wordpress-sdk/issues',
32
  lastTranslator : 'Vova Feldman <vova@freemius.com>',
33
  team : 'Freemius Team <admin@freemius.com>',
 
 
 
 
 
 
34
  gettextFunctions: [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  {name: '_fs_text'},
 
36
  {name: '_fs_echo'},
37
  {name: '_fs_esc_attr'},
38
  {name: '_fs_esc_attr_echo'},
39
  {name: '_fs_esc_html'},
40
  {name: '_fs_esc_html_echo'},
41
- {name: '_fs_x', context: 2},
42
  {name: '_fs_ex', context: 2},
43
  {name: '_fs_esc_attr_x', context: 2},
44
  {name: '_fs_esc_html_x', context: 2},
 
45
  {name: '_fs_n', plural: 2},
46
  {name: '_fs_n_noop', plural: 2},
47
  {name: '_fs_nx', plural: 2, context: 4},
48
- {name: '_fs_nx_noop', plural: 2, context: 3}
49
  ]
50
  }))
51
  .pipe(gulp.dest(languagesFolder + 'freemius.pot'));
10
 
11
  var languagesFolder = './languages/';
12
 
13
+ var options = require('./transifex-config.json');
14
 
15
  function getFolders(dir) {
16
  return filesystem.readdirSync(dir)
23
 
24
  // Create POT out of i18n.php.
25
  gulp.task('prepare-source', function () {
26
+ gulp.src('**/*.php')
27
  .pipe(sort())
28
  .pipe(wpPot({
29
  destFile : 'freemius.pot',
31
  bugReport : 'https://github.com/Freemius/wordpress-sdk/issues',
32
  lastTranslator : 'Vova Feldman <vova@freemius.com>',
33
  team : 'Freemius Team <admin@freemius.com>',
34
+ /*gettextMethods: {
35
+ instances: ['this', '_fs'],
36
+ methods: [
37
+ 'get_text_inline'
38
+ ]
39
+ },*/
40
  gettextFunctions: [
41
+ {name: 'get_text_inline'},
42
+
43
+ {name: 'fs_text_inline'},
44
+ {name: 'fs_echo_inline'},
45
+ {name: 'fs_esc_js_inline'},
46
+ {name: 'fs_esc_attr_inline'},
47
+ {name: 'fs_esc_attr_echo_inline'},
48
+ {name: 'fs_esc_html_inline'},
49
+ {name: 'fs_esc_html_echo_inline'},
50
+
51
+ {name: 'get_text_x_inline', context: 2},
52
+ {name: 'fs_text_x_inline', context: 2},
53
+ {name: 'fs_echo_x_inline', context: 2},
54
+ {name: 'fs_esc_attr_x_inline', context: 2},
55
+ {name: 'fs_esc_js_x_inline', context: 2},
56
+ {name: 'fs_esc_js_echo_x_inline', context: 2},
57
+ {name: 'fs_esc_html_x_inline', context: 2},
58
+ {name: 'fs_esc_html_echo_x_inline', context: 2}
59
+ /*,
60
+
61
+
62
  {name: '_fs_text'},
63
+ {name: '_fs_x', context: 2},
64
  {name: '_fs_echo'},
65
  {name: '_fs_esc_attr'},
66
  {name: '_fs_esc_attr_echo'},
67
  {name: '_fs_esc_html'},
68
  {name: '_fs_esc_html_echo'},
 
69
  {name: '_fs_ex', context: 2},
70
  {name: '_fs_esc_attr_x', context: 2},
71
  {name: '_fs_esc_html_x', context: 2},
72
+
73
  {name: '_fs_n', plural: 2},
74
  {name: '_fs_n_noop', plural: 2},
75
  {name: '_fs_nx', plural: 2, context: 4},
76
+ {name: '_fs_nx_noop', plural: 2, context: 3}*/
77
  ]
78
  }))
79
  .pipe(gulp.dest(languagesFolder + 'freemius.pot'));
freemius/includes/class-freemius-abstract.php CHANGED
@@ -1,597 +1,597 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.7
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
-
14
- /**
15
- * - Each instance of Freemius class represents a single plugin
16
- * install by a single user (the installer of the plugin).
17
- *
18
- * - Each website can only have one install of the same plugin.
19
- *
20
- * - Install entity is only created after a user connects his account with Freemius.
21
- *
22
- * Class Freemius_Abstract
23
- */
24
- abstract class Freemius_Abstract {
25
-
26
- #----------------------------------------------------------------------------------
27
- #region Identity
28
- #----------------------------------------------------------------------------------
29
-
30
- /**
31
- * Check if user has connected his account (opted-in).
32
- *
33
- * Note:
34
- * If the user opted-in and opted-out on a later stage,
35
- * this will still return true. If you want to check if the
36
- * user is currently opted-in, use:
37
- * `$fs->is_registered() && $fs->is_tracking_allowed()`
38
- *
39
- * @since 1.0.1
40
- * @return bool
41
- */
42
- abstract function is_registered();
43
-
44
- /**
45
- * Check if the user skipped connecting the account with Freemius.
46
- *
47
- * @since 1.0.7
48
- *
49
- * @return bool
50
- */
51
- abstract function is_anonymous();
52
-
53
- /**
54
- * Check if the user currently in activation mode.
55
- *
56
- * @since 1.0.7
57
- *
58
- * @return bool
59
- */
60
- abstract function is_activation_mode();
61
-
62
- #endregion
63
-
64
- #----------------------------------------------------------------------------------
65
- #region Usage Tracking
66
- #----------------------------------------------------------------------------------
67
-
68
- /**
69
- * Returns TRUE if the user opted-in and didn't disconnect (opt-out).
70
- *
71
- * @author Leo Fajardo (@leorw)
72
- * @since 1.2.1.5
73
- *
74
- * @return bool
75
- */
76
- abstract function is_tracking_allowed();
77
-
78
- /**
79
- * Returns TRUE if the user never opted-in or manually opted-out.
80
- *
81
- * @author Vova Feldman (@svovaf)
82
- * @since 1.2.1.5
83
- *
84
- * @return bool
85
- */
86
- function is_tracking_prohibited() {
87
- return ! $this->is_registered() || ! $this->is_tracking_allowed();
88
- }
89
-
90
- /**
91
- * Opt-out from usage tracking.
92
- *
93
- * Note: This will not delete the account information but will stop all tracking.
94
- *
95
- * Returns:
96
- * 1. FALSE - If the user never opted-in.
97
- * 2. TRUE - If successfully opted-out.
98
- * 3. object - API Result on failure.
99
- *
100
- * @author Leo Fajardo (@leorw)
101
- * @since 1.2.1.5
102
- *
103
- * @return bool|object
104
- */
105
- abstract function stop_tracking();
106
-
107
- /**
108
- * Opt-in back into usage tracking.
109
- *
110
- * Note: This will only work if the user opted-in previously.
111
- *
112
- * Returns:
113
- * 1. FALSE - If the user never opted-in.
114
- * 2. TRUE - If successfully opted-in back to usage tracking.
115
- * 3. object - API result on failure.
116
- *
117
- * @author Leo Fajardo (@leorw)
118
- * @since 1.2.1.5
119
- *
120
- * @return bool|object
121
- */
122
- abstract function allow_tracking();
123
-
124
- #endregion
125
-
126
- #----------------------------------------------------------------------------------
127
- #region Module Type
128
- #----------------------------------------------------------------------------------
129
-
130
- /**
131
- * Checks if the plugin's type is "plugin". The other type is "theme".
132
- *
133
- * @author Leo Fajardo (@leorw)
134
- * @since 1.2.2
135
- *
136
- * @return bool
137
- */
138
- abstract function is_plugin();
139
-
140
- /**
141
- * Checks if the module type is "theme". The other type is "plugin".
142
- *
143
- * @author Leo Fajardo (@leorw)
144
- * @since 1.2.2
145
- *
146
- * @return bool
147
- */
148
- function is_theme() {
149
- return ( ! $this->is_plugin() );
150
- }
151
-
152
- #endregion
153
-
154
- #----------------------------------------------------------------------------------
155
- #region Permissions
156
- #----------------------------------------------------------------------------------
157
-
158
- /**
159
- * Check if plugin must be WordPress.org compliant.
160
- *
161
- * @since 1.0.7
162
- *
163
- * @return bool
164
- */
165
- abstract function is_org_repo_compliant();
166
-
167
- /**
168
- * Check if plugin is allowed to install executable files.
169
- *
170
- * @author Vova Feldman (@svovaf)
171
- * @since 1.0.5
172
- *
173
- * @return bool
174
- */
175
- function is_allowed_to_install() {
176
- return ( $this->is_premium() || ! $this->is_org_repo_compliant() );
177
- }
178
-
179
- #endregion
180
-
181
- /**
182
- * Check if user in trial or in free plan (not paying).
183
- *
184
- * @author Vova Feldman (@svovaf)
185
- * @since 1.0.4
186
- *
187
- * @return bool
188
- */
189
- function is_not_paying() {
190
- return ( $this->is_trial() || $this->is_free_plan() );
191
- }
192
-
193
- /**
194
- * Check if the user has an activated and valid paid license on current plugin's install.
195
- *
196
- * @since 1.0.9
197
- *
198
- * @return bool
199
- */
200
- abstract function is_paying();
201
-
202
- /**
203
- * Check if the user is paying or in trial.
204
- *
205
- * @since 1.0.9
206
- *
207
- * @return bool
208
- */
209
- function is_paying_or_trial() {
210
- return ( $this->is_paying() || $this->is_trial() );
211
- }
212
-
213
- /**
214
- * Check if user in a trial or have feature enabled license.
215
- *
216
- * @author Vova Feldman (@svovaf)
217
- * @since 1.1.7
218
- *
219
- * @return bool
220
- */
221
- abstract function can_use_premium_code();
222
-
223
- #----------------------------------------------------------------------------------
224
- #region Premium Only
225
- #----------------------------------------------------------------------------------
226
-
227
- /**
228
- * All logic wrapped in methods with "__premium_only()" suffix will be only
229
- * included in the premium code.
230
- *
231
- * Example:
232
- * if ( freemius()->is__premium_only() ) {
233
- * ...
234
- * }
235
- */
236
-
237
- /**
238
- * Returns true when running premium plugin code.
239
- *
240
- * @since 1.0.9
241
- *
242
- * @return bool
243
- */
244
- function is__premium_only() {
245
- return $this->is_premium();
246
- }
247
-
248
- /**
249
- * Check if the user has an activated and valid paid license on current plugin's install.
250
- *
251
- * @since 1.0.9
252
- *
253
- * @return bool
254
- *
255
- */
256
- function is_paying__premium_only() {
257
- return ( $this->is__premium_only() && $this->is_paying() );
258
- }
259
-
260
- /**
261
- * All code wrapped in this statement will be only included in the premium code.
262
- *
263
- * @since 1.0.9
264
- *
265
- * @param string $plan Plan name.
266
- * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans.
267
- *
268
- * @return bool
269
- */
270
- function is_plan__premium_only( $plan, $exact = false ) {
271
- return ( $this->is_premium() && $this->is_plan( $plan, $exact ) );
272
- }
273
-
274
- /**
275
- * Check if plan matches active license' plan or active trial license' plan.
276
- *
277
- * All code wrapped in this statement will be only included in the premium code.
278
- *
279
- * @since 1.0.9
280
- *
281
- * @param string $plan Plan name.
282
- * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans.
283
- *
284
- * @return bool
285
- */
286
- function is_plan_or_trial__premium_only( $plan, $exact = false ) {
287
- return ( $this->is_premium() && $this->is_plan_or_trial( $plan, $exact ) );
288
- }
289
-
290
- /**
291
- * Check if the user is paying or in trial.
292
- *
293
- * All code wrapped in this statement will be only included in the premium code.
294
- *
295
- * @since 1.0.9
296
- *
297
- * @return bool
298
- */
299
- function is_paying_or_trial__premium_only() {
300
- return $this->is_premium() && $this->is_paying_or_trial();
301
- }
302
-
303
- /**
304
- * Check if the user has an activated and valid paid license on current plugin's install.
305
- *
306
- * @since 1.0.4
307
- *
308
- * @return bool
309
- *
310
- * @deprecated Method name is confusing since it's not clear from the name the code will be removed.
311
- * @using Alias to is_paying__premium_only()
312
- */
313
- function is_paying__fs__() {
314
- return $this->is_paying__premium_only();
315
- }
316
-
317
- /**
318
- * Check if user in a trial or have feature enabled license.
319
- *
320
- * All code wrapped in this statement will be only included in the premium code.
321
- *
322
- * @author Vova Feldman (@svovaf)
323
- * @since 1.1.9
324
- *
325
- * @return bool
326
- */
327
- function can_use_premium_code__premium_only() {
328
- return $this->is_premium() && $this->can_use_premium_code();
329
- }
330
-
331
- #endregion
332
-
333
- #----------------------------------------------------------------------------------
334
- #region Trial
335
- #----------------------------------------------------------------------------------
336
-
337
- /**
338
- * Check if the user in a trial.
339
- *
340
- * @since 1.0.3
341
- *
342
- * @return bool
343
- */
344
- abstract function is_trial();
345
-
346
- /**
347
- * Check if trial already utilized.
348
- *
349
- * @since 1.0.9
350
- *
351
- * @return bool
352
- */
353
- abstract function is_trial_utilized();
354
-
355
- #endregion
356
-
357
- #----------------------------------------------------------------------------------
358
- #region Plans
359
- #----------------------------------------------------------------------------------
360
-
361
- /**
362
- * Check if plugin using the free plan.
363
- *
364
- * @since 1.0.4
365
- *
366
- * @return bool
367
- */
368
- abstract function is_free_plan();
369
-
370
- /**
371
- * @since 1.0.2
372
- *
373
- * @param string $plan Plan name.
374
- * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans.
375
- *
376
- * @return bool
377
- */
378
- abstract function is_plan( $plan, $exact = false );
379
-
380
- /**
381
- * Check if plan based on trial. If not in trial mode, should return false.
382
- *
383
- * @since 1.0.9
384
- *
385
- * @param string $plan Plan name.
386
- * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans.
387
- *
388
- * @return bool
389
- */
390
- abstract function is_trial_plan( $plan, $exact = false );
391
-
392
- /**
393
- * Check if plan matches active license' plan or active trial license' plan.
394
- *
395
- * @since 1.0.9
396
- *
397
- * @param string $plan Plan name.
398
- * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans.
399
- *
400
- * @return bool
401
- */
402
- function is_plan_or_trial( $plan, $exact = false ) {
403
- return $this->is_plan( $plan, $exact ) ||
404
- $this->is_trial_plan( $plan, $exact );
405
- }
406
-
407
- /**
408
- * Check if plugin has any paid plans.
409
- *
410
- * @author Vova Feldman (@svovaf)
411
- * @since 1.0.7
412
- *
413
- * @return bool
414
- */
415
- abstract function has_paid_plan();
416
-
417
- /**
418
- * Check if plugin has any free plan, or is it premium only.
419
- *
420
- * Note: If no plans configured, assume plugin is free.
421
- *
422
- * @author Vova Feldman (@svovaf)
423
- * @since 1.0.7
424
- *
425
- * @return bool
426
- */
427
- abstract function has_free_plan();
428
-
429
- /**
430
- * Check if plugin is premium only (no free plans).
431
- *
432
- * NOTE: is__premium_only() is very different method, don't get confused.
433
- *
434
- * @author Vova Feldman (@svovaf)
435
- * @since 1.1.9
436
- *
437
- * @return bool
438
- */
439
- abstract function is_only_premium();
440
-
441
- /**
442
- * Check if module has a premium code version.
443
- *
444
- * Serviceware module might be freemium without any
445
- * premium code version, where the paid features
446
- * are all part of the service.
447
- *
448
- * @author Vova Feldman (@svovaf)
449
- * @since 1.2.1.6
450
- *
451
- * @return bool
452
- */
453
- abstract function has_premium_version();
454
-
455
- /**
456
- * Check if module has any release on Freemius,
457
- * or all plugin's code is on WordPress.org (Serviceware).
458
- *
459
- * @return bool
460
- */
461
- function has_release_on_freemius() {
462
- return ! $this->is_org_repo_compliant() ||
463
- $this->has_premium_version();
464
- }
465
-
466
- /**
467
- * Checks if it's a freemium plugin.
468
- *
469
- * @author Vova Feldman (@svovaf)
470
- * @since 1.1.9
471
- *
472
- * @return bool
473
- */
474
- function is_freemium() {
475
- return $this->has_paid_plan() &&
476
- $this->has_free_plan();
477
- }
478
-
479
- /**
480
- * Check if module has only one plan.
481
- *
482
- * @author Vova Feldman (@svovaf)
483
- * @since 1.2.1.7
484
- *
485
- * @return bool
486
- */
487
- abstract function is_single_plan();
488
-
489
- #endregion
490
-
491
- /**
492
- * Check if running payments in sandbox mode.
493
- *
494
- * @since 1.0.4
495
- *
496
- * @return bool
497
- */
498
- abstract function is_payments_sandbox();
499
-
500
- /**
501
- * Check if running test vs. live plugin.
502
- *
503
- * @since 1.0.5
504
- *
505
- * @return bool
506
- */
507
- abstract function is_live();
508
-
509
- /**
510
- * Check if running premium plugin code.
511
- *
512
- * @since 1.0.5
513
- *
514
- * @return bool
515
- */
516
- abstract function is_premium();
517
-
518
- /**
519
- * Get upgrade URL.
520
- *
521
- * @author Vova Feldman (@svovaf)
522
- * @since 1.0.2
523
- *
524
- * @param string $period Billing cycle.
525
- *
526
- * @return string
527
- */
528
- abstract function get_upgrade_url( $period = WP_FS__PERIOD_ANNUALLY );
529
-
530
- /**
531
- * Check if Freemius was first added in a plugin update.
532
- *
533
- * @author Vova Feldman (@svovaf)
534
- * @since 1.1.5
535
- *
536
- * @return bool
537
- */
538
- function is_plugin_update() {
539
- return ! $this->is_plugin_new_install();
540
- }
541
-
542
- /**
543
- * Check if Freemius was part of the plugin when the user installed it first.
544
- *
545
- * @author Vova Feldman (@svovaf)
546
- * @since 1.1.5
547
- *
548
- * @return bool
549
- */
550
- abstract function is_plugin_new_install();
551
-
552
- #----------------------------------------------------------------------------------
553
- #region Marketing
554
- #----------------------------------------------------------------------------------
555
-
556
- /**
557
- * Check if current user purchased any other plugins before.
558
- *
559
- * @author Vova Feldman (@svovaf)
560
- * @since 1.0.9
561
- *
562
- * @return bool
563
- */
564
- abstract function has_purchased_before();
565
-
566
- /**
567
- * Check if current user classified as an agency.
568
- *
569
- * @author Vova Feldman (@svovaf)
570
- * @since 1.0.9
571
- *
572
- * @return bool
573
- */
574
- abstract function is_agency();
575
-
576
- /**
577
- * Check if current user classified as a developer.
578
- *
579
- * @author Vova Feldman (@svovaf)
580
- * @since 1.0.9
581
- *
582
- * @return bool
583
- */
584
- abstract function is_developer();
585
-
586
- /**
587
- * Check if current user classified as a business.
588
- *
589
- * @author Vova Feldman (@svovaf)
590
- * @since 1.0.9
591
- *
592
- * @return bool
593
- */
594
- abstract function is_business();
595
-
596
- #endregion
597
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.7
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+
14
+ /**
15
+ * - Each instance of Freemius class represents a single plugin
16
+ * install by a single user (the installer of the plugin).
17
+ *
18
+ * - Each website can only have one install of the same plugin.
19
+ *
20
+ * - Install entity is only created after a user connects his account with Freemius.
21
+ *
22
+ * Class Freemius_Abstract
23
+ */
24
+ abstract class Freemius_Abstract {
25
+
26
+ #----------------------------------------------------------------------------------
27
+ #region Identity
28
+ #----------------------------------------------------------------------------------
29
+
30
+ /**
31
+ * Check if user has connected his account (opted-in).
32
+ *
33
+ * Note:
34
+ * If the user opted-in and opted-out on a later stage,
35
+ * this will still return true. If you want to check if the
36
+ * user is currently opted-in, use:
37
+ * `$fs->is_registered() && $fs->is_tracking_allowed()`
38
+ *
39
+ * @since 1.0.1
40
+ * @return bool
41
+ */
42
+ abstract function is_registered();
43
+
44
+ /**
45
+ * Check if the user skipped connecting the account with Freemius.
46
+ *
47
+ * @since 1.0.7
48
+ *
49
+ * @return bool
50
+ */
51
+ abstract function is_anonymous();
52
+
53
+ /**
54
+ * Check if the user currently in activation mode.
55
+ *
56
+ * @since 1.0.7
57
+ *
58
+ * @return bool
59
+ */
60
+ abstract function is_activation_mode();
61
+
62
+ #endregion
63
+
64
+ #----------------------------------------------------------------------------------
65
+ #region Usage Tracking
66
+ #----------------------------------------------------------------------------------
67
+
68
+ /**
69
+ * Returns TRUE if the user opted-in and didn't disconnect (opt-out).
70
+ *
71
+ * @author Leo Fajardo (@leorw)
72
+ * @since 1.2.1.5
73
+ *
74
+ * @return bool
75
+ */
76
+ abstract function is_tracking_allowed();
77
+
78
+ /**
79
+ * Returns TRUE if the user never opted-in or manually opted-out.
80
+ *
81
+ * @author Vova Feldman (@svovaf)
82
+ * @since 1.2.1.5
83
+ *
84
+ * @return bool
85
+ */
86
+ function is_tracking_prohibited() {
87
+ return ! $this->is_registered() || ! $this->is_tracking_allowed();
88
+ }
89
+
90
+ /**
91
+ * Opt-out from usage tracking.
92
+ *
93
+ * Note: This will not delete the account information but will stop all tracking.
94
+ *
95
+ * Returns:
96
+ * 1. FALSE - If the user never opted-in.
97
+ * 2. TRUE - If successfully opted-out.
98
+ * 3. object - API Result on failure.
99
+ *
100
+ * @author Leo Fajardo (@leorw)
101
+ * @since 1.2.1.5
102
+ *
103
+ * @return bool|object
104
+ */
105
+ abstract function stop_tracking();
106
+
107
+ /**
108
+ * Opt-in back into usage tracking.
109
+ *
110
+ * Note: This will only work if the user opted-in previously.
111
+ *
112
+ * Returns:
113
+ * 1. FALSE - If the user never opted-in.
114
+ * 2. TRUE - If successfully opted-in back to usage tracking.
115
+ * 3. object - API result on failure.
116
+ *
117
+ * @author Leo Fajardo (@leorw)
118
+ * @since 1.2.1.5
119
+ *
120
+ * @return bool|object
121
+ */
122
+ abstract function allow_tracking();
123
+
124
+ #endregion
125
+
126
+ #----------------------------------------------------------------------------------
127
+ #region Module Type
128
+ #----------------------------------------------------------------------------------
129
+
130
+ /**
131
+ * Checks if the plugin's type is "plugin". The other type is "theme".
132
+ *
133
+ * @author Leo Fajardo (@leorw)
134
+ * @since 1.2.2
135
+ *
136
+ * @return bool
137
+ */
138
+ abstract function is_plugin();
139
+
140
+ /**
141
+ * Checks if the module type is "theme". The other type is "plugin".
142
+ *
143
+ * @author Leo Fajardo (@leorw)
144
+ * @since 1.2.2
145
+ *
146
+ * @return bool
147
+ */
148
+ function is_theme() {
149
+ return ( ! $this->is_plugin() );
150
+ }
151
+
152
+ #endregion
153
+
154
+ #----------------------------------------------------------------------------------
155
+ #region Permissions
156
+ #----------------------------------------------------------------------------------
157
+
158
+ /**
159
+ * Check if plugin must be WordPress.org compliant.
160
+ *
161
+ * @since 1.0.7
162
+ *
163
+ * @return bool
164
+ */
165
+ abstract function is_org_repo_compliant();
166
+
167
+ /**
168
+ * Check if plugin is allowed to install executable files.
169
+ *
170
+ * @author Vova Feldman (@svovaf)
171
+ * @since 1.0.5
172
+ *
173
+ * @return bool
174
+ */
175
+ function is_allowed_to_install() {
176
+ return ( $this->is_premium() || ! $this->is_org_repo_compliant() );
177
+ }
178
+
179
+ #endregion
180
+
181
+ /**
182
+ * Check if user in trial or in free plan (not paying).
183
+ *
184
+ * @author Vova Feldman (@svovaf)
185
+ * @since 1.0.4
186
+ *
187
+ * @return bool
188
+ */
189
+ function is_not_paying() {
190
+ return ( $this->is_trial() || $this->is_free_plan() );
191
+ }
192
+
193
+ /**
194
+ * Check if the user has an activated and valid paid license on current plugin's install.
195
+ *
196
+ * @since 1.0.9
197
+ *
198
+ * @return bool
199
+ */
200
+ abstract function is_paying();
201
+
202
+ /**
203
+ * Check if the user is paying or in trial.
204
+ *
205
+ * @since 1.0.9
206
+ *
207
+ * @return bool
208
+ */
209
+ function is_paying_or_trial() {
210
+ return ( $this->is_paying() || $this->is_trial() );
211
+ }
212
+
213
+ /**
214
+ * Check if user in a trial or have feature enabled license.
215
+ *
216
+ * @author Vova Feldman (@svovaf)
217
+ * @since 1.1.7
218
+ *
219
+ * @return bool
220
+ */
221
+ abstract function can_use_premium_code();
222
+
223
+ #----------------------------------------------------------------------------------
224
+ #region Premium Only
225
+ #----------------------------------------------------------------------------------
226
+
227
+ /**
228
+ * All logic wrapped in methods with "__premium_only()" suffix will be only
229
+ * included in the premium code.
230
+ *
231
+ * Example:
232
+ * if ( freemius()->is__premium_only() ) {
233
+ * ...
234
+ * }
235
+ */
236
+
237
+ /**
238
+ * Returns true when running premium plugin code.
239
+ *
240
+ * @since 1.0.9
241
+ *
242
+ * @return bool
243
+ */
244
+ function is__premium_only() {
245
+ return $this->is_premium();
246
+ }
247
+
248
+ /**
249
+ * Check if the user has an activated and valid paid license on current plugin's install.
250
+ *
251
+ * @since 1.0.9
252
+ *
253
+ * @return bool
254
+ *
255
+ */
256
+ function is_paying__premium_only() {
257
+ return ( $this->is__premium_only() && $this->is_paying() );
258
+ }
259
+
260
+ /**
261
+ * All code wrapped in this statement will be only included in the premium code.
262
+ *
263
+ * @since 1.0.9
264
+ *
265
+ * @param string $plan Plan name.
266
+ * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans.
267
+ *
268
+ * @return bool
269
+ */
270
+ function is_plan__premium_only( $plan, $exact = false ) {
271
+ return ( $this->is_premium() && $this->is_plan( $plan, $exact ) );
272
+ }
273
+
274
+ /**
275
+ * Check if plan matches active license' plan or active trial license' plan.
276
+ *
277
+ * All code wrapped in this statement will be only included in the premium code.
278
+ *
279
+ * @since 1.0.9
280
+ *
281
+ * @param string $plan Plan name.
282
+ * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans.
283
+ *
284
+ * @return bool
285
+ */
286
+ function is_plan_or_trial__premium_only( $plan, $exact = false ) {
287
+ return ( $this->is_premium() && $this->is_plan_or_trial( $plan, $exact ) );
288
+ }
289
+
290
+ /**
291
+ * Check if the user is paying or in trial.
292
+ *
293
+ * All code wrapped in this statement will be only included in the premium code.
294
+ *
295
+ * @since 1.0.9
296
+ *
297
+ * @return bool
298
+ */
299
+ function is_paying_or_trial__premium_only() {
300
+ return $this->is_premium() && $this->is_paying_or_trial();
301
+ }
302
+
303
+ /**
304
+ * Check if the user has an activated and valid paid license on current plugin's install.
305
+ *
306
+ * @since 1.0.4
307
+ *
308
+ * @return bool
309
+ *
310
+ * @deprecated Method name is confusing since it's not clear from the name the code will be removed.
311
+ * @using Alias to is_paying__premium_only()
312
+ */
313
+ function is_paying__fs__() {
314
+ return $this->is_paying__premium_only();
315
+ }
316
+
317
+ /**
318
+ * Check if user in a trial or have feature enabled license.
319
+ *
320
+ * All code wrapped in this statement will be only included in the premium code.
321
+ *
322
+ * @author Vova Feldman (@svovaf)
323
+ * @since 1.1.9
324
+ *
325
+ * @return bool
326
+ */
327
+ function can_use_premium_code__premium_only() {
328
+ return $this->is_premium() && $this->can_use_premium_code();
329
+ }
330
+
331
+ #endregion
332
+
333
+ #----------------------------------------------------------------------------------
334
+ #region Trial
335
+ #----------------------------------------------------------------------------------
336
+
337
+ /**
338
+ * Check if the user in a trial.
339
+ *
340
+ * @since 1.0.3
341
+ *
342
+ * @return bool
343
+ */
344
+ abstract function is_trial();
345
+
346
+ /**
347
+ * Check if trial already utilized.
348
+ *
349
+ * @since 1.0.9
350
+ *
351
+ * @return bool
352
+ */
353
+ abstract function is_trial_utilized();
354
+
355
+ #endregion
356
+
357
+ #----------------------------------------------------------------------------------
358
+ #region Plans
359
+ #----------------------------------------------------------------------------------
360
+
361
+ /**
362
+ * Check if plugin using the free plan.
363
+ *
364
+ * @since 1.0.4
365
+ *
366
+ * @return bool
367
+ */
368
+ abstract function is_free_plan();
369
+
370
+ /**
371
+ * @since 1.0.2
372
+ *
373
+ * @param string $plan Plan name.
374
+ * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans.
375
+ *
376
+ * @return bool
377
+ */
378
+ abstract function is_plan( $plan, $exact = false );
379
+
380
+ /**
381
+ * Check if plan based on trial. If not in trial mode, should return false.
382
+ *
383
+ * @since 1.0.9
384
+ *
385
+ * @param string $plan Plan name.
386
+ * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans.
387
+ *
388
+ * @return bool
389
+ */
390
+ abstract function is_trial_plan( $plan, $exact = false );
391
+
392
+ /**
393
+ * Check if plan matches active license' plan or active trial license' plan.
394
+ *
395
+ * @since 1.0.9
396
+ *
397
+ * @param string $plan Plan name.
398
+ * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans.
399
+ *
400
+ * @return bool
401
+ */
402
+ function is_plan_or_trial( $plan, $exact = false ) {
403
+ return $this->is_plan( $plan, $exact ) ||
404
+ $this->is_trial_plan( $plan, $exact );
405
+ }
406
+
407
+ /**
408
+ * Check if plugin has any paid plans.
409
+ *
410
+ * @author Vova Feldman (@svovaf)
411
+ * @since 1.0.7
412
+ *
413
+ * @return bool
414
+ */
415
+ abstract function has_paid_plan();
416
+
417
+ /**
418
+ * Check if plugin has any free plan, or is it premium only.
419
+ *
420
+ * Note: If no plans configured, assume plugin is free.
421
+ *
422
+ * @author Vova Feldman (@svovaf)
423
+ * @since 1.0.7
424
+ *
425
+ * @return bool
426
+ */
427
+ abstract function has_free_plan();
428
+
429
+ /**
430
+ * Check if plugin is premium only (no free plans).
431
+ *
432
+ * NOTE: is__premium_only() is very different method, don't get confused.
433
+ *
434
+ * @author Vova Feldman (@svovaf)
435
+ * @since 1.1.9
436
+ *
437
+ * @return bool
438
+ */
439
+ abstract function is_only_premium();
440
+
441
+ /**
442
+ * Check if module has a premium code version.
443
+ *
444
+ * Serviceware module might be freemium without any
445
+ * premium code version, where the paid features
446
+ * are all part of the service.
447
+ *
448
+ * @author Vova Feldman (@svovaf)
449
+ * @since 1.2.1.6
450
+ *
451
+ * @return bool
452
+ */
453
+ abstract function has_premium_version();
454
+
455
+ /**
456
+ * Check if module has any release on Freemius,
457
+ * or all plugin's code is on WordPress.org (Serviceware).
458
+ *
459
+ * @return bool
460
+ */
461
+ function has_release_on_freemius() {
462
+ return ! $this->is_org_repo_compliant() ||
463
+ $this->has_premium_version();
464
+ }
465
+
466
+ /**
467
+ * Checks if it's a freemium plugin.
468
+ *
469
+ * @author Vova Feldman (@svovaf)
470
+ * @since 1.1.9
471
+ *
472
+ * @return bool
473
+ */
474
+ function is_freemium() {
475
+ return $this->has_paid_plan() &&
476
+ $this->has_free_plan();
477
+ }
478
+
479
+ /**
480
+ * Check if module has only one plan.
481
+ *
482
+ * @author Vova Feldman (@svovaf)
483
+ * @since 1.2.1.7
484
+ *
485
+ * @return bool
486
+ */
487
+ abstract function is_single_plan();
488
+
489
+ #endregion
490
+
491
+ /**
492
+ * Check if running payments in sandbox mode.
493
+ *
494
+ * @since 1.0.4
495
+ *
496
+ * @return bool
497
+ */
498
+ abstract function is_payments_sandbox();
499
+
500
+ /**
501
+ * Check if running test vs. live plugin.
502
+ *
503
+ * @since 1.0.5
504
+ *
505
+ * @return bool
506
+ */
507
+ abstract function is_live();
508
+
509
+ /**
510
+ * Check if running premium plugin code.
511
+ *
512
+ * @since 1.0.5
513
+ *
514
+ * @return bool
515
+ */
516
+ abstract function is_premium();
517
+
518
+ /**
519
+ * Get upgrade URL.
520
+ *
521
+ * @author Vova Feldman (@svovaf)
522
+ * @since 1.0.2
523
+ *
524
+ * @param string $period Billing cycle.
525
+ *
526
+ * @return string
527
+ */
528
+ abstract function get_upgrade_url( $period = WP_FS__PERIOD_ANNUALLY );
529
+
530
+ /**
531
+ * Check if Freemius was first added in a plugin update.
532
+ *
533
+ * @author Vova Feldman (@svovaf)
534
+ * @since 1.1.5
535
+ *
536
+ * @return bool
537
+ */
538
+ function is_plugin_update() {
539
+ return ! $this->is_plugin_new_install();
540
+ }
541
+
542
+ /**
543
+ * Check if Freemius was part of the plugin when the user installed it first.
544
+ *
545
+ * @author Vova Feldman (@svovaf)
546
+ * @since 1.1.5
547
+ *
548
+ * @return bool
549
+ */
550
+ abstract function is_plugin_new_install();
551
+
552
+ #----------------------------------------------------------------------------------
553
+ #region Marketing
554
+ #----------------------------------------------------------------------------------
555
+
556
+ /**
557
+ * Check if current user purchased any other plugins before.
558
+ *
559
+ * @author Vova Feldman (@svovaf)
560
+ * @since 1.0.9
561
+ *
562
+ * @return bool
563
+ */
564
+ abstract function has_purchased_before();
565
+
566
+ /**
567
+ * Check if current user classified as an agency.
568
+ *
569
+ * @author Vova Feldman (@svovaf)
570
+ * @since 1.0.9
571
+ *
572
+ * @return bool
573
+ */
574
+ abstract function is_agency();
575
+
576
+ /**
577
+ * Check if current user classified as a developer.
578
+ *
579
+ * @author Vova Feldman (@svovaf)
580
+ * @since 1.0.9
581
+ *
582
+ * @return bool
583
+ */
584
+ abstract function is_developer();
585
+
586
+ /**
587
+ * Check if current user classified as a business.
588
+ *
589
+ * @author Vova Feldman (@svovaf)
590
+ * @since 1.0.9
591
+ *
592
+ * @return bool
593
+ */
594
+ abstract function is_business();
595
+
596
+ #endregion
597
  }
freemius/includes/class-freemius.php CHANGED
@@ -555,6 +555,18 @@
555
  return;
556
  }
557
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  if ( version_compare( $sdk_prev_version, '1.1.5', '<' ) &&
559
  version_compare( $sdk_version, '1.1.5', '>=' )
560
  ) {
@@ -578,6 +590,46 @@
578
  }
579
  }
580
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  /**
582
  * @author Vova Feldman (@svovaf)
583
  * @since 1.2.2.7
@@ -874,8 +926,11 @@
874
  // Try to load the cached value of the file path.
875
  if ( isset( $this->_storage->plugin_main_file ) ) {
876
  $plugin_main_file = $this->_storage->plugin_main_file;
877
- if ( isset( $plugin_main_file->path ) && file_exists( $plugin_main_file->path ) ) {
878
- return $plugin_main_file->path;
 
 
 
879
  }
880
  }
881
 
@@ -894,15 +949,16 @@
894
  if ( isset( $this->_storage->plugin_main_file ) &&
895
  isset( $this->_storage->plugin_main_file->prev_path )
896
  ) {
897
- if ( file_exists( $this->_storage->plugin_main_file->prev_path ) ) {
898
- return $this->_storage->plugin_main_file->prev_path;
 
899
  }
900
  }
901
 
902
  wp_die(
903
- $this->get_text( 'failed-finding-main-path' ) .
904
  " Module: {$this->_slug}; SDK: " . WP_FS__SDK_VERSION . ";",
905
- $this->get_text( 'error' ),
906
  array( 'back_link' => true )
907
  );
908
  }
@@ -918,9 +974,62 @@
918
  'path' => $id_slug_type_path_map[ $this->_module_id ]['path'],
919
  );
920
 
921
- return $id_slug_type_path_map[ $this->_module_id ]['path'];
922
  }
923
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
924
  /**
925
  * @author Leo Fajardo (@leorw)
926
  *
@@ -950,7 +1059,10 @@
950
  * @author Vova Feldman (@svovaf)
951
  * @since 1.2.3
952
  */
953
- ! file_exists( $id_slug_type_path_map[ $module_id ]['path'] )
 
 
 
954
  ) {
955
  $caller_main_file_and_type = $this->get_caller_main_file_and_type();
956
 
@@ -1041,8 +1153,18 @@
1041
  */
1042
 
1043
  if ( $caller_file_path == fs_normalize_path( realpath( trailingslashit( $themes_dir ) . basename( dirname( $caller_file_path ) ) . '/' . basename( $caller_file_path ) ) ) ) {
1044
- $module_type = WP_FS__MODULE_TYPE_THEME;
1045
- $caller_file_candidate = $caller_file_path;
 
 
 
 
 
 
 
 
 
 
1046
  continue;
1047
  }
1048
  }
@@ -1060,7 +1182,7 @@
1060
 
1061
  if ( isset( $caller_map[ $caller_file_hash ] ) ) {
1062
  $module_type = WP_FS__MODULE_TYPE_PLUGIN;
1063
- $caller_file_candidate = $caller_map[ $caller_file_hash ];
1064
  }
1065
  }
1066
 
@@ -1152,18 +1274,18 @@
1152
 
1153
  $reason_found_better_plugin = array(
1154
  'id' => self::REASON_FOUND_A_BETTER_PLUGIN,
1155
- 'text' => sprintf( $this->get_text( 'reason-found-a-better-plugin' ), $module_type ),
1156
  'input_type' => 'textfield',
1157
- 'input_placeholder' => sprintf( $this->get_text( 'placeholder-plugin-name' ), $module_type ),
1158
  );
1159
 
1160
  $reason_temporary_deactivation = array(
1161
  'id' => self::REASON_TEMPORARY_DEACTIVATION,
1162
  'text' => sprintf(
1163
- $this->get_text( 'reason-temporary-x' ),
1164
  strtolower( $this->is_plugin() ?
1165
- $this->get_text( 'deactivation' ) :
1166
- $this->get_text( 'theme-switch' )
1167
  )
1168
  ),
1169
  'input_type' => '',
@@ -1172,7 +1294,7 @@
1172
 
1173
  $reason_other = array(
1174
  'id' => self::REASON_OTHER,
1175
- 'text' => $this->get_text( 'reason-other' ),
1176
  'input_type' => 'textfield',
1177
  'input_placeholder' => ''
1178
  );
@@ -1180,27 +1302,27 @@
1180
  $long_term_user_reasons = array(
1181
  array(
1182
  'id' => self::REASON_NO_LONGER_NEEDED,
1183
- 'text' => sprintf( $this->get_text( 'reason-no-longer-needed' ), $module_type ),
1184
  'input_type' => '',
1185
  'input_placeholder' => ''
1186
  ),
1187
  $reason_found_better_plugin,
1188
  array(
1189
  'id' => self::REASON_NEEDED_FOR_A_SHORT_PERIOD,
1190
- 'text' => sprintf( $this->get_text( 'reason-needed-for-a-short-period' ), $module_type ),
1191
  'input_type' => '',
1192
  'input_placeholder' => ''
1193
  ),
1194
  array(
1195
  'id' => self::REASON_BROKE_MY_SITE,
1196
- 'text' => sprintf( $this->get_text( 'reason-broke-my-site' ), $module_type ),
1197
  'input_type' => '',
1198
  'input_placeholder' => '',
1199
  'internal_message' => $contact_support_template
1200
  ),
1201
  array(
1202
  'id' => self::REASON_SUDDENLY_STOPPED_WORKING,
1203
- 'text' => sprintf( $this->get_text( 'reason-suddenly-stopped-working' ), $module_type ),
1204
  'input_type' => '',
1205
  'input_placeholder' => '',
1206
  'internal_message' => $contact_support_template
@@ -1210,15 +1332,15 @@
1210
  if ( $this->is_paying() ) {
1211
  $long_term_user_reasons[] = array(
1212
  'id' => self::REASON_CANT_PAY_ANYMORE,
1213
- 'text' => $this->get_text( 'reason-cant-pay-anymore' ),
1214
  'input_type' => 'textfield',
1215
- 'input_placeholder' => $this->get_text( 'placeholder-comfortable-price' )
1216
  );
1217
  }
1218
 
1219
  $reason_dont_share_info = array(
1220
  'id' => self::REASON_DONT_LIKE_TO_SHARE_MY_INFORMATION,
1221
- 'text' => $this->get_text( 'reason-dont-like-to-share-my-information' ),
1222
  'input_type' => '',
1223
  'input_placeholder' => ''
1224
  );
@@ -1239,7 +1361,7 @@
1239
  'non-registered-and-non-anonymous-short-term' => array(
1240
  array(
1241
  'id' => self::REASON_DIDNT_WORK,
1242
- 'text' => sprintf( $this->get_text( 'reason-didnt-work' ), $module_type ),
1243
  'input_type' => '',
1244
  'input_placeholder' => ''
1245
  ),
@@ -1249,7 +1371,7 @@
1249
  'short-term' => array(
1250
  array(
1251
  'id' => self::REASON_COULDNT_MAKE_IT_WORK,
1252
- 'text' => $this->get_text( 'reason-couldnt-make-it-work' ),
1253
  'input_type' => '',
1254
  'input_placeholder' => '',
1255
  'internal_message' => $contact_support_template
@@ -1257,27 +1379,27 @@
1257
  $reason_found_better_plugin,
1258
  array(
1259
  'id' => self::REASON_GREAT_BUT_NEED_SPECIFIC_FEATURE,
1260
- 'text' => sprintf( $this->get_text( 'reason-great-but-need-specific-feature' ), $module_type ),
1261
  'input_type' => 'textarea',
1262
- 'input_placeholder' => $this->get_text( 'placeholder-feature' )
1263
  ),
1264
  array(
1265
  'id' => self::REASON_NOT_WORKING,
1266
- 'text' => sprintf( $this->get_text( 'reason-not-working' ), $module_type ),
1267
  'input_type' => 'textarea',
1268
- 'input_placeholder' => $this->get_text( 'placeholder-share-what-didnt-work' )
1269
  ),
1270
  array(
1271
  'id' => self::REASON_NOT_WHAT_I_WAS_LOOKING_FOR,
1272
- 'text' => $this->get_text( 'reason-not-what-i-was-looking-for' ),
1273
  'input_type' => 'textarea',
1274
- 'input_placeholder' => $this->get_text( 'placeholder-what-youve-been-looking-for' )
1275
  ),
1276
  array(
1277
  'id' => self::REASON_DIDNT_WORK_AS_EXPECTED,
1278
- 'text' => sprintf( $this->get_text( 'reason-didnt-work-as-expected' ), $module_type ),
1279
  'input_type' => 'textarea',
1280
- 'input_placeholder' => $this->get_text( 'placeholder-what-did-you-expect' )
1281
  )
1282
  )
1283
  );
@@ -1813,7 +1935,7 @@
1813
 
1814
  self::$_static_logger->entrance();
1815
 
1816
- $title = sprintf( '%s [v.%s]', fs_text( 'freemius-debug' ), WP_FS__SDK_VERSION );
1817
 
1818
  if ( WP_FS__DEV_MODE ) {
1819
  // Add top-level debug menu item.
@@ -2220,29 +2342,36 @@
2220
  *
2221
  * @return string
2222
  */
2223
- function get_anonymous_id() {
2224
- $unique_id = self::$_accounts->get_option( 'unique_id' );
2225
 
2226
- if ( empty( $unique_id ) || ! is_string( $unique_id ) ) {
2227
- $key = get_site_url();
2228
 
2229
- // If localhost, assign microtime instead of domain.
2230
- if ( WP_FS__IS_LOCALHOST ||
2231
- false !== strpos( $key, 'localhost' ) ||
2232
- false === strpos( $key, '.' )
2233
- ) {
2234
- $key = microtime();
2235
- }
2236
 
2237
- $unique_id = md5( $key );
 
 
 
 
 
 
 
2238
 
2239
- self::$_accounts->set_option( 'unique_id', $unique_id, true );
2240
- }
2241
 
2242
- $this->_logger->departure( $unique_id );
2243
 
2244
- return $unique_id;
2245
- }
2246
 
2247
  /**
2248
  * @author Vova Feldman (@svovaf)
@@ -2322,6 +2451,18 @@
2322
  // $admin_email = get_option( 'admin_email' );
2323
  $admin_email = $current_user->user_email;
2324
 
 
 
 
 
 
 
 
 
 
 
 
 
2325
  $message = false;
2326
  if ( is_object( $api_result ) &&
2327
  isset( $api_result->error ) &&
@@ -2348,15 +2489,15 @@
2348
  if ( ! empty( $missing_methods ) ) {
2349
  $missing_methods = sprintf(
2350
  '<br><br><b>%s</b> %s',
2351
- $this->get_text( 'curl-disabled-methods' ),
2352
  $missing_methods
2353
  );
2354
  }
2355
  }
2356
 
2357
  $message = sprintf(
2358
- $this->get_text( 'x-requires-access-to-api' ) . ' ' .
2359
- $this->get_text( 'curl-missing-message' ) . ' ' .
2360
  $missing_methods .
2361
  ' %s',
2362
  '<b>' . $this->get_plugin_name() . '</b>',
@@ -2364,95 +2505,95 @@
2364
  '<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
2365
  sprintf(
2366
  '<a class="fs-resolve" data-type="curl" href="#"><b>%s</b></a>%s',
2367
- $this->get_text( 'curl-missing-no-clue-title' ),
2368
  ' - ' . sprintf(
2369
- $this->get_text( 'curl-missing-no-clue-desc' ),
2370
  '<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
2371
  )
2372
  ),
2373
  sprintf(
2374
  '<b>%s</b> - %s',
2375
- $this->get_text( 'sysadmin-title' ),
2376
- sprintf( $this->get_text( 'curl-missing-sysadmin-desc' ), $this->_module_type )
2377
  ),
2378
  sprintf(
2379
- '<a href="%s"><b>%s</b></a>%s',
2380
- wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=' . 'all' . '&amp;paged=' . '1' . '&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
2381
- $this->get_text( 'deactivate-plugin-title' ),
2382
- ' - ' . $this->get_text( 'deactivate-plugin-desc' )
2383
  )
2384
  )
2385
  );
2386
  break;
2387
  case 'cloudflare_ddos_protection':
2388
  $message = sprintf(
2389
- $this->get_text( 'x-requires-access-to-api' ) . ' ' .
2390
- $this->get_text( 'cloudflare-blocks-connection-message' ) . ' ' .
2391
- $this->get_text( 'happy-to-resolve-issue-asap' ) .
2392
  ' %s',
2393
  '<b>' . $this->get_plugin_name() . '</b>',
2394
  sprintf(
2395
  '<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
2396
  sprintf(
2397
  '<a class="fs-resolve" data-type="cloudflare" href="#"><b>%s</b></a>%s',
2398
- $this->get_text( 'fix-issue-title' ),
2399
  ' - ' . sprintf(
2400
- $this->get_text( 'fix-issue-desc' ),
2401
  '<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
2402
  )
2403
  ),
2404
  sprintf(
2405
- '<a href="%s" target="_blank"><b>%s</b></a>%s',
2406
  sprintf( 'https://wordpress.org/plugins/%s/download/', $this->_slug ),
2407
- $this->get_text( 'install-previous-title' ),
2408
- ' - ' . $this->get_text( 'install-previous-desc' )
2409
  ),
2410
  sprintf(
2411
- '<a href="%s"><b>%s</b></a>%s',
2412
- wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=' . 'all' . '&amp;paged=' . '1' . '&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
2413
- $this->get_text( 'deactivate-plugin-title' ),
2414
- ' - ' . $this->get_text( 'deactivate-plugin-desc' )
2415
  )
2416
  )
2417
  );
2418
  break;
2419
  case 'squid_cache_block':
2420
  $message = sprintf(
2421
- $this->get_text( 'x-requires-access-to-api' ) . ' ' .
2422
- $this->get_text( 'squid-blocks-connection-message' ) .
2423
  ' %s',
2424
  '<b>' . $this->get_plugin_name() . '</b>',
2425
  sprintf(
2426
  '<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
2427
  sprintf(
2428
- '<a class="fs-resolve" data-type="squid" href="#"><b>%s</b></a>%s',
2429
- $this->get_text( 'squid-no-clue-title' ),
2430
- ' - ' . sprintf(
2431
- $this->get_text( 'squid-no-clue-desc' ),
2432
  '<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
2433
  )
2434
  ),
2435
  sprintf(
2436
  '<b>%s</b> - %s',
2437
- $this->get_text( 'sysadmin-title' ),
2438
  sprintf(
2439
- $this->get_text( 'squid-sysadmin-desc' ),
2440
  // We use a filter since the plugin might require additional API connectivity.
2441
  '<b>' . implode( ', ', $this->apply_filters( 'api_domains', array( 'api.freemius.com', 'wp.freemius.com' ) ) ) . '</b>',
2442
  $this->_module_type
2443
  )
2444
  ),
2445
  sprintf(
2446
- '<a href="%s"><b>%s</b></a>%s',
2447
- wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=' . 'all' . '&amp;paged=' . '1' . '&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
2448
- $this->get_text( 'deactivate-plugin-title' ),
2449
- ' - ' . $this->get_text( 'deactivate-plugin-desc' )
2450
  )
2451
  )
2452
  );
2453
  break;
2454
  // default:
2455
- // $message = $this->get_text( 'connectivity-test-fails-message' );
2456
  // break;
2457
  }
2458
  }
@@ -2460,25 +2601,27 @@
2460
  $message_id = 'failed_connect_api';
2461
  $type = 'error';
2462
 
 
 
2463
  if ( false === $message ) {
2464
  if ( $is_first_failure ) {
2465
  // First attempt failed.
2466
  $message = sprintf(
2467
- $this->get_text( 'x-requires-access-to-api' ) . ' ' .
2468
- $this->get_text( 'connectivity-test-fails-message' ) . ' ' .
2469
- $this->get_text( 'connectivity-test-maybe-temporary' ) . '<br><br>' .
2470
  '%s',
2471
  '<b>' . $this->get_plugin_name() . '</b>',
2472
  sprintf(
2473
  '<div id="fs_firewall_issue_options">%s %s</div>',
2474
  sprintf(
2475
  '<a class="button button-primary fs-resolve" data-type="retry_ping" href="#">%s</a>',
2476
- $this->get_text( 'yes-do-your-thing' )
2477
  ),
2478
  sprintf(
2479
  '<a href="%s" class="button">%s</a>',
2480
- wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=' . 'all' . '&amp;paged=' . '1' . '&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
2481
- $this->get_text( 'no-deactivate' )
2482
  )
2483
  )
2484
  );
@@ -2488,32 +2631,32 @@
2488
  } else {
2489
  // Second connectivity attempt failed.
2490
  $message = sprintf(
2491
- $this->get_text( 'x-requires-access-to-api' ) . ' ' .
2492
- $this->get_text( 'connectivity-test-fails-message' ) . ' ' .
2493
- $this->get_text( 'happy-to-resolve-issue-asap' ) .
2494
  ' %s',
2495
  '<b>' . $this->get_plugin_name() . '</b>',
2496
  sprintf(
2497
  '<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
2498
  sprintf(
2499
  '<a class="fs-resolve" data-type="general" href="#"><b>%s</b></a>%s',
2500
- $this->get_text( 'fix-issue-title' ),
2501
  ' - ' . sprintf(
2502
- $this->get_text( 'fix-issue-desc' ),
2503
  '<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
2504
  )
2505
  ),
2506
  sprintf(
2507
- '<a href="%s" target="_blank"><b>%s</b></a>%s',
2508
  sprintf( 'https://wordpress.org/plugins/%s/download/', $this->_slug ),
2509
- $this->get_text( 'install-previous-title' ),
2510
- ' - ' . $this->get_text( 'install-previous-desc' )
2511
  ),
2512
  sprintf(
2513
- '<a href="%s"><b>%s</b></a>%s',
2514
- wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=' . 'all' . '&amp;paged=' . '1' . '&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
2515
- $this->get_text( 'deactivate-plugin-title' ),
2516
- ' - ' . $this->get_text( 'deactivate-plugin-desc' )
2517
  )
2518
  )
2519
  );
@@ -2523,7 +2666,7 @@
2523
  $this->_admin_notices->add_sticky(
2524
  $message,
2525
  $message_id,
2526
- $this->get_text( 'oops' ) . '...',
2527
  $type
2528
  );
2529
  }
@@ -2590,7 +2733,7 @@
2590
 
2591
  $this->_admin_notices->add_sticky(
2592
  sprintf(
2593
- $this->get_text( 'fix-request-sent-message' ),
2594
  '<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
2595
  ),
2596
  'server_details_sent'
@@ -2866,10 +3009,6 @@
2866
 
2867
  $this->parse_settings( $plugin_info );
2868
 
2869
- if ( $this->has_affiliate_program() ) {
2870
- $this->fetch_affiliate_and_terms();
2871
- }
2872
-
2873
  if ( ! self::is_ajax() ) {
2874
  if ( ! $this->is_addon() || $this->is_only_premium() ) {
2875
  add_action( 'admin_menu', array( &$this, '_prepare_admin_menu' ), WP_FS__LOWEST_PRIORITY );
@@ -2985,10 +3124,10 @@
2985
 
2986
  $this->_admin_notices->add(
2987
  ( ! empty( $parent_name ) ?
2988
- sprintf( $this->get_text( 'addon-x-cannot-run-without-y' ), $this->get_plugin_name(), $parent_name ) :
2989
- sprintf( $this->get_text( 'addon-x-cannot-run-without-parent' ), $this->get_plugin_name() )
2990
  ),
2991
- $this->get_text( 'oops' ) . '...',
2992
  'error'
2993
  );
2994
 
@@ -3107,7 +3246,7 @@
3107
  $this->_logger->api_error( $result );
3108
 
3109
  self::shoot_ajax_failure(
3110
- $this->get_text( 'unexpected-api-error' ) .
3111
  ( $this->is_api_error( $result ) && isset( $result->error ) ?
3112
  $result->error->message :
3113
  var_export( $result, true ) )
@@ -3132,7 +3271,7 @@
3132
  $this->_logger->api_error( $result );
3133
 
3134
  self::shoot_ajax_failure(
3135
- $this->get_text( 'unexpected-api-error' ) .
3136
  ( $this->is_api_error( $result ) && isset( $result->error ) ?
3137
  $result->error->message :
3138
  var_export( $result, true ) )
@@ -3540,9 +3679,10 @@
3540
 
3541
  if ( ! $this->is_only_premium() ) {
3542
  $this->_admin_notices->add_sticky(
3543
- sprintf( $this->get_text( 'premium-activated-message' ), $this->_module_type ),
3544
  'premium_activated',
3545
- $this->get_text( 'woot' ) . '!'
 
3546
  );
3547
  }
3548
  } else {
@@ -3555,11 +3695,12 @@
3555
  if ( $this->is_paying() && ! $this->is_premium() ) {
3556
  $this->_admin_notices->add_sticky(
3557
  sprintf(
3558
- $this->get_text( 'you-have-x-license' ),
 
3559
  $this->_site->plan->title
3560
  ) . $this->get_complete_upgrade_instructions(),
3561
  'plan_upgraded',
3562
- $this->get_text( 'yee-haw' ) . '!'
3563
  );
3564
  }
3565
  }
@@ -3822,19 +3963,25 @@
3822
 
3823
  $this->_parent->_admin_notices->add_sticky(
3824
  sprintf(
3825
- $this->_parent->get_text( $is_after_trial_cancel ?
3826
- 'addon-trial-cancelled-message' :
3827
- 'addon-no-license-message'
 
 
 
 
 
 
3828
  ),
3829
  '<b>' . $this->_plugin->title . '</b>'
3830
  ) . ' ' . sprintf(
3831
  '<a href="%s" aria-label="%s" class="button button-primary" style="margin-left: 10px; vertical-align: middle;">%s &nbsp;&#10140;</a>',
3832
  $this->_parent->addon_url( $this->_slug ),
3833
- esc_attr( sprintf( $this->_parent->get_text( 'more-information-about-x' ), $this->_plugin->title ) ),
3834
- $this->_parent->get_text( 'purchase-license' )
3835
  ),
3836
  'no_addon_license_' . $this->_slug,
3837
- ( $is_after_trial_cancel ? '' : $this->_parent->get_text( 'oops' ) . '...' ),
3838
  ( $is_after_trial_cancel ? 'success' : 'error' )
3839
  );
3840
 
@@ -4268,10 +4415,12 @@
4268
 
4269
  $this->_admin_notices->add_sticky(
4270
  sprintf(
4271
- $this->get_text( 'pending-activation-message' ),
4272
  '<b>' . $this->get_plugin_name() . '</b>',
4273
  '<b>' . $email . '</b>',
4274
- $this->get_text( $is_pending_trial ? 'start-the-trial' : 'complete-the-install' )
 
 
4275
  ),
4276
  'activation_pending',
4277
  'Thanks!'
@@ -4350,10 +4499,11 @@
4350
  // Show notice for new plugin installations.
4351
  $this->_admin_notices->add(
4352
  sprintf(
4353
- $this->get_text( 'you-are-step-away' ),
4354
  sprintf( '<b><a href="%s">%s</a></b>',
4355
  $this->get_activation_url(),
4356
- sprintf( $this->get_text( 'activate-x-now' ), $this->get_plugin_name() )
 
4357
  )
4358
  ),
4359
  '',
@@ -4366,11 +4516,11 @@
4366
  // Show notice for new plugin installations.
4367
  $this->_admin_notices->add_sticky(
4368
  sprintf(
4369
- $this->get_text( 'few-plugin-tweaks' ),
4370
  $this->_module_type,
4371
  sprintf( '<b><a href="%s">%s</a></b>',
4372
  $this->get_activation_url(),
4373
- sprintf( $this->get_text( 'optin-x-now' ), $this->get_plugin_name() )
4374
  )
4375
  ),
4376
  'connect_account',
@@ -4755,8 +4905,9 @@
4755
  // If activating the premium module version, add an admin notice to congratulate for an upgrade completion.
4756
  if ( $is_premium_version_activation ) {
4757
  $this->_admin_notices->add(
4758
- sprintf( $this->get_text( 'successful-version-upgrade-message' ), sprintf( '<b>%s</b>', $this->_plugin->title ) ),
4759
- $this->get_text( 'woot' ) . '!'
 
4760
  );
4761
  }
4762
  } else if ( $this->is_anonymous() ) {
@@ -5839,10 +5990,10 @@
5839
  */
5840
  function get_module_label( $lowercase = false ) {
5841
  $label = $this->is_addon() ?
5842
- $this->get_text( 'addon' ) :
5843
  ( $this->is_plugin() ?
5844
- $this->get_text( 'plugin' ) :
5845
- $this->get_text( 'theme' ) );
5846
 
5847
  if ( $lowercase ) {
5848
  $label = strtolower( $label );
@@ -7099,9 +7250,9 @@
7099
  $error = $result->error;
7100
 
7101
  if ( in_array( $error->code, array( 'invalid_email', 'no_user' ) ) ) {
7102
- $error = $this->get_text( 'email-not-found' );
7103
  } else if ( 'no_license' === $error->code ) {
7104
- $error = $this->get_text( 'no-active-licenses' );
7105
  } else {
7106
  $error = $error->message;
7107
  }
@@ -7112,7 +7263,7 @@
7112
  );
7113
 
7114
  if ( false !== $error ) {
7115
- $licenses['error'] = sprintf( '%s... %s', $this->get_text( 'oops' ), strtolower( $error ) );
7116
  }
7117
 
7118
  echo json_encode( $licenses );
@@ -7145,7 +7296,7 @@
7145
  if ( self::is_ajax() &&
7146
  'admin-ajax.php' === $pagenow
7147
  ) {
7148
- $referer = wp_get_raw_referer();
7149
 
7150
  if ( is_string( $referer ) ) {
7151
  $parts = explode( '?', $referer );
@@ -7202,14 +7353,12 @@
7202
 
7203
  /**
7204
  * @author Leo Fajardo (@leorw)
7205
- * @since 1.2.3
7206
  */
7207
- private function fetch_affiliate_and_terms() {
7208
- $this->_logger->entrance();
7209
-
7210
  if ( ! is_object( $this->plugin_affiliate_terms ) ) {
7211
  $plugins_api = $this->get_api_plugin_scope();
7212
- $affiliate_terms = $plugins_api->get( '/aff.json?type=affiliation', true );
7213
 
7214
  if ( ! $this->is_api_result_entity( $affiliate_terms ) ) {
7215
  return;
@@ -7217,20 +7366,30 @@
7217
 
7218
  $this->plugin_affiliate_terms = new FS_AffiliateTerms( $affiliate_terms );
7219
  }
 
 
 
 
 
 
 
 
 
 
7220
 
7221
- if ( $this->is_registered() ) {
7222
  $users_api = $this->get_api_user_scope();
7223
- $result = $users_api->get( "/plugins/{$this->_plugin->id}/aff/{$this->plugin_affiliate_terms->id}/affiliates.json", true );
7224
  if ( $this->is_api_result_object( $result, 'affiliates' ) ) {
7225
  if ( ! empty( $result->affiliates ) ) {
7226
  $affiliate = new FS_Affiliate( $result->affiliates[0] );
7227
 
7228
- if ( ! $affiliate->is_pending() && ! empty( $this->_storage->affiliate_application_data ) ) {
7229
- unset( $this->_storage->affiliate_application_data );
 
7230
  }
7231
 
7232
  if ( $affiliate->is_using_custom_terms ) {
7233
- $affiliate_terms = $users_api->get( "/plugins/{$this->_plugin->id}/affiliates/{$affiliate->id}/aff/{$affiliate->custom_affiliate_terms_id}.json", true );
7234
  if ( $this->is_api_result_entity( $affiliate_terms ) ) {
7235
  $this->custom_affiliate_terms = new FS_AffiliateTerms( $affiliate_terms );
7236
  }
@@ -7242,6 +7401,17 @@
7242
  }
7243
  }
7244
 
 
 
 
 
 
 
 
 
 
 
 
7245
  /**
7246
  * @author Leo Fajardo
7247
  * @since 1.2.3
@@ -7310,10 +7480,12 @@
7310
  var_export( $next_page, true )
7311
  );
7312
  } else if ( $this->is_pending_activation() ) {
7313
- self::shoot_ajax_failure( $this->get_text( 'account-is-pending-activation' ) );
7314
  }
7315
  }
7316
 
 
 
7317
  $api = $this->get_api_user_scope();
7318
  $result = $api->call(
7319
  ( "/plugins/{$this->_plugin->id}/aff/{$this->plugin_affiliate_terms->id}/affiliates.json" ),
@@ -7335,6 +7507,7 @@
7335
  }
7336
 
7337
  $affiliate_application_data = array(
 
7338
  'stats_description' => $affiliate['stats_description'],
7339
  'promotion_method_description' => $affiliate['promotion_method_description'],
7340
  );
@@ -8552,7 +8725,19 @@
8552
  return true;
8553
  }
8554
 
8555
- $decoded = @json_decode( $response['body'] );
 
 
 
 
 
 
 
 
 
 
 
 
8556
 
8557
  if ( empty( $decoded ) ) {
8558
  return false;
@@ -8647,7 +8832,8 @@
8647
 
8648
  if ( ! $this->is_paying_or_trial() ) {
8649
  $this->_admin_notices->add_sticky(
8650
- sprintf( $this->get_text( 'plugin-x-activation-message' ), '<b>' . $this->get_plugin_name() . '</b>' ),
 
8651
  'activation_complete'
8652
  );
8653
  }
@@ -8658,20 +8844,20 @@
8658
  if ( $this->is_paying() ) {
8659
  $this->_admin_notices->add_sticky(
8660
  sprintf(
8661
- $this->get_text( 'activation-with-plan-x-message' ),
8662
  $this->_site->plan->title
8663
  ) . $this->get_complete_upgrade_instructions(),
8664
  'plan_upgraded',
8665
- $this->get_text( 'yee-haw' ) . '!'
8666
  );
8667
  } else {
8668
  $this->_admin_notices->add_sticky(
8669
  sprintf(
8670
- $this->get_text( 'trial-started-message' ),
8671
  '<i>' . $this->get_plugin_name() . '</i>'
8672
  ) . $this->get_complete_upgrade_instructions( $this->_storage->trial_plan->title ),
8673
  'trial_started',
8674
- $this->get_text( 'yee-haw' ) . '!'
8675
  );
8676
  }
8677
  }
@@ -8956,9 +9142,9 @@
8956
  $install = $this->apply_filters( 'after_install_failure', $install, $args );
8957
 
8958
  $this->_admin_notices->add(
8959
- sprintf( $this->get_text( 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' .
8960
- $this->get_text( 'contact-us-with-error-message' ) . ' ' . '<b>' . $install->error->message . '</b>',
8961
- $this->get_text( 'oops' ) . '...',
8962
  'error'
8963
  );
8964
 
@@ -9012,9 +9198,9 @@
9012
 
9013
  if ( isset( $addon_install->error ) ) {
9014
  $this->_admin_notices->add(
9015
- sprintf( $this->get_text( 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' .
9016
- $this->get_text( 'contact-us-with-error-message' ) . ' ' . '<b>' . $addon_install->error->message . '</b>',
9017
- $this->get_text( 'oops' ) . '...',
9018
  'error'
9019
  );
9020
 
@@ -9073,9 +9259,9 @@
9073
 
9074
  if ( isset( $parent_install->error ) ) {
9075
  $this->_admin_notices->add(
9076
- sprintf( $this->get_text( 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' .
9077
- $this->get_text( 'contact-us-with-error-message' ) . ' ' . '<b>' . $parent_install->error->message . '</b>',
9078
- $this->get_text( 'oops' ) . '...',
9079
  'error'
9080
  );
9081
 
@@ -9332,15 +9518,15 @@
9332
  * @return string
9333
  */
9334
  function get_pricing_cta_label() {
9335
- $label = 'upgrade';
9336
 
9337
  if ( $this->is_in_trial_promotion() &&
9338
  ! $this->is_paying_or_trial()
9339
  ) {
9340
  // If running a trial promotion, modify the pricing to load the trial.
9341
- $label = 'start-trial';
9342
  } else if ( $this->is_paying() ) {
9343
- $label = 'pricing';
9344
  }
9345
 
9346
  return $label;
@@ -9380,9 +9566,9 @@
9380
  if ( $this->has_affiliate_program() ) {
9381
  // Add affiliation page.
9382
  $this->add_submenu_item(
9383
- $this->get_text( 'affiliation' ),
9384
  array( &$this, '_affiliation_page_render' ),
9385
- $this->get_plugin_name() . ' &ndash; ' . $this->get_text( 'affiliation' ),
9386
  'manage_options',
9387
  'affiliation',
9388
  'Freemius::_clean_admin_content_section',
@@ -9402,9 +9588,9 @@
9402
 
9403
  // Add user account page.
9404
  $this->add_submenu_item(
9405
- $this->get_text( 'account' ),
9406
  array( &$this, '_account_page_render' ),
9407
- $this->get_plugin_name() . ' &ndash; ' . $this->get_text( 'account' ),
9408
  'manage_options',
9409
  'account',
9410
  array( &$this, '_account_page_load' ),
@@ -9415,9 +9601,9 @@
9415
 
9416
  // Add contact page.
9417
  $this->add_submenu_item(
9418
- $this->get_text( 'contact-us' ),
9419
  array( &$this, '_contact_page_render' ),
9420
- $this->get_plugin_name() . ' &ndash; ' . $this->get_text( 'contact-us' ),
9421
  'manage_options',
9422
  'contact',
9423
  'Freemius::_clean_admin_content_section',
@@ -9427,9 +9613,9 @@
9427
 
9428
  if ( $this->has_addons() ) {
9429
  $this->add_submenu_item(
9430
- $this->get_text( 'add-ons' ),
9431
  array( &$this, '_addons_page_render' ),
9432
- $this->get_plugin_name() . ' &ndash; ' . $this->get_text( 'add-ons' ),
9433
  'manage_options',
9434
  'addons',
9435
  array( &$this, '_addons_page_load' ),
@@ -9443,7 +9629,7 @@
9443
  $this->is_pricing_page_visible()
9444
  );
9445
 
9446
- $pricing_cta_slug = $this->get_pricing_cta_label();
9447
  $pricing_class = 'upgrade-mode';
9448
  if ( $show_pricing ) {
9449
  if ( $this->is_in_trial_promotion() &&
@@ -9458,9 +9644,9 @@
9458
 
9459
  // Add upgrade/pricing page.
9460
  $this->add_submenu_item(
9461
- $this->get_text( $pricing_cta_slug ) . '&nbsp;&nbsp;' . ( is_rtl() ? '&#x2190;' : '&#x27a4;' ),
9462
  array( &$this, '_pricing_page_render' ),
9463
- $this->get_plugin_name() . ' &ndash; ' . $this->get_text( 'pricing' ),
9464
  'manage_options',
9465
  'pricing',
9466
  'Freemius::_clean_admin_content_section',
@@ -9640,7 +9826,7 @@
9640
 
9641
  if ( ! $this->is_activation_mode() ) {
9642
  $this->add_submenu_link_item(
9643
- $this->apply_filters( 'support_forum_submenu', $this->get_text( 'support-forum' ) ),
9644
  $this->get_support_forum_url(),
9645
  'wp-support-forum',
9646
  null,
@@ -10380,8 +10566,8 @@
10380
  $this->do_action( 'account_email_verified', $user->email );
10381
 
10382
  $this->_admin_notices->add(
10383
- $this->get_text( 'email-verified-message' ),
10384
- $this->get_text( 'right-on' ) . '!',
10385
  'success',
10386
  // Make admin sticky if account menu item is invisible,
10387
  // since the page will be auto redirected to the plugin's
@@ -10513,21 +10699,24 @@
10513
  * @return FS_Plugin_Plan[]|object
10514
  */
10515
  private function _fetch_plugin_plans() {
10516
- $this->_logger->entrance();
10517
- $api = $this->get_api_site_scope();
10518
 
10519
- $result = $api->get( '/plans.json', true );
 
 
 
10520
 
10521
- if ( $this->is_api_result_object( $result, 'plans' ) && is_array( $result->plans ) ) {
10522
- for ( $i = 0, $len = count( $result->plans ); $i < $len; $i ++ ) {
10523
- $result->plans[ $i ] = new FS_Plugin_Plan( $result->plans[ $i ] );
10524
- }
10525
 
10526
- $result = $result->plans;
10527
- }
10528
 
10529
- return $result;
10530
- }
10531
 
10532
  /**
10533
  * @author Vova Feldman (@svovaf)
@@ -10912,24 +11101,18 @@
10912
  }
10913
 
10914
  $this->_admin_notices->add_sticky(
10915
- FS_Plan_Manager::instance()->has_free_plan( $plans ) ?
10916
- sprintf(
10917
- $this->get_text( 'addon-successfully-upgraded-message' ),
10918
- $addon->title
10919
- ) . ' ' . $this->get_latest_download_link(
10920
- $this->get_text( 'download-latest-version' ),
10921
- $addon_id
10922
- )
10923
- :
10924
- sprintf(
10925
- $this->get_text( 'addon-successfully-purchased-message' ),
10926
- $addon->title
10927
- ) . ' ' . $this->get_latest_download_link(
10928
- $this->get_text( 'download-latest-version' ),
10929
- $addon_id
10930
- ),
10931
  'addon_plan_upgraded_' . $addon->slug,
10932
- $this->get_text( 'yee-haw' ) . '!'
10933
  );
10934
  }
10935
  }
@@ -10974,11 +11157,11 @@
10974
  if ( ! self::$_global_admin_notices->has_sticky( 'api_blocked' ) ) {
10975
  self::$_global_admin_notices->add(
10976
  sprintf(
10977
- $this->get_text( 'server-blocking-access' ),
10978
  $this->get_plugin_name(),
10979
  '<a href="' . $api->get_url() . '" target="_blank">' . $api->get_url() . '</a>'
10980
- ) . '<br> ' . $this->get_text( 'server-error-message' ) . var_export( $site->error, true ),
10981
- $this->get_text( 'oops' ) . '...',
10982
  'error',
10983
  $background,
10984
  false,
@@ -10988,8 +11171,8 @@
10988
  } else {
10989
  // Authentication params are broken.
10990
  $this->_admin_notices->add(
10991
- $this->get_text( 'wrong-authentication-param-message' ),
10992
- $this->get_text( 'oops' ) . '...',
10993
  'error'
10994
  );
10995
  }
@@ -11105,6 +11288,8 @@
11105
  }
11106
  }
11107
 
 
 
11108
  if ( $this->has_paid_plan() ) {
11109
  switch ( $plan_change ) {
11110
  case 'none':
@@ -11116,19 +11301,19 @@
11116
  if ( $plan->is_free() ) {
11117
  $this->_admin_notices->add(
11118
  sprintf(
11119
- $this->get_text( 'plan-did-not-change-message' ),
11120
- '<i><b>' . $plan->title . ( $this->is_trial() ? ' ' . $this->get_text( 'trial' ) : '' ) . '</b></i>'
11121
  ) . ' ' . sprintf(
11122
  '<a href="%s">%s</a>',
11123
  $this->contact_url(
11124
  'bug',
11125
- sprintf( $this->get_text( 'plan-did-not-change-email-message' ),
11126
  strtoupper( $plan->name )
11127
  )
11128
  ),
11129
- $this->get_text( 'contact-us-here' )
11130
  ),
11131
- $this->get_text( 'hmm' ) . '...'
11132
  );
11133
  }
11134
  }
@@ -11136,11 +11321,11 @@
11136
  case 'upgraded':
11137
  $this->_admin_notices->add_sticky(
11138
  sprintf(
11139
- $this->get_text( 'plan-upgraded-message' ),
11140
  '<i>' . $this->get_plugin_name() . '</i>'
11141
  ) . $this->get_complete_upgrade_instructions(),
11142
  'plan_upgraded',
11143
- $this->get_text( 'yee-haw' ) . '!'
11144
  );
11145
 
11146
  $this->_admin_notices->remove_sticky( array(
@@ -11153,7 +11338,7 @@
11153
  case 'changed':
11154
  $this->_admin_notices->add_sticky(
11155
  sprintf(
11156
- $this->get_text( 'plan-changed-to-x-message' ),
11157
  $this->_site->plan->title
11158
  ),
11159
  'plan_changed'
@@ -11168,41 +11353,41 @@
11168
  break;
11169
  case 'downgraded':
11170
  $this->_admin_notices->add_sticky(
11171
- sprintf( $this->get_text( 'license-expired-blocking-message' ), $this->_module_type ),
11172
  'license_expired',
11173
- $this->get_text( 'hmm' ) . '...'
11174
  );
11175
  $this->_admin_notices->remove_sticky( 'plan_upgraded' );
11176
  break;
11177
  case 'cancelled':
11178
  $this->_admin_notices->add(
11179
- $this->get_text( 'license-cancelled' ) . ' ' .
11180
  sprintf(
11181
  '<a href="%s">%s</a>',
11182
  $this->contact_url( 'bug' ),
11183
- $this->get_text( 'contact-us-here' )
11184
  ),
11185
- $this->get_text( 'hmm' ) . '...',
11186
  'error'
11187
  );
11188
  $this->_admin_notices->remove_sticky( 'plan_upgraded' );
11189
  break;
11190
  case 'expired':
11191
  $this->_admin_notices->add_sticky(
11192
- sprintf( $this->get_text( 'license-expired-non-blocking-message' ), $this->_site->plan->title ),
11193
  'license_expired',
11194
- $this->get_text( 'hmm' ) . '...'
11195
  );
11196
  $this->_admin_notices->remove_sticky( 'plan_upgraded' );
11197
  break;
11198
  case 'trial_started':
11199
  $this->_admin_notices->add_sticky(
11200
  sprintf(
11201
- $this->get_text( 'trial-started-message' ),
11202
  '<i>' . $this->get_plugin_name() . '</i>'
11203
  ) . $this->get_complete_upgrade_instructions( $this->_storage->trial_plan->title ),
11204
  'trial_started',
11205
- $this->get_text( 'yee-haw' ) . '!'
11206
  );
11207
 
11208
  $this->_admin_notices->remove_sticky( array(
@@ -11211,9 +11396,9 @@
11211
  break;
11212
  case 'trial_expired':
11213
  $this->_admin_notices->add_sticky(
11214
- $this->get_text( 'trial-expired-message' ),
11215
  'trial_expired',
11216
- $this->get_text( 'hmm' ) . '...'
11217
  );
11218
  $this->_admin_notices->remove_sticky( array(
11219
  'trial_started',
@@ -11281,23 +11466,22 @@
11281
  if ( ! $background ) {
11282
  $this->_admin_notices->add( sprintf(
11283
  '%s %s',
11284
- $this->get_text( 'license-activation-failed-message' ),
11285
  ( is_object( $license ) && isset( $license->error ) ?
11286
  $license->error->message :
11287
  sprintf( '%s<br><code>%s</code>',
11288
- $this->get_text( 'server-error-message' ),
11289
  var_export( $license, true )
11290
  )
11291
  )
11292
  ),
11293
- $this->get_text( 'hmm' ) . '...',
11294
  'error'
11295
  );
11296
  }
11297
 
11298
  return;
11299
  }
11300
-
11301
  $premium_license = new FS_Plugin_License( $license );
11302
 
11303
  // Updated site plan.
@@ -11312,10 +11496,10 @@
11312
 
11313
  if ( ! $background ) {
11314
  $this->_admin_notices->add_sticky(
11315
- $this->get_text( 'license-activated-message' ) .
11316
  $this->get_complete_upgrade_instructions(),
11317
  'license_activated',
11318
- $this->get_text( 'yee-haw' ) . '!'
11319
  );
11320
  }
11321
 
@@ -11334,10 +11518,12 @@
11334
  protected function _deactivate_license( $show_notice = true ) {
11335
  $this->_logger->entrance();
11336
 
 
 
11337
  if ( ! is_object( $this->_license ) ) {
11338
  $this->_admin_notices->add(
11339
- sprintf( $this->get_text( 'no-active-license-message' ), $this->_site->plan->title ),
11340
- $this->get_text( 'hmm' ) . '...'
11341
  );
11342
 
11343
  return;
@@ -11348,9 +11534,9 @@
11348
 
11349
  if ( isset( $license->error ) ) {
11350
  $this->_admin_notices->add(
11351
- $this->get_text( 'license-deactivation-failed-message' ) . '<br> ' .
11352
- $this->get_text( 'server-error-message' ) . ' ' . var_export( $license->error, true ),
11353
- $this->get_text( 'hmm' ) . '...',
11354
  'error'
11355
  );
11356
 
@@ -11375,8 +11561,8 @@
11375
 
11376
  if ( $show_notice ) {
11377
  $this->_admin_notices->add(
11378
- sprintf( $this->get_text( 'license-deactivation-message' ), $this->_site->plan->title ),
11379
- $this->get_text( 'ok' )
11380
  );
11381
  }
11382
 
@@ -11424,7 +11610,7 @@
11424
  $this->_admin_notices->remove_sticky( 'plan_upgraded' );
11425
 
11426
  $this->_admin_notices->add(
11427
- sprintf( $this->get_text( 'plan-x-downgraded-message' ),
11428
  $plan->title,
11429
  human_time_diff( time(), strtotime( $this->_license->expiration ) )
11430
  )
@@ -11434,8 +11620,8 @@
11434
  $this->_store_site();
11435
  } else {
11436
  $this->_admin_notices->add(
11437
- $this->get_text( 'plan-downgraded-failure-message' ),
11438
- $this->get_text( 'oops' ) . '...',
11439
  'error'
11440
  );
11441
  }
@@ -11452,11 +11638,14 @@
11452
  function start_trial( $plan_name = false ) {
11453
  $this->_logger->entrance();
11454
 
 
 
 
11455
  if ( $this->is_trial() ) {
11456
  // Already in trial mode.
11457
  $this->_admin_notices->add(
11458
- sprintf( $this->get_text( 'in-trial-mode' ), $this->_module_type ),
11459
- $this->get_text( 'oops' ) . '...',
11460
  'error'
11461
  );
11462
 
@@ -11466,8 +11655,8 @@
11466
  if ( $this->_site->is_trial_utilized() ) {
11467
  // Trial was already utilized.
11468
  $this->_admin_notices->add(
11469
- $this->get_text( 'trial-utilized' ),
11470
- $this->get_text( 'oops' ) . '...',
11471
  'error'
11472
  );
11473
 
@@ -11480,8 +11669,8 @@
11480
  if ( false === $plan ) {
11481
  // Plan doesn't exist.
11482
  $this->_admin_notices->add(
11483
- sprintf( $this->get_text( 'trial-plan-x-not-exist' ), $plan_name ),
11484
- $this->get_text( 'oops' ) . '...',
11485
  'error'
11486
  );
11487
 
@@ -11491,8 +11680,8 @@
11491
  if ( ! $plan->has_trial() ) {
11492
  // Plan doesn't exist.
11493
  $this->_admin_notices->add(
11494
- sprintf( $this->get_text( 'plan-x-no-trial' ), $plan_name ),
11495
- $this->get_text( 'oops' ) . '...',
11496
  'error'
11497
  );
11498
 
@@ -11502,8 +11691,8 @@
11502
  if ( ! $this->has_trial_plan() ) {
11503
  // None of the plans have a trial.
11504
  $this->_admin_notices->add(
11505
- sprintf( $this->get_text( 'no-trials' ), $this->_module_type ),
11506
- $this->get_text( 'oops' ) . '...',
11507
  'error'
11508
  );
11509
 
@@ -11521,9 +11710,9 @@
11521
  if ( ! $this->is_api_result_entity( $plan ) ) {
11522
  // Some API error while trying to start the trial.
11523
  $this->_admin_notices->add(
11524
- sprintf( $this->get_text( 'unexpected-api-error' ), $this->_module_type )
11525
  . ' ' . var_export( $plan, true ),
11526
- $this->get_text( 'oops' ) . '...',
11527
  'error'
11528
  );
11529
 
@@ -11547,10 +11736,13 @@
11547
  private function _cancel_trial() {
11548
  $this->_logger->entrance();
11549
 
 
 
 
11550
  if ( ! $this->is_trial() ) {
11551
  $this->_admin_notices->add(
11552
- $this->get_text( 'trial-cancel-no-trial-message' ),
11553
- $this->get_text( 'oops' ) . '...',
11554
  'error'
11555
  );
11556
 
@@ -11600,7 +11792,7 @@
11600
  ! $this->deactivate_premium_only_addon_without_license( true )
11601
  ) {
11602
  $this->_admin_notices->add(
11603
- sprintf( $this->get_text( 'trial-cancel-message' ), $this->_storage->trial_plan->title )
11604
  );
11605
  }
11606
 
@@ -11608,8 +11800,8 @@
11608
  unset( $this->_storage->trial_plan );
11609
  } else {
11610
  $this->_admin_notices->add(
11611
- $this->get_text( 'trial-cancel-failure-message' ),
11612
- $this->get_text( 'oops' ) . '...',
11613
  'error'
11614
  );
11615
  }
@@ -11846,21 +12038,26 @@
11846
  if ( ! $background ) {
11847
  $this->_admin_notices->add(
11848
  sprintf(
11849
- $this->get_text( 'version-x-released' ) . ' ' . $this->get_text( 'please-download-x' ),
 
11850
  $update->version,
11851
  sprintf(
11852
  '<a href="%s" target="_blank">%s</a>',
11853
  $this->get_account_url( 'download_latest' ),
11854
- sprintf( $this->get_text( 'latest-x-version' ), $this->_site->plan->title )
 
 
 
 
11855
  )
11856
  ),
11857
- $this->get_text( 'new' ) . '!'
11858
  );
11859
  }
11860
  } else if ( false === $new_version && ! $background ) {
11861
  $this->_admin_notices->add(
11862
- $this->get_text( 'you-have-latest' ),
11863
- $this->get_text( 'you-are-good' )
11864
  );
11865
  }
11866
 
@@ -12126,7 +12323,7 @@
12126
 
12127
  if ( ! isset( $result->error ) ) {
12128
  $this->_admin_notices->add( sprintf(
12129
- $this->get_text( 'verification-email-sent-message' ),
12130
  sprintf( '<a href="mailto:%1s">%2s</a>', esc_url( $this->_user->email ), $this->_user->email )
12131
  ) );
12132
  } else {
@@ -12214,6 +12411,9 @@
12214
  $plugin_id = fs_request_get( 'plugin_id', $this->get_id() );
12215
  $action = fs_get_action();
12216
 
 
 
 
12217
  switch ( $action ) {
12218
  case 'delete_account':
12219
  check_admin_referer( $action );
@@ -12298,20 +12498,20 @@
12298
  $candidate_email = fs_request_get( 'candidate_email', '' );
12299
 
12300
  if ( $this->init_change_owner( $candidate_email ) ) {
12301
- $this->_admin_notices->add( sprintf( $this->get_text( 'change-owner-request-sent-x' ), '<b>' . $this->_user->email . '</b>' ) );
12302
  }
12303
  break;
12304
  case 'owner_confirmed':
12305
  $candidate_email = fs_request_get( 'candidate_email', '' );
12306
 
12307
- $this->_admin_notices->add( sprintf( $this->get_text( 'change-owner-request_owner-confirmed' ), '<b>' . $candidate_email . '</b>' ) );
12308
  break;
12309
  case 'candidate_confirmed':
12310
  if ( $this->complete_change_owner() ) {
12311
  $this->_admin_notices->add_sticky(
12312
- sprintf( $this->get_text( 'change-owner-request_candidate-confirmed' ), '<b>' . $this->_user->email . '</b>' ),
12313
  'ownership_changed',
12314
- $this->get_text( 'congrats' ) . '!'
12315
  );
12316
  } else {
12317
  // @todo Handle failed ownership change message.
@@ -12331,23 +12531,23 @@
12331
  switch ( $result->error->code ) {
12332
  case 'user_exist':
12333
  $this->_admin_notices->add(
12334
- $this->get_text( 'user-exist-message' ) . ' ' .
12335
- sprintf( $this->get_text( 'user-exist-message_ownership' ), $this->_module_type, '<b>' . $new_email . '</b>' ) .
12336
  sprintf(
12337
  '<a style="margin-left: 10px;" href="%s"><button class="button button-primary">%s &nbsp;&#10140;</button></a>',
12338
  $this->get_account_url( 'change_owner', array(
12339
  'state' => 'init',
12340
  'candidate_email' => $new_email
12341
  ) ),
12342
- $this->get_text( 'change-ownership' )
12343
  ),
12344
- $this->get_text( 'oops' ) . '...',
12345
  'error'
12346
  );
12347
  break;
12348
  }
12349
  } else {
12350
- $this->_admin_notices->add( $this->get_text( 'email-updated-message' ) );
12351
  }
12352
 
12353
  return;
@@ -12359,12 +12559,12 @@
12359
 
12360
  if ( isset( $result->error ) ) {
12361
  $this->_admin_notices->add(
12362
- $this->get_text( 'name-update-failed-message' ),
12363
- $this->get_text( 'oops' ) . '...',
12364
  'error'
12365
  );
12366
  } else {
12367
- $this->_admin_notices->add( $this->get_text( 'name-updated-message' ) );
12368
  }
12369
 
12370
  return;
@@ -12424,8 +12624,10 @@
12424
  $this->do_action( 'account_property_edit', 'site', $site_property, $site_property_value );
12425
 
12426
  $this->_admin_notices->add( sprintf(
12427
- $this->get_text( 'x-updated' ),
12428
- '<b>' . str_replace( '_', ' ', $p ) . '</b>' ) );
 
 
12429
 
12430
  return;
12431
  }
@@ -12487,6 +12689,8 @@
12487
  function _affiliation_page_render() {
12488
  $this->_logger->entrance();
12489
 
 
 
12490
  fs_enqueue_local_style( 'fs_affiliation', '/admin/affiliation.css' );
12491
 
12492
  $vars = array( 'id' => $this->_module_id );
@@ -12561,8 +12765,8 @@
12561
 
12562
  if ( ! $this->is_registered() && $this->is_org_repo_compliant() ) {
12563
  $this->_admin_notices->add(
12564
- sprintf( $this->get_text( 'addons-info-external-message' ), '<b>' . $this->get_plugin_name() . '</b>' ),
12565
- $this->get_text( 'heads-up' ),
12566
  'update-nag'
12567
  );
12568
  }
@@ -12880,7 +13084,7 @@
12880
  $trial_period = $this->_trial_days;
12881
  $require_payment = $this->_is_trial_require_payment;
12882
  $trial_url = $this->get_trial_url();
12883
- $plans_string = strtolower( $this->get_text( 'awesome' ) );
12884
 
12885
  if ( $this->is_registered() ) {
12886
  // If opted-in, override trial with up to date data from API.
@@ -12920,7 +13124,7 @@
12920
  }
12921
 
12922
  $message = sprintf(
12923
- $this->get_text( 'hey' ) . '! ' . $this->get_text( 'trial-x-promotion-message' ),
12924
  sprintf( '<b>%s</b>', $this->get_plugin_name() ),
12925
  $plans_string,
12926
  $trial_period
@@ -12928,15 +13132,15 @@
12928
 
12929
  // "No Credit-Card Required" or "No Commitment for N Days".
12930
  $cc_string = $require_payment ?
12931
- sprintf( $this->get_text( 'no-commitment-for-x-days' ), $trial_period ) :
12932
- $this->get_text( 'no-cc-required' ) . '!';
12933
 
12934
 
12935
  // Start trial button.
12936
  $button = ' ' . sprintf(
12937
  '<a style="margin-left: 10px; vertical-align: super;" href="%s"><button class="button button-primary">%s &nbsp;&#10140;</button></a>',
12938
  $trial_url,
12939
- $this->get_text( 'start-free-trial' )
12940
  );
12941
 
12942
  $this->_admin_notices->add_sticky(
@@ -12976,8 +13180,6 @@
12976
  if (
12977
  // Product has no affiliate program.
12978
  ! $this->has_affiliate_program() ||
12979
- // User is already an affiliate.
12980
- is_object( $this->affiliate ) ||
12981
  // User has applied for an affiliate account.
12982
  ! empty( $this->_storage->affiliate_application_data ) ) {
12983
  return false;
@@ -13013,7 +13215,7 @@
13013
  }
13014
 
13015
  $message = sprintf(
13016
- $this->get_text( 'become-an-ambassador-admin-notice' ),
13017
  sprintf( '<strong>%s</strong>', $this->get_plugin_name() ),
13018
  $this->get_module_label( true )
13019
  );
@@ -13022,7 +13224,7 @@
13022
  $button = ' ' . sprintf(
13023
  '<a style="display: block; margin-top: 10px;" href="%s"><button class="button button-primary">%s &nbsp;&#10140;</button></a>',
13024
  $this->_get_admin_page_url( 'affiliation' ),
13025
- $this->get_text( 'learn-more' ) . '...'
13026
  );
13027
 
13028
  $this->_admin_notices->add_sticky(
@@ -13139,7 +13341,7 @@
13139
  if ( $this->is_registered() ) {
13140
  if ( ! $this->is_paying() && $this->has_paid_plan() ) {
13141
  $this->add_plugin_action_link(
13142
- $this->get_text( 'upgrade' ),
13143
  $this->get_upgrade_url(),
13144
  false,
13145
  7,
@@ -13149,7 +13351,7 @@
13149
 
13150
  if ( $this->has_addons() ) {
13151
  $this->add_plugin_action_link(
13152
- $this->get_text( 'add-ons' ),
13153
  $this->_get_admin_page_url( 'addons' ),
13154
  false,
13155
  9,
@@ -13177,11 +13379,9 @@
13177
  add_action( 'admin_footer', array( &$this, '_add_license_activation_dialog_box' ) );
13178
  }
13179
 
13180
- $link_text = $this->get_text(
13181
- $this->is_free_plan() ?
13182
- 'activate-license' :
13183
- 'change-license'
13184
- );
13185
 
13186
  $this->add_plugin_action_link(
13187
  $link_text,
@@ -13242,14 +13442,14 @@
13242
 
13243
  if ( $this->is_registered() ) {
13244
  if ( $this->is_tracking_allowed() ) {
13245
- $link_text_id = 'opt-out';
13246
  } else {
13247
- $link_text_id = 'opt-in';
13248
  }
13249
 
13250
  add_action( 'admin_footer', array( &$this, '_add_optout_dialog' ) );
13251
  } else {
13252
- $link_text_id = 'opt-in';
13253
 
13254
  $params = ! $this->is_anonymous() ?
13255
  array() :
@@ -13263,7 +13463,7 @@
13263
 
13264
  if ( $this->is_plugin() && self::is_plugins_page() ) {
13265
  $this->add_plugin_action_link(
13266
- $this->get_text( $link_text_id ),
13267
  $url,
13268
  false,
13269
  13,
@@ -13432,20 +13632,21 @@
13432
 
13433
  // @since 1.2.1.5 The free version is auto deactivated.
13434
  $deactivation_step = version_compare( $this->version, '1.2.1.5', '<' ) ?
13435
- ( '<li>' . $this->get_text( 'deactivate-free-version' ) . '.</li>' ) :
13436
  '';
13437
 
13438
  return sprintf(
13439
  ' %s: <ol><li>%s.</li>%s<li>%s (<a href="%s" target="_blank">%s</a>).</li></ol>',
13440
- $this->get_text( 'follow-steps-to-complete-upgrade' ),
13441
  $this->get_latest_download_link( sprintf(
13442
- $this->get_text( 'download-latest-x-version' ),
 
13443
  $plan_title
13444
  ) ),
13445
  $deactivation_step,
13446
- $this->get_text( 'upload-and-activate' ),
13447
  '//bit.ly/upload-wp-' . $this->_module_type . 's',
13448
- $this->get_text( 'howto-upload-activate' )
13449
  );
13450
  }
13451
 
@@ -13461,6 +13662,46 @@
13461
  return fs_text( $key, $this->_slug );
13462
  }
13463
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13464
  #----------------------------------------------------------------------------------
13465
  #region Versioning
13466
  #----------------------------------------------------------------------------------
@@ -13564,17 +13805,17 @@
13564
  if ( ! $this->is_registered() ) {
13565
  // Not registered.
13566
  self::shoot_ajax_failure( array(
13567
- 'message' => $this->get_text( 'auto-install-error-not-opted-in' ),
13568
  'code' => 'premium_installed',
13569
  ) );
13570
  }
13571
 
13572
- $plugin_id = fs_request_get( 'module_id', $this->get_id() );
13573
 
13574
  if ( ! FS_Plugin::is_valid_id( $plugin_id ) ) {
13575
  // Invalid ID.
13576
  self::shoot_ajax_failure( array(
13577
- 'message' => $this->get_text( 'auto-install-error-invalid-id' ),
13578
  'code' => 'invalid_module_id',
13579
  ) );
13580
  }
@@ -13583,21 +13824,21 @@
13583
  if ( $this->is_premium() ) {
13584
  // Already using the premium code version.
13585
  self::shoot_ajax_failure( array(
13586
- 'message' => $this->get_text( 'auto-install-error-premium-activated' ),
13587
  'code' => 'premium_installed',
13588
  ) );
13589
  }
13590
  if ( ! $this->can_use_premium_code() ) {
13591
  // Don't have access to the premium code.
13592
  self::shoot_ajax_failure( array(
13593
- 'message' => $this->get_text( 'auto-install-error-invalid-license' ),
13594
  'code' => 'invalid_license',
13595
  ) );
13596
  }
13597
  if ( ! $this->has_release_on_freemius() ) {
13598
  // Plugin is a serviceware, no premium code version.
13599
  self::shoot_ajax_failure( array(
13600
- 'message' => $this->get_text( 'auto-install-error-serviceware' ),
13601
  'code' => 'premium_version_missing',
13602
  ) );
13603
  }
@@ -13607,7 +13848,7 @@
13607
  if ( ! is_object( $addon ) ) {
13608
  // Invalid add-on ID.
13609
  self::shoot_ajax_failure( array(
13610
- 'message' => $this->get_text( 'auto-install-error-invalid-id' ),
13611
  'code' => 'invalid_module_id',
13612
  ) );
13613
  }
@@ -13615,7 +13856,7 @@
13615
  if ( $this->is_addon_activated( $plugin_id, true ) ) {
13616
  // Premium add-on version is already activated.
13617
  self::shoot_ajax_failure( array(
13618
- 'message' => $this->get_text( 'auto-install-error-premium-addon-activated' ),
13619
  'code' => 'premium_installed',
13620
  ) );
13621
  }
@@ -13687,10 +13928,11 @@
13687
  }
13688
  }
13689
 
13690
- $vars = array(
13691
- 'id' => $plugin_id,
13692
- 'slug' => $this->_slug,
13693
- );
 
13694
 
13695
  fs_require_template( 'auto-installation.php', $vars );
13696
  }
@@ -13960,7 +14202,7 @@
13960
  require_once WP_FS__DIR_INCLUDES . '/customizer/class-fs-customizer-upsell-control.php';
13961
 
13962
  $customizer->add_section( 'freemius_upsell', array(
13963
- 'title' => '&#9733; ' . $this->get_text( 'view-paid-features' ),
13964
  'priority' => 1,
13965
  ) );
13966
  $customizer->add_setting( 'freemius_upsell', array(
555
  return;
556
  }
557
 
558
+ if ( version_compare( $sdk_prev_version, '1.2.3', '<' ) &&
559
+ version_compare( $sdk_version, '1.2.3', '>=' )
560
+ ) {
561
+ /**
562
+ * Starting from version 1.2.3, paths are stored as relative paths and not absolute paths; so when
563
+ * upgrading to 1.2.3, make paths relative.
564
+ *
565
+ * @author Leo Fajardo (@leorw)
566
+ */
567
+ $this->make_paths_relative();
568
+ }
569
+
570
  if ( version_compare( $sdk_prev_version, '1.1.5', '<' ) &&
571
  version_compare( $sdk_version, '1.1.5', '>=' )
572
  ) {
590
  }
591
  }
592
 
593
+ /**
594
+ * Makes paths relative.
595
+ *
596
+ * @author Leo Fajardo
597
+ * @since 1.2.3
598
+ */
599
+ private function make_paths_relative() {
600
+ $id_slug_type_path_map = self::$_accounts->get_option( 'id_slug_type_path_map', array() );
601
+
602
+ if ( isset( $id_slug_type_path_map[ $this->_module_id ]['path'] ) ) {
603
+ $id_slug_type_path_map[ $this->_module_id ]['path'] = $this->get_relative_path( $id_slug_type_path_map[ $this->_module_id ]['path'] );
604
+
605
+ self::$_accounts->set_option( 'id_slug_type_path_map', $id_slug_type_path_map, true );
606
+ }
607
+
608
+ if ( isset( $this->_storage->plugin_main_file ) ) {
609
+ $plugin_main_file = $this->_storage->plugin_main_file;
610
+
611
+ if ( isset( $plugin_main_file->path ) ) {
612
+ $this->_storage->plugin_main_file->path = $this->get_relative_path( $this->_storage->plugin_main_file->path );
613
+ } else if ( isset( $plugin_main_file->prev_path ) ) {
614
+ $this->_storage->plugin_main_file->prev_path = $this->get_relative_path( $this->_storage->plugin_main_file->prev_path );
615
+ }
616
+ }
617
+
618
+ // Remove invalid path that is still associated with the current slug if there's any.
619
+ $file_slug_map = self::$_accounts->get_option( 'file_slug_map', array() );
620
+ foreach ( $file_slug_map as $plugin_basename => $slug ) {
621
+ if ( $slug === $this->_slug &&
622
+ $plugin_basename !== $this->_plugin_basename &&
623
+ ! file_exists( $this->get_absolute_path( $plugin_basename ) )
624
+ ) {
625
+ unset( $file_slug_map[ $plugin_basename ] );
626
+ self::$_accounts->set_option( 'file_slug_map', $file_slug_map, true );
627
+
628
+ break;
629
+ }
630
+ }
631
+ }
632
+
633
  /**
634
  * @author Vova Feldman (@svovaf)
635
  * @since 1.2.2.7
926
  // Try to load the cached value of the file path.
927
  if ( isset( $this->_storage->plugin_main_file ) ) {
928
  $plugin_main_file = $this->_storage->plugin_main_file;
929
+ if ( isset( $plugin_main_file->path ) ) {
930
+ $absolute_path = $this->get_absolute_path( $plugin_main_file->path );
931
+ if ( file_exists( $absolute_path ) ) {
932
+ return $absolute_path;
933
+ }
934
  }
935
  }
936
 
949
  if ( isset( $this->_storage->plugin_main_file ) &&
950
  isset( $this->_storage->plugin_main_file->prev_path )
951
  ) {
952
+ $absolute_path = $this->get_absolute_path( $this->_storage->plugin_main_file->prev_path );
953
+ if ( file_exists( $absolute_path ) ) {
954
+ return $absolute_path;
955
  }
956
  }
957
 
958
  wp_die(
959
+ $this->get_text_inline( 'Freemius SDK couldn\'t find the plugin\'s main file. Please contact sdk@freemius.com with the current error.', 'failed-finding-main-path' ) .
960
  " Module: {$this->_slug}; SDK: " . WP_FS__SDK_VERSION . ";",
961
+ $this->get_text_inline( 'Error', 'error' ),
962
  array( 'back_link' => true )
963
  );
964
  }
974
  'path' => $id_slug_type_path_map[ $this->_module_id ]['path'],
975
  );
976
 
977
+ return $this->get_absolute_path( $id_slug_type_path_map[ $this->_module_id ]['path'] );
978
  }
979
 
980
+ /**
981
+ * @author Leo Fajardo (@leorw)
982
+ * @since 1.2.3
983
+ *
984
+ * @param string $path
985
+ *
986
+ * @return string
987
+ */
988
+ private function get_relative_path( $path ) {
989
+ $module_root_dir = $this->get_module_root_dir_path();
990
+ if ( 0 === strpos( $path, $module_root_dir ) ) {
991
+ $path = substr( $path, strlen( $module_root_dir ) );
992
+ }
993
+
994
+ return $path;
995
+ }
996
+
997
+ /**
998
+ * @author Leo Fajardo (@leorw)
999
+ * @since 1.2.3
1000
+ *
1001
+ * @param string $path
1002
+ * @param string|bool $module_type
1003
+ *
1004
+ * @return string
1005
+ */
1006
+ private function get_absolute_path( $path, $module_type = false ) {
1007
+ $module_root_dir = $this->get_module_root_dir_path( $module_type );
1008
+ if ( 0 !== strpos( $path, $module_root_dir ) ) {
1009
+ $path = fs_normalize_path( $module_root_dir . $path );
1010
+ }
1011
+
1012
+ return $path;
1013
+ }
1014
+
1015
+ /**
1016
+ * @author Leo Fajardo (@leorw)
1017
+ * @since 1.2.3
1018
+ *
1019
+ * @param string|bool $module_type
1020
+ *
1021
+ * @return string
1022
+ */
1023
+ private function get_module_root_dir_path( $module_type = false ) {
1024
+ $is_plugin = empty( $module_type ) ?
1025
+ $this->is_plugin() :
1026
+ ( WP_FS__MODULE_TYPE_PLUGIN === $module_type );
1027
+
1028
+ return fs_normalize_path( trailingslashit( $is_plugin ?
1029
+ WP_PLUGIN_DIR :
1030
+ get_theme_root() ) );
1031
+ }
1032
+
1033
  /**
1034
  * @author Leo Fajardo (@leorw)
1035
  *
1059
  * @author Vova Feldman (@svovaf)
1060
  * @since 1.2.3
1061
  */
1062
+ ! file_exists( $this->get_absolute_path(
1063
+ $id_slug_type_path_map[ $module_id ]['path'],
1064
+ $id_slug_type_path_map[ $module_id ]['type']
1065
+ ) )
1066
  ) {
1067
  $caller_main_file_and_type = $this->get_caller_main_file_and_type();
1068
 
1153
  */
1154
 
1155
  if ( $caller_file_path == fs_normalize_path( realpath( trailingslashit( $themes_dir ) . basename( dirname( $caller_file_path ) ) . '/' . basename( $caller_file_path ) ) ) ) {
1156
+ $module_type = WP_FS__MODULE_TYPE_THEME;
1157
+
1158
+ /**
1159
+ * Relative path of the theme, e.g.:
1160
+ * `my-theme/functions.php`
1161
+ *
1162
+ * @author Leo Fajardo (@leorw)
1163
+ */
1164
+ $caller_file_candidate = basename( dirname( $caller_file_path ) ) .
1165
+ '/' .
1166
+ basename( $caller_file_path );
1167
+
1168
  continue;
1169
  }
1170
  }
1182
 
1183
  if ( isset( $caller_map[ $caller_file_hash ] ) ) {
1184
  $module_type = WP_FS__MODULE_TYPE_PLUGIN;
1185
+ $caller_file_candidate = plugin_basename( $caller_map[ $caller_file_hash ] );
1186
  }
1187
  }
1188
 
1274
 
1275
  $reason_found_better_plugin = array(
1276
  'id' => self::REASON_FOUND_A_BETTER_PLUGIN,
1277
+ 'text' => sprintf( $this->get_text_inline( 'I found a better %s', 'reason-found-a-better-plugin' ), $module_type ),
1278
  'input_type' => 'textfield',
1279
+ 'input_placeholder' => sprintf( $this->get_text_inline( "What's the %s's name?", 'placeholder-plugin-name' ), $module_type ),
1280
  );
1281
 
1282
  $reason_temporary_deactivation = array(
1283
  'id' => self::REASON_TEMPORARY_DEACTIVATION,
1284
  'text' => sprintf(
1285
+ $this->get_text_inline( "It's a temporary %s. I'm just debugging an issue.", 'reason-temporary-x' ),
1286
  strtolower( $this->is_plugin() ?
1287
+ $this->get_text_inline( 'Deactivation', 'deactivation' ) :
1288
+ $this->get_text_inline( 'Theme Switch', 'theme-switch' )
1289
  )
1290
  ),
1291
  'input_type' => '',
1294
 
1295
  $reason_other = array(
1296
  'id' => self::REASON_OTHER,
1297
+ 'text' => $this->get_text_inline( 'Other', 'reason-other' ),
1298
  'input_type' => 'textfield',
1299
  'input_placeholder' => ''
1300
  );
1302
  $long_term_user_reasons = array(
1303
  array(
1304
  'id' => self::REASON_NO_LONGER_NEEDED,
1305
+ 'text' => sprintf( $this->get_text_inline( 'I no longer need the %s', 'reason-no-longer-needed' ), $module_type ),
1306
  'input_type' => '',
1307
  'input_placeholder' => ''
1308
  ),
1309
  $reason_found_better_plugin,
1310
  array(
1311
  'id' => self::REASON_NEEDED_FOR_A_SHORT_PERIOD,
1312
+ 'text' => sprintf( $this->get_text_inline( 'I only needed the %s for a short period', 'reason-needed-for-a-short-period' ), $module_type ),
1313
  'input_type' => '',
1314
  'input_placeholder' => ''
1315
  ),
1316
  array(
1317
  'id' => self::REASON_BROKE_MY_SITE,
1318
+ 'text' => sprintf( $this->get_text_inline( 'The %s broke my site', 'reason-broke-my-site' ), $module_type ),
1319
  'input_type' => '',
1320
  'input_placeholder' => '',
1321
  'internal_message' => $contact_support_template
1322
  ),
1323
  array(
1324
  'id' => self::REASON_SUDDENLY_STOPPED_WORKING,
1325
+ 'text' => sprintf( $this->get_text_inline( 'The %s suddenly stopped working', 'reason-suddenly-stopped-working' ), $module_type ),
1326
  'input_type' => '',
1327
  'input_placeholder' => '',
1328
  'internal_message' => $contact_support_template
1332
  if ( $this->is_paying() ) {
1333
  $long_term_user_reasons[] = array(
1334
  'id' => self::REASON_CANT_PAY_ANYMORE,
1335
+ 'text' => $this->get_text_inline( "I can't pay for it anymore", 'reason-cant-pay-anymore' ),
1336
  'input_type' => 'textfield',
1337
+ 'input_placeholder' => $this->get_text_inline( 'What price would you feel comfortable paying?', 'placeholder-comfortable-price' )
1338
  );
1339
  }
1340
 
1341
  $reason_dont_share_info = array(
1342
  'id' => self::REASON_DONT_LIKE_TO_SHARE_MY_INFORMATION,
1343
+ 'text' => $this->get_text_inline( "I don't like to share my information with you", 'reason-dont-like-to-share-my-information' ),
1344
  'input_type' => '',
1345
  'input_placeholder' => ''
1346
  );
1361
  'non-registered-and-non-anonymous-short-term' => array(
1362
  array(
1363
  'id' => self::REASON_DIDNT_WORK,
1364
+ 'text' => sprintf( $this->get_text_inline( "The %s didn't work", 'reason-didnt-work' ), $module_type ),
1365
  'input_type' => '',
1366
  'input_placeholder' => ''
1367
  ),
1371
  'short-term' => array(
1372
  array(
1373
  'id' => self::REASON_COULDNT_MAKE_IT_WORK,
1374
+ 'text' => $this->get_text_inline( "I couldn't understand how to make it work", 'reason-couldnt-make-it-work' ),
1375
  'input_type' => '',
1376
  'input_placeholder' => '',
1377
  'internal_message' => $contact_support_template
1379
  $reason_found_better_plugin,
1380
  array(
1381
  'id' => self::REASON_GREAT_BUT_NEED_SPECIFIC_FEATURE,
1382
+ 'text' => sprintf( $this->get_text_inline( "The %s is great, but I need specific feature that you don't support", 'reason-great-but-need-specific-feature' ), $module_type ),
1383
  'input_type' => 'textarea',
1384
+ 'input_placeholder' => $this->get_text_inline( 'What feature?', 'placeholder-feature' )
1385
  ),
1386
  array(
1387
  'id' => self::REASON_NOT_WORKING,
1388
+ 'text' => sprintf( $this->get_text_inline( 'The %s is not working', 'reason-not-working' ), $module_type ),
1389
  'input_type' => 'textarea',
1390
+ 'input_placeholder' => $this->get_text_inline( "Kindly share what didn't work so we can fix it for future users...", 'placeholder-share-what-didnt-work' )
1391
  ),
1392
  array(
1393
  'id' => self::REASON_NOT_WHAT_I_WAS_LOOKING_FOR,
1394
+ 'text' => $this->get_text_inline( "It's not what I was looking for", 'reason-not-what-i-was-looking-for' ),
1395
  'input_type' => 'textarea',
1396
+ 'input_placeholder' => $this->get_text_inline( "What you've been looking for?", 'placeholder-what-youve-been-looking-for' )
1397
  ),
1398
  array(
1399
  'id' => self::REASON_DIDNT_WORK_AS_EXPECTED,
1400
+ 'text' => sprintf( $this->get_text_inline( "The %s didn't work as expected", 'reason-didnt-work-as-expected' ), $module_type ),
1401
  'input_type' => 'textarea',
1402
+ 'input_placeholder' => $this->get_text_inline( 'What did you expect?', 'placeholder-what-did-you-expect' )
1403
  )
1404
  )
1405
  );
1935
 
1936
  self::$_static_logger->entrance();
1937
 
1938
+ $title = sprintf( '%s [v.%s]', fs_text_inline( 'Freemius Debug' ), WP_FS__SDK_VERSION );
1939
 
1940
  if ( WP_FS__DEV_MODE ) {
1941
  // Add top-level debug menu item.
2342
  *
2343
  * @return string
2344
  */
2345
+ function get_anonymous_id() {
2346
+ $unique_id = self::$_accounts->get_option( 'unique_id' );
2347
 
2348
+ if ( empty( $unique_id ) || ! is_string( $unique_id ) ) {
2349
+ $key = get_site_url();
2350
 
2351
+ // If localhost, assign microtime instead of domain.
2352
+ if ( WP_FS__IS_LOCALHOST ||
2353
+ false !== strpos( $key, 'localhost' ) ||
2354
+ false === strpos( $key, '.' )
2355
+ ) {
2356
+ $key = microtime();
2357
+ }
2358
 
2359
+ /**
2360
+ * Base the unique identifier on the WP secure authentication key. Which
2361
+ * turns the key into a secret anonymous identifier.
2362
+ *
2363
+ * @author Vova Feldman (@svovaf)
2364
+ * @since 1.2.3
2365
+ */
2366
+ $unique_id = md5( $key . SECURE_AUTH_KEY );
2367
 
2368
+ self::$_accounts->set_option( 'unique_id', $unique_id, true );
2369
+ }
2370
 
2371
+ $this->_logger->departure( $unique_id );
2372
 
2373
+ return $unique_id;
2374
+ }
2375
 
2376
  /**
2377
  * @author Vova Feldman (@svovaf)
2451
  // $admin_email = get_option( 'admin_email' );
2452
  $admin_email = $current_user->user_email;
2453
 
2454
+ // Aliases.
2455
+ $deactivate_plugin_title = $this->esc_html_inline( 'That\'s exhausting, please deactivate', 'deactivate-plugin-title' );
2456
+ $deactivate_plugin_desc = $this->esc_html_inline( 'We feel your frustration and sincerely apologize for the inconvenience. Hope to see you again in the future.', 'deactivate-plugin-desc' );
2457
+ $install_previous_title = $this->esc_html_inline( 'Let\'s try your previous version', 'install-previous-title' );
2458
+ $install_previous_desc = $this->esc_html_inline( 'Uninstall this version and install the previous one.', 'install-previous-desc' );
2459
+ $fix_issue_title = $this->esc_html_inline( 'Yes - I\'m giving you a chance to fix it', 'fix-issue-title' );
2460
+ $fix_issue_desc = $this->esc_html_inline( 'We will do our best to whitelist your server and resolve this issue ASAP. You will get a follow-up email to %s once we have an update.', 'fix-issue-desc' );
2461
+ /* translators: %s: product title (e.g. "Awesome Plugin" requires an access to...) */
2462
+ $x_requires_access_to_api = $this->esc_html_inline( '%s requires an access to our API.', 'x-requires-access-to-api' );
2463
+ $sysadmin_title = $this->esc_html_inline( 'I\'m a system administrator', 'sysadmin-title' );
2464
+ $happy_to_resolve_issue_asap = $this->esc_html_inline( 'We are sure it\'s an issue on our side and more than happy to resolve it for you ASAP if you give us a chance.', 'happy-to-resolve-issue-asap' );
2465
+
2466
  $message = false;
2467
  if ( is_object( $api_result ) &&
2468
  isset( $api_result->error ) &&
2489
  if ( ! empty( $missing_methods ) ) {
2490
  $missing_methods = sprintf(
2491
  '<br><br><b>%s</b> %s',
2492
+ $this->esc_html_inline( 'Disabled method(s):', 'curl-disabled-methods' ),
2493
  $missing_methods
2494
  );
2495
  }
2496
  }
2497
 
2498
  $message = sprintf(
2499
+ $x_requires_access_to_api . ' ' .
2500
+ $this->esc_html_inline( 'We use PHP cURL library for the API calls, which is a very common library and usually installed and activated out of the box. Unfortunately, cURL is not activated (or disabled) on your server.', 'curl-missing-message' ) . ' ' .
2501
  $missing_methods .
2502
  ' %s',
2503
  '<b>' . $this->get_plugin_name() . '</b>',
2505
  '<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
2506
  sprintf(
2507
  '<a class="fs-resolve" data-type="curl" href="#"><b>%s</b></a>%s',
2508
+ $this->get_text_inline( 'I don\'t know what is cURL or how to install it, help me!', 'curl-missing-no-clue-title' ),
2509
  ' - ' . sprintf(
2510
+ $this->get_text_inline( 'We\'ll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update.', 'curl-missing-no-clue-desc' ),
2511
  '<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
2512
  )
2513
  ),
2514
  sprintf(
2515
  '<b>%s</b> - %s',
2516
+ $sysadmin_title,
2517
+ esc_html( sprintf( $this->get_text_inline( 'Great, please install cURL and enable it in your php.ini file. In addition, search for the \'disable_functions\' directive in your php.ini file and remove any disabled methods starting with \'curl_\'. To make sure it was successfully activated, use \'phpinfo()\'. Once activated, deactivate the %s and reactivate it back again.', 'curl-missing-sysadmin-desc' ), $this->get_module_label( true ) ) )
2518
  ),
2519
  sprintf(
2520
+ '<a href="%s"><b>%s</b></a> - %s',
2521
+ wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=all&amp;paged=1&amp;s=', 'deactivate-plugin_' . $this->_plugin_basename ),
2522
+ $deactivate_plugin_title,
2523
+ $deactivate_plugin_desc
2524
  )
2525
  )
2526
  );
2527
  break;
2528
  case 'cloudflare_ddos_protection':
2529
  $message = sprintf(
2530
+ $x_requires_access_to_api . ' ' .
2531
+ $this->esc_html_inline( 'From unknown reason, CloudFlare, the firewall we use, blocks the connection.', 'cloudflare-blocks-connection-message' ) . ' ' .
2532
+ $happy_to_resolve_issue_asap .
2533
  ' %s',
2534
  '<b>' . $this->get_plugin_name() . '</b>',
2535
  sprintf(
2536
  '<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
2537
  sprintf(
2538
  '<a class="fs-resolve" data-type="cloudflare" href="#"><b>%s</b></a>%s',
2539
+ $fix_issue_title,
2540
  ' - ' . sprintf(
2541
+ $fix_issue_desc,
2542
  '<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
2543
  )
2544
  ),
2545
  sprintf(
2546
+ '<a href="%s" target="_blank"><b>%s</b></a> - %s',
2547
  sprintf( 'https://wordpress.org/plugins/%s/download/', $this->_slug ),
2548
+ $install_previous_title,
2549
+ $install_previous_desc
2550
  ),
2551
  sprintf(
2552
+ '<a href="%s"><b>%s</b></a> - %s',
2553
+ wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=all&amp;paged=1&amp;s=' . '', 'deactivate-plugin_' . $this->_plugin_basename ),
2554
+ $deactivate_plugin_title,
2555
+ $deactivate_plugin_desc
2556
  )
2557
  )
2558
  );
2559
  break;
2560
  case 'squid_cache_block':
2561
  $message = sprintf(
2562
+ $x_requires_access_to_api . ' ' .
2563
+ $this->esc_html_inline( 'It looks like your server is using Squid ACL (access control lists), which blocks the connection.', 'squid-blocks-connection-message' ) .
2564
  ' %s',
2565
  '<b>' . $this->get_plugin_name() . '</b>',
2566
  sprintf(
2567
  '<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
2568
  sprintf(
2569
+ '<a class="fs-resolve" data-type="squid" href="#"><b>%s</b></a> - %s',
2570
+ $this->esc_html_inline( 'I don\'t know what is Squid or ACL, help me!', 'squid-no-clue-title' ),
2571
+ sprintf(
2572
+ $this->esc_html_inline( 'We\'ll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update.', 'squid-no-clue-desc' ),
2573
  '<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
2574
  )
2575
  ),
2576
  sprintf(
2577
  '<b>%s</b> - %s',
2578
+ $sysadmin_title,
2579
  sprintf(
2580
+ $this->esc_html_inline( 'Great, please whitelist the following domains: %s. Once you are done, deactivate the %s and activate it again.', 'squid-sysadmin-desc' ),
2581
  // We use a filter since the plugin might require additional API connectivity.
2582
  '<b>' . implode( ', ', $this->apply_filters( 'api_domains', array( 'api.freemius.com', 'wp.freemius.com' ) ) ) . '</b>',
2583
  $this->_module_type
2584
  )
2585
  ),
2586
  sprintf(
2587
+ '<a href="%s"><b>%s</b></a> - %s',
2588
+ wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=all&amp;paged=1&amp;s=', 'deactivate-plugin_' . $this->_plugin_basename ),
2589
+ $deactivate_plugin_title,
2590
+ $deactivate_plugin_desc
2591
  )
2592
  )
2593
  );
2594
  break;
2595
  // default:
2596
+ // $message = $this->get_text_inline( 'connectivity-test-fails-message' );
2597
  // break;
2598
  }
2599
  }
2601
  $message_id = 'failed_connect_api';
2602
  $type = 'error';
2603
 
2604
+ $connectivity_test_fails_message = $this->esc_html_inline( 'From unknown reason, the API connectivity test failed.', 'connectivity-test-fails-message' );
2605
+
2606
  if ( false === $message ) {
2607
  if ( $is_first_failure ) {
2608
  // First attempt failed.
2609
  $message = sprintf(
2610
+ $x_requires_access_to_api . ' ' .
2611
+ $connectivity_test_fails_message . ' ' .
2612
+ $this->esc_html_inline( 'It\'s probably a temporary issue on our end. Just to be sure, with your permission, would it be o.k to run another connectivity test?', 'connectivity-test-maybe-temporary' ) . '<br><br>' .
2613
  '%s',
2614
  '<b>' . $this->get_plugin_name() . '</b>',
2615
  sprintf(
2616
  '<div id="fs_firewall_issue_options">%s %s</div>',
2617
  sprintf(
2618
  '<a class="button button-primary fs-resolve" data-type="retry_ping" href="#">%s</a>',
2619
+ $this->get_text_inline( 'Yes - do your thing', 'yes-do-your-thing' )
2620
  ),
2621
  sprintf(
2622
  '<a href="%s" class="button">%s</a>',
2623
+ wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=all&amp;paged=1&amp;s=', 'deactivate-plugin_' . $this->_plugin_basename ),
2624
+ $this->get_text_inline( 'No - just deactivate', 'no-deactivate' )
2625
  )
2626
  )
2627
  );
2631
  } else {
2632
  // Second connectivity attempt failed.
2633
  $message = sprintf(
2634
+ $x_requires_access_to_api . ' ' .
2635
+ $connectivity_test_fails_message . ' ' .
2636
+ $happy_to_resolve_issue_asap .
2637
  ' %s',
2638
  '<b>' . $this->get_plugin_name() . '</b>',
2639
  sprintf(
2640
  '<ol id="fs_firewall_issue_options"><li>%s</li><li>%s</li><li>%s</li></ol>',
2641
  sprintf(
2642
  '<a class="fs-resolve" data-type="general" href="#"><b>%s</b></a>%s',
2643
+ $fix_issue_title,
2644
  ' - ' . sprintf(
2645
+ $fix_issue_desc,
2646
  '<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
2647
  )
2648
  ),
2649
  sprintf(
2650
+ '<a href="%s" target="_blank"><b>%s</b></a> - %s',
2651
  sprintf( 'https://wordpress.org/plugins/%s/download/', $this->_slug ),
2652
+ $install_previous_title,
2653
+ $install_previous_desc
2654
  ),
2655
  sprintf(
2656
+ '<a href="%s"><b>%s</b></a> - %s',
2657
+ wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $this->_plugin_basename . '&amp;plugin_status=all&amp;paged=1&amp;s=', 'deactivate-plugin_' . $this->_plugin_basename ),
2658
+ $deactivate_plugin_title,
2659
+ $deactivate_plugin_desc
2660
  )
2661
  )
2662
  );
2666
  $this->_admin_notices->add_sticky(
2667
  $message,
2668
  $message_id,
2669
+ $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
2670
  $type
2671
  );
2672
  }
2733
 
2734
  $this->_admin_notices->add_sticky(
2735
  sprintf(
2736
+ $this->get_text_inline( 'Thank for giving us the chance to fix it! A message was just sent to our technical staff. We will get back to you as soon as we have an update to %s. Appreciate your patience.', 'fix-request-sent-message' ),
2737
  '<a href="mailto:' . $admin_email . '">' . $admin_email . '</a>'
2738
  ),
2739
  'server_details_sent'
3009
 
3010
  $this->parse_settings( $plugin_info );
3011
 
 
 
 
 
3012
  if ( ! self::is_ajax() ) {
3013
  if ( ! $this->is_addon() || $this->is_only_premium() ) {
3014
  add_action( 'admin_menu', array( &$this, '_prepare_admin_menu' ), WP_FS__LOWEST_PRIORITY );
3124
 
3125
  $this->_admin_notices->add(
3126
  ( ! empty( $parent_name ) ?
3127
+ sprintf( $this->get_text_x_inline( '%s cannot run without %s.', 'addonX cannot run without pluginY', 'addon-x-cannot-run-without-y' ), $this->get_plugin_name(), $parent_name ) :
3128
+ sprintf( $this->get_text_x_inline( '%s cannot run without the plugin.', 'addonX cannot run...', 'addon-x-cannot-run-without-parent' ), $this->get_plugin_name() )
3129
  ),
3130
+ $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
3131
  'error'
3132
  );
3133
 
3246
  $this->_logger->api_error( $result );
3247
 
3248
  self::shoot_ajax_failure(
3249
+ sprintf( $this->get_text_inline( 'Unexpected API error. Please contact the %s\'s author with the following error.', 'unexpected-api-error' ), $this->_module_type ) .
3250
  ( $this->is_api_error( $result ) && isset( $result->error ) ?
3251
  $result->error->message :
3252
  var_export( $result, true ) )
3271
  $this->_logger->api_error( $result );
3272
 
3273
  self::shoot_ajax_failure(
3274
+ sprintf( $this->get_text_inline( 'Unexpected API error. Please contact the %s\'s author with the following error.', 'unexpected-api-error' ), $this->_module_type ) .
3275
  ( $this->is_api_error( $result ) && isset( $result->error ) ?
3276
  $result->error->message :
3277
  var_export( $result, true ) )
3679
 
3680
  if ( ! $this->is_only_premium() ) {
3681
  $this->_admin_notices->add_sticky(
3682
+ sprintf( $this->get_text_inline( 'Premium %s version was successfully activated.', 'premium-activated-message' ), $this->_module_type ),
3683
  'premium_activated',
3684
+ $this->get_text_x_inline( 'W00t',
3685
+ 'Used to express elation, enthusiasm, or triumph (especially in electronic communication).', 'woot' ) . '!'
3686
  );
3687
  }
3688
  } else {
3695
  if ( $this->is_paying() && ! $this->is_premium() ) {
3696
  $this->_admin_notices->add_sticky(
3697
  sprintf(
3698
+ /* translators: %s: License type (e.g. you have a professional license) */
3699
+ $this->get_text_inline( 'You have a %s license.', 'you-have-x-license' ),
3700
  $this->_site->plan->title
3701
  ) . $this->get_complete_upgrade_instructions(),
3702
  'plan_upgraded',
3703
+ $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!'
3704
  );
3705
  }
3706
  }
3963
 
3964
  $this->_parent->_admin_notices->add_sticky(
3965
  sprintf(
3966
+ ($is_after_trial_cancel ?
3967
+ $this->_parent->get_text_inline(
3968
+ '%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you\'ll have to purchase a license.',
3969
+ 'addon-trial-cancelled-message'
3970
+ ) :
3971
+ $this->_parent->get_text_inline(
3972
+ '%s is a premium only add-on. You have to purchase a license first before activating the plugin.',
3973
+ 'addon-no-license-message'
3974
+ )
3975
  ),
3976
  '<b>' . $this->_plugin->title . '</b>'
3977
  ) . ' ' . sprintf(
3978
  '<a href="%s" aria-label="%s" class="button button-primary" style="margin-left: 10px; vertical-align: middle;">%s &nbsp;&#10140;</a>',
3979
  $this->_parent->addon_url( $this->_slug ),
3980
+ esc_attr( sprintf( $this->_parent->get_text_inline( 'More information about %s', 'more-information-about-x' ), $this->_plugin->title ) ),
3981
+ $this->_parent->get_text_inline( 'Purchase License', 'purchase-license' )
3982
  ),
3983
  'no_addon_license_' . $this->_slug,
3984
+ ( $is_after_trial_cancel ? '' : $this->_parent->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...' ),
3985
  ( $is_after_trial_cancel ? 'success' : 'error' )
3986
  );
3987
 
4415
 
4416
  $this->_admin_notices->add_sticky(
4417
  sprintf(
4418
+ $this->get_text_inline( 'You should receive an activation email for %s to your mailbox at %s. Please make sure you click the activation button in that email to %s.', 'pending-activation-message' ),
4419
  '<b>' . $this->get_plugin_name() . '</b>',
4420
  '<b>' . $email . '</b>',
4421
+ ( $is_pending_trial ?
4422
+ $this->get_text_inline( 'start the trial', 'start-the-trial' ) :
4423
+ $this->get_text_inline( 'complete the install', 'complete-the-install' ) )
4424
  ),
4425
  'activation_pending',
4426
  'Thanks!'
4499
  // Show notice for new plugin installations.
4500
  $this->_admin_notices->add(
4501
  sprintf(
4502
+ $this->get_text_inline( 'You are just one step away - %s', 'you-are-step-away' ),
4503
  sprintf( '<b><a href="%s">%s</a></b>',
4504
  $this->get_activation_url(),
4505
+ sprintf( $this->get_text_x_inline( 'Complete "%s" Activation Now',
4506
+ '%s - plugin name. As complete "PluginX" activation now', 'activate-x-now' ), $this->get_plugin_name() )
4507
  )
4508
  ),
4509
  '',
4516
  // Show notice for new plugin installations.
4517
  $this->_admin_notices->add_sticky(
4518
  sprintf(
4519
+ $this->get_text_inline( 'We made a few tweaks to the %s, %s', 'few-plugin-tweaks' ),
4520
  $this->_module_type,
4521
  sprintf( '<b><a href="%s">%s</a></b>',
4522
  $this->get_activation_url(),
4523
+ sprintf( $this->get_text_inline( 'Opt in to make "%s" Better!', 'optin-x-now' ), $this->get_plugin_name() )
4524
  )
4525
  ),
4526
  'connect_account',
4905
  // If activating the premium module version, add an admin notice to congratulate for an upgrade completion.
4906
  if ( $is_premium_version_activation ) {
4907
  $this->_admin_notices->add(
4908
+ sprintf( $this->get_text_inline( 'The upgrade of %s was successfully completed.', 'successful-version-upgrade-message' ), sprintf( '<b>%s</b>', $this->_plugin->title ) ),
4909
+ $this->get_text_x_inline( 'W00t',
4910
+ 'Used to express elation, enthusiasm, or triumph (especially in electronic communication).', 'woot' ) . '!'
4911
  );
4912
  }
4913
  } else if ( $this->is_anonymous() ) {
5990
  */
5991
  function get_module_label( $lowercase = false ) {
5992
  $label = $this->is_addon() ?
5993
+ $this->get_text_inline( 'Add-On', 'addon' ) :
5994
  ( $this->is_plugin() ?
5995
+ $this->get_text_inline( 'Plugin', 'plugin' ) :
5996
+ $this->get_text_inline( 'Theme', 'theme' ) );
5997
 
5998
  if ( $lowercase ) {
5999
  $label = strtolower( $label );
7250
  $error = $result->error;
7251
 
7252
  if ( in_array( $error->code, array( 'invalid_email', 'no_user' ) ) ) {
7253
+ $error = $this->get_text_inline( "We couldn't find your email address in the system, are you sure it's the right address?", 'email-not-found' );
7254
  } else if ( 'no_license' === $error->code ) {
7255
+ $error = $this->get_text_inline( "We can't see any active licenses associated with that email address, are you sure it's the right address?", 'no-active-licenses' );
7256
  } else {
7257
  $error = $error->message;
7258
  }
7263
  );
7264
 
7265
  if ( false !== $error ) {
7266
+ $licenses['error'] = sprintf( '%s... %s', $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ), strtolower( $error ) );
7267
  }
7268
 
7269
  echo json_encode( $licenses );
7296
  if ( self::is_ajax() &&
7297
  'admin-ajax.php' === $pagenow
7298
  ) {
7299
+ $referer = fs_get_raw_referer();
7300
 
7301
  if ( is_string( $referer ) ) {
7302
  $parts = explode( '?', $referer );
7353
 
7354
  /**
7355
  * @author Leo Fajardo (@leorw)
7356
+ * @since 1.2.4
7357
  */
7358
+ private function fetch_affiliate_terms() {
 
 
7359
  if ( ! is_object( $this->plugin_affiliate_terms ) ) {
7360
  $plugins_api = $this->get_api_plugin_scope();
7361
+ $affiliate_terms = $plugins_api->get( '/aff.json?type=affiliation', false, WP_FS__TIME_WEEK_IN_SEC );
7362
 
7363
  if ( ! $this->is_api_result_entity( $affiliate_terms ) ) {
7364
  return;
7366
 
7367
  $this->plugin_affiliate_terms = new FS_AffiliateTerms( $affiliate_terms );
7368
  }
7369
+ }
7370
+
7371
+ /**
7372
+ * @author Leo Fajardo (@leorw)
7373
+ * @since 1.2.4
7374
+ */
7375
+ private function fetch_affiliate_and_custom_terms() {
7376
+ if ( ! empty( $this->_storage->affiliate_application_data ) ) {
7377
+ $application_data = $this->_storage->affiliate_application_data;
7378
+ $flush = ( ! isset( $application_data['status'] ) || 'pending' === $application_data['status'] );
7379
 
 
7380
  $users_api = $this->get_api_user_scope();
7381
+ $result = $users_api->get( "/plugins/{$this->_plugin->id}/aff/{$this->plugin_affiliate_terms->id}/affiliates.json", $flush, WP_FS__TIME_WEEK_IN_SEC );
7382
  if ( $this->is_api_result_object( $result, 'affiliates' ) ) {
7383
  if ( ! empty( $result->affiliates ) ) {
7384
  $affiliate = new FS_Affiliate( $result->affiliates[0] );
7385
 
7386
+ if ( ! isset( $application_data['status'] ) || $application_data['status'] !== $affiliate->status ) {
7387
+ $application_data['status'] = $affiliate->status;
7388
+ $this->_storage->affiliate_application_data = $application_data;
7389
  }
7390
 
7391
  if ( $affiliate->is_using_custom_terms ) {
7392
+ $affiliate_terms = $users_api->get( "/plugins/{$this->_plugin->id}/affiliates/{$affiliate->id}/aff/{$affiliate->custom_affiliate_terms_id}.json", $flush, WP_FS__TIME_WEEK_IN_SEC );
7393
  if ( $this->is_api_result_entity( $affiliate_terms ) ) {
7394
  $this->custom_affiliate_terms = new FS_AffiliateTerms( $affiliate_terms );
7395
  }
7401
  }
7402
  }
7403
 
7404
+ /**
7405
+ * @author Leo Fajardo (@leorw)
7406
+ * @since 1.2.3
7407
+ */
7408
+ private function fetch_affiliate_and_terms() {
7409
+ $this->_logger->entrance();
7410
+
7411
+ $this->fetch_affiliate_terms();
7412
+ $this->fetch_affiliate_and_custom_terms();
7413
+ }
7414
+
7415
  /**
7416
  * @author Leo Fajardo
7417
  * @since 1.2.3
7480
  var_export( $next_page, true )
7481
  );
7482
  } else if ( $this->is_pending_activation() ) {
7483
+ self::shoot_ajax_failure( $this->get_text_inline( 'Account is pending activation.', 'account-is-pending-activation' ) );
7484
  }
7485
  }
7486
 
7487
+ $this->fetch_affiliate_terms();
7488
+
7489
  $api = $this->get_api_user_scope();
7490
  $result = $api->call(
7491
  ( "/plugins/{$this->_plugin->id}/aff/{$this->plugin_affiliate_terms->id}/affiliates.json" ),
7507
  }
7508
 
7509
  $affiliate_application_data = array(
7510
+ 'status' => 'pending',
7511
  'stats_description' => $affiliate['stats_description'],
7512
  'promotion_method_description' => $affiliate['promotion_method_description'],
7513
  );
8725
  return true;
8726
  }
8727
 
8728
+ /**
8729
+ * When json_decode() executed on PHP 5.2 with an invalid JSON, it will throw a PHP warning. Unfortunately, the new Theme Check doesn't allow PHP silencing and the theme review team isn't open to change that, therefore, instead of using `@json_decode()` we had to use the method without the `@` directive.
8730
+ *
8731
+ * @author Vova Feldman (@svovaf)
8732
+ * @since 1.2.3
8733
+ * @link https://themes.trac.wordpress.org/ticket/46134#comment:5
8734
+ * @link https://themes.trac.wordpress.org/ticket/46134#comment:9
8735
+ * @link https://themes.trac.wordpress.org/ticket/46134#comment:12
8736
+ * @link https://themes.trac.wordpress.org/ticket/46134#comment:14
8737
+ */
8738
+ $decoded = is_string( $response['body'] ) ?
8739
+ json_decode( $response['body'] ) :
8740
+ null;
8741
 
8742
  if ( empty( $decoded ) ) {
8743
  return false;
8832
 
8833
  if ( ! $this->is_paying_or_trial() ) {
8834
  $this->_admin_notices->add_sticky(
8835
+ sprintf( $this->get_text_x_inline( '%s activation was successfully completed.',
8836
+ 'pluginX activation was successfully...', 'plugin-x-activation-message' ), '<b>' . $this->get_plugin_name() . '</b>' ),
8837
  'activation_complete'
8838
  );
8839
  }
8844
  if ( $this->is_paying() ) {
8845
  $this->_admin_notices->add_sticky(
8846
  sprintf(
8847
+ $this->get_text_inline( 'Your account was successfully activated with the %s plan.', 'activation-with-plan-x-message' ),
8848
  $this->_site->plan->title
8849
  ) . $this->get_complete_upgrade_instructions(),
8850
  'plan_upgraded',
8851
+ $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!'
8852
  );
8853
  } else {
8854
  $this->_admin_notices->add_sticky(
8855
  sprintf(
8856
+ $this->get_text_inline( 'Your trial has been successfully started.', 'trial-started-message' ),
8857
  '<i>' . $this->get_plugin_name() . '</i>'
8858
  ) . $this->get_complete_upgrade_instructions( $this->_storage->trial_plan->title ),
8859
  'trial_started',
8860
+ $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!'
8861
  );
8862
  }
8863
  }
9142
  $install = $this->apply_filters( 'after_install_failure', $install, $args );
9143
 
9144
  $this->_admin_notices->add(
9145
+ sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' .
9146
+ $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . $install->error->message . '</b>',
9147
+ $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
9148
  'error'
9149
  );
9150
 
9198
 
9199
  if ( isset( $addon_install->error ) ) {
9200
  $this->_admin_notices->add(
9201
+ sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' .
9202
+ $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . $addon_install->error->message . '</b>',
9203
+ $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
9204
  'error'
9205
  );
9206
 
9259
 
9260
  if ( isset( $parent_install->error ) ) {
9261
  $this->_admin_notices->add(
9262
+ sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' .
9263
+ $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . $parent_install->error->message . '</b>',
9264
+ $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
9265
  'error'
9266
  );
9267
 
9518
  * @return string
9519
  */
9520
  function get_pricing_cta_label() {
9521
+ $label = $this->get_text_inline( 'Upgrade', 'upgrade' );
9522
 
9523
  if ( $this->is_in_trial_promotion() &&
9524
  ! $this->is_paying_or_trial()
9525
  ) {
9526
  // If running a trial promotion, modify the pricing to load the trial.
9527
+ $label = $this->get_text_inline( 'Start Trial', 'start-trial' );
9528
  } else if ( $this->is_paying() ) {
9529
+ $label = $this->get_text_inline( 'Pricing', 'pricing' );
9530
  }
9531
 
9532
  return $label;
9566
  if ( $this->has_affiliate_program() ) {
9567
  // Add affiliation page.
9568
  $this->add_submenu_item(
9569
+ $this->get_text_inline( 'Affiliation', 'affiliation' ),
9570
  array( &$this, '_affiliation_page_render' ),
9571
+ $this->get_plugin_name() . ' &ndash; ' . $this->get_text_inline( 'Affiliation', 'affiliation' ),
9572
  'manage_options',
9573
  'affiliation',
9574
  'Freemius::_clean_admin_content_section',
9588
 
9589
  // Add user account page.
9590
  $this->add_submenu_item(
9591
+ $this->get_text_inline( 'Account', 'account' ),
9592
  array( &$this, '_account_page_render' ),
9593
+ $this->get_plugin_name() . ' &ndash; ' . $this->get_text_inline( 'Account', 'account' ),
9594
  'manage_options',
9595
  'account',
9596
  array( &$this, '_account_page_load' ),
9601
 
9602
  // Add contact page.
9603
  $this->add_submenu_item(
9604
+ $this->get_text_inline( 'Contact Us', 'contact-us' ),
9605
  array( &$this, '_contact_page_render' ),
9606
+ $this->get_plugin_name() . ' &ndash; ' . $this->get_text_inline( 'Contact Us', 'contact-us' ),
9607
  'manage_options',
9608
  'contact',
9609
  'Freemius::_clean_admin_content_section',
9613
 
9614
  if ( $this->has_addons() ) {
9615
  $this->add_submenu_item(
9616
+ $this->get_text_inline( 'Add-Ons', 'add-ons' ),
9617
  array( &$this, '_addons_page_render' ),
9618
+ $this->get_plugin_name() . ' &ndash; ' . $this->get_text_inline( 'Add-Ons', 'add-ons' ),
9619
  'manage_options',
9620
  'addons',
9621
  array( &$this, '_addons_page_load' ),
9629
  $this->is_pricing_page_visible()
9630
  );
9631
 
9632
+ $pricing_cta_text = $this->get_pricing_cta_label();
9633
  $pricing_class = 'upgrade-mode';
9634
  if ( $show_pricing ) {
9635
  if ( $this->is_in_trial_promotion() &&
9644
 
9645
  // Add upgrade/pricing page.
9646
  $this->add_submenu_item(
9647
+ $pricing_cta_text . '&nbsp;&nbsp;' . ( is_rtl() ? '&#x2190;' : '&#x27a4;' ),
9648
  array( &$this, '_pricing_page_render' ),
9649
+ $this->get_plugin_name() . ' &ndash; ' . $this->get_text_x_inline( 'Pricing', 'noun', 'pricing' ),
9650
  'manage_options',
9651
  'pricing',
9652
  'Freemius::_clean_admin_content_section',
9826
 
9827
  if ( ! $this->is_activation_mode() ) {
9828
  $this->add_submenu_link_item(
9829
+ $this->apply_filters( 'support_forum_submenu', $this->get_text_inline( 'Support Forum', 'support-forum' ) ),
9830
  $this->get_support_forum_url(),
9831
  'wp-support-forum',
9832
  null,
10566
  $this->do_action( 'account_email_verified', $user->email );
10567
 
10568
  $this->_admin_notices->add(
10569
+ $this->get_text_inline( 'Your email has been successfully verified - you are AWESOME!', 'email-verified-message' ),
10570
+ $this->get_text_x_inline( 'Right on', 'a positive response', 'right-on' ) . '!',
10571
  'success',
10572
  // Make admin sticky if account menu item is invisible,
10573
  // since the page will be auto redirected to the plugin's
10699
  * @return FS_Plugin_Plan[]|object
10700
  */
10701
  private function _fetch_plugin_plans() {
10702
+ $this->_logger->entrance();
10703
+ $api = $this->get_api_site_scope();
10704
 
10705
+ /**
10706
+ * @since 1.2.3 When running in DEV mode, retrieve pending plans as well.
10707
+ */
10708
+ $result = $api->get( '/plans.json?show_pending=' . ( $this->has_secret_key() ? 'true' : 'false' ), true );
10709
 
10710
+ if ( $this->is_api_result_object( $result, 'plans' ) && is_array( $result->plans ) ) {
10711
+ for ( $i = 0, $len = count( $result->plans ); $i < $len; $i ++ ) {
10712
+ $result->plans[ $i ] = new FS_Plugin_Plan( $result->plans[ $i ] );
10713
+ }
10714
 
10715
+ $result = $result->plans;
10716
+ }
10717
 
10718
+ return $result;
10719
+ }
10720
 
10721
  /**
10722
  * @author Vova Feldman (@svovaf)
11101
  }
11102
 
11103
  $this->_admin_notices->add_sticky(
11104
+ sprintf(
11105
+ ( FS_Plan_Manager::instance()->has_free_plan( $plans ) ?
11106
+ $this->get_text_inline( 'Your %s Add-on plan was successfully upgraded.', 'addon-successfully-upgraded-message' ) :
11107
+ /* translators: %s:product name, e.g. Facebook add-on was successfully... */
11108
+ $this->get_text_inline( '%s Add-on was successfully purchased.', 'addon-successfully-purchased-message' ) ),
11109
+ $addon->title
11110
+ ) . ' ' . $this->get_latest_download_link(
11111
+ $this->get_text_inline( 'Download the latest version', 'download-latest-version' ),
11112
+ $addon_id
11113
+ ),
 
 
 
 
 
 
11114
  'addon_plan_upgraded_' . $addon->slug,
11115
+ $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!'
11116
  );
11117
  }
11118
  }
11157
  if ( ! self::$_global_admin_notices->has_sticky( 'api_blocked' ) ) {
11158
  self::$_global_admin_notices->add(
11159
  sprintf(
11160
+ $this->get_text_x_inline( 'Your server is blocking the access to Freemius\' API, which is crucial for %1s synchronization. Please contact your host to whitelist %2s', '%1s - plugin title, %2s - API domain', 'server-blocking-access' ),
11161
  $this->get_plugin_name(),
11162
  '<a href="' . $api->get_url() . '" target="_blank">' . $api->get_url() . '</a>'
11163
+ ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $site->error, true ),
11164
+ $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
11165
  'error',
11166
  $background,
11167
  false,
11171
  } else {
11172
  // Authentication params are broken.
11173
  $this->_admin_notices->add(
11174
+ $this->get_text_inline( 'It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.', 'wrong-authentication-param-message' ),
11175
+ $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
11176
  'error'
11177
  );
11178
  }
11288
  }
11289
  }
11290
 
11291
+ $hmm_text = $this->get_text_x_inline( 'Hmm', 'something somebody says when they are thinking about what you have just said.', 'hmm' ) . '...';
11292
+
11293
  if ( $this->has_paid_plan() ) {
11294
  switch ( $plan_change ) {
11295
  case 'none':
11301
  if ( $plan->is_free() ) {
11302
  $this->_admin_notices->add(
11303
  sprintf(
11304
+ $this->get_text_inline( 'It looks like you are still on the %s plan. If you did upgrade or change your plan, it\'s probably an issue on our side - sorry.', 'plan-did-not-change-message' ),
11305
+ '<i><b>' . $plan->title . ( $this->is_trial() ? ' ' . $this->get_text_x_inline( 'Trial', 'trial period', 'trial' ) : '' ) . '</b></i>'
11306
  ) . ' ' . sprintf(
11307
  '<a href="%s">%s</a>',
11308
  $this->contact_url(
11309
  'bug',
11310
+ sprintf( $this->get_text_inline( 'I have upgraded my account but when I try to Sync the License, the plan remains %s.', 'plan-did-not-change-email-message' ),
11311
  strtoupper( $plan->name )
11312
  )
11313
  ),
11314
+ $this->get_text_inline( 'Please contact us here', 'contact-us-here' )
11315
  ),
11316
+ $hmm_text
11317
  );
11318
  }
11319
  }
11321
  case 'upgraded':
11322
  $this->_admin_notices->add_sticky(
11323
  sprintf(
11324
+ $this->get_text_inline( 'Your plan was successfully upgraded.', 'plan-upgraded-message' ),
11325
  '<i>' . $this->get_plugin_name() . '</i>'
11326
  ) . $this->get_complete_upgrade_instructions(),
11327
  'plan_upgraded',
11328
+ $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!'
11329
  );
11330
 
11331
  $this->_admin_notices->remove_sticky( array(
11338
  case 'changed':
11339
  $this->_admin_notices->add_sticky(
11340
  sprintf(
11341
+ $this->get_text_inline( 'Your plan was successfully changed to %s.', 'plan-changed-to-x-message' ),
11342
  $this->_site->plan->title
11343
  ),
11344
  'plan_changed'
11353
  break;
11354
  case 'downgraded':
11355
  $this->_admin_notices->add_sticky(
11356
+ sprintf( $this->get_text_inline( 'Your license has expired. You can still continue using the free %s forever.', 'license-expired-blocking-message' ), $this->_module_type ),
11357
  'license_expired',
11358
+ $hmm_text
11359
  );
11360
  $this->_admin_notices->remove_sticky( 'plan_upgraded' );
11361
  break;
11362
  case 'cancelled':
11363
  $this->_admin_notices->add(
11364
+ $this->get_text_inline( 'Your license has been cancelled. If you think it\'s a mistake, please contact support.', 'license-cancelled' ) . ' ' .
11365
  sprintf(
11366
  '<a href="%s">%s</a>',
11367
  $this->contact_url( 'bug' ),
11368
+ $this->get_text_inline( 'Please contact us here', 'contact-us-here' )
11369
  ),
11370
+ $hmm_text,
11371
  'error'
11372
  );
11373
  $this->_admin_notices->remove_sticky( 'plan_upgraded' );
11374
  break;
11375
  case 'expired':
11376
  $this->_admin_notices->add_sticky(
11377
+ sprintf( $this->get_text_inline( 'Your license has expired. You can still continue using all the %s features, but you\'ll need to renew your license to continue getting updates and support.', 'license-expired-non-blocking-message' ), $this->_site->plan->title ),
11378
  'license_expired',
11379
+ $hmm_text
11380
  );
11381
  $this->_admin_notices->remove_sticky( 'plan_upgraded' );
11382
  break;
11383
  case 'trial_started':
11384
  $this->_admin_notices->add_sticky(
11385
  sprintf(
11386
+ $this->get_text_inline( 'Your trial has been successfully started.', 'trial-started-message' ),
11387
  '<i>' . $this->get_plugin_name() . '</i>'
11388
  ) . $this->get_complete_upgrade_instructions( $this->_storage->trial_plan->title ),
11389
  'trial_started',
11390
+ $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!'
11391
  );
11392
 
11393
  $this->_admin_notices->remove_sticky( array(
11396
  break;
11397
  case 'trial_expired':
11398
  $this->_admin_notices->add_sticky(
11399
+ $this->get_text_inline( 'Your trial has expired. You can still continue using all our free features.', 'trial-expired-message' ),
11400
  'trial_expired',
11401
+ $hmm_text
11402
  );
11403
  $this->_admin_notices->remove_sticky( array(
11404
  'trial_started',
11466
  if ( ! $background ) {
11467
  $this->_admin_notices->add( sprintf(
11468
  '%s %s',
11469
+ $this->get_text_inline( 'It looks like the license could not be activated.', 'license-activation-failed-message' ),
11470
  ( is_object( $license ) && isset( $license->error ) ?
11471
  $license->error->message :
11472
  sprintf( '%s<br><code>%s</code>',
11473
+ $this->get_text_inline( 'Error received from the server:', 'server-error-message' ),
11474
  var_export( $license, true )
11475
  )
11476
  )
11477
  ),
11478
+ $this->get_text_x_inline( 'Hmm', 'something somebody says when they are thinking about what you have just said.', 'hmm' ) . '...',
11479
  'error'
11480
  );
11481
  }
11482
 
11483
  return;
11484
  }
 
11485
  $premium_license = new FS_Plugin_License( $license );
11486
 
11487
  // Updated site plan.
11496
 
11497
  if ( ! $background ) {
11498
  $this->_admin_notices->add_sticky(
11499
+ $this->get_text_inline( 'Your license was successfully activated.', 'license-activated-message' ) .
11500
  $this->get_complete_upgrade_instructions(),
11501
  'license_activated',
11502
+ $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!'
11503
  );
11504
  }
11505
 
11518
  protected function _deactivate_license( $show_notice = true ) {
11519
  $this->_logger->entrance();
11520
 
11521
+ $hmm_text = $this->get_text_x_inline( 'Hmm', 'something somebody says when they are thinking about what you have just said.', 'hmm' ) . '...';
11522
+
11523
  if ( ! is_object( $this->_license ) ) {
11524
  $this->_admin_notices->add(
11525
+ sprintf( $this->get_text_inline( 'It looks like your site currently doesn\'t have an active license.', 'no-active-license-message' ), $this->_site->plan->title ),
11526
+ $hmm_text
11527
  );
11528
 
11529
  return;
11534
 
11535
  if ( isset( $license->error ) ) {
11536
  $this->_admin_notices->add(
11537
+ $this->get_text_inline( 'It looks like the license deactivation failed.', 'license-deactivation-failed-message' ) . '<br> ' .
11538
+ $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . ' ' . var_export( $license->error, true ),
11539
+ $hmm_text,
11540
  'error'
11541
  );
11542
 
11561
 
11562
  if ( $show_notice ) {
11563
  $this->_admin_notices->add(
11564
+ sprintf( $this->get_text_inline( 'Your license was successfully deactivated, you are back to the %s plan.', 'license-deactivation-message' ), $this->_site->plan->title ),
11565
+ $this->get_text_inline( 'O.K', 'ok' )
11566
  );
11567
  }
11568
 
11610
  $this->_admin_notices->remove_sticky( 'plan_upgraded' );
11611
 
11612
  $this->_admin_notices->add(
11613
+ sprintf( $this->get_text_inline( 'Your plan was successfully downgraded. Your %s plan license will expire in %s.', 'plan-x-downgraded-message' ),
11614
  $plan->title,
11615
  human_time_diff( time(), strtotime( $this->_license->expiration ) )
11616
  )
11620
  $this->_store_site();
11621
  } else {
11622
  $this->_admin_notices->add(
11623
+ $this->get_text_inline( 'Seems like we are having some temporary issue with your plan downgrade. Please try again in few minutes.', 'plan-downgraded-failure-message' ),
11624
+ $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
11625
  'error'
11626
  );
11627
  }
11638
  function start_trial( $plan_name = false ) {
11639
  $this->_logger->entrance();
11640
 
11641
+ // Alias.
11642
+ $oops_text = $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...';
11643
+
11644
  if ( $this->is_trial() ) {
11645
  // Already in trial mode.
11646
  $this->_admin_notices->add(
11647
+ sprintf( $this->get_text_inline( 'You are already running the %s in a trial mode.', 'in-trial-mode' ), $this->_module_type ),
11648
+ $oops_text,
11649
  'error'
11650
  );
11651
 
11655
  if ( $this->_site->is_trial_utilized() ) {
11656
  // Trial was already utilized.
11657
  $this->_admin_notices->add(
11658
+ $this->get_text_inline( 'You already utilized a trial before.', 'trial-utilized' ),
11659
+ $oops_text,
11660
  'error'
11661
  );
11662
 
11669
  if ( false === $plan ) {
11670
  // Plan doesn't exist.
11671
  $this->_admin_notices->add(
11672
+ sprintf( $this->get_text_inline( 'Plan %s do not exist, therefore, can\'t start a trial.', 'trial-plan-x-not-exist' ), $plan_name ),
11673
+ $oops_text,
11674
  'error'
11675
  );
11676
 
11680
  if ( ! $plan->has_trial() ) {
11681
  // Plan doesn't exist.
11682
  $this->_admin_notices->add(
11683
+ sprintf( $this->get_text_inline( 'Plan %s does not support a trial period.', 'plan-x-no-trial' ), $plan_name ),
11684
+ $oops_text,
11685
  'error'
11686
  );
11687
 
11691
  if ( ! $this->has_trial_plan() ) {
11692
  // None of the plans have a trial.
11693
  $this->_admin_notices->add(
11694
+ sprintf( $this->get_text_inline( 'None of the %s\'s plans supports a trial period.', 'no-trials' ), $this->_module_type ),
11695
+ $oops_text,
11696
  'error'
11697
  );
11698
 
11710
  if ( ! $this->is_api_result_entity( $plan ) ) {
11711
  // Some API error while trying to start the trial.
11712
  $this->_admin_notices->add(
11713
+ sprintf( $this->get_text_inline( 'Unexpected API error. Please contact the %s\'s author with the following error.', 'unexpected-api-error' ), $this->_module_type )
11714
  . ' ' . var_export( $plan, true ),
11715
+ $oops_text,
11716
  'error'
11717
  );
11718
 
11736
  private function _cancel_trial() {
11737
  $this->_logger->entrance();
11738
 
11739
+ // Alias.
11740
+ $oops_text = $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...';
11741
+
11742
  if ( ! $this->is_trial() ) {
11743
  $this->_admin_notices->add(
11744
+ $this->get_text_inline( 'It looks like you are not in trial mode anymore so there\'s nothing to cancel :)', 'trial-cancel-no-trial-message' ),
11745
+ $oops_text,
11746
  'error'
11747
  );
11748
 
11792
  ! $this->deactivate_premium_only_addon_without_license( true )
11793
  ) {
11794
  $this->_admin_notices->add(
11795
+ sprintf( $this->get_text_inline( 'Your %s free trial was successfully cancelled.', 'trial-cancel-message' ), $this->_storage->trial_plan->title )
11796
  );
11797
  }
11798
 
11800
  unset( $this->_storage->trial_plan );
11801
  } else {
11802
  $this->_admin_notices->add(
11803
+ $this->get_text_inline( 'Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.', 'trial-cancel-failure-message' ),
11804
+ $oops_text,
11805
  'error'
11806
  );
11807
  }
12038
  if ( ! $background ) {
12039
  $this->_admin_notices->add(
12040
  sprintf(
12041
+ /* translators: %s: Numeric version number (e.g. '2.1.9' */
12042
+ $this->get_text_inline( 'Version %s was released.', 'version-x-released' ) . ' ' . $this->get_text_inline( 'Please download %s.', 'please-download-x' ),
12043
  $update->version,
12044
  sprintf(
12045
  '<a href="%s" target="_blank">%s</a>',
12046
  $this->get_account_url( 'download_latest' ),
12047
+ sprintf(
12048
+ /* translators: %s: plan name (e.g. latest "Professional" version) */
12049
+ $this->get_text_inline( 'the latest %s version here', 'latest-x-version' ),
12050
+ $this->_site->plan->title
12051
+ )
12052
  )
12053
  ),
12054
+ $this->get_text_inline( 'New', 'new' ) . '!'
12055
  );
12056
  }
12057
  } else if ( false === $new_version && ! $background ) {
12058
  $this->_admin_notices->add(
12059
+ $this->get_text_inline( 'Seems like you got the latest release.', 'you-have-latest' ),
12060
+ $this->get_text_inline( 'You are all good!', 'you-are-good' )
12061
  );
12062
  }
12063
 
12323
 
12324
  if ( ! isset( $result->error ) ) {
12325
  $this->_admin_notices->add( sprintf(
12326
+ $this->get_text_inline( 'Verification mail was just sent to %s. If you can\'t find it after 5 min, please check your spam box.', 'verification-email-sent-message' ),
12327
  sprintf( '<a href="mailto:%1s">%2s</a>', esc_url( $this->_user->email ), $this->_user->email )
12328
  ) );
12329
  } else {
12411
  $plugin_id = fs_request_get( 'plugin_id', $this->get_id() );
12412
  $action = fs_get_action();
12413
 
12414
+ // Alias.
12415
+ $oops_text = $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...';
12416
+
12417
  switch ( $action ) {
12418
  case 'delete_account':
12419
  check_admin_referer( $action );
12498
  $candidate_email = fs_request_get( 'candidate_email', '' );
12499
 
12500
  if ( $this->init_change_owner( $candidate_email ) ) {
12501
+ $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Please check your mailbox, you should receive an email via %s to confirm the ownership change. From security reasons, you must confirm the change within the next 15 min. If you cannot find the email, please check your spam folder.', 'change-owner-request-sent-x' ), '<b>' . $this->_user->email . '</b>' ) );
12502
  }
12503
  break;
12504
  case 'owner_confirmed':
12505
  $candidate_email = fs_request_get( 'candidate_email', '' );
12506
 
12507
+ $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Thanks for confirming the ownership change. An email was just sent to %s for final approval.', 'change-owner-request_owner-confirmed' ), '<b>' . $candidate_email . '</b>' ) );
12508
  break;
12509
  case 'candidate_confirmed':
12510
  if ( $this->complete_change_owner() ) {
12511
  $this->_admin_notices->add_sticky(
12512
+ sprintf( $this->get_text_inline( '%s is the new owner of the account.', 'change-owner-request_candidate-confirmed' ), '<b>' . $this->_user->email . '</b>' ),
12513
  'ownership_changed',
12514
+ $this->get_text_x_inline( 'Congrats', 'as congratulations', 'congrats' ) . '!'
12515
  );
12516
  } else {
12517
  // @todo Handle failed ownership change message.
12531
  switch ( $result->error->code ) {
12532
  case 'user_exist':
12533
  $this->_admin_notices->add(
12534
+ $this->get_text_inline( 'Sorry, we could not complete the email update. Another user with the same email is already registered.', 'user-exist-message' ) . ' ' .
12535
+ sprintf( $this->get_text_inline( 'If you would like to give up the ownership of the %s\'s account to %s click the Change Ownership button.', 'user-exist-message_ownership' ), $this->_module_type, '<b>' . $new_email . '</b>' ) .
12536
  sprintf(
12537
  '<a style="margin-left: 10px;" href="%s"><button class="button button-primary">%s &nbsp;&#10140;</button></a>',
12538
  $this->get_account_url( 'change_owner', array(
12539
  'state' => 'init',
12540
  'candidate_email' => $new_email
12541
  ) ),
12542
+ $this->get_text_inline( 'Change Ownership', 'change-ownership' )
12543
  ),
12544
+ $oops_text,
12545
  'error'
12546
  );
12547
  break;
12548
  }
12549
  } else {
12550
+ $this->_admin_notices->add( $this->get_text_inline( 'Your email was successfully updated. You should receive an email with confirmation instructions in few moments.', 'email-updated-message' ) );
12551
  }
12552
 
12553
  return;
12559
 
12560
  if ( isset( $result->error ) ) {
12561
  $this->_admin_notices->add(
12562
+ $this->get_text_inline( 'Please provide your full name.', 'name-update-failed-message' ),
12563
+ $oops_text,
12564
  'error'
12565
  );
12566
  } else {
12567
+ $this->_admin_notices->add( $this->get_text_inline( 'Your name was successfully updated.', 'name-updated-message' ) );
12568
  }
12569
 
12570
  return;
12624
  $this->do_action( 'account_property_edit', 'site', $site_property, $site_property_value );
12625
 
12626
  $this->_admin_notices->add( sprintf(
12627
+ /* translators: %s: User's account property (e.g. email address, name) */
12628
+ $this->get_text_inline( 'You have successfully updated your %s.', 'x-updated' ),
12629
+ '<b>' . str_replace( '_', ' ', $p ) . '</b>'
12630
+ ) );
12631
 
12632
  return;
12633
  }
12689
  function _affiliation_page_render() {
12690
  $this->_logger->entrance();
12691
 
12692
+ $this->fetch_affiliate_and_terms();
12693
+
12694
  fs_enqueue_local_style( 'fs_affiliation', '/admin/affiliation.css' );
12695
 
12696
  $vars = array( 'id' => $this->_module_id );
12765
 
12766
  if ( ! $this->is_registered() && $this->is_org_repo_compliant() ) {
12767
  $this->_admin_notices->add(
12768
+ sprintf( $this->get_text_inline( 'Just letting you know that the add-ons information of %s is being pulled from an external server.', 'addons-info-external-message' ), '<b>' . $this->get_plugin_name() . '</b>' ),
12769
+ $this->get_text_x_inline( 'Heads up', 'advance notice of something that will need attention.', 'heads-up' ),
12770
  'update-nag'
12771
  );
12772
  }
13084
  $trial_period = $this->_trial_days;
13085
  $require_payment = $this->_is_trial_require_payment;
13086
  $trial_url = $this->get_trial_url();
13087
+ $plans_string = strtolower( $this->get_text_inline( 'Awesome', 'awesome' ) );
13088
 
13089
  if ( $this->is_registered() ) {
13090
  // If opted-in, override trial with up to date data from API.
13124
  }
13125
 
13126
  $message = sprintf(
13127
+ $this->get_text_x_inline( 'Hey', 'exclamation', 'hey' ) . '! ' . $this->get_text_inline( 'How do you like %s so far? Test all our %s premium features with a %d-day free trial.', 'trial-x-promotion-message' ),
13128
  sprintf( '<b>%s</b>', $this->get_plugin_name() ),
13129
  $plans_string,
13130
  $trial_period
13132
 
13133
  // "No Credit-Card Required" or "No Commitment for N Days".
13134
  $cc_string = $require_payment ?
13135
+ sprintf( $this->get_text_inline( 'No commitment for %s days - cancel anytime!', 'no-commitment-for-x-days' ), $trial_period ) :
13136
+ $this->get_text_inline( 'No credit card required', 'no-cc-required' ) . '!';
13137
 
13138
 
13139
  // Start trial button.
13140
  $button = ' ' . sprintf(
13141
  '<a style="margin-left: 10px; vertical-align: super;" href="%s"><button class="button button-primary">%s &nbsp;&#10140;</button></a>',
13142
  $trial_url,
13143
+ $this->get_text_x_inline( 'Start free trial', 'call to action', 'start-free-trial' )
13144
  );
13145
 
13146
  $this->_admin_notices->add_sticky(
13180
  if (
13181
  // Product has no affiliate program.
13182
  ! $this->has_affiliate_program() ||
 
 
13183
  // User has applied for an affiliate account.
13184
  ! empty( $this->_storage->affiliate_application_data ) ) {
13185
  return false;
13215
  }
13216
 
13217
  $message = sprintf(
13218
+ $this->get_text_inline( 'Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!', 'become-an-ambassador-admin-notice' ),
13219
  sprintf( '<strong>%s</strong>', $this->get_plugin_name() ),
13220
  $this->get_module_label( true )
13221
  );
13224
  $button = ' ' . sprintf(
13225
  '<a style="display: block; margin-top: 10px;" href="%s"><button class="button button-primary">%s &nbsp;&#10140;</button></a>',
13226
  $this->_get_admin_page_url( 'affiliation' ),
13227
+ $this->get_text_inline( 'Learn more', 'learn-more' ) . '...'
13228
  );
13229
 
13230
  $this->_admin_notices->add_sticky(
13341
  if ( $this->is_registered() ) {
13342
  if ( ! $this->is_paying() && $this->has_paid_plan() ) {
13343
  $this->add_plugin_action_link(
13344
+ $this->get_text_inline( 'Upgrade', 'upgrade' ),
13345
  $this->get_upgrade_url(),
13346
  false,
13347
  7,
13351
 
13352
  if ( $this->has_addons() ) {
13353
  $this->add_plugin_action_link(
13354
+ $this->get_text_inline( 'Add-Ons', 'add-ons' ),
13355
  $this->_get_admin_page_url( 'addons' ),
13356
  false,
13357
  9,
13379
  add_action( 'admin_footer', array( &$this, '_add_license_activation_dialog_box' ) );
13380
  }
13381
 
13382
+ $link_text = $this->is_free_plan() ?
13383
+ $this->get_text_inline( 'Activate License', 'activate-license' ) :
13384
+ $this->get_text_inline( 'Change License', 'change-license' );
 
 
13385
 
13386
  $this->add_plugin_action_link(
13387
  $link_text,
13442
 
13443
  if ( $this->is_registered() ) {
13444
  if ( $this->is_tracking_allowed() ) {
13445
+ $link_text_id = $this->get_text_inline( 'Opt Out', 'opt-out' );
13446
  } else {
13447
+ $link_text_id = $this->get_text_inline( 'Opt In', 'opt-in' );
13448
  }
13449
 
13450
  add_action( 'admin_footer', array( &$this, '_add_optout_dialog' ) );
13451
  } else {
13452
+ $link_text_id = $this->get_text_inline( 'Opt In', 'opt-in' );
13453
 
13454
  $params = ! $this->is_anonymous() ?
13455
  array() :
13463
 
13464
  if ( $this->is_plugin() && self::is_plugins_page() ) {
13465
  $this->add_plugin_action_link(
13466
+ $link_text_id,
13467
  $url,
13468
  false,
13469
  13,
13632
 
13633
  // @since 1.2.1.5 The free version is auto deactivated.
13634
  $deactivation_step = version_compare( $this->version, '1.2.1.5', '<' ) ?
13635
+ ( '<li>' . $this->esc_html_inline( 'Deactivate the free version', 'deactivate-free-version' ) . '.</li>' ) :
13636
  '';
13637
 
13638
  return sprintf(
13639
  ' %s: <ol><li>%s.</li>%s<li>%s (<a href="%s" target="_blank">%s</a>).</li></ol>',
13640
+ $this->get_text_inline( 'Please follow these steps to complete the upgrade', 'follow-steps-to-complete-upgrade' ),
13641
  $this->get_latest_download_link( sprintf(
13642
+ /* translators: %s: Plan title */
13643
+ $this->get_text_inline( 'Download the latest %s version', 'download-latest-x-version' ),
13644
  $plan_title
13645
  ) ),
13646
  $deactivation_step,
13647
+ $this->get_text_inline( 'Upload and activate the downloaded version', 'upload-and-activate' ),
13648
  '//bit.ly/upload-wp-' . $this->_module_type . 's',
13649
+ $this->get_text_inline( 'How to upload and activate?', 'howto-upload-activate' )
13650
  );
13651
  }
13652
 
13662
  return fs_text( $key, $this->_slug );
13663
  }
13664
 
13665
+ /**
13666
+ * @author Vova Feldman (@svovaf)
13667
+ * @since 1.2.3
13668
+ *
13669
+ * @param string $text Translatable string.
13670
+ * @param string $key String key for overrides.
13671
+ *
13672
+ * @return string
13673
+ */
13674
+ function get_text_inline( $text, $key = '' ) {
13675
+ return _fs_text_inline( $text, $key, $this->_slug );
13676
+ }
13677
+
13678
+ /**
13679
+ * @author Vova Feldman (@svovaf)
13680
+ * @since 1.2.3
13681
+ *
13682
+ * @param string $text Translatable string.
13683
+ * @param string $context Context information for the translators.
13684
+ * @param string $key String key for overrides.
13685
+ *
13686
+ * @return string
13687
+ */
13688
+ function get_text_x_inline( $text, $context, $key ) {
13689
+ return _fs_text_x_inline( $text, $context, $key, $this->_slug );
13690
+ }
13691
+
13692
+ /**
13693
+ * @author Vova Feldman (@svovaf)
13694
+ * @since 1.2.3
13695
+ *
13696
+ * @param string $text Translatable string.
13697
+ * @param string $key String key for overrides.
13698
+ *
13699
+ * @return string
13700
+ */
13701
+ function esc_html_inline( $text, $key ) {
13702
+ return esc_html( _fs_text_inline( $text, $key, $this->_slug ) );
13703
+ }
13704
+
13705
  #----------------------------------------------------------------------------------
13706
  #region Versioning
13707
  #----------------------------------------------------------------------------------
13805
  if ( ! $this->is_registered() ) {
13806
  // Not registered.
13807
  self::shoot_ajax_failure( array(
13808
+ 'message' => $this->get_text_inline( 'Auto installation only works for opted-in users.', 'auto-install-error-not-opted-in' ),
13809
  'code' => 'premium_installed',
13810
  ) );
13811
  }
13812
 
13813
+ $plugin_id = fs_request_get( 'target_module_id', $this->get_id() );
13814
 
13815
  if ( ! FS_Plugin::is_valid_id( $plugin_id ) ) {
13816
  // Invalid ID.
13817
  self::shoot_ajax_failure( array(
13818
+ 'message' => $this->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
13819
  'code' => 'invalid_module_id',
13820
  ) );
13821
  }
13824
  if ( $this->is_premium() ) {
13825
  // Already using the premium code version.
13826
  self::shoot_ajax_failure( array(
13827
+ 'message' => $this->get_text_inline( 'Premium version already active.', 'auto-install-error-premium-activated' ),
13828
  'code' => 'premium_installed',
13829
  ) );
13830
  }
13831
  if ( ! $this->can_use_premium_code() ) {
13832
  // Don't have access to the premium code.
13833
  self::shoot_ajax_failure( array(
13834
+ 'message' => $this->get_text_inline( 'You do not have a valid license to access the premium version.', 'auto-install-error-invalid-license' ),
13835
  'code' => 'invalid_license',
13836
  ) );
13837
  }
13838
  if ( ! $this->has_release_on_freemius() ) {
13839
  // Plugin is a serviceware, no premium code version.
13840
  self::shoot_ajax_failure( array(
13841
+ 'message' => $this->get_text_inline( 'Plugin is a "Serviceware" which means it does not have a premium code version.', 'auto-install-error-serviceware' ),
13842
  'code' => 'premium_version_missing',
13843
  ) );
13844
  }
13848
  if ( ! is_object( $addon ) ) {
13849
  // Invalid add-on ID.
13850
  self::shoot_ajax_failure( array(
13851
+ 'message' => $this->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
13852
  'code' => 'invalid_module_id',
13853
  ) );
13854
  }
13856
  if ( $this->is_addon_activated( $plugin_id, true ) ) {
13857
  // Premium add-on version is already activated.
13858
  self::shoot_ajax_failure( array(
13859
+ 'message' => $this->get_text_inline( 'Premium add-on version already installed.', 'auto-install-error-premium-addon-activated' ),
13860
  'code' => 'premium_installed',
13861
  ) );
13862
  }
13928
  }
13929
  }
13930
 
13931
+ $vars = array(
13932
+ 'id' => $this->_module_id,
13933
+ 'target_module_id' => $plugin_id,
13934
+ 'slug' => $this->_slug,
13935
+ );
13936
 
13937
  fs_require_template( 'auto-installation.php', $vars );
13938
  }
14202
  require_once WP_FS__DIR_INCLUDES . '/customizer/class-fs-customizer-upsell-control.php';
14203
 
14204
  $customizer->add_section( 'freemius_upsell', array(
14205
+ 'title' => '&#9733; ' . $this->get_text_inline( 'View paid features', 'view-paid-features' ),
14206
  'priority' => 1,
14207
  ) );
14208
  $customizer->add_setting( 'freemius_upsell', array(
freemius/includes/class-fs-logger.php CHANGED
@@ -1,688 +1,688 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Logger {
14
- private $_id;
15
- private $_on = false;
16
- private $_echo = false;
17
- private $_file_start = 0;
18
- /**
19
- * @var int PHP Process ID.
20
- */
21
- private static $_processID;
22
- /**
23
- * @var string PHP Script user name.
24
- */
25
- private static $_ownerName;
26
- /**
27
- * @var bool Is storage logging turned on.
28
- */
29
- private static $_isStorageLoggingOn;
30
- /**
31
- * @var int ABSPATH length.
32
- */
33
- private static $_abspathLength;
34
-
35
- private static $LOGGERS = array();
36
- private static $LOG = array();
37
- private static $CNT = 0;
38
- private static $_HOOKED_FOOTER = false;
39
-
40
- private function __construct( $id, $on = false, $echo = false ) {
41
- $this->_id = $id;
42
-
43
- $bt = debug_backtrace();
44
- $caller = $bt[2];
45
-
46
- if ( false !== strpos( $caller['file'], 'plugins' ) ) {
47
- $this->_file_start = strpos( $caller['file'], 'plugins' ) + strlen( 'plugins/' );
48
- } else {
49
- $this->_file_start = strpos( $caller['file'], 'themes' ) + strlen( 'themes/' );
50
- }
51
-
52
- if ( $on ) {
53
- $this->on();
54
- }
55
- if ( $echo ) {
56
- $this->echo_on();
57
- }
58
- }
59
-
60
- /**
61
- * @param string $id
62
- * @param bool $on
63
- * @param bool $echo
64
- *
65
- * @return FS_Logger
66
- */
67
- public static function get_logger( $id, $on = false, $echo = false ) {
68
- $id = strtolower( $id );
69
-
70
- if ( ! isset( self::$_processID ) ) {
71
- self::init();
72
- }
73
-
74
- if ( ! isset( self::$LOGGERS[ $id ] ) ) {
75
- self::$LOGGERS[ $id ] = new FS_Logger( $id, $on, $echo );
76
- }
77
-
78
- return self::$LOGGERS[ $id ];
79
- }
80
-
81
- /**
82
- * Initialize logging global info.
83
- *
84
- * @author Vova Feldman (@svovaf)
85
- * @since 1.2.1.6
86
- */
87
- private static function init() {
88
- self::$_ownerName = function_exists( 'get_current_user' ) ?
89
- get_current_user() :
90
- 'unknown';
91
- self::$_isStorageLoggingOn = ( 1 == get_option( 'fs_storage_logger', 0 ) );
92
- self::$_abspathLength = strlen( ABSPATH );
93
- self::$_processID = mt_rand( 0, 32000 );
94
-
95
- // Process ID may be `false` on errors.
96
- if ( ! is_numeric( self::$_processID ) ) {
97
- self::$_processID = 0;
98
- }
99
- }
100
-
101
- private static function hook_footer() {
102
- if ( self::$_HOOKED_FOOTER ) {
103
- return;
104
- }
105
-
106
- if ( is_admin() ) {
107
- add_action( 'admin_footer', 'FS_Logger::dump', 100 );
108
- } else {
109
- add_action( 'wp_footer', 'FS_Logger::dump', 100 );
110
- }
111
- }
112
-
113
- function is_on() {
114
- return $this->_on;
115
- }
116
-
117
- function on() {
118
- $this->_on = true;
119
-
120
- if ( ! function_exists( 'dbDelta' ) ) {
121
- require_once ABSPATH . 'wp-admin/includes/upgrade.php';
122
- }
123
-
124
- self::hook_footer();
125
- }
126
-
127
- function echo_on() {
128
- $this->on();
129
-
130
- $this->_echo = true;
131
- }
132
-
133
- function is_echo_on() {
134
- return $this->_echo;
135
- }
136
-
137
- function get_id() {
138
- return $this->_id;
139
- }
140
-
141
- function get_file() {
142
- return $this->_file_start;
143
- }
144
-
145
- private function _log( &$message, $type = 'log', $wrapper ) {
146
- if ( ! $this->is_on() ) {
147
- return;
148
- }
149
-
150
- $bt = debug_backtrace();
151
- $depth = $wrapper ? 3 : 2;
152
- while ( $depth < count( $bt ) - 1 && 'eval' === $bt[ $depth ]['function'] ) {
153
- $depth ++;
154
- }
155
-
156
- $caller = $bt[ $depth ];
157
-
158
- /**
159
- * Retrieve the correct call file & line number from backtrace
160
- * when logging from a wrapper method.
161
- *
162
- * @author Vova Feldman
163
- * @since 1.2.1.6
164
- */
165
- if ( empty( $caller['line'] ) ) {
166
- $depth --;
167
-
168
- while ( $depth >= 0 ) {
169
- if ( ! empty( $bt[ $depth ]['line'] ) ) {
170
- $caller['line'] = $bt[ $depth ]['line'];
171
- $caller['file'] = $bt[ $depth ]['file'];
172
- break;
173
- }
174
- }
175
- }
176
-
177
- $log = array_merge( $caller, array(
178
- 'cnt' => self::$CNT ++,
179
- 'logger' => $this,
180
- 'timestamp' => microtime( true ),
181
- 'log_type' => $type,
182
- 'msg' => $message,
183
- ) );
184
-
185
- if ( self::$_isStorageLoggingOn ) {
186
- $this->db_log( $type, $message, self::$CNT, $caller );
187
- }
188
-
189
- self::$LOG[] = $log;
190
-
191
- if ( $this->is_echo_on() && ! Freemius::is_ajax() ) {
192
- echo self::format_html( $log ) . "\n";
193
- }
194
- }
195
-
196
- function log( $message, $wrapper = false ) {
197
- $this->_log( $message, 'log', $wrapper );
198
- }
199
-
200
- function info( $message, $wrapper = false ) {
201
- $this->_log( $message, 'info', $wrapper );
202
- }
203
-
204
- function warn( $message, $wrapper = false ) {
205
- $this->_log( $message, 'warn', $wrapper );
206
- }
207
-
208
- function error( $message, $wrapper = false ) {
209
- $this->_log( $message, 'error', $wrapper );
210
- }
211
-
212
- /**
213
- * Log API error.
214
- *
215
- * @author Vova Feldman (@svovaf)
216
- * @since 1.2.1.5
217
- *
218
- * @param mixed $api_result
219
- * @param bool $wrapper
220
- */
221
- function api_error( $api_result, $wrapper = false ) {
222
- $message = '';
223
- if ( is_object( $api_result ) && isset( $api_result->error ) ) {
224
- $message = $api_result->error->message;
225
- } else if ( is_object( $api_result ) ) {
226
- $message = var_export( $api_result, true );
227
- } else if ( is_string( $api_result ) ) {
228
- $message = $api_result;
229
- } else if ( empty( $api_result ) ) {
230
- $message = 'Empty API result.';
231
- }
232
-
233
- $message = 'API Error: ' . $message;
234
-
235
- $this->_log( $message, 'error', $wrapper );
236
- }
237
-
238
- function entrance( $message = '', $wrapper = false ) {
239
- $msg = 'Entrance' . ( empty( $message ) ? '' : ' > ' ) . $message;
240
-
241
- $this->_log( $msg, 'log', $wrapper );
242
- }
243
-
244
- function departure( $message = '', $wrapper = false ) {
245
- $msg = 'Departure' . ( empty( $message ) ? '' : ' > ' ) . $message;
246
-
247
- $this->_log( $msg, 'log', $wrapper );
248
- }
249
-
250
- #--------------------------------------------------------------------------------
251
- #region Log Formatting
252
- #--------------------------------------------------------------------------------
253
-
254
- private static function format( $log, $show_type = true ) {
255
- return '[' . str_pad( $log['cnt'], strlen( self::$CNT ), '0', STR_PAD_LEFT ) . '] [' . $log['logger']->_id . '] ' . ( $show_type ? '[' . $log['log_type'] . ']' : '' ) . ( ! empty( $log['class'] ) ? $log['class'] . $log['type'] : '' ) . $log['function'] . ' >> ' . $log['msg'] . ( isset( $log['file'] ) ? ' (' . substr( $log['file'], $log['logger']->_file_start ) . ' ' . $log['line'] . ') ' : '' ) . ' [' . $log['timestamp'] . ']';
256
- }
257
-
258
- private static function format_html( $log ) {
259
- return '<div style="font-size: 13px; font-family: monospace; color: #7da767; padding: 8px 3px; background: #000; border-bottom: 1px solid #555;">[' . $log['cnt'] . '] [' . $log['logger']->_id . '] [' . $log['log_type'] . '] <b><code style="color: #c4b1e0;">' . ( ! empty( $log['class'] ) ? $log['class'] . $log['type'] : '' ) . $log['function'] . '</code> >> <b style="color: #f59330;">' . esc_html( $log['msg'] ) . '</b></b>' . ( isset( $log['file'] ) ? ' (' . substr( $log['file'], $log['logger']->_file_start ) . ' ' . $log['line'] . ')' : '' ) . ' [' . $log['timestamp'] . ']</div>';
260
- }
261
-
262
- #endregion
263
-
264
- static function dump() {
265
- ?>
266
- <!-- BEGIN: Freemius PHP Console Log -->
267
- <script type="text/javascript">
268
- <?php
269
- foreach ( self::$LOG as $log ) {
270
- echo 'console.' . $log['log_type'] . '(' . json_encode( self::format( $log, false ) ) . ')' . "\n";
271
- }
272
- ?>
273
- </script>
274
- <!-- END: Freemius PHP Console Log -->
275
- <?php
276
- }
277
-
278
- static function get_log() {
279
- return self::$LOG;
280
- }
281
-
282
- #--------------------------------------------------------------------------------
283
- #region Database Logging
284
- #--------------------------------------------------------------------------------
285
-
286
- /**
287
- * @author Vova Feldman (@svovaf)
288
- * @since 1.2.1.6
289
- *
290
- * @return bool
291
- */
292
- public static function is_storage_logging_on() {
293
- if ( ! isset( self::$_isStorageLoggingOn ) ) {
294
- self::$_isStorageLoggingOn = ( 1 == get_option( 'fs_storage_logger', 0 ) );
295
- }
296
-
297
- return self::$_isStorageLoggingOn;
298
- }
299
-
300
- /**
301
- * Turns on/off database persistent debugging to capture
302
- * multi-session logs to debug complex flows like
303
- * plugin auto-deactivate on premium version activation.
304
- *
305
- * @todo Check if Theme Check has issues with DB tables for themes.
306
- *
307
- * @author Vova Feldman (@svovaf)
308
- * @since 1.2.1.6
309
- *
310
- * @param bool $is_on
311
- *
312
- * @return bool
313
- */
314
- public static function _set_storage_logging( $is_on = true ) {
315
- global $wpdb;
316
-
317
- $table = "{$wpdb->prefix}fs_logger";
318
-
319
- if ( $is_on ) {
320
- /**
321
- * Create logging table.
322
- *
323
- * NOTE:
324
- * dbDelta must use KEY and not INDEX for indexes.
325
- *
326
- * @link https://core.trac.wordpress.org/ticket/2695
327
- */
328
- $result = $wpdb->query( "CREATE TABLE {$table} (
329
- `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
330
- `process_id` INT UNSIGNED NOT NULL,
331
- `user_name` VARCHAR(64) NOT NULL,
332
- `logger` VARCHAR(128) NOT NULL,
333
- `log_order` INT UNSIGNED NOT NULL,
334
- `type` ENUM('log','info','warn','error') NOT NULL DEFAULT 'log',
335
- `message` TEXT NOT NULL,
336
- `file` VARCHAR(256) NOT NULL,
337
- `line` INT UNSIGNED NOT NULL,
338
- `function` VARCHAR(256) NOT NULL,
339
- `request_type` ENUM('call','ajax','cron') NOT NULL DEFAULT 'call',
340
- `request_url` VARCHAR(1024) NOT NULL,
341
- `created` DECIMAL(16, 6) NOT NULL,
342
- PRIMARY KEY (`id`),
343
- KEY `process_id` (`process_id` ASC),
344
- KEY `process_logger` (`process_id` ASC, `logger` ASC),
345
- KEY `function` (`function` ASC),
346
- KEY `type` (`type` ASC))" );
347
- } else {
348
- /**
349
- * Drop logging table.
350
- */
351
- $result = $wpdb->query( "DROP TABLE IF EXISTS $table;" );
352
- }
353
-
354
- if ( false !== $result ) {
355
- update_option( 'fs_storage_logger', ( $is_on ? 1 : 0 ) );
356
- }
357
-
358
- return ( false !== $result );
359
- }
360
-
361
- /**
362
- * @author Vova Feldman (@svovaf)
363
- * @since 1.2.1.6
364
- *
365
- * @param string $type
366
- * @param string $message
367
- * @param int $log_order
368
- * @param array $caller
369
- *
370
- * @return false|int
371
- */
372
- private function db_log(
373
- &$type,
374
- &$message,
375
- &$log_order,
376
- &$caller
377
- ) {
378
- global $wpdb;
379
-
380
- $request_type = 'call';
381
- if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
382
- $request_type = 'cron';
383
- } else if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
384
- $request_type = 'ajax';
385
- }
386
-
387
- $request_url = WP_FS__IS_HTTP_REQUEST ?
388
- $_SERVER['REQUEST_URI'] :
389
- '';
390
-
391
- return $wpdb->insert(
392
- "{$wpdb->prefix}fs_logger",
393
- array(
394
- 'process_id' => self::$_processID,
395
- 'user_name' => self::$_ownerName,
396
- 'logger' => $this->_id,
397
- 'log_order' => $log_order,
398
- 'type' => $type,
399
- 'request_type' => $request_type,
400
- 'request_url' => $request_url,
401
- 'message' => $message,
402
- 'file' => isset( $caller['file'] ) ?
403
- substr( $caller['file'], self::$_abspathLength ) :
404
- '',
405
- 'line' => $caller['line'],
406
- 'function' => ( ! empty( $caller['class'] ) ? $caller['class'] . $caller['type'] : '' ) . $caller['function'],
407
- 'created' => microtime( true ),
408
- )
409
- );
410
- }
411
-
412
- /**
413
- * Persistent DB logger columns.
414
- *
415
- * @var array
416
- */
417
- private static $_log_columns = array(
418
- 'id',
419
- 'process_id',
420
- 'user_name',
421
- 'logger',
422
- 'log_order',
423
- 'type',
424
- 'message',
425
- 'file',
426
- 'line',
427
- 'function',
428
- 'request_type',
429
- 'request_url',
430
- 'created',
431
- );
432
-
433
- /**
434
- * Create DB logs query.
435
- *
436
- * @author Vova Feldman (@svovaf)
437
- * @since 1.2.1.6
438
- *
439
- * @param bool $filters
440
- * @param int $limit
441
- * @param int $offset
442
- * @param bool $order
443
- * @param bool $escape_eol
444
- *
445
- * @return string
446
- */
447
- private static function build_db_logs_query(
448
- $filters = false,
449
- $limit = 200,
450
- $offset = 0,
451
- $order = false,
452
- $escape_eol = false
453
- ) {
454
- global $wpdb;
455
-
456
- $select = '*';
457
-
458
- if ( $escape_eol ) {
459
- $select = '';
460
- for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) {
461
- if ( $i > 0 ) {
462
- $select .= ', ';
463
- }
464
-
465
- if ( 'message' !== self::$_log_columns[ $i ] ) {
466
- $select .= self::$_log_columns[ $i ];
467
- } else {
468
- $select .= 'REPLACE(message , \'\n\', \' \') AS message';
469
- }
470
- }
471
- }
472
-
473
- $query = "SELECT {$select} FROM {$wpdb->prefix}fs_logger";
474
- if ( is_array( $filters ) ) {
475
- $criteria = array();
476
-
477
- if ( ! empty( $filters['type'] ) && 'all' !== $filters['type'] ) {
478
- $filters['type'] = strtolower( $filters['type'] );
479
-
480
- switch ( $filters['type'] ) {
481
- case 'warn_error':
482
- $criteria[] = array( 'col' => 'type', 'val' => array( 'warn', 'error' ) );
483
- break;
484
- case 'error':
485
- case 'warn':
486
- $criteria[] = array( 'col' => 'type', 'val' => $filters['type'] );
487
- break;
488
- case 'info':
489
- default:
490
- $criteria[] = array( 'col' => 'type', 'val' => array( 'info', 'log' ) );
491
- break;
492
- }
493
- }
494
-
495
- if ( ! empty( $filters['request_type'] ) ) {
496
- $filters['request_type'] = strtolower( $filters['request_type'] );
497
-
498
- if ( in_array( $filters['request_type'], array( 'call', 'ajax', 'cron' ) ) ) {
499
- $criteria[] = array( 'col' => 'request_type', 'val' => $filters['request_type'] );
500
- }
501
- }
502
-
503
- if ( ! empty( $filters['file'] ) ) {
504
- $criteria[] = array(
505
- 'col' => 'file',
506
- 'op' => 'LIKE',
507
- 'val' => '%' . esc_sql( $filters['file'] ),
508
- );
509
- }
510
-
511
- if ( ! empty( $filters['function'] ) ) {
512
- $criteria[] = array(
513
- 'col' => 'function',
514
- 'op' => 'LIKE',
515
- 'val' => '%' . esc_sql( $filters['function'] ),
516
- );
517
- }
518
-
519
- if ( ! empty( $filters['process_id'] ) && is_numeric( $filters['process_id'] ) ) {
520
- $criteria[] = array( 'col' => 'process_id', 'val' => $filters['process_id'] );
521
- }
522
-
523
- if ( ! empty( $filters['logger'] ) ) {
524
- $criteria[] = array(
525
- 'col' => 'logger',
526
- 'op' => 'LIKE',
527
- 'val' => '%' . esc_sql( $filters['logger'] ) . '%',
528
- );
529
- }
530
-
531
- if ( ! empty( $filters['message'] ) ) {
532
- $criteria[] = array(
533
- 'col' => 'message',
534
- 'op' => 'LIKE',
535
- 'val' => '%' . esc_sql( $filters['message'] ) . '%',
536
- );
537
- }
538
-
539
- if ( 0 < count( $criteria ) ) {
540
- $query .= "\nWHERE\n";
541
-
542
- $first = true;
543
- foreach ( $criteria as $c ) {
544
- if ( ! $first ) {
545
- $query .= "AND\n";
546
- }
547
-
548
- if ( is_array( $c['val'] ) ) {
549
- $operator = 'IN';
550
-
551
- for ( $i = 0, $len = count( $c['val'] ); $i < $len; $i ++ ) {
552
- $c['val'][ $i ] = "'" . esc_sql( $c['val'][ $i ] ) . "'";
553
- }
554
-
555
- $val = '(' . implode( ',', $c['val'] ) . ')';
556
- } else {
557
- $operator = ! empty( $c['op'] ) ? $c['op'] : '=';
558
- $val = "'" . esc_sql( $c['val'] ) . "'";
559
- }
560
-
561
- $query .= "`{$c['col']}` {$operator} {$val}\n";
562
-
563
- $first = false;
564
- }
565
- }
566
- }
567
-
568
- if ( ! is_array( $order ) ) {
569
- $order = array(
570
- 'col' => 'id',
571
- 'order' => 'desc'
572
- );
573
- }
574
-
575
- $query .= " ORDER BY {$order['col']} {$order['order']} LIMIT {$offset},{$limit}";
576
-
577
- return $query;
578
- }
579
-
580
- /**
581
- * Load logs from DB.
582
- *
583
- * @author Vova Feldman (@svovaf)
584
- * @since 1.2.1.6
585
- *
586
- * @param bool $filters
587
- * @param int $limit
588
- * @param int $offset
589
- * @param bool $order
590
- *
591
- * @return object[]|null
592
- */
593
- public static function load_db_logs(
594
- $filters = false,
595
- $limit = 200,
596
- $offset = 0,
597
- $order = false
598
- ) {
599
- global $wpdb;
600
-
601
- $query = self::build_db_logs_query(
602
- $filters,
603
- $limit,
604
- $offset,
605
- $order
606
- );
607
-
608
- return $wpdb->get_results( $query );
609
- }
610
-
611
- /**
612
- * Load logs from DB.
613
- *
614
- * @author Vova Feldman (@svovaf)
615
- * @since 1.2.1.6
616
- *
617
- * @param bool $filters
618
- * @param string $filename
619
- * @param int $limit
620
- * @param int $offset
621
- * @param bool $order
622
- *
623
- * @return false|string File download URL or false on failure.
624
- */
625
- public static function download_db_logs(
626
- $filters = false,
627
- $filename = '',
628
- $limit = 10000,
629
- $offset = 0,
630
- $order = false
631
- ) {
632
- global $wpdb;
633
-
634
- $query = self::build_db_logs_query(
635
- $filters,
636
- $limit,
637
- $offset,
638
- $order,
639
- true
640
- );
641
-
642
- $upload_dir = wp_upload_dir();
643
- if ( empty( $filename ) ) {
644
- $filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv';
645
- }
646
- $filepath = rtrim( $upload_dir['path'], '/' ) . "/{$filename}";
647
-
648
- $query .= " INTO OUTFILE '{$filepath}' FIELDS TERMINATED BY '\t' ESCAPED BY '\\\\' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\\n'";
649
-
650
- $columns = '';
651
- for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) {
652
- if ( $i > 0 ) {
653
- $columns .= ', ';
654
- }
655
-
656
- $columns .= "'" . self::$_log_columns[ $i ] . "'";
657
- }
658
-
659
- $query = "SELECT {$columns} UNION ALL " . $query;
660
-
661
- $result = $wpdb->query( $query );
662
-
663
- if ( false === $result ) {
664
- return false;
665
- }
666
-
667
- return rtrim( $upload_dir['url'], '/' ) . '/' . $filename;
668
- }
669
-
670
- /**
671
- * @author Vova Feldman (@svovaf)
672
- * @since 1.2.1.6
673
- *
674
- * @param string $filename
675
- *
676
- * @return string
677
- */
678
- public static function get_logs_download_url( $filename = '' ) {
679
- $upload_dir = wp_upload_dir();
680
- if ( empty( $filename ) ) {
681
- $filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv';
682
- }
683
-
684
- return rtrim( $upload_dir['url'], '/' ) . $filename;
685
- }
686
-
687
- #endregion
688
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Logger {
14
+ private $_id;
15
+ private $_on = false;
16
+ private $_echo = false;
17
+ private $_file_start = 0;
18
+ /**
19
+ * @var int PHP Process ID.
20
+ */
21
+ private static $_processID;
22
+ /**
23
+ * @var string PHP Script user name.
24
+ */
25
+ private static $_ownerName;
26
+ /**
27
+ * @var bool Is storage logging turned on.
28
+ */
29
+ private static $_isStorageLoggingOn;
30
+ /**
31
+ * @var int ABSPATH length.
32
+ */
33
+ private static $_abspathLength;
34
+
35
+ private static $LOGGERS = array();
36
+ private static $LOG = array();
37
+ private static $CNT = 0;
38
+ private static $_HOOKED_FOOTER = false;
39
+
40
+ private function __construct( $id, $on = false, $echo = false ) {
41
+ $this->_id = $id;
42
+
43
+ $bt = debug_backtrace();
44
+ $caller = $bt[2];
45
+
46
+ if ( false !== strpos( $caller['file'], 'plugins' ) ) {
47
+ $this->_file_start = strpos( $caller['file'], 'plugins' ) + strlen( 'plugins/' );
48
+ } else {
49
+ $this->_file_start = strpos( $caller['file'], 'themes' ) + strlen( 'themes/' );
50
+ }
51
+
52
+ if ( $on ) {
53
+ $this->on();
54
+ }
55
+ if ( $echo ) {
56
+ $this->echo_on();
57
+ }
58
+ }
59
+
60
+ /**
61
+ * @param string $id
62
+ * @param bool $on
63
+ * @param bool $echo
64
+ *
65
+ * @return FS_Logger
66
+ */
67
+ public static function get_logger( $id, $on = false, $echo = false ) {
68
+ $id = strtolower( $id );
69
+
70
+ if ( ! isset( self::$_processID ) ) {
71
+ self::init();
72
+ }
73
+
74
+ if ( ! isset( self::$LOGGERS[ $id ] ) ) {
75
+ self::$LOGGERS[ $id ] = new FS_Logger( $id, $on, $echo );
76
+ }
77
+
78
+ return self::$LOGGERS[ $id ];
79
+ }
80
+
81
+ /**
82
+ * Initialize logging global info.
83
+ *
84
+ * @author Vova Feldman (@svovaf)
85
+ * @since 1.2.1.6
86
+ */
87
+ private static function init() {
88
+ self::$_ownerName = function_exists( 'get_current_user' ) ?
89
+ get_current_user() :
90
+ 'unknown';
91
+ self::$_isStorageLoggingOn = ( 1 == get_option( 'fs_storage_logger', 0 ) );
92
+ self::$_abspathLength = strlen( ABSPATH );
93
+ self::$_processID = mt_rand( 0, 32000 );
94
+
95
+ // Process ID may be `false` on errors.
96
+ if ( ! is_numeric( self::$_processID ) ) {
97
+ self::$_processID = 0;
98
+ }
99
+ }
100
+
101
+ private static function hook_footer() {
102
+ if ( self::$_HOOKED_FOOTER ) {
103
+ return;
104
+ }
105
+
106
+ if ( is_admin() ) {
107
+ add_action( 'admin_footer', 'FS_Logger::dump', 100 );
108
+ } else {
109
+ add_action( 'wp_footer', 'FS_Logger::dump', 100 );
110
+ }
111
+ }
112
+
113
+ function is_on() {
114
+ return $this->_on;
115
+ }
116
+
117
+ function on() {
118
+ $this->_on = true;
119
+
120
+ if ( ! function_exists( 'dbDelta' ) ) {
121
+ require_once ABSPATH . 'wp-admin/includes/upgrade.php';
122
+ }
123
+
124
+ self::hook_footer();
125
+ }
126
+
127
+ function echo_on() {
128
+ $this->on();
129
+
130
+ $this->_echo = true;
131
+ }
132
+
133
+ function is_echo_on() {
134
+ return $this->_echo;
135
+ }
136
+
137
+ function get_id() {
138
+ return $this->_id;
139
+ }
140
+
141
+ function get_file() {
142
+ return $this->_file_start;
143
+ }
144
+
145
+ private function _log( &$message, $type = 'log', $wrapper ) {
146
+ if ( ! $this->is_on() ) {
147
+ return;
148
+ }
149
+
150
+ $bt = debug_backtrace();
151
+ $depth = $wrapper ? 3 : 2;
152
+ while ( $depth < count( $bt ) - 1 && 'eval' === $bt[ $depth ]['function'] ) {
153
+ $depth ++;
154
+ }
155
+
156
+ $caller = $bt[ $depth ];
157
+
158
+ /**
159
+ * Retrieve the correct call file & line number from backtrace
160
+ * when logging from a wrapper method.
161
+ *
162
+ * @author Vova Feldman
163
+ * @since 1.2.1.6
164
+ */
165
+ if ( empty( $caller['line'] ) ) {
166
+ $depth --;
167
+
168
+ while ( $depth >= 0 ) {
169
+ if ( ! empty( $bt[ $depth ]['line'] ) ) {
170
+ $caller['line'] = $bt[ $depth ]['line'];
171
+ $caller['file'] = $bt[ $depth ]['file'];
172
+ break;
173
+ }
174
+ }
175
+ }
176
+
177
+ $log = array_merge( $caller, array(
178
+ 'cnt' => self::$CNT ++,
179
+ 'logger' => $this,
180
+ 'timestamp' => microtime( true ),
181
+ 'log_type' => $type,
182
+ 'msg' => $message,
183
+ ) );
184
+
185
+ if ( self::$_isStorageLoggingOn ) {
186
+ $this->db_log( $type, $message, self::$CNT, $caller );
187
+ }
188
+
189
+ self::$LOG[] = $log;
190
+
191
+ if ( $this->is_echo_on() && ! Freemius::is_ajax() ) {
192
+ echo self::format_html( $log ) . "\n";
193
+ }
194
+ }
195
+
196
+ function log( $message, $wrapper = false ) {
197
+ $this->_log( $message, 'log', $wrapper );
198
+ }
199
+
200
+ function info( $message, $wrapper = false ) {
201
+ $this->_log( $message, 'info', $wrapper );
202
+ }
203
+
204
+ function warn( $message, $wrapper = false ) {
205
+ $this->_log( $message, 'warn', $wrapper );
206
+ }
207
+
208
+ function error( $message, $wrapper = false ) {
209
+ $this->_log( $message, 'error', $wrapper );
210
+ }
211
+
212
+ /**
213
+ * Log API error.
214
+ *
215
+ * @author Vova Feldman (@svovaf)
216
+ * @since 1.2.1.5
217
+ *
218
+ * @param mixed $api_result
219
+ * @param bool $wrapper
220
+ */
221
+ function api_error( $api_result, $wrapper = false ) {
222
+ $message = '';
223
+ if ( is_object( $api_result ) && isset( $api_result->error ) ) {
224
+ $message = $api_result->error->message;
225
+ } else if ( is_object( $api_result ) ) {
226
+ $message = var_export( $api_result, true );
227
+ } else if ( is_string( $api_result ) ) {
228
+ $message = $api_result;
229
+ } else if ( empty( $api_result ) ) {
230
+ $message = 'Empty API result.';
231
+ }
232
+
233
+ $message = 'API Error: ' . $message;
234
+
235
+ $this->_log( $message, 'error', $wrapper );
236
+ }
237
+
238
+ function entrance( $message = '', $wrapper = false ) {
239
+ $msg = 'Entrance' . ( empty( $message ) ? '' : ' > ' ) . $message;
240
+
241
+ $this->_log( $msg, 'log', $wrapper );
242
+ }
243
+
244
+ function departure( $message = '', $wrapper = false ) {
245
+ $msg = 'Departure' . ( empty( $message ) ? '' : ' > ' ) . $message;
246
+
247
+ $this->_log( $msg, 'log', $wrapper );
248
+ }
249
+
250
+ #--------------------------------------------------------------------------------
251
+ #region Log Formatting
252
+ #--------------------------------------------------------------------------------
253
+
254
+ private static function format( $log, $show_type = true ) {
255
+ return '[' . str_pad( $log['cnt'], strlen( self::$CNT ), '0', STR_PAD_LEFT ) . '] [' . $log['logger']->_id . '] ' . ( $show_type ? '[' . $log['log_type'] . ']' : '' ) . ( ! empty( $log['class'] ) ? $log['class'] . $log['type'] : '' ) . $log['function'] . ' >> ' . $log['msg'] . ( isset( $log['file'] ) ? ' (' . substr( $log['file'], $log['logger']->_file_start ) . ' ' . $log['line'] . ') ' : '' ) . ' [' . $log['timestamp'] . ']';
256
+ }
257
+
258
+ private static function format_html( $log ) {
259
+ return '<div style="font-size: 13px; font-family: monospace; color: #7da767; padding: 8px 3px; background: #000; border-bottom: 1px solid #555;">[' . $log['cnt'] . '] [' . $log['logger']->_id . '] [' . $log['log_type'] . '] <b><code style="color: #c4b1e0;">' . ( ! empty( $log['class'] ) ? $log['class'] . $log['type'] : '' ) . $log['function'] . '</code> >> <b style="color: #f59330;">' . esc_html( $log['msg'] ) . '</b></b>' . ( isset( $log['file'] ) ? ' (' . substr( $log['file'], $log['logger']->_file_start ) . ' ' . $log['line'] . ')' : '' ) . ' [' . $log['timestamp'] . ']</div>';
260
+ }
261
+
262
+ #endregion
263
+
264
+ static function dump() {
265
+ ?>
266
+ <!-- BEGIN: Freemius PHP Console Log -->
267
+ <script type="text/javascript">
268
+ <?php
269
+ foreach ( self::$LOG as $log ) {
270
+ echo 'console.' . $log['log_type'] . '(' . json_encode( self::format( $log, false ) ) . ')' . "\n";
271
+ }
272
+ ?>
273
+ </script>
274
+ <!-- END: Freemius PHP Console Log -->
275
+ <?php
276
+ }
277
+
278
+ static function get_log() {
279
+ return self::$LOG;
280
+ }
281
+
282
+ #--------------------------------------------------------------------------------
283
+ #region Database Logging
284
+ #--------------------------------------------------------------------------------
285
+
286
+ /**
287
+ * @author Vova Feldman (@svovaf)
288
+ * @since 1.2.1.6
289
+ *
290
+ * @return bool
291
+ */
292
+ public static function is_storage_logging_on() {
293
+ if ( ! isset( self::$_isStorageLoggingOn ) ) {
294
+ self::$_isStorageLoggingOn = ( 1 == get_option( 'fs_storage_logger', 0 ) );
295
+ }
296
+
297
+ return self::$_isStorageLoggingOn;
298
+ }
299
+
300
+ /**
301
+ * Turns on/off database persistent debugging to capture
302
+ * multi-session logs to debug complex flows like
303
+ * plugin auto-deactivate on premium version activation.
304
+ *
305
+ * @todo Check if Theme Check has issues with DB tables for themes.
306
+ *
307
+ * @author Vova Feldman (@svovaf)
308
+ * @since 1.2.1.6
309
+ *
310
+ * @param bool $is_on
311
+ *
312
+ * @return bool
313
+ */
314
+ public static function _set_storage_logging( $is_on = true ) {
315
+ global $wpdb;
316
+
317
+ $table = "{$wpdb->prefix}fs_logger";
318
+
319
+ if ( $is_on ) {
320
+ /**
321
+ * Create logging table.
322
+ *
323
+ * NOTE:
324
+ * dbDelta must use KEY and not INDEX for indexes.
325
+ *
326
+ * @link https://core.trac.wordpress.org/ticket/2695
327
+ */
328
+ $result = $wpdb->query( "CREATE TABLE {$table} (
329
+ `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
330
+ `process_id` INT UNSIGNED NOT NULL,
331
+ `user_name` VARCHAR(64) NOT NULL,
332
+ `logger` VARCHAR(128) NOT NULL,
333
+ `log_order` INT UNSIGNED NOT NULL,
334
+ `type` ENUM('log','info','warn','error') NOT NULL DEFAULT 'log',
335
+ `message` TEXT NOT NULL,
336
+ `file` VARCHAR(256) NOT NULL,
337
+ `line` INT UNSIGNED NOT NULL,
338
+ `function` VARCHAR(256) NOT NULL,
339
+ `request_type` ENUM('call','ajax','cron') NOT NULL DEFAULT 'call',
340
+ `request_url` VARCHAR(1024) NOT NULL,
341
+ `created` DECIMAL(16, 6) NOT NULL,
342
+ PRIMARY KEY (`id`),
343
+ KEY `process_id` (`process_id` ASC),
344
+ KEY `process_logger` (`process_id` ASC, `logger` ASC),
345
+ KEY `function` (`function` ASC),
346
+ KEY `type` (`type` ASC))" );
347
+ } else {
348
+ /**
349
+ * Drop logging table.
350
+ */
351
+ $result = $wpdb->query( "DROP TABLE IF EXISTS $table;" );
352
+ }
353
+
354
+ if ( false !== $result ) {
355
+ update_option( 'fs_storage_logger', ( $is_on ? 1 : 0 ) );
356
+ }
357
+
358
+ return ( false !== $result );
359
+ }
360
+
361
+ /**
362
+ * @author Vova Feldman (@svovaf)
363
+ * @since 1.2.1.6
364
+ *
365
+ * @param string $type
366
+ * @param string $message
367
+ * @param int $log_order
368
+ * @param array $caller
369
+ *
370
+ * @return false|int
371
+ */
372
+ private function db_log(
373
+ &$type,
374
+ &$message,
375
+ &$log_order,
376
+ &$caller
377
+ ) {
378
+ global $wpdb;
379
+
380
+ $request_type = 'call';
381
+ if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
382
+ $request_type = 'cron';
383
+ } else if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
384
+ $request_type = 'ajax';
385
+ }
386
+
387
+ $request_url = WP_FS__IS_HTTP_REQUEST ?
388
+ $_SERVER['REQUEST_URI'] :
389
+ '';
390
+
391
+ return $wpdb->insert(
392
+ "{$wpdb->prefix}fs_logger",
393
+ array(
394
+ 'process_id' => self::$_processID,
395
+ 'user_name' => self::$_ownerName,
396
+ 'logger' => $this->_id,
397
+ 'log_order' => $log_order,
398
+ 'type' => $type,
399
+ 'request_type' => $request_type,
400
+ 'request_url' => $request_url,
401
+ 'message' => $message,
402
+ 'file' => isset( $caller['file'] ) ?
403
+ substr( $caller['file'], self::$_abspathLength ) :
404
+ '',
405
+ 'line' => $caller['line'],
406
+ 'function' => ( ! empty( $caller['class'] ) ? $caller['class'] . $caller['type'] : '' ) . $caller['function'],
407
+ 'created' => microtime( true ),
408
+ )
409
+ );
410
+ }
411
+
412
+ /**
413
+ * Persistent DB logger columns.
414
+ *
415
+ * @var array
416
+ */
417
+ private static $_log_columns = array(
418
+ 'id',
419
+ 'process_id',
420
+ 'user_name',
421
+ 'logger',
422
+ 'log_order',
423
+ 'type',
424
+ 'message',
425
+ 'file',
426
+ 'line',
427
+ 'function',
428
+ 'request_type',
429
+ 'request_url',
430
+ 'created',
431
+ );
432
+
433
+ /**
434
+ * Create DB logs query.
435
+ *
436
+ * @author Vova Feldman (@svovaf)
437
+ * @since 1.2.1.6
438
+ *
439
+ * @param bool $filters
440
+ * @param int $limit
441
+ * @param int $offset
442
+ * @param bool $order
443
+ * @param bool $escape_eol
444
+ *
445
+ * @return string
446
+ */
447
+ private static function build_db_logs_query(
448
+ $filters = false,
449
+ $limit = 200,
450
+ $offset = 0,
451
+ $order = false,
452
+ $escape_eol = false
453
+ ) {
454
+ global $wpdb;
455
+
456
+ $select = '*';
457
+
458
+ if ( $escape_eol ) {
459
+ $select = '';
460
+ for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) {
461
+ if ( $i > 0 ) {
462
+ $select .= ', ';
463
+ }
464
+
465
+ if ( 'message' !== self::$_log_columns[ $i ] ) {
466
+ $select .= self::$_log_columns[ $i ];
467
+ } else {
468
+ $select .= 'REPLACE(message , \'\n\', \' \') AS message';
469
+ }
470
+ }
471
+ }
472
+
473
+ $query = "SELECT {$select} FROM {$wpdb->prefix}fs_logger";
474
+ if ( is_array( $filters ) ) {
475
+ $criteria = array();
476
+
477
+ if ( ! empty( $filters['type'] ) && 'all' !== $filters['type'] ) {
478
+ $filters['type'] = strtolower( $filters['type'] );
479
+
480
+ switch ( $filters['type'] ) {
481
+ case 'warn_error':
482
+ $criteria[] = array( 'col' => 'type', 'val' => array( 'warn', 'error' ) );
483
+ break;
484
+ case 'error':
485
+ case 'warn':
486
+ $criteria[] = array( 'col' => 'type', 'val' => $filters['type'] );
487
+ break;
488
+ case 'info':
489
+ default:
490
+ $criteria[] = array( 'col' => 'type', 'val' => array( 'info', 'log' ) );
491
+ break;
492
+ }
493
+ }
494
+
495
+ if ( ! empty( $filters['request_type'] ) ) {
496
+ $filters['request_type'] = strtolower( $filters['request_type'] );
497
+
498
+ if ( in_array( $filters['request_type'], array( 'call', 'ajax', 'cron' ) ) ) {
499
+ $criteria[] = array( 'col' => 'request_type', 'val' => $filters['request_type'] );
500
+ }
501
+ }
502
+
503
+ if ( ! empty( $filters['file'] ) ) {
504
+ $criteria[] = array(
505
+ 'col' => 'file',
506
+ 'op' => 'LIKE',
507
+ 'val' => '%' . esc_sql( $filters['file'] ),
508
+ );
509
+ }
510
+
511
+ if ( ! empty( $filters['function'] ) ) {
512
+ $criteria[] = array(
513
+ 'col' => 'function',
514
+ 'op' => 'LIKE',
515
+ 'val' => '%' . esc_sql( $filters['function'] ),
516
+ );
517
+ }
518
+
519
+ if ( ! empty( $filters['process_id'] ) && is_numeric( $filters['process_id'] ) ) {
520
+ $criteria[] = array( 'col' => 'process_id', 'val' => $filters['process_id'] );
521
+ }
522
+
523
+ if ( ! empty( $filters['logger'] ) ) {
524
+ $criteria[] = array(
525
+ 'col' => 'logger',
526
+ 'op' => 'LIKE',
527
+ 'val' => '%' . esc_sql( $filters['logger'] ) . '%',
528
+ );
529
+ }
530
+
531
+ if ( ! empty( $filters['message'] ) ) {
532
+ $criteria[] = array(
533
+ 'col' => 'message',
534
+ 'op' => 'LIKE',
535
+ 'val' => '%' . esc_sql( $filters['message'] ) . '%',
536
+ );
537
+ }
538
+
539
+ if ( 0 < count( $criteria ) ) {
540
+ $query .= "\nWHERE\n";
541
+
542
+ $first = true;
543
+ foreach ( $criteria as $c ) {
544
+ if ( ! $first ) {
545
+ $query .= "AND\n";
546
+ }
547
+
548
+ if ( is_array( $c['val'] ) ) {
549
+ $operator = 'IN';
550
+
551
+ for ( $i = 0, $len = count( $c['val'] ); $i < $len; $i ++ ) {
552
+ $c['val'][ $i ] = "'" . esc_sql( $c['val'][ $i ] ) . "'";
553
+ }
554
+
555
+ $val = '(' . implode( ',', $c['val'] ) . ')';
556
+ } else {
557
+ $operator = ! empty( $c['op'] ) ? $c['op'] : '=';
558
+ $val = "'" . esc_sql( $c['val'] ) . "'";
559
+ }
560
+
561
+ $query .= "`{$c['col']}` {$operator} {$val}\n";
562
+
563
+ $first = false;
564
+ }
565
+ }
566
+ }
567
+
568
+ if ( ! is_array( $order ) ) {
569
+ $order = array(
570
+ 'col' => 'id',
571
+ 'order' => 'desc'
572
+ );
573
+ }
574
+
575
+ $query .= " ORDER BY {$order['col']} {$order['order']} LIMIT {$offset},{$limit}";
576
+
577
+ return $query;
578
+ }
579
+
580
+ /**
581
+ * Load logs from DB.
582
+ *
583
+ * @author Vova Feldman (@svovaf)
584
+ * @since 1.2.1.6
585
+ *
586
+ * @param bool $filters
587
+ * @param int $limit
588
+ * @param int $offset
589
+ * @param bool $order
590
+ *
591
+ * @return object[]|null
592
+ */
593
+ public static function load_db_logs(
594
+ $filters = false,
595
+ $limit = 200,
596
+ $offset = 0,
597
+ $order = false
598
+ ) {
599
+ global $wpdb;
600
+
601
+ $query = self::build_db_logs_query(
602
+ $filters,
603
+ $limit,
604
+ $offset,
605
+ $order
606
+ );
607
+
608
+ return $wpdb->get_results( $query );
609
+ }
610
+
611
+ /**
612
+ * Load logs from DB.
613
+ *
614
+ * @author Vova Feldman (@svovaf)
615
+ * @since 1.2.1.6
616
+ *
617
+ * @param bool $filters
618
+ * @param string $filename
619
+ * @param int $limit
620
+ * @param int $offset
621
+ * @param bool $order
622
+ *
623
+ * @return false|string File download URL or false on failure.
624
+ */
625
+ public static function download_db_logs(
626
+ $filters = false,
627
+ $filename = '',
628
+ $limit = 10000,
629
+ $offset = 0,
630
+ $order = false
631
+ ) {
632
+ global $wpdb;
633
+
634
+ $query = self::build_db_logs_query(
635
+ $filters,
636
+ $limit,
637
+ $offset,
638
+ $order,
639
+ true
640
+ );
641
+
642
+ $upload_dir = wp_upload_dir();
643
+ if ( empty( $filename ) ) {
644
+ $filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv';
645
+ }
646
+ $filepath = rtrim( $upload_dir['path'], '/' ) . "/{$filename}";
647
+
648
+ $query .= " INTO OUTFILE '{$filepath}' FIELDS TERMINATED BY '\t' ESCAPED BY '\\\\' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\\n'";
649
+
650
+ $columns = '';
651
+ for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) {
652
+ if ( $i > 0 ) {
653
+ $columns .= ', ';
654
+ }
655
+
656
+ $columns .= "'" . self::$_log_columns[ $i ] . "'";
657
+ }
658
+
659
+ $query = "SELECT {$columns} UNION ALL " . $query;
660
+
661
+ $result = $wpdb->query( $query );
662
+
663
+ if ( false === $result ) {
664
+ return false;
665
+ }
666
+
667
+ return rtrim( $upload_dir['url'], '/' ) . '/' . $filename;
668
+ }
669
+
670
+ /**
671
+ * @author Vova Feldman (@svovaf)
672
+ * @since 1.2.1.6
673
+ *
674
+ * @param string $filename
675
+ *
676
+ * @return string
677
+ */
678
+ public static function get_logs_download_url( $filename = '' ) {
679
+ $upload_dir = wp_upload_dir();
680
+ if ( empty( $filename ) ) {
681
+ $filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv';
682
+ }
683
+
684
+ return rtrim( $upload_dir['url'], '/' ) . $filename;
685
+ }
686
+
687
+ #endregion
688
  }
freemius/includes/class-fs-plugin-updater.php CHANGED
@@ -128,7 +128,7 @@
128
  $plugin_update_row = preg_replace(
129
  '/(\<div.+>)(.+)(\<a.+\<a.+)\<\/div\>/is',
130
  '$1 $2 ' . sprintf(
131
- $this->_fs->get_text( 'renew-license-now' ),
132
  '<a href="' . $this->_fs->pricing_url() . '">', '</a>',
133
  $r->new_version ) .
134
  '$4',
@@ -355,7 +355,7 @@ if ( !isset($info->error) ) {
355
  $data->version = $this->_fs->get_plugin_version();
356
  } else {
357
  if ( $is_addon ) {
358
- $data->name = $addon->title . ' ' . $this->_fs->get_text( 'addon' );
359
  $data->slug = $addon->slug;
360
  $data->url = WP_FS__ADDRESS;
361
  $data->package = $new_version->url;
@@ -487,7 +487,7 @@ if ( !isset($info->error) ) {
487
  if ( ! empty( $plugin_id ) && ! FS_Plugin::is_valid_id( $plugin_id ) ) {
488
  // Invalid plugin ID.
489
  return array(
490
- 'message' => $this->_fs->get_text( 'auto-install-error-invalid-id' ),
491
  'code' => 'invalid_module_id',
492
  );
493
  }
@@ -501,29 +501,27 @@ if ( !isset($info->error) ) {
501
  if ( ! is_object( $addon ) ) {
502
  // Invalid add-on ID.
503
  return array(
504
- 'message' => $this->_fs->get_text( 'auto-install-error-invalid-id' ),
505
  'code' => 'invalid_module_id',
506
  );
507
  }
508
 
509
  $slug = $addon->slug;
510
- $title = $addon->title . ' ' . $this->_fs->get_text( 'addon' );
511
 
512
  $is_addon = true;
513
  } else {
514
  $slug = $this->_fs->get_slug();
515
  $title = $this->_fs->get_plugin_title() .
516
- ( $this->_fs->is_addon() ? ' ' . $this->_fs->get_text( 'addon' ) : '' );
517
  }
518
 
519
  if ( $this->is_premium_plugin_active( $plugin_id ) ) {
520
  // Premium version already activated.
521
  return array(
522
- 'message' => $this->_fs->get_text(
523
- $is_addon ?
524
- 'auto-install-error-premium-addon-activated' :
525
- 'auto-install-error-premium-activated'
526
- ),
527
  'code' => 'premium_installed',
528
  );
529
  }
@@ -552,7 +550,7 @@ if ( !isset($info->error) ) {
552
 
553
  $skin_args = array(
554
  'type' => 'web',
555
- 'title' => sprintf( $this->_fs->get_text( 'installing-plugin-x' ), $title ),
556
  'url' => esc_url_raw( $install_url ),
557
  'nonce' => 'install-plugin_' . $slug,
558
  'plugin' => '',
@@ -593,7 +591,7 @@ if ( !isset($info->error) ) {
593
  global $wp_filesystem;
594
 
595
  $error_code = 'unable_to_connect_to_filesystem';
596
- $error_message = $this->_fs->get_text( 'Unable to connect to the filesystem. Please confirm your credentials.' );
597
 
598
  // Pass through the error from WP_Filesystem if one was raised.
599
  if ( $wp_filesystem instanceof WP_Filesystem_Base &&
@@ -699,7 +697,7 @@ if ( !isset($info->error) ) {
699
  } else {
700
  return new WP_Error(
701
  'rename_failed',
702
- $this->_fs->get_text( 'module-package-rename-failure' ),
703
  array(
704
  'found' => $subdir_name,
705
  'expected' => $desired_slug
128
  $plugin_update_row = preg_replace(
129
  '/(\<div.+>)(.+)(\<a.+\<a.+)\<\/div\>/is',
130
  '$1 $2 ' . sprintf(
131
+ $this->_fs->get_text_inline( '%sRenew your license now%s to access version %s features and support.', 'renew-license-now' ),
132
  '<a href="' . $this->_fs->pricing_url() . '">', '</a>',
133
  $r->new_version ) .
134
  '$4',
355
  $data->version = $this->_fs->get_plugin_version();
356
  } else {
357
  if ( $is_addon ) {
358
+ $data->name = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
359
  $data->slug = $addon->slug;
360
  $data->url = WP_FS__ADDRESS;
361
  $data->package = $new_version->url;
487
  if ( ! empty( $plugin_id ) && ! FS_Plugin::is_valid_id( $plugin_id ) ) {
488
  // Invalid plugin ID.
489
  return array(
490
+ 'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
491
  'code' => 'invalid_module_id',
492
  );
493
  }
501
  if ( ! is_object( $addon ) ) {
502
  // Invalid add-on ID.
503
  return array(
504
+ 'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
505
  'code' => 'invalid_module_id',
506
  );
507
  }
508
 
509
  $slug = $addon->slug;
510
+ $title = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
511
 
512
  $is_addon = true;
513
  } else {
514
  $slug = $this->_fs->get_slug();
515
  $title = $this->_fs->get_plugin_title() .
516
+ ( $this->_fs->is_addon() ? ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' ) : '' );
517
  }
518
 
519
  if ( $this->is_premium_plugin_active( $plugin_id ) ) {
520
  // Premium version already activated.
521
  return array(
522
+ 'message' => $is_addon ?
523
+ $this->_fs->get_text_inline( 'Premium add-on version already installed.', 'auto-install-error-premium-addon-activated' ) :
524
+ $this->_fs->get_text_inline( 'Premium version already active.', 'auto-install-error-premium-activated' ),
 
 
525
  'code' => 'premium_installed',
526
  );
527
  }
550
 
551
  $skin_args = array(
552
  'type' => 'web',
553
+ 'title' => sprintf( $this->_fs->get_text_inline( 'Installing plugin: %s', 'installing-plugin-x' ), $title ),
554
  'url' => esc_url_raw( $install_url ),
555
  'nonce' => 'install-plugin_' . $slug,
556
  'plugin' => '',
591
  global $wp_filesystem;
592
 
593
  $error_code = 'unable_to_connect_to_filesystem';
594
+ $error_message = $this->_fs->get_text_inline( 'Unable to connect to the filesystem. Please confirm your credentials.' );
595
 
596
  // Pass through the error from WP_Filesystem if one was raised.
597
  if ( $wp_filesystem instanceof WP_Filesystem_Base &&
697
  } else {
698
  return new WP_Error(
699
  'rename_failed',
700
+ $this->_fs->get_text_inline( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'module-package-rename-failure' ),
701
  array(
702
  'found' => $subdir_name,
703
  'expected' => $desired_slug
freemius/includes/class-fs-security.php CHANGED
@@ -1,85 +1,85 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- define( 'WP_FS__SECURITY_PARAMS_PREFIX', 's_' );
14
-
15
- /**
16
- * Class FS_Security
17
- */
18
- class FS_Security {
19
- /**
20
- * @var FS_Security
21
- * @since 1.0.3
22
- */
23
- private static $_instance;
24
- /**
25
- * @var FS_Logger
26
- * @since 1.0.3
27
- */
28
- private static $_logger;
29
-
30
- /**
31
- * @return \FS_Security
32
- */
33
- public static function instance() {
34
- if ( ! isset( self::$_instance ) ) {
35
- self::$_instance = new FS_Security();
36
- self::$_logger = FS_Logger::get_logger(
37
- WP_FS__SLUG,
38
- WP_FS__DEBUG_SDK,
39
- WP_FS__ECHO_DEBUG_SDK
40
- );
41
- }
42
-
43
- return self::$_instance;
44
- }
45
-
46
- private function __construct() {
47
- }
48
-
49
- /**
50
- * @param \FS_Scope_Entity $entity
51
- * @param int $timestamp
52
- * @param string $action
53
- *
54
- * @return string
55
- */
56
- function get_secure_token( FS_Scope_Entity $entity, $timestamp, $action = '' ) {
57
- return md5(
58
- $timestamp .
59
- $entity->id .
60
- $entity->secret_key .
61
- $entity->public_key .
62
- $action
63
- );
64
- }
65
-
66
- /**
67
- * @param \FS_Scope_Entity $entity
68
- * @param int|bool $timestamp
69
- * @param string $action
70
- *
71
- * @return array
72
- */
73
- function get_context_params( FS_Scope_Entity $entity, $timestamp = false, $action = '' ) {
74
- if ( false === $timestamp ) {
75
- $timestamp = time();
76
- }
77
-
78
- return array(
79
- 's_ctx_type' => $entity->get_type(),
80
- 's_ctx_id' => $entity->id,
81
- 's_ctx_ts' => $timestamp,
82
- 's_ctx_secure' => $this->get_secure_token( $entity, $timestamp, $action ),
83
- );
84
- }
85
- }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ define( 'WP_FS__SECURITY_PARAMS_PREFIX', 's_' );
14
+
15
+ /**
16
+ * Class FS_Security
17
+ */
18
+ class FS_Security {
19
+ /**
20
+ * @var FS_Security
21
+ * @since 1.0.3
22
+ */
23
+ private static $_instance;
24
+ /**
25
+ * @var FS_Logger
26
+ * @since 1.0.3
27
+ */
28
+ private static $_logger;
29
+
30
+ /**
31
+ * @return \FS_Security
32
+ */
33
+ public static function instance() {
34
+ if ( ! isset( self::$_instance ) ) {
35
+ self::$_instance = new FS_Security();
36
+ self::$_logger = FS_Logger::get_logger(
37
+ WP_FS__SLUG,
38
+ WP_FS__DEBUG_SDK,
39
+ WP_FS__ECHO_DEBUG_SDK
40
+ );
41
+ }
42
+
43
+ return self::$_instance;
44
+ }
45
+
46
+ private function __construct() {
47
+ }
48
+
49
+ /**
50
+ * @param \FS_Scope_Entity $entity
51
+ * @param int $timestamp
52
+ * @param string $action
53
+ *
54
+ * @return string
55
+ */
56
+ function get_secure_token( FS_Scope_Entity $entity, $timestamp, $action = '' ) {
57
+ return md5(
58
+ $timestamp .
59
+ $entity->id .
60
+ $entity->secret_key .
61
+ $entity->public_key .
62
+ $action
63
+ );
64
+ }
65
+
66
+ /**
67
+ * @param \FS_Scope_Entity $entity
68
+ * @param int|bool $timestamp
69
+ * @param string $action
70
+ *
71
+ * @return array
72
+ */
73
+ function get_context_params( FS_Scope_Entity $entity, $timestamp = false, $action = '' ) {
74
+ if ( false === $timestamp ) {
75
+ $timestamp = time();
76
+ }
77
+
78
+ return array(
79
+ 's_ctx_type' => $entity->get_type(),
80
+ 's_ctx_id' => $entity->id,
81
+ 's_ctx_ts' => $timestamp,
82
+ 's_ctx_secure' => $this->get_secure_token( $entity, $timestamp, $action ),
83
+ );
84
+ }
85
+ }
freemius/includes/customizer/class-fs-customizer-support-section.php CHANGED
@@ -52,19 +52,19 @@
52
  $json['theme_title'] = $this->fs->get_plugin_name();
53
 
54
  if ( $is_contact_visible && $is_support_visible ) {
55
- $json['theme_title'] .= ' ' . $this->fs->get_text( 'support' );
56
  }
57
 
58
  if ( $is_contact_visible ) {
59
  $json['contact'] = array(
60
- 'label' => $this->fs->get_text( 'contact-us' ),
61
  'url' => $this->fs->contact_url(),
62
  );
63
  }
64
 
65
  if ( $is_support_visible ) {
66
  $json['support'] = array(
67
- 'label' => $this->fs->get_text( 'support-forum' ),
68
  'url' => $this->fs->get_support_forum_url()
69
  );
70
  }
52
  $json['theme_title'] = $this->fs->get_plugin_name();
53
 
54
  if ( $is_contact_visible && $is_support_visible ) {
55
+ $json['theme_title'] .= ' ' . $this->fs->get_text_inline( 'Support', 'support' );
56
  }
57
 
58
  if ( $is_contact_visible ) {
59
  $json['contact'] = array(
60
+ 'label' => $this->fs->get_text_inline( 'Contact Us', 'contact-us' ),
61
  'url' => $this->fs->contact_url(),
62
  );
63
  }
64
 
65
  if ( $is_support_visible ) {
66
  $json['support'] = array(
67
+ 'label' => $this->fs->get_text_inline( 'Support Forum', 'support-forum' ),
68
  'url' => $this->fs->get_support_forum_url()
69
  );
70
  }
freemius/includes/customizer/class-fs-customizer-upsell-control.php CHANGED
@@ -49,7 +49,7 @@
49
  * Json conversion
50
  */
51
  public function to_json() {
52
- $pricing_cta = esc_html( $this->fs->get_text( $this->fs->get_pricing_cta_label() ) ) . '&nbsp;&nbsp;' . ( is_rtl() ? '&#x2190;' : '&#x27a4;' );
53
 
54
  parent::to_json();
55
 
@@ -103,7 +103,7 @@
103
  $this->json['plans'] = $pricing->plans;
104
 
105
  $this->json['strings'] = array(
106
- 'plan' => $this->fs->get_text( 'plan' ),
107
  );
108
  }
109
 
49
  * Json conversion
50
  */
51
  public function to_json() {
52
+ $pricing_cta = esc_html( $this->fs->get_pricing_cta_label() ) . '&nbsp;&nbsp;' . ( is_rtl() ? '&#x2190;' : '&#x27a4;' );
53
 
54
  parent::to_json();
55
 
103
  $this->json['plans'] = $pricing->plans;
104
 
105
  $this->json['strings'] = array(
106
+ 'plan' => $this->fs->get_text_x_inline( 'Plan', 'as product pricing plan', 'plan' ),
107
  );
108
  }
109
 
freemius/includes/customizer/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden.
3
+ // Hide file structure from users on unprotected servers.
freemius/includes/debug/class-fs-debug-bar-panel.php CHANGED
@@ -45,7 +45,7 @@
45
  $total_time += $l['total'];
46
  }
47
 
48
- return number_format( 100 * $total_time, 2 ) . ' ' . fs_text( 'ms' );
49
  }
50
 
51
  function render() {
45
  $total_time += $l['total'];
46
  }
47
 
48
+ return number_format( 100 * $total_time, 2 ) . ' ' . fs_text_x_inline( 'ms', 'milliseconds' );
49
  }
50
 
51
  function render() {
freemius/includes/debug/debug-bar-start.php CHANGED
@@ -38,8 +38,8 @@
38
  require_once dirname( __FILE__ ) . '/class-fs-debug-bar-panel.php';
39
  $statuses[] = array(
40
  'fs_api_requests',
41
- fs_text( 'Freemius API' ),
42
- Freemius_Debug_Bar_Panel::requests_count() . ' ' . fs_text( 'Requests' ) .
43
  ' (' . Freemius_Debug_Bar_Panel::total_time() . ')'
44
  );
45
  }
38
  require_once dirname( __FILE__ ) . '/class-fs-debug-bar-panel.php';
39
  $statuses[] = array(
40
  'fs_api_requests',
41
+ fs_text_inline( 'Freemius API' ),
42
+ Freemius_Debug_Bar_Panel::requests_count() . ' ' . fs_text_inline( 'Requests' ) .
43
  ' (' . Freemius_Debug_Bar_Panel::total_time() . ')'
44
  );
45
  }
freemius/includes/entities/class-fs-affiliate-terms.php CHANGED
@@ -1,128 +1,128 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.2.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_AffiliateTerms extends FS_Scope_Entity {
14
-
15
- #region Properties
16
-
17
- /**
18
- * @var bool
19
- */
20
- public $is_active;
21
- /**
22
- * @var string Enum: `affiliation` or `rewards`. Defaults to `affiliation`.
23
- */
24
- public $type;
25
- /**
26
- * @var string Enum: `payout` or `credit`. Defaults to `payout`.
27
- */
28
- public $reward_type;
29
- /**
30
- * If `first`, the referral will be attributed to the first visited source containing the affiliation link that
31
- * was clicked.
32
- *
33
- * @var string Enum: `first` or `last`. Defaults to `first`.
34
- */
35
- public $referral_attribution;
36
- /**
37
- * @var int Defaults to `30`, `0` for session cookie, and `null` for endless cookie (until cookies are cleaned).
38
- */
39
- public $cookie_days;
40
- /**
41
- * @var int
42
- */
43
- public $commission;
44
- /**
45
- * @var string Enum: `percentage` or `dollar`. Defaults to `percentage`.
46
- */
47
- public $commission_type;
48
- /**
49
- * @var null|int Defaults to `0` (affiliate only on first payment). `null` for commission for all renewals. If
50
- * greater than `0`, affiliate will get paid for all renewals for `commission_renewals_days` days after
51
- * the initial upgrade/purchase.
52
- */
53
- public $commission_renewals_days;
54
- /**
55
- * @var int Only cents and no percentage. In US cents, e.g.: 100 = $1.00. Defaults to `null`.
56
- */
57
- public $install_commission;
58
- /**
59
- * @var string Required default target link, e.g.: pricing page.
60
- */
61
- public $default_url;
62
- /**
63
- * @var string One of the following: 'all', 'new_customer', 'new_user'.
64
- * If 'all' - reward for any user type.
65
- * If 'new_customer' - reward only for new customers.
66
- * If 'new_user' - reward only for new users.
67
- */
68
- public $reward_customer_type;
69
- /**
70
- * @var int Defaults to `0` (affiliate only on directly affiliated links). `null` if an affiliate will get
71
- * paid for all customers' lifetime payments. If greater than `0`, an affiliate will get paid for all
72
- * customer payments for `future_payments_days` days after the initial payment.
73
- */
74
- public $future_payments_days;
75
- /**
76
- * @var bool If `true`, allow referrals from social sites.
77
- */
78
- public $is_social_allowed;
79
- /**
80
- * @var bool If `true`, allow conversions without HTTP referrer header at all.
81
- */
82
- public $is_app_allowed;
83
- /**
84
- * @var bool If `true`, allow referrals from any site.
85
- */
86
- public $is_any_site_allowed;
87
-
88
- #endregion Properties
89
-
90
- /**
91
- * @author Leo Fajardo (@leorw)
92
- *
93
- * @return string
94
- */
95
- function get_formatted_commission()
96
- {
97
- return ( 'dollar' === $this->commission_type ) ?
98
- ( '$' . $this->commission ) :
99
- ( $this->commission . '%' );
100
- }
101
-
102
- /**
103
- * @author Leo Fajardo (@leorw)
104
- *
105
- * @return bool
106
- */
107
- function has_lifetime_commission() {
108
- return ( 0 !== $this->future_payments_days );
109
- }
110
-
111
- /**
112
- * @author Leo Fajardo (@leorw)
113
- *
114
- * @return bool
115
- */
116
- function is_session_cookie() {
117
- return ( 0 == $this->cookie_days );
118
- }
119
-
120
- /**
121
- * @author Leo Fajardo (@leorw)
122
- *
123
- * @return bool
124
- */
125
- function has_renewals_commission() {
126
- return ( is_null( $this->commission_renewals_days ) || $this->commission_renewals_days > 0 );
127
- }
128
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.2.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_AffiliateTerms extends FS_Scope_Entity {
14
+
15
+ #region Properties
16
+
17
+ /**
18
+ * @var bool
19
+ */
20
+ public $is_active;
21
+ /**
22
+ * @var string Enum: `affiliation` or `rewards`. Defaults to `affiliation`.
23
+ */
24
+ public $type;
25
+ /**
26
+ * @var string Enum: `payout` or `credit`. Defaults to `payout`.
27
+ */
28
+ public $reward_type;
29
+ /**
30
+ * If `first`, the referral will be attributed to the first visited source containing the affiliation link that
31
+ * was clicked.
32
+ *
33
+ * @var string Enum: `first` or `last`. Defaults to `first`.
34
+ */
35
+ public $referral_attribution;
36
+ /**
37
+ * @var int Defaults to `30`, `0` for session cookie, and `null` for endless cookie (until cookies are cleaned).
38
+ */
39
+ public $cookie_days;
40
+ /**
41
+ * @var int
42
+ */
43
+ public $commission;
44
+ /**
45
+ * @var string Enum: `percentage` or `dollar`. Defaults to `percentage`.
46
+ */
47
+ public $commission_type;
48
+ /**
49
+ * @var null|int Defaults to `0` (affiliate only on first payment). `null` for commission for all renewals. If
50
+ * greater than `0`, affiliate will get paid for all renewals for `commission_renewals_days` days after
51
+ * the initial upgrade/purchase.
52
+ */
53
+ public $commission_renewals_days;
54
+ /**
55
+ * @var int Only cents and no percentage. In US cents, e.g.: 100 = $1.00. Defaults to `null`.
56
+ */
57
+ public $install_commission;
58
+ /**
59
+ * @var string Required default target link, e.g.: pricing page.
60
+ */
61
+ public $default_url;
62
+ /**
63
+ * @var string One of the following: 'all', 'new_customer', 'new_user'.
64
+ * If 'all' - reward for any user type.
65
+ * If 'new_customer' - reward only for new customers.
66
+ * If 'new_user' - reward only for new users.
67
+ */
68
+ public $reward_customer_type;
69
+ /**
70
+ * @var int Defaults to `0` (affiliate only on directly affiliated links). `null` if an affiliate will get
71
+ * paid for all customers' lifetime payments. If greater than `0`, an affiliate will get paid for all
72
+ * customer payments for `future_payments_days` days after the initial payment.
73
+ */
74
+ public $future_payments_days;
75
+ /**
76
+ * @var bool If `true`, allow referrals from social sites.
77
+ */
78
+ public $is_social_allowed;
79
+ /**
80
+ * @var bool If `true`, allow conversions without HTTP referrer header at all.
81
+ */
82
+ public $is_app_allowed;
83
+ /**
84
+ * @var bool If `true`, allow referrals from any site.
85
+ */
86
+ public $is_any_site_allowed;
87
+
88
+ #endregion Properties
89
+
90
+ /**
91
+ * @author Leo Fajardo (@leorw)
92
+ *
93
+ * @return string
94
+ */
95
+ function get_formatted_commission()
96
+ {
97
+ return ( 'dollar' === $this->commission_type ) ?
98
+ ( '$' . $this->commission ) :
99
+ ( $this->commission . '%' );
100
+ }
101
+
102
+ /**
103
+ * @author Leo Fajardo (@leorw)
104
+ *
105
+ * @return bool
106
+ */
107
+ function has_lifetime_commission() {
108
+ return ( 0 !== $this->future_payments_days );
109
+ }
110
+
111
+ /**
112
+ * @author Leo Fajardo (@leorw)
113
+ *
114
+ * @return bool
115
+ */
116
+ function is_session_cookie() {
117
+ return ( 0 == $this->cookie_days );
118
+ }
119
+
120
+ /**
121
+ * @author Leo Fajardo (@leorw)
122
+ *
123
+ * @return bool
124
+ */
125
+ function has_renewals_commission() {
126
+ return ( is_null( $this->commission_renewals_days ) || $this->commission_renewals_days > 0 );
127
+ }
128
  }
freemius/includes/entities/class-fs-affiliate.php CHANGED
@@ -1,84 +1,84 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.2.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Affiliate extends FS_Scope_Entity {
14
-
15
- #region Properties
16
-
17
- /**
18
- * @var string
19
- */
20
- public $paypal_email;
21
- /**
22
- * @var number
23
- */
24
- public $custom_affiliate_terms_id;
25
- /**
26
- * @var boolean
27
- */
28
- public $is_using_custom_terms;
29
- /**
30
- * @var string status Enum: `pending`, `rejected`, `suspended`, or `active`. Defaults to `pending`.
31
- */
32
- public $status;
33
- /**
34
- * @var string
35
- */
36
- public $domain;
37
-
38
- #endregion Properties
39
-
40
- /**
41
- * @author Leo Fajardo
42
- *
43
- * @return bool
44
- */
45
- function is_active() {
46
- return ( 'active' === $this->status );
47
- }
48
-
49
- /**
50
- * @author Leo Fajardo
51
- *
52
- * @return bool
53
- */
54
- function is_pending() {
55
- return ( 'pending' === $this->status );
56
- }
57
-
58
- /**
59
- * @author Leo Fajardo
60
- *
61
- * @return bool
62
- */
63
- function is_suspended() {
64
- return ( 'suspended' === $this->status );
65
- }
66
-
67
- /**
68
- * @author Leo Fajardo
69
- *
70
- * @return bool
71
- */
72
- function is_rejected() {
73
- return ( 'rejected' === $this->status );
74
- }
75
-
76
- /**
77
- * @author Leo Fajardo
78
- *
79
- * @return bool
80
- */
81
- function is_blocked() {
82
- return ( 'blocked' === $this->status );
83
- }
84
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.2.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Affiliate extends FS_Scope_Entity {
14
+
15
+ #region Properties
16
+
17
+ /**
18
+ * @var string
19
+ */
20
+ public $paypal_email;
21
+ /**
22
+ * @var number
23
+ */
24
+ public $custom_affiliate_terms_id;
25
+ /**
26
+ * @var boolean
27
+ */
28
+ public $is_using_custom_terms;
29
+ /**
30
+ * @var string status Enum: `pending`, `rejected`, `suspended`, or `active`. Defaults to `pending`.
31
+ */
32
+ public $status;
33
+ /**
34
+ * @var string
35
+ */
36
+ public $domain;
37
+
38
+ #endregion Properties
39
+
40
+ /**
41
+ * @author Leo Fajardo
42
+ *
43
+ * @return bool
44
+ */
45
+ function is_active() {
46
+ return ( 'active' === $this->status );
47
+ }
48
+
49
+ /**
50
+ * @author Leo Fajardo
51
+ *
52
+ * @return bool
53
+ */
54
+ function is_pending() {
55
+ return ( 'pending' === $this->status );
56
+ }
57
+
58
+ /**
59
+ * @author Leo Fajardo
60
+ *
61
+ * @return bool
62
+ */
63
+ function is_suspended() {
64
+ return ( 'suspended' === $this->status );
65
+ }
66
+
67
+ /**
68
+ * @author Leo Fajardo
69
+ *
70
+ * @return bool
71
+ */
72
+ function is_rejected() {
73
+ return ( 'rejected' === $this->status );
74
+ }
75
+
76
+ /**
77
+ * @author Leo Fajardo
78
+ *
79
+ * @return bool
80
+ */
81
+ function is_blocked() {
82
+ return ( 'blocked' === $this->status );
83
+ }
84
  }
freemius/includes/entities/class-fs-billing.php CHANGED
@@ -1,95 +1,95 @@
1
- <?php
2
- /**
3
- * @package Freemius for EDD Add-On
4
- * @copyright Copyright (c) 2016, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.0
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Billing extends FS_Entity {
14
-
15
- #region Properties
16
-
17
- /**
18
- * @var int
19
- */
20
- public $entity_id;
21
- /**
22
- * @var string (Enum) Linked entity type. One of: developer, plugin, user, install
23
- */
24
- public $entity_type;
25
- /**
26
- * @var string
27
- */
28
- public $business_name;
29
- /**
30
- * @var string
31
- */
32
- public $first;
33
- /**
34
- * @var string
35
- */
36
- public $last;
37
- /**
38
- * @var string
39
- */
40
- public $email;
41
- /**
42
- * @var string
43
- */
44
- public $phone;
45
- /**
46
- * @var string
47
- */
48
- public $website;
49
- /**
50
- * @var string Tax or VAT ID.
51
- */
52
- public $tax_id;
53
- /**
54
- * @var string
55
- */
56
- public $address_street;
57
- /**
58
- * @var string
59
- */
60
- public $address_apt;
61
- /**
62
- * @var string
63
- */
64
- public $address_city;
65
- /**
66
- * @var string
67
- */
68
- public $address_country;
69
- /**
70
- * @var string Two chars country code.
71
- */
72
- public $address_country_code;
73
- /**
74
- * @var string
75
- */
76
- public $address_state;
77
- /**
78
- * @var number Numeric ZIP code (cab be with leading zeros).
79
- */
80
- public $address_zip;
81
-
82
- #endregion Properties
83
-
84
-
85
- /**
86
- * @param object|bool $event
87
- */
88
- function __construct( $event = false ) {
89
- parent::__construct( $event );
90
- }
91
-
92
- static function get_type() {
93
- return 'billing';
94
- }
95
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius for EDD Add-On
4
+ * @copyright Copyright (c) 2016, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.0
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Billing extends FS_Entity {
14
+
15
+ #region Properties
16
+
17
+ /**
18
+ * @var int
19
+ */
20
+ public $entity_id;
21
+ /**
22
+ * @var string (Enum) Linked entity type. One of: developer, plugin, user, install
23
+ */
24
+ public $entity_type;
25
+ /**
26
+ * @var string
27
+ */
28
+ public $business_name;
29
+ /**
30
+ * @var string
31
+ */
32
+ public $first;
33
+ /**
34
+ * @var string
35
+ */
36
+ public $last;
37
+ /**
38
+ * @var string
39
+ */
40
+ public $email;
41
+ /**
42
+ * @var string
43
+ */
44
+ public $phone;
45
+ /**
46
+ * @var string
47
+ */
48
+ public $website;
49
+ /**
50
+ * @var string Tax or VAT ID.
51
+ */
52
+ public $tax_id;
53
+ /**
54
+ * @var string
55
+ */
56
+ public $address_street;
57
+ /**
58
+ * @var string
59
+ */
60
+ public $address_apt;
61
+ /**
62
+ * @var string
63
+ */
64
+ public $address_city;
65
+ /**
66
+ * @var string
67
+ */
68
+ public $address_country;
69
+ /**
70
+ * @var string Two chars country code.
71
+ */
72
+ public $address_country_code;
73
+ /**
74
+ * @var string
75
+ */
76
+ public $address_state;
77
+ /**
78
+ * @var number Numeric ZIP code (cab be with leading zeros).
79
+ */
80
+ public $address_zip;
81
+
82
+ #endregion Properties
83
+
84
+
85
+ /**
86
+ * @param object|bool $event
87
+ */
88
+ function __construct( $event = false ) {
89
+ parent::__construct( $event );
90
+ }
91
+
92
+ static function get_type() {
93
+ return 'billing';
94
+ }
95
  }
freemius/includes/entities/class-fs-entity.php CHANGED
@@ -1,149 +1,149 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- /**
14
- * Get object's public variables.
15
- *
16
- * @author Vova Feldman (@svovaf)
17
- * @since 1.0.0
18
- *
19
- * @param object $object
20
- *
21
- * @return array
22
- */
23
- function fs_get_object_public_vars( $object ) {
24
- return get_object_vars( $object );
25
- }
26
-
27
- class FS_Entity {
28
- /**
29
- * @var number
30
- */
31
- public $id;
32
- /**
33
- * @var string Datetime value in 'YYYY-MM-DD HH:MM:SS' format.
34
- */
35
- public $updated;
36
- /**
37
- * @var string Datetime value in 'YYYY-MM-DD HH:MM:SS' format.
38
- */
39
- public $created;
40
-
41
- /**
42
- * @param bool|object $entity
43
- */
44
- function __construct( $entity = false ) {
45
- if ( ! ( $entity instanceof stdClass ) && ! ( $entity instanceof FS_Entity ) ) {
46
- return;
47
- }
48
-
49
- $props = fs_get_object_public_vars( $this );
50
-
51
- foreach ( $props as $key => $def_value ) {
52
- $this->{$key} = isset( $entity->{$key} ) ?
53
- $entity->{$key} :
54
- $def_value;
55
- }
56
- }
57
-
58
- static function get_type() {
59
- return 'type';
60
- }
61
-
62
- /**
63
- * @author Vova Feldman (@svovaf)
64
- * @since 1.0.6
65
- *
66
- * @param FS_Entity $entity1
67
- * @param FS_Entity $entity2
68
- *
69
- * @return bool
70
- */
71
- static function equals( $entity1, $entity2 ) {
72
- if ( is_null( $entity1 ) && is_null( $entity2 ) ) {
73
- return true;
74
- } else if ( is_object( $entity1 ) && is_object( $entity2 ) ) {
75
- return ( $entity1->id == $entity2->id );
76
- } else if ( is_object( $entity1 ) ) {
77
- return is_null( $entity1->id );
78
- } else {
79
- return is_null( $entity2->id );
80
- }
81
- }
82
-
83
- private $_is_updated = false;
84
-
85
- /**
86
- * Update object property.
87
- *
88
- * @author Vova Feldman (@svovaf)
89
- * @since 1.0.9
90
- *
91
- * @param string|array[string]mixed $key
92
- * @param string|bool $val
93
- *
94
- * @return bool
95
- */
96
- function update( $key, $val = false ) {
97
- if ( ! is_array( $key ) ) {
98
- $key = array( $key => $val );
99
- }
100
-
101
- $is_updated = false;
102
-
103
- foreach ( $key as $k => $v ) {
104
- if ( $this->{$k} === $v ) {
105
- continue;
106
- }
107
-
108
- if ( ( is_string( $this->{$k} ) && is_numeric( $v ) ||
109
- ( is_numeric( $this->{$k} ) && is_string( $v ) ) ) &&
110
- $this->{$k} == $v
111
- ) {
112
- continue;
113
- }
114
-
115
- // Update value.
116
- $this->{$k} = $v;
117
-
118
- $is_updated = true;
119
- }
120
-
121
- $this->_is_updated = $is_updated;
122
-
123
- return $is_updated;
124
- }
125
-
126
- /**
127
- * Checks if entity was updated.
128
- *
129
- * @author Vova Feldman (@svovaf)
130
- * @since 1.0.9
131
- *
132
- * @return bool
133
- */
134
- function is_updated() {
135
- return $this->_is_updated;
136
- }
137
-
138
- /**
139
- * @param $id
140
- *
141
- * @author Vova Feldman (@svovaf)
142
- * @since 1.1.2
143
- *
144
- * @return bool
145
- */
146
- static function is_valid_id($id){
147
- return is_numeric($id);
148
- }
149
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Get object's public variables.
15
+ *
16
+ * @author Vova Feldman (@svovaf)
17
+ * @since 1.0.0
18
+ *
19
+ * @param object $object
20
+ *
21
+ * @return array
22
+ */
23
+ function fs_get_object_public_vars( $object ) {
24
+ return get_object_vars( $object );
25
+ }
26
+
27
+ class FS_Entity {
28
+ /**
29
+ * @var number
30
+ */
31
+ public $id;
32
+ /**
33
+ * @var string Datetime value in 'YYYY-MM-DD HH:MM:SS' format.
34
+ */
35
+ public $updated;
36
+ /**
37
+ * @var string Datetime value in 'YYYY-MM-DD HH:MM:SS' format.
38
+ */
39
+ public $created;
40
+
41
+ /**
42
+ * @param bool|object $entity
43
+ */
44
+ function __construct( $entity = false ) {
45
+ if ( ! ( $entity instanceof stdClass ) && ! ( $entity instanceof FS_Entity ) ) {
46
+ return;
47
+ }
48
+
49
+ $props = fs_get_object_public_vars( $this );
50
+
51
+ foreach ( $props as $key => $def_value ) {
52
+ $this->{$key} = isset( $entity->{$key} ) ?
53
+ $entity->{$key} :
54
+ $def_value;
55
+ }
56
+ }
57
+
58
+ static function get_type() {
59
+ return 'type';
60
+ }
61
+
62
+ /**
63
+ * @author Vova Feldman (@svovaf)
64
+ * @since 1.0.6
65
+ *
66
+ * @param FS_Entity $entity1
67
+ * @param FS_Entity $entity2
68
+ *
69
+ * @return bool
70
+ */
71
+ static function equals( $entity1, $entity2 ) {
72
+ if ( is_null( $entity1 ) && is_null( $entity2 ) ) {
73
+ return true;
74
+ } else if ( is_object( $entity1 ) && is_object( $entity2 ) ) {
75
+ return ( $entity1->id == $entity2->id );
76
+ } else if ( is_object( $entity1 ) ) {
77
+ return is_null( $entity1->id );
78
+ } else {
79
+ return is_null( $entity2->id );
80
+ }
81
+ }
82
+
83
+ private $_is_updated = false;
84
+
85
+ /**
86
+ * Update object property.
87
+ *
88
+ * @author Vova Feldman (@svovaf)
89
+ * @since 1.0.9
90
+ *
91
+ * @param string|array[string]mixed $key
92
+ * @param string|bool $val
93
+ *
94
+ * @return bool
95
+ */
96
+ function update( $key, $val = false ) {
97
+ if ( ! is_array( $key ) ) {
98
+ $key = array( $key => $val );
99
+ }
100
+
101
+ $is_updated = false;
102
+
103
+ foreach ( $key as $k => $v ) {
104
+ if ( $this->{$k} === $v ) {
105
+ continue;
106
+ }
107
+
108
+ if ( ( is_string( $this->{$k} ) && is_numeric( $v ) ||
109
+ ( is_numeric( $this->{$k} ) && is_string( $v ) ) ) &&
110
+ $this->{$k} == $v
111
+ ) {
112
+ continue;
113
+ }
114
+
115
+ // Update value.
116
+ $this->{$k} = $v;
117
+
118
+ $is_updated = true;
119
+ }
120
+
121
+ $this->_is_updated = $is_updated;
122
+
123
+ return $is_updated;
124
+ }
125
+
126
+ /**
127
+ * Checks if entity was updated.
128
+ *
129
+ * @author Vova Feldman (@svovaf)
130
+ * @since 1.0.9
131
+ *
132
+ * @return bool
133
+ */
134
+ function is_updated() {
135
+ return $this->_is_updated;
136
+ }
137
+
138
+ /**
139
+ * @param $id
140
+ *
141
+ * @author Vova Feldman (@svovaf)
142
+ * @since 1.1.2
143
+ *
144
+ * @return bool
145
+ */
146
+ static function is_valid_id($id){
147
+ return is_numeric($id);
148
+ }
149
  }
freemius/includes/entities/class-fs-payment.php CHANGED
@@ -1,94 +1,94 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2016, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.0
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Payment extends FS_Entity {
14
-
15
- #region Properties
16
-
17
- /**
18
- * @var number
19
- */
20
- public $plugin_id;
21
- /**
22
- * @var number
23
- */
24
- public $user_id;
25
- /**
26
- * @var number
27
- */
28
- public $install_id;
29
- /**
30
- * @var number
31
- */
32
- public $subscription_id;
33
- /**
34
- * @var number
35
- */
36
- public $plan_id;
37
- /**
38
- * @var number
39
- */
40
- public $license_id;
41
- /**
42
- * @var float
43
- */
44
- public $gross;
45
- /**
46
- * @var number
47
- */
48
- public $bound_payment_id;
49
- /**
50
- * @var string
51
- */
52
- public $external_id;
53
- /**
54
- * @var string
55
- */
56
- public $gateway;
57
- /**
58
- * @var string ISO 3166-1 alpha-2 - two-letter country code.
59
- *
60
- * @link http://www.wikiwand.com/en/ISO_3166-1_alpha-2
61
- */
62
- public $country_code;
63
- /**
64
- * @var string
65
- */
66
- public $vat_id;
67
- /**
68
- * @var float Actual Tax / VAT in $$$
69
- */
70
- public $vat;
71
-
72
- #endregion Properties
73
-
74
- /**
75
- * @param object|bool $payment
76
- */
77
- function __construct( $payment = false ) {
78
- parent::__construct( $payment );
79
- }
80
-
81
- static function get_type() {
82
- return 'payment';
83
- }
84
-
85
- /**
86
- * @author Vova Feldman (@svovaf)
87
- * @since 1.0.0
88
- *
89
- * @return bool
90
- */
91
- function is_refund() {
92
- return ( parent::is_valid_id( $this->bound_payment_id ) && 0 > $this->gross );
93
- }
94
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2016, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.0
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Payment extends FS_Entity {
14
+
15
+ #region Properties
16
+
17
+ /**
18
+ * @var number
19
+ */
20
+ public $plugin_id;
21
+ /**
22
+ * @var number
23
+ */
24
+ public $user_id;
25
+ /**
26
+ * @var number
27
+ */
28
+ public $install_id;
29
+ /**
30
+ * @var number
31
+ */
32
+ public $subscription_id;
33
+ /**
34
+ * @var number
35
+ */
36
+ public $plan_id;
37
+ /**
38
+ * @var number
39
+ */
40
+ public $license_id;
41
+ /**
42
+ * @var float
43
+ */
44
+ public $gross;
45
+ /**
46
+ * @var number
47
+ */
48
+ public $bound_payment_id;
49
+ /**
50
+ * @var string
51
+ */
52
+ public $external_id;
53
+ /**
54
+ * @var string
55
+ */
56
+ public $gateway;
57
+ /**
58
+ * @var string ISO 3166-1 alpha-2 - two-letter country code.
59
+ *
60
+ * @link http://www.wikiwand.com/en/ISO_3166-1_alpha-2
61
+ */
62
+ public $country_code;
63
+ /**
64
+ * @var string
65
+ */
66
+ public $vat_id;
67
+ /**
68
+ * @var float Actual Tax / VAT in $$$
69
+ */
70
+ public $vat;
71
+
72
+ #endregion Properties
73
+
74
+ /**
75
+ * @param object|bool $payment
76
+ */
77
+ function __construct( $payment = false ) {
78
+ parent::__construct( $payment );
79
+ }
80
+
81
+ static function get_type() {
82
+ return 'payment';
83
+ }
84
+
85
+ /**
86
+ * @author Vova Feldman (@svovaf)
87
+ * @since 1.0.0
88
+ *
89
+ * @return bool
90
+ */
91
+ function is_refund() {
92
+ return ( parent::is_valid_id( $this->bound_payment_id ) && 0 > $this->gross );
93
+ }
94
  }
freemius/includes/entities/class-fs-plugin-info.php CHANGED
@@ -1,34 +1,34 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Plugin_Info extends FS_Entity {
14
- public $plugin_id;
15
- public $description;
16
- public $short_description;
17
- public $banner_url;
18
- public $card_banner_url;
19
- public $selling_point_0;
20
- public $selling_point_1;
21
- public $selling_point_2;
22
- public $screenshots;
23
-
24
- /**
25
- * @param stdClass|bool $plugin_info
26
- */
27
- function __construct( $plugin_info = false ) {
28
- parent::__construct( $plugin_info );
29
- }
30
-
31
- static function get_type() {
32
- return 'plugin';
33
- }
34
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Plugin_Info extends FS_Entity {
14
+ public $plugin_id;
15
+ public $description;
16
+ public $short_description;
17
+ public $banner_url;
18
+ public $card_banner_url;
19
+ public $selling_point_0;
20
+ public $selling_point_1;
21
+ public $selling_point_2;
22
+ public $screenshots;
23
+
24
+ /**
25
+ * @param stdClass|bool $plugin_info
26
+ */
27
+ function __construct( $plugin_info = false ) {
28
+ parent::__construct( $plugin_info );
29
+ }
30
+
31
+ static function get_type() {
32
+ return 'plugin';
33
+ }
34
  }
freemius/includes/entities/class-fs-plugin-license.php CHANGED
@@ -1,224 +1,224 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.5
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- /**
14
- * Class FS_Plugin_License
15
- */
16
- class FS_Plugin_License extends FS_Entity {
17
-
18
- #region Properties
19
-
20
- /**
21
- * @var number
22
- */
23
- public $plugin_id;
24
- /**
25
- * @var number
26
- */
27
- public $user_id;
28
- /**
29
- * @var number
30
- */
31
- public $plan_id;
32
- /**
33
- * @var number
34
- */
35
- public $pricing_id;
36
- /**
37
- * @var int|null
38
- */
39
- public $quota;
40
- /**
41
- * @var int
42
- */
43
- public $activated;
44
- /**
45
- * @var int
46
- */
47
- public $activated_local;
48
- /**
49
- * @var string
50
- */
51
- public $expiration;
52
- /**
53
- * @var string
54
- */
55
- public $secret_key;
56
- /**
57
- * @var bool $is_free_localhost Defaults to true. If true, allow unlimited localhost installs with the same
58
- * license.
59
- */
60
- public $is_free_localhost;
61
- /**
62
- * @var bool $is_block_features Defaults to true. If false, don't block features after license expiry - only
63
- * block updates and support.
64
- */
65
- public $is_block_features;
66
- /**
67
- * @var bool
68
- */
69
- public $is_cancelled;
70
-
71
- #endregion Properties
72
-
73
- /**
74
- * @param stdClass|bool $license
75
- */
76
- function __construct( $license = false ) {
77
- parent::__construct( $license );
78
- }
79
-
80
- /**
81
- * Get entity type.
82
- *
83
- * @return string
84
- */
85
- static function get_type() {
86
- return 'license';
87
- }
88
-
89
- /**
90
- * Check how many site activations left.
91
- *
92
- * @author Vova Feldman (@svovaf)
93
- * @since 1.0.5
94
- *
95
- * @return int
96
- */
97
- function left() {
98
- if ( ! $this->is_active() || $this->is_expired() ) {
99
- return 0;
100
- }
101
-
102
- if ( $this->is_unlimited() ) {
103
- return 999;
104
- }
105
-
106
- return ( $this->quota - $this->activated - ( $this->is_free_localhost ? 0 : $this->activated_local ) );
107
- }
108
-
109
- /**
110
- * Check if single site license.
111
- *
112
- * @author Vova Feldman (@svovaf)
113
- * @since 1.1.8.1
114
- *
115
- * @return bool
116
- */
117
- function is_single_site() {
118
- return ( is_numeric( $this->quota ) && 1 == $this->quota );
119
- }
120
-
121
- /**
122
- * @author Vova Feldman (@svovaf)
123
- * @since 1.0.5
124
- *
125
- * @return bool
126
- */
127
- function is_expired() {
128
- return ! $this->is_lifetime() && ( strtotime( $this->expiration ) < WP_FS__SCRIPT_START_TIME );
129
- }
130
-
131
- /**
132
- * Check if license is not expired.
133
- *
134
- * @author Vova Feldman (@svovaf)
135
- * @since 1.2.1
136
- *
137
- * @return bool
138
- */
139
- function is_valid() {
140
- return ! $this->is_expired();
141
- }
142
-
143
- /**
144
- * @author Vova Feldman (@svovaf)
145
- * @since 1.0.6
146
- *
147
- * @return bool
148
- */
149
- function is_lifetime() {
150
- return is_null( $this->expiration );
151
- }
152
-
153
- /**
154
- * @author Vova Feldman (@svovaf)
155
- * @since 1.2.0
156
- *
157
- * @return bool
158
- */
159
- function is_unlimited() {
160
- return is_null( $this->quota );
161
- }
162
-
163
- /**
164
- * Check if license is fully utilized.
165
- *
166
- * @author Vova Feldman (@svovaf)
167
- * @since 1.0.6
168
- *
169
- * @param bool $is_localhost
170
- *
171
- * @return bool
172
- */
173
- function is_utilized( $is_localhost = null ) {
174
- if ( is_null( $is_localhost ) ) {
175
- $is_localhost = WP_FS__IS_LOCALHOST_FOR_SERVER;
176
- }
177
-
178
- if ( $this->is_unlimited() ) {
179
- return false;
180
- }
181
-
182
- return ! ( $this->is_free_localhost && $is_localhost ) &&
183
- ( $this->quota <= $this->activated + ( $this->is_free_localhost ? 0 : $this->activated_local ) );
184
- }
185
-
186
- /**
187
- * @author Vova Feldman (@svovaf)
188
- * @since 1.2.1
189
- *
190
- * @return bool
191
- */
192
- function is_active() {
193
- return ( ! $this->is_cancelled );
194
- }
195
-
196
- /**
197
- * Check if license's plan features are enabled.
198
- *
199
- * - Either if plan not expired
200
- * - If expired, based on the configuration to block features or not.
201
- *
202
- * @author Vova Feldman (@svovaf)
203
- * @since 1.0.6
204
- *
205
- * @return bool
206
- */
207
- function is_features_enabled() {
208
- return $this->is_active() && ( ! $this->is_block_features || ! $this->is_expired() );
209
- }
210
-
211
- /**
212
- * Subscription considered to be new without any payments
213
- * if the license expires in less than 24 hours
214
- * from the license creation.
215
- *
216
- * @author Vova Feldman (@svovaf)
217
- * @since 1.0.9
218
- *
219
- * @return bool
220
- */
221
- function is_first_payment_pending() {
222
- return ( WP_FS__TIME_24_HOURS_IN_SEC >= strtotime( $this->expiration ) - strtotime( $this->created ) );
223
- }
224
- }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.5
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Class FS_Plugin_License
15
+ */
16
+ class FS_Plugin_License extends FS_Entity {
17
+
18
+ #region Properties
19
+
20
+ /**
21
+ * @var number
22
+ */
23
+ public $plugin_id;
24
+ /**
25
+ * @var number
26
+ */
27
+ public $user_id;
28
+ /**
29
+ * @var number
30
+ */
31
+ public $plan_id;
32
+ /**
33
+ * @var number
34
+ */
35
+ public $pricing_id;
36
+ /**
37
+ * @var int|null
38
+ */
39
+ public $quota;
40
+ /**
41
+ * @var int
42
+ */
43
+ public $activated;
44
+ /**
45
+ * @var int
46
+ */
47
+ public $activated_local;
48
+ /**
49
+ * @var string
50
+ */
51
+ public $expiration;
52
+ /**
53
+ * @var string
54
+ */
55
+ public $secret_key;
56
+ /**
57
+ * @var bool $is_free_localhost Defaults to true. If true, allow unlimited localhost installs with the same
58
+ * license.
59
+ */
60
+ public $is_free_localhost;
61
+ /**
62
+ * @var bool $is_block_features Defaults to true. If false, don't block features after license expiry - only
63
+ * block updates and support.
64
+ */
65
+ public $is_block_features;
66
+ /**
67
+ * @var bool
68
+ */
69
+ public $is_cancelled;
70
+
71
+ #endregion Properties
72
+
73
+ /**
74
+ * @param stdClass|bool $license
75
+ */
76
+ function __construct( $license = false ) {
77
+ parent::__construct( $license );
78
+ }
79
+
80
+ /**
81
+ * Get entity type.
82
+ *
83
+ * @return string
84
+ */
85
+ static function get_type() {
86
+ return 'license';
87
+ }
88
+
89
+ /**
90
+ * Check how many site activations left.
91
+ *
92
+ * @author Vova Feldman (@svovaf)
93
+ * @since 1.0.5
94
+ *
95
+ * @return int
96
+ */
97
+ function left() {
98
+ if ( ! $this->is_active() || $this->is_expired() ) {
99
+ return 0;
100
+ }
101
+
102
+ if ( $this->is_unlimited() ) {
103
+ return 999;
104
+ }
105
+
106
+ return ( $this->quota - $this->activated - ( $this->is_free_localhost ? 0 : $this->activated_local ) );
107
+ }
108
+
109
+ /**
110
+ * Check if single site license.
111
+ *
112
+ * @author Vova Feldman (@svovaf)
113
+ * @since 1.1.8.1
114
+ *
115
+ * @return bool
116
+ */
117
+ function is_single_site() {
118
+ return ( is_numeric( $this->quota ) && 1 == $this->quota );
119
+ }
120
+
121
+ /**
122
+ * @author Vova Feldman (@svovaf)
123
+ * @since 1.0.5
124
+ *
125
+ * @return bool
126
+ */
127
+ function is_expired() {
128
+ return ! $this->is_lifetime() && ( strtotime( $this->expiration ) < WP_FS__SCRIPT_START_TIME );
129
+ }
130
+
131
+ /**
132
+ * Check if license is not expired.
133
+ *
134
+ * @author Vova Feldman (@svovaf)
135
+ * @since 1.2.1
136
+ *
137
+ * @return bool
138
+ */
139
+ function is_valid() {
140
+ return ! $this->is_expired();
141
+ }
142
+
143
+ /**
144
+ * @author Vova Feldman (@svovaf)
145
+ * @since 1.0.6
146
+ *
147
+ * @return bool
148
+ */
149
+ function is_lifetime() {
150
+ return is_null( $this->expiration );
151
+ }
152
+
153
+ /**
154
+ * @author Vova Feldman (@svovaf)
155
+ * @since 1.2.0
156
+ *
157
+ * @return bool
158
+ */
159
+ function is_unlimited() {
160
+ return is_null( $this->quota );
161
+ }
162
+
163
+ /**
164
+ * Check if license is fully utilized.
165
+ *
166
+ * @author Vova Feldman (@svovaf)
167
+ * @since 1.0.6
168
+ *
169
+ * @param bool $is_localhost
170
+ *
171
+ * @return bool
172
+ */
173
+ function is_utilized( $is_localhost = null ) {
174
+ if ( is_null( $is_localhost ) ) {
175
+ $is_localhost = WP_FS__IS_LOCALHOST_FOR_SERVER;
176
+ }
177
+
178
+ if ( $this->is_unlimited() ) {
179
+ return false;
180
+ }
181
+
182
+ return ! ( $this->is_free_localhost && $is_localhost ) &&
183
+ ( $this->quota <= $this->activated + ( $this->is_free_localhost ? 0 : $this->activated_local ) );
184
+ }
185
+
186
+ /**
187
+ * @author Vova Feldman (@svovaf)
188
+ * @since 1.2.1
189
+ *
190
+ * @return bool
191
+ */
192
+ function is_active() {
193
+ return ( ! $this->is_cancelled );
194
+ }
195
+
196
+ /**
197
+ * Check if license's plan features are enabled.
198
+ *
199
+ * - Either if plan not expired
200
+ * - If expired, based on the configuration to block features or not.
201
+ *
202
+ * @author Vova Feldman (@svovaf)
203
+ * @since 1.0.6
204
+ *
205
+ * @return bool
206
+ */
207
+ function is_features_enabled() {
208
+ return $this->is_active() && ( ! $this->is_block_features || ! $this->is_expired() );
209
+ }
210
+
211
+ /**
212
+ * Subscription considered to be new without any payments
213
+ * if the license expires in less than 24 hours
214
+ * from the license creation.
215
+ *
216
+ * @author Vova Feldman (@svovaf)
217
+ * @since 1.0.9
218
+ *
219
+ * @return bool
220
+ */
221
+ function is_first_payment_pending() {
222
+ return ( WP_FS__TIME_24_HOURS_IN_SEC >= strtotime( $this->expiration ) - strtotime( $this->created ) );
223
+ }
224
+ }
freemius/includes/entities/class-fs-plugin-plan.php CHANGED
@@ -1,145 +1,145 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.5
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- /**
14
- * Class FS_Plugin_Plan
15
- *
16
- * @property FS_Pricing[] $pricing
17
- */
18
- class FS_Plugin_Plan extends FS_Entity {
19
-
20
- #region Properties
21
-
22
- /**
23
- * @var number
24
- */
25
- public $plugin_id;
26
- /**
27
- * @var string
28
- */
29
- public $name;
30
- /**
31
- * @var string
32
- */
33
- public $title;
34
- /**
35
- * @var string
36
- */
37
- public $description;
38
- /**
39
- * @var bool Defaults to true. If true, allow unlimited localhost installs with the same license.
40
- */
41
- public $is_free_localhost;
42
- /**
43
- * @var bool Defaults to true. If false, don't block features after license expiry - only block updates and
44
- * support.
45
- */
46
- public $is_block_features;
47
- /**
48
- * @var int
49
- */
50
- public $license_type;
51
- /**
52
- * @var bool
53
- */
54
- public $is_https_support;
55
- /**
56
- * @var int Trial days.
57
- */
58
- public $trial_period;
59
- /**
60
- * @var string If true, require payment for trial.
61
- */
62
- public $is_require_subscription;
63
- /**
64
- * @var string Knowledge Base URL.
65
- */
66
- public $support_kb;
67
- /**
68
- * @var string Support Forum URL.
69
- */
70
- public $support_forum;
71
- /**
72
- * @var string Support email address.
73
- */
74
- public $support_email;
75
- /**
76
- * @var string Support phone.
77
- */
78
- public $support_phone;
79
- /**
80
- * @var string Support skype username.
81
- */
82
- public $support_skype;
83
- /**
84
- * @var bool Is personal success manager supported with the plan.
85
- */
86
- public $is_success_manager;
87
- /**
88
- * @var bool Is featured plan.
89
- */
90
- public $is_featured;
91
-
92
- #endregion Properties
93
-
94
- /**
95
- * @param object|bool $plan
96
- */
97
- function __construct( $plan = false ) {
98
- parent::__construct( $plan );
99
-
100
- if ( is_object( $plan ) ) {
101
- $this->name = strtolower( $plan->name );
102
- }
103
- }
104
-
105
- static function get_type() {
106
- return 'plan';
107
- }
108
-
109
- /**
110
- * @author Vova Feldman (@svovaf)
111
- * @since 1.0.9
112
- *
113
- * @return bool
114
- */
115
- function is_free() {
116
- return ( 'free' === $this->name );
117
- }
118
-
119
- /**
120
- * Checks if this plan supports "Technical Support".
121
- *
122
- * @author Leo Fajardo (leorw)
123
- * @since 1.2.0
124
- *
125
- * @return bool
126
- */
127
- function has_technical_support() {
128
- return ( ! empty( $this->support_email ) ||
129
- ! empty( $this->support_skype ) ||
130
- ! empty( $this->support_phone ) ||
131
- ! empty( $this->is_success_manager )
132
- );
133
- }
134
-
135
- /**
136
- * @author Vova Feldman (@svovaf)
137
- * @since 1.0.9
138
- *
139
- * @return bool
140
- */
141
- function has_trial() {
142
- return ! $this->is_free() &&
143
- is_numeric( $this->trial_period ) && ( $this->trial_period > 0 );
144
- }
145
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.5
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Class FS_Plugin_Plan
15
+ *
16
+ * @property FS_Pricing[] $pricing
17
+ */
18
+ class FS_Plugin_Plan extends FS_Entity {
19
+
20
+ #region Properties
21
+
22
+ /**
23
+ * @var number
24
+ */
25
+ public $plugin_id;
26
+ /**
27
+ * @var string
28
+ */
29
+ public $name;
30
+ /**
31
+ * @var string
32
+ */
33
+ public $title;
34
+ /**
35
+ * @var string
36
+ */
37
+ public $description;
38
+ /**
39
+ * @var bool Defaults to true. If true, allow unlimited localhost installs with the same license.
40
+ */
41
+ public $is_free_localhost;
42
+ /**
43
+ * @var bool Defaults to true. If false, don't block features after license expiry - only block updates and
44
+ * support.
45
+ */
46
+ public $is_block_features;
47
+ /**
48
+ * @var int
49
+ */
50
+ public $license_type;
51
+ /**
52
+ * @var bool
53
+ */
54
+ public $is_https_support;
55
+ /**
56
+ * @var int Trial days.
57
+ */
58
+ public $trial_period;
59
+ /**
60
+ * @var string If true, require payment for trial.
61
+ */
62
+ public $is_require_subscription;
63
+ /**
64
+ * @var string Knowledge Base URL.
65
+ */
66
+ public $support_kb;
67
+ /**
68
+ * @var string Support Forum URL.
69
+ */
70
+ public $support_forum;
71
+ /**
72
+ * @var string Support email address.
73
+ */
74
+ public $support_email;
75
+ /**
76
+ * @var string Support phone.
77
+ */
78
+ public $support_phone;
79
+ /**
80
+ * @var string Support skype username.
81
+ */
82
+ public $support_skype;
83
+ /**
84
+ * @var bool Is personal success manager supported with the plan.
85
+ */
86
+ public $is_success_manager;
87
+ /**
88
+ * @var bool Is featured plan.
89
+ */
90
+ public $is_featured;
91
+
92
+ #endregion Properties
93
+
94
+ /**
95
+ * @param object|bool $plan
96
+ */
97
+ function __construct( $plan = false ) {
98
+ parent::__construct( $plan );
99
+
100
+ if ( is_object( $plan ) ) {
101
+ $this->name = strtolower( $plan->name );
102
+ }
103
+ }
104
+
105
+ static function get_type() {
106
+ return 'plan';
107
+ }
108
+
109
+ /**
110
+ * @author Vova Feldman (@svovaf)
111
+ * @since 1.0.9
112
+ *
113
+ * @return bool
114
+ */
115
+ function is_free() {
116
+ return ( 'free' === $this->name );
117
+ }
118
+
119
+ /**
120
+ * Checks if this plan supports "Technical Support".
121
+ *
122
+ * @author Leo Fajardo (leorw)
123
+ * @since 1.2.0
124
+ *
125
+ * @return bool
126
+ */
127
+ function has_technical_support() {
128
+ return ( ! empty( $this->support_email ) ||
129
+ ! empty( $this->support_skype ) ||
130
+ ! empty( $this->support_phone ) ||
131
+ ! empty( $this->is_success_manager )
132
+ );
133
+ }
134
+
135
+ /**
136
+ * @author Vova Feldman (@svovaf)
137
+ * @since 1.0.9
138
+ *
139
+ * @return bool
140
+ */
141
+ function has_trial() {
142
+ return ! $this->is_free() &&
143
+ is_numeric( $this->trial_period ) && ( $this->trial_period > 0 );
144
+ }
145
  }
freemius/includes/entities/class-fs-plugin-tag.php CHANGED
@@ -1,24 +1,24 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.4
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Plugin_Tag extends FS_Entity {
14
- public $version;
15
- public $url;
16
-
17
- function __construct( $tag = false ) {
18
- parent::__construct( $tag );
19
- }
20
-
21
- static function get_type() {
22
- return 'tag';
23
- }
24
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.4
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Plugin_Tag extends FS_Entity {
14
+ public $version;
15
+ public $url;
16
+
17
+ function __construct( $tag = false ) {
18
+ parent::__construct( $tag );
19
+ }
20
+
21
+ static function get_type() {
22
+ return 'tag';
23
+ }
24
  }
freemius/includes/entities/class-fs-plugin.php CHANGED
@@ -1,117 +1,117 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Plugin extends FS_Scope_Entity {
14
- /**
15
- * @since 1.0.6
16
- * @var null|number
17
- */
18
- public $parent_plugin_id;
19
- /**
20
- * @var string
21
- */
22
- public $title;
23
- /**
24
- * @var string
25
- */
26
- public $slug;
27
- /**
28
- * @since 1.2.2
29
- *
30
- * @var string 'plugin' or 'theme'
31
- */
32
- public $type;
33
-
34
- #region Install Specific Properties
35
-
36
- /**
37
- * @var string
38
- */
39
- public $file;
40
- /**
41
- * @var string
42
- */
43
- public $version;
44
- /**
45
- * @var bool
46
- */
47
- public $auto_update;
48
- /**
49
- * @var FS_Plugin_Info
50
- */
51
- public $info;
52
- /**
53
- * @since 1.0.9
54
- *
55
- * @var bool
56
- */
57
- public $is_premium;
58
- /**
59
- * @since 1.0.9
60
- *
61
- * @var bool
62
- */
63
- public $is_live;
64
- /**
65
- * @author Leo Fajardo (@leorw)
66
- *
67
- * @since 1.2.3
68
- *
69
- * @var string|false false if the module doesn't have an affiliate program or one of the following:
70
- * 'selected', 'customers', or 'all'.
71
- */
72
- public $affiliate_moderation;
73
-
74
- const AFFILIATE_MODERATION_CUSTOMERS = 'customers';
75
-
76
- #endregion Install Specific Properties
77
-
78
- /**
79
- * @param stdClass|bool $plugin
80
- */
81
- function __construct( $plugin = false ) {
82
- parent::__construct( $plugin );
83
-
84
- $this->is_premium = false;
85
- $this->is_live = true;
86
-
87
- if ( isset( $plugin->info ) && is_object( $plugin->info ) ) {
88
- $this->info = new FS_Plugin_Info( $plugin->info );
89
- }
90
- }
91
-
92
- /**
93
- * Check if plugin is an add-on (has parent).
94
- *
95
- * @author Vova Feldman (@svovaf)
96
- * @since 1.0.6
97
- *
98
- * @return bool
99
- */
100
- function is_addon() {
101
- return isset( $this->parent_plugin_id ) && is_numeric( $this->parent_plugin_id );
102
- }
103
-
104
- /**
105
- * @author Leo Fajardo (@leorw)
106
- * @since 1.2.3
107
- *
108
- * @return bool
109
- */
110
- function has_affiliate_program() {
111
- return ( ! empty( $this->affiliate_moderation ) );
112
- }
113
-
114
- static function get_type() {
115
- return 'plugin';
116
- }
117
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Plugin extends FS_Scope_Entity {
14
+ /**
15
+ * @since 1.0.6
16
+ * @var null|number
17
+ */
18
+ public $parent_plugin_id;
19
+ /**
20
+ * @var string
21
+ */
22
+ public $title;
23
+ /**
24
+ * @var string
25
+ */
26
+ public $slug;
27
+ /**
28
+ * @since 1.2.2
29
+ *
30
+ * @var string 'plugin' or 'theme'
31
+ */
32
+ public $type;
33
+
34
+ #region Install Specific Properties
35
+
36
+ /**
37
+ * @var string
38
+ */
39
+ public $file;
40
+ /**
41
+ * @var string
42
+ */
43
+ public $version;
44
+ /**
45
+ * @var bool
46
+ */
47
+ public $auto_update;
48
+ /**
49
+ * @var FS_Plugin_Info
50
+ */
51
+ public $info;
52
+ /**
53
+ * @since 1.0.9
54
+ *
55
+ * @var bool
56
+ */
57
+ public $is_premium;
58
+ /**
59
+ * @since 1.0.9
60
+ *
61
+ * @var bool
62
+ */
63
+ public $is_live;
64
+ /**
65
+ * @author Leo Fajardo (@leorw)
66
+ *
67
+ * @since 1.2.3
68
+ *
69
+ * @var string|false false if the module doesn't have an affiliate program or one of the following:
70
+ * 'selected', 'customers', or 'all'.
71
+ */
72
+ public $affiliate_moderation;
73
+
74
+ const AFFILIATE_MODERATION_CUSTOMERS = 'customers';
75
+
76
+ #endregion Install Specific Properties
77
+
78
+ /**
79
+ * @param stdClass|bool $plugin
80
+ */
81
+ function __construct( $plugin = false ) {
82
+ parent::__construct( $plugin );
83
+
84
+ $this->is_premium = false;
85
+ $this->is_live = true;
86
+
87
+ if ( isset( $plugin->info ) && is_object( $plugin->info ) ) {
88
+ $this->info = new FS_Plugin_Info( $plugin->info );
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Check if plugin is an add-on (has parent).
94
+ *
95
+ * @author Vova Feldman (@svovaf)
96
+ * @since 1.0.6
97
+ *
98
+ * @return bool
99
+ */
100
+ function is_addon() {
101
+ return isset( $this->parent_plugin_id ) && is_numeric( $this->parent_plugin_id );
102
+ }
103
+
104
+ /**
105
+ * @author Leo Fajardo (@leorw)
106
+ * @since 1.2.3
107
+ *
108
+ * @return bool
109
+ */
110
+ function has_affiliate_program() {
111
+ return ( ! empty( $this->affiliate_moderation ) );
112
+ }
113
+
114
+ static function get_type() {
115
+ return 'plugin';
116
+ }
117
  }
freemius/includes/entities/class-fs-pricing.php CHANGED
@@ -1,141 +1,141 @@
1
- <?php
2
- /**
3
- * @package Freemius for EDD Add-On
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.0
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Pricing extends FS_Entity {
14
-
15
- #region Properties
16
-
17
- /**
18
- * @var number
19
- */
20
- public $plan_id;
21
- /**
22
- * @var int
23
- */
24
- public $licenses;
25
- /**
26
- * @var null|float
27
- */
28
- public $monthly_price;
29
- /**
30
- * @var null|float
31
- */
32
- public $annual_price;
33
- /**
34
- * @var null|float
35
- */
36
- public $lifetime_price;
37
-
38
- #endregion Properties
39
-
40
- /**
41
- * @param object|bool $pricing
42
- */
43
- function __construct( $pricing = false ) {
44
- parent::__construct( $pricing );
45
- }
46
-
47
- static function get_type() {
48
- return 'pricing';
49
- }
50
-
51
- /**
52
- * @author Vova Feldman (@svovaf)
53
- * @since 1.1.8
54
- *
55
- * @return bool
56
- */
57
- function has_monthly() {
58
- return ( is_numeric( $this->monthly_price ) && $this->monthly_price > 0 );
59
- }
60
-
61
- /**
62
- * @author Vova Feldman (@svovaf)
63
- * @since 1.1.8
64
- *
65
- * @return bool
66
- */
67
- function has_annual() {
68
- return ( is_numeric( $this->annual_price ) && $this->annual_price > 0 );
69
- }
70
-
71
- /**
72
- * @author Vova Feldman (@svovaf)
73
- * @since 1.1.8
74
- *
75
- * @return bool
76
- */
77
- function has_lifetime() {
78
- return ( is_numeric( $this->lifetime_price ) && $this->lifetime_price > 0 );
79
- }
80
-
81
- /**
82
- * Check if unlimited licenses pricing.
83
- *
84
- * @author Vova Feldman (@svovaf)
85
- * @since 1.1.8
86
- *
87
- * @return bool
88
- */
89
- function is_unlimited() {
90
- return is_null( $this->licenses );
91
- }
92
-
93
-
94
- /**
95
- * Check if pricing has more than one billing cycle.
96
- *
97
- * @author Vova Feldman (@svovaf)
98
- * @since 1.1.8
99
- *
100
- * @return bool
101
- */
102
- function is_multi_cycle() {
103
- $cycles = 0;
104
- if ( $this->has_monthly() ) {
105
- $cycles ++;
106
- }
107
- if ( $this->has_annual() ) {
108
- $cycles ++;
109
- }
110
- if ( $this->has_lifetime() ) {
111
- $cycles ++;
112
- }
113
-
114
- return $cycles > 1;
115
- }
116
-
117
- /**
118
- * Get annual over monthly discount.
119
- *
120
- * @author Vova Feldman (@svovaf)
121
- * @since 1.1.8
122
- *
123
- * @return int
124
- */
125
- function annual_discount_percentage() {
126
- return floor( $this->annual_savings() / ( $this->monthly_price * 12 * ( $this->is_unlimited() ? 1 : $this->licenses ) ) * 100 );
127
- }
128
-
129
- /**
130
- * Get annual over monthly savings.
131
- *
132
- * @author Vova Feldman (@svovaf)
133
- * @since 1.1.8
134
- *
135
- * @return float
136
- */
137
- function annual_savings() {
138
- return ( $this->monthly_price * 12 - $this->annual_price ) * ( $this->is_unlimited() ? 1 : $this->licenses );
139
- }
140
-
141
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius for EDD Add-On
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.0
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Pricing extends FS_Entity {
14
+
15
+ #region Properties
16
+
17
+ /**
18
+ * @var number
19
+ */
20
+ public $plan_id;
21
+ /**
22
+ * @var int
23
+ */
24
+ public $licenses;
25
+ /**
26
+ * @var null|float
27
+ */
28
+ public $monthly_price;
29
+ /**
30
+ * @var null|float
31
+ */
32
+ public $annual_price;
33
+ /**
34
+ * @var null|float
35
+ */
36
+ public $lifetime_price;
37
+
38
+ #endregion Properties
39
+
40
+ /**
41
+ * @param object|bool $pricing
42
+ */
43
+ function __construct( $pricing = false ) {
44
+ parent::__construct( $pricing );
45
+ }
46
+
47
+ static function get_type() {
48
+ return 'pricing';
49
+ }
50
+
51
+ /**
52
+ * @author Vova Feldman (@svovaf)
53
+ * @since 1.1.8
54
+ *
55
+ * @return bool
56
+ */
57
+ function has_monthly() {
58
+ return ( is_numeric( $this->monthly_price ) && $this->monthly_price > 0 );
59
+ }
60
+
61
+ /**
62
+ * @author Vova Feldman (@svovaf)
63
+ * @since 1.1.8
64
+ *
65
+ * @return bool
66
+ */
67
+ function has_annual() {
68
+ return ( is_numeric( $this->annual_price ) && $this->annual_price > 0 );
69
+ }
70
+
71
+ /**
72
+ * @author Vova Feldman (@svovaf)
73
+ * @since 1.1.8
74
+ *
75
+ * @return bool
76
+ */
77
+ function has_lifetime() {
78
+ return ( is_numeric( $this->lifetime_price ) && $this->lifetime_price > 0 );
79
+ }
80
+
81
+ /**
82
+ * Check if unlimited licenses pricing.
83
+ *
84
+ * @author Vova Feldman (@svovaf)
85
+ * @since 1.1.8
86
+ *
87
+ * @return bool
88
+ */
89
+ function is_unlimited() {
90
+ return is_null( $this->licenses );
91
+ }
92
+
93
+
94
+ /**
95
+ * Check if pricing has more than one billing cycle.
96
+ *
97
+ * @author Vova Feldman (@svovaf)
98
+ * @since 1.1.8
99
+ *
100
+ * @return bool
101
+ */
102
+ function is_multi_cycle() {
103
+ $cycles = 0;
104
+ if ( $this->has_monthly() ) {
105
+ $cycles ++;
106
+ }
107
+ if ( $this->has_annual() ) {
108
+ $cycles ++;
109
+ }
110
+ if ( $this->has_lifetime() ) {
111
+ $cycles ++;
112
+ }
113
+
114
+ return $cycles > 1;
115
+ }
116
+
117
+ /**
118
+ * Get annual over monthly discount.
119
+ *
120
+ * @author Vova Feldman (@svovaf)
121
+ * @since 1.1.8
122
+ *
123
+ * @return int
124
+ */
125
+ function annual_discount_percentage() {
126
+ return floor( $this->annual_savings() / ( $this->monthly_price * 12 * ( $this->is_unlimited() ? 1 : $this->licenses ) ) * 100 );
127
+ }
128
+
129
+ /**
130
+ * Get annual over monthly savings.
131
+ *
132
+ * @author Vova Feldman (@svovaf)
133
+ * @since 1.1.8
134
+ *
135
+ * @return float
136
+ */
137
+ function annual_savings() {
138
+ return ( $this->monthly_price * 12 - $this->annual_price ) * ( $this->is_unlimited() ? 1 : $this->licenses );
139
+ }
140
+
141
  }
freemius/includes/entities/class-fs-scope-entity.php CHANGED
@@ -1,29 +1,29 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.4
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Scope_Entity extends FS_Entity {
14
- /**
15
- * @var string
16
- */
17
- public $public_key;
18
- /**
19
- * @var string
20
- */
21
- public $secret_key;
22
-
23
- /**
24
- * @param bool|stdClass $scope_entity
25
- */
26
- function __construct( $scope_entity = false ) {
27
- parent::__construct( $scope_entity );
28
- }
29
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.4
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Scope_Entity extends FS_Entity {
14
+ /**
15
+ * @var string
16
+ */
17
+ public $public_key;
18
+ /**
19
+ * @var string
20
+ */
21
+ public $secret_key;
22
+
23
+ /**
24
+ * @param bool|stdClass $scope_entity
25
+ */
26
+ function __construct( $scope_entity = false ) {
27
+ parent::__construct( $scope_entity );
28
+ }
29
  }
freemius/includes/entities/class-fs-site.php CHANGED
@@ -1,148 +1,148 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Site extends FS_Scope_Entity {
14
- /**
15
- * @var string
16
- */
17
- public $slug;
18
- /**
19
- * @var number
20
- */
21
- public $site_id;
22
- /**
23
- * @var number
24
- */
25
- public $plugin_id;
26
- /**
27
- * @var number
28
- */
29
- public $user_id;
30
- /**
31
- * @var string
32
- */
33
- public $title;
34
- /**
35
- * @var string
36
- */
37
- public $url;
38
- /**
39
- * @var string
40
- */
41
- public $version;
42
- /**
43
- * @var string E.g. en-GB
44
- */
45
- public $language;
46
- /**
47
- * @var string E.g. UTF-8
48
- */
49
- public $charset;
50
- /**
51
- * @var string Platform version (e.g WordPress version).
52
- */
53
- public $platform_version;
54
- /**
55
- * Freemius SDK version
56
- *
57
- * @author Leo Fajardo (@leorw)
58
- * @since 1.2.2
59
- *
60
- * @var string SDK version (e.g.: 1.2.2)
61
- */
62
- public $sdk_version;
63
- /**
64
- * @var string Programming language version (e.g PHP version).
65
- */
66
- public $programming_language_version;
67
- /**
68
- * @var FS_Plugin_Plan $plan
69
- */
70
- public $plan;
71
- /**
72
- * @var number|null
73
- */
74
- public $license_id;
75
- /**
76
- * @var number|null
77
- */
78
- public $trial_plan_id;
79
- /**
80
- * @var string|null
81
- */
82
- public $trial_ends;
83
- /**
84
- * @since 1.0.9
85
- *
86
- * @var bool
87
- */
88
- public $is_premium = false;
89
- /**
90
- * @author Leo Fajardo (@leorw)
91
- *
92
- * @since 1.2.1.5
93
- *
94
- * @var bool
95
- */
96
- public $is_disconnected = false;
97
-
98
- /**
99
- * @param stdClass|bool $site
100
- */
101
- function __construct( $site = false ) {
102
- $this->plan = new FS_Plugin_Plan();
103
-
104
- parent::__construct( $site );
105
-
106
- if ( is_object( $site ) ) {
107
- $this->plan->id = $site->plan_id;
108
- }
109
-
110
- if ( ! is_bool( $this->is_disconnected ) ) {
111
- $this->is_disconnected = false;
112
- }
113
- }
114
-
115
- static function get_type() {
116
- return 'install';
117
- }
118
-
119
- function is_localhost() {
120
- // The server has no way to verify if localhost unless localhost appears in domain.
121
- return WP_FS__IS_LOCALHOST_FOR_SERVER;
122
- // return (substr($_SERVER['REMOTE_ADDR'], 0, 4) == '127.' || $_SERVER['REMOTE_ADDR'] == '::1');
123
- }
124
-
125
- /**
126
- * Check if site in trial.
127
- *
128
- * @author Vova Feldman (@svovaf)
129
- * @since 1.0.9
130
- *
131
- * @return bool
132
- */
133
- function is_trial() {
134
- return is_numeric( $this->trial_plan_id ) && ( strtotime( $this->trial_ends ) > WP_FS__SCRIPT_START_TIME );
135
- }
136
-
137
- /**
138
- * Check if user already utilized the trial with the current install.
139
- *
140
- * @author Vova Feldman (@svovaf)
141
- * @since 1.0.9
142
- *
143
- * @return bool
144
- */
145
- function is_trial_utilized() {
146
- return is_numeric( $this->trial_plan_id );
147
- }
148
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Site extends FS_Scope_Entity {
14
+ /**
15
+ * @var string
16
+ */
17
+ public $slug;
18
+ /**
19
+ * @var number
20
+ */
21
+ public $site_id;
22
+ /**
23
+ * @var number
24
+ */
25
+ public $plugin_id;
26
+ /**
27
+ * @var number
28
+ */
29
+ public $user_id;
30
+ /**
31
+ * @var string
32
+ */
33
+ public $title;
34
+ /**
35
+ * @var string
36
+ */
37
+ public $url;
38
+ /**
39
+ * @var string
40
+ */
41
+ public $version;
42
+ /**
43
+ * @var string E.g. en-GB
44
+ */
45
+ public $language;
46
+ /**
47
+ * @var string E.g. UTF-8
48
+ */
49
+ public $charset;
50
+ /**
51
+ * @var string Platform version (e.g WordPress version).
52
+ */
53
+ public $platform_version;
54
+ /**
55
+ * Freemius SDK version
56
+ *
57
+ * @author Leo Fajardo (@leorw)
58
+ * @since 1.2.2
59
+ *
60
+ * @var string SDK version (e.g.: 1.2.2)
61
+ */
62
+ public $sdk_version;
63
+ /**
64
+ * @var string Programming language version (e.g PHP version).
65
+ */
66
+ public $programming_language_version;
67
+ /**
68
+ * @var FS_Plugin_Plan $plan
69
+ */
70
+ public $plan;
71
+ /**
72
+ * @var number|null
73
+ */
74
+ public $license_id;
75
+ /**
76
+ * @var number|null
77
+ */
78
+ public $trial_plan_id;
79
+ /**
80
+ * @var string|null
81
+ */
82
+ public $trial_ends;
83
+ /**
84
+ * @since 1.0.9
85
+ *
86
+ * @var bool
87
+ */
88
+ public $is_premium = false;
89
+ /**
90
+ * @author Leo Fajardo (@leorw)
91
+ *
92
+ * @since 1.2.1.5
93
+ *
94
+ * @var bool
95
+ */
96
+ public $is_disconnected = false;
97
+
98
+ /**
99
+ * @param stdClass|bool $site
100
+ */
101
+ function __construct( $site = false ) {
102
+ $this->plan = new FS_Plugin_Plan();
103
+
104
+ parent::__construct( $site );
105
+
106
+ if ( is_object( $site ) ) {
107
+ $this->plan->id = $site->plan_id;
108
+ }
109
+
110
+ if ( ! is_bool( $this->is_disconnected ) ) {
111
+ $this->is_disconnected = false;
112
+ }
113
+ }
114
+
115
+ static function get_type() {
116
+ return 'install';
117
+ }
118
+
119
+ function is_localhost() {
120
+ // The server has no way to verify if localhost unless localhost appears in domain.
121
+ return WP_FS__IS_LOCALHOST_FOR_SERVER;
122
+ // return (substr($_SERVER['REMOTE_ADDR'], 0, 4) == '127.' || $_SERVER['REMOTE_ADDR'] == '::1');
123
+ }
124
+
125
+ /**
126
+ * Check if site in trial.
127
+ *
128
+ * @author Vova Feldman (@svovaf)
129
+ * @since 1.0.9
130
+ *
131
+ * @return bool
132
+ */
133
+ function is_trial() {
134
+ return is_numeric( $this->trial_plan_id ) && ( strtotime( $this->trial_ends ) > WP_FS__SCRIPT_START_TIME );
135
+ }
136
+
137
+ /**
138
+ * Check if user already utilized the trial with the current install.
139
+ *
140
+ * @author Vova Feldman (@svovaf)
141
+ * @since 1.0.9
142
+ *
143
+ * @return bool
144
+ */
145
+ function is_trial_utilized() {
146
+ return is_numeric( $this->trial_plan_id );
147
+ }
148
  }
freemius/includes/entities/class-fs-subscription.php CHANGED
@@ -1,125 +1,125 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.9
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Subscription extends FS_Entity {
14
-
15
- #region Properties
16
-
17
- /**
18
- * @var number
19
- */
20
- public $user_id;
21
- /**
22
- * @var number
23
- */
24
- public $install_id;
25
- /**
26
- * @var number
27
- */
28
- public $plan_id;
29
- /**
30
- * @var number
31
- */
32
- public $license_id;
33
- /**
34
- * @var float
35
- */
36
- public $total_gross;
37
- /**
38
- * @var float
39
- */
40
- public $amount_per_cycle;
41
- /**
42
- * @var int # of months
43
- */
44
- public $billing_cycle;
45
- /**
46
- * @var float
47
- */
48
- public $outstanding_balance;
49
- /**
50
- * @var int
51
- */
52
- public $failed_payments;
53
- /**
54
- * @var string
55
- */
56
- public $gateway;
57
- /**
58
- * @var string
59
- */
60
- public $external_id;
61
- /**
62
- * @var string|null
63
- */
64
- public $trial_ends;
65
- /**
66
- * @var string|null Datetime of the next payment, or null if cancelled
67
- */
68
- public $next_payment;
69
- /**
70
- * @var string|null
71
- */
72
- public $vat_id;
73
- /**
74
- * @var string Two characters country code
75
- */
76
- public $country_code;
77
-
78
- #endregion Properties
79
-
80
- /**
81
- * @param object|bool $subscription
82
- */
83
- function __construct( $subscription = false ) {
84
- parent::__construct( $subscription );
85
- }
86
-
87
- static function get_type() {
88
- return 'subscription';
89
- }
90
-
91
- /**
92
- * Check if subscription is active.
93
- *
94
- * @author Vova Feldman (@svovaf)
95
- * @since 1.0.9
96
- *
97
- * @return bool
98
- */
99
- function is_active() {
100
- return ! empty( $this->next_payment ) &&
101
- ( strtotime( $this->next_payment ) > WP_FS__SCRIPT_START_TIME );
102
- }
103
-
104
- /**
105
- * Subscription considered to be new without any payments
106
- * if the next payment should be made within less than 24 hours
107
- * from the subscription creation.
108
- *
109
- * @author Vova Feldman (@svovaf)
110
- * @since 1.0.9
111
- *
112
- * @return bool
113
- */
114
- function is_first_payment_pending() {
115
- return ( WP_FS__TIME_24_HOURS_IN_SEC >= strtotime( $this->next_payment ) - strtotime( $this->created ) );
116
- }
117
-
118
- /**
119
- * @author Vova Feldman (@svovaf)
120
- * @since 1.1.7
121
- */
122
- function has_trial() {
123
- return ! is_null( $this->trial_ends );
124
- }
125
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.9
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Subscription extends FS_Entity {
14
+
15
+ #region Properties
16
+
17
+ /**
18
+ * @var number
19
+ */
20
+ public $user_id;
21
+ /**
22
+ * @var number
23
+ */
24
+ public $install_id;
25
+ /**
26
+ * @var number
27
+ */
28
+ public $plan_id;
29
+ /**
30
+ * @var number
31
+ */
32
+ public $license_id;
33
+ /**
34
+ * @var float
35
+ */
36
+ public $total_gross;
37
+ /**
38
+ * @var float
39
+ */
40
+ public $amount_per_cycle;
41
+ /**
42
+ * @var int # of months
43
+ */
44
+ public $billing_cycle;
45
+ /**
46
+ * @var float
47
+ */
48
+ public $outstanding_balance;
49
+ /**
50
+ * @var int
51
+ */
52
+ public $failed_payments;
53
+ /**
54
+ * @var string
55
+ */
56
+ public $gateway;
57
+ /**
58
+ * @var string
59
+ */
60
+ public $external_id;
61
+ /**
62
+ * @var string|null
63
+ */
64
+ public $trial_ends;
65
+ /**
66
+ * @var string|null Datetime of the next payment, or null if cancelled
67
+ */
68
+ public $next_payment;
69
+ /**
70
+ * @var string|null
71
+ */
72
+ public $vat_id;
73
+ /**
74
+ * @var string Two characters country code
75
+ */
76
+ public $country_code;
77
+
78
+ #endregion Properties
79
+
80
+ /**
81
+ * @param object|bool $subscription
82
+ */
83
+ function __construct( $subscription = false ) {
84
+ parent::__construct( $subscription );
85
+ }
86
+
87
+ static function get_type() {
88
+ return 'subscription';
89
+ }
90
+
91
+ /**
92
+ * Check if subscription is active.
93
+ *
94
+ * @author Vova Feldman (@svovaf)
95
+ * @since 1.0.9
96
+ *
97
+ * @return bool
98
+ */
99
+ function is_active() {
100
+ return ! empty( $this->next_payment ) &&
101
+ ( strtotime( $this->next_payment ) > WP_FS__SCRIPT_START_TIME );
102
+ }
103
+
104
+ /**
105
+ * Subscription considered to be new without any payments
106
+ * if the next payment should be made within less than 24 hours
107
+ * from the subscription creation.
108
+ *
109
+ * @author Vova Feldman (@svovaf)
110
+ * @since 1.0.9
111
+ *
112
+ * @return bool
113
+ */
114
+ function is_first_payment_pending() {
115
+ return ( WP_FS__TIME_24_HOURS_IN_SEC >= strtotime( $this->next_payment ) - strtotime( $this->created ) );
116
+ }
117
+
118
+ /**
119
+ * @author Vova Feldman (@svovaf)
120
+ * @since 1.1.7
121
+ */
122
+ function has_trial() {
123
+ return ! is_null( $this->trial_ends );
124
+ }
125
  }
freemius/includes/entities/class-fs-user.php CHANGED
@@ -1,62 +1,62 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_User extends FS_Scope_Entity {
14
-
15
- #region Properties
16
-
17
- /**
18
- * @var string
19
- */
20
- public $email;
21
- /**
22
- * @var string
23
- */
24
- public $first;
25
- /**
26
- * @var string
27
- */
28
- public $last;
29
- /**
30
- * @var bool
31
- */
32
- public $is_verified;
33
- /**
34
- * @var string|null
35
- */
36
- public $customer_id;
37
- /**
38
- * @var float
39
- */
40
- public $gross;
41
-
42
- #endregion Properties
43
-
44
- /**
45
- * @param object|bool $user
46
- */
47
- function __construct( $user = false ) {
48
- parent::__construct( $user );
49
- }
50
-
51
- function get_name() {
52
- return trim( ucfirst( trim( is_string( $this->first ) ? $this->first : '' ) ) . ' ' . ucfirst( trim( is_string( $this->last ) ? $this->last : '' ) ) );
53
- }
54
-
55
- function is_verified() {
56
- return ( isset( $this->is_verified ) && true === $this->is_verified );
57
- }
58
-
59
- static function get_type() {
60
- return 'user';
61
- }
62
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_User extends FS_Scope_Entity {
14
+
15
+ #region Properties
16
+
17
+ /**
18
+ * @var string
19
+ */
20
+ public $email;
21
+ /**
22
+ * @var string
23
+ */
24
+ public $first;
25
+ /**
26
+ * @var string
27
+ */
28
+ public $last;
29
+ /**
30
+ * @var bool
31
+ */
32
+ public $is_verified;
33
+ /**
34
+ * @var string|null
35
+ */
36
+ public $customer_id;
37
+ /**
38
+ * @var float
39
+ */
40
+ public $gross;
41
+
42
+ #endregion Properties
43
+
44
+ /**
45
+ * @param object|bool $user
46
+ */
47
+ function __construct( $user = false ) {
48
+ parent::__construct( $user );
49
+ }
50
+
51
+ function get_name() {
52
+ return trim( ucfirst( trim( is_string( $this->first ) ? $this->first : '' ) ) . ' ' . ucfirst( trim( is_string( $this->last ) ? $this->last : '' ) ) );
53
+ }
54
+
55
+ function is_verified() {
56
+ return ( isset( $this->is_verified ) && true === $this->is_verified );
57
+ }
58
+
59
+ static function get_type() {
60
+ return 'user';
61
+ }
62
  }
freemius/includes/fs-core-functions.php CHANGED
@@ -1,335 +1,381 @@
1
  <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- if ( ! function_exists( 'fs_dummy' ) ) {
14
- function fs_dummy() {
15
- }
16
- }
17
-
18
- /* Url.
19
- --------------------------------------------------------------------------------------------*/
20
- if ( ! function_exists( 'fs_get_url_daily_cache_killer' ) ) {
21
- function fs_get_url_daily_cache_killer() {
22
- return date( '\YY\Mm\Dd' );
23
- }
24
- }
25
-
26
- /* Templates / Views.
27
- --------------------------------------------------------------------------------------------*/
28
- if ( ! function_exists( 'fs_get_template_path' ) ) {
29
- function fs_get_template_path( $path ) {
30
- return WP_FS__DIR_TEMPLATES . '/' . trim( $path, '/' );
31
- }
32
-
33
- function fs_include_template( $path, &$params = null ) {
34
- $VARS = &$params;
35
- include fs_get_template_path( $path );
36
- }
37
-
38
- function fs_include_once_template( $path, &$params = null ) {
39
- $VARS = &$params;
40
- include_once fs_get_template_path( $path );
41
- }
42
-
43
- function fs_require_template( $path, &$params = null ) {
44
- $VARS = &$params;
45
- require fs_get_template_path( $path );
46
- }
47
-
48
- function fs_require_once_template( $path, &$params = null ) {
49
- $VARS = &$params;
50
- require_once fs_get_template_path( $path );
51
- }
52
-
53
- function fs_get_template( $path, &$params = null ) {
54
- ob_start();
55
-
56
- $VARS = &$params;
57
- require fs_get_template_path( $path );
58
-
59
- return ob_get_clean();
60
- }
61
- }
62
-
63
- /* Scripts and styles including.
64
- --------------------------------------------------------------------------------------------*/
65
-
66
- /**
67
- * Generates an absolute URL to the given path. This function ensures that the URL will be correct whether the asset
68
- * is inside a plugin's folder or a theme's folder.
69
- *
70
- * Examples:
71
- * 1. "themes" folder
72
- * Path: C:/xampp/htdocs/fswp/wp-content/themes/twentytwelve/freemius/assets/css/admin/common.css
73
- * URL: http://fswp:8080/wp-content/themes/twentytwelve/freemius/assets/css/admin/common.css
74
- *
75
- * 2. "plugins" folder
76
- * Path: C:/xampp/htdocs/fswp/wp-content/plugins/rating-widget-premium/freemius/assets/css/admin/common.css
77
- * URL: http://fswp:8080/wp-content/plugins/rating-widget-premium/freemius/assets/css/admin/common.css
78
- *
79
- * @author Leo Fajardo (@leorw)
80
- * @since 1.2.2
81
- *
82
- * @param string $asset_abs_path Asset's absolute path.
83
- *
84
- * @return string Asset's URL.
85
- */
86
- function fs_asset_url( $asset_abs_path ) {
87
- $wp_content_dir = fs_normalize_path( WP_CONTENT_DIR );
88
- $asset_abs_path = fs_normalize_path( $asset_abs_path );
89
- $asset_rel_path = str_replace( $wp_content_dir, '', $asset_abs_path );
90
-
91
- $asset_url = content_url( fs_normalize_path( $asset_rel_path ) );
92
-
93
- return $asset_url;
94
- }
95
-
96
- function fs_enqueue_local_style( $handle, $path, $deps = array(), $ver = false, $media = 'all' ) {
97
- wp_enqueue_style( $handle, fs_asset_url( WP_FS__DIR_CSS . '/' . trim( $path, '/' ) ), $deps, $ver, $media );
98
- }
99
-
100
- function fs_enqueue_local_script( $handle, $path, $deps = array(), $ver = false, $in_footer = 'all' ) {
101
- wp_enqueue_script( $handle, fs_asset_url( WP_FS__DIR_JS . '/' . trim( $path, '/' ) ), $deps, $ver, $in_footer );
102
- }
103
-
104
- function fs_img_url( $path, $img_dir = WP_FS__DIR_IMG ) {
105
- return ( fs_asset_url( $img_dir . '/' . trim( $path, '/' ) ) );
106
- }
107
-
108
- /* Request handlers.
109
- --------------------------------------------------------------------------------------------*/
110
- /**
111
- * @param string $key
112
- * @param mixed $def
113
- * @param string|bool $type Since 1.2.1.7 - when set to 'get' will look for the value passed via querystring, when
114
- * set to 'post' will look for the value passed via the POST request's body, otherwise,
115
- * will check if the parameter was passed in any of the two.
116
- *
117
- * @return mixed
118
- */
119
- function fs_request_get( $key, $def = false, $type = false ) {
120
- if ( is_string( $type ) ) {
121
- $type = strtolower( $type );
122
- }
123
-
124
- switch ( $type ) {
125
- case 'post':
126
- $value = isset( $_POST[ $key ] ) ? $_POST[ $key ] : $def;
127
- break;
128
- case 'get':
129
- $value = isset( $_GET[ $key ] ) ? $_GET[ $key ] : $def;
130
- break;
131
- default:
132
- $value = isset( $_REQUEST[ $key ] ) ? $_REQUEST[ $key ] : $def;
133
- break;
134
- }
135
-
136
- return $value;
137
- }
138
-
139
- function fs_request_has( $key ) {
140
- return isset( $_REQUEST[ $key ] );
141
- }
142
-
143
- function fs_request_get_bool( $key, $def = false ) {
144
- if ( ! isset( $_REQUEST[ $key ] ) ) {
145
- return $def;
146
- }
147
-
148
- if ( 1 == $_REQUEST[ $key ] || 'true' === strtolower( $_REQUEST[ $key ] ) ) {
149
- return true;
150
- }
151
-
152
- if ( 0 == $_REQUEST[ $key ] || 'false' === strtolower( $_REQUEST[ $key ] ) ) {
153
- return false;
154
- }
155
-
156
- return $def;
157
- }
158
-
159
- function fs_request_is_post() {
160
- return ( 'post' === strtolower( $_SERVER['REQUEST_METHOD'] ) );
161
- }
162
-
163
- function fs_request_is_get() {
164
- return ( 'get' === strtolower( $_SERVER['REQUEST_METHOD'] ) );
165
- }
166
-
167
- function fs_get_action( $action_key = 'action' ) {
168
- if ( ! empty( $_REQUEST[ $action_key ] ) ) {
169
- return strtolower( $_REQUEST[ $action_key ] );
170
- }
171
-
172
- if ( 'action' == $action_key ) {
173
- $action_key = 'fs_action';
174
-
175
- if ( ! empty( $_REQUEST[ $action_key ] ) ) {
176
- return strtolower( $_REQUEST[ $action_key ] );
177
- }
178
- }
179
-
180
- return false;
181
- }
182
-
183
- function fs_request_is_action( $action, $action_key = 'action' ) {
184
- return ( strtolower( $action ) === fs_get_action( $action_key ) );
185
- }
186
-
187
- /**
188
- * @author Vova Feldman (@svovaf)
189
- * @since 1.0.0
190
- *
191
- * @since 1.2.1.5 Allow nonce verification.
192
- *
193
- * @param string $action
194
- * @param string $action_key
195
- * @param string $nonce_key
196
- *
197
- * @return bool
198
- */
199
- function fs_request_is_action_secure(
200
- $action,
201
- $action_key = 'action',
202
- $nonce_key = 'nonce'
203
- ) {
204
- if ( strtolower( $action ) !== fs_get_action( $action_key ) ) {
205
- return false;
206
- }
207
-
208
- $nonce = ! empty( $_REQUEST[ $nonce_key ] ) ?
209
- $_REQUEST[ $nonce_key ] :
210
- '';
211
-
212
- if ( empty( $nonce ) ||
213
- ( false === wp_verify_nonce( $nonce, $action ) )
214
- ) {
215
- return false;
216
- }
217
-
218
- return true;
219
- }
220
-
221
- function fs_is_plugin_page( $page_slug ) {
222
- return ( is_admin() && $page_slug === fs_request_get( 'page' ) );
223
- }
224
-
225
- /* Core UI.
226
- --------------------------------------------------------------------------------------------*/
227
- /**
228
- * @param number $module_id
229
- * @param string $page
230
- * @param string $action
231
- * @param string $title
232
- * @param array $params
233
- * @param bool $is_primary
234
- * @param string|bool $icon_class Optional class for an icon (since 1.1.7).
235
- * @param string|bool $confirmation Optional confirmation message before submit (since 1.1.7).
236
- * @param string $method Since 1.1.7
237
- *
238
- * @uses fs_ui_get_action_button()
239
- */
240
- function fs_ui_action_button(
241
- $module_id,
242
- $page,
243
- $action,
244
- $title,
245
- $params = array(),
246
- $is_primary = true,
247
- $icon_class = false,
248
- $confirmation = false,
249
- $method = 'GET'
250
- ) {
251
- echo fs_ui_get_action_button(
252
- $module_id,
253
- $page,
254
- $action,
255
- $title,
256
- $params,
257
- $is_primary,
258
- $icon_class,
259
- $confirmation,
260
- $method
261
- );
262
- }
263
-
264
- /**
265
- * @author Vova Feldman (@svovaf)
266
- * @since 1.1.7
267
- *
268
- * @param number $module_id
269
- * @param string $page
270
- * @param string $action
271
- * @param string $title
272
- * @param array $params
273
- * @param bool $is_primary
274
- * @param string|bool $icon_class Optional class for an icon.
275
- * @param string|bool $confirmation Optional confirmation message before submit.
276
- * @param string $method
277
- *
278
- * @return string
279
- */
280
- function fs_ui_get_action_button(
281
- $module_id,
282
- $page,
283
- $action,
284
- $title,
285
- $params = array(),
286
- $is_primary = true,
287
- $icon_class = false,
288
- $confirmation = false,
289
- $method = 'GET'
290
- ) {
291
- // Prepend icon (if set).
292
- $title = ( is_string( $icon_class ) ? '<i class="' . $icon_class . '"></i> ' : '' ) . $title;
293
-
294
- if ( is_string( $confirmation ) ) {
295
- return sprintf( '<form action="%s" method="%s"><input type="hidden" name="fs_action" value="%s">%s<a href="#" class="%s" onclick="if (confirm(\'%s\')) this.parentNode.submit(); return false;">%s</a></form>',
296
- freemius( $module_id )->_get_admin_page_url( $page, $params ),
297
- $method,
298
- $action,
299
- wp_nonce_field( $action, '_wpnonce', true, false ),
300
- 'button' . ( $is_primary ? ' button-primary' : '' ),
301
- $confirmation,
302
- $title
303
- );
304
- } else if ( 'GET' !== strtoupper( $method ) ) {
305
- return sprintf( '<form action="%s" method="%s"><input type="hidden" name="fs_action" value="%s">%s<a href="#" class="%s" onclick="this.parentNode.submit(); return false;">%s</a></form>',
306
- freemius( $module_id )->_get_admin_page_url( $page, $params ),
307
- $method,
308
- $action,
309
- wp_nonce_field( $action, '_wpnonce', true, false ),
310
- 'button' . ( $is_primary ? ' button-primary' : '' ),
311
- $title
312
- );
313
- } else {
314
- return sprintf( '<a href="%s" class="%s">%s</a></form>',
315
- wp_nonce_url( freemius( $module_id )->_get_admin_page_url( $page, array_merge( $params, array( 'fs_action' => $action ) ) ), $action ),
316
- 'button' . ( $is_primary ? ' button-primary' : '' ),
317
- $title
318
- );
319
- }
320
- }
321
-
322
- function fs_ui_action_link( $module_id, $page, $action, $title, $params = array() ) {
323
- ?><a class=""
324
- href="<?php echo wp_nonce_url( freemius( $module_id )->_get_admin_page_url( $page, array_merge( $params, array( 'fs_action' => $action ) ) ), $action ) ?>"><?php echo $title ?></a><?php
325
- }
326
-
327
- /*function fs_error_handler($errno, $errstr, $errfile, $errline)
328
- {
329
- if (false === strpos($errfile, 'freemius/'))
330
- {
331
- // @todo Dump Freemius errors to local log.
332
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
 
334
  // switch ($errno) {
335
  // case E_USER_ERROR:
@@ -343,344 +389,730 @@
343
  // default:
344
  // break;
345
  // }
346
- }
347
-
348
- set_error_handler('fs_error_handler');*/
349
-
350
- if ( ! function_exists( 'fs_nonce_url' ) ) {
351
- /**
352
- * Retrieve URL with nonce added to URL query.
353
- *
354
- * Originally was using `wp_nonce_url()` but the new version
355
- * changed the return value to escaped URL, that's not the expected
356
- * behaviour.
357
- *
358
- * @author Vova Feldman (@svovaf)
359
- * @since ~1.1.3
360
- *
361
- * @param string $actionurl URL to add nonce action.
362
- * @param int|string $action Optional. Nonce action name. Default -1.
363
- * @param string $name Optional. Nonce name. Default '_wpnonce'.
364
- *
365
- * @return string Escaped URL with nonce action added.
366
- */
367
- function fs_nonce_url( $actionurl, $action = - 1, $name = '_wpnonce' ) {
368
- return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
369
- }
370
- }
371
-
372
- if ( ! function_exists( 'fs_starts_with' ) ) {
373
- /**
374
- * Check if string starts with.
375
- *
376
- * @author Vova Feldman (@svovaf)
377
- * @since 1.1.3
378
- *
379
- * @param string $haystack
380
- * @param string $needle
381
- *
382
- * @return bool
383
- */
384
- function fs_starts_with( $haystack, $needle ) {
385
- $length = strlen( $needle );
386
-
387
- return ( substr( $haystack, 0, $length ) === $needle );
388
- }
389
- }
390
-
391
- #region Url Canonization ------------------------------------------------------------------
392
-
393
- if ( ! function_exists( 'fs_canonize_url' ) ) {
394
- /**
395
- * @author Vova Feldman (@svovaf)
396
- * @since 1.1.3
397
- *
398
- * @param string $url
399
- * @param bool $omit_host
400
- * @param array $ignore_params
401
- *
402
- * @return string
403
- */
404
- function fs_canonize_url( $url, $omit_host = false, $ignore_params = array() ) {
405
- $parsed_url = parse_url( strtolower( $url ) );
406
 
407
  // if ( ! isset( $parsed_url['host'] ) ) {
408
  // return $url;
409
  // }
410
 
411
- $canonical = ( ( $omit_host || ! isset( $parsed_url['host'] ) ) ? '' : $parsed_url['host'] ) . $parsed_url['path'];
412
-
413
- if ( isset( $parsed_url['query'] ) ) {
414
- parse_str( $parsed_url['query'], $queryString );
415
- $canonical .= '?' . fs_canonize_query_string( $queryString, $ignore_params );
416
- }
417
-
418
- return $canonical;
419
- }
420
- }
421
-
422
- if ( ! function_exists( 'fs_canonize_query_string' ) ) {
423
- /**
424
- * @author Vova Feldman (@svovaf)
425
- * @since 1.1.3
426
- *
427
- * @param array $params
428
- * @param array $ignore_params
429
- * @param bool $params_prefix
430
- *
431
- * @return string
432
- */
433
- function fs_canonize_query_string( array $params, array &$ignore_params, $params_prefix = false ) {
434
- if ( ! is_array( $params ) || 0 === count( $params ) ) {
435
- return '';
436
- }
437
-
438
- // Url encode both keys and values
439
- $keys = fs_urlencode_rfc3986( array_keys( $params ) );
440
- $values = fs_urlencode_rfc3986( array_values( $params ) );
441
- $params = array_combine( $keys, $values );
442
-
443
- // Parameters are sorted by name, using lexicographical byte value ordering.
444
- // Ref: Spec: 9.1.1 (1)
445
- uksort( $params, 'strcmp' );
446
-
447
- $pairs = array();
448
- foreach ( $params as $parameter => $value ) {
449
- $lower_param = strtolower( $parameter );
450
-
451
- // Skip ignore params.
452
- if ( in_array( $lower_param, $ignore_params ) ||
453
- ( false !== $params_prefix && fs_starts_with( $lower_param, $params_prefix ) )
454
- ) {
455
- continue;
456
- }
457
-
458
- if ( is_array( $value ) ) {
459
- // If two or more parameters share the same name, they are sorted by their value
460
- // Ref: Spec: 9.1.1 (1)
461
- natsort( $value );
462
- foreach ( $value as $duplicate_value ) {
463
- $pairs[] = $lower_param . '=' . $duplicate_value;
464
- }
465
- } else {
466
- $pairs[] = $lower_param . '=' . $value;
467
- }
468
- }
469
-
470
- if ( 0 === count( $pairs ) ) {
471
- return '';
472
- }
473
-
474
- return implode( "&", $pairs );
475
- }
476
- }
477
-
478
- if ( ! function_exists( 'fs_urlencode_rfc3986' ) ) {
479
- /**
480
- * @author Vova Feldman (@svovaf)
481
- * @since 1.1.3
482
- *
483
- * @param string|string[] $input
484
- *
485
- * @return array|mixed|string
486
- */
487
- function fs_urlencode_rfc3986( $input ) {
488
- if ( is_array( $input ) ) {
489
- return array_map( 'fs_urlencode_rfc3986', $input );
490
- } else if ( is_scalar( $input ) ) {
491
- return str_replace( '+', ' ', str_replace( '%7E', '~', rawurlencode( $input ) ) );
492
- }
493
-
494
- return '';
495
- }
496
- }
497
-
498
- #endregion Url Canonization ------------------------------------------------------------------
499
-
500
- /**
501
- * @author Vova Feldman (@svovaf)
502
- *
503
- * @since 1.2.2 Changed to usage of WP_Filesystem_Direct.
504
- *
505
- * @param string $from URL
506
- * @param string $to File path.
507
- *
508
- * @return bool Is successfully downloaded.
509
- */
510
- function fs_download_image( $from, $to ) {
511
- $dir = dirname( $to );
512
-
513
- if ( 'direct' !== get_filesystem_method( array(), $dir ) ) {
514
- return false;
515
- }
516
-
517
- if ( ! class_exists( 'WP_Filesystem_Direct' ) ) {
518
- require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
519
- require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
520
- }
521
-
522
- $fs = new WP_Filesystem_Direct( '' );
523
- $tmpfile = download_url( $from );
524
-
525
- if ($tmpfile instanceof WP_Error) {
526
- // Issue downloading the file.
527
- return false;
528
- }
529
-
530
- $fs->copy( $tmpfile, $to );
531
- $fs->delete( $tmpfile );
532
-
533
- return true;
534
- }
535
-
536
- /* General Utilities
537
- --------------------------------------------------------------------------------------------*/
538
-
539
- /**
540
- * Sorts an array by the value of the priority key.
541
- *
542
- * @author Daniel Iser (@danieliser)
543
- * @since 1.1.7
544
- *
545
- * @param $a
546
- * @param $b
547
- *
548
- * @return int
549
- */
550
- function fs_sort_by_priority( $a, $b ) {
551
-
552
- // If b has a priority and a does not, b wins.
553
- if ( ! isset( $a['priority'] ) && isset( $b['priority'] ) ) {
554
- return 1;
555
- } // If b has a priority and a does not, b wins.
556
- elseif ( isset( $a['priority'] ) && ! isset( $b['priority'] ) ) {
557
- return - 1;
558
- } // If neither has a priority or both priorities are equal its a tie.
559
- elseif ( ( ! isset( $a['priority'] ) && ! isset( $b['priority'] ) ) || $a['priority'] === $b['priority'] ) {
560
- return 0;
561
- }
562
-
563
- // If both have priority return the winner.
564
- return ( $a['priority'] < $b['priority'] ) ? - 1 : 1;
565
- }
566
-
567
- #--------------------------------------------------------------------------------
568
- #region Localization
569
- #--------------------------------------------------------------------------------
570
-
571
- if ( ! function_exists( 'fs_text' ) ) {
572
- /**
573
- * Retrieve a translated text by key.
574
- *
575
- * @author Vova Feldman (@svovaf)
576
- * @since 1.2.1.7
577
- *
578
- * @param string $key
579
- * @param string $slug
580
- *
581
- * @return string
582
- *
583
- * @global $fs_text, $fs_text_overrides
584
- */
585
- function fs_text( $key, $slug = 'freemius' ) {
586
- return __fs( $key, $slug );
587
- }
588
-
589
- /**
590
- * Output a translated text by key.
591
- *
592
- * @author Vova Feldman (@svovaf)
593
- * @since 1.2.1.7
594
- *
595
- * @param string $key
596
- * @param string $slug
597
- */
598
- function fs_echo( $key, $slug = 'freemius' ) {
599
- echo fs_text( $key, $slug );
600
- }
601
- }
602
-
603
- /**
604
- * @author Vova Feldman
605
- * @since 1.2.1.6
606
- *
607
- * @param string $key
608
- * @param string $slug
609
- *
610
- * @return string
611
- */
612
- function fs_esc_attr( $key, $slug ) {
613
- return esc_attr( fs_text( $key, $slug ) );
614
- }
615
-
616
- /**
617
- * @author Vova Feldman
618
- * @since 1.2.1.6
619
- *
620
- * @param string $key
621
- * @param string $slug
622
- */
623
- function fs_esc_attr_echo( $key, $slug ) {
624
- echo esc_attr( fs_text( $key, $slug ) );
625
- }
626
-
627
- /**
628
- * @author Vova Feldman
629
- * @since 1.2.1.6
630
- *
631
- * @param string $key
632
- * @param string $slug
633
- *
634
- * @return string
635
- */
636
- function fs_esc_js( $key, $slug ) {
637
- return esc_js( fs_text( $key, $slug ) );
638
- }
639
-
640
- /**
641
- * @author Vova Feldman
642
- * @since 1.2.1.6
643
- *
644
- * @param string $key
645
- * @param string $slug
646
- */
647
- function fs_esc_js_echo( $key, $slug ) {
648
- echo esc_js( fs_text( $key, $slug ) );
649
- }
650
-
651
- /**
652
- * @author Vova Feldman
653
- * @since 1.2.1.6
654
- *
655
- * @param string $key
656
- * @param string $slug
657
- */
658
- function fs_json_encode_echo( $key, $slug ) {
659
- echo json_encode( fs_text( $key, $slug ) );
660
- }
661
-
662
- /**
663
- * @author Vova Feldman
664
- * @since 1.2.1.6
665
- *
666
- * @param string $key
667
- * @param string $slug
668
- *
669
- * @return string
670
- */
671
- function fs_esc_html( $key, $slug ) {
672
- return esc_html( fs_text( $key, $slug ) );
673
- }
674
-
675
- /**
676
- * @author Vova Feldman
677
- * @since 1.2.1.6
678
- *
679
- * @param string $key
680
- * @param string $slug
681
- */
682
- function fs_esc_html_echo( $key, $slug ) {
683
- echo esc_html( fs_text( $key, $slug ) );
684
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
685
 
686
  #endregion
1
  <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ if ( ! function_exists( 'fs_dummy' ) ) {
14
+ function fs_dummy() {
15
+ }
16
+ }
17
+
18
+ /* Url.
19
+ --------------------------------------------------------------------------------------------*/
20
+ if ( ! function_exists( 'fs_get_url_daily_cache_killer' ) ) {
21
+ function fs_get_url_daily_cache_killer() {
22
+ return date( '\YY\Mm\Dd' );
23
+ }
24
+ }
25
+
26
+ /* Templates / Views.
27
+ --------------------------------------------------------------------------------------------*/
28
+ if ( ! function_exists( 'fs_get_template_path' ) ) {
29
+ function fs_get_template_path( $path ) {
30
+ return WP_FS__DIR_TEMPLATES . '/' . trim( $path, '/' );
31
+ }
32
+
33
+ function fs_include_template( $path, &$params = null ) {
34
+ $VARS = &$params;
35
+ include fs_get_template_path( $path );
36
+ }
37
+
38
+ function fs_include_once_template( $path, &$params = null ) {
39
+ $VARS = &$params;
40
+ include_once fs_get_template_path( $path );
41
+ }
42
+
43
+ function fs_require_template( $path, &$params = null ) {
44
+ $VARS = &$params;
45
+ require fs_get_template_path( $path );
46
+ }
47
+
48
+ function fs_require_once_template( $path, &$params = null ) {
49
+ $VARS = &$params;
50
+ require_once fs_get_template_path( $path );
51
+ }
52
+
53
+ function fs_get_template( $path, &$params = null ) {
54
+ ob_start();
55
+
56
+ $VARS = &$params;
57
+ require fs_get_template_path( $path );
58
+
59
+ return ob_get_clean();
60
+ }
61
+ }
62
+
63
+ /* Scripts and styles including.
64
+ --------------------------------------------------------------------------------------------*/
65
+
66
+ /**
67
+ * Generates an absolute URL to the given path. This function ensures that the URL will be correct whether the asset
68
+ * is inside a plugin's folder or a theme's folder.
69
+ *
70
+ * Examples:
71
+ * 1. "themes" folder
72
+ * Path: C:/xampp/htdocs/fswp/wp-content/themes/twentytwelve/freemius/assets/css/admin/common.css
73
+ * URL: http://fswp:8080/wp-content/themes/twentytwelve/freemius/assets/css/admin/common.css
74
+ *
75
+ * 2. "plugins" folder
76
+ * Path: C:/xampp/htdocs/fswp/wp-content/plugins/rating-widget-premium/freemius/assets/css/admin/common.css
77
+ * URL: http://fswp:8080/wp-content/plugins/rating-widget-premium/freemius/assets/css/admin/common.css
78
+ *
79
+ * @author Leo Fajardo (@leorw)
80
+ * @since 1.2.2
81
+ *
82
+ * @param string $asset_abs_path Asset's absolute path.
83
+ *
84
+ * @return string Asset's URL.
85
+ */
86
+ function fs_asset_url( $asset_abs_path ) {
87
+ $wp_content_dir = fs_normalize_path( WP_CONTENT_DIR );
88
+ $asset_abs_path = fs_normalize_path( $asset_abs_path );
89
+ $asset_rel_path = str_replace( $wp_content_dir, '', $asset_abs_path );
90
+
91
+ $asset_url = content_url( fs_normalize_path( $asset_rel_path ) );
92
+
93
+ return $asset_url;
94
+ }
95
+
96
+ function fs_enqueue_local_style( $handle, $path, $deps = array(), $ver = false, $media = 'all' ) {
97
+ wp_enqueue_style( $handle, fs_asset_url( WP_FS__DIR_CSS . '/' . trim( $path, '/' ) ), $deps, $ver, $media );
98
+ }
99
+
100
+ function fs_enqueue_local_script( $handle, $path, $deps = array(), $ver = false, $in_footer = 'all' ) {
101
+ wp_enqueue_script( $handle, fs_asset_url( WP_FS__DIR_JS . '/' . trim( $path, '/' ) ), $deps, $ver, $in_footer );
102
+ }
103
+
104
+ function fs_img_url( $path, $img_dir = WP_FS__DIR_IMG ) {
105
+ return ( fs_asset_url( $img_dir . '/' . trim( $path, '/' ) ) );
106
+ }
107
+
108
+ #--------------------------------------------------------------------------------
109
+ #region Request handlers.
110
+ #--------------------------------------------------------------------------------
111
+
112
+ if ( ! function_exists( 'fs_request_get' ) ) {
113
+ /**
114
+ * @param string $key
115
+ * @param mixed $def
116
+ * @param string|bool $type Since 1.2.1.7 - when set to 'get' will look for the value passed via querystring, when
117
+ * set to 'post' will look for the value passed via the POST request's body, otherwise,
118
+ * will check if the parameter was passed in any of the two.
119
+ *
120
+ * @return mixed
121
+ */
122
+ function fs_request_get( $key, $def = false, $type = false ) {
123
+ if ( is_string( $type ) ) {
124
+ $type = strtolower( $type );
125
+ }
126
+
127
+ switch ( $type ) {
128
+ case 'post':
129
+ $value = isset( $_POST[ $key ] ) ? $_POST[ $key ] : $def;
130
+ break;
131
+ case 'get':
132
+ $value = isset( $_GET[ $key ] ) ? $_GET[ $key ] : $def;
133
+ break;
134
+ default:
135
+ $value = isset( $_REQUEST[ $key ] ) ? $_REQUEST[ $key ] : $def;
136
+ break;
137
+ }
138
+
139
+ return $value;
140
+ }
141
+ }
142
+
143
+ if ( ! function_exists( 'fs_request_has' ) ) {
144
+ function fs_request_has( $key ) {
145
+ return isset( $_REQUEST[ $key ] );
146
+ }
147
+ }
148
+
149
+ if ( ! function_exists( 'fs_request_get_bool' ) ) {
150
+ function fs_request_get_bool( $key, $def = false ) {
151
+ if ( ! isset( $_REQUEST[ $key ] ) ) {
152
+ return $def;
153
+ }
154
+
155
+ if ( 1 == $_REQUEST[ $key ] || 'true' === strtolower( $_REQUEST[ $key ] ) ) {
156
+ return true;
157
+ }
158
+
159
+ if ( 0 == $_REQUEST[ $key ] || 'false' === strtolower( $_REQUEST[ $key ] ) ) {
160
+ return false;
161
+ }
162
+
163
+ return $def;
164
+ }
165
+ }
166
+
167
+ if ( ! function_exists( 'fs_request_is_post' ) ) {
168
+ function fs_request_is_post() {
169
+ return ( 'post' === strtolower( $_SERVER['REQUEST_METHOD'] ) );
170
+ }
171
+ }
172
+
173
+ if ( ! function_exists( 'fs_request_is_get' ) ) {
174
+ function fs_request_is_get() {
175
+ return ( 'get' === strtolower( $_SERVER['REQUEST_METHOD'] ) );
176
+ }
177
+ }
178
+
179
+ if ( ! function_exists( 'fs_get_action' ) ) {
180
+ function fs_get_action( $action_key = 'action' ) {
181
+ if ( ! empty( $_REQUEST[ $action_key ] ) && is_string( $_REQUEST[ $action_key ] ) ) {
182
+ return strtolower( $_REQUEST[ $action_key ] );
183
+ }
184
+
185
+ if ( 'action' == $action_key ) {
186
+ $action_key = 'fs_action';
187
+
188
+ if ( ! empty( $_REQUEST[ $action_key ] ) && is_string( $_REQUEST[ $action_key ] ) ) {
189
+ return strtolower( $_REQUEST[ $action_key ] );
190
+ }
191
+ }
192
+
193
+ return false;
194
+ }
195
+ }
196
+
197
+ if ( ! function_exists( 'fs_request_is_action' ) ) {
198
+ function fs_request_is_action( $action, $action_key = 'action' ) {
199
+ return ( strtolower( $action ) === fs_get_action( $action_key ) );
200
+ }
201
+ }
202
+
203
+ if ( ! function_exists( 'fs_request_is_action_secure' ) ) {
204
+ /**
205
+ * @author Vova Feldman (@svovaf)
206
+ * @since 1.0.0
207
+ *
208
+ * @since 1.2.1.5 Allow nonce verification.
209
+ *
210
+ * @param string $action
211
+ * @param string $action_key
212
+ * @param string $nonce_key
213
+ *
214
+ * @return bool
215
+ */
216
+ function fs_request_is_action_secure(
217
+ $action,
218
+ $action_key = 'action',
219
+ $nonce_key = 'nonce'
220
+ ) {
221
+ if ( strtolower( $action ) !== fs_get_action( $action_key ) ) {
222
+ return false;
223
+ }
224
+
225
+ $nonce = ! empty( $_REQUEST[ $nonce_key ] ) ?
226
+ $_REQUEST[ $nonce_key ] :
227
+ '';
228
+
229
+ if ( empty( $nonce ) ||
230
+ ( false === wp_verify_nonce( $nonce, $action ) )
231
+ ) {
232
+ return false;
233
+ }
234
+
235
+ return true;
236
+ }
237
+ }
238
+
239
+ #endregion
240
+
241
+ if ( ! function_exists( 'fs_is_plugin_page' ) ) {
242
+ function fs_is_plugin_page( $page_slug ) {
243
+ return ( is_admin() && $page_slug === fs_request_get( 'page' ) );
244
+ }
245
+ }
246
+
247
+ if ( ! function_exists( 'fs_get_raw_referer' ) ) {
248
+ /**
249
+ * Retrieves unvalidated referer from '_wp_http_referer' or HTTP referer.
250
+ *
251
+ * Do not use for redirects, use {@see wp_get_referer()} instead.
252
+ *
253
+ * @since 1.2.3
254
+ *
255
+ * @return string|false Referer URL on success, false on failure.
256
+ */
257
+ function fs_get_raw_referer() {
258
+ if ( function_exists( 'wp_get_raw_referer' ) ) {
259
+ return wp_get_raw_referer();
260
+ }
261
+ if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
262
+ return wp_unslash( $_REQUEST['_wp_http_referer'] );
263
+ } else if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
264
+ return wp_unslash( $_SERVER['HTTP_REFERER'] );
265
+ }
266
+
267
+ return false;
268
+ }
269
+ }
270
+
271
+ /* Core UI.
272
+ --------------------------------------------------------------------------------------------*/
273
+ /**
274
+ * @param number $module_id
275
+ * @param string $page
276
+ * @param string $action
277
+ * @param string $title
278
+ * @param array $params
279
+ * @param bool $is_primary
280
+ * @param string|bool $icon_class Optional class for an icon (since 1.1.7).
281
+ * @param string|bool $confirmation Optional confirmation message before submit (since 1.1.7).
282
+ * @param string $method Since 1.1.7
283
+ *
284
+ * @uses fs_ui_get_action_button()
285
+ */
286
+ function fs_ui_action_button(
287
+ $module_id,
288
+ $page,
289
+ $action,
290
+ $title,
291
+ $params = array(),
292
+ $is_primary = true,
293
+ $icon_class = false,
294
+ $confirmation = false,
295
+ $method = 'GET'
296
+ ) {
297
+ echo fs_ui_get_action_button(
298
+ $module_id,
299
+ $page,
300
+ $action,
301
+ $title,
302
+ $params,
303
+ $is_primary,
304
+ $icon_class,
305
+ $confirmation,
306
+ $method
307
+ );
308
+ }
309
+
310
+ /**
311
+ * @author Vova Feldman (@svovaf)
312
+ * @since 1.1.7
313
+ *
314
+ * @param number $module_id
315
+ * @param string $page
316
+ * @param string $action
317
+ * @param string $title
318
+ * @param array $params
319
+ * @param bool $is_primary
320
+ * @param string|bool $icon_class Optional class for an icon.
321
+ * @param string|bool $confirmation Optional confirmation message before submit.
322
+ * @param string $method
323
+ *
324
+ * @return string
325
+ */
326
+ function fs_ui_get_action_button(
327
+ $module_id,
328
+ $page,
329
+ $action,
330
+ $title,
331
+ $params = array(),
332
+ $is_primary = true,
333
+ $icon_class = false,
334
+ $confirmation = false,
335
+ $method = 'GET'
336
+ ) {
337
+ // Prepend icon (if set).
338
+ $title = ( is_string( $icon_class ) ? '<i class="' . $icon_class . '"></i> ' : '' ) . $title;
339
+
340
+ if ( is_string( $confirmation ) ) {
341
+ return sprintf( '<form action="%s" method="%s"><input type="hidden" name="fs_action" value="%s">%s<a href="#" class="%s" onclick="if (confirm(\'%s\')) this.parentNode.submit(); return false;">%s</a></form>',
342
+ freemius( $module_id )->_get_admin_page_url( $page, $params ),
343
+ $method,
344
+ $action,
345
+ wp_nonce_field( $action, '_wpnonce', true, false ),
346
+ 'button' . ( $is_primary ? ' button-primary' : '' ),
347
+ $confirmation,
348
+ $title
349
+ );
350
+ } else if ( 'GET' !== strtoupper( $method ) ) {
351
+ return sprintf( '<form action="%s" method="%s"><input type="hidden" name="fs_action" value="%s">%s<a href="#" class="%s" onclick="this.parentNode.submit(); return false;">%s</a></form>',
352
+ freemius( $module_id )->_get_admin_page_url( $page, $params ),
353
+ $method,
354
+ $action,
355
+ wp_nonce_field( $action, '_wpnonce', true, false ),
356
+ 'button' . ( $is_primary ? ' button-primary' : '' ),
357
+ $title
358
+ );
359
+ } else {
360
+ return sprintf( '<a href="%s" class="%s">%s</a></form>',
361
+ wp_nonce_url( freemius( $module_id )->_get_admin_page_url( $page, array_merge( $params, array( 'fs_action' => $action ) ) ), $action ),
362
+ 'button' . ( $is_primary ? ' button-primary' : '' ),
363
+ $title
364
+ );
365
+ }
366
+ }
367
+
368
+ function fs_ui_action_link( $module_id, $page, $action, $title, $params = array() ) {
369
+ ?><a class=""
370
+ href="<?php echo wp_nonce_url( freemius( $module_id )->_get_admin_page_url( $page, array_merge( $params, array( 'fs_action' => $action ) ) ), $action ) ?>"><?php echo $title ?></a><?php
371
+ }
372
+
373
+ /*function fs_error_handler($errno, $errstr, $errfile, $errline)
374
+ {
375
+ if (false === strpos($errfile, 'freemius/'))
376
+ {
377
+ // @todo Dump Freemius errors to local log.
378
+ }
379
 
380
  // switch ($errno) {
381
  // case E_USER_ERROR:
389
  // default:
390
  // break;
391
  // }
392
+ }
393
+
394
+ set_error_handler('fs_error_handler');*/
395
+
396
+ if ( ! function_exists( 'fs_nonce_url' ) ) {
397
+ /**
398
+ * Retrieve URL with nonce added to URL query.
399
+ *
400
+ * Originally was using `wp_nonce_url()` but the new version
401
+ * changed the return value to escaped URL, that's not the expected
402
+ * behaviour.
403
+ *
404
+ * @author Vova Feldman (@svovaf)
405
+ * @since ~1.1.3
406
+ *
407
+ * @param string $actionurl URL to add nonce action.
408
+ * @param int|string $action Optional. Nonce action name. Default -1.
409
+ * @param string $name Optional. Nonce name. Default '_wpnonce'.
410
+ *
411
+ * @return string Escaped URL with nonce action added.
412
+ */
413
+ function fs_nonce_url( $actionurl, $action = - 1, $name = '_wpnonce' ) {
414
+ return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
415
+ }
416
+ }
417
+
418
+ if ( ! function_exists( 'fs_starts_with' ) ) {
419
+ /**
420
+ * Check if string starts with.
421
+ *
422
+ * @author Vova Feldman (@svovaf)
423
+ * @since 1.1.3
424
+ *
425
+ * @param string $haystack
426
+ * @param string $needle
427
+ *
428
+ * @return bool
429
+ */
430
+ function fs_starts_with( $haystack, $needle ) {
431
+ $length = strlen( $needle );
432
+
433
+ return ( substr( $haystack, 0, $length ) === $needle );
434
+ }
435
+ }
436
+
437
+ #region Url Canonization ------------------------------------------------------------------
438
+
439
+ if ( ! function_exists( 'fs_canonize_url' ) ) {
440
+ /**
441
+ * @author Vova Feldman (@svovaf)
442
+ * @since 1.1.3
443
+ *
444
+ * @param string $url
445
+ * @param bool $omit_host
446
+ * @param array $ignore_params
447
+ *
448
+ * @return string
449
+ */
450
+ function fs_canonize_url( $url, $omit_host = false, $ignore_params = array() ) {
451
+ $parsed_url = parse_url( strtolower( $url ) );
452
 
453
  // if ( ! isset( $parsed_url['host'] ) ) {
454
  // return $url;
455
  // }
456
 
457
+ $canonical = ( ( $omit_host || ! isset( $parsed_url['host'] ) ) ? '' : $parsed_url['host'] ) . $parsed_url['path'];
458
+
459
+ if ( isset( $parsed_url['query'] ) ) {
460
+ parse_str( $parsed_url['query'], $queryString );
461
+ $canonical .= '?' . fs_canonize_query_string( $queryString, $ignore_params );
462
+ }
463
+
464
+ return $canonical;
465
+ }
466
+ }
467
+
468
+ if ( ! function_exists( 'fs_canonize_query_string' ) ) {
469
+ /**
470
+ * @author Vova Feldman (@svovaf)
471
+ * @since 1.1.3
472
+ *
473
+ * @param array $params
474
+ * @param array $ignore_params
475
+ * @param bool $params_prefix
476
+ *
477
+ * @return string
478
+ */
479
+ function fs_canonize_query_string( array $params, array &$ignore_params, $params_prefix = false ) {
480
+ if ( ! is_array( $params ) || 0 === count( $params ) ) {
481
+ return '';
482
+ }
483
+
484
+ // Url encode both keys and values
485
+ $keys = fs_urlencode_rfc3986( array_keys( $params ) );
486
+ $values = fs_urlencode_rfc3986( array_values( $params ) );
487
+ $params = array_combine( $keys, $values );
488
+
489
+ // Parameters are sorted by name, using lexicographical byte value ordering.
490
+ // Ref: Spec: 9.1.1 (1)
491
+ uksort( $params, 'strcmp' );
492
+
493
+ $pairs = array();
494
+ foreach ( $params as $parameter => $value ) {
495
+ $lower_param = strtolower( $parameter );
496
+
497
+ // Skip ignore params.
498
+ if ( in_array( $lower_param, $ignore_params ) ||
499
+ ( false !== $params_prefix && fs_starts_with( $lower_param, $params_prefix ) )
500
+ ) {
501
+ continue;
502
+ }
503
+
504
+ if ( is_array( $value ) ) {
505
+ // If two or more parameters share the same name, they are sorted by their value
506
+ // Ref: Spec: 9.1.1 (1)
507
+ natsort( $value );
508
+ foreach ( $value as $duplicate_value ) {
509
+ $pairs[] = $lower_param . '=' . $duplicate_value;
510
+ }
511
+ } else {
512
+ $pairs[] = $lower_param . '=' . $value;
513
+ }
514
+ }
515
+
516
+ if ( 0 === count( $pairs ) ) {
517
+ return '';
518
+ }
519
+
520
+ return implode( "&", $pairs );
521
+ }
522
+ }
523
+
524
+ if ( ! function_exists( 'fs_urlencode_rfc3986' ) ) {
525
+ /**
526
+ * @author Vova Feldman (@svovaf)
527
+ * @since 1.1.3
528
+ *
529
+ * @param string|string[] $input
530
+ *
531
+ * @return array|mixed|string
532
+ */
533
+ function fs_urlencode_rfc3986( $input ) {
534
+ if ( is_array( $input ) ) {
535
+ return array_map( 'fs_urlencode_rfc3986', $input );
536
+ } else if ( is_scalar( $input ) ) {
537
+ return str_replace( '+', ' ', str_replace( '%7E', '~', rawurlencode( $input ) ) );
538
+ }
539
+
540
+ return '';
541
+ }
542
+ }
543
+
544
+ #endregion Url Canonization ------------------------------------------------------------------
545
+
546
+ /**
547
+ * @author Vova Feldman (@svovaf)
548
+ *
549
+ * @since 1.2.2 Changed to usage of WP_Filesystem_Direct.
550
+ *
551
+ * @param string $from URL
552
+ * @param string $to File path.
553
+ *
554
+ * @return bool Is successfully downloaded.
555
+ */
556
+ function fs_download_image( $from, $to ) {
557
+ $dir = dirname( $to );
558
+
559
+ if ( 'direct' !== get_filesystem_method( array(), $dir ) ) {
560
+ return false;
561
+ }
562
+
563
+ if ( ! class_exists( 'WP_Filesystem_Direct' ) ) {
564
+ require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
565
+ require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
566
+ }
567
+
568
+ $fs = new WP_Filesystem_Direct( '' );
569
+ $tmpfile = download_url( $from );
570
+
571
+ if ( $tmpfile instanceof WP_Error ) {
572
+ // Issue downloading the file.
573
+ return false;
574
+ }
575
+
576
+ $fs->copy( $tmpfile, $to );
577
+ $fs->delete( $tmpfile );
578
+
579
+ return true;
580
+ }
581
+
582
+ /* General Utilities
583
+ --------------------------------------------------------------------------------------------*/
584
+
585
+ /**
586
+ * Sorts an array by the value of the priority key.
587
+ *
588
+ * @author Daniel Iser (@danieliser)
589
+ * @since 1.1.7
590
+ *
591
+ * @param $a
592
+ * @param $b
593
+ *
594
+ * @return int
595
+ */
596
+ function fs_sort_by_priority( $a, $b ) {
597
+
598
+ // If b has a priority and a does not, b wins.
599
+ if ( ! isset( $a['priority'] ) && isset( $b['priority'] ) ) {
600
+ return 1;
601
+ } // If b has a priority and a does not, b wins.
602
+ elseif ( isset( $a['priority'] ) && ! isset( $b['priority'] ) ) {
603
+ return - 1;
604
+ } // If neither has a priority or both priorities are equal its a tie.
605
+ elseif ( ( ! isset( $a['priority'] ) && ! isset( $b['priority'] ) ) || $a['priority'] === $b['priority'] ) {
606
+ return 0;
607
+ }
608
+
609
+ // If both have priority return the winner.
610
+ return ( $a['priority'] < $b['priority'] ) ? - 1 : 1;
611
+ }
612
+
613
+ #--------------------------------------------------------------------------------
614
+ #region Localization
615
+ #--------------------------------------------------------------------------------
616
+
617
+ if ( ! function_exists( 'fs_text' ) ) {
618
+ /**
619
+ * Retrieve a translated text by key.
620
+ *
621
+ * @author Vova Feldman (@svovaf)
622
+ * @since 1.2.1.7
623
+ *
624
+ * @param string $key
625
+ * @param string $slug
626
+ *
627
+ * @return string
628
+ *
629
+ * @global $fs_text , $fs_text_overrides
630
+ */
631
+ function fs_text( $key, $slug = 'freemius' ) {
632
+ return __fs( $key, $slug );
633
+ }
634
+
635
+ /**
636
+ * Get a translatable text override if exists, or `false`.
637
+ *
638
+ * @author Vova Feldman (@svovaf)
639
+ * @since 1.2.1.7
640
+ *
641
+ * @param string $text Translatable string.
642
+ * @param string $key String key for overrides.
643
+ * @param string $slug Module slug for overrides.
644
+ *
645
+ * @return string|false
646
+ */
647
+ function fs_text_override( $text, $key, $slug ) {
648
+ global $fs_text_overrides;
649
+
650
+ /**
651
+ * Check if string is overridden.
652
+ */
653
+ if ( ! isset( $fs_text_overrides[ $slug ] ) ) {
654
+ return false;
655
+ }
656
+
657
+ if ( empty( $key ) ) {
658
+ $key = strtolower( str_replace( ' ', '-', $text ) );
659
+ }
660
+
661
+ if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) {
662
+ return $fs_text_overrides[ $slug ][ $key ];
663
+ }
664
+
665
+ $lower_key = strtolower( $key );
666
+ if ( isset( $fs_text_overrides[ $slug ][ $lower_key ] ) ) {
667
+ return $fs_text_overrides[ $slug ][ $lower_key ];
668
+ }
669
+
670
+ return false;
671
+ }
672
+
673
+ /**
674
+ * Get a translatable text and its text domain.
675
+ *
676
+ * When the text is overridden by the module, returns the overridden text and the text domain of the module. Otherwise, returns the original text and 'freemius' as the text domain.
677
+ *
678
+ * @author Vova Feldman (@svovaf)
679
+ * @since 1.2.1.7
680
+ *
681
+ * @param string $text Translatable string.
682
+ * @param string $key String key for overrides.
683
+ * @param string $slug Module slug for overrides.
684
+ *
685
+ * @return string[]
686
+ */
687
+ function fs_text_and_domain( $text, $key, $slug ) {
688
+ $override = fs_text_override( $text, $key, $slug );
689
+
690
+ if ( false === $override ) {
691
+ // No override, use FS text domain.
692
+ $text_domain = 'freemius';
693
+ } else {
694
+ // Found an override.
695
+ $text = $override;
696
+ // Use the module's text domain.
697
+ $text_domain = $slug;
698
+ }
699
+
700
+ return array( $text, $text_domain );
701
+ }
702
+
703
+ #region Private
704
+
705
+ /**
706
+ * Retrieve an inline translated text by key.
707
+ *
708
+ * @author Vova Feldman (@svovaf)
709
+ * @since 1.2.3
710
+ *
711
+ * @param string $text Translatable string.
712
+ * @param string $key String key for overrides.
713
+ * @param string $slug Module slug for overrides.
714
+ *
715
+ * @return string
716
+ *
717
+ * @global $fs_text_overrides
718
+ */
719
+ function _fs_text_inline( $text, $key = '', $slug = 'freemius' ) {
720
+ list( $text, $text_domain ) = fs_text_and_domain( $text, $key, $slug );
721
+
722
+ // Avoid misleading Theme Check warning.
723
+ $fn = 'translate';
724
+
725
+ return $fn( $text, $text_domain );
726
+ }
727
+
728
+ /**
729
+ * Retrieve an inline translated text by key with a context.
730
+ *
731
+ * @author Vova Feldman (@svovaf)
732
+ * @since 1.2.3
733
+ *
734
+ * @param string $text Translatable string.
735
+ * @param string $context Context information for the translators.
736
+ * @param string $key String key for overrides.
737
+ * @param string $slug Module slug for overrides.
738
+ *
739
+ * @return string
740
+ *
741
+ * @global $fs_text_overrides
742
+ */
743
+ function _fs_text_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
744
+ list( $text, $text_domain ) = fs_text_and_domain( $text, $key, $slug );
745
+
746
+ // Avoid misleading Theme Check warning.
747
+ $fn = 'translate_with_gettext_context';
748
+
749
+ return $fn( $text, $context, $text_domain );
750
+ }
751
+
752
+ #endregion
753
+
754
+ /**
755
+ * Retrieve an inline translated text by key.
756
+ *
757
+ * @author Vova Feldman (@svovaf)
758
+ * @since 1.2.3
759
+ *
760
+ * @param string $text Translatable string.
761
+ * @param string $key String key for overrides.
762
+ * @param string $slug Module slug for overrides.
763
+ *
764
+ * @return string
765
+ *
766
+ * @global $fs_text_overrides
767
+ */
768
+ function fs_text_inline( $text, $key = '', $slug = 'freemius' ) {
769
+ return _fs_text_inline( $text, $key, $slug );
770
+ }
771
+
772
+ /**
773
+ * Retrieve an inline translated text by key with a context.
774
+ *
775
+ * @author Vova Feldman (@svovaf)
776
+ * @since 1.2.3
777
+ *
778
+ * @param string $text Translatable string.
779
+ * @param string $context Context information for the translators.
780
+ * @param string $key String key for overrides.
781
+ * @param string $slug Module slug for overrides.
782
+ *
783
+ * @return string
784
+ *
785
+ * @global $fs_text_overrides
786
+ */
787
+ function fs_text_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
788
+ return _fs_text_x_inline( $text, $context, $key, $slug );
789
+ }
790
+
791
+ /**
792
+ * Output a translated text by key.
793
+ *
794
+ * @author Vova Feldman (@svovaf)
795
+ * @since 1.2.1.7
796
+ *
797
+ * @param string $key
798
+ * @param string $slug
799
+ */
800
+ function fs_echo( $key, $slug = 'freemius' ) {
801
+ echo fs_text( $key, $slug );
802
+ }
803
+
804
+ /**
805
+ * Output an inline translated text.
806
+ *
807
+ * @author Vova Feldman (@svovaf)
808
+ * @since 1.2.3
809
+ *
810
+ * @param string $text Translatable string.
811
+ * @param string $key String key for overrides.
812
+ * @param string $slug Module slug for overrides.
813
+ */
814
+ function fs_echo_inline( $text, $key = '', $slug = 'freemius' ) {
815
+ echo _fs_text_inline( $text, $key, $slug );
816
+ }
817
+
818
+ /**
819
+ * Output an inline translated text with a context.
820
+ *
821
+ * @author Vova Feldman (@svovaf)
822
+ * @since 1.2.3
823
+ *
824
+ * @param string $text Translatable string.
825
+ * @param string $context Context information for the translators.
826
+ * @param string $key String key for overrides.
827
+ * @param string $slug Module slug for overrides.
828
+ */
829
+ function fs_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
830
+ echo _fs_text_x_inline( $text, $context, $key, $slug );
831
+ }
832
+ }
833
+
834
+ if ( ! function_exists( 'fs_esc_attr' ) ) {
835
+ /**
836
+ * @author Vova Feldman
837
+ * @since 1.2.1.6
838
+ *
839
+ * @param string $key
840
+ * @param string $slug
841
+ *
842
+ * @return string
843
+ */
844
+ function fs_esc_attr( $key, $slug ) {
845
+ return esc_attr( fs_text( $key, $slug ) );
846
+ }
847
+ }
848
+
849
+ if ( ! function_exists( 'fs_esc_attr_inline' ) ) {
850
+ /**
851
+ * @author Vova Feldman (@svovaf)
852
+ * @since 1.2.3
853
+ *
854
+ * @param string $text Translatable string.
855
+ * @param string $key String key for overrides.
856
+ * @param string $slug Module slug for overrides.
857
+ *
858
+ * @return string
859
+ */
860
+ function fs_esc_attr_inline( $text, $key = '', $slug = 'freemius' ) {
861
+ return esc_attr( _fs_text_inline( $text, $key, $slug ) );
862
+ }
863
+ }
864
+
865
+ if ( ! function_exists( 'fs_esc_attr_x_inline' ) ) {
866
+ /**
867
+ * @author Vova Feldman (@svovaf)
868
+ * @since 1.2.3
869
+ *
870
+ * @param string $text Translatable string.
871
+ * @param string $context Context information for the translators.
872
+ * @param string $key String key for overrides.
873
+ * @param string $slug Module slug for overrides.
874
+ *
875
+ * @return string
876
+ */
877
+ function fs_esc_attr_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
878
+ return esc_attr( _fs_text_x_inline( $text, $context, $key, $slug ) );
879
+ }
880
+ }
881
+
882
+ if ( ! function_exists( 'fs_esc_attr_echo' ) ) {
883
+ /**
884
+ * @author Vova Feldman
885
+ * @since 1.2.1.6
886
+ *
887
+ * @param string $key
888
+ * @param string $slug
889
+ */
890
+ function fs_esc_attr_echo( $key, $slug ) {
891
+ echo esc_attr( fs_text( $key, $slug ) );
892
+ }
893
+ }
894
+
895
+ if ( ! function_exists( 'fs_esc_attr_echo_inline' ) ) {
896
+ /**
897
+ * @author Vova Feldman (@svovaf)
898
+ * @since 1.2.3
899
+ *
900
+ * @param string $text Translatable string.
901
+ * @param string $key String key for overrides.
902
+ * @param string $slug Module slug for overrides.
903
+ */
904
+ function fs_esc_attr_echo_inline( $text, $key = '', $slug = 'freemius' ) {
905
+ echo esc_attr( _fs_text_inline( $text, $key, $slug ) );
906
+ }
907
+ }
908
+
909
+ if ( ! function_exists( 'fs_esc_js' ) ) {
910
+ /**
911
+ * @author Vova Feldman
912
+ * @since 1.2.1.6
913
+ *
914
+ * @param string $key
915
+ * @param string $slug
916
+ *
917
+ * @return string
918
+ */
919
+ function fs_esc_js( $key, $slug ) {
920
+ return esc_js( fs_text( $key, $slug ) );
921
+ }
922
+ }
923
+
924
+ if ( ! function_exists( 'fs_esc_js_inline' ) ) {
925
+ /**
926
+ * @author Vova Feldman (@svovaf)
927
+ * @since 1.2.3
928
+ *
929
+ * @param string $text Translatable string.
930
+ * @param string $key String key for overrides.
931
+ * @param string $slug Module slug for overrides.
932
+ *
933
+ * @return string
934
+ */
935
+ function fs_esc_js_inline( $text, $key = '', $slug = 'freemius' ) {
936
+ return esc_js( _fs_text_inline( $text, $key, $slug ) );
937
+ }
938
+ }
939
+
940
+ if ( ! function_exists( 'fs_esc_js_x_inline' ) ) {
941
+ /**
942
+ * @author Vova Feldman (@svovaf)
943
+ * @since 1.2.3
944
+ *
945
+ * @param string $text Translatable string.
946
+ * @param string $context Context information for the translators.
947
+ * @param string $key String key for overrides.
948
+ * @param string $slug Module slug for overrides.
949
+ *
950
+ * @return string
951
+ */
952
+ function fs_esc_js_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
953
+ return esc_js( _fs_text_x_inline( $text, $context, $key, $slug ) );
954
+ }
955
+ }
956
+
957
+ if ( ! function_exists( 'fs_esc_js_echo_x_inline' ) ) {
958
+ /**
959
+ * @author Vova Feldman (@svovaf)
960
+ * @since 1.2.3
961
+ *
962
+ * @param string $text Translatable string.
963
+ * @param string $context Context information for the translators.
964
+ * @param string $key String key for overrides.
965
+ * @param string $slug Module slug for overrides.
966
+ *
967
+ * @return string
968
+ */
969
+ function fs_esc_js_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
970
+ echo esc_js( _fs_text_x_inline( $text, $context, $key, $slug ) );
971
+ }
972
+ }
973
+
974
+ if ( ! function_exists( 'fs_esc_js_echo' ) ) {
975
+ /**
976
+ * @author Vova Feldman
977
+ * @since 1.2.1.6
978
+ *
979
+ * @param string $key
980
+ * @param string $slug
981
+ */
982
+ function fs_esc_js_echo( $key, $slug ) {
983
+ echo esc_js( fs_text( $key, $slug ) );
984
+ }
985
+ }
986
+
987
+ if ( ! function_exists( 'fs_esc_js_echo_inline' ) ) {
988
+ /**
989
+ * @author Vova Feldman (@svovaf)
990
+ * @since 1.2.3
991
+ *
992
+ * @param string $text Translatable string.
993
+ * @param string $key String key for overrides.
994
+ * @param string $slug Module slug for overrides.
995
+ */
996
+ function fs_esc_js_echo_inline( $text, $key = '', $slug = 'freemius' ) {
997
+ echo esc_js( _fs_text_inline( $text, $key, $slug ) );
998
+ }
999
+ }
1000
+
1001
+ if ( ! function_exists( 'fs_json_encode_echo' ) ) {
1002
+ /**
1003
+ * @author Vova Feldman
1004
+ * @since 1.2.1.6
1005
+ *
1006
+ * @param string $key
1007
+ * @param string $slug
1008
+ */
1009
+ function fs_json_encode_echo( $key, $slug ) {
1010
+ echo json_encode( fs_text( $key, $slug ) );
1011
+ }
1012
+ }
1013
+
1014
+ if ( ! function_exists( 'fs_json_encode_echo_inline' ) ) {
1015
+ /**
1016
+ * @author Vova Feldman (@svovaf)
1017
+ * @since 1.2.3
1018
+ *
1019
+ * @param string $text Translatable string.
1020
+ * @param string $key String key for overrides.
1021
+ * @param string $slug Module slug for overrides.
1022
+ */
1023
+ function fs_json_encode_echo_inline( $text, $key = '', $slug = 'freemius' ) {
1024
+ echo json_encode( _fs_text_inline( $text, $key, $slug ) );
1025
+ }
1026
+ }
1027
+
1028
+ if ( ! function_exists( 'fs_esc_html' ) ) {
1029
+ /**
1030
+ * @author Vova Feldman
1031
+ * @since 1.2.1.6
1032
+ *
1033
+ * @param string $key
1034
+ * @param string $slug
1035
+ *
1036
+ * @return string
1037
+ */
1038
+ function fs_esc_html( $key, $slug ) {
1039
+ return esc_html( fs_text( $key, $slug ) );
1040
+ }
1041
+ }
1042
+
1043
+ if ( ! function_exists( 'fs_esc_html_inline' ) ) {
1044
+ /**
1045
+ * @author Vova Feldman (@svovaf)
1046
+ * @since 1.2.3
1047
+ *
1048
+ * @param string $text Translatable string.
1049
+ * @param string $key String key for overrides.
1050
+ * @param string $slug Module slug for overrides.
1051
+ *
1052
+ * @return string
1053
+ */
1054
+ function fs_esc_html_inline( $text, $key = '', $slug = 'freemius' ) {
1055
+ return esc_html( _fs_text_inline( $text, $key, $slug ) );
1056
+ }
1057
+ }
1058
+
1059
+ if ( ! function_exists( 'fs_esc_html_x_inline' ) ) {
1060
+ /**
1061
+ * @author Vova Feldman (@svovaf)
1062
+ * @since 1.2.3
1063
+ *
1064
+ * @param string $text Translatable string.
1065
+ * @param string $context Context information for the translators.
1066
+ * @param string $key String key for overrides.
1067
+ * @param string $slug Module slug for overrides.
1068
+ *
1069
+ * @return string
1070
+ */
1071
+ function fs_esc_html_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
1072
+ return esc_html( _fs_text_x_inline( $text, $context, $key, $slug ) );
1073
+ }
1074
+ }
1075
+
1076
+ if ( ! function_exists( 'fs_esc_html_echo_x_inline' ) ) {
1077
+ /**
1078
+ * @author Vova Feldman (@svovaf)
1079
+ * @since 1.2.3
1080
+ *
1081
+ * @param string $text Translatable string.
1082
+ * @param string $context Context information for the translators.
1083
+ * @param string $key String key for overrides.
1084
+ * @param string $slug Module slug for overrides.
1085
+ */
1086
+ function fs_esc_html_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
1087
+ echo esc_html( _fs_text_x_inline( $text, $context, $key, $slug ) );
1088
+ }
1089
+ }
1090
+
1091
+ if ( ! function_exists( 'fs_esc_html_echo' ) ) {
1092
+ /**
1093
+ * @author Vova Feldman
1094
+ * @since 1.2.1.6
1095
+ *
1096
+ * @param string $key
1097
+ * @param string $slug
1098
+ */
1099
+ function fs_esc_html_echo( $key, $slug ) {
1100
+ echo esc_html( fs_text( $key, $slug ) );
1101
+ }
1102
+ }
1103
+
1104
+ if ( ! function_exists( 'fs_esc_html_echo_inline' ) ) {
1105
+ /**
1106
+ * @author Vova Feldman (@svovaf)
1107
+ * @since 1.2.3
1108
+ *
1109
+ * @param string $text Translatable string.
1110
+ * @param string $key String key for overrides.
1111
+ * @param string $slug Module slug for overrides.
1112
+ */
1113
+ function fs_esc_html_echo_inline( $text, $key = '', $slug = 'freemius' ) {
1114
+ echo esc_html( _fs_text_inline( $text, $key, $slug ) );
1115
+ }
1116
+ }
1117
 
1118
  #endregion
freemius/includes/fs-essential-functions.php CHANGED
@@ -158,6 +158,7 @@
158
  * Retrieve a translated text by key.
159
  *
160
  * @deprecated Use `fs_text()` instead since methods starting with `__` trigger warnings in Php 7.
 
161
  *
162
  * @author Vova Feldman (@svovaf)
163
  * @since 1.1.4
@@ -209,6 +210,8 @@
209
  *
210
  * @deprecated Use `fs_echo()` instead for consistency with `fs_text()`.
211
  *
 
 
212
  * @author Vova Feldman (@svovaf)
213
  * @since 1.1.4
214
  *
158
  * Retrieve a translated text by key.
159
  *
160
  * @deprecated Use `fs_text()` instead since methods starting with `__` trigger warnings in Php 7.
161
+ * @todo Remove this method in the future.
162
  *
163
  * @author Vova Feldman (@svovaf)
164
  * @since 1.1.4
210
  *
211
  * @deprecated Use `fs_echo()` instead for consistency with `fs_text()`.
212
  *
213
+ * @todo Remove this method in the future.
214
+ *
215
  * @author Vova Feldman (@svovaf)
216
  * @since 1.1.4
217
  *
freemius/includes/fs-plugin-info-dialog.php CHANGED
@@ -325,8 +325,12 @@
325
  $plan->has_trial()
326
  ) . '" target="_parent">' .
327
  ( ! $plan->has_trial() ?
328
- fs_text( 'purchase', $api->slug ) :
329
- sprintf( fs_text( 'start-free-x', $api->slug ), $this->get_trial_period( $plan ) )
 
 
 
 
330
  ) .
331
  '</a>';
332
 
@@ -348,23 +352,23 @@
348
  * plugin is wordpress.org compliant. Therefore, require a download
349
  * since installing external plugins is not allowed by the wp.org guidelines.
350
  */
351
- return ' <a class="button button-primary right" href="' . esc_url( $api->download_link ) . '" target="_blank">' . fs_text( 'download-latest', $api->slug ) . '</a>';
352
  } else {
353
  if ( $status['url'] ) {
354
- return '<a class="button button-primary right" href="' . $status['url'] . '" target="_parent">' . fs_text( 'install-now', $api->slug ) . '</a>';
355
  }
356
  }
357
  break;
358
  case 'update_available':
359
  if ( $status['url'] ) {
360
- return '<a class="button button-primary right" href="' . $status['url'] . '" target="_parent">' . fs_text( 'install-update-now', $api->slug ) . '</a>';
361
  }
362
  break;
363
  case 'newer_installed':
364
- return '<a class="button button-primary right disabled">' . sprintf( fs_text( 'newer-installed', $api->slug ), $status['version'] ) . '</a>';
365
  break;
366
  case 'latest_installed':
367
- return '<a class="button button-primary right disabled">' . fs_text( 'latest-installed', $api->slug ) . '</a>';
368
  break;
369
  }
370
 
@@ -477,13 +481,13 @@
477
  );
478
 
479
  $plugins_section_titles = array(
480
- 'description' => fs_text( 'description', $api->slug ),
481
- 'installation' => fs_text( 'installation', $api->slug ),
482
- 'faq' => fs_text( 'faq', $api->slug ),
483
- 'screenshots' => fs_text( 'screenshots', $api->slug ),
484
- 'changelog' => fs_text( 'changelog', $api->slug ),
485
- 'reviews' => fs_text( 'reviews', $api->slug ),
486
- 'other_notes' => fs_text( 'other_notes', $api->slug ),
487
  );
488
 
489
  // Sanitize HTML
@@ -498,7 +502,7 @@
498
  }
499
 
500
  // Add after $api->slug is ready.
501
- $plugins_section_titles['features'] = fs_text( 'features-and-pricing', $api->slug );
502
 
503
  $_tab = esc_attr( $tab );
504
 
@@ -508,7 +512,7 @@
508
  $section = array_shift( $section_titles );
509
  }
510
 
511
- iframe_header( fs_text( 'plugin-install', $api->slug ) );
512
 
513
  $_with_banner = '';
514
 
@@ -580,7 +584,7 @@
580
  <div class="fs-plan<?php if ( ! $is_multi_cycle ) {
581
  echo ' fs-single-cycle';
582
  } ?>" data-plan-id="<?php echo $plan->id ?>">
583
- <h3 data-plan="<?php echo $plan->id ?>"><?php printf( fs_text( 'x-plan', $api->slug ), $plan->title ) ?></h3>
584
  <?php $has_annual = $first_pricing->has_annual() ?>
585
  <?php $has_monthly = $first_pricing->has_monthly() ?>
586
  <div class="nav-tab-wrapper">
@@ -605,9 +609,22 @@
605
  <a class="nav-tab" data-billing-cycle="<?php echo $cycle ?>"
606
  data-pricing="<?php echo esc_attr( json_encode( $prices ) ) ?>">
607
  <?php if ( $is_featured ) : ?>
608
- <label>&#9733; <?php fs_echo( 'best', $api->slug ) ?> &#9733;</label>
609
  <?php endif ?>
610
- <?php fs_echo( $cycle, $api->slug ) ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
611
  </a>
612
  <?php endif ?>
613
  <?php $i ++; endforeach ?>
@@ -618,21 +635,21 @@
618
  _formatBillingFrequency = function (cycle) {
619
  switch (cycle) {
620
  case 'monthly':
621
- return '<?php printf(fs_text('billed-x', $api->slug), fs_text('monthly', $api->slug)) ?>';
622
  case 'annual':
623
- return '<?php printf(fs_text('billed-x', $api->slug), fs_text('annually', $api->slug)) ?>';
624
  case 'lifetime':
625
- return '<?php printf(fs_text('billed-x', $api->slug), fs_text('once', $api->slug)) ?>';
626
  }
627
  },
628
  _formatLicensesTitle = function (pricing) {
629
  switch (pricing.licenses) {
630
  case 1:
631
- return '<?php fs_echo( 'license-single-site', $api->slug ) ?>';
632
  case null:
633
- return '<?php fs_echo( 'license-unlimited', $api->slug ) ?>';
634
  default:
635
- return '<?php fs_echo( 'license-x-sites', $api->slug ) ?>'.replace('%s', pricing.licenses);
636
  }
637
  },
638
  _formatPrice = function (pricing, cycle, multipleLicenses) {
@@ -642,14 +659,14 @@
642
  var priceCycle;
643
  switch (cycle) {
644
  case 'monthly':
645
- priceCycle = ' / <?php fs_echo('mo', $api->slug) ?>';
646
  break;
647
  case 'lifetime':
648
  priceCycle = '';
649
  break;
650
  case 'annual':
651
  default:
652
- priceCycle = ' / <?php fs_echo('year', $api->slug) ?>';
653
  break;
654
  }
655
 
@@ -701,7 +718,7 @@
701
 
702
  // Render licenses prices.
703
  if (1 == pricing.length) {
704
- html = '<li><label><?php fs_echo( 'price', $api->slug ) ?>: ' + _formatPrice(pricing[0], billingCycle, false) + '</label></li>';
705
  } else {
706
  for (var i = 0; i < pricing.length; i++) {
707
  html += '<li><label><input name="pricing-<?php echo $plan->id ?>" type="radio" value="' + pricing[i].id + '">' + _formatPrice(pricing[i], billingCycle) + '</label></li>';
@@ -747,7 +764,7 @@
747
  <?php $annual_discount = ( $has_annual && $has_monthly ) ? $plan->pricing[0]->annual_discount_percentage() : 0 ?>
748
  <?php if ( $annual_discount > 0 ) : ?>
749
  <span
750
- class="fs-annual-discount"><?php printf( fs_text( 'save-x', $api->slug ), $annual_discount . '%' ) ?></span>
751
  <?php endif ?>
752
  <ul class="fs-licenses">
753
  </ul>
@@ -757,10 +774,10 @@
757
  <?php $trial_period = $this->get_trial_period( $plan ) ?>
758
  <ul class="fs-trial-terms">
759
  <li>
760
- <i class="dashicons dashicons-yes"></i><?php printf( fs_text( 'no-commitment-x', $api->slug ), $trial_period ) ?>
761
  </li>
762
  <li>
763
- <i class="dashicons dashicons-yes"></i><?php printf( fs_text( 'after-x-pay-as-little-y', $api->slug ), $trial_period, '<var class="fs-price">' . $this->get_price_tag( $plan, $plan->pricing[0] ) . '</var>' ) ?>
764
  </li>
765
  </ul>
766
  <?php endif ?>
@@ -771,85 +788,99 @@
771
  <?php endif ?>
772
  <?php endif ?>
773
  <div>
774
- <h3><?php fs_echo( 'details', $api->slug ) ?></h3>
775
  <ul>
776
  <?php if ( ! empty( $api->version ) ) { ?>
777
- <li><strong><?php fs_echo( 'version', $api->slug ); ?>:</strong> <?php echo $api->version; ?></li>
778
  <?php
779
  }
780
  if ( ! empty( $api->author ) ) {
781
  ?>
782
  <li>
783
- <strong><?php fs_echo( 'author:', $api->slug ); ?></strong> <?php echo links_add_target( $api->author, '_blank' ); ?>
784
  </li>
785
  <?php
786
  }
787
  if ( ! empty( $api->last_updated ) ) {
788
  ?>
789
- <li><strong><?php fs_echo( 'last-updated:', $api->slug ); ?></strong> <span
790
  title="<?php echo $api->last_updated; ?>">
791
- <?php printf( fs_text( 'x-ago', $api->slug ), human_time_diff( strtotime( $api->last_updated ) ) ); ?>
 
 
 
 
792
  </span></li>
793
  <?php
794
  }
795
  if ( ! empty( $api->requires ) ) {
796
  ?>
797
  <li>
798
- <strong><?php fs_echo( 'requires-wordpress-version:', $api->slug ); ?></strong> <?php printf( fs_text( 'x-or-higher', $api->slug ), $api->requires ); ?>
799
  </li>
800
  <?php
801
  }
802
  if ( ! empty( $api->tested ) ) {
803
  ?>
804
- <li><strong><?php fs_echo( 'compatible-up-to:', $api->slug ); ?></strong> <?php echo $api->tested; ?>
805
  </li>
806
  <?php
807
  }
808
  if ( ! empty( $api->downloaded ) ) {
809
  ?>
810
  <li>
811
- <strong><?php fs_echo( 'downloaded:', $api->slug ); ?></strong> <?php printf(
812
- fs_text( ( 1 == $api->downloaded ) ? 'x-time' : 'x-times', $api->slug ),
 
 
 
 
 
813
  number_format_i18n( $api->downloaded )
814
- ); ?>
815
  </li>
816
  <?php
817
  }
818
  if ( ! empty( $api->slug ) && empty( $api->external ) ) {
819
  ?>
820
  <li><a target="_blank"
821
- href="https://wordpress.org/plugins/<?php echo $api->slug; ?>/"><?php fs_echo( 'wp-org-plugin-page', $api->slug ); ?> &#187;</a>
822
  </li>
823
  <?php
824
  }
825
  if ( ! empty( $api->homepage ) ) {
826
  ?>
827
  <li><a target="_blank"
828
- href="<?php echo esc_url( $api->homepage ); ?>"><?php fs_echo( 'plugin-homepage', $api->slug ); ?> &#187;</a>
829
  </li>
830
  <?php
831
  }
832
  if ( ! empty( $api->donate_link ) && empty( $api->contributors ) ) {
833
  ?>
834
  <li><a target="_blank"
835
- href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_echo( 'donate-to-plugin', $api->slug ); ?> &#187;</a>
836
  </li>
837
  <?php } ?>
838
  </ul>
839
  </div>
840
  <?php if ( ! empty( $api->rating ) ) { ?>
841
- <h3><?php fs_echo( 'average-rating', $api->slug ); ?></h3>
842
  <?php wp_star_rating( array(
843
  'rating' => $api->rating,
844
  'type' => 'percent',
845
  'number' => $api->num_ratings
846
  ) ); ?>
847
- <small>(<?php printf(
848
- fs_text( 'based-on-x', $api->slug ),
849
  sprintf(
850
- fs_text( ( 1 == $api->num_ratings ) ? 'x-rating' : 'x-ratings', $api->slug ),
 
 
 
 
 
851
  number_format_i18n( $api->num_ratings )
852
- ) ); ?>)</small>
853
  <?php
854
  }
855
 
@@ -858,7 +889,12 @@
858
  // Avoid div-by-zero.
859
  $_rating = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0;
860
  $stars_label = sprintf(
861
- fs_text( ( 1 == $key ) ? 'x-star' : 'x-stars', $api->slug ),
 
 
 
 
 
862
  number_format_i18n( $key )
863
  );
864
  ?>
@@ -866,7 +902,11 @@
866
  <span class="counter-label"><a
867
  href="https://wordpress.org/support/view/plugin-reviews/<?php echo $api->slug; ?>?filter=<?php echo $key; ?>"
868
  target="_blank"
869
- title="<?php echo esc_attr( sprintf( fs_text('click-to-reviews', $api->slug), $stars_label) ) ?>"><?php echo $stars_label ?></a></span>
 
 
 
 
870
  <span class="counter-back">
871
  <span class="counter-bar" style="width: <?php echo 92 * $_rating; ?>px;"></span>
872
  </span>
@@ -877,7 +917,7 @@
877
  }
878
  if ( ! empty( $api->contributors ) ) {
879
  ?>
880
- <h3><?php fs_echo( 'contributors', $api->slug ); ?></h3>
881
  <ul class="contributors">
882
  <?php
883
  foreach ( (array) $api->contributors as $contrib_username => $contrib_profile ) {
@@ -898,16 +938,16 @@
898
  </ul>
899
  <?php if ( ! empty( $api->donate_link ) ) { ?>
900
  <a target="_blank"
901
- href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_echo( 'donate-to-plugin', $api->slug ) ?> &#187;</a>
902
  <?php } ?>
903
  <?php } ?>
904
  </div>
905
  <div id="section-holder" class="wrap">
906
  <?php
907
  if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) {
908
- echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text( 'warning:', $api->slug ) . '</strong> ' . fs_text( 'not-tested-warning', $api->slug ) . '</p></div>';
909
  } else if ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) {
910
- echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text( 'warning:', $api->slug ) . '</strong> ' . fs_text( 'not-compatible-warning', $api->slug ) . '</p></div>';
911
  }
912
 
913
  foreach ( (array) $api->sections as $section_name => $content ) {
@@ -925,7 +965,9 @@
925
  $missing_notice = array(
926
  'type' => 'error',
927
  'id' => md5( microtime() ),
928
- 'message' => fs_text( ( $api->is_paid ? 'paid-addon-not-deployed' : 'free-addon-not-deployed' ), $api->slug ),
 
 
929
  );
930
  fs_require_template( 'admin-notice.php', $missing_notice );
931
  }
325
  $plan->has_trial()
326
  ) . '" target="_parent">' .
327
  ( ! $plan->has_trial() ?
328
+ fs_text_x_inline( 'Purchase', 'verb', 'purchase', $api->slug ) :
329
+ sprintf(
330
+ /* translators: %s: N-days trial */
331
+ fs_text_inline( 'Start my free %s', 'start-free-x', $api->slug ),
332
+ $this->get_trial_period( $plan )
333
+ )
334
  ) .
335
  '</a>';
336
 
352
  * plugin is wordpress.org compliant. Therefore, require a download
353
  * since installing external plugins is not allowed by the wp.org guidelines.
354
  */
355
+ return ' <a class="button button-primary right" href="' . esc_url( $api->download_link ) . '" target="_blank">' . fs_esc_html_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $api->slug ) . '</a>';
356
  } else {
357
  if ( $status['url'] ) {
358
+ return '<a class="button button-primary right" href="' . $status['url'] . '" target="_parent">' . fs_esc_html_inline( 'Install Now', 'install-now', $api->slug ) . '</a>';
359
  }
360
  }
361
  break;
362
  case 'update_available':
363
  if ( $status['url'] ) {
364
+ return '<a class="button button-primary right" href="' . $status['url'] . '" target="_parent">' . fs_text_inline( 'Install Update Now', 'install-update-now', $api->slug ) . '</a>';
365
  }
366
  break;
367
  case 'newer_installed':
368
+ return '<a class="button button-primary right disabled">' . sprintf( fs_text_inline( 'Newer Version (%s) Installed', 'newer-installed', $api->slug ), $status['version'] ) . '</a>';
369
  break;
370
  case 'latest_installed':
371
+ return '<a class="button button-primary right disabled">' . fs_text_inline( 'Latest Version Installed', 'latest-installed', $api->slug ) . '</a>';
372
  break;
373
  }
374
 
481
  );
482
 
483
  $plugins_section_titles = array(
484
+ 'description' => fs_text_x_inline( 'Description', 'Plugin installer section title', 'description', $api->slug ),
485
+ 'installation' => fs_text_x_inline( 'Installation', 'Plugin installer section title', 'installation', $api->slug ),
486
+ 'faq' => fs_text_x_inline( 'FAQ', 'Plugin installer section title', 'faq', $api->slug ),
487
+ 'screenshots' => fs_text_inline( 'Screenshots', 'screenshots', $api->slug ),
488
+ 'changelog' => fs_text_x_inline( 'Changelog', 'Plugin installer section title', 'changelog', $api->slug ),
489
+ 'reviews' => fs_text_x_inline( 'Reviews', 'Plugin installer section title', 'reviews', $api->slug ),
490
+ 'other_notes' => fs_text_x_inline( 'Other Notes', 'Plugin installer section title', 'other-notes', $api->slug ),
491
  );
492
 
493
  // Sanitize HTML
502
  }
503
 
504
  // Add after $api->slug is ready.
505
+ $plugins_section_titles['features'] = fs_text_x_inline( 'Features & Pricing', 'Plugin installer section title', 'features-and-pricing', $api->slug );
506
 
507
  $_tab = esc_attr( $tab );
508
 
512
  $section = array_shift( $section_titles );
513
  }
514
 
515
+ iframe_header( fs_text_inline( 'Plugin Install', 'plugin-install', $api->slug ) );
516
 
517
  $_with_banner = '';
518
 
584
  <div class="fs-plan<?php if ( ! $is_multi_cycle ) {
585
  echo ' fs-single-cycle';
586
  } ?>" data-plan-id="<?php echo $plan->id ?>">
587
+ <h3 data-plan="<?php echo $plan->id ?>"><?php echo esc_html( sprintf( fs_text_x_inline( '%s Plan', 'e.g. Professional Plan', 'x-plan', $api->slug ), $plan->title ) ) ?></h3>
588
  <?php $has_annual = $first_pricing->has_annual() ?>
589
  <?php $has_monthly = $first_pricing->has_monthly() ?>
590
  <div class="nav-tab-wrapper">
609
  <a class="nav-tab" data-billing-cycle="<?php echo $cycle ?>"
610
  data-pricing="<?php echo esc_attr( json_encode( $prices ) ) ?>">
611
  <?php if ( $is_featured ) : ?>
612
+ <label>&#9733; <?php fs_esc_html_echo_x_inline( 'Best', 'e.g. the best product', 'best', $api->slug ) ?> &#9733;</label>
613
  <?php endif ?>
614
+ <?php
615
+ switch ($cycle)
616
+ {
617
+ case 'monthly':
618
+ fs_esc_html_echo_x_inline( 'Monthly', 'as every month', 'monthly', $api->slug );
619
+ break;
620
+ case 'annual':
621
+ fs_esc_html_echo_x_inline( 'Annual', 'as once a year', 'annual', $api->slug );
622
+ break;
623
+ case 'lifetime':
624
+ fs_esc_html_echo_inline( 'Lifetime', 'lifetime', $api->slug );
625
+ break;
626
+ }
627
+ ?>
628
  </a>
629
  <?php endif ?>
630
  <?php $i ++; endforeach ?>
635
  _formatBillingFrequency = function (cycle) {
636
  switch (cycle) {
637
  case 'monthly':
638
+ return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug), fs_text_x_inline( 'Monthly', 'as every month', 'monthly', $api->slug ) ) ?>';
639
  case 'annual':
640
+ return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug), fs_text_x_inline( 'Annually', 'as once a year', 'annually', $api->slug ) ) ?>';
641
  case 'lifetime':
642
+ return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug), fs_text_x_inline( 'Once', 'as once a year', 'once', $api->slug ) ) ?>';
643
  }
644
  },
645
  _formatLicensesTitle = function (pricing) {
646
  switch (pricing.licenses) {
647
  case 1:
648
+ return '<?php fs_esc_attr_echo_inline( 'Single Site License', 'license-single-site', $api->slug ) ?>';
649
  case null:
650
+ return '<?php fs_esc_attr_echo_inline( 'Unlimited Licenses', 'license-unlimited', $api->slug ) ?>';
651
  default:
652
+ return '<?php fs_esc_attr_echo_inline( 'Up to %s Sites', 'license-x-sites', $api->slug ) ?>'.replace('%s', pricing.licenses);
653
  }
654
  },
655
  _formatPrice = function (pricing, cycle, multipleLicenses) {
659
  var priceCycle;
660
  switch (cycle) {
661
  case 'monthly':
662
+ priceCycle = ' / <?php fs_echo_x_inline( 'mo', 'as monthly period', 'mo', $api->slug ) ?>';
663
  break;
664
  case 'lifetime':
665
  priceCycle = '';
666
  break;
667
  case 'annual':
668
  default:
669
+ priceCycle = ' / <?php fs_echo_x_inline('year', 'as annual period', 'year', $api->slug) ?>';
670
  break;
671
  }
672
 
718
 
719
  // Render licenses prices.
720
  if (1 == pricing.length) {
721
+ html = '<li><label><?php echo fs_esc_attr_x_inline( 'Price', 'noun', 'price', $api->slug ) ?>: ' + _formatPrice(pricing[0], billingCycle, false) + '</label></li>';
722
  } else {
723
  for (var i = 0; i < pricing.length; i++) {
724
  html += '<li><label><input name="pricing-<?php echo $plan->id ?>" type="radio" value="' + pricing[i].id + '">' + _formatPrice(pricing[i], billingCycle) + '</label></li>';
764
  <?php $annual_discount = ( $has_annual && $has_monthly ) ? $plan->pricing[0]->annual_discount_percentage() : 0 ?>
765
  <?php if ( $annual_discount > 0 ) : ?>
766
  <span
767
+ class="fs-annual-discount"><?php printf( fs_esc_html_x_inline( 'Save %s', 'as a discount of $5 or 10%', 'save-x', $api->slug ), $annual_discount . '%' ) ?></span>
768
  <?php endif ?>
769
  <ul class="fs-licenses">
770
  </ul>
774
  <?php $trial_period = $this->get_trial_period( $plan ) ?>
775
  <ul class="fs-trial-terms">
776
  <li>
777
+ <i class="dashicons dashicons-yes"></i><?php echo esc_html( sprintf( fs_text_inline( 'No commitment for %s - cancel anytime', 'no-commitment-x', $api->slug ), $trial_period ) ) ?>
778
  </li>
779
  <li>
780
+ <i class="dashicons dashicons-yes"></i><?php printf( esc_html( fs_text_inline( 'After your free %s, pay as little as %s', 'after-x-pay-as-little-y', $api->slug ) ), $trial_period, '<var class="fs-price">' . $this->get_price_tag( $plan, $plan->pricing[0] ) . '</var>' ) ?>
781
  </li>
782
  </ul>
783
  <?php endif ?>
788
  <?php endif ?>
789
  <?php endif ?>
790
  <div>
791
+ <h3><?php fs_echo_inline( 'Details', 'details', $api->slug ) ?></h3>
792
  <ul>
793
  <?php if ( ! empty( $api->version ) ) { ?>
794
+ <li><strong><?php fs_esc_html_echo_x_inline( 'Version', 'product version', 'version', $api->slug ); ?>:</strong> <?php echo $api->version; ?></li>
795
  <?php
796
  }
797
  if ( ! empty( $api->author ) ) {
798
  ?>
799
  <li>
800
+ <strong><?php fs_echo_x_inline( 'Author', 'as the plugin author', 'author', $api->slug ); ?>:</strong> <?php echo links_add_target( $api->author, '_blank' ); ?>
801
  </li>
802
  <?php
803
  }
804
  if ( ! empty( $api->last_updated ) ) {
805
  ?>
806
+ <li><strong><?php fs_echo_inline( 'Last Updated', 'last-updated', $api->slug ); ?>:</strong> <span
807
  title="<?php echo $api->last_updated; ?>">
808
+ <?php echo esc_html( sprintf(
809
+ /* translators: %s: time period (e.g. "2 hours" ago) */
810
+ fs_text_x_inline( '%s ago', 'x-ago', $api->slug ),
811
+ human_time_diff( strtotime( $api->last_updated ) )
812
+ ) ) ?>
813
  </span></li>
814
  <?php
815
  }
816
  if ( ! empty( $api->requires ) ) {
817
  ?>
818
  <li>
819
+ <strong><?php fs_esc_html_echo_inline( 'Requires WordPress Version', 'requires-wordpress-version', $api->slug ) ?>:</strong> <?php echo esc_html( sprintf( fs_text_inline( '%s or higher', 'x-or-higher', $api->slug ), $api->requires ) ) ?>
820
  </li>
821
  <?php
822
  }
823
  if ( ! empty( $api->tested ) ) {
824
  ?>
825
+ <li><strong><?php fs_esc_html_echo_inline( 'Compatible up to', 'compatible-up-to', $api->slug ); ?>:</strong> <?php echo $api->tested; ?>
826
  </li>
827
  <?php
828
  }
829
  if ( ! empty( $api->downloaded ) ) {
830
  ?>
831
  <li>
832
+ <strong><?php fs_esc_html_echo_inline( 'Downloaded', 'downloaded', $api->slug ) ?>:</strong> <?php echo esc_html( sprintf(
833
+ ( ( 1 == $api->downloaded ) ?
834
+ /* translators: %s: 1 or One (Number of times downloaded) */
835
+ fs_text_inline( '%s time', 'x-time', $api->slug ) :
836
+ /* translators: %s: Number of times downloaded */
837
+ fs_text_inline( '%s times', 'x-times', $api->slug )
838
+ ),
839
  number_format_i18n( $api->downloaded )
840
+ ) ); ?>
841
  </li>
842
  <?php
843
  }
844
  if ( ! empty( $api->slug ) && empty( $api->external ) ) {
845
  ?>
846
  <li><a target="_blank"
847
+ href="https://wordpress.org/plugins/<?php echo $api->slug; ?>/"><?php fs_esc_html_echo_inline( 'WordPress.org Plugin Page', 'wp-org-plugin-page', $api->slug ) ?> &#187;</a>
848
  </li>
849
  <?php
850
  }
851
  if ( ! empty( $api->homepage ) ) {
852
  ?>
853
  <li><a target="_blank"
854
+ href="<?php echo esc_url( $api->homepage ); ?>"><?php fs_esc_html_echo_inline( 'Plugin Homepage', 'plugin-homepage', $api->slug ) ?> &#187;</a>
855
  </li>
856
  <?php
857
  }
858
  if ( ! empty( $api->donate_link ) && empty( $api->contributors ) ) {
859
  ?>
860
  <li><a target="_blank"
861
+ href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_esc_html_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?> &#187;</a>
862
  </li>
863
  <?php } ?>
864
  </ul>
865
  </div>
866
  <?php if ( ! empty( $api->rating ) ) { ?>
867
+ <h3><?php fs_echo_inline( 'Average Rating', 'average-rating', $api->slug ); ?></h3>
868
  <?php wp_star_rating( array(
869
  'rating' => $api->rating,
870
  'type' => 'percent',
871
  'number' => $api->num_ratings
872
  ) ); ?>
873
+ <small>(<?php echo esc_html( sprintf(
874
+ fs_text_inline( 'based on %s', 'based-on-x', $api->slug ),
875
  sprintf(
876
+ ( ( 1 == $api->num_ratings ) ?
877
+ /* translators: %s: 1 or One */
878
+ fs_text_inline( '%s rating', 'x-rating', $api->slug ) :
879
+ /* translators: %s: Number larger than 1 */
880
+ fs_text_inline( '%s ratings', 'x-ratings', $api->slug )
881
+ ),
882
  number_format_i18n( $api->num_ratings )
883
+ ) ) ) ?>)</small>
884
  <?php
885
  }
886
 
889
  // Avoid div-by-zero.
890
  $_rating = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0;
891
  $stars_label = sprintf(
892
+ ( ( 1 == $key ) ?
893
+ /* translators: %s: 1 or One */
894
+ fs_text_inline( '%s star', 'x-star', $api->slug ) :
895
+ /* translators: %s: Number larger than 1 */
896
+ fs_text_inline( '%s stars', 'x-stars', $api->slug )
897
+ ),
898
  number_format_i18n( $key )
899
  );
900
  ?>
902
  <span class="counter-label"><a
903
  href="https://wordpress.org/support/view/plugin-reviews/<?php echo $api->slug; ?>?filter=<?php echo $key; ?>"
904
  target="_blank"
905
+ title="<?php echo esc_attr( sprintf(
906
+ /* translators: %s: # of stars (e.g. 5 stars) */
907
+ fs_text_inline( 'Click to see reviews that provided a rating of %s', 'click-to-reviews', $api->slug ),
908
+ $stars_label
909
+ ) ) ?>"><?php echo $stars_label ?></a></span>
910
  <span class="counter-back">
911
  <span class="counter-bar" style="width: <?php echo 92 * $_rating; ?>px;"></span>
912
  </span>
917
  }
918
  if ( ! empty( $api->contributors ) ) {
919
  ?>
920
+ <h3><?php fs_echo_inline( 'Contributors', 'contributors', $api->slug ); ?></h3>
921
  <ul class="contributors">
922
  <?php
923
  foreach ( (array) $api->contributors as $contrib_username => $contrib_profile ) {
938
  </ul>
939
  <?php if ( ! empty( $api->donate_link ) ) { ?>
940
  <a target="_blank"
941
+ href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?> &#187;</a>
942
  <?php } ?>
943
  <?php } ?>
944
  </div>
945
  <div id="section-holder" class="wrap">
946
  <?php
947
  if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) {
948
+ echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been tested with your current version of WordPress.', 'not-tested-warning', $api->slug ) . '</p></div>';
949
  } else if ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) {
950
+ echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been marked as compatible with your version of WordPress.', 'not-compatible-warning', $api->slug ) . '</p></div>';
951
  }
952
 
953
  foreach ( (array) $api->sections as $section_name => $content ) {
965
  $missing_notice = array(
966
  'type' => 'error',
967
  'id' => md5( microtime() ),
968
+ 'message' => $api->is_paid ?
969
+ fs_text_inline( 'Paid add-on must be deployed to Freemius.', 'paid-addon-not-deployed', $api->slug ) :
970
+ fs_text_inline( 'Add-on must be deployed to WordPress.org or Freemius.', 'free-addon-not-deployed', $api->slug ),
971
  );
972
  fs_require_template( 'admin-notice.php', $missing_notice );
973
  }
freemius/includes/i18n.php CHANGED
@@ -4,6 +4,8 @@
4
  * @copyright Copyright (c) 2015, Freemius, Inc.
5
  * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
  * @since 1.1.4
 
 
7
  */
8
 
9
  if ( ! defined( 'ABSPATH' ) ) {
@@ -29,7 +31,7 @@
29
  * my_freemius->override_i18n( array(
30
  * 'opt-in-connect' => __( "Yes - I'm in!", '{your-text_domain}' ),
31
  * 'skip' => __( 'Not today', '{your-text_domain}' ),
32
- * );
33
  */
34
  global $fs_text;
35
 
4
  * @copyright Copyright (c) 2015, Freemius, Inc.
5
  * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
  * @since 1.1.4
7
+ *
8
+ * @deprecated This file is no longer in use. It's still in the project for backward compatibility.
9
  */
10
 
11
  if ( ! defined( 'ABSPATH' ) ) {
31
  * my_freemius->override_i18n( array(
32
  * 'opt-in-connect' => __( "Yes - I'm in!", '{your-text_domain}' ),
33
  * 'skip' => __( 'Not today', '{your-text_domain}' ),
34
+ * ) );
35
  */
36
  global $fs_text;
37
 
freemius/includes/l10n.php CHANGED
@@ -25,75 +25,6 @@
25
  return $fn( $text, 'freemius' );
26
  }
27
 
28
- /**
29
- * Retrieve the translation of $text and escapes it for safe use in an attribute.
30
- *
31
- * @since 1.2.1.6
32
- *
33
- * @param string $text
34
- *
35
- * @return string
36
- */
37
- function _fs_esc_attr( $text ) {
38
- // Avoid misleading Theme Check warning.
39
- $fn = 'translate';
40
- return esc_attr( $fn( $text, 'freemius' ) );
41
- }
42
-
43
- /**
44
- * Retrieve the translation of $text and escapes it for safe use in HTML output.
45
- *
46
- * @since 1.2.1.6
47
- *
48
- * @param string $text
49
- *
50
- * @return string
51
- */
52
- function _fs_esc_html( $text ) {
53
- // Avoid misleading Theme Check warning.
54
- $fn = 'translate';
55
- return esc_html( $fn( $text, 'freemius' ) );
56
- }
57
-
58
- /**
59
- * Display translated text.
60
- *
61
- * @since 1.2.0
62
- *
63
- * @param string $text
64
- */
65
- function _fs_echo( $text ) {
66
- // Avoid misleading Theme Check warning.
67
- $fn = 'translate';
68
- echo $fn( $text, 'freemius' );
69
- }
70
-
71
- /**
72
- * Display translated text that has been escaped for safe use in an attribute.
73
- *
74
- * @since 1.2.1.6
75
- *
76
- * @param string $text
77
- */
78
- function _fs_esc_attr_echo( $text ) {
79
- // Avoid misleading Theme Check warning.
80
- $fn = 'translate';
81
- echo esc_attr( $fn( $text, 'freemius' ) );
82
- }
83
-
84
- /**
85
- * Display translated text that has been escaped for safe use in HTML output.
86
- *
87
- * @since 1.2.1.6
88
- *
89
- * @param string $text
90
- */
91
- function _fs_esc_html_echo( $text ) {
92
- // Avoid misleading Theme Check warning.
93
- $fn = 'translate';
94
- echo esc_html( $fn( $text, 'freemius' ) );
95
- }
96
-
97
  /**
98
  * Retrieve translated string with gettext context.
99
  *
@@ -115,154 +46,3 @@
115
  $fn = 'translate_with_gettext_context';
116
  return $fn( $text, $context, 'freemius' );
117
  }
118
-
119
- /**
120
- * Display translated string with gettext context.
121
- *
122
- * @since 1.2.1.6
123
- *
124
- * @param string $text
125
- * @param string $context
126
- */
127
- function _fs_ex( $text, $context ) {
128
- // Avoid misleading Theme Check warning.
129
- $fn = '_x';
130
- echo $fn( $text, $context, 'freemius' );
131
- }
132
-
133
- /**
134
- * Translate string with gettext context, and escapes it for safe use in an attribute.
135
- *
136
- * @since 1.2.1.6
137
- *
138
- * @param string $text
139
- * @param string $context
140
- *
141
- * @return string
142
- */
143
- function _fs_esc_attr_x( $text, $context ) {
144
- // Avoid misleading Theme Check warning.
145
- $fn = 'translate_with_gettext_context';
146
- return esc_attr( $fn( $text, $context, 'freemius' ) );
147
- }
148
-
149
- /**
150
- * Translate string with gettext context, and escapes it for safe use in HTML output.
151
- *
152
- * @since 2.9.0
153
- *
154
- * @param string $text
155
- * @param string $context
156
- *
157
- * @return string
158
- */
159
- function _fs_esc_html_x( $text, $context ) {
160
- // Avoid misleading Theme Check warning.
161
- $fn = 'translate_with_gettext_context';
162
- return esc_html( $fn( $text, $context, 'freemius' ) );
163
- }
164
-
165
- /**
166
- * Translates and retrieves the singular or plural form based on the supplied number.
167
- *
168
- * @since 1.2.1.6
169
- *
170
- * @param string $single
171
- * @param string $plural
172
- * @param int $number
173
- *
174
- * @return string
175
- */
176
- function _fs_n( $single, $plural, $number ) {
177
- $translations = get_translations_for_domain( 'freemius' );
178
- $translation = $translations->translate_plural( $single, $plural, $number );
179
-
180
- /**
181
- * Filters the singular or plural form of a string.
182
- *
183
- * @since WP 2.2.0
184
- *
185
- * @param string $translation
186
- * @param string $single
187
- * @param string $plural
188
- * @param string $number
189
- * @param string $domain
190
- */
191
- return apply_filters( 'ngettext', $translation, $single, $plural, $number, 'freemius' );
192
- }
193
-
194
- /**
195
- * Translates and retrieves the singular or plural form based on the supplied number, with gettext context.
196
- *
197
- * @since 1.2.1.6
198
- *
199
- * @param string $single
200
- * @param string $plural
201
- * @param int $number
202
- * @param string $context
203
- *
204
- * @return string
205
- */
206
- function _fs_nx($single, $plural, $number, $context ) {
207
- $translations = get_translations_for_domain( 'freemius' );
208
- $translation = $translations->translate_plural( $single, $plural, $number, $context );
209
-
210
- /**
211
- * Filters the singular or plural form of a string with gettext context.
212
- *
213
- * @since WP 3.0
214
- *
215
- * @param string $translation
216
- * @param string $single
217
- * @param string $plural
218
- * @param string $number
219
- * @param string $context
220
- * @param string $domain
221
- */
222
- return apply_filters( 'ngettext_with_context', $translation, $single, $plural, $number, $context, 'freemius' );
223
- }
224
-
225
- /**
226
- * Registers plural strings in POT file, but does not translate them.
227
- *
228
- * Used when you want to keep structures with translatable plural
229
- * strings and use them later when the number is known.
230
- *
231
- * @since 1.2.1.6
232
- *
233
- * @param string $singular
234
- * @param string $plural
235
- *
236
- * @return array
237
- */
238
- function _fs_n_noop( $singular, $plural ) {
239
- return array(
240
- 'singular' => $singular,
241
- 'plural' => $plural,
242
- 'context' => null,
243
- 'domain' => 'freemius'
244
- );
245
- }
246
-
247
- /**
248
- * Registers plural strings with gettext context in POT file, but does not translate them.
249
- *
250
- * Used when you want to keep structures with translatable plural
251
- * strings and use them later when the number is known.
252
- *
253
- * @since 1.2.1.6
254
- *
255
- * @param string $singular
256
- * @param string $plural
257
- * @param string $context
258
- *
259
- * @return array
260
- */
261
- function _fs_nx_noop( $singular, $plural, $context ) {
262
- return array(
263
- 'singular' => $singular,
264
- 'plural' => $plural,
265
- 'context' => $context,
266
- 'domain' => 'freemius'
267
- );
268
- }
25
  return $fn( $text, 'freemius' );
26
  }
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  /**
29
  * Retrieve translated string with gettext context.
30
  *
46
  $fn = 'translate_with_gettext_context';
47
  return $fn( $text, $context, 'freemius' );
48
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
freemius/includes/managers/class-fs-admin-menu-manager.php CHANGED
@@ -1,850 +1,850 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.1.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Admin_Menu_Manager {
14
-
15
- #region Properties
16
-
17
- /**
18
- * @since 1.2.2
19
- *
20
- * @var string
21
- */
22
- protected $_module_unique_affix;
23
-
24
- /**
25
- * @since 1.2.2
26
- *
27
- * @var number
28
- */
29
- protected $_module_id;
30
-
31
- /**
32
- * @since 1.2.2
33
- *
34
- * @var string
35
- */
36
- protected $_module_type;
37
-
38
- /**
39
- * @since 1.0.6
40
- *
41
- * @var string
42
- */
43
- private $_menu_slug;
44
- /**
45
- * @since 1.1.3
46
- *
47
- * @var string
48
- */
49
- private $_parent_slug;
50
- /**
51
- * @since 1.1.3
52
- *
53
- * @var string
54
- */
55
- private $_parent_type;
56
- /**
57
- * @since 1.1.3
58
- *
59
- * @var string
60
- */
61
- private $_type;
62
- /**
63
- * @since 1.1.3
64
- *
65
- * @var bool
66
- */
67
- private $_is_top_level;
68
- /**
69
- * @since 1.1.3
70
- *
71
- * @var bool
72
- */
73
- private $_is_override_exact;
74
- /**
75
- * @since 1.1.3
76
- *
77
- * @var array<string,bool>
78
- */
79
- private $_default_submenu_items;
80
- /**
81
- * @since 1.1.3
82
- *
83
- * @var string
84
- */
85
- private $_first_time_path;
86
- /**
87
- * @since 1.2.2
88
- *
89
- * @var bool
90
- */
91
- private $_menu_exists;
92
-
93
- #endregion Properties
94
-
95
- /**
96
- * @var FS_Logger
97
- */
98
- protected $_logger;
99
-
100
- #region Singleton
101
-
102
- /**
103
- * @var FS_Admin_Menu_Manager[]
104
- */
105
- private static $_instances = array();
106
-
107
- /**
108
- * @param number $module_id
109
- * @param string $module_type
110
- * @param string $module_unique_affix
111
- *
112
- * @return FS_Admin_Menu_Manager
113
- */
114
- static function instance( $module_id, $module_type, $module_unique_affix ) {
115
- $key = 'm_' . $module_id;
116
-
117
- if ( ! isset( self::$_instances[ $key ] ) ) {
118
- self::$_instances[ $key ] = new FS_Admin_Menu_Manager( $module_id, $module_type, $module_unique_affix );
119
- }
120
-
121
- return self::$_instances[ $key ];
122
- }
123
-
124
- protected function __construct( $module_id, $module_type, $module_unique_affix ) {
125
- $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $module_id . '_admin_menu', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
126
-
127
- $this->_module_id = $module_id;
128
- $this->_module_type = $module_type;
129
- $this->_module_unique_affix = $module_unique_affix;
130
- }
131
-
132
- #endregion Singleton
133
-
134
- #region Helpers
135
-
136
- private function get_option( &$options, $key, $default = false ) {
137
- return ! empty( $options[ $key ] ) ? $options[ $key ] : $default;
138
- }
139
-
140
- private function get_bool_option( &$options, $key, $default = false ) {
141
- return isset( $options[ $key ] ) && is_bool( $options[ $key ] ) ? $options[ $key ] : $default;
142
- }
143
-
144
- #endregion Helpers
145
-
146
- /**
147
- * @param array $menu
148
- * @param bool $is_addon
149
- */
150
- function init( $menu, $is_addon = false ) {
151
- $this->_menu_exists = ( isset( $menu['slug'] ) && ! empty( $menu['slug'] ) );
152
-
153
- $this->_menu_slug = ( $this->_menu_exists ? $menu['slug'] : $this->_module_unique_affix );
154
-
155
- $this->_default_submenu_items = array();
156
- // @deprecated
157
- $this->_type = 'page';
158
- $this->_is_top_level = true;
159
- $this->_is_override_exact = false;
160
- $this->_parent_slug = false;
161
- // @deprecated
162
- $this->_parent_type = 'page';
163
-
164
- if ( ! $is_addon && isset( $menu ) ) {
165
- $this->_default_submenu_items = array(
166
- 'contact' => $this->get_bool_option( $menu, 'contact', true ),
167
- 'support' => $this->get_bool_option( $menu, 'support', true ),
168
- 'affiliation' => $this->get_bool_option( $menu, 'affiliation', true ),
169
- 'account' => $this->get_bool_option( $menu, 'account', true ),
170
- 'pricing' => $this->get_bool_option( $menu, 'pricing', true ),
171
- 'addons' => $this->get_bool_option( $menu, 'addons', true ),
172
- );
173
-
174
- // @deprecated
175
- $this->_type = $this->get_option( $menu, 'type', 'page' );
176
- $this->_is_override_exact = $this->get_bool_option( $menu, 'override_exact' );
177
-
178
- if ( isset( $menu['parent'] ) ) {
179
- $this->_parent_slug = $this->get_option( $menu['parent'], 'slug' );
180
- // @deprecated
181
- $this->_parent_type = $this->get_option( $menu['parent'], 'type', 'page' );
182
-
183
- // If parent's slug is different, then it's NOT a top level menu item.
184
- $this->_is_top_level = ( $this->_parent_slug === $this->_menu_slug );
185
- } else {
186
- /**
187
- * If no parent then top level if:
188
- * - Has custom admin menu ('page')
189
- * - CPT menu type ('cpt')
190
- */
191
- // $this->_is_top_level = in_array( $this->_type, array(
192
- // 'cpt',
193
- // 'page'
194
- // ) );
195
- }
196
-
197
- $this->_first_time_path = $this->get_option( $menu, 'first-path', false );
198
- if ( ! empty( $this->_first_time_path ) && is_string( $this->_first_time_path ) ) {
199
- $this->_first_time_path = admin_url( $this->_first_time_path, 'admin' );
200
- }
201
- }
202
- }
203
-
204
- /**
205
- * Check if top level menu.
206
- *
207
- * @author Vova Feldman (@svovaf)
208
- * @since 1.1.3
209
- *
210
- * @return bool False if submenu item.
211
- */
212
- function is_top_level() {
213
- return $this->_is_top_level;
214
- }
215
-
216
- /**
217
- * Check if the page should be override on exact URL match.
218
- *
219
- * @author Vova Feldman (@svovaf)
220
- * @since 1.1.3
221
- *
222
- * @return bool False if submenu item.
223
- */
224
- function is_override_exact() {
225
- return $this->_is_override_exact;
226
- }
227
-
228
-
229
- /**
230
- * Get the path of the page the user should be forwarded to after first activation.
231
- *
232
- * @author Vova Feldman (@svovaf)
233
- * @since 1.1.3
234
- *
235
- * @return string
236
- */
237
- function get_first_time_path() {
238
- return $this->_first_time_path;
239
- }
240
-
241
- /**
242
- * Check if plugin's menu item is part of a custom top level menu.
243
- *
244
- * @author Vova Feldman (@svovaf)
245
- * @since 1.1.3
246
- *
247
- * @return bool
248
- */
249
- function has_custom_parent() {
250
- return ! $this->_is_top_level && is_string( $this->_parent_slug );
251
- }
252
-
253
- /**
254
- * @author Leo Fajardo (@leorw)
255
- * @since 1.2.2
256
- *
257
- * @return bool
258
- */
259
- function has_menu() {
260
- return $this->_menu_exists;
261
- }
262
-
263
- /**
264
- * @author Vova Feldman (@svovaf)
265
- * @since 1.1.3
266
- *
267
- * @param string $id
268
- * @param bool $default
269
- * @param bool $ignore_menu_existence Since 1.2.2.7 If true, check if the submenu item visible even if there's no parent menu.
270
- *
271
- * @return bool
272
- */
273
- function is_submenu_item_visible( $id, $default = true, $ignore_menu_existence = false ) {
274
- if ( ! $ignore_menu_existence && ! $this->has_menu() ) {
275
- return false;
276
- }
277
-
278
- return fs_apply_filter(
279
- $this->_module_unique_affix,
280
- 'is_submenu_visible',
281
- $this->get_bool_option( $this->_default_submenu_items, $id, $default ),
282
- $id
283
- );
284
- }
285
-
286
- /**
287
- * Calculates admin settings menu slug.
288
- * If plugin's menu slug is a file (e.g. CPT), uses plugin's slug as the menu slug.
289
- *
290
- * @author Vova Feldman (@svovaf)
291
- * @since 1.1.3
292
- *
293
- * @param string $page
294
- *
295
- * @return string
296
- */
297
- function get_slug( $page = '' ) {
298
- return ( ( false === strpos( $this->_menu_slug, '.php?' ) ) ?
299
- $this->_menu_slug :
300
- $this->_module_unique_affix ) . ( empty( $page ) ? '' : ( '-' . $page ) );
301
- }
302
-
303
- /**
304
- * @author Vova Feldman (@svovaf)
305
- * @since 1.1.3
306
- *
307
- * @return string
308
- */
309
- function get_parent_slug() {
310
- return $this->_parent_slug;
311
- }
312
-
313
- /**
314
- * @author Vova Feldman (@svovaf)
315
- * @since 1.1.3
316
- *
317
- * @return string
318
- */
319
- function get_type() {
320
- return $this->_type;
321
- }
322
-
323
- /**
324
- * @author Vova Feldman (@svovaf)
325
- * @since 1.1.3
326
- *
327
- * @return bool
328
- */
329
- function is_cpt() {
330
- return ( 0 === strpos( $this->_menu_slug, 'edit.php?post_type=' ) ||
331
- // Back compatibility.
332
- 'cpt' === $this->_type
333
- );
334
- }
335
-
336
- /**
337
- * @author Vova Feldman (@svovaf)
338
- * @since 1.1.3
339
- *
340
- * @return string
341
- */
342
- function get_parent_type() {
343
- return $this->_parent_type;
344
- }
345
-
346
- /**
347
- * @author Vova Feldman (@svovaf)
348
- * @since 1.1.3
349
- *
350
- * @return string
351
- */
352
- function get_raw_slug() {
353
- return $this->_menu_slug;
354
- }
355
-
356
- /**
357
- * Get plugin's original menu slug.
358
- *
359
- * @author Vova Feldman (@svovaf)
360
- * @since 1.1.3
361
- *
362
- * @return string
363
- */
364
- function get_original_menu_slug() {
365
- if ( 'cpt' === $this->_type ) {
366
- return add_query_arg( array(
367
- 'post_type' => $this->_menu_slug
368
- ), 'edit.php' );
369
- }
370
-
371
- if ( false === strpos( $this->_menu_slug, '.php?' ) ) {
372
- return $this->_menu_slug;
373
- } else {
374
- return $this->_module_unique_affix;
375
- }
376
- }
377
-
378
- /**
379
- * @author Vova Feldman (@svovaf)
380
- * @since 1.1.3
381
- *
382
- * @return string
383
- */
384
- function get_top_level_menu_slug() {
385
- return $this->has_custom_parent() ?
386
- $this->get_parent_slug() :
387
- $this->get_raw_slug();
388
- }
389
-
390
- /**
391
- * Is user on plugin's admin activation page.
392
- *
393
- * @author Vova Feldman (@svovaf)
394
- * @since 1.0.8
395
- *
396
- * @return bool
397
- */
398
- function is_main_settings_page() {
399
- if ( $this->_menu_exists &&
400
- ( fs_is_plugin_page( $this->_menu_slug ) || fs_is_plugin_page( $this->_module_unique_affix ) )
401
- ) {
402
- /**
403
- * Module has a settings menu and the context page is the main settings page, so assume it's in
404
- * activation (doesn't really check if already opted-in/skipped or not).
405
- *
406
- * @since 1.2.2
407
- */
408
- return true;
409
- }
410
-
411
- global $pagenow;
412
- if ( ( WP_FS__MODULE_TYPE_THEME === $this->_module_type ) && Freemius::is_themes_page() ) {
413
- /**
414
- * In activation only when show_optin query string param is given.
415
- *
416
- * @since 1.2.2
417
- */
418
- return fs_request_get_bool( $this->_module_unique_affix . '_show_optin' );
419
- }
420
-
421
- return false;
422
- }
423
-
424
- #region Submenu Override
425
-
426
- /**
427
- * Override submenu's action.
428
- *
429
- * @author Vova Feldman (@svovaf)
430
- * @since 1.1.0
431
- *
432
- * @param string $parent_slug
433
- * @param string $menu_slug
434
- * @param callable $function
435
- *
436
- * @return false|string If submenu exist, will return the hook name.
437
- */
438
- function override_submenu_action( $parent_slug, $menu_slug, $function ) {
439
- global $submenu;
440
-
441
- $menu_slug = plugin_basename( $menu_slug );
442
- $parent_slug = plugin_basename( $parent_slug );
443
-
444
- if ( ! isset( $submenu[ $parent_slug ] ) ) {
445
- // Parent menu not exist.
446
- return false;
447
- }
448
-
449
- $found_submenu_item = false;
450
- foreach ( $submenu[ $parent_slug ] as $submenu_item ) {
451
- if ( $menu_slug === $submenu_item[2] ) {
452
- $found_submenu_item = $submenu_item;
453
- break;
454
- }
455
- }
456
-
457
- if ( false === $found_submenu_item ) {
458
- // Submenu item not found.
459
- return false;
460
- }
461
-
462
- // Remove current function.
463
- $hookname = get_plugin_page_hookname( $menu_slug, $parent_slug );
464
- remove_all_actions( $hookname );
465
-
466
- // Attach new action.
467
- add_action( $hookname, $function );
468
-
469
- return $hookname;
470
- }
471
-
472
- #endregion Submenu Override
473
-
474
- #region Top level menu Override
475
-
476
- /**
477
- * Find plugin's admin dashboard main menu item.
478
- *
479
- * @author Vova Feldman (@svovaf)
480
- * @since 1.0.2
481
- *
482
- * @return string[]|false
483
- */
484
- private function find_top_level_menu() {
485
- global $menu;
486
-
487
- $position = - 1;
488
- $found_menu = false;
489
-
490
- $menu_slug = $this->get_raw_slug();
491
-
492
- $hook_name = get_plugin_page_hookname( $menu_slug, '' );
493
- foreach ( $menu as $pos => $m ) {
494
- if ( $menu_slug === $m[2] ) {
495
- $position = $pos;
496
- $found_menu = $m;
497
- break;
498
- }
499
- }
500
-
501
- if ( false === $found_menu ) {
502
- return false;
503
- }
504
-
505
- return array(
506
- 'menu' => $found_menu,
507
- 'position' => $position,
508
- 'hook_name' => $hook_name
509
- );
510
- }
511
-
512
- /**
513
- * Find plugin's admin dashboard main submenu item.
514
- *
515
- * @author Vova Feldman (@svovaf)
516
- * @since 1.2.1.6
517
- *
518
- * @return array|false
519
- */
520
- private function find_main_submenu() {
521
- global $submenu;
522
-
523
- $top_level_menu_slug = $this->get_top_level_menu_slug();
524
-
525
- if ( ! isset( $submenu[ $top_level_menu_slug ] ) ) {
526
- return false;
527
- }
528
-
529
- $submenu_slug = $this->get_raw_slug();
530
-
531
- $position = - 1;
532
- $found_submenu = false;
533
-
534
- $hook_name = get_plugin_page_hookname( $submenu_slug, '' );
535
-
536
- foreach ( $submenu[ $top_level_menu_slug ] as $pos => $sub ) {
537
- if ( $submenu_slug === $sub[2] ) {
538
- $position = $pos;
539
- $found_submenu = $sub;
540
- }
541
- }
542
-
543
- if ( false === $found_submenu ) {
544
- return false;
545
- }
546
-
547
- return array(
548
- 'menu' => $found_submenu,
549
- 'parent_slug' => $top_level_menu_slug,
550
- 'position' => $position,
551
- 'hook_name' => $hook_name
552
- );
553
- }
554
-
555
- /**
556
- * Remove all sub-menu items.
557
- *
558
- * @author Vova Feldman (@svovaf)
559
- * @since 1.0.7
560
- *
561
- * @return bool If submenu with plugin's menu slug was found.
562
- */
563
- private function remove_all_submenu_items() {
564
- global $submenu;
565
-
566
- $menu_slug = $this->get_raw_slug();
567
-
568
- if ( ! isset( $submenu[ $menu_slug ] ) ) {
569
- return false;
570
- }
571
-
572
- /**
573
- * This method is NOT executed for WordPress.org themes.
574
- * Since we maintain only one version of the SDK we added this small
575
- * hack to avoid the error from Theme Check since it's a false-positive.
576
- *
577
- * @author Vova Feldman (@svovaf)
578
- * @since 1.2.2.7
579
- */
580
- $submenu_ref = &$submenu;
581
- $submenu_ref[ $menu_slug ] = array();
582
-
583
- return true;
584
- }
585
-
586
- /**
587
- *
588
- * @author Vova Feldman (@svovaf)
589
- * @since 1.0.9
590
- *
591
- * @return false|array[string]mixed
592
- */
593
- function remove_menu_item() {
594
- $this->_logger->entrance();
595
-
596
- // Find main menu item.
597
- $menu = $this->find_top_level_menu();
598
-
599
- if ( false === $menu ) {
600
- return false;
601
- }
602
-
603
- // Remove it with its actions.
604
- remove_all_actions( $menu['hook_name'] );
605
-
606
- // Remove all submenu items.
607
- $this->remove_all_submenu_items();
608
-
609
- return $menu;
610
- }
611
-
612
- /**
613
- * Get module's main admin setting page URL.
614
- *
615
- * @todo This method was only tested for wp.org compliant themes with a submenu item. Need to test for plugins with top level, submenu, and CPT top level, menu items.
616
- *
617
- * @author Vova Feldman (@svovaf)
618
- * @since 1.2.2.7
619
- *
620
- * @return string
621
- */
622
- function main_menu_url() {
623
- $this->_logger->entrance();
624
-
625
- if ( $this->_is_top_level ) {
626
- $menu = $this->find_top_level_menu();
627
- } else {
628
- $menu = $this->find_main_submenu();
629
- }
630
-
631
- $parent_slug = isset( $menu['parent_slug'] ) ?
632
- $menu['parent_slug'] :
633
- 'admin.php';
634
-
635
- return admin_url( $parent_slug . '?page=' . $menu['menu'][2] );
636
- }
637
-
638
- /**
639
- * @author Vova Feldman (@svovaf)
640
- * @since 1.1.4
641
- *
642
- * @param callable $function
643
- *
644
- * @return false|array[string]mixed
645
- */
646
- function override_menu_item( $function ) {
647
- $found_menu = $this->remove_menu_item();
648
-
649
- if ( false === $found_menu ) {
650
- return false;
651
- }
652
-
653
- if ( ! $this->is_top_level() || ! $this->is_cpt() ) {
654
- $menu_slug = plugin_basename( $this->get_slug() );
655
-
656
- $hookname = get_plugin_page_hookname( $menu_slug, '' );
657
-
658
- // Override menu action.
659
- add_action( $hookname, $function );
660
- } else {
661
- global $menu;
662
-
663
- // Remove original CPT menu.
664
- unset( $menu[ $found_menu['position'] ] );
665
-
666
- // Create new top-level menu action.
667
- $hookname = self::add_page(
668
- $found_menu['menu'][3],
669
- $found_menu['menu'][0],
670
- 'manage_options',
671
- $this->get_slug(),
672
- $function,
673
- $found_menu['menu'][6],
674
- $found_menu['position']
675
- );
676
- }
677
-
678
- return $hookname;
679
- }
680
-
681
- /**
682
- * Adds a counter to the module's top level menu item.
683
- *
684
- * @author Vova Feldman (@svovaf)
685
- * @since 1.2.1.5
686
- *
687
- * @param int $counter
688
- * @param string $class
689
- */
690
- function add_counter_to_menu_item( $counter = 1, $class = '' ) {
691
- global $menu, $submenu;
692
-
693
- $mask = '%s <span class="update-plugins %s count-%3$s" aria-hidden="true"><span>%3$s<span class="screen-reader-text">%3$s notifications</span></span></span>';
694
-
695
- /**
696
- * This method is NOT executed for WordPress.org themes.
697
- * Since we maintain only one version of the SDK we added this small
698
- * hack to avoid the error from Theme Check since it's a false-positive.
699
- *
700
- * @author Vova Feldman (@svovaf)
701
- * @since 1.2.2.7
702
- */
703
- $menu_ref = &$menu;
704
- $submenu_ref = &$submenu;
705
-
706
- if ( $this->_is_top_level ) {
707
- // Find main menu item.
708
- $found_menu = $this->find_top_level_menu();
709
-
710
- if ( false !== $found_menu ) {
711
- // Override menu label.
712
- $menu_ref[ $found_menu['position'] ][0] = sprintf(
713
- $mask,
714
- $found_menu['menu'][0],
715
- $class,
716
- $counter
717
- );
718
- }
719
- } else {
720
- $found_submenu = $this->find_main_submenu();
721
-
722
- if ( false !== $found_submenu ) {
723
- // Override menu label.
724
- $submenu_ref[ $found_submenu['parent_slug'] ][ $found_submenu['position'] ][0] = sprintf(
725
- $mask,
726
- $found_submenu['menu'][0],
727
- $class,
728
- $counter
729
- );
730
- }
731
- }
732
- }
733
-
734
- #endregion Top level menu Override
735
-
736
- /**
737
- * Add a top-level menu page.
738
- *
739
- * Note for WordPress.org Theme/Plugin reviewer:
740
- *
741
- * This is a replication of `add_menu_page()` to avoid Theme Check warning.
742
- *
743
- * Why?
744
- * ====
745
- * Freemius is an SDK for plugin and theme developers. Since the core
746
- * of the SDK is relevant both for plugins and themes, for obvious reasons,
747
- * we only develop and maintain one code base.
748
- *
749
- * This method will not run for wp.org themes (only plugins) since theme
750
- * admin settings/options are now only allowed in the customizer.
751
- *
752
- * If you have any questions or need clarifications, please don't hesitate
753
- * pinging me on slack, my username is @svovaf.
754
- *
755
- * @author Vova Feldman (@svovaf)
756
- * @since 1.2.2
757
- *
758
- * @param string $page_title The text to be displayed in the title tags of the page when the menu is
759
- * selected.
760
- * @param string $menu_title The text to be used for the menu.
761
- * @param string $capability The capability required for this menu to be displayed to the user.
762
- * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu).
763
- * @param callable|string $function The function to be called to output the content for this page.
764
- * @param string $icon_url The URL to the icon to be used for this menu.
765
- * * Pass a base64-encoded SVG using a data URI, which will be colored to
766
- * match the color scheme. This should begin with
767
- * 'data:image/svg+xml;base64,'.
768
- * * Pass the name of a Dashicons helper class to use a font icon,
769
- * e.g. 'dashicons-chart-pie'.
770
- * * Pass 'none' to leave div.wp-menu-image empty so an icon can be added
771
- * via CSS.
772
- * @param int $position The position in the menu order this one should appear.
773
- *
774
- * @return string The resulting page's hook_suffix.
775
- */
776
- static function add_page(
777
- $page_title,
778
- $menu_title,
779
- $capability,
780
- $menu_slug,
781
- $function = '',
782
- $icon_url = '',
783
- $position = null
784
- ) {
785
- $fn = 'add_menu' . '_page';
786
-
787
- return $fn(
788
- $page_title,
789
- $menu_title,
790
- $capability,
791
- $menu_slug,
792
- $function,
793
- $icon_url,
794
- $position
795
- );
796
- }
797
-
798
- /**
799
- * Add a submenu page.
800
- *
801
- * Note for WordPress.org Theme/Plugin reviewer:
802
- *
803
- * This is a replication of `add_submenu_page()` to avoid Theme Check warning.
804
- *
805
- * Why?
806
- * ====
807
- * Freemius is an SDK for plugin and theme developers. Since the core
808
- * of the SDK is relevant both for plugins and themes, for obvious reasons,
809
- * we only develop and maintain one code base.
810
- *
811
- * This method will not run for wp.org themes (only plugins) since theme
812
- * admin settings/options are now only allowed in the customizer.
813
- *
814
- * If you have any questions or need clarifications, please don't hesitate
815
- * pinging me on slack, my username is @svovaf.
816
- *
817
- * @author Vova Feldman (@svovaf)
818
- * @since 1.2.2
819
- *
820
- * @param string $parent_slug The slug name for the parent menu (or the file name of a standard
821
- * WordPress admin page).
822
- * @param string $page_title The text to be displayed in the title tags of the page when the menu is
823
- * selected.
824
- * @param string $menu_title The text to be used for the menu.
825
- * @param string $capability The capability required for this menu to be displayed to the user.
826
- * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu).
827
- * @param callable|string $function The function to be called to output the content for this page.
828
- *
829
- * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability
830
- * required.
831
- */
832
- static function add_subpage(
833
- $parent_slug,
834
- $page_title,
835
- $menu_title,
836
- $capability,
837
- $menu_slug,
838
- $function = ''
839
- ) {
840
- $fn = 'add_submenu' . '_page';
841
-
842
- return $fn( $parent_slug,
843
- $page_title,
844
- $menu_title,
845
- $capability,
846
- $menu_slug,
847
- $function
848
- );
849
- }
850
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.1.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Admin_Menu_Manager {
14
+
15
+ #region Properties
16
+
17
+ /**
18
+ * @since 1.2.2
19
+ *
20
+ * @var string
21
+ */
22
+ protected $_module_unique_affix;
23
+
24
+ /**
25
+ * @since 1.2.2
26
+ *
27
+ * @var number
28
+ */
29
+ protected $_module_id;
30
+
31
+ /**
32
+ * @since 1.2.2
33
+ *
34
+ * @var string
35
+ */
36
+ protected $_module_type;
37
+
38
+ /**
39
+ * @since 1.0.6
40
+ *
41
+ * @var string
42
+ */
43
+ private $_menu_slug;
44
+ /**
45
+ * @since 1.1.3
46
+ *
47
+ * @var string
48
+ */
49
+ private $_parent_slug;
50
+ /**
51
+ * @since 1.1.3
52
+ *
53
+ * @var string
54
+ */
55
+ private $_parent_type;
56
+ /**
57
+ * @since 1.1.3
58
+ *
59
+ * @var string
60
+ */
61
+ private $_type;
62
+ /**
63
+ * @since 1.1.3
64
+ *
65
+ * @var bool
66
+ */
67
+ private $_is_top_level;
68
+ /**
69
+ * @since 1.1.3
70
+ *
71
+ * @var bool
72
+ */
73
+ private $_is_override_exact;
74
+ /**
75
+ * @since 1.1.3
76
+ *
77
+ * @var array<string,bool>
78
+ */
79
+ private $_default_submenu_items;
80
+ /**
81
+ * @since 1.1.3
82
+ *
83
+ * @var string
84
+ */
85
+ private $_first_time_path;
86
+ /**
87
+ * @since 1.2.2
88
+ *
89
+ * @var bool
90
+ */
91
+ private $_menu_exists;
92
+
93
+ #endregion Properties
94
+
95
+ /**
96
+ * @var FS_Logger
97
+ */
98
+ protected $_logger;
99
+
100
+ #region Singleton
101
+
102
+ /**
103
+ * @var FS_Admin_Menu_Manager[]
104
+ */
105
+ private static $_instances = array();
106
+
107
+ /**
108
+ * @param number $module_id
109
+ * @param string $module_type
110
+ * @param string $module_unique_affix
111
+ *
112
+ * @return FS_Admin_Menu_Manager
113
+ */
114
+ static function instance( $module_id, $module_type, $module_unique_affix ) {
115
+ $key = 'm_' . $module_id;
116
+
117
+ if ( ! isset( self::$_instances[ $key ] ) ) {
118
+ self::$_instances[ $key ] = new FS_Admin_Menu_Manager( $module_id, $module_type, $module_unique_affix );
119
+ }
120
+
121
+ return self::$_instances[ $key ];
122
+ }
123
+
124
+ protected function __construct( $module_id, $module_type, $module_unique_affix ) {
125
+ $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $module_id . '_admin_menu', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
126
+
127
+ $this->_module_id = $module_id;
128
+ $this->_module_type = $module_type;
129
+ $this->_module_unique_affix = $module_unique_affix;
130
+ }
131
+
132
+ #endregion Singleton
133
+
134
+ #region Helpers
135
+
136
+ private function get_option( &$options, $key, $default = false ) {
137
+ return ! empty( $options[ $key ] ) ? $options[ $key ] : $default;
138
+ }
139
+
140
+ private function get_bool_option( &$options, $key, $default = false ) {
141
+ return isset( $options[ $key ] ) && is_bool( $options[ $key ] ) ? $options[ $key ] : $default;
142
+ }
143
+
144
+ #endregion Helpers
145
+
146
+ /**
147
+ * @param array $menu
148
+ * @param bool $is_addon
149
+ */
150
+ function init( $menu, $is_addon = false ) {
151
+ $this->_menu_exists = ( isset( $menu['slug'] ) && ! empty( $menu['slug'] ) );
152
+
153
+ $this->_menu_slug = ( $this->_menu_exists ? $menu['slug'] : $this->_module_unique_affix );
154
+
155
+ $this->_default_submenu_items = array();
156
+ // @deprecated
157
+ $this->_type = 'page';
158
+ $this->_is_top_level = true;
159
+ $this->_is_override_exact = false;
160
+ $this->_parent_slug = false;
161
+ // @deprecated
162
+ $this->_parent_type = 'page';
163
+
164
+ if ( ! $is_addon && isset( $menu ) ) {
165
+ $this->_default_submenu_items = array(
166
+ 'contact' => $this->get_bool_option( $menu, 'contact', true ),
167
+ 'support' => $this->get_bool_option( $menu, 'support', true ),
168
+ 'affiliation' => $this->get_bool_option( $menu, 'affiliation', true ),
169
+ 'account' => $this->get_bool_option( $menu, 'account', true ),
170
+ 'pricing' => $this->get_bool_option( $menu, 'pricing', true ),
171
+ 'addons' => $this->get_bool_option( $menu, 'addons', true ),
172
+ );
173
+
174
+ // @deprecated
175
+ $this->_type = $this->get_option( $menu, 'type', 'page' );
176
+ $this->_is_override_exact = $this->get_bool_option( $menu, 'override_exact' );
177
+
178
+ if ( isset( $menu['parent'] ) ) {
179
+ $this->_parent_slug = $this->get_option( $menu['parent'], 'slug' );
180
+ // @deprecated
181
+ $this->_parent_type = $this->get_option( $menu['parent'], 'type', 'page' );
182
+
183
+ // If parent's slug is different, then it's NOT a top level menu item.
184
+ $this->_is_top_level = ( $this->_parent_slug === $this->_menu_slug );
185
+ } else {
186
+ /**
187
+ * If no parent then top level if:
188
+ * - Has custom admin menu ('page')
189
+ * - CPT menu type ('cpt')
190
+ */
191
+ // $this->_is_top_level = in_array( $this->_type, array(
192
+ // 'cpt',
193
+ // 'page'
194
+ // ) );
195
+ }
196
+
197
+ $this->_first_time_path = $this->get_option( $menu, 'first-path', false );
198
+ if ( ! empty( $this->_first_time_path ) && is_string( $this->_first_time_path ) ) {
199
+ $this->_first_time_path = admin_url( $this->_first_time_path, 'admin' );
200
+ }
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Check if top level menu.
206
+ *
207
+ * @author Vova Feldman (@svovaf)
208
+ * @since 1.1.3
209
+ *
210
+ * @return bool False if submenu item.
211
+ */
212
+ function is_top_level() {
213
+ return $this->_is_top_level;
214
+ }
215
+
216
+ /**
217
+ * Check if the page should be override on exact URL match.
218
+ *
219
+ * @author Vova Feldman (@svovaf)
220
+ * @since 1.1.3
221
+ *
222
+ * @return bool False if submenu item.
223
+ */
224
+ function is_override_exact() {
225
+ return $this->_is_override_exact;
226
+ }
227
+
228
+
229
+ /**
230
+ * Get the path of the page the user should be forwarded to after first activation.
231
+ *
232
+ * @author Vova Feldman (@svovaf)
233
+ * @since 1.1.3
234
+ *
235
+ * @return string
236
+ */
237
+ function get_first_time_path() {
238
+ return $this->_first_time_path;
239
+ }
240
+
241
+ /**
242
+ * Check if plugin's menu item is part of a custom top level menu.
243
+ *
244
+ * @author Vova Feldman (@svovaf)
245
+ * @since 1.1.3
246
+ *
247
+ * @return bool
248
+ */
249
+ function has_custom_parent() {
250
+ return ! $this->_is_top_level && is_string( $this->_parent_slug );
251
+ }
252
+
253
+ /**
254
+ * @author Leo Fajardo (@leorw)
255
+ * @since 1.2.2
256
+ *
257
+ * @return bool
258
+ */
259
+ function has_menu() {
260
+ return $this->_menu_exists;
261
+ }
262
+
263
+ /**
264
+ * @author Vova Feldman (@svovaf)
265
+ * @since 1.1.3
266
+ *
267
+ * @param string $id
268
+ * @param bool $default
269
+ * @param bool $ignore_menu_existence Since 1.2.2.7 If true, check if the submenu item visible even if there's no parent menu.
270
+ *
271
+ * @return bool
272
+ */
273
+ function is_submenu_item_visible( $id, $default = true, $ignore_menu_existence = false ) {
274
+ if ( ! $ignore_menu_existence && ! $this->has_menu() ) {
275
+ return false;
276
+ }
277
+
278
+ return fs_apply_filter(
279
+ $this->_module_unique_affix,
280
+ 'is_submenu_visible',
281
+ $this->get_bool_option( $this->_default_submenu_items, $id, $default ),
282
+ $id
283
+ );
284
+ }
285
+
286
+ /**
287
+ * Calculates admin settings menu slug.
288
+ * If plugin's menu slug is a file (e.g. CPT), uses plugin's slug as the menu slug.
289
+ *
290
+ * @author Vova Feldman (@svovaf)
291
+ * @since 1.1.3
292
+ *
293
+ * @param string $page
294
+ *
295
+ * @return string
296
+ */
297
+ function get_slug( $page = '' ) {
298
+ return ( ( false === strpos( $this->_menu_slug, '.php?' ) ) ?
299
+ $this->_menu_slug :
300
+ $this->_module_unique_affix ) . ( empty( $page ) ? '' : ( '-' . $page ) );
301
+ }
302
+
303
+ /**
304
+ * @author Vova Feldman (@svovaf)
305
+ * @since 1.1.3
306
+ *
307
+ * @return string
308
+ */
309
+ function get_parent_slug() {
310
+ return $this->_parent_slug;
311
+ }
312
+
313
+ /**
314
+ * @author Vova Feldman (@svovaf)
315
+ * @since 1.1.3
316
+ *
317
+ * @return string
318
+ */
319
+ function get_type() {
320
+ return $this->_type;
321
+ }
322
+
323
+ /**
324
+ * @author Vova Feldman (@svovaf)
325
+ * @since 1.1.3
326
+ *
327
+ * @return bool
328
+ */
329
+ function is_cpt() {
330
+ return ( 0 === strpos( $this->_menu_slug, 'edit.php?post_type=' ) ||
331
+ // Back compatibility.
332
+ 'cpt' === $this->_type
333
+ );
334
+ }
335
+
336
+ /**
337
+ * @author Vova Feldman (@svovaf)
338
+ * @since 1.1.3
339
+ *
340
+ * @return string
341
+ */
342
+ function get_parent_type() {
343
+ return $this->_parent_type;
344
+ }
345
+
346
+ /**
347
+ * @author Vova Feldman (@svovaf)
348
+ * @since 1.1.3
349
+ *
350
+ * @return string
351
+ */
352
+ function get_raw_slug() {
353
+ return $this->_menu_slug;
354
+ }
355
+
356
+ /**
357
+ * Get plugin's original menu slug.
358
+ *
359
+ * @author Vova Feldman (@svovaf)
360
+ * @since 1.1.3
361
+ *
362
+ * @return string
363
+ */
364
+ function get_original_menu_slug() {
365
+ if ( 'cpt' === $this->_type ) {
366
+ return add_query_arg( array(
367
+ 'post_type' => $this->_menu_slug
368
+ ), 'edit.php' );
369
+ }
370
+
371
+ if ( false === strpos( $this->_menu_slug, '.php?' ) ) {
372
+ return $this->_menu_slug;
373
+ } else {
374
+ return $this->_module_unique_affix;
375
+ }
376
+ }
377
+
378
+ /**
379
+ * @author Vova Feldman (@svovaf)
380
+ * @since 1.1.3
381
+ *
382
+ * @return string
383
+ */
384
+ function get_top_level_menu_slug() {
385
+ return $this->has_custom_parent() ?
386
+ $this->get_parent_slug() :
387
+ $this->get_raw_slug();
388
+ }
389
+
390
+ /**
391
+ * Is user on plugin's admin activation page.
392
+ *
393
+ * @author Vova Feldman (@svovaf)
394
+ * @since 1.0.8
395
+ *
396
+ * @return bool
397
+ */
398
+ function is_main_settings_page() {
399
+ if ( $this->_menu_exists &&
400
+ ( fs_is_plugin_page( $this->_menu_slug ) || fs_is_plugin_page( $this->_module_unique_affix ) )
401
+ ) {
402
+ /**
403
+ * Module has a settings menu and the context page is the main settings page, so assume it's in
404
+ * activation (doesn't really check if already opted-in/skipped or not).
405
+ *
406
+ * @since 1.2.2
407
+ */
408
+ return true;
409
+ }
410
+
411
+ global $pagenow;
412
+ if ( ( WP_FS__MODULE_TYPE_THEME === $this->_module_type ) && Freemius::is_themes_page() ) {
413
+ /**
414
+ * In activation only when show_optin query string param is given.
415
+ *
416
+ * @since 1.2.2
417
+ */
418
+ return fs_request_get_bool( $this->_module_unique_affix . '_show_optin' );
419
+ }
420
+
421
+ return false;
422
+ }
423
+
424
+ #region Submenu Override
425
+
426
+ /**
427
+ * Override submenu's action.
428
+ *
429
+ * @author Vova Feldman (@svovaf)
430
+ * @since 1.1.0
431
+ *
432
+ * @param string $parent_slug
433
+ * @param string $menu_slug
434
+ * @param callable $function
435
+ *
436
+ * @return false|string If submenu exist, will return the hook name.
437
+ */
438
+ function override_submenu_action( $parent_slug, $menu_slug, $function ) {
439
+ global $submenu;
440
+
441
+ $menu_slug = plugin_basename( $menu_slug );
442
+ $parent_slug = plugin_basename( $parent_slug );
443
+
444
+ if ( ! isset( $submenu[ $parent_slug ] ) ) {
445
+ // Parent menu not exist.
446
+ return false;
447
+ }
448
+
449
+ $found_submenu_item = false;
450
+ foreach ( $submenu[ $parent_slug ] as $submenu_item ) {
451
+ if ( $menu_slug === $submenu_item[2] ) {
452
+ $found_submenu_item = $submenu_item;
453
+ break;
454
+ }
455
+ }
456
+
457
+ if ( false === $found_submenu_item ) {
458
+ // Submenu item not found.
459
+ return false;
460
+ }
461
+
462
+ // Remove current function.
463
+ $hookname = get_plugin_page_hookname( $menu_slug, $parent_slug );
464
+ remove_all_actions( $hookname );
465
+
466
+ // Attach new action.
467
+ add_action( $hookname, $function );
468
+
469
+ return $hookname;
470
+ }
471
+
472
+ #endregion Submenu Override
473
+
474
+ #region Top level menu Override
475
+
476
+ /**
477
+ * Find plugin's admin dashboard main menu item.
478
+ *
479
+ * @author Vova Feldman (@svovaf)
480
+ * @since 1.0.2
481
+ *
482
+ * @return string[]|false
483
+ */
484
+ private function find_top_level_menu() {
485
+ global $menu;
486
+
487
+ $position = - 1;
488
+ $found_menu = false;
489
+
490
+ $menu_slug = $this->get_raw_slug();
491
+
492
+ $hook_name = get_plugin_page_hookname( $menu_slug, '' );
493
+ foreach ( $menu as $pos => $m ) {
494
+ if ( $menu_slug === $m[2] ) {
495
+ $position = $pos;
496
+ $found_menu = $m;
497
+ break;
498
+ }
499
+ }
500
+
501
+ if ( false === $found_menu ) {
502
+ return false;
503
+ }
504
+
505
+ return array(
506
+ 'menu' => $found_menu,
507
+ 'position' => $position,
508
+ 'hook_name' => $hook_name
509
+ );
510
+ }
511
+
512
+ /**
513
+ * Find plugin's admin dashboard main submenu item.
514
+ *
515
+ * @author Vova Feldman (@svovaf)
516
+ * @since 1.2.1.6
517
+ *
518
+ * @return array|false
519
+ */
520
+ private function find_main_submenu() {
521
+ global $submenu;
522
+
523
+ $top_level_menu_slug = $this->get_top_level_menu_slug();
524
+
525
+ if ( ! isset( $submenu[ $top_level_menu_slug ] ) ) {
526
+ return false;
527
+ }
528
+
529
+ $submenu_slug = $this->get_raw_slug();
530
+
531
+ $position = - 1;
532
+ $found_submenu = false;
533
+
534
+ $hook_name = get_plugin_page_hookname( $submenu_slug, '' );
535
+
536
+ foreach ( $submenu[ $top_level_menu_slug ] as $pos => $sub ) {
537
+ if ( $submenu_slug === $sub[2] ) {
538
+ $position = $pos;
539
+ $found_submenu = $sub;
540
+ }
541
+ }
542
+
543
+ if ( false === $found_submenu ) {
544
+ return false;
545
+ }
546
+
547
+ return array(
548
+ 'menu' => $found_submenu,
549
+ 'parent_slug' => $top_level_menu_slug,
550
+ 'position' => $position,
551
+ 'hook_name' => $hook_name
552
+ );
553
+ }
554
+
555
+ /**
556
+ * Remove all sub-menu items.
557
+ *
558
+ * @author Vova Feldman (@svovaf)
559
+ * @since 1.0.7
560
+ *
561
+ * @return bool If submenu with plugin's menu slug was found.
562
+ */
563
+ private function remove_all_submenu_items() {
564
+ global $submenu;
565
+
566
+ $menu_slug = $this->get_raw_slug();
567
+
568
+ if ( ! isset( $submenu[ $menu_slug ] ) ) {
569
+ return false;
570
+ }
571
+
572
+ /**
573
+ * This method is NOT executed for WordPress.org themes.
574
+ * Since we maintain only one version of the SDK we added this small
575
+ * hack to avoid the error from Theme Check since it's a false-positive.
576
+ *
577
+ * @author Vova Feldman (@svovaf)
578
+ * @since 1.2.2.7
579
+ */
580
+ $submenu_ref = &$submenu;
581
+ $submenu_ref[ $menu_slug ] = array();
582
+
583
+ return true;
584
+ }
585
+
586
+ /**
587
+ *
588
+ * @author Vova Feldman (@svovaf)
589
+ * @since 1.0.9
590
+ *
591
+ * @return false|array[string]mixed
592
+ */
593
+ function remove_menu_item() {
594
+ $this->_logger->entrance();
595
+
596
+ // Find main menu item.
597
+ $menu = $this->find_top_level_menu();
598
+
599
+ if ( false === $menu ) {
600
+ return false;
601
+ }
602
+
603
+ // Remove it with its actions.
604
+ remove_all_actions( $menu['hook_name'] );
605
+
606
+ // Remove all submenu items.
607
+ $this->remove_all_submenu_items();
608
+
609
+ return $menu;
610
+ }
611
+
612
+ /**
613
+ * Get module's main admin setting page URL.
614
+ *
615
+ * @todo This method was only tested for wp.org compliant themes with a submenu item. Need to test for plugins with top level, submenu, and CPT top level, menu items.
616
+ *
617
+ * @author Vova Feldman (@svovaf)
618
+ * @since 1.2.2.7
619
+ *
620
+ * @return string
621
+ */
622
+ function main_menu_url() {
623
+ $this->_logger->entrance();
624
+
625
+ if ( $this->_is_top_level ) {
626
+ $menu = $this->find_top_level_menu();
627
+ } else {
628
+ $menu = $this->find_main_submenu();
629
+ }
630
+
631
+ $parent_slug = isset( $menu['parent_slug'] ) ?
632
+ $menu['parent_slug'] :
633
+ 'admin.php';
634
+
635
+ return admin_url( $parent_slug . '?page=' . $menu['menu'][2] );
636
+ }
637
+
638
+ /**
639
+ * @author Vova Feldman (@svovaf)
640
+ * @since 1.1.4
641
+ *
642
+ * @param callable $function
643
+ *
644
+ * @return false|array[string]mixed
645
+ */
646
+ function override_menu_item( $function ) {
647
+ $found_menu = $this->remove_menu_item();
648
+
649
+ if ( false === $found_menu ) {
650
+ return false;
651
+ }
652
+
653
+ if ( ! $this->is_top_level() || ! $this->is_cpt() ) {
654
+ $menu_slug = plugin_basename( $this->get_slug() );
655
+
656
+ $hookname = get_plugin_page_hookname( $menu_slug, '' );
657
+
658
+ // Override menu action.
659
+ add_action( $hookname, $function );
660
+ } else {
661
+ global $menu;
662
+
663
+ // Remove original CPT menu.
664
+ unset( $menu[ $found_menu['position'] ] );
665
+
666
+ // Create new top-level menu action.
667
+ $hookname = self::add_page(
668
+ $found_menu['menu'][3],
669
+ $found_menu['menu'][0],
670
+ 'manage_options',
671
+ $this->get_slug(),
672
+ $function,
673
+ $found_menu['menu'][6],
674
+ $found_menu['position']
675
+ );
676
+ }
677
+
678
+ return $hookname;
679
+ }
680
+
681
+ /**
682
+ * Adds a counter to the module's top level menu item.
683
+ *
684
+ * @author Vova Feldman (@svovaf)
685
+ * @since 1.2.1.5
686
+ *
687
+ * @param int $counter
688
+ * @param string $class
689
+ */
690
+ function add_counter_to_menu_item( $counter = 1, $class = '' ) {
691
+ global $menu, $submenu;
692
+
693
+ $mask = '%s <span class="update-plugins %s count-%3$s" aria-hidden="true"><span>%3$s<span class="screen-reader-text">%3$s notifications</span></span></span>';
694
+
695
+ /**
696
+ * This method is NOT executed for WordPress.org themes.
697
+ * Since we maintain only one version of the SDK we added this small
698
+ * hack to avoid the error from Theme Check since it's a false-positive.
699
+ *
700
+ * @author Vova Feldman (@svovaf)
701
+ * @since 1.2.2.7
702
+ */
703
+ $menu_ref = &$menu;
704
+ $submenu_ref = &$submenu;
705
+
706
+ if ( $this->_is_top_level ) {
707
+ // Find main menu item.
708
+ $found_menu = $this->find_top_level_menu();
709
+
710
+ if ( false !== $found_menu ) {
711
+ // Override menu label.
712
+ $menu_ref[ $found_menu['position'] ][0] = sprintf(
713
+ $mask,
714
+ $found_menu['menu'][0],
715
+ $class,
716
+ $counter
717
+ );
718
+ }
719
+ } else {
720
+ $found_submenu = $this->find_main_submenu();
721
+
722
+ if ( false !== $found_submenu ) {
723
+ // Override menu label.
724
+ $submenu_ref[ $found_submenu['parent_slug'] ][ $found_submenu['position'] ][0] = sprintf(
725
+ $mask,
726
+ $found_submenu['menu'][0],
727
+ $class,
728
+ $counter
729
+ );
730
+ }
731
+ }
732
+ }
733
+
734
+ #endregion Top level menu Override
735
+
736
+ /**
737
+ * Add a top-level menu page.
738
+ *
739
+ * Note for WordPress.org Theme/Plugin reviewer:
740
+ *
741
+ * This is a replication of `add_menu_page()` to avoid Theme Check warning.
742
+ *
743
+ * Why?
744
+ * ====
745
+ * Freemius is an SDK for plugin and theme developers. Since the core
746
+ * of the SDK is relevant both for plugins and themes, for obvious reasons,
747
+ * we only develop and maintain one code base.
748
+ *
749
+ * This method will not run for wp.org themes (only plugins) since theme
750
+ * admin settings/options are now only allowed in the customizer.
751
+ *
752
+ * If you have any questions or need clarifications, please don't hesitate
753
+ * pinging me on slack, my username is @svovaf.
754
+ *
755
+ * @author Vova Feldman (@svovaf)
756
+ * @since 1.2.2
757
+ *
758
+ * @param string $page_title The text to be displayed in the title tags of the page when the menu is
759
+ * selected.
760
+ * @param string $menu_title The text to be used for the menu.
761
+ * @param string $capability The capability required for this menu to be displayed to the user.
762
+ * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu).
763
+ * @param callable|string $function The function to be called to output the content for this page.
764
+ * @param string $icon_url The URL to the icon to be used for this menu.
765
+ * * Pass a base64-encoded SVG using a data URI, which will be colored to
766
+ * match the color scheme. This should begin with
767
+ * 'data:image/svg+xml;base64,'.
768
+ * * Pass the name of a Dashicons helper class to use a font icon,
769
+ * e.g. 'dashicons-chart-pie'.
770
+ * * Pass 'none' to leave div.wp-menu-image empty so an icon can be added
771
+ * via CSS.
772
+ * @param int $position The position in the menu order this one should appear.
773
+ *
774
+ * @return string The resulting page's hook_suffix.
775
+ */
776
+ static function add_page(
777
+ $page_title,
778
+ $menu_title,
779
+ $capability,
780
+ $menu_slug,
781
+ $function = '',
782
+ $icon_url = '',
783
+ $position = null
784
+ ) {
785
+ $fn = 'add_menu' . '_page';
786
+
787
+ return $fn(
788
+ $page_title,
789
+ $menu_title,
790
+ $capability,
791
+ $menu_slug,
792
+ $function,
793
+ $icon_url,
794
+ $position
795
+ );
796
+ }
797
+
798
+ /**
799
+ * Add a submenu page.
800
+ *
801
+ * Note for WordPress.org Theme/Plugin reviewer:
802
+ *
803
+ * This is a replication of `add_submenu_page()` to avoid Theme Check warning.
804
+ *
805
+ * Why?
806
+ * ====
807
+ * Freemius is an SDK for plugin and theme developers. Since the core
808
+ * of the SDK is relevant both for plugins and themes, for obvious reasons,
809
+ * we only develop and maintain one code base.
810
+ *
811
+ * This method will not run for wp.org themes (only plugins) since theme
812
+ * admin settings/options are now only allowed in the customizer.
813
+ *
814
+ * If you have any questions or need clarifications, please don't hesitate
815
+ * pinging me on slack, my username is @svovaf.
816
+ *
817
+ * @author Vova Feldman (@svovaf)
818
+ * @since 1.2.2
819
+ *
820
+ * @param string $parent_slug The slug name for the parent menu (or the file name of a standard
821
+ * WordPress admin page).
822
+ * @param string $page_title The text to be displayed in the title tags of the page when the menu is
823
+ * selected.
824
+ * @param string $menu_title The text to be used for the menu.
825
+ * @param string $capability The capability required for this menu to be displayed to the user.
826
+ * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu).
827
+ * @param callable|string $function The function to be called to output the content for this page.
828
+ *
829
+ * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability
830
+ * required.
831
+ */
832
+ static function add_subpage(
833
+ $parent_slug,
834
+ $page_title,
835
+ $menu_title,
836
+ $capability,
837
+ $menu_slug,
838
+ $function = ''
839
+ ) {
840
+ $fn = 'add_submenu' . '_page';
841
+
842
+ return $fn( $parent_slug,
843
+ $page_title,
844
+ $menu_title,
845
+ $capability,
846
+ $menu_slug,
847
+ $function
848
+ );
849
+ }
850
  }
freemius/includes/managers/class-fs-cache-manager.php CHANGED
@@ -1,281 +1,281 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.1.6
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Cache_Manager {
14
- /**
15
- * @var FS_Option_Manager
16
- */
17
- private $_options;
18
- /**
19
- * @var FS_Logger
20
- */
21
- private $_logger;
22
-
23
- /**
24
- * @var FS_Cache_Manager[]
25
- */
26
- private static $_MANAGERS = array();
27
-
28
- /**
29
- * @author Vova Feldman (@svovaf)
30
- * @since 1.1.3
31
- *
32
- * @param string $id
33
- */
34
- private function __construct( $id ) {
35
- $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_cach_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
36
-
37
- $this->_logger->entrance();
38
- $this->_logger->log( 'id = ' . $id );
39
-
40
- $this->_options = FS_Option_Manager::get_manager( $id, true );
41
- }
42
-
43
- /**
44
- * @author Vova Feldman (@svovaf)
45
- * @since 1.1.6
46
- *
47
- * @param $id
48
- *
49
- * @return FS_Cache_Manager
50
- */
51
- static function get_manager( $id ) {
52
- $id = strtolower( $id );
53
-
54
- if ( ! isset( self::$_MANAGERS[ $id ] ) ) {
55
- self::$_MANAGERS[ $id ] = new FS_Cache_Manager( $id );
56
- }
57
-
58
- return self::$_MANAGERS[ $id ];
59
- }
60
-
61
- /**
62
- * @author Vova Feldman (@svovaf)
63
- * @since 1.1.6
64
- *
65
- * @return bool
66
- */
67
- function is_empty() {
68
- $this->_logger->entrance();
69
-
70
- return $this->_options->is_empty();
71
- }
72
-
73
- /**
74
- * @author Vova Feldman (@svovaf)
75
- * @since 1.1.6
76
- */
77
- function clear() {
78
- $this->_logger->entrance();
79
-
80
- $this->_options->clear( true );
81
- }
82
-
83
- /**
84
- * Delete cache manager from DB.
85
- *
86
- * @author Vova Feldman (@svovaf)
87
- * @since 1.0.9
88
- */
89
- function delete() {
90
- $this->_options->delete();
91
- }
92
-
93
- /**
94
- * Check if there's a cached item.
95
- *
96
- * @author Vova Feldman (@svovaf)
97
- * @since 1.1.6
98
- *
99
- * @param string $key
100
- *
101
- * @return bool
102
- */
103
- function has( $key ) {
104
- $cache_entry = $this->_options->get_option( $key, false );
105
-
106
- return ( is_object( $cache_entry ) &&
107
- isset( $cache_entry->timestamp ) &&
108
- is_numeric( $cache_entry->timestamp )
109
- );
110
- }
111
-
112
- /**
113
- * Check if there's a valid cached item.
114
- *
115
- * @author Vova Feldman (@svovaf)
116
- * @since 1.1.6
117
- *
118
- * @param string $key
119
- * @param null|int $expiration Since 1.2.2.7
120
- *
121
- * @return bool
122
- */
123
- function has_valid( $key, $expiration = null ) {
124
- $cache_entry = $this->_options->get_option( $key, false );
125
-
126
- $is_valid = ( is_object( $cache_entry ) &&
127
- isset( $cache_entry->timestamp ) &&
128
- is_numeric( $cache_entry->timestamp ) &&
129
- $cache_entry->timestamp > WP_FS__SCRIPT_START_TIME
130
- );
131
-
132
- if ( $is_valid &&
133
- is_numeric( $expiration ) &&
134
- isset( $cache_entry->created ) &&
135
- is_numeric( $cache_entry->created ) &&
136
- $cache_entry->created + $expiration < WP_FS__SCRIPT_START_TIME
137
- ) {
138
- /**
139
- * Even if the cache is still valid, since we are checking for validity
140
- * with an explicit expiration period, if the period has past, return
141
- * `false` as if the cache is invalid.
142
- *
143
- * @since 1.2.2.7
144
- */
145
- $is_valid = false;
146
- }
147
-
148
- return $is_valid;
149
- }
150
-
151
- /**
152
- * @author Vova Feldman (@svovaf)
153
- * @since 1.1.6
154
- *
155
- * @param string $key
156
- * @param mixed $default
157
- *
158
- * @return mixed
159
- */
160
- function get( $key, $default = null ) {
161
- $this->_logger->entrance( 'key = ' . $key );
162
-
163
- $cache_entry = $this->_options->get_option( $key, false );
164
-
165
- if ( is_object( $cache_entry ) &&
166
- isset( $cache_entry->timestamp ) &&
167
- is_numeric( $cache_entry->timestamp )
168
- ) {
169
- return $cache_entry->result;
170
- }
171
-
172
- return is_object($default) ? clone $default : $default;
173
- }
174
-
175
- /**
176
- * @author Vova Feldman (@svovaf)
177
- * @since 1.1.6
178
- *
179
- * @param string $key
180
- * @param mixed $default
181
- *
182
- * @return mixed
183
- */
184
- function get_valid( $key, $default = null ) {
185
- $this->_logger->entrance( 'key = ' . $key );
186
-
187
- $cache_entry = $this->_options->get_option( $key, false );
188
-
189
- if ( is_object( $cache_entry ) &&
190
- isset( $cache_entry->timestamp ) &&
191
- is_numeric( $cache_entry->timestamp ) &&
192
- $cache_entry->timestamp > WP_FS__SCRIPT_START_TIME
193
- ) {
194
- return $cache_entry->result;
195
- }
196
-
197
- return is_object($default) ? clone $default : $default;
198
- }
199
-
200
- /**
201
- * @author Vova Feldman (@svovaf)
202
- * @since 1.1.6
203
- *
204
- * @param string $key
205
- * @param mixed $value
206
- * @param int $expiration
207
- */
208
- function set( $key, $value, $expiration = WP_FS__TIME_24_HOURS_IN_SEC ) {
209
- $this->_logger->entrance( 'key = ' . $key );
210
-
211
- $cache_entry = new stdClass();
212
-
213
- $cache_entry->result = $value;
214
- $cache_entry->created = WP_FS__SCRIPT_START_TIME;
215
- $cache_entry->timestamp = WP_FS__SCRIPT_START_TIME + $expiration;
216
- $this->_options->set_option( $key, $cache_entry, true );
217
- }
218
-
219
- /**
220
- * Get cached record expiration, or false if not cached or expired.
221
- *
222
- * @author Vova Feldman (@svovaf)
223
- * @since 1.1.7.3
224
- *
225
- * @param string $key
226
- *
227
- * @return bool|int
228
- */
229
- function get_record_expiration( $key ) {
230
- $this->_logger->entrance( 'key = ' . $key );
231
-
232
- $cache_entry = $this->_options->get_option( $key, false );
233
-
234
- if ( is_object( $cache_entry ) &&
235
- isset( $cache_entry->timestamp ) &&
236
- is_numeric( $cache_entry->timestamp ) &&
237
- $cache_entry->timestamp > WP_FS__SCRIPT_START_TIME
238
- ) {
239
- return $cache_entry->timestamp;
240
- }
241
-
242
- return false;
243
- }
244
-
245
- /**
246
- * Purge cached item.
247
- *
248
- * @author Vova Feldman (@svovaf)
249
- * @since 1.1.6
250
- *
251
- * @param string $key
252
- */
253
- function purge( $key ) {
254
- $this->_logger->entrance( 'key = ' . $key );
255
-
256
- $this->_options->unset_option( $key, true );
257
- }
258
-
259
- /**
260
- * Set cached item as expired.
261
- *
262
- * @author Vova Feldman (@svovaf)
263
- * @since 1.2.2.7
264
- *
265
- * @param string $key
266
- */
267
- function expire( $key ) {
268
- $this->_logger->entrance( 'key = ' . $key );
269
-
270
- $cache_entry = $this->_options->get_option( $key, false );
271
-
272
- if ( is_object( $cache_entry ) &&
273
- isset( $cache_entry->timestamp ) &&
274
- is_numeric( $cache_entry->timestamp ))
275
- {
276
- // Set to expired.
277
- $cache_entry->timestamp = WP_FS__SCRIPT_START_TIME;
278
- $this->_options->set_option( $key, $cache_entry, true );
279
- }
280
- }
281
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.1.6
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Cache_Manager {
14
+ /**
15
+ * @var FS_Option_Manager
16
+ */
17
+ private $_options;
18
+ /**
19
+ * @var FS_Logger
20
+ */
21
+ private $_logger;
22
+
23
+ /**
24
+ * @var FS_Cache_Manager[]
25
+ */
26
+ private static $_MANAGERS = array();
27
+
28
+ /**
29
+ * @author Vova Feldman (@svovaf)
30
+ * @since 1.1.3
31
+ *
32
+ * @param string $id
33
+ */
34
+ private function __construct( $id ) {
35
+ $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_cach_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
36
+
37
+ $this->_logger->entrance();
38
+ $this->_logger->log( 'id = ' . $id );
39
+
40
+ $this->_options = FS_Option_Manager::get_manager( $id, true );
41
+ }
42
+
43
+ /**
44
+ * @author Vova Feldman (@svovaf)
45
+ * @since 1.1.6
46
+ *
47
+ * @param $id
48
+ *
49
+ * @return FS_Cache_Manager
50
+ */
51
+ static function get_manager( $id ) {
52
+ $id = strtolower( $id );
53
+
54
+ if ( ! isset( self::$_MANAGERS[ $id ] ) ) {
55
+ self::$_MANAGERS[ $id ] = new FS_Cache_Manager( $id );
56
+ }
57
+
58
+ return self::$_MANAGERS[ $id ];
59
+ }
60
+
61
+ /**
62
+ * @author Vova Feldman (@svovaf)
63
+ * @since 1.1.6
64
+ *
65
+ * @return bool
66
+ */
67
+ function is_empty() {
68
+ $this->_logger->entrance();
69
+
70
+ return $this->_options->is_empty();
71
+ }
72
+
73
+ /**
74
+ * @author Vova Feldman (@svovaf)
75
+ * @since 1.1.6
76
+ */
77
+ function clear() {
78
+ $this->_logger->entrance();
79
+
80
+ $this->_options->clear( true );
81
+ }
82
+
83
+ /**
84
+ * Delete cache manager from DB.
85
+ *
86
+ * @author Vova Feldman (@svovaf)
87
+ * @since 1.0.9
88
+ */
89
+ function delete() {
90
+ $this->_options->delete();
91
+ }
92
+
93
+ /**
94
+ * Check if there's a cached item.
95
+ *
96
+ * @author Vova Feldman (@svovaf)
97
+ * @since 1.1.6
98
+ *
99
+ * @param string $key
100
+ *
101
+ * @return bool
102
+ */
103
+ function has( $key ) {
104
+ $cache_entry = $this->_options->get_option( $key, false );
105
+
106
+ return ( is_object( $cache_entry ) &&
107
+ isset( $cache_entry->timestamp ) &&
108
+ is_numeric( $cache_entry->timestamp )
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Check if there's a valid cached item.
114
+ *
115
+ * @author Vova Feldman (@svovaf)
116
+ * @since 1.1.6
117
+ *
118
+ * @param string $key
119
+ * @param null|int $expiration Since 1.2.2.7
120
+ *
121
+ * @return bool
122
+ */
123
+ function has_valid( $key, $expiration = null ) {
124
+ $cache_entry = $this->_options->get_option( $key, false );
125
+
126
+ $is_valid = ( is_object( $cache_entry ) &&
127
+ isset( $cache_entry->timestamp ) &&
128
+ is_numeric( $cache_entry->timestamp ) &&
129
+ $cache_entry->timestamp > WP_FS__SCRIPT_START_TIME
130
+ );
131
+
132
+ if ( $is_valid &&
133
+ is_numeric( $expiration ) &&
134
+ isset( $cache_entry->created ) &&
135
+ is_numeric( $cache_entry->created ) &&
136
+ $cache_entry->created + $expiration < WP_FS__SCRIPT_START_TIME
137
+ ) {
138
+ /**
139
+ * Even if the cache is still valid, since we are checking for validity
140
+ * with an explicit expiration period, if the period has past, return
141
+ * `false` as if the cache is invalid.
142
+ *
143
+ * @since 1.2.2.7
144
+ */
145
+ $is_valid = false;
146
+ }
147
+
148
+ return $is_valid;
149
+ }
150
+
151
+ /**
152
+ * @author Vova Feldman (@svovaf)
153
+ * @since 1.1.6
154
+ *
155
+ * @param string $key
156
+ * @param mixed $default
157
+ *
158
+ * @return mixed
159
+ */
160
+ function get( $key, $default = null ) {
161
+ $this->_logger->entrance( 'key = ' . $key );
162
+
163
+ $cache_entry = $this->_options->get_option( $key, false );
164
+
165
+ if ( is_object( $cache_entry ) &&
166
+ isset( $cache_entry->timestamp ) &&
167
+ is_numeric( $cache_entry->timestamp )
168
+ ) {
169
+ return $cache_entry->result;
170
+ }
171
+
172
+ return is_object($default) ? clone $default : $default;
173
+ }
174
+
175
+ /**
176
+ * @author Vova Feldman (@svovaf)
177
+ * @since 1.1.6
178
+ *
179
+ * @param string $key
180
+ * @param mixed $default
181
+ *
182
+ * @return mixed
183
+ */
184
+ function get_valid( $key, $default = null ) {
185
+ $this->_logger->entrance( 'key = ' . $key );
186
+
187
+ $cache_entry = $this->_options->get_option( $key, false );
188
+
189
+ if ( is_object( $cache_entry ) &&
190
+ isset( $cache_entry->timestamp ) &&
191
+ is_numeric( $cache_entry->timestamp ) &&
192
+ $cache_entry->timestamp > WP_FS__SCRIPT_START_TIME
193
+ ) {
194
+ return $cache_entry->result;
195
+ }
196
+
197
+ return is_object($default) ? clone $default : $default;
198
+ }
199
+
200
+ /**
201
+ * @author Vova Feldman (@svovaf)
202
+ * @since 1.1.6
203
+ *
204
+ * @param string $key
205
+ * @param mixed $value
206
+ * @param int $expiration
207
+ */
208
+ function set( $key, $value, $expiration = WP_FS__TIME_24_HOURS_IN_SEC ) {
209
+ $this->_logger->entrance( 'key = ' . $key );
210
+
211
+ $cache_entry = new stdClass();
212
+
213
+ $cache_entry->result = $value;
214
+ $cache_entry->created = WP_FS__SCRIPT_START_TIME;
215
+ $cache_entry->timestamp = WP_FS__SCRIPT_START_TIME + $expiration;
216
+ $this->_options->set_option( $key, $cache_entry, true );
217
+ }
218
+
219
+ /**
220
+ * Get cached record expiration, or false if not cached or expired.
221
+ *
222
+ * @author Vova Feldman (@svovaf)
223
+ * @since 1.1.7.3
224
+ *
225
+ * @param string $key
226
+ *
227
+ * @return bool|int
228
+ */
229
+ function get_record_expiration( $key ) {
230
+ $this->_logger->entrance( 'key = ' . $key );
231
+
232
+ $cache_entry = $this->_options->get_option( $key, false );
233
+
234
+ if ( is_object( $cache_entry ) &&
235
+ isset( $cache_entry->timestamp ) &&
236
+ is_numeric( $cache_entry->timestamp ) &&
237
+ $cache_entry->timestamp > WP_FS__SCRIPT_START_TIME
238
+ ) {
239
+ return $cache_entry->timestamp;
240
+ }
241
+
242
+ return false;
243
+ }
244
+
245
+ /**
246
+ * Purge cached item.
247
+ *
248
+ * @author Vova Feldman (@svovaf)
249
+ * @since 1.1.6
250
+ *
251
+ * @param string $key
252
+ */
253
+ function purge( $key ) {
254
+ $this->_logger->entrance( 'key = ' . $key );
255
+
256
+ $this->_options->unset_option( $key, true );
257
+ }
258
+
259
+ /**
260
+ * Set cached item as expired.
261
+ *
262
+ * @author Vova Feldman (@svovaf)
263
+ * @since 1.2.2.7
264
+ *
265
+ * @param string $key
266
+ */
267
+ function expire( $key ) {
268
+ $this->_logger->entrance( 'key = ' . $key );
269
+
270
+ $cache_entry = $this->_options->get_option( $key, false );
271
+
272
+ if ( is_object( $cache_entry ) &&
273
+ isset( $cache_entry->timestamp ) &&
274
+ is_numeric( $cache_entry->timestamp ))
275
+ {
276
+ // Set to expired.
277
+ $cache_entry->timestamp = WP_FS__SCRIPT_START_TIME;
278
+ $this->_options->set_option( $key, $cache_entry, true );
279
+ }
280
+ }
281
  }
freemius/includes/managers/class-fs-key-value-storage.php CHANGED
@@ -1,328 +1,328 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.7
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- /**
14
- * Class FS_Key_Value_Storage
15
- *
16
- * @property int $install_timestamp
17
- * @property int $activation_timestamp
18
- * @property int $sync_timestamp
19
- * @property object $sync_cron
20
- * @property int $install_sync_timestamp
21
- * @property array $connectivity_test
22
- * @property array $is_on
23
- * @property object $trial_plan
24
- * @property bool $has_trial_plan
25
- * @property bool $trial_promotion_shown
26
- * @property string $sdk_version
27
- * @property string $sdk_last_version
28
- * @property bool $sdk_upgrade_mode
29
- * @property bool $sdk_downgrade_mode
30
- * @property bool $plugin_upgrade_mode
31
- * @property bool $plugin_downgrade_mode
32
- * @property string $plugin_version
33
- * @property string $plugin_last_version
34
- * @property bool $is_plugin_new_install
35
- * @property bool $was_plugin_loaded
36
- * @property object $plugin_main_file
37
- * @property bool $prev_is_premium
38
- * @property array $is_anonymous
39
- * @property bool $is_pending_activation
40
- * @property bool $sticky_optin_added
41
- * @property object $uninstall_reason
42
- * @property object $subscription
43
- */
44
- class FS_Key_Value_Storage implements ArrayAccess, Iterator, Countable {
45
- /**
46
- * @var string
47
- */
48
- protected $_id;
49
- /**
50
- * @since 1.2.2
51
- *
52
- * @var string
53
- */
54
- protected $_secondary_id;
55
- /**
56
- * @var array
57
- */
58
- protected $_data;
59
-
60
- /**
61
- * @var FS_Plugin_Manager[]
62
- */
63
- private static $_instances = array();
64
- /**
65
- * @var FS_Logger
66
- */
67
- protected $_logger;
68
-
69
- /**
70
- * @param string $id
71
- * @param string $secondary_id
72
- *
73
- * @return FS_Key_Value_Storage
74
- */
75
- static function instance( $id, $secondary_id ) {
76
- $key = $id . ':' . $secondary_id;
77
- if ( ! isset( self::$_instances[ $key ] ) ) {
78
- self::$_instances[ $key ] = new FS_Key_Value_Storage( $id, $secondary_id );
79
- }
80
-
81
- return self::$_instances[ $key ];
82
- }
83
-
84
- protected function __construct( $id, $secondary_id ) {
85
- $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $secondary_id . '_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
86
-
87
- $this->_secondary_id = $secondary_id;
88
- $this->_id = $id;
89
- $this->load();
90
- }
91
-
92
- protected function get_option_manager() {
93
- return FS_Option_Manager::get_manager( WP_FS__ACCOUNTS_OPTION_NAME, true );
94
- }
95
-
96
- protected function get_all_data() {
97
- return $this->get_option_manager()->get_option( $this->_id, array() );
98
- }
99
-
100
- /**
101
- * Load plugin data from local DB.
102
- *
103
- * @author Vova Feldman (@svovaf)
104
- * @since 1.0.7
105
- */
106
- function load() {
107
- $all_plugins_data = $this->get_all_data();
108
- $this->_data = isset( $all_plugins_data[ $this->_secondary_id ] ) ?
109
- $all_plugins_data[ $this->_secondary_id ] :
110
- array();
111
- }
112
-
113
- /**
114
- * @author Vova Feldman (@svovaf)
115
- * @since 1.0.7
116
- *
117
- * @param string $key
118
- * @param mixed $value
119
- * @param bool $flush
120
- */
121
- function store( $key, $value, $flush = true ) {
122
- if ( $this->_logger->is_on() ) {
123
- $this->_logger->entrance( $key . ' = ' . var_export( $value, true ) );
124
- }
125
-
126
- if ( array_key_exists( $key, $this->_data ) && $value === $this->_data[ $key ] ) {
127
- // No need to store data if the value wasn't changed.
128
- return;
129
- }
130
-
131
- $all_data = $this->get_all_data();
132
-
133
- $this->_data[ $key ] = $value;
134
-
135
- $all_data[ $this->_secondary_id ] = $this->_data;
136
-
137
- $options_manager = $this->get_option_manager();
138
- $options_manager->set_option( $this->_id, $all_data, $flush );
139
- }
140
-
141
- /**
142
- * @author Vova Feldman (@svovaf)
143
- * @since 1.0.7
144
- *
145
- * @param bool $store
146
- * @param string[] $exceptions Set of keys to keep and not clear.
147
- */
148
- function clear_all( $store = true, $exceptions = array() ) {
149
- $new_data = array();
150
- foreach ( $exceptions as $key ) {
151
- if ( isset( $this->_data[ $key ] ) ) {
152
- $new_data[ $key ] = $this->_data[ $key ];
153
- }
154
- }
155
-
156
- $this->_data = $new_data;
157
-
158
- if ( $store ) {
159
- $all_data = $this->get_all_data();
160
- $all_data[ $this->_secondary_id ] = $this->_data;
161
- $options_manager = $this->get_option_manager();
162
- $options_manager->set_option( $this->_id, $all_data, true );
163
- }
164
- }
165
-
166
- /**
167
- * Delete key-value storage.
168
- *
169
- * @author Vova Feldman (@svovaf)
170
- * @since 1.0.9
171
- */
172
- function delete() {
173
- $this->_data = array();
174
-
175
- $all_data = $this->get_all_data();
176
- unset( $all_data[ $this->_secondary_id ] );
177
- $options_manager = $this->get_option_manager();
178
- $options_manager->set_option( $this->_id, $all_data, true );
179
- }
180
-
181
- /**
182
- * @author Vova Feldman (@svovaf)
183
- * @since 1.0.7
184
- *
185
- * @param string $key
186
- * @param bool $store
187
- */
188
- function remove( $key, $store = true ) {
189
- if ( ! array_key_exists( $key, $this->_data ) ) {
190
- return;
191
- }
192
-
193
- unset( $this->_data[ $key ] );
194
-
195
- if ( $store ) {
196
- $all_data = $this->get_all_data();
197
- $all_data[ $this->_secondary_id ] = $this->_data;
198
- $options_manager = $this->get_option_manager();
199
- $options_manager->set_option( $this->_id, $all_data, true );
200
- }
201
- }
202
-
203
- /**
204
- * @author Vova Feldman (@svovaf)
205
- * @since 1.0.7
206
- *
207
- * @param string $key
208
- * @param mixed $default
209
- *
210
- * @return bool|\FS_Plugin
211
- */
212
- function get( $key, $default = false ) {
213
- return array_key_exists( $key, $this->_data ) ?
214
- $this->_data[ $key ] :
215
- $default;
216
- }
217
-
218
-
219
- /* ArrayAccess + Magic Access (better for refactoring)
220
- -----------------------------------------------------------------------------------*/
221
- function __set( $k, $v ) {
222
- $this->store( $k, $v );
223
- }
224
-
225
- function __isset( $k ) {
226
- return array_key_exists( $k, $this->_data );
227
- }
228
-
229
- function __unset( $k ) {
230
- $this->remove( $k );
231
- }
232
-
233
- function __get( $k ) {
234
- return $this->get( $k, null );
235
- }
236
-
237
- function offsetSet( $k, $v ) {
238
- if ( is_null( $k ) ) {
239
- throw new Exception( 'Can\'t append value to request params.' );
240
- } else {
241
- $this->{$k} = $v;
242
- }
243
- }
244
-
245
- function offsetExists( $k ) {
246
- return array_key_exists( $k, $this->_data );
247
- }
248
-
249
- function offsetUnset( $k ) {
250
- unset( $this->$k );
251
- }
252
-
253
- function offsetGet( $k ) {
254
- return $this->get( $k, null );
255
- }
256
-
257
- /**
258
- * (PHP 5 &gt;= 5.0.0)<br/>
259
- * Return the current element
260
- *
261
- * @link http://php.net/manual/en/iterator.current.php
262
- * @return mixed Can return any type.
263
- */
264
- public function current() {
265
- return current( $this->_data );
266
- }
267
-
268
- /**
269
- * (PHP 5 &gt;= 5.0.0)<br/>
270
- * Move forward to next element
271
- *
272
- * @link http://php.net/manual/en/iterator.next.php
273
- * @return void Any returned value is ignored.
274
- */
275
- public function next() {
276
- next( $this->_data );
277
- }
278
-
279
- /**
280
- * (PHP 5 &gt;= 5.0.0)<br/>
281
- * Return the key of the current element
282
- *
283
- * @link http://php.net/manual/en/iterator.key.php
284
- * @return mixed scalar on success, or null on failure.
285
- */
286
- public function key() {
287
- return key( $this->_data );
288
- }
289
-
290
- /**
291
- * (PHP 5 &gt;= 5.0.0)<br/>
292
- * Checks if current position is valid
293
- *
294
- * @link http://php.net/manual/en/iterator.valid.php
295
- * @return boolean The return value will be casted to boolean and then evaluated.
296
- * Returns true on success or false on failure.
297
- */
298
- public function valid() {
299
- $key = key( $this->_data );
300
-
301
- return ( $key !== null && $key !== false );
302
- }
303
-
304
- /**
305
- * (PHP 5 &gt;= 5.0.0)<br/>
306
- * Rewind the Iterator to the first element
307
- *
308
- * @link http://php.net/manual/en/iterator.rewind.php
309
- * @return void Any returned value is ignored.
310
- */
311
- public function rewind() {
312
- reset( $this->_data );
313
- }
314
-
315
- /**
316
- * (PHP 5 &gt;= 5.1.0)<br/>
317
- * Count elements of an object
318
- *
319
- * @link http://php.net/manual/en/countable.count.php
320
- * @return int The custom count as an integer.
321
- * </p>
322
- * <p>
323
- * The return value is cast to an integer.
324
- */
325
- public function count() {
326
- return count( $this->_data );
327
- }
328
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.7
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Class FS_Key_Value_Storage
15
+ *
16
+ * @property int $install_timestamp
17
+ * @property int $activation_timestamp
18
+ * @property int $sync_timestamp
19
+ * @property object $sync_cron
20
+ * @property int $install_sync_timestamp
21
+ * @property array $connectivity_test
22
+ * @property array $is_on
23
+ * @property object $trial_plan
24
+ * @property bool $has_trial_plan
25
+ * @property bool $trial_promotion_shown
26
+ * @property string $sdk_version
27
+ * @property string $sdk_last_version
28
+ * @property bool $sdk_upgrade_mode
29
+ * @property bool $sdk_downgrade_mode
30
+ * @property bool $plugin_upgrade_mode
31
+ * @property bool $plugin_downgrade_mode
32
+ * @property string $plugin_version
33
+ * @property string $plugin_last_version
34
+ * @property bool $is_plugin_new_install
35
+ * @property bool $was_plugin_loaded
36
+ * @property object $plugin_main_file
37
+ * @property bool $prev_is_premium
38
+ * @property array $is_anonymous
39
+ * @property bool $is_pending_activation
40
+ * @property bool $sticky_optin_added
41
+ * @property object $uninstall_reason
42
+ * @property object $subscription
43
+ */
44
+ class FS_Key_Value_Storage implements ArrayAccess, Iterator, Countable {
45
+ /**
46
+ * @var string
47
+ */
48
+ protected $_id;
49
+ /**
50
+ * @since 1.2.2
51
+ *
52
+ * @var string
53
+ */
54
+ protected $_secondary_id;
55
+ /**
56
+ * @var array
57
+ */
58
+ protected $_data;
59
+
60
+ /**
61
+ * @var FS_Plugin_Manager[]
62
+ */
63
+ private static $_instances = array();
64
+ /**
65
+ * @var FS_Logger
66
+ */
67
+ protected $_logger;
68
+
69
+ /**
70
+ * @param string $id
71
+ * @param string $secondary_id
72
+ *
73
+ * @return FS_Key_Value_Storage
74
+ */
75
+ static function instance( $id, $secondary_id ) {
76
+ $key = $id . ':' . $secondary_id;
77
+ if ( ! isset( self::$_instances[ $key ] ) ) {
78
+ self::$_instances[ $key ] = new FS_Key_Value_Storage( $id, $secondary_id );
79
+ }
80
+
81
+ return self::$_instances[ $key ];
82
+ }
83
+
84
+ protected function __construct( $id, $secondary_id ) {
85
+ $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $secondary_id . '_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
86
+
87
+ $this->_secondary_id = $secondary_id;
88
+ $this->_id = $id;
89
+ $this->load();
90
+ }
91
+
92
+ protected function get_option_manager() {
93
+ return FS_Option_Manager::get_manager( WP_FS__ACCOUNTS_OPTION_NAME, true );
94
+ }
95
+
96
+ protected function get_all_data() {
97
+ return $this->get_option_manager()->get_option( $this->_id, array() );
98
+ }
99
+
100
+ /**
101
+ * Load plugin data from local DB.
102
+ *
103
+ * @author Vova Feldman (@svovaf)
104
+ * @since 1.0.7
105
+ */
106
+ function load() {
107
+ $all_plugins_data = $this->get_all_data();
108
+ $this->_data = isset( $all_plugins_data[ $this->_secondary_id ] ) ?
109
+ $all_plugins_data[ $this->_secondary_id ] :
110
+ array();
111
+ }
112
+
113
+ /**
114
+ * @author Vova Feldman (@svovaf)
115
+ * @since 1.0.7
116
+ *
117
+ * @param string $key
118
+ * @param mixed $value
119
+ * @param bool $flush
120
+ */
121
+ function store( $key, $value, $flush = true ) {
122
+ if ( $this->_logger->is_on() ) {
123
+ $this->_logger->entrance( $key . ' = ' . var_export( $value, true ) );
124
+ }
125
+
126
+ if ( array_key_exists( $key, $this->_data ) && $value === $this->_data[ $key ] ) {
127
+ // No need to store data if the value wasn't changed.
128
+ return;
129
+ }
130
+
131
+ $all_data = $this->get_all_data();
132
+
133
+ $this->_data[ $key ] = $value;
134
+
135
+ $all_data[ $this->_secondary_id ] = $this->_data;
136
+
137
+ $options_manager = $this->get_option_manager();
138
+ $options_manager->set_option( $this->_id, $all_data, $flush );
139
+ }
140
+
141
+ /**
142
+ * @author Vova Feldman (@svovaf)
143
+ * @since 1.0.7
144
+ *
145
+ * @param bool $store
146
+ * @param string[] $exceptions Set of keys to keep and not clear.
147
+ */
148
+ function clear_all( $store = true, $exceptions = array() ) {
149
+ $new_data = array();
150
+ foreach ( $exceptions as $key ) {
151
+ if ( isset( $this->_data[ $key ] ) ) {
152
+ $new_data[ $key ] = $this->_data[ $key ];
153
+ }
154
+ }
155
+
156
+ $this->_data = $new_data;
157
+
158
+ if ( $store ) {
159
+ $all_data = $this->get_all_data();
160
+ $all_data[ $this->_secondary_id ] = $this->_data;
161
+ $options_manager = $this->get_option_manager();
162
+ $options_manager->set_option( $this->_id, $all_data, true );
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Delete key-value storage.
168
+ *
169
+ * @author Vova Feldman (@svovaf)
170
+ * @since 1.0.9
171
+ */
172
+ function delete() {
173
+ $this->_data = array();
174
+
175
+ $all_data = $this->get_all_data();
176
+ unset( $all_data[ $this->_secondary_id ] );
177
+ $options_manager = $this->get_option_manager();
178
+ $options_manager->set_option( $this->_id, $all_data, true );
179
+ }
180
+
181
+ /**
182
+ * @author Vova Feldman (@svovaf)
183
+ * @since 1.0.7
184
+ *
185
+ * @param string $key
186
+ * @param bool $store
187
+ */
188
+ function remove( $key, $store = true ) {
189
+ if ( ! array_key_exists( $key, $this->_data ) ) {
190
+ return;
191
+ }
192
+
193
+ unset( $this->_data[ $key ] );
194
+
195
+ if ( $store ) {
196
+ $all_data = $this->get_all_data();
197
+ $all_data[ $this->_secondary_id ] = $this->_data;
198
+ $options_manager = $this->get_option_manager();
199
+ $options_manager->set_option( $this->_id, $all_data, true );
200
+ }
201
+ }
202
+
203
+ /**
204
+ * @author Vova Feldman (@svovaf)
205
+ * @since 1.0.7
206
+ *
207
+ * @param string $key
208
+ * @param mixed $default
209
+ *
210
+ * @return bool|\FS_Plugin
211
+ */
212
+ function get( $key, $default = false ) {
213
+ return array_key_exists( $key, $this->_data ) ?
214
+ $this->_data[ $key ] :
215
+ $default;
216
+ }
217
+
218
+
219
+ /* ArrayAccess + Magic Access (better for refactoring)
220
+ -----------------------------------------------------------------------------------*/
221
+ function __set( $k, $v ) {
222
+ $this->store( $k, $v );
223
+ }
224
+
225
+ function __isset( $k ) {
226
+ return array_key_exists( $k, $this->_data );
227
+ }
228
+
229
+ function __unset( $k ) {
230
+ $this->remove( $k );
231
+ }
232
+
233
+ function __get( $k ) {
234
+ return $this->get( $k, null );
235
+ }
236
+
237
+ function offsetSet( $k, $v ) {
238
+ if ( is_null( $k ) ) {
239
+ throw new Exception( 'Can\'t append value to request params.' );
240
+ } else {
241
+ $this->{$k} = $v;
242
+ }
243
+ }
244
+
245
+ function offsetExists( $k ) {
246
+ return array_key_exists( $k, $this->_data );
247
+ }
248
+
249
+ function offsetUnset( $k ) {
250
+ unset( $this->$k );
251
+ }
252
+
253
+ function offsetGet( $k ) {
254
+ return $this->get( $k, null );
255
+ }
256
+
257
+ /**
258
+ * (PHP 5 &gt;= 5.0.0)<br/>
259
+ * Return the current element
260
+ *
261
+ * @link http://php.net/manual/en/iterator.current.php
262
+ * @return mixed Can return any type.
263
+ */
264
+ public function current() {
265
+ return current( $this->_data );
266
+ }
267
+
268
+ /**
269
+ * (PHP 5 &gt;= 5.0.0)<br/>
270
+ * Move forward to next element
271
+ *
272
+ * @link http://php.net/manual/en/iterator.next.php
273
+ * @return void Any returned value is ignored.
274
+ */
275
+ public function next() {
276
+ next( $this->_data );
277
+ }
278
+
279
+ /**
280
+ * (PHP 5 &gt;= 5.0.0)<br/>
281
+ * Return the key of the current element
282
+ *
283
+ * @link http://php.net/manual/en/iterator.key.php
284
+ * @return mixed scalar on success, or null on failure.
285
+ */
286
+ public function key() {
287
+ return key( $this->_data );
288
+ }
289
+
290
+ /**
291
+ * (PHP 5 &gt;= 5.0.0)<br/>
292
+ * Checks if current position is valid
293
+ *
294
+ * @link http://php.net/manual/en/iterator.valid.php
295
+ * @return boolean The return value will be casted to boolean and then evaluated.
296
+ * Returns true on success or false on failure.
297
+ */
298
+ public function valid() {
299
+ $key = key( $this->_data );
300
+
301
+ return ( $key !== null && $key !== false );
302
+ }
303
+
304
+ /**
305
+ * (PHP 5 &gt;= 5.0.0)<br/>
306
+ * Rewind the Iterator to the first element
307
+ *
308
+ * @link http://php.net/manual/en/iterator.rewind.php
309
+ * @return void Any returned value is ignored.
310
+ */
311
+ public function rewind() {
312
+ reset( $this->_data );
313
+ }
314
+
315
+ /**
316
+ * (PHP 5 &gt;= 5.1.0)<br/>
317
+ * Count elements of an object
318
+ *
319
+ * @link http://php.net/manual/en/countable.count.php
320
+ * @return int The custom count as an integer.
321
+ * </p>
322
+ * <p>
323
+ * The return value is cast to an integer.
324
+ */
325
+ public function count() {
326
+ return count( $this->_data );
327
+ }
328
  }
freemius/includes/managers/class-fs-license-manager.php CHANGED
@@ -1,104 +1,104 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.6
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_License_Manager /*extends FS_Abstract_Manager*/
14
- {
15
- //
16
- //
17
- // /**
18
- // * @var FS_License_Manager[]
19
- // */
20
- // private static $_instances = array();
21
- //
22
- // static function instance( Freemius $fs ) {
23
- // $slug = strtolower( $fs->get_slug() );
24
- //
25
- // if ( ! isset( self::$_instances[ $slug ] ) ) {
26
- // self::$_instances[ $slug ] = new FS_License_Manager( $slug, $fs );
27
- // }
28
- //
29
- // return self::$_instances[ $slug ];
30
- // }
31
- //
32
- //// private function __construct($slug) {
33
- //// parent::__construct($slug);
34
- //// }
35
- //
36
- // function entry_id() {
37
- // return 'licenses';
38
- // }
39
- //
40
- // function sync( $id ) {
41
- //
42
- // }
43
- //
44
- // /**
45
- // * @author Vova Feldman (@svovaf)
46
- // * @since 1.0.5
47
- // * @uses FS_Api
48
- // *
49
- // * @param number|bool $plugin_id
50
- // *
51
- // * @return FS_Plugin_License[]|stdClass Licenses or API error.
52
- // */
53
- // function api_get_user_plugin_licenses( $plugin_id = false ) {
54
- // $api = $this->_fs->get_api_user_scope();
55
- //
56
- // if ( ! is_numeric( $plugin_id ) ) {
57
- // $plugin_id = $this->_fs->get_id();
58
- // }
59
- //
60
- // $result = $api->call( "/plugins/{$plugin_id}/licenses.json" );
61
- //
62
- // if ( ! isset( $result->error ) ) {
63
- // for ( $i = 0, $len = count( $result->licenses ); $i < $len; $i ++ ) {
64
- // $result->licenses[ $i ] = new FS_Plugin_License( $result->licenses[ $i ] );
65
- // }
66
- //
67
- // $result = $result->licenses;
68
- // }
69
- //
70
- // return $result;
71
- // }
72
- //
73
- // function api_get_many() {
74
- //
75
- // }
76
- //
77
- // function api_activate( $id ) {
78
- //
79
- // }
80
- //
81
- // function api_deactivate( $id ) {
82
- //
83
- // }
84
-
85
- /**
86
- * @param FS_Plugin_License[] $licenses
87
- *
88
- * @return bool
89
- */
90
- static function has_premium_license( $licenses ) {
91
- if ( is_array( $licenses ) ) {
92
- foreach ( $licenses as $license ) {
93
- /**
94
- * @var FS_Plugin_License $license
95
- */
96
- if ( ! $license->is_utilized() && $license->is_features_enabled() ) {
97
- return true;
98
- }
99
- }
100
- }
101
-
102
- return false;
103
- }
104
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.6
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_License_Manager /*extends FS_Abstract_Manager*/
14
+ {
15
+ //
16
+ //
17
+ // /**
18
+ // * @var FS_License_Manager[]
19
+ // */
20
+ // private static $_instances = array();
21
+ //
22
+ // static function instance( Freemius $fs ) {
23
+ // $slug = strtolower( $fs->get_slug() );
24
+ //
25
+ // if ( ! isset( self::$_instances[ $slug ] ) ) {
26
+ // self::$_instances[ $slug ] = new FS_License_Manager( $slug, $fs );
27
+ // }
28
+ //
29
+ // return self::$_instances[ $slug ];
30
+ // }
31
+ //
32
+ //// private function __construct($slug) {
33
+ //// parent::__construct($slug);
34
+ //// }
35
+ //
36
+ // function entry_id() {
37
+ // return 'licenses';
38
+ // }
39
+ //
40
+ // function sync( $id ) {
41
+ //
42
+ // }
43
+ //
44
+ // /**
45
+ // * @author Vova Feldman (@svovaf)
46
+ // * @since 1.0.5
47
+ // * @uses FS_Api
48
+ // *
49
+ // * @param number|bool $plugin_id
50
+ // *
51
+ // * @return FS_Plugin_License[]|stdClass Licenses or API error.
52
+ // */
53
+ // function api_get_user_plugin_licenses( $plugin_id = false ) {
54
+ // $api = $this->_fs->get_api_user_scope();
55
+ //
56
+ // if ( ! is_numeric( $plugin_id ) ) {
57
+ // $plugin_id = $this->_fs->get_id();
58
+ // }
59
+ //
60
+ // $result = $api->call( "/plugins/{$plugin_id}/licenses.json" );
61
+ //
62
+ // if ( ! isset( $result->error ) ) {
63
+ // for ( $i = 0, $len = count( $result->licenses ); $i < $len; $i ++ ) {
64
+ // $result->licenses[ $i ] = new FS_Plugin_License( $result->licenses[ $i ] );
65
+ // }
66
+ //
67
+ // $result = $result->licenses;
68
+ // }
69
+ //
70
+ // return $result;
71
+ // }
72
+ //
73
+ // function api_get_many() {
74
+ //
75
+ // }
76
+ //
77
+ // function api_activate( $id ) {
78
+ //
79
+ // }
80
+ //
81
+ // function api_deactivate( $id ) {
82
+ //
83
+ // }
84
+
85
+ /**
86
+ * @param FS_Plugin_License[] $licenses
87
+ *
88
+ * @return bool
89
+ */
90
+ static function has_premium_license( $licenses ) {
91
+ if ( is_array( $licenses ) ) {
92
+ foreach ( $licenses as $license ) {
93
+ /**
94
+ * @var FS_Plugin_License $license
95
+ */
96
+ if ( ! $license->is_utilized() && $license->is_features_enabled() ) {
97
+ return true;
98
+ }
99
+ }
100
+ }
101
+
102
+ return false;
103
+ }
104
  }
freemius/includes/managers/class-fs-option-manager.php CHANGED
@@ -1,353 +1,353 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.3
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- /**
14
- * 3-layer lazy options manager.
15
- * layer 3: Memory
16
- * layer 2: Cache (if there's any caching plugin and if WP_FS__DEBUG_SDK is FALSE)
17
- * layer 1: Database (options table). All options stored as one option record in the DB to reduce number of DB
18
- * queries.
19
- *
20
- * If load() is not explicitly called, starts as empty manager. Same thing about saving the data - you have to
21
- * explicitly call store().
22
- *
23
- * Class Freemius_Option_Manager
24
- */
25
- class FS_Option_Manager {
26
- /**
27
- * @var string
28
- */
29
- private $_id;
30
- /**
31
- * @var array
32
- */
33
- private $_options;
34
- /**
35
- * @var FS_Logger
36
- */
37
- private $_logger;
38
-
39
- /**
40
- * @var FS_Option_Manager[]
41
- */
42
- private static $_MANAGERS = array();
43
-
44
- /**
45
- * @author Vova Feldman (@svovaf)
46
- * @since 1.0.3
47
- *
48
- * @param string $id
49
- * @param bool $load
50
- */
51
- private function __construct( $id, $load = false ) {
52
- $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_opt_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
53
-
54
- $this->_logger->entrance();
55
- $this->_logger->log( 'id = ' . $id );
56
-
57
- $this->_id = $id;
58
-
59
- if ( $load ) {
60
- $this->load();
61
- }
62
- }
63
-
64
- /**
65
- * @author Vova Feldman (@svovaf)
66
- * @since 1.0.3
67
- *
68
- * @param $id
69
- * @param $load
70
- *
71
- * @return FS_Option_Manager
72
- */
73
- static function get_manager( $id, $load = false ) {
74
- $id = strtolower( $id );
75
-
76
- if ( ! isset( self::$_MANAGERS[ $id ] ) ) {
77
- self::$_MANAGERS[ $id ] = new FS_Option_Manager( $id, $load );
78
- } // If load required but not yet loaded, load.
79
- else if ( $load && ! self::$_MANAGERS[ $id ]->is_loaded() ) {
80
- self::$_MANAGERS[ $id ]->load();
81
- }
82
-
83
- return self::$_MANAGERS[ $id ];
84
- }
85
-
86
- private function _get_option_manager_name() {
87
- // return WP_FS__SLUG . '_' . $this->_id;
88
- return $this->_id;
89
- }
90
-
91
- /**
92
- * @author Vova Feldman (@svovaf)
93
- * @since 1.0.3
94
- *
95
- * @param bool $flush
96
- */
97
- function load( $flush = false ) {
98
- $this->_logger->entrance();
99
-
100
- $option_name = $this->_get_option_manager_name();
101
-
102
- if ( $flush || ! isset( $this->_options ) ) {
103
- if ( isset( $this->_options ) ) {
104
- // Clear prev options.
105
- $this->clear();
106
- }
107
-
108
- if ( ! WP_FS__DEBUG_SDK ) {
109
- $this->_options = wp_cache_get( $option_name, WP_FS__SLUG );
110
- }
111
-
112
- // $this->_logger->info('wp_cache_get = ' . var_export($this->_options, true));
113
-
114
- // if ( is_array( $this->_options ) ) {
115
- // $this->clear();
116
- // }
117
-
118
- $cached = true;
119
-
120
- if ( empty( $this->_options ) ) {
121
- $this->_options = get_option( $option_name );
122
-
123
- if ( is_string( $this->_options ) ) {
124
- $this->_options = json_decode( $this->_options );
125
- }
126
-
127
- // $this->_logger->info('get_option = ' . var_export($this->_options, true));
128
-
129
- if ( false === $this->_options ) {
130
- $this->clear();
131
- }
132
-
133
- $cached = false;
134
- }
135
-
136
- if ( ! WP_FS__DEBUG_SDK && ! $cached ) // Set non encoded cache.
137
- {
138
- wp_cache_set( $option_name, $this->_options, WP_FS__SLUG );
139
- }
140
- }
141
- }
142
-
143
- /**
144
- * @author Vova Feldman (@svovaf)
145
- * @since 1.0.3
146
- *
147
- * @return bool
148
- */
149
- function is_loaded() {
150
- return isset( $this->_options );
151
- }
152
-
153
- /**
154
- * @author Vova Feldman (@svovaf)
155
- * @since 1.0.3
156
- *
157
- * @return bool
158
- */
159
- function is_empty() {
160
- return ( $this->is_loaded() && false === $this->_options );
161
- }
162
-
163
- /**
164
- * @author Vova Feldman (@svovaf)
165
- * @since 1.0.6
166
- *
167
- * @param bool $flush
168
- */
169
- function clear( $flush = false ) {
170
- $this->_logger->entrance();
171
-
172
- $this->_options = array();
173
-
174
- if ( $flush ) {
175
- $this->store();
176
- }
177
- }
178
-
179
- /**
180
- * Delete options manager from DB.
181
- *
182
- * @author Vova Feldman (@svovaf)
183
- * @since 1.0.9
184
- */
185
- function delete() {
186
- delete_option( $this->_get_option_manager_name() );
187
- }
188
-
189
- /**
190
- * @author Vova Feldman (@svovaf)
191
- * @since 1.0.6
192
- *
193
- * @param string $option
194
- *
195
- * @return bool
196
- */
197
- function has_option( $option ) {
198
- return array_key_exists( $option, $this->_options );
199
- }
200
-
201
- /**
202
- * @author Vova Feldman (@svovaf)
203
- * @since 1.0.3
204
- *
205
- * @param string $option
206
- * @param mixed $default
207
- *
208
- * @return mixed
209
- */
210
- function get_option( $option, $default = null ) {
211
- $this->_logger->entrance( 'option = ' . $option );
212
-
213
- if ( is_array( $this->_options ) ) {
214
- $value = isset( $this->_options[ $option ] ) ?
215
- $this->_options[ $option ] :
216
- $default;
217
- } else if ( is_object( $this->_options ) ) {
218
- $value = isset( $this->_options->{$option} ) ?
219
- $this->_options->{$option} :
220
- $default;
221
- } else {
222
- $value = $default;
223
- }
224
-
225
- /**
226
- * If it's an object, return a clone of the object, otherwise,
227
- * external changes of the object will actually change the value
228
- * of the object in the options manager which may lead to an unexpected
229
- * behaviour and data integrity when a store() call is triggered.
230
- *
231
- * Example:
232
- * $object1 = $options->get_option( 'object1' );
233
- * $object1->x = 123;
234
- *
235
- * $object2 = $options->get_option( 'object2' );
236
- * $object2->y = 'dummy';
237
- *
238
- * $options->set_option( 'object2', $object2, true );
239
- *
240
- * If we don't return a clone of option 'object1', setting 'object2'
241
- * will also store the updated value of 'object1' which is quite not
242
- * an expected behaviour.
243
- *
244
- * @author Vova Feldman
245
- */
246
- return is_object($value) ? clone $value : $value;
247
- }
248
-
249
- /**
250
- * @author Vova Feldman (@svovaf)
251
- * @since 1.0.3
252
- *
253
- * @param string $option
254
- * @param mixed $value
255
- * @param bool $flush
256
- */
257
- function set_option( $option, $value, $flush = false ) {
258
- $this->_logger->entrance( 'option = ' . $option );
259
-
260
- if ( ! $this->is_loaded() ) {
261
- $this->clear();
262
- }
263
-
264
- /**
265
- * If it's an object, store a clone of the object, otherwise,
266
- * external changes of the object will actually change the value
267
- * of the object in the options manager which may lead to an unexpected
268
- * behaviour and data integrity when a store() call is triggered.
269
- *
270
- * Example:
271
- * $object1 = new stdClass();
272
- * $object1->x = 123;
273
- *
274
- * $options->set_option( 'object1', $object1 );
275
- *
276
- * $object1->x = 456;
277
- *
278
- * $options->set_option( 'object2', $object2, true );
279
- *
280
- * If we don't set the option as a clone of option 'object1', setting 'object2'
281
- * will also store the updated value of 'object1' ($object1->x = 456 instead of
282
- * $object1->x = 123) which is quite not an expected behaviour.
283
- *
284
- * @author Vova Feldman
285
- */
286
- $copy = is_object($value) ? clone $value : $value;
287
-
288
- if ( is_array( $this->_options ) ) {
289
- $this->_options[ $option ] = $copy;
290
- } else if ( is_object( $this->_options ) ) {
291
- $this->_options->{$option} = $copy;
292
- }
293
-
294
- if ( $flush ) {
295
- $this->store();
296
- }
297
- }
298
-
299
- /**
300
- * Unset option.
301
- *
302
- * @author Vova Feldman (@svovaf)
303
- * @since 1.0.3
304
- *
305
- * @param string $option
306
- * @param bool $flush
307
- */
308
- function unset_option( $option, $flush = false ) {
309
- $this->_logger->entrance( 'option = ' . $option );
310
-
311
- if ( is_array( $this->_options ) ) {
312
- if ( ! isset( $this->_options[ $option ] ) ) {
313
- return;
314
- }
315
-
316
- unset( $this->_options[ $option ] );
317
-
318
- } else if ( is_object( $this->_options ) ) {
319
- if ( ! isset( $this->_options->{$option} ) ) {
320
- return;
321
- }
322
-
323
- unset( $this->_options->{$option} );
324
- }
325
-
326
- if ( $flush ) {
327
- $this->store();
328
- }
329
- }
330
-
331
- /**
332
- * Dump options to database.
333
- *
334
- * @author Vova Feldman (@svovaf)
335
- * @since 1.0.3
336
- */
337
- function store() {
338
- $this->_logger->entrance();
339
-
340
- $option_name = $this->_get_option_manager_name();
341
-
342
- if ( $this->_logger->is_on() ) {
343
- $this->_logger->info( $option_name . ' = ' . var_export( $this->_options, true ) );
344
- }
345
-
346
- // Update DB.
347
- update_option( $option_name, $this->_options );
348
-
349
- if ( ! WP_FS__DEBUG_SDK ) {
350
- wp_cache_set( $option_name, $this->_options, WP_FS__SLUG );
351
- }
352
- }
353
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.3
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * 3-layer lazy options manager.
15
+ * layer 3: Memory
16
+ * layer 2: Cache (if there's any caching plugin and if WP_FS__DEBUG_SDK is FALSE)
17
+ * layer 1: Database (options table). All options stored as one option record in the DB to reduce number of DB
18
+ * queries.
19
+ *
20
+ * If load() is not explicitly called, starts as empty manager. Same thing about saving the data - you have to
21
+ * explicitly call store().
22
+ *
23
+ * Class Freemius_Option_Manager
24
+ */
25
+ class FS_Option_Manager {
26
+ /**
27
+ * @var string
28
+ */
29
+ private $_id;
30
+ /**
31
+ * @var array
32
+ */
33
+ private $_options;
34
+ /**
35
+ * @var FS_Logger
36
+ */
37
+ private $_logger;
38
+
39
+ /**
40
+ * @var FS_Option_Manager[]
41
+ */
42
+ private static $_MANAGERS = array();
43
+
44
+ /**
45
+ * @author Vova Feldman (@svovaf)
46
+ * @since 1.0.3
47
+ *
48
+ * @param string $id
49
+ * @param bool $load
50
+ */
51
+ private function __construct( $id, $load = false ) {
52
+ $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_opt_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
53
+
54
+ $this->_logger->entrance();
55
+ $this->_logger->log( 'id = ' . $id );
56
+
57
+ $this->_id = $id;
58
+
59
+ if ( $load ) {
60
+ $this->load();
61
+ }
62
+ }
63
+
64
+ /**
65
+ * @author Vova Feldman (@svovaf)
66
+ * @since 1.0.3
67
+ *
68
+ * @param $id
69
+ * @param $load
70
+ *
71
+ * @return FS_Option_Manager
72
+ */
73
+ static function get_manager( $id, $load = false ) {
74
+ $id = strtolower( $id );
75
+
76
+ if ( ! isset( self::$_MANAGERS[ $id ] ) ) {
77
+ self::$_MANAGERS[ $id ] = new FS_Option_Manager( $id, $load );
78
+ } // If load required but not yet loaded, load.
79
+ else if ( $load && ! self::$_MANAGERS[ $id ]->is_loaded() ) {
80
+ self::$_MANAGERS[ $id ]->load();
81
+ }
82
+
83
+ return self::$_MANAGERS[ $id ];
84
+ }
85
+
86
+ private function _get_option_manager_name() {
87
+ // return WP_FS__SLUG . '_' . $this->_id;
88
+ return $this->_id;
89
+ }
90
+
91
+ /**
92
+ * @author Vova Feldman (@svovaf)
93
+ * @since 1.0.3
94
+ *
95
+ * @param bool $flush
96
+ */
97
+ function load( $flush = false ) {
98
+ $this->_logger->entrance();
99
+
100
+ $option_name = $this->_get_option_manager_name();
101
+
102
+ if ( $flush || ! isset( $this->_options ) ) {
103
+ if ( isset( $this->_options ) ) {
104
+ // Clear prev options.
105
+ $this->clear();
106
+ }
107
+
108
+ if ( ! WP_FS__DEBUG_SDK ) {
109
+ $this->_options = wp_cache_get( $option_name, WP_FS__SLUG );
110
+ }
111
+
112
+ // $this->_logger->info('wp_cache_get = ' . var_export($this->_options, true));
113
+
114
+ // if ( is_array( $this->_options ) ) {
115
+ // $this->clear();
116
+ // }
117
+
118
+ $cached = true;
119
+
120
+ if ( empty( $this->_options ) ) {
121
+ $this->_options = get_option( $option_name );
122
+
123
+ if ( is_string( $this->_options ) ) {
124
+ $this->_options = json_decode( $this->_options );
125
+ }
126
+
127
+ // $this->_logger->info('get_option = ' . var_export($this->_options, true));
128
+
129
+ if ( false === $this->_options ) {
130
+ $this->clear();
131
+ }
132
+
133
+ $cached = false;
134
+ }
135
+
136
+ if ( ! WP_FS__DEBUG_SDK && ! $cached ) // Set non encoded cache.
137
+ {
138
+ wp_cache_set( $option_name, $this->_options, WP_FS__SLUG );
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * @author Vova Feldman (@svovaf)
145
+ * @since 1.0.3
146
+ *
147
+ * @return bool
148
+ */
149
+ function is_loaded() {
150
+ return isset( $this->_options );
151
+ }
152
+
153
+ /**
154
+ * @author Vova Feldman (@svovaf)
155
+ * @since 1.0.3
156
+ *
157
+ * @return bool
158
+ */
159
+ function is_empty() {
160
+ return ( $this->is_loaded() && false === $this->_options );
161
+ }
162
+
163
+ /**
164
+ * @author Vova Feldman (@svovaf)
165
+ * @since 1.0.6
166
+ *
167
+ * @param bool $flush
168
+ */
169
+ function clear( $flush = false ) {
170
+ $this->_logger->entrance();
171
+
172
+ $this->_options = array();
173
+
174
+ if ( $flush ) {
175
+ $this->store();
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Delete options manager from DB.
181
+ *
182
+ * @author Vova Feldman (@svovaf)
183
+ * @since 1.0.9
184
+ */
185
+ function delete() {
186
+ delete_option( $this->_get_option_manager_name() );
187
+ }
188
+
189
+ /**
190
+ * @author Vova Feldman (@svovaf)
191
+ * @since 1.0.6
192
+ *
193
+ * @param string $option
194
+ *
195
+ * @return bool
196
+ */
197
+ function has_option( $option ) {
198
+ return array_key_exists( $option, $this->_options );
199
+ }
200
+
201
+ /**
202
+ * @author Vova Feldman (@svovaf)
203
+ * @since 1.0.3
204
+ *
205
+ * @param string $option
206
+ * @param mixed $default
207
+ *
208
+ * @return mixed
209
+ */
210
+ function get_option( $option, $default = null ) {
211
+ $this->_logger->entrance( 'option = ' . $option );
212
+
213
+ if ( is_array( $this->_options ) ) {
214
+ $value = isset( $this->_options[ $option ] ) ?
215
+ $this->_options[ $option ] :
216
+ $default;
217
+ } else if ( is_object( $this->_options ) ) {
218
+ $value = isset( $this->_options->{$option} ) ?
219
+ $this->_options->{$option} :
220
+ $default;
221
+ } else {
222
+ $value = $default;
223
+ }
224
+
225
+ /**
226
+ * If it's an object, return a clone of the object, otherwise,
227
+ * external changes of the object will actually change the value
228
+ * of the object in the options manager which may lead to an unexpected
229
+ * behaviour and data integrity when a store() call is triggered.
230
+ *
231
+ * Example:
232
+ * $object1 = $options->get_option( 'object1' );
233
+ * $object1->x = 123;
234
+ *
235
+ * $object2 = $options->get_option( 'object2' );
236
+ * $object2->y = 'dummy';
237
+ *
238
+ * $options->set_option( 'object2', $object2, true );
239
+ *
240
+ * If we don't return a clone of option 'object1', setting 'object2'
241
+ * will also store the updated value of 'object1' which is quite not
242
+ * an expected behaviour.
243
+ *
244
+ * @author Vova Feldman
245
+ */
246
+ return is_object($value) ? clone $value : $value;
247
+ }
248
+
249
+ /**
250
+ * @author Vova Feldman (@svovaf)
251
+ * @since 1.0.3
252
+ *
253
+ * @param string $option
254
+ * @param mixed $value
255
+ * @param bool $flush
256
+ */
257
+ function set_option( $option, $value, $flush = false ) {
258
+ $this->_logger->entrance( 'option = ' . $option );
259
+
260
+ if ( ! $this->is_loaded() ) {
261
+ $this->clear();
262
+ }
263
+
264
+ /**
265
+ * If it's an object, store a clone of the object, otherwise,
266
+ * external changes of the object will actually change the value
267
+ * of the object in the options manager which may lead to an unexpected
268
+ * behaviour and data integrity when a store() call is triggered.
269
+ *
270
+ * Example:
271
+ * $object1 = new stdClass();
272
+ * $object1->x = 123;
273
+ *
274
+ * $options->set_option( 'object1', $object1 );
275
+ *
276
+ * $object1->x = 456;
277
+ *
278
+ * $options->set_option( 'object2', $object2, true );
279
+ *
280
+ * If we don't set the option as a clone of option 'object1', setting 'object2'
281
+ * will also store the updated value of 'object1' ($object1->x = 456 instead of
282
+ * $object1->x = 123) which is quite not an expected behaviour.
283
+ *
284
+ * @author Vova Feldman
285
+ */
286
+ $copy = is_object($value) ? clone $value : $value;
287
+
288
+ if ( is_array( $this->_options ) ) {
289
+ $this->_options[ $option ] = $copy;
290
+ } else if ( is_object( $this->_options ) ) {
291
+ $this->_options->{$option} = $copy;
292
+ }
293
+
294
+ if ( $flush ) {
295
+ $this->store();
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Unset option.
301
+ *
302
+ * @author Vova Feldman (@svovaf)
303
+ * @since 1.0.3
304
+ *
305
+ * @param string $option
306
+ * @param bool $flush
307
+ */
308
+ function unset_option( $option, $flush = false ) {
309
+ $this->_logger->entrance( 'option = ' . $option );
310
+
311
+ if ( is_array( $this->_options ) ) {
312
+ if ( ! isset( $this->_options[ $option ] ) ) {
313
+ return;
314
+ }
315
+
316
+ unset( $this->_options[ $option ] );
317
+
318
+ } else if ( is_object( $this->_options ) ) {
319
+ if ( ! isset( $this->_options->{$option} ) ) {
320
+ return;
321
+ }
322
+
323
+ unset( $this->_options->{$option} );
324
+ }
325
+
326
+ if ( $flush ) {
327
+ $this->store();
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Dump options to database.
333
+ *
334
+ * @author Vova Feldman (@svovaf)
335
+ * @since 1.0.3
336
+ */
337
+ function store() {
338
+ $this->_logger->entrance();
339
+
340
+ $option_name = $this->_get_option_manager_name();
341
+
342
+ if ( $this->_logger->is_on() ) {
343
+ $this->_logger->info( $option_name . ' = ' . var_export( $this->_options, true ) );
344
+ }
345
+
346
+ // Update DB.
347
+ update_option( $option_name, $this->_options );
348
+
349
+ if ( ! WP_FS__DEBUG_SDK ) {
350
+ wp_cache_set( $option_name, $this->_options, WP_FS__SLUG );
351
+ }
352
+ }
353
  }
freemius/includes/managers/class-fs-plan-manager.php CHANGED
@@ -1,162 +1,162 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.6
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Plan_Manager {
14
- /**
15
- * @var FS_Plan_Manager
16
- */
17
- private static $_instance;
18
-
19
- /**
20
- * @return FS_Plan_Manager
21
- */
22
- static function instance() {
23
- if ( ! isset( self::$_instance ) ) {
24
- self::$_instance = new FS_Plan_Manager();
25
- }
26
-
27
- return self::$_instance;
28
- }
29
-
30
- private function __construct() {
31
- }
32
-
33
- /**
34
- * @param FS_Plugin_License[] $licenses
35
- *
36
- * @return bool
37
- */
38
- function has_premium_license( $licenses ) {
39
- if ( is_array( $licenses ) ) {
40
- /**
41
- * @var FS_Plugin_License[] $licenses
42
- */
43
- foreach ( $licenses as $license ) {
44
- if ( ! $license->is_utilized() && $license->is_features_enabled() ) {
45
- return true;
46
- }
47
- }
48
- }
49
-
50
- return false;
51
- }
52
-
53
- /**
54
- * Check if plugin has any paid plans.
55
- *
56
- * @author Vova Feldman (@svovaf)
57
- * @since 1.0.7
58
- *
59
- * @param FS_Plugin_Plan[] $plans
60
- *
61
- * @return bool
62
- */
63
- function has_paid_plan( $plans ) {
64
- if ( ! is_array( $plans ) || 0 === count( $plans ) ) {
65
- return false;
66
- }
67
-
68
- /**
69
- * @var FS_Plugin_Plan[] $plans
70
- */
71
- for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
72
- if ( ! $plans[ $i ]->is_free() ) {
73
- return true;
74
- }
75
- }
76
-
77
- return false;
78
- }
79
-
80
- /**
81
- * Check if plugin has any free plan, or is it premium only.
82
- *
83
- * Note: If no plans configured, assume plugin is free.
84
- *
85
- * @author Vova Feldman (@svovaf)
86
- * @since 1.0.7
87
- *
88
- * @param FS_Plugin_Plan[] $plans
89
- *
90
- * @return bool
91
- */
92
- function has_free_plan( $plans ) {
93
- if ( ! is_array( $plans ) || 0 === count( $plans ) ) {
94
- return true;
95
- }
96
-
97
- /**
98
- * @var FS_Plugin_Plan[] $plans
99
- */
100
- for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
101
- if ( $plans[ $i ]->is_free() ) {
102
- return true;
103
- }
104
- }
105
-
106
- return false;
107
- }
108
-
109
- /**
110
- * Find all plans that have trial.
111
- *
112
- * @author Vova Feldman (@svovaf)
113
- * @since 1.0.9
114
- *
115
- * @param FS_Plugin_Plan[] $plans
116
- *
117
- * @return FS_Plugin_Plan[]
118
- */
119
- function get_trial_plans( $plans ) {
120
- $trial_plans = array();
121
-
122
- if ( is_array( $plans ) && 0 < count( $plans ) ) {
123
- /**
124
- * @var FS_Plugin_Plan[] $plans
125
- */
126
- for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
127
- if ( $plans[ $i ]->has_trial() ) {
128
- $trial_plans[] = $plans[ $i ];
129
- }
130
- }
131
- }
132
-
133
- return $trial_plans;
134
- }
135
-
136
- /**
137
- * Check if plugin has any trial plan.
138
- *
139
- * @author Vova Feldman (@svovaf)
140
- * @since 1.0.9
141
- *
142
- * @param FS_Plugin_Plan[] $plans
143
- *
144
- * @return bool
145
- */
146
- function has_trial_plan( $plans ) {
147
- if ( ! is_array( $plans ) || 0 === count( $plans ) ) {
148
- return true;
149
- }
150
-
151
- /**
152
- * @var FS_Plugin_Plan[] $plans
153
- */
154
- for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
155
- if ( $plans[ $i ]->has_trial() ) {
156
- return true;
157
- }
158
- }
159
-
160
- return false;
161
- }
162
  }
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 1.0.6
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class FS_Plan_Manager {
14
+ /**
15
+ * @var FS_Plan_Manager
16
+ */
17
+ private static $_instance;
18
+
19
+ /**
20
+ * @return FS_Plan_Manager
21
+ */
22
+ static function instance() {
23
+ if ( ! isset( self::$_instance ) ) {
24
+ self::$_instance = new FS_Plan_Manager();
25
+ }
26
+
27
+ return self::$_instance;
28
+ }
29
+
30
+ private function __construct() {
31
+ }
32
+
33
+ /**
34
+ * @param FS_Plugin_License[] $licenses
35
+ *
36
+ * @return bool
37
+ */
38
+ function has_premium_license( $licenses ) {
39
+ if ( is_array( $licenses ) ) {
40
+ /**
41
+ * @var FS_Plugin_License[] $licenses
42
+ */
43
+ foreach ( $licenses as $license ) {
44
+ if ( ! $license->is_utilized() && $license->is_features_enabled() ) {
45
+ return true;
46
+ }
47
+ }
48
+ }
49
+
50
+ return false;
51
+ }
52
+
53
+ /**
54
+ * Check if plugin has any paid plans.
55
+ *
56
+ * @author Vova Feldman (@svovaf)
57
+ * @since 1.0.7
58
+ *
59
+ * @param FS_Plugin_Plan[] $plans
60
+ *
61
+ * @return bool
62
+ */
63
+ function has_paid_plan( $plans ) {
64
+ if ( ! is_array( $plans ) || 0 === count( $plans ) ) {
65
+ return false;
66
+ }
67
+
68
+ /**
69
+ * @var FS_Plugin_Plan[] $plans
70
+ */
71
+ for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
72
+ if ( ! $plans[ $i ]->is_free() ) {
73
+ return true;
74
+ }
75
+ }
76
+
77
+ return false;
78
+ }
79
+
80
+ /**
81
+ * Check if plugin has any free plan, or is it premium only.
82
+ *
83
+ * Note: If no plans configured, assume plugin is free.
84
+ *
85
+ * @author Vova Feldman (@svovaf)
86
+ * @since 1.0.7
87
+ *
88
+ * @param FS_Plugin_Plan[] $plans
89
+ *
90
+ * @return bool
91
+ */
92
+ function has_free_plan( $plans ) {
93
+ if ( ! is_array( $plans ) || 0 === count( $plans ) ) {
94
+ return true;
95
+ }
96
+
97
+ /**
98
+ * @var FS_Plugin_Plan[] $plans
99
+ */
100
+ for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
101
+ if ( $plans[ $i ]->is_free() ) {
102
+ return true;
103
+ }
104
+ }
105
+
106
+ return false;
107
+ }
108
+
109
+ /**
110
+ * Find all plans that have trial.
111
+ *
112
+ * @author Vova Feldman (@svovaf)
113
+ * @since 1.0.9
114
+ *
115
+ * @param FS_Plugin_Plan[] $plans
116
+ *
117
+ * @return FS_Plugin_Plan[]
118
+ */
119
+ function get_trial_plans( $plans ) {
120
+ $trial_plans = array();
121
+
122
+ if ( is_array( $plans ) && 0 < count( $plans ) ) {
123
+ /**
124
+ * @var FS_Plugin_Plan[] $plans
125
+ */
126
+ for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
127
+ if ( $plans[ $i ]->has_trial() ) {
128
+ $trial_plans[] = $plans[ $i ];
129
+ }
130
+ }
131
+ }
132
+
133
+ return $trial_plans;
134
+ }
135
+
136
+ /**
137
+ * Check if plugin has any trial plan.
138
+ *
139
+ * @author Vova Feldman (@svovaf)
140
+ * @since 1.0.9
141
+ *
142
+ * @param FS_Plugin_Plan[] $plans
143
+ *
144
+ * @return bool
145
+ */
146
+ function has_trial_plan( $plans ) {
147
+ if ( ! is_array( $plans ) || 0 === count( $plans ) ) {
148
+ return true;
149
+ }
150
+
151
+ /**
152
+ * @var FS_Plugin_Plan[] $plans
153
+ */
154
+ for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
155
+ if ( $plans[ $i ]->has_trial() ) {
156
+ return true;
157
+ }
158
+ }
159
+
160
+ return false;
161
+ }
162
  }
freemius/includes/managers/class-fs-plugin-manager.php CHANGED
@@ -1,220 +1,220 @@
1
- <?php
2
- /**
3
- * @package Freemius
4
- * @copyright Copyright (c) 2015, Freemius, Inc.
5
- * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
- * @since 1.0.6
7
- */
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit;
11
- }
12
-
13
- class FS_Plugin_Manager {
14
- /**
15
- * @since 1.2.2
16
- *
17
- * @var string|number
18
- */
19
- protected $_module_id;
20
- /**
21
- * @since 1.2.2
22
- *
23
- * @var FS_Plugin
24
- */
25
- protected $_module;
26
-
27
- /**
28
- * @var FS_Plugin_Manager[]
29
- */
30
- private static $_instances = array();
31
- /**
32
- * @var FS_Logger
33
- */
34
- protected $_logger;
35
-
36
- /**
37
- * Option names
38
- *
39
- * @author Leo Fajardo (@leorw)
40
- * @since 1.2.2
41
- */
42
- const OPTION_NAME_PLUGINS = 'plugins';
43
- const OPTION_NAME_THEMES = 'themes';
44
-
45
- /**
46
- * @param string|number $module_id
47
- *
48
- * @return FS_Plugin_Manager
49
- */
50
- static function instance( $module_id ) {
51
- $key = 'm_' . $module_id;
52
-
53
- if ( ! isset( self::$_instances[ $key ] ) ) {
54
- self::$_instances[ $key ] = new FS_Plugin_Manager( $module_id );
55
- }
56
-
57
- return self::$_instances[ $key ];
58
- }
59
-
60
- /**
61
- * @param string|number $module_id
62
- */
63
- protected function __construct( $module_id ) {
64
- $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $module_id . '_' . 'plugins', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
65
- $this->_module_id = $module_id;
66
-
67
- $this->load();
68
- }
69
-
70
- protected function get_option_manager() {
71
- return FS_Option_Manager::get_manager( WP_FS__ACCOUNTS_OPTION_NAME, true );
72
- }
73
-
74
- /**
75
- * @author Leo Fajardo (@leorw)
76
- * @since 1.2.2
77
- *
78
- * @param string|false $module_type "plugin", "theme", or "false" for all modules.
79
- *
80
- * @return array
81
- */
82
- protected function get_all_modules( $module_type = false ) {
83
- $option_manager = $this->get_option_manager();
84
-
85
- if ( false !== $module_type ) {
86
- return $option_manager->get_option( $module_type . 's', array() );
87
- }
88
-
89
- return array(
90
- self::OPTION_NAME_PLUGINS => $option_manager->get_option( self::OPTION_NAME_PLUGINS, array() ),
91
- self::OPTION_NAME_THEMES => $option_manager->get_option( self::OPTION_NAME_THEMES, array() ),
92
- );
93
- }
94
-
95
- /**
96
- * Load plugin data from local DB.
97
- *
98
- * @author Vova Feldman (@svovaf)
99
- * @since 1.0.6
100
- */
101
- function load() {
102
- $all_modules = $this->get_all_modules();
103
-
104
- if ( ! is_numeric( $this->_module_id ) ) {
105
- unset( $all_modules[ self::OPTION_NAME_THEMES ] );
106
- }
107
-
108
- foreach ( $all_modules as $modules ) {
109
- /**
110
- * @since 1.2.2
111
- *
112
- * @var $modules FS_Plugin[]
113
- */
114
- foreach ( $modules as $module ) {
115
- $found_module = false;
116
-
117
- /**
118
- * If module ID is not numeric, it must be a plugin's slug.
119
- *
120
- * @author Leo Fajardo (@leorw)
121
- * @since 1.2.2
122
- */
123
- if ( ! is_numeric( $this->_module_id ) ) {
124
- if ( $this->_module_id === $module->slug ) {
125
- $this->_module_id = $module->id;
126
- $found_module = true;
127
- }
128
- } else if ( $this->_module_id == $module->id ) {
129
- $found_module = true;
130
- }
131
-
132
- if ( $found_module ) {
133
- $this->_module = $module;
134
- break;
135
- }
136
- }
137
- }
138
- }
139
-
140
- /**
141
- * Store plugin on local DB.
142
- *
143
- * @author Vova Feldman (@svovaf)
144
- * @since 1.0.6
145
- *
146
- * @param bool|FS_Plugin $module
147
- * @param bool $flush
148
- *
149
- * @return bool|\FS_Plugin
150
- */
151
- function store( $module = false, $flush = true ) {
152
- if ( false !== $module ) {
153
- $this->_module = $module;
154
- }
155
-
156
- $all_modules = $this->get_all_modules( $this->_module->type );
157
- $all_modules[ $this->_module->slug ] = $this->_module;
158
-
159
- $options_manager = $this->get_option_manager();
160
- $options_manager->set_option( $this->_module->type . 's', $all_modules, $flush );
161
-
162
- return $this->_module;
163
- }