Simple Lightbox - Version 2.7.1

Version Description

  • Update: Confirm compatibility with WordPress 5.0+
  • Optimize: Improved support for captions generated by Block Editor.
Download this release

Release Info

Developer Archetyped
Plugin Icon wp plugin Simple Lightbox
Version 2.7.1
Comparing to
See all releases

Code changes from version 2.0 to 2.7.1

Files changed (87) hide show
  1. .gitignore +1 -0
  2. Gruntfile.js +49 -0
  3. assets/screenshot-1.png +0 -0
  4. assets/screenshot-2.jpg +0 -0
  5. assets/screenshot-3.jpg +0 -0
  6. changelog.txt +319 -0
  7. client/config.rb +0 -24
  8. client/css/admin.css +1 -1
  9. client/css/app.css +1 -0
  10. client/js/{lib.admin.js → dev/lib.admin.js} +5 -9
  11. client/js/dev/lib.core.js +934 -0
  12. client/js/{lib.view.js → dev/lib.view.js} +1183 -1129
  13. client/js/lib.core.js +0 -591
  14. client/js/prod/lib.admin.js +1 -0
  15. client/js/prod/lib.core.js +1 -0
  16. client/js/prod/lib.view.js +1 -0
  17. client/sass/admin.scss +23 -0
  18. client/sass/app.scss +10 -0
  19. content-handlers/image/handler.image.js +0 -29
  20. content-handlers/image/js/dev/handler.image.js +33 -0
  21. content-handlers/image/js/prod/handler.image.js +1 -0
  22. controller.php +1654 -0
  23. functions.php +24 -0
  24. grunt/jshint.js +38 -0
  25. grunt/phplint.js +14 -0
  26. grunt/sass.js +34 -0
  27. grunt/uglify.js +21 -0
  28. grunt/watch.js +57 -0
  29. includes/class-requirements-check.php +168 -0
  30. includes/class.admin.php +182 -1097
  31. includes/class.admin_action.php +109 -0
  32. includes/class.admin_menu.php +40 -0
  33. includes/class.admin_page.php +182 -0
  34. includes/class.admin_section.php +51 -0
  35. includes/class.admin_view.php +529 -0
  36. includes/class.base.php +96 -64
  37. includes/class.base_collection.php +33 -33
  38. includes/class.base_object.php +71 -11
  39. includes/class.component.php +72 -26
  40. includes/class.content_handler.php +27 -2
  41. includes/class.content_handlers.php +86 -37
  42. includes/class.field.php +2 -0
  43. includes/class.field_base.php +1048 -0
  44. includes/class.field_collection.php +764 -0
  45. includes/class.field_type.php +405 -0
  46. includes/class.fields.php +59 -2237
  47. includes/class.option.php +179 -0
  48. includes/class.options.php +229 -261
  49. includes/class.template_tags.php +43 -22
  50. includes/class.theme.php +22 -16
  51. includes/class.themes.php +127 -56
  52. includes/class.utilities.php +552 -592
  53. l10n/simple-lightbox.pot +151 -109
  54. load.php +38 -0
  55. main.php +39 -48
  56. model.php +0 -1097
  57. package-lock.json +2941 -0
  58. package.json +20 -0
  59. readme.txt +30 -152
  60. template-tags/item/js/dev/tag.item.js +26 -0
  61. template-tags/item/js/prod/tag.item.js +1 -0
  62. template-tags/item/tag.item.js +0 -17
  63. template-tags/ui/js/dev/tag.ui.js +105 -0
  64. template-tags/ui/js/prod/tag.ui.js +1 -0
  65. template-tags/ui/tag.ui.js +0 -108
  66. themes/baseline/css/style.css +1 -0
  67. themes/baseline/images/loading.gif +0 -0
  68. themes/baseline/js/dev/client.js +37 -0
  69. themes/baseline/js/prod/client.js +1 -0
  70. themes/{default → baseline}/layout.html +22 -14
  71. themes/baseline/sass/style.scss +176 -0
  72. themes/black/config.rb +0 -23
  73. themes/black/css/style.css +1 -1
  74. themes/black/sass/style.scss +10 -7
  75. themes/default/client.js +0 -202
  76. themes/default/config.rb +0 -23
  77. themes/default/css/style.css +1 -1
  78. themes/default/fonts/OFL.txt +97 -0
  79. themes/default/fonts/yanone-kaffeesatz-v9-latin-regular.eot +0 -0
  80. themes/default/fonts/yanone-kaffeesatz-v9-latin-regular.svg +407 -0
  81. themes/default/fonts/yanone-kaffeesatz-v9-latin-regular.ttf +0 -0
  82. themes/default/fonts/yanone-kaffeesatz-v9-latin-regular.woff +0 -0
  83. themes/default/fonts/yanone-kaffeesatz-v9-latin-regular.woff2 +0 -0
  84. themes/default/js/dev/client.js +184 -0
  85. themes/default/js/prod/client.js +1 -0
  86. themes/default/sass/_fonts.scss +13 -0
  87. themes/default/sass/style.scss +77 -180
.gitignore ADDED
@@ -0,0 +1 @@
 
1
+ .vscode
Gruntfile.js ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = function(grunt) {
2
+ // Load tasks
3
+ require('load-grunt-tasks')(grunt);
4
+ // Display task timing
5
+ require('time-grunt')(grunt);
6
+ // Project configuration.
7
+ grunt.initConfig({
8
+ // Metadata
9
+ pkg : grunt.file.readJSON('package.json'),
10
+ // Variables
11
+ paths : {
12
+ // Base dir assets dir
13
+ base : 'client',
14
+
15
+ // PHP assets
16
+ php : {
17
+ files_std : ['*.php', '**/*.php', '!node_modules/**/*.php'], // Standard file match
18
+ files : '<%= paths.php.files_std %>' // Dynamic file match
19
+ },
20
+
21
+ // JavaScript assets
22
+ js : {
23
+ base : 'js', // Base dir
24
+ src : '<%= paths.js.base %>/dev', // Development code
25
+ dest : '<%= paths.js.base %>/prod', // Production code
26
+ files_std : '**/<%= paths.js.src %>/**/*.js', // Standard file match
27
+ files : '<%= paths.js.files_std %>' // Dynamic file match
28
+ },
29
+
30
+ // Sass assets
31
+ sass : {
32
+ src : 'sass', // Source files dir
33
+ dest : 'css', // Compiled files dir
34
+ ext : '.css', // Compiled extension
35
+ target : '*.scss', // Only Sass files in CWD
36
+ exclude : '!_*.scss', // Do not process partials
37
+ base_src : '<%= paths.base %>/<%= paths.sass.src %>', // Base source dir
38
+ base_dest : '<%= paths.base %>/<%= paths.sass.dest %>', // Base compile dir
39
+ }
40
+ },
41
+ });
42
+
43
+ // Load task configurations
44
+ grunt.loadTasks('grunt');
45
+
46
+ // Default Tasks
47
+ grunt.registerTask('build', ['phplint', 'jshint:all', 'uglify', 'sass']);
48
+ grunt.registerTask('watch_all', ['watch:js', 'watch:sass']);
49
+ };
assets/screenshot-1.png DELETED
Binary file
assets/screenshot-2.jpg DELETED
Binary file
assets/screenshot-3.jpg DELETED
Binary file
changelog.txt ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ = 2.7.0 =
2
+
3
+ * Fix: Remove reference to deprecated `screen_icon()` function (The Icon of Finnegan Island)
4
+ * Add: Validate requirements before initialization.
5
+ * Optimize: PHP 7.2+ Compatibility
6
+ * Optimize: Internal code optimizations
7
+ * Themes
8
+ * Add: RTL Support
9
+ * Update: Load font locally
10
+
11
+ = 2.6.0 =
12
+
13
+ * Add: Activate links in native WordPress navigation menus (enable in admin settings)
14
+ * Add: Group menu links separately (enable in admin settings)
15
+ * Optimize: Fallback lightbox title text retrieval (link text)
16
+ * Fix: Undefined variable in `Utilities::get_plugin_base_file()` (The Lost Temple of Xavivars)
17
+
18
+ = 2.5.3 =
19
+
20
+ * Optimize: Entity handling in URIs for different server environments
21
+
22
+ = 2.5.2 =
23
+
24
+ * Fix: Activation when Home page set to static page (Lyra's Static Cling)
25
+ * Optimize: Prep for WordPress language packs
26
+
27
+ = 2.5.1 =
28
+
29
+ * Update: Client-side Utilities library
30
+ * Optimize: Request processing
31
+
32
+ = 2.5.0 =
33
+
34
+ * Fix: Query string removed from URI (A Stern Query)
35
+ * Optimize: Key-based asset data storage/retrieval
36
+ * Optimize: Improved cache usage when processing links
37
+ * Optimize: Refactor image URI detection
38
+
39
+ = 2.4.1 =
40
+
41
+ * Fix: Ungrouped items in empty group (Robert & The Lost Group)
42
+ * Fix: IE8 Support (S.Franzis' Legacy)
43
+ * Optimize: Widget support
44
+ * Optimize: Relative and internal URI handling
45
+ * Optimize: Link activation performance
46
+
47
+ = 2.4.0 =
48
+
49
+ * Update: WordPress version compatibility (v4.2.1)
50
+ * Optimize: Standardize code
51
+ * Optimize: Do not process excerpt content
52
+ * Optimize: Client-side libraries (Phase 1)
53
+ * Add: Set group via `slb_activate()`
54
+ * Add: Set group via `activate_links()`
55
+ * Add: `slb_is_enabled` filter
56
+
57
+ = 2.3.1 =
58
+
59
+ * Fix: WordPress version requirement
60
+ * Optimize: Field collection group parsing
61
+
62
+ = 2.3.0 =
63
+ [Full Release Notes](http://archetyped.com/lab/slb-2-3-0 "Simple Lightbox 2.3.0")
64
+
65
+ * Update: WordPress 3.9 support
66
+ * Update: Support URI, content
67
+ * Add: Enhanced grouping support
68
+ * Add: Shortcode: `[slb_group]`
69
+ * Add: Shortcode: `[slb_exclude]`
70
+ * Add: Filter: `slb_pre_process_links`
71
+ * Add: Filter: `slb_post_process_links`
72
+ * Add: Filter: `slb_process_link_attributes`
73
+ * Add: Filter: `slb_media_item_properties`
74
+ * Add: Filter: `slb_pre_exclude_content`
75
+ * Add: Filter: `slb_exclude_shortcodes`
76
+ * Add: Filter: `slb_group_shortcodes`
77
+ * Add: Template Tag: `slb_activate()` - Manually activate content
78
+ * Add: Option to enable/disable usage of WordPress-generated media title
79
+ * Add: Dev mode
80
+ * Add: Theme breakpoints
81
+ * Optimize: Remove deprecated code
82
+ * Optimize: Remove deprecated legacy support
83
+ * Optimize: Content exclusion performance
84
+ * Optimize: Content grouping performance
85
+ * Optimize: Harden code against third-party post query modifications
86
+ * Optimize: Utility code
87
+ * Optimize: Loading process
88
+ * Optimize: Client-side code
89
+ * Optimize: Client-side: Code loading
90
+ * Optimize: Client-side: Simplified dependency detection
91
+ * Optimize: Client-side: Default Theme transitions
92
+ * Optimize: Grunt: Cleanup
93
+ * Optimize: Grunt: Path abstraction
94
+ * Optimize: Grunt: Task loading
95
+ * Optimize: Grunt: Selective file compilation
96
+
97
+ = 2.2.2 =
98
+
99
+ * Optimize: Widget processing
100
+ * Optimize: Remove call-time-pass-by-references
101
+
102
+ = 2.2.1 =
103
+
104
+ * Fix: Enable/Disable lightbox on certain requests (Danny the Enabler)
105
+ * Fix: Widget links grouped with post links (Rafa's Widgetarian Adventure)
106
+ * Optimize: Client-side loading
107
+ * Optimize: Theme validation
108
+ * Optimize: Widget processing
109
+
110
+ = 2.2.0 =
111
+
112
+ * Update: WordPress 3.8 support
113
+ * Add: Add-on support
114
+ * Add: Load external data for item
115
+ * Add: Unloading process for viewer
116
+ * Add: Relative links marked as "internal"
117
+ * Add: Grunt build workflow
118
+ * Optimize: Initialization process
119
+ * Optimize: Client-side output (JavaScript, CSS)
120
+ * Optimize: Improved URI handling (variants, query strings, etc.)
121
+ * Optimize: Improved support for content types (video, etc.)
122
+ * Optimize: Improved File contents retrieval
123
+ * Optimize: Plugin metadata cleanup
124
+ * Optimize: Use absolute paths for file includes (props k3davis)
125
+
126
+ = 2.1.3 =
127
+
128
+ * Fix: PHP configuration issue on some web hosts (Tim's got (config) issues)
129
+ * Optimize: Hide overlapping elements when lightbox is displayed (e.g. Flash, etc.)
130
+
131
+ = 2.1.2 =
132
+
133
+ * Fix: Incorrect paths when WP in subdirectory (Kim's Van Repair)
134
+
135
+ = 2.1.1 =
136
+
137
+ * Fix: Automatic resizing
138
+ * Fix: Compatibility with non-standard wp-content location (On the Path of the Wijdemans)
139
+ * Optimize: jQuery dependency handling
140
+ * Optimize: Plugin initialization
141
+ * Optimize: Deferred component stylesheet loading
142
+ * Optimize: Code cleanup
143
+
144
+ = 2.1 =
145
+
146
+ * Update: Finalized Theme API
147
+ * Update: Finalized Content Handler API
148
+ * Update: Finalized Template Tag API
149
+ * Update: Administration framework
150
+ * Add: Baseline theme
151
+ * Add: Hook for extending image link matching
152
+ * Optimize: Link validation
153
+ * Optimize: Intelligent client-side loading
154
+ * Optimize: Server-side processing
155
+ * Optimize: Default theme display
156
+ * Fix: False positive link activation (What's eating Gilbert's links?)
157
+ * Fix: Gallery post format compatibility (Just Juan problem with galleries)
158
+
159
+ = 2.0 =
160
+
161
+ * Completely rewritten lightbox code
162
+ * Add: Automatically resize lightbox to fit window
163
+ * Add: APIs for third-party add-ons
164
+ * Add: Flexible theme support
165
+ * Add: Flexible content handler support
166
+ * Add: Mobile-optimized responsive themes (2)
167
+ * Optimize: PHP class autoloading
168
+ * Optimize: Improved performance and compatibility
169
+ * Optimize: Full internationalization support
170
+
171
+ = 1.6 =
172
+
173
+ * Add: Widget support
174
+ * Add: WordPress 3.3 support
175
+ * Add: Localization support
176
+ * Add: Option to group gallery links separately (supports WordPress & NextGen galleries)
177
+ * Add: Upgrade notice
178
+ * Optimize: WP 3.3 compatibility
179
+ * Optimize: Improved compatibility with URI case-sensitivity
180
+ * Optimize: Activation processing
181
+ * Optimize: Image grouping
182
+ * Optimize: Image metadata loading performance
183
+ * Optimize: File loading
184
+ * Optimize: Improved safeguards against interference by bugs in other plugins
185
+ * Optimize: Link processing performance
186
+ * Optimize: Lightbox styling isolated from site styles
187
+ * Optimize: Improved link processing performance
188
+ * Optimize: Improved image metadata support
189
+ * Optimize: Improved support for HTTP/HTTPS requests
190
+ * Fix: SLB is not defined in JS (Jezz Hands)
191
+ * Fix: Boolean case-sensitivity (78 Truths)
192
+ * Fix: YouTube embed using iFrame overlaps lightbox (Elena in Hiding)
193
+ * Fix: Issue when scanning links without valid URLs (McCloskey Iteration)
194
+ * Fix: Image activation is case-sensitive (Sensitive Tanya)
195
+ * Fix: Visible lightbox overlay edges when image larger than browser window (Chibi Overlay)
196
+ * Fix: Options availability for some users
197
+ * Fix: Inconsistent loading of image metadata
198
+ * Fix: Links not fully processed when group is set manually
199
+
200
+ = 1.5.6 =
201
+
202
+ * Add: Display image description in lightbox (with HTML support)
203
+ * Add: Support for W3 Total Cache plugin
204
+ * Add: Initial support for NextGEN galleries
205
+ * Update: **Important:** [System Requirements](http://wordpress.org/about/requirements/) aligned with WP 3.2.1
206
+ * Optimize: Improved support for small images in default template
207
+ * Optimize: Support for non-English text in user options
208
+ * Optimize: Improved IE compatibility
209
+ * Optimize: Improved data handling
210
+ * Optimize: Skin loading performance
211
+ * Optimize: Skin CSS Cleanup
212
+ * Optimize: Caption support for galleries
213
+ * Optimize: Options code cleanup (Juga Sweep)
214
+ * Fix: User-defined UI text not used (Ivan gets Even (cooler))
215
+ * Fix: Options reset after update (KRazy Donna)
216
+
217
+ = 1.5.5.1 =
218
+
219
+ * Fix: Disabled links not being disabled (Disabling Sascha)
220
+
221
+ = 1.5.5 =
222
+
223
+ * Add: Distinct link activation (will not affect other lightboxes)
224
+ * Add: Backwards compatibility with legacy lightbox links (optional)
225
+ * Add: Support for WordPress 3.2
226
+ * Add: Support for links added after page load (e.g. via AJAX, etc.)
227
+ * Add: Admin option to enable/disable attachment links
228
+ * Add: Support for image attachment links
229
+ * Update: Options management overhaul
230
+ * Update: Additional WordPress 3.2 support (Gallery)
231
+ * Update: Cache-management for enqueued files
232
+ * Update: Improved UI consistency
233
+ * Update: Improved compatibility for older versions of PHP
234
+ * Update: Internal optimizations
235
+ * Update: Improved URL handling
236
+ * Fix: Improved options migration from old versions (Hutchison Migration)
237
+ * Fix: XHTML Validation (Hajo Validation)
238
+
239
+ = 1.5.4 =
240
+
241
+ * Add: Optional Link validation
242
+ * Add: Keyboard Navigation
243
+ * Add: Option to enable/disable image caption
244
+ * Add: `rel` attribute supported again
245
+ * Add: Use `slb_off` in link's `rel` attribute to disable automatic activation for link
246
+ * Fix: HTTPS compatibility (J&uuml;rgen Protocol)
247
+ * Fix: Enabling SLB on Pages issue
248
+ * Fix: Zmanu is_single
249
+ * Fix: Image order is sometimes incorrect
250
+ * Optimize: Filter double clicks
251
+ * Optimize: Separate options to enable/disable SLB on Posts and Pages
252
+ * Optimize: Better grouping support
253
+
254
+ = 1.5.3 =
255
+
256
+ * Fix: Caption may not display under certain circumstances (Caption Erin)
257
+ * Fix: Images not grouped when "separate by post" option is activated (Logical Ross)
258
+ * Update: Lightbox will not be activated for links that already have `rel` attribute set
259
+
260
+ = 1.5.2 =
261
+
262
+ * Fix: Slideshow loops out of control (Mirage of Wallentin)
263
+ * Fix: Lightbox fails when group by posts disabled (Lange Find)
264
+ * Add: Option to use the image's URI as caption when link title not set (Under UI options)
265
+
266
+ = 1.5.1 =
267
+
268
+ * Add: WP Gallery support
269
+ * Fix: Navigation hidden when only one image
270
+ * Fix: Use user-defined UI text
271
+
272
+ = 1.5 =
273
+
274
+ * Add: Theme support
275
+ * Optimize: JavaScript cleanup and file size reductions
276
+ * Optimize: CSS cleanup
277
+
278
+ = 1.4 =
279
+
280
+ * Update: Integrated with jQuery
281
+ * Optimize: JavaScript file size 9x smaller
282
+ * Add: Close lightbox by clicking to left/right outside of image (an oft-requested feature)
283
+
284
+ = 1.3.2 =
285
+
286
+ * Add: Option to enable/disable lightbox resizing animation (thanks Maria!)
287
+
288
+ = 1.3.1 =
289
+
290
+ * Update: Utilities code (internal)
291
+
292
+ = 1.3 =
293
+
294
+ * Add: Customizable UI label text (close, next, and previous button images can be replaced in `images` directory)
295
+ * Add: Group image links by Post (separate slideshow for each post)
296
+ * Add: Reset settings link on plugin listings page
297
+ * Optimize: Organized settings page
298
+
299
+ = 1.2.1 =
300
+
301
+ * Fixed: Image title given higher precedence than Image alt (more compatible w/WP workflow)
302
+
303
+ = 1.2 =
304
+
305
+ * Added: Option to group automatically activated links
306
+ * Optimized: Lightbox caption retrieval
307
+
308
+ = 1.1 =
309
+
310
+ * Added: Enable/disable lightbox functionality by page type (Home, Pages/Posts, Archive, etc.)
311
+ * Added: Automatically activate lightbox functionality for image links
312
+ * Added: Link to settings menu on plugin listing page
313
+ * Optimized: Options menu field building
314
+ * Optimized: Loading of default values for plugin options
315
+ * Optimized: General code optimizations
316
+
317
+ = 1.0 =
318
+
319
+ * Initial release
client/config.rb DELETED
@@ -1,24 +0,0 @@
1
- # Require any additional compass plugins here.
2
-
3
- # Set this to the root of your project when deployed:
4
- http_path = "/"
5
- css_dir = "css"
6
- sass_dir = "sass"
7
- images_dir = "images"
8
- javascripts_dir = "js"
9
-
10
- # You can select your preferred output style here (can be overridden via the command line):
11
- output_style = :compressed
12
-
13
- # To enable relative paths to assets via compass helper functions. Uncomment:
14
- # relative_assets = true
15
-
16
- # To disable debugging comments that display the original location of your selectors. Uncomment:
17
- # line_comments = false
18
-
19
-
20
- # If you prefer the indented syntax, you might want to regenerate this
21
- # project again passing --syntax sass, or you can uncomment this:
22
- # preferred_syntax = :sass
23
- # and then run:
24
- # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
client/css/admin.css CHANGED
@@ -1 +1 @@
1
- .slb_section_head{display:block;padding:2em 0 0}.slb_option_item .block{display:inline-block}.slb_option_item label.title{width:200px;padding:10px}.slb_option_item .input{font-size:11px;line-height:20px;margin-bottom:9px;padding:8px 10px}.slb_option_item .input select{min-width:12em}.slb_notice{color:#f00;font-weight:bold}
1
+ .slb_section_head{display:block;padding:2em 0 0}.slb_option_item .block{display:inline-block}.slb_option_item label.title{width:200px;padding:10px}.slb_option_item .input{font-size:11px;line-height:20px;margin-bottom:9px;padding:8px 10px}.slb_option_item .input select{min-width:12em}.slb_notice{color:#f00;font-weight:bold}.slb .columns-2{margin-right:300px}.slb .columns-2 .postbox-container{float:left;width:100%}.slb .columns-2 .content-secondary{margin-right:-300px;width:280px;float:right}.slb_admin_action_reset{color:#a00}.slb_admin_action_reset:hover{color:#dc3232;border:none}
client/css/app.css ADDED
@@ -0,0 +1 @@
 
1
+ html.slb_overlay object,html.slb_overlay embed,html.slb_overlay iframe{visibility:hidden}html.slb_overlay #slb_viewer_wrap object,html.slb_overlay #slb_viewer_wrap embed,html.slb_overlay #slb_viewer_wrap iframe{visibility:visible}
client/js/{lib.admin.js → dev/lib.admin.js} RENAMED
@@ -5,13 +5,11 @@
5
  * @author Archetyped
6
  */
7
 
8
- (function ($) {
9
 
10
- if ( !SLB || !SLB.attach ) {
11
- return false;
12
- }
13
 
14
- var Admin = {
15
  /**
16
  * Initialization routines
17
  */
@@ -20,12 +18,10 @@ var Admin = {
20
  postboxes.add_postbox_toggles(pagenow);
21
  }
22
  },
23
- }
24
-
25
- SLB.attach('Admin', Admin);
26
 
27
  $(document).ready(function() {
28
  SLB.Admin.init();
29
  });
30
 
31
- })(jQuery);
5
  * @author Archetyped
6
  */
7
 
8
+ /* global SLB, postboxes, pagenow */
9
 
10
+ if ( !!window.SLB && !!SLB.attach ) { (function ($) {
 
 
11
 
12
+ SLB.attach('Admin', {
13
  /**
14
  * Initialization routines
15
  */
18
  postboxes.add_postbox_toggles(pagenow);
19
  }
20
  },
21
+ });
 
 
22
 
23
  $(document).ready(function() {
24
  SLB.Admin.init();
25
  });
26
 
27
+ })(jQuery);}
client/js/dev/lib.core.js ADDED
@@ -0,0 +1,934 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Core
3
+ * @package SLB
4
+ * @author Archetyped
5
+ */
6
+ if ( window.jQuery ){(function($) {
7
+ 'use strict';
8
+
9
+ /**
10
+ * Extendible class
11
+ * Adapted from John Resig
12
+ * @link http://ejohn.org/blog/simple-javascript-inheritance/
13
+ */
14
+ var c_init = false;
15
+ var Class = function() {};
16
+
17
+ /**
18
+ * Create class that extends another class
19
+ * @param object members Child class' properties
20
+ * @return function New class
21
+ */
22
+ Class.extend = function(members) {
23
+ var _super = this.prototype;
24
+
25
+ // Copy instance to prototype
26
+ c_init = true;
27
+ var proto = new this();
28
+ c_init = false;
29
+
30
+ var val, name;
31
+ // Scrub prototype objects (Decouple from super class)
32
+ for ( name in proto ) {
33
+ if ( $.isPlainObject(proto[name]) ) {
34
+ val = $.extend({}, proto[name]);
35
+ proto[name] = val;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Create class method with access to super class method
41
+ * @param string nm Method name
42
+ * @param function fn Class method
43
+ * @return function Class method with access to super class method
44
+ */
45
+ var make_handler = function(nm, fn) {
46
+ return function() {
47
+ // Cache super variable
48
+ var tmp = this._super;
49
+ // Set variable to super class method
50
+ this._super = _super[nm];
51
+ // Call method
52
+ var ret = fn.apply(this, arguments);
53
+ // Restore super variable
54
+ this._super = tmp;
55
+ // Return value
56
+ return ret;
57
+ };
58
+ };
59
+ // Copy properties to Class
60
+ for ( name in members ) {
61
+ // Add access to super class method to methods
62
+ if ( 'function' === typeof members[name] && 'function' === typeof _super[name] ) {
63
+ proto[name] = make_handler(name, members[name]);
64
+ } else {
65
+ // Transfer properties
66
+ // Objects are copied, not referenced
67
+ proto[name] = ( $.isPlainObject(members[name]) ) ? $.extend({}, members[name]) : members[name];
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Class constructor
73
+ * Supports pre-construction initilization (`Class._init()`)
74
+ * Supports passing constructor for new classes (`Class._c()`)
75
+ */
76
+ function Class() {
77
+ if ( !c_init ) {
78
+ // Private initialization
79
+ if ( 'function' === typeof this._init ) {
80
+ this._init.apply(this, arguments);
81
+ }
82
+ // Main Constructor
83
+ if ( 'function' === typeof this._c ) {
84
+ this._c.apply(this, arguments);
85
+ }
86
+ }
87
+ }
88
+
89
+
90
+ // Populate new prototype
91
+ Class.prototype = proto;
92
+
93
+ // Set constructor
94
+ Class.prototype.constructor = Class;
95
+
96
+ // Set extender
97
+ Class.extend = this.extend;
98
+
99
+ // Return function
100
+ return Class;
101
+ };
102
+
103
+ /**
104
+ * Base Class
105
+ */
106
+ var Base = {
107
+ /* Properties */
108
+
109
+ /**
110
+ * Base object flag
111
+ * @var bool
112
+ */
113
+ base: false,
114
+ /**
115
+ * Instance parent
116
+ * @var object
117
+ */
118
+ _parent: null,
119
+ /**
120
+ * Class prefix
121
+ * @var string
122
+ */
123
+ prefix: 'slb',
124
+
125
+ /* Methods */
126
+
127
+ /**
128
+ * Constructor
129
+ * Sets instance parent
130
+ */
131
+ _init: function() {
132
+ this._set_parent();
133
+ },
134
+
135
+ /**
136
+ * Set instance parent
137
+ * Set utilities parent to current instance
138
+ * @param obj p Parent instance
139
+ */
140
+ _set_parent: function(p) {
141
+ if ( this.util.is_set(p) ) {
142
+ this._parent = p;
143
+ }
144
+ this.util._parent = this;
145
+ },
146
+
147
+ /**
148
+ * Attach new member to instance
149
+ * Member can be property (value) or method
150
+ * @param string name Member name
151
+ * @param object data Member data
152
+ * @param bool simple (optional) Save new member as data object or new class instance (Default: new instance)
153
+ * @return obj Attached object
154
+ */
155
+ attach: function(member, data, simple) {
156
+ var ret = data;
157
+ // Validate
158
+ simple = ( typeof simple === 'undefined' ) ? false : !!simple;
159
+ // Add member to instance
160
+ if ( 'string' === $.type(member) ) {
161
+ // Prepare member value
162
+ if ( $.isPlainObject(data) && !simple ) {
163
+ // Set parent reference for attached instance
164
+ data['_parent'] = this;
165
+ // Define new class
166
+ data = this.Class.extend(data);
167
+ }
168
+ // Save member to current instance
169
+ // Initialize new instance if data is a class
170
+ this[member] = ( 'function' === $.type(data) ) ? new data() : data;
171
+ ret = this[member];
172
+ }
173
+ return ret;
174
+ },
175
+
176
+ /**
177
+ * Check for child object
178
+ * Child object can be multi-level (e.g. Child.Level2child.Level3child)
179
+ *
180
+ * @param string child Name of child object
181
+ */
182
+ has_child: function(child) {
183
+ // Validate
184
+ if ( !this.util.is_string(child) ) {
185
+ return false;
186
+ }
187
+
188
+ var children = child.split('.');
189
+ child = null;
190
+ var o = this;
191
+ var x;
192
+ for ( x = 0; x < children.length; x++ ) {
193
+ child = children[x];
194
+ if ( "" === child ) {
195
+ continue;
196
+ }
197
+ if ( this.util.is_obj(o) && o[child] ) {
198
+ o = o[child];
199
+ } else {
200
+ return false;
201
+ }
202
+ }
203
+ return true;
204
+ },
205
+
206
+ /**
207
+ * Check if instance is set as a base
208
+ * @uses base
209
+ * @return bool TRUE if object is set as a base
210
+ */
211
+ is_base: function() {
212
+ return !!this.base;
213
+ },
214
+
215
+ /**
216
+ * Get parent instance
217
+ * @uses `Base._parent` property
218
+ * @return obj Parent instance
219
+ */
220
+ get_parent: function() {
221
+ var p = this._parent;
222
+ // Validate
223
+ if ( !p ) {
224
+ this._parent = {};
225
+ }
226
+ return this._parent;
227
+ }
228
+ };
229
+
230
+ /**
231
+ * Utility methods
232
+ */
233
+ var Utilities = {
234
+ /* Properties */
235
+
236
+ _base: null,
237
+ _parent: null,
238
+
239
+ /* Methods */
240
+
241
+ /* Connections */
242
+
243
+ /**
244
+ * Get base ancestor
245
+ * @return obj Base ancestor
246
+ */
247
+ get_base: function() {
248
+ if ( !this._base ) {
249
+ var p = this.get_parent();
250
+ var p_prev = null;
251
+ var methods = ['is_base', 'get_parent'];
252
+ // Find base ancestor
253
+ // Either oldest ancestor or object explicitly set as a base
254
+ while ( ( p_prev !== p ) && this.is_method(p, methods) && !p.is_base() ) {
255
+ // Save previous parent
256
+ p_prev = p;
257
+ // Get new parent
258
+ p = p.get_parent();
259
+ }
260
+ // Set base
261
+ this._base = p;
262
+ }
263
+ return this._base;
264
+ },
265
+
266
+ /**
267
+ * Get parent object or parent property value
268
+ * @param string prop (optional) Property to retrieve
269
+ * @return obj Parent object or property value
270
+ */
271
+ get_parent: function(prop) {
272
+ var ret = this._parent;
273
+ // Validate
274
+ if ( !ret ) {
275
+ // Set default parent value
276
+ ret = this._parent = {};
277
+ }
278
+ // Get parent property
279
+ if ( this.is_string(prop) ) {
280
+ ret = ( this.in_obj(ret, prop) ) ? ret[prop] : null;
281
+ }
282
+ return ret;
283
+ },
284
+
285
+ /* Prefix */
286
+
287
+ /**
288
+ * Retrieve valid separator
289
+ * If supplied argument is not a valid separator, use default separator
290
+ * @param string (optional) sep Separator text
291
+ * @return string Separator text
292
+ */
293
+ get_sep: function(sep) {
294
+ var sep_default = '_';
295
+ return ( this.is_string(sep, false) ) ? sep : sep_default;
296
+ },
297
+
298
+ /**
299
+ * Retrieve prefix
300
+ * @return string Prefix
301
+ */
302
+ get_prefix: function() {
303
+ var p = this.get_parent('prefix');
304
+ return ( this.is_string(p, false) ) ? p : '';
305
+ },
306
+
307
+ /**
308
+ * Check if string is prefixed
309
+ */
310
+ has_prefix: function(val, sep) {
311
+ return ( this.is_string(val) && 0 === val.indexOf(this.get_prefix() + this.get_sep(sep)) );
312
+ },
313
+
314
+ /**
315
+ * Add Prefix to a string
316
+ * @param string val Value to add prefix to
317
+ * @param string sep (optional) Separator (Default: `_`)
318
+ * @param bool (optional) once If text should only be prefixed once (Default: TRUE)
319
+ */
320
+ add_prefix: function(val, sep, once) {
321
+ // Validate
322
+ if ( !this.is_string(val) ) {
323
+ // Return prefix if value to add prefix to is empty
324
+ return this.get_prefix();
325
+ }
326
+ sep = this.get_sep(sep);
327
+ if ( !this.is_bool(once) ) {
328
+ once = true;
329
+ }
330
+
331
+ return ( once && this.has_prefix(val, sep) ) ? val : [this.get_prefix(), val].join(sep);
332
+ },
333
+
334
+ /**
335
+ * Remove Prefix from a string
336
+ * @param string val Value to add prefix to
337
+ * @param string sep (optional) Separator (Default: `_`)
338
+ * @param bool (optional) once If text should only be prefixed once (Default: true)
339
+ * @return string Original value with prefix removed
340
+ */
341
+ remove_prefix: function(val, sep, once) {
342
+ // Validate parameters
343
+ if ( !this.is_string(val, true) ) {
344
+ return '';
345
+ }
346
+ // Default values
347
+ sep = this.get_sep(sep);
348
+ if ( !this.is_bool(once) ) {
349
+ once = true;
350
+ }
351
+ // Check if string is prefixed
352
+ if ( this.has_prefix(val, sep) ) {
353
+ // Remove prefix
354
+ var prfx = this.get_prefix() + sep;
355
+ do {
356
+ val = val.substr(prfx.length);
357
+ } while ( !once && this.has_prefix(val, sep) );
358
+ }
359
+ return val;
360
+ },
361
+
362
+ /* Attributes */
363
+
364
+ /*
365
+ * Get attribute name
366
+ * @param string attr_base Attribute's base name
367
+ * @return string Fully-formed attribute name
368
+ */
369
+ get_attribute: function(attr_base) {
370
+ // Setup
371
+ var sep = '-';
372
+ var top = 'data';
373
+ // Validate
374
+ var attr = [top, this.get_prefix()].join(sep);
375
+ // Process
376
+ if ( this.is_string(attr_base) && 0 !== attr_base.indexOf(attr + sep) ) {
377
+ attr = [attr, attr_base].join(sep);
378
+ }
379
+ return attr;
380
+ },
381
+
382
+ /* Request */
383
+
384
+ /**
385
+ * Retrieve valid context
386
+ * @return array Context
387
+ */
388
+ get_context: function() {
389
+ // Validate
390
+ var b = this.get_base();
391
+ if ( !$.isArray(b.context) ) {
392
+ b.context = [];
393
+ }
394
+ // Return context
395
+ return b.context;
396
+ },
397
+
398
+ /**
399
+ * Check if a context exists in current request
400
+ * If multiple contexts are supplied, result will be TRUE if at least ONE context exists
401
+ *
402
+ * @param string|array ctx Context to check for
403
+ * @return bool TRUE if context exists, FALSE otherwise
404
+ */
405
+ is_context: function(ctx) {
406
+ // Validate context
407
+ if ( this.is_string(ctx) ) {
408
+ ctx = [ctx];
409
+ }
410
+ return ( this.is_array(ctx) && this.arr_intersect(this.get_context(), ctx).length > 0 );
411
+ },
412
+
413
+ /* Helpers */
414
+
415
+ /**
416
+ * Check if value is set/defined
417
+ * @param mixed val Value to check
418
+ * @return bool TRUE if value is defined
419
+ */
420
+ is_set: function(val) {
421
+ return ( typeof val !== 'undefined' );
422
+ },
423
+
424
+ /**
425
+ * Validate data type
426
+ * @param mixed val Value to validate
427
+ * @param mixed type Data type to compare with (function gets for instance, string checks data type)
428
+ * @param bool nonempty (optional) Check for empty value? (Default: TRUE)
429
+ * @return bool TRUE if Value matches specified data type
430
+ */
431
+ is_type: function(val, type, nonempty) {
432
+ var ret = false;
433
+ if ( this.is_set(val) && null !== val && this.is_set(type) ) {
434
+ switch ( $.type(type) ) {
435
+ case 'function':
436
+ ret = ( val instanceof type ) ? true : false;
437
+ break;
438
+ case 'string':
439
+ ret = ( $.type(val) === type ) ? true : false;
440
+ break;
441
+ default:
442
+ ret = false;
443
+ break;
444
+ }
445
+ }
446
+
447
+ // Validate empty values
448
+ if ( ret && ( !this.is_set(nonempty) || !!nonempty ) ) {
449
+ ret = !this.is_empty(val);
450
+ }
451
+ return ret;
452
+ },
453
+
454
+ /**
455
+ * Check if value is a string
456
+ * @uses is_type()
457
+ * @param mixed value Value to check
458
+ * @param bool nonempty (optional) Check for empty value? (Default: TRUE)
459
+ * @return bool TRUE if value is a valid string
460
+ */
461
+ is_string: function(value, nonempty) {
462
+ return this.is_type(value, 'string', nonempty);
463
+ },
464
+
465
+ /**
466
+ * Check if value is an array
467
+ * @uses is_type()
468
+ * @param mixed value Value to check
469
+ * @param bool nonempty (optional) Check for empty value? (Default: TRUE)
470
+ * @return bool TRUE if value is a valid array
471
+ */
472
+ is_array: function(value, nonempty) {
473
+ return ( this.is_type(value, 'array', nonempty) );
474
+ },
475
+
476
+ /**
477
+ * Check if value is a boolean
478
+ * @uses is_type()
479
+ * @param mixed value Value to check
480
+ * @return bool TRUE if value is a valid boolean
481
+ */
482
+ is_bool: function(value) {
483
+ return this.is_type(value, 'boolean', false);
484
+ },
485
+
486
+ /**
487
+ * Check if value is an object
488
+ * @uses is_type()
489
+ * @param mixed value Value to check
490
+ * @param bool nonempty (optional) Check for empty value? (Default: TRUE)
491
+ * @return bool TRUE if value is a valid object
492
+ */
493
+ is_obj: function(value, nonempty) {
494
+ return this.is_type(value, 'object', nonempty);
495
+ },
496
+
497
+ /**
498
+ * Check if value is a function
499
+ * @uses is_type()
500
+ * @param mixed value Value to check
501
+ * @return bool TRUE if value is a valid function
502
+ */
503
+ is_func: function(value) {
504
+ return this.is_type(value, 'function', false);
505
+ },
506
+
507
+ /**
508
+ * Checks if an object has a method
509
+ * @param obj obj Object to check
510
+ * @param string|array key Name(s) of methods to check for
511
+ * @return bool TRUE if method(s) exist, FALSE otherwise
512
+ */
513
+ is_method: function(obj, key) {
514
+ var ret = false;
515
+ if ( this.is_string(key) ) {
516
+ key = [key];
517
+ }
518
+ if ( this.in_obj(obj, key) ) {
519
+ ret = true;
520
+ var x = 0;
521
+ while ( ret && x < key.length ) {
522
+ ret = this.is_func(obj[key[x]]);
523
+ x++;
524
+ }
525
+ }
526
+ return ret;
527
+ },
528
+
529
+ /**
530
+ * Check if object is instance of a class
531
+ * @param obj obj Instance object
532
+ * @param obj parent Class to compare with
533
+ * @return bool TRUE if object is instance of class
534
+ */
535
+ is_instance: function(obj, parent) {
536
+ if ( !this.is_func(parent) ) {
537
+ return false;
538
+ }
539
+ return ( this.is_obj(obj) && ( obj instanceof parent ) );
540
+ },
541
+
542
+ /**
543
+ * Check if object is class
544
+ * Optionally check if class is sub-class of another class
545
+ * @param func cls Class to check
546
+ * @param func parent (optional) parent class
547
+ * @return bool TRUE if object is valid class (and sub-class if parent is specified)
548
+ */
549
+ is_class: function(cls, parent) {
550
+ // Validate class
551
+ var ret = ( this.is_func(cls) && ( 'prototype' in cls ) );
552
+ // Check parent class
553
+ if ( ret && this.is_set(parent) ) {
554
+ ret = this.is_instance(cls.prototype, parent);
555
+ }
556
+ return ret;
557
+ },
558
+
559
+ /**
560
+ * Check if value is a number
561
+ * @uses is_type()
562
+ * @param mixed value Value to check
563
+ * @param bool nonempty (optional) Check for empty value? (Default: TRUE)
564
+ * @return bool TRUE if value is a valid number
565
+ */
566
+ is_num: function(value, nonempty) {
567
+ var f = {
568
+ 'nan': ( Number.isNaN ) ? Number.isNaN : isNaN,
569
+ 'finite': ( Number.isFinite ) ? Number.isFinite : isFinite
570
+ };
571
+ return ( this.is_type(value, 'number', nonempty) && !f.nan(value) && f.finite(value) );
572
+ },
573
+
574
+ /**
575
+ * Check if value is a integer
576
+ * @uses is_type()
577
+ * @param mixed value Value to check
578
+ * @param bool nonempty (optional) Check for empty value? (Default: TRUE)
579
+ * @return bool TRUE if value is a valid integer
580
+ */
581
+ is_int: function(value, nonempty) {
582
+ return ( this.is_num(value, nonempty) && Math.floor(value) === value );
583
+ },
584
+
585
+ /**
586
+ * Check if value is scalar (string, number, boolean)
587
+ * @uses is_type()
588
+ * @param mixed value Value to check
589
+ * @param bool nonempty (optional) Check for empty value? (Default: TRUE)
590
+ * @return bool TRUE if value is scalar
591
+ */
592
+ is_scalar: function(value, nonempty) {
593
+ return ( this.is_num(value, nonempty) || this.is_string(value, nonempty) || this.is_bool(value) );
594
+ },
595
+
596
+ /**
597
+ * Checks if value is empty
598
+ * @param mixed value Value to check
599
+ * @param string type (optional) Data type
600
+ * @return bool TRUE if value is empty
601
+ */
602
+ is_empty: function(value, type) {
603
+ var ret = false;
604
+ // Check Undefined
605
+ if ( !this.is_set(value) ) {
606
+ ret = true;
607
+ } else {
608
+ // Check standard values
609
+ var empties = [null, "", false, 0];
610
+ var x = 0;
611
+ while ( !ret && x < empties.length ) {
612
+ ret = ( empties[x] === value );
613
+ x++;
614
+ }
615
+ }
616
+
617
+ // Advanced check
618
+ if ( !ret ) {
619
+ // Validate type
620
+ if ( !this.is_set(type) ) {
621
+ type = $.type(value);
622
+ }
623
+ // Type-based check
624
+ if ( this.is_type(value, type, false) ) {
625
+ switch ( type ) {
626
+ case 'string':
627
+ case 'array':
628
+ ret = ( value.length === 0 );
629
+ break;
630
+ case 'number':
631
+ ret = ( value == 0 ); // jshint ignore:line
632
+ break;
633
+ case 'object':
634
+ if ( !$.isPlainObject(value) ) {
635
+ // Custom object. Unable to evaluate emptiness further
636
+ ret = false;
637
+ } else {
638
+ // Evaluate plain object
639
+ if ( Object.getOwnPropertyNames ) {
640
+ // Modern browser check
641
+ ret = ( Object.getOwnPropertyNames(value).length === 0 );
642
+ } else if ( value.hasOwnProperty ) {
643
+ // Legacy browser check
644
+ ret = true;
645
+ for ( var key in value ) {
646
+ if ( value.hasOwnProperty(key) ) {
647
+ ret = false;
648
+ break;
649
+ }
650
+ }
651
+ }
652
+ }
653
+ break;
654
+ }
655
+ } else {
656
+ ret = true;
657
+ }
658
+ }
659
+ return ret;
660
+ },
661
+
662
+ /**
663
+ * Check if object is a jQuery.Promise instance
664
+ * Will also match (but not guarantee) jQuery.Deferred instances
665
+ * @uses is_method()
666
+ * @param obj obj Object to check
667
+ * @return bool TRUE if object is Promise/Deferred
668
+ */
669
+ is_promise: function(obj) {
670
+ return ( this.is_method(obj, ['then', 'done', 'always', 'fail', 'pipe']) );
671
+ },
672
+
673
+ /**
674
+ * Return formatted string
675
+ * @param string fmt Format template
676
+ * @param string val Replacement value (Multiple parameters may be set)
677
+ * @return string Formatted string
678
+ */
679
+ format: function(fmt, val) {
680
+ // Validate format
681
+ if ( !this.is_string(fmt) ) {
682
+ return '';
683
+ }
684
+ var params = [];
685
+ var ph = '%s';
686
+ /**
687
+ * Clean string (remove placeholders)
688
+ */
689
+ var strip = function(txt) {
690
+ return ( txt.indexOf(ph) !== -1 ) ? txt.replace(ph, '') : txt;
691
+ };
692
+ // Stop processing if no replacement values specified or format string contains no placeholders
693
+ if ( arguments.length < 2 || fmt.indexOf(ph) === -1 ) {
694
+ return strip(fmt);
695
+ }
696
+ // Get replacement values
697
+ params = Array.prototype.slice.call(arguments, 1);
698
+ val = null;
699
+ // Clean parameters
700
+ for ( var x = 0; x < params.length; x++ ) {
701
+ if ( !this.is_scalar(params[x], false) ) {
702
+ params[x] = '';
703
+ }
704
+ }
705
+
706
+ // Replace all placeholders at once if single parameter set
707
+ if ( params.length === 1 ) {
708
+ fmt = fmt.replace(ph, params[0].toString());
709
+ } else {
710
+ var idx = 0; // Current replacement index
711
+ var len = params.length; // Number of replacements
712
+ var rlen = ph.length; // Placeholder length
713
+ var pos = 0; // Current placeholder position (in format template)
714
+ while ( ( pos = fmt.indexOf(ph) ) && pos !== -1 && idx < len ) {
715
+ // Replace current placeholder with respective parameter
716
+ fmt = fmt.substr(0, pos) + params[idx].toString() + fmt.substr(pos + rlen);
717
+ idx++;
718
+ }
719
+ // Remove any remaining placeholders
720
+ fmt = strip(fmt);
721
+ }
722
+ return fmt;
723
+ },
724
+
725
+ /**
726
+ * Checks if key(s) exist in an object
727
+ * @param object obj Object to check
728
+ * @param string|array key Key(s) to check for in object
729
+ * @param bool all (optional) All keys must exist in object? (Default: TRUE)
730
+ * @return bool TRUE if key(s) exist in object
731
+ */
732
+ in_obj: function(obj, key, all) {
733
+ // Validate
734
+ if ( !this.is_bool(all) ) {
735
+ all = true;
736
+ }
737
+ if ( this.is_string(key) ) {
738
+ key = [key];
739
+ }
740
+ // Check for keys
741
+ var ret = false;
742
+ if ( this.is_obj(obj) && this.is_array(key) ) {
743
+ var val;
744
+ for ( var x = 0; x < key.length; x++ ) {
745
+ val = key[x];
746
+ ret = ( this.is_string(val) && ( val in obj ) ) ? true : false;
747
+ // Stop processing if conditions have been met
748
+ if ( ( !all && ret ) || ( all && !ret ) ) {
749
+ break;
750
+ }
751
+ }
752
+ }
753
+ return ret;
754
+ },
755
+
756
+ /**
757
+ * Retrieve an object's keys
758
+ * @param obj Object to parse
759
+ * @return array List of object's keys
760
+ */
761
+ obj_keys: function(obj) {
762
+ var keys = [];
763
+ // Validation
764
+ if ( !this.is_obj(obj) ) {
765
+ return keys;
766
+ }
767
+ if ( Object.keys ) {
768
+ keys = Object.keys(obj);
769
+ } else {
770
+ var prop;
771
+ for ( prop in obj ) {
772
+ if ( obj.hasOwnProperty(prop) ) {
773
+ keys.push(prop);
774
+ }
775
+ }
776
+ }
777
+ return keys;
778
+ },
779
+
780
+ /**
781
+ * Find common elements of 2 or more arrays
782
+ * @param array arr1 First array
783
+ * @param array arr2 Second array (additional arrays can be passed as well)
784
+ * @return array Elements common to all
785
+ */
786
+ arr_intersect: function(arr1, arr2) {
787
+ var ret = [];
788
+ // Get arrays
789
+ var params = Array.prototype.slice.call(arguments);
790
+ // Clean arrays
791
+ var arrs = [];
792
+ var x;
793
+ for ( x = 0; x < params.length; x++ ) {
794
+ if ( this.is_array(params[x], false) ) {
795
+ arrs.push(params[x]);
796
+ }
797
+ }
798
+ // Stop processing if no valid arrays to compare
799
+ if ( arrs.length < 2 ) {
800
+ return ret;
801
+ }
802
+ params = arr1 = arr2 = null;
803
+ // Find common elements in arrays
804
+ var base = arrs.shift();
805
+ var add;
806
+ var sub;
807
+ for ( x = 0; x < base.length; x++ ) {
808
+ add = true;
809
+ // Check other arrays for element match
810
+ for ( sub = 0; sub < arrs.length; sub++ ) {
811
+ if ( arrs[sub].indexOf(base[x]) === -1 ) {
812
+ add = false;
813
+ break;
814
+ }
815
+ }
816
+ if ( add ) {
817
+ ret.push(base[x]);
818
+ }
819
+ }
820
+ // Return intersection results
821
+ return ret;
822
+ },
823
+
824
+ /**
825
+ * Generates a GUID string.
826
+ * @returns string The generated GUID.
827
+ * @example af8a8416-6e18-a307-bd9c-f2c947bbb3aa
828
+ * @author Slavik Meltser (slavik@meltser.info).
829
+ * @link http://slavik.meltser.info/?p=142
830
+ */
831
+ guid: function() {
832
+ function _p8(s) {
833
+ var p = (Math.random().toString(16)+"000000000").substr(2,8);
834
+ return s ? "-" + p.substr(0,4) + "-" + p.substr(4,4) : p ;
835
+ }
836
+ return _p8() + _p8(true) + _p8(true) + _p8();
837
+ },
838
+
839
+ /**
840
+ * Parse URI
841
+ * @param string uri URI to parse
842
+ * @return obj URI components (DOM anchor element)
843
+ */
844
+ parse_uri: function(uri) {
845
+ return $('<a href="' + uri + '"/>').get(0);
846
+ },
847
+ /**
848
+ * Parse URI query string
849
+ * @param string uri URI with query string to parse
850
+ * @return obj Query variables and values (empty if no query string)
851
+ */
852
+ parse_query: function(uri) {
853
+ var delim = {
854
+ 'vars': '&',
855
+ 'val': '='
856
+ };
857
+ var query = {
858
+ 'raw': [],
859
+ 'parsed': {},
860
+ 'string': ''
861
+ };
862
+ uri = this.parse_uri(uri);
863
+ if ( 0 === uri.search.indexOf('?') ) {
864
+ // Extract query string
865
+ query.raw = uri.search.substr(1).split(delim.vars);
866
+ var i, temp, key, val;
867
+ // Build query object
868
+ for ( i = 0; i < query.raw.length; i++ ) {
869
+ // Split var and value
870
+ temp = query.raw[i].split(delim.val);
871
+ key = temp.shift();
872
+ val = ( temp.length > 0 ) ? temp.join(delim.val) : null;
873
+ query.parsed[key] = val;
874
+ }
875
+ }
876
+ return query.parsed;
877
+ },
878
+ /**
879
+ * Build query string from object
880
+ * @param obj query Query data
881
+ * @return string Query data formatted as HTTP query string
882
+ */
883
+ build_query: function(query) {
884
+ var q = [];
885
+ var delim = {
886
+ 'vars': '&',
887
+ 'val': '='
888
+ };
889
+ var val;
890
+ for ( var key in query ) {
891
+ val = ( null !== query[key] ) ? delim.val + query[key] : '';
892
+ q.push(key + val);
893
+ }
894
+ return q.join(delim.vars);
895
+ }
896
+ };
897
+
898
+ // Attach Utilities
899
+ Base.attach('util', Utilities, true);
900
+
901
+ /**
902
+ * SLB Base Class
903
+ */
904
+ var SLB_Base = Class.extend(Base);
905
+
906
+ /**
907
+ * Core
908
+ */
909
+ var Core = {
910
+ /* Properties */
911
+
912
+ base: true,
913
+ context: [],
914
+
915
+ /**
916
+ * New object initializer
917
+ * @var obj
918
+ */
919
+ Class: SLB_Base,
920
+
921
+ /* Methods */
922
+
923
+ /**
924
+ * Init
925
+ * Set variables, DOM, etc.
926
+ */
927
+ _init: function() {
928
+ this._super();
929
+ $('html').addClass(this.util.get_prefix());
930
+ }
931
+ };
932
+ var SLB_Core = SLB_Base.extend(Core);
933
+ window.SLB = new SLB_Core();
934
+ })(jQuery);}
client/js/{lib.view.js → dev/lib.view.js} RENAMED
@@ -4,12 +4,8 @@
4
  * @subpackage View
5
  * @author Archetyped
6
  */
7
-
8
- (function ($) {
9
-
10
- if ( typeof SLB == 'undefined' || !SLB.attach ) {
11
- return false;
12
- }
13
 
14
  /*-** Controller **-*/
15
 
@@ -41,21 +37,11 @@ var View = {
41
  */
42
  loading: [],
43
 
44
- /* Component Collections */
45
-
46
- viewers: {},
47
- items: [],
48
- content_handlers: {},
49
- groups: {},
50
- template_tags: {},
51
-
52
  /**
53
- * Collection/Data type mapping
54
- * > Key: Collection name
55
- * > Value: Data type
56
  * @var object
57
  */
58
- collections: {},
59
 
60
  /**
61
  * Temporary component instances
@@ -66,56 +52,74 @@ var View = {
66
  component_temps: {},
67
 
68
  /* Options */
69
- options: {
70
- ui_animate: true,
71
- slideshow_enabled: true,
72
- slideshow_autostart: false,
73
- slideshow_duration: '6'
74
- },
75
 
76
  /* Methods */
77
 
78
  /* Init */
79
 
80
- update_refs: function() {
81
- var c;
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  var r;
83
  var ref;
84
- for ( var p in this ) {
85
- if ( !this.util.is_func(this[p]) || !( '_refs' in this[p].prototype ) ) {
 
 
 
86
  continue;
87
  }
88
- //Set component
89
- c = this[p];
90
- if ( !this.util.is_empty(c.prototype._refs) ) {
91
- for ( r in c.prototype._refs ) {
92
- ref = c.prototype._refs[r];
93
- if ( this.util.is_func(ref) ) {
94
- continue;
95
- }
96
  if ( this.util.is_string(ref) && ref in this ) {
97
- ref = c.prototype._refs[r] = this[ref];
98
  }
99
- if ( !this.util.is_func(ref) ) {
100
- delete c.prototype_refs[r];
101
  }
102
  }
103
  }
104
  }
105
-
106
- /* Initialize components */
107
- this.init_components();
108
  },
109
 
110
  /**
111
- * Initialization
 
 
 
 
 
 
 
 
 
 
112
  */
113
  init: function(options) {
114
  var t = this;
 
115
  $.when.apply($, this.loading).always(function() {
116
- //Set options
117
  $.extend(true, t.options, options);
118
- //History
 
 
 
119
  $(window).on('popstate', function(e) {
120
  var state = e.originalEvent.state;
121
  if ( t.util.in_obj(state, ['item', 'viewer']) ) {
@@ -127,65 +131,47 @@ var View = {
127
 
128
  /* Set defaults */
129
 
130
- //Items
131
  t.init_items();
132
  });
133
  },
134
 
135
- init_components: function() {
136
- this.collections = {
137
- 'viewers': this.Viewer,
138
- 'items': this.Content_Item,
139
- 'content_handlers': this.Content_Handler,
140
- 'groups': this.Group,
141
- 'themes': this.Theme,
142
- 'template_tags': this.Template_Tag
143
- };
144
-
145
- this.component_defaults = [
146
- this.Viewer,
147
- ];
148
-
149
- },
150
-
151
  /* Components */
152
 
153
- component_make_default: function(type) {
154
- var ret = false;
155
- for ( var x = 0; x < this.component_defaults.length; x++ ) {
156
- if ( type == this.component_defaults[x] ) {
157
- ret = true;
158
- break;
159
- }
160
- }
161
- return ret;
162
  },
163
 
164
  /**
165
- * Validates component type
166
- * @param function comp Component type to check
167
- * @return bool TRUE if param is valid component, FALSE otherwise
168
  */
169
- check_component: function(comp) {
170
- //Validate component type
171
- return ( this.util.is_func(comp) && ('_slug' in comp.prototype ) && ( comp.prototype instanceof (this.Component) ) ) ? true : false;
172
  },
173
 
174
  /**
175
  * Retrieve collection of components of specified type
176
- * @param function type Component type
177
- * @return object|array|null Component collection (NULL if invalid)
178
  */
179
  get_components: function(type) {
180
- var ret = null;
181
- if ( this.util.is_func(type) ) {
182
- //Determine collection
183
- for ( var coll in this.collections ) {
184
- if ( type == this.collections[coll] && coll in this ) {
185
- ret = this[coll];
186
- break;
187
- }
188
  }
 
189
  }
190
  return ret;
191
  },
@@ -198,16 +184,16 @@ var View = {
198
  */
199
  get_component: function(type, id) {
200
  var ret = null;
201
- //Validate parameters
202
  if ( !this.util.is_func(type) ) {
203
  return ret;
204
  }
205
- //Sanitize id
206
  if ( !this.util.is_string(id) ) {
207
  id = null;
208
  }
209
 
210
- //Get component from collection
211
  var coll = this.get_components(type);
212
  if ( this.util.is_obj(coll) ) {
213
  var tid = ( this.util.is_string(id) ) ? id : this.util.add_prefix('default');
@@ -216,13 +202,13 @@ var View = {
216
  }
217
  }
218
 
219
- //Default: Create default component
220
  if ( this.util.is_empty(ret) ) {
221
- if ( this.util.is_string(id) || this.component_make_default(type) ) {
222
  ret = this.add_component(type, id);
223
  }
224
  }
225
- //Return component
226
  return ret;
227
  },
228
 
@@ -234,15 +220,15 @@ var View = {
234
  * @return object|null New component (NULL if invalid)
235
  */
236
  add_component: function(type, id, options) {
237
- //Validate type
238
  if ( !this.util.is_func(type) ) {
239
  return false;
240
  }
241
- //Validate request
242
- if ( this.util.is_empty(id) && !this.component_make_default(type) ) {
243
  return false;
244
  }
245
- //Defaults
246
  var ret = null;
247
  if ( this.util.is_empty(id) ) {
248
  id = this.util.add_prefix('default');
@@ -250,21 +236,21 @@ var View = {
250
  if ( !this.util.is_obj(options) ) {
251
  options = {};
252
  }
253
- //Check if specialized method exists for component type
254
- var m = ( 'component' != type.prototype._slug ) ? 'add_' + type.prototype._slug : null;
255
  if ( !this.util.is_empty(m) && ( m in this ) && this.util.is_func(this[m]) ) {
256
  ret = this[m](id, options);
257
  }
258
- //Default process
259
  else {
260
  ret = new type(id, options);
261
  }
262
 
263
- //Add new component to collection
264
  if ( this.util.is_type(ret, type) ) {
265
- //Get collection
266
  var coll = this.get_components(type);
267
- //Add to collection
268
  switch ( $.type(coll) ) {
269
  case 'object' :
270
  coll[id] = ret;
@@ -276,7 +262,7 @@ var View = {
276
  } else {
277
  ret = null;
278
  }
279
- //Return new component
280
  return ret;
281
  },
282
 
@@ -287,10 +273,10 @@ var View = {
287
  */
288
  add_component_temp: function(type) {
289
  var ret = null;
290
- if ( this.check_component(type) ) {
291
- //Create new instance
292
  ret = new type('');
293
- //Save to collection
294
  this.component_temps[ret._slug] = ret;
295
  }
296
  return ret;
@@ -312,7 +298,7 @@ var View = {
312
  * @return bool TRUE if temp instance exists, FALSE otherwise
313
  */
314
  has_component_temp: function(type) {
315
- return ( this.check_component(type) && ( type.prototype._slug in this.component_temps ) ) ? true : false;
316
  },
317
 
318
  /* Properties */
@@ -324,16 +310,16 @@ var View = {
324
  */
325
  get_options: function(opts) {
326
  var ret = {};
327
- //Validate
328
  if ( this.util.is_string(opts) ) {
329
  opts = [opts];
330
  }
331
  if ( !this.util.is_array(opts) ) {
332
  return ret;
333
  }
334
- //Get specified options
335
  for ( var x = 0; x < opts.length; x++ ) {
336
- //Skip if option not set
337
  if ( !( opts[x] in this.options ) ) {
338
  continue;
339
  }
@@ -365,20 +351,14 @@ var View = {
365
  * Add viewer instance to collection
366
  * @param string id Viewer ID
367
  * @param obj options Viewer options
 
368
  */
369
  add_viewer: function(id, options) {
370
- //Validate
371
- if ( !this.util.is_string(id) ) {
372
- return false;
373
- }
374
- if ( !this.util.is_obj(options, false) ) {
375
- options = {};
376
- }
377
- //Create viewer
378
  var v = new this.Viewer(id, options);
379
- //Add to collection
380
- this.viewers[v.get_id()] = v;
381
- //Return viewer
382
  return v;
383
  },
384
 
@@ -387,7 +367,7 @@ var View = {
387
  * @return obj Viewer instances
388
  */
389
  get_viewers: function() {
390
- return this.viewers;
391
  },
392
 
393
  /**
@@ -407,12 +387,13 @@ var View = {
407
  * @return Viewer Viewer instance
408
  */
409
  get_viewer: function(v) {
410
- //Retrieve default viewer if specified viewer not set
411
  if ( !this.has_viewer(v) ) {
412
  v = this.util.add_prefix('default');
413
- //Create default viewer if necessary
414
  if ( !this.has_viewer(v) ) {
415
- this.add_viewer(v);
 
416
  }
417
  }
418
  return this.get_viewers()[v];
@@ -424,7 +405,7 @@ var View = {
424
  * Set event handlers
425
  */
426
  init_items: function() {
427
- //Define handler
428
  var t = this;
429
  var handler = function() {
430
  var ret = t.show_item(this);
@@ -434,12 +415,16 @@ var View = {
434
  return !ret;
435
  };
436
 
437
- //Get activated links
438
  var sel = this.util.format('a[href][%s="%s"]', this.util.get_attribute('active'), 1);
439
- //Add event handler
440
- $(document).on('click', sel, handler);
441
  },
442
 
 
 
 
 
443
  get_items: function() {
444
  return this.get_components(this.Content_Item);
445
  },
@@ -453,30 +438,30 @@ var View = {
453
  * @return Content_Item Item instance for DOM node
454
  */
455
  get_item: function(ref) {
456
- //Evaluate reference type
457
 
458
- //Content Item instance
459
  if ( this.util.is_type(ref, this.Content_Item) ) {
460
  return ref;
461
  }
462
- //Retrieve item instance
463
  var item = null;
464
 
465
- //DOM element
466
  if ( this.util.in_obj(ref, 'nodeType') ) {
467
- //Check if item instance attached to element
468
  var key = this.get_component_temp(this.Content_Item).get_data_key();
469
  item = $(ref).data(key);
470
  }
471
- //Cached item (index)
472
- else if ( this.util.is_int(ref, false) ) {
473
  var items = this.get_items();
474
- if ( items.length > ref ) {
475
  item = items[ref];
476
  }
477
  }
478
- //Create default item instance
479
- if ( !this.util.is_type(item, this.Content_Item) ) {
480
  item = this.add_item(ref);
481
  }
482
  return item;
@@ -488,44 +473,40 @@ var View = {
488
  * @return Content_Item New item instance
489
  */
490
  add_item: function(el) {
491
- var item = new this.Content_Item(el);
492
- return item;
493
  },
494
 
495
  /**
496
  * Display item in viewer
497
  * @param obj el DOM element representing item
 
498
  */
499
  show_item: function(el) {
500
- var ret = this.get_item(el).show();
501
- return ret;
502
  },
503
 
504
  /**
505
  * Cache item instance
506
- * @uses this.items to store cached items
507
  * @param Content_Item item Item to cache
508
- * @return int Index of item in cache
509
  */
510
  save_item: function(item) {
511
- var ret = -1;
512
- if ( !this.util.is_type(item, this.Content_Item) ) {
513
- return ret;
514
- }
515
- var prop = 'items';
516
- var items = this.get_items();
517
- //Check if item exists in collection
518
- ret = $.inArray(item, items);
519
- //Cache item
520
- if ( -1 == ret ) {
521
- ret = items.push(item) - 1;
522
  }
523
- //Return item index in cache
524
- return ret;
 
 
525
  },
526
 
527
  /* Content Handler */
528
 
 
 
 
 
529
  get_content_handlers: function() {
530
  return this.get_components(this.Content_Handler);
531
  },
@@ -536,72 +517,39 @@ var View = {
536
  * @return Content_Handler|null Matching content handler (NULL if no matching handler found)
537
  */
538
  get_content_handler: function(item) {
539
- //Determine handler to retrieve
540
- var type = ( this.util.is_type(item, this.Content_Item) ) ? item.get_attribute('type', '') : item.toString();
541
- //Retrieve handler
542
  var types = this.get_content_handlers();
543
  return ( type in types ) ? types[type] : null;
544
  },
545
 
546
  /**
547
- * Add Content Handler
548
  * @param string id Handler ID
549
- * @param obj attributes (optional) Handler properties/methods
 
550
  */
551
- add_content_handler: function(id, attributes) {
552
- //Validate
553
- if ( !this.util.is_string(id) ) {
554
- return false;
555
  }
556
- var dfr = $.Deferred();
557
- var t = this;
558
-
559
- if ( !this.util.is_obj(attributes, false) ) {
560
- //Check for URI (external loading)
561
- if ( this.util.is_string(attributes) ) {
562
- $.get(attributes).always(function(data, textStatus) {
563
- var r = null;
564
- try {
565
- eval('r = ' + data);
566
- } catch (e) {}
567
- if ( !t.util.is_obj(r) ) {
568
- r = {};
569
- }
570
- dfr.resolve(r);
571
- });
572
- } else {
573
- dfr.resolve({});
574
- }
575
- } else {
576
- dfr.resolve(attributes);
577
  }
578
-
579
- dfr.done(function(o) {
580
- //Save
581
- var types = t.get_components(t.Content_Handler);
582
- if ( !t.util.is_obj(types, false) ) {
583
- types = {};
584
- }
585
- types[id] = new t.Content_Handler(id, o);
586
- });
587
- },
588
-
589
- /**
590
- * Update content handler
591
- * @param string id Handler to update
592
- * @param obj attr Variable number of attribute objects to add handlera
593
- */
594
- update_content_handler: function(id, attr) {
595
- if ( !this.util.is_string(id) || !this.util.is_obj(attr) ) {
596
- return false;
597
  }
598
- //Get existing handler
599
- var h = this.get_content_handler(id);
600
- if ( null == h ) {
601
- return false;
602
  }
603
- //Add additional attributes
604
- h.set_attributes(attr);
605
  },
606
 
607
  /* Group */
@@ -611,14 +559,15 @@ var View = {
611
  * @param string g Group ID
612
  * > If group with same ID already set, new group replaces existing one
613
  * @param object attrs (optional) Group attributes
 
614
  */
615
  add_group: function(g, attrs) {
616
- //Create new group
617
  g = new this.Group(g, attrs);
618
- //Add group to collection
619
- if ( this.util.is_string(g.get_id()) ) {
620
- this.groups[g.get_id()] = g;
621
- }
622
  },
623
 
624
  /**
@@ -627,24 +576,20 @@ var View = {
627
  * @return object Registered groups
628
  */
629
  get_groups: function() {
630
- return this.groups;
631
  },
632
 
633
  /**
634
  * Retrieve specified group
 
 
 
 
635
  * @param string g Group ID
636
- * @return object|null Group instance (NULL if group does not exist)
637
  */
638
  get_group: function(g) {
639
- if ( this.util.is_string(g) ) {
640
- if ( !this.has_group(g) ) {
641
- //Add new group (if necessary)
642
- this.add_group(g);
643
- }
644
- //Retrieve group
645
- g = this.get_groups()[g];
646
- }
647
- return ( this.util.is_type(g, this.Group) ) ? g : null;
648
  },
649
 
650
  /**
@@ -653,132 +598,55 @@ var View = {
653
  * @return bool TRUE if group exists, FALSE otherwise
654
  */
655
  has_group: function(g) {
656
- return ( this.util.is_string(g) && ( g in this.get_groups() ) ) ? true : false;
657
  },
658
 
659
  /* Theme */
660
 
661
  /**
662
- * Add theme
663
  * @param string name Theme name
664
- * @param obj attr Theme options
665
- * > Multiple attribute parameters are merged
666
- * @return obj Theme model
667
  */
668
- add_theme: function(id, attr) {
669
- var t = this;
670
- //Validate
671
  if ( !this.util.is_string(id) ) {
672
  return false;
673
  }
674
  var dfr = $.Deferred();
675
  this.loading.push(dfr);
676
 
677
- //Build attributes
678
- var attrs = [{'parent': null}];
679
- if ( arguments.length >= 2 ) {
680
- var args = Array.prototype.slice.call(arguments, 1);
681
- var t = this;
682
- $.each(args, function(idx, arg) {
683
- if ( t.util.is_obj(arg) ) {
684
- attrs.push(arg)
685
- }
686
- });
687
- }
688
-
689
- //Set ID
690
- attrs.push({'id': id});
691
-
692
- //Create theme model
693
- var model = $.extend.apply(null, attrs);
694
 
695
- //Connect to parent model
696
- if ( this.util.is_string(model.parent) ) {
697
- model.parent = this.get_theme_model(model.parent);
 
698
  }
699
 
700
- /* Process attributes */
701
-
702
- //Layout
703
- var prop_layout = 'layout_uri';
704
- var dfr_layout = $.Deferred();
705
- if ( prop_layout in model && this.util.is_string(model[prop_layout]) ) {
706
- $.get(model[prop_layout]).always(function(data) {
707
- //Set layout (raw) attribute
708
- if ( t.util.is_string(data) ) {
709
- model['layout_raw'] = data;
710
- }
711
- dfr_layout.resolve();
712
- });
713
- } else {
714
- dfr_layout.resolve();
715
  }
716
 
717
- //Attributes (external)
718
- var prop_script = 'client_script';
719
- var dfr_script = $.Deferred();
720
- if ( prop_script in model && this.util.is_string(model[prop_script]) ) {
721
- //Retrieve client script
722
- $.get(model[prop_script]).always(function(data) {
723
- var r = null;
724
- try {
725
- eval('r = ' + data);
726
- } catch (e) {}
727
- if ( t.util.is_obj(r) ) {
728
- //Add attributes to model
729
- $.extend(model, r);
730
- }
731
- dfr_script.resolve();
732
- });
733
- } else {
734
- dfr_script.resolve();
735
  }
736
 
737
- //Styles
738
- var prop_style = 'client_style';
739
- if ( prop_style in model && this.util.is_string(model[prop_style]) ) {
740
- //Add stylesheet to document
741
- $('<link />', {
742
- 'id': 'theme_style_' + model.id,
743
- 'href': model[prop_style],
744
- 'type': 'text/css',
745
- 'rel': 'stylesheet'
746
- }).appendTo('body');
747
  }
748
 
749
- //Complete loading when all components loaded
750
- $.when(dfr_layout, dfr_script).always(function() {
751
- dfr.resolve();
752
- });
753
- //Add theme model
754
- this.Theme.prototype._models[id] = model;
755
- return model;
756
- },
757
-
758
- /**
759
- * Update theme model
760
- * @param string id Theme to update
761
- * @param obj attr Variable number of attribute objects to add to model
762
- */
763
- update_theme: function(id, attr) {
764
- var model = this.get_theme_model(id);
765
- var args = Array.prototype.slice.call(arguments);
766
- if ( this.util.is_empty(model) ) {
767
- model = this.add_model.apply(this, args);
768
- } else {
769
- //Process attributes
770
- args.shift();
771
- var attrs = [];
772
- var t = this;
773
- $.each(args, function(idx, arg) {
774
- if ( t.util.is_obj(arg) ) {
775
- attrs.push(arg);
776
- }
777
- });
778
- //Merge attributes into model
779
- attrs.unshift(model);
780
- $.extend.apply($, attrs);
781
- }
782
  return model;
783
  },
784
 
@@ -787,7 +655,7 @@ var View = {
787
  * @return obj Theme models
788
  */
789
  get_theme_models: function() {
790
- //Retrieve matching theme model
791
  return this.Theme.prototype._models;
792
  },
793
 
@@ -802,42 +670,49 @@ var View = {
802
  },
803
 
804
  /**
805
- * Add Theme Tag Handler to Theme prototype
806
- * @param string id Unique ID
807
- * @param obj attrs (optional) Default tag attributes/values (or URI to attributes definition)
 
808
  */
809
- add_template_tag_handler: function(id, attributes) {
810
- //Validate
811
- if ( !this.util.is_string(id) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
812
  return false;
813
  }
814
- var dfr = $.Deferred();
815
- var t = this;
816
-
817
- if ( !this.util.is_obj(attributes, false) ) {
818
- //Check for URI (external loading)
819
- if ( this.util.is_string(attributes) ) {
820
- $.get(attributes).always(function(data) {
821
- var r = null;
822
- try {
823
- eval('r = ' + data);
824
- } catch (e) {}
825
- if ( !t.util.is_obj(r) ) {
826
- r = {};
827
- }
828
- dfr.resolve(r);
829
- });
830
- } else {
831
- dfr.resolve({});
832
- }
833
  } else {
834
- dfr.resolve(attributes);
 
 
835
  }
836
-
837
- dfr.done(function(o) {
838
- //Add handler
839
- t.get_template_tag_handlers()[id] = new t.Template_Tag_Handler(id, o);
840
- });
 
 
 
 
841
  },
842
 
843
  /**
@@ -851,16 +726,31 @@ var View = {
851
  /**
852
  * Retrieve template tag handler
853
  * @param string id ID of tag handler to retrieve
854
- * @return Template_Tag_Handler Tag Handler instance (new instance for invalid ID)
855
  */
856
  get_template_tag_handler: function(id) {
857
  var handlers = this.get_template_tag_handlers();
858
- //Retrieve existing handler
859
- if ( this.util.is_string(id) && ( id in handlers ) ) {
860
- return handlers[id];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
  }
862
- //Default: Return empty handler
863
- return new this.Template_Tag_Handler(id, {});
864
  }
865
  };
866
 
@@ -876,23 +766,20 @@ var Component = {
876
  */
877
  _slug: 'component',
878
 
 
 
 
 
 
 
879
  /**
880
  * Valid component references for current component
 
881
  * > Key (string): Property name that stores reference
882
  * > Value (function): Data type of component
883
- * @var object
884
  */
885
  _refs: {},
886
 
887
- /**
888
- * Components that may contain current object
889
- * Used for retrieving data from a parent object
890
- * Example: An Item may be contained by a Group
891
- * > Value (strong): Property name of container component
892
- * @var array
893
- */
894
- _containers: [],
895
-
896
  /**
897
  * Whether DOM element and component are connected in 1:1 relationship
898
  * Some components will be assigned to different DOM elements depending on usage
@@ -905,6 +792,14 @@ var Component = {
905
  * @var DOM Element
906
  */
907
  _dom: null,
 
 
 
 
 
 
 
 
908
 
909
  /**
910
  * Default attributes
@@ -913,16 +808,16 @@ var Component = {
913
  _attr_default: {},
914
 
915
  /**
916
- * Attributes passed to constructor
917
- * @var obj
918
  */
919
- _attr_init: null,
920
 
921
  /**
922
- * Attributes to retrieve from parent (controller)
923
- * @var array
924
  */
925
- _attr_parent: [],
926
 
927
  /**
928
  * Defines how parent properties should be remapped to component properties
@@ -936,7 +831,7 @@ var Component = {
936
  * > Key: string Event type
937
  * > Value: array Handlers
938
  */
939
- _events: null,
940
 
941
  /**
942
  * Status management
@@ -946,77 +841,62 @@ var Component = {
946
  */
947
  _status: null,
948
 
949
- /* Public */
950
-
951
- attributes: false,
952
-
953
  /**
954
  * Component ID
955
  * @var string
956
  */
957
- id: '',
958
 
959
  /* Init */
960
 
 
 
 
 
 
961
  _c: function(id, attributes) {
962
- //Set ID
963
- this.set_id(id);
964
- //Save init attributes
965
- this._attr_init = attributes;
966
- this.register_hooks();
 
 
 
967
  },
968
 
 
 
 
 
969
  _set_parent: function() {
970
- this._parent = View;
971
- this.util._parent = this;
972
  },
973
 
974
  /**
975
  * Register hooks on init
976
  * Placeholder method to be overridden by child classes
977
  */
978
- register_hooks: function() {},
979
 
980
  /* Methods */
981
 
982
  /* Properties */
983
 
984
  /**
985
- * Retrieve status
986
- * @param string id Status to retrieve
987
- * @param bool raw (optional) Retrieve raw value (Default: FALSE)
988
- * @return mixed Status value (Default: bool)
989
- */
990
- get_status: function(id, raw) {
991
- var ret = false;
992
- if ( this.util.in_obj(this._status, id) ) {
993
- ret = ( !!raw ) ? this._status[id] : !!this._status[id];
994
- }
995
- return ret;
996
- },
997
-
998
- /**
999
- * Set status
1000
- * @param string id Status to retrieve
1001
- * @param mixed val Status value (Default: TRUE)
1002
- * @return mixed Status value (Default: bool)
1003
  */
1004
- set_status: function(id, val) {
1005
- //Validate
1006
- if ( this.util.is_string(id) ) {
1007
- if ( !this.util.is_set(val) ) {
1008
- val = true;
1009
- }
1010
- //Initialize property
1011
- if ( !this.util.is_obj(this._status, false) ) {
1012
- this._status = {};
1013
- }
1014
- //Set status
1015
- this._status[id] = val;
1016
- } else if ( !this.util.is_set(val) ) {
1017
- val = false;
1018
  }
1019
- return val;
1020
  },
1021
 
1022
  /**
@@ -1027,12 +907,9 @@ var Component = {
1027
  * @return string Instance ID
1028
  */
1029
  get_id: function(ns) {
1030
- //Validate
1031
- if ( !this.check_id() ) {
1032
- this.id = '';
1033
- }
1034
- var id = this.id;
1035
- //Namespace ID
1036
  if ( this.util.is_bool(ns) && ns ) {
1037
  id = this.add_ns(id);
1038
  }
@@ -1040,35 +917,6 @@ var Component = {
1040
  return id;
1041
  },
1042
 
1043
- /**
1044
- * Set instance ID
1045
- * @param string id Unique ID
1046
- */
1047
- set_id: function(id) {
1048
- this.id = ( this.check_id(id) ) ? id : '';
1049
- },
1050
-
1051
- /**
1052
- * Validate ID value
1053
- * @param string id (optional) ID value (Default: Component ID)
1054
- * @param bool nonempty (optional) TRUE if it should also check for empty strings, FALSE otherwise (Default: FALSE)
1055
- * @return bool TRUE if ID is valid, FALSE otherwise
1056
- */
1057
- check_id: function(id, nonempty) {
1058
- //Validate
1059
- if ( arguments.length == 1 && this.util.is_bool(arguments[0]) ) {
1060
- nonempty = arguments[0];
1061
- id = null;
1062
- }
1063
- if ( this.util.is_empty(id) ) {
1064
- id = this.id;
1065
- }
1066
- if ( !this.util.is_bool(nonempty) ) {
1067
- nonempty = false;
1068
- }
1069
- return ( this.util.is_string(id, nonempty) ) ? true : false;
1070
- },
1071
-
1072
  /**
1073
  * Get namespace
1074
  * @uses _slug for namespace segment
@@ -1076,7 +924,10 @@ var Component = {
1076
  * @return string Component namespace
1077
  */
1078
  get_ns: function() {
1079
- return this.util.add_prefix(this._slug);
 
 
 
1080
  },
1081
 
1082
  /**
@@ -1088,30 +939,56 @@ var Component = {
1088
  return ( this.util.is_string(val) ) ? this.get_ns() + '_' + val : '';
1089
  },
1090
 
1091
- /* Components */
 
 
 
 
 
 
 
 
 
 
 
 
1092
 
1093
  /**
1094
- * Retrieve component containers
1095
- * @uses _container property
1096
- * @return array Component containers
 
1097
  */
1098
- get_containers: function() {
1099
- //Sanitize property
1100
- if ( !this.util.is_array(this._containers) ) {
1101
- this._containers = [];
 
 
 
 
 
 
 
 
 
 
 
1102
  }
1103
- //Return value
1104
- return this._containers;
1105
  },
1106
 
1107
  /**
1108
- * Check if current object has potential container objects
1109
- * @return bool TRUE if containers exist, FALSE otherwise
 
1110
  */
1111
- has_containers: function() {
1112
- return ( this.get_containers().length > 0 );
1113
  },
1114
 
 
 
1115
  /**
1116
  * Check if reference exists in object
1117
  * @param string ref Reference ID
@@ -1140,115 +1017,54 @@ var Component = {
1140
  return ( this.has_reference(ref) ) ? this._refs[ref] : null;
1141
  },
1142
 
1143
- /**
1144
- * Checks if component is valid
1145
- * @param obj|string Component instance or ID
1146
- * > If ID is specified then it will check for component on current instance
1147
- * @param function|string ctype Component type
1148
- * > If component is an object, then ctype is required
1149
- * > If component is string ID, then ctype is optional (Default: reference type)
1150
- * > If ctype is a function, then it is compared to component directly
1151
- * > If ctype is a string, then the component reference type is retrieved
1152
- * @uses get_reference()
1153
- * @return bool TRUE if component is valid, FALSE otherwise
1154
- */
1155
- check_component: function(comp, ctype) {
1156
- //Validate
1157
- if ( this.util.is_empty(comp)
1158
- || ( this.util.is_obj(comp) && !this.util.is_func(ctype) )
1159
- || ( this.util.is_string(comp) && !this.has_reference(comp) )
1160
- || ( this.util.is_empty(ctype) && !this.util.is_string(comp) )
1161
- || ( !this.util.is_obj(comp) && !this.util.is_string(comp) )
1162
- ) {
1163
- return false;
1164
- }
1165
- //Get component type
1166
- if ( !this.util.is_func(ctype) ) {
1167
- //Component is a string ID
1168
- ctype = this.get_reference(comp);
1169
- }
1170
- //Get component instance
1171
- if ( this.util.is_string(comp) ) {
1172
- comp = this.get_component(comp, false);
1173
- }
1174
- return this.util.is_type(comp, ctype);
1175
- },
1176
-
1177
  /**
1178
  * Retrieve component reference from current object
1179
  * > Procedure:
1180
- * > Check if property already set
1181
  * > Check attributes
1182
- * > Check container object(s)
1183
- * > Check parent object (controller)
1184
- * @uses _containers to check potential container components for references
1185
  * @param string cname Component name
1186
- * @param bool check_attr (optional) Whether or not to check instance attributes for component (Default: TRUE)
1187
- * @param bool get_default (optional) Whether or not to retrieve default object from controller if none exists in current instance (Default: TRUE)
1188
- * @param bool recursive (optional) Whether or not to check containers for specified component reference (Default: TRUE)
1189
  * @return object|null Component reference (NULL if no component found)
1190
  */
1191
- get_component: function(cname, check_attr, get_default, recursive) {
1192
  var c = null;
1193
- //Validate request
1194
- if ( !this.util.is_string(cname) || !( cname in this ) || !this.has_reference(cname) ) {
1195
  return c;
1196
  }
1197
 
1198
- //Normalize parameters
1199
- if ( !this.util.is_bool(check_attr) ) {
1200
- check_attr = true;
1201
- }
1202
- if ( !this.util.is_bool(get_default) ) {
1203
- get_default = true;
1204
- }
1205
- if ( !this.util.is_bool(recursive) ) {
1206
- recursive = true;
1207
- }
1208
- var ctype = this._refs[cname];
1209
- //Phase 1: Check if component reference previously set
1210
  if ( this.util.is_type(this[cname], ctype) ) {
1211
  return this[cname];
1212
  }
1213
- //If reference not set, iterate through component hierarchy until reference is found
1214
  c = this[cname] = null;
1215
 
1216
- //Phase 2: Check attributes
1217
- if ( check_attr ) {
1218
  c = this.get_attribute(cname);
1219
- //Save object-specific component reference
1220
  if ( !this.util.is_empty(c) ) {
1221
  c = this.set_component(cname, c);
1222
  }
1223
  }
1224
-
1225
- //Phase 3: Check Container(s)
1226
- if ( recursive && this.util.is_empty(c) && this.has_containers() ) {
1227
- var containers = this.get_containers();
1228
- var con = null;
1229
- for ( var i = 0; i < containers.length; i++ ) {
1230
- con = containers[i];
1231
- //Validate container
1232
- if ( con == cname ) {
1233
- continue;
1234
- }
1235
- //Retrieve container
1236
- con = this.get_component(con, true, false);
1237
- if ( this.util.is_empty(con) ) {
1238
- continue;
1239
- }
1240
- //Attempt to retrieve component from container
1241
- c = con.get_component(cname);
1242
- //Stop iterating if valid component found
1243
- if ( !this.util.is_empty(c) ) {
1244
- break;
1245
- }
1246
- }
1247
- }
1248
 
1249
- //Phase 4: From controller (optional)
1250
- if ( get_default && this.util.is_empty(c) ) {
1251
- c = this.get_parent().get_component(ctype);
1252
  }
1253
  return c;
1254
  },
@@ -1262,106 +1078,119 @@ var Component = {
1262
  * @return object Component (NULL if invalid)
1263
  */
1264
  set_component: function(name, ref, validate) {
1265
- var clear = null;
1266
- //Make sure component property exists
1267
  if ( !this.has_reference(name) ) {
1268
- return clear;
1269
  }
1270
- //Normalize reference
 
1271
  if ( this.util.is_empty(ref) ) {
1272
- ref = clear;
1273
- }
1274
- var ctype = this.get_reference(name);
1275
-
1276
- //Get component from controller if ID supplied
1277
- if ( this.util.is_string(ref) ) {
1278
- ref = this.get_parent().get_component(ctype, ref);
 
 
 
 
 
 
1279
  }
1280
 
1281
- if ( !this.util.is_type(ref, ctype) ) {
1282
- ref = clear;
1283
- }
1284
-
1285
- //Additional validation
1286
- if ( !this.util.is_empty(ref) && this.util.is_func(validate) && !validate.call(this, ref) ) {
1287
- ref = clear;
1288
- }
1289
- //Set (or clear) component reference
1290
  this[name] = ref;
1291
- //Return value for confirmation
1292
  return this[name];
1293
  },
 
 
 
 
 
 
 
 
 
 
1294
 
1295
  /* Attributes */
1296
 
1297
  /**
1298
- * Initializes attributes
 
 
1299
  */
1300
  init_attributes: function(force) {
1301
  if ( !this.util.is_bool(force) ) {
1302
  force = false;
1303
  }
1304
- if ( force || !this.util.is_obj(this.attributes) ) {
1305
- this.attributes = {};
1306
- //Build attribute groups
1307
- var attrs = [{}];
1308
- attrs.push(this.init_default_attributes());
1309
- if ( this.dom_has() ) {
1310
- attrs.push(this.get_dom_attributes());
1311
- }
1312
  if ( this.util.is_obj(this._attr_init) ) {
1313
- attrs.push(this._attr_init);
1314
  }
1315
- //Merge attributes
1316
- this.attributes = $.extend.apply(null, attrs);
1317
  }
1318
  },
1319
 
1320
  /**
1321
  * Generate default attributes for component
1322
- * @uses _attr_parent to determine options to retrieve from controller
1323
  * @uses View.get_options() to get values from controller
1324
  * @uses _attr_map to Remap controller attributes to instance attributes
1325
  * @uses _attr_default to Store default attributes
 
1326
  */
1327
  init_default_attributes: function() {
1328
- //Get parent options
1329
- var opts = this.get_parent().get_options(this._attr_parent);
1330
- if ( this.util.is_obj(opts) ) {
1331
- //Remap
1332
- for ( var opt in this._attr_map ) {
1333
- if ( opt in opts ) {
1334
- //Move value to new property
1335
- opts[this._attr_map[opt]] = opts[opt];
1336
- //Delete old property
1337
- delete opts[opt];
 
 
 
1338
  }
 
 
1339
  }
1340
- //Merge with default attributes
1341
- $.extend(true, this._attr_default, opts);
1342
  }
1343
  return this._attr_default;
1344
  },
1345
 
1346
  /**
1347
  * Retrieve DOM attributes
 
1348
  */
1349
  get_dom_attributes: function() {
1350
  var attrs = {};
1351
- var el = this.dom_get();
1352
- if ( el.length ) {
1353
- //Get attributes from element
1354
- var opts = $(el).get(0).attributes;
1355
- if ( this.util.is_obj(opts) ) {
1356
  var attr_prefix = this.util.get_attribute();
1357
- $.each(opts, function(idx, opt) {
1358
- if ( opt.name.indexOf( attr_prefix ) == -1 ) {
 
1359
  return true;
1360
  }
1361
- //Process custom attributes
1362
- //Strip prefix
1363
- var key = opt.name.substr(attr_prefix.length + 1);
1364
- attrs[key] = opt.value;
1365
  });
1366
  }
1367
  }
@@ -1370,15 +1199,15 @@ var Component = {
1370
 
1371
  /**
1372
  * Retrieve all instance attributes
1373
- * @uses parse_attributes() to initialize attributes (if necessary)
1374
- * @uses attributes
1375
- * @return obj Attributes
1376
  */
1377
  get_attributes: function() {
1378
- //Initilize attributes
1379
  this.init_attributes();
1380
- //Return attributes
1381
- return this.attributes;
1382
  },
1383
 
1384
  /**
@@ -1391,7 +1220,7 @@ var Component = {
1391
  * @return mixed Attribute value (NULL if attribute is not set)
1392
  */
1393
  get_attribute: function(key, def, enforce_type) {
1394
- //Validate
1395
  if ( !this.util.is_set(def) ) {
1396
  def = null;
1397
  }
@@ -1401,30 +1230,35 @@ var Component = {
1401
  if ( !this.util.is_bool(enforce_type) ) {
1402
  enforce_type = true;
1403
  }
1404
- //Get attribute value
 
1405
  var ret = ( this.has_attribute(key) ) ? this.get_attributes()[key] : def;
1406
- //Validate type
1407
  if ( enforce_type && ret !== def && null !== def && !this.util.is_type(ret, $.type(def), false) ) {
1408
- //Convert type
1409
- //Scalar default
1410
  if ( this.util.is_scalar(def, false) ) {
1411
- //Non-scalar attribute
1412
  if ( !this.util.is_scalar(ret, false) ) {
 
1413
  ret = def;
1414
  } else if ( this.util.is_string(def, false) ) {
 
1415
  ret = ret.toString();
1416
  } else if ( this.util.is_num(def, false) && !this.util.is_num(ret, false) ) {
 
1417
  ret = ( this.util.is_int(def, false) ) ? parseInt(ret) : parseFloat(ret);
1418
  if ( !this.util.is_num(ret, false) ) {
1419
  ret = def;
1420
  }
1421
  } else if ( this.util.is_bool(def, false) ) {
 
1422
  ret = ( this.util.is_string(ret) || ( this.util.is_num(ret) ) );
1423
  } else {
 
1424
  ret = def;
1425
  }
1426
  }
1427
- //Non-scalar default
1428
  else {
1429
  ret = def;
1430
  }
@@ -1436,13 +1270,14 @@ var Component = {
1436
  * Call attribute as method
1437
  * @param string attr Attribute to call
1438
  * @param arguments (optional) Additional arguments to pass to method
 
1439
  */
1440
  call_attribute: function(attr, args) {
1441
  attr = this.get_attribute(attr);
1442
  if ( this.util.is_func(attr) ) {
1443
- //Get arguments
1444
- var args = Array.prototype.slice.call(arguments, 1);
1445
- //Pass arguments to user-defined method
1446
  attr = attr.apply(this, args);
1447
  }
1448
  return attr;
@@ -1454,7 +1289,7 @@ var Component = {
1454
  * @return bool TRUE if exists, FALSE otherwise
1455
  */
1456
  has_attribute: function(key) {
1457
- return ( key in this.get_attributes() );
1458
  },
1459
 
1460
  /**
@@ -1463,16 +1298,17 @@ var Component = {
1463
  * @param bool full (optional) Whether to fully replace or merge component's attributes with new values (Default: Merge)
1464
  */
1465
  set_attributes: function(attributes, full) {
 
1466
  if ( !this.util.is_bool(full) ) {
1467
  full = false;
1468
  }
1469
 
1470
- //Initialize attributes
1471
  this.init_attributes(full);
1472
 
1473
- //Merge new/existing attributes
1474
  if ( this.util.is_obj(attributes) ) {
1475
- $.extend(this.attributes, attributes);
1476
  }
1477
  },
1478
 
@@ -1481,6 +1317,7 @@ var Component = {
1481
  * @uses get_attributes() to retrieve attributes
1482
  * @param string key Attribute to set
1483
  * @param mixed val Attribute value
 
1484
  */
1485
  set_attribute: function(key, val) {
1486
  if ( this.util.is_string(key) && this.util.is_set(val) ) {
@@ -1512,9 +1349,9 @@ var Component = {
1512
  */
1513
  dom_set: function(el) {
1514
  el = $(el);
1515
- //Save instance to DOM object
1516
  el.data(this.get_data_key(), this);
1517
- //Save DOM object to instance
1518
  if ( this._reciprocal ) {
1519
  this._dom = el;
1520
  }
@@ -1525,27 +1362,34 @@ var Component = {
1525
  * Retrieve attached DOM element
1526
  * @uses _dom to retrieve attached DOM element
1527
  * @uses dom_put() to insert child element
1528
- * @param string element Child element to retrieve
1529
  * @param bool put (optional) Whether to insert element if it does not exist (Default: FALSE)
1530
- * @param obj options (optional) Options for creating new object
1531
  * @return obj jQuery DOM element
1532
  */
1533
- dom_get: function(element, put, options) {
1534
- //Init Component DOM
1535
- if ( !this.get_status('dom_init') ) {
 
 
 
 
 
 
 
1536
  this.set_status('dom_init');
1537
  this.dom_init();
1538
  }
1539
- //Check for main DOM element
1540
  var ret = this._dom;
1541
  if ( !!ret && this.util.is_string(element) ) {
1542
  var ch = $(ret).find( this.dom_get_selector(element) );
1543
- //Check for child element
1544
  if ( ch.length ) {
1545
  ret = ch;
1546
- } else if ( this.util.is_bool(put) && put ) {
1547
- //Insert child element
1548
- ret = this.dom_put(element, options);
1549
  }
1550
  }
1551
  return $(ret);
@@ -1562,24 +1406,24 @@ var Component = {
1562
  * Wrapper element created and added to main DOM element if not yet created
1563
  * @param string element ID for DOM element (Used as class name for wrapper)
1564
  * @param string|jQuery|obj content Content to add to DOM (Object contains element properties)
1565
- * > tag : Element tag name
1566
- * > content : Element content
1567
  * @return jQuery Inserted element(s)
1568
  */
1569
  dom_put: function(element, content) {
1570
  var r = null;
1571
- //Stop processing if main DOM element not set or element is not valid
1572
  if ( !this.dom_has() || !this.util.is_string(element) ) {
1573
  return $(r);
1574
  }
1575
- //Setup options
1576
- var strip = ['tag', 'content', 'put_success'];
1577
  var options = {
1578
  'tag': 'div',
1579
  'content': '',
1580
  'class': this.add_ns(element)
1581
- }
1582
- //Setup content
1583
  if ( !this.util.is_empty(content) ) {
1584
  if ( this.util.is_type(content, jQuery, false) || this.util.is_string(content, false) ) {
1585
  options.content = content;
@@ -1592,17 +1436,17 @@ var Component = {
1592
  for ( var x = 0; x < strip.length; x++ ) {
1593
  delete attrs[strip[x]];
1594
  }
1595
- //Retrieve existing element
1596
  var d = this.dom_get();
1597
  r = $(this.dom_get_selector(element), d);
1598
- //Create element (if necessary)
1599
  if ( !r.length ) {
1600
  r = $(this.util.format('<%s />', options.tag), attrs).appendTo(d);
1601
- if ( r.length && this.util.is_method(options, 'put_success') ) {
1602
- options['put_success'].call(r, r);
1603
  }
1604
  }
1605
- //Set content
1606
  $(r).append(options.content);
1607
  return $(r);
1608
  },
@@ -1644,17 +1488,17 @@ var Component = {
1644
  * @return obj Component instance (allows chaining)
1645
  */
1646
  on: function(event, fn, options) {
1647
- //Handle request types
1648
  if ( !this.util.is_string(event) || !this.util.is_func(fn) ) {
1649
  var t = this;
1650
  var args = Array.prototype.slice.call(arguments, 1);
1651
  if ( this.util.is_array(event) ) {
1652
- //Events array
1653
  $.each(event, function(idx, val) {
1654
  t.on.apply(t, [val].concat(args));
1655
  });
1656
  } else if ( this.util.is_obj(event) ) {
1657
- //Events map
1658
  $.each(event, function(ev, hdl) {
1659
  t.on.apply(t, [ev, hdl].concat(args));
1660
  });
@@ -1662,28 +1506,28 @@ var Component = {
1662
  return this;
1663
  }
1664
 
1665
- //Options
1666
 
1667
- //Default options
1668
  var options_std = {
1669
  clear: false
1670
  };
1671
  if ( !this.util.is_obj(options, false) ) {
1672
- //Reset options
1673
  options = {};
1674
  }
1675
- //Build options
1676
  options = $.extend({}, options_std, options);
1677
- //Initialize events bucket
1678
  if ( !this.util.is_obj(this._events, false) ) {
1679
  this._events = {};
1680
  }
1681
- //Setup event
1682
  var es = this._events;
1683
  if ( !( event in es ) || !this.util.is_obj(es[event], false) || !!options.clear ) {
1684
  es[event] = [];
1685
  }
1686
- //Add event handler
1687
  es[event].push(fn);
1688
  return this;
1689
  },
@@ -1693,8 +1537,8 @@ var Component = {
1693
  * Event handlers are executed in the context of the current component instance
1694
  * Event handlers are passed parameters
1695
  * > ev (obj) Event object
1696
- * > type (string) Event name
1697
- * > data (mixed) Data to pass to handlers (if supplied)
1698
  * > component (obj) Current component instance
1699
  * @param string event Custom event to trigger
1700
  * @param mixed data (optional) Data to pass to event handlers
@@ -1704,36 +1548,36 @@ var Component = {
1704
  var dfr = $.Deferred();
1705
  var dfrs = [];
1706
  var t = this;
1707
- //Handle array of events
1708
  if ( this.util.is_array(event) ) {
1709
  $.each(event, function(idx, val) {
1710
- //Collect promises from triggered events
1711
  dfrs.push( t.trigger(val, data) );
1712
  });
1713
- //Resolve trigger when all events have been resolved
1714
  $.when.apply(t, dfrs).done(function() {
1715
  dfr.resolve();
1716
  });
1717
  return dfr.promise();
1718
  }
1719
- //Validate
1720
  if ( !this.util.is_string(event) || !( event in this._events ) ) {
1721
  dfr.resolve();
1722
  return dfr.promise();
1723
  }
1724
- //Create event object
1725
  var ev = { 'type': event, 'data': null };
1726
- //Add data to event object
1727
  if ( this.util.is_set(data) ) {
1728
  ev.data = data;
1729
  }
1730
- //Fire handlers for event
1731
  $.each(this._events[event], function(idx, fn) {
1732
- //Call handler (`this` set to current instance)
1733
- //Collect promises from event handlers
1734
  dfrs.push( fn.call(t, ev, t) );
1735
  });
1736
- //Resolve trigger when all handlers have been resolved
1737
  $.when.apply(this, dfrs).done(function() {
1738
  dfr.resolve();
1739
  });
@@ -1766,9 +1610,10 @@ var Viewer = {
1766
  autofit: true,
1767
  overlay_enabled: true,
1768
  overlay_opacity: '0.8',
 
1769
  container: null,
1770
  slideshow_enabled: true,
1771
- slideshow_autostart: true,
1772
  slideshow_duration: 2,
1773
  slideshow_active: false,
1774
  slideshow_timer: null,
@@ -1783,18 +1628,17 @@ var Viewer = {
1783
  }
1784
  },
1785
 
1786
- _attr_parent: [
1787
- 'theme',
1788
- 'group_loop',
1789
- 'ui_autofit', 'ui_animate', 'ui_overlay_opacity', 'ui_labels',
1790
- 'slideshow_enabled', 'slideshow_autostart', 'slideshow_duration'],
1791
-
1792
  _attr_map: {
 
1793
  'group_loop': 'loop',
1794
  'ui_autofit': 'autofit',
1795
  'ui_animate': 'animate',
1796
  'ui_overlay_opacity': 'overlay_opacity',
1797
- 'ui_labels': 'labels'
 
 
 
 
1798
  },
1799
 
1800
  /* References */
@@ -1830,19 +1674,29 @@ var Viewer = {
1830
 
1831
  /* Init */
1832
 
1833
- register_hooks: function() {
1834
  var t = this;
1835
  this
1836
  .on(['item-prev', 'item-next'], function() {
1837
  t.trigger('item-change');
1838
  })
1839
  .on(['close', 'item-change'], function() {
1840
- t.unlock();
 
 
1841
  });
1842
  },
1843
 
1844
  /* References */
1845
 
 
 
 
 
 
 
 
 
1846
  /**
1847
  * Set item reference
1848
  * Validates item before setting
@@ -1850,7 +1704,7 @@ var Viewer = {
1850
  * @return bool TRUE if valid item set, FALSE otherwise
1851
  */
1852
  set_item: function(item) {
1853
- //Clear existing item
1854
  this.clear_item(false);
1855
  var i = this.set_component('item', item, function(item) {
1856
  return ( item.has_type() );
@@ -1858,8 +1712,13 @@ var Viewer = {
1858
  return ( !this.util.is_empty(i) );
1859
  },
1860
 
 
 
 
 
 
1861
  clear_item: function(full) {
1862
- //Validate
1863
  if ( !this.util.is_bool(full) ) {
1864
  full = true;
1865
  }
@@ -1868,27 +1727,19 @@ var Viewer = {
1868
  item.reset();
1869
  }
1870
  if ( full ) {
1871
- this.set_item(false);
1872
  }
1873
  },
1874
 
1875
- /**
1876
- * Retrieve item instance current attached to viewer
1877
- * @return Content_Item|NULL Current item instance
1878
- */
1879
- get_item: function() {
1880
- return this.get_component('item', true, false);
1881
- },
1882
-
1883
  /**
1884
  * Retrieve theme reference
1885
  * @return object Theme reference
1886
  */
1887
  get_theme: function() {
1888
- //Get saved theme
1889
- var ret = this.get_component('theme', false, false, false);
1890
  if ( this.util.is_empty(ret) ) {
1891
- //Theme needs to be initialized
1892
  ret = this.set_component('theme', new View.Theme(this));
1893
  }
1894
  return ret;
@@ -1920,7 +1771,7 @@ var Viewer = {
1920
  * @return jQuery.Promise Resolved when item processing is complete
1921
  */
1922
  get_lock: function(simple, full) {
1923
- //Validate
1924
  if ( !this.util.is_bool(simple) ) {
1925
  simple = false;
1926
  }
@@ -1928,14 +1779,14 @@ var Viewer = {
1928
  full = false;
1929
  }
1930
  var s = 'item_working';
1931
- //Simple status
1932
  if ( simple ) {
1933
  return this.get_status(s);
1934
  }
1935
- //Full value
1936
  var r = this.get_status(s, true);
1937
  if ( !this.util.is_promise(r) ) {
1938
- //Create default
1939
  r = this.lock();
1940
  }
1941
  return ( full ) ? r : r.promise();
@@ -1985,15 +1836,15 @@ var Viewer = {
1985
  mode = true;
1986
  }
1987
  this.loading = mode;
1988
- //Pause/Resume slideshow
1989
  if ( this.slideshow_active() ) {
1990
  this.slideshow_pause(mode);
1991
  }
1992
- //Set CSS class on DOM element
1993
  var m = ( mode ) ? 'addClass' : 'removeClass';
1994
  $(this.dom_get())[m]('loading');
1995
  if ( mode ) {
1996
- //Loading transition
1997
  this.get_theme().transition('load').always(function() {
1998
  dfr.resolve();
1999
  });
@@ -2038,13 +1889,14 @@ var Viewer = {
2038
  show: function(item) {
2039
  this.item_queued = item;
2040
  var fin_set = 'show_deferred';
2041
- //Validate theme
2042
  var vt = 'theme_valid';
2043
  var valid = true;
2044
- if ( !this.has_attribute(vt)) {
2045
- valid = this.set_attribute(vt, ( this.get_theme() && this.get_theme().get_template().get_layout(false) ) );
2046
- } else {
2047
  valid = this.get_attribute(vt, true);
 
 
 
2048
  }
2049
 
2050
  if ( !valid ) {
@@ -2053,26 +1905,26 @@ var Viewer = {
2053
  }
2054
  var v = this;
2055
  var fin = function() {
2056
- //Lock viewer
2057
  v.lock();
2058
- //Reset callback flag (for new lock)
2059
  v.set_status(fin_set, false);
2060
- //Validate request
2061
  if ( !v.set_item(v.item_queued) ) {
2062
  v.close();
2063
  return false;
2064
  }
2065
- //Add item to history stack
2066
  v.history_add();
2067
- //Activate
2068
  v.set_active();
2069
- //Display
2070
  v.render();
2071
- }
2072
  if ( !this.is_locked() ) {
2073
  fin();
2074
  } else if ( !this.get_status(fin_set) ) {
2075
- //Set flag to avoid duplicate callbacks
2076
  this.set_status(fin_set);
2077
  this.get_lock().always(function() {
2078
  fin();
@@ -2084,16 +1936,16 @@ var Viewer = {
2084
 
2085
  history_handle: function(e) {
2086
  var state = e.originalEvent.state;
2087
- //Load item
2088
- if ( this.util.is_int(state.item, false) ) {
2089
- this.get_parent().get_item(state.item).show({'event': e});
2090
  this.trigger('item-change');
2091
  } else {
2092
  var count = this.history_get(true);
2093
- //Reset count
2094
  this.history_set(0);
2095
- //Close viewer
2096
- if ( -1 != count ) {
2097
  this.close();
2098
  }
2099
  }
@@ -2109,24 +1961,24 @@ var Viewer = {
2109
  if ( !history.pushState ) {
2110
  return false;
2111
  }
2112
- //Get display options
2113
  var item = this.get_item();
2114
  var opts = item.get_attribute('options_show');
2115
- //Save history state
2116
  var count = ( this.history_get() ) ? this.history_get(true) : 0;
2117
  if ( !this.util.in_obj(opts, 'event') ) {
2118
- //Create state
2119
  var state = {
2120
  'viewer': this.get_id(),
2121
  'item': null,
2122
  'count': count
2123
  };
2124
- //Init: Save viewer state
2125
  if ( !count ) {
2126
  history.replaceState(state, null);
2127
  }
2128
- //Always: Save item state
2129
- state.item = this.get_parent().save_item(item);
2130
  state.count = ++count;
2131
  history.pushState(state, '');
2132
  } else {
@@ -2135,15 +1987,15 @@ var Viewer = {
2135
  count = e.state.count;
2136
  }
2137
  }
2138
- //Save history item count
2139
  this.history_set(count);
2140
  },
2141
  history_reset: function() {
2142
  var count = this.history_get(true);
2143
  if ( count ) {
2144
- //Clear history status
2145
  this.history_set(-1);
2146
- //Restore history stack
2147
  history.go( -1 * count );
2148
  }
2149
  },
@@ -2154,21 +2006,22 @@ var Viewer = {
2154
  * @return bool TRUE if viewer is open, FALSE otherwise
2155
  */
2156
  is_open: function() {
2157
- return ( this.dom_get().css('display') == 'none' ) ? false : true;
2158
  },
2159
 
2160
  /**
2161
  * Load output into DOM
2162
  */
2163
  render: function() {
2164
- //Get theme output
2165
  var v = this;
2166
  var thm = this.get_theme();
2167
- //Register theme event handlers
 
2168
  if ( !this.get_status('render-events') ) {
2169
  this.set_status('render-events');
2170
  thm
2171
- //Loading
2172
  .on('render-loading', function(ev, thm) {
2173
  var dfr = $.Deferred();
2174
  if ( !v.is_active() ) {
@@ -2176,11 +2029,11 @@ var Viewer = {
2176
  return dfr.promise();
2177
  }
2178
  var set_pos = function() {
2179
- //Set position
2180
  v.dom_get().css('top', $(window).scrollTop());
2181
  };
2182
  var always = function() {
2183
- //Set loading flag
2184
  v.set_loading().always(function() {
2185
  dfr.resolve();
2186
  });
@@ -2200,21 +2053,21 @@ var Viewer = {
2200
  v.open = true;
2201
  })
2202
  .fail(function() {
2203
- set_pos();
2204
- //Fallback open
2205
  v.get_overlay().show();
2206
  v.dom_get().show();
2207
  });
2208
  }
2209
  return dfr.promise();
2210
  })
2211
- //Complete
2212
  .on('render-complete', function(ev, thm) {
2213
- //Stop if viewer not active
2214
  if ( !v.is_active() ) {
2215
  return false;
2216
  }
2217
- //Set classes
2218
  var d = v.dom_get();
2219
  var classes = ['item_single', 'item_multi'];
2220
  var ms = ['addClass', 'removeClass'];
@@ -2224,28 +2077,28 @@ var Viewer = {
2224
  $.each(ms, function(idx, val) {
2225
  d[val](classes[idx]);
2226
  });
2227
- //Bind events
2228
  v.events_complete();
2229
- //Transition
2230
  thm.transition('complete')
2231
  .fail(function() {
2232
- //Autofit content
2233
  if ( v.get_attribute('autofit', true) ) {
2234
  var dims = $.extend({'display': 'inline-block'}, thm.get_item_dimensions());
2235
- var tag = thm.dom_get_tag('item', 'content').css(dims);
2236
  }
2237
  })
2238
  .always(function() {
2239
- //Unset loading flag
2240
  v.unset_loading();
2241
- //Trigger event
2242
  v.trigger('render-complete');
2243
- //Set viewer as initialized
2244
  v.init = true;
2245
  });
2246
  });
2247
  }
2248
- //Render
2249
  thm.render();
2250
  },
2251
 
@@ -2256,16 +2109,16 @@ var Viewer = {
2256
  */
2257
  dom_get_container: function() {
2258
  var sel = this.get_attribute('container');
2259
- //Set default container
2260
  if ( this.util.is_empty(sel) ) {
2261
  sel = '#' + this.add_ns('wrap');
2262
  }
2263
- //Add default container to DOM if not yet present
2264
  var c = $(sel);
2265
  if ( !c.length ) {
2266
- //Prepare ID
2267
  var id = ( sel.indexOf('#') === 0 ) ? sel.substr(1) : sel;
2268
- //Add element
2269
  c = $('<div />', {'id': id}).appendTo('body');
2270
  }
2271
  return c;
@@ -2275,40 +2128,51 @@ var Viewer = {
2275
  * Custom Viewer DOM initialization
2276
  */
2277
  dom_init: function() {
2278
- //Create element & add to DOM
2279
- //Save element to instance
2280
  var d = this.dom_set($('<div/>', {
2281
  'id': this.get_id(true),
2282
  'class': this.get_ns()
2283
  })).appendTo(this.dom_get_container()).hide();
2284
- //Add theme classes
2285
  var thm = this.get_theme();
2286
  d.addClass(thm.get_classes(' '));
2287
- //Add theme layout (basic)
2288
  var v = this;
2289
  if ( !this.get_status('render-init') ) {
2290
  this.set_status('render-init');
2291
  thm.on('render-init', function(ev) {
2292
- //Add rendered theme layout to viewer DOM
2293
  v.dom_put('layout', ev.data);
2294
  });
2295
  }
2296
  thm.render(true);
2297
  },
2298
 
 
 
 
 
 
 
 
 
2299
  /**
2300
  * Restore DOM
2301
- * Show overlapping DOM elements, etc.
2302
- * @TODO Build functionality
2303
  */
2304
- dom_restore: function() {},
 
 
2305
 
2306
  /* Layout */
2307
 
2308
  get_layout: function() {
2309
- var ret = this.dom_get('layout', true, {
2310
- 'put_success': function() {
2311
- $(this).hide();
 
 
2312
  }
2313
  });
2314
  return ret;
@@ -2339,38 +2203,47 @@ var Viewer = {
2339
  var o = null;
2340
  var v = this;
2341
  if ( this.overlay_enabled() ) {
2342
- o = this.dom_get('overlay', true, {
2343
- 'put_success': function() {
2344
- $(this).hide().css('opacity', v.get_attribute('overlay_opacity'));
 
 
2345
  }
2346
  });
2347
  }
2348
  return $(o);
2349
  },
2350
 
 
 
 
2351
  unload: function() {
2352
-
 
 
 
 
2353
  },
2354
 
2355
  /**
2356
  * Reset viewer
2357
  */
2358
  reset: function() {
2359
- //Hide viewer
2360
  this.dom_get().hide();
2361
- //Restore DOM
2362
  this.dom_restore();
2363
- //History
2364
  this.history_reset();
2365
- //Item
2366
  this.clear_item();
2367
- //Reset properties
2368
  this.set_active(false);
2369
  this.set_loading(false);
2370
  this.slideshow_stop();
2371
  this.keys_disable();
2372
- //Clear for next item
2373
- this.get_status('item_working', true).resolve();
2374
  },
2375
 
2376
  /* Content */
@@ -2390,13 +2263,13 @@ var Viewer = {
2390
  * Initialize event handlers upon opening lightbox
2391
  */
2392
  events_open: function() {
2393
- //Keyboard bindings
2394
  this.keys_enable();
2395
  if ( this.open ) {
2396
  return false;
2397
  }
2398
 
2399
- //Control event bubbling
2400
  var l = this.get_layout();
2401
  l.children().click(function(ev) {
2402
  ev.stopPropagation();
@@ -2406,12 +2279,12 @@ var Viewer = {
2406
  var v = this;
2407
  var close = function() {
2408
  v.close();
2409
- }
2410
- //Layout
2411
  l.click(close);
2412
- //Overlay
2413
  this.get_overlay().click(close);
2414
- //Fire event
2415
  this.trigger('events-open');
2416
  },
2417
 
@@ -2422,7 +2295,7 @@ var Viewer = {
2422
  if ( this.init ) {
2423
  return false;
2424
  }
2425
- //Fire event
2426
  this.trigger('events-complete');
2427
  },
2428
 
@@ -2434,7 +2307,7 @@ var Viewer = {
2434
  var v = this;
2435
  var h = function(ev) {
2436
  return v.keys_control(ev);
2437
- }
2438
  if ( mode ) {
2439
  $(document).on(e, h);
2440
  } else {
@@ -2448,10 +2321,15 @@ var Viewer = {
2448
 
2449
  keys_control: function(ev) {
2450
  var handlers = {
2451
- 27: this.close,
2452
- 37: this.item_prev,
2453
- 39: this.item_next
2454
  };
 
 
 
 
 
2455
  if ( ev.which in handlers ) {
2456
  handlers[ev.which].call(this);
2457
  return false;
@@ -2499,15 +2377,15 @@ var Viewer = {
2499
  }
2500
  this.set_attribute('slideshow_active', true);
2501
  this.dom_get().addClass('slideshow_active');
2502
- //Clear residual timers
2503
  this.slideshow_clear_timer();
2504
- //Start timer
2505
  var v = this;
2506
  this.slideshow_set_timer(function() {
2507
- //Pause slideshow until next item fully loaded
2508
  v.slideshow_pause();
2509
 
2510
- //Show next item
2511
  v.item_next();
2512
  });
2513
  this.trigger('slideshow-start');
@@ -2525,7 +2403,7 @@ var Viewer = {
2525
  this.set_attribute('slideshow_active', false);
2526
  this.dom_get().removeClass('slideshow_active');
2527
  }
2528
- //Kill timers
2529
  this.slideshow_clear_timer();
2530
  this.trigger('slideshow-stop');
2531
  },
@@ -2547,17 +2425,17 @@ var Viewer = {
2547
  * @param bool mode (optional) Pause (TRUE) or Resume (FALSE) slideshow (default: TRUE)
2548
  */
2549
  slideshow_pause: function(mode) {
2550
- //Validate
2551
  if ( !this.util.is_bool(mode) ) {
2552
  mode = true;
2553
  }
2554
- //Set viewer slideshow properties
2555
  if ( this.slideshow_active() ) {
2556
  if ( !mode ) {
2557
- //Slideshow resumed
2558
  this.slideshow_start();
2559
  } else {
2560
- //Slideshow paused
2561
  this.slideshow_stop(false);
2562
  }
2563
  }
@@ -2579,7 +2457,7 @@ var Viewer = {
2579
  var v = this;
2580
  var ev = 'item-next';
2581
  var st = ['events', 'viewer', ev].join('_');
2582
- //Setup event handler
2583
  if ( !g.get_status(st) ) {
2584
  g.set_status(st);
2585
  g.on(ev, function(e) {
@@ -2610,14 +2488,14 @@ var Viewer = {
2610
  * Close viewer
2611
  */
2612
  close: function() {
2613
- //Deactivate
2614
  this.set_active(false);
2615
  var v = this;
2616
  var thm = this.get_theme();
2617
  thm.transition('unload')
2618
  .always(function() {
2619
  thm.transition('close', true).always(function() {
2620
- //End processes
2621
  v.reset();
2622
  v.trigger('close');
2623
  });
@@ -2660,7 +2538,7 @@ var Group = {
2660
 
2661
  /* Init */
2662
 
2663
- register_hooks: function() {
2664
  var t = this;
2665
  this.on(['item-prev', 'item-next'], function() {
2666
  t.trigger('item-change');
@@ -2675,7 +2553,7 @@ var Group = {
2675
  */
2676
  get_selector: function() {
2677
  if ( this.util.is_empty(this.selector) ) {
2678
- //Build selector
2679
  this.selector = this.util.format('a[%s="%s"]', this.dom_get_attribute(), this.get_id());
2680
  }
2681
  return this.selector;
@@ -2685,7 +2563,10 @@ var Group = {
2685
  * Retrieve group items
2686
  */
2687
  get_items: function() {
2688
- var items = ( !this.util.is_empty(this.get_id()) ) ? $(this.get_selector()) : this.get_current().dom_get();
 
 
 
2689
  return items;
2690
  },
2691
 
@@ -2696,18 +2577,18 @@ var Group = {
2696
  * @return Content_Item Item
2697
  */
2698
  get_item: function(idx) {
2699
- //Validation
2700
  if ( !this.util.is_int(idx) ) {
2701
  idx = 0;
2702
  }
2703
- //Retrieve all items
2704
  var items = this.get_items();
2705
- //Validate index
2706
  var max = this.get_size() - 1;
2707
  if ( idx > max ) {
2708
  idx = max;
2709
  }
2710
- //Return specified item
2711
  return items.get(idx);
2712
  },
2713
 
@@ -2718,19 +2599,29 @@ var Group = {
2718
  */
2719
  get_pos: function(item) {
2720
  if ( this.util.is_empty(item) ) {
2721
- //Get current item
2722
  item = this.get_current();
2723
  }
2724
  return ( this.util.is_type(item, View.Content_Item) ) ? this.get_items().index(item.dom_get()) : -1;
2725
  },
 
 
 
 
 
 
 
 
 
2726
 
2727
  /**
2728
  * Retrieve current item
2729
- * @return Content_Item Current item
 
2730
  */
2731
  get_current: function() {
2732
- //Sanitize
2733
- if ( !this.util.is_empty(this.current) && !this.util.is_type(this.current, View.Content_Item) ) {
2734
  this.current = null;
2735
  }
2736
  return this.current;
@@ -2741,26 +2632,26 @@ var Group = {
2741
  * @param Content_Item item Item to set as current
2742
  */
2743
  set_current: function(item) {
2744
- //Validate
2745
  if ( this.util.is_type(item, View.Content_Item) ) {
2746
- //Set current item
2747
  this.current = item;
2748
  }
2749
  },
2750
 
2751
  get_next: function(item) {
2752
- //Validate
2753
  if ( !this.util.is_type(item, View.Content_Item) ) {
2754
  item = this.get_current();
2755
  }
2756
- if ( this.get_size() == 1 ) {
2757
  return item;
2758
  }
2759
  var next = null;
2760
  var pos = this.get_pos(item);
2761
- if ( pos != -1 ) {
2762
  pos = ( pos + 1 < this.get_size() ) ? pos + 1 : 0;
2763
- if ( 0 != pos || item.get_viewer().get_attribute('loop') ) {
2764
  next = this.get_item(pos);
2765
  }
2766
  }
@@ -2768,17 +2659,17 @@ var Group = {
2768
  },
2769
 
2770
  get_prev: function(item) {
2771
- //Validate
2772
  if ( !this.util.is_type(item, View.Content_Item) ) {
2773
  item = this.get_current();
2774
  }
2775
- if ( this.get_size() == 1 ) {
2776
  return item;
2777
  }
2778
  var prev = null;
2779
  var pos = this.get_pos(item);
2780
- if ( pos != -1 && ( 0 != pos || item.get_viewer().get_attribute('loop') ) ) {
2781
- if ( pos == 0 ) {
2782
  pos = this.get_size();
2783
  }
2784
  pos -= 1;
@@ -2789,7 +2680,7 @@ var Group = {
2789
 
2790
  show_next: function(item) {
2791
  if ( this.get_size() > 1 ) {
2792
- //Retrieve item
2793
  var next = this.get_next(item);
2794
  if ( !next ) {
2795
  if ( !this.util.is_type(item, View.Content_Item) ) {
@@ -2797,19 +2688,19 @@ var Group = {
2797
  }
2798
  item.get_viewer().close();
2799
  }
2800
- var i = this.get_parent().get_item(next);
2801
- //Update current item
2802
  this.set_current(i);
2803
- //Show item
2804
  i.show();
2805
- //Fire event
2806
  this.trigger('item-next');
2807
  }
2808
  },
2809
 
2810
  show_prev: function(item) {
2811
  if ( this.get_size() > 1 ) {
2812
- //Retrieve item
2813
  var prev = this.get_prev(item);
2814
  if ( !prev ) {
2815
  if ( !this.util.is_type(item, View.Content_Item) ) {
@@ -2817,12 +2708,12 @@ var Group = {
2817
  }
2818
  item.get_viewer().close();
2819
  }
2820
- var i = this.get_parent().get_item(prev);
2821
- //Update current item
2822
  this.set_current(i);
2823
- //Show item
2824
  i.show();
2825
- //Fire event
2826
  this.trigger('item-prev');
2827
  }
2828
  },
@@ -2836,7 +2727,7 @@ var Group = {
2836
  },
2837
 
2838
  is_single: function() {
2839
- return ( this.get_size() == 1 );
2840
  }
2841
  };
2842
 
@@ -2887,7 +2778,7 @@ var Content_Handler = {
2887
  * @return mixed Content_Item if valid item set, NULL otherwise
2888
  */
2889
  get_item: function() {
2890
- return this.get_component('item', true, false);
2891
  },
2892
 
2893
  /**
@@ -2898,7 +2789,7 @@ var Content_Handler = {
2898
  * @return obj|null Item instance if item successfully set, NULL otherwise
2899
  */
2900
  set_item: function(item) {
2901
- //Set reference
2902
  var r = this.set_component('item', item);
2903
  return r;
2904
  },
@@ -2908,7 +2799,7 @@ var Content_Handler = {
2908
  * Sets value to NULL
2909
  */
2910
  clear_item: function() {
2911
- this.item = null;
2912
  },
2913
 
2914
  /* Evaluation */
@@ -2919,34 +2810,49 @@ var Content_Handler = {
2919
  * @return bool TRUE if type matches, FALSE otherwise
2920
  */
2921
  match: function(item) {
2922
- //Validate
2923
  var attr = 'match';
2924
  var m = this.get_attribute(attr);
2925
- //Stop processing types with no matching algorithm
2926
  if ( !this.util.is_empty(m) ) {
2927
- //Process regex patterns
2928
 
2929
- //String-based
2930
  if ( this.util.is_string(m) ) {
2931
- //Create new regexp object
2932
  m = new RegExp(m, "i");
2933
  this.set_attribute(attr, m);
2934
  }
2935
- //RegExp based
2936
  if ( this.util.is_type(m, RegExp) ) {
2937
  return m.test(item.get_uri());
2938
  }
2939
- //Process function
2940
  if ( this.util.is_func(m) ) {
2941
  return ( m.call(this, item) ) ? true : false;
2942
  }
2943
  }
2944
- //Default
2945
  return false;
2946
  },
2947
 
2948
  /* Processing/Output */
2949
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2950
  /**
2951
  * Render output to display item
2952
  * @param Content_Item item Item to render output for
@@ -2954,20 +2860,8 @@ var Content_Handler = {
2954
  */
2955
  render: function(item) {
2956
  var dfr = $.Deferred();
2957
- //Validate
2958
- var ret = this.call_attribute('render', item);
2959
- if ( this.util.is_promise(ret) ) {
2960
- ret.done(function(output) {
2961
- dfr.resolve(output);
2962
- });
2963
- } else {
2964
- //String format
2965
- if ( this.util.is_string(ret) ) {
2966
- ret = this.util.format(ret, item.get_uri());
2967
- }
2968
- //Resolve deferred immediately
2969
- dfr.resolve(ret);
2970
- }
2971
  return dfr.promise();
2972
  }
2973
  };
@@ -2988,7 +2882,6 @@ var Content_Item = {
2988
  'group': 'Group',
2989
  'type': 'Content_Handler'
2990
  },
2991
- _containers: ['group'],
2992
 
2993
  _attr_default: {
2994
  source: null,
@@ -3009,13 +2902,14 @@ var Content_Item = {
3009
  /* Properties */
3010
 
3011
  data: null,
 
3012
 
3013
  /* Init */
3014
 
3015
  _c: function(el) {
3016
- //Save element to instance
3017
  this.dom_set(el);
3018
- //Default initialization
3019
  this._super();
3020
  },
3021
 
@@ -3031,36 +2925,30 @@ var Content_Item = {
3031
  */
3032
  init_default_attributes: function() {
3033
  this._super();
3034
- //Add asset properties
3035
  var d = this.dom_get();
3036
- var key = d.attr('href') || null;
3037
- var assets = this.get_parent().assets || null;
3038
- //Merge asset data with default attributes
3039
  if ( this.util.is_string(key) ) {
3040
- var attrs = [{}, this._attr_default, {'permalink': key}];
3041
  if ( this.util.is_obj(assets) ) {
3042
  var t = this;
3043
- var get_assets = function(key, raw) {
 
 
 
 
 
 
3044
  var ret = {};
3045
  if ( key in assets && t.util.is_obj(assets[key]) ) {
3046
- var ret = assets[key];
3047
- if ( t.util.is_string(raw) ) {
3048
- var e = '_entries';
3049
- if ( !( e in ret ) || -1 == $.inArray(raw, ret[e]) ) {
3050
- ret = {};
3051
- }
3052
- }
3053
  }
3054
  return ret;
3055
  };
3056
- var asset = get_assets(key);
3057
- if ( this.util.is_empty(asset) && ( kpos = key.indexOf('?') ) && kpos != -1 ) {
3058
- var key_base = key.substr(0, kpos);
3059
- asset = get_assets(key_base, key);
3060
- }
3061
- if ( !this.util.is_empty(asset) ) {
3062
- attrs.push(asset);
3063
- }
3064
  }
3065
  this._attr_default = $.extend.apply(this, attrs);
3066
  }
@@ -3080,18 +2968,18 @@ var Content_Item = {
3080
  */
3081
  get_output: function() {
3082
  var dfr = $.Deferred();
3083
- //Check for cached output
3084
  var ret = this.get_attribute('output');
3085
  if ( this.util.is_string(ret) ) {
3086
  dfr.resolve(ret);
3087
  } else if ( this.has_type() ) {
3088
- //Render output from scratch (if necessary)
3089
- //Get item type
3090
  var type = this.get_type();
3091
- //Render type-based output
3092
  var item = this;
3093
  type.render(this).done(function(output) {
3094
- //Cache output
3095
  item.set_output(output);
3096
  dfr.resolve(output);
3097
  });
@@ -3128,15 +3016,17 @@ var Content_Item = {
3128
  * @return string Item URI
3129
  */
3130
  get_uri: function(mode) {
3131
- //Validate
3132
- if ( $.inArray(mode ,['source', 'permalink']) == -1 ) {
3133
  mode = 'source';
3134
  }
3135
- //Retrieve URI
3136
  var ret = this.get_attribute(mode);
3137
  if ( !this.util.is_string(ret) ) {
3138
- ret = ( 'source' == mode ) ? this.get_attribute('permalink') : '';
3139
  }
 
 
3140
  return ret;
3141
  },
3142
 
@@ -3146,28 +3036,32 @@ var Content_Item = {
3146
  get_title: function() {
3147
  var prop = 'title';
3148
  var prop_cached = prop + '_cached';
3149
- //Check for cached value
3150
  if ( this.has_attribute(prop_cached) ) {
3151
  return this.get_attribute(prop_cached, '');
3152
  }
3153
 
3154
  var title = '';
3155
- var sel_cap = '.wp-caption-text';
3156
- //Generate title from DOM values
3157
  var dom = this.dom_get();
3158
 
3159
- //Standalone link
3160
- if ( dom.length && !this.in_gallery() ) {
3161
- //Link title
3162
  title = dom.attr(prop);
3163
 
3164
- //Caption
3165
  if ( !title ) {
3166
- title = dom.siblings(sel_cap).html();
 
 
 
 
 
3167
  }
3168
  }
3169
 
3170
- //Saved attributes
3171
  if ( !title ) {
3172
  var props = ['caption', 'title'];
3173
  for ( var x = 0; x < props.length; x++ ) {
@@ -3178,25 +3072,41 @@ var Content_Item = {
3178
  }
3179
  }
3180
 
3181
- //Fallbacks
3182
  if ( !title && dom.length ) {
3183
- //Alt attribute
3184
  title = dom.find('img').first().attr('alt');
3185
 
3186
- //Element text
3187
  if ( !title ) {
3188
- title = dom.text();
3189
  }
3190
  }
3191
 
3192
- //Validate
3193
  if ( !this.util.is_string(title, false) ) {
3194
  title = '';
3195
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3196
 
3197
- //Cache retrieved value
3198
  this.set_attribute(prop_cached, title);
3199
- //Return value
3200
  return title;
3201
  },
3202
 
@@ -3217,6 +3127,10 @@ var Content_Item = {
3217
  this.data = data;
3218
  },
3219
 
 
 
 
 
3220
  /**
3221
  * Determine gallery type
3222
  * @return string|null Gallery type ID (NULL if item not in gallery)
@@ -3245,16 +3159,16 @@ var Content_Item = {
3245
  */
3246
  in_gallery: function(gType) {
3247
  var type = this.gallery_type();
3248
- //No gallery
3249
- if ( null == type ) {
3250
  return false;
3251
  }
3252
- //Boolean check
3253
  if ( !this.util.is_string(gType) ) {
3254
  return true;
3255
  }
3256
- //Check for specific gallery type
3257
- return ( gType == type ) ? true : false;
3258
  },
3259
 
3260
  /*-** Component References **-*/
@@ -3262,7 +3176,7 @@ var Content_Item = {
3262
  /* Viewer */
3263
 
3264
  get_viewer: function() {
3265
- return this.get_component('viewer');
3266
  },
3267
 
3268
  /**
@@ -3285,11 +3199,11 @@ var Content_Item = {
3285
  */
3286
  get_group: function(set_current) {
3287
  var prop = 'group';
3288
- //Check if group reference already set
3289
- var g = this.get_component(prop, true, false, false);
3290
  if ( g ) {
3291
  } else {
3292
- //Set empty group if no group exists
3293
  g = this.set_component(prop, new View.Group());
3294
  set_current = true;
3295
  }
@@ -3307,12 +3221,12 @@ var Content_Item = {
3307
  * > Item's group is reset if invalid group provided
3308
  */
3309
  set_group: function(g) {
3310
- //If group ID set, get object reference
3311
  if ( this.util.is_string(g) ) {
3312
- g = this.get_parent().get_group(g);
3313
  }
3314
 
3315
- //Set (or clear) group property
3316
  this.group = ( this.util.is_type(g, View.Group) ) ? g : false;
3317
  },
3318
 
@@ -3325,9 +3239,9 @@ var Content_Item = {
3325
  * @return Content_Handler|null Content Handler of item (NULL no valid type exists)
3326
  */
3327
  get_type: function() {
3328
- var t = this.get_component('type', false, false, false);
3329
  if ( !t ) {
3330
- t = this.set_type(this.get_parent().get_content_handler(this));
3331
  }
3332
  return t;
3333
  },
@@ -3359,19 +3273,35 @@ var Content_Item = {
3359
  * @param obj options (optional) Options
3360
  */
3361
  show: function(options) {
3362
- //Validate content handler
3363
  if ( !this.has_type() ) {
3364
  return false;
3365
  }
3366
- //Set display options
3367
  this.set_attribute('options_show', options);
3368
- //Retrieve viewer
3369
  var v = this.get_viewer();
3370
- //Load item
 
3371
  var ret = v.show(this);
3372
  return ret;
3373
  },
3374
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3375
  reset: function() {
3376
  this.set_attribute('options_show', null);
3377
  }
@@ -3401,28 +3331,52 @@ var Modeled_Component = {
3401
  * @return mixed Attribute value
3402
  */
3403
  get_attribute: function(key, def, check_model, enforce_type) {
3404
- //Validate
3405
  if ( !this.util.is_string(key) ) {
3406
- //Invalid requests sent straight to super method
3407
  return this._super(key, def, enforce_type);
3408
  }
3409
  if ( !this.util.is_bool(check_model) ) {
3410
  check_model = true;
3411
  }
3412
  var ret = null;
3413
- //Check model for attribute
3414
  if ( check_model ) {
3415
  var m = this.get_ancestor(key, false);
3416
  if ( this.util.in_obj(m, key) ) {
3417
  ret = m[key];
3418
  }
3419
  }
3420
- //Check standard attributes as fallback
3421
- if ( null == ret ) {
3422
  ret = this._super(key, def, enforce_type);
3423
  }
3424
  return ret;
3425
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3426
 
3427
  /**
3428
  * Set attribute value
@@ -3436,21 +3390,21 @@ var Modeled_Component = {
3436
  * @return mixed Attribute value
3437
  */
3438
  set_attribute: function(key, val, use_model) {
3439
- //Validate
3440
  if ( ( !this.util.is_string(key) ) || !this.util.is_set(val) ) {
3441
  return false;
3442
  }
3443
  if ( !this.util.is_bool(use_model) && !this.util.is_obj(use_model) ) {
3444
  use_model = true;
3445
  }
3446
- //Determine where to set attribute
3447
  if ( !!use_model ) {
3448
  var model = this.util.is_obj(use_model) ? use_model : this.get_model();
3449
 
3450
- //Set attribute in model
3451
  model[key] = val;
3452
  } else {
3453
- //Set as standard attribute
3454
  this._super(key, val);
3455
  }
3456
  return val;
@@ -3466,7 +3420,7 @@ var Modeled_Component = {
3466
  get_model: function() {
3467
  var m = this.get_attribute('model', null, false);
3468
  if ( !this.util.is_obj(m) ) {
3469
- //Set default value
3470
  m = {};
3471
  this.set_attribute('model', m, false);
3472
  }
@@ -3505,7 +3459,7 @@ var Modeled_Component = {
3505
  ret.push(m);
3506
  m = ( this.util.in_obj(m, 'parent') && this.util.is_obj(m.parent) ) ? m.parent : null;
3507
  }
3508
- //Remove current model from list
3509
  if ( !inc_current ) {
3510
  ret.shift();
3511
  }
@@ -3520,32 +3474,32 @@ var Modeled_Component = {
3520
  * @return obj Theme ancestor (Default: Current theme model)
3521
  */
3522
  get_ancestor: function(attr, safe_mode) {
3523
- //Validate
3524
  if ( !this.util.is_string(attr) ) {
3525
  return false;
3526
  }
3527
  if ( !this.util.is_bool(safe_mode) ) {
3528
  safe_mode = true;
3529
  }
3530
- var mcurr;
3531
- var m = mcurr = this.get_model();
3532
  var found = false;
3533
  while ( this.util.is_obj(m) ) {
3534
- //Check if attribute exists in model
3535
  if ( this.util.in_obj(m, attr) && !this.util.is_empty(m[attr]) ) {
3536
  found = true;
3537
  break;
3538
  }
3539
- //Get next model
3540
  m = ( this.util.in_obj(m, 'parent') ) ? m['parent'] : null;
3541
  }
3542
  if ( !found ) {
3543
  if ( safe_mode ) {
3544
- //Use current model as fallback
3545
  if ( this.util.is_empty(m) ) {
3546
  m = mcurr;
3547
  }
3548
- //Add attribute to object
3549
  if ( !this.util.in_obj(m, attr) ) {
3550
  m[attr] = null;
3551
  }
@@ -3574,8 +3528,6 @@ var Theme = {
3574
  },
3575
  _models: {},
3576
 
3577
- _containers: ['viewer'],
3578
-
3579
  _attr_default: {
3580
  template: null,
3581
  model: null
@@ -3593,25 +3545,25 @@ var Theme = {
3593
  * @see Component._c()
3594
  */
3595
  _c: function(id, attributes, viewer) {
3596
- //Validate
3597
- if ( arguments.length == 1 && this.util.is_type(arguments[0], View.Viewer) ) {
3598
  viewer = arguments[0];
3599
  id = null;
3600
  }
3601
- //Pass parameters to parent constructor
3602
  this._super(id, attributes);
3603
 
3604
- //Set viewer instance
3605
  this.set_viewer(viewer);
3606
 
3607
- //Set theme model
3608
  this.set_model(id);
3609
  },
3610
 
3611
  /* Viewer */
3612
 
3613
  get_viewer: function() {
3614
- return this.get_component('viewer', false, true, false);
3615
  },
3616
 
3617
  /**
@@ -3632,17 +3584,19 @@ var Theme = {
3632
  * @return Template instance
3633
  */
3634
  get_template: function() {
3635
- //Get saved template
3636
- var ret = this.get_component('template', true, false, false);
3637
- //Template needs to be initialized
3638
  if ( this.util.is_empty(ret) ) {
3639
- //Pass model to Template instance
3640
  var attr = { 'theme': this, 'model': this.get_model() };
3641
  ret = this.set_component('template', new View.Template(attr));
3642
  }
3643
  return ret;
3644
  },
3645
 
 
 
3646
  /**
3647
  * Retrieve tags from template
3648
  * All tags will be retrieved by default
@@ -3664,6 +3618,17 @@ var Theme = {
3664
  return $(this.get_template().dom_get_tag(tag, prop));
3665
  },
3666
 
 
 
 
 
 
 
 
 
 
 
 
3667
  /* Model */
3668
 
3669
  /**
@@ -3682,16 +3647,16 @@ var Theme = {
3682
  */
3683
  get_model: function(id) {
3684
  var ret = null;
3685
- //Pass request to superclass method
3686
  if ( !this.util.is_set(id) && this.util.is_obj( this.get_attribute('model', null, false) ) ) {
3687
  ret = this._super();
3688
  } else {
3689
- //Retrieve matching theme model
3690
  var models = this.get_models();
3691
  if ( !this.util.is_string(id) ) {
3692
- var id = this.get_parent().get_option('theme_default');
3693
  }
3694
- //Select first theme model if specified model is invalid
3695
  if ( !this.util.in_obj(models, id) ) {
3696
  id = $.map(models, function(v, key) { return key; })[0];
3697
  }
@@ -3706,13 +3671,15 @@ var Theme = {
3706
  */
3707
  set_model: function(id) {
3708
  this.set_attribute('model', this.get_model(id), false);
3709
- //Set ID using model attributes (if necessary)
3710
- if ( !this.check_id(true) ) {
 
3711
  var m = this.get_model();
3712
  if ( 'id' in m ) {
3713
- this.set_id(m.id);
3714
  }
3715
  }
 
3716
  },
3717
 
3718
  /* Properties */
@@ -3727,19 +3694,19 @@ var Theme = {
3727
  * @return array Class names
3728
  */
3729
  get_classes: function(rtype) {
3730
- //Build array of class names
3731
  var cls = [];
3732
  var thm = this;
3733
- //Include theme parent's class name
3734
  var models = this.get_ancestors(true);
3735
  $.each(models, function(idx, model) {
3736
  cls.push(thm.add_ns(model.id));
3737
  });
3738
- //Convert class names array to string
3739
  if ( this.util.is_string(rtype) ) {
3740
  cls = cls.join(rtype);
3741
  }
3742
- //Return class names
3743
  return cls;
3744
  },
3745
 
@@ -3751,52 +3718,52 @@ var Theme = {
3751
  */
3752
  get_measurement: function(attr, def) {
3753
  var meas = null;
3754
- //Validate
3755
  if ( !this.util.is_string(attr) ) {
3756
  return meas;
3757
  }
3758
  if ( !this.util.is_obj(def, false) ) {
3759
  def = {};
3760
  }
3761
- //Manage cache
3762
  var attr_cache = this.util.format('%s_cache', attr);
3763
  var cache = this.get_attribute(attr_cache, {}, false);
3764
  var status = '_status';
3765
  var item = this.get_viewer().get_item();
3766
  var w = $(window);
3767
- //Check cache freshness
3768
- if ( !( status in cache ) || !this.util.is_obj(cache[status]) || cache[status].width != w.width() || cache[status].height != w.height() ) {
3769
  cache = {};
3770
  }
3771
  if ( this.util.is_empty(cache) ) {
3772
- //Set status
3773
  cache[status] = {
3774
  'width': w.width(),
3775
  'height': w.height(),
3776
  'index': []
3777
  };
3778
  }
3779
- //Retrieve cached values
3780
  var pos = $.inArray(item, cache[status].index);
3781
- if ( pos != -1 && pos in cache ) {
3782
  meas = cache[pos];
3783
  }
3784
- //Generate measurement
3785
  if ( !this.util.is_obj(meas) ) {
3786
- //Get custom theme measurement
3787
  meas = this.call_attribute(attr);
3788
  if ( !this.util.is_obj(meas) ) {
3789
- //Retrieve fallback value
3790
  meas = this.get_measurement_default(attr);
3791
  }
3792
  }
3793
- //Normalize measurement
3794
  meas = ( this.util.is_obj(meas) ) ? $.extend({}, def, meas) : def;
3795
- //Cache measurement
3796
  pos = cache[status].index.push(item) - 1;
3797
  cache[pos] = meas;
3798
  this.set_attribute(attr_cache, cache, false);
3799
- //Return measurement (copy)
3800
  return $.extend({}, meas);
3801
  },
3802
 
@@ -3806,16 +3773,16 @@ var Theme = {
3806
  * @return obj Measurement values
3807
  */
3808
  get_measurement_default: function(attr) {
3809
- //Validate
3810
  if ( !this.util.is_string(attr) ) {
3811
  return null;
3812
  }
3813
- //Find default handler
3814
  attr = this.util.format('get_%s_default', attr);
3815
  if ( this.util.in_obj(this, attr) ) {
3816
  attr = this[attr];
3817
  if ( this.util.is_func(attr) ) {
3818
- //Execute default handler
3819
  attr = attr.call(this);
3820
  }
3821
  } else {
@@ -3840,37 +3807,37 @@ var Theme = {
3840
  var offset = { 'width': 0, 'height': 0 };
3841
  var v = this.get_viewer();
3842
  var vn = v.dom_get();
3843
- //Clone viewer
3844
  var vc = vn
3845
  .clone()
3846
  .attr('id', '')
3847
  .css({'visibility': 'hidden', 'position': 'absolute', 'top': ''})
3848
  .removeClass('loading')
3849
  .appendTo(vn.parent());
3850
- //Get offset from layout node
3851
  var l = vc.find(v.dom_get_selector('layout'));
3852
  if ( l.length ) {
3853
- //Clear inline styles
3854
  l.find('*').css({
3855
  'width': '',
3856
  'height': '',
3857
  'display': ''
3858
  });
3859
- //Resize content nodes
3860
  var tags = this.get_tags('item', 'content');
3861
  if ( tags.length ) {
3862
  var offset_item = v.get_item().get_dimensions();
3863
- //Set content dimensions
3864
  tags = $(l.find(tags[0].get_selector('full')).get(0)).css({'width': offset_item.width, 'height': offset_item.height});
3865
  $.each(offset_item, function(key, val) {
3866
  offset[key] = -1 * val;
3867
  });
3868
  }
3869
 
3870
- //Set offset
3871
  offset.width += l.width();
3872
  offset.height += l.height();
3873
- //Normalize
3874
  $.each(offset, function(key, val) {
3875
  if ( val < 0 ) {
3876
  offset[key] = 0;
@@ -3898,7 +3865,7 @@ var Theme = {
3898
  var v = this.get_viewer();
3899
  var dims = v.get_item().get_dimensions();
3900
  if ( v.get_attribute('autofit', false) ) {
3901
- //Get maximum dimensions
3902
  var margin = this.get_margin();
3903
  var offset = this.get_offset();
3904
  offset.height += margin.height;
@@ -3910,11 +3877,11 @@ var Theme = {
3910
  if ( max.height > offset.height ) {
3911
  max.height -= offset.height;
3912
  }
3913
- //Get resize factor
3914
  var factor = Math.min(max.width / dims.width, max.height / dims.height);
3915
- //Resize dimensions
3916
  if ( factor < 1 ) {
3917
- $.each(dims, function(key, val) {
3918
  dims[key] = Math.round(dims[key] * factor);
3919
  });
3920
  }
@@ -3929,12 +3896,36 @@ var Theme = {
3929
  get_dimensions: function() {
3930
  var dims = this.get_item_dimensions();
3931
  var offset = this.get_offset();
3932
- $.each(dims, function(key, val) {
3933
  dims[key] += offset[key];
3934
  });
3935
  return dims;
3936
  },
3937
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3938
  /* Output */
3939
 
3940
  /**
@@ -3948,7 +3939,7 @@ var Theme = {
3948
  var st = 'events_render';
3949
  if ( !this.get_status(st) ) {
3950
  this.set_status(st);
3951
- //Register events
3952
  tpl.on([
3953
  'render-init',
3954
  'render-loading',
@@ -3958,7 +3949,7 @@ var Theme = {
3958
  return thm.trigger(ev.type, ev.data);
3959
  });
3960
  }
3961
- //Render template
3962
  tpl.render(init);
3963
  },
3964
 
@@ -3977,12 +3968,12 @@ var Theme = {
3977
  el.stop(false, true);
3978
  }
3979
  });
3980
- }
3981
- //Stop queued animations
3982
  if ( !!clear_queue ) {
3983
  anim_stop();
3984
  }
3985
- //Get transition handlers
3986
  var attr_set = [attr, 'set'].join('_');
3987
  var trns;
3988
  if ( !this.get_attribute(attr_set) ) {
@@ -3995,19 +3986,19 @@ var Theme = {
3995
  trns.push(model[attr]);
3996
  }
3997
  });
3998
- //Merge transition handlers into current theme
3999
  trns.push({});
4000
  trns = this.set_attribute(attr, $.extend.apply($, trns.reverse()));
4001
  } else {
4002
  trns = this.get_attribute(attr, {});
4003
  }
4004
  if ( this.util.is_method(trns, event) ) {
4005
- //Disable animations if necessary
4006
  if ( !anim_on ) {
4007
  fx_temp = $.fx.off;
4008
  $.fx.off = true;
4009
  }
4010
- //Pass control to transition event
4011
  dfr = trns[event].call(this, v, $.Deferred());
4012
  }
4013
  }
@@ -4016,7 +4007,7 @@ var Theme = {
4016
  dfr.reject();
4017
  }
4018
  dfr.always(function() {
4019
- //Restore animation state
4020
  if ( null !== fx_temp ) {
4021
  $.fx.off = fx_temp;
4022
  }
@@ -4040,8 +4031,7 @@ var Template = {
4040
  _refs: {
4041
  'theme': 'Theme'
4042
  },
4043
- _containers: ['theme'],
4044
-
4045
  _attr_default: {
4046
  /**
4047
  * URI to layout (raw) file
@@ -4084,8 +4074,26 @@ var Template = {
4084
  this._super('', attributes);
4085
  },
4086
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4087
  get_theme: function() {
4088
- var ret = this.get_component('theme', true, false, false);
4089
  return ret;
4090
  },
4091
 
@@ -4104,7 +4112,7 @@ var Template = {
4104
  if ( !this.util.is_bool(init) ) {
4105
  init = false;
4106
  }
4107
- //Populate layout
4108
  if ( !init ) {
4109
  if ( !v.is_active() ) {
4110
  return false;
@@ -4114,14 +4122,14 @@ var Template = {
4114
  v.close();
4115
  return false;
4116
  }
4117
- //Iterate through tags and populate layout
4118
  if ( v.is_active() && this.has_tags() ) {
4119
  var loading_promise = this.trigger('render-loading');
4120
  var tpl = this;
4121
  var tags = this.get_tags(),
4122
  tag_promises = [];
4123
- //Render Tag output
4124
- loading_promise.done(function() {
4125
  if ( !v.is_active() ) {
4126
  return false;
4127
  }
@@ -4136,7 +4144,7 @@ var Template = {
4136
  r.tag.dom_get().html(r.output);
4137
  }));
4138
  });
4139
- //Fire event when all tags rendered
4140
  if ( !v.is_active() ) {
4141
  return false;
4142
  }
@@ -4146,7 +4154,7 @@ var Template = {
4146
  });
4147
  }
4148
  } else {
4149
- //Get Layout (basic)
4150
  this.trigger('render-init', this.dom_get());
4151
  }
4152
  },
@@ -4159,11 +4167,11 @@ var Template = {
4159
  * @return string Layout (HTML)
4160
  */
4161
  get_layout: function(parsed) {
4162
- //Validate
4163
  if ( !this.util.is_bool(parsed) ) {
4164
  parsed = true;
4165
  }
4166
- //Determine which layout to retrieve (raw/parsed)
4167
  var l = ( parsed ) ? this.parse_layout() : this.get_attribute('layout_raw', '');
4168
  return l;
4169
  },
@@ -4176,20 +4184,20 @@ var Template = {
4176
  * @return string Parsed layout
4177
  */
4178
  parse_layout: function() {
4179
- //Check for previously-parsed layout
4180
  var a = 'layout_parsed';
4181
  var ret = this.get_attribute(a);
4182
- //Return cached layout immediately
4183
  if ( this.util.is_string(ret) ) {
4184
  return ret;
4185
  }
4186
- //Parse raw layout
4187
  ret = this.sanitize_layout( this.get_layout(false) );
4188
  ret = this.parse_tags(ret);
4189
- //Save parsed layout
4190
  this.set_attribute(a, ret);
4191
 
4192
- //Return parsed layout
4193
  return ret;
4194
  },
4195
 
@@ -4199,28 +4207,30 @@ var Template = {
4199
  * @return obj|string Sanitized layout (Same data type that was passed to method)
4200
  */
4201
  sanitize_layout: function(l) {
4202
- //Stop processing if invalid value
4203
  if ( this.util.is_empty(l) ) {
4204
  return l;
4205
  }
4206
- //Set return type
4207
  var rtype = ( this.util.is_string(l) ) ? 'string' : null;
4208
  /* Quarantine hard-coded tags */
4209
 
4210
- //Create DOM structure from raw template
4211
  var dom = $(l);
4212
- //Find hard-coded tag nodes
4213
- var tag_temp = new View.Template_Tag();
4214
  var cls = tag_temp.get_class();
4215
  var cls_new = ['x', cls].join('_');
4216
- $(tag_temp.get_selector(), dom).each(function(idx) {
4217
- //Replace matching class name with blocking class
4218
  $(this).removeClass(cls).addClass(cls_new);
4219
  });
4220
- //Format return value
4221
  switch ( rtype ) {
4222
  case 'string' :
4223
  dom = dom.wrap('<div />').parent().html();
 
 
4224
  default :
4225
  l = dom;
4226
  }
@@ -4237,18 +4247,18 @@ var Template = {
4237
  * @return string Parsed layout
4238
  */
4239
  parse_tags: function(l) {
4240
- //Validate
4241
  if ( !this.util.is_string(l) ) {
4242
  return '';
4243
  }
4244
- //Parse tags in layout
4245
- //Tag regex
4246
  var re = /\{{2}\s*(\w.*?)\s*\}{2}/gim;
4247
- //Tag match results
4248
  var match;
4249
- //Iterate through template and find tags
4250
  while ( match = re.exec(l) ) {
4251
- //Replace tag in layout with DOM container
4252
  l = l.substring(0, match.index) + this.get_tag_container(match[1]) + l.substring(match.index + match[0].length);
4253
  }
4254
  return l;
@@ -4260,13 +4270,13 @@ var Template = {
4260
  * @return string DOM element
4261
  */
4262
  get_tag_container: function(tag) {
4263
- //Build element
4264
  var attr = this.get_tag_attribute();
4265
- return this.util.format('<span %s="%s"></span>', attr, escape(tag));
4266
  },
4267
 
4268
  get_tag_attribute: function() {
4269
- return this.get_parent().get_component_temp(View.Template_Tag).dom_get_attribute();
4270
  },
4271
 
4272
  /**
@@ -4292,43 +4302,54 @@ var Template = {
4292
  * Template is parsed if tags not set
4293
  * @param string name (optional) Tag type to retrieve instances of
4294
  * @param string prop (optional) Tag property to retrieve instances of
 
4295
  * @return array Template_Tag instances
4296
  */
4297
- get_tags: function(name, prop) {
 
 
 
 
 
4298
  var a = 'tags';
4299
  var tags = this.get_attribute(a);
4300
- //Initialize tags
4301
  if ( !this.util.is_array(tags) ) {
4302
  tags = [];
4303
- //Retrieve layout DOM tree
4304
  var d = this.dom_get();
4305
- //Select tag nodes
4306
  var attr = this.get_tag_attribute();
4307
  var nodes = $(d).find('[' + attr + ']');
4308
- //Build tag instances from nodes
4309
- $(nodes).each(function(idx) {
4310
- //Get tag placeholder
4311
  var el = $(this);
4312
- var tag = new View.Template_Tag(unescape(el.attr(attr)));
4313
- //Populate valid tags
4314
  if ( tag.has_handler() ) {
4315
- //Add tag to array
4316
  tags.push(tag);
4317
- //Connect tag to DOM node
4318
- tag.dom_set(el);
4319
- //Set classes
4320
- el.addClass(tag.get_classes(' '));
 
 
 
 
 
 
4321
  }
4322
- //Clear data attribute
4323
- el.removeAttr(attr);
4324
  });
4325
- //Save tags
4326
- this.set_attribute(a, tags, false);
 
 
4327
  }
4328
- tags = this.get_attribute(a, [], false);
4329
- //Filter tags by parameters
4330
  if ( !this.util.is_empty(tags) && this.util.is_string(name) ) {
4331
- //Normalize
4332
  if ( !this.util.is_string(prop) ) {
4333
  prop = false;
4334
  }
@@ -4336,9 +4357,9 @@ var Template = {
4336
  var tc = null;
4337
  for ( var x = 0; x < tags.length; x++ ) {
4338
  tc = tags[x];
4339
- if ( name == tc.get_name() ) {
4340
- //Check tag property
4341
- if ( !prop || prop == tc.get_prop() ) {
4342
  tags_filtered.push(tc);
4343
  }
4344
  }
@@ -4356,14 +4377,44 @@ var Template = {
4356
  return ( this.get_tags().length > 0 ) ? true : false;
4357
  },
4358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4359
  /*-** DOM **-*/
4360
 
4361
  /**
4362
  * Custom DOM initialization
4363
  */
4364
  dom_init: function() {
4365
- //Create DOM object from parsed layout
4366
  this.dom_set(this.get_layout());
 
4367
  },
4368
 
4369
  /**
@@ -4376,7 +4427,7 @@ var Template = {
4376
  var ret = $();
4377
  var tags = this.get_tags(tag, prop);
4378
  if ( tags.length ) {
4379
- //Build selector
4380
  var level = null;
4381
  if ( this.util.is_string(tag) ) {
4382
  level = ( this.util.is_string(prop) ) ? 'full' : 'tag';
@@ -4424,11 +4475,11 @@ var Template_Tag = {
4424
  * @param string tag_match Extracted tag match
4425
  */
4426
  parse: function(tag_match) {
4427
- //Return default value for invalid instances
4428
  if ( !this.util.is_string(tag_match) ) {
4429
  return false;
4430
  }
4431
- //Parse instance options
4432
  var parts = tag_match.split('|'),
4433
  part;
4434
  if ( !parts.length ) {
@@ -4439,23 +4490,23 @@ var Template_Tag = {
4439
  prop: null,
4440
  match: tag_match
4441
  };
4442
- //Get tag ID
4443
  attrs.name = parts[0];
4444
- //Get main property
4445
- if ( attrs.name.indexOf('.') != -1 ) {
4446
  attrs.name = attrs.name.split('.', 2);
4447
  attrs.prop = attrs.name[1];
4448
  attrs.name = attrs.name[0];
4449
  }
4450
- //Get other attributes
4451
  for ( var x = 1; x < parts.length; x++ ) {
4452
  part = parts[x].split(':', 1);
4453
  if ( part.length > 1 && !( part[0] in attrs ) ) {
4454
- //Add key/value pair to attributes
4455
  attrs[part[0]] = part[1];
4456
  }
4457
  }
4458
- //Save to instance
4459
  this.set_attributes(attrs, true);
4460
  },
4461
 
@@ -4464,7 +4515,7 @@ var Template_Tag = {
4464
  * @param Content_Item item
4465
  * @return obj jQuery.Promise object that is resolved when tag is rendered
4466
  * Parameters passed to callbacks
4467
- * > tag obj Current tag instance
4468
  * > output string Tag output
4469
  */
4470
  render: function(item) {
@@ -4515,20 +4566,20 @@ var Template_Tag = {
4515
  * @return array Class names
4516
  */
4517
  get_classes: function(rtype) {
4518
- //Build array of class names
4519
  var cls = [
4520
- //General tag class
4521
  this.get_class(),
4522
- //Tag name
4523
  this.get_class('tag'),
4524
- //Tag name + property
4525
  this.get_class('full')
4526
  ];
4527
- //Convert class names array to string
4528
  if ( this.util.is_string(rtype) ) {
4529
  cls = cls.join(rtype);
4530
  }
4531
- //Return class names
4532
  return cls;
4533
  },
4534
 
@@ -4542,21 +4593,27 @@ var Template_Tag = {
4542
  */
4543
  get_class: function(level) {
4544
  var cls = '';
 
4545
  switch ( level ) {
4546
  case 'tag' :
4547
- //Tag name
4548
- cls = this.add_ns(this.get_name());
4549
  break;
4550
  case 'full' :
4551
- //Tag name + property
4552
- cls = this.add_ns([this.get_name(), this.get_prop()].join('_'));
4553
- break;
4554
- default :
4555
- //General
4556
- cls = this.get_ns();
 
 
 
 
4557
  break;
4558
  }
4559
- return cls;
 
4560
  },
4561
 
4562
  /**
@@ -4565,7 +4622,15 @@ var Template_Tag = {
4565
  * @return string Tag selector
4566
  */
4567
  get_selector: function(level) {
4568
- return '.' + this.get_class(level);
 
 
 
 
 
 
 
 
4569
  }
4570
  };
4571
 
@@ -4594,40 +4659,31 @@ var Template_Tag_Handler = {
4594
  */
4595
  render: function(item, instance) {
4596
  var dfr = $.Deferred();
4597
- //Pass to attribute method
4598
- var ret = this.call_attribute('render', item, instance);
4599
- //Check for promise
4600
- if ( this.util.is_promise(ret) ) {
4601
- ret.done(function(output) {
4602
- dfr.resolve(output);
4603
- });
4604
- } else {
4605
- //Resolve non-promises immediately
4606
- dfr.resolve(ret);
4607
- }
4608
- //Return promise
4609
  return dfr.promise();
4610
  },
4611
 
4612
  add_prop: function(prop, fn) {
4613
- //Get attribute
4614
  var a = 'props';
4615
  var props = this.get_attribute(a);
4616
- //Validate
4617
  if ( !this.util.is_string(prop) || !this.util.is_func(fn) ) {
4618
  return false;
4619
  }
4620
  if ( !this.util.is_obj(props, false) ) {
4621
  props = {};
4622
  }
4623
- //Add property
4624
  props[prop] = fn;
4625
- //Save attribute
4626
  this.set_attribute(a, props);
4627
  },
4628
 
4629
  handle_prop: function(prop, item, instance) {
4630
- //Locate property
4631
  var props = this.get_attribute('props');
4632
  var out = '';
4633
  if ( this.util.is_obj(props) && ( prop in props ) && this.util.is_func(props[prop]) ) {
@@ -4642,8 +4698,6 @@ var Template_Tag_Handler = {
4642
  View.Template_Tag_Handler = Component.extend(Template_Tag_Handler);
4643
  /* Update References */
4644
 
4645
- //Attach to global object
4646
- SLB.attach('View', View);
4647
- View = SLB.View;
4648
- View.update_refs();
4649
- })(jQuery);
4
  * @subpackage View
5
  * @author Archetyped
6
  */
7
+ /* global SLB */
8
+ if ( !!window.SLB && !!SLB.attach ) { (function ($) {
 
 
 
 
9
 
10
  /*-** Controller **-*/
11
 
37
  */
38
  loading: [],
39
 
 
 
 
 
 
 
 
 
40
  /**
41
+ * Cache
 
 
42
  * @var object
43
  */
44
+ cache: {},
45
 
46
  /**
47
  * Temporary component instances
52
  component_temps: {},
53
 
54
  /* Options */
55
+ options: {},
 
 
 
 
 
56
 
57
  /* Methods */
58
 
59
  /* Init */
60
 
61
+ /**
62
+ * Instance initialization
63
+ */
64
+ _init: function() {
65
+ this._super();
66
+ // Component References
67
+ this.init_refs();
68
+ // Components
69
+ this.init_components();
70
+ },
71
+
72
+ /**
73
+ * Update component references in component definitions
74
+ */
75
+ init_refs: function() {
76
  var r;
77
  var ref;
78
+ var prop;
79
+ for ( prop in this ) {
80
+ prop = this[prop];
81
+ // Process only components
82
+ if ( !this.is_component(prop) ) {
83
  continue;
84
  }
85
+ // Update component references
86
+ if ( !this.util.is_empty(prop.prototype._refs) ) {
87
+ for ( r in prop.prototype._refs ) {
88
+ ref = prop.prototype._refs[r];
 
 
 
 
89
  if ( this.util.is_string(ref) && ref in this ) {
90
+ ref = prop.prototype._refs[r] = this[ref];
91
  }
92
+ if ( !this.util.is_class(ref) ) {
93
+ delete prop.prototype_refs[r];
94
  }
95
  }
96
  }
97
  }
 
 
 
98
  },
99
 
100
  /**
101
+ * Initialize Components
102
+ */
103
+ init_components: function() {
104
+ this.component_defaults = [
105
+ this.Viewer
106
+ ];
107
+ },
108
+
109
+ /**
110
+ * Client Initialization
111
+ * @param obj options Global options
112
  */
113
  init: function(options) {
114
  var t = this;
115
+ // Defer initialization until all components loaded
116
  $.when.apply($, this.loading).always(function() {
117
+ // Set options
118
  $.extend(true, t.options, options);
119
+
120
+ /* Event handlers */
121
+
122
+ // History
123
  $(window).on('popstate', function(e) {
124
  var state = e.originalEvent.state;
125
  if ( t.util.in_obj(state, ['item', 'viewer']) ) {
131
 
132
  /* Set defaults */
133
 
134
+ // Items
135
  t.init_items();
136
  });
137
  },
138
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  /* Components */
140
 
141
+ /**
142
+ * Check if default component instance can be created
143
+ * @uses View.component_defaults
144
+ * @param func type Component type to check
145
+ * @return bool TRUE if default component instance creation is allowed
146
+ */
147
+ can_make_default_component: function(type) {
148
+ return ( -1 !== $.inArray(type, this.component_defaults) );
 
149
  },
150
 
151
  /**
152
+ * Check if object is valid component class
153
+ * @param func comp Component to check
154
+ * @return bool TRUE if object is valid component class
155
  */
156
+ is_component: function(comp) {
157
+ return ( this.util.is_class(comp, this.Component) );
 
158
  },
159
 
160
  /**
161
  * Retrieve collection of components of specified type
162
+ * @param func type Component type
163
+ * @return object Component collection (Default: Empty object)
164
  */
165
  get_components: function(type) {
166
+ var ret = {};
167
+ if ( this.is_component(type) ) {
168
+ // Determine collection based on component slug
169
+ var coll = type.prototype._slug + 's';
170
+ // Create default collection
171
+ if ( ! ( coll in this.cache ) ) {
172
+ this.cache[coll] = {};
 
173
  }
174
+ ret = this.cache[coll];
175
  }
176
  return ret;
177
  },
184
  */
185
  get_component: function(type, id) {
186
  var ret = null;
187
+ // Validate parameters
188
  if ( !this.util.is_func(type) ) {
189
  return ret;
190
  }
191
+ // Sanitize id
192
  if ( !this.util.is_string(id) ) {
193
  id = null;
194
  }
195
 
196
+ // Get component from collection
197
  var coll = this.get_components(type);
198
  if ( this.util.is_obj(coll) ) {
199
  var tid = ( this.util.is_string(id) ) ? id : this.util.add_prefix('default');
202
  }
203
  }
204
 
205
+ // Default: Create default component
206
  if ( this.util.is_empty(ret) ) {
207
+ if ( this.util.is_string(id) || this.can_make_default_component(type) ) {
208
  ret = this.add_component(type, id);
209
  }
210
  }
211
+ // Return component
212
  return ret;
213
  },
214
 
220
  * @return object|null New component (NULL if invalid)
221
  */
222
  add_component: function(type, id, options) {
223
+ // Validate type
224
  if ( !this.util.is_func(type) ) {
225
  return false;
226
  }
227
+ // Validate request
228
+ if ( this.util.is_empty(id) && !this.can_make_default_component(type) ) {
229
  return false;
230
  }
231
+ // Defaults
232
  var ret = null;
233
  if ( this.util.is_empty(id) ) {
234
  id = this.util.add_prefix('default');
236
  if ( !this.util.is_obj(options) ) {
237
  options = {};
238
  }
239
+ // Check if specialized method exists for component type
240
+ var m = ( 'component' !== type.prototype._slug ) ? 'add_' + type.prototype._slug : null;
241
  if ( !this.util.is_empty(m) && ( m in this ) && this.util.is_func(this[m]) ) {
242
  ret = this[m](id, options);
243
  }
244
+ // Default process
245
  else {
246
  ret = new type(id, options);
247
  }
248
 
249
+ // Add new component to collection
250
  if ( this.util.is_type(ret, type) ) {
251
+ // Get collection
252
  var coll = this.get_components(type);
253
+ // Add to collection
254
  switch ( $.type(coll) ) {
255
  case 'object' :
256
  coll[id] = ret;
262
  } else {
263
  ret = null;
264
  }
265
+ // Return new component
266
  return ret;
267
  },
268
 
273
  */
274
  add_component_temp: function(type) {
275
  var ret = null;
276
+ if ( this.is_component(type) ) {
277
+ // Create new instance
278
  ret = new type('');
279
+ // Save to collection
280
  this.component_temps[ret._slug] = ret;
281
  }
282
  return ret;
298
  * @return bool TRUE if temp instance exists, FALSE otherwise
299
  */
300
  has_component_temp: function(type) {
301
+ return ( this.is_component(type) && ( type.prototype._slug in this.component_temps ) ) ? true : false;
302
  },
303
 
304
  /* Properties */
310
  */
311
  get_options: function(opts) {
312
  var ret = {};
313
+ // Validate
314
  if ( this.util.is_string(opts) ) {
315
  opts = [opts];
316
  }
317
  if ( !this.util.is_array(opts) ) {
318
  return ret;
319
  }
320
+ // Get specified options
321
  for ( var x = 0; x < opts.length; x++ ) {
322
+ // Skip if option not set
323
  if ( !( opts[x] in this.options ) ) {
324
  continue;
325
  }
351
  * Add viewer instance to collection
352
  * @param string id Viewer ID
353
  * @param obj options Viewer options
354
+ * @return Viewer New viewer instance
355
  */
356
  add_viewer: function(id, options) {
357
+ // Create viewer
 
 
 
 
 
 
 
358
  var v = new this.Viewer(id, options);
359
+ // Save viewer
360
+ this.get_viewers()[v.get_id()] = v;
361
+ // Return viewer
362
  return v;
363
  },
364
 
367
  * @return obj Viewer instances
368
  */
369
  get_viewers: function() {
370
+ return this.get_components(this.Viewer);
371
  },
372
 
373
  /**
387
  * @return Viewer Viewer instance
388
  */
389
  get_viewer: function(v) {
390
+ // Retrieve default viewer if specified viewer not set
391
  if ( !this.has_viewer(v) ) {
392
  v = this.util.add_prefix('default');
393
+ // Create default viewer if necessary
394
  if ( !this.has_viewer(v) ) {
395
+ v = this.add_viewer(v);
396
+ v = v.get_id();
397
  }
398
  }
399
  return this.get_viewers()[v];
405
  * Set event handlers
406
  */
407
  init_items: function() {
408
+ // Define handler
409
  var t = this;
410
  var handler = function() {
411
  var ret = t.show_item(this);
415
  return !ret;
416
  };
417
 
418
+ // Get activated links
419
  var sel = this.util.format('a[href][%s="%s"]', this.util.get_attribute('active'), 1);
420
+ // Add event handler
421
+ $(document).on('click', sel, null, handler);
422
  },
423
 
424
+ /**
425
+ * Retrieve cached items
426
+ * @return obj Items collection
427
+ */
428
  get_items: function() {
429
  return this.get_components(this.Content_Item);
430
  },
438
  * @return Content_Item Item instance for DOM node
439
  */
440
  get_item: function(ref) {
441
+ // Evaluate reference type
442
 
443
+ // Content Item instance
444
  if ( this.util.is_type(ref, this.Content_Item) ) {
445
  return ref;
446
  }
447
+ // Retrieve item instance
448
  var item = null;
449
 
450
+ // DOM element
451
  if ( this.util.in_obj(ref, 'nodeType') ) {
452
+ // Check if item instance attached to element
453
  var key = this.get_component_temp(this.Content_Item).get_data_key();
454
  item = $(ref).data(key);
455
  }
456
+ // Cached item (index)
457
+ else if ( this.util.is_string(ref, false) ) {
458
  var items = this.get_items();
459
+ if ( ref in items ) {
460
  item = items[ref];
461
  }
462
  }
463
+ // Create default item instance
464
+ if ( !this.util.is_instance(item, this.Content_Item) ) {
465
  item = this.add_item(ref);
466
  }
467
  return item;
473
  * @return Content_Item New item instance
474
  */
475
  add_item: function(el) {
476
+ return ( new this.Content_Item(el) );
 
477
  },
478
 
479
  /**
480
  * Display item in viewer
481
  * @param obj el DOM element representing item
482
+ * @return bool Display result (TRUE if item displayed without issues)
483
  */
484
  show_item: function(el) {
485
+ return this.get_item(el).show();
 
486
  },
487
 
488
  /**
489
  * Cache item instance
490
+ * @uses View.get_items() to retrieve item cache
491
  * @param Content_Item item Item to cache
492
+ * @return Content_item Item instance
493
  */
494
  save_item: function(item) {
495
+ if ( !this.util.is_instance(item, this.Content_Item) ) {
496
+ return item;
 
 
 
 
 
 
 
 
 
497
  }
498
+ // Save item
499
+ this.get_items()[item.get_id()] = item;
500
+ // Return item instance
501
+ return item;
502
  },
503
 
504
  /* Content Handler */
505
 
506
+ /**
507
+ * Retrieve content handlers
508
+ * @return object Content handlers
509
+ */
510
  get_content_handlers: function() {
511
  return this.get_components(this.Content_Handler);
512
  },
517
  * @return Content_Handler|null Matching content handler (NULL if no matching handler found)
518
  */
519
  get_content_handler: function(item) {
520
+ // Determine handler to retrieve
521
+ var type = ( this.util.is_instance(item, this.Content_Item) ) ? item.get_attribute('type', '') : item.toString();
522
+ // Retrieve handler
523
  var types = this.get_content_handlers();
524
  return ( type in types ) ? types[type] : null;
525
  },
526
 
527
  /**
528
+ * Add/Update Content Handler
529
  * @param string id Handler ID
530
+ * @param obj attr Handler attributes
531
+ * @return obj|null Handler instance (NULL on failure)
532
  */
533
+ extend_content_handler: function(id, attr) {
534
+ var hdl = null;
535
+ if ( !this.util.is_string(id) || !this.util.is_obj(attr) ) {
536
+ return hdl;
537
  }
538
+ hdl = this.get_content_handler(id);
539
+ // Add new content handler
540
+ if ( null === hdl ) {
541
+ var hdls = this.get_content_handlers();
542
+ hdls[id] = hdl = new this.Content_Handler(id, attr);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
543
  }
544
+ // Update existing handler
545
+ else {
546
+ hdl.set_attributes(attr);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
547
  }
548
+ // Load styles
549
+ if ( this.util.in_obj(attr, 'styles') ) {
550
+ this.load_styles(attr.styles);
 
551
  }
552
+ return hdl;
 
553
  },
554
 
555
  /* Group */
559
  * @param string g Group ID
560
  * > If group with same ID already set, new group replaces existing one
561
  * @param object attrs (optional) Group attributes
562
+ * @return Group New group
563
  */
564
  add_group: function(g, attrs) {
565
+ // Create new group
566
  g = new this.Group(g, attrs);
567
+ // Cache group
568
+ this.get_groups()[g.get_id()] = g;
569
+
570
+ return g;
571
  },
572
 
573
  /**
576
  * @return object Registered groups
577
  */
578
  get_groups: function() {
579
+ return this.get_components(this.Group);
580
  },
581
 
582
  /**
583
  * Retrieve specified group
584
+ * New group created if not yet set
585
+ * @uses View.has_group()
586
+ * @uses View.add_group()
587
+ * @uses View.get_groups()
588
  * @param string g Group ID
589
+ * @return Group Group instance
590
  */
591
  get_group: function(g) {
592
+ return ( !this.has_group(g) ) ? this.add_group(g) : this.get_groups()[g];
 
 
 
 
 
 
 
 
593
  },
594
 
595
  /**
598
  * @return bool TRUE if group exists, FALSE otherwise
599
  */
600
  has_group: function(g) {
601
+ return ( this.util.is_string(g) && ( g in this.get_groups() ) );
602
  },
603
 
604
  /* Theme */
605
 
606
  /**
607
+ * Add/Update theme
608
  * @param string name Theme name
609
+ * @param obj attr (optional) Theme options
610
+ * @return obj|bool Theme model
 
611
  */
612
+ extend_theme: function(id, attr) {
613
+ // Validate
 
614
  if ( !this.util.is_string(id) ) {
615
  return false;
616
  }
617
  var dfr = $.Deferred();
618
  this.loading.push(dfr);
619
 
620
+ // Get model if it already exists
621
+ var model = this.get_theme_model(id);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
622
 
623
+ // Create default attributes for new theme
624
+ if ( this.util.is_empty(model) ) {
625
+ // Save default model
626
+ model = this.save_theme_model( {'parent': null, 'id': id} );
627
  }
628
 
629
+ // Add custom attributes
630
+ if ( this.util.is_obj(attr) ) {
631
+ // Sanitize
632
+ if ( 'id' in attr ) {
633
+ delete(attr['id']);
634
+ }
635
+ $.extend(model, attr);
 
 
 
 
 
 
 
 
636
  }
637
 
638
+ // Load styles
639
+ if ( this.util.in_obj(attr, 'styles') ) {
640
+ this.load_styles(attr.styles);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
  }
642
 
643
+ // Link parent model
644
+ if ( !this.util.is_obj(model.parent) ) {
645
+ model.parent = this.get_theme_model(model.parent);
 
 
 
 
 
 
 
646
  }
647
 
648
+ // Complete loading when all components loaded
649
+ dfr.resolve();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
650
  return model;
651
  },
652
 
655
  * @return obj Theme models
656
  */
657
  get_theme_models: function() {
658
+ // Retrieve matching theme model
659
  return this.Theme.prototype._models;
660
  },
661
 
670
  },
671
 
672
  /**
673
+ * Save theme model
674
+ * @uses View.get_theme_models() to retrieve Theme model collection
675
+ * @param obj Theme model to save
676
+ * @return obj Saved model
677
  */
678
+ save_theme_model: function(model) {
679
+ if ( this.util.in_obj(model, 'id') && this.util.is_string(model.id) ) {
680
+ // Save model
681
+ this.get_theme_models()[model.id] = model;
682
+ }
683
+ return model;
684
+ },
685
+
686
+ /**
687
+ * Add/Update Template Tag Handler
688
+ * @param string id Handler ID
689
+ * @param obj attr Handler attributes
690
+ * @return obj|bool Handler instance (FALSE on failure)
691
+ */
692
+ extend_template_tag_handler: function(id, attr) {
693
+ if ( !this.util.is_string(id) || !this.util.is_obj(attr) ) {
694
  return false;
695
  }
696
+ var hdl;
697
+ var hdls = this.get_template_tag_handlers();
698
+ if ( this.util.in_obj(hdls, id) ) {
699
+ // Update existing handler
700
+ hdl = hdls[id];
701
+ hdl.set_attributes(attr);
 
 
 
 
 
 
 
 
 
 
 
 
 
702
  } else {
703
+ // Add new content handler
704
+ hdl = new this.Template_Tag_Handler(id, attr);
705
+ hdls[hdl.get_id()] = hdl;
706
  }
707
+ // Load styles
708
+ if ( this.util.in_obj(attr, 'styles') ) {
709
+ this.load_styles(attr.styles);
710
+ }
711
+ // Set hooks
712
+ if ( this.util.in_obj(attr, '_hooks') ) {
713
+ attr._hooks.call(hdl);
714
+ }
715
+ return hdl;
716
  },
717
 
718
  /**
726
  /**
727
  * Retrieve template tag handler
728
  * @param string id ID of tag handler to retrieve
729
+ * @return Template_Tag_Handler|null Tag Handler instance (NULL for invalid ID)
730
  */
731
  get_template_tag_handler: function(id) {
732
  var handlers = this.get_template_tag_handlers();
733
+ // Retrieve existing handler or return new handler
734
+ return ( this.util.in_obj(handlers, id) ) ? handlers[id] : null;
735
+ },
736
+
737
+ /**
738
+ * Load styles
739
+ * @param array styles Styles to load
740
+ */
741
+ load_styles: function(styles) {
742
+ if ( this.util.is_array(styles) ) {
743
+ var out = [];
744
+ var style;
745
+ for ( var x = 0; x < styles.length; x++ ) {
746
+ style = styles[x];
747
+ if ( !this.util.in_obj(style, 'uri') || !this.util.is_string(style.uri) ) {
748
+ continue;
749
+ }
750
+ out.push('<link rel="stylesheet" type="text/css" href="' + style.uri + '" />');
751
+ }
752
+ $('head').append(out.join(''));
753
  }
 
 
754
  }
755
  };
756
 
766
  */
767
  _slug: 'component',
768
 
769
+ /**
770
+ * Component namespace
771
+ * @var string
772
+ */
773
+ _ns: null,
774
+
775
  /**
776
  * Valid component references for current component
777
+ * @var object
778
  * > Key (string): Property name that stores reference
779
  * > Value (function): Data type of component
 
780
  */
781
  _refs: {},
782
 
 
 
 
 
 
 
 
 
 
783
  /**
784
  * Whether DOM element and component are connected in 1:1 relationship
785
  * Some components will be assigned to different DOM elements depending on usage
792
  * @var DOM Element
793
  */
794
  _dom: null,
795
+
796
+ /**
797
+ * Component attributes
798
+ * @var object
799
+ * > Key: Attribute ID
800
+ * > Value: Attribute value
801
+ */
802
+ _attributes: false,
803
 
804
  /**
805
  * Default attributes
808
  _attr_default: {},
809
 
810
  /**
811
+ * Flag indicates whether default attributes have previously been parsed
812
+ * @var bool
813
  */
814
+ _attr_default_parsed: false,
815
 
816
  /**
817
+ * Attributes passed to constructor
818
+ * @var obj
819
  */
820
+ _attr_init: null,
821
 
822
  /**
823
  * Defines how parent properties should be remapped to component properties
831
  * > Key: string Event type
832
  * > Value: array Handlers
833
  */
834
+ _events: {},
835
 
836
  /**
837
  * Status management
841
  */
842
  _status: null,
843
 
 
 
 
 
844
  /**
845
  * Component ID
846
  * @var string
847
  */
848
+ _id: '',
849
 
850
  /* Init */
851
 
852
+ /**
853
+ * Constructor
854
+ * @param string id (optional) Component ID (Default ID will be generated if no valid ID provided)
855
+ * @param object attributes (optional) Component attributes
856
+ */
857
  _c: function(id, attributes) {
858
+ // Set ID
859
+ this._set_id(id);
860
+ // Save init attributes
861
+ if ( this.util.is_obj(attributes) ) {
862
+ this._attr_init = attributes;
863
+ }
864
+ // Call hooks
865
+ this._hooks();
866
  },
867
 
868
+ /**
869
+ * Set Component parent to View module
870
+ * @uses `_super._set_parent()`
871
+ */
872
  _set_parent: function() {
873
+ this._super(View);
 
874
  },
875
 
876
  /**
877
  * Register hooks on init
878
  * Placeholder method to be overridden by child classes
879
  */
880
+ _hooks: function() {},
881
 
882
  /* Methods */
883
 
884
  /* Properties */
885
 
886
  /**
887
+ * Set instance ID
888
+ * Instance ID can only be set once (will not change ID if valid ID already set)
889
+ * Generates random GUID if no valid ID provided
890
+ * @uses Utilities.guid()
891
+ * @param string id Unique ID
892
+ * @return string Instance ID
 
 
 
 
 
 
 
 
 
 
 
 
893
  */
894
+ _set_id: function(id) {
895
+ // Set ID only once
896
+ if ( this.util.is_empty(this._id) ) {
897
+ this._id = ( this.util.is_string(id) ) ? id : this.util.guid();
 
 
 
 
 
 
 
 
 
 
898
  }
899
+ return this._id;
900
  },
901
 
902
  /**
907
  * @return string Instance ID
908
  */
909
  get_id: function(ns) {
910
+ // Get raw ID
911
+ var id = this._id;
912
+ // Namespace ID
 
 
 
913
  if ( this.util.is_bool(ns) && ns ) {
914
  id = this.add_ns(id);
915
  }
917
  return id;
918
  },
919
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
920
  /**
921
  * Get namespace
922
  * @uses _slug for namespace segment
924
  * @return string Component namespace
925
  */
926
  get_ns: function() {
927
+ if ( null === this._ns ) {
928
+ this._ns = this.util.add_prefix(this._slug);
929
+ }
930
+ return this._ns;
931
  },
932
 
933
  /**
939
  return ( this.util.is_string(val) ) ? this.get_ns() + '_' + val : '';
940
  },
941
 
942
+ /**
943
+ * Retrieve status
944
+ * @param string id Status to retrieve
945
+ * @param bool raw (optional) Retrieve raw value (Default: FALSE)
946
+ * @return mixed Status value (Default: bool)
947
+ */
948
+ get_status: function(id, raw) {
949
+ var ret = false;
950
+ if ( this.util.in_obj(this._status, id) ) {
951
+ ret = ( !!raw ) ? this._status[id] : !!this._status[id];
952
+ }
953
+ return ret;
954
+ },
955
 
956
  /**
957
+ * Set status
958
+ * @param string id Status to retrieve
959
+ * @param mixed val Status value (Default: TRUE)
960
+ * @return mixed Status value
961
  */
962
+ set_status: function(id, val) {
963
+ // Validate ID
964
+ if ( this.util.is_string(id) ) {
965
+ // Validate value
966
+ if ( !this.util.is_set(val) ) {
967
+ val = true;
968
+ }
969
+ // Initialize status collection
970
+ if ( !this.util.is_obj(this._status, false) ) {
971
+ this._status = {};
972
+ }
973
+ // Set status
974
+ this._status[id] = val;
975
+ } else if ( !this.util.is_set(val) ) {
976
+ val = false;
977
  }
978
+ return val;
 
979
  },
980
 
981
  /**
982
+ * Get controller object
983
+ * @uses get_parent() (alias)
984
+ * @return object Controller object (SLB.View)
985
  */
986
+ get_controller: function() {
987
+ return this.get_parent();
988
  },
989
 
990
+ /* Components */
991
+
992
  /**
993
  * Check if reference exists in object
994
  * @param string ref Reference ID
1017
  return ( this.has_reference(ref) ) ? this._refs[ref] : null;
1018
  },
1019
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1020
  /**
1021
  * Retrieve component reference from current object
1022
  * > Procedure:
1023
+ * > Check if top-level property already set
1024
  * > Check attributes
1025
+ * > Check parent object (controller)
 
 
1026
  * @param string cname Component name
1027
+ * @param object options (optional) Request options
1028
+ * > check_attr bool Whether or not to check instance attributes for component (Default: TRUE)
1029
+ * > get_default bool Whether or not to retrieve default object from controller if none exists in current instance (Default: FALSE)
1030
  * @return object|null Component reference (NULL if no component found)
1031
  */
1032
+ get_component: function(cname, options) {
1033
  var c = null;
1034
+ // Validate request
1035
+ if ( !this.has_reference(cname) ) {
1036
  return c;
1037
  }
1038
 
1039
+ // Initialize options
1040
+ var opt_defaults = {
1041
+ check_attr: true,
1042
+ get_default: false
1043
+ };
1044
+ options = $.extend({}, opt_defaults, options);
1045
+
1046
+ // Get component type
1047
+ var ctype = this.get_reference(cname);
1048
+
1049
+ // Phase 1: Check if component reference previously set
 
1050
  if ( this.util.is_type(this[cname], ctype) ) {
1051
  return this[cname];
1052
  }
1053
+ // If reference not set, iterate through component hierarchy until reference is found
1054
  c = this[cname] = null;
1055
 
1056
+ // Phase 2: Check attributes
1057
+ if ( options.check_attr ) {
1058
  c = this.get_attribute(cname);
1059
+ // Save object-specific component reference
1060
  if ( !this.util.is_empty(c) ) {
1061
  c = this.set_component(cname, c);
1062
  }
1063
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1064
 
1065
+ // Phase 3: From controller (optional)
1066
+ if ( this.util.is_empty(c) && options.get_default ) {
1067
+ c = this.get_controller().get_component(ctype);
1068
  }
1069
  return c;
1070
  },
1078
  * @return object Component (NULL if invalid)
1079
  */
1080
  set_component: function(name, ref, validate) {
1081
+ var invalid = null;
1082
+ // Make sure component property exists
1083
  if ( !this.has_reference(name) ) {
1084
+ return invalid;
1085
  }
1086
+
1087
+ // Validate reference
1088
  if ( this.util.is_empty(ref) ) {
1089
+ ref = invalid;
1090
+ } else {
1091
+ var ctype = this.get_reference(name);
1092
+
1093
+ // Get component from controller when ID supplied
1094
+ if ( this.util.is_string(ref, false) ) {
1095
+ ref = this.get_controller().get_component(ctype, ref);
1096
+ }
1097
+
1098
+ // Validation callback
1099
+ if ( !this.util.is_type(ref, ctype) || ( this.util.is_func(validate) && !validate.call(this, ref) ) ) {
1100
+ ref = invalid;
1101
+ }
1102
  }
1103
 
1104
+ // Set (or clear) component reference
 
 
 
 
 
 
 
 
1105
  this[name] = ref;
1106
+ // Return value for confirmation
1107
  return this[name];
1108
  },
1109
+
1110
+
1111
+ /**
1112
+ * Clear component reference
1113
+ * @uses set_component() to handle component manipulation
1114
+ * @param string Component name
1115
+ */
1116
+ clear_component: function(name) {
1117
+ this.set_component(name, null);
1118
+ },
1119
 
1120
  /* Attributes */
1121
 
1122
  /**
1123
+ * Initialize attributes
1124
+ * @param bool force (optional) Force full initialization of attributes (Default: FALSE)
1125
+ * @return void
1126
  */
1127
  init_attributes: function(force) {
1128
  if ( !this.util.is_bool(force) ) {
1129
  force = false;
1130
  }
1131
+ if ( force || !this.util.is_obj(this._attributes) ) {
1132
+ var a = this._attributes = {};
1133
+ // Default attributes
1134
+ $.extend(a, this.init_default_attributes());
1135
+ // Instantiation attributes
 
 
 
1136
  if ( this.util.is_obj(this._attr_init) ) {
1137
+ $.extend(a, this._attr_init);
1138
  }
1139
+ // DOM attributes
1140
+ $.extend(a, this.get_dom_attributes());
1141
  }
1142
  },
1143
 
1144
  /**
1145
  * Generate default attributes for component
 
1146
  * @uses View.get_options() to get values from controller
1147
  * @uses _attr_map to Remap controller attributes to instance attributes
1148
  * @uses _attr_default to Store default attributes
1149
+ * @return obj Default attributes
1150
  */
1151
  init_default_attributes: function() {
1152
+ // Get options from controller
1153
+ if ( !this._attr_default_parsed && this.util.is_obj(this._attr_map) ) {
1154
+ var opts = this.get_controller().get_options(this.util.obj_keys(this._attr_map));
1155
+
1156
+ if ( this.util.is_obj(opts) ) {
1157
+ // Remap
1158
+ for ( var opt in this._attr_map ) {
1159
+ if ( opt in opts && null !== this._attr_map[opt]) {
1160
+ // Move value to new property
1161
+ opts[this._attr_map[opt]] = opts[opt];
1162
+ // Delete old property
1163
+ delete opts[opt];
1164
+ }
1165
  }
1166
+ // Merge with default attributes
1167
+ $.extend(true, this._attr_default, opts);
1168
  }
1169
+ this._attr_default_parsed = true;
 
1170
  }
1171
  return this._attr_default;
1172
  },
1173
 
1174
  /**
1175
  * Retrieve DOM attributes
1176
+ * @return obj DOM Attributes
1177
  */
1178
  get_dom_attributes: function() {
1179
  var attrs = {};
1180
+ var el = this.dom_get(null, {'init': false});
1181
+ if ( el.length > 0 ) {
1182
+ // Get attributes from element
1183
+ var attrs_full = $(el).get(0).attributes;
1184
+ if ( this.util.is_obj(attrs_full) ) {
1185
  var attr_prefix = this.util.get_attribute();
1186
+ var attr_key;
1187
+ $.each(attrs_full, function(idx, attr) {
1188
+ if ( attr.name.indexOf( attr_prefix ) === -1 ) {
1189
  return true;
1190
  }
1191
+ // Process custom attributes (Strip prefix)
1192
+ attr_key = attr.name.substr(attr_prefix.length + 1);
1193
+ attrs[attr_key] = attr.value;
 
1194
  });
1195
  }
1196
  }
1199
 
1200
  /**
1201
  * Retrieve all instance attributes
1202
+ * @uses init_attributes() to initialize attributes (if necessary)
1203
+ * @uses _attributes object
1204
+ * @return obj Component attributes
1205
  */
1206
  get_attributes: function() {
1207
+ // Initilize attributes
1208
  this.init_attributes();
1209
+ // Return attributes
1210
+ return this._attributes;
1211
  },
1212
 
1213
  /**
1220
  * @return mixed Attribute value (NULL if attribute is not set)
1221
  */
1222
  get_attribute: function(key, def, enforce_type) {
1223
+ // Validate
1224
  if ( !this.util.is_set(def) ) {
1225
  def = null;
1226
  }
1230
  if ( !this.util.is_bool(enforce_type) ) {
1231
  enforce_type = true;
1232
  }
1233
+
1234
+ // Get attribute value
1235
  var ret = ( this.has_attribute(key) ) ? this.get_attributes()[key] : def;
1236
+ // Validate type
1237
  if ( enforce_type && ret !== def && null !== def && !this.util.is_type(ret, $.type(def), false) ) {
1238
+ // Convert type
1239
+ // Scalar default
1240
  if ( this.util.is_scalar(def, false) ) {
 
1241
  if ( !this.util.is_scalar(ret, false) ) {
1242
+ // Non-scalar attribute
1243
  ret = def;
1244
  } else if ( this.util.is_string(def, false) ) {
1245
+ // Convert to string
1246
  ret = ret.toString();
1247
  } else if ( this.util.is_num(def, false) && !this.util.is_num(ret, false) ) {
1248
+ // Convert to number
1249
  ret = ( this.util.is_int(def, false) ) ? parseInt(ret) : parseFloat(ret);
1250
  if ( !this.util.is_num(ret, false) ) {
1251
  ret = def;
1252
  }
1253
  } else if ( this.util.is_bool(def, false) ) {
1254
+ // Convert to boolean
1255
  ret = ( this.util.is_string(ret) || ( this.util.is_num(ret) ) );
1256
  } else {
1257
+ // Fallback: Set to default
1258
  ret = def;
1259
  }
1260
  }
1261
+ // Non-scalar default
1262
  else {
1263
  ret = def;
1264
  }
1270
  * Call attribute as method
1271
  * @param string attr Attribute to call
1272
  * @param arguments (optional) Additional arguments to pass to method
1273
+ * @return mixed Attribute return value (if attribute is not a function, attribute's value is returned)
1274
  */
1275
  call_attribute: function(attr, args) {
1276
  attr = this.get_attribute(attr);
1277
  if ( this.util.is_func(attr) ) {
1278
+ // Get arguments
1279
+ args = Array.prototype.slice.call(arguments, 1);
1280
+ // Pass arguments to user-defined method
1281
  attr = attr.apply(this, args);
1282
  }
1283
  return attr;
1289
  * @return bool TRUE if exists, FALSE otherwise
1290
  */
1291
  has_attribute: function(key) {
1292
+ return ( this.util.is_string(key) && ( key in this.get_attributes() ) );
1293
  },
1294
 
1295
  /**
1298
  * @param bool full (optional) Whether to fully replace or merge component's attributes with new values (Default: Merge)
1299
  */
1300
  set_attributes: function(attributes, full) {
1301
+ // Validate
1302
  if ( !this.util.is_bool(full) ) {
1303
  full = false;
1304
  }
1305
 
1306
+ // Initialize attributes
1307
  this.init_attributes(full);
1308
 
1309
+ // Merge new/existing attributes
1310
  if ( this.util.is_obj(attributes) ) {
1311
+ $.extend(this._attributes, attributes);
1312
  }
1313
  },
1314
 
1317
  * @uses get_attributes() to retrieve attributes
1318
  * @param string key Attribute to set
1319
  * @param mixed val Attribute value
1320
+ * @return mixed Attribute value
1321
  */
1322
  set_attribute: function(key, val) {
1323
  if ( this.util.is_string(key) && this.util.is_set(val) ) {
1349
  */
1350
  dom_set: function(el) {
1351
  el = $(el);
1352
+ // Save instance to DOM object
1353
  el.data(this.get_data_key(), this);
1354
+ // Save DOM object to instance
1355
  if ( this._reciprocal ) {
1356
  this._dom = el;
1357
  }
1362
  * Retrieve attached DOM element
1363
  * @uses _dom to retrieve attached DOM element
1364
  * @uses dom_put() to insert child element
1365
+ * @param string element (optional) ID of child element to retrieve (Default: Main element)
1366
  * @param bool put (optional) Whether to insert element if it does not exist (Default: FALSE)
1367
+ * @param obj options (optional) Runtime options
1368
  * @return obj jQuery DOM element
1369
  */
1370
+ dom_get: function(element, options) {
1371
+ // Build options
1372
+ var opts_default = {
1373
+ 'init': true,
1374
+ 'put': false
1375
+ };
1376
+ options = ( this.util.is_obj(options) ) ? $.extend({}, opts_default, options) : opts_default;
1377
+
1378
+ // Init Component DOM
1379
+ if ( options.init && !this.get_status('dom_init') ) {
1380
  this.set_status('dom_init');
1381
  this.dom_init();
1382
  }
1383
+ // Check for main DOM element
1384
  var ret = this._dom;
1385
  if ( !!ret && this.util.is_string(element) ) {
1386
  var ch = $(ret).find( this.dom_get_selector(element) );
1387
+ // Check for child element
1388
  if ( ch.length ) {
1389
  ret = ch;
1390
+ } else if ( true === options.put || this.util.is_obj(options.put) ) {
1391
+ // Insert child element
1392
+ ret = this.dom_put(element, options.put);
1393
  }
1394
  }
1395
  return $(ret);
1406
  * Wrapper element created and added to main DOM element if not yet created
1407
  * @param string element ID for DOM element (Used as class name for wrapper)
1408
  * @param string|jQuery|obj content Content to add to DOM (Object contains element properties)
1409
+ * > tag : Element tag name
1410
+ * > content : Element content
1411
  * @return jQuery Inserted element(s)
1412
  */
1413
  dom_put: function(element, content) {
1414
  var r = null;
1415
+ // Stop processing if main DOM element not set or element is not valid
1416
  if ( !this.dom_has() || !this.util.is_string(element) ) {
1417
  return $(r);
1418
  }
1419
+ // Setup options
1420
+ var strip = ['tag', 'content', 'success'];
1421
  var options = {
1422
  'tag': 'div',
1423
  'content': '',
1424
  'class': this.add_ns(element)
1425
+ };
1426
+ // Setup content
1427
  if ( !this.util.is_empty(content) ) {
1428
  if ( this.util.is_type(content, jQuery, false) || this.util.is_string(content, false) ) {
1429
  options.content = content;
1436
  for ( var x = 0; x < strip.length; x++ ) {
1437
  delete attrs[strip[x]];
1438
  }
1439
+ // Retrieve existing element
1440
  var d = this.dom_get();
1441
  r = $(this.dom_get_selector(element), d);
1442
+ // Create element (if necessary)
1443
  if ( !r.length ) {
1444
  r = $(this.util.format('<%s />', options.tag), attrs).appendTo(d);
1445
+ if ( r.length && this.util.is_method(options, 'success') ) {
1446
+ options['success'].call(r, r);
1447
  }
1448
  }
1449
+ // Set content
1450
  $(r).append(options.content);
1451
  return $(r);
1452
  },
1488
  * @return obj Component instance (allows chaining)
1489
  */
1490
  on: function(event, fn, options) {
1491
+ // Handle request types
1492
  if ( !this.util.is_string(event) || !this.util.is_func(fn) ) {
1493
  var t = this;
1494
  var args = Array.prototype.slice.call(arguments, 1);
1495
  if ( this.util.is_array(event) ) {
1496
+ // Events array
1497
  $.each(event, function(idx, val) {
1498
  t.on.apply(t, [val].concat(args));
1499
  });
1500
  } else if ( this.util.is_obj(event) ) {
1501
+ // Events map
1502
  $.each(event, function(ev, hdl) {
1503
  t.on.apply(t, [ev, hdl].concat(args));
1504
  });
1506
  return this;
1507
  }
1508
 
1509
+ // Options
1510
 
1511
+ // Default options
1512
  var options_std = {
1513
  clear: false
1514
  };
1515
  if ( !this.util.is_obj(options, false) ) {
1516
+ // Reset options
1517
  options = {};
1518
  }
1519
+ // Build options
1520
  options = $.extend({}, options_std, options);
1521
+ // Initialize events bucket
1522
  if ( !this.util.is_obj(this._events, false) ) {
1523
  this._events = {};
1524
  }
1525
+ // Setup event
1526
  var es = this._events;
1527
  if ( !( event in es ) || !this.util.is_obj(es[event], false) || !!options.clear ) {
1528
  es[event] = [];
1529
  }
1530
+ // Add event handler
1531
  es[event].push(fn);
1532
  return this;
1533
  },
1537
  * Event handlers are executed in the context of the current component instance
1538
  * Event handlers are passed parameters
1539
  * > ev (obj) Event object
1540
+ * > type (string) Event name
1541
+ * > data (mixed) Data to pass to handlers (if supplied)
1542
  * > component (obj) Current component instance
1543
  * @param string event Custom event to trigger
1544
  * @param mixed data (optional) Data to pass to event handlers
1548
  var dfr = $.Deferred();
1549
  var dfrs = [];
1550
  var t = this;
1551
+ // Handle array of events
1552
  if ( this.util.is_array(event) ) {
1553
  $.each(event, function(idx, val) {
1554
+ // Collect promises from triggered events
1555
  dfrs.push( t.trigger(val, data) );
1556
  });
1557
+ // Resolve trigger when all events have been resolved
1558
  $.when.apply(t, dfrs).done(function() {
1559
  dfr.resolve();
1560
  });
1561
  return dfr.promise();
1562
  }
1563
+ // Validate
1564
  if ( !this.util.is_string(event) || !( event in this._events ) ) {
1565
  dfr.resolve();
1566
  return dfr.promise();
1567
  }
1568
+ // Create event object
1569
  var ev = { 'type': event, 'data': null };
1570
+ // Add data to event object
1571
  if ( this.util.is_set(data) ) {
1572
  ev.data = data;
1573
  }
1574
+ // Fire handlers for event
1575
  $.each(this._events[event], function(idx, fn) {
1576
+ // Call handler (`this` set to current instance)
1577
+ // Collect promises from event handlers
1578
  dfrs.push( fn.call(t, ev, t) );
1579
  });
1580
+ // Resolve trigger when all handlers have been resolved
1581
  $.when.apply(this, dfrs).done(function() {
1582
  dfr.resolve();
1583
  });
1610
  autofit: true,
1611
  overlay_enabled: true,
1612
  overlay_opacity: '0.8',
1613
+ title_default: false,
1614
  container: null,
1615
  slideshow_enabled: true,
1616
+ slideshow_autostart: false,
1617
  slideshow_duration: 2,
1618
  slideshow_active: false,
1619
  slideshow_timer: null,
1628
  }
1629
  },
1630
 
 
 
 
 
 
 
1631
  _attr_map: {
1632
+ 'theme': null,
1633
  'group_loop': 'loop',
1634
  'ui_autofit': 'autofit',
1635
  'ui_animate': 'animate',
1636
  'ui_overlay_opacity': 'overlay_opacity',
1637
+ 'ui_labels': 'labels',
1638
+ 'ui_title_default': 'title_default',
1639
+ 'slideshow_enabled': null,
1640
+ 'slideshow_autostart': null,
1641
+ 'slideshow_duration': null
1642
  },
1643
 
1644
  /* References */
1674
 
1675
  /* Init */
1676
 
1677
+ _hooks: function() {
1678
  var t = this;
1679
  this
1680
  .on(['item-prev', 'item-next'], function() {
1681
  t.trigger('item-change');
1682
  })
1683
  .on(['close', 'item-change'], function() {
1684
+ t.unload().done(function() {
1685
+ t.unlock();
1686
+ });
1687
  });
1688
  },
1689
 
1690
  /* References */
1691
 
1692
+ /**
1693
+ * Retrieve item instance current attached to viewer
1694
+ * @return Content_Item|NULL Current item instance
1695
+ */
1696
+ get_item: function() {
1697
+ return this.get_component('item');
1698
+ },
1699
+
1700
  /**
1701
  * Set item reference
1702
  * Validates item before setting
1704
  * @return bool TRUE if valid item set, FALSE otherwise
1705
  */
1706
  set_item: function(item) {
1707
+ // Clear existing item
1708
  this.clear_item(false);
1709
  var i = this.set_component('item', item, function(item) {
1710
  return ( item.has_type() );
1712
  return ( !this.util.is_empty(i) );
1713
  },
1714
 
1715
+ /**
1716
+ * Clear item from viewer
1717
+ * Resets item state and removes reference (if necessary)
1718
+ * @param bool full (optional) Fully remove item? (Default: TRUE)
1719
+ */
1720
  clear_item: function(full) {
1721
+ // Validate
1722
  if ( !this.util.is_bool(full) ) {
1723
  full = true;
1724
  }
1727
  item.reset();
1728
  }
1729
  if ( full ) {
1730
+ this.clear_component('item');
1731
  }
1732
  },
1733
 
 
 
 
 
 
 
 
 
1734
  /**
1735
  * Retrieve theme reference
1736
  * @return object Theme reference
1737
  */
1738
  get_theme: function() {
1739
+ // Get saved theme
1740
+ var ret = this.get_component('theme', {check_attr: false});
1741
  if ( this.util.is_empty(ret) ) {
1742
+ // Theme needs to be initialized
1743
  ret = this.set_component('theme', new View.Theme(this));
1744
  }
1745
  return ret;
1771
  * @return jQuery.Promise Resolved when item processing is complete
1772
  */
1773
  get_lock: function(simple, full) {
1774
+ // Validate
1775
  if ( !this.util.is_bool(simple) ) {
1776
  simple = false;
1777
  }
1779
  full = false;
1780
  }
1781
  var s = 'item_working';
1782
+ // Simple status
1783
  if ( simple ) {
1784
  return this.get_status(s);
1785
  }
1786
+ // Full value
1787
  var r = this.get_status(s, true);
1788
  if ( !this.util.is_promise(r) ) {
1789
+ // Create default
1790
  r = this.lock();
1791
  }
1792
  return ( full ) ? r : r.promise();
1836
  mode = true;
1837
  }
1838
  this.loading = mode;
1839
+ // Pause/Resume slideshow
1840
  if ( this.slideshow_active() ) {
1841
  this.slideshow_pause(mode);
1842
  }
1843
+ // Set CSS class on DOM element
1844
  var m = ( mode ) ? 'addClass' : 'removeClass';
1845
  $(this.dom_get())[m]('loading');
1846
  if ( mode ) {
1847
+ // Loading transition
1848
  this.get_theme().transition('load').always(function() {
1849
  dfr.resolve();
1850
  });
1889
  show: function(item) {
1890
  this.item_queued = item;
1891
  var fin_set = 'show_deferred';
1892
+ // Validate theme
1893
  var vt = 'theme_valid';
1894
  var valid = true;
1895
+ if ( this.has_attribute(vt)) {
 
 
1896
  valid = this.get_attribute(vt, true);
1897
+ } else {
1898
+ valid = ( this.get_theme() && this.get_theme().get_template().get_layout(false) !== "" ) ? true : false;
1899
+ this.set_attribute(vt, valid);
1900
  }
1901
 
1902
  if ( !valid ) {
1905
  }
1906
  var v = this;
1907
  var fin = function() {
1908
+ // Lock viewer
1909
  v.lock();
1910
+ // Reset callback flag (for new lock)
1911
  v.set_status(fin_set, false);
1912
+ // Validate request
1913
  if ( !v.set_item(v.item_queued) ) {
1914
  v.close();
1915
  return false;
1916
  }
1917
+ // Add item to history stack
1918
  v.history_add();
1919
+ // Activate
1920
  v.set_active();
1921
+ // Display
1922
  v.render();
1923
+ };
1924
  if ( !this.is_locked() ) {
1925
  fin();
1926
  } else if ( !this.get_status(fin_set) ) {
1927
+ // Set flag to avoid duplicate callbacks
1928
  this.set_status(fin_set);
1929
  this.get_lock().always(function() {
1930
  fin();
1936
 
1937
  history_handle: function(e) {
1938
  var state = e.originalEvent.state;
1939
+ // Load item
1940
+ if ( this.util.is_string(state.item, false) ) {
1941
+ this.get_controller().get_item(state.item).show({'event': e});
1942
  this.trigger('item-change');
1943
  } else {
1944
  var count = this.history_get(true);
1945
+ // Reset count
1946
  this.history_set(0);
1947
+ // Close viewer
1948
+ if ( -1 !== count ) {
1949
  this.close();
1950
  }
1951
  }
1961
  if ( !history.pushState ) {
1962
  return false;
1963
  }
1964
+ // Get display options
1965
  var item = this.get_item();
1966
  var opts = item.get_attribute('options_show');
1967
+ // Save history state
1968
  var count = ( this.history_get() ) ? this.history_get(true) : 0;
1969
  if ( !this.util.in_obj(opts, 'event') ) {
1970
+ // Create state
1971
  var state = {
1972
  'viewer': this.get_id(),
1973
  'item': null,
1974
  'count': count
1975
  };
1976
+ // Init: Save viewer state
1977
  if ( !count ) {
1978
  history.replaceState(state, null);
1979
  }
1980
+ // Always: Save item state
1981
+ state.item = this.get_controller().save_item(item).get_id();
1982
  state.count = ++count;
1983
  history.pushState(state, '');
1984
  } else {
1987
  count = e.state.count;
1988
  }
1989
  }
1990
+ // Save history item count
1991
  this.history_set(count);
1992
  },
1993
  history_reset: function() {
1994
  var count = this.history_get(true);
1995
  if ( count ) {
1996
+ // Clear history status
1997
  this.history_set(-1);
1998
+ // Restore history stack
1999
  history.go( -1 * count );
2000
  }
2001
  },
2006
  * @return bool TRUE if viewer is open, FALSE otherwise
2007
  */
2008
  is_open: function() {
2009
+ return ( this.dom_get().css('display') === 'none' ) ? false : true;
2010
  },
2011
 
2012
  /**
2013
  * Load output into DOM
2014
  */
2015
  render: function() {
2016
+ // Get theme output
2017
  var v = this;
2018
  var thm = this.get_theme();
2019
+ v.dom_prep();
2020
+ // Register theme event handlers
2021
  if ( !this.get_status('render-events') ) {
2022
  this.set_status('render-events');
2023
  thm
2024
+ // Loading
2025
  .on('render-loading', function(ev, thm) {
2026
  var dfr = $.Deferred();
2027
  if ( !v.is_active() ) {
2029
  return dfr.promise();
2030
  }
2031
  var set_pos = function() {
2032
+ // Set position
2033
  v.dom_get().css('top', $(window).scrollTop());
2034
  };
2035
  var always = function() {
2036
+ // Set loading flag
2037
  v.set_loading().always(function() {
2038
  dfr.resolve();
2039
  });
2053
  v.open = true;
2054
  })
2055
  .fail(function() {
2056
+ set_pos();
2057
+ // Fallback open
2058
  v.get_overlay().show();
2059
  v.dom_get().show();
2060
  });
2061
  }
2062
  return dfr.promise();
2063
  })
2064
+ // Complete
2065
  .on('render-complete', function(ev, thm) {
2066
+ // Stop if viewer not active
2067
  if ( !v.is_active() ) {
2068
  return false;
2069
  }
2070
+ // Set classes
2071
  var d = v.dom_get();
2072
  var classes = ['item_single', 'item_multi'];
2073
  var ms = ['addClass', 'removeClass'];
2077
  $.each(ms, function(idx, val) {
2078
  d[val](classes[idx]);
2079
  });
2080
+ // Bind events
2081
  v.events_complete();
2082
+ // Transition
2083
  thm.transition('complete')
2084
  .fail(function() {
2085
+ // Autofit content
2086
  if ( v.get_attribute('autofit', true) ) {
2087
  var dims = $.extend({'display': 'inline-block'}, thm.get_item_dimensions());
2088
+ thm.dom_get_tag('item', 'content').css(dims);
2089
  }
2090
  })
2091
  .always(function() {
2092
+ // Unset loading flag
2093
  v.unset_loading();
2094
+ // Trigger event
2095
  v.trigger('render-complete');
2096
+ // Set viewer as initialized
2097
  v.init = true;
2098
  });
2099
  });
2100
  }
2101
+ // Render
2102
  thm.render();
2103
  },
2104
 
2109
  */
2110
  dom_get_container: function() {
2111
  var sel = this.get_attribute('container');
2112
+ // Set default container
2113
  if ( this.util.is_empty(sel) ) {
2114
  sel = '#' + this.add_ns('wrap');
2115
  }
2116
+ // Add default container to DOM if not yet present
2117
  var c = $(sel);
2118
  if ( !c.length ) {
2119
+ // Prepare ID
2120
  var id = ( sel.indexOf('#') === 0 ) ? sel.substr(1) : sel;
2121
+ // Add element
2122
  c = $('<div />', {'id': id}).appendTo('body');
2123
  }
2124
  return c;
2128
  * Custom Viewer DOM initialization
2129
  */
2130
  dom_init: function() {
2131
+ // Create element & add to DOM
2132
+ // Save element to instance
2133
  var d = this.dom_set($('<div/>', {
2134
  'id': this.get_id(true),
2135
  'class': this.get_ns()
2136
  })).appendTo(this.dom_get_container()).hide();
2137
+ // Add theme classes
2138
  var thm = this.get_theme();
2139
  d.addClass(thm.get_classes(' '));
2140
+ // Add theme layout (basic)
2141
  var v = this;
2142
  if ( !this.get_status('render-init') ) {
2143
  this.set_status('render-init');
2144
  thm.on('render-init', function(ev) {
2145
+ // Add rendered theme layout to viewer DOM
2146
  v.dom_put('layout', ev.data);
2147
  });
2148
  }
2149
  thm.render(true);
2150
  },
2151
 
2152
+ /**
2153
+ * Prepare DOM for viewer
2154
+ */
2155
+ dom_prep: function(mode) {
2156
+ var m = ( this.util.is_bool(mode) && !mode ) ? 'removeClass' : 'addClass';
2157
+ $('html')[m](this.util.add_prefix('overlay'));
2158
+ },
2159
+
2160
  /**
2161
  * Restore DOM
2162
+ * Required after viewer is closed
 
2163
  */
2164
+ dom_restore: function() {
2165
+ this.dom_prep(false);
2166
+ },
2167
 
2168
  /* Layout */
2169
 
2170
  get_layout: function() {
2171
+ var ret = this.dom_get('layout', {
2172
+ 'put': {
2173
+ 'success': function() {
2174
+ $(this).hide();
2175
+ }
2176
  }
2177
  });
2178
  return ret;
2203
  var o = null;
2204
  var v = this;
2205
  if ( this.overlay_enabled() ) {
2206
+ o = this.dom_get('overlay', {
2207
+ 'put': {
2208
+ 'success': function() {
2209
+ $(this).hide().css('opacity', v.get_attribute('overlay_opacity'));
2210
+ }
2211
  }
2212
  });
2213
  }
2214
  return $(o);
2215
  },
2216
 
2217
+ /**
2218
+ * Unload viewer
2219
+ */
2220
  unload: function() {
2221
+ var dfr = $.Deferred();
2222
+ // Unload item data
2223
+ this.get_theme().dom_get_tag('item').text('');
2224
+ dfr.resolve();
2225
+ return dfr.promise();
2226
  },
2227
 
2228
  /**
2229
  * Reset viewer
2230
  */
2231
  reset: function() {
2232
+ // Hide viewer
2233
  this.dom_get().hide();
2234
+ // Restore DOM
2235
  this.dom_restore();
2236
+ // History
2237
  this.history_reset();
2238
+ // Item
2239
  this.clear_item();
2240
+ // Reset properties
2241
  this.set_active(false);
2242
  this.set_loading(false);
2243
  this.slideshow_stop();
2244
  this.keys_disable();
2245
+ // Clear for next item
2246
+ this.unlock();
2247
  },
2248
 
2249
  /* Content */
2263
  * Initialize event handlers upon opening lightbox
2264
  */
2265
  events_open: function() {
2266
+ // Keyboard bindings
2267
  this.keys_enable();
2268
  if ( this.open ) {
2269
  return false;
2270
  }
2271
 
2272
+ // Control event bubbling
2273
  var l = this.get_layout();
2274
  l.children().click(function(ev) {
2275
  ev.stopPropagation();
2279
  var v = this;
2280
  var close = function() {
2281
  v.close();
2282
+ };
2283
+ // Layout
2284
  l.click(close);
2285
+ // Overlay
2286
  this.get_overlay().click(close);
2287
+ // Fire event
2288
  this.trigger('events-open');
2289
  },
2290
 
2295
  if ( this.init ) {
2296
  return false;
2297
  }
2298
+ // Fire event
2299
  this.trigger('events-complete');
2300
  },
2301
 
2307
  var v = this;
2308
  var h = function(ev) {
2309
  return v.keys_control(ev);
2310
+ };
2311
  if ( mode ) {
2312
  $(document).on(e, h);
2313
  } else {
2321
 
2322
  keys_control: function(ev) {
2323
  var handlers = {
2324
+ 27: this.close, /* esc */
2325
+ 37: this.item_prev, /* left-arrow */
2326
+ 39: this.item_next, /* right-arrow */
2327
  };
2328
+ // Swap next/prev keys on RTL pages
2329
+ if ('rtl' === document.documentElement.getAttribute('dir')) {
2330
+ handlers[37] = this.item_next; /* left-arrow */
2331
+ handlers[39] = this.item_prev; /* right-arrow */
2332
+ }
2333
  if ( ev.which in handlers ) {
2334
  handlers[ev.which].call(this);
2335
  return false;
2377
  }
2378
  this.set_attribute('slideshow_active', true);
2379
  this.dom_get().addClass('slideshow_active');
2380
+ // Clear residual timers
2381
  this.slideshow_clear_timer();
2382
+ // Start timer
2383
  var v = this;
2384
  this.slideshow_set_timer(function() {
2385
+ // Pause slideshow until next item fully loaded
2386
  v.slideshow_pause();
2387
 
2388
+ // Show next item
2389
  v.item_next();
2390
  });
2391
  this.trigger('slideshow-start');
2403
  this.set_attribute('slideshow_active', false);
2404
  this.dom_get().removeClass('slideshow_active');
2405
  }
2406
+ // Kill timers
2407
  this.slideshow_clear_timer();
2408
  this.trigger('slideshow-stop');
2409
  },
2425
  * @param bool mode (optional) Pause (TRUE) or Resume (FALSE) slideshow (default: TRUE)
2426
  */
2427
  slideshow_pause: function(mode) {
2428
+ // Validate
2429
  if ( !this.util.is_bool(mode) ) {
2430
  mode = true;
2431
  }
2432
+ // Set viewer slideshow properties
2433
  if ( this.slideshow_active() ) {
2434
  if ( !mode ) {
2435
+ // Slideshow resumed
2436
  this.slideshow_start();
2437
  } else {
2438
+ // Slideshow paused
2439
  this.slideshow_stop(false);
2440
  }
2441
  }
2457
  var v = this;
2458
  var ev = 'item-next';
2459
  var st = ['events', 'viewer', ev].join('_');
2460
+ // Setup event handler
2461
  if ( !g.get_status(st) ) {
2462
  g.set_status(st);
2463
  g.on(ev, function(e) {
2488
  * Close viewer
2489
  */
2490
  close: function() {
2491
+ // Deactivate
2492
  this.set_active(false);
2493
  var v = this;
2494
  var thm = this.get_theme();
2495
  thm.transition('unload')
2496
  .always(function() {
2497
  thm.transition('close', true).always(function() {
2498
+ // End processes
2499
  v.reset();
2500
  v.trigger('close');
2501
  });
2538
 
2539
  /* Init */
2540
 
2541
+ _hooks: function() {
2542
  var t = this;
2543
  this.on(['item-prev', 'item-next'], function() {
2544
  t.trigger('item-change');
2553
  */
2554
  get_selector: function() {
2555
  if ( this.util.is_empty(this.selector) ) {
2556
+ // Build selector
2557
  this.selector = this.util.format('a[%s="%s"]', this.dom_get_attribute(), this.get_id());
2558
  }
2559
  return this.selector;
2563
  * Retrieve group items
2564
  */
2565
  get_items: function() {
2566
+ var items = $(this.get_selector());
2567
+ if ( 0 === items.length && this.has_current() ) {
2568
+ items = this.get_current().dom_get();
2569
+ }
2570
  return items;
2571
  },
2572
 
2577
  * @return Content_Item Item
2578
  */
2579
  get_item: function(idx) {
2580
+ // Validation
2581
  if ( !this.util.is_int(idx) ) {
2582
  idx = 0;
2583
  }
2584
+ // Retrieve all items
2585
  var items = this.get_items();
2586
+ // Validate index
2587
  var max = this.get_size() - 1;
2588
  if ( idx > max ) {
2589
  idx = max;
2590
  }
2591
+ // Return specified item
2592
  return items.get(idx);
2593
  },
2594
 
2599
  */
2600
  get_pos: function(item) {
2601
  if ( this.util.is_empty(item) ) {
2602
+ // Get current item
2603
  item = this.get_current();
2604
  }
2605
  return ( this.util.is_type(item, View.Content_Item) ) ? this.get_items().index(item.dom_get()) : -1;
2606
  },
2607
+
2608
+ /**
2609
+ * Check if current item set in group
2610
+ * @return bool TRUE if current item is set
2611
+ */
2612
+ has_current: function() {
2613
+ // Sanitize
2614
+ return ( !this.util.is_empty( this.get_current() ) );
2615
+ },
2616
 
2617
  /**
2618
  * Retrieve current item
2619
+ * @uses Group.current
2620
+ * @return NULL|Content_Item Current item (NULL if current item not set or invalid)
2621
  */
2622
  get_current: function() {
2623
+ // Sanitize
2624
+ if ( null !== this.current && !this.util.is_type(this.current, View.Content_Item) ) {
2625
  this.current = null;
2626
  }
2627
  return this.current;
2632
  * @param Content_Item item Item to set as current
2633
  */
2634
  set_current: function(item) {
2635
+ // Validate
2636
  if ( this.util.is_type(item, View.Content_Item) ) {
2637
+ // Set current item
2638
  this.current = item;
2639
  }
2640
  },
2641
 
2642
  get_next: function(item) {
2643
+ // Validate
2644
  if ( !this.util.is_type(item, View.Content_Item) ) {
2645
  item = this.get_current();
2646
  }
2647
+ if ( this.get_size() === 1 ) {
2648
  return item;
2649
  }
2650
  var next = null;
2651
  var pos = this.get_pos(item);
2652
+ if ( pos !== -1 ) {
2653
  pos = ( pos + 1 < this.get_size() ) ? pos + 1 : 0;
2654
+ if ( 0 !== pos || item.get_viewer().get_attribute('loop') ) {
2655
  next = this.get_item(pos);
2656
  }
2657
  }
2659
  },
2660
 
2661
  get_prev: function(item) {
2662
+ // Validate
2663
  if ( !this.util.is_type(item, View.Content_Item) ) {
2664
  item = this.get_current();
2665
  }
2666
+ if ( this.get_size() === 1 ) {
2667
  return item;
2668
  }
2669
  var prev = null;
2670
  var pos = this.get_pos(item);
2671
+ if ( pos !== -1 && ( 0 !== pos || item.get_viewer().get_attribute('loop') ) ) {
2672
+ if ( pos === 0 ) {
2673
  pos = this.get_size();
2674
  }
2675
  pos -= 1;
2680
 
2681
  show_next: function(item) {
2682
  if ( this.get_size() > 1 ) {
2683
+ // Retrieve item
2684
  var next = this.get_next(item);
2685
  if ( !next ) {
2686
  if ( !this.util.is_type(item, View.Content_Item) ) {
2688
  }
2689
  item.get_viewer().close();
2690
  }
2691
+ var i = this.get_controller().get_item(next);
2692
+ // Update current item
2693
  this.set_current(i);
2694
+ // Show item
2695
  i.show();
2696
+ // Fire event
2697
  this.trigger('item-next');
2698
  }
2699
  },
2700
 
2701
  show_prev: function(item) {
2702
  if ( this.get_size() > 1 ) {
2703
+ // Retrieve item
2704
  var prev = this.get_prev(item);
2705
  if ( !prev ) {
2706
  if ( !this.util.is_type(item, View.Content_Item) ) {
2708
  }
2709
  item.get_viewer().close();
2710
  }
2711
+ var i = this.get_controller().get_item(prev);
2712
+ // Update current item
2713
  this.set_current(i);
2714
+ // Show item
2715
  i.show();
2716
+ // Fire event
2717
  this.trigger('item-prev');
2718
  }
2719
  },
2727
  },
2728
 
2729
  is_single: function() {
2730
+ return ( this.get_size() === 1 );
2731
  }
2732
  };
2733
 
2778
  * @return mixed Content_Item if valid item set, NULL otherwise
2779
  */
2780
  get_item: function() {
2781
+ return this.get_component('item');
2782
  },
2783
 
2784
  /**
2789
  * @return obj|null Item instance if item successfully set, NULL otherwise
2790
  */
2791
  set_item: function(item) {
2792
+ // Set reference
2793
  var r = this.set_component('item', item);
2794
  return r;
2795
  },
2799
  * Sets value to NULL
2800
  */
2801
  clear_item: function() {
2802
+ this.clear_component('item');
2803
  },
2804
 
2805
  /* Evaluation */
2810
  * @return bool TRUE if type matches, FALSE otherwise
2811
  */
2812
  match: function(item) {
2813
+ // Validate
2814
  var attr = 'match';
2815
  var m = this.get_attribute(attr);
2816
+ // Stop processing types with no matching algorithm
2817
  if ( !this.util.is_empty(m) ) {
2818
+ // Process regex patterns
2819
 
2820
+ // String-based
2821
  if ( this.util.is_string(m) ) {
2822
+ // Create new regexp object
2823
  m = new RegExp(m, "i");
2824
  this.set_attribute(attr, m);
2825
  }
2826
+ // RegExp based
2827
  if ( this.util.is_type(m, RegExp) ) {
2828
  return m.test(item.get_uri());
2829
  }
2830
+ // Process function
2831
  if ( this.util.is_func(m) ) {
2832
  return ( m.call(this, item) ) ? true : false;
2833
  }
2834
  }
2835
+ // Default
2836
  return false;
2837
  },
2838
 
2839
  /* Processing/Output */
2840
 
2841
+ /**
2842
+ * Loads item data
2843
+ * @param obj item Content item to load data for
2844
+ * @return obj Promise that is resolved when item data is loaded
2845
+ */
2846
+ load: function(item) {
2847
+ var dfr = $.Deferred();
2848
+ var ret = this.call_attribute('load', item, dfr);
2849
+ // Handle missing load method
2850
+ if ( null === ret ) {
2851
+ dfr.resolve();
2852
+ }
2853
+ return dfr.promise();
2854
+ },
2855
+
2856
  /**
2857
  * Render output to display item
2858
  * @param Content_Item item Item to render output for
2860
  */
2861
  render: function(item) {
2862
  var dfr = $.Deferred();
2863
+ // Validate
2864
+ this.call_attribute('render', item, dfr);
 
 
 
 
 
 
 
 
 
 
 
 
2865
  return dfr.promise();
2866
  }
2867
  };
2882
  'group': 'Group',
2883
  'type': 'Content_Handler'
2884
  },
 
2885
 
2886
  _attr_default: {
2887
  source: null,
2902
  /* Properties */
2903
 
2904
  data: null,
2905
+ loaded: null,
2906
 
2907
  /* Init */
2908
 
2909
  _c: function(el) {
2910
+ // Save element to instance
2911
  this.dom_set(el);
2912
+ // Default initialization
2913
  this._super();
2914
  },
2915
 
2925
  */
2926
  init_default_attributes: function() {
2927
  this._super();
2928
+ // Add asset properties
2929
  var d = this.dom_get();
2930
+ var key = d.attr(this.util.get_attribute('asset')) || null;
2931
+ var assets = this.get_controller().assets || null;
2932
+ // Merge asset data with default attributes
2933
  if ( this.util.is_string(key) ) {
2934
+ var attrs = [{}, this._attr_default, {'permalink': d.attr('href')}];
2935
  if ( this.util.is_obj(assets) ) {
2936
  var t = this;
2937
+ /**
2938
+ * Retrieve item assets
2939
+ * Handles variant items as well (Retrieves parent item assets)
2940
+ * @param string key Item URI
2941
+ * @return obj Item assets (Empty if no match)
2942
+ */
2943
+ var get_assets = function(key) {
2944
  var ret = {};
2945
  if ( key in assets && t.util.is_obj(assets[key]) ) {
2946
+ ret = assets[key];
 
 
 
 
 
 
2947
  }
2948
  return ret;
2949
  };
2950
+ // Save assets
2951
+ attrs.push(get_assets(key));
 
 
 
 
 
 
2952
  }
2953
  this._attr_default = $.extend.apply(this, attrs);
2954
  }
2968
  */
2969
  get_output: function() {
2970
  var dfr = $.Deferred();
2971
+ // Check for cached output
2972
  var ret = this.get_attribute('output');
2973
  if ( this.util.is_string(ret) ) {
2974
  dfr.resolve(ret);
2975
  } else if ( this.has_type() ) {
2976
+ // Render output from scratch (if necessary)
2977
+ // Get item type
2978
  var type = this.get_type();
2979
+ // Render type-based output
2980
  var item = this;
2981
  type.render(this).done(function(output) {
2982
+ // Cache output
2983
  item.set_output(output);
2984
  dfr.resolve(output);
2985
  });
3016
  * @return string Item URI
3017
  */
3018
  get_uri: function(mode) {
3019
+ // Validate
3020
+ if ( $.inArray(mode ,['source', 'permalink']) === -1 ) {
3021
  mode = 'source';
3022
  }
3023
+ // Retrieve URI
3024
  var ret = this.get_attribute(mode);
3025
  if ( !this.util.is_string(ret) ) {
3026
+ ret = ( 'source' === mode ) ? this.get_attribute('permalink') : '';
3027
  }
3028
+ // Format
3029
+ ret = ret.replace(/&(#38|amp);/, '&');
3030
  return ret;
3031
  },
3032
 
3036
  get_title: function() {
3037
  var prop = 'title';
3038
  var prop_cached = prop + '_cached';
3039
+ // Check for cached value
3040
  if ( this.has_attribute(prop_cached) ) {
3041
  return this.get_attribute(prop_cached, '');
3042
  }
3043
 
3044
  var title = '';
3045
+ // Generate title from DOM values
 
3046
  var dom = this.dom_get();
3047
 
3048
+ // DOM-based caption
3049
+ if ( dom.length ) {
3050
+ // Link title (generally must be manually-entered)
3051
  title = dom.attr(prop);
3052
 
3053
+ // Figcaption element
3054
  if ( !title ) {
3055
+ title = dom.closest('figure').find('figcaption').first().html();
3056
+ }
3057
+
3058
+ // Class Name
3059
+ if ( !title ) {
3060
+ title = dom.closest('figure').find('.wp-caption-text').first().html();
3061
  }
3062
  }
3063
 
3064
+ // Saved attributes
3065
  if ( !title ) {
3066
  var props = ['caption', 'title'];
3067
  for ( var x = 0; x < props.length; x++ ) {
3072
  }
3073
  }
3074
 
3075
+ // Fallbacks
3076
  if ( !title && dom.length ) {
3077
+ // Alt attribute
3078
  title = dom.find('img').first().attr('alt');
3079
 
3080
+ // Element text
3081
  if ( !title ) {
3082
+ title = dom.get(0).innerText.trim();
3083
  }
3084
  }
3085
 
3086
+ // Validate
3087
  if ( !this.util.is_string(title, false) ) {
3088
  title = '';
3089
  }
3090
+ // Strip default title
3091
+ if ( !this.util.is_empty(title) && !this.get_viewer().get_attribute('title_default') ) {
3092
+ var f = this.get_uri('source');
3093
+ var i = f.lastIndexOf('/');
3094
+ if ( -1 !== i ) {
3095
+ f = f.substr(i + 1);
3096
+ i = f.lastIndexOf('.');
3097
+ if ( -1 !== i ) {
3098
+ f = f.substr(0, i);
3099
+ }
3100
+ if ( title === f ) {
3101
+ title = '';
3102
+ }
3103
+ }
3104
+ }
3105
+
3106
 
3107
+ // Cache retrieved value
3108
  this.set_attribute(prop_cached, title);
3109
+ // Return value
3110
  return title;
3111
  },
3112
 
3127
  this.data = data;
3128
  },
3129
 
3130
+ get_data: function() {
3131
+ return this.data;
3132
+ },
3133
+
3134
  /**
3135
  * Determine gallery type
3136
  * @return string|null Gallery type ID (NULL if item not in gallery)
3159
  */
3160
  in_gallery: function(gType) {
3161
  var type = this.gallery_type();
3162
+ // No gallery
3163
+ if ( null === type ) {
3164
  return false;
3165
  }
3166
+ // Boolean check
3167
  if ( !this.util.is_string(gType) ) {
3168
  return true;
3169
  }
3170
+ // Check for specific gallery type
3171
+ return ( gType === type ) ? true : false;
3172
  },
3173
 
3174
  /*-** Component References **-*/
3176
  /* Viewer */
3177
 
3178
  get_viewer: function() {
3179
+ return this.get_component('viewer', {get_default: true});
3180
  },
3181
 
3182
  /**
3199
  */
3200
  get_group: function(set_current) {
3201
  var prop = 'group';
3202
+ // Check if group reference already set
3203
+ var g = this.get_component(prop);
3204
  if ( g ) {
3205
  } else {
3206
+ // Set empty group if no group exists
3207
  g = this.set_component(prop, new View.Group());
3208
  set_current = true;
3209
  }
3221
  * > Item's group is reset if invalid group provided
3222
  */
3223
  set_group: function(g) {
3224
+ // If group ID set, get object reference
3225
  if ( this.util.is_string(g) ) {
3226
+ g = this.get_controller().get_group(g);
3227
  }
3228
 
3229
+ // Set (or clear) group property
3230
  this.group = ( this.util.is_type(g, View.Group) ) ? g : false;
3231
  },
3232
 
3239
  * @return Content_Handler|null Content Handler of item (NULL no valid type exists)
3240
  */
3241
  get_type: function() {
3242
+ var t = this.get_component('type', {check_attr: false});
3243
  if ( !t ) {
3244
+ t = this.set_type(this.get_controller().get_content_handler(this));
3245
  }
3246
  return t;
3247
  },
3273
  * @param obj options (optional) Options
3274
  */
3275
  show: function(options) {
3276
+ // Validate content handler
3277
  if ( !this.has_type() ) {
3278
  return false;
3279
  }
3280
+ // Set display options
3281
  this.set_attribute('options_show', options);
3282
+ // Retrieve viewer
3283
  var v = this.get_viewer();
3284
+ // Load item
3285
+ this.load();
3286
  var ret = v.show(this);
3287
  return ret;
3288
  },
3289
 
3290
+ /**
3291
+ * Load item data
3292
+ *
3293
+ * Retrieves item data from external sources (if necessary)
3294
+ * @uses this.loaded to save loaded state
3295
+ * @return obj Promise that is resolved when item data is loaded
3296
+ */
3297
+ load: function() {
3298
+ if ( !this.util.is_promise(this.loaded) ) {
3299
+ // Load item data (via content handler)
3300
+ this.loaded = this.get_type().load(this);
3301
+ }
3302
+ return this.loaded.promise();
3303
+ },
3304
+
3305
  reset: function() {
3306
  this.set_attribute('options_show', null);
3307
  }
3331
  * @return mixed Attribute value
3332
  */
3333
  get_attribute: function(key, def, check_model, enforce_type) {
3334
+ // Validate
3335
  if ( !this.util.is_string(key) ) {
3336
+ // Invalid requests sent straight to super method
3337
  return this._super(key, def, enforce_type);
3338
  }
3339
  if ( !this.util.is_bool(check_model) ) {
3340
  check_model = true;
3341
  }
3342
  var ret = null;
3343
+ // Check model for attribute
3344
  if ( check_model ) {
3345
  var m = this.get_ancestor(key, false);
3346
  if ( this.util.in_obj(m, key) ) {
3347
  ret = m[key];
3348
  }
3349
  }
3350
+ // Check standard attributes as fallback
3351
+ if ( null === ret ) {
3352
  ret = this._super(key, def, enforce_type);
3353
  }
3354
  return ret;
3355
  },
3356
+
3357
+ /**
3358
+ * Get attribute recursively
3359
+ * Merges objects from ancestors together
3360
+ * @see Component.get_attribute() for more information
3361
+ */
3362
+ get_attribute_recursive: function(key, def, enforce_type) {
3363
+ var ret = this.get_attribute(key, def, true, enforce_type);
3364
+ if ( this.util.is_obj(ret) ) {
3365
+ // Merge ancestor objects
3366
+ var models = this.get_ancestors(false);
3367
+ ret = [ret];
3368
+ var t = this;
3369
+ $.each(models, function(idx, model) {
3370
+ if ( key in model && t.util.is_obj(model[key]) ) {
3371
+ ret.push(model[key]);
3372
+ }
3373
+ });
3374
+ // Merge transition handlers into current theme
3375
+ ret.push({});
3376
+ ret = $.extend.apply($, ret.reverse());
3377
+ }
3378
+ return ret;
3379
+ },
3380
 
3381
  /**
3382
  * Set attribute value
3390
  * @return mixed Attribute value
3391
  */
3392
  set_attribute: function(key, val, use_model) {
3393
+ // Validate
3394
  if ( ( !this.util.is_string(key) ) || !this.util.is_set(val) ) {
3395
  return false;
3396
  }
3397
  if ( !this.util.is_bool(use_model) && !this.util.is_obj(use_model) ) {
3398
  use_model = true;
3399
  }
3400
+ // Determine where to set attribute
3401
  if ( !!use_model ) {
3402
  var model = this.util.is_obj(use_model) ? use_model : this.get_model();
3403
 
3404
+ // Set attribute in model
3405
  model[key] = val;
3406
  } else {
3407
+ // Set as standard attribute
3408
  this._super(key, val);
3409
  }
3410
  return val;
3420
  get_model: function() {
3421
  var m = this.get_attribute('model', null, false);
3422
  if ( !this.util.is_obj(m) ) {
3423
+ // Set default value
3424
  m = {};
3425
  this.set_attribute('model', m, false);
3426
  }
3459
  ret.push(m);
3460
  m = ( this.util.in_obj(m, 'parent') && this.util.is_obj(m.parent) ) ? m.parent : null;
3461
  }
3462
+ // Remove current model from list
3463
  if ( !inc_current ) {
3464
  ret.shift();
3465
  }
3474
  * @return obj Theme ancestor (Default: Current theme model)
3475
  */
3476
  get_ancestor: function(attr, safe_mode) {
3477
+ // Validate
3478
  if ( !this.util.is_string(attr) ) {
3479
  return false;
3480
  }
3481
  if ( !this.util.is_bool(safe_mode) ) {
3482
  safe_mode = true;
3483
  }
3484
+ var mcurr = this.get_model();
3485
+ var m = mcurr;
3486
  var found = false;
3487
  while ( this.util.is_obj(m) ) {
3488
+ // Check if attribute exists in model
3489
  if ( this.util.in_obj(m, attr) && !this.util.is_empty(m[attr]) ) {
3490
  found = true;
3491
  break;
3492
  }
3493
+ // Get next model
3494
  m = ( this.util.in_obj(m, 'parent') ) ? m['parent'] : null;
3495
  }
3496
  if ( !found ) {
3497
  if ( safe_mode ) {
3498
+ // Use current model as fallback
3499
  if ( this.util.is_empty(m) ) {
3500
  m = mcurr;
3501
  }
3502
+ // Add attribute to object
3503
  if ( !this.util.in_obj(m, attr) ) {
3504
  m[attr] = null;
3505
  }
3528
  },
3529
  _models: {},
3530
 
 
 
3531
  _attr_default: {
3532
  template: null,
3533
  model: null
3545
  * @see Component._c()
3546
  */
3547
  _c: function(id, attributes, viewer) {
3548
+ // Validate
3549
+ if ( arguments.length === 1 && this.util.is_type(arguments[0], View.Viewer) ) {
3550
  viewer = arguments[0];
3551
  id = null;
3552
  }
3553
+ // Pass parameters to parent constructor
3554
  this._super(id, attributes);
3555
 
3556
+ // Set viewer instance
3557
  this.set_viewer(viewer);
3558
 
3559
+ // Set theme model
3560
  this.set_model(id);
3561
  },
3562
 
3563
  /* Viewer */
3564
 
3565
  get_viewer: function() {
3566
+ return this.get_component('viewer', {check_attr: false, get_default: true});
3567
  },
3568
 
3569
  /**
3584
  * @return Template instance
3585
  */
3586
  get_template: function() {
3587
+ // Get saved template
3588
+ var ret = this.get_component('template');
3589
+ // Template needs to be initialized
3590
  if ( this.util.is_empty(ret) ) {
3591
+ // Pass model to Template instance
3592
  var attr = { 'theme': this, 'model': this.get_model() };
3593
  ret = this.set_component('template', new View.Template(attr));
3594
  }
3595
  return ret;
3596
  },
3597
 
3598
+ /* Tags */
3599
+
3600
  /**
3601
  * Retrieve tags from template
3602
  * All tags will be retrieved by default
3618
  return $(this.get_template().dom_get_tag(tag, prop));
3619
  },
3620
 
3621
+ /**
3622
+ * Retrieve template tag CSS selector
3623
+ * @uses Template.get_tag_selector()
3624
+ * @param name string Tag name
3625
+ * @param prop string Tag Property
3626
+ * @return string Template tag CSS selector
3627
+ */
3628
+ get_tag_selector: function(name, prop) {
3629
+ return this.get_template().get_tag_selector(name, prop);
3630
+ },
3631
+
3632
  /* Model */
3633
 
3634
  /**
3647
  */
3648
  get_model: function(id) {
3649
  var ret = null;
3650
+ // Pass request to superclass method
3651
  if ( !this.util.is_set(id) && this.util.is_obj( this.get_attribute('model', null, false) ) ) {
3652
  ret = this._super();
3653
  } else {
3654
+ // Retrieve matching theme model
3655
  var models = this.get_models();
3656
  if ( !this.util.is_string(id) ) {
3657
+ id = this.get_controller().get_option('theme_default');
3658
  }
3659
+ // Select first theme model if specified model is invalid
3660
  if ( !this.util.in_obj(models, id) ) {
3661
  id = $.map(models, function(v, key) { return key; })[0];
3662
  }
3671
  */
3672
  set_model: function(id) {
3673
  this.set_attribute('model', this.get_model(id), false);
3674
+ /* @deprecated
3675
+ // Set ID using model attributes (if necessary)
3676
+ if ( !this._check_id(true) ) {
3677
  var m = this.get_model();
3678
  if ( 'id' in m ) {
3679
+ this._set_id(m.id);
3680
  }
3681
  }
3682
+ */
3683
  },
3684
 
3685
  /* Properties */
3694
  * @return array Class names
3695
  */
3696
  get_classes: function(rtype) {
3697
+ // Build array of class names
3698
  var cls = [];
3699
  var thm = this;
3700
+ // Include theme parent's class name
3701
  var models = this.get_ancestors(true);
3702
  $.each(models, function(idx, model) {
3703
  cls.push(thm.add_ns(model.id));
3704
  });
3705
+ // Convert class names array to string
3706
  if ( this.util.is_string(rtype) ) {
3707
  cls = cls.join(rtype);
3708
  }
3709
+ // Return class names
3710
  return cls;
3711
  },
3712
 
3718
  */
3719
  get_measurement: function(attr, def) {
3720
  var meas = null;
3721
+ // Validate
3722
  if ( !this.util.is_string(attr) ) {
3723
  return meas;
3724
  }
3725
  if ( !this.util.is_obj(def, false) ) {
3726
  def = {};
3727
  }
3728
+ // Manage cache
3729
  var attr_cache = this.util.format('%s_cache', attr);
3730
  var cache = this.get_attribute(attr_cache, {}, false);
3731
  var status = '_status';
3732
  var item = this.get_viewer().get_item();
3733
  var w = $(window);
3734
+ // Check cache freshness
3735
+ if ( !( status in cache ) || !this.util.is_obj(cache[status]) || cache[status].width !== w.width() || cache[status].height !== w.height() ) {
3736
  cache = {};
3737
  }
3738
  if ( this.util.is_empty(cache) ) {
3739
+ // Set status
3740
  cache[status] = {
3741
  'width': w.width(),
3742
  'height': w.height(),
3743
  'index': []
3744
  };
3745
  }
3746
+ // Retrieve cached values
3747
  var pos = $.inArray(item, cache[status].index);
3748
+ if ( pos !== -1 && pos in cache ) {
3749
  meas = cache[pos];
3750
  }
3751
+ // Generate measurement
3752
  if ( !this.util.is_obj(meas) ) {
3753
+ // Get custom theme measurement
3754
  meas = this.call_attribute(attr);
3755
  if ( !this.util.is_obj(meas) ) {
3756
+ // Retrieve fallback value
3757
  meas = this.get_measurement_default(attr);
3758
  }
3759
  }
3760
+ // Normalize measurement
3761
  meas = ( this.util.is_obj(meas) ) ? $.extend({}, def, meas) : def;
3762
+ // Cache measurement
3763
  pos = cache[status].index.push(item) - 1;
3764
  cache[pos] = meas;
3765
  this.set_attribute(attr_cache, cache, false);
3766
+ // Return measurement (copy)
3767
  return $.extend({}, meas);
3768
  },
3769
 
3773
  * @return obj Measurement values
3774
  */
3775
  get_measurement_default: function(attr) {
3776
+ // Validate
3777
  if ( !this.util.is_string(attr) ) {
3778
  return null;
3779
  }
3780
+ // Find default handler
3781
  attr = this.util.format('get_%s_default', attr);
3782
  if ( this.util.in_obj(this, attr) ) {
3783
  attr = this[attr];
3784
  if ( this.util.is_func(attr) ) {
3785
+ // Execute default handler
3786
  attr = attr.call(this);
3787
  }
3788
  } else {
3807
  var offset = { 'width': 0, 'height': 0 };
3808
  var v = this.get_viewer();
3809
  var vn = v.dom_get();
3810
+ // Clone viewer
3811
  var vc = vn
3812
  .clone()
3813
  .attr('id', '')
3814
  .css({'visibility': 'hidden', 'position': 'absolute', 'top': ''})
3815
  .removeClass('loading')
3816
  .appendTo(vn.parent());
3817
+ // Get offset from layout node
3818
  var l = vc.find(v.dom_get_selector('layout'));
3819
  if ( l.length ) {
3820
+ // Clear inline styles
3821
  l.find('*').css({
3822
  'width': '',
3823
  'height': '',
3824
  'display': ''
3825
  });
3826
+ // Resize content nodes
3827
  var tags = this.get_tags('item', 'content');
3828
  if ( tags.length ) {
3829
  var offset_item = v.get_item().get_dimensions();
3830
+ // Set content dimensions
3831
  tags = $(l.find(tags[0].get_selector('full')).get(0)).css({'width': offset_item.width, 'height': offset_item.height});
3832
  $.each(offset_item, function(key, val) {
3833
  offset[key] = -1 * val;
3834
  });
3835
  }
3836
 
3837
+ // Set offset
3838
  offset.width += l.width();
3839
  offset.height += l.height();
3840
+ // Normalize
3841
  $.each(offset, function(key, val) {
3842
  if ( val < 0 ) {
3843
  offset[key] = 0;
3865
  var v = this.get_viewer();
3866
  var dims = v.get_item().get_dimensions();
3867
  if ( v.get_attribute('autofit', false) ) {
3868
+ // Get maximum dimensions
3869
  var margin = this.get_margin();
3870
  var offset = this.get_offset();
3871
  offset.height += margin.height;
3877
  if ( max.height > offset.height ) {
3878
  max.height -= offset.height;
3879
  }
3880
+ // Get resize factor
3881
  var factor = Math.min(max.width / dims.width, max.height / dims.height);
3882
+ // Resize dimensions
3883
  if ( factor < 1 ) {
3884
+ $.each(dims, function(key) {
3885
  dims[key] = Math.round(dims[key] * factor);
3886
  });
3887
  }
3896
  get_dimensions: function() {
3897
  var dims = this.get_item_dimensions();
3898
  var offset = this.get_offset();
3899
+ $.each(dims, function(key) {
3900
  dims[key] += offset[key];
3901
  });
3902
  return dims;
3903
  },
3904
 
3905
+ /**
3906
+ * Retrieve all breakpoints
3907
+ * @return object Breakpoints
3908
+ */
3909
+ get_breakpoints: function() {
3910
+ return this.get_attribute_recursive('breakpoints');
3911
+ },
3912
+
3913
+ /**
3914
+ * Get breakpoint value
3915
+ * @param string target Breakpoint target
3916
+ * @return int Breakpoint value (pixels)
3917
+ */
3918
+ get_breakpoint: function(target) {
3919
+ var ret = 0;
3920
+ if ( this.util.is_string(target) ) {
3921
+ var b = this.get_attribute_recursive('breakpoints');
3922
+ if ( this.util.is_obj(b) && target in b ) {
3923
+ ret = b[target];
3924
+ }
3925
+ }
3926
+ return ret;
3927
+ },
3928
+
3929
  /* Output */
3930
 
3931
  /**
3939
  var st = 'events_render';
3940
  if ( !this.get_status(st) ) {
3941
  this.set_status(st);
3942
+ // Register events
3943
  tpl.on([
3944
  'render-init',
3945
  'render-loading',
3949
  return thm.trigger(ev.type, ev.data);
3950
  });
3951
  }
3952
+ // Render template
3953
  tpl.render(init);
3954
  },
3955
 
3968
  el.stop(false, true);
3969
  }
3970
  });
3971
+ };
3972
+ // Stop queued animations
3973
  if ( !!clear_queue ) {
3974
  anim_stop();
3975
  }
3976
+ // Get transition handlers
3977
  var attr_set = [attr, 'set'].join('_');
3978
  var trns;
3979
  if ( !this.get_attribute(attr_set) ) {
3986
  trns.push(model[attr]);
3987
  }
3988
  });
3989
+ // Merge transition handlers into current theme
3990
  trns.push({});
3991
  trns = this.set_attribute(attr, $.extend.apply($, trns.reverse()));
3992
  } else {
3993
  trns = this.get_attribute(attr, {});
3994
  }
3995
  if ( this.util.is_method(trns, event) ) {
3996
+ // Disable animations if necessary
3997
  if ( !anim_on ) {
3998
  fx_temp = $.fx.off;
3999
  $.fx.off = true;
4000
  }
4001
+ // Pass control to transition event
4002
  dfr = trns[event].call(this, v, $.Deferred());
4003
  }
4004
  }
4007
  dfr.reject();
4008
  }
4009
  dfr.always(function() {
4010
+ // Restore animation state
4011
  if ( null !== fx_temp ) {
4012
  $.fx.off = fx_temp;
4013
  }
4031
  _refs: {
4032
  'theme': 'Theme'
4033
  },
4034
+
 
4035
  _attr_default: {
4036
  /**
4037
  * URI to layout (raw) file
4074
  this._super('', attributes);
4075
  },
4076
 
4077
+ _hooks: function() {
4078
+ // TODO: Refactor to event that can save retrieved tags
4079
+ // (`dom_init` event called during attribute initialization so tags are not saved)
4080
+ this.on('dom_init', function(ev) {
4081
+ // Init tag handlers
4082
+ var tags = this.get_tags(null, null, true);
4083
+ var names = [];
4084
+ var t = this;
4085
+ $.each(tags, function(idx, tag) {
4086
+ var name = tag.get_name();
4087
+ if ( -1 === $.inArray(name, names) ) {
4088
+ names.push(name);
4089
+ tag.get_handler().trigger(ev.type, {template: t});
4090
+ }
4091
+ });
4092
+ });
4093
+ },
4094
+
4095
  get_theme: function() {
4096
+ var ret = this.get_component('theme');
4097
  return ret;
4098
  },
4099
 
4112
  if ( !this.util.is_bool(init) ) {
4113
  init = false;
4114
  }
4115
+ // Populate layout
4116
  if ( !init ) {
4117
  if ( !v.is_active() ) {
4118
  return false;
4122
  v.close();
4123
  return false;
4124
  }
4125
+ // Iterate through tags and populate layout
4126
  if ( v.is_active() && this.has_tags() ) {
4127
  var loading_promise = this.trigger('render-loading');
4128
  var tpl = this;
4129
  var tags = this.get_tags(),
4130
  tag_promises = [];
4131
+ // Render Tag output
4132
+ $.when(item.load(), loading_promise).done(function() {
4133
  if ( !v.is_active() ) {
4134
  return false;
4135
  }
4144
  r.tag.dom_get().html(r.output);
4145
  }));
4146
  });
4147
+ // Fire event when all tags rendered
4148
  if ( !v.is_active() ) {
4149
  return false;
4150
  }
4154
  });
4155
  }
4156
  } else {
4157
+ // Get Layout (basic)
4158
  this.trigger('render-init', this.dom_get());
4159
  }
4160
  },
4167
  * @return string Layout (HTML)
4168
  */
4169
  get_layout: function(parsed) {
4170
+ // Validate
4171
  if ( !this.util.is_bool(parsed) ) {
4172
  parsed = true;
4173
  }
4174
+ // Determine which layout to retrieve (raw/parsed)
4175
  var l = ( parsed ) ? this.parse_layout() : this.get_attribute('layout_raw', '');
4176
  return l;
4177
  },
4184
  * @return string Parsed layout
4185
  */
4186
  parse_layout: function() {
4187
+ // Check for previously-parsed layout
4188
  var a = 'layout_parsed';
4189
  var ret = this.get_attribute(a);
4190
+ // Return cached layout immediately
4191
  if ( this.util.is_string(ret) ) {
4192
  return ret;
4193
  }
4194
+ // Parse raw layout
4195
  ret = this.sanitize_layout( this.get_layout(false) );
4196
  ret = this.parse_tags(ret);
4197
+ // Save parsed layout
4198
  this.set_attribute(a, ret);
4199
 
4200
+ // Return parsed layout
4201
  return ret;
4202
  },
4203
 
4207
  * @return obj|string Sanitized layout (Same data type that was passed to method)
4208
  */
4209
  sanitize_layout: function(l) {
4210
+ // Stop processing if invalid value
4211
  if ( this.util.is_empty(l) ) {
4212
  return l;
4213
  }
4214
+ // Set return type
4215
  var rtype = ( this.util.is_string(l) ) ? 'string' : null;
4216
  /* Quarantine hard-coded tags */
4217
 
4218
+ // Create DOM structure from raw template
4219
  var dom = $(l);
4220
+ // Find hard-coded tag nodes
4221
+ var tag_temp = this.get_tag_temp();
4222
  var cls = tag_temp.get_class();
4223
  var cls_new = ['x', cls].join('_');
4224
+ $(tag_temp.get_selector(), dom).each(function() {
4225
+ // Replace matching class name with blocking class
4226
  $(this).removeClass(cls).addClass(cls_new);
4227
  });
4228
+ // Format return value
4229
  switch ( rtype ) {
4230
  case 'string' :
4231
  dom = dom.wrap('<div />').parent().html();
4232
+ l = dom;
4233
+ break;
4234
  default :
4235
  l = dom;
4236
  }
4247
  * @return string Parsed layout
4248
  */
4249
  parse_tags: function(l) {
4250
+ // Validate
4251
  if ( !this.util.is_string(l) ) {
4252
  return '';
4253
  }
4254
+ // Parse tags in layout
4255
+ // Tag regex
4256
  var re = /\{{2}\s*(\w.*?)\s*\}{2}/gim;
4257
+ // Tag match results
4258
  var match;
4259
+ // Iterate through template and find tags
4260
  while ( match = re.exec(l) ) {
4261
+ // Replace tag in layout with DOM container
4262
  l = l.substring(0, match.index) + this.get_tag_container(match[1]) + l.substring(match.index + match[0].length);
4263
  }
4264
  return l;
4270
  * @return string DOM element
4271
  */
4272
  get_tag_container: function(tag) {
4273
+ // Build element
4274
  var attr = this.get_tag_attribute();
4275
+ return this.util.format('<span %s="%s"></span>', attr, encodeURI(tag));
4276
  },
4277
 
4278
  get_tag_attribute: function() {
4279
+ return this.get_tag_temp().dom_get_attribute();
4280
  },
4281
 
4282
  /**
4302
  * Template is parsed if tags not set
4303
  * @param string name (optional) Tag type to retrieve instances of
4304
  * @param string prop (optional) Tag property to retrieve instances of
4305
+ * @param bool isolate (optional) Do not save retrieved tags, only fetch and return (Default: TRUE)
4306
  * @return array Template_Tag instances
4307
  */
4308
+ get_tags: function(name, prop, isolate) {
4309
+ // Validate
4310
+ if ( !this.util.is_bool(isolate) ) {
4311
+ isolate = false;
4312
+ }
4313
+ // Setup
4314
  var a = 'tags';
4315
  var tags = this.get_attribute(a);
4316
+ // Initialize tags
4317
  if ( !this.util.is_array(tags) ) {
4318
  tags = [];
4319
+ // Retrieve layout DOM tree
4320
  var d = this.dom_get();
4321
+ // Select tag nodes
4322
  var attr = this.get_tag_attribute();
4323
  var nodes = $(d).find('[' + attr + ']');
4324
+ // Build tag instances from nodes
4325
+ $(nodes).each(function() {
4326
+ // Get tag placeholder
4327
  var el = $(this);
4328
+ var tag = new View.Template_Tag(decodeURI(el.attr(attr)));
4329
+ // Populate valid tags
4330
  if ( tag.has_handler() ) {
4331
+ // Add tag to array
4332
  tags.push(tag);
4333
+ if ( !isolate ) {
4334
+ // Connect tag to DOM node
4335
+ tag.dom_set(el);
4336
+ // Set classes
4337
+ el.addClass(tag.get_classes(' '));
4338
+ }
4339
+ }
4340
+ // Clear data attribute
4341
+ if ( !isolate ) {
4342
+ el.removeAttr(attr);
4343
  }
 
 
4344
  });
4345
+ if ( !isolate ) {
4346
+ // Save tags
4347
+ this.set_attribute(a, tags, false);
4348
+ }
4349
  }
4350
+ // Filter tags by parameters
 
4351
  if ( !this.util.is_empty(tags) && this.util.is_string(name) ) {
4352
+ // Normalize
4353
  if ( !this.util.is_string(prop) ) {
4354
  prop = false;
4355
  }
4357
  var tc = null;
4358
  for ( var x = 0; x < tags.length; x++ ) {
4359
  tc = tags[x];
4360
+ if ( name === tc.get_name() ) {
4361
+ // Check tag property
4362
+ if ( !prop || prop === tc.get_prop() ) {
4363
  tags_filtered.push(tc);
4364
  }
4365
  }
4377
  return ( this.get_tags().length > 0 ) ? true : false;
4378
  },
4379
 
4380
+ /**
4381
+ * Retrieve temporary tag instance
4382
+ * @return Template_Tag Temporary tag
4383
+ */
4384
+ get_tag_temp: function() {
4385
+ return this.get_controller().get_component_temp(View.Template_Tag);
4386
+ },
4387
+
4388
+ /**
4389
+ * Retrieve Template tag CSS selector
4390
+ * @uses Template.get_tag_temp() to retrieve temporary tag instance
4391
+ * @uses Template_Tag.get_selector() to retrieve selector
4392
+ * @param name string Tag name
4393
+ * @param prop string Tag Property
4394
+ * @return string Template Tag CSS selector
4395
+ */
4396
+ get_tag_selector: function(name, prop) {
4397
+ if ( !this.util.is_string(name) ) {
4398
+ name = '';
4399
+ }
4400
+ if ( !this.util.is_string(prop) ) {
4401
+ prop = '';
4402
+ }
4403
+ var tag = this.get_tag_temp();
4404
+ tag.set_attribute('name', name);
4405
+ tag.set_attribute('prop', prop);
4406
+ return tag.get_selector('full');
4407
+ },
4408
+
4409
  /*-** DOM **-*/
4410
 
4411
  /**
4412
  * Custom DOM initialization
4413
  */
4414
  dom_init: function() {
4415
+ // Create DOM object from parsed layout
4416
  this.dom_set(this.get_layout());
4417
+ this.trigger('dom_init');
4418
  },
4419
 
4420
  /**
4427
  var ret = $();
4428
  var tags = this.get_tags(tag, prop);
4429
  if ( tags.length ) {
4430
+ // Build selector
4431
  var level = null;
4432
  if ( this.util.is_string(tag) ) {
4433
  level = ( this.util.is_string(prop) ) ? 'full' : 'tag';
4475
  * @param string tag_match Extracted tag match
4476
  */
4477
  parse: function(tag_match) {
4478
+ // Return default value for invalid instances
4479
  if ( !this.util.is_string(tag_match) ) {
4480
  return false;
4481
  }
4482
+ // Parse instance options
4483
  var parts = tag_match.split('|'),
4484
  part;
4485
  if ( !parts.length ) {
4490
  prop: null,
4491
  match: tag_match
4492
  };
4493
+ // Get tag ID
4494
  attrs.name = parts[0];
4495
+ // Get main property
4496
+ if ( attrs.name.indexOf('.') !== -1 ) {
4497
  attrs.name = attrs.name.split('.', 2);
4498
  attrs.prop = attrs.name[1];
4499
  attrs.name = attrs.name[0];
4500
  }
4501
+ // Get other attributes
4502
  for ( var x = 1; x < parts.length; x++ ) {
4503
  part = parts[x].split(':', 1);
4504
  if ( part.length > 1 && !( part[0] in attrs ) ) {
4505
+ // Add key/value pair to attributes
4506
  attrs[part[0]] = part[1];
4507
  }
4508
  }
4509
+ // Save to instance
4510
  this.set_attributes(attrs, true);
4511
  },
4512
 
4515
  * @param Content_Item item
4516
  * @return obj jQuery.Promise object that is resolved when tag is rendered
4517
  * Parameters passed to callbacks
4518
+ * > tag obj Current tag instance
4519
  * > output string Tag output
4520
  */
4521
  render: function(item) {
4566
  * @return array Class names
4567
  */
4568
  get_classes: function(rtype) {
4569
+ // Build array of class names
4570
  var cls = [
4571
+ // General tag class
4572
  this.get_class(),
4573
+ // Tag name
4574
  this.get_class('tag'),
4575
+ // Tag name + property
4576
  this.get_class('full')
4577
  ];
4578
+ // Convert class names array to string
4579
  if ( this.util.is_string(rtype) ) {
4580
  cls = cls.join(rtype);
4581
  }
4582
+ // Return class names
4583
  return cls;
4584
  },
4585
 
4593
  */
4594
  get_class: function(level) {
4595
  var cls = '';
4596
+ // Build base
4597
  switch ( level ) {
4598
  case 'tag' :
4599
+ // Tag name
4600
+ cls = this.get_name();
4601
  break;
4602
  case 'full' :
4603
+ // Tag name + property
4604
+ var parts = [this.get_name(), this.get_prop()];
4605
+ var a = [];
4606
+ var i;
4607
+ for ( i = 0; i < parts.length; i++ ) {
4608
+ if ( this.util.is_string(parts[i]) ) {
4609
+ a.push(parts[i]);
4610
+ }
4611
+ }
4612
+ cls = a.join('_');
4613
  break;
4614
  }
4615
+ // Format & return
4616
+ return ( !this.util.is_string(cls) ) ? this.get_ns() : this.add_ns(cls);
4617
  },
4618
 
4619
  /**
4622
  * @return string Tag selector
4623
  */
4624
  get_selector: function(level) {
4625
+ // Get base
4626
+ var ret = this.get_class(level);
4627
+ // Format
4628
+ if ( this.util.is_string(ret) ) {
4629
+ ret = '.' + ret;
4630
+ } else {
4631
+ ret = '';
4632
+ }
4633
+ return ret;
4634
  }
4635
  };
4636
 
4659
  */
4660
  render: function(item, instance) {
4661
  var dfr = $.Deferred();
4662
+ // Pass to attribute method
4663
+ this.call_attribute('render', item, instance, dfr);
4664
+ // Return promise
 
 
 
 
 
 
 
 
 
4665
  return dfr.promise();
4666
  },
4667
 
4668
  add_prop: function(prop, fn) {
4669
+ // Get attribute
4670
  var a = 'props';
4671
  var props = this.get_attribute(a);
4672
+ // Validate
4673
  if ( !this.util.is_string(prop) || !this.util.is_func(fn) ) {
4674
  return false;
4675
  }
4676
  if ( !this.util.is_obj(props, false) ) {
4677
  props = {};
4678
  }
4679
+ // Add property
4680
  props[prop] = fn;
4681
+ // Save attribute
4682
  this.set_attribute(a, props);
4683
  },
4684
 
4685
  handle_prop: function(prop, item, instance) {
4686
+ // Locate property
4687
  var props = this.get_attribute('props');
4688
  var out = '';
4689
  if ( this.util.is_obj(props) && ( prop in props ) && this.util.is_func(props[prop]) ) {
4698
  View.Template_Tag_Handler = Component.extend(Template_Tag_Handler);
4699
  /* Update References */
4700
 
4701
+ // Attach to global object
4702
+ View = SLB.attach('View', View);
4703
+ })(jQuery);}
 
 
client/js/lib.core.js DELETED
@@ -1,591 +0,0 @@
1
- /**
2
- * Core
3
- * @package SLB
4
- * @author Archetyped
5
- */
6
-
7
- (function($) {
8
-
9
- /**
10
- * Extendible class
11
- * Adapted from John Resig
12
- * @link http://ejohn.org/blog/simple-javascript-inheritance/
13
- */
14
- var c_init = false;
15
- var Class = function() {};
16
-
17
- Class.extend = function(members) {
18
- var _super = this.prototype;
19
-
20
- //Copy instance to prototype
21
- c_init = true;
22
- var proto = new this();
23
- c_init = false;
24
-
25
- var val;
26
- //Scrub prototype objects (Decouple from super class)
27
- for ( var name in proto ) {
28
- if ( $.isPlainObject(proto[name]) ) {
29
- val = $.extend({}, proto[name]);
30
- proto[name] = val;
31
- }
32
- }
33
-
34
- //Copy members
35
- for ( var name in members ) {
36
- //Evaluate function members (if overwriting super class method)
37
- if ( 'function' == typeof members[name] && 'function' == typeof _super[name] ) {
38
- proto[name] = (function(name, fn) {
39
- return function() {
40
- //Cache super variable
41
- var tmp = this._super;
42
- //Set variable to super class method
43
- this._super = _super[name];
44
- //Call method
45
- var ret = fn.apply(this, arguments);
46
- //Restore super variable
47
- this._super = tmp;
48
- //Return value
49
- return ret;
50
- }
51
- })(name, members[name]);
52
- } else {
53
- val = members[name];
54
- if ( $.isPlainObject(members[name]) ) {
55
- val = $.extend({}, members[name]);
56
- }
57
- proto[name] = val;
58
- }
59
- }
60
-
61
- //Constructor
62
- function Class() {
63
- if ( !c_init ) {
64
- //Private init
65
- if ( this._init ) {
66
- this._init.apply(this, arguments);
67
- }
68
- //Main Constructor
69
- if ( this._c ) {
70
- this._c.apply(this, arguments);
71
- }
72
- }
73
- }
74
-
75
-
76
- //Populate new prototype
77
- Class.prototype = proto;
78
-
79
- //Set constructor
80
- Class.prototype.constructor = Class;
81
-
82
- Class.extend = arguments.callee;
83
-
84
- //Return function
85
- return Class;
86
- };
87
-
88
- /* Base */
89
- var Base = {
90
- /* Properties */
91
-
92
- base: false,
93
- _parent: null,
94
- prefix: 'slb',
95
-
96
- /* Methods */
97
-
98
- /**
99
- * Constructor
100
- */
101
- _init: function() {
102
- this._set_parent();
103
- },
104
-
105
- _set_parent: function(p) {
106
- if ( typeof p != 'undefined' )
107
- this._parent = p;
108
- this.util._parent = this;
109
- },
110
-
111
- /**
112
- * Attach member to object
113
- * @param string name Member name
114
- * @param mixed Member data
115
- * > obj: Member inherits from base object
116
- * > other: Simple data object
117
- */
118
- attach: function(member, data, simple) {
119
- simple = ( typeof simple == undefined ) ? false : !!simple;
120
- if ( $.type(member) == 'string' && $.isPlainObject(data) ) {
121
- //Add initial member
122
- var obj = {};
123
- if ( simple ) {
124
- //Simple object
125
- obj[member] = $.extend({}, data);
126
- $.extend(this, obj);
127
- } else {
128
- //Add new instance object
129
- data['_parent'] = this;
130
- var c = this.Class.extend(data);
131
- this[member] = new c();
132
- }
133
- }
134
- },
135
-
136
- /**
137
- * Get parent object
138
- * @return obj Parent object
139
- */
140
- get_parent: function() {
141
- return this._parent;
142
- },
143
-
144
- /**
145
- * Utility methods
146
- */
147
- util: {
148
- /* Properties */
149
-
150
- _base: null,
151
- _parent: null,
152
-
153
- /* Constants */
154
-
155
- string: 'string',
156
- bool: 'boolean',
157
- array: 'array',
158
- obj: 'object',
159
- func: 'function',
160
- num: 'number',
161
-
162
- /* Methods */
163
-
164
- get_base: function() {
165
- if ( !this._base ) {
166
- var p = this.get_parent();
167
- var p_last = null;
168
- //Iterate through parents
169
- while ( !p.base && p_last != p && p._parent ) {
170
- p_last = p;
171
- p = p._parent;
172
- }
173
- //Set base
174
- this._base = p;
175
- }
176
- return this._base;
177
- },
178
-
179
- get_parent: function() {
180
- return this._parent;
181
- },
182
-
183
- /**
184
- * Retrieve valid separator
185
- * If supplied argument is not a valid separator, use default separator
186
- * @param string (optional) sep Separator text
187
- * @return string Separator text
188
- */
189
- get_sep: function(sep) {
190
- return ( this.is_string(sep, false) ) ? sep : '_';
191
- },
192
-
193
- /**
194
- * Retrieve prefix
195
- * @return string Prefix
196
- */
197
- get_prefix: function() {
198
- return ( this.is_string(this.get_parent().prefix) ) ? this.get_parent().prefix : '';
199
- },
200
-
201
- /**
202
- * Check if string is prefixed
203
- */
204
- has_prefix: function(val, sep) {
205
- return ( this.is_string(val) && val.indexOf(this.get_prefix() + this.get_sep(sep)) === 0 );
206
- },
207
-
208
- /**
209
- * Add Prefix to a string
210
- * @param string val Value to add prefix to
211
- * @param string sep (optional) Separator (Default: `_`)
212
- * @param bool (optional) once If text should only be prefixed once (Default: TRUE)
213
- */
214
- add_prefix: function(val, sep, once) {
215
- //Validate
216
- if ( !this.is_string(val) ) {
217
- //Return prefix if value to add prefix to is empty
218
- return this.get_prefix();
219
- }
220
- sep = this.get_sep(sep);
221
- if ( !this.is_bool(once) ) {
222
- once = true;
223
- }
224
-
225
- return ( once && this.has_prefix(val, sep) ) ? val : [this.get_prefix(), val].join(sep);
226
- },
227
-
228
- /**
229
- * Remove Prefix from a string
230
- * @param string val Value to add prefix to
231
- * @param string sep (optional) Separator (Default: `_`)
232
- * @param bool (optional) once If text should only be prefixed once (Default: true)
233
- */
234
- remove_prefix: function(val, sep, once) {
235
- //Validate parameters
236
- if ( !this.is_string(val, true) ) {
237
- return val;
238
- }
239
- //Default values
240
- sep = this.get_sep(sep);
241
- if ( !this.is_bool(once) ) {
242
- once = true;
243
- }
244
- //Check if string is prefixed
245
- if ( this.has_prefix(val, sep) ) {
246
- //Remove prefix
247
- var prfx = this.get_prefix() + sep;
248
- do {
249
- val = val.substr(prfx.length);
250
- } while ( !once && this.has_prefix(val, sep) );
251
- }
252
- return val;
253
- },
254
-
255
- /*
256
- * Get attribute name
257
- * @param string val Attribute's base name
258
- */
259
- get_attribute: function(val) {
260
- //Setup
261
- var sep = '-';
262
- var top = 'data';
263
- //Validate
264
- var pre = [top, this.get_prefix()].join(sep);
265
- if ( !this.is_string(val, false) ) {
266
- return pre;
267
- }
268
- //Process
269
- if ( val.indexOf(pre + sep) == -1 ) {
270
- val = [pre, val].join(sep);
271
- }
272
- return val;
273
- },
274
-
275
- /* Request */
276
-
277
- /**
278
- * Retrieve valid context
279
- * @return array Context
280
- */
281
- get_context: function() {
282
- //Valid context
283
- var b = this.get_base();
284
- if ( !$.isArray(b.context) )
285
- b.context = [];
286
- //Return context
287
- return b.context;
288
- },
289
-
290
- /**
291
- * Check if a context exists in current request
292
- * If multiple contexts are supplied, result will be TRUE if at least ONE context exists
293
- *
294
- * @param string|array ctx Context to check for
295
- * @return bool TRUE if context exists, FALSE otherwise
296
- */
297
- is_context: function(ctx) {
298
- var ret = false;
299
- //Validate context
300
- if ( typeof ctx == 'string' )
301
- ctx = [ctx];
302
- if ( $.isArray(ctx) && this.arr_intersect(this.get_context(), ctx).length ) {
303
- ret = true;
304
- }
305
- return ret;
306
- },
307
-
308
- /* Helpers */
309
-
310
- is_set: function(value) {
311
- return ( $.type(value) != 'undefined' ) ? true : false;
312
- },
313
-
314
- is_type: function(value, type, nonempty) {
315
- var ret = false;
316
- if ( this.is_set(value) && null != value && this.is_set(type) ) {
317
- switch ( $.type(type) ) {
318
- case this.func:
319
- ret = ( value instanceof type ) ? true : false;
320
- break;
321
- case this.string:
322
- ret = ( $.type(value) == type ) ? true : false;
323
- break;
324
- default:
325
- ret = false;
326
- break;
327
- }
328
- }
329
-
330
- //Validate empty values
331
- if ( ret && ( $.type(nonempty) != this.bool || nonempty ) ) {
332
- ret = !this.is_empty(value);
333
- }
334
- return ret;
335
- },
336
-
337
- is_string: function(value, nonempty) {
338
- return this.is_type(value, this.string, nonempty);
339
- },
340
-
341
- is_array: function(value, nonempty) {
342
- return ( this.is_type(value, this.array, nonempty) );
343
- },
344
-
345
- is_bool: function(value) {
346
- return this.is_type(value, this.bool, false);
347
- },
348
-
349
- is_obj: function(value, nonempty) {
350
- return this.is_type(value, this.obj, nonempty);
351
- },
352
-
353
- is_func: function(value) {
354
- return this.is_type(value, this.func, false);
355
- },
356
-
357
- /**
358
- * Checks if an object has a method
359
- * @param obj Object to check
360
- * @param string|array Names of methods to check for
361
- * @return bool TRUE if method(s) exist, FALSE otherwise
362
- */
363
- is_method: function(obj, value) {
364
- var ret = false;
365
- if ( this.is_string(value) ) {
366
- value = [value];
367
- }
368
- if ( this.in_obj(obj, value) ) {
369
- var t = this;
370
- $.each(value, function(idx, val) {
371
- ret = ( t.is_func(obj[val]) ) ? true : false;
372
- return ret;
373
- });
374
- }
375
- return ret;
376
- },
377
-
378
- is_num: function(value, nonempty) {
379
- return ( this.is_type(value, this.num, nonempty) && !isNaN(value) );
380
- },
381
-
382
- is_int: function(value, nonempty) {
383
- return ( this.is_num(value, nonempty) && Math.floor(value) === value );
384
- },
385
-
386
- is_scalar: function(value, nonempty) {
387
- return ( this.is_num(value, nonempty) || this.is_string(value, nonempty) || this.is_bool(value, nonempty) );
388
- },
389
-
390
- /**
391
- * Checks if value is empty
392
- * @param mixed value Value to check
393
- * @param string type (optional) Data type
394
- * @return bool TRUE if value is empty, FALSE if not empty
395
- */
396
- is_empty: function(value, type) {
397
- var ret = false;
398
- //Initial check for empty value
399
- if ( !this.is_set(value) || null === value || false === value ) {
400
- ret = true;
401
- } else {
402
- //Validate type
403
- if ( !this.is_set(type) ) {
404
- type = $.type(value);
405
- }
406
- //Type-based check
407
- if ( this.is_type(value, type, false) ) {
408
- switch ( type ) {
409
- case this.string:
410
- case this.array:
411
- if ( value.length == 0 ) {
412
- ret = true;
413
- }
414
- break;
415
- case this.obj:
416
- //Only evaluate literal objects
417
- ret = ( $.isPlainObject(value) && !$.map(value, function(v, key) { return key; }).length );
418
- break;
419
- case this.num:
420
- ret = ( value === 0 );
421
- break;
422
- }
423
- } else {
424
- ret = true;
425
- }
426
- }
427
- return ret;
428
- },
429
-
430
- /**
431
- * Check if object is a jQuery.Promise instance
432
- * Will also match (but not guarantee) jQuery.Deferred instances
433
- * @return bool TRUE if object is Promise/Deferred, FALSE otherwise
434
- */
435
- is_promise: function(obj) {
436
- return ( this.is_obj(obj) && this.is_method(obj, ['then', 'done', 'always', 'fail', 'pipe']) )
437
- },
438
-
439
- /**
440
- * Check if object is a jQuery.Deferred instance
441
- */
442
- is_deferred: function(obj) {
443
- return ( this.is_promise(obj) && this.is_method(obj, ['resolve', 'reject', 'promise']));
444
- },
445
-
446
- /**
447
- * Validate specified value's data type and return default value if necessary
448
- * Data type of default value is used to determine data type
449
- * @param mixed val Value to check
450
- * @param mixed def Default value
451
- * @return mixed Valid value
452
- */
453
- validate: function(val, def) {
454
- return ( this.is_type(val, def, true) ) ? val : def;
455
- },
456
-
457
- /**
458
- * Return formatted string
459
- */
460
- format: function(fmt, val) {
461
- if ( !this.is_string(fmt) ) {
462
- return '';
463
- }
464
- var params = [],
465
- ph = '%s';
466
- //Stop processing if no replacement values specified or format string contains no placeholders
467
- if ( arguments.length < 2 || fmt.indexOf(ph) == -1 ) {
468
- return fmt;
469
- }
470
- //Get replacement values
471
- params = Array.prototype.slice.call(arguments, 1);
472
-
473
- //Replace placeholders in string with parameters
474
-
475
- //Replace all placeholders at once if single parameter set
476
- if ( params.length == 1 ) {
477
- fmt = fmt.replace(ph, params[0].toString());
478
- } else {
479
- var idx = 0,
480
- len = params.length,
481
- pos = 0;
482
- while ( ( pos = fmt.indexOf(ph) ) && idx < len ) {
483
- fmt = fmt.substr(0, pos) + params[idx].toString() + fmt.substr(pos + ph.length);
484
- idx++;
485
- }
486
- //Remove any remaining placeholders
487
- fmt = fmt.replace(ph, '');
488
- }
489
- return fmt;
490
- },
491
-
492
- /**
493
- * Checks if key(s) exist in an object
494
- * @param object obj Object to check
495
- * @param string|array key Key(s) to check for in object
496
- * @return bool TRUE if key(s) exist in object, FALSE otherwise
497
- */
498
- in_obj: function(obj, key, all) {
499
- //Validate
500
- if ( !this.is_bool(all) ) {
501
- all = true;
502
- }
503
- if ( this.is_string(key) ) {
504
- key = [key];
505
- }
506
- var ret = false;
507
- if ( this.is_obj(obj) && this.is_array(key) ) {
508
- var val;
509
- for ( var x = 0; x < key.length; x++ ) {
510
- val = key[x];
511
- ret = ( this.is_string(val) && ( val in obj ) ) ? true : false;
512
- //Stop processing if conditions have been met
513
- if ( ( !all && ret ) || ( all && !ret ) ) {
514
- break;
515
- }
516
- }
517
- }
518
- return ret;
519
- },
520
-
521
- /**
522
- * Find common elements of 2 arrays
523
- * @param array arr1 First array
524
- * @param array arr2 Second array
525
- * @return array Elements common to both arrays
526
- */
527
- arr_intersect: function(arr1, arr2) {
528
- var ret = [];
529
- if ( arr1 == arr2 ) {
530
- return arr2;
531
- }
532
- if ( !$.isArray(arr2) || !arr2.length || !arr1.length ) {
533
- return ret;
534
- }
535
- //Compare elements in arrays
536
- var a1;
537
- var a2;
538
- var val;
539
- if ( arr1.length < arr2.length ) {
540
- a1 = arr1;
541
- a2 = arr2;
542
- } else {
543
- a1 = arr2;
544
- a2 = arr1;
545
- }
546
-
547
- for ( var x = 0; x < a1.length; x++ ) {
548
- //Add mutual elements into intersection array
549
- val = a1[x];
550
- if ( a2.indexOf(val) != -1 && ret.indexOf(val) == -1 )
551
- ret.push(val);
552
- }
553
-
554
- //Return intersection results
555
- return ret;
556
- }
557
- }
558
- };
559
- var SLB_Base = Class.extend(Base);
560
-
561
- //Init global object
562
- var Core = {
563
- /* Properties */
564
-
565
- base: true,
566
- context: [],
567
-
568
- /**
569
- * New object initializer
570
- * @var obj
571
- */
572
- Class: SLB_Base,
573
-
574
- /* Methods */
575
-
576
- /**
577
- * Setup client
578
- * Set variables, DOM, etc.
579
- */
580
- setup_client: function() {
581
- /* Quick Hide */
582
- $('html').addClass(this.util.get_prefix());
583
- }
584
- };
585
- var SLB_Core = SLB_Base.extend(Core);
586
-
587
- this.SLB = new SLB_Core();
588
-
589
- SLB.setup_client();
590
-
591
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
client/js/prod/lib.admin.js ADDED
@@ -0,0 +1 @@
 
1
+ window.SLB&&SLB.attach&&!function($){SLB.attach("Admin",{init:function(){postboxes&&postboxes.add_postbox_toggles(pagenow)}}),$(document).ready(function(){SLB.Admin.init()})}(jQuery);
client/js/prod/lib.core.js ADDED
@@ -0,0 +1 @@
 
1
+ window.jQuery&&!function($){"use strict";var c_init=!1,Class=function(){};Class.extend=function(members){function Class(){c_init||("function"==typeof this._init&&this._init.apply(this,arguments),"function"==typeof this._c&&this._c.apply(this,arguments))}var _super=this.prototype;c_init=!0;var proto=new this;c_init=!1;var val,name;for(name in proto)$.isPlainObject(proto[name])&&(val=$.extend({},proto[name]),proto[name]=val);var make_handler=function(nm,fn){return function(){var tmp=this._super;this._super=_super[nm];var ret=fn.apply(this,arguments);return this._super=tmp,ret}};for(name in members)"function"==typeof members[name]&&"function"==typeof _super[name]?proto[name]=make_handler(name,members[name]):proto[name]=$.isPlainObject(members[name])?$.extend({},members[name]):members[name];return Class.prototype=proto,Class.prototype.constructor=Class,Class.extend=this.extend,Class};var Base={base:!1,_parent:null,prefix:"slb",_init:function(){this._set_parent()},_set_parent:function(p){this.util.is_set(p)&&(this._parent=p),this.util._parent=this},attach:function(member,data,simple){var ret=data;return simple="undefined"!=typeof simple&&!!simple,"string"===$.type(member)&&($.isPlainObject(data)&&!simple&&(data._parent=this,data=this.Class.extend(data)),this[member]="function"===$.type(data)?new data:data,ret=this[member]),ret},has_child:function(child){if(!this.util.is_string(child))return!1;var children=child.split(".");child=null;var x,o=this;for(x=0;x<children.length;x++)if(child=children[x],""!==child){if(!this.util.is_obj(o)||!o[child])return!1;o=o[child]}return!0},is_base:function(){return!!this.base},get_parent:function(){var p=this._parent;return p||(this._parent={}),this._parent}},Utilities={_base:null,_parent:null,get_base:function(){if(!this._base){for(var p=this.get_parent(),p_prev=null,methods=["is_base","get_parent"];p_prev!==p&&this.is_method(p,methods)&&!p.is_base();)p_prev=p,p=p.get_parent();this._base=p}return this._base},get_parent:function(prop){var ret=this._parent;return ret||(ret=this._parent={}),this.is_string(prop)&&(ret=this.in_obj(ret,prop)?ret[prop]:null),ret},get_sep:function(sep){var sep_default="_";return this.is_string(sep,!1)?sep:sep_default},get_prefix:function(){var p=this.get_parent("prefix");return this.is_string(p,!1)?p:""},has_prefix:function(val,sep){return this.is_string(val)&&0===val.indexOf(this.get_prefix()+this.get_sep(sep))},add_prefix:function(val,sep,once){return this.is_string(val)?(sep=this.get_sep(sep),this.is_bool(once)||(once=!0),once&&this.has_prefix(val,sep)?val:[this.get_prefix(),val].join(sep)):this.get_prefix()},remove_prefix:function(val,sep,once){if(!this.is_string(val,!0))return"";if(sep=this.get_sep(sep),this.is_bool(once)||(once=!0),this.has_prefix(val,sep)){var prfx=this.get_prefix()+sep;do val=val.substr(prfx.length);while(!once&&this.has_prefix(val,sep))}return val},get_attribute:function(attr_base){var sep="-",top="data",attr=[top,this.get_prefix()].join(sep);return this.is_string(attr_base)&&0!==attr_base.indexOf(attr+sep)&&(attr=[attr,attr_base].join(sep)),attr},get_context:function(){var b=this.get_base();return $.isArray(b.context)||(b.context=[]),b.context},is_context:function(ctx){return this.is_string(ctx)&&(ctx=[ctx]),this.is_array(ctx)&&this.arr_intersect(this.get_context(),ctx).length>0},is_set:function(val){return"undefined"!=typeof val},is_type:function(val,type,nonempty){var ret=!1;if(this.is_set(val)&&null!==val&&this.is_set(type))switch($.type(type)){case"function":ret=val instanceof type;break;case"string":ret=$.type(val)===type;break;default:ret=!1}return!ret||this.is_set(nonempty)&&!nonempty||(ret=!this.is_empty(val)),ret},is_string:function(value,nonempty){return this.is_type(value,"string",nonempty)},is_array:function(value,nonempty){return this.is_type(value,"array",nonempty)},is_bool:function(value){return this.is_type(value,"boolean",!1)},is_obj:function(value,nonempty){return this.is_type(value,"object",nonempty)},is_func:function(value){return this.is_type(value,"function",!1)},is_method:function(obj,key){var ret=!1;if(this.is_string(key)&&(key=[key]),this.in_obj(obj,key)){ret=!0;for(var x=0;ret&&x<key.length;)ret=this.is_func(obj[key[x]]),x++}return ret},is_instance:function(obj,parent){return!!this.is_func(parent)&&(this.is_obj(obj)&&obj instanceof parent)},is_class:function(cls,parent){var ret=this.is_func(cls)&&"prototype"in cls;return ret&&this.is_set(parent)&&(ret=this.is_instance(cls.prototype,parent)),ret},is_num:function(value,nonempty){var f={nan:Number.isNaN?Number.isNaN:isNaN,finite:Number.isFinite?Number.isFinite:isFinite};return this.is_type(value,"number",nonempty)&&!f.nan(value)&&f.finite(value)},is_int:function(value,nonempty){return this.is_num(value,nonempty)&&Math.floor(value)===value},is_scalar:function(value,nonempty){return this.is_num(value,nonempty)||this.is_string(value,nonempty)||this.is_bool(value)},is_empty:function(value,type){var ret=!1;if(this.is_set(value))for(var empties=[null,"",!1,0],x=0;!ret&&x<empties.length;)ret=empties[x]===value,x++;else ret=!0;if(!ret)if(this.is_set(type)||(type=$.type(value)),this.is_type(value,type,!1))switch(type){case"string":case"array":ret=0===value.length;break;case"number":ret=0==value;break;case"object":if($.isPlainObject(value)){if(Object.getOwnPropertyNames)ret=0===Object.getOwnPropertyNames(value).length;else if(value.hasOwnProperty){ret=!0;for(var key in value)if(value.hasOwnProperty(key)){ret=!1;break}}}else ret=!1}else ret=!0;return ret},is_promise:function(obj){return this.is_method(obj,["then","done","always","fail","pipe"])},format:function(fmt,val){if(!this.is_string(fmt))return"";var params=[],ph="%s",strip=function(txt){return txt.indexOf(ph)!==-1?txt.replace(ph,""):txt};if(arguments.length<2||fmt.indexOf(ph)===-1)return strip(fmt);params=Array.prototype.slice.call(arguments,1),val=null;for(var x=0;x<params.length;x++)this.is_scalar(params[x],!1)||(params[x]="");if(1===params.length)fmt=fmt.replace(ph,params[0].toString());else{for(var idx=0,len=params.length,rlen=ph.length,pos=0;(pos=fmt.indexOf(ph))&&pos!==-1&&idx<len;)fmt=fmt.substr(0,pos)+params[idx].toString()+fmt.substr(pos+rlen),idx++;fmt=strip(fmt)}return fmt},in_obj:function(obj,key,all){this.is_bool(all)||(all=!0),this.is_string(key)&&(key=[key]);var ret=!1;if(this.is_obj(obj)&&this.is_array(key))for(var val,x=0;x<key.length&&(val=key[x],ret=!!(this.is_string(val)&&val in obj),!(!all&&ret||all&&!ret));x++);return ret},obj_keys:function(obj){var keys=[];if(!this.is_obj(obj))return keys;if(Object.keys)keys=Object.keys(obj);else{var prop;for(prop in obj)obj.hasOwnProperty(prop)&&keys.push(prop)}return keys},arr_intersect:function(arr1,arr2){var x,ret=[],params=Array.prototype.slice.call(arguments),arrs=[];for(x=0;x<params.length;x++)this.is_array(params[x],!1)&&arrs.push(params[x]);if(arrs.length<2)return ret;params=arr1=arr2=null;var add,sub,base=arrs.shift();for(x=0;x<base.length;x++){for(add=!0,sub=0;sub<arrs.length;sub++)if(arrs[sub].indexOf(base[x])===-1){add=!1;break}add&&ret.push(base[x])}return ret},guid:function(){function _p8(s){var p=(Math.random().toString(16)+"000000000").substr(2,8);return s?"-"+p.substr(0,4)+"-"+p.substr(4,4):p}return _p8()+_p8(!0)+_p8(!0)+_p8()},parse_uri:function(uri){return $('<a href="'+uri+'"/>').get(0)},parse_query:function(uri){var delim={vars:"&",val:"="},query={raw:[],parsed:{},string:""};if(uri=this.parse_uri(uri),0===uri.search.indexOf("?")){query.raw=uri.search.substr(1).split(delim.vars);var i,temp,key,val;for(i=0;i<query.raw.length;i++)temp=query.raw[i].split(delim.val),key=temp.shift(),val=temp.length>0?temp.join(delim.val):null,query.parsed[key]=val}return query.parsed},build_query:function(query){var val,q=[],delim={vars:"&",val:"="};for(var key in query)val=null!==query[key]?delim.val+query[key]:"",q.push(key+val);return q.join(delim.vars)}};Base.attach("util",Utilities,!0);var SLB_Base=Class.extend(Base),Core={base:!0,context:[],Class:SLB_Base,_init:function(){this._super(),$("html").addClass(this.util.get_prefix())}},SLB_Core=SLB_Base.extend(Core);window.SLB=new SLB_Core}(jQuery);
client/js/prod/lib.view.js ADDED
@@ -0,0 +1 @@
 
1
+ window.SLB&&SLB.attach&&function($){var View={assets:{},component_defaults:[],loading:[],cache:{},component_temps:{},options:{},_init:function(){this._super(),this.init_refs(),this.init_components()},init_refs:function(){var r,ref,prop;for(prop in this)if(prop=this[prop],this.is_component(prop)&&!this.util.is_empty(prop.prototype._refs))for(r in prop.prototype._refs)ref=prop.prototype._refs[r],this.util.is_string(ref)&&ref in this&&(ref=prop.prototype._refs[r]=this[ref]),this.util.is_class(ref)||delete prop.prototype_refs[r]},init_components:function(){this.component_defaults=[this.Viewer]},init:function(options){var t=this;$.when.apply($,this.loading).always(function(){$.extend(!0,t.options,options),$(window).on("popstate",function(e){var state=e.originalEvent.state;if(t.util.in_obj(state,["item","viewer"]))return t.get_viewer(state.viewer).history_handle(e),e.preventDefault()}),t.init_items()})},can_make_default_component:function(type){return-1!==$.inArray(type,this.component_defaults)},is_component:function(comp){return this.util.is_class(comp,this.Component)},get_components:function(type){var ret={};if(this.is_component(type)){var coll=type.prototype._slug+"s";coll in this.cache||(this.cache[coll]={}),ret=this.cache[coll]}return ret},get_component:function(type,id){var ret=null;if(!this.util.is_func(type))return ret;this.util.is_string(id)||(id=null);var coll=this.get_components(type);if(this.util.is_obj(coll)){var tid=this.util.is_string(id)?id:this.util.add_prefix("default");tid in coll&&(ret=coll[tid])}return this.util.is_empty(ret)&&(this.util.is_string(id)||this.can_make_default_component(type))&&(ret=this.add_component(type,id)),ret},add_component:function(type,id,options){if(!this.util.is_func(type))return!1;if(this.util.is_empty(id)&&!this.can_make_default_component(type))return!1;var ret=null;this.util.is_empty(id)&&(id=this.util.add_prefix("default")),this.util.is_obj(options)||(options={});var m="component"!==type.prototype._slug?"add_"+type.prototype._slug:null;if(ret=!this.util.is_empty(m)&&m in this&&this.util.is_func(this[m])?this[m](id,options):new type(id,options),this.util.is_type(ret,type)){var coll=this.get_components(type);switch($.type(coll)){case"object":coll[id]=ret;break;case"array":coll.push(ret)}}else ret=null;return ret},add_component_temp:function(type){var ret=null;return this.is_component(type)&&(ret=new type(""),this.component_temps[ret._slug]=ret),ret},get_component_temp:function(type){return this.has_component_temp(type)?this.component_temps[type.prototype._slug]:this.add_component_temp(type)},has_component_temp:function(type){return!!(this.is_component(type)&&type.prototype._slug in this.component_temps)},get_options:function(opts){var ret={};if(this.util.is_string(opts)&&(opts=[opts]),!this.util.is_array(opts))return ret;for(var x=0;x<opts.length;x++)opts[x]in this.options&&(ret[opts[x]]=this.options[opts[x]]);return ret},get_option:function(opt,def){var ret=this.get_options(opt);return ret=this.util.is_obj(ret)&&opt in ret?ret[opt]:this.util.is_set(def)?def:null},add_viewer:function(id,options){var v=new this.Viewer(id,options);return this.get_viewers()[v.get_id()]=v},get_viewers:function(){return this.get_components(this.Viewer)},has_viewer:function(v){return!!(this.util.is_string(v)&&v in this.get_viewers())},get_viewer:function(v){return this.has_viewer(v)||(v=this.util.add_prefix("default"),this.has_viewer(v)||(v=(v=this.add_viewer(v)).get_id())),this.get_viewers()[v]},init_items:function(){var t=this,sel=this.util.format('a[href][%s="%s"]',this.util.get_attribute("active"),1);$(document).on("click",sel,null,function(){var ret=t.show_item(this);return t.util.is_bool(ret)||(ret=!0),!ret})},get_items:function(){return this.get_components(this.Content_Item)},get_item:function(ref){if(this.util.is_type(ref,this.Content_Item))return ref;var item=null;if(this.util.in_obj(ref,"nodeType")){var key=this.get_component_temp(this.Content_Item).get_data_key();item=$(ref).data(key)}else if(this.util.is_string(ref,!1)){var items=this.get_items();ref in items&&(item=items[ref])}return this.util.is_instance(item,this.Content_Item)||(item=this.add_item(ref)),item},add_item:function(el){return new this.Content_Item(el)},show_item:function(el){return this.get_item(el).show()},save_item:function(item){return this.util.is_instance(item,this.Content_Item)?this.get_items()[item.get_id()]=item:item},get_content_handlers:function(){return this.get_components(this.Content_Handler)},get_content_handler:function(item){var type=this.util.is_instance(item,this.Content_Item)?item.get_attribute("type",""):item.toString(),types=this.get_content_handlers();return type in types?types[type]:null},extend_content_handler:function(id,attr){var hdl=null;if(!this.util.is_string(id)||!this.util.is_obj(attr))return hdl;null===(hdl=this.get_content_handler(id))?this.get_content_handlers()[id]=hdl=new this.Content_Handler(id,attr):hdl.set_attributes(attr);return this.util.in_obj(attr,"styles")&&this.load_styles(attr.styles),hdl},add_group:function(g,attrs){return g=new this.Group(g,attrs),this.get_groups()[g.get_id()]=g},get_groups:function(){return this.get_components(this.Group)},get_group:function(g){return this.has_group(g)?this.get_groups()[g]:this.add_group(g)},has_group:function(g){return this.util.is_string(g)&&g in this.get_groups()},extend_theme:function(id,attr){if(!this.util.is_string(id))return!1;var dfr=$.Deferred();this.loading.push(dfr);var model=this.get_theme_model(id);return this.util.is_empty(model)&&(model=this.save_theme_model({parent:null,id:id})),this.util.is_obj(attr)&&("id"in attr&&delete attr.id,$.extend(model,attr)),this.util.in_obj(attr,"styles")&&this.load_styles(attr.styles),this.util.is_obj(model.parent)||(model.parent=this.get_theme_model(model.parent)),dfr.resolve(),model},get_theme_models:function(){return this.Theme.prototype._models},get_theme_model:function(id){var ms=this.get_theme_models();return this.util.in_obj(ms,id)?ms[id]:{}},save_theme_model:function(model){return this.util.in_obj(model,"id")&&this.util.is_string(model.id)&&(this.get_theme_models()[model.id]=model),model},extend_template_tag_handler:function(id,attr){if(!this.util.is_string(id)||!this.util.is_obj(attr))return!1;var hdl,hdls=this.get_template_tag_handlers();return this.util.in_obj(hdls,id)?(hdl=hdls[id]).set_attributes(attr):hdls[(hdl=new this.Template_Tag_Handler(id,attr)).get_id()]=hdl,this.util.in_obj(attr,"styles")&&this.load_styles(attr.styles),this.util.in_obj(attr,"_hooks")&&attr._hooks.call(hdl),hdl},get_template_tag_handlers:function(){return this.Template_Tag.prototype.handlers},get_template_tag_handler:function(id){var handlers=this.get_template_tag_handlers();return this.util.in_obj(handlers,id)?handlers[id]:null},load_styles:function(styles){if(this.util.is_array(styles)){for(var style,out=[],x=0;x<styles.length;x++)style=styles[x],this.util.in_obj(style,"uri")&&this.util.is_string(style.uri)&&out.push('<link rel="stylesheet" type="text/css" href="'+style.uri+'" />');$("head").append(out.join(""))}}},Component={_slug:"component",_ns:null,_refs:{},_reciprocal:!1,_dom:null,_attributes:!1,_attr_default:{},_attr_default_parsed:!1,_attr_init:null,_attr_map:{},_events:{},_status:null,_id:"",_c:function(id,attributes){this._set_id(id),this.util.is_obj(attributes)&&(this._attr_init=attributes),this._hooks()},_set_parent:function(){this._super(View)},_hooks:function(){},_set_id:function(id){return this.util.is_empty(this._id)&&(this._id=this.util.is_string(id)?id:this.util.guid()),this._id},get_id:function(ns){var id=this._id;return this.util.is_bool(ns)&&ns&&(id=this.add_ns(id)),id},get_ns:function(){return null===this._ns&&(this._ns=this.util.add_prefix(this._slug)),this._ns},add_ns:function(val){return this.util.is_string(val)?this.get_ns()+"_"+val:""},get_status:function(id,raw){var ret=!1;return this.util.in_obj(this._status,id)&&(ret=raw?this._status[id]:!!this._status[id]),ret},set_status:function(id,val){return this.util.is_string(id)?(this.util.is_set(val)||(val=!0),this.util.is_obj(this._status,!1)||(this._status={}),this._status[id]=val):this.util.is_set(val)||(val=!1),val},get_controller:function(){return this.get_parent()},has_reference:function(ref){return!!(this.util.is_string(ref)&&ref in this&&ref in this.get_references())},get_references:function(){return this._refs},get_reference:function(ref){return this.has_reference(ref)?this._refs[ref]:null},get_component:function(cname,options){var c=null;if(!this.has_reference(cname))return c;options=$.extend({},{check_attr:!0,get_default:!1},options);var ctype=this.get_reference(cname);return this.util.is_type(this[cname],ctype)?this[cname]:(c=this[cname]=null,options.check_attr&&(c=this.get_attribute(cname),this.util.is_empty(c)||(c=this.set_component(cname,c))),this.util.is_empty(c)&&options.get_default&&(c=this.get_controller().get_component(ctype)),c)},set_component:function(name,ref,validate){if(!this.has_reference(name))return null;if(this.util.is_empty(ref))ref=null;else{var ctype=this.get_reference(name);this.util.is_string(ref,!1)&&(ref=this.get_controller().get_component(ctype,ref)),(!this.util.is_type(ref,ctype)||this.util.is_func(validate)&&!validate.call(this,ref))&&(ref=null)}return this[name]=ref,this[name]},clear_component:function(name){this.set_component(name,null)},init_attributes:function(force){if(this.util.is_bool(force)||(force=!1),force||!this.util.is_obj(this._attributes)){var a=this._attributes={};$.extend(a,this.init_default_attributes()),this.util.is_obj(this._attr_init)&&$.extend(a,this._attr_init),$.extend(a,this.get_dom_attributes())}},init_default_attributes:function(){if(!this._attr_default_parsed&&this.util.is_obj(this._attr_map)){var opts=this.get_controller().get_options(this.util.obj_keys(this._attr_map));if(this.util.is_obj(opts)){for(var opt in this._attr_map)opt in opts&&null!==this._attr_map[opt]&&(opts[this._attr_map[opt]]=opts[opt],delete opts[opt]);$.extend(!0,this._attr_default,opts)}this._attr_default_parsed=!0}return this._attr_default},get_dom_attributes:function(){var attrs={},el=this.dom_get(null,{init:!1});if(0<el.length){var attrs_full=$(el).get(0).attributes;if(this.util.is_obj(attrs_full)){var attr_key,attr_prefix=this.util.get_attribute();$.each(attrs_full,function(idx,attr){if(-1===attr.name.indexOf(attr_prefix))return!0;attr_key=attr.name.substr(attr_prefix.length+1),attrs[attr_key]=attr.value})}}return attrs},get_attributes:function(){return this.init_attributes(),this._attributes},get_attribute:function(key,def,enforce_type){if(this.util.is_set(def)||(def=null),!this.util.is_string(key))return def;this.util.is_bool(enforce_type)||(enforce_type=!0);var ret=this.has_attribute(key)?this.get_attributes()[key]:def;return enforce_type&&ret!==def&&null!==def&&!this.util.is_type(ret,$.type(def),!1)&&(this.util.is_scalar(def,!1)&&this.util.is_scalar(ret,!1)?this.util.is_string(def,!1)?ret=ret.toString():this.util.is_num(def,!1)&&!this.util.is_num(ret,!1)?(ret=this.util.is_int(def,!1)?parseInt(ret):parseFloat(ret),this.util.is_num(ret,!1)||(ret=def)):ret=this.util.is_bool(def,!1)?this.util.is_string(ret)||this.util.is_num(ret):def:ret=def),ret},call_attribute:function(attr,args){return attr=this.get_attribute(attr),this.util.is_func(attr)&&(args=Array.prototype.slice.call(arguments,1),attr=attr.apply(this,args)),attr},has_attribute:function(key){return this.util.is_string(key)&&key in this.get_attributes()},set_attributes:function(attributes,full){this.util.is_bool(full)||(full=!1),this.init_attributes(full),this.util.is_obj(attributes)&&$.extend(this._attributes,attributes)},set_attribute:function(key,val){return this.util.is_string(key)&&this.util.is_set(val)&&(this.get_attributes()[key]=val),val},dom_get_selector:function(element){return this.util.is_string(element)?"."+this.add_ns(element):""},dom_get_attribute:function(){return this.util.get_attribute(this._slug)},dom_set:function(el){return(el=$(el)).data(this.get_data_key(),this),this._reciprocal&&(this._dom=el),el},dom_get:function(element,options){var opts_default={init:!0,put:!1};(options=this.util.is_obj(options)?$.extend({},opts_default,options):opts_default).init&&!this.get_status("dom_init")&&(this.set_status("dom_init"),this.dom_init());var ret=this._dom;if(ret&&this.util.is_string(element)){var ch=$(ret).find(this.dom_get_selector(element));ch.length?ret=ch:(!0===options.put||this.util.is_obj(options.put))&&(ret=this.dom_put(element,options.put))}return $(ret)},dom_init:function(){},dom_put:function(element,content){var r=null;if(!this.dom_has()||!this.util.is_string(element))return $(r);var strip=["tag","content","success"],options={tag:"div",content:"",class:this.add_ns(element)};this.util.is_empty(content)||(this.util.is_type(content,jQuery,!1)||this.util.is_string(content,!1)?options.content=content:this.util.is_obj(content,!1)&&$.extend(options,content));for(var attrs=$.extend({},options),x=0;x<strip.length;x++)delete attrs[strip[x]];var d=this.dom_get();return(r=$(this.dom_get_selector(element),d)).length||(r=$(this.util.format("<%s />",options.tag),attrs).appendTo(d)).length&&this.util.is_method(options,"success")&&options.success.call(r,r),$(r).append(options.content),$(r)},dom_has:function(){return!!this.dom_get().length},get_data_key:function(){return this.get_ns()},on:function(event,fn,options){if(!this.util.is_string(event)||!this.util.is_func(fn)){var t=this,args=Array.prototype.slice.call(arguments,1);return this.util.is_array(event)?$.each(event,function(idx,val){t.on.apply(t,[val].concat(args))}):this.util.is_obj(event)&&$.each(event,function(ev,hdl){t.on.apply(t,[ev,hdl].concat(args))}),this}this.util.is_obj(options,!1)||(options={}),options=$.extend({},{clear:!1},options),this.util.is_obj(this._events,!1)||(this._events={});var es=this._events;return event in es&&this.util.is_obj(es[event],!1)&&!options.clear||(es[event]=[]),es[event].push(fn),this},trigger:function(event,data){var dfr=$.Deferred(),dfrs=[],t=this;if(this.util.is_array(event))return $.each(event,function(idx,val){dfrs.push(t.trigger(val,data))}),$.when.apply(t,dfrs).done(function(){dfr.resolve()}),dfr.promise();if(!(this.util.is_string(event)&&event in this._events))return dfr.resolve(),dfr.promise();var ev={type:event,data:null};return this.util.is_set(data)&&(ev.data=data),$.each(this._events[event],function(idx,fn){dfrs.push(fn.call(t,ev,t))}),$.when.apply(this,dfrs).done(function(){dfr.resolve()}),dfr.promise()}};View.Component=Component=SLB.Class.extend(Component);var Viewer={_slug:"viewer",_refs:{item:"Content_Item",theme:"Theme"},_reciprocal:!0,_attr_default:{loop:!0,animate:!0,autofit:!0,overlay_enabled:!0,overlay_opacity:"0.8",title_default:!1,container:null,slideshow_enabled:!0,slideshow_autostart:!1,slideshow_duration:2,slideshow_active:!1,slideshow_timer:null,labels:{close:"close",nav_prev:"&laquo; prev",nav_next:"next &raquo;",slideshow_start:"start slideshow",slideshow_stop:"stop slideshow",group_status:"Image %current% of %total%",loading:"loading"}},_attr_map:{theme:null,group_loop:"loop",ui_autofit:"autofit",ui_animate:"animate",ui_overlay_opacity:"overlay_opacity",ui_labels:"labels",ui_title_default:"title_default",slideshow_enabled:null,slideshow_autostart:null,slideshow_duration:null},item:null,item_queued:null,theme:null,item_working:null,active:!1,init:!1,open:!1,loading:!1,_hooks:function(){var t=this;this.on(["item-prev","item-next"],function(){t.trigger("item-change")}).on(["close","item-change"],function(){t.unload().done(function(){t.unlock()})})},get_item:function(){return this.get_component("item")},set_item:function(item){this.clear_item(!1);var i=this.set_component("item",item,function(item){return item.has_type()});return!this.util.is_empty(i)},clear_item:function(full){this.util.is_bool(full)||(full=!0);var item=this.get_item();item&&item.reset(),full&&this.clear_component("item")},get_theme:function(){var ret=this.get_component("theme",{check_attr:!1});return this.util.is_empty(ret)&&(ret=this.set_component("theme",new View.Theme(this))),ret},set_theme:function(theme){this.set_component("theme",theme)},lock:function(){return this.set_status("item_working",$.Deferred())},get_lock:function(simple,full){this.util.is_bool(simple)||(simple=!1),this.util.is_bool(full)||(full=!1);var s="item_working";if(simple)return this.get_status(s);var r=this.get_status(s,!0);return this.util.is_promise(r)||(r=this.lock()),full?r:r.promise()},is_locked:function(){return this.get_lock(!0)},unlock:function(){return this.get_lock(!1,!0).resolve()},set_active:function(mode){return this.util.is_bool(mode)||(mode=!0),this.set_status("active",mode)},is_active:function(){return this.get_status("active")},set_loading:function(mode){var dfr=$.Deferred();this.util.is_bool(mode)||(mode=!0),this.loading=mode,this.slideshow_active()&&this.slideshow_pause(mode);var m=mode?"addClass":"removeClass";return $(this.dom_get())[m]("loading"),mode?this.get_theme().transition("load").always(function(){dfr.resolve()}):dfr.resolve(),dfr.promise()},unset_loading:function(){return this.set_loading(!1)},get_loading:function(){return!!this.util.is_bool(this.loading)&&this.loading},is_loading:function(){return this.get_loading()},show:function(item){this.item_queued=item;var vt="theme_valid",valid=!0;if(this.has_attribute(vt)?valid=this.get_attribute(vt,!0):(valid=!(!this.get_theme()||""===this.get_theme().get_template().get_layout(!1)),this.set_attribute(vt,valid)),!valid)return this.close(),!1;var v=this,fin=function(){if(v.lock(),v.set_status("show_deferred",!1),!v.set_item(v.item_queued))return v.close(),!1;v.history_add(),v.set_active(),v.render()};this.is_locked()?this.get_status("show_deferred")||(this.set_status("show_deferred"),this.get_lock().always(function(){fin()})):fin()},history_handle:function(e){var state=e.originalEvent.state;if(this.util.is_string(state.item,!1))this.get_controller().get_item(state.item).show({event:e}),this.trigger("item-change");else{var count=this.history_get(!0);this.history_set(0),-1!==count&&this.close()}},history_get:function(full){return this.get_status("history_count",full)},history_set:function(val){return this.set_status("history_count",val)},history_add:function(){if(!history.pushState)return!1;var item=this.get_item(),opts=item.get_attribute("options_show"),count=this.history_get()?this.history_get(!0):0;if(this.util.in_obj(opts,"event")){var e=opts.event.originalEvent;this.util.in_obj(e,"state")&&this.util.in_obj(e.state,"count")&&(count=e.state.count)}else{var state={viewer:this.get_id(),item:null,count:count};count||history.replaceState(state,null),state.item=this.get_controller().save_item(item).get_id(),state.count=++count,history.pushState(state,"")}this.history_set(count)},history_reset:function(){var count=this.history_get(!0);count&&(this.history_set(-1),history.go(-1*count))},is_open:function(){return"none"!==this.dom_get().css("display")},render:function(){var v=this,thm=this.get_theme();v.dom_prep(),this.get_status("render-events")||(this.set_status("render-events"),thm.on("render-loading",function(ev,thm){var dfr=$.Deferred();if(!v.is_active())return dfr.reject(),dfr.promise();var set_pos=function(){v.dom_get().css("top",$(window).scrollTop())},always=function(){v.set_loading().always(function(){dfr.resolve()})};return v.is_open()?thm.transition("unload").fail(function(){set_pos(),thm.dom_get_tag("item","content").attr("style","")}).always(always):thm.transition("open").always(function(){always(),v.events_open(),v.open=!0}).fail(function(){set_pos(),v.get_overlay().show(),v.dom_get().show()}),dfr.promise()}).on("render-complete",function(ev,thm){if(!v.is_active())return!1;var d=v.dom_get(),classes=["item_single","item_multi"],ms=["addClass","removeClass"];v.get_item().get_group().is_single()||ms.reverse(),$.each(ms,function(idx,val){d[val](classes[idx])}),v.events_complete(),thm.transition("complete").fail(function(){if(v.get_attribute("autofit",!0)){var dims=$.extend({display:"inline-block"},thm.get_item_dimensions());thm.dom_get_tag("item","content").css(dims)}}).always(function(){v.unset_loading(),v.trigger("render-complete"),v.init=!0})})),thm.render()},dom_get_container:function(){var sel=this.get_attribute("container");this.util.is_empty(sel)&&(sel="#"+this.add_ns("wrap"));var c=$(sel);if(!c.length){var id=0===sel.indexOf("#")?sel.substr(1):sel;c=$("<div />",{id:id}).appendTo("body")}return c},dom_init:function(){var d=this.dom_set($("<div/>",{id:this.get_id(!0),class:this.get_ns()})).appendTo(this.dom_get_container()).hide(),thm=this.get_theme();d.addClass(thm.get_classes(" "));var v=this;this.get_status("render-init")||(this.set_status("render-init"),thm.on("render-init",function(ev){v.dom_put("layout",ev.data)})),thm.render(!0)},dom_prep:function(mode){var m=this.util.is_bool(mode)&&!mode?"removeClass":"addClass";$("html")[m](this.util.add_prefix("overlay"))},dom_restore:function(){this.dom_prep(!1)},get_layout:function(){return this.dom_get("layout",{put:{success:function(){$(this).hide()}}})},animation_enabled:function(){return this.get_attribute("animate",!0)},overlay_enabled:function(){var ov=this.get_attribute("overlay_enabled");return!!this.util.is_bool(ov)&&ov},get_overlay:function(){var o=null,v=this;return this.overlay_enabled()&&(o=this.dom_get("overlay",{put:{success:function(){$(this).hide().css("opacity",v.get_attribute("overlay_opacity"))}}})),$(o)},unload:function(){var dfr=$.Deferred();return this.get_theme().dom_get_tag("item").text(""),dfr.resolve(),dfr.promise()},reset:function(){this.dom_get().hide(),this.dom_restore(),this.history_reset(),this.clear_item(),this.set_active(!1),this.set_loading(!1),this.slideshow_stop(),this.keys_disable(),this.unlock()},get_labels:function(){return this.get_attribute("labels",{})},get_label:function(name){var lbls=this.get_labels();return name in lbls?lbls[name]:""},events_open:function(){if(this.keys_enable(),this.open)return!1;var l=this.get_layout();l.children().click(function(ev){ev.stopPropagation()});var v=this,close=function(){v.close()};l.click(close),this.get_overlay().click(close),this.trigger("events-open")},events_complete:function(){if(this.init)return!1;this.trigger("events-complete")},keys_enable:function(mode){this.util.is_bool(mode)||(mode=!0);var e=["keyup",this.util.get_prefix()].join("."),v=this;mode?$(document).on(e,function(ev){return v.keys_control(ev)}):$(document).off(e)},keys_disable:function(){this.keys_enable(!1)},keys_control:function(ev){var handlers={27:this.close,37:this.item_prev,39:this.item_next};if("rtl"===document.documentElement.getAttribute("dir")&&(handlers[37]=this.item_next,handlers[39]=this.item_prev),ev.which in handlers)return handlers[ev.which].call(this),!1},slideshow_enabled:function(){var o=this.get_attribute("slideshow_enabled");return!(!(this.util.is_bool(o)&&o&&this.get_item())||this.get_item().get_group().is_single())},slideshow_active:function(){return!(!this.slideshow_enabled()||!(this.get_attribute("slideshow_active")||!this.init&&this.get_attribute("slideshow_autostart")))},slideshow_clear_timer:function(){clearInterval(this.get_attribute("slideshow_timer"))},slideshow_set_timer:function(callback){this.set_attribute("slideshow_timer",setInterval(callback,1e3*this.get_attribute("slideshow_duration")))},slideshow_start:function(){if(!this.slideshow_enabled())return!1;this.set_attribute("slideshow_active",!0),this.dom_get().addClass("slideshow_active"),this.slideshow_clear_timer();var v=this;this.slideshow_set_timer(function(){v.slideshow_pause(),v.item_next()}),this.trigger("slideshow-start")},slideshow_stop:function(full){this.util.is_bool(full)||(full=!0),full&&(this.set_attribute("slideshow_active",!1),this.dom_get().removeClass("slideshow_active")),this.slideshow_clear_timer(),this.trigger("slideshow-stop")},slideshow_toggle:function(){if(!this.slideshow_enabled())return!1;this.slideshow_active()?this.slideshow_stop():this.slideshow_start(),this.trigger("slideshow-toggle")},slideshow_pause:function(mode){this.util.is_bool(mode)||(mode=!0),this.slideshow_active()&&(mode?this.slideshow_stop(!1):this.slideshow_start()),this.trigger("slideshow-pause")},slideshow_resume:function(){this.slideshow_pause(!1)},item_next:function(){var g=this.get_item().get_group(!0),v=this,ev="item-next",st=["events","viewer",ev].join("_");g.get_status(st)||(g.set_status(st),g.on(ev,function(e){v.trigger(e.type)})),g.show_next()},item_prev:function(){var g=this.get_item().get_group(!0),v=this,ev="item-prev",st=["events","viewer",ev].join("_");g.get_status(st)||(g.set_status(st),g.on(ev,function(){v.trigger(ev)})),g.show_prev()},close:function(){this.set_active(!1);var v=this,thm=this.get_theme();return thm.transition("unload").always(function(){thm.transition("close",!0).always(function(){v.reset(),v.trigger("close")})}).fail(function(){thm.dom_get_tag("item","content").attr("style","")}),!1}};View.Viewer=Component.extend(Viewer);var Group={_slug:"group",_reciprocal:!0,_refs:{current:"Content_Item"},current:null,selector:null,_hooks:function(){var t=this;this.on(["item-prev","item-next"],function(){t.trigger("item-change")})},get_selector:function(){return this.util.is_empty(this.selector)&&(this.selector=this.util.format('a[%s="%s"]',this.dom_get_attribute(),this.get_id())),this.selector},get_items:function(){var items=$(this.get_selector());return 0===items.length&&this.has_current()&&(items=this.get_current().dom_get()),items},get_item:function(idx){this.util.is_int(idx)||(idx=0);var items=this.get_items(),max=this.get_size()-1;return max<idx&&(idx=max),items.get(idx)},get_pos:function(item){return this.util.is_empty(item)&&(item=this.get_current()),this.util.is_type(item,View.Content_Item)?this.get_items().index(item.dom_get()):-1},has_current:function(){return!this.util.is_empty(this.get_current())},get_current:function(){return null===this.current||this.util.is_type(this.current,View.Content_Item)||(this.current=null),this.current},set_current:function(item){this.util.is_type(item,View.Content_Item)&&(this.current=item)},get_next:function(item){if(this.util.is_type(item,View.Content_Item)||(item=this.get_current()),1===this.get_size())return item;var next=null,pos=this.get_pos(item);return-1!==pos&&(0!==(pos=pos+1<this.get_size()?pos+1:0)||item.get_viewer().get_attribute("loop"))&&(next=this.get_item(pos)),next},get_prev:function(item){if(this.util.is_type(item,View.Content_Item)||(item=this.get_current()),1===this.get_size())return item;var prev=null,pos=this.get_pos(item);return-1===pos||0===pos&&!item.get_viewer().get_attribute("loop")||(0===pos&&(pos=this.get_size()),pos-=1,prev=this.get_item(pos)),prev},show_next:function(item){if(1<this.get_size()){var next=this.get_next(item);next||(this.util.is_type(item,View.Content_Item)||(item=this.get_current()),item.get_viewer().close());var i=this.get_controller().get_item(next);this.set_current(i),i.show(),this.trigger("item-next")}},show_prev:function(item){if(1<this.get_size()){var prev=this.get_prev(item);prev||(this.util.is_type(item,View.Content_Item)||(item=this.get_current()),item.get_viewer().close());var i=this.get_controller().get_item(prev);this.set_current(i),i.show(),this.trigger("item-prev")}},get_size:function(){return this.get_items().length},is_single:function(){return 1===this.get_size()}};View.Group=Component.extend(Group);var Content_Handler={_slug:"content_handler",_refs:{item:"Content_Item"},item:null,template:"",has_item:function(){return!this.util.is_empty(this.get_item())},get_item:function(){return this.get_component("item")},set_item:function(item){return this.set_component("item",item)},clear_item:function(){this.clear_component("item")},match:function(item){var m=this.get_attribute("match");if(!this.util.is_empty(m)){if(this.util.is_string(m)&&(m=new RegExp(m,"i"),this.set_attribute("match",m)),this.util.is_type(m,RegExp))return m.test(item.get_uri());if(this.util.is_func(m))return!!m.call(this,item)}return!1},load:function(item){var dfr=$.Deferred();return null===this.call_attribute("load",item,dfr)&&dfr.resolve(),dfr.promise()},render:function(item){var dfr=$.Deferred();return this.call_attribute("render",item,dfr),dfr.promise()}};View.Content_Handler=Component.extend(Content_Handler);var Content_Item={_slug:"content_item",_reciprocal:!0,_refs:{viewer:"Viewer",group:"Group",type:"Content_Handler"},_attr_default:{source:null,permalink:null,dimensions:null,title:"",group:null,internal:!1,output:null},group:null,viewer:null,type:null,data:null,loaded:null,_c:function(el){this.dom_set(el),this._super()},init_default_attributes:function(){this._super();var d=this.dom_get(),key=d.attr(this.util.get_attribute("asset"))||null,assets=this.get_controller().assets||null;if(this.util.is_string(key)){var attrs=[{},this._attr_default,{permalink:d.attr("href")}];if(this.util.is_obj(assets)){var t=this;attrs.push(function(key){var ret={};return key in assets&&t.util.is_obj(assets[key])&&(ret=assets[key]),ret}(key))}this._attr_default=$.extend.apply(this,attrs)}return this._attr_default},get_output:function(){var dfr=$.Deferred(),ret=this.get_attribute("output");if(this.util.is_string(ret))dfr.resolve(ret);else if(this.has_type()){var type=this.get_type(),item=this;type.render(this).done(function(output){item.set_output(output),dfr.resolve(output)})}else dfr.resolve("");return dfr.promise()},set_output:function(out){this.util.is_string(out,!1)&&this.set_attribute("output",out)},get_content:function(){return this.get_output()},get_uri:function(mode){-1===$.inArray(mode,["source","permalink"])&&(mode="source");var ret=this.get_attribute(mode);return this.util.is_string(ret)||(ret="source"===mode?this.get_attribute("permalink"):""),ret=ret.replace(/&(#38|amp);/,"&")},get_title:function(){if(this.has_attribute("title_cached"))return this.get_attribute("title_cached","");var title="",dom=this.dom_get();if(dom.length&&((title=dom.attr("title"))||(title=dom.closest("figure").find("figcaption").first().html()),title||(title=dom.closest("figure").find(".wp-caption-text").first().html())),!title)for(var props=["caption","title"],x=0;x<props.length&&(title=this.get_attribute(props[x],""),this.util.is_empty(title));x++);if(!title&&dom.length&&((title=dom.find("img").first().attr("alt"))||(title=dom.get(0).innerText.trim())),this.util.is_string(title,!1)||(title=""),!this.util.is_empty(title)&&!this.get_viewer().get_attribute("title_default")){var f=this.get_uri("source"),i=f.lastIndexOf("/");-1!==i&&(-1!==(i=(f=f.substr(i+1)).lastIndexOf("."))&&(f=f.substr(0,i)),title===f&&(title=""))}return this.set_attribute("title_cached",title),title},get_dimensions:function(){return $.extend({width:0,height:0},this.get_attribute("dimensions"),{})},set_data:function(data){this.data=data},get_data:function(){return this.data},gallery_type:function(){var ret=null,types={wp:".gallery-icon",ngg:".ngg-gallery-thumbnail"},dom=this.dom_get();for(var type in types)if(0<dom.parent(types[type]).length){ret=type;break}return ret},in_gallery:function(gType){var type=this.gallery_type();return null!==type&&(!this.util.is_string(gType)||gType===type)},get_viewer:function(){return this.get_component("viewer",{get_default:!0})},set_viewer:function(v){return this.set_component("viewer",v)},get_group:function(set_current){var g=this.get_component("group");return g||(g=this.set_component("group",new View.Group),set_current=!0),set_current&&g.set_current(this),g},set_group:function(g){this.util.is_string(g)&&(g=this.get_controller().get_group(g)),this.group=!!this.util.is_type(g,View.Group)&&g},get_type:function(){var t=this.get_component("type",{check_attr:!1});return t||(t=this.set_type(this.get_controller().get_content_handler(this))),t},set_type:function(type){return this.set_component("type",type)},has_type:function(){return!this.util.is_empty(this.get_type())},show:function(options){if(!this.has_type())return!1;this.set_attribute("options_show",options);var v=this.get_viewer();return this.load(),v.show(this)},load:function(){return this.util.is_promise(this.loaded)||(this.loaded=this.get_type().load(this)),this.loaded.promise()},reset:function(){this.set_attribute("options_show",null)}};View.Content_Item=Component.extend(Content_Item);var Modeled_Component={_slug:"modeled_component",get_attribute:function(key,def,check_model,enforce_type){if(!this.util.is_string(key))return this._super(key,def,enforce_type);this.util.is_bool(check_model)||(check_model=!0);var ret=null;if(check_model){var m=this.get_ancestor(key,!1);this.util.in_obj(m,key)&&(ret=m[key])}return null===ret&&(ret=this._super(key,def,enforce_type)),ret},get_attribute_recursive:function(key,def,enforce_type){var ret=this.get_attribute(key,def,!0,enforce_type);if(this.util.is_obj(ret)){var models=this.get_ancestors(!1);ret=[ret];var t=this;$.each(models,function(idx,model){key in model&&t.util.is_obj(model[key])&&ret.push(model[key])}),ret.push({}),ret=$.extend.apply($,ret.reverse())}return ret},set_attribute:function(key,val,use_model){if(!this.util.is_string(key)||!this.util.is_set(val))return!1;(this.util.is_bool(use_model)||this.util.is_obj(use_model)||(use_model=!0),use_model)?(this.util.is_obj(use_model)?use_model:this.get_model())[key]=val:this._super(key,val);return val},get_model:function(){var m=this.get_attribute("model",null,!1);return this.util.is_obj(m)||(m={},this.set_attribute("model",m,!1)),m},has_model:function(){return!this.util.is_empty(this.get_model())},in_model:function(key){return!!this.util.in_obj(this.get_model(),key)},get_ancestors:function(inc_current){for(var ret=[],m=this.get_model();this.util.is_obj(m);)ret.push(m),m=this.util.in_obj(m,"parent")&&this.util.is_obj(m.parent)?m.parent:null;return inc_current||ret.shift(),ret},get_ancestor:function(attr,safe_mode){if(!this.util.is_string(attr))return!1;this.util.is_bool(safe_mode)||(safe_mode=!0);for(var mcurr=this.get_model(),m=mcurr,found=!1;this.util.is_obj(m);){if(this.util.in_obj(m,attr)&&!this.util.is_empty(m[attr])){found=!0;break}m=this.util.in_obj(m,"parent")?m.parent:null}return found||(safe_mode?(this.util.is_empty(m)&&(m=mcurr),this.util.in_obj(m,attr)||(m[attr]=null)):m=null),m}};Modeled_Component=Component.extend(Modeled_Component);var Theme={_slug:"theme",_refs:{viewer:"Viewer",template:"Template"},_models:{},_attr_default:{template:null,model:null},viewer:null,template:null,_c:function(id,attributes,viewer){1===arguments.length&&this.util.is_type(id,View.Viewer)&&(viewer=id,id=null),this._super(id,attributes),this.set_viewer(viewer),this.set_model(id)},get_viewer:function(){return this.get_component("viewer",{check_attr:!1,get_default:!0})},set_viewer:function(v){return this.set_component("viewer",v)},get_template:function(){var ret=this.get_component("template");if(this.util.is_empty(ret)){var attr={theme:this,model:this.get_model()};ret=this.set_component("template",new View.Template(attr))}return ret},get_tags:function(name,prop){return this.get_template().get_tags(name,prop)},dom_get_tag:function(tag,prop){return $(this.get_template().dom_get_tag(tag,prop))},get_tag_selector:function(name,prop){return this.get_template().get_tag_selector(name,prop)},get_models:function(){return this._models},get_model:function(id){var ret=null;if(!this.util.is_set(id)&&this.util.is_obj(this.get_attribute("model",null,!1)))ret=this._super();else{var models=this.get_models();this.util.is_string(id)||(id=this.get_controller().get_option("theme_default")),this.util.in_obj(models,id)||(id=$.map(models,function(v,key){return key})[0]),ret=models[id]}return ret},set_model:function(id){this.set_attribute("model",this.get_model(id),!1)},get_classes:function(rtype){var cls=[],thm=this,models=this.get_ancestors(!0);return $.each(models,function(idx,model){cls.push(thm.add_ns(model.id))}),this.util.is_string(rtype)&&(cls=cls.join(rtype)),cls},get_measurement:function(attr,def){var meas=null;if(!this.util.is_string(attr))return meas;this.util.is_obj(def,!1)||(def={});var attr_cache=this.util.format("%s_cache",attr),cache=this.get_attribute(attr_cache,{},!1),status="_status",item=this.get_viewer().get_item(),w=$(window);status in cache&&this.util.is_obj(cache._status)&&cache._status.width===w.width()&&cache._status.height===w.height()||(cache={}),this.util.is_empty(cache)&&(cache._status={width:w.width(),height:w.height(),index:[]});var pos=$.inArray(item,cache._status.index);return-1!==pos&&pos in cache&&(meas=cache[pos]),this.util.is_obj(meas)||(meas=this.call_attribute(attr),this.util.is_obj(meas)||(meas=this.get_measurement_default(attr))),meas=this.util.is_obj(meas)?$.extend({},def,meas):def,cache[pos=cache._status.index.push(item)-1]=meas,this.set_attribute(attr_cache,cache,!1),$.extend({},meas)},get_measurement_default:function(attr){return this.util.is_string(attr)?(attr=this.util.format("get_%s_default",attr),this.util.in_obj(this,attr)?(attr=this[attr],this.util.is_func(attr)&&(attr=attr.call(this))):attr=null,attr):null},get_offset:function(){return this.get_measurement("offset",{width:0,height:0})},get_offset_default:function(){var offset={width:0,height:0},v=this.get_viewer(),vn=v.dom_get(),vc=vn.clone().attr("id","").css({visibility:"hidden",position:"absolute",top:""}).removeClass("loading").appendTo(vn.parent()),l=vc.find(v.dom_get_selector("layout"));if(l.length){l.find("*").css({width:"",height:"",display:""});var tags=this.get_tags("item","content");if(tags.length){var offset_item=v.get_item().get_dimensions();tags=$(l.find(tags[0].get_selector("full")).get(0)).css({width:offset_item.width,height:offset_item.height}),$.each(offset_item,function(key,val){offset[key]=-1*val})}offset.width+=l.width(),offset.height+=l.height(),$.each(offset,function(key,val){val<0&&(offset[key]=0)})}return vc.empty().remove(),offset},get_margin:function(){return this.get_measurement("margin",{width:0,height:0})},get_item_dimensions:function(){var v=this.get_viewer(),dims=v.get_item().get_dimensions();if(v.get_attribute("autofit",!1)){var margin=this.get_margin(),offset=this.get_offset();offset.height+=margin.height,offset.width+=margin.width;var max={width:$(window).width(),height:$(window).height()};max.width>offset.width&&(max.width-=offset.width),max.height>offset.height&&(max.height-=offset.height);var factor=Math.min(max.width/dims.width,max.height/dims.height);factor<1&&$.each(dims,function(key){dims[key]=Math.round(dims[key]*factor)})}return $.extend({},dims)},get_dimensions:function(){var dims=this.get_item_dimensions(),offset=this.get_offset();return $.each(dims,function(key){dims[key]+=offset[key]}),dims},get_breakpoints:function(){return this.get_attribute_recursive("breakpoints")},get_breakpoint:function(target){var ret=0;if(this.util.is_string(target)){var b=this.get_attribute_recursive("breakpoints");this.util.is_obj(b)&&target in b&&(ret=b[target])}return ret},render:function(init){var thm=this,tpl=this.get_template(),st="events_render";this.get_status(st)||(this.set_status(st),tpl.on(["render-init","render-loading","render-complete"],function(ev){return thm.trigger(ev.type,ev.data)})),tpl.render(init)},transition:function(event,clear_queue){var dfr=null,attr="transition",v=this.get_viewer(),fx_temp=null,anim_on=v.animation_enabled();if(v.get_attribute(attr,!0)&&this.util.is_string(event)){clear_queue&&v.get_layout().find("*").each(function(){for(var el=$(this);el.queue().length;)el.stop(!1,!0)});var trns,attr_set=[attr,"set"].join("_");if(this.get_attribute(attr_set))trns=this.get_attribute(attr,{});else{var models=this.get_ancestors(!0);trns=[],this.set_attribute(attr_set,!0);var thm=this;$.each(models,function(idx,model){attr in model&&thm.util.is_obj(model[attr])&&trns.push(model[attr])}),trns.push({}),trns=this.set_attribute(attr,$.extend.apply($,trns.reverse()))}this.util.is_method(trns,event)&&(anim_on||(fx_temp=$.fx.off,$.fx.off=!0),dfr=trns[event].call(this,v,$.Deferred()))}return this.util.is_promise(dfr)||(dfr=$.Deferred()).reject(),dfr.always(function(){null!==fx_temp&&($.fx.off=fx_temp)}),dfr.promise()}};View.Theme=Modeled_Component.extend(Theme);var Template={_slug:"template",_reciprocal:!0,_refs:{theme:"Theme"},_attr_default:{layout_uri:"",layout_raw:"",layout_parsed:"",tags:null,model:null},theme:null,_c:function(attributes){this._super("",attributes)},_hooks:function(){this.on("dom_init",function(ev){var tags=this.get_tags(null,null,!0),names=[],t=this;$.each(tags,function(idx,tag){var name=tag.get_name();-1===$.inArray(name,names)&&(names.push(name),tag.get_handler().trigger(ev.type,{template:t}))})})},get_theme:function(){return this.get_component("theme")},render:function(init){var v=this.get_theme().get_viewer();if(this.util.is_bool(init)||(init=!1),init)this.trigger("render-init",this.dom_get());else{if(!v.is_active())return!1;var item=v.get_item();if(!this.util.is_type(item,View.Content_Item))return v.close(),!1;if(v.is_active()&&this.has_tags()){var loading_promise=this.trigger("render-loading"),tpl=this,tags=this.get_tags(),tag_promises=[];$.when(item.load(),loading_promise).done(function(){return!!v.is_active()&&($.each(tags,function(idx,tag){if(!v.is_active())return!1;tag_promises.push(tag.render(item).done(function(r){if(!v.is_active())return!1;r.tag.dom_get().html(r.output)}))}),!!v.is_active()&&void $.when.apply($,tag_promises).done(function(){tpl.trigger("render-complete")}))})}}},get_layout:function(parsed){return this.util.is_bool(parsed)||(parsed=!0),parsed?this.parse_layout():this.get_attribute("layout_raw","")},parse_layout:function(){var a="layout_parsed",ret=this.get_attribute(a);return this.util.is_string(ret)||(ret=this.sanitize_layout(this.get_layout(!1)),ret=this.parse_tags(ret),this.set_attribute(a,ret)),ret},sanitize_layout:function(l){if(this.util.is_empty(l))return l;var rtype=this.util.is_string(l)?"string":null,dom=$(l),tag_temp=this.get_tag_temp(),cls=tag_temp.get_class(),cls_new=["x",cls].join("_");switch($(tag_temp.get_selector(),dom).each(function(){$(this).removeClass(cls).addClass(cls_new)}),rtype){case"string":l=dom=dom.wrap("<div />").parent().html();break;default:l=dom}return l},parse_tags:function(l){if(!this.util.is_string(l))return"";for(var match,re=/\{{2}\s*(\w.*?)\s*\}{2}/gim;match=re.exec(l);)l=l.substring(0,match.index)+this.get_tag_container(match[1])+l.substring(match.index+match[0].length);return l},get_tag_container:function(tag){var attr=this.get_tag_attribute();return this.util.format('<span %s="%s"></span>',attr,encodeURI(tag))},get_tag_attribute:function(){return this.get_tag_temp().dom_get_attribute()},get_tag:function(idx){var ret=null;if(this.has_tags()){var tags=this.get_tags();(!this.util.is_int(idx)||idx<0||idx>=tags.length)&&(idx=0),ret=tags[idx]}return ret},get_tags:function(name,prop,isolate){this.util.is_bool(isolate)||(isolate=!1);var a="tags",tags=this.get_attribute(a);if(!this.util.is_array(tags)){tags=[];var d=this.dom_get(),attr=this.get_tag_attribute(),nodes=$(d).find("["+attr+"]");$(nodes).each(function(){var el=$(this),tag=new View.Template_Tag(decodeURI(el.attr(attr)));tag.has_handler()&&(tags.push(tag),isolate||(tag.dom_set(el),el.addClass(tag.get_classes(" ")))),isolate||el.removeAttr(attr)}),isolate||this.set_attribute(a,tags,!1)}if(!this.util.is_empty(tags)&&this.util.is_string(name)){this.util.is_string(prop)||(prop=!1);for(var tags_filtered=[],tc=null,x=0;x<tags.length;x++)name===(tc=tags[x]).get_name()&&(prop&&prop!==tc.get_prop()||tags_filtered.push(tc));tags=tags_filtered}return this.util.is_array(tags,!1)?tags:[]},has_tags:function(){return 0<this.get_tags().length},get_tag_temp:function(){return this.get_controller().get_component_temp(View.Template_Tag)},get_tag_selector:function(name,prop){this.util.is_string(name)||(name=""),this.util.is_string(prop)||(prop="");var tag=this.get_tag_temp();return tag.set_attribute("name",name),tag.set_attribute("prop",prop),tag.get_selector("full")},dom_init:function(){this.dom_set(this.get_layout()),this.trigger("dom_init")},dom_get_tag:function(tag,prop){var ret=$(),tags=this.get_tags(tag,prop);if(tags.length){var level=null;this.util.is_string(tag)&&(level=this.util.is_string(prop)?"full":"tag");var sel="."+tags[0].get_class(level);ret=this.dom_get().find(sel)}return ret}};View.Template=Modeled_Component.extend(Template);var Template_Tag={_slug:"template_tag",_reciprocal:!0,_attr_default:{name:null,prop:null,match:null},handlers:{},_c:function(tag_match){this.parse(tag_match)},parse:function(tag_match){if(!this.util.is_string(tag_match))return!1;var part,parts=tag_match.split("|");if(!parts.length)return null;var attrs={name:null,prop:null,match:tag_match};attrs.name=parts[0],-1!==attrs.name.indexOf(".")&&(attrs.name=attrs.name.split(".",2),attrs.prop=attrs.name[1],attrs.name=attrs.name[0]);for(var x=1;x<parts.length;x++)1<(part=parts[x].split(":",1)).length&&!(part[0]in attrs)&&(attrs[part[0]]=part[1]);this.set_attributes(attrs,!0)},render:function(item){var tag=this;return tag.get_handler().render(item,tag).pipe(function(output){return{tag:tag,output:output}})},get_name:function(){return this.get_attribute("name")},get_prop:function(){return this.get_attribute("prop")},get_handler:function(){return this.has_handler()?this.handlers[this.get_name()]:new View.Template_Tag_Handler("")},has_handler:function(){return this.get_name()in this.handlers},get_classes:function(rtype){var cls=[this.get_class(),this.get_class("tag"),this.get_class("full")];return this.util.is_string(rtype)&&(cls=cls.join(rtype)),cls},get_class:function(level){var cls="";switch(level){case"tag":cls=this.get_name();break;case"full":var i,parts=[this.get_name(),this.get_prop()],a=[];for(i=0;i<parts.length;i++)this.util.is_string(parts[i])&&a.push(parts[i]);cls=a.join("_")}return this.util.is_string(cls)?this.add_ns(cls):this.get_ns()},get_selector:function(level){var ret=this.get_class(level);return ret=this.util.is_string(ret)?"."+ret:""}};View.Template_Tag=Component.extend(Template_Tag);var Template_Tag_Handler={_slug:"template_tag_handler",_attr_default:{supports_modifiers:!1,dynamic:!1,props:{}},render:function(item,instance){var dfr=$.Deferred();return this.call_attribute("render",item,instance,dfr),dfr.promise()},add_prop:function(prop,fn){var a="props",props=this.get_attribute(a);if(!this.util.is_string(prop)||!this.util.is_func(fn))return!1;this.util.is_obj(props,!1)||(props={}),props[prop]=fn,this.set_attribute(a,props)},handle_prop:function(prop,item,instance){var props=this.get_attribute("props");return this.util.is_obj(props)&&prop in props&&this.util.is_func(props[prop])?props[prop].call(this,item,instance):item.get_viewer().get_label(prop)}};View.Template_Tag_Handler=Component.extend(Template_Tag_Handler),View=SLB.attach("View",View)}(jQuery);
client/sass/admin.scss CHANGED
@@ -28,4 +28,27 @@
28
  .slb_notice {
29
  color: #f00;
30
  font-weight: bold;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
28
  .slb_notice {
29
  color: #f00;
30
  font-weight: bold;
31
+ }
32
+
33
+ .slb {
34
+ .columns-2 {
35
+ margin-right: 300px;
36
+ .postbox-container {
37
+ float: left;
38
+ width: 100%;
39
+ }
40
+ .content-secondary {
41
+ margin-right: -300px;
42
+ width: 280px;
43
+ float: right;
44
+ }
45
+ }
46
+ }
47
+
48
+ .slb_admin_action_reset {
49
+ color: #a00;
50
+ &:hover {
51
+ color: #dc3232;
52
+ border: none;
53
+ }
54
  }
client/sass/app.scss ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ html.slb_overlay {
2
+ object,embed,iframe {
3
+ visibility: hidden;
4
+ }
5
+ #slb_viewer_wrap {
6
+ object,embed,iframe {
7
+ visibility: visible;
8
+ }
9
+ }
10
+ }
content-handlers/image/handler.image.js DELETED
@@ -1,29 +0,0 @@
1
- (function($) {
2
- return {
3
- render: function(item) {
4
- var dfr = $.Deferred();
5
- //Create image object
6
- var img = new Image();
7
- var type = this;
8
- //Set load event
9
- var handler = function(e) {
10
- //Save Data
11
- item.set_data(img);
12
- //Set attributes
13
- var dim = {'width': img.width, 'height': img.height};
14
- item.set_attribute('dimensions', dim);
15
- //Build output
16
- var out = $('<img />', {'src': item.get_uri()});
17
- //Resolve deferred
18
- dfr.resolve(out);
19
- };
20
-
21
- //Attach event handler
22
- $(img).on('load', function(e) { handler(e); });
23
- //Load image
24
- img.src = item.get_uri();
25
- //Return promise
26
- return dfr.promise();
27
- }
28
- }
29
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
content-handlers/image/js/dev/handler.image.js ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ if ( !!window.SLB && SLB.has_child('View.extend_content_handler') ) {(function($) {
2
+ SLB.View.extend_content_handler('image', {
3
+ /**
4
+ * Render images
5
+ * @param obj item Content Item
6
+ * @param obj dfr Promise for rendering process
7
+ * @return obj Promise for rendering process (Resolved when content is loaded)
8
+ */
9
+ render: function(item, dfr) {
10
+ // Create image object
11
+ var img = new Image();
12
+ // Set load event
13
+ var handler = function() {
14
+ // Save Data
15
+ item.set_data(img);
16
+ // Set attributes
17
+ item.set_attribute('dimensions', {'width': img.width, 'height': img.height});
18
+ // Build output
19
+ var out = $('<img />', {'src': item.get_uri()});
20
+ // Resolve deferred
21
+ dfr.resolve(out);
22
+ };
23
+
24
+ // Attach event handler
25
+ $(img).on('load', function(e) { handler(e); });
26
+ // Load image
27
+ img.src = item.get_uri();
28
+ // Return promise
29
+ return dfr.promise();
30
+ }
31
+ });
32
+ })(jQuery);
33
+ }
content-handlers/image/js/prod/handler.image.js ADDED
@@ -0,0 +1 @@
 
1
+ window.SLB&&SLB.has_child("View.extend_content_handler")&&!function($){SLB.View.extend_content_handler("image",{render:function(item,dfr){var img=new Image,handler=function(){item.set_data(img),item.set_attribute("dimensions",{width:img.width,height:img.height});var out=$("<img />",{src:item.get_uri()});dfr.resolve(out)};return $(img).on("load",function(e){handler(e)}),img.src=item.get_uri(),dfr.promise()}})}(jQuery);
controller.php ADDED
@@ -0,0 +1,1654 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Controller
4
+ * @package Simple Lightbox
5
+ * @author Archetyped
6
+ */
7
+ class SLB_Lightbox extends SLB_Base {
8
+
9
+ /*-** Properties **-*/
10
+
11
+ protected $model = true;
12
+
13
+ /**
14
+ * Fields
15
+ * @var SLB_Fields
16
+ */
17
+ public $fields = null;
18
+
19
+ /**
20
+ * Themes collection
21
+ * @var SLB_Themes
22
+ */
23
+ var $themes = null;
24
+
25
+ /**
26
+ * Content types
27
+ * @var SLB_Content_Handlers
28
+ */
29
+ var $handlers = null;
30
+
31
+ /**
32
+ * Template tags
33
+ * @var SLB_Template_Tags
34
+ */
35
+ var $template_tags = null;
36
+
37
+ /**
38
+ * Collection of processed media items for output to client
39
+ * > Key (string) Attachment URI
40
+ * > Value (assoc-array) Attachment properties (url, etc.)
41
+ * > source: Source URL
42
+ * @var array
43
+ */
44
+ var $media_items = array();
45
+
46
+ /**
47
+ * Collection of unprocessed media items
48
+ * Multi-dimensional array
49
+ * > props (array) Media properties indexed by ID
50
+ * > Key: (string) Unique ID (system-generated)
51
+ * > Value: (object) Media properties
52
+ * > type: (string) Item type (Default: null)
53
+ * > id: (int) WP item ID (Default: null)
54
+ * > uri (array) Index of cached URIs
55
+ * > Key: (string) Item URI
56
+ * > Value: (string) Item ID (pointer to item in `id` array)
57
+ * @var array
58
+ */
59
+ private $media_items_raw = array( 'props' => array(), 'uri' => array() );
60
+
61
+ /**
62
+ * Manage excluded content
63
+ * @var object
64
+ */
65
+ private $exclude = null;
66
+
67
+ private $groups = array (
68
+ 'auto' => 0,
69
+ 'manual' => array(),
70
+ );
71
+
72
+ /**
73
+ * Validated URIs
74
+ * Caches validation of parsed URIs
75
+ * > Key: URI
76
+ * > Value: (bool) TRUE if valid
77
+ * @var array
78
+ */
79
+ private $validated_uris = array();
80
+
81
+ /* Widget properties */
82
+
83
+ /**
84
+ * Used to track if widget is currently being processed or not
85
+ * Set to Widget ID currently being processed
86
+ * @var bool|string
87
+ */
88
+ private $widget_processing = false;
89
+
90
+ /**
91
+ * Parameters for widget being processed
92
+ * @param array
93
+ */
94
+ private $widget_processing_params = null;
95
+
96
+ /**
97
+ * Manage nested widget processing
98
+ * Used to avoid premature widget output
99
+ * @var int
100
+ */
101
+ private $widget_processing_level = 0;
102
+
103
+ /**
104
+ * Constructor
105
+ */
106
+ public function __construct() {
107
+ parent::__construct();
108
+ // Init instances
109
+ $this->fields = new SLB_Fields();
110
+ $this->themes = new SLB_Themes($this);
111
+ if ( !is_admin() ) {
112
+ $this->template_tags = new SLB_Template_Tags($this);
113
+ }
114
+ }
115
+
116
+ /* Init */
117
+
118
+ public function _init() {
119
+ parent::_init();
120
+ $this->util->do_action('init');
121
+ }
122
+
123
+ /**
124
+ * Declare client files (scripts, styles)
125
+ * @uses parent::_client_files()
126
+ * @return void
127
+ */
128
+ protected function _client_files($files = null) {
129
+ $js_path = 'client/js/';
130
+ $js_path .= ( SLB_DEV ) ? 'dev' : 'prod';
131
+ $files = array (
132
+ 'scripts' => array (
133
+ 'core' => array (
134
+ 'file' => "$js_path/lib.core.js",
135
+ 'deps' => 'jquery',
136
+ 'enqueue' => false,
137
+ 'in_footer' => true,
138
+ ),
139
+ 'view' => array (
140
+ 'file' => "$js_path/lib.view.js",
141
+ 'deps' => array('[core]'),
142
+ 'context' => array( array('public', $this->m('is_request_valid')) ),
143
+ 'in_footer' => true,
144
+ ),
145
+ ),
146
+ 'styles' => array (
147
+ 'core' => array (
148
+ 'file' => 'client/css/app.css',
149
+ 'context' => array('public'),
150
+ )
151
+ )
152
+ );
153
+ parent::_client_files($files);
154
+ }
155
+
156
+ /**
157
+ * Register hooks
158
+ * @uses parent::_hooks()
159
+ */
160
+ protected function _hooks() {
161
+ parent::_hooks();
162
+
163
+ /* Admin */
164
+ add_action('admin_menu', $this->m('admin_menus'));
165
+ $this->util->add_filter('admin_plugin_row_meta_support', $this->m('admin_plugin_row_meta_support'));
166
+
167
+ /* Init */
168
+ add_action('wp', $this->m('_hooks_init'));
169
+ }
170
+
171
+ /**
172
+ * Init Hooks
173
+ */
174
+ public function _hooks_init() {
175
+ if ( $this->is_enabled() ) {
176
+ $priority = $this->util->priority('low');
177
+
178
+ // Init lightbox
179
+ add_action('wp_footer', $this->m('client_footer'));
180
+ $this->util->add_action('footer_script', $this->m('client_init'), 1);
181
+ $this->util->add_filter('footer_script', $this->m('client_script_media'), 2);
182
+ // Link activation
183
+ add_filter('the_content', $this->m('activate_links'), $priority);
184
+ add_filter('get_post_galleries', $this->m('activate_galleries'), $priority);
185
+ $this->util->add_filter('post_process_links', $this->m('activate_groups'), 11);
186
+ $this->util->add_filter('validate_uri_regex', $this->m('validate_uri_regex_default'), 1);
187
+ // Content exclusion
188
+ $this->util->add_filter('pre_process_links', $this->m('exclude_content'));
189
+ $this->util->add_filter('pre_exclude_content', $this->m('exclude_shortcodes'));
190
+ $this->util->add_filter('post_process_links', $this->m('restore_excluded_content'));
191
+
192
+ // Grouping
193
+ if ( $this->options->get_bool('group_post') ) {
194
+ $this->util->add_filter('get_group_id', $this->m('post_group_id'), 1);
195
+ }
196
+
197
+ // Shortcode grouping
198
+ if ( $this->options->get_bool('group_gallery') ) {
199
+ add_filter('the_content', $this->m('group_shortcodes'), 1);
200
+ }
201
+
202
+ // Widgets
203
+ if ( $this->options->get_bool('enabled_widget') ) {
204
+ add_action('dynamic_sidebar_before', $this->m('widget_process_nested'));
205
+ add_action('dynamic_sidebar', $this->m('widget_process_start'), PHP_INT_MAX);
206
+ add_filter('dynamic_sidebar_params', $this->m('widget_process_inter'), PHP_INT_MAX);
207
+ add_action('dynamic_sidebar_after', $this->m('widget_process_finish'), PHP_INT_MAX - 1);
208
+ add_action('dynamic_sidebar_after', $this->m('widget_process_nested_finish'), PHP_INT_MAX);
209
+ } else {
210
+ add_action('dynamic_sidebar_before', $this->m('widget_block_start'));
211
+ add_action('dynamic_sidebar_after', $this->m('widget_block_finish'));
212
+ }
213
+
214
+ // Menus
215
+ if ( $this->options->get_bool('enabled_menu') ) {
216
+ add_filter('wp_nav_menu', $this->m('menu_process'), $priority, 2);
217
+ }
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Add post ID to link group ID
223
+ * @uses `SLB::get_group_id` filter
224
+ * @param array $group_segments Group ID segments
225
+ * @return array Modified group ID segments
226
+ */
227
+ public function post_group_id($group_segments) {
228
+ if ( in_the_loop() ) {
229
+ // Prepend post ID to group ID
230
+ $post = get_post();
231
+ if ( $post ) {
232
+ array_unshift($group_segments, $post->ID);
233
+ }
234
+ }
235
+ return $group_segments;
236
+ }
237
+
238
+ /**
239
+ * Init options
240
+ */
241
+ protected function _options() {
242
+ // Setup options
243
+ $opts = array (
244
+ 'groups' => array (
245
+ 'activation' => array ( 'title' => __('Activation', 'simple-lightbox'), 'priority' => 10),
246
+ 'grouping' => array ( 'title' => __('Grouping', 'simple-lightbox'), 'priority' => 20),
247
+ 'ui' => array ( 'title' => __('UI', 'simple-lightbox'), 'priority' => 30),
248
+ 'labels' => array ( 'title' => __('Labels', 'simple-lightbox'), 'priority' => 40),
249
+ ),
250
+ 'items' => array (
251
+ 'enabled' => array('title' => __('Enable Lightbox Functionality', 'simple-lightbox'), 'default' => true, 'group' => array('activation', 10)),
252
+ 'enabled_home' => array('title' => __('Enable on Home page', 'simple-lightbox'), 'default' => true, 'group' => array('activation', 20)),
253
+ 'enabled_post' => array('title' => __('Enable on Single Posts', 'simple-lightbox'), 'default' => true, 'group' => array('activation', 30)),
254
+ 'enabled_page' => array('title' => __('Enable on Pages', 'simple-lightbox'), 'default' => true, 'group' => array('activation', 40)),
255
+ 'enabled_archive' => array('title' => __('Enable on Archive Pages (tags, categories, etc.)', 'simple-lightbox'), 'default' => true, 'group' => array('activation', 50)),
256
+ 'enabled_widget' => array('title' => __('Enable for Widgets', 'simple-lightbox'), 'default' => false, 'group' => array('activation', 60)),
257
+ 'enabled_menu' => array('title' => __('Enable for Menus', 'simple-lightbox'), 'default' => false, 'group' => array('activation', 60)),
258
+ 'group_links' => array('title' => __('Group items (for displaying as a slideshow)', 'simple-lightbox'), 'default' => true, 'group' => array('grouping', 10)),
259
+ 'group_post' => array('title' => __('Group items by Post (e.g. on pages with multiple posts)', 'simple-lightbox'), 'default' => true, 'group' => array('grouping', 20)),
260
+ 'group_gallery' => array('title' => __('Group gallery items separately', 'simple-lightbox'), 'default' => false, 'group' => array('grouping', 30)),
261
+ 'group_widget' => array('title' => __('Group widget items separately', 'simple-lightbox'), 'default' => false, 'group' => array('grouping', 40)),
262
+ 'group_menu' => array('title' => __('Group menu items separately', 'simple-lightbox'), 'default' => false, 'group' => array('grouping', 50)),
263
+ 'ui_autofit' => array('title' => __('Resize lightbox to fit in window', 'simple-lightbox'), 'default' => true, 'group' => array('ui', 10), 'in_client' => true),
264
+ 'ui_animate' => array('title' => __('Enable animations', 'simple-lightbox'), 'default' => true, 'group' => array('ui', 20), 'in_client' => true),
265
+ 'slideshow_autostart' => array('title' => __('Start Slideshow Automatically', 'simple-lightbox'), 'default' => true, 'group' => array('ui', 30), 'in_client' => true),
266
+ 'slideshow_duration' => array('title' => __('Slide Duration (Seconds)', 'simple-lightbox'), 'default' => '6', 'attr' => array('size' => 3, 'maxlength' => 3), 'group' => array('ui', 40), 'in_client' => true),
267
+ 'group_loop' => array('title' => __('Loop through items', 'simple-lightbox'),'default' => true, 'group' => array('ui', 50), 'in_client' => true),
268
+ 'ui_overlay_opacity' => array('title' => __('Overlay Opacity (0 - 1)', 'simple-lightbox'), 'default' => '0.8', 'attr' => array('size' => 3, 'maxlength' => 3), 'group' => array('ui', 60), 'in_client' => true),
269
+ 'ui_title_default' => array('title' => __('Enable default title', 'simple-lightbox'), 'default' => false, 'group' => array('ui', 70), 'in_client' => true),
270
+ 'txt_loading' => array('title' => __('Loading indicator', 'simple-lightbox'), 'default' => 'Loading', 'group' => array('labels', 20)),
271
+ 'txt_close' => array('title' => __('Close button', 'simple-lightbox'), 'default' => 'Close', 'group' => array('labels', 10)),
272
+ 'txt_nav_next' => array('title' => __('Next Item button', 'simple-lightbox'), 'default' => 'Next', 'group' => array('labels', 30)),
273
+ 'txt_nav_prev' => array('title' => __('Previous Item button', 'simple-lightbox'), 'default' => 'Previous', 'group' => array('labels', 40)),
274
+ 'txt_slideshow_start' => array('title' => __('Start Slideshow button', 'simple-lightbox'), 'default' => 'Start slideshow', 'group' => array('labels', 50)),
275
+ 'txt_slideshow_stop' => array('title' => __('Stop Slideshow button', 'simple-lightbox'),'default' => 'Stop slideshow', 'group' => array('labels', 60)),
276
+ 'txt_group_status' => array('title' => __('Slideshow status format', 'simple-lightbox'), 'default' => 'Item %current% of %total%', 'group' => array('labels', 70))
277
+ ),
278
+ 'legacy' => array (
279
+ 'header_activation' => null,
280
+ 'header_enabled' => null,
281
+ 'header_strings' => null,
282
+ 'header_ui' => null,
283
+ 'activate_attachments' => null,
284
+ 'validate_links' => null,
285
+ 'enabled_compat' => null,
286
+ 'enabled_single' => array('enabled_post', 'enabled_page'),
287
+ 'enabled_caption' => null,
288
+ 'enabled_desc' => null,
289
+ 'ui_enabled_caption' => null,
290
+ 'ui_caption_src' => null,
291
+ 'ui_enabled_desc' => null,
292
+ 'caption_src' => null,
293
+ 'animate' => 'ui_animate',
294
+ 'overlay_opacity' => 'ui_overlay_opacity',
295
+ 'loop' => 'group_loop',
296
+ 'autostart' => 'slideshow_autostart',
297
+ 'duration' => 'slideshow_duration',
298
+ 'txt_numDisplayPrefix' => null,
299
+ 'txt_numDisplaySeparator' => null,
300
+ 'txt_closeLink' => 'txt_link_close',
301
+ 'txt_nextLink' => 'txt_link_next',
302
+ 'txt_prevLink' => 'txt_link_prev',
303
+ 'txt_startSlideshow' => 'txt_slideshow_start',
304
+ 'txt_stopSlideshow' => 'txt_slideshow_stop',
305
+ 'txt_loadingMsg' => 'txt_loading',
306
+ 'txt_link_next' => 'txt_nav_next',
307
+ 'txt_link_prev' => 'txt_nav_prev',
308
+ 'txt_link_close' => 'txt_close',
309
+ )
310
+ );
311
+
312
+ parent::_set_options($opts);
313
+ }
314
+
315
+ /* Methods */
316
+
317
+ /*-** Admin **-*/
318
+
319
+ /**
320
+ * Add admin menus
321
+ * @uses this->admin->add_theme_page
322
+ */
323
+ function admin_menus() {
324
+ // Build options page
325
+ $lbls_opts = array(
326
+ 'menu' => __('Lightbox', 'simple-lightbox'),
327
+ 'header' => __('Lightbox Settings', 'simple-lightbox'),
328
+ 'plugin_action' => __('Settings', 'simple-lightbox')
329
+ );
330
+ $pg_opts = $this->admin->add_theme_page('options', $lbls_opts)
331
+ ->require_form()
332
+ ->add_content('options', 'Options', $this->options);
333
+
334
+ // Add Support information
335
+ $support = $this->util->get_plugin_info('SupportURI');
336
+ if ( !empty($support) ) {
337
+ $pg_opts->add_content('support', __('Feedback & Support', 'simple-lightbox'), $this->m('theme_page_callback_support'), 'secondary');
338
+ }
339
+
340
+ // Add Actions
341
+ $lbls_reset = array (
342
+ 'title' => __('Reset', 'simple-lightbox'),
343
+ 'confirm' => __('Are you sure you want to reset Simple Lightbox\'s settings?', 'simple-lightbox'),
344
+ 'success' => __('Settings have been reset', 'simple-lightbox'),
345
+ 'failure' => __('Settings were not reset', 'simple-lightbox')
346
+ );
347
+ $this->admin->add_action('reset', $lbls_reset, $this->options);
348
+ }
349
+
350
+ /**
351
+ * Support information
352
+ */
353
+ public function theme_page_callback_support() {
354
+ // Description
355
+ $desc = __("<p>Simple Lightbox thrives on your feedback!</p><p>Click the button below to <strong>get help</strong>, <strong>request a feature</strong>, or <strong>provide some feedback</strong>!</p>", 'simple-lightbox');
356
+ echo $desc;
357
+ // Link
358
+ $lnk_uri = $this->util->get_plugin_info('SupportURI');
359
+ $lnk_txt = __('Get Support &amp; Provide Feedback', 'simple-lightbox');
360
+ echo $this->util->build_html_link($lnk_uri, $lnk_txt, array('target' => '_blank', 'class' => 'button'));
361
+ }
362
+
363
+ /**
364
+ * Filter support link text in plugin metadata
365
+ * @param string $text Original link text
366
+ * @return string Modified link text
367
+ */
368
+ public function admin_plugin_row_meta_support($text) {
369
+ return __("Feedback &amp; Support", 'simple-lightbox');
370
+ }
371
+
372
+ /*-** Functionality **-*/
373
+
374
+ /**
375
+ * Checks whether lightbox is currently enabled/disabled
376
+ * @return bool TRUE if lightbox is currently enabled, FALSE otherwise
377
+ */
378
+ function is_enabled() {
379
+ static $ret = null;
380
+ if ( is_null($ret) ) {
381
+ $ret = ( !is_admin() && $this->options->get_bool('enabled') && !is_feed() ) ? true : false;
382
+ if ( $ret ) {
383
+ $opt = '';
384
+ // Determine option to check
385
+ if ( is_home() || is_front_page() ) {
386
+ $opt = 'home';
387
+ }
388
+ elseif ( is_singular() ) {
389
+ $opt = ( is_page() ) ? 'page' : 'post';
390
+ }
391
+ elseif ( is_archive() || is_search() ) {
392
+ $opt = 'archive';
393
+ }
394
+ // Check sub-option
395
+ if ( !empty($opt) && ( $opt = 'enabled_' . $opt ) && $this->options->has($opt) ) {
396
+ $ret = $this->options->get_bool($opt);
397
+ }
398
+ }
399
+ }
400
+ // Filter return value
401
+ if ( !is_admin() ) {
402
+ $ret = $this->util->apply_filters('is_enabled', $ret);
403
+ }
404
+ // Return value (force boolean)
405
+ return !!$ret;
406
+ }
407
+
408
+ /**
409
+ * Make sure content is valid for processing/activation
410
+ *
411
+ * @param string $content Content to validate
412
+ * @return bool TRUE if content is valid (FALSE otherwise)
413
+ */
414
+ protected function is_content_valid($content) {
415
+ // Invalid hooks
416
+ if ( doing_filter('get_the_excerpt') )
417
+ return false;
418
+
419
+ // Non-string value
420
+ if ( !is_string($content) )
421
+ return false;
422
+
423
+ // Empty string
424
+ $content = trim($content);
425
+ if ( empty($content) )
426
+ return false;
427
+
428
+ // Content is valid
429
+ return $this->util->apply_filters('is_content_valid', true, $content);
430
+ }
431
+
432
+ /**
433
+ * Activates galleries extracted from post
434
+ * @see get_post_galleries()
435
+ * @param array $galleries A list of galleries in post
436
+ * @return A list of galleries with links activated
437
+ */
438
+ function activate_galleries($galleries) {
439
+ // Validate
440
+ if ( empty($galleries) ) {
441
+ return $galleries;
442
+ }
443
+ // Check galleries for HTML output
444
+ $gallery = reset($galleries);
445
+ if ( is_array($gallery) ) {
446
+ return $galleries;
447
+ }
448
+
449
+ // Activate galleries
450
+ $group = ( $this->options->get_bool('group_gallery') ) ? true : null;
451
+ foreach ( $galleries as $key => $val ) {
452
+ if ( !is_null($group) ) {
453
+ $group = 'gallery_' . $key;
454
+ }
455
+ // Activate links in gallery
456
+ $gallery = $this->process_links($val, $group);
457
+
458
+ // Save modified gallery
459
+ $galleries[$key] = $gallery;
460
+ }
461
+
462
+ return $galleries;
463
+ }
464
+
465
+ /**
466
+ * Scans post content for image links and activates them
467
+ *
468
+ * Lightbox will not be activated for feeds
469
+ * @param string $content Content to activate
470
+ * @param string (optonal) $group Group ID for content
471
+ * @return string Post content
472
+ */
473
+ public function activate_links($content, $group = null) {
474
+ // Validate content
475
+ if ( !$this->is_content_valid($content) ) {
476
+ return $content;
477
+ }
478
+ // Filter content before processing links
479
+ $content = $this->util->apply_filters('pre_process_links', $content);
480
+
481
+ // Process links
482
+ $content = $this->process_links($content, $group);
483
+
484
+ // Filter content after processing links
485
+ $content = $this->util->apply_filters('post_process_links', $content);
486
+
487
+ return $content;
488
+ }
489
+
490
+ /**
491
+ * Process links in content
492
+ * @global obj $wpdb DB instance
493
+ * @global obj $post Current post
494
+ * @param string $content Text containing links
495
+ * @param string (optional) $group Group to add links to (Default: none)
496
+ * @return string Content with processed links
497
+ */
498
+ protected function process_links($content, $group = null) {
499
+ // Extract links
500
+ $links = $this->get_links($content, true);
501
+ // Do not process content without links
502
+ if ( empty($links) ) {
503
+ return $content;
504
+ }
505
+ // Process links
506
+ static $protocol = array('http://', 'https://');
507
+ static $qv_att = 'attachment_id';
508
+ static $uri_origin = null;
509
+ if ( !is_array($uri_origin) ) {
510
+ $uri_parts = array_fill_keys(array('scheme', 'host', 'path'), '');
511
+ $uri_origin = wp_parse_args(parse_url( strtolower(home_url()) ), $uri_parts);
512
+ }
513
+ static $uri_proto = null;
514
+ if ( empty($uri_proto) ) {
515
+ $uri_proto = (object) array('raw' => '', 'source' => '', 'parts' => '');
516
+ }
517
+ $uri_parts_required = array('host' => '');
518
+
519
+ // Setup group properties
520
+ $g_props = (object) array(
521
+ 'enabled' => $this->options->get_bool('group_links'),
522
+ 'attr' => 'group',
523
+ 'base' => '',
524
+ 'legacy_prefix' => 'lightbox[',
525
+ 'legacy_suffix' => ']'
526
+ );
527
+ if ( $g_props->enabled ) {
528
+ $g_props->base = ( is_scalar($group) ) ? trim(strval($group)) : '';
529
+ }
530
+
531
+ // Initialize content handlers
532
+ if ( !( $this->handlers instanceof SLB_Content_Handlers ) ) {
533
+ $this->handlers = new SLB_Content_Handlers($this);
534
+ }
535
+
536
+ // Iterate through and activate supported links
537
+
538
+ foreach ( $links as $link ) {
539
+ // Init vars
540
+ $pid = 0;
541
+ $link_new = $link;
542
+ $uri = clone $uri_proto;
543
+ $type = false;
544
+ $props_extra = array();
545
+ $key = null;
546
+ $internal = false;
547
+
548
+ // Parse link attributes
549
+ $attrs = $this->util->parse_attribute_string($link_new, array('href' => ''));
550
+ // Get URI
551
+ $uri->raw = $attrs['href'];
552
+
553
+ // Stop processing invalid links
554
+ if ( !$this->validate_uri($uri->raw)
555
+ || $this->has_attribute($attrs, 'active', false) // Previously-processed
556
+ ) {
557
+ continue;
558
+ }
559
+
560
+ // Normalize URI (make absolute)
561
+ $uri->source = WP_HTTP::make_absolute_url($uri->raw, $uri_origin['scheme'] . '://' . $uri_origin['host']);
562
+
563
+ // URI cached?
564
+ $key = $this->get_media_item_id($uri->source);
565
+
566
+ // Internal URI? (e.g. attachments)
567
+ if ( !$key ) {
568
+ $uri->parts = array_merge( $uri_parts_required, (array) parse_url($uri->source) );
569
+ $internal = ( $uri->parts['host'] === $uri_origin['host'] ) ? true : false;
570
+
571
+ // Attachment?
572
+ if ( $internal && is_local_attachment($uri->source) ) {
573
+ $pid = url_to_postid($uri->source);
574
+ $src = wp_get_attachment_url($pid);
575
+ if ( !!$src ) {
576
+ $uri->source = $src;
577
+ $props_extra['id'] = $pid;
578
+ // Check cache for attachment source URI
579
+ $key = $this->get_media_item_id($uri->source);
580
+ }
581
+ unset($src);
582
+ }
583
+ }
584
+
585
+ // Determine content type
586
+ if ( !$key ) {
587
+ // Get handler match
588
+ $hdl_result = $this->handlers->match($uri->source);
589
+ if ( !!$hdl_result->handler ) {
590
+ $type = $hdl_result->handler->get_id();
591
+ $props_extra = $hdl_result->props;
592
+ // Updated source URI
593
+ if ( isset($props_extra['uri']) ) {
594
+ $uri->source = $props_extra['uri'];
595
+ unset($props_extra['uri']);
596
+ }
597
+ }
598
+
599
+ // Cache valid item
600
+ if ( !!$type ) {
601
+ $key = $this->cache_media_item($uri, $type, $internal, $props_extra);
602
+ }
603
+ }
604
+
605
+ // Stop processing invalid links
606
+ if ( !$key ) {
607
+ // Cache invalid URI
608
+ $this->validated_uris[$uri->source] = false;
609
+ if ( $uri->raw !== $uri->source ) {
610
+ $this->validated_uris[$uri->raw] = false;
611
+ }
612
+ continue;
613
+ }
614
+
615
+ // Activate link
616
+ $this->set_attribute($attrs, 'active');
617
+ $this->set_attribute($attrs, 'asset', $key);
618
+ // Mark internal links
619
+ if ( $internal ) {
620
+ $this->set_attribute($attrs, 'internal', $pid);
621
+ }
622
+
623
+ // Set group (if enabled)
624
+ if ( $g_props->enabled ) {
625
+ $group = array();
626
+ // Get preset group attribute
627
+ $g = ( $this->has_attribute($attrs, $g_props->attr) ) ? $this->get_attribute($attrs, $g_props->attr) : '';
628
+ if ( is_string($g) && ($g = trim($g)) && !empty($g) ) {
629
+ $group[] = $g;
630
+ } elseif ( !empty($g_props->base) ) {
631
+ $group[] = $g_props->base;
632
+ }
633
+
634
+ /**
635
+ * Filter group ID components
636
+ *
637
+ * @see process_links()
638
+ *
639
+ * @param array $group Components used to build group ID
640
+ */
641
+ $group = $this->util->apply_filters('get_group_id', $group);
642
+
643
+ // Default group
644
+ if ( empty($group) || !is_array($group) ) {
645
+ $group = $this->get_prefix();
646
+ } else {
647
+ $group = implode('_', $group);
648
+ }
649
+
650
+ // Set group attribute
651
+ $this->set_attribute($attrs, $g_props->attr, $group);
652
+ unset($g);
653
+ }
654
+
655
+ // Filter attributes
656
+ $attrs = $this->util->apply_filters('process_link_attributes', $attrs);
657
+
658
+ // Update link in content
659
+ $link_new = '<a ' . $this->util->build_attribute_string($attrs) . '>';
660
+ $content = str_replace($link, $link_new, $content);
661
+ }
662
+
663
+ // Handle widget content
664
+ if ( !!$this->widget_processing && 'the_content' == current_filter() ) {
665
+ $content = $this->exclude_wrap($content);
666
+ }
667
+
668
+ return $content;
669
+ }
670
+
671
+ /**
672
+ * Retrieve HTML links in content
673
+ * @param string $content Content to get links from
674
+ * @param bool (optional) $unique Remove duplicates from returned links (Default: FALSE)
675
+ * @return array Links in content
676
+ */
677
+ function get_links($content, $unique = false) {
678
+ $rgx = "/\<a[^\>]+href=.*?\>/i";
679
+ $links = array();
680
+ preg_match_all($rgx, $content, $links);
681
+ $links = $links[0];
682
+ if ( $unique )
683
+ $links = array_unique($links);
684
+ return $links;
685
+ }
686
+
687
+ /**
688
+ * Validate URI
689
+ * Matches specified URI against internal & external regex patterns
690
+ * URI is **invalid** if it matches a regex
691
+ *
692
+ * @param string $uri URI to validate
693
+ * @return bool TRUE if URI is valid
694
+ */
695
+ protected function validate_uri($uri) {
696
+ static $patterns = null;
697
+ // Previously-validated URI
698
+ if ( isset($this->validated_uris[$uri]) )
699
+ return $this->validated_uris[$uri];
700
+
701
+ $valid = true;
702
+ // Boilerplate validation
703
+ if ( empty($uri) // Empty
704
+ || 0 === strpos($uri, '#') // Anchor
705
+ )
706
+ $valid = false;
707
+
708
+ // Regex matching
709
+ if ( $valid ) {
710
+ // Get patterns
711
+ if ( is_null($patterns) ) {
712
+ $patterns = $this->util->apply_filters('validate_uri_regex', array());
713
+ }
714
+ // Iterate through patterns until match found
715
+ foreach ( $patterns as $pattern ) {
716
+ if ( 1 === preg_match($pattern, $uri) ) {
717
+ $valid = false;
718
+ break;
719
+ }
720
+ }
721
+ }
722
+
723
+ // Cache
724
+ $this->validated_uris[$uri] = $valid;
725
+ return $valid;
726
+ }
727
+
728
+ /**
729
+ * Add URI validation regex pattern
730
+ * @param
731
+ */
732
+ public function validate_uri_regex_default($patterns) {
733
+ $patterns[] = '@^https?://[^/]*(wikipedia|wikimedia)\.org/wiki/file:.*$@i';
734
+ return $patterns;
735
+ }
736
+
737
+ /* Client */
738
+
739
+ /**
740
+ * Checks if output should be loaded in current request
741
+ * @uses `is_enabled()`
742
+ * @uses `has_cached_media_items()`
743
+ * @return bool TRUE if output is being loaded into client
744
+ */
745
+ public function is_request_valid() {
746
+ return ( $this->is_enabled() && $this->has_cached_media_items() ) ? true : false;
747
+ }
748
+
749
+ /**
750
+ * Sets options/settings to initialize lightbox functionality on page load
751
+ * @return void
752
+ */
753
+ function client_init($client_script) {
754
+ // Get options
755
+ $options = $this->options->build_client_output();
756
+
757
+ // Load UI Strings
758
+ if ( ($labels = $this->build_labels()) && !empty($labels) ) {
759
+ $options['ui_labels'] = $labels;
760
+ }
761
+
762
+ // Build client output
763
+ $client_script[] = $this->util->call_client_method('View.init', $options);
764
+ return $client_script;
765
+ }
766
+
767
+ /**
768
+ * Output code in footer
769
+ * > Media attachment URLs
770
+ * @uses `_wp_attached_file` to match attachment ID to URI
771
+ * @uses `_wp_attachment_metadata` to retrieve attachment metadata
772
+ */
773
+ function client_footer() {
774
+ if ( !$this->has_cached_media_items() )
775
+ return false;
776
+
777
+ // Set up hooks
778
+ add_action('wp_print_footer_scripts', $this->m('client_footer_script'));
779
+
780
+ // Build client output
781
+ $this->util->do_action('footer');
782
+ }
783
+
784
+ /**
785
+ * Output client footer scripts
786
+ */
787
+ function client_footer_script() {
788
+ $client_script = $this->util->apply_filters('footer_script', array());
789
+ if ( !empty($client_script) ) {
790
+ echo $this->util->build_script_element($client_script, 'footer', true, true);
791
+ }
792
+ }
793
+
794
+ /**
795
+ * Add media information to client output
796
+ *
797
+ * @param array $commands Client script commands
798
+ * @return array Modified script commands
799
+ * TODO Refactor
800
+ */
801
+ function client_script_media($client_script) {
802
+ global $wpdb;
803
+
804
+ // Init variables
805
+ $this->media_items = array();
806
+ $props = array('id', 'type', 'description', 'title', 'source', 'caption');
807
+ $props = (object) array_combine($props, $props);
808
+ $props_map = array('description' => 'post_content', 'title' => 'post_title', 'caption' => 'post_excerpt');
809
+
810
+ // Separate media into buckets by type
811
+ $m_internals = array();
812
+ $type = $id = null;
813
+
814
+ $m_items = $this->media_items = $this->get_cached_media_items();
815
+ foreach ( $m_items as $key => $p ) {
816
+ // Set aside internal links for additional processing
817
+ if ( $p->internal && !isset($m_internals[$key]) ) {
818
+ $m_internals[$key] =& $m_items[$key];
819
+ }
820
+ }
821
+ unset($key, $p);
822
+
823
+ // Process internal links
824
+ if ( !empty($m_internals) ) {
825
+ $uris_base = array();
826
+ $uri_prefix = wp_upload_dir();
827
+ $uri_prefix = $this->util->normalize_path($uri_prefix['baseurl'], true);
828
+ foreach ( $m_internals as $key => $p ) {
829
+ // Prepare internal links
830
+ // Create relative URIs for attachment data retrieval
831
+ if ( !$p->id && strpos($p->source, $uri_prefix) === 0 ) {
832
+ $uris_base[str_replace($uri_prefix, '', $p->source)] = $key;
833
+ }
834
+ }
835
+ unset($key, $p);
836
+
837
+ // Retrieve attachment IDs
838
+ $uris_flat = "('" . implode("','", array_keys($uris_base)) . "')";
839
+ $q = $wpdb->prepare("SELECT post_id, meta_value FROM $wpdb->postmeta WHERE `meta_key` = %s AND LOWER(`meta_value`) IN $uris_flat LIMIT %d", '_wp_attached_file', count($uris_base));
840
+ $pids = $wpdb->get_results($q);
841
+ // Match IDs to URIs
842
+ if ( $pids ) {
843
+ foreach ( $pids as $pd ) {
844
+ $file =& $pd->meta_value;
845
+ if ( isset($uris_base[$file]) ) {
846
+ $m_internals[ $uris_base[$file] ]->{$props->id} = absint($pd->post_id);
847
+ }
848
+ }
849
+ }
850
+ // Destroy worker vars
851
+ unset($uris_base, $uris_flat, $q, $pids, $pd, $file);
852
+ }
853
+
854
+ // Process items with attachment IDs
855
+ $pids = array();
856
+ foreach ( $m_items as $key => $p ) {
857
+ // Add post ID to query
858
+ if ( !!$p->id ) {
859
+ // Create array for ID (support multiple URIs per ID)
860
+ if ( !isset($pids[$p->id]) ) {
861
+ $pids[$p->id] = array();
862
+ }
863
+ // Add URI to ID
864
+ $pids[$p->id][] = $key;
865
+ }
866
+ }
867
+ unset($key, $p);
868
+
869
+ // Retrieve attachment properties
870
+ if ( !empty($pids) ) {
871
+ $pids_flat = array_keys($pids);
872
+ // Retrieve attachment post data
873
+ $atts = get_posts(array('post_type' => 'attachment', 'include' => $pids_flat));
874
+
875
+ // Process attachments
876
+ if ( $atts ) {
877
+ // Retrieve attachment metadata
878
+ $pids_flat = "('" . implode("','", $pids_flat) . "')";
879
+ $atts_meta = $wpdb->get_results($wpdb->prepare("SELECT `post_id`,`meta_value` FROM $wpdb->postmeta WHERE `post_id` IN $pids_flat AND `meta_key` = %s LIMIT %d", '_wp_attachment_metadata', count($atts)));
880
+ // Restructure metadata array by post ID
881
+ if ( $atts_meta ) {
882
+ $meta = array();
883
+ foreach ( $atts_meta as $att_meta ) {
884
+ $meta[$att_meta->post_id] = $att_meta->meta_value;
885
+ }
886
+ $atts_meta = $meta;
887
+ unset($meta);
888
+ } else {
889
+ $atts_meta = array();
890
+ }
891
+ $props_size = array('file', 'width', 'height');
892
+ $props_exclude = array('hwstring_small');
893
+ foreach ( $atts as $att ) {
894
+ // Set post data
895
+ $m = array();
896
+
897
+ // Remap post data to properties
898
+ foreach ( $props_map as $prop_key => $prop_source ) {
899
+ $m[$props->{$prop_key}] = $att->{$prop_source};
900
+ }
901
+ unset($prop_key, $prop_source);
902
+
903
+ // Add metadata
904
+ if ( isset($atts_meta[$att->ID]) && ($a = unserialize($atts_meta[$att->ID])) && is_array($a) ) {
905
+ // Move original size into `sizes` array
906
+ foreach ( $props_size as $d ) {
907
+ if ( !isset($a[$d]) ) {
908
+ continue;
909
+ }
910
+ $a['sizes']['original'][$d] = $a[$d];
911
+ unset($a[$d]);
912
+ }
913
+
914
+ // Strip extraneous metadata
915
+ foreach ( $props_exclude as $d ) {
916
+ if ( isset($a[$d]) ) {
917
+ unset($a[$d]);
918
+ }
919
+ }
920
+
921
+ // Merge post data & meta data
922
+ $m = array_merge($a, $m);
923
+ // Destroy worker vars
924
+ unset($a, $d);
925
+ }
926
+
927
+ // Save attachment data (post & meta) to original object(s)
928
+ if ( isset($pids[$att->ID]) ) {
929
+ foreach ( $pids[$att->ID] as $key ) {
930
+ $this->media_items[$key] = array_merge( (array) $m_items[$key], $m);
931
+ }
932
+ }
933
+ }
934
+ }
935
+ unset($atts, $atts_meta, $m, $a, $uri, $pids, $pids_flat);
936
+ }
937
+
938
+ // Filter media item properties
939
+ foreach ( $this->media_items as $key => $props ) {
940
+ $this->media_items[$key] = $this->util->apply_filters('media_item_properties', (object) $props);
941
+ }
942
+
943
+ // Build client output
944
+ $obj = 'View.assets';
945
+ $client_script[] = $this->util->extend_client_object($obj, $this->media_items);
946
+ return $client_script;
947
+ }
948
+
949
+ /*-** Media **-*/
950
+
951
+ /**
952
+ * Cache media properties for later processing
953
+ * @uses array self::$media_items_raw Stores media items for output
954
+ * @param object $uri URI to cache
955
+ * Members
956
+ * > raw: Raw Link URI
957
+ * > source: Source URI (e.g. for attachment URIs)
958
+ * @param string $type Media type (image, attachment, etc.)
959
+ * @param bool $internal TRUE if media is internal (e.g. attachment)
960
+ * @param array $props (optional) Properties to store for item (Default: NULL)
961
+ * @return string Unique ID for cached media item
962
+ */
963
+ private function cache_media_item($uri, $type, $internal, $props = null) {
964
+ // Validate
965
+ if ( !is_object($uri) || !is_string($type) ) {
966
+ return false;
967
+ }
968
+ // Check if URI already cached
969
+ $key = $this->get_media_item_id($uri->source);
970
+ // Cache new item
971
+ if ( null == $key ) {
972
+ // Generate Unique ID
973
+ do {
974
+ $key = (string) mt_rand();
975
+ } while ( isset($this->media_items_raw['props'][$key]) );
976
+ // Build properties object
977
+ $i = array('id' => null);
978
+ if ( is_array($props) && !empty($props) ) {
979
+ $i = array_merge($i, $props);
980
+ }
981
+ $i = array_merge($i, array('type' => $type, 'source' => $uri->source, 'internal' => $internal));
982
+ // Cache item properties
983
+ $this->media_items_raw['props'][$key] = (object) $i;
984
+ // Cache Source URI (point to properties object)
985
+ $this->media_items_raw['uri'][$uri->source] = $key;
986
+ }
987
+ return $key;
988
+ }
989
+
990
+ /**
991
+ * Retrieve ID for media item
992
+ * @uses self::$media_items_raw
993
+ * @param string $uri Media item URI
994
+ * @return string|null Media item ID (Default: NULL if URI doesn't exist in collection)
995
+ */
996
+ private function get_media_item_id($uri) {
997
+ if ( $this->media_item_cached($uri) ) {
998
+ return $this->media_items_raw['uri'][$uri];
999
+ }
1000
+ return null;
1001
+ }
1002
+
1003
+ /**
1004
+ * Checks if media item has already been cached
1005
+ * @param string $uri URI of media item
1006
+ * @return boolean Whether media item has been cached
1007
+ */
1008
+ private function media_item_cached($uri) {
1009
+ return ( is_string($uri) && !empty($uri) && isset($this->media_items_raw['uri'][$uri]) ) ? true : false;
1010
+ }
1011
+
1012
+ /**
1013
+ * Retrieve cached media item
1014
+ * @param string $uri Media item URI
1015
+ * @return object|null Media item properties (NULL if not set)
1016
+ */
1017
+ private function get_cached_media_item($uri) {
1018
+ $key = $this->get_media_item_id($uri);
1019
+ if ( null != $key ) {
1020
+ return $this->media_items_raw['props'][$key];
1021
+ }
1022
+ return null;
1023
+ }
1024
+
1025
+ /**
1026
+ * Retrieve cached media items (properties)
1027
+ * @uses self::$media_items_raw
1028
+ * @return array Cached media items (objects)
1029
+ */
1030
+ private function &get_cached_media_items() {
1031
+ return $this->media_items_raw['props'];
1032
+ }
1033
+
1034
+ /**
1035
+ * Check if media items have been cached
1036
+ * @return boolean
1037
+ */
1038
+ private function has_cached_media_items() {
1039
+ return ( empty($this->media_items_raw['props']) ) ? false : true;
1040
+ }
1041
+
1042
+ /*-** Exclusion **-*/
1043
+
1044
+ /**
1045
+ * Retrieve exclude object
1046
+ * Initialize object properties if necessary
1047
+ * @return object Exclude properties
1048
+ */
1049
+ private function get_exclude() {
1050
+ // Initialize exclude data
1051
+ if ( !is_object($this->exclude) ) {
1052
+ $this->exclude = (object) array (
1053
+ 'tags' => $this->get_exclude_tags(),
1054
+ 'ph' => $this->get_exclude_placeholder(),
1055
+ 'group_default' => 'default',
1056
+ 'cache' => array(),
1057
+ );
1058
+ }
1059
+ return $this->exclude;
1060
+ }
1061
+
1062
+ /**
1063
+ * Get exclusion tags (open/close)
1064
+ * Example: open => [slb_exclude], close => [/slb_exclude]
1065
+ *
1066
+ * @return object Exclusion tags
1067
+ */
1068
+ private function get_exclude_tags() {
1069
+ static $tags = null;
1070
+ if ( null == $tags ) {
1071
+ $base = $this->add_prefix('exclude');
1072
+ $tags = (object) array (
1073
+ 'base' => $base,
1074
+ 'open' => $this->util->add_wrapper($base),
1075
+ 'close' => $this->util->add_wrapper($base, '[/', ']')
1076
+ );
1077
+ $tags->search ='#' . preg_quote($tags->open) . '(.*?)' . preg_quote($tags->close) . '#s';
1078
+ }
1079
+ return $tags;
1080
+ }
1081
+
1082
+ /**
1083
+ * Get exclusion tag ("[slb_exclude]")
1084
+ * @uses `get_exclude_tags()` to retrieve tag
1085
+ *
1086
+ * @param string $type (optional) Tag to retrieve (open or close)
1087
+ * @return string Exclusion tag
1088
+ */
1089
+ private function get_exclude_tag( $type = "open" ) {
1090
+ // Validate
1091
+ $tags = $this->get_exclude_tags();
1092
+ if ( !isset($tags->{$type}) ) {
1093
+ $type = "open";
1094
+ }
1095
+ return $tags->{$type};
1096
+ }
1097
+
1098
+ /**
1099
+ * Build exclude placeholder
1100
+ * @return object Exclude placeholder properties
1101
+ */
1102
+ private function get_exclude_placeholder() {
1103
+ static $ph;
1104
+ if ( !is_object($ph) ) {
1105
+ $ph = (object) array (
1106
+ 'base' => $this->add_prefix('exclude_temp'),
1107
+ 'open' => '{{',
1108
+ 'close' => '}}',
1109
+ 'attrs' => array ( 'group' => '', 'key' => '' ),
1110
+ );
1111
+ // Search Patterns
1112
+ $sub = '(.+?)';
1113
+ $ph->search = '#' . preg_quote($ph->open) . $ph->base . '\s+' . $sub . preg_quote($ph->close) . '#s';
1114
+ $ph->search_group = str_replace($sub, '(group="%s"\s+.?)', $ph->search);
1115
+ // Templates
1116
+ $attr_string = '';
1117
+ foreach ( $ph->attrs as $attr => $val ) {
1118
+ $attr_string .= ' ' . $attr . '="%s"';
1119
+ }
1120
+ $ph->template = $ph->open . $ph->base . $attr_string . $ph->close;
1121
+ }
1122
+ return $ph;
1123
+ }
1124
+
1125
+ /**
1126
+ * Wrap content in exclusion tags
1127
+ * @uses `get_exclude_tag()` to wrap content with exclusion tag
1128
+ * @param string $content Content to exclude
1129
+ * @return string Content wrapped in exclusion tags
1130
+ */
1131
+ private function exclude_wrap($content) {
1132
+ // Validate
1133
+ if ( !is_string($content) ) {
1134
+ $content = "";
1135
+ }
1136
+ // Wrap
1137
+ $tags = $this->get_exclude_tags();
1138
+ return $tags->open . $content . $tags->close;
1139
+ }
1140
+
1141
+ /**
1142
+ * Remove excluded content
1143
+ * Caches content for restoring later
1144
+ * @param string $content Content to remove excluded content from
1145
+ * @return string Updated content
1146
+ */
1147
+ public function exclude_content($content, $group = null) {
1148
+ $ex = $this->get_exclude();
1149
+ // Setup cache
1150
+ if ( !is_string($group) || empty($group) ) {
1151
+ $group = $ex->group_default;
1152
+ }
1153
+ if ( !isset($ex->cache[$group]) ) {
1154
+ $ex->cache[$group] = array();
1155
+ }
1156
+ $cache =& $ex->cache[$group];
1157
+
1158
+ $content = $this->util->apply_filters('pre_exclude_content', $content);
1159
+
1160
+ // Search content
1161
+ $matches = null;
1162
+ if ( false !== strpos($content, $ex->tags->open) && preg_match_all($ex->tags->search, $content, $matches) ) {
1163
+ // Determine index
1164
+ $idx = ( !!end($cache) ) ? key($cache) : -1;
1165
+ $ph = array();
1166
+ foreach ( $matches[1] as $midx => $match ) {
1167
+ // Update index
1168
+ $idx++;
1169
+ // Cache content
1170
+ $cache[$idx] = $match;
1171
+ // Build placeholder
1172
+ $ph[] = sprintf($ex->ph->template, $group, $idx);
1173
+ }
1174
+ unset($midx, $match);
1175
+ // Replace content with placeholder
1176
+ $content = str_replace($matches[0], $ph, $content);
1177
+
1178
+ // Cleanup
1179
+ unset($matches, $ph);
1180
+ }
1181
+
1182
+ return $content;
1183
+ }
1184
+
1185
+ /**
1186
+ * Exclude shortcodes from link activation
1187
+ * @param string $content Content to exclude shortcodes from
1188
+ * @return string Content with shortcodes excluded
1189
+ */
1190
+ public function exclude_shortcodes($content) {
1191
+ // Get shortcodes to exclude
1192
+ $shortcodes = $this->util->apply_filters('exclude_shortcodes', array( $this->add_prefix('group') ));
1193
+ // Set callback
1194
+ $shortcodes = array_fill_keys($shortcodes, $this->m('exclude_shortcodes_handler'));
1195
+ return $this->util->do_shortcode($content, $shortcodes);
1196
+ }
1197
+
1198
+ /**
1199
+ * Wrap shortcode in exclude tags
1200
+ * @uses Util->make_shortcode() to rebuild original shortcode
1201
+ *
1202
+ * @param array $attr Shortcode attributes
1203
+ * @param string $content Content enclosed in shortcode
1204
+ * @param string $tag Shortcode name
1205
+ * @return string Excluded shortcode
1206
+ */
1207
+ public function exclude_shortcodes_handler($attr, $content, $tag) {
1208
+ $code = $this->util->make_shortcode($tag, $attr, $content);
1209
+ // Exclude shortcode
1210
+ return $this->exclude_wrap($code);
1211
+ }
1212
+
1213
+ /**
1214
+ * Restore excluded content
1215
+ * @param string $content Content to restore excluded content to
1216
+ * @return string Content with excluded content restored
1217
+ */
1218
+ public function restore_excluded_content($content, $group = null) {
1219
+ $ex = $this->get_exclude();
1220
+ // Setup cache
1221
+ if ( !is_string($group) || empty($group) ) {
1222
+ $group = $ex->group_default;
1223
+ }
1224
+ // Nothing to restore if cache group doesn't exist
1225
+ if ( !isset($ex->cache[$group]) ) {
1226
+ return $content;
1227
+ }
1228
+ $cache =& $ex->cache[$group];
1229
+
1230
+ // Search content for placeholders
1231
+ $matches = null;
1232
+ if ( false !== strpos($content, $ex->ph->open . $ex->ph->base) && preg_match_all($ex->ph->search, $content, $matches) ) {
1233
+ // Restore placeholders
1234
+ foreach ( $matches[1] as $idx => $ph ) {
1235
+ // Parse placeholder attributes
1236
+ $attrs = $this->util->parse_attribute_string($ph, $ex->ph->attrs);
1237
+ // Validate
1238
+ if ( $attrs['group'] !== $group ) {
1239
+ continue;
1240
+ }
1241
+ // Restore content
1242
+ $key = $attrs['key'] = intval($attrs['key']);
1243
+ if ( isset($cache[$key]) ) {
1244
+ $content = str_replace($matches[0][$idx], $cache[$key], $content);
1245
+ }
1246
+ }
1247
+ // Cleanup
1248
+ unset($idx, $ph, $matches, $key);
1249
+ }
1250
+
1251
+ return $content;
1252
+ }
1253
+
1254
+ /*-** Grouping **-*/
1255
+
1256
+ /**
1257
+ * Builds wrapper for grouping
1258
+ * @return string Format for wrapping content in group
1259
+ */
1260
+ function group_get_wrapper() {
1261
+ static $fmt = null;
1262
+ if ( is_null($fmt) ) {
1263
+ $fmt = $this->util->make_shortcode($this->add_prefix('group'), null, '%s');
1264
+ }
1265
+ return $fmt;
1266
+ }
1267
+
1268
+ /**
1269
+ * Wraps shortcodes for automatic grouping
1270
+ * @uses `the_content` Filter hook
1271
+ * @uses group_shortcodes_handler to Wrap shortcodes for grouping
1272
+ * @param string $content Post content
1273
+ * @return string Modified post content
1274
+ */
1275
+ function group_shortcodes($content) {
1276
+ if ( !$this->is_content_valid($content) ) {
1277
+ return $content;
1278
+ }
1279
+ // Setup shortcodes to wrap
1280
+ $shortcodes = $this->util->apply_filters('group_shortcodes', array( 'gallery', 'nggallery' ));
1281
+ // Set custom callback
1282
+ $shortcodes = array_fill_keys($shortcodes, $this->m('group_shortcodes_handler'));
1283
+ // Process gallery shortcodes
1284
+ return $this->util->do_shortcode($content, $shortcodes);
1285
+ }
1286
+
1287
+ /**
1288
+ * Groups shortcodes for later processing
1289
+ * @param array $attr Shortcode attributes
1290
+ * @param string $content Content enclosed in shortcode
1291
+ * @param string $tag Shortcode name
1292
+ * @return string Grouped shortcode
1293
+ */
1294
+ function group_shortcodes_handler($attr, $content, $tag) {
1295
+ $code = $this->util->make_shortcode($tag, $attr, $content);
1296
+ // Wrap shortcode
1297
+ return sprintf( $this->group_get_wrapper(), $code);
1298
+ }
1299
+
1300
+ /**
1301
+ * Activate groups in content
1302
+ * @param string $content Content to activate
1303
+ * @return string Updated content
1304
+ */
1305
+ public function activate_groups($content) {
1306
+ return $this->util->do_shortcode($content, array( $this->add_prefix('group') => $this->m('activate_groups_handler') ) );
1307
+ }
1308
+
1309
+ /**
1310
+ * Groups shortcodes for later processing
1311
+ * @param array $attr Shortcode attributes
1312
+ * @param string $content Content enclosed in shortcode
1313
+ * @param string $tag Shortcode name
1314
+ * @return string Grouped shortcode
1315
+ */
1316
+ function activate_groups_handler($attr, $content, $tag) {
1317
+ // Get Group ID
1318
+ // Custom group
1319
+ if ( isset($attr['id']) ) {
1320
+ $group = $attr['id'];
1321
+ trim($group);
1322
+ }
1323
+ // Automatically-generated group
1324
+ if ( empty($group) ) {
1325
+ $group = 'auto_' . ++$this->groups['auto'];
1326
+ }
1327
+ return $this->process_links($content, $group);
1328
+ }
1329
+
1330
+ /*-** Widgets **-*/
1331
+
1332
+ /**
1333
+ * Set widget up for processing/activation
1334
+ * Buffers widget output for further processing
1335
+ * @param array $widget_args Widget arguments
1336
+ * @return void
1337
+ */
1338
+ public function widget_process_start($widget_args) {
1339
+ // Do not continue if a widget is currently being processed (avoid nested processing)
1340
+ if ( 0 < $this->widget_processing_level ) {
1341
+ return;
1342
+ }
1343
+ // Start widget processing
1344
+ $this->widget_processing = true;
1345
+ $this->widget_processing_params = $widget_args;
1346
+ // Enable widget grouping
1347
+ if ( $this->options->get_bool('group_widget') ) {
1348
+ $this->util->add_filter('get_group_id', $this->m('widget_group_id'));
1349
+ }
1350
+ // Begin output buffer
1351
+ ob_start();
1352
+ }
1353
+
1354
+ /**
1355
+ * Handles inter-widget processing
1356
+ * After widget output generated, Before next widget starts
1357
+ * @param array $params New widget parameters
1358
+ */
1359
+ public function widget_process_inter( $params ) {
1360
+ $this->widget_process_finish();
1361
+ return $params;
1362
+ }
1363
+
1364
+ /**
1365
+ * Complete widget processing
1366
+ * Activate widget output
1367
+ * @uses $widget_processing
1368
+ * @uses $widget_processing_level
1369
+ * @uses $widget_processing_params
1370
+ * @return void
1371
+ */
1372
+ public function widget_process_finish() {
1373
+ /**
1374
+ * Stop processing on conditions:
1375
+ * - No widget is being processed
1376
+ * - Processing a nested widget
1377
+ */
1378
+ if ( !$this->widget_processing || 0 < $this->widget_processing_level ) {
1379
+ return;
1380
+ }
1381
+ // Activate widget output
1382
+ $out = $this->activate_links(ob_get_clean());
1383
+
1384
+ // Clear grouping callback
1385
+ if ( $this->options->get_bool('group_widget') ) {
1386
+ $this->util->remove_filter('get_group_id', $this->m('widget_group_id'));
1387
+ }
1388
+ // End widget processing
1389
+ $this->widget_processing = false;
1390
+ $this->widget_processing_params = null;
1391
+ // Output widget
1392
+ echo $out;
1393
+ }
1394
+
1395
+ /**
1396
+ * Add widget ID to link group ID
1397
+ * Widget ID precedes all other group segments
1398
+ * @uses `SLB::get_group_id` filter
1399
+ * @param array $group_segments Group ID segments
1400
+ * @return array Modified group ID segments
1401
+ */
1402
+ public function widget_group_id($group_segments) {
1403
+ // Add current widget ID to group ID
1404
+ if ( isset($this->widget_processing_params['id']) ) {
1405
+ array_unshift($group_segments, $this->widget_processing_params['id']);
1406
+ }
1407
+ return $group_segments;
1408
+ }
1409
+
1410
+ /**
1411
+ * Handles nested activation in widgets
1412
+ * @uses widget_processing
1413
+ * @uses $widget_processing_level
1414
+ * @return void
1415
+ */
1416
+ public function widget_process_nested() {
1417
+ // Stop if no widget is being processed
1418
+ if ( !$this->widget_processing ) {
1419
+ return;
1420
+ }
1421
+
1422
+ // Increment nesting level
1423
+ $this->widget_processing_level++;
1424
+ }
1425
+
1426
+ /**
1427
+ * Mark the end of a nested widget
1428
+ * @uses $widget_processing_level
1429
+ */
1430
+ public function widget_process_nested_finish() {
1431
+ // Decrement nesting level
1432
+ if ( 0 < $this->widget_processing_level ) {
1433
+ $this->widget_processing_level--;
1434
+ }
1435
+ }
1436
+
1437
+ /**
1438
+ * Begin blocking widget activation
1439
+ * @return void
1440
+ */
1441
+ public function widget_block_start() {
1442
+ $this->util->add_filter('is_content_valid', $this->m('widget_block_handle'));
1443
+ }
1444
+
1445
+ /**
1446
+ * Stop blocking widget activation
1447
+ * @return void
1448
+ */
1449
+ public function widget_block_finish() {
1450
+ $this->util->remove_filter('is_content_valid', $this->m('widget_block_handle'));
1451
+ }
1452
+
1453
+ /**
1454
+ * Handle widget activation blocking
1455
+ */
1456
+ public function widget_block_handle($is_content_valid) {
1457
+ return false;
1458
+ }
1459
+
1460
+ /*-** Menus **-*/
1461
+
1462
+ /**
1463
+ * Process navigation menu links
1464
+ *
1465
+ * @see wp_nav_menu()/filter: wp_nav_menu
1466
+ *
1467
+ * @param string $nav_menu HTML content for navigation menu.
1468
+ * @param object $args Navigation menu's arguments.
1469
+ */
1470
+ public function menu_process($nav_menu, $args) {
1471
+ // Grouping
1472
+ if ( $this->options->get_bool('group_menu') ) {
1473
+ // Generate group ID for menu
1474
+ $group = 'menu';
1475
+ $sep = '_';
1476
+ if ( !empty( $args->menu_id ) ) {
1477
+ $group .= $sep . $args->menu_id;
1478
+ } elseif ( !empty( $args->menu ) ) {
1479
+ $group .= $sep . ( ( is_object($args->menu) ) ? $args->menu->slug : $args->menu );
1480
+ }
1481
+ $group = $this->group_id_unique( $group );
1482
+ } else {
1483
+ $group = null;
1484
+ }
1485
+
1486
+ // Process menu
1487
+ $nav_menu = $this->activate_links($nav_menu, $group);
1488
+
1489
+ return $nav_menu;
1490
+ }
1491
+
1492
+ /**
1493
+ * Generate unique group ID
1494
+ *
1495
+ * @param string $group Group ID to check
1496
+ * @return string Unique group ID
1497
+ */
1498
+ public function group_id_unique($group) {
1499
+ static $groups = array();
1500
+ while ( in_array($group, $groups) ) {
1501
+ $patt = '#-(\d+)$#';
1502
+ if ( preg_match( $patt, $group, $matches ) )
1503
+ $group = preg_replace($patt, '-' . ++$matches[1], $group );
1504
+ else
1505
+ $group = $group . '-1';
1506
+ }
1507
+ return $group;
1508
+ }
1509
+
1510
+ /*-** Helpers **-*/
1511
+
1512
+ /**
1513
+ * Build attribute name
1514
+ * Makes sure name is only prefixed once
1515
+ * @param string $name (optional) Attribute base name
1516
+ * @return string Formatted attribute name
1517
+ */
1518
+ function make_attribute_name($name = '') {
1519
+ // Validate
1520
+ if ( !is_string($name) ) {
1521
+ $name = '';
1522
+ } else {
1523
+ $name = trim($name);
1524
+ }
1525
+ // Setup
1526
+ $sep = '-';
1527
+ $top = 'data';
1528
+ // Generate valid name
1529
+ if ( strpos($name, $top . $sep . $this->get_prefix()) !== 0 ) {
1530
+ $name = $top . $sep . $this->add_prefix($name, $sep);
1531
+ }
1532
+ return $name;
1533
+ }
1534
+
1535
+ /**
1536
+ * Set attribute to array
1537
+ * Attribute is added to array if it does not exist
1538
+ * @param array $attrs Array to add attribute to (Passed by reference)
1539
+ * @param string $name Name of attribute to add
1540
+ * @param string (optional) $value Attribute value
1541
+ * @return array Updated attribute array
1542
+ */
1543
+ function set_attribute(&$attrs, $name, $value = true) {
1544
+ // Validate
1545
+ $attrs = $this->get_attributes($attrs, false);
1546
+ if ( !is_string($name) || empty($name) ) {
1547
+ return $attrs;
1548
+ }
1549
+ if ( !is_scalar($value) ) {
1550
+ $value = true;
1551
+ }
1552
+ // Add attribute
1553
+ $attrs = array_merge($attrs, array( $this->make_attribute_name($name) => strval($value) ));
1554
+
1555
+ return $attrs;
1556
+ }
1557
+
1558
+ /**
1559
+ * Convert attribute string into array
1560
+ * @param string $attr_string Attribute string
1561
+ * @param bool (optional) $internal Whether only internal attributes should be evaluated (Default: TRUE)
1562
+ * @return array Attributes as associative array
1563
+ */
1564
+ function get_attributes($attr_string, $internal = true) {
1565
+ if ( is_string($attr_string) ) {
1566
+ $attr_string = $this->util->parse_attribute_string($attr_string);
1567
+ }
1568
+ $ret = ( is_array($attr_string) ) ? $attr_string : array();
1569
+ // Filter out external attributes
1570
+ if ( !empty($ret) && is_bool($internal) && $internal ) {
1571
+ $ret_f = array();
1572
+ foreach ( $ret as $key => $val ) {
1573
+ if ( strpos($key, $this->make_attribute_name()) == 0 ) {
1574
+ $ret_f[$key] = $val;
1575
+ }
1576
+ }
1577
+ if ( !empty($ret_f) ) {
1578
+ $ret = $ret_f;
1579
+ }
1580
+ }
1581
+
1582
+ return $ret;
1583
+ }
1584
+
1585
+ /**
1586
+ * Retrieve attribute value
1587
+ * @param string|array $attrs Attributes to retrieve attribute value from
1588
+ * @param string $attr Attribute name to retrieve
1589
+ * @param bool (optional) $internal Whether only internal attributes should be evaluated (Default: TRUE)
1590
+ * @return string|bool Attribute value (Default: FALSE)
1591
+ */
1592
+ function get_attribute($attrs, $attr, $internal = true) {
1593
+ $ret = false;
1594
+ // Validate
1595
+ $attrs = $this->get_attributes($attrs, $internal);
1596
+ if ( $internal ) {
1597
+ $attr = $this->make_attribute_name($attr);
1598
+ }
1599
+ if ( isset($attrs[$attr]) ) {
1600
+ $ret = $attrs[$attr];
1601
+ }
1602
+ return $ret;
1603
+ }
1604
+
1605
+ /**
1606
+ * Checks if attribute exists
1607
+ * If supplied, the attribute's value is also validated
1608
+ * @param string|array $attrs Attributes to retrieve attribute value from
1609
+ * @param string $attr Attribute name to retrieve
1610
+ * @param mixed $value (optional) Attribute value to check for
1611
+ * @param bool $internal (optional) Whether to check only internal attributes (Default: TRUE)
1612
+ * @see get_attribute()
1613
+ * @return bool Whether or not attribute (with matching value if specified) exists
1614
+ */
1615
+ function has_attribute($attrs, $attr, $value = null, $internal = true) {
1616
+ $a = $this->get_attribute($attrs, $attr, $internal);
1617
+ $ret = false;
1618
+ if ( $a !== false ) {
1619
+ $ret = true;
1620
+ // Check value
1621
+ if ( !is_null($value) ) {
1622
+ if ( is_string($value) ) {
1623
+ $ret = ( $a == strval($value) ) ? true : false;
1624
+ } elseif ( is_bool($value) ) {
1625
+ $ret = ( !!$a == $value ) ? true : false;
1626
+ } else {
1627
+ $ret = false;
1628
+ }
1629
+ }
1630
+ }
1631
+ return $ret;
1632
+ }
1633
+
1634
+ /**
1635
+ * Build JS object of UI strings when initializing lightbox
1636
+ * @return array UI strings
1637
+ */
1638
+ private function build_labels() {
1639
+ $ret = array();
1640
+ /* Get all UI options */
1641
+ $prefix = 'txt_';
1642
+ $opt_strings = array_filter( array_keys( $this->options->get_items() ), function ( $opt ) use ( $prefix ) {
1643
+ return ( strpos( $opt, $prefix ) === 0 );
1644
+ } );
1645
+ if ( count( $opt_strings ) ) {
1646
+ /* Build array of UI options */
1647
+ foreach ( $opt_strings as $key ) {
1648
+ $name = substr( $key, strlen( $prefix ) );
1649
+ $ret[ $name ] = $this->options->get_value( $key );
1650
+ }
1651
+ }
1652
+ return $ret;
1653
+ }
1654
+ }
functions.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Functions
4
+ * Provides global access to specific functionality
5
+ * @package Simple Lightbox
6
+ * @author Archetyped
7
+ */
8
+
9
+ /* Template Tags */
10
+
11
+ /**
12
+ * Activate links in user-defined content
13
+ * @param string $content
14
+ * @return string Updated content with activated links
15
+ */
16
+ function slb_activate($content, $group = null) {
17
+ // Validate
18
+ if ( empty($content) ) {
19
+ return $content;
20
+ }
21
+ // Activate links
22
+ $content = $GLOBALS['slb']->activate_links($content, $group);
23
+ return $content;
24
+ }
grunt/jshint.js ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = function(grunt) {
2
+
3
+ grunt.config('jshint', {
4
+ options : {
5
+ reporter: require('jshint-stylish'),
6
+ curly : true,
7
+ eqeqeq : true,
8
+ immed : true,
9
+ latedef : true,
10
+ newcap : false,
11
+ noarg : true,
12
+ sub : true,
13
+ undef : true,
14
+ unused : true,
15
+ boss : true,
16
+ eqnull : true,
17
+ browser : true,
18
+ jquery : true,
19
+ globals : {}
20
+ },
21
+ grunt : {
22
+ options : {
23
+ node : true
24
+ },
25
+ src : ['Gruntfile.js', 'grunt/*.js']
26
+ },
27
+ all : {
28
+ options : {
29
+ globals : {
30
+ 'SLB' : true,
31
+ 'console' : true
32
+ }
33
+ },
34
+ src : ['<%= paths.js.files %>']
35
+ },
36
+ });
37
+
38
+ };
grunt/phplint.js ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = function(grunt) {
2
+
3
+ grunt.config('phplint', {
4
+ options : {
5
+ phpArgs : {
6
+ '-lf': null
7
+ }
8
+ },
9
+ all : {
10
+ src : '<%= paths.php.files %>'
11
+ }
12
+ });
13
+
14
+ };
grunt/sass.js ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = function(grunt) {
2
+
3
+ grunt.config('sass', {
4
+ options : {
5
+ outputStyle : 'compressed',
6
+ },
7
+ core : {
8
+ files : [{
9
+ expand : true,
10
+ cwd : '<%= paths.sass.base_src %>/',
11
+ dest : '<%= paths.sass.base_dest %>/',
12
+ src : ['<%= paths.sass.target %>', '<%= paths.sass.exclude %>'],
13
+ ext : '<%= paths.sass.ext %>'
14
+ }]
15
+ },
16
+ themes : {
17
+ options : {
18
+ },
19
+ files : [{
20
+ expand : true,
21
+ cwd : 'themes/',
22
+ src : ['*/**/*.scss', '<%= paths.sass.exclude %>'],
23
+ dest : '<%= paths.sass.dest %>/',
24
+ srcd : '<%= paths.sass.src %>/',
25
+ ext : '<%= paths.sass.ext %>',
26
+ rename : function(dest, matchedSrcPath, options) {
27
+ var path = [options.cwd, matchedSrcPath.replace(options.srcd, dest)].join('');
28
+ return path;
29
+ }
30
+ }]
31
+ }
32
+ });
33
+
34
+ };
grunt/uglify.js ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = function(grunt) {
2
+
3
+ grunt.config('uglify', {
4
+ options : {
5
+ mangle: false,
6
+ report: 'min'
7
+ },
8
+ all : {
9
+ files : [{
10
+ expand : true,
11
+ cwd : '',
12
+ dest : '',
13
+ src : ['<%= paths.js.files %>'],
14
+ rename : function(dest, srcPath) {
15
+ return srcPath.replace('/' + grunt.config.get('paths.js.src') + '/', '/' + grunt.config.get('paths.js.dest') + '/');
16
+ }
17
+ }]
18
+ },
19
+ });
20
+
21
+ };
grunt/watch.js ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = function(grunt) {
2
+
3
+ grunt.config('watch', {
4
+ phplint : {
5
+ files : '<%= paths.php.files_std %>',
6
+ tasks : ['phplint'],
7
+ options : {
8
+ spawn : false
9
+ }
10
+ },
11
+ sass_core : {
12
+ files : ['<%= paths.sass.base_src %>/**/*.scss'],
13
+ tasks : ['sass:core']
14
+ },
15
+ sass_themes : {
16
+ files : ['themes/**/<%= paths.sass.src %>/**/*.scss'],
17
+ tasks : ['sass:themes']
18
+ },
19
+ jshint : {
20
+ files : '<%= paths.js.files_std %>',
21
+ tasks : ['jshint:all'],
22
+ options : {
23
+ spawn : false
24
+ }
25
+ },
26
+ js : {
27
+ files : '<%= paths.js.files_std %>',
28
+ tasks : ['jshint:all', 'uglify:all'],
29
+ options : {
30
+ spawn : false
31
+ }
32
+ }
33
+ });
34
+
35
+ grunt.event.on('watch', function(action, filepath) {
36
+ // Determine task based on filepath
37
+ var get_ext = function(path) {
38
+ var ret = '';
39
+ var i = path.lastIndexOf('.');
40
+ if ( -1 !== i && i <= path.length ) {
41
+ ret = path.substr(i + 1);
42
+ }
43
+ return ret;
44
+ };
45
+ switch ( get_ext(filepath) ) {
46
+ // PHP
47
+ case 'php' :
48
+ grunt.config('paths.php.files', [filepath]);
49
+ break;
50
+ // JavaScript
51
+ case 'js' :
52
+ grunt.config('paths.js.files', [filepath]);
53
+ break;
54
+ }
55
+ });
56
+
57
+ };
includes/class-requirements-check.php ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Requirements Validation
4
+ *
5
+ * Used to ensure environment meets plugin requirements.
6
+ *
7
+ * @package Simple Lightbox
8
+ * @since 2.7.0
9
+ */
10
+
11
+ /**
12
+ * Plugin Requirements Validation class
13
+ *
14
+ * @since 2.7.0
15
+ */
16
+ class SLB_Requirements_Check {
17
+ /**
18
+ * Plugin name
19
+ *
20
+ * @var string
21
+ */
22
+ private $name = '';
23
+
24
+ /**
25
+ * Plugin file
26
+ *
27
+ * @var string
28
+ */
29
+ private $file = '';
30
+
31
+ /**
32
+ * Plugin dependencies
33
+ *
34
+ * @var array
35
+ */
36
+ private $deps = array(
37
+ 'php' => '5.4',
38
+ );
39
+
40
+ /**
41
+ * Dependency failures log
42
+ *
43
+ * @var array
44
+ */
45
+ private $fail = array();
46
+
47
+ /**
48
+ * URIs for notices, etc.
49
+ *
50
+ * @var array
51
+ */
52
+ private $uri = array();
53
+
54
+ /**
55
+ * Constructor
56
+ *
57
+ * @param array $args Requirements data.
58
+ * @return void
59
+ */
60
+ public function __construct( $args ) {
61
+ $args = (array) $args;
62
+ // Set properties.
63
+ foreach ( array_keys( get_class_vars( get_class( $this ) ) ) as $prop ) {
64
+ if ( ! isset( $args[ $prop ] ) ) {
65
+ continue;
66
+ }
67
+ // Merge array properties.
68
+ if ( is_array( $this->$prop ) && is_array( $args[ $prop ] ) ) {
69
+ $this->$prop = array_merge( $this->$prop, $args[ $prop ] );
70
+ continue;
71
+ }
72
+
73
+ // Set string properties.
74
+ if ( is_string( $this->$prop ) && is_scalar( $args[ $prop ] ) ) {
75
+ $this->$prop = (string) $args[ $prop ];
76
+ continue;
77
+ }
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Check if plugin passes all requirements
83
+ *
84
+ * @return bool Requirements check result.
85
+ */
86
+ public function passes() {
87
+ $result = true;
88
+ foreach ( $this->deps as $dep => $req ) {
89
+ $m = $dep . '_passes';
90
+ if ( ! method_exists( $this, $m ) ) {
91
+ continue;
92
+ }
93
+ $passes = $this->$m();
94
+ if ( ! $passes ) {
95
+ // Requirements do not pass.
96
+ $result = $passes;
97
+ // Log dependency failures.
98
+ $this->fail[] = $dep;
99
+ }
100
+ }
101
+ // Handle requirements failure.
102
+ if ( ! $result ) {
103
+ add_action( 'load-plugins.php', array( $this, 'handle_failure' ) );
104
+ }
105
+ return $result;
106
+ }
107
+
108
+ /**
109
+ * Handle requirements failure
110
+ *
111
+ * @return void
112
+ */
113
+ public function handle_failure() {
114
+ // Handle each failed dependency.
115
+ foreach ( $this->fail as $dep ) {
116
+ $m = $dep . '_handle_failure';
117
+ if ( method_exists( $this, $m ) ) {
118
+ $this->$m();
119
+ }
120
+ }
121
+ // Deactivate plugin.
122
+ deactivate_plugins( plugin_basename( $this->file ) );
123
+ }
124
+
125
+ /**
126
+ * Validates PHP version.
127
+ *
128
+ * @return bool PHP requirement passes.
129
+ */
130
+ private function php_passes() {
131
+ return version_compare( PHP_VERSION, $this->deps['php'], '>=' );
132
+ }
133
+
134
+ /**
135
+ * Handle PHP requirement failure
136
+ *
137
+ * @return void
138
+ */
139
+ private function php_handle_failure() {
140
+ // Clear activation query variable from request (stop UI notices).
141
+ unset( $_GET['activate'] );
142
+ // Display notice to user.
143
+ add_action( 'admin_notices', array( $this, 'php_notice' ) );
144
+ }
145
+
146
+ /**
147
+ * Display requirements failure notice and deactivate plugin.
148
+ *
149
+ * @return void
150
+ */
151
+ public function php_notice() {
152
+ global $slb_requirements;
153
+ // Display message to user.
154
+ $link = (object) array(
155
+ /* translators: 1: Plugin name */
156
+ 'title' => sprintf( __( 'Learn more about %1$s\'s requirements', 'simple-lightbox' ), $this->name ),
157
+ /* translators: Plugin requirements link text. */
158
+ 'text' => __( 'Learn More', 'simple-lightbox' ),
159
+ );
160
+ // Full link.
161
+ $link = sprintf( '<a target="_blank" href="%1$s" title="%2$s">%3$s</a>', $this->uri['reference'], esc_attr( $link->title ), esc_html( $link->text ) );
162
+ /* translators: 1: Plugin name. 2: PHP version requirement. 3: Plugin requirements link. */
163
+ $err_msg = sprintf( __( '%1$s requires PHP %2$s or higher. Please have your hosting provider update PHP to enable Simple Lightbox. (%3$s)', 'simple-lightbox' ), $this->name, $this->deps['php'], $link );
164
+ ?>
165
+ <div class="error"><p><?php echo $err_msg; ?></p></div>
166
+ <?php
167
+ }
168
+ }
includes/class.admin.php CHANGED
@@ -11,24 +11,6 @@ class SLB_Admin extends SLB_Base {
11
 
12
  protected $mode = 'sub';
13
 
14
- /* Files */
15
-
16
- var $scripts = array (
17
- 'admin' => array (
18
- 'file' => 'client/js/lib.admin.js',
19
- 'deps' => array('[core]'),
20
- 'context' => array( 'admin_page_slb_options' ),
21
- 'in_footer' => true,
22
- ),
23
- );
24
-
25
- var $styles = array (
26
- 'admin' => array (
27
- 'file' => 'client/css/admin.css',
28
- 'context' => array( 'admin_page_slb_options', 'admin_page_plugins' )
29
- )
30
- );
31
-
32
  /* Properties */
33
 
34
  /**
@@ -36,13 +18,17 @@ class SLB_Admin extends SLB_Base {
36
  * Set on initialization
37
  * @var obj
38
  */
39
- var $parent = null;
40
 
41
  /**
42
  * Messages
43
  * @var array
44
  */
45
- var $messages = array();
 
 
 
 
46
 
47
  /* Views */
48
 
@@ -53,7 +39,7 @@ class SLB_Admin extends SLB_Base {
53
  * > Val: Menu properties
54
  * @var array
55
  */
56
- var $menus = array();
57
 
58
  /**
59
  * Custom admin pages
@@ -62,7 +48,7 @@ class SLB_Admin extends SLB_Base {
62
  * > Val: Page properties
63
  * @var array
64
  */
65
- var $pages = array();
66
 
67
  /**
68
  * Custom admin sections
@@ -71,50 +57,79 @@ class SLB_Admin extends SLB_Base {
71
  * > Val: Section properties
72
  * @var array
73
  */
74
- var $sections = array();
75
 
76
  /**
77
- * Reset options
78
- * Indexed Array
79
  * @var array
80
  */
81
- var $resets = array();
82
 
83
  /* Constructor */
84
 
85
- /**
86
- * TODO Determine if $parent needed
87
- */
88
- function __construct(&$parent) {
89
  parent::__construct();
90
- //Set parent
91
  if ( is_object($parent) )
92
- $this->parent =& $parent;
93
  }
94
 
95
  /* Init */
96
 
97
  protected function _hooks() {
98
  parent::_hooks();
99
- //Init
100
  add_action('admin_menu', $this->m('init_menus'), 11);
101
 
102
- //Reset Settings
103
  add_action('admin_action_' . $this->add_prefix('admin'), $this->m('handle_action'));
104
 
105
- //Notices
106
  add_action('admin_notices', $this->m('handle_notices'));
107
 
108
- //Plugin listing
109
  add_filter('plugin_action_links_' . $this->util->get_plugin_base_name(), $this->m('plugin_action_links'), 10, 4);
 
110
  add_action('in_plugin_update_message-' . $this->util->get_plugin_base_name(), $this->m('plugin_update_message'), 10, 2);
111
  add_filter('site_transient_update_plugins', $this->m('plugin_update_transient'));
112
  }
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  /* Handlers */
115
 
116
- function handle_action() {
117
- //Parse action
 
 
 
118
  $t = 'type';
119
  $g = 'group';
120
  $o = 'obj';
@@ -123,11 +138,11 @@ class SLB_Admin extends SLB_Base {
123
  $this->add_prefix_ref($o);
124
  $r =& $_REQUEST;
125
 
126
- //Retrieve view that initiated the action
127
  if ( isset($r[$t]) && 'view' == $r[$t] ) {
128
  if ( isset($r[$g]) && ( $prop = $r[$g] . 's' ) && property_exists($this, $prop) && is_array($this->{$prop}) && isset($r[$o]) && isset($this->{$prop}[$r[$o]]) ) {
129
  $view =& $this->{$prop}[$r[$o]];
130
- //Pass request to view
131
  $view->do_callback();
132
  }
133
  }
@@ -138,13 +153,13 @@ class SLB_Admin extends SLB_Base {
138
  * Messages are localized upon display
139
  * @uses `admin_notices` action hook to display messages
140
  */
141
- function handle_notices() {
142
  $msgs = $this->util->apply_filters('admin_messages', array());
143
  foreach ( $msgs as $mid => $msg ) {
144
- //Filter out empty messages
145
  if ( empty($msg) )
146
  continue;
147
- //Build and display message
148
  $mid = $this->add_prefix('msg_' . $mid);
149
  ?>
150
  <div id="<?php echo esc_attr($mid); ?>" class="updated fade">
@@ -156,21 +171,6 @@ class SLB_Admin extends SLB_Base {
156
  }
157
  }
158
 
159
- /**
160
- * Displays notices for admin operations
161
- */
162
- function show_notices() {
163
- if ( is_admin() && isset($_REQUEST[$this->add_prefix('action')]) ) {
164
- $action = $_REQUEST[$this->add_prefix('action')];
165
- $msg = null;
166
- if ( $action ) {
167
- $msg = $this->get_message($action);
168
- if ( ! empty($msg) ) {
169
-
170
- }
171
- }
172
- }
173
- }
174
  /* Views */
175
 
176
  /**
@@ -178,56 +178,45 @@ class SLB_Admin extends SLB_Base {
178
  * Section is added to specified admin section/menu
179
  * @uses `admin_init` hook
180
  */
181
- function init_menus() {
182
- //Add top level menus (when necessary)
183
- /**
184
- * @var SLB_Admin_Menu
185
- */
186
  $menu;
187
  foreach ( $this->menus as $menu ) {
188
- //Register menu
189
  $hook = add_menu_page($menu->get_label('title'), $menu->get_label('menu'), $menu->get_capability(), $menu->get_id(), $menu->get_callback());
190
- //Add hook to menu object
191
  $menu->set_hookname($hook);
192
  $this->menus[$menu->get_id_raw()] =& $menu;
193
  }
194
 
195
- /**
196
- * @var SLB_Admin_Page
197
- */
198
  $page;
199
- //Add subpages
200
  foreach ( $this->pages as $page ) {
201
- //Build Arguments
202
  $args = array ( $page->get_label('header'), $page->get_label('menu'), $page->get_capability(), $page->get_id(), $page->get_callback() );
203
  $f = null;
204
- //Handle pages for default WP menus
205
  if ( $page->is_parent_wp() ) {
206
  $f = 'add_' . $page->get_parent() . '_page';
207
  }
208
 
209
- //Handle pages for custom menus
210
  if ( ! function_exists($f) ) {
211
  array_unshift( $args, $page->get_parent() );
212
  $f = 'add_submenu_page';
213
  }
214
 
215
- //Add admin page
216
  $hook = call_user_func_array($f, $args);
217
- //Save hook to page properties
218
  $page->set_hookname($hook);
219
  $this->pages[$page->get_id_raw()] =& $page;
220
  }
221
 
222
- //Add sections
223
- /**
224
- * @var SLB_Admin_Section
225
- */
226
  $section;
227
  foreach ( $this->sections as $section ) {
228
  add_settings_section($section->get_id(), $section->get_title(), $section->get_callback(), $section->get_parent());
229
- if ( $section->is_options_valid() )
230
- register_setting($section->get_parent(), $section->get_id(), $section->get_options()->m('validate'));
231
  }
232
  }
233
 
@@ -239,38 +228,40 @@ class SLB_Admin extends SLB_Base {
239
  * @param string $type View type
240
  * @param string $id Unique view ID
241
  * @param array $args Arguments to pass to view constructor
242
- * @return int|bool View ID (FALSE if view was not properly initialized)
243
  */
244
- private function add_view($type, $id, $args) {
245
- //Validate request
246
  $class = $this->add_prefix('admin_' . $type);
247
  $collection = $type . 's';
248
- if ( !class_exists($class) || !property_exists($this, $collection) || !is_array($this->{$collection}) )
249
- return false;
250
- //Create new instance
 
 
251
  $r = new ReflectionClass($class);
252
- $view =& $r->newInstanceArgs($args);
253
- if ( $view->is_valid() )
254
  $this->{$collection}[$id] =& $view;
255
- else
256
- $id = false;
257
- unset($view, $r);
258
- return $id;
259
  }
260
 
261
  /**
262
- * Add reset option to plugin action links
263
- * @param string $id Unique ID
264
- * @param array $labels Text for reset instance
265
- * > title - Link text (also title attribute value)
266
- * > confirm - Confirmation message
267
- * > success - Success message
268
- * > failure - Failure message
269
- * @param SLB_Options|array $options Options instance (or instance + specific groups)
 
 
270
  */
271
- function add_reset($id, $labels, $options) {
272
  $args = func_get_args();
273
- return $this->add_view('reset', $id, $args);
274
  }
275
 
276
  /*-** Menus **-*/
@@ -280,9 +271,9 @@ class SLB_Admin extends SLB_Base {
280
  * @param string $id Menu ID
281
  * @param string|array $labels Text labels
282
  * @param int $pos (optional) Menu position in navigation (index order)
283
- * @return string Menu ID
284
  */
285
- function add_menu($id, $labels, $position = null) {
286
  $args = array ( $id, $labels, null, null, null, $position );
287
  return $this->add_view('menu', $id, $args);
288
  }
@@ -293,19 +284,15 @@ class SLB_Admin extends SLB_Base {
293
  * Add admin page
294
  * @uses this->pages
295
  * @param string $id Page ID (unique)
 
296
  * @param string|array $labels Text labels (Associative array for multiple labels)
297
  * > menu: Menu title
298
  * > header: Page header
299
- * @param string $menu Menu ID to add page to
300
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
301
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
302
- * @param callback $callback (optional) Callback for custom page building
303
  * @param string $capability (optional) Custom capability for accessing page
304
- * @return string Page ID
305
  */
306
- function add_page($id, $parent, $labels, $options = null, $callback = null, $capability = null) {
307
  $args = func_get_args();
308
- wp_enqueue_script('postbox');
309
  return $this->add_view('page', $id, $args);
310
  }
311
 
@@ -315,24 +302,21 @@ class SLB_Admin extends SLB_Base {
315
  * Add admin page to a standard WP menu
316
  * @uses this->add_page()
317
  * @param string $id Page ID (unique)
 
318
  * @param string|array $labels Text labels (Associative array for multiple labels)
319
  * > menu: Menu title
320
  * > header: Page header
321
- * @param string $menu Name of WP menu to add page to
322
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
323
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
324
- * @param callback $callback (optional) Callback for custom page building
325
  * @param string $capability (optional) Custom capability for accessing page
326
- * @return string Page ID
327
- */
328
- function add_wp_page($id, $parent, $labels, $options = null, $callback = null, $capability = null) {
329
- //Add page
330
- $pid = $this->add_page($id, $parent, $labels, $options, $callback, $capability);
331
- //Set parent as WP
332
- if ( $pid ) {
333
- $this->pages[$pid]->set_parent_wp();
334
  }
335
- return $pid;
336
  }
337
 
338
  /**
@@ -341,15 +325,11 @@ class SLB_Admin extends SLB_Base {
341
  * @uses this->add_wp_page()
342
  * @param string $id Page ID (unique)
343
  * @param string|array $labels Text labels (Associative array for multiple labels)
344
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
345
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
346
- * @param callback $callback (optional) Callback for custom page building
347
  * @param string $capability (optional) Custom capability for accessing page
348
- * @return string Page ID
349
  */
350
- function add_dashboard_page($id, $labels, $options = null, $callback = null, $capability = null) {
351
- $id = $this->add_wp_page($id, 'dashboard', $labels, $options, $callback, $capability);
352
- return $id;
353
  }
354
 
355
  /**
@@ -358,15 +338,11 @@ class SLB_Admin extends SLB_Base {
358
  * @uses this->add_wp_page()
359
  * @param string $id Page ID (unique)
360
  * @param string|array $labels Text labels (Associative array for multiple labels)
361
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
362
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
363
- * @param callback $callback (optional) Callback for custom page building
364
  * @param string $capability (optional) Custom capability for accessing page
365
  * @return string Page ID
366
  */
367
- function add_comments_page($id, $labels, $options = null, $callback = null, $capability = null) {
368
- $id = $this->add_wp_page($id, 'comments', $labels, $options, $callback, $capability);
369
- return $id;
370
  }
371
 
372
  /**
@@ -375,15 +351,11 @@ class SLB_Admin extends SLB_Base {
375
  * @uses this->add_wp_page()
376
  * @param string $id Page ID (unique)
377
  * @param string|array $labels Text labels (Associative array for multiple labels)
378
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
379
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
380
- * @param callback $callback (optional) Callback for custom page building
381
  * @param string $capability (optional) Custom capability for accessing page
382
  * @return string Page ID
383
  */
384
- function add_links_page($id, $labels, $options = null, $callback = null, $capability = null) {
385
- $id = $this->add_wp_page($id, 'links', $labels, $options, $callback, $capability);
386
- return $id;
387
  }
388
 
389
 
@@ -393,15 +365,11 @@ class SLB_Admin extends SLB_Base {
393
  * @uses this->add_wp_page()
394
  * @param string $id Page ID (unique)
395
  * @param string|array $labels Text labels (Associative array for multiple labels)
396
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
397
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
398
- * @param callback $callback (optional) Callback for custom page building
399
  * @param string $capability (optional) Custom capability for accessing page
400
  * @return string Page ID
401
  */
402
- function add_posts_page($id, $labels, $options = null, $callback = null, $capability = null) {
403
- $id = $this->add_wp_page($id, 'posts', $labels, $options, $callback, $capability);
404
- return $id;
405
  }
406
 
407
  /**
@@ -410,15 +378,11 @@ class SLB_Admin extends SLB_Base {
410
  * @uses this->add_wp_page()
411
  * @param string $id Page ID (unique)
412
  * @param string|array $labels Text labels (Associative array for multiple labels)
413
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
414
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
415
- * @param callback $callback (optional) Callback for custom page building
416
  * @param string $capability (optional) Custom capability for accessing page
417
  * @return string Page ID
418
  */
419
- function add_pages_page($id, $labels, $options = null, $callback = null, $capability = null) {
420
- $id = $this->add_wp_page($id, 'pages', $labels, $options, $callback, $capability);
421
- return $id;
422
  }
423
 
424
  /**
@@ -427,15 +391,11 @@ class SLB_Admin extends SLB_Base {
427
  * @uses this->add_wp_page()
428
  * @param string $id Page ID (unique)
429
  * @param string|array $labels Text labels (Associative array for multiple labels)
430
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
431
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
432
- * @param callback $callback (optional) Callback for custom page building
433
  * @param string $capability (optional) Custom capability for accessing page
434
  * @return string Page ID
435
  */
436
- function add_media_page($id, $labels, $options = null, $callback = null, $capability = null) {
437
- $id = $this->add_wp_page($id, 'media', $labels, $options, $callback, $capability);
438
- return $id;
439
  }
440
 
441
  /**
@@ -444,15 +404,11 @@ class SLB_Admin extends SLB_Base {
444
  * @uses this->add_wp_page()
445
  * @param string $id Page ID (unique)
446
  * @param string|array $labels Text labels (Associative array for multiple labels)
447
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
448
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
449
- * @param callback $callback (optional) Callback for custom page building
450
  * @param string $capability (optional) Custom capability for accessing page
451
  * @return string Page ID
452
  */
453
- function add_theme_page($id, $labels, $options = null, $callback = null, $capability = null) {
454
- $id = $this->add_wp_page($id, 'theme', $labels, $options, $callback, $capability);
455
- return $id;
456
  }
457
 
458
  /**
@@ -461,15 +417,11 @@ class SLB_Admin extends SLB_Base {
461
  * @uses this->add_wp_page()
462
  * @param string $id Page ID (unique)
463
  * @param string|array $labels Text labels (Associative array for multiple labels)
464
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
465
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
466
- * @param callback $callback (optional) Callback for custom page building
467
  * @param string $capability (optional) Custom capability for accessing page
468
  * @return string Page ID
469
  */
470
- function add_plugins_page($id, $labels, $options = null, $callback = null, $capability = null) {
471
- $id = $this->add_wp_page($id, 'plugins', $labels, $options, $callback, $capability);
472
- return $id;
473
  }
474
 
475
  /**
@@ -478,15 +430,11 @@ class SLB_Admin extends SLB_Base {
478
  * @uses this->add_wp_page()
479
  * @param string $id Page ID (unique)
480
  * @param string|array $labels Text labels (Associative array for multiple labels)
481
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
482
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
483
- * @param callback $callback (optional) Callback for custom page building
484
  * @param string $capability (optional) Custom capability for accessing page
485
  * @return string Page ID
486
  */
487
- function add_options_page($id, $labels, $options = null, $callback = null, $capability = null) {
488
- $id = $this->add_wp_page($id, 'options', $labels, $options, $callback, $capability);
489
- return $id;
490
  }
491
 
492
  /**
@@ -495,15 +443,11 @@ class SLB_Admin extends SLB_Base {
495
  * @uses this->add_wp_page()
496
  * @param string $id Page ID (unique)
497
  * @param string|array $labels Text labels (Associative array for multiple labels)
498
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
499
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
500
- * @param callback $callback (optional) Callback for custom page building
501
  * @param string $capability (optional) Custom capability for accessing page
502
  * @return string Page ID
503
  */
504
- function add_management_page($id, $labels, $options = null, $callback = null, $capability = null) {
505
- $id = $this->add_wp_page($id, 'management', $labels, $options, $callback, $capability);
506
- return $id;
507
  }
508
 
509
  /**
@@ -511,15 +455,11 @@ class SLB_Admin extends SLB_Base {
511
  * @uses this->add_wp_page()
512
  * @param string $id Page ID (unique)
513
  * @param string|array $labels Text labels (Associative array for multiple labels)
514
- * @param obj|array $options (optional) Options object (Use array to define options object & specific group(s))
515
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
516
- * @param callback $callback (optional) Callback for custom page building
517
  * @param string $capability (optional) Custom capability for accessing page
518
  * @return string Page ID
519
  */
520
- function add_users_page($id, $labels, $options = null, $callback = null, $capability = null) {
521
- $id = $this->add_wp_page($id, 'users', $labels, $options, $callback, $capability);
522
- return $id;
523
  }
524
 
525
  /* Section */
@@ -530,20 +470,17 @@ class SLB_Admin extends SLB_Base {
530
  * @param string $id Unique section ID
531
  * @param string $page Page ID
532
  * @param string $labels Label text
533
- * @param obj|array $options Options object (Use array to define options object & specific group(s))
534
- * > Array Example: array($options, 'group_1') or array($options, array('group_1', 'group_3'))
535
- * @param callback $callback (optional) Callback for custom building
536
- * @return string Section ID
537
  */
538
- function add_section($id, $parent, $labels, $options = null, $callback = null) {
539
- $section = new SLB_Admin_Section($id, $parent, $labels, $options, $callback);
 
540
 
541
- //Add Section
542
  if ( $section->is_valid() )
543
- $this->sections[$id] =& $section;
544
- else
545
- $id = false;
546
- return $id;
547
  }
548
 
549
  /* Operations */
@@ -556,9 +493,9 @@ class SLB_Admin extends SLB_Base {
556
  * @param $plugin_data
557
  * @param $context
558
  */
559
- function plugin_action_links($actions, $plugin_file, $plugin_data, $context) {
560
  global $admin_page_hooks;
561
- //Add link to settings (only if active)
562
  if ( is_plugin_active($this->util->get_plugin_base_name()) ) {
563
  /* Get Actions */
564
 
@@ -579,33 +516,55 @@ class SLB_Admin extends SLB_Base {
579
  }
580
  }
581
 
582
- /* Get reset links */
583
  $type = 'title';
584
- foreach ( $this->resets as $reset ) {
585
- if ( !$reset->has_label($type) )
586
  continue;
587
- $id = 'reset_' . $reset->get_id();
588
  $acts[] = (object) array (
589
- 'id' => $id,
590
- 'label' => $reset->get_label($type),
591
- 'uri' => $reset->get_uri(),
592
- 'attributes' => $reset->get_link_attr()
593
  );
594
  }
 
595
 
596
- //Add links
597
  $links = array();
598
  foreach ( $acts as $act ) {
599
  $links[$act->id] = $this->util->build_html_link($act->uri, $act->label, $act->attributes);
600
  }
601
 
602
- //Add links
603
  $actions = array_merge($links, $actions);
604
  }
605
  return $actions;
606
  }
607
-
608
- /*-** START: Refactor **-*/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
609
 
610
  /**
611
  * Adds additional message for plugin updates
@@ -614,7 +573,7 @@ class SLB_Admin extends SLB_Base {
614
  * @var array $plugin_data Current plugin data
615
  * @var object $r Update response data
616
  */
617
- function plugin_update_message($plugin_data, $r) {
618
  if ( !isset($r->new_version) )
619
  return false;
620
  if ( stripos($r->new_version, 'beta') !== false ) {
@@ -630,7 +589,7 @@ class SLB_Admin extends SLB_Base {
630
  * @param obj $transient Transient data
631
  * @return obj Modified transient data
632
  */
633
- function plugin_update_transient($transient) {
634
  $n = $this->util->get_plugin_base_name();
635
  if ( isset($transient->response) && isset($transient->response[$n]) && is_object($transient->response[$n]) && !isset($transient->response[$n]->upgrade_notice) ) {
636
  $r =& $transient->response[$n];
@@ -645,7 +604,7 @@ class SLB_Admin extends SLB_Base {
645
  * @param obj $r Response data from plugin update API
646
  * @return string Message (Default: empty string)
647
  */
648
- function plugin_update_get_message($r) {
649
  $msg = '';
650
  $cls_notice = $this->add_prefix('notice');
651
  if ( !is_object($r) || !isset($r->new_version) )
@@ -663,7 +622,7 @@ class SLB_Admin extends SLB_Base {
663
  * @param string $msg_id Message ID
664
  * @return string Message text
665
  */
666
- function get_message($msg_id) {
667
  $msg = '';
668
  $msgs = $this->get_messages();
669
  if ( is_string($msg_id) && isset($msgs[$msg_id]) ) {
@@ -680,7 +639,7 @@ class SLB_Admin extends SLB_Base {
680
  */
681
  function get_messages() {
682
  if ( empty($this->messages) ) {
683
- //Initialize messages if necessary
684
  $this->messages = array(
685
  'reset' => __('The settings have been reset', 'simple-lightbox'),
686
  'beta' => __('<strong class="%1$s">Notice:</strong> This update is a <strong class="%1$s">Beta version</strong>. It is highly recommended that you test the update on a test server before updating the plugin on a production server.', 'simple-lightbox'),
@@ -696,881 +655,7 @@ class SLB_Admin extends SLB_Base {
696
  * @param string $id Message ID
697
  * @param string $text Message text
698
  */
699
- function set_message($id, $text) {
700
  $this->messages[trim($id)] = $text;
701
  }
702
- /*-** END: Refactor **-*/
703
-
704
- }
705
-
706
- /**
707
- * Admin View Base functionality
708
- * Core functionality for Menus/Pages/Sections
709
- * @package Simple Lightbox
710
- * @subpackage Admin
711
- * @author Archetyped
712
- */
713
- class SLB_Admin_View extends SLB_Base {
714
- /* Properties */
715
-
716
- /**
717
- * Unique ID
718
- * @var string
719
- */
720
- var $id = null;
721
-
722
- /**
723
- * Labels
724
- * @var array (Associative)
725
- */
726
- var $labels = array();
727
-
728
- /**
729
- * Options object to use
730
- * @var SLB_Options
731
- */
732
- var $options = null;
733
-
734
- /**
735
- * Option groups to use
736
- * If empty, use entire options object
737
- * @var array
738
- */
739
- var $option_groups = array();
740
-
741
- /**
742
- * Option building arguments
743
- * @var array
744
- */
745
- var $option_args = array();
746
-
747
- /**
748
- * Function to handle building UI
749
- * @var callback
750
- */
751
- var $callback = null;
752
-
753
- /**
754
- * Capability for access control
755
- * @var string
756
- */
757
- var $capability = 'manage_options';
758
-
759
- /**
760
- * Icon to use
761
- * @var string
762
- */
763
- var $icon = null;
764
-
765
- /**
766
- * View parent ID/Slug
767
- * @var string
768
- */
769
- var $parent = null;
770
-
771
- /**
772
- * Whether parent is a custom view or a default WP one
773
- * @var bool
774
- */
775
- var $parent_custom = true;
776
-
777
- /**
778
- * If view requires a parent
779
- * @var bool
780
- */
781
- var $parent_required = false;
782
-
783
- /**
784
- * WP-Generated hook name for view
785
- * @var string
786
- */
787
- var $hookname = null;
788
-
789
- /**
790
- * Messages to be displayed
791
- * Indexed Array
792
- * @var array
793
- */
794
- var $messages = array();
795
-
796
- /**
797
- * Required properties
798
- * Associative array
799
- * > Key: Property name
800
- * > Value: Required data type
801
- * @var array
802
- */
803
- protected $required = array();
804
-
805
- /**
806
- * Default required properties
807
- * Merged into $required array with this->init_required()
808
- * @see this->required for more information
809
- * @var array
810
- */
811
- protected $_required = array ( 'id' => 'string', 'labels' => 'array' );
812
-
813
- /* Init */
814
-
815
- function __construct($id, $labels, $options = null, $callback = null, $capability = null, $icon = null) {
816
- parent::__construct();
817
-
818
- $this->init_required();
819
- $this->set_id($id);
820
- $this->set_labels($labels);
821
- $this->set_options($options);
822
- $this->set_callback($callback);
823
- $this->set_capability($capability);
824
- $this->set_icon($icon);
825
- }
826
-
827
- function init_required() {
828
- $this->required = array_merge($this->_required, $this->required);
829
- //Check for parent requirement
830
- if ( $this->parent_required )
831
- $this->required['parent'] = 'string';
832
- }
833
-
834
- /* Property Methods */
835
-
836
- /**
837
- * Retrieve ID (Formatted by default)
838
- * @param bool $formatted (optional) Whether ID should be formatted for external use or not
839
- * @return string ID
840
- */
841
- function get_id($formatted = true) {
842
- $id = $this->id;
843
- if ( $formatted )
844
- $this->add_prefix_ref($id);
845
- return $id;
846
- }
847
-
848
- /**
849
- * Retrieve raw ID
850
- * @return string Raw ID
851
- */
852
- function get_id_raw() {
853
- return $this->get_id(false);
854
- }
855
-
856
- /**
857
- * Set ID
858
- * @param string ID
859
- */
860
- function set_id($id) {
861
- if ( is_scalar($id) ) {
862
- $this->id = trim(strval($id));
863
- return true;
864
- }
865
- return false;
866
- }
867
-
868
- /**
869
- * Retrieve label
870
- * Uses first label (or default if defined) if specified type does not exist
871
- * @param string $type Label type to retrieve
872
- * @param string $default (optional) Default value if label type does not exist
873
- * @return string Label text
874
- */
875
- function get_label($type, $default = null) {
876
- //Retrieve existing label type
877
- if ( $this->has_label($type) )
878
- return $this->labels[$type];
879
- //Use default label if type is not set
880
- if ( empty($default) && !empty($this->labels) ) {
881
- reset($this->labels);
882
- $default = current($this->labels);
883
- }
884
-
885
- return ( empty($default) ) ? '' : $default;
886
- }
887
-
888
- /**
889
- * Set text labels
890
- * @param array|string $labels
891
- */
892
- function set_labels($labels) {
893
- if ( empty($labels) )
894
- return false;
895
- //Single string
896
- if ( is_string($labels) ) {
897
- $labels = array ( $labels );
898
- }
899
-
900
- //Array
901
- if ( is_array($labels) ) {
902
- //Merge with existing labels
903
- if ( empty($this->labels) || !is_array($this->labels) ) {
904
- $this->labels = array();
905
- }
906
- $this->labels = array_merge($this->labels, $labels);
907
- }
908
- }
909
-
910
- /**
911
- * Set single text label
912
- * @uses this->set_labels()
913
- * @param string $type Label type to set
914
- * @param string $value Label value
915
- */
916
- function set_label($type, $value) {
917
- if ( is_string($type) && is_string($value) ) {
918
- $label = array( $type => $value );
919
- $this->set_labels($label);
920
- }
921
- }
922
-
923
- /**
924
- * Checks if specified label is set on view
925
- * @param string $type Label type
926
- * @return bool TRUE if label exists, FALSE otherwise
927
- */
928
- function has_label($type) {
929
- return ( isset($this->labels[$type]) );
930
- }
931
-
932
- /**
933
- * Retrieve instance options
934
- * @return SLB_Options Options instance
935
- */
936
- function &get_options() {
937
- return $this->options;
938
- }
939
-
940
- /**
941
- * Set options object
942
- * @param obj|array $options Options instance
943
- * > If array, Options instance and specific groups are specified
944
- * > 0: Options instance
945
- * > 1: Group(s)
946
- */
947
- function set_options($options) {
948
- if ( empty($options) )
949
- return false;
950
-
951
- $groups = null;
952
-
953
- if ( is_array($options) ) {
954
- $options = array_values($options);
955
- //Set option groups
956
- if ( isset($options[1]) ) {
957
- $groups = $options[1];
958
- }
959
- //Set options object
960
- $options =& $options[0];
961
- }
962
-
963
- if ( $this->util->is_a($options, 'Options') ) {
964
- //Save options
965
- $this->options =& $options;
966
-
967
- //Save option groups for valid options
968
- $this->set_option_groups($groups);
969
- }
970
- }
971
-
972
- /**
973
- * Set option groups
974
- * @param string|array $groups Specified group(s)
975
- */
976
- function set_option_groups($groups) {
977
- if ( empty($groups) )
978
- return false;
979
-
980
- //Validate data
981
- if ( !is_array($groups) ) {
982
- if ( is_scalar($groups) ) {
983
- $groups = array(strval($groups));
984
- }
985
- }
986
-
987
- if ( is_array($groups) ) {
988
- $this->option_groups = $groups;
989
- }
990
- }
991
-
992
- /**
993
- * Retrieve view messages
994
- * @return array Messages
995
- */
996
- function &get_messages() {
997
- if ( !is_array($this->messages) )
998
- $this->messages = array();
999
- return $this->messages;
1000
- }
1001
-
1002
- /**
1003
- * Save message
1004
- * @param string $text Message text
1005
- */
1006
- function set_message($text) {
1007
- $msgs =& $this->get_messages();
1008
- $text = trim($text);
1009
- if ( empty($msgs) && !empty($text) )
1010
- $this->util->add_filter('admin_messages', $this->m('do_messages'));
1011
- $msgs[] = $text;
1012
- }
1013
-
1014
- /**
1015
- * Add messages to array
1016
- * Called by internal `admin_messages` filter hook
1017
- * @param array $msgs Aggregated messages
1018
- * @return array Merged messages array
1019
- */
1020
- function do_messages($msgs = array()) {
1021
- $m =& $this->get_messages();
1022
- if ( !empty($m) )
1023
- $msgs = array_merge($msgs, $m);
1024
- return $msgs;
1025
- }
1026
-
1027
- /**
1028
- * Retrieve view callback
1029
- * @return callback Callback
1030
- */
1031
- function get_callback() {
1032
- return ( $this->has_callback() ) ? $this->callback : $this->m('handle');
1033
- }
1034
-
1035
- /**
1036
- * Set callback function for building item
1037
- * @param callback $callback Callback function to use
1038
- */
1039
- function set_callback($callback) {
1040
- if ( is_callable($callback) )
1041
- $this->callback = $callback;
1042
- }
1043
-
1044
- function has_callback() {
1045
- return ( !empty($this->callback) ) ? true : false;
1046
- }
1047
-
1048
- function do_callback() {
1049
- call_user_func($this->get_callback());
1050
- }
1051
-
1052
- /**
1053
- * Retrieve capability
1054
- * @return string Capability
1055
- */
1056
- function get_capability() {
1057
- return $this->capability;
1058
- }
1059
-
1060
- /**
1061
- * Set capability for access control
1062
- * @param string $capability Capability
1063
- */
1064
- function set_capability($capability) {
1065
- if ( is_string($capability) && !empty($capability) )
1066
- $this->capability = $capability;
1067
- }
1068
-
1069
- /**
1070
- * Set icon
1071
- * @param string $icon Icon URI
1072
- */
1073
- function set_icon($icon) {
1074
- if ( !empty($icon) && is_string($icon) )
1075
- $this->icon = $icon;
1076
- }
1077
-
1078
- function get_hookname() {
1079
- return ( empty($this->hookname) ) ? '' : $this->hookname;
1080
- }
1081
-
1082
- /**
1083
- * Set hookname
1084
- * @param string $hookname Hookname value
1085
- */
1086
- function set_hookname($hookname) {
1087
- if ( !empty($hookname) && is_string($hookname) )
1088
- $this->hookname = $hookname;
1089
- }
1090
-
1091
- /**
1092
- * Retrieve parent
1093
- * Formats parent ID for custom parents
1094
- * @return string Parent ID
1095
- */
1096
- function get_parent() {
1097
- $parent = $this->parent;
1098
- return ( $this->is_parent_custom() ) ? $this->add_prefix($parent) : $parent;
1099
- }
1100
-
1101
- /**
1102
- * Set parent for view
1103
- * @param string $parent Parent ID
1104
- */
1105
- function set_parent($parent) {
1106
- if ( $this->parent_required ) {
1107
- if ( !empty($parent) && is_string($parent) )
1108
- $this->parent = $parent;
1109
- } else {
1110
- $this->parent = null;
1111
- }
1112
- }
1113
-
1114
- /**
1115
- * Specify whether parent is a custom view or a WP view
1116
- * @param bool $custom (optional) TRUE if custom, FALSE if WP
1117
- */
1118
- function set_parent_custom($custom = true) {
1119
- if ( $this->parent_required ) {
1120
- $this->parent_custom = !!$custom;
1121
- }
1122
- }
1123
-
1124
- /**
1125
- * Set parent as WP view
1126
- * @uses this->set_parent_custom()
1127
- */
1128
- function set_parent_wp() {
1129
- $this->set_parent_custom(false);
1130
- }
1131
-
1132
- /**
1133
- * Get view URI
1134
- * URI Structures:
1135
- * > Top Level Menus: admin.php?page={menu_id}
1136
- * > Pages: [parent_page_file.php|admin.php]?page={page_id}
1137
- * > Section: [parent_menu_uri]#{section_id}
1138
- *
1139
- * @uses $admin_page_hooks to determine if page is child of default WP page
1140
- * @return string Object URI
1141
- */
1142
- function get_uri($file = null, $format = null) {
1143
- static $page_hooks = null;
1144
- $uri = '';
1145
- if ( empty($file) )
1146
- $file = 'admin.php';
1147
- if ( $this->is_child() ) {
1148
- $parent = str_replace('_page_' . $this->get_id(), '', $this->get_hookname());
1149
- if ( is_null($page_hooks) ) {
1150
- $page_hooks = array_flip($GLOBALS['admin_page_hooks']);
1151
- }
1152
- if ( isset($page_hooks[$parent]) )
1153
- $file = $page_hooks[$parent];
1154
- }
1155
-
1156
- if ( empty($format) ) {
1157
- $delim = ( strpos($file, '?') === false ) ? '?' : '&amp;';
1158
- $format = '%1$s' . $delim . 'page=%2$s';
1159
- }
1160
- $uri = sprintf($format, $file, $this->get_id());
1161
-
1162
- return $uri;
1163
- }
1164
-
1165
- /* Handlers */
1166
-
1167
- /**
1168
- * Default View handler
1169
- * Used as callback when none set
1170
- */
1171
- function handle() {}
1172
-
1173
- /* Validation */
1174
-
1175
- /**
1176
- * Check if instance is valid based on required properties/data types
1177
- * @return bool TRUE if valid, FALSE if not valid
1178
- */
1179
- function is_valid() {
1180
- $valid = true;
1181
- foreach ( $this->required as $prop => $type ) {
1182
- if ( empty($this->{$prop} )
1183
- || ( !empty($type) && is_string($type) && ( $f = 'is_' . $type ) && function_exists($f) && !$f($this->{$prop}) ) ) {
1184
- $valid = false;
1185
- break;
1186
- }
1187
- }
1188
- return $valid;
1189
- }
1190
-
1191
- function is_child() {
1192
- return $this->parent_required;
1193
- }
1194
-
1195
- function is_parent_custom() {
1196
- return ( $this->is_child() && $this->parent_custom ) ? true : false;
1197
- }
1198
-
1199
- function is_parent_wp() {
1200
- return ( $this->is_child() && !$this->parent_custom ) ? true : false;
1201
- }
1202
-
1203
- function is_options_valid() {
1204
- return ( is_object($this->get_options()) && $this->util->is_a($this->get_options(), $this->util->get_class('Options')) ) ? true : false;
1205
- }
1206
-
1207
- /* Options */
1208
-
1209
- /**
1210
- * Parse options build vars
1211
- * @uses `options_parse_build_vars` filter hook
1212
- */
1213
- function options_parse_build_vars($vars, $opts) {
1214
- //Handle form submission
1215
- if ( isset($_REQUEST[$opts->get_id('formatted')]) ) {
1216
- $vars['validate_pre'] = $vars['save_pre'] = true;
1217
- }
1218
- return $vars;
1219
- }
1220
-
1221
- function options_build_pre(&$opts) {
1222
- //Build form output
1223
- $form_id = $this->add_prefix('admin_form_' . $this->get_id_raw());
1224
- ?>
1225
- <form id="<?php esc_attr_e($form_id); ?>" name="<?php esc_attr_e($form_id); ?>" action="" method="post">
1226
- <?php
1227
- }
1228
-
1229
- function options_build_post(&$opts) {
1230
- submit_button();
1231
- ?>
1232
- </form>
1233
- <?php
1234
- }
1235
-
1236
- /**
1237
- * Builds option groups output
1238
- * @param SLB_Options $options Options instance
1239
- * @param array $groups Groups to build
1240
- */
1241
- function options_build_groups($options, $groups) {
1242
- //Add meta box for each group
1243
- $screen = get_current_screen();
1244
- foreach ( $groups as $gid ) {
1245
- $g = $options->get_group($gid);
1246
- if ( !count($options->get_items($gid)) ) {
1247
- continue;
1248
- }
1249
- add_meta_box($gid, $g->title, $this->m('options_build_group'), $screen, 'normal', 'default', array('options' => $options, 'group' => $gid));
1250
- }
1251
- //Build options
1252
- do_meta_boxes($screen, 'normal', null);
1253
- }
1254
-
1255
- function options_build_group($obj, $args) {
1256
- $args = $args['args'];
1257
- $group = $args['group'];
1258
- $opts = $args['options'];
1259
- $opts->build_group($group);
1260
- }
1261
-
1262
- function show_options($show_submit = true) {
1263
- //Build options output
1264
- if ( !$this->is_options_valid() ) {
1265
- return false;
1266
- }
1267
- /**
1268
- * @var SLB_Options
1269
- */
1270
- $opts =& $this->get_options();
1271
- $hooks = array (
1272
- 'filter' => array (
1273
- 'parse_build_vars' => array( $this->m('options_parse_build_vars'), 10, 2 )
1274
- ),
1275
- 'action' => array (
1276
- 'build_pre' => array( $this->m('options_build_pre') ),
1277
- 'build_post' => array ( $this->m('options_build_post') ),
1278
- )
1279
- );
1280
- //Add hooks
1281
- foreach ( $hooks as $type => $hook ) {
1282
- $m = 'add_' . $type;
1283
- foreach ( $hook as $tag => $args ) {
1284
- array_unshift($args, $tag);
1285
- call_user_func_array($opts->util->m($m), $args);
1286
- }
1287
- }
1288
- ?>
1289
- <div class="metabox-holder">
1290
- <?php
1291
- //Build output
1292
- $opts->build(array('build_groups' => $this->m('options_build_groups')));
1293
- ?>
1294
- </div>
1295
- <?php
1296
- //Remove hooks
1297
- foreach ( $hooks as $type => $hook ) {
1298
- $m = 'remove_' . $type;
1299
- foreach ( $hook as $tag => $args ) {
1300
- call_user_func($opts->util->m($m), $tag, $args[0]);
1301
- }
1302
- }
1303
- }
1304
-
1305
- /* UI Elements */
1306
-
1307
- /**
1308
- * Build submit button element
1309
- * @param string $text (optional) Button text
1310
- * @param string $id (optional) Button ID (prefixed on output)
1311
- * @param object $parent (optional) Page/Section object that contains button
1312
- * @return object Button properties (id, output)
1313
- */
1314
- function get_button_submit($text = null, $id = null, $parent = null) {
1315
- //Format values
1316
- if ( !is_string($text) || empty($text) )
1317
- $text = __('Save Changes');
1318
- if ( is_object($parent) && isset($parent->id) )
1319
- $parent = $parent->id . '_';
1320
- else
1321
- $parent = '';
1322
- if ( !is_string($id) || empty($id) )
1323
- $id = 'submit';
1324
- $id = $this->add_prefix($parent . $id);
1325
- //Build HTML
1326
- $out = $this->util->build_html_element(array(
1327
- 'tag' => 'input',
1328
- 'wrap' => false,
1329
- 'attributes' => array(
1330
- 'type' => 'submit',
1331
- 'class' => 'button-primary',
1332
- 'id' => $id,
1333
- 'name' => $id,
1334
- 'value' => $text
1335
- )
1336
- ));
1337
- $out = '<p class="submit">' . $out . '</p>';
1338
- $ret = new stdClass;
1339
- $ret->id = $id;
1340
- $ret->output = $out;
1341
- return $ret;
1342
- }
1343
-
1344
- /**
1345
- * Output submit button element
1346
- * @param string $text (optional) Button text
1347
- * @param string $id (optional) Button ID (prefixed on output)
1348
- * @param object $parent (optional) Page/Section object that contains button
1349
- * @return object Button properties (id, output)
1350
- */
1351
- function button_submit($text = null, $id = null, $parent = null) {
1352
- $btn = $this->get_button_submit($text, $id, $parent);
1353
- echo $btn->output;
1354
- return $btn;
1355
- }
1356
- }
1357
-
1358
- /**
1359
- * Admin Menu functionality
1360
- * @package Simple Lightbox
1361
- * @subpackage Admin
1362
- * @author Archetyped
1363
- */
1364
- class SLB_Admin_Menu extends SLB_Admin_View {
1365
- /* Properties */
1366
-
1367
- /**
1368
- * Menu position
1369
- * @var int
1370
- */
1371
- var $position = null;
1372
-
1373
- /* Init */
1374
-
1375
- function __construct($id, $labels, $options = null, $callback = null, $capability = null, $icon = null, $position = null) {
1376
- //Default
1377
- parent::__construct($id, $labels, $options, $callback, $capability, $icon);
1378
- //Class specific
1379
- $this->set_position($position);
1380
- }
1381
-
1382
- /* Getters/Setters */
1383
-
1384
- function set_position($position) {
1385
- if ( is_int($position) )
1386
- $this->position = $position;
1387
- }
1388
-
1389
- /* Handlers */
1390
-
1391
- function handle() {
1392
- if ( !current_user_can($this->get_capability()) )
1393
- wp_die(__('Access Denied', 'simple-lightbox'));
1394
- ?>
1395
- <div class="wrap">
1396
- <h2><?php esc_html_e( $this->get_label('header') ); ?></h2>
1397
- <?php
1398
- $this->show_options();
1399
- ?>
1400
- </div>
1401
- <?php
1402
- }
1403
- }
1404
-
1405
- /**
1406
- * Admin Page functionality
1407
- * @package Simple Lightbox
1408
- * @subpackage Admin
1409
- * @author Archetyped
1410
- */
1411
- class SLB_Admin_Page extends SLB_Admin_View {
1412
- /* Properties */
1413
-
1414
- var $parent_required = true;
1415
-
1416
- /* Init */
1417
-
1418
- function __construct($id, $parent, $labels, $options = null, $callback = null, $capability = null, $icon = null) {
1419
- //Default
1420
- parent::__construct($id, $labels, $options, $callback, $capability, $icon);
1421
- //Class specific
1422
- $this->set_parent($parent);
1423
- }
1424
-
1425
- /* Operations */
1426
-
1427
- function show_icon() {
1428
- echo screen_icon();
1429
- }
1430
-
1431
- /* Handlers */
1432
-
1433
- /**
1434
- * Default Page handler
1435
- * Builds options form UI for page
1436
- * @see this->init_menus() Set as callback for custom admin pages
1437
- * @uses current_user_can() to check if user has access to current page
1438
- * @uses wp_die() to end execution when user does not have permission to access page
1439
- */
1440
- function handle() {
1441
- if ( !current_user_can($this->get_capability()) )
1442
- wp_die(__('Access Denied', 'simple-lightbox'));
1443
- ?>
1444
- <div class="wrap">
1445
- <?php $this->show_icon(); ?>
1446
- <h2><?php esc_html_e( $this->get_label('header') ); ?></h2>
1447
- <?php
1448
- $this->show_options();
1449
- ?>
1450
- </div>
1451
- <?php
1452
- }
1453
- }
1454
-
1455
- /**
1456
- * Admin Section functionality
1457
- * @package Simple Lightbox
1458
- * @subpackage Admin
1459
- * @author Archetyped
1460
- */
1461
- class SLB_Admin_Section extends SLB_Admin_View {
1462
- /* Properties */
1463
-
1464
- var $parent_required = true;
1465
- var $parent_custom = false;
1466
-
1467
- /* Init */
1468
-
1469
- function __construct($id, $parent, $labels, $options = null, $callback = null, $capability = null) {
1470
- //Default
1471
- parent::__construct($id, $labels, $options, $callback, $capability);
1472
- //Class specific
1473
- $this->set_parent($parent);
1474
- }
1475
-
1476
- /* Getters/Setters */
1477
-
1478
- function get_uri() {
1479
- $file = 'options-' . $this->get_parent() . '.php';
1480
- return parent::get_uri($file, '%1$s#%2$s');
1481
- }
1482
-
1483
- /**
1484
- * Retrieve formatted title for section
1485
- * Wraps title text in element with anchor so that it can be linked to
1486
- * @return string Title
1487
- */
1488
- function get_title() {
1489
- return '<div id="' . $this->get_id() . '" class="' . $this->add_prefix('section_head') . '">' . $this->get_label('title') . '</div>';
1490
- }
1491
-
1492
- /* Handlers */
1493
-
1494
- function handle() {
1495
- $this->show_options(false);
1496
- }
1497
-
1498
- function options_parse_build_vars($vars, $opts) {
1499
- return $vars;
1500
- }
1501
-
1502
- function options_build_pre() {}
1503
-
1504
- function options_build_post() {}
1505
- }
1506
-
1507
- class SLB_Admin_Reset extends SLB_Admin_View {
1508
- /* Properties */
1509
-
1510
- var $required = array ( 'options' => 'object' );
1511
-
1512
- var $parent_required = false;
1513
-
1514
- /* Init */
1515
-
1516
- function __construct($id, $labels, $options) {
1517
- parent::__construct($id, $labels, $options);
1518
- }
1519
-
1520
- /* Handlers */
1521
-
1522
- /**
1523
- * Default handler
1524
- * Resets plugin settings
1525
- * @return string Status message (success, fail, etc.)
1526
- */
1527
- function handle() {
1528
- //Validate user
1529
- if ( ! current_user_can('activate_plugins') || ! check_admin_referer($this->get_id()) )
1530
- wp_die(__('Access Denied', 'simple-lightbox'));
1531
-
1532
- //Reset settings
1533
- if ( $this->is_options_valid() )
1534
- $this->get_options()->reset(true);
1535
-
1536
- //Set Status Message
1537
- $this->set_message($this->get_label('success'));
1538
-
1539
- /*
1540
- //Redirect user
1541
- $uri = remove_query_arg(array('_wpnonce', 'action'), add_query_arg(array($this->add_prefix('action') => $action), $_SERVER['REQUEST_URI']));
1542
- wp_redirect($uri);
1543
- exit;
1544
- */
1545
- }
1546
-
1547
- function get_uri() {
1548
- return wp_nonce_url(add_query_arg($this->get_query_args(), remove_query_arg($this->get_query_args_remove(), $_SERVER['REQUEST_URI'])), $this->get_id());
1549
- }
1550
-
1551
- function get_query_args() {
1552
- return array (
1553
- 'action' => $this->add_prefix('admin'),
1554
- $this->add_prefix('type') => 'view',
1555
- $this->add_prefix('group') => 'reset',
1556
- $this->add_prefix('obj') => $this->get_id_raw()
1557
- );
1558
- }
1559
-
1560
- function get_query_args_remove() {
1561
- $args_r = array (
1562
- '_wpnonce',
1563
- $this->add_prefix('action')
1564
- );
1565
-
1566
- return array_unique( array_merge( array_keys( $this->get_query_args() ), $args_r ) );
1567
- }
1568
-
1569
- function get_link_attr() {
1570
- return array (
1571
- 'class' => 'delete',
1572
- 'onclick' => "return confirm('" . $this->get_label('confirm') . "')"
1573
- );
1574
- }
1575
-
1576
- }
11
 
12
  protected $mode = 'sub';
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  /* Properties */
15
 
16
  /**
18
  * Set on initialization
19
  * @var obj
20
  */
21
+ protected $parent = null;
22
 
23
  /**
24
  * Messages
25
  * @var array
26
  */
27
+ protected $messages = array(
28
+ 'reset' => 'The settings have been reset',
29
+ 'beta' => '<strong class="%1$s">Notice:</strong> This update is a <strong class="%1$s">Beta version</strong>. It is highly recommended that you test the update on a test server before updating the plugin on a production server.',
30
+ 'access_denied' => 'You do not have sufficient permissions'
31
+ );
32
 
33
  /* Views */
34
 
39
  * > Val: Menu properties
40
  * @var array
41
  */
42
+ protected $menus = array();
43
 
44
  /**
45
  * Custom admin pages
48
  * > Val: Page properties
49
  * @var array
50
  */
51
+ protected $pages = array();
52
 
53
  /**
54
  * Custom admin sections
57
  * > Val: Section properties
58
  * @var array
59
  */
60
+ protected $sections = array();
61
 
62
  /**
63
+ * Actions
64
+ * Index Array
65
  * @var array
66
  */
67
+ protected $actions = array();
68
 
69
  /* Constructor */
70
 
71
+ public function __construct(&$parent) {
 
 
 
72
  parent::__construct();
73
+ // Set parent
74
  if ( is_object($parent) )
75
+ $this->parent = $parent;
76
  }
77
 
78
  /* Init */
79
 
80
  protected function _hooks() {
81
  parent::_hooks();
82
+ // Init
83
  add_action('admin_menu', $this->m('init_menus'), 11);
84
 
85
+ // Plugin actions
86
  add_action('admin_action_' . $this->add_prefix('admin'), $this->m('handle_action'));
87
 
88
+ // Notices
89
  add_action('admin_notices', $this->m('handle_notices'));
90
 
91
+ // Plugin listing
92
  add_filter('plugin_action_links_' . $this->util->get_plugin_base_name(), $this->m('plugin_action_links'), 10, 4);
93
+ add_filter('plugin_row_meta', $this->m('plugin_row_meta'), 10, 4);
94
  add_action('in_plugin_update_message-' . $this->util->get_plugin_base_name(), $this->m('plugin_update_message'), 10, 2);
95
  add_filter('site_transient_update_plugins', $this->m('plugin_update_transient'));
96
  }
97
 
98
+ /**
99
+ * Declare client files (scripts, styles)
100
+ * @uses parent::_client_files()
101
+ * @return void
102
+ */
103
+ protected function _client_files($files = null) {
104
+ $js_path = 'client/js/';
105
+ $js_path .= ( SLB_DEV ) ? 'dev' : 'prod';
106
+ $pfx = $this->get_prefix();
107
+ $files = array (
108
+ 'scripts' => array (
109
+ 'admin' => array (
110
+ 'file' => "$js_path/lib.admin.js",
111
+ 'deps' => array('[core]'),
112
+ 'context' => array( "admin_page_$pfx" ),
113
+ 'in_footer' => true,
114
+ ),
115
+ ),
116
+ 'styles' => array (
117
+ 'admin' => array (
118
+ 'file' => 'client/css/admin.css',
119
+ 'context' => array( "admin_page_$pfx", 'admin_page_plugins' )
120
+ )
121
+ )
122
+ );
123
+ parent::_client_files($files);
124
+ }
125
+
126
  /* Handlers */
127
 
128
+ /**
129
+ * Handle routing of internal action to appropriate handler
130
+ */
131
+ public function handle_action() {
132
+ // Parse action
133
  $t = 'type';
134
  $g = 'group';
135
  $o = 'obj';
138
  $this->add_prefix_ref($o);
139
  $r =& $_REQUEST;
140
 
141
+ // Retrieve view that initiated the action
142
  if ( isset($r[$t]) && 'view' == $r[$t] ) {
143
  if ( isset($r[$g]) && ( $prop = $r[$g] . 's' ) && property_exists($this, $prop) && is_array($this->{$prop}) && isset($r[$o]) && isset($this->{$prop}[$r[$o]]) ) {
144
  $view =& $this->{$prop}[$r[$o]];
145
+ // Pass request to view
146
  $view->do_callback();
147
  }
148
  }
153
  * Messages are localized upon display
154
  * @uses `admin_notices` action hook to display messages
155
  */
156
+ public function handle_notices() {
157
  $msgs = $this->util->apply_filters('admin_messages', array());
158
  foreach ( $msgs as $mid => $msg ) {
159
+ // Filter out empty messages
160
  if ( empty($msg) )
161
  continue;
162
+ // Build and display message
163
  $mid = $this->add_prefix('msg_' . $mid);
164
  ?>
165
  <div id="<?php echo esc_attr($mid); ?>" class="updated fade">
171
  }
172
  }
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  /* Views */
175
 
176
  /**
178
  * Section is added to specified admin section/menu
179
  * @uses `admin_init` hook
180
  */
181
+ public function init_menus() {
182
+ // Add top level menus (when necessary)
 
 
 
183
  $menu;
184
  foreach ( $this->menus as $menu ) {
185
+ // Register menu
186
  $hook = add_menu_page($menu->get_label('title'), $menu->get_label('menu'), $menu->get_capability(), $menu->get_id(), $menu->get_callback());
187
+ // Add hook to menu object
188
  $menu->set_hookname($hook);
189
  $this->menus[$menu->get_id_raw()] =& $menu;
190
  }
191
 
 
 
 
192
  $page;
193
+ // Add subpages
194
  foreach ( $this->pages as $page ) {
195
+ // Build Arguments
196
  $args = array ( $page->get_label('header'), $page->get_label('menu'), $page->get_capability(), $page->get_id(), $page->get_callback() );
197
  $f = null;
198
+ // Handle pages for default WP menus
199
  if ( $page->is_parent_wp() ) {
200
  $f = 'add_' . $page->get_parent() . '_page';
201
  }
202
 
203
+ // Handle pages for custom menus
204
  if ( ! function_exists($f) ) {
205
  array_unshift( $args, $page->get_parent() );
206
  $f = 'add_submenu_page';
207
  }
208
 
209
+ // Add admin page
210
  $hook = call_user_func_array($f, $args);
211
+ // Save hook to page properties
212
  $page->set_hookname($hook);
213
  $this->pages[$page->get_id_raw()] =& $page;
214
  }
215
 
216
+ // Add sections
 
 
 
217
  $section;
218
  foreach ( $this->sections as $section ) {
219
  add_settings_section($section->get_id(), $section->get_title(), $section->get_callback(), $section->get_parent());
 
 
220
  }
221
  }
222
 
228
  * @param string $type View type
229
  * @param string $id Unique view ID
230
  * @param array $args Arguments to pass to view constructor
231
+ * @return Admin_View|bool View instance (FALSE if view was not properly initialized)
232
  */
233
+ protected function add_view($type, $id, $args) {
234
+ // Validate request
235
  $class = $this->add_prefix('admin_' . $type);
236
  $collection = $type . 's';
237
+ if ( !class_exists($class) ) {
238
+ $class = $this->add_prefix('admin_view');
239
+ $collection = null;
240
+ }
241
+ // Create new instance
242
  $r = new ReflectionClass($class);
243
+ $view = $r->newInstanceArgs($args);
244
+ if ( $view->is_valid() && !empty($collection) && property_exists($this, $collection) && is_array($this->{$collection}) )
245
  $this->{$collection}[$id] =& $view;
246
+ unset($r);
247
+ return $view;
 
 
248
  }
249
 
250
  /**
251
+ * Add plugin action link
252
+ * @uses `add_view()` to init/attach action instance
253
+ * @param string $id Action ID
254
+ * @param array $labels Text for action
255
+ * > title - Link text (also title attribute value)
256
+ * > confirm - Confirmation message
257
+ * > success - Success message
258
+ * > failure - Failure message
259
+ * @param array $data Additional data for action
260
+ * @return obj Action instance
261
  */
262
+ public function add_action($id, $labels, $data = null) {
263
  $args = func_get_args();
264
+ return $this->add_view('action', $id, $args);
265
  }
266
 
267
  /*-** Menus **-*/
271
  * @param string $id Menu ID
272
  * @param string|array $labels Text labels
273
  * @param int $pos (optional) Menu position in navigation (index order)
274
+ * @return Admin_Menu Menu instance
275
  */
276
+ public function add_menu($id, $labels, $position = null) {
277
  $args = array ( $id, $labels, null, null, null, $position );
278
  return $this->add_view('menu', $id, $args);
279
  }
284
  * Add admin page
285
  * @uses this->pages
286
  * @param string $id Page ID (unique)
287
+ * @param string $parent Menu ID to add page to
288
  * @param string|array $labels Text labels (Associative array for multiple labels)
289
  * > menu: Menu title
290
  * > header: Page header
 
 
 
 
291
  * @param string $capability (optional) Custom capability for accessing page
292
+ * @return Admin_Page Page instance
293
  */
294
+ public function add_page($id, $parent, $labels, $callback = null, $capability = null) {
295
  $args = func_get_args();
 
296
  return $this->add_view('page', $id, $args);
297
  }
298
 
302
  * Add admin page to a standard WP menu
303
  * @uses this->add_page()
304
  * @param string $id Page ID (unique)
305
+ * @param string $parent Name of WP menu to add page to
306
  * @param string|array $labels Text labels (Associative array for multiple labels)
307
  * > menu: Menu title
308
  * > header: Page header
 
 
 
 
309
  * @param string $capability (optional) Custom capability for accessing page
310
+ * @return Admin_Page Page instance
311
+ */
312
+ public function add_wp_page($id, $parent, $labels, $callback = null, $capability = null) {
313
+ // Add page
314
+ $pg = $this->add_page($id, $parent, $labels, $capability);
315
+ // Set parent as WP
316
+ if ( $pg ) {
317
+ $pg->set_parent_wp();
318
  }
319
+ return $pg;
320
  }
321
 
322
  /**
325
  * @uses this->add_wp_page()
326
  * @param string $id Page ID (unique)
327
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
328
  * @param string $capability (optional) Custom capability for accessing page
329
+ * @return Admin_Page Page instance
330
  */
331
+ public function add_dashboard_page($id, $labels, $callback = null, $capability = null) {
332
+ return $this->add_wp_page($id, 'dashboard', $labels, $capability);
 
333
  }
334
 
335
  /**
338
  * @uses this->add_wp_page()
339
  * @param string $id Page ID (unique)
340
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
341
  * @param string $capability (optional) Custom capability for accessing page
342
  * @return string Page ID
343
  */
344
+ public function add_comments_page($id, $labels, $callback = null, $capability = null) {
345
+ return $this->add_wp_page($id, 'comments', $labels, $capability);
 
346
  }
347
 
348
  /**
351
  * @uses this->add_wp_page()
352
  * @param string $id Page ID (unique)
353
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
354
  * @param string $capability (optional) Custom capability for accessing page
355
  * @return string Page ID
356
  */
357
+ public function add_links_page($id, $labels, $callback = null, $capability = null) {
358
+ return $this->add_wp_page($id, 'links', $labels, $capability);
 
359
  }
360
 
361
 
365
  * @uses this->add_wp_page()
366
  * @param string $id Page ID (unique)
367
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
368
  * @param string $capability (optional) Custom capability for accessing page
369
  * @return string Page ID
370
  */
371
+ public function add_posts_page($id, $labels, $callback = null, $capability = null) {
372
+ return $this->add_wp_page($id, 'posts', $labels, $capability);
 
373
  }
374
 
375
  /**
378
  * @uses this->add_wp_page()
379
  * @param string $id Page ID (unique)
380
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
381
  * @param string $capability (optional) Custom capability for accessing page
382
  * @return string Page ID
383
  */
384
+ public function add_pages_page($id, $labels, $callback = null, $capability = null) {
385
+ return $this->add_wp_page($id, 'pages', $labels, $capability);
 
386
  }
387
 
388
  /**
391
  * @uses this->add_wp_page()
392
  * @param string $id Page ID (unique)
393
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
394
  * @param string $capability (optional) Custom capability for accessing page
395
  * @return string Page ID
396
  */
397
+ public function add_media_page($id, $labels, $callback = null, $capability = null) {
398
+ return $this->add_wp_page($id, 'media', $labels, $capability);
 
399
  }
400
 
401
  /**
404
  * @uses this->add_wp_page()
405
  * @param string $id Page ID (unique)
406
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
407
  * @param string $capability (optional) Custom capability for accessing page
408
  * @return string Page ID
409
  */
410
+ public function add_theme_page($id, $labels, $callback = null, $capability = null) {
411
+ return $this->add_wp_page($id, 'theme', $labels, $capability);
 
412
  }
413
 
414
  /**
417
  * @uses this->add_wp_page()
418
  * @param string $id Page ID (unique)
419
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
420
  * @param string $capability (optional) Custom capability for accessing page
421
  * @return string Page ID
422
  */
423
+ public function add_plugins_page($id, $labels, $callback = null, $capability = null) {
424
+ return $this->add_wp_page($id, 'plugins', $labels, $capability);
 
425
  }
426
 
427
  /**
430
  * @uses this->add_wp_page()
431
  * @param string $id Page ID (unique)
432
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
433
  * @param string $capability (optional) Custom capability for accessing page
434
  * @return string Page ID
435
  */
436
+ public function add_options_page($id, $labels, $callback = null, $capability = null) {
437
+ return $this->add_wp_page($id, 'options', $labels, $capability);
 
438
  }
439
 
440
  /**
443
  * @uses this->add_wp_page()
444
  * @param string $id Page ID (unique)
445
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
446
  * @param string $capability (optional) Custom capability for accessing page
447
  * @return string Page ID
448
  */
449
+ public function add_management_page($id, $labels, $callback = null, $capability = null) {
450
+ return $this->add_wp_page($id, 'management', $labels, $capability);
 
451
  }
452
 
453
  /**
455
  * @uses this->add_wp_page()
456
  * @param string $id Page ID (unique)
457
  * @param string|array $labels Text labels (Associative array for multiple labels)
 
 
 
458
  * @param string $capability (optional) Custom capability for accessing page
459
  * @return string Page ID
460
  */
461
+ public function add_users_page($id, $labels, $callback = null, $capability = null) {
462
+ return $this->add_wp_page($id, 'users', $labels, $capability);
 
463
  }
464
 
465
  /* Section */
470
  * @param string $id Unique section ID
471
  * @param string $page Page ID
472
  * @param string $labels Label text
473
+ * @return obj Section instance
 
 
 
474
  */
475
+ public function add_section($id, $parent, $labels) {
476
+ $args = func_get_args();
477
+ $section = $this->add_view('section', $id, $args);
478
 
479
+ // Add Section
480
  if ( $section->is_valid() )
481
+ $this->sections[$id] = $section;
482
+
483
+ return $section;
 
484
  }
485
 
486
  /* Operations */
493
  * @param $plugin_data
494
  * @param $context
495
  */
496
+ public function plugin_action_links($actions, $plugin_file, $plugin_data, $context) {
497
  global $admin_page_hooks;
498
+ // Add link to settings (only if active)
499
  if ( is_plugin_active($this->util->get_plugin_base_name()) ) {
500
  /* Get Actions */
501
 
516
  }
517
  }
518
 
519
+ /* Get action links */
520
  $type = 'title';
521
+ foreach ( $this->actions as $a ) {
522
+ if ( !$a->has_label($type) )
523
  continue;
524
+ $id = 'action_' . $a->get_id();
525
  $acts[] = (object) array (
526
+ 'id' => $id,
527
+ 'label' => $a->get_label($type),
528
+ 'uri' => $a->get_uri(),
529
+ 'attributes' => $a->get_link_attr()
530
  );
531
  }
532
+ unset($a);
533
 
534
+ // Add links
535
  $links = array();
536
  foreach ( $acts as $act ) {
537
  $links[$act->id] = $this->util->build_html_link($act->uri, $act->label, $act->attributes);
538
  }
539
 
540
+ // Add links
541
  $actions = array_merge($links, $actions);
542
  }
543
  return $actions;
544
  }
545
+
546
+ /**
547
+ * Update plugin listings metadata
548
+ * @param array $plugin_meta Plugin metadata
549
+ * @param string $plugin_file Plugin file
550
+ * @param array $plugin_data Plugin Data
551
+ * @param string $status Plugin status
552
+ * @return array Updated plugin metadata
553
+ */
554
+ public function plugin_row_meta($plugin_meta, $plugin_file, $plugin_data, $status) {
555
+ $u = ( is_object($this->parent) && isset($this->parent->util) ) ? $this->parent->util : $this->util;
556
+ $hook_base = 'admin_plugin_row_meta_';
557
+ if ( $plugin_file == $u->get_plugin_base_name() ) {
558
+ // Add metadata
559
+ // Support
560
+ $l = $u->get_plugin_info('SupportURI');
561
+ if ( !empty($l) ) {
562
+ $t = __( $this->util->apply_filters($hook_base . 'support', 'Get Support'), 'simple-lightbox');
563
+ $plugin_meta[] = $u->build_html_link($l, $t);
564
+ }
565
+ }
566
+ return $plugin_meta;
567
+ }
568
 
569
  /**
570
  * Adds additional message for plugin updates
573
  * @var array $plugin_data Current plugin data
574
  * @var object $r Update response data
575
  */
576
+ public function plugin_update_message($plugin_data, $r) {
577
  if ( !isset($r->new_version) )
578
  return false;
579
  if ( stripos($r->new_version, 'beta') !== false ) {
589
  * @param obj $transient Transient data
590
  * @return obj Modified transient data
591
  */
592
+ public function plugin_update_transient($transient) {
593
  $n = $this->util->get_plugin_base_name();
594
  if ( isset($transient->response) && isset($transient->response[$n]) && is_object($transient->response[$n]) && !isset($transient->response[$n]->upgrade_notice) ) {
595
  $r =& $transient->response[$n];
604
  * @param obj $r Response data from plugin update API
605
  * @return string Message (Default: empty string)
606
  */
607
+ protected function plugin_update_get_message($r) {
608
  $msg = '';
609
  $cls_notice = $this->add_prefix('notice');
610
  if ( !is_object($r) || !isset($r->new_version) )
622
  * @param string $msg_id Message ID
623
  * @return string Message text
624
  */
625
+ public function get_message($msg_id) {
626
  $msg = '';
627
  $msgs = $this->get_messages();
628
  if ( is_string($msg_id) && isset($msgs[$msg_id]) ) {
639
  */
640
  function get_messages() {
641
  if ( empty($this->messages) ) {
642
+ // Initialize messages if necessary
643
  $this->messages = array(
644
  'reset' => __('The settings have been reset', 'simple-lightbox'),
645
  'beta' => __('<strong class="%1$s">Notice:</strong> This update is a <strong class="%1$s">Beta version</strong>. It is highly recommended that you test the update on a test server before updating the plugin on a production server.', 'simple-lightbox'),
655
  * @param string $id Message ID
656
  * @param string $text Message text
657
  */
658
+ public function set_message($id, $text) {
659
  $this->messages[trim($id)] = $text;
660
  }
661
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class.admin_action.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Plugin action functionality
5
+ * Used for adding action links to plugin listing, etc.
6
+ * @package Simple Lightbox
7
+ * @subpackage Admin
8
+ * @author Archetyped
9
+ */
10
+ class SLB_Admin_Action extends SLB_Admin_View {
11
+ /* Properties */
12
+
13
+ protected $parent_required = false;
14
+
15
+ public $hook_prefix = 'admin_action';
16
+
17
+ /* Init */
18
+
19
+ /**
20
+ * Init
21
+ * @param string $id ID
22
+ * @param array $labels Labels
23
+ * @param obj $data Action data
24
+ * @return obj Current instance
25
+ */
26
+ function __construct($id, $labels, $data = null) {
27
+ parent::__construct($id, $labels);
28
+ // Default options instance
29
+ if ( !empty($data) ) {
30
+ $this->add_content('data', $data);
31
+ }
32
+ return $this;
33
+ }
34
+
35
+ /* Handlers */
36
+
37
+ /**
38
+ * Default handler
39
+ * Handles action
40
+ * @return string Status message (success, fail, etc.)
41
+ */
42
+ public function handle() {
43
+ // Validate user
44
+ if ( ! current_user_can('activate_plugins') || ! check_admin_referer($this->get_id()) )
45
+ wp_die(__('Access Denied', 'simple-lightbox'));
46
+
47
+ // Get data
48
+ $content = $this->get_content();
49
+
50
+ $success = true;
51
+
52
+ // Iterate through data
53
+ $hook = $this->util->get_hook($this->get_id_raw());
54
+ foreach ( $content as $c ) {
55
+ // Trigger action
56
+ $res = apply_filters($hook, $success, $c->data, $this);
57
+ // Set result
58
+ if ( !!$success ) {
59
+ $success = $res;
60
+ }
61
+ }
62
+
63
+ // Set Status Message
64
+ $lbl = ( $success ) ? 'success' : 'failure';
65
+ $this->set_message($this->get_label($lbl));
66
+ }
67
+
68
+ /**
69
+ * Get URI
70
+ * @see Admin_View::get_uri()
71
+ */
72
+ public function get_uri($file = null, $format = null) {
73
+ return wp_nonce_url(add_query_arg($this->get_query_args(), remove_query_arg($this->get_query_args_remove(), $_SERVER['REQUEST_URI'])), $this->get_id());
74
+ }
75
+
76
+ protected function get_query_args() {
77
+ return array (
78
+ 'action' => $this->add_prefix('admin'),
79
+ $this->add_prefix('type') => 'view',
80
+ $this->add_prefix('group') => 'action',
81
+ $this->add_prefix('obj') => $this->get_id_raw()
82
+ );
83
+ }
84
+
85
+ protected function get_query_args_remove() {
86
+ $args_r = array (
87
+ '_wpnonce',
88
+ $this->add_prefix('action')
89
+ );
90
+
91
+ return array_unique( array_merge( array_keys( $this->get_query_args() ), $args_r ) );
92
+ }
93
+
94
+ public function get_link_attr() {
95
+ return array (
96
+ 'class' => $this->util->get_hook($this->get_id_raw()),
97
+ 'onclick' => "return confirm('" . esc_js( $this->get_label('confirm') ) . "')"
98
+ );
99
+ }
100
+
101
+ /* Content */
102
+
103
+ /**
104
+ * Save options
105
+ */
106
+ public function add_content($id, $data) {
107
+ return parent::add_content($id, array('data' => $data));
108
+ }
109
+ }
includes/class.admin_menu.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Admin Menu
5
+ * Menus are top-level views in the Admin UI
6
+ * @package Simple Lightbox
7
+ * @subpackage Admin
8
+ * @author Archetyped
9
+ */
10
+ class SLB_Admin_Menu extends SLB_Admin_View {
11
+ /* Properties */
12
+
13
+ /**
14
+ * Menu position
15
+ * @var int
16
+ */
17
+ protected $position = null;
18
+
19
+ /* Init */
20
+
21
+ public function __construct($id, $labels, $callback = null, $capability = null, $icon = null, $position = null) {
22
+ // Default
23
+ parent::__construct($id, $labels, $callback, $capability, $icon);
24
+ // Class specific
25
+ $this->set_position($position);
26
+ return $this;
27
+ }
28
+
29
+ /* Getters/Setters */
30
+
31
+ /**
32
+ * Set menu position
33
+ * @return obj Current instance
34
+ */
35
+ public function set_position($position) {
36
+ if ( is_int($position) )
37
+ $this->position = $position;
38
+ return $this;
39
+ }
40
+ }
includes/class.admin_page.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Admin Page
5
+ * Pages are part of a Menu
6
+ * @package Simple Lightbox
7
+ * @subpackage Admin
8
+ * @author Archetyped
9
+ */
10
+ class SLB_Admin_Page extends SLB_Admin_View {
11
+ /* Properties */
12
+
13
+ protected $parent_required = true;
14
+
15
+ public $hook_prefix = 'admin_page';
16
+
17
+ /**
18
+ * Required features/elements
19
+ */
20
+ private $_required = array();
21
+
22
+ /* Init */
23
+
24
+ public function __construct($id, $parent, $labels, $callback = null, $capability = null) {
25
+ // Default
26
+ parent::__construct($id, $labels, $callback, $capability);
27
+ // Class specific
28
+ $this->set_parent($parent);
29
+ return $this;
30
+ }
31
+
32
+ /* Operations */
33
+
34
+ /**
35
+ * Add content to page
36
+ * @uses parent::add_content()
37
+ * @param string $id Module ID
38
+ * @param string $title Module title
39
+ * @param mixed $callback Callback method or other data for building module UI
40
+ * @param string $context (optional) Context to add module to (Default: primary)
41
+ * @param string $priority (optional) Controls module ordering (Default: default)
42
+ * @param array $callback_args (optional) Additional data to pass callback (Default: NULL)
43
+ * @return object Page instance reference
44
+ */
45
+ public function add_content($id, $title, $callback = null, $context = 'primary', $priority = 'default', $callback_args = null) {
46
+ return parent::add_content($id, array(
47
+ 'id' => $id,
48
+ 'title' => $title,
49
+ 'callback' => $callback,
50
+ 'context' => $context,
51
+ 'priority' => $priority,
52
+ 'callback_args' => $callback_args
53
+ )
54
+ );
55
+ }
56
+
57
+ /**
58
+ * Parse content by parameters
59
+ * Sets content value
60
+ */
61
+ protected function parse_content() {
62
+ // Get raw content
63
+ $raw = $this->get_content(false);
64
+ // Group by context
65
+ $content = array();
66
+ foreach ( $raw as $c ) {
67
+ // Add new context
68
+ if ( !isset($content[$c->context]) ) {
69
+ $content[$c->context] = array();
70
+ }
71
+ // Add item to context
72
+ $content[$c->context][] = $c;
73
+ }
74
+ return $content;
75
+ }
76
+
77
+ /**
78
+ * Render content blocks
79
+ * @param string $context (optional) Context to render
80
+ */
81
+ protected function render_content($context = 'primary') {
82
+ // Get content
83
+ $content = $this->get_content();
84
+ // Check for context
85
+ if ( !isset($content[$context]) ) {
86
+ return false;
87
+ }
88
+ $content = $content[$context];
89
+ $out = '';
90
+ // Render content
91
+ ?>
92
+ <div class="content-wrap">
93
+ <?php
94
+ // Add meta boxes
95
+ $screen = get_current_screen();
96
+ foreach ( $content as $c ) {
97
+ $c->screen = $screen;
98
+ // Callback
99
+ if ( is_callable($c->callback) ) {
100
+ $callback = $c->callback;
101
+ add_meta_box($c->id, $c->title, $c->callback, $c->screen, $c->context, $c->priority, $c->callback_args);
102
+ } else {
103
+ // Let handlers build output
104
+ $this->util->do_action('render_content', $c->callback, $this, $c);
105
+ }
106
+ }
107
+ // Output meta boxes
108
+ do_meta_boxes($screen, $context, null);
109
+ ?>
110
+ </div>
111
+ <?php
112
+ }
113
+
114
+ /**
115
+ * Require form submission support
116
+ * @return obj Page instance
117
+ */
118
+ public function require_form() {
119
+ $this->_require('form_submit');
120
+ return $this;
121
+ }
122
+
123
+ /**
124
+ * Check if form submission is required
125
+ * @return bool TRUE if form submission required
126
+ */
127
+ private function is_required_form() {
128
+ return $this->_is_required('form_submit');
129
+ }
130
+
131
+ /* Handlers */
132
+
133
+ /**
134
+ * Default Page handler
135
+ * Builds content blocks
136
+ * @see this->init_menus() Set as callback for custom admin pages
137
+ * @uses current_user_can() to check if user has access to current page
138
+ * @uses wp_die() to end execution when user does not have permission to access page
139
+ */
140
+ public function handle() {
141
+ if ( !current_user_can($this->get_capability()) )
142
+ wp_die(__('Access Denied', 'simple-lightbox'));
143
+ wp_enqueue_script('postbox');
144
+ ?>
145
+ <div class="wrap slb">
146
+ <h2><?php esc_html_e( $this->get_label('header') ); ?></h2>
147
+ <?php
148
+ // Form submission support
149
+ if ( $this->is_required_form() ) {
150
+ // Build form output
151
+ $form_id = $this->add_prefix('admin_form_' . $this->get_id_raw());
152
+ ?>
153
+ <form id="<?php esc_attr_e($form_id); ?>" name="<?php esc_attr_e($form_id); ?>" action="" method="post">
154
+ <?php
155
+ }
156
+ ?>
157
+ <div class="metabox-holder columns-2">
158
+ <div class="content-primary postbox-container">
159
+ <?php
160
+ $this->render_content('primary');
161
+ ?>
162
+ </div>
163
+ <div class="content-secondary postbox-container">
164
+ <?php
165
+ $this->render_content('secondary');
166
+ ?>
167
+ </div>
168
+ </div>
169
+ <br class="clear" />
170
+ <?php
171
+ // Form submission support
172
+ if ( $this->is_required_form() ) {
173
+ submit_button();
174
+ ?>
175
+ </form>
176
+ <?php
177
+ }
178
+ ?>
179
+ </div>
180
+ <?php
181
+ }
182
+ }
includes/class.admin_section.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Admin Section
5
+ * Sections are part of a Page
6
+ * @package Simple Lightbox
7
+ * @subpackage Admin
8
+ * @author Archetyped
9
+ */
10
+ class SLB_Admin_Section extends SLB_Admin_View {
11
+ /* Properties */
12
+
13
+ protected $parent_required = true;
14
+ protected $parent_custom = false;
15
+
16
+ /* Init */
17
+
18
+ public function __construct($id, $parent, $labels, $callback = null, $capability = null) {
19
+ // Default
20
+ parent::__construct($id, $labels, $callback, $capability);
21
+ // Class specific
22
+ $this->set_parent($parent);
23
+ return $this;
24
+ }
25
+
26
+ /* Getters/Setters */
27
+
28
+ /**
29
+ * Retrieve URI
30
+ * @uses Admin_View::get_uri()
31
+ * @param string $file (optional) Base file name
32
+ * @param string $format (optional) String format
33
+ * @return string Section URI
34
+ */
35
+ public function get_uri($file = null, $format = null) {
36
+ if ( !is_string($file) )
37
+ $file = 'options-' . $this->get_parent() . '.php';
38
+ if ( !is_string($format) )
39
+ $format = '%1$s#%2$s';
40
+ return parent::get_uri($file, $format);
41
+ }
42
+
43
+ /**
44
+ * Retrieve formatted title for section
45
+ * Wraps title text in element with anchor so that it can be linked to
46
+ * @return string Title
47
+ */
48
+ public function get_title() {
49
+ return sprintf('<div id="%1$s" class="%2$s">%3$s</div>', $this->get_id(), $this->add_prefix('section_head'), $this->get_label('title'));
50
+ }
51
+ }
includes/class.admin_view.php ADDED
@@ -0,0 +1,529 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Admin View Base
5
+ * Core functionality Admin UI components
6
+ * @package Simple Lightbox
7
+ * @subpackage Admin
8
+ * @author Archetyped
9
+ */
10
+ class SLB_Admin_View extends SLB_Base_Object {
11
+ /* Properties */
12
+
13
+ /**
14
+ * Labels
15
+ * @var array (Associative)
16
+ */
17
+ protected $labels = array();
18
+
19
+ /**
20
+ * Function to handle building UI
21
+ * @var callback
22
+ */
23
+ protected $callback = null;
24
+
25
+ /**
26
+ * Capability for access control
27
+ * @var string
28
+ */
29
+ protected $capability = 'manage_options';
30
+
31
+ /**
32
+ * Icon to use
33
+ * @var string
34
+ */
35
+ protected $icon = null;
36
+
37
+ /**
38
+ * View parent ID/Slug
39
+ * @var string
40
+ */
41
+ protected $parent = null;
42
+
43
+ /**
44
+ * Whether parent is a custom view or a default WP one
45
+ * @var bool
46
+ */
47
+ protected $parent_custom = true;
48
+
49
+ /**
50
+ * If view requires a parent
51
+ * @var bool
52
+ */
53
+ protected $parent_required = false;
54
+
55
+ /**
56
+ * WP-Generated hook name for view
57
+ * @var string
58
+ */
59
+ protected $hookname = null;
60
+
61
+ /**
62
+ * Raw content parameters
63
+ * Stores pre-rendered content parameters
64
+ * Items stored by ID (key)
65
+ * @var array
66
+ */
67
+ protected $content_raw = array();
68
+
69
+ /**
70
+ * Parsed content parameters
71
+ * @var array
72
+ */
73
+ protected $content = array();
74
+
75
+ /**
76
+ * Messages to be displayed
77
+ * Indexed Array
78
+ * @var array
79
+ */
80
+ protected $messages = array();
81
+
82
+ /**
83
+ * Required properties
84
+ * Associative array
85
+ * > Key: Property name
86
+ * > Value: Required data type
87
+ * @var array
88
+ */
89
+ private $required = array();
90
+
91
+ /**
92
+ * Default required properties
93
+ * Merged into $required array with this->init_required()
94
+ * @see this->required for more information
95
+ * @var array
96
+ */
97
+ private $_required = array ( 'id' => 'string', 'labels' => 'array' );
98
+
99
+ /* Init */
100
+
101
+ /**
102
+ * Constructor
103
+ * @return obj Current instance
104
+ */
105
+ public function __construct($id, $labels, $callback = null, $capability = null, $icon = null) {
106
+ $props = array(
107
+ 'labels' => $labels,
108
+ 'callback' => $callback,
109
+ 'capability' => $capability,
110
+ 'icon' => $icon,
111
+ );
112
+ parent::__construct($id, $props);
113
+ $this->init_required();
114
+ return $this;
115
+ }
116
+
117
+ protected function init_required() {
118
+ $this->required = array_merge($this->_required, $this->required);
119
+ // Check for parent requirement
120
+ if ( $this->parent_required )
121
+ $this->required['parent'] = 'string';
122
+ }
123
+
124
+ /**
125
+ * Set required feature
126
+ * @param string $feature Required feature
127
+ */
128
+ protected function _require($feature) {
129
+ if ( !isset($this->_required[$feature]) ) {
130
+ $this->_required[$feature] = true;
131
+ }
132
+ return $this;
133
+ }
134
+
135
+ /**
136
+ * Check if feature is required
137
+ * @param string $feature Feature to check for
138
+ * @return bool TRUE if feature required
139
+ */
140
+ protected function _is_required($feature) {
141
+ return ( isset($this->_required[$feature]) ) ? true : false;
142
+ }
143
+
144
+ /* Property Methods */
145
+
146
+ /**
147
+ * Retrieve ID (Formatted by default)
148
+ * @param bool $formatted (optional) Whether ID should be formatted for external use or not
149
+ * @return string ID
150
+ */
151
+ public function get_id($formatted = true) {
152
+ $id = parent::get_id();
153
+ if ( $formatted )
154
+ $this->add_prefix_ref($id);
155
+ return $id;
156
+ }
157
+
158
+ /**
159
+ * Retrieve raw ID
160
+ * @return string Raw ID
161
+ */
162
+ public function get_id_raw() {
163
+ return $this->get_id(false);
164
+ }
165
+
166
+ /**
167
+ * Retrieve label
168
+ * Uses first label (or default if defined) if specified type does not exist
169
+ * @param string $type Label type to retrieve
170
+ * @param string $default (optional) Default value if label type does not exist
171
+ * @return string Label text
172
+ */
173
+ public function get_label($type, $default = null) {
174
+ // Retrieve existing label type
175
+ if ( $this->has_label($type) )
176
+ return $this->labels[$type];
177
+ // Use default label if type is not set
178
+ if ( empty($default) && !empty($this->labels) ) {
179
+ reset($this->labels);
180
+ $default = current($this->labels);
181
+ }
182
+
183
+ return ( empty($default) ) ? '' : $default;
184
+ }
185
+
186
+ /**
187
+ * Set text labels
188
+ * @param array|string $labels
189
+ * @return obj Current instance
190
+ */
191
+ public function set_labels($labels) {
192
+ if ( empty($labels) )
193
+ return this;
194
+ // Single string
195
+ if ( is_string($labels) ) {
196
+ $labels = array ( $labels );
197
+ }
198
+
199
+ // Array
200
+ if ( is_array($labels) ) {
201
+ // Merge with existing labels
202
+ if ( empty($this->labels) || !is_array($this->labels) ) {
203
+ $this->labels = array();
204
+ }
205
+ $this->labels = array_merge($this->labels, $labels);
206
+ }
207
+ return $this;
208
+ }
209
+
210
+ /**
211
+ * Set single text label
212
+ * @uses this->set_labels()
213
+ * @param string $type Label type to set
214
+ * @param string $value Label value
215
+ * @return obj Current instance
216
+ */
217
+ public function set_label($type, $value) {
218
+ if ( is_string($type) && is_string($value) ) {
219
+ $label = array( $type => $value );
220
+ $this->set_labels($label);
221
+ }
222
+ return $this;
223
+ }
224
+
225
+ /**
226
+ * Checks if specified label is set on view
227
+ * @param string $type Label type
228
+ * @return bool TRUE if label exists, FALSE otherwise
229
+ */
230
+ public function has_label($type) {
231
+ return ( isset($this->labels[$type]) );
232
+ }
233
+
234
+ /* Content */
235
+
236
+ /**
237
+ * Add content block to view
238
+ * Child classes define method functionality
239
+ * @param string $id Content block ID
240
+ * @param array $args Content arguments (Defined by child class), converted to an object
241
+ * @return obj Current View instance
242
+ */
243
+ public function add_content($id, $args) {
244
+ // Save parameters
245
+ $this->content_raw[$id] = (object) $args;
246
+ // Clear parsed content
247
+ $this->content = array();
248
+ // Return instance reference
249
+ return $this;
250
+ }
251
+
252
+ /**
253
+ * Retrieve content
254
+ */
255
+ protected function get_content($parsed = true) {
256
+ $content = $this->content_raw;
257
+ if ( $parsed ) {
258
+ // Return previously parsed content
259
+ if ( !empty($this->content) ) {
260
+ $content = $this->content;
261
+ }
262
+ elseif ( !empty($this->content_raw) ) {
263
+ // Parse content before returning
264
+ $content = $this->content = $this->parse_content();
265
+ }
266
+ }
267
+ return $content;
268
+ }
269
+
270
+ /**
271
+ * Parse content
272
+ * Child classes define functionality
273
+ * @return array Parsed content
274
+ */
275
+ protected function parse_content() {
276
+ return $this->get_content(false);
277
+ }
278
+
279
+ /**
280
+ * Check if content has been added to view
281
+ * @return bool TRUE if content added
282
+ */
283
+ protected function has_content() {
284
+ $raw = $this->get_content(false);
285
+ return !empty($raw);
286
+ }
287
+
288
+ /**
289
+ * Render content
290
+ */
291
+ protected function render_content($context = 'default') {}
292
+
293
+ /**
294
+ * Retrieve view messages
295
+ * @return array Messages
296
+ */
297
+ protected function &get_messages() {
298
+ if ( !is_array($this->messages) )
299
+ $this->messages = array();
300
+ return $this->messages;
301
+ }
302
+
303
+ /**
304
+ * Save message
305
+ * @param string $text Message text
306
+ * @return obj Current instance
307
+ */
308
+ public function set_message($text) {
309
+ $msgs =& $this->get_messages();
310
+ $text = trim($text);
311
+ if ( empty($msgs) && !empty($text) )
312
+ $this->util->add_filter('admin_messages', $this->m('do_messages'), 10, 1, false);
313
+ $msgs[] = $text;
314
+ return $this;
315
+ }
316
+
317
+ /**
318
+ * Add messages to array
319
+ * Called by internal `admin_messages` filter hook
320
+ * @param array $msgs Aggregated messages
321
+ * @return array Merged messages array
322
+ */
323
+ public function do_messages($msgs = array()) {
324
+ $m =& $this->get_messages();
325
+ if ( !empty($m) )
326
+ $msgs = array_merge($msgs, $m);
327
+ return $msgs;
328
+ }
329
+
330
+ /**
331
+ * Retrieve view callback
332
+ * @return callback Callback (Default: standard handler method)
333
+ */
334
+ public function get_callback() {
335
+ return ( $this->has_callback() ) ? $this->callback : $this->m('handle');
336
+ }
337
+
338
+ /**
339
+ * Set callback function for building item
340
+ * @param callback $callback Callback function to use
341
+ * @return obj Current instance
342
+ */
343
+ public function set_callback($callback) {
344
+ $this->callback = ( is_callable($callback) ) ? $callback : null;
345
+ return $this;
346
+ }
347
+
348
+ /**
349
+ * Check if callback set
350
+ * @return bool TRUE if callback is set
351
+ */
352
+ protected function has_callback() {
353
+ return ( !empty($this->callback) ) ? true : false;
354
+ }
355
+
356
+ /**
357
+ * Run callback
358
+ */
359
+ public function do_callback() {
360
+ call_user_func($this->get_callback());
361
+ }
362
+
363
+ /**
364
+ * Retrieve capability
365
+ * @return string Capability
366
+ */
367
+ public function get_capability() {
368
+ return $this->capability;
369
+ }
370
+
371
+ /**
372
+ * Set capability for access control
373
+ * @param string $capability Capability
374
+ * @return obj Current instance
375
+ */
376
+ public function set_capability($capability) {
377
+ if ( is_string($capability) && !empty($capability) )
378
+ $this->capability = $capability;
379
+ return $this;
380
+ }
381
+
382
+ /**
383
+ * Set icon
384
+ * @param string $icon Icon URI
385
+ * @return obj Current instance
386
+ */
387
+ public function set_icon($icon) {
388
+ if ( !empty($icon) && is_string($icon) )
389
+ $this->icon = $icon;
390
+ return $this;
391
+ }
392
+
393
+ protected function get_hookname() {
394
+ return ( empty($this->hookname) ) ? '' : $this->hookname;
395
+ }
396
+
397
+ /**
398
+ * Set hookname
399
+ * @param string $hookname Hookname value
400
+ * @return obj Current instance
401
+ */
402
+ public function set_hookname($hookname) {
403
+ if ( !empty($hookname) && is_string($hookname) )
404
+ $this->hookname = $hookname;
405
+ return $this;
406
+ }
407
+
408
+ /**
409
+ * Retrieve parent
410
+ * Formats parent ID for custom parents
411
+ * @uses parent::get_parent()
412
+ * @return string Parent ID
413
+ */
414
+ public function get_parent() {
415
+ $parent = parent::get_parent();
416
+ return ( $this->is_parent_custom() ) ? $this->add_prefix($parent) : $parent;
417
+ }
418
+
419
+ /**
420
+ * Set parent for view
421
+ * @param string $parent Parent ID
422
+ * @return obj Current instance
423
+ */
424
+ public function set_parent($parent) {
425
+ if ( $this->parent_required ) {
426
+ if ( !empty($parent) && is_string($parent) )
427
+ $this->parent = $parent;
428
+ } else {
429
+ $this->parent = null;
430
+ }
431
+ return $this;
432
+ }
433
+
434
+ /**
435
+ * Specify whether parent is a custom view or a WP view
436
+ * @param bool $custom (optional) TRUE if custom, FALSE if WP
437
+ * @return obj Current instance
438
+ */
439
+ protected function set_parent_custom($custom = true) {
440
+ if ( $this->parent_required ) {
441
+ $this->parent_custom = !!$custom;
442
+ }
443
+ return $this;
444
+ }
445
+
446
+ /**
447
+ * Set parent as WP view
448
+ * @uses this->set_parent_custom()
449
+ * @return obj Current instance
450
+ */
451
+ public function set_parent_wp() {
452
+ $this->set_parent_custom(false);
453
+ return $this;
454
+ }
455
+
456
+ /**
457
+ * Get view URI
458
+ * URI Structures:
459
+ * > Top Level Menus: admin.php?page={menu_id}
460
+ * > Pages: [parent_page_file.php|admin.php]?page={page_id}
461
+ * > Section: [parent_menu_uri]#{section_id}
462
+ *
463
+ * @uses $admin_page_hooks to determine if page is child of default WP page
464
+ * @param string $file (optional) Base file name
465
+ * @param string $format (optional) Format string for URI
466
+ * @return string Object URI
467
+ */
468
+ public function get_uri($file = null, $format = null) {
469
+ static $page_hooks = null;
470
+ $uri = '';
471
+ if ( empty($file) )
472
+ $file = 'admin.php';
473
+ if ( $this->is_child() ) {
474
+ $parent = str_replace('_page_' . $this->get_id(), '', $this->get_hookname());
475
+ if ( is_null($page_hooks) ) {
476
+ $page_hooks = array_flip($GLOBALS['admin_page_hooks']);
477
+ }
478
+ if ( isset($page_hooks[$parent]) )
479
+ $file = $page_hooks[$parent];
480
+ }
481
+
482
+ if ( empty($format) ) {
483
+ $delim = ( strpos($file, '?') === false ) ? '?' : '&amp;';
484
+ $format = '%1$s' . $delim . 'page=%2$s';
485
+ }
486
+ $uri = sprintf($format, $file, $this->get_id());
487
+
488
+ return $uri;
489
+ }
490
+
491
+ /* Handlers */
492
+
493
+ /**
494
+ * Default View handler
495
+ * Used as callback when none set
496
+ */
497
+ public function handle() {}
498
+
499
+ /* Validation */
500
+
501
+ /**
502
+ * Check if instance is valid based on required properties/data types
503
+ * @return bool TRUE if valid, FALSE if not valid
504
+ */
505
+ public function is_valid() {
506
+ $valid = true;
507
+ foreach ( $this->required as $prop => $type ) {
508
+ if ( empty($this->{$prop} )
509
+ || ( !empty($type) && is_string($type) && ( $f = 'is_' . $type ) && function_exists($f) && !$f($this->{$prop}) ) ) {
510
+ $valid = false;
511
+ break;
512
+ }
513
+ }
514
+ return $valid;
515
+ }
516
+
517
+ protected function is_child() {
518
+ return $this->parent_required;
519
+ }
520
+
521
+ protected function is_parent_custom() {
522
+ return ( $this->is_child() && $this->parent_custom ) ? true : false;
523
+ }
524
+
525
+ public function is_parent_wp() {
526
+ return ( $this->is_child() && !$this->parent_custom ) ? true : false;
527
+ }
528
+
529
+ }
includes/class.base.php CHANGED
@@ -83,7 +83,7 @@ class SLB_Base {
83
  *
84
  * Array is processed and converted to an object on init
85
  */
86
- var $client_files = array(
87
  'scripts' => array(),
88
  'styles' => array()
89
  );
@@ -100,7 +100,7 @@ class SLB_Base {
100
  * Options
101
  * @var SLB_Options
102
  */
103
- var $options = null;
104
 
105
  /**
106
  * Admin
@@ -116,11 +116,11 @@ class SLB_Base {
116
  function __construct() {
117
  $this->util = new SLB_Utilities($this);
118
  if ( $this->can('init') ) {
119
- $hook = 'plugins_loaded';
120
- if ( current_filter() == $hook || self::$_init_passed ) {
121
  $this->_init();
122
  } else {
123
- add_action($hook, $this->m('_init'));
124
  }
125
  }
126
  }
@@ -139,22 +139,22 @@ class SLB_Base {
139
  if ( $this->_init || !isset($this) || !$this->can('init') )
140
  return false;
141
  $this->_init = true;
142
- //Environment
143
  $this->_env();
144
 
145
  if ( $this->can('control') ) {
146
- //Options
147
  $this->_options();
148
 
149
- //Admin
150
  if ( is_admin() )
151
  $this->_admin();
152
  }
153
 
154
- //Hooks
155
  $this->_hooks();
156
 
157
- //Client files
158
  $this->_client_files();
159
  }
160
 
@@ -165,42 +165,45 @@ class SLB_Base {
165
  if ( !$this->can('singleton') ) {
166
  return false;
167
  }
168
- //Localization
169
  $ldir = 'l10n';
170
  $lpath = $this->util->get_plugin_file_path($ldir, array(false, false));
171
  $lpath_abs = $this->util->get_file_path($ldir);
172
  if ( is_dir($lpath_abs) ) {
173
- load_plugin_textdomain('ar-series', false, $lpath);
174
  }
175
 
176
- //Context
177
  add_action( ( is_admin() ) ? 'admin_print_footer_scripts' : 'wp_footer', $this->util->m('set_client_context'), $this->util->priority('client_footer_output') );
178
  }
179
 
 
 
 
 
 
 
180
  /**
181
  * Initialize options
182
  * To be called by child class
183
  */
184
- protected function _options($options_config = null) {
185
  $class = $this->util->get_class('Options');
186
  $key = 'options';
187
  if ( $this->shares($key) ) {
188
- /**
189
- * @var SLB_Options
190
- */
191
  $opts = $this->gvar($key);
192
- //Setup options instance
193
- if ( !is_a($opts, $class) ) {
194
  $opts = $this->gvar($key, new $class());
195
  }
196
  } else {
197
  $opts = new $class();
198
  }
199
- //Load options
200
  if ( $this->is_options_valid($options_config, false) ) {
201
  $opts->load($options_config);
202
  }
203
- //Set instance property
204
  $this->options = $opts;
205
  }
206
 
@@ -215,18 +218,15 @@ class SLB_Base {
215
  $class = $this->util->get_class('Admin');
216
  $key = 'admin';
217
  if ( $this->shares($key) ) {
218
- /**
219
- * @var SLB_Admin
220
- */
221
  $adm = $this->gvar($key);
222
- //Setup options instance
223
- if ( !is_a($adm, $class) ) {
224
  $adm = $this->gvar($key, new $class($this));
225
  }
226
  } else {
227
  $adm = new $class($this);
228
  }
229
- //Set instance property
230
  $this->admin = $adm;
231
  }
232
 
@@ -235,12 +235,12 @@ class SLB_Base {
235
  */
236
  protected function _hooks() {
237
  $base = $this->util->get_plugin_base_file();
238
- //Activation
239
  $func_activate = '_activate';
240
  if ( method_exists($this, $func_activate) )
241
  register_activation_hook($base, $this->m($func_activate));
242
 
243
- //Deactivation
244
  $func_deactivate = '_deactivate';
245
  if ( method_exists($this, $func_deactivate) )
246
  register_deactivation_hook($base, $this->m($func_deactivate));
@@ -249,25 +249,36 @@ class SLB_Base {
249
  /**
250
  * Initialize client files
251
  */
252
- protected function _client_files() {
 
 
 
 
253
  foreach ( $this->client_files as $key => $val ) {
254
- if ( empty($val) && isset($this->{$key}) )
255
- $this->client_files[$key] =& $this->{$key};
256
- $g =& $this->client_files[$key];
257
- if ( is_array($g) && !empty($g) ) {
258
- $g = $this->util->parse_client_files($g, $key);
259
  }
260
- //Remove empty file groups
261
- if ( empty($g) )
262
  unset($this->client_files[$key]);
 
263
  }
264
-
265
- //Register
 
 
 
 
 
 
266
  add_action('init', $this->m('register_client_files'));
267
 
268
- //Enqueue
269
- $hook_enqueue = ( ( is_admin() ) ? 'admin' : 'wp' ) . '_enqueue_scripts' ;
270
- add_action($hook_enqueue, $this->m('enqueue_client_files'));
 
 
 
271
  }
272
 
273
  /**
@@ -283,10 +294,10 @@ class SLB_Base {
283
  if ( !$func )
284
  continue;
285
  foreach ( $files as $f ) {
286
- //Get file URI
287
- $f->file = ( !$this->util->is_file($f->file) && is_callable($f->file) ) ? call_user_func($f->file) : $this->util->get_file_url($f->file);
288
  $params = array($f->id, $f->file, $f->deps, $v);
289
- //Set additional parameters based on file type (script, style, etc.)
290
  switch ( $type ) {
291
  case 'scripts':
292
  $params[] = $f->in_footer;
@@ -295,7 +306,7 @@ class SLB_Base {
295
  $params[] = $f->media;
296
  break;
297
  }
298
- //Register file
299
  call_user_func_array($func, $params);
300
  }
301
  }
@@ -305,53 +316,74 @@ class SLB_Base {
305
  * Enqueues files for client output (scripts/styles) based on context
306
  * @uses `admin_enqueue_scripts` Action hook depending on context
307
  * @uses `wp_enqueue_scripts` Action hook depending on context
 
308
  * @return void
309
  */
310
- function enqueue_client_files() {
311
- //Enqueue files
 
 
 
 
312
  foreach ( $this->client_files as $type => $files ) {
313
  $func = $this->get_client_files_handler($type, 'enqueue');
314
  if ( !$func ) {
315
  continue;
316
  }
317
- foreach ( $files as $f ) {
318
- //Skip shadow files
319
- if ( !$f->enqueue ) {
 
 
 
 
 
 
 
 
320
  continue;
321
  }
322
  $load = true;
323
- //Global Callback
324
  if ( is_callable($f->callback) && !call_user_func($f->callback) ) {
325
  $load = false;
326
  }
327
- //Context
328
  if ( $load && !empty($f->context) ) {
329
- //Reset $load before evaluating context
330
  $load = false;
331
- //Iterate through contexts
332
  foreach ( $f->context as $ctx ) {
333
- //Context + Callback
334
  if ( is_array($ctx) ) {
335
- //Stop checking context if callback is invalid
336
  if ( !is_callable($ctx[1]) || !call_user_func($ctx[1]) )
337
  continue;
338
  $ctx = $ctx[0];
339
  }
340
- //Stop checking context if valid context found
341
  if ( $this->util->is_context($ctx) ) {
342
  $load = true;
343
  break;
344
  }
345
  }
346
  }
347
-
348
- //Load valid file
349
  if ( $load ) {
 
 
350
  $func($f->id);
351
  }
352
  }
353
  }
354
  }
 
 
 
 
 
 
 
355
 
356
  /**
357
  * Build function name for handling client operations
@@ -462,7 +494,7 @@ class SLB_Base {
462
 
463
  protected function can($cap) {
464
  if ( is_null($this->caps) ) {
465
- //Build capabilities based on instance properties
466
  $this->caps = array(
467
  'init' => ( 'object' != $this->mode ) ? true : false,
468
  'singleton' => ( !!$this->model ) ? true : false,
@@ -491,10 +523,10 @@ class SLB_Base {
491
  }
492
  $ret = $val;
493
  if ( null !== $val ) {
494
- //Set Value
495
  $g[$name] = $val;
496
  } elseif ( isset($g[$name]) ) {
497
- //Retrieve variable
498
  $ret = $g[$name];
499
  }
500
  return $ret;
@@ -514,7 +546,7 @@ class SLB_Base {
514
  function is_options_valid($data, $check_var = true) {
515
  $class = $this->util->get_class('Options');
516
  $ret = ( empty($data) || !is_array($data) || !class_exists($class) ) ? false : true;
517
- if ( $ret && $check_var && !is_a($this->options, $class) )
518
  $ret = false;
519
  return $ret;
520
  }
83
  *
84
  * Array is processed and converted to an object on init
85
  */
86
+ private $client_files = array (
87
  'scripts' => array(),
88
  'styles' => array()
89
  );
100
  * Options
101
  * @var SLB_Options
102
  */
103
+ protected $options = null;
104
 
105
  /**
106
  * Admin
116
  function __construct() {
117
  $this->util = new SLB_Utilities($this);
118
  if ( $this->can('init') ) {
119
+ $hook = 'init';
120
+ if ( did_action($hook) || self::$_init_passed ) {
121
  $this->_init();
122
  } else {
123
+ add_action($hook, $this->m('_init'), 1);
124
  }
125
  }
126
  }
139
  if ( $this->_init || !isset($this) || !$this->can('init') )
140
  return false;
141
  $this->_init = true;
142
+ // Environment
143
  $this->_env();
144
 
145
  if ( $this->can('control') ) {
146
+ // Options
147
  $this->_options();
148
 
149
+ // Admin
150
  if ( is_admin() )
151
  $this->_admin();
152
  }
153
 
154
+ // Hooks
155
  $this->_hooks();
156
 
157
+ // Client files
158
  $this->_client_files();
159
  }
160
 
165
  if ( !$this->can('singleton') ) {
166
  return false;
167
  }
168
+ // Localization
169
  $ldir = 'l10n';
170
  $lpath = $this->util->get_plugin_file_path($ldir, array(false, false));
171
  $lpath_abs = $this->util->get_file_path($ldir);
172
  if ( is_dir($lpath_abs) ) {
173
+ load_plugin_textdomain('simple-lightbox', false, $lpath);
174
  }
175
 
176
+ // Context
177
  add_action( ( is_admin() ) ? 'admin_print_footer_scripts' : 'wp_footer', $this->util->m('set_client_context'), $this->util->priority('client_footer_output') );
178
  }
179
 
180
+ /**
181
+ * Initialize options
182
+ * To be implemented in child classes
183
+ */
184
+ protected function _options() {}
185
+
186
  /**
187
  * Initialize options
188
  * To be called by child class
189
  */
190
+ protected function _set_options($options_config = null) {
191
  $class = $this->util->get_class('Options');
192
  $key = 'options';
193
  if ( $this->shares($key) ) {
 
 
 
194
  $opts = $this->gvar($key);
195
+ // Setup options instance
196
+ if ( !($opts instanceof $class) ) {
197
  $opts = $this->gvar($key, new $class());
198
  }
199
  } else {
200
  $opts = new $class();
201
  }
202
+ // Load options
203
  if ( $this->is_options_valid($options_config, false) ) {
204
  $opts->load($options_config);
205
  }
206
+ // Set instance property
207
  $this->options = $opts;
208
  }
209
 
218
  $class = $this->util->get_class('Admin');
219
  $key = 'admin';
220
  if ( $this->shares($key) ) {
 
 
 
221
  $adm = $this->gvar($key);
222
+ // Setup options instance
223
+ if ( !($adm instanceof $class) ) {
224
  $adm = $this->gvar($key, new $class($this));
225
  }
226
  } else {
227
  $adm = new $class($this);
228
  }
229
+ // Set instance property
230
  $this->admin = $adm;
231
  }
232
 
235
  */
236
  protected function _hooks() {
237
  $base = $this->util->get_plugin_base_file();
238
+ // Activation
239
  $func_activate = '_activate';
240
  if ( method_exists($this, $func_activate) )
241
  register_activation_hook($base, $this->m($func_activate));
242
 
243
+ // Deactivation
244
  $func_deactivate = '_deactivate';
245
  if ( method_exists($this, $func_deactivate) )
246
  register_deactivation_hook($base, $this->m($func_deactivate));
249
  /**
250
  * Initialize client files
251
  */
252
+ protected function _client_files($files = null) {
253
+ // Validation
254
+ if ( !is_array($files) || empty($files) ) {
255
+ return false;
256
+ }
257
  foreach ( $this->client_files as $key => $val ) {
258
+ if ( isset($files[$key]) && is_array($files[$key]) || !empty($files[$key]) ) {
259
+ $this->client_files[$key] = $this->util->parse_client_files($files[$key], $key);
 
 
 
260
  }
261
+ // Remove empty file groups
262
+ if ( empty($this->client_files[$key]) ) {
263
  unset($this->client_files[$key]);
264
+ }
265
  }
266
+
267
+
268
+ // Stop if no files are set for registration
269
+ if ( empty($this->client_files) ) {
270
+ return false;
271
+ }
272
+
273
+ // Register
274
  add_action('init', $this->m('register_client_files'));
275
 
276
+ // Enqueue
277
+ $hk_prfx = ( ( is_admin() ) ? 'admin' : 'wp' );
278
+ $hk_enqueue = $hk_prfx . '_enqueue_scripts' ;
279
+ $hk_enqueue_ft = $hk_prfx . '_footer';
280
+ add_action($hk_enqueue, $this->m('enqueue_client_files'), 10, 0);
281
+ add_action($hk_enqueue_ft, $this->m('enqueue_client_files_footer'), 1);
282
  }
283
 
284
  /**
294
  if ( !$func )
295
  continue;
296
  foreach ( $files as $f ) {
297
+ // Get file URI
298
+ $f->file = ( !$this->util->is_file($f->file) && is_callable($f->file) ) ? call_user_func($f->file) : $this->util->get_file_url($f->file, true);
299
  $params = array($f->id, $f->file, $f->deps, $v);
300
+ // Set additional parameters based on file type (script, style, etc.)
301
  switch ( $type ) {
302
  case 'scripts':
303
  $params[] = $f->in_footer;
306
  $params[] = $f->media;
307
  break;
308
  }
309
+ // Register file
310
  call_user_func_array($func, $params);
311
  }
312
  }
316
  * Enqueues files for client output (scripts/styles) based on context
317
  * @uses `admin_enqueue_scripts` Action hook depending on context
318
  * @uses `wp_enqueue_scripts` Action hook depending on context
319
+ * @param bool $footer (optional) Whether to enqueue footer files (Default: No)
320
  * @return void
321
  */
322
+ function enqueue_client_files($footer = false) {
323
+ // Validate
324
+ if ( !is_bool($footer) ) {
325
+ $footer = false;
326
+ }
327
+ // Enqueue files
328
  foreach ( $this->client_files as $type => $files ) {
329
  $func = $this->get_client_files_handler($type, 'enqueue');
330
  if ( !$func ) {
331
  continue;
332
  }
333
+ foreach ( $files as $fkey => $f ) {
334
+ // Skip previously-enqueued files and shadow files
335
+ if ( $f->enqueued || !$f->enqueue ) {
336
+ continue;
337
+ }
338
+ // Enqueue files only for current location (header/footer)
339
+ if ( isset($f->in_footer) ) {
340
+ if ( $f->in_footer != $footer ) {
341
+ continue;
342
+ }
343
+ } elseif ( $footer ) {
344
  continue;
345
  }
346
  $load = true;
347
+ // Global Callback
348
  if ( is_callable($f->callback) && !call_user_func($f->callback) ) {
349
  $load = false;
350
  }
351
+ // Context
352
  if ( $load && !empty($f->context) ) {
353
+ // Reset $load before evaluating context
354
  $load = false;
355
+ // Iterate through contexts
356
  foreach ( $f->context as $ctx ) {
357
+ // Context + Callback
358
  if ( is_array($ctx) ) {
359
+ // Stop checking context if callback is invalid
360
  if ( !is_callable($ctx[1]) || !call_user_func($ctx[1]) )
361
  continue;
362
  $ctx = $ctx[0];
363
  }
364
+ // Stop checking context if valid context found
365
  if ( $this->util->is_context($ctx) ) {
366
  $load = true;
367
  break;
368
  }
369
  }
370
  }
371
+ // Load valid file
 
372
  if ( $load ) {
373
+ // Mark file as enqueued
374
+ $this->client_files[$type]->{$fkey}->enqueued = true;
375
  $func($f->id);
376
  }
377
  }
378
  }
379
  }
380
+
381
+ /**
382
+ * Enqueue client files in the footer
383
+ */
384
+ public function enqueue_client_files_footer() {
385
+ $this->enqueue_client_files(true);
386
+ }
387
 
388
  /**
389
  * Build function name for handling client operations
494
 
495
  protected function can($cap) {
496
  if ( is_null($this->caps) ) {
497
+ // Build capabilities based on instance properties
498
  $this->caps = array(
499
  'init' => ( 'object' != $this->mode ) ? true : false,
500
  'singleton' => ( !!$this->model ) ? true : false,
523
  }
524
  $ret = $val;
525
  if ( null !== $val ) {
526
+ // Set Value
527
  $g[$name] = $val;
528
  } elseif ( isset($g[$name]) ) {
529
+ // Retrieve variable
530
  $ret = $g[$name];
531
  }
532
  return $ret;
546
  function is_options_valid($data, $check_var = true) {
547
  $class = $this->util->get_class('Options');
548
  $ret = ( empty($data) || !is_array($data) || !class_exists($class) ) ? false : true;
549
+ if ( $ret && $check_var && !($this->options instanceof $class) )
550
  $ret = false;
551
  return $ret;
552
  }
includes/class.base_collection.php CHANGED
@@ -64,7 +64,7 @@ class SLB_Base_Collection extends SLB_Base {
64
  * Calls `init` action if collection has a hook prefix
65
  */
66
  private function init() {
67
- //Initialize
68
  if ( is_null($this->items) ) {
69
  $this->items = array();
70
  if ( !empty($this->hook_prefix) ) {
@@ -84,10 +84,10 @@ class SLB_Base_Collection extends SLB_Base {
84
  if ( !is_array($items) ) {
85
  $items = array($items);
86
  }
87
- //Validate item type
88
  if ( !is_null($this->item_type) ) {
89
  foreach ( $items as $idx => $item ) {
90
- //Remove invalid items
91
  if ( !( $item instanceof $this->item_type ) ) {
92
  unset($items[$idx]);
93
  }
@@ -100,7 +100,7 @@ class SLB_Base_Collection extends SLB_Base {
100
  }
101
 
102
  protected function item_valid($item) {
103
- //Validate item type
104
  return ( empty($this->item_type) || ( $item instanceof $this->item_type ) ) ? true : false;
105
  }
106
 
@@ -123,7 +123,7 @@ class SLB_Base_Collection extends SLB_Base {
123
  protected function get_key($item, $check_existing = false) {
124
  $ret = null;
125
  if ( $this->unique || !!$check_existing ) {
126
- //Check for item in collection
127
  if ( $this->has($item) ) {
128
  $ret = array_search($item, $this->items);
129
  } elseif ( !!$this->key_prop && ( is_object($item) || is_array($item) ) ) {
@@ -150,9 +150,9 @@ class SLB_Base_Collection extends SLB_Base {
150
  */
151
  public function add($item, $meta = null) {
152
  $this->init();
153
- //Validate
154
  if ( $this->item_valid($item) ) {
155
- //Add item to collection
156
  $key = $this->get_key($item);
157
  if ( !$key ) {
158
  $this->items[] = $item;
@@ -160,7 +160,7 @@ class SLB_Base_Collection extends SLB_Base {
160
  } else {
161
  $this->items[$key] = $item;
162
  }
163
- //Add metadata
164
  if ( !!$key && is_array($meta) ) {
165
  $this->add_meta($key, $meta);
166
  }
@@ -195,7 +195,7 @@ class SLB_Base_Collection extends SLB_Base {
195
  * @return bool TRUE if item(s) in collection
196
  */
197
  public function has($items) {
198
- //Attempt to locate item
199
  return false;
200
  }
201
 
@@ -207,7 +207,7 @@ class SLB_Base_Collection extends SLB_Base {
207
  */
208
  public function get($args = null) {
209
  $this->init();
210
- //Parse args
211
  $args_default = array(
212
  'orderby' => null,
213
  'order' => 'DESC',
@@ -220,42 +220,42 @@ class SLB_Base_Collection extends SLB_Base {
220
 
221
  /* Sort */
222
  if ( !is_null($r['orderby']) ) {
223
- //Validate
224
  if ( !is_array($r['orderby']) ) {
225
  $r['orderby'] = array('item' => $r['orderby']);
226
  }
227
- //Prep
228
  $metas = ( isset($r['orderby']['meta']) ) ? $this->items_meta : array();
229
- //Sort
230
  foreach ( $r['orderby'] as $stype => $sval ) {
231
  /* Meta sorting */
232
  if ( 'meta' == $stype ) {
233
- //Build sorting buckets
234
  $buckets = array();
235
  foreach ( $metas as $item => $meta ) {
236
  if ( !isset($meta[$sval]) ) {
237
  continue;
238
  }
239
- //Create bucket
240
  $idx = $meta[$sval];
241
  if ( !isset($buckets[ $idx ]) ) {
242
  $buckets[ $idx ] = array();
243
  }
244
- //Add item to bucket
245
  $buckets[ $idx ][] = $item;
246
  }
247
- //Sort buckets
248
  ksort($buckets, SORT_NUMERIC);
249
- //Merge buckets
250
  $pool = array();
251
  foreach ( $buckets as $bucket ) {
252
  $pool = array_merge($pool, $bucket);
253
  }
254
- //Fill with items
255
  $items = array_merge( array_fill_keys($pool, null), $items);
256
  }
257
  }
258
- //Clear workers
259
  unset($stype, $sval, $buckets, $pool, $item, $metas, $meta, $idx);
260
  }
261
  return $items;
@@ -272,18 +272,18 @@ class SLB_Base_Collection extends SLB_Base {
272
  * @return object Current instance
273
  */
274
  protected function add_meta($item, $meta_key, $meta_value = null, $reset = false) {
275
- //Validate
276
  if ( $this->key_valid($item) && ( is_array($meta_key) || is_string($meta_key) ) ) {
277
- //Prepare metadata
278
  $meta = ( is_string($meta_key) ) ? array($meta_key => $meta_value) : $meta_key;
279
- //Reset existing meta (if necessary)
280
  if ( is_array($meta_key) && func_num_args() > 2) {
281
  $reset = func_get_arg(2);
282
  }
283
  if ( !!$reset ) {
284
  unset($this->items_meta[$item]);
285
  }
286
- //Add metadata
287
  if ( !isset($this->items_meta[$item]) ) {
288
  $this->items_meta[$item] = array();
289
  }
@@ -300,10 +300,10 @@ class SLB_Base_Collection extends SLB_Base {
300
  protected function remove_meta($item, $meta_key = null) {
301
  if ( $this->key_valid($item) && isset($this->items_meta[$item]) ) {
302
  if ( is_string($meta_key) ) {
303
- //Remove specific meta value
304
  unset($this->items_meta[$item][$meta_key]);
305
  } else {
306
- //Remove all metadata
307
  unset($this->items_meta[$item]);
308
  }
309
  }
@@ -335,15 +335,15 @@ class SLB_Base_Collection extends SLB_Base {
335
  * Prints output
336
  */
337
  function build($build_vars = array()) {
338
- //Parse vars
339
  $this->parse_build_vars($build_vars);
340
- $this->util->do_action_ref_array('build_init', array(&$this));
341
- //Pre-build output
342
- $this->util->do_action_ref_array('build_pre', array(&$this));
343
- //Build groups
344
  $this->build_groups();
345
- //Post-build output
346
- $this->util->do_action_ref_array('build_post', array(&$this));
347
  }
348
 
349
  /**
64
  * Calls `init` action if collection has a hook prefix
65
  */
66
  private function init() {
67
+ // Initialize
68
  if ( is_null($this->items) ) {
69
  $this->items = array();
70
  if ( !empty($this->hook_prefix) ) {
84
  if ( !is_array($items) ) {
85
  $items = array($items);
86
  }
87
+ // Validate item type
88
  if ( !is_null($this->item_type) ) {
89
  foreach ( $items as $idx => $item ) {
90
+ // Remove invalid items
91
  if ( !( $item instanceof $this->item_type ) ) {
92
  unset($items[$idx]);
93
  }
100
  }
101
 
102
  protected function item_valid($item) {
103
+ // Validate item type
104
  return ( empty($this->item_type) || ( $item instanceof $this->item_type ) ) ? true : false;
105
  }
106
 
123
  protected function get_key($item, $check_existing = false) {
124
  $ret = null;
125
  if ( $this->unique || !!$check_existing ) {
126
+ // Check for item in collection
127
  if ( $this->has($item) ) {
128
  $ret = array_search($item, $this->items);
129
  } elseif ( !!$this->key_prop && ( is_object($item) || is_array($item) ) ) {
150
  */
151
  public function add($item, $meta = null) {
152
  $this->init();
153
+ // Validate
154
  if ( $this->item_valid($item) ) {
155
+ // Add item to collection
156
  $key = $this->get_key($item);
157
  if ( !$key ) {
158
  $this->items[] = $item;
160
  } else {
161
  $this->items[$key] = $item;
162
  }
163
+ // Add metadata
164
  if ( !!$key && is_array($meta) ) {
165
  $this->add_meta($key, $meta);
166
  }
195
  * @return bool TRUE if item(s) in collection
196
  */
197
  public function has($items) {
198
+ // Attempt to locate item
199
  return false;
200
  }
201
 
207
  */
208
  public function get($args = null) {
209
  $this->init();
210
+ // Parse args
211
  $args_default = array(
212
  'orderby' => null,
213
  'order' => 'DESC',
220
 
221
  /* Sort */
222
  if ( !is_null($r['orderby']) ) {
223
+ // Validate
224
  if ( !is_array($r['orderby']) ) {
225
  $r['orderby'] = array('item' => $r['orderby']);
226
  }
227
+ // Prep
228
  $metas = ( isset($r['orderby']['meta']) ) ? $this->items_meta : array();
229
+ // Sort
230
  foreach ( $r['orderby'] as $stype => $sval ) {
231
  /* Meta sorting */
232
  if ( 'meta' == $stype ) {
233
+ // Build sorting buckets
234
  $buckets = array();
235
  foreach ( $metas as $item => $meta ) {
236
  if ( !isset($meta[$sval]) ) {
237
  continue;
238
  }
239
+ // Create bucket
240
  $idx = $meta[$sval];
241
  if ( !isset($buckets[ $idx ]) ) {
242
  $buckets[ $idx ] = array();
243
  }
244
+ // Add item to bucket
245
  $buckets[ $idx ][] = $item;
246
  }
247
+ // Sort buckets
248
  ksort($buckets, SORT_NUMERIC);
249
+ // Merge buckets
250
  $pool = array();
251
  foreach ( $buckets as $bucket ) {
252
  $pool = array_merge($pool, $bucket);
253
  }
254
+ // Fill with items
255
  $items = array_merge( array_fill_keys($pool, null), $items);
256
  }
257
  }
258
+ // Clear workers
259
  unset($stype, $sval, $buckets, $pool, $item, $metas, $meta, $idx);
260
  }
261
  return $items;
272
  * @return object Current instance
273
  */
274
  protected function add_meta($item, $meta_key, $meta_value = null, $reset = false) {
275
+ // Validate
276
  if ( $this->key_valid($item) && ( is_array($meta_key) || is_string($meta_key) ) ) {
277
+ // Prepare metadata
278
  $meta = ( is_string($meta_key) ) ? array($meta_key => $meta_value) : $meta_key;
279
+ // Reset existing meta (if necessary)
280
  if ( is_array($meta_key) && func_num_args() > 2) {
281
  $reset = func_get_arg(2);
282
  }
283
  if ( !!$reset ) {
284
  unset($this->items_meta[$item]);
285
  }
286
+ // Add metadata
287
  if ( !isset($this->items_meta[$item]) ) {
288
  $this->items_meta[$item] = array();
289
  }
300
  protected function remove_meta($item, $meta_key = null) {
301
  if ( $this->key_valid($item) && isset($this->items_meta[$item]) ) {
302
  if ( is_string($meta_key) ) {
303
+ // Remove specific meta value
304
  unset($this->items_meta[$item][$meta_key]);
305
  } else {
306
+ // Remove all metadata
307
  unset($this->items_meta[$item]);
308
  }
309
  }
335
  * Prints output
336
  */
337
  function build($build_vars = array()) {
338
+ // Parse vars
339
  $this->parse_build_vars($build_vars);
340
+ $this->util->do_action_ref_array('build_init', array($this));
341
+ // Pre-build output
342
+ $this->util->do_action_ref_array('build_pre', array($this));
343
+ // Build groups
344
  $this->build_groups();
345
+ // Post-build output
346
+ $this->util->do_action_ref_array('build_post', array($this));
347
  }
348
 
349
  /**
includes/class.base_object.php CHANGED
@@ -98,7 +98,7 @@ class SLB_Base_Object extends SLB_Base {
98
  protected function set_props($props) {
99
  if ( is_array($props) && !empty($props) ) {
100
  foreach ( $props as $key => $val ) {
101
- //Check for setter method
102
  $m = 'set_' . $key;
103
  if ( method_exists($this, $m) ) {
104
  $this->{$m}($val);
@@ -142,9 +142,9 @@ class SLB_Base_Object extends SLB_Base {
142
  $ret = array();
143
  $curr = $this;
144
  while ( $curr->has_parent() ) {
145
- //Add ancestor
146
  $ret[] = $par = $curr->get_parent();
147
- //Get next ancestor
148
  $curr = $par;
149
  }
150
  return $ret;
@@ -161,20 +161,41 @@ class SLB_Base_Object extends SLB_Base {
161
  */
162
  protected function add_file($type, $handle, $src, $deps = array()) {
163
  if ( is_string($type) && is_string($handle) && is_string($src) ) {
164
- //Validate dependencies
165
  if ( !is_array($deps) ) {
166
  $deps = array();
167
  }
168
- //Init file group
169
  if ( !isset($this->files[$type]) || !is_array($this->files[$type]) ) {
170
  $this->files[$type] = array();
171
  }
172
- //Add file to group
173
  $this->files[$type][$handle] = array('handle' => $handle, 'uri' => $src, 'deps' => $deps);
174
  }
175
  return $this;
176
  }
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  /**
179
  * Retrieve files
180
  * All files or a specific group of files can be retrieved
@@ -200,19 +221,39 @@ class SLB_Base_Object extends SLB_Base {
200
  * @return array|null File properties (Default: NULL)
201
  */
202
  protected function get_file($type, $handle, $format = null) {
203
- //Get files
204
  $files = $this->get_files($type);
205
- //Get specified file
206
  $ret = ( is_string($type) && isset($files[$handle]) ) ? $files[$handle] : null;
207
- //Format return value
208
  if ( !empty($ret) && !!$format ) {
209
  switch ( $format ) {
210
  case 'uri':
211
  $ret = $ret['uri'];
 
 
 
 
 
 
 
 
 
 
 
 
212
  break;
213
  case 'object':
214
  $ret = (object) $ret;
215
  break;
 
 
 
 
 
 
 
 
216
  }
217
  }
218
  return $ret;
@@ -232,8 +273,27 @@ class SLB_Base_Object extends SLB_Base {
232
  * Retrieve stylesheet files
233
  * @return array Stylesheet files
234
  */
235
- public function get_styles() {
236
- return $this->get_files('styles');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  }
238
 
239
  /**
98
  protected function set_props($props) {
99
  if ( is_array($props) && !empty($props) ) {
100
  foreach ( $props as $key => $val ) {
101
+ // Check for setter method
102
  $m = 'set_' . $key;
103
  if ( method_exists($this, $m) ) {
104
  $this->{$m}($val);
142
  $ret = array();
143
  $curr = $this;
144
  while ( $curr->has_parent() ) {
145
+ // Add ancestor
146
  $ret[] = $par = $curr->get_parent();
147
+ // Get next ancestor
148
  $curr = $par;
149
  }
150
  return $ret;
161
  */
162
  protected function add_file($type, $handle, $src, $deps = array()) {
163
  if ( is_string($type) && is_string($handle) && is_string($src) ) {
164
+ // Validate dependencies
165
  if ( !is_array($deps) ) {
166
  $deps = array();
167
  }
168
+ // Init file group
169
  if ( !isset($this->files[$type]) || !is_array($this->files[$type]) ) {
170
  $this->files[$type] = array();
171
  }
172
+ // Add file to group
173
  $this->files[$type][$handle] = array('handle' => $handle, 'uri' => $src, 'deps' => $deps);
174
  }
175
  return $this;
176
  }
177
 
178
+ /**
179
+ * Add multiple files
180
+ * @param string $type Group to add files to
181
+ * @param array $files Files to add
182
+ * @see add_file() for file parameters
183
+ * @return object Current instance
184
+ */
185
+ protected function add_files($type, $files) {
186
+ if ( !is_array($files) || empty($files) )
187
+ return false;
188
+ $m = $this->m('add_file');
189
+ foreach ( $files as $file ) {
190
+ if ( !is_array($file) || empty($file) ) {
191
+ continue;
192
+ }
193
+ array_unshift($file, $type);
194
+ call_user_func_array($m, $file);
195
+ }
196
+ return $this;
197
+ }
198
+
199
  /**
200
  * Retrieve files
201
  * All files or a specific group of files can be retrieved
221
  * @return array|null File properties (Default: NULL)
222
  */
223
  protected function get_file($type, $handle, $format = null) {
224
+ // Get files
225
  $files = $this->get_files($type);
226
+ // Get specified file
227
  $ret = ( is_string($type) && isset($files[$handle]) ) ? $files[$handle] : null;
228
+ // Format return value
229
  if ( !empty($ret) && !!$format ) {
230
  switch ( $format ) {
231
  case 'uri':
232
  $ret = $ret['uri'];
233
+ // Normalize URI
234
+ if ( !$this->util->is_uri($ret) ) {
235
+ $ret = $this->util->normalize_path(site_url(), $ret);
236
+ }
237
+ break;
238
+ case 'path':
239
+ $ret = $ret['uri'];
240
+ // Normalize path
241
+ if ( !$this->util->is_uri($ret) ) {
242
+ $ret = $this->util->get_relative_path($ret);
243
+ $ret = $this->util->normalize_path(ABSPATH, $ret);
244
+ }
245
  break;
246
  case 'object':
247
  $ret = (object) $ret;
248
  break;
249
+ case 'contents':
250
+ $ret = $ret['uri'];
251
+ if ( !$this->util->is_uri($ret) ) {
252
+ $ret = $this->util->normalize_path(site_url(), $ret);
253
+ }
254
+ $get = wp_safe_remote_get($ret);
255
+ $ret = ( !is_wp_error($get) && 200 == $get['response']['code'] ) ? $get['body'] : '';
256
+ break;
257
  }
258
  }
259
  return $ret;
273
  * Retrieve stylesheet files
274
  * @return array Stylesheet files
275
  */
276
+ public function get_styles($opts = null) {
277
+ $files = $this->get_files('styles');
278
+ if ( is_array($opts) ) {
279
+ $opts = (object) $opts;
280
+ }
281
+ if ( is_object($opts) && !empty($opts) ) {
282
+ // Parse options
283
+ // URI Format
284
+ if ( isset($opts->uri_format) ) {
285
+ foreach ( $files as $hdl => $props ) {
286
+ switch ( $opts->uri_format ) {
287
+ case 'full':
288
+ if ( !$this->util->is_uri($props['uri']) ) {
289
+ $files[$hdl]['uri'] = $this->util->normalize_path(site_url(), $props['uri']);
290
+ }
291
+ break;
292
+ }
293
+ }
294
+ }
295
+ }
296
+ return $files;
297
  }
298
 
299
  /**
includes/class.component.php CHANGED
@@ -40,6 +40,77 @@ class SLB_Component extends SLB_Base_Object {
40
  return $this->name;
41
  }
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  /* Helpers */
44
 
45
  /**
@@ -50,7 +121,7 @@ class SLB_Component extends SLB_Base_Object {
50
  public function is_valid() {
51
  $ret = parent::is_valid();
52
  if ( $ret ) {
53
- //Check required component properties
54
  $props = array_merge($this->props_required_base, $this->props_required);
55
  foreach ( $props as $prop ) {
56
  if ( !isset($this->{$prop}) || empty($this->{$prop}) ) {
@@ -61,29 +132,4 @@ class SLB_Component extends SLB_Base_Object {
61
  }
62
  return $ret;
63
  }
64
-
65
- /* Client */
66
-
67
- /**
68
- * Set client script file
69
- * @see Base_Object::add_script()
70
- * @param string $src Script URI
71
- * @param array $deps (optional) File dependencies
72
- */
73
- public function set_client_script($src, $deps = array()) {
74
- if ( is_array($src) ) {
75
- list($src, $deps) = func_get_arg(0);
76
- }
77
- return $this->add_script('client', $src, $deps);
78
- }
79
-
80
- /**
81
- * Retrieve client script
82
- * @see Base_Object::get_script()
83
- * @param string $format (optional) Data format of return value
84
- * @return mixed Client script data (formatted according to $format parameter)
85
- */
86
- public function get_client_script($format = null) {
87
- return $this->get_script('client', $format);
88
- }
89
  }
40
  return $this->name;
41
  }
42
 
43
+ public function set_scripts($scripts) {
44
+ $this->add_files('scripts', $scripts);
45
+ }
46
+
47
+ public function set_styles($styles) {
48
+ $this->add_files('styles', $styles);
49
+ }
50
+
51
+ /* Assets */
52
+
53
+ /**
54
+ * Get formatted handle for file
55
+ * @param string $base_handle Base handle to format
56
+ * @return string Formatted handle
57
+ */
58
+ public function get_handle($base_handle) {
59
+ return $this->add_prefix( array('asset', $this->get_id(), $base_handle), '-');
60
+ }
61
+
62
+ /**
63
+ * Enqueue files in client
64
+ * @param string $type (optional) Type of file to load (singular) (Default: All client file types)
65
+ */
66
+ public function enqueue_client_files($type = null) {
67
+ if ( empty($type) ) {
68
+ $type = array ( 'script', 'style');
69
+ }
70
+ if ( !is_array($type) ) {
71
+ $type = array ( $type );
72
+ }
73
+ foreach ( $type as $t ) {
74
+ $m = (object) array (
75
+ 'get' => $this->m('get_' . $t . 's'),
76
+ 'enqueue' => 'wp_enqueue_' . $t,
77
+ );
78
+ $v = $this->util->get_plugin_version();
79
+ $files = call_user_func($m->get);
80
+ $param_final = ( 'script' == $t ) ? true : 'all';
81
+ foreach ( $files as $f ) {
82
+ $f = (object) $f;
83
+ // Format handle
84
+ $handle = $this->get_handle($f->handle);
85
+
86
+ // Format dependencies
87
+ $deps = array();
88
+ foreach ( $f->deps as $dep ) {
89
+ if ( $this->util->has_wrapper($dep) ) {
90
+ $dep = $this->get_handle( $this->util->remove_wrapper($dep) );
91
+ }
92
+ $deps[] = $dep;
93
+ }
94
+ call_user_func($m->enqueue, $handle, $f->uri, $deps, $v, $param_final);
95
+ }
96
+ unset($files, $f, $param_final, $handle, $deps, $dep);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Enqueue scripts
102
+ */
103
+ public function enqueue_scripts() {
104
+ $this->enqueue_client_files('script');
105
+ }
106
+
107
+ /**
108
+ * Enqueue styles
109
+ */
110
+ public function enqueue_styles() {
111
+ $this->enqueue_client_files('style');
112
+ }
113
+
114
  /* Helpers */
115
 
116
  /**
121
  public function is_valid() {
122
  $ret = parent::is_valid();
123
  if ( $ret ) {
124
+ // Check required component properties
125
  $props = array_merge($this->props_required_base, $this->props_required);
126
  foreach ( $props as $prop ) {
127
  if ( !isset($this->{$prop}) || empty($this->{$prop}) ) {
132
  }
133
  return $ret;
134
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  }
includes/class.content_handler.php CHANGED
@@ -15,6 +15,12 @@ class SLB_Content_Handler extends SLB_Component {
15
  */
16
  protected $match;
17
 
 
 
 
 
 
 
18
  /* Matching */
19
 
20
  /**
@@ -47,11 +53,30 @@ class SLB_Content_Handler extends SLB_Component {
47
  * @param string $uri URI to check for match
48
  * @return bool TRUE if handler matches URI
49
  */
50
- public function match($uri) {
51
  $ret = false;
52
  if ( !!$uri && is_string($uri) && $this->has_match() ) {
53
- $ret = call_user_func($this->get_match(), $uri);
54
  }
55
  return $ret;
56
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
15
  */
16
  protected $match;
17
 
18
+ /**
19
+ * Custom attributes
20
+ * @var callback
21
+ */
22
+ protected $attributes;
23
+
24
  /* Matching */
25
 
26
  /**
53
  * @param string $uri URI to check for match
54
  * @return bool TRUE if handler matches URI
55
  */
56
+ public function match($uri, $uri_raw = null) {
57
  $ret = false;
58
  if ( !!$uri && is_string($uri) && $this->has_match() ) {
59
+ $ret = call_user_func($this->get_match(), $uri, $uri_raw);
60
  }
61
  return $ret;
62
  }
63
+
64
+ /* Attributes */
65
+
66
+ public function set_attributes($callback) {
67
+ $this->attributes = ( is_callable($callback) ) ? $callback : null;
68
+ return $this;
69
+ }
70
+
71
+ public function get_attributes() {
72
+ $ret = array();
73
+ // Callback
74
+ if ( !is_null($this->attributes) ) {
75
+ $ret = call_user_func($this->attributes);
76
+ }
77
+ // Filter
78
+ $hook = sprintf('content_handler_%s_attributes', $this->get_id());
79
+ $ret = $this->util->apply_filters($hook, $ret);
80
+ return ( is_array($ret) ) ? $ret : array();
81
+ }
82
  }
includes/class.content_handlers.php CHANGED
@@ -31,9 +31,9 @@ class SLB_Content_Handlers extends SLB_Collection_Controller {
31
 
32
  protected function _hooks() {
33
  parent::_hooks();
34
- $this->util->add_action('init', $this->m('init_defaults'));
35
-
36
- add_action('wp_footer', $this->m('client_output'), $this->util->priority('client_footer_output'));
37
  }
38
 
39
  /* Collection Management */
@@ -50,10 +50,10 @@ class SLB_Content_Handlers extends SLB_Collection_Controller {
50
  public function add($id, $props = array(), $priority = 10) {
51
  $this->clear_cache();
52
  if ( is_string($id) ) {
53
- //Initialize new handler
54
  $handler = new $this->item_type($id, $props);
55
  } else {
56
- //Remap parameters
57
  $handler = func_get_arg(0);
58
  if ( func_num_args() == 2 ) {
59
  $priority = func_get_arg(1);
@@ -62,7 +62,7 @@ class SLB_Content_Handlers extends SLB_Collection_Controller {
62
  if ( !is_int($priority) ) {
63
  $priority = 10;
64
  }
65
- //Add to collection
66
  return parent::add($handler, array('priority' => $priority));
67
  }
68
 
@@ -92,12 +92,13 @@ class SLB_Content_Handlers extends SLB_Collection_Controller {
92
  * Retrieves handlers sorted by priority
93
  * @see parent::get()
94
  * @uses get_cache()
 
95
  * @return array Handlers
96
  */
97
- public function get() {
98
  $items = $this->get_cache();
99
  if ( empty($items) ) {
100
- //Retrieve items
101
  $items = parent::get( array( 'orderby' => array('meta' => 'priority') ) );
102
  $this->update_cache($items);
103
  }
@@ -107,20 +108,30 @@ class SLB_Content_Handlers extends SLB_Collection_Controller {
107
  /**
108
  * Get matching handler for URI
109
  * @param string $uri URI to find match for
110
- * @return SLB_Content_Handler Matching handler (NULL if no handler matched)
 
 
 
111
  */
112
  public function match($uri) {
 
113
  foreach ( $this->get() as $handler ) {
114
- if ( $handler->match($uri) ) {
115
- //Save match
 
 
 
 
 
 
116
  $hid = $handler->get_id();
117
  if ( !isset($this->request_matches[$hid]) ) {
118
  $this->request_matches[$hid] = $handler;
119
  }
120
- return $handler;
121
  }
122
  }
123
- return null;
124
  }
125
 
126
  /* Cache */
@@ -175,54 +186,92 @@ class SLB_Content_Handlers extends SLB_Collection_Controller {
175
 
176
  /**
177
  * Initialize default handlers
178
- * @param SLB_Content_Handlers $controller Handlers controller
179
  */
180
- public function init_defaults($controller) {
181
- $handlers = array (
 
 
 
182
  'image' => array (
183
  'match' => $this->m('match_image'),
184
- 'client_script' => $this->util->get_file_url('content-handlers/image/handler.image.js'),
185
- ),
 
 
186
  );
187
- foreach ( $handlers as $id => $props ) {
188
- $controller->add($id, $props);
189
  }
190
  }
191
 
192
  /**
193
  * Matches image URIs
194
  * @param string $uri URI to match
195
- * @return bool TRUE if URI is image
196
  */
197
- public function match_image($uri) {
198
- return ( $this->util->has_file_extension($uri, array('jpg', 'jpeg', 'jpe', 'jfif', 'jif', 'gif', 'png')) ) ? true : false;
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
200
 
201
  /* Output */
202
 
203
  /**
204
- * Client output
 
205
  */
206
  public function client_output() {
207
- //Stop if not enabled
208
- if ( !$this->has_parent() || !$this->get_parent()->is_enabled() ) {
209
- return;
210
  }
211
- $id_fmt = 'add_handler_%s';
212
- $out = array();
213
- $out[] = '<!-- SLB-HDL -->' . PHP_EOL;
 
 
 
 
 
 
214
  $code = array();
215
- //Load matched handlers
216
  foreach ( $this->request_matches as $handler ) {
217
- //Define
 
 
 
 
 
 
 
 
 
 
218
  $params = array(
219
  sprintf("'%s'", $handler->get_id()),
220
- sprintf("'%s'", $handler->get_client_script('uri')),
221
  );
222
- $code[] = $this->util->call_client_method('View.add_content_handler', $params, false);
 
 
 
 
 
223
  }
224
- $out[] = $this->util->build_script_element(implode('', $code), 'add_content_handlers', true, true);
225
- $out[] = '<!-- /SLB-HDL -->' . PHP_EOL;
226
- echo implode('', $out);
227
  }
228
  }
31
 
32
  protected function _hooks() {
33
  parent::_hooks();
34
+ $this->util->add_action('init', $this->m('init_defaults'), 5);
35
+ $this->util->add_action('footer', $this->m('client_output'), 1, 0, false);
36
+ $this->util->add_filter('footer_script', $this->m('client_output_script'), $this->util->priority('client_footer_output'), 1, false);
37
  }
38
 
39
  /* Collection Management */
50
  public function add($id, $props = array(), $priority = 10) {
51
  $this->clear_cache();
52
  if ( is_string($id) ) {
53
+ // Initialize new handler
54
  $handler = new $this->item_type($id, $props);
55
  } else {
56
+ // Remap parameters
57
  $handler = func_get_arg(0);
58
  if ( func_num_args() == 2 ) {
59
  $priority = func_get_arg(1);
62
  if ( !is_int($priority) ) {
63
  $priority = 10;
64
  }
65
+ // Add to collection
66
  return parent::add($handler, array('priority' => $priority));
67
  }
68
 
92
  * Retrieves handlers sorted by priority
93
  * @see parent::get()
94
  * @uses get_cache()
95
+ * @param mixed $args Unused
96
  * @return array Handlers
97
  */
98
+ public function get($args = null) {
99
  $items = $this->get_cache();
100
  if ( empty($items) ) {
101
+ // Retrieve items
102
  $items = parent::get( array( 'orderby' => array('meta' => 'priority') ) );
103
  $this->update_cache($items);
104
  }
108
  /**
109
  * Get matching handler for URI
110
  * @param string $uri URI to find match for
111
+ * @return object Handler package (FALSE if no match found)
112
+ * Package members
113
+ * > handler (Content_Handler) Matching handler instance (Default: NULL)
114
+ * > props (array) Properties returned from matching handler (May be empty depending on handler)
115
  */
116
  public function match($uri) {
117
+ $ret = (object) array('handler' => null, 'props' => array());
118
  foreach ( $this->get() as $handler ) {
119
+ $props = $handler->match($uri, $this);
120
+ if ( !!$props ) {
121
+ $ret->handler = $handler;
122
+ // Add handler props
123
+ if ( is_array($props) ) {
124
+ $ret->props = $props;
125
+ }
126
+ // Save match
127
  $hid = $handler->get_id();
128
  if ( !isset($this->request_matches[$hid]) ) {
129
  $this->request_matches[$hid] = $handler;
130
  }
131
+ break;
132
  }
133
  }
134
+ return $ret;
135
  }
136
 
137
  /* Cache */
186
 
187
  /**
188
  * Initialize default handlers
189
+ * @param SLB_Content_Handlers $handlers Handlers controller
190
  */
191
+ public function init_defaults($handlers) {
192
+ $src_base = $this->util->get_file_url('content-handlers', true);
193
+ $js_path = 'js/';
194
+ $js_path .= ( SLB_DEV ) ? 'dev' : 'prod';
195
+ $defaults = array (
196
  'image' => array (
197
  'match' => $this->m('match_image'),
198
+ 'scripts' => array (
199
+ array ( 'base', "$src_base/image/$js_path/handler.image.js" ),
200
+ ),
201
+ )
202
  );
203
+ foreach ( $defaults as $id => $props ) {
204
+ $handlers->add($id, $props);
205
  }
206
  }
207
 
208
  /**
209
  * Matches image URIs
210
  * @param string $uri URI to match
211
+ * @return bool|array TRUE if URI is image (array is used if extra data needs to be sent)
212
  */
213
+ public function match_image($uri, $handlers) {
214
+ // Basic matching
215
+ $match = ( $this->util->has_file_extension($uri, array('jpg', 'jpeg', 'jpe', 'jfif', 'jif', 'gif', 'png')) ) ? true : false;
216
+
217
+ // Filter result
218
+ $extra = new stdClass();
219
+ $match = $this->util->apply_filters('image_match', $match, $uri, $extra);
220
+
221
+ // Handle extra data passed from filters
222
+ // Currently only `uri` supported
223
+ if ( $match && isset($extra->uri) && is_string($extra->uri) ) {
224
+ $match = array('uri' => $extra->uri);
225
+ }
226
+
227
+ return $match;
228
  }
229
 
230
  /* Output */
231
 
232
  /**
233
+ * Build client output
234
+ * Load handler files in client
235
  */
236
  public function client_output() {
237
+ // Get handlers for current request
238
+ foreach ( $this->request_matches as $handler ) {
239
+ $handler->enqueue_scripts();
240
  }
241
+ }
242
+
243
+ /**
244
+ * Client output script
245
+ * @param array $commands Client script commands
246
+ * @return array Modified script commands
247
+ */
248
+ public function client_output_script($commands) {
249
+ $out = array('/* CHDL */');
250
  $code = array();
251
+
252
  foreach ( $this->request_matches as $handler ) {
253
+ // Attributes
254
+ $attrs = $handler->get_attributes();
255
+ // Styles
256
+ $styles = $handler->get_styles(array('uri_format'=>'full'));
257
+ if ( !empty($styles) ) {
258
+ $attrs['styles'] = array_values($styles);
259
+ }
260
+ if ( empty($attrs) ) {
261
+ continue;
262
+ }
263
+ // Setup client parameters
264
  $params = array(
265
  sprintf("'%s'", $handler->get_id()),
266
+ json_encode($attrs),
267
  );
268
+ // Extend handler in client
269
+ $code[] = $this->util->call_client_method('View.extend_content_handler', $params, false);
270
+ }
271
+ if ( !empty($code) ) {
272
+ $out[] = implode('', $code);
273
+ $commands[] = implode(PHP_EOL, $out);
274
  }
275
+ return $commands;
 
 
276
  }
277
  }
includes/class.field.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ class SLB_Field extends SLB_Field_Type {}
includes/class.field_base.php ADDED
@@ -0,0 +1,1048 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Fields - Base class
5
+ * Core properties/methods for fields
6
+ * @package Simple Lightbox
7
+ * @subpackage Fields
8
+ * @author Archetyped
9
+ */
10
+ class SLB_Field_Base extends SLB_Base {
11
+ /*-** Config **-*/
12
+ protected $mode = 'object';
13
+ protected $shared = false;
14
+
15
+ /*-** Properties **-*/
16
+
17
+ /**
18
+ * @var string Unique name
19
+ */
20
+ var $id = '';
21
+
22
+ /**
23
+ * ID formatting options
24
+ * Merged with defaults during initialization
25
+ * @see $id_formats_default
26
+ * @var array
27
+ */
28
+ var $id_formats = null;
29
+
30
+ /**
31
+ * Default ID Formatting options
32
+ * Structure:
33
+ * > Key (string): Format name
34
+ * > Val (array): Options
35
+ * @var array
36
+ */
37
+ var $id_formats_default = array(
38
+ 'attr_id' => array(
39
+ 'wrap' => array('open' => '_', 'segment_open' => '_'),
40
+ 'prefix' => array('get_container', 'get_id', 'add_prefix'),
41
+ 'recursive' => true
42
+ ),
43
+ 'attr_name' => array(
44
+ 'wrap' => array('open' => '[', 'close' => ']', 'segment_open' => '[', 'segment_close' => ']'),
45
+ 'recursive' => true,
46
+ 'prefix' => array('get_container', 'get_id', 'add_prefix')
47
+ )
48
+ );
49
+
50
+ /**
51
+ * Special characters/phrases
52
+ * Used for preserving special characters during formatting
53
+ * Merged with $special_chars_default
54
+ * Array Structure
55
+ * > Key: Special character/phrase
56
+ * > Value: Placeholder for special character
57
+ * @var array
58
+ */
59
+ var $special_chars = null;
60
+
61
+ var $special_chars_default = array(
62
+ '{' => '%SQB_L%',
63
+ '}' => '%SQB_R%',
64
+ );
65
+
66
+ /**
67
+ * Reference to parent object that current instance inherits from
68
+ * @var object
69
+ */
70
+ var $parent = null;
71
+
72
+ /**
73
+ * Title
74
+ * @var string
75
+ */
76
+ var $title = '';
77
+
78
+ /**
79
+ * @var string Short description
80
+ */
81
+ var $description = '';
82
+
83
+ /**
84
+ * @var array Object Properties
85
+ */
86
+ var $properties = array();
87
+
88
+ /**
89
+ * Initialization properties
90
+ * @var array
91
+ */
92
+ protected $properties_init = null;
93
+
94
+ /**
95
+ * Structure: Property names stored as keys in group
96
+ * Root
97
+ * -> Group Name
98
+ * -> Property Name => Null
99
+ * Reason: Faster searching over large arrays
100
+ * @var array Groupings of Properties
101
+ */
102
+ var $property_groups = array();
103
+
104
+ /**
105
+ * Keys to filter out of properties array before setting properties
106
+ * @var array
107
+ */
108
+ var $property_filter = array('group');
109
+
110
+ /**
111
+ * Define order of properties
112
+ * Useful when processing order is important (e.g. one property depends on another)
113
+ * @var array
114
+ */
115
+ var $property_priority = array();
116
+
117
+ /**
118
+ * Data for object
119
+ * May also contain data for nested objects
120
+ * @var mixed
121
+ */
122
+ var $data = null;
123
+
124
+ /**
125
+ * Whether data has been fetched or not
126
+ * @var bool
127
+ */
128
+ var $data_loaded = false;
129
+
130
+ /**
131
+ * @var array Script resources to include for object
132
+ */
133
+ var $scripts = array();
134
+
135
+ /**
136
+ * @var array CSS style resources to include for object
137
+ */
138
+ var $styles = array();
139
+
140
+ /**
141
+ * Hooks (Filters/Actions) for object
142
+ * @var array
143
+ */
144
+ var $hooks = array();
145
+
146
+ /**
147
+ * Mapping of child properties to parent members
148
+ * Allows more flexibility when creating new instances of child objects using property arrays
149
+ * Associative array structure:
150
+ * > Key: Child property to map FROM
151
+ * > Val: Parent property to map TO
152
+ * @var array
153
+ */
154
+ var $map = null;
155
+
156
+ /**
157
+ * Options used when building collection (callbacks, etc.)
158
+ * Associative array
159
+ * > Key: Option name
160
+ * > Value: Option value
161
+ * @var array
162
+ */
163
+ var $build_vars = array();
164
+
165
+ var $build_vars_default = array();
166
+
167
+ /**
168
+ * Constructor
169
+ */
170
+ function __construct($id = '', $properties = null) {
171
+ parent::__construct();
172
+ // Normalize Properties
173
+ $args = func_get_args();
174
+ $defaults = $this->integrate_id($id);
175
+ $properties = $this->make_properties($args, $defaults);
176
+ // Save init properties
177
+ $this->properties_init = $properties;
178
+ // Set Properties
179
+ $this->set_properties($properties);
180
+ }
181
+
182
+ /* Getters/Setters */
183
+
184
+ /**
185
+ * Checks if the specified path exists in the object
186
+ * @param array $path Path to check for
187
+ * @return bool TRUE if path exists in object, FALSE otherwise
188
+ */
189
+ function path_isset($path = '') {
190
+ // Stop execution if no path is supplied
191
+ if ( empty($path) )
192
+ return false;
193
+ $args = func_get_args();
194
+ $path = $this->util->build_path($args);
195
+ $item =& $this;
196
+ // Iterate over path and check if each level exists before moving on to the next
197
+ for ($x = 0; $x < count($path); $x++) {
198
+ if ( $this->util->property_exists($item, $path[$x]) ) {
199
+ // Set $item as reference to next level in path for next iteration
200
+ $item =& $this->util->get_property($item, $path[$x]);
201
+ // $item =& $item[ $path[$x] ];
202
+ } else {
203
+ return false;
204
+ }
205
+ }
206
+ return true;
207
+ }
208
+
209
+ /**
210
+ * Retrieves a value from object using a specified path
211
+ * Checks to make sure path exists in object before retrieving value
212
+ * @param array $path Path to retrieve value from. Each item in array is a deeper dimension
213
+ * @return mixed Value at specified path
214
+ */
215
+ function &get_path_value($path = '') {
216
+ $ret = '';
217
+ $path = $this->util->build_path(func_get_args());
218
+ if ( $this->path_isset($path) ) {
219
+ $ret =& $this;
220
+ for ($x = 0; $x < count($path); $x++) {
221
+ if ( 0 == $x )
222
+ $ret =& $ret->{ $path[$x] };
223
+ else
224
+ $ret =& $ret[ $path[$x] ];
225
+ }
226
+ }
227
+ return $ret;
228
+ }
229
+
230
+ /**
231
+ * Search for specified member value in field type ancestors
232
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
233
+ * @param string $name Value to retrieve from member
234
+ * @return mixed Member value if found (Default: empty string)
235
+ */
236
+ function get_parent_value($member, $name = '', $default = '') {
237
+ $parent = $this->get_parent();
238
+ return $this->get_object_value($parent, $member, $name, $default, 'parent');
239
+ }
240
+
241
+ /**
242
+ * Retrieves specified member value
243
+ * Handles inherited values
244
+ * Merging corresponding parents if value is an array (e.g. for property groups)
245
+ * @param string|array $member Member to search. May also contain a path to the desired member
246
+ * @param string $name Value to retrieve from member
247
+ * @param mixed $default Default value if no value found (Default: empty string)
248
+ * @param string $dir Direction to move through hierarchy to find value
249
+ * Possible Values:
250
+ * parent (default) - Search through field parents
251
+ * current - Do not search through connected objects
252
+ * container - Search through field containers
253
+ * caller - Search through field callers
254
+ * @return mixed Specified member value
255
+ * @todo Return reference
256
+ */
257
+ function &get_member_value($member, $name = '', $default = '', $dir = 'parent') {
258
+ // Check if path to member is supplied
259
+ $path = array();
260
+ if ( is_array($member) && isset($member['tag']) ) {
261
+ if ( isset($member['attributes']['ref_base']) ) {
262
+ if ( 'root' != $member['attributes']['ref_base'] )
263
+ $path[] = $member['attributes']['ref_base'];
264
+ } else {
265
+ $path[] = 'properties';
266
+ }
267
+
268
+ $path[] = $member['tag'];
269
+ } else {
270
+ $path = $member;
271
+ }
272
+
273
+ $path = $this->util->build_path($path, $name);
274
+ // Set defaults and prepare data
275
+ $val = $default;
276
+ $inherit = false;
277
+ $inherit_tag = '{inherit}';
278
+
279
+ /* Determine whether the value must be retrieved from a parent/container object
280
+ * Conditions:
281
+ * > Path does not exist in current field
282
+ * > Path exists and is not an object, but at least one of the following is true:
283
+ * > Value at path is an array (e.g. properties, elements, etc. array)
284
+ * > Parent/container values should be merged with retrieved array
285
+ * > Value at path is a string that inherits from another field
286
+ * > Value from other field will be retrieved and will replace inheritance placeholder in retrieved value
287
+ */
288
+
289
+ $deeper = false;
290
+
291
+ if ( !$this->path_isset($path) )
292
+ $deeper = true;
293
+ else {
294
+ $val = $this->get_path_value($path);
295
+ if ( !is_object($val) && ( is_array($val) || ($inherit = strpos($val, $inherit_tag)) !== false ) )
296
+ $deeper = true;
297
+ else
298
+ $deeper = false;
299
+ }
300
+ if ( $deeper && 'current' != $dir ) {
301
+ $ex_val = '';
302
+ // Get Parent value (recursive)
303
+ if ( 'parent' == $dir )
304
+ $ex_val = $this->get_parent_value($member, $name, $default);
305
+ elseif ( method_exists($this, 'get_container_value') )
306
+ $ex_val = $this->get_container_value($member, $name, $default);
307
+ // Handle inheritance
308
+ if ( is_array($val) ) {
309
+ // Combine Arrays
310
+ if ( is_array($ex_val) )
311
+ $val = array_merge($ex_val, $val);
312
+ } elseif ( $inherit !== false ) {
313
+ // Replace placeholder with inherited string
314
+ $val = str_replace($inherit_tag, $ex_val, $val);
315
+ } else {
316
+ // Default: Set parent value as value
317
+ $val = $ex_val;
318
+ }
319
+ }
320
+
321
+ return $val;
322
+ }
323
+
324
+ /**
325
+ * Search for specified member value in an object
326
+ * @param object $object Reference to object to retrieve value from
327
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
328
+ * @param string $name (optional) Value to retrieve from member
329
+ * @param mixed $default (optional) Default value to use if no value found (Default: empty string)
330
+ * @param string $dir Direction to move through hierarchy to find value @see SLB_Field_Type::get_member_value() for possible values
331
+ * @return mixed Member value if found (Default: $default)
332
+ */
333
+ function get_object_value(&$object, $member, $name = '', $default = '', $dir = 'parent') {
334
+ $ret = $default;
335
+ if ( is_object($object) && method_exists($object, 'get_member_value') )
336
+ $ret = $object->get_member_value($member, $name, $default, $dir);
337
+ return $ret;
338
+ }
339
+
340
+ /**
341
+ * Set item ID
342
+ * @param string $id Unique item ID
343
+ */
344
+ function set_id($id) {
345
+ if ( empty($id) || !is_string($id) )
346
+ return false;
347
+ $this->id = trim($id);
348
+ }
349
+
350
+ /**
351
+ * Retrieves field ID
352
+ * @param array|string $options (optional) Options or ID of format to use
353
+ * @return string item ID
354
+ */
355
+ function get_id($options = array()) {
356
+ $item_id = trim($this->id);
357
+ $formats = $this->get_id_formats();
358
+
359
+ // Setup options
360
+ $wrap_default = array('open' => '', 'close' => '', 'segment_open' => '', 'segment_close' => '');
361
+
362
+ $options_default = array(
363
+ 'format' => null,
364
+ 'wrap' => array(),
365
+ 'segments_pre' => null,
366
+ 'prefix' => '',
367
+ 'recursive' => false
368
+ );
369
+
370
+ // Load options based on format
371
+ if ( !is_array($options) )
372
+ $options = array('format' => $options);
373
+ if ( isset($options['format']) && is_string($options['format']) && isset($formats[$options['format']]) )
374
+ $options_default = wp_parse_args($formats[$options['format']], $options_default);
375
+ else
376
+ unset($options['format']);
377
+ $options = wp_parse_args($options, $options_default);
378
+ // Import options into function
379
+ extract($options);
380
+
381
+ // Validate options
382
+ $wrap = wp_parse_args($wrap, $wrap_default);
383
+
384
+ if ( !is_array($segments_pre) )
385
+ $segments_pre = array($segments_pre);
386
+ $segments_pre = array_reverse($segments_pre);
387
+
388
+ // Format ID based on options
389
+ $item_id = array($item_id);
390
+
391
+ // Add parent objects to ID
392
+ if ( !!$recursive ) {
393
+ // Create array of ID components
394
+ $m = 'get_caller';
395
+ $c = ( method_exists($this, $m) ) ? $this->{$m}() : null;
396
+ while ( !!$c ) {
397
+ // Add ID of current caller to array
398
+ if ( method_exists($c, 'get_id') && ( $itemp = $c->get_id() ) && !empty($itemp) )
399
+ $item_id = $itemp;
400
+ // Get parent object
401
+ $c = ( method_exists($c, $m) ) ? $c->{$m}() : null;
402
+ $itemp = '';
403
+ }
404
+ unset($c);
405
+ }
406
+
407
+ // Additional segments (Pre)
408
+ foreach ( $segments_pre as $seg ) {
409
+ if ( is_null($seg) )
410
+ continue;
411
+ if ( is_object($seg) )
412
+ $seg = (array)$seg;
413
+ if ( is_array($seg) )
414
+ $item_id = array_merge($item_id, array_reverse($seg));
415
+ elseif ( '' != strval($seg) )
416
+ $item_id[] = strval($seg);
417
+ }
418
+
419
+ // Prefix
420
+ if ( is_array($prefix) ) {
421
+ // Array is sequence of instance methods to call on object
422
+ // Last array member can be an array of parameters to pass to methods
423
+ $count = count($prefix);
424
+ $args = ( $count > 1 && is_array($prefix[$count - 1]) ) ? array_pop($prefix) : array();
425
+ $p = $this;
426
+ $val = '';
427
+ // Iterate through methods
428
+ foreach ( $prefix as $m ) {
429
+ if ( !method_exists($p, $m) )
430
+ continue;
431
+ // Build callback
432
+ $m = $this->util->m($p, $m);
433
+ // Call callback
434
+ $val = call_user_func_array($m, $args);
435
+ // Returned value may be an instance object
436
+ if ( is_object($val) )
437
+ $p = $val; // Use returned object in next round
438
+ else
439
+ array_unshift($args, $val); // Pass returned value as parameter to next method on using current object
440
+ }
441
+ $prefix = $val;
442
+ unset($p, $val);
443
+ }
444
+ if ( is_numeric($prefix) )
445
+ $prefix = strval($prefix);
446
+ if ( empty($prefix) || !is_string($prefix) )
447
+ $prefix = '';
448
+
449
+ // Convert array to string
450
+ $item_id = $prefix . $wrap['open'] . implode($wrap['segment_close'] . $wrap['segment_open'], array_reverse($item_id)) . $wrap['close'];
451
+ return $item_id;
452
+ }
453
+
454
+ /**
455
+ * Retrieve ID formatting options for class
456
+ * Format options arrays are merged together and saved to $id_formats
457
+ * @uses $id_formats
458
+ * @uses $id_formats_default
459
+ * @return array ID Formatting options
460
+ */
461
+ function &get_id_formats() {
462
+ if ( is_null($this->id_formats) ) {
463
+ $this->id_formats = wp_parse_args($this->id_formats, $this->id_formats_default);
464
+ }
465
+ return $this->id_formats;
466
+ }
467
+
468
+ /**
469
+ * Retrieve value from data member
470
+ * @param string $context Context to format data for
471
+ * @param bool $top (optional) Whether to traverse through the field hierarchy to get data for field (Default: TRUE)
472
+ * @return mixed Value at specified path
473
+ */
474
+ function get_data($context = '', $top = true) {
475
+ $opt_d = array('context' => '', 'top' => true);
476
+ $args = func_get_args();
477
+ $a = false;
478
+ if ( count($args) == 1 && is_array($args[0]) && !empty($args[0]) ) {
479
+ $a = true;
480
+ $args = wp_parse_args($args[0], $opt_d);
481
+ extract($args);
482
+ }
483
+
484
+ if ( is_string($top) ) {
485
+ if ( 'false' == $top )
486
+ $top = false;
487
+ elseif ( 'true' == $top )
488
+ $top = true;
489
+ elseif ( is_numeric($top) )
490
+ $top = intval($top);
491
+ }
492
+ $top = !!$top;
493
+ $obj =& $this;
494
+ $obj_path = array($this);
495
+ $path = array();
496
+ if ( $top ) {
497
+ // Iterate through hiearchy to get top-most object
498
+ while ( !empty($obj) ) {
499
+ $new = null;
500
+ // Try to get caller first
501
+ if ( method_exists($obj, 'get_caller') ) {
502
+ $checked = true;
503
+ $new =& $obj->get_caller();
504
+ }
505
+ // Try to get container if no caller found
506
+ if ( empty($new) && method_exists($obj, 'get_container') ) {
507
+ $checked = true;
508
+ $new =& $obj->get_container();
509
+ // Load data
510
+ if ( method_exists($new, 'load_data') ) {
511
+ $new->load_data();
512
+ }
513
+ }
514
+
515
+ $obj =& $new;
516
+ unset($new);
517
+ // Stop iteration
518
+ if ( !empty($obj) ) {
519
+ // Add object to path if it is valid
520
+ $obj_path[] =& $obj;
521
+ }
522
+ }
523
+ unset($obj);
524
+ }
525
+
526
+ // Check each object (starting with top-most) for matching data for current field
527
+
528
+ // Reverse array
529
+ $obj_path = array_reverse($obj_path);
530
+ // Build path for data location
531
+ foreach ( $obj_path as $obj ) {
532
+ if ( method_exists($obj, 'get_id') )
533
+ $path[] = $obj->get_id();
534
+ }
535
+ // Iterate through objects
536
+ while ( !empty($obj_path) ) {
537
+ // Get next object
538
+ $obj = array_shift($obj_path);
539
+ // Shorten path
540
+ array_shift($path);
541
+ // Check for value in object and stop iteration if matching data found
542
+ $val = $this->get_object_value($obj, 'data', $path, null, 'current');
543
+ if ( !is_null($val) ) {
544
+ break;
545
+ }
546
+ }
547
+ return $this->format($val, $context);
548
+ }
549
+
550
+ /**
551
+ * Sets value in data member
552
+ * Sets value to data member itself by default
553
+ * @param mixed $value Value to set
554
+ * @param string|array $name Name of value to set (Can also be path to value)
555
+ */
556
+ function set_data($value, $name = '') {
557
+ $ref =& $this->get_path_value('data', $name);
558
+ $ref = $value;
559
+ }
560
+
561
+ /**
562
+ * Sets parent object of current instance
563
+ * Parent objects must be the same object type as current instance
564
+ * @uses SLB to get field type definition
565
+ * @uses SLB_Fields::has() to check if field type exists
566
+ * @uses SLB_Fields::get() to retrieve field type object reference
567
+ * @param string|object $parent Parent ID or reference
568
+ */
569
+ function set_parent($parent = null) {
570
+ // Stop processing if parent empty
571
+ if ( empty($parent) && !is_string($this->parent) )
572
+ return false;
573
+ // Parent passed as object reference wrapped in array
574
+ if ( is_array($parent) && isset($parent[0]) && is_object($parent[0]) )
575
+ $parent = $parent[0];
576
+
577
+ // No parent set but parent ID (previously) set in object
578
+ if ( empty($parent) && is_string($this->parent) )
579
+ $parent = $this->parent;
580
+
581
+ // Retrieve reference object if ID was supplied
582
+ if ( is_string($parent) ) {
583
+ $parent = trim($parent);
584
+ // Get parent object reference
585
+ /**
586
+ * @var SLB
587
+ */
588
+ $b = $this->get_base();
589
+ if ( !!$b && isset($b->fields) && $b->fields->has($parent) ) {
590
+ $parent = $b->fields->get($parent);
591
+ }
592
+ }
593
+
594
+ // Set parent value on object
595
+ if ( is_string($parent) || is_object($parent) )
596
+ $this->parent = $parent;
597
+ }
598
+
599
+ /**
600
+ * Retrieve field type parent
601
+ * @return SLB_Field_Type Parent field
602
+ */
603
+ function get_parent() {
604
+ return $this->parent;
605
+ }
606
+
607
+ /**
608
+ * Set object title
609
+ * @param string $title Title for object
610
+ * @param string $plural Plural form of title
611
+ */
612
+ function set_title($title = '') {
613
+ if ( is_scalar($title) )
614
+ $this->title = strip_tags(trim($title));
615
+ }
616
+
617
+ /**
618
+ * Retrieve object title
619
+ */
620
+ function get_title() {
621
+ return $this->get_member_value('title', '','', 'current');
622
+ }
623
+
624
+ /**
625
+ * Set object description
626
+ * @param string $description Description for object
627
+ */
628
+ function set_description($description = '') {
629
+ $this->description = strip_tags(trim($description));
630
+ }
631
+
632
+ /**
633
+ * Retrieve object description
634
+ * @return string Object description
635
+ */
636
+ function get_description() {
637
+ $dir = 'current';
638
+ return $this->get_member_value('description', '','', $dir);
639
+ return $desc;
640
+ }
641
+
642
+ /**
643
+ * Sets multiple properties on field type at once
644
+ * @param array $properties Properties. Each element is an array containing the arguments to set a new property
645
+ * @return boolean TRUE if successful, FALSE otherwise
646
+ */
647
+ function set_properties($properties) {
648
+ if ( !is_array($properties) ) {
649
+ return false;
650
+ }
651
+ // Normalize properties
652
+ $properties = $this->remap_properties($properties);
653
+ $properties = $this->sort_properties($properties);
654
+ // Set Member properties
655
+ foreach ( $properties as $prop => $val ) {
656
+ if ( ( $m = 'set_' . $prop ) && method_exists($this, $m) ) {
657
+ $this->{$m}($val);
658
+ // Remove member property from array
659
+ unset($properties[$prop]);
660
+ }
661
+ }
662
+
663
+ // Filter properties
664
+ $properties = $this->filter_properties($properties);
665
+ // Set additional instance properties
666
+ foreach ( $properties as $name => $val) {
667
+ $this->set_property($name, $val);
668
+ }
669
+ }
670
+
671
+ /**
672
+ * Remap properties based on $map
673
+ * @uses $map For determine how child properties should map to parent properties
674
+ * @uses SLB_Utlities::array_remap() to perform array remapping
675
+ * @param array $properties Associative array of properties
676
+ * @return array Remapped properties
677
+ */
678
+ function remap_properties($properties) {
679
+ // Return remapped properties
680
+ return $this->util->array_remap($properties, $this->map);
681
+ }
682
+
683
+ /**
684
+ * Sort properties based on priority
685
+ * @uses this::property_priority
686
+ * @return array Sorted priorities
687
+ */
688
+ function sort_properties($properties) {
689
+ // Stop if sorting not necessary
690
+ if ( empty($properties) || !is_array($properties) || empty($this->property_priority) || !is_array($this->property_priority) )
691
+ return $properties;
692
+ $props = array();
693
+ foreach ( $this->property_priority as $prop ) {
694
+ if ( !array_key_exists($prop, $properties) )
695
+ continue;
696
+ // Add to new array
697
+ $props[$prop] = $properties[$prop];
698
+ // Remove from old array
699
+ unset($properties[$prop]);
700
+ }
701
+ // Append any remaining properties
702
+ $props = array_merge($props, $properties);
703
+ return $props;
704
+ }
705
+
706
+ /**
707
+ * Build properties array
708
+ * @param array $props Instance properties
709
+ * @param array $signature (optional) Default properties
710
+ * @return array Normalized properties
711
+ */
712
+ function make_properties($props, $signature = array()) {
713
+ $p = array();
714
+ if ( is_array($props) ) {
715
+ foreach ( $props as $prop ) {
716
+ if ( is_array($prop) ) {
717
+ $p = array_merge($prop, $p);
718
+ }
719
+ }
720
+ }
721
+ $props = $p;
722
+ if ( is_array($signature) ) {
723
+ $props = array_merge($signature, $props);
724
+ }
725
+ return $props;
726
+ }
727
+
728
+ function validate_id($id) {
729
+ return ( is_scalar($id) && !empty($id) ) ? true : false;
730
+ }
731
+
732
+ function integrate_id($id) {
733
+ return ( $this->validate_id($id) ) ? array('id' => $id) : array();
734
+ }
735
+
736
+ /**
737
+ * Filter property members
738
+ * @uses $property_filter to remove define members to remove from $properties
739
+ * @param array $props Properties
740
+ * @return array Filtered properties
741
+ */
742
+ function filter_properties($props = array()) {
743
+ return $this->util->array_filter_keys($props, $this->property_filter);
744
+ }
745
+
746
+ /**
747
+ * Add/Set a property on the field definition
748
+ * @param string $name Name of property
749
+ * @param mixed $value Default value for property
750
+ * @param string|array $group Group(s) property belongs to
751
+ * @return boolean TRUE if property is successfully added to field type, FALSE otherwise
752
+ */
753
+ function set_property($name, $value = '', $group = null) {
754
+ // Do not add if property name is not a string
755
+ if ( !is_string($name) )
756
+ return false;
757
+ // Create property array
758
+ $prop_arr = array();
759
+ $prop_arr['value'] = $value;
760
+ // Add to properties array
761
+ $this->properties[$name] = $value;
762
+ // Add property to specified groups
763
+ if ( !empty($group) ) {
764
+ $this->set_group_property($group, $name);
765
+ }
766
+ return true;
767
+ }
768
+
769
+ /**
770
+ * Retreives property from field type
771
+ * @param string $name Name of property to retrieve
772
+ * @return mixed Specified Property if exists (Default: Empty string)
773
+ */
774
+ function get_property($name) {
775
+ $val = $this->get_member_value('properties', $name);
776
+ return $val;
777
+ }
778
+
779
+ /**
780
+ * Removes a property from item
781
+ * @param string $name Property ID
782
+ */
783
+ function remove_property($name) {
784
+ // Remove property
785
+ if ( isset($this->properties[$name]) )
786
+ unset($this->properties[$name]);
787
+ // Remove from group
788
+ foreach ( array_keys($this->property_groups) as $g ) {
789
+ if ( isset($this->property_groups[$g][$name]) ) {
790
+ unset($this->property_groups[$g][$name]);
791
+ break;
792
+ }
793
+ }
794
+ }
795
+
796
+ /**
797
+ * Adds Specified Property to a Group
798
+ * @param string|array $group Group(s) to add property to
799
+ * @param string $property Property to add to group
800
+ */
801
+ function set_group_property($group, $property) {
802
+ if ( is_string($group) && isset($this->property_groups[$group][$property]) )
803
+ return;
804
+ if ( !is_array($group) ) {
805
+ $group = array($group);
806
+ }
807
+
808
+ foreach ($group as $g) {
809
+ $g = trim($g);
810
+ // Initialize group if it doesn't already exist
811
+ if ( !isset($this->property_groups[$g]) )
812
+ $this->property_groups[$g] = array();
813
+
814
+ // Add property to group
815
+ $this->property_groups[$g][$property] = null;
816
+ }
817
+ }
818
+
819
+ /**
820
+ * Retrieve property group
821
+ * @param string $group Group to retrieve
822
+ * @return array Array of properties in specified group
823
+ */
824
+ function get_group($group) {
825
+ return $this->get_member_value('property_groups', $group, array());
826
+ }
827
+
828
+ /**
829
+ * Save field data
830
+ * Child classes will define their own
831
+ * functionality for this method
832
+ * @return bool TRUE if save was successful (FALSE otherwise)
833
+ */
834
+ function save() {
835
+ return true;
836
+ }
837
+
838
+ /*-** Hooks **-*/
839
+
840
+ /**
841
+ * Retrieve hooks added to object
842
+ * @return array Hooks
843
+ */
844
+ function get_hooks() {
845
+ return $this->get_member_value('hooks', '', array());
846
+ }
847
+
848
+ /**
849
+ * Add hook for object
850
+ * @see add_filter() for parameter defaults
851
+ * @param $tag
852
+ * @param $function_to_add
853
+ * @param $priority
854
+ * @param $accepted_args
855
+ */
856
+ function add_hook($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
857
+ // Create new array for tag (if not already set)
858
+ if ( !isset($this->hooks[$tag]) )
859
+ $this->hooks[$tag] = array();
860
+ // Build Unique ID
861
+ if ( is_string($function_to_add) )
862
+ $id = $function_to_add;
863
+ elseif ( is_array($function_to_add) && !empty($function_to_add) )
864
+ $id = strval($function_to_add[count($function_to_add) - 1]);
865
+ else
866
+ $id = 'function_' . ( count($this->hooks[$tag]) + 1 );
867
+ // Add hook
868
+ $this->hooks[$tag][$id] = func_get_args();
869
+ }
870
+
871
+ /**
872
+ * Convenience method for adding an action for object
873
+ * @see add_filter() for parameter defaults
874
+ * @param $tag
875
+ * @param $function_to_add
876
+ * @param $priority
877
+ * @param $accepted_args
878
+ */
879
+ function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
880
+ $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
881
+ }
882
+
883
+ /**
884
+ * Convenience method for adding a filter for object
885
+ * @see add_filter() for parameter defaults
886
+ * @param $tag
887
+ * @param $function_to_add
888
+ * @param $priority
889
+ * @param $accepted_args
890
+ */
891
+ function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
892
+ $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
893
+ }
894
+
895
+ /*-** Dependencies **-*/
896
+
897
+ /**
898
+ * Adds dependency to object
899
+ * @param string $type Type of dependency to add (script, style)
900
+ * @param array|string $context When dependency will be added (@see SLB_Utilities::get_action() for possible contexts)
901
+ * @see wp_enqueue_script for the following of the parameters
902
+ * @param $handle
903
+ * @param $src
904
+ * @param $deps
905
+ * @param $ver
906
+ * @param $ex
907
+ */
908
+ function add_dependency($type, $context, $handle, $src = false, $deps = array(), $ver = false, $ex = false) {
909
+ $args = func_get_args();
910
+ // Remove type/context from arguments
911
+ $args = array_slice($args, 2);
912
+
913
+ // Set context
914
+ if ( !is_array($context) ) {
915
+ // Wrap single contexts in an array
916
+ if ( is_string($context) )
917
+ $context = array($context);
918
+ else
919
+ $context = array();
920
+ }
921
+ // Add file to instance property
922
+ if ( isset($this->{$type}) && is_array($this->{$type}) )
923
+ $this->{$type}[$handle] = array('context' => $context, 'params' => $args);
924
+ }
925
+
926
+ /**
927
+ * Add script to object to be added in specified contexts
928
+ * @param array|string $context Array of contexts to add script to page
929
+ * @see wp_enqueue_script for the following of the parameters
930
+ * @param $handle
931
+ * @param $src
932
+ * @param $deps
933
+ * @param $ver
934
+ * @param $in_footer
935
+ */
936
+ function add_script( $context, $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
937
+ $args = func_get_args();
938
+ // Add file type to front of arguments array
939
+ array_unshift($args, 'scripts');
940
+ call_user_func_array($this->m('add_dependency'), $args);
941
+ }
942
+
943
+ /**
944
+ * Retrieve script dependencies for object
945
+ * @return array Script dependencies
946
+ */
947
+ function get_scripts() {
948
+ return $this->get_member_value('scripts', '', array());
949
+ }
950
+
951
+ /**
952
+ * Add style to object to be added in specified contexts
953
+ * @param array|string $context Array of contexts to add style to page
954
+ * @see wp_enqueue_style for the following of the parameters
955
+ * @param $handle
956
+ * @param $src
957
+ * @param $deps
958
+ * @param $ver
959
+ * @param $in_footer
960
+ */
961
+ function add_style( $handle, $src = false, $deps = array(), $ver = false, $media = false ) {
962
+ $args = func_get_args();
963
+ array_unshift($args, 'styles');
964
+ call_user_func_array($this->m('add_dependency'), $args);
965
+ }
966
+
967
+ /**
968
+ * Retrieve Style dependencies for object
969
+ * @return array Style dependencies
970
+ */
971
+ function get_styles() {
972
+ return $this->get_member_value('styles', '', array());
973
+ }
974
+
975
+ /* Helpers */
976
+
977
+ /**
978
+ * Format value based on specified context
979
+ * @param mixed $value Value to format
980
+ * @param string $context Current context
981
+ * @return mixed Formatted value
982
+ */
983
+ function format($value, $context = '') {
984
+ if ( is_scalar($context) && !empty($context) ) {
985
+ $handler = 'format_' . trim(strval($context));
986
+ // Only process if context is valid and has a handler
987
+ if ( !empty($context) && method_exists($this, $handler) ) {
988
+ // Pass value to handler
989
+ $value = $this->{$handler}($value, $context);
990
+ }
991
+ }
992
+ // Return formatted value
993
+ return $value;
994
+ }
995
+
996
+ /**
997
+ * Format value for output in form field
998
+ * @param mixed $value Value to format
999
+ * @return mixed Formatted value
1000
+ */
1001
+ function format_form($value) {
1002
+ if ( is_string($value) )
1003
+ $value = htmlspecialchars($value);
1004
+ return $value;
1005
+ }
1006
+
1007
+ /**
1008
+ * Final formatting before output
1009
+ * Restores special characters, etc.
1010
+ * @uses $special_chars
1011
+ * @uses $special_chars_default
1012
+ * @param mixed $value Pre-final field output
1013
+ * @param string $context (Optional) Formatting context
1014
+ * @return mixed Formatted value
1015
+ */
1016
+ function format_final($value, $context = '') {
1017
+ if ( !is_string($value) )
1018
+ return $value;
1019
+
1020
+ // Restore special chars
1021
+ return $this->restore_special_chars($value, $context);
1022
+ }
1023
+
1024
+ function preserve_special_chars($value, $context = '') {
1025
+ if ( !is_string($value) )
1026
+ return $value;
1027
+ $specials = $this->get_special_chars();
1028
+ return str_replace(array_keys($specials), $specials, $value);
1029
+ }
1030
+
1031
+ function restore_special_chars($value, $context = '') {
1032
+ if ( !is_string($value) )
1033
+ return $value;
1034
+ $specials = $this->get_special_chars();
1035
+ return str_replace($specials, array_keys($specials), $value);
1036
+ }
1037
+
1038
+ /**
1039
+ * Retrieve special characters/placeholders
1040
+ * Merges defaults with class-specific characters
1041
+ * @uses $special_chars
1042
+ * @uses $special_chars_default
1043
+ * @return array Special characters/placeholders
1044
+ */
1045
+ function get_special_chars() {
1046
+ return wp_parse_args($this->special_chars, $this->special_chars_default);
1047
+ }
1048
+ }
includes/class.field_collection.php ADDED
@@ -0,0 +1,764 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Managed collection of fields
4
+ * @package Simple Lightbox
5
+ * @subpackage Fields
6
+ * @author Archetyped
7
+ */
8
+ class SLB_Field_Collection extends SLB_Field_Base {
9
+
10
+ /* Configuration */
11
+
12
+ protected $mode = 'sub';
13
+
14
+ /* Properties */
15
+
16
+ /**
17
+ * Item type
18
+ * @var string
19
+ */
20
+ var $item_type = 'SLB_Field';
21
+
22
+ /**
23
+ * Indexed array of items in collection
24
+ * @var array
25
+ */
26
+ var $items = array();
27
+
28
+ var $id_formats = array (
29
+ 'formatted' => array(
30
+ 'wrap' => array ( 'open' => '_' ),
31
+ 'recursive' => false,
32
+ 'prefix' => array('get_prefix')
33
+ )
34
+ );
35
+
36
+ var $build_vars_default = array (
37
+ 'groups' => array(),
38
+ 'context' => '',
39
+ 'layout' => 'form',
40
+ 'build' => true,
41
+ 'build_groups' => true,
42
+ );
43
+
44
+ /**
45
+ * Associative array of groups in collection
46
+ * Key: Group ID
47
+ * Value: object of group properties
48
+ * > id
49
+ * > title
50
+ * > description string Group description
51
+ * > items array Items in group
52
+ * @var array
53
+ */
54
+ var $groups = array();
55
+
56
+ protected $properties_init = null;
57
+
58
+ /* Constructors */
59
+
60
+ /**
61
+ * Class constructor
62
+ * @uses parent::__construct()
63
+ * @uses self::make_properties()
64
+ * @uses self::init()
65
+ * @uses self::add_groups()
66
+ * @uses self::add_items()
67
+ * @param string $id Collection ID
68
+ * @param array $properties (optional) Properties to set for collection (Default: none)
69
+ */
70
+ public function __construct($id, $properties = null) {
71
+ $args = func_get_args();
72
+ $properties = $this->make_properties($args);
73
+ // Parent constructor
74
+ parent::__construct($properties);
75
+
76
+ // Save initial properties
77
+ $this->properties_init = $properties;
78
+ }
79
+
80
+ public function _init() {
81
+ parent::_init();
82
+ $this->load($this->properties_init, false);
83
+ }
84
+
85
+ /*-** Getters/Setters **-*/
86
+
87
+ /* Setup */
88
+
89
+ /**
90
+ * Load collection with specified properties
91
+ * Updates existing properties
92
+ * @param array $properties Properties to load
93
+ * @param bool $update (optional) Update (TRUE) or overwrite (FALSE) items/groups (Default: TRUE)
94
+ * @return object Current instance
95
+ */
96
+ public function load($properties, $update = true) {
97
+ $args = func_get_args();
98
+ $properties = $this->make_properties($args);
99
+ if ( !empty($properties) ) {
100
+ // Groups
101
+ if ( isset($properties['groups']) ) {
102
+ $this->add_groups($properties['groups'], $update);
103
+ }
104
+ // Items
105
+ if ( isset($properties['items']) ) {
106
+ $this->add_items($properties['items'], $update);
107
+ }
108
+ }
109
+ return $this;
110
+ }
111
+
112
+ /* Data */
113
+
114
+ /**
115
+ * Retrieve external data for items in collection
116
+ * Retrieved data is saved to the collection's $data property
117
+ * Uses class properties to determine how data is retrieved
118
+ * Examples:
119
+ * > DB
120
+ * > XML
121
+ * > JSON
122
+ * @return void
123
+ */
124
+ function load_data() {
125
+ $this->data_loaded = true;
126
+ }
127
+
128
+ /**
129
+ * Set data for an item
130
+ * @param mixed $item Field to set data for
131
+ * > string Field ID
132
+ * > object Field Reference
133
+ * > array Data for multiple items (associative array [field ID => data])
134
+ * @param mixed $value Data to set
135
+ * @param bool $save (optional) Whether or not data should be saved to DB (Default: Yes)
136
+ */
137
+ function set_data($item, $value = '', $save = true, $force_set = false) {
138
+ // Set data for entire collection
139
+ if ( is_array($item) ) {
140
+ $this->data = wp_parse_args($item, $this->data);
141
+ // Update save option
142
+ $args = func_get_args();
143
+ if ( 2 == count($args) && is_bool($args[1]) ) {
144
+ $save = $args[1];
145
+ }
146
+ }
147
+ // Get $item's ID
148
+ elseif ( is_object($item) && method_exists($item, 'get_id') )
149
+ $item = $item->get_id();
150
+ // Set data
151
+ if ( is_string($item) && !empty($item) && ( isset($this->items[$item]) || !!$force_set ) )
152
+ $this->data[$item] = $value;
153
+ if ( !!$save )
154
+ $this->save();
155
+ }
156
+
157
+ /* Item */
158
+
159
+ /**
160
+ * Adds item to collection
161
+ * @param string|obj $id Unique name for item or item instance
162
+ * @param array $properties (optional) Item properties
163
+ * @param bool $update (optional) Update or overwrite existing item (Default: FALSE)
164
+ * @return object Newly-added item
165
+ */
166
+ function add($id, $properties = array(), $update = false) {
167
+ $item;
168
+ $args = func_get_args();
169
+ // Properties
170
+ foreach ( array_reverse($args) as $arg ) {
171
+ if ( is_array($arg) ) {
172
+ $properties = $arg;
173
+ break;
174
+ }
175
+ }
176
+ if ( !is_array($properties) ) {
177
+ $properties = array();
178
+ }
179
+
180
+ // Handle item instance
181
+ if ( $id instanceof $this->item_type ) {
182
+ $item = $id;
183
+ $item->set_properties($properties);
184
+ } elseif ( class_exists($this->item_type) ) {
185
+ $defaults = array (
186
+ 'parent' => null,
187
+ 'group' => null
188
+ );
189
+ $properties = array_merge($defaults, $properties);
190
+ if ( is_string($id) ) {
191
+ $properties['id'] = $id;
192
+ }
193
+ if ( !!$update && $this->has($properties['id']) ) {
194
+ // Update existing item
195
+ $item = $this->get($properties['id']);
196
+ $item->set_properties($properties);
197
+ } else {
198
+ // Init item
199
+ $type = $this->item_type;
200
+ $item = new $type($properties);
201
+ }
202
+ }
203
+
204
+ if ( empty($item) || 0 == strlen($item->get_id()) ) {
205
+ return false;
206
+ }
207
+
208
+ // Set container
209
+ $item->set_container($this);
210
+
211
+ // Add item to collection
212
+ $this->items[$item->get_id()] = $item;
213
+
214
+ if ( isset($properties['group']) ) {
215
+ $this->add_to_group($properties['group'], $item->get_id());
216
+ }
217
+
218
+ return $item;
219
+ }
220
+
221
+ /**
222
+ * Removes item from collection
223
+ * @param string|object $item Object or item ID to remove
224
+ * @param bool $save (optional) Whether to save the collection after removing item (Default: YES)
225
+ */
226
+ function remove($item, $save = true) {
227
+ // Remove item
228
+ if ( $this->has($item) ) {
229
+ $item = $this->get($item);
230
+ $item = $item->get_id();
231
+ // Remove from items array
232
+ unset($this->items[$item]);
233
+ // Remove item from groups
234
+ $this->remove_from_group($item);
235
+ }
236
+ // Remove item data from collection
237
+ $this->remove_data($item, false);
238
+
239
+ if ( !!$save )
240
+ $this->save();
241
+ }
242
+
243
+ /**
244
+ * Remove item data from collection
245
+ * @param string|object $item Object or item ID to remove
246
+ * @param bool $save (optional) Whether to save the collection after removing item (Default: YES)
247
+ */
248
+ function remove_data($item, $save = true) {
249
+ // Get item ID from object
250
+ if ( $this->has($item) ) {
251
+ $item = $this->get($item);
252
+ $item = $item->get_id();
253
+ }
254
+
255
+ // Remove data from data member
256
+ if ( is_string($item) && is_array($this->data) ) {
257
+ unset($this->data[$item]);
258
+ if ( !!$save )
259
+ $this->save();
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Checks if item exists in the collection
265
+ * @param string $item Item ID
266
+ * @return bool TRUE if item exists, FALSE otherwise
267
+ */
268
+ function has($item) {
269
+ return ( !is_string($item) || empty($item) || is_null($this->get_member_value('items', $item, null)) ) ? false : true;
270
+ }
271
+
272
+ /**
273
+ * Retrieve specified item in collection
274
+ * @param string|object $item Item object or ID to retrieve
275
+ * @return SLB_Field Specified item
276
+ */
277
+ function get($item, $safe_mode = false) {
278
+ if ( $this->has($item) ) {
279
+ if ( !is_object($item) || !($item instanceof $this->item_type) ) {
280
+ if ( is_string($item) ) {
281
+ $item = trim($item);
282
+ $item =& $this->items[$item];
283
+ }
284
+ else {
285
+ $item = false;
286
+ }
287
+ }
288
+ } else {
289
+ $item = false;
290
+ }
291
+
292
+ if ( !!$safe_mode && !is_object($item) ) {
293
+ // Fallback: Return empty item if no item exists
294
+ $type = $this->item_type;
295
+ $item = new $type('');
296
+ }
297
+ return $item;
298
+ }
299
+
300
+ /**
301
+ * Retrieve item data
302
+ * @param $item Item to get data for
303
+ * @param $context (optional) Context
304
+ * @param $top (optional) Iterate through ancestors to get data (Default: Yes)
305
+ * @return mixed Item data
306
+ */
307
+ function get_data($item = null, $context = '', $top = true) {
308
+ $this->load_data();
309
+ $ret = null;
310
+ if ( $this->has($item) ) {
311
+ $item = $this->get($item);
312
+ $ret = $item->get_data($context, $top);
313
+ } else {
314
+ $ret = parent::get_data($context, $top);
315
+ }
316
+
317
+ if ( is_string($item) && is_array($ret) && isset($ret[$item]) )
318
+ $ret = $ret[$item];
319
+ return $ret;
320
+ }
321
+
322
+ /* Items (Collection) */
323
+
324
+ /**
325
+ * Add multiple items to collection
326
+ * @param array $items Items to add to collection
327
+ * Array Structure:
328
+ * > Key (string): Item ID
329
+ * > Val (array): Item properties
330
+ * @return void
331
+ */
332
+ function add_items($items = array(), $update = false) {
333
+ // Validate
334
+ if ( !is_array($items) || empty($items) ) {
335
+ return false;
336
+ }
337
+ // Add items
338
+ foreach ( $items as $id => $props ) {
339
+ $this->add($id, $props, $update);
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Retrieve reference to items in collection
345
+ * @return array Collection items (reference)
346
+ */
347
+ function &get_items($group = null, $sort = 'priority') {
348
+ $gset = $this->group_exists($group);
349
+ if ( $gset ) {
350
+ $items = $this->get_group_items($group);
351
+ } elseif ( !empty($group) ) {
352
+ $items = array();
353
+ } else {
354
+ $items = $this->items;
355
+ }
356
+ if ( !empty($items) ) {
357
+ // Sort items
358
+ if ( !empty($sort) && is_string($sort) ) {
359
+ if ( 'priority' == $sort ) {
360
+ if ( $gset ) {
361
+ // Sort by priority
362
+ ksort($items, SORT_NUMERIC);
363
+ }
364
+ }
365
+ }
366
+ // Release from buckets
367
+ if ( $gset ) {
368
+ $items = call_user_func_array('array_merge', $items);
369
+ }
370
+ }
371
+ return $items;
372
+ }
373
+
374
+ /**
375
+ * Build output for items in specified group
376
+ * If no group specified, all items in collection are built
377
+ * @param string|object $group (optional) Group to build items for (ID or instance object)
378
+ */
379
+ function build_items($group = null) {
380
+ // Get group items
381
+ $items =& $this->get_items($group);
382
+ if ( empty($items) ) {
383
+ return false;
384
+ }
385
+
386
+ $this->util->do_action_ref_array('build_items_pre', array($this));
387
+ foreach ( $items as $item ) {
388
+ $item->build();
389
+ }
390
+ $this->util->do_action_ref_array('build_items_post', array($this));
391
+ }
392
+
393
+ /* Group */
394
+
395
+ /**
396
+ * Add groups to collection
397
+ * @param array $groups Associative array of group properties
398
+ * Array structure:
399
+ * > Key (string): group ID
400
+ * > Val (string): Group Title
401
+ */
402
+ function add_groups($groups = array(), $update = false) {
403
+ // Validate
404
+ if ( !is_array($groups) || empty($groups) ) {
405
+ return false;
406
+ }
407
+ // Iterate
408
+ foreach ( $groups as $id => $props ) {
409
+ $this->add_group($id, $props, null, $update);
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Adds group to collection
415
+ * Groups are used to display related items in the UI
416
+ * @param string $id Unique name for group
417
+ * @param string $title Group title
418
+ * @param string $description Short description of group's purpose
419
+ * @param array $items (optional) ID's of existing items to add to group
420
+ * @return object Group object
421
+ */
422
+ function &add_group($id, $properties = array(), $items = array(), $update = false) {
423
+ // Create new group and set properties
424
+ $default = array (
425
+ 'title' => '',
426
+ 'description' => '',
427
+ 'priority' => 10
428
+ );
429
+ $p = ( is_array($properties) ) ? array_merge($default, $properties) : $default;
430
+ if ( !is_int($p['priority']) || $p['priority'] < 0 ) {
431
+ $p['priority'] = $default['priority'];
432
+ }
433
+ $id = trim($id);
434
+ // Retrieve or init group
435
+ if ( !!$update && $this->group_exists($id) ) {
436
+ $grp = $this->get_group($id);
437
+ $grp->title = $p['title'];
438
+ $grp->description = $p['description'];
439
+ $grp->priority = $p['priority'];
440
+ } else {
441
+ $this->groups[$id] =& $this->create_group($id, $p['title'], $p['description'], $p['priority']);
442
+ }
443
+ // Add items to group (if supplied)
444
+ if ( !empty($items) && is_array($items) ) {
445
+ $this->add_to_group($id, $items);
446
+ }
447
+ return $this->groups[$id];
448
+ }
449
+
450
+ /**
451
+ * Remove specified group from collection
452
+ * @param string $id Group ID to remove
453
+ */
454
+ function remove_group($id) {
455
+ $id = trim($id);
456
+ if ( $this->group_exists($id) ) {
457
+ unset($this->groups[$id]);
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Standardized method to create a new item group
463
+ * @param string $title Group title (used in meta boxes, etc.)
464
+ * @param string $description Short description of group's purpose
465
+ * @param int $priority (optional) Group priority (e.g. used to sort groups during output)
466
+ * @return object Group object
467
+ */
468
+ function &create_group($id = '', $title = '', $description = '', $priority = 10) {
469
+ // Create new group object
470
+ $group = new stdClass();
471
+ /* Set group properties */
472
+ // Set ID
473
+ $id = ( is_scalar($id) ) ? trim($id) : '';
474
+ $group->id = $id;
475
+ // Set Title
476
+ $title = ( is_scalar($title) ) ? trim($title) : '';
477
+ $group->title = $title;
478
+ // Set Description
479
+ $description = ( is_scalar($description) ) ? trim($description) : '';
480
+ $group->description = $description;
481
+ // Priority
482
+ $group->priority = ( is_int($priority) ) ? $priority : 10;
483
+ // Create array to hold items
484
+ $group->items = array();
485
+ return $group;
486
+ }
487
+
488
+ /**
489
+ * Checks if group exists in collection
490
+ * @param string $id Group name
491
+ * @return bool TRUE if group exists, FALSE otherwise
492
+ */
493
+ function group_exists($group) {
494
+ $ret = false;
495
+ if ( is_object($group) ) {
496
+ $ret = true;
497
+ } elseif ( is_string($group) && ($group = trim($group)) && strlen($group) > 0 ) {
498
+ $group = trim($group);
499
+ // Check if group exists
500
+ $ret = !is_null($this->get_member_value('groups', $group, null));
501
+ }
502
+ return $ret;
503
+ }
504
+
505
+ /**
506
+ * Adds item to a group in the collection
507
+ * Group is created if it does not already exist
508
+ * @param string|array $group ID of group (or group parameters if new group) to add item to
509
+ * @param string|array $items Name or array of item(s) to add to group
510
+ */
511
+ function add_to_group($group, $items, $priority = 10) {
512
+ // Validate
513
+ if ( empty($items) || empty($group) || ( !is_string($group) && !is_array($group) ) ) {
514
+ return false;
515
+ }
516
+
517
+ // Get group ID
518
+ if ( is_string($group) ) {
519
+ $group = array($group, $priority);
520
+ }
521
+ list($gid, $priority) = $group;
522
+ $gid = trim(sanitize_title_with_dashes($gid));
523
+ if ( empty($gid) ) {
524
+ return false;
525
+ }
526
+ // Item priority
527
+ if ( !is_int($priority) ) {
528
+ $priority = 10;
529
+ }
530
+
531
+ // Prepare group
532
+ if ( !$this->group_exists($gid) ) {
533
+ // TODO Follow
534
+ call_user_func($this->m('add_group'), $gid, $group);
535
+ }
536
+ // Prepare items
537
+ if ( !is_array($items) ) {
538
+ $items = array($items);
539
+ }
540
+ // Add Items
541
+ foreach ( $items as $item ) {
542
+ // Skip if not in current collection
543
+ $itm_ref = $this->get($item);
544
+ if ( !$itm_ref ) {
545
+ continue;
546
+ }
547
+ $itm_id = $itm_ref->get_id();
548
+ // Remove item from any other group it's in (items can only be in one group)
549
+ foreach ( $this->get_groups() as $group ) {
550
+ foreach ( $group->items as $tmp_pri => $tmp_items ) {
551
+ if ( isset($group->items[$tmp_pri][$itm_id]) ) {
552
+ unset($group->items[$tmp_pri][$itm_id]);
553
+ }
554
+ }
555
+ }
556
+ // Add reference to item in group
557
+ $items =& $this->get_group($gid)->items;
558
+ if ( !isset($items[$priority]) ) {
559
+ $items[$priority] = array();
560
+ }
561
+ $items[$priority][$itm_id] = $itm_ref;
562
+ }
563
+ unset($itm_ref);
564
+ }
565
+
566
+ /**
567
+ * Remove item from a group
568
+ * If no group is specified, then item is removed from all groups
569
+ * @param string|object $item Object or ID of item to remove from group
570
+ * @param string $group (optional) Group ID to remove item from
571
+ */
572
+ function remove_from_group($item, $group = '') {
573
+ // Get ID of item to remove or stop execution if item invalid
574
+ $item = $this->get($item);
575
+ $item = $item->get_id();
576
+ if ( !$item )
577
+ return false;
578
+
579
+ // Remove item from group
580
+ if ( !empty($group) ) {
581
+ // Remove item from single group
582
+ if ( ($group =& $this->get_group($group)) && isset($group->items[$item]) ) {
583
+ unset($group->items[$item]);
584
+ }
585
+ } else {
586
+ // Remove item from all groups
587
+ foreach ( array_keys($this->groups) as $group ) {
588
+ if ( ($group =& $this->get_group($group)) && isset($group->items[$item]) ) {
589
+ unset($group->items[$item]);
590
+ }
591
+ }
592
+ }
593
+ }
594
+
595
+ /**
596
+ * Retrieve specified group
597
+ * @param string $group ID of group to retrieve
598
+ * @return object Reference to specified group
599
+ */
600
+ function &get_group($group) {
601
+ if ( is_object($group) ) {
602
+ return $group;
603
+ }
604
+ if ( is_string($group) ) {
605
+ $group = trim($group);
606
+ }
607
+ // Create group if it doesn't already exist
608
+ if ( ! $this->group_exists($group) ) {
609
+ $this->add_group($group);
610
+ }
611
+ return $this->get_member_value('groups', $group);
612
+ }
613
+
614
+ /**
615
+ * Retrieve a group's items
616
+ * @uses SLB_Field_Collection::get_group() to retrieve group object
617
+ * @param object|string $group Group object or group ID
618
+ * @return array Group's items
619
+ */
620
+ function &get_group_items($group) {
621
+ $group =& $this->get_group($group);
622
+ return $group->items;
623
+ }
624
+
625
+ /**
626
+ * Retrieve all groups in collection
627
+ * @return array Reference to group objects
628
+ */
629
+ function &get_groups($opts = array()) {
630
+ $groups =& $this->get_member_value('groups');
631
+ if ( is_array($opts) && !empty($opts) ) {
632
+ extract($opts, EXTR_SKIP);
633
+ if ( !empty($groups) && !empty($sort) && is_string($sort) ) {
634
+ if ( property_exists(current($groups), $sort) ) {
635
+ // Sort groups by property
636
+ $sfunc = function ( $a, $b ) use ($sort) {
637
+ $ap = $a->$sort;
638
+ $bp = $b->$sort;
639
+ if ( $ap == $bp ) {
640
+ return 0;
641
+ }
642
+ return ( $ap > $bp ) ? 1 : -1;
643
+ };
644
+ uasort($groups, $sfunc);
645
+ }
646
+ }
647
+ }
648
+ return $groups;
649
+ }
650
+
651
+ /**
652
+ * Output groups
653
+ * @uses self::build_vars to determine groups to build
654
+ */
655
+ function build_groups() {
656
+ $this->util->do_action_ref_array('build_groups_pre', array($this));
657
+
658
+ // Get groups to build
659
+ $groups = ( !empty($this->build_vars['groups']) ) ? $this->build_vars['groups'] : array_keys($this->get_groups(array('sort' => 'priority')));
660
+ // Check options
661
+ if ( is_callable($this->build_vars['build_groups']) ) {
662
+ // Pass groups to callback to build output
663
+ call_user_func_array($this->build_vars['build_groups'], array($this, $groups));
664
+ } elseif ( !!$this->build_vars['build_groups'] ) {
665
+ // Build groups
666
+ foreach ( $groups as $group ) {
667
+ $this->build_group($group);
668
+ }
669
+ }
670
+
671
+ $this->util->do_action_ref_array('build_groups_post', array($this));
672
+ }
673
+
674
+ /**
675
+ * Build group
676
+ */
677
+ function build_group($group) {
678
+ if ( !$this->group_exists($group) ) {
679
+ return false;
680
+ }
681
+ $group =& $this->get_group($group);
682
+ // Stop processing if group contains no items
683
+ if ( !count($this->get_items($group)) ) {
684
+ return false;
685
+ }
686
+
687
+ // Pre action
688
+ $this->util->do_action_ref_array('build_group_pre', array($this, $group));
689
+
690
+ // Build items
691
+ $this->build_items($group);
692
+
693
+ // Post action
694
+ $this->util->do_action_ref_array('build_group_post', array($this, $group));
695
+ }
696
+
697
+ /* Collection */
698
+
699
+ /**
700
+ * Build entire collection of items
701
+ * Prints output
702
+ */
703
+ function build($build_vars = array()) {
704
+ // Parse vars
705
+ $this->parse_build_vars($build_vars);
706
+ $this->util->do_action_ref_array('build_init', array($this));
707
+ // Pre-build output
708
+ $this->util->do_action_ref_array('build_pre', array($this));
709
+ // Build groups
710
+ $this->build_groups();
711
+ // Post-build output
712
+ $this->util->do_action_ref_array('build_post', array($this));
713
+ }
714
+
715
+ /**
716
+ * Set build variable
717
+ * @param string $key Variable name
718
+ * @param mixed $val Variable value
719
+ */
720
+ function set_build_var($key, $val) {
721
+ $this->build_vars[$key] = $val;
722
+ }
723
+
724
+ /**
725
+ * Retrieve build variable
726
+ * @param string $key Variable name
727
+ * @param mixed $default Value if variable is not set
728
+ * @return mixed Variable value
729
+ */
730
+ function get_build_var($key, $default = null) {
731
+ return ( array_key_exists($key, $this->build_vars) ) ? $this->build_vars[$key] : $default;
732
+ }
733
+
734
+ /**
735
+ * Delete build variable
736
+ * @param string $key Variable name to delete
737
+ */
738
+ function delete_build_var($key) {
739
+ if ( array_key_exists($key, $this->build_vars) ) {
740
+ unset($this->build_vars[$key]);
741
+ }
742
+ }
743
+
744
+ /**
745
+ * Parses build variables prior to use
746
+ * @uses this->reset_build_vars() to reset build variables for each request
747
+ * @param array $build_vars Variables to use for current request
748
+ */
749
+ function parse_build_vars($build_vars = array()) {
750
+ $this->reset_build_vars();
751
+ $this->build_vars = $this->util->apply_filters('parse_build_vars', wp_parse_args($build_vars, $this->build_vars), $this);
752
+ }
753
+
754
+ /**
755
+ * Reset build variables to defaults
756
+ * Default Variables
757
+ * > groups - array - Names of groups to build
758
+ * > context - string - Context of current request
759
+ * > layout - string - Name of default layout to use
760
+ */
761
+ function reset_build_vars() {
762
+ $this->build_vars = wp_parse_args($this->build_vars, $this->build_vars_default);
763
+ }
764
+ }
includes/class.field_type.php ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Field Types
4
+ * Stores properties for a specific field
5
+ * @package Simple Lightbox
6
+ * @subpackage Fields
7
+ * @author Archetyped
8
+ */
9
+ class SLB_Field_Type extends SLB_Field_Base {
10
+ /* Properties */
11
+
12
+ /**
13
+ * @var array Array of Field types that make up current Field type
14
+ */
15
+ var $elements = array();
16
+
17
+ /**
18
+ * @var array Field type layouts
19
+ */
20
+ var $layout = array();
21
+
22
+ /**
23
+ * @var SLB_Field_Type Parent field type (reference)
24
+ */
25
+ var $parent = null;
26
+
27
+ /**
28
+ * Object that field is in
29
+ * @var SLB_Field|SLB_Field_Type|SLB_Field_Collection
30
+ */
31
+ var $container = null;
32
+
33
+ /**
34
+ * Object that called field
35
+ * Used to determine field hierarchy/nesting
36
+ * @var SLB_Field|SLB_Field_Type|SLB_Field_Collection
37
+ */
38
+ var $caller = null;
39
+
40
+ function __construct($id = '', $parent = null) {
41
+ $args = func_get_args();
42
+ $defaults = $this->integrate_id($id);
43
+ if ( !is_array($parent) )
44
+ $defaults['parent'] = $parent;
45
+
46
+ $props = $this->make_properties($args, $defaults);
47
+ parent::__construct($props);
48
+ }
49
+
50
+ /* Getters/Setters */
51
+
52
+ /**
53
+ * Search for specified member value in field's container object (if exists)
54
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
55
+ * @param string $name Value to retrieve from member
56
+ * @return mixed Member value if found (Default: empty string)
57
+ */
58
+ function get_container_value($member, $name = '', $default = '') {
59
+ $container =& $this->get_container();
60
+ return $this->get_object_value($container, $member, $name, $default, 'container');
61
+ }
62
+
63
+ /**
64
+ * Search for specified member value in field's container object (if exists)
65
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
66
+ * @param string $name Value to retrieve from member
67
+ * @return mixed Member value if found (Default: empty string)
68
+ */
69
+ function get_caller_value($member, $name = '', $default = '') {
70
+ $caller =& $this->get_caller();
71
+ return $this->get_object_value($caller, $member, $name, $default, 'caller');
72
+ }
73
+
74
+ /**
75
+ * Sets reference to container object of current field
76
+ * Reference is cleared if no valid object is passed to method
77
+ * @param object $container
78
+ */
79
+ function set_container(&$container) {
80
+ if ( !empty($container) && is_object($container) ) {
81
+ // Set as param as container for current field
82
+ $this->container =& $container;
83
+ } else {
84
+ // Clear container member if argument is invalid
85
+ $this->clear_container();
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Clears reference to container object of current field
91
+ */
92
+ function clear_container() {
93
+ $this->container = null;
94
+ }
95
+
96
+ /**
97
+ * Retrieves reference to container object of current field
98
+ * @return object Reference to container object
99
+ */
100
+ function &get_container() {
101
+ $ret = null;
102
+ if ( $this->has_container() )
103
+ $ret =& $this->container;
104
+ return $ret;
105
+ }
106
+
107
+ /**
108
+ * Checks if field has a container reference
109
+ * @return bool TRUE if field is contained, FALSE otherwise
110
+ */
111
+ function has_container() {
112
+ return !empty($this->container);
113
+ }
114
+
115
+ /**
116
+ * Sets reference to calling object of current field
117
+ * Any existing reference is cleared if no valid object is passed to method
118
+ * @param object $caller Calling object
119
+ */
120
+ function set_caller(&$caller) {
121
+ if ( !empty($caller) && is_object($caller) )
122
+ $this->caller =& $caller;
123
+ else
124
+ $this->clear_caller();
125
+ }
126
+
127
+ /**
128
+ * Clears reference to calling object of current field
129
+ */
130
+ function clear_caller() {
131
+ unset($this->caller);
132
+ }
133
+
134
+ /**
135
+ * Retrieves reference to caller object of current field
136
+ * @return object Reference to caller object
137
+ */
138
+ function &get_caller() {
139
+ $ret = null;
140
+ if ( $this->has_caller() )
141
+ $ret =& $this->caller;
142
+ return $ret;
143
+ }
144
+
145
+ /**
146
+ * Checks if field has a caller reference
147
+ * @return bool TRUE if field is called by another field, FALSE otherwise
148
+ */
149
+ function has_caller() {
150
+ return !empty($this->caller);
151
+ }
152
+
153
+
154
+
155
+ /**
156
+ * Sets an element for the field type
157
+ * @param string $name Name of element
158
+ * @param SLB_Field_Type $type Reference of field type to use for element
159
+ * @param array $properties Properties for element (passed as keyed associative array)
160
+ * @param string $id_prop Name of property to set $name to (e.g. ID, etc.)
161
+ */
162
+ function set_element($name, $type, $properties = array(), $id_prop = 'id') {
163
+ $name = trim(strval($name));
164
+ if ( empty($name) )
165
+ return false;
166
+ // Create new field for element
167
+ $el = new SLB_Field($name, $type);
168
+ // Set container to current field instance
169
+ $el->set_container($this);
170
+ // Add properties to element
171
+ $el->set_properties($properties);
172
+ // Save element to current instance
173
+ $this->elements[$name] =& $el;
174
+ }
175
+
176
+ /**
177
+ * Add a layout to the field
178
+ * @param string $name Name of layout
179
+ * @param string $value Layout text
180
+ */
181
+ function set_layout($name, $value = '') {
182
+ if ( !is_string($name) )
183
+ return false;
184
+ $name = trim($name);
185
+ $this->layout[$name] = $value;
186
+ return true;
187
+ }
188
+
189
+ /**
190
+ * Retrieve specified layout
191
+ * @param string $name Layout name
192
+ * @param bool $parse_nested (optional) Whether nested layouts should be expanded in retreived layout or not (Default: TRUE)
193
+ * @return string Specified layout text
194
+ */
195
+ function get_layout($name = 'form', $parse_nested = true) {
196
+ // Retrieve specified layout (use $name value if no layout by that name exists)
197
+ if ( empty($name) )
198
+ $name = $this->get_container_value('build_vars', 'layout', 'form');
199
+ $layout = $this->get_member_value('layout', $name, $name);
200
+
201
+ // Find all nested layouts in current layout
202
+ if ( !empty($layout) && !!$parse_nested ) {
203
+ $ph = $this->get_placeholder_defaults();
204
+
205
+ while ($ph->match = $this->parse_layout($layout, $ph->pattern_layout)) {
206
+ // Iterate through the different types of layout placeholders
207
+ foreach ($ph->match as $tag => $instances) {
208
+ // Iterate through instances of a specific type of layout placeholder
209
+ foreach ($instances as $instance) {
210
+ // Get nested layout
211
+ $nested_layout = $this->get_member_value($instance);
212
+
213
+ // Replace layout placeholder with retrieved item data
214
+ if ( !empty($nested_layout) )
215
+ $layout = str_replace($ph->start . $instance['match'] . $ph->end, $nested_layout, $layout);
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ return $layout;
222
+ }
223
+
224
+ /**
225
+ * Checks if specified layout exists
226
+ * Finds layout if it exists in current object or any of its parents
227
+ * @param string $layout Name of layout to check for
228
+ * @return bool TRUE if layout exists, FALSE otherwise
229
+ */
230
+ function has_layout($layout) {
231
+ $ret = false;
232
+ if ( is_string($layout) && ($layout = trim($layout)) && !empty($layout) ) {
233
+ $layout = $this->get_member_value('layout', $layout, false);
234
+ if ( $layout !== false )
235
+ $ret = true;
236
+ }
237
+
238
+ return $ret;
239
+ }
240
+
241
+ /**
242
+ * Checks if layout content is valid
243
+ * Layouts need to have placeholders to be valid
244
+ * @param string $layout_content Layout content (markup)
245
+ * @return bool TRUE if layout is valid, FALSE otherwise
246
+ */
247
+ function is_valid_layout($layout_content) {
248
+ $ph = $this->get_placeholder_defaults();
249
+ return preg_match($ph->pattern_general, $layout_content);
250
+ }
251
+
252
+ /**
253
+ * Parse field layout with a regular expression
254
+ * @param string $layout Layout data
255
+ * @param string $search Regular expression pattern to search layout for
256
+ * @return array Associative array containing all of the regular expression matches in the layout data
257
+ * Array Structure:
258
+ * root => placeholder tags
259
+ * => Tag instances (array)
260
+ * 'tag' => (string) tag name
261
+ * 'match' => (string) placeholder match
262
+ * 'attributes' => (array) attributes
263
+ */
264
+ function parse_layout($layout, $search) {
265
+ $ph_xml = '';
266
+ $parse_match = '';
267
+ $ph_root_tag = 'ph_root_element';
268
+ $ph_start_xml = '<';
269
+ $ph_end_xml = ' />';
270
+ $ph_wrap_start = '<' . $ph_root_tag . '>';
271
+ $ph_wrap_end = '</' . $ph_root_tag . '>';
272
+ $parse_result = false;
273
+
274
+ // Find all nested layouts in layout
275
+ $match_value = preg_match_all($search, $layout, $parse_match, PREG_PATTERN_ORDER);
276
+
277
+ if ($match_value !== false && $match_value > 0) {
278
+ $parse_result = array();
279
+ // Get all matched elements
280
+ $parse_match = $parse_match[1];
281
+
282
+ // Build XML string from placeholders
283
+ foreach ($parse_match as $ph) {
284
+ $ph_xml .= $ph_start_xml . $ph . $ph_end_xml . ' ';
285
+ }
286
+ $ph_xml = $ph_wrap_start . $ph_xml . $ph_wrap_end;
287
+ // Parse XML data
288
+ $ph_prs = xml_parser_create();
289
+ xml_parser_set_option($ph_prs, XML_OPTION_SKIP_WHITE, 1);
290
+ xml_parser_set_option($ph_prs, XML_OPTION_CASE_FOLDING, 0);
291
+ $ret = xml_parse_into_struct($ph_prs, $ph_xml, $parse_result['values'], $parse_result['index']);
292
+ xml_parser_free($ph_prs);
293
+
294
+ // Build structured array with all parsed data
295
+
296
+ unset($parse_result['index'][$ph_root_tag]);
297
+
298
+ // Build structured array
299
+ $result = array();
300
+ foreach ($parse_result['index'] as $tag => $instances) {
301
+ $result[$tag] = array();
302
+ // Instances
303
+ foreach ($instances as $instance) {
304
+ // Skip instance if it doesn't exist in parse results
305
+ if (!isset($parse_result['values'][$instance]))
306
+ continue;
307
+
308
+ // Stop processing instance if a previously-saved instance with the same options already exists
309
+ foreach ($result[$tag] as $tag_match) {
310
+ if ($tag_match['match'] == $parse_match[$instance - 1])
311
+ continue 2;
312
+ }
313
+
314
+ // Init instance data array
315
+ $inst_data = array();
316
+
317
+ // Add Tag to array
318
+ $inst_data['tag'] = $parse_result['values'][$instance]['tag'];
319
+
320
+ // Add instance data to array
321
+ $inst_data['attributes'] = (isset($parse_result['values'][$instance]['attributes'])) ? $inst_data['attributes'] = $parse_result['values'][$instance]['attributes'] : '';
322
+
323
+ // Add match to array
324
+ $inst_data['match'] = $parse_match[$instance - 1];
325
+
326
+ // Add to result array
327
+ $result[$tag][] = $inst_data;
328
+ }
329
+ }
330
+ $parse_result = $result;
331
+ }
332
+
333
+ return $parse_result;
334
+ }
335
+
336
+ /**
337
+ * Retrieves default properties to use when evaluating layout placeholders
338
+ * @return object Object with properties for evaluating layout placeholders
339
+ */
340
+ function get_placeholder_defaults() {
341
+ $ph = new stdClass();
342
+ $ph->start = '{';
343
+ $ph->end = '}';
344
+ $ph->reserved = array('ref' => 'ref_base');
345
+ $ph->pattern_general = '/' . $ph->start . '([a-zA-Z0-9_].*?)' . $ph->end . '/i';
346
+ $ph->pattern_layout = '/' . $ph->start . '([a-zA-Z0-9].*?\s+' . $ph->reserved['ref'] . '="layout.*?".*?)' . $ph->end . '/i';
347
+ return $ph;
348
+ }
349
+
350
+ /**
351
+ * Build item output
352
+ * @param string $layout (optional) Layout to build
353
+ * @param string $data Data to pass to layout
354
+ */
355
+ function build($layout = null, $data = null) {
356
+ $this->util->do_action_ref_array('build_pre', array($this));
357
+ echo $this->build_layout($layout, $data);
358
+ $this->util->do_action_ref_array('build_post', array($this));
359
+ }
360
+
361
+ /**
362
+ * Builds HTML for a field based on its properties
363
+ * @param string $layout (optional) Name of layout to build
364
+ * @param array $data Additional data for current item
365
+ */
366
+ function build_layout($layout = 'form', $data = null) {
367
+ $out_default = '';
368
+ // Get base layout
369
+ $out = $this->get_layout($layout);
370
+ // Only parse valid layouts
371
+ if ( $this->is_valid_layout($out) ) {
372
+ // Parse Layout
373
+ $ph = $this->get_placeholder_defaults();
374
+
375
+ // Search layout for placeholders
376
+ while ( $ph->match = $this->parse_layout($out, $ph->pattern_general) ) {
377
+ // Iterate through placeholders (tag, id, etc.)
378
+ foreach ( $ph->match as $tag => $instances ) {
379
+ // Iterate through instances of current placeholder
380
+ foreach ( $instances as $instance ) {
381
+ // Process value based on placeholder name
382
+ $target_property = $this->util->apply_filters(array('process_placeholder_' . $tag, false), '', $this, $instance, $layout, $data);
383
+ // Process value using default processors (if necessary)
384
+ if ( '' == $target_property ) {
385
+ $target_property = $this->util->apply_filters(array('process_placeholder', false), $target_property, $this, $instance, $layout, $data);
386
+ }
387
+
388
+ // Clear value if value not a string
389
+ if ( !is_scalar($target_property) ) {
390
+ $target_property = '';
391
+ }
392
+
393
+ // Replace layout placeholder with retrieved item data
394
+ $out = str_replace($ph->start . $instance['match'] . $ph->end, $target_property, $out);
395
+ }
396
+ }
397
+ }
398
+ } else {
399
+ $out = $out_default;
400
+ }
401
+ /* Return generated value */
402
+ $out = $this->format_final($out);
403
+ return $out;
404
+ }
405
+ }
includes/class.fields.php CHANGED
@@ -1,2182 +1,4 @@
1
  <?php
2
-
3
- /**
4
- * Fields - Base class
5
- * Core properties/methods for fields
6
- * @package Simple Lightbox
7
- * @subpackage Fields
8
- * @author Archetyped
9
- */
10
- class SLB_Field_Base extends SLB_Base {
11
- /*-** Config **-*/
12
- protected $mode = 'object';
13
- protected $shared = false;
14
-
15
- /*-** Properties **-*/
16
-
17
- /**
18
- * @var string Unique name
19
- */
20
- var $id = '';
21
-
22
- /**
23
- * ID formatting options
24
- * Merged with defaults during initialization
25
- * @see $id_formats_default
26
- * @var array
27
- */
28
- var $id_formats = null;
29
-
30
- /**
31
- * Default ID Formatting options
32
- * Structure:
33
- * > Key (string): Format name
34
- * > Val (array): Options
35
- * @var array
36
- */
37
- var $id_formats_default = array(
38
- 'attr_id' => array(
39
- 'wrap' => array('open' => '_', 'segment_open' => '_'),
40
- 'prefix' => array('get_container', 'get_id', 'add_prefix'),
41
- 'recursive' => true
42
- ),
43
- 'attr_name' => array(
44
- 'wrap' => array('open' => '[', 'close' => ']', 'segment_open' => '[', 'segment_close' => ']'),
45
- 'recursive' => true,
46
- 'prefix' => array('get_container', 'get_id', 'add_prefix')
47
- )
48
- );
49
-
50
- /**
51
- * Special characters/phrases
52
- * Used for preserving special characters during formatting
53
- * Merged with $special_chars_default
54
- * Array Structure
55
- * > Key: Special character/phrase
56
- * > Value: Placeholder for special character
57
- * @var array
58
- */
59
- var $special_chars = null;
60
-
61
- var $special_chars_default = array(
62
- '{' => '%SQB_L%',
63
- '}' => '%SQB_R%',
64
- );
65
-
66
- /**
67
- * Reference to parent object that current instance inherits from
68
- * @var object
69
- */
70
- var $parent = null;
71
-
72
- /**
73
- * Title
74
- * @var string
75
- */
76
- var $title = '';
77
-
78
- /**
79
- * @var string Short description
80
- */
81
- var $description = '';
82
-
83
- /**
84
- * @var array Object Properties
85
- */
86
- var $properties = array();
87
-
88
- /**
89
- * Initialization properties
90
- * @var array
91
- */
92
- protected $properties_init = null;
93
-
94
- /**
95
- * Structure: Property names stored as keys in group
96
- * Root
97
- * -> Group Name
98
- * -> Property Name => Null
99
- * Reason: Faster searching over large arrays
100
- * @var array Groupings of Properties
101
- */
102
- var $property_groups = array();
103
-
104
- /**
105
- * Keys to filter out of properties array before setting properties
106
- * @var array
107
- */
108
- var $property_filter = array('group');
109
-
110
- /**
111
- * Define order of properties
112
- * Useful when processing order is important (e.g. one property depends on another)
113
- * @var array
114
- */
115
- var $property_priority = array();
116
-
117
- /**
118
- * Data for object
119
- * May also contain data for nested objects
120
- * @var mixed
121
- */
122
- var $data = null;
123
-
124
- /**
125
- * Whether data has been fetched or not
126
- * @var bool
127
- */
128
- var $data_loaded = false;
129
-
130
- /**
131
- * @var array Script resources to include for object
132
- */
133
- var $scripts = array();
134
-
135
- /**
136
- * @var array CSS style resources to include for object
137
- */
138
- var $styles = array();
139
-
140
- /**
141
- * Hooks (Filters/Actions) for object
142
- * @var array
143
- */
144
- var $hooks = array();
145
-
146
- /**
147
- * Mapping of child properties to parent members
148
- * Allows more flexibility when creating new instances of child objects using property arrays
149
- * Associative array structure:
150
- * > Key: Child property to map FROM
151
- * > Val: Parent property to map TO
152
- * @var array
153
- */
154
- var $map = null;
155
-
156
- /**
157
- * Options used when building collection (callbacks, etc.)
158
- * Associative array
159
- * > Key: Option name
160
- * > Value: Option value
161
- * @var array
162
- */
163
- var $build_vars = array();
164
-
165
- var $build_vars_default = array();
166
-
167
- /**
168
- * Constructor
169
- */
170
- function __construct($id = '', $properties = null) {
171
- parent::__construct();
172
- //Normalize Properties
173
- $args = func_get_args();
174
- $defaults = $this->integrate_id($id);
175
- $properties = $this->make_properties($args, $defaults);
176
- //Save init properties
177
- $this->properties_init = $properties;
178
- //Set Properties
179
- $this->set_properties($properties);
180
- }
181
-
182
- /* Getters/Setters */
183
-
184
- /**
185
- * Checks if the specified path exists in the object
186
- * @param array $path Path to check for
187
- * @return bool TRUE if path exists in object, FALSE otherwise
188
- */
189
- function path_isset($path = '') {
190
- //Stop execution if no path is supplied
191
- if ( empty($path) )
192
- return false;
193
- $args = func_get_args();
194
- $path = $this->util->build_path($args);
195
- $item =& $this;
196
- //Iterate over path and check if each level exists before moving on to the next
197
- for ($x = 0; $x < count($path); $x++) {
198
- if ( $this->util->property_exists($item, $path[$x]) ) {
199
- //Set $item as reference to next level in path for next iteration
200
- $item =& $this->util->get_property($item, $path[$x]);
201
- //$item =& $item[ $path[$x] ];
202
- } else {
203
- return false;
204
- }
205
- }
206
- return true;
207
- }
208
-
209
- /**
210
- * Retrieves a value from object using a specified path
211
- * Checks to make sure path exists in object before retrieving value
212
- * @param array $path Path to retrieve value from. Each item in array is a deeper dimension
213
- * @return mixed Value at specified path
214
- */
215
- function &get_path_value($path = '') {
216
- $ret = '';
217
- $path = $this->util->build_path(func_get_args());
218
- if ( $this->path_isset($path) ) {
219
- $ret =& $this;
220
- for ($x = 0; $x < count($path); $x++) {
221
- if ( 0 == $x )
222
- $ret =& $ret->{ $path[$x] };
223
- else
224
- $ret =& $ret[ $path[$x] ];
225
- }
226
- }
227
- return $ret;
228
- }
229
-
230
- /**
231
- * Search for specified member value in field type ancestors
232
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
233
- * @param string $name Value to retrieve from member
234
- * @return mixed Member value if found (Default: empty string)
235
- */
236
- function get_parent_value($member, $name = '', $default = '') {
237
- $parent =& $this->get_parent();
238
- return $this->get_object_value($parent, $member, $name, $default, 'parent');
239
- }
240
-
241
- /**
242
- * Retrieves specified member value
243
- * Handles inherited values
244
- * Merging corresponding parents if value is an array (e.g. for property groups)
245
- * @param string|array $member Member to search. May also contain a path to the desired member
246
- * @param string $name Value to retrieve from member
247
- * @param mixed $default Default value if no value found (Default: empty string)
248
- * @param string $dir Direction to move through hierarchy to find value
249
- * Possible Values:
250
- * parent (default) - Search through field parents
251
- * current - Do not search through connected objects
252
- * container - Search through field containers
253
- * caller - Search through field callers
254
- * @return mixed Specified member value
255
- * @todo Return reference
256
- */
257
- function &get_member_value($member, $name = '', $default = '', $dir = 'parent') {
258
- //Check if path to member is supplied
259
- $path = array();
260
- if ( is_array($member) && isset($member['tag']) ) {
261
- if ( isset($member['attributes']['ref_base']) ) {
262
- if ( 'root' != $member['attributes']['ref_base'] )
263
- $path[] = $member['attributes']['ref_base'];
264
- } else {
265
- $path[] = 'properties';
266
- }
267
-
268
- $path[] = $member['tag'];
269
- } else {
270
- $path = $member;
271
- }
272
-
273
- $path = $this->util->build_path($path, $name);
274
- //Set defaults and prepare data
275
- $val = $default;
276
- $inherit = false;
277
- $inherit_tag = '{inherit}';
278
-
279
- /* Determine whether the value must be retrieved from a parent/container object
280
- * Conditions:
281
- * > Path does not exist in current field
282
- * > Path exists and is not an object, but at least one of the following is true:
283
- * > Value at path is an array (e.g. properties, elements, etc. array)
284
- * > Parent/container values should be merged with retrieved array
285
- * > Value at path is a string that inherits from another field
286
- * > Value from other field will be retrieved and will replace inheritance placeholder in retrieved value
287
- */
288
-
289
- $deeper = false;
290
-
291
- if ( !$this->path_isset($path) )
292
- $deeper = true;
293
- else {
294
- $val = $this->get_path_value($path);
295
- if ( !is_object($val) && ( is_array($val) || ($inherit = strpos($val, $inherit_tag)) !== false ) )
296
- $deeper = true;
297
- else
298
- $deeper = false;
299
- }
300
- if ( $deeper && 'current' != $dir ) {
301
- $ex_val = '';
302
- //Get Parent value (recursive)
303
- if ( 'parent' == $dir )
304
- $ex_val = $this->get_parent_value($member, $name, $default);
305
- elseif ( method_exists($this, 'get_container_value') )
306
- $ex_val = $this->get_container_value($member, $name, $default);
307
- //Handle inheritance
308
- if ( is_array($val) ) {
309
- //Combine Arrays
310
- if ( is_array($ex_val) )
311
- $val = array_merge($ex_val, $val);
312
- } elseif ( $inherit !== false ) {
313
- //Replace placeholder with inherited string
314
- $val = str_replace($inherit_tag, $ex_val, $val);
315
- } else {
316
- //Default: Set parent value as value
317
- $val = $ex_val;
318
- }
319
- }
320
-
321
- return $val;
322
- }
323
-
324
- /**
325
- * Search for specified member value in an object
326
- * @param object $object Reference to object to retrieve value from
327
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
328
- * @param string $name (optional) Value to retrieve from member
329
- * @param mixed $default (optional) Default value to use if no value found (Default: empty string)
330
- * @param string $dir Direction to move through hierarchy to find value @see SLB_Field_Type::get_member_value() for possible values
331
- * @return mixed Member value if found (Default: $default)
332
- */
333
- function get_object_value(&$object, $member, $name = '', $default = '', $dir = 'parent') {
334
- $ret = $default;
335
- if ( is_object($object) && method_exists($object, 'get_member_value') )
336
- $ret = $object->get_member_value($member, $name, $default, $dir);
337
- return $ret;
338
- }
339
-
340
- /**
341
- * Set item ID
342
- * @param string $id Unique item ID
343
- */
344
- function set_id($id) {
345
- if ( empty($id) || !is_string($id) )
346
- return false;
347
- $this->id = trim($id);
348
- }
349
-
350
- /**
351
- * Retrieves field ID
352
- * @param array|string $options (optional) Options or ID of format to use
353
- * @return string item ID
354
- */
355
- function get_id($options = array()) {
356
- $item_id = trim($this->id);
357
- $formats = $this->get_id_formats();
358
-
359
- //Setup options
360
- $wrap_default = array('open' => '', 'close' => '', 'segment_open' => '', 'segment_close' => '');
361
-
362
- $options_default = array(
363
- 'format' => null,
364
- 'wrap' => array(),
365
- 'segments_pre' => null,
366
- 'prefix' => '',
367
- 'recursive' => false
368
- );
369
-
370
- //Load options based on format
371
- if ( !is_array($options) )
372
- $options = array('format' => $options);
373
- if ( isset($options['format']) && is_string($options['format']) && isset($formats[$options['format']]) )
374
- $options_default = wp_parse_args($formats[$options['format']], $options_default);
375
- else
376
- unset($options['format']);
377
- $options = wp_parse_args($options, $options_default);
378
- //Import options into function
379
- extract($options);
380
-
381
- //Validate options
382
- $wrap = wp_parse_args($wrap, $wrap_default);
383
-
384
- if ( !is_array($segments_pre) )
385
- $segments_pre = array($segments_pre);
386
- $segments_pre = array_reverse($segments_pre);
387
-
388
- //Format ID based on options
389
- $item_id = array($item_id);
390
-
391
- //Add parent objects to ID
392
- if ( !!$recursive ) {
393
- //Create array of ID components
394
- $m = 'get_caller';
395
- $c = ( method_exists($this, $m) ) ? $this->{$m}() : null;
396
- while ( !!$c ) {
397
- //Add ID of current caller to array
398
- if ( method_exists($c, 'get_id') && ( $itemp = $c->get_id() ) && !empty($itemp) )
399
- $item_id = $itemp;
400
- //Get parent object
401
- $c = ( method_exists($c, $m) ) ? $c->{$m}() : null;
402
- $itemp = '';
403
- }
404
- unset($c);
405
- }
406
-
407
- //Additional segments (Pre)
408
- foreach ( $segments_pre as $seg ) {
409
- if ( is_null($seg) )
410
- continue;
411
- if ( is_object($seg) )
412
- $seg = (array)$seg;
413
- if ( is_array($seg) )
414
- $item_id = array_merge($item_id, array_reverse($seg));
415
- elseif ( '' != strval($seg) )
416
- $item_id[] = strval($seg);
417
- }
418
-
419
- //Prefix
420
- if ( is_array($prefix) ) {
421
- //Array is sequence of instance methods to call on object
422
- //Last array member can be an array of parameters to pass to methods
423
- $count = count($prefix);
424
- $args = ( $count > 1 && is_array($prefix[$count - 1]) ) ? array_pop($prefix) : array();
425
- $p = $this;
426
- $val = '';
427
- //Iterate through methods
428
- foreach ( $prefix as $m ) {
429
- if ( !method_exists($p, $m) )
430
- continue;
431
- //Build callback
432
- $m = $this->util->m($p, $m);
433
- //Call callback
434
- $val = call_user_func_array($m, $args);
435
- //Returned value may be an instance object
436
- if ( is_object($val) )
437
- $p = $val; //Use returned object in next round
438
- else
439
- array_unshift($args, $val); //Pass returned value as parameter to next method on using current object
440
- }
441
- $prefix = $val;
442
- unset($p, $val);
443
- }
444
- if ( is_numeric($prefix) )
445
- $prefix = strval($prefix);
446
- if ( empty($prefix) || !is_string($prefix) )
447
- $prefix = '';
448
-
449
- //Convert array to string
450
- $item_id = $prefix . $wrap['open'] . implode($wrap['segment_close'] . $wrap['segment_open'], array_reverse($item_id)) . $wrap['close'];
451
- return $item_id;
452
- }
453
-
454
- /**
455
- * Retrieve ID formatting options for class
456
- * Format options arrays are merged together and saved to $id_formats
457
- * @uses $id_formats
458
- * @uses $id_formats_default
459
- * @return array ID Formatting options
460
- */
461
- function &get_id_formats() {
462
- if ( is_null($this->id_formats) ) {
463
- $this->id_formats = wp_parse_args($this->id_formats, $this->id_formats_default);
464
- }
465
- return $this->id_formats;
466
- }
467
-
468
- /**
469
- * Retrieve value from data member
470
- * @param string $context Context to format data for
471
- * @param bool $top (optional) Whether to traverse through the field hierarchy to get data for field (Default: TRUE)
472
- * @return mixed Value at specified path
473
- */
474
- function get_data($context = '', $top = true) {
475
- $opt_d = array('context' => '', 'top' => true);
476
- $args = func_get_args();
477
- $a = false;
478
- if ( count($args) == 1 && is_array($args[0]) && !empty($args[0]) ) {
479
- $a = true;
480
- $args = wp_parse_args($args[0], $opt_d);
481
- extract($args);
482
- }
483
-
484
- if ( is_string($top) ) {
485
- if ( 'false' == $top )
486
- $top = false;
487
- elseif ( 'true' == $top )
488
- $top = true;
489
- elseif ( is_numeric($top) )
490
- $top = intval($top);
491
- }
492
- $top = !!$top;
493
- $obj =& $this;
494
- $obj_path = array(&$this);
495
- $path = array();
496
- if ( $top ) {
497
- //Iterate through hiearchy to get top-most object
498
- while ( !empty($obj) ) {
499
- $new = null;
500
- //Try to get caller first
501
- if ( method_exists($obj, 'get_caller') ) {
502
- $checked = true;
503
- $new =& $obj->get_caller();
504
- }
505
- //Try to get container if no caller found
506
- if ( empty($new) && method_exists($obj, 'get_container') ) {
507
- $checked = true;
508
- $new =& $obj->get_container();
509
- //Load data
510
- if ( method_exists($new, 'load_data') ) {
511
- $new->load_data();
512
- }
513
- }
514
-
515
- $obj =& $new;
516
- unset($new);
517
- //Stop iteration
518
- if ( !empty($obj) ) {
519
- //Add object to path if it is valid
520
- $obj_path[] =& $obj;
521
- }
522
- }
523
- unset($obj);
524
- }
525
-
526
- //Check each object (starting with top-most) for matching data for current field
527
-
528
- //Reverse array
529
- $obj_path = array_reverse($obj_path);
530
- //Build path for data location
531
- foreach ( $obj_path as $obj ) {
532
- if ( method_exists($obj, 'get_id') )
533
- $path[] = $obj->get_id();
534
- }
535
- //Iterate through objects
536
- while ( !empty($obj_path) ) {
537
- //Get next object
538
- $obj =& array_shift($obj_path);
539
- //Shorten path
540
- array_shift($path);
541
- //Check for value in object and stop iteration if matching data found
542
- $val = $this->get_object_value($obj, 'data', $path, null, 'current');
543
- if ( !is_null($val) ) {
544
- break;
545
- }
546
- }
547
- return $this->format($val, $context);
548
- }
549
-
550
- /**
551
- * Sets value in data member
552
- * Sets value to data member itself by default
553
- * @param mixed $value Value to set
554
- * @param string|array $name Name of value to set (Can also be path to value)
555
- */
556
- function set_data($value, $name = '') {
557
- $ref =& $this->get_path_value('data', $name);
558
- $ref = $value;
559
- }
560
-
561
- /**
562
- * Sets parent object of current instance
563
- * Parent objects must be the same object type as current instance
564
- * @uses SLB to get field type definition
565
- * @uses SLB_Fields::has() to check if field type exists
566
- * @uses SLB_Fields::get() to retrieve field type object reference
567
- * @param string|object $parent Parent ID or reference
568
- */
569
- function set_parent($parent = null) {
570
- //Stop processing if parent empty
571
- if ( empty($parent) && !is_string($this->parent) )
572
- return false;
573
- //Parent passed as object reference wrapped in array
574
- if ( is_array($parent) && isset($parent[0]) && is_object($parent[0]) )
575
- $parent =& $parent[0];
576
-
577
- //No parent set but parent ID (previously) set in object
578
- if ( empty($parent) && is_string($this->parent) )
579
- $parent = $this->parent;
580
-
581
- //Retrieve reference object if ID was supplied
582
- if ( is_string($parent) ) {
583
- $parent = trim($parent);
584
- //Get parent object reference
585
- /**
586
- * @var SLB
587
- */
588
- $b =& $this->get_base();
589
- if ( $b && isset($b->fields) && $b->fields->has($parent) ) {
590
- $parent =& $b->fields->get($parent);
591
- }
592
- }
593
-
594
- //Set parent value on object
595
- if ( is_string($parent) || is_object($parent) )
596
- $this->parent =& $parent;
597
- }
598
-
599
- /**
600
- * Retrieve field type parent
601
- * @return SLB_Field_Type Reference to parent field
602
- */
603
- function &get_parent() {
604
- return $this->parent;
605
- }
606
-
607
- /**
608
- * Set object title
609
- * @param string $title Title for object
610
- * @param string $plural Plural form of title
611
- */
612
- function set_title($title = '') {
613
- if ( is_scalar($title) )
614
- $this->title = strip_tags(trim($title));
615
- }
616
-
617
- /**
618
- * Retrieve object title
619
- */
620
- function get_title() {
621
- return $this->get_member_value('title', '','', 'current');
622
- }
623
-
624
- /**
625
- * Set object description
626
- * @param string $description Description for object
627
- */
628
- function set_description($description = '') {
629
- $this->description = strip_tags(trim($description));
630
- }
631
-
632
- /**
633
- * Retrieve object description
634
- * @return string Object description
635
- */
636
- function get_description() {
637
- $dir = 'current';
638
- return $this->get_member_value('description', '','', $dir);
639
- return $desc;
640
- }
641
-
642
- /**
643
- * Sets multiple properties on field type at once
644
- * @param array $properties Properties. Each element is an array containing the arguments to set a new property
645
- * @return boolean TRUE if successful, FALSE otherwise
646
- */
647
- function set_properties($properties) {
648
- if ( !is_array($properties) ) {
649
- return false;
650
- }
651
- //Normalize properties
652
- $properties = $this->remap_properties($properties);
653
- $properties = $this->sort_properties($properties);
654
- //Set Member properties
655
- foreach ( $properties as $prop => $val ) {
656
- if ( ( $m = 'set_' . $prop ) && method_exists($this, $m) ) {
657
- $this->{$m}($val);
658
- //Remove member property from array
659
- unset($properties[$prop]);
660
- }
661
- }
662
-
663
- //Filter properties
664
- $properties = $this->filter_properties($properties);
665
- //Set additional instance properties
666
- foreach ( $properties as $name => $val) {
667
- $this->set_property($name, $val);
668
- }
669
- }
670
-
671
- /**
672
- * Remap properties based on $map
673
- * @uses $map For determine how child properties should map to parent properties
674
- * @uses SLB_Utlities::array_remap() to perform array remapping
675
- * @param array $properties Associative array of properties
676
- * @return array Remapped properties
677
- */
678
- function remap_properties($properties) {
679
- //Return remapped properties
680
- return $this->util->array_remap($properties, $this->map);
681
- }
682
-
683
- /**
684
- * Sort properties based on priority
685
- * @uses this::property_priority
686
- * @return array Sorted priorities
687
- */
688
- function sort_properties($properties) {
689
- //Stop if sorting not necessary
690
- if ( empty($properties) || !is_array($properties) || empty($this->property_priority) || !is_array($this->property_priority) )
691
- return $properties;
692
- $props = array();
693
- foreach ( $this->property_priority as $prop ) {
694
- if ( !array_key_exists($prop, $properties) )
695
- continue;
696
- //Add to new array
697
- $props[$prop] = $properties[$prop];
698
- //Remove from old array
699
- unset($properties[$prop]);
700
- }
701
- //Append any remaining properties
702
- $props = array_merge($props, $properties);
703
- return $props;
704
- }
705
-
706
- /**
707
- * Build properties array
708
- * @param array $props Instance properties
709
- * @param array $signature (optional) Default properties
710
- * @return array Normalized properties
711
- */
712
- function make_properties($props, $signature = array()) {
713
- $p = array();
714
- if ( is_array($props) ) {
715
- foreach ( $props as $prop ) {
716
- if ( is_array($prop) ) {
717
- $p = array_merge($prop, $p);
718
- }
719
- }
720
- }
721
- $props = $p;
722
- if ( is_array($signature) ) {
723
- $props = array_merge($signature, $props);
724
- }
725
- return $props;
726
- }
727
-
728
- function validate_id($id) {
729
- return ( is_scalar($id) && !empty($id) ) ? true : false;
730
- }
731
-
732
- function integrate_id($id) {
733
- return ( $this->validate_id($id) ) ? array('id' => $id) : array();
734
- }
735
-
736
- /**
737
- * Filter property members
738
- * @uses $property_filter to remove define members to remove from $properties
739
- * @param array $props Properties
740
- * @return array Filtered properties
741
- */
742
- function filter_properties($props = array()) {
743
- return $this->util->array_filter_keys($props, $this->property_filter);
744
- }
745
-
746
- /**
747
- * Add/Set a property on the field definition
748
- * @param string $name Name of property
749
- * @param mixed $value Default value for property
750
- * @param string|array $group Group(s) property belongs to
751
- * @return boolean TRUE if property is successfully added to field type, FALSE otherwise
752
- */
753
- function set_property($name, $value = '', $group = null) {
754
- //Do not add if property name is not a string
755
- if ( !is_string($name) )
756
- return false;
757
- //Create property array
758
- $prop_arr = array();
759
- $prop_arr['value'] = $value;
760
- //Add to properties array
761
- $this->properties[$name] = $value;
762
- //Add property to specified groups
763
- if ( !empty($group) ) {
764
- $this->set_group_property($group, $name);
765
- }
766
- return true;
767
- }
768
-
769
- /**
770
- * Retreives property from field type
771
- * @param string $name Name of property to retrieve
772
- * @return mixed Specified Property if exists (Default: Empty string)
773
- */
774
- function get_property($name) {
775
- $val = $this->get_member_value('properties', $name);
776
- return $val;
777
- }
778
-
779
- /**
780
- * Removes a property from item
781
- * @param string $name Property ID
782
- */
783
- function remove_property($name) {
784
- //Remove property
785
- if ( isset($this->properties[$name]) )
786
- unset($this->properties[$name]);
787
- //Remove from group
788
- foreach ( array_keys($this->property_groups) as $g ) {
789
- if ( isset($this->property_groups[$g][$name]) ) {
790
- unset($this->property_groups[$g][$name]);
791
- break;
792
- }
793
- }
794
- }
795
-
796
- /**
797
- * Adds Specified Property to a Group
798
- * @param string|array $group Group(s) to add property to
799
- * @param string $property Property to add to group
800
- */
801
- function set_group_property($group, $property) {
802
- if ( is_string($group) && isset($this->property_groups[$group][$property]) )
803
- return;
804
- if ( !is_array($group) ) {
805
- $group = array($group);
806
- }
807
-
808
- foreach ($group as $g) {
809
- $g = trim($g);
810
- //Initialize group if it doesn't already exist
811
- if ( !isset($this->property_groups[$g]) )
812
- $this->property_groups[$g] = array();
813
-
814
- //Add property to group
815
- $this->property_groups[$g][$property] = null;
816
- }
817
- }
818
-
819
- /**
820
- * Retrieve property group
821
- * @param string $group Group to retrieve
822
- * @return array Array of properties in specified group
823
- */
824
- function get_group($group) {
825
- return $this->get_member_value('property_groups', $group, array());
826
- }
827
-
828
- /**
829
- * Save field data
830
- * Child classes will define their own
831
- * functionality for this method
832
- * @return bool TRUE if save was successful (FALSE otherwise)
833
- */
834
- function save() {
835
- return true;
836
- }
837
-
838
- /*-** Hooks **-*/
839
-
840
- /**
841
- * Retrieve hooks added to object
842
- * @return array Hooks
843
- */
844
- function get_hooks() {
845
- return $this->get_member_value('hooks', '', array());
846
- }
847
-
848
- /**
849
- * Add hook for object
850
- * @see add_filter() for parameter defaults
851
- * @param $tag
852
- * @param $function_to_add
853
- * @param $priority
854
- * @param $accepted_args
855
- */
856
- function add_hook($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
857
- //Create new array for tag (if not already set)
858
- if ( !isset($this->hooks[$tag]) )
859
- $this->hooks[$tag] = array();
860
- //Build Unique ID
861
- if ( is_string($function_to_add) )
862
- $id = $function_to_add;
863
- elseif ( is_array($function_to_add) && !empty($function_to_add) )
864
- $id = strval($function_to_add[count($function_to_add) - 1]);
865
- else
866
- $id = 'function_' . ( count($this->hooks[$tag]) + 1 );
867
- //Add hook
868
- $this->hooks[$tag][$id] = func_get_args();
869
- }
870
-
871
- /**
872
- * Convenience method for adding an action for object
873
- * @see add_filter() for parameter defaults
874
- * @param $tag
875
- * @param $function_to_add
876
- * @param $priority
877
- * @param $accepted_args
878
- */
879
- function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
880
- $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
881
- }
882
-
883
- /**
884
- * Convenience method for adding a filter for object
885
- * @see add_filter() for parameter defaults
886
- * @param $tag
887
- * @param $function_to_add
888
- * @param $priority
889
- * @param $accepted_args
890
- */
891
- function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
892
- $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
893
- }
894
-
895
- /*-** Dependencies **-*/
896
-
897
- /**
898
- * Adds dependency to object
899
- * @param string $type Type of dependency to add (script, style)
900
- * @param array|string $context When dependency will be added (@see SLB_Utilities::get_action() for possible contexts)
901
- * @see wp_enqueue_script for the following of the parameters
902
- * @param $handle
903
- * @param $src
904
- * @param $deps
905
- * @param $ver
906
- * @param $ex
907
- */
908
- function add_dependency($type, $context, $handle, $src = false, $deps = array(), $ver = false, $ex = false) {
909
- $args = func_get_args();
910
- //Remove type/context from arguments
911
- $args = array_slice($args, 2);
912
-
913
- //Set context
914
- if ( !is_array($context) ) {
915
- //Wrap single contexts in an array
916
- if ( is_string($context) )
917
- $context = array($context);
918
- else
919
- $context = array();
920
- }
921
- //Add file to instance property
922
- if ( isset($this->{$type}) && is_array($this->{$type}) )
923
- $this->{$type}[$handle] = array('context' => $context, 'params' => $args);
924
- }
925
-
926
- /**
927
- * Add script to object to be added in specified contexts
928
- * @param array|string $context Array of contexts to add script to page
929
- * @see wp_enqueue_script for the following of the parameters
930
- * @param $handle
931
- * @param $src
932
- * @param $deps
933
- * @param $ver
934
- * @param $in_footer
935
- */
936
- function add_script( $context, $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
937
- $args = func_get_args();
938
- //Add file type to front of arguments array
939
- array_unshift($args, 'scripts');
940
- call_user_func_array($this->m('add_dependency'), $args);
941
- }
942
-
943
- /**
944
- * Retrieve script dependencies for object
945
- * @return array Script dependencies
946
- */
947
- function get_scripts() {
948
- return $this->get_member_value('scripts', '', array());
949
- }
950
-
951
- /**
952
- * Add style to object to be added in specified contexts
953
- * @param array|string $context Array of contexts to add style to page
954
- * @see wp_enqueue_style for the following of the parameters
955
- * @param $handle
956
- * @param $src
957
- * @param $deps
958
- * @param $ver
959
- * @param $in_footer
960
- */
961
- function add_style( $handle, $src = false, $deps = array(), $ver = false, $media = false ) {
962
- $args = func_get_args();
963
- array_unshift($args, 'styles');
964
- call_user_func_array($this->m('add_dependency'), $args);
965
- }
966
-
967
- /**
968
- * Retrieve Style dependencies for object
969
- * @return array Style dependencies
970
- */
971
- function get_styles() {
972
- return $this->get_member_value('styles', '', array());
973
- }
974
-
975
- /* Helpers */
976
-
977
- /**
978
- * Format value based on specified context
979
- * @param mixed $value Value to format
980
- * @param string $context Current context
981
- * @return mixed Formatted value
982
- */
983
- function format($value, $context = '') {
984
- $handler = 'format_' . trim(strval($context));
985
- //Only process if context is valid and has a handler
986
- if ( !empty($context) && method_exists($this, $handler) ) {
987
- //Pass value to handler
988
- $value = $this->{$handler}($value, $context);
989
- }
990
- //Return formatted value
991
- return $value;
992
- }
993
-
994
- /**
995
- * Format value for output in form field
996
- * @param mixed $value Value to format
997
- * @return mixed Formatted value
998
- */
999
- function format_form($value) {
1000
- if ( is_string($value) )
1001
- $value = htmlspecialchars($value);
1002
- return $value;
1003
- }
1004
-
1005
- /**
1006
- * Final formatting before output
1007
- * Restores special characters, etc.
1008
- * @uses $special_chars
1009
- * @uses $special_chars_default
1010
- * @param mixed $value Pre-final field output
1011
- * @param string $context (Optional) Formatting context
1012
- * @return mixed Formatted value
1013
- */
1014
- function format_final($value, $context = '') {
1015
- if ( !is_string($value) )
1016
- return $value;
1017
-
1018
- //Restore special chars
1019
- return $this->restore_special_chars($value, $context);
1020
- }
1021
-
1022
- function preserve_special_chars($value, $context = '') {
1023
- if ( !is_string($value) )
1024
- return $value;
1025
- $specials = $this->get_special_chars();
1026
- return str_replace(array_keys($specials), $specials, $value);
1027
- }
1028
-
1029
- function restore_special_chars($value, $context = '') {
1030
- if ( !is_string($value) )
1031
- return $value;
1032
- $specials = $this->get_special_chars();
1033
- return str_replace($specials, array_keys($specials), $value);
1034
- }
1035
-
1036
- /**
1037
- * Retrieve special characters/placeholders
1038
- * Merges defaults with class-specific characters
1039
- * @uses $special_chars
1040
- * @uses $special_chars_default
1041
- * @return array Special characters/placeholders
1042
- */
1043
- function get_special_chars() {
1044
- return wp_parse_args($this->special_chars, $this->special_chars_default);
1045
- }
1046
- }
1047
-
1048
- /**
1049
- * Field Types
1050
- * Stores properties for a specific field
1051
- * @package Simple Lightbox
1052
- * @subpackage Fields
1053
- * @author Archetyped
1054
- */
1055
- class SLB_Field_Type extends SLB_Field_Base {
1056
- /* Properties */
1057
-
1058
- /**
1059
- * @var array Array of Field types that make up current Field type
1060
- */
1061
- var $elements = array();
1062
-
1063
- /**
1064
- * @var array Field type layouts
1065
- */
1066
- var $layout = array();
1067
-
1068
- /**
1069
- * @var SLB_Field_Type Parent field type (reference)
1070
- */
1071
- var $parent = null;
1072
-
1073
- /**
1074
- * Object that field is in
1075
- * @var SLB_Field|SLB_Field_Type|SLB_Field_Collection
1076
- */
1077
- var $container = null;
1078
-
1079
- /**
1080
- * Object that called field
1081
- * Used to determine field hierarchy/nesting
1082
- * @var SLB_Field|SLB_Field_Type|SLB_Field_Collection
1083
- */
1084
- var $caller = null;
1085
-
1086
- function __construct($id = '', $parent = null) {
1087
- $args = func_get_args();
1088
- $defaults = $this->integrate_id($id);
1089
- if ( !is_array($parent) )
1090
- $defaults['parent'] = $parent;
1091
-
1092
- $props = $this->make_properties($args, $defaults);
1093
- parent::__construct($props);
1094
- }
1095
-
1096
- /* Getters/Setters */
1097
-
1098
- /**
1099
- * Search for specified member value in field's container object (if exists)
1100
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
1101
- * @param string $name Value to retrieve from member
1102
- * @return mixed Member value if found (Default: empty string)
1103
- */
1104
- function get_container_value($member, $name = '', $default = '') {
1105
- $container =& $this->get_container();
1106
- return $this->get_object_value($container, $member, $name, $default, 'container');
1107
- }
1108
-
1109
- /**
1110
- * Search for specified member value in field's container object (if exists)
1111
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
1112
- * @param string $name Value to retrieve from member
1113
- * @return mixed Member value if found (Default: empty string)
1114
- */
1115
- function get_caller_value($member, $name = '', $default = '') {
1116
- $caller =& $this->get_caller();
1117
- return $this->get_object_value($caller, $member, $name, $default, 'caller');
1118
- }
1119
-
1120
- /**
1121
- * Sets reference to container object of current field
1122
- * Reference is cleared if no valid object is passed to method
1123
- * @param object $container
1124
- */
1125
- function set_container(&$container) {
1126
- if ( !empty($container) && is_object($container) ) {
1127
- //Set as param as container for current field
1128
- $this->container =& $container;
1129
- } else {
1130
- //Clear container member if argument is invalid
1131
- $this->clear_container();
1132
- }
1133
- }
1134
-
1135
- /**
1136
- * Clears reference to container object of current field
1137
- */
1138
- function clear_container() {
1139
- $this->container = null;
1140
- }
1141
-
1142
- /**
1143
- * Retrieves reference to container object of current field
1144
- * @return object Reference to container object
1145
- */
1146
- function &get_container() {
1147
- $ret = null;
1148
- if ( $this->has_container() )
1149
- $ret =& $this->container;
1150
- return $ret;
1151
- }
1152
-
1153
- /**
1154
- * Checks if field has a container reference
1155
- * @return bool TRUE if field is contained, FALSE otherwise
1156
- */
1157
- function has_container() {
1158
- return !empty($this->container);
1159
- }
1160
-
1161
- /**
1162
- * Sets reference to calling object of current field
1163
- * Any existing reference is cleared if no valid object is passed to method
1164
- * @param object $caller Calling object
1165
- */
1166
- function set_caller(&$caller) {
1167
- if ( !empty($caller) && is_object($caller) )
1168
- $this->caller =& $caller;
1169
- else
1170
- $this->clear_caller();
1171
- }
1172
-
1173
- /**
1174
- * Clears reference to calling object of current field
1175
- */
1176
- function clear_caller() {
1177
- unset($this->caller);
1178
- }
1179
-
1180
- /**
1181
- * Retrieves reference to caller object of current field
1182
- * @return object Reference to caller object
1183
- */
1184
- function &get_caller() {
1185
- $ret = null;
1186
- if ( $this->has_caller() )
1187
- $ret =& $this->caller;
1188
- return $ret;
1189
- }
1190
-
1191
- /**
1192
- * Checks if field has a caller reference
1193
- * @return bool TRUE if field is called by another field, FALSE otherwise
1194
- */
1195
- function has_caller() {
1196
- return !empty($this->caller);
1197
- }
1198
-
1199
-
1200
-
1201
- /**
1202
- * Sets an element for the field type
1203
- * @param string $name Name of element
1204
- * @param SLB_Field_Type $type Reference of field type to use for element
1205
- * @param array $properties Properties for element (passed as keyed associative array)
1206
- * @param string $id_prop Name of property to set $name to (e.g. ID, etc.)
1207
- */
1208
- function set_element($name, $type, $properties = array(), $id_prop = 'id') {
1209
- $name = trim(strval($name));
1210
- if ( empty($name) )
1211
- return false;
1212
- //Create new field for element
1213
- $el = new SLB_Field($name, $type);
1214
- //Set container to current field instance
1215
- $el->set_container($this);
1216
- //Add properties to element
1217
- $el->set_properties($properties);
1218
- //Save element to current instance
1219
- $this->elements[$name] =& $el;
1220
- }
1221
-
1222
- /**
1223
- * Add a layout to the field
1224
- * @param string $name Name of layout
1225
- * @param string $value Layout text
1226
- */
1227
- function set_layout($name, $value = '') {
1228
- if ( !is_string($name) )
1229
- return false;
1230
- $name = trim($name);
1231
- $this->layout[$name] = $value;
1232
- return true;
1233
- }
1234
-
1235
- /**
1236
- * Retrieve specified layout
1237
- * @param string $name Layout name
1238
- * @param bool $parse_nested (optional) Whether nested layouts should be expanded in retreived layout or not (Default: TRUE)
1239
- * @return string Specified layout text
1240
- */
1241
- function get_layout($name = 'form', $parse_nested = true) {
1242
- //Retrieve specified layout (use $name value if no layout by that name exists)
1243
- if ( empty($name) )
1244
- $name = $this->get_container_value('build_vars', 'layout', 'form');
1245
- $layout = $this->get_member_value('layout', $name, $name);
1246
-
1247
- //Find all nested layouts in current layout
1248
- if ( !empty($layout) && !!$parse_nested ) {
1249
- $ph = $this->get_placeholder_defaults();
1250
-
1251
- while ($ph->match = $this->parse_layout($layout, $ph->pattern_layout)) {
1252
- //Iterate through the different types of layout placeholders
1253
- foreach ($ph->match as $tag => $instances) {
1254
- //Iterate through instances of a specific type of layout placeholder
1255
- foreach ($instances as $instance) {
1256
- //Get nested layout
1257
- $nested_layout = $this->get_member_value($instance);
1258
-
1259
- //Replace layout placeholder with retrieved item data
1260
- if ( !empty($nested_layout) )
1261
- $layout = str_replace($ph->start . $instance['match'] . $ph->end, $nested_layout, $layout);
1262
- }
1263
- }
1264
- }
1265
- }
1266
-
1267
- return $layout;
1268
- }
1269
-
1270
- /**
1271
- * Checks if specified layout exists
1272
- * Finds layout if it exists in current object or any of its parents
1273
- * @param string $layout Name of layout to check for
1274
- * @return bool TRUE if layout exists, FALSE otherwise
1275
- */
1276
- function has_layout($layout) {
1277
- $ret = false;
1278
- if ( is_string($layout) && ($layout = trim($layout)) && !empty($layout) ) {
1279
- $layout = $this->get_member_value('layout', $layout, false);
1280
- if ( $layout !== false )
1281
- $ret = true;
1282
- }
1283
-
1284
- return $ret;
1285
- }
1286
-
1287
- /**
1288
- * Checks if layout content is valid
1289
- * Layouts need to have placeholders to be valid
1290
- * @param string $layout_content Layout content (markup)
1291
- * @return bool TRUE if layout is valid, FALSE otherwise
1292
- */
1293
- function is_valid_layout($layout_content) {
1294
- $ph = $this->get_placeholder_defaults();
1295
- return preg_match($ph->pattern_general, $layout_content);
1296
- }
1297
-
1298
- /**
1299
- * Parse field layout with a regular expression
1300
- * @param string $layout Layout data
1301
- * @param string $search Regular expression pattern to search layout for
1302
- * @return array Associative array containing all of the regular expression matches in the layout data
1303
- * Array Structure:
1304
- * root => placeholder tags
1305
- * => Tag instances (array)
1306
- * 'tag' => (string) tag name
1307
- * 'match' => (string) placeholder match
1308
- * 'attributes' => (array) attributes
1309
- */
1310
- function parse_layout($layout, $search) {
1311
- $ph_xml = '';
1312
- $parse_match = '';
1313
- $ph_root_tag = 'ph_root_element';
1314
- $ph_start_xml = '<';
1315
- $ph_end_xml = ' />';
1316
- $ph_wrap_start = '<' . $ph_root_tag . '>';
1317
- $ph_wrap_end = '</' . $ph_root_tag . '>';
1318
- $parse_result = false;
1319
-
1320
- //Find all nested layouts in layout
1321
- $match_value = preg_match_all($search, $layout, $parse_match, PREG_PATTERN_ORDER);
1322
-
1323
- if ($match_value !== false && $match_value > 0) {
1324
- $parse_result = array();
1325
- //Get all matched elements
1326
- $parse_match = $parse_match[1];
1327
-
1328
- //Build XML string from placeholders
1329
- foreach ($parse_match as $ph) {
1330
- $ph_xml .= $ph_start_xml . $ph . $ph_end_xml . ' ';
1331
- }
1332
- $ph_xml = $ph_wrap_start . $ph_xml . $ph_wrap_end;
1333
- //Parse XML data
1334
- $ph_prs = xml_parser_create();
1335
- xml_parser_set_option($ph_prs, XML_OPTION_SKIP_WHITE, 1);
1336
- xml_parser_set_option($ph_prs, XML_OPTION_CASE_FOLDING, 0);
1337
- $ret = xml_parse_into_struct($ph_prs, $ph_xml, $parse_result['values'], $parse_result['index']);
1338
- xml_parser_free($ph_prs);
1339
-
1340
- //Build structured array with all parsed data
1341
-
1342
- unset($parse_result['index'][$ph_root_tag]);
1343
-
1344
- //Build structured array
1345
- $result = array();
1346
- foreach ($parse_result['index'] as $tag => $instances) {
1347
- $result[$tag] = array();
1348
- //Instances
1349
- foreach ($instances as $instance) {
1350
- //Skip instance if it doesn't exist in parse results
1351
- if (!isset($parse_result['values'][$instance]))
1352
- continue;
1353
-
1354
- //Stop processing instance if a previously-saved instance with the same options already exists
1355
- foreach ($result[$tag] as $tag_match) {
1356
- if ($tag_match['match'] == $parse_match[$instance - 1])
1357
- continue 2;
1358
- }
1359
-
1360
- //Init instance data array
1361
- $inst_data = array();
1362
-
1363
- //Add Tag to array
1364
- $inst_data['tag'] = $parse_result['values'][$instance]['tag'];
1365
-
1366
- //Add instance data to array
1367
- $inst_data['attributes'] = (isset($parse_result['values'][$instance]['attributes'])) ? $inst_data['attributes'] = $parse_result['values'][$instance]['attributes'] : '';
1368
-
1369
- //Add match to array
1370
- $inst_data['match'] = $parse_match[$instance - 1];
1371
-
1372
- //Add to result array
1373
- $result[$tag][] = $inst_data;
1374
- }
1375
- }
1376
- $parse_result = $result;
1377
- }
1378
-
1379
- return $parse_result;
1380
- }
1381
-
1382
- /**
1383
- * Retrieves default properties to use when evaluating layout placeholders
1384
- * @return object Object with properties for evaluating layout placeholders
1385
- */
1386
- function get_placeholder_defaults() {
1387
- $ph = new stdClass();
1388
- $ph->start = '{';
1389
- $ph->end = '}';
1390
- $ph->reserved = array('ref' => 'ref_base');
1391
- $ph->pattern_general = '/' . $ph->start . '([a-zA-Z0-9_].*?)' . $ph->end . '/i';
1392
- $ph->pattern_layout = '/' . $ph->start . '([a-zA-Z0-9].*?\s+' . $ph->reserved['ref'] . '="layout.*?".*?)' . $ph->end . '/i';
1393
- return $ph;
1394
- }
1395
-
1396
- /**
1397
- * Build item output
1398
- * @param string $layout (optional) Layout to build
1399
- * @param string $data Data to pass to layout
1400
- */
1401
- function build($layout = null, $data = null) {
1402
- $this->util->do_action_ref_array('build_pre', array(&$this));
1403
- echo $this->build_layout($layout, $data);
1404
- $this->util->do_action_ref_array('build_post', array(&$this));
1405
- }
1406
-
1407
- /**
1408
- * Builds HTML for a field based on its properties
1409
- * @param string $layout (optional) Name of layout to build
1410
- * @param array $data Additional data for current item
1411
- */
1412
- function build_layout($layout = 'form', $data = null) {
1413
- $out_default = '';
1414
- //Get base layout
1415
- $out = $this->get_layout($layout);
1416
- //Only parse valid layouts
1417
- if ( $this->is_valid_layout($out) ) {
1418
- //Parse Layout
1419
- $ph = $this->get_placeholder_defaults();
1420
-
1421
- //Search layout for placeholders
1422
- while ( $ph->match = $this->parse_layout($out, $ph->pattern_general) ) {
1423
- //Iterate through placeholders (tag, id, etc.)
1424
- foreach ( $ph->match as $tag => $instances ) {
1425
- //Iterate through instances of current placeholder
1426
- foreach ( $instances as $instance ) {
1427
- //Process value based on placeholder name
1428
- $target_property = apply_filters($this->add_prefix('process_placeholder_' . $tag), '', $this, $instance, $layout, $data);
1429
- //Process value using default processors (if necessary)
1430
- if ( '' == $target_property ) {
1431
- $target_property = apply_filters($this->add_prefix('process_placeholder'), $target_property, $this, $instance, $layout, $data);
1432
- }
1433
-
1434
- //Clear value if value not a string
1435
- if ( !is_scalar($target_property) ) {
1436
- $target_property = '';
1437
- }
1438
-
1439
- //Replace layout placeholder with retrieved item data
1440
- $out = str_replace($ph->start . $instance['match'] . $ph->end, $target_property, $out);
1441
- }
1442
- }
1443
- }
1444
- } else {
1445
- $out = $out_default;
1446
- }
1447
- /* Return generated value */
1448
- $out = $this->format_final($out);
1449
- return $out;
1450
- }
1451
- }
1452
-
1453
- class SLB_Field extends SLB_Field_Type {}
1454
-
1455
- /**
1456
- * Managed collection of fields
1457
- * @package Simple Lightbox
1458
- * @subpackage Fields
1459
- * @author Archetyped
1460
- */
1461
- class SLB_Field_Collection extends SLB_Field_Base {
1462
-
1463
- /* Configuration */
1464
-
1465
- protected $mode = 'sub';
1466
-
1467
- /* Properties */
1468
-
1469
- /**
1470
- * Item type
1471
- * @var string
1472
- */
1473
- var $item_type = 'SLB_Field';
1474
-
1475
- /**
1476
- * Indexed array of items in collection
1477
- * @var array
1478
- */
1479
- var $items = array();
1480
-
1481
- var $id_formats = array (
1482
- 'formatted' => array(
1483
- 'wrap' => array ( 'open' => '_' ),
1484
- 'recursive' => false,
1485
- 'prefix' => array('get_prefix')
1486
- )
1487
- );
1488
-
1489
- var $build_vars_default = array (
1490
- 'groups' => array(),
1491
- 'context' => '',
1492
- 'layout' => 'form',
1493
- 'build' => true,
1494
- 'build_groups' => true,
1495
- );
1496
-
1497
- /**
1498
- * Associative array of groups in collection
1499
- * Key: Group name
1500
- * Value: object of group properties
1501
- * > title
1502
- * > description string Group description
1503
- * > items array Items in group
1504
- * @var array
1505
- */
1506
- var $groups = array();
1507
-
1508
- protected $properties_init = null;
1509
-
1510
- /* Constructors */
1511
-
1512
- /**
1513
- * Class constructor
1514
- * @uses parent::__construct()
1515
- * @uses self::make_properties()
1516
- * @uses self::init()
1517
- * @uses self::add_groups()
1518
- * @uses self::add_items()
1519
- * @param string $id Collection ID
1520
- * @param array $properties (optional) Properties to set for collection (Default: none)
1521
- */
1522
- public function __construct($id, $properties = null) {
1523
- $args = func_get_args();
1524
- $properties = $this->make_properties($args);
1525
- //Parent constructor
1526
- parent::__construct($properties);
1527
-
1528
- //Save initial properties
1529
- $this->properties_init = $properties;
1530
- }
1531
-
1532
- public function _init() {
1533
- parent::_init();
1534
- $this->load($this->properties_init, false);
1535
- }
1536
-
1537
- /*-** Getters/Setters **-*/
1538
-
1539
- /* Setup */
1540
-
1541
- /**
1542
- * Load collection with specified properties
1543
- * Updates existing properties
1544
- * @param array $properties Properties to load
1545
- * @param bool $update (optional) Update (TRUE) or overwrite (FALSE) items/groups (Default: TRUE)
1546
- * @return object Current instance
1547
- */
1548
- public function load($properties, $update = true) {
1549
- $args = func_get_args();
1550
- $properties = $this->make_properties($args);
1551
- if ( !empty($properties) ) {
1552
- //Groups
1553
- if ( isset($properties['groups']) ) {
1554
- $this->add_groups($properties['groups'], $update);
1555
- }
1556
- //Items
1557
- if ( isset($properties['items']) ) {
1558
- $this->add_items($properties['items'], $update);
1559
- }
1560
- }
1561
- return $this;
1562
- }
1563
-
1564
- /* Data */
1565
-
1566
- /**
1567
- * Retrieve external data for items in collection
1568
- * Retrieved data is saved to the collection's $data property
1569
- * Uses class properties to determine how data is retrieved
1570
- * Examples:
1571
- * > DB
1572
- * > XML
1573
- * > JSON
1574
- * @return void
1575
- */
1576
- function load_data() {
1577
- $this->data_loaded = true;
1578
- }
1579
-
1580
- /**
1581
- * Set data for an item
1582
- * @param mixed $item Field to set data for
1583
- * > string Field ID
1584
- * > object Field Reference
1585
- * > array Data for multiple items (associative array [field ID => data])
1586
- * @param mixed $value Data to set
1587
- * @param bool $save (optional) Whether or not data should be saved to DB (Default: Yes)
1588
- */
1589
- function set_data($item, $value = '', $save = true, $force_set = false) {
1590
- //Set data for entire collection
1591
- if ( is_array($item) ) {
1592
- $this->data = wp_parse_args($item, $this->data);
1593
- //Update save option
1594
- $args = func_get_args();
1595
- if ( 2 == count($args) && is_bool($args[1]) ) {
1596
- $save = $args[1];
1597
- }
1598
- }
1599
- //Get $item's ID
1600
- elseif ( is_object($item) && method_exists($item, 'get_id') )
1601
- $item = $item->get_id();
1602
- //Set data
1603
- if ( is_string($item) && !empty($item) && ( isset($this->items[$item]) || !!$force_set ) )
1604
- $this->data[$item] = $value;
1605
- if ( !!$save )
1606
- $this->save();
1607
- }
1608
-
1609
- /* Item */
1610
-
1611
- /**
1612
- * Adds item to collection
1613
- * @param string|obj $id Unique name for item or item instance
1614
- * @param array $properties (optional) Item properties
1615
- * @param bool $update (optional) Update or overwrite existing item (Default: FALSE)
1616
- * @return object Reference to new item
1617
- */
1618
- function &add($id, $properties = array(), $update = false) {
1619
- $item;
1620
- $args = func_get_args();
1621
- //Properties
1622
- foreach ( array_reverse($args) as $arg ) {
1623
- if ( is_array($arg) ) {
1624
- $properties = $arg;
1625
- break;
1626
- }
1627
- }
1628
- if ( !is_array($properties) ) {
1629
- $properties = array();
1630
- }
1631
-
1632
- //Handle item instance
1633
- if ( $id instanceof $this->item_type ) {
1634
- $item = $id;
1635
- $item->set_properties($properties);
1636
- } elseif ( class_exists($this->item_type) ) {
1637
- $defaults = array (
1638
- 'parent' => null,
1639
- 'group' => null
1640
- );
1641
- $properties = array_merge($defaults, $properties);
1642
- if ( is_string($id) ) {
1643
- $properties['id'] = $id;
1644
- }
1645
- if ( !!$update && $this->has($properties['id']) ) {
1646
- //Update existing item
1647
- $item = $this->get($properties['id']);
1648
- $item->set_properties($properties);
1649
- } else {
1650
- //Init item
1651
- $type = $this->item_type;
1652
- $item = new $type($properties);
1653
- }
1654
- }
1655
-
1656
- if ( empty($item) || 0 == strlen($item->get_id()) ) {
1657
- return false;
1658
- }
1659
-
1660
- //Set container
1661
- $item->set_container($this);
1662
-
1663
- //Add item to collection
1664
- $this->items[$item->get_id()] = $item;
1665
-
1666
- if ( isset($properties['group']) ) {
1667
- $this->add_to_group($properties['group'], $item->get_id());
1668
- }
1669
-
1670
- return $item;
1671
- }
1672
-
1673
- /**
1674
- * Removes item from collection
1675
- * @param string|object $item Object or item ID to remove
1676
- * @param bool $save (optional) Whether to save the collection after removing item (Default: YES)
1677
- */
1678
- function remove($item, $save = true) {
1679
- //Remove item
1680
- if ( $this->has($item) ) {
1681
- $item = $this->get($item);
1682
- $item = $item->get_id();
1683
- //Remove from items array
1684
- unset($this->items[$item]);
1685
- //Remove item from groups
1686
- $this->remove_from_group($item);
1687
- }
1688
- //Remove item data from collection
1689
- $this->remove_data($item, false);
1690
-
1691
- if ( !!$save )
1692
- $this->save();
1693
- }
1694
-
1695
- /**
1696
- * Remove item data from collection
1697
- * @param string|object $item Object or item ID to remove
1698
- * @param bool $save (optional) Whether to save the collection after removing item (Default: YES)
1699
- */
1700
- function remove_data($item, $save = true) {
1701
- //Get item ID from object
1702
- if ( $this->has($item) ) {
1703
- $item = $this->get($item);
1704
- $item = $item->get_id();
1705
- }
1706
-
1707
- //Remove data from data member
1708
- if ( is_string($item) && is_array($this->data) ) {
1709
- unset($this->data[$item]);
1710
- if ( !!$save )
1711
- $this->save();
1712
- }
1713
- }
1714
-
1715
- /**
1716
- * Checks if item exists in the collection
1717
- * @param string $item Item ID
1718
- * @return bool TRUE if item exists, FALSE otherwise
1719
- */
1720
- function has($item) {
1721
- return ( !is_string($item) || empty($item) || is_null($this->get_member_value('items', $item, null)) ) ? false : true;
1722
- }
1723
-
1724
- /**
1725
- * Retrieve specified item in collection
1726
- * @param string|object $item Item object or ID to retrieve
1727
- * @return SLB_Field Specified item
1728
- */
1729
- function get($item, $safe_mode = false) {
1730
- if ( $this->has($item) ) {
1731
- if ( !is_object($item) || !is_a($item, $this->item_type) ) {
1732
- if ( is_string($item) ) {
1733
- $item = trim($item);
1734
- $item =& $this->items[$item];
1735
- }
1736
- else {
1737
- $item = false;
1738
- }
1739
- }
1740
- } else {
1741
- $item = false;
1742
- }
1743
-
1744
- if ( !!$safe_mode && !is_object($item) ) {
1745
- //Fallback: Return empty item if no item exists
1746
- $type = $this->item_type;
1747
- $item = new $type('');
1748
- }
1749
- return $item;
1750
- }
1751
-
1752
- /**
1753
- * Retrieve item data
1754
- * @param $item Item to get data for
1755
- * @param $context (optional) Context
1756
- * @param $top (optional) Iterate through ancestors to get data (Default: Yes)
1757
- * @return mixed Item data
1758
- */
1759
- function get_data($item = null, $context = '', $top = true) {
1760
- $this->load_data();
1761
- $ret = null;
1762
- if ( $this->has($item) ) {
1763
- $item =& $this->get($item);
1764
- $ret = $item->get_data($context, $top);
1765
- } else {
1766
- $ret = parent::get_data($context, $top);
1767
- }
1768
-
1769
- if ( is_string($item) && is_array($ret) && isset($ret[$item]) )
1770
- $ret = $ret[$item];
1771
- return $ret;
1772
- }
1773
-
1774
- /* Items (Collection) */
1775
-
1776
- /**
1777
- * Add multiple items to collection
1778
- * @param array $items Items to add to collection
1779
- * Array Structure:
1780
- * > Key (string): Item ID
1781
- * > Val (array): Item properties
1782
- * @return void
1783
- */
1784
- function add_items($items = array(), $update = false) {
1785
- //Validate
1786
- if ( !is_array($items) || empty($items) ) {
1787
- return false;
1788
- }
1789
- //Add items
1790
- foreach ( $items as $id => $props ) {
1791
- $this->add($id, $props, $update);
1792
- }
1793
- }
1794
-
1795
- /**
1796
- * Retrieve reference to items in collection
1797
- * @return array Collection items (reference)
1798
- */
1799
- function get_items($group = null, $sort = 'priority') {
1800
- $gset = $this->group_exists($group);
1801
- if ( $gset ) {
1802
- $items = $this->get_group_items($group);
1803
- } elseif ( !empty($group) ) {
1804
- $items = array();
1805
- } else {
1806
- $items = $this->items;
1807
- }
1808
- if ( !empty($items) ) {
1809
- //Sort items
1810
- if ( !empty($sort) && is_string($sort) ) {
1811
- if ( 'priority' == $sort ) {
1812
- if ( $gset ) {
1813
- //Sort by priority
1814
- ksort($items, SORT_NUMERIC);
1815
- }
1816
- }
1817
- }
1818
- //Release from buckets
1819
- if ( $gset ) {
1820
- $items = call_user_func_array('array_merge', $items);
1821
- }
1822
- }
1823
- return $items;
1824
- }
1825
-
1826
- /**
1827
- * Build output for items in specified group
1828
- * If no group specified, all items in collection are built
1829
- * @param string|object $group (optional) Group to build items for (ID or instance object)
1830
- */
1831
- function build_items($group = null) {
1832
- //Get group items
1833
- $items =& $this->get_items($group);
1834
- if ( empty($items) ) {
1835
- return false;
1836
- }
1837
-
1838
- $this->util->do_action_ref_array('build_items_pre', array(&$this));
1839
- foreach ( $items as $item ) {
1840
- $item->build();
1841
- }
1842
- $this->util->do_action_ref_array('build_items_post', array(&$this));
1843
- }
1844
-
1845
- /* Group */
1846
-
1847
- /**
1848
- * Add groups to collection
1849
- * @param array $groups Associative array of group properties
1850
- * Array structure:
1851
- * > Key (string): group ID
1852
- * > Val (string): Group Title
1853
- */
1854
- function add_groups($groups = array(), $update = false) {
1855
- //Validate
1856
- if ( !is_array($groups) || empty($groups) ) {
1857
- return false;
1858
- }
1859
- //Iterate
1860
- foreach ( $groups as $id => $props ) {
1861
- $this->add_group($id, $props, null, $update);
1862
- }
1863
- }
1864
-
1865
- /**
1866
- * Adds group to collection
1867
- * Groups are used to display related items in the UI
1868
- * @param string $id Unique name for group
1869
- * @param string $title Group title
1870
- * @param string $description Short description of group's purpose
1871
- * @param array $items (optional) ID's of existing items to add to group
1872
- * @return object Group object
1873
- */
1874
- function &add_group($id, $properties = array(), $items = array(), $update = false) {
1875
- //Create new group and set properties
1876
- $default = array (
1877
- 'title' => '',
1878
- 'description' => '',
1879
- 'priority' => 10
1880
- );
1881
- $p = ( is_array($properties) ) ? array_merge($default, $properties) : $default;
1882
- if ( !is_int($p['priority']) || $p['priority'] < 0 ) {
1883
- $p['priority'] = $default['priority'];
1884
- }
1885
- $id = trim($id);
1886
- //Retrieve or init group
1887
- if ( !!$update && $this->group_exists($id) ) {
1888
- $grp = $this->get_group($id);
1889
- $grp->title = $p['title'];
1890
- $grp->description = $p['description'];
1891
- $grp->priority = $p['priority'];
1892
- } else {
1893
- $this->groups[$id] =& $this->create_group($p['title'], $p['description'], $p['priority']);
1894
- }
1895
- //Add items to group (if supplied)
1896
- if ( !empty($items) && is_array($items) ) {
1897
- $this->add_to_group($id, $items);
1898
- }
1899
- return $this->groups[$id];
1900
- }
1901
-
1902
- /**
1903
- * Remove specified group from collection
1904
- * @param string $id Group ID to remove
1905
- */
1906
- function remove_group($id) {
1907
- $id = trim($id);
1908
- if ( $this->group_exists($id) ) {
1909
- unset($this->groups[$id]);
1910
- }
1911
- }
1912
-
1913
- /**
1914
- * Standardized method to create a new item group
1915
- * @param string $title Group title (used in meta boxes, etc.)
1916
- * @param string $description Short description of group's purpose
1917
- * @param int $priority (optional) Group priority (e.g. used to sort groups during output)
1918
- * @return object Group object
1919
- */
1920
- function &create_group($title = '', $description = '', $priority = 10) {
1921
- //Create new group object
1922
- $group = new stdClass();
1923
- /* Set group properties */
1924
-
1925
- //Set Title
1926
- $title = ( is_scalar($title) ) ? trim($title) : '';
1927
- $group->title = $title;
1928
- //Set Description
1929
- $description = ( is_scalar($description) ) ? trim($description) : '';
1930
- $group->description = $description;
1931
- //Priority
1932
- $group->priority = ( is_int($priority) ) ? $priority : 10;
1933
- //Create array to hold items
1934
- $group->items = array();
1935
- return $group;
1936
- }
1937
-
1938
- /**
1939
- * Checks if group exists in collection
1940
- * @param string $id Group name
1941
- * @return bool TRUE if group exists, FALSE otherwise
1942
- */
1943
- function group_exists($group) {
1944
- $ret = false;
1945
- if ( is_object($group) ) {
1946
- $ret = true;
1947
- } elseif ( is_string($group) && ($group = trim($group)) && strlen($group) > 0 ) {
1948
- $group = trim($group);
1949
- //Check if group exists
1950
- $ret = !is_null($this->get_member_value('groups', $group, null));
1951
- }
1952
- return $ret;
1953
- }
1954
-
1955
- /**
1956
- * Adds item to a group in the collection
1957
- * Group is created if it does not already exist
1958
- * @param string|array $group ID of group (or group parameters if new group) to add item to
1959
- * @param string|array $items Name or array of item(s) to add to group
1960
- */
1961
- function add_to_group($group, $items, $priority = 10) {
1962
- //Validate
1963
- if ( empty($items) || empty($group) || ( !is_string($group) && !is_array($group) ) ) {
1964
- return false;
1965
- }
1966
-
1967
- //Get group ID
1968
- if ( is_string($group) ) {
1969
- $group = array($group);
1970
- }
1971
- list($gid, $priority) = $group;
1972
- $gid = trim(sanitize_title_with_dashes($gid));
1973
- if ( empty($gid) ) {
1974
- return false;
1975
- }
1976
- //Item priority
1977
- if ( !is_int($priority) ) {
1978
- $priority = 10;
1979
- }
1980
-
1981
- //Prepare group
1982
- if ( !$this->group_exists($gid) ) {
1983
- //TODO Follow
1984
- call_user_func($this->m('add_group'), $gid, $group);
1985
- }
1986
- //Prepare items
1987
- if ( !is_array($items) ) {
1988
- $items = array($items);
1989
- }
1990
- //Add Items
1991
- foreach ( $items as $item ) {
1992
- //Skip if not in current collection
1993
- $itm_ref =& $this->get($item);
1994
- if ( !$itm_ref ) {
1995
- continue;
1996
- }
1997
- $itm_id = $itm_ref->get_id();
1998
- //Remove item from any other group it's in (items can only be in one group)
1999
- foreach ( $this->get_groups() as $group ) {
2000
- foreach ( $group->items as $tmp_pri => $tmp_items ) {
2001
- if ( isset($group->items[$tmp_pri][$itm_id]) ) {
2002
- unset($group->items[$tmp_pri][$itm_id]);
2003
- }
2004
- }
2005
- }
2006
- //Add reference to item in group
2007
- $items =& $this->get_group($gid)->items;
2008
- if ( !isset($items[$priority]) ) {
2009
- $items[$priority] = array();
2010
- }
2011
- $items[$priority][$itm_id] =& $itm_ref;
2012
- }
2013
- unset($itm_ref);
2014
- }
2015
-
2016
- /**
2017
- * Remove item from a group
2018
- * If no group is specified, then item is removed from all groups
2019
- * @param string|object $item Object or ID of item to remove from group
2020
- * @param string $group (optional) Group ID to remove item from
2021
- */
2022
- function remove_from_group($item, $group = '') {
2023
- //Get ID of item to remove or stop execution if item invalid
2024
- $item = $this->get($item);
2025
- $item = $item->get_id();
2026
- if ( !$item )
2027
- return false;
2028
-
2029
- //Remove item from group
2030
- if ( !empty($group) ) {
2031
- //Remove item from single group
2032
- if ( ($group =& $this->get_group($group)) && isset($group->items[$item]) ) {
2033
- unset($group->items[$item]);
2034
- }
2035
- } else {
2036
- //Remove item from all groups
2037
- foreach ( array_keys($this->groups) as $group ) {
2038
- if ( ($group =& $this->get_group($group)) && isset($group->items[$item]) ) {
2039
- unset($group->items[$item]);
2040
- }
2041
- }
2042
- }
2043
- }
2044
-
2045
- /**
2046
- * Retrieve specified group
2047
- * @param string $group ID of group to retrieve
2048
- * @return object Reference to specified group
2049
- */
2050
- function &get_group($group) {
2051
- if ( is_object($group) ) {
2052
- return $group;
2053
- }
2054
- if ( is_string($group) ) {
2055
- $group = trim($group);
2056
- }
2057
- //Create group if it doesn't already exist
2058
- if ( ! $this->group_exists($group) ) {
2059
- $this->add_group($group);
2060
- }
2061
- return $this->get_member_value('groups', $group);
2062
- }
2063
-
2064
- /**
2065
- * Retrieve a group's items
2066
- * @uses SLB_Field_Collection::get_group() to retrieve group object
2067
- * @param object|string $group Group object or group ID
2068
- * @return array Group's items
2069
- */
2070
- function &get_group_items($group) {
2071
- $group =& $this->get_group($group);
2072
- return $group->items;
2073
- }
2074
-
2075
- /**
2076
- * Retrieve all groups in collection
2077
- * @return array Reference to group objects
2078
- */
2079
- function &get_groups($opts = array()) {
2080
- $groups =& $this->get_member_value('groups');
2081
- if ( is_array($opts) && !empty($opts) ) {
2082
- extract($opts, EXTR_SKIP);
2083
- if ( !empty($groups) && !empty($sort) && is_string($sort) ) {
2084
- if ( property_exists(current($groups), $sort) ) {
2085
- //Sort groups by property
2086
- $sfunc = create_function('$a,$b', '$ap = $a->' . $sort . '; $bp = $b->' . $sort . '; if ( $ap == $bp ) return 0; return ( $ap > $bp ) ? 1 : -1;');
2087
- uasort($groups, $sfunc);
2088
- }
2089
- }
2090
- }
2091
- return $groups;
2092
- }
2093
-
2094
- /**
2095
- * Output groups
2096
- * @uses self::build_vars to determine groups to build
2097
- */
2098
- function build_groups() {
2099
- $this->util->do_action_ref_array('build_groups_pre', array(&$this));
2100
-
2101
- //Get groups to build
2102
- $groups = ( !empty($this->build_vars['groups']) ) ? $this->build_vars['groups'] : array_keys($this->get_groups(array('sort' => 'priority')));
2103
- //Check options
2104
- if ( is_callable($this->build_vars['build_groups']) ) {
2105
- //Pass groups to callback to build output
2106
- call_user_func_array($this->build_vars['build_groups'], array($this, $groups));
2107
- } elseif ( !!$this->build_vars['build_groups'] ) {
2108
- //Build groups
2109
- foreach ( $groups as $group ) {
2110
- $this->build_group($group);
2111
- }
2112
- }
2113
-
2114
- $this->util->do_action_ref_array('build_groups_post', array(&$this));
2115
- }
2116
-
2117
- /**
2118
- * Build group
2119
- */
2120
- function build_group($group) {
2121
- if ( !$this->group_exists($group) ) {
2122
- return false;
2123
- }
2124
- $group =& $this->get_group($group);
2125
- //Stop processing if group contains no items
2126
- if ( !count($this->get_items($group)) ) {
2127
- return false;
2128
- }
2129
-
2130
- //Pre action
2131
- $this->util->do_action_ref_array('build_group_pre', array(&$this, $group));
2132
-
2133
- //Build items
2134
- $this->build_items($group);
2135
-
2136
- //Post action
2137
- $this->util->do_action_ref_array('build_group_post', array(&$this, $group));
2138
- }
2139
-
2140
- /* Collection */
2141
-
2142
- /**
2143
- * Build entire collection of items
2144
- * Prints output
2145
- */
2146
- function build($build_vars = array()) {
2147
- //Parse vars
2148
- $this->parse_build_vars($build_vars);
2149
- $this->util->do_action_ref_array('build_init', array(&$this));
2150
- //Pre-build output
2151
- $this->util->do_action_ref_array('build_pre', array(&$this));
2152
- //Build groups
2153
- $this->build_groups();
2154
- //Post-build output
2155
- $this->util->do_action_ref_array('build_post', array(&$this));
2156
- }
2157
-
2158
- /**
2159
- * Parses build variables prior to use
2160
- * @uses this->reset_build_vars() to reset build variables for each request
2161
- * @param array $build_vars Variables to use for current request
2162
- */
2163
- function parse_build_vars($build_vars = array()) {
2164
- $this->reset_build_vars();
2165
- $this->build_vars = $this->util->apply_filters('parse_build_vars', wp_parse_args($build_vars, $this->build_vars), $this);
2166
- }
2167
-
2168
- /**
2169
- * Reset build variables to defaults
2170
- * Default Variables
2171
- * > groups - array - Names of groups to build
2172
- * > context - string - Context of current request
2173
- * > layout - string - Name of default layout to use
2174
- */
2175
- function reset_build_vars() {
2176
- $this->build_vars = wp_parse_args($this->build_vars, $this->build_vars_default);
2177
- }
2178
- }
2179
-
2180
  /**
2181
  * Collection of default system-wide fields
2182
  * @package Simple Lightbox
@@ -2202,9 +24,9 @@ class SLB_Fields extends SLB_Field_Collection {
2202
 
2203
  protected function _hooks() {
2204
  parent::_hooks();
2205
- //Init fields
2206
  add_action('init', $this->m('register_types'));
2207
- //Init placeholders
2208
  add_action('init', $this->m('register_placeholders'));
2209
  }
2210
 
@@ -2216,7 +38,7 @@ class SLB_Fields extends SLB_Field_Collection {
2216
  function register_types() {
2217
  /* Field Types */
2218
 
2219
- //Base
2220
  $base = new SLB_Field_Type('base');
2221
  $base->set_description(__('Default Element', 'simple-lightbox'));
2222
  $base->set_property('tag', 'span');
@@ -2227,7 +49,7 @@ class SLB_Fields extends SLB_Field_Collection {
2227
  $base->set_layout('display', '{data context="display"}');
2228
  $this->add($base);
2229
 
2230
- //Base closed
2231
  $base_closed = new SLB_Field_Type('base_closed');
2232
  $base_closed->set_parent('base');
2233
  $base_closed->set_description(__('Default Element (Closed Tag)', 'simple-lightbox'));
@@ -2236,7 +58,7 @@ class SLB_Fields extends SLB_Field_Collection {
2236
  $base_closed->set_layout('form', '{form_start ref_base="layout"}{data}{form_end ref_base="layout"}');
2237
  $this->add($base_closed);
2238
 
2239
- //Input
2240
  $input = new SLB_Field_Type('input', 'base');
2241
  $input->set_description(__('Default Input Element', 'simple-lightbox'));
2242
  $input->set_property('tag', 'input');
@@ -2244,7 +66,7 @@ class SLB_Fields extends SLB_Field_Collection {
2244
  $input->set_property('value', '{data}', 'attr');
2245
  $this->add($input);
2246
 
2247
- //Text input
2248
  $text = new SLB_Field_Type('text', 'input');
2249
  $text->set_description(__('Text Box', 'simple-lightbox'));
2250
  $text->set_property('size', 15, 'attr');
@@ -2252,7 +74,7 @@ class SLB_Fields extends SLB_Field_Collection {
2252
  $text->set_layout('form', '{label ref_base="layout"} {inherit}');
2253
  $this->add($text);
2254
 
2255
- //Checkbox
2256
  $cb = new SLB_Field_Type('checkbox', 'input');
2257
  $cb->set_property('type', 'checkbox');
2258
  $cb->set_property('value', null);
@@ -2260,28 +82,28 @@ class SLB_Fields extends SLB_Field_Collection {
2260
  $cb->set_layout('form', '{label ref_base="layout"} <{form_attr ref_base="layout"} />');
2261
  $this->add($cb);
2262
 
2263
- //Textarea
2264
  $ta = new SLB_Field_Type('textarea', 'base_closed');
2265
  $ta->set_property('tag', 'textarea');
2266
  $ta->set_property('cols', 40, 'attr');
2267
  $ta->set_property('rows', 3, 'attr');
2268
  $this->add($ta);
2269
 
2270
- //Rich Text
2271
  $rt = new SLB_Field_Type('richtext', 'textarea');
2272
  $rt->set_property('class', 'theEditor {inherit}');
2273
  $rt->set_layout('form', '<div class="rt_container">{inherit}</div>');
2274
  $rt->add_action('admin_print_footer_scripts', 'wp_tiny_mce', 25);
2275
  $this->add($rt);
2276
 
2277
- //Hidden
2278
  $hidden = new SLB_Field_Type('hidden');
2279
  $hidden->set_parent('input');
2280
  $hidden->set_description(__('Hidden Field', 'simple-lightbox'));
2281
  $hidden->set_property('type', 'hidden');
2282
  $this->add($hidden);
2283
 
2284
- //Select
2285
  $select = new SLB_Field_Type('select', 'base_closed');
2286
  $select->set_description(__('Select tag', 'simple-lightbox'));
2287
  $select->set_property('tag', 'select');
@@ -2293,24 +115,24 @@ class SLB_Fields extends SLB_Field_Collection {
2293
  $select->set_layout('option_data', '<{tag_option} value="{data_ext id="option_value"}" selected="selected">{data_ext id="option_text"}</{tag_option}>');
2294
  $this->add($select);
2295
 
2296
- //Span
2297
  $span = new SLB_Field_Type('span', 'base_closed');
2298
  $span->set_description(__('Inline wrapper', 'simple-lightbox'));
2299
  $span->set_property('tag', 'span');
2300
  $span->set_property('value', 'Hello there!');
2301
  $this->add($span);
2302
 
2303
- //Enable plugins to modify (add, remove, etc.) field types
2304
- do_action_ref_array($this->add_prefix('register_fields'), array(&$this));
2305
 
2306
- //Signal completion of field registration
2307
- do_action_ref_array($this->add_prefix('fields_registered'), array(&$this));
2308
  }
2309
 
2310
  /* Placeholder handlers */
2311
 
2312
  function register_placeholders() {
2313
- //Default placeholder handlers
2314
  $this->register_placeholder('all', $this->m('process_placeholder_default'), 11);
2315
  $this->register_placeholder('field_id', $this->m('process_placeholder_id'));
2316
  $this->register_placeholder('field_name', $this->m('process_placeholder_name'));
@@ -2320,11 +142,11 @@ class SLB_Fields extends SLB_Field_Collection {
2320
  $this->register_placeholder('label', $this->m('process_placeholder_label'));
2321
  $this->register_placeholder('checked', $this->m('process_placeholder_checked'));
2322
 
2323
- //Allow other code to register placeholders
2324
- do_action_ref_array($this->add_prefix('register_field_placeholders'), array(&$this));
2325
 
2326
- //Signal completion of field placeholder registration
2327
- do_action_ref_array($this->add_prefix('field_placeholders_registered'), array(&$this));
2328
  }
2329
 
2330
  /**
@@ -2359,13 +181,13 @@ class SLB_Fields extends SLB_Field_Collection {
2359
  * @return string Value to use in place of current placeholder
2360
  */
2361
  function process_placeholder_default($output, $item, $placeholder, $layout, $data) {
2362
- //Validate parameters before processing
2363
- if ( empty($output) && is_a($item, 'SLB_Field_Type') && is_array($placeholder) ) {
2364
- //Build path to replacement data
2365
  $output = $item->get_member_value($placeholder);
2366
 
2367
- //Check if value is group (properties, etc.)
2368
- //All groups must have additional attributes (beyond reserved attributes) that define how items in group are used
2369
  if (is_array($output)
2370
  && !empty($placeholder['attributes'])
2371
  && is_array($placeholder['attributes'])
@@ -2374,12 +196,12 @@ class SLB_Fields extends SLB_Field_Collection {
2374
  ) {
2375
  /* Targeted property is an array, but the placeholder contains additional options on how property is to be used */
2376
 
2377
- //Find items matching criteria in $output
2378
- //Check for group criteria
2379
  if ( 'properties' == $placeholder['tag'] && ($prop_group = $item->get_group($placeholder['attributes']['group'])) && !empty($prop_group) ) {
2380
  /* Process group */
2381
  $group_out = array();
2382
- //Iterate through properties in group and build string
2383
  foreach ( array_keys($prop_group) as $prop_key ) {
2384
  $prop_val = $item->get_property($prop_key);
2385
  if ( !is_null($prop_val) )
@@ -2387,11 +209,11 @@ class SLB_Fields extends SLB_Field_Collection {
2387
  }
2388
  $output = implode(' ', $group_out);
2389
  }
2390
- } elseif ( is_object($output) && is_a($output, $item->base_class) ) {
2391
  /* Targeted property is actually a nested item */
2392
- //Set caller to current item
2393
  $output->set_caller($item);
2394
- //Build layout for nested element
2395
  $output = $output->build_layout($layout);
2396
  }
2397
  }
@@ -2405,7 +227,7 @@ class SLB_Fields extends SLB_Field_Collection {
2405
  * @return string Placeholder output
2406
  */
2407
  function process_placeholder_id($output, $item, $placeholder, $layout, $data) {
2408
- //Get attributes
2409
  $args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_id'));
2410
  return $item->get_id($args);
2411
  }
@@ -2417,7 +239,7 @@ class SLB_Fields extends SLB_Field_Collection {
2417
  * @return string Placeholder output
2418
  */
2419
  function process_placeholder_name($output, $item, $placeholder, $layout, $data) {
2420
- //Get attributes
2421
  $args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_name'));
2422
  return $item->get_id($args);
2423
  }
@@ -2428,9 +250,9 @@ class SLB_Fields extends SLB_Field_Collection {
2428
  * @return string Field label
2429
  */
2430
  function process_placeholder_label($output, $item, $placeholder, $layout, $data) {
2431
- //Check if item has label property (e.g. sub-elements)
2432
  $out = $item->get_property('label');
2433
- //If property not set, use item title
2434
  if ( empty($out) )
2435
  $out = $item->get_title();
2436
  return $out;
@@ -2446,21 +268,21 @@ class SLB_Fields extends SLB_Field_Collection {
2446
  'context' => '',
2447
  );
2448
  $opts = wp_parse_args($placeholder['attributes'], $attr_default);
2449
- //Save context to separate variable
2450
  $context = $opts['context'];
2451
  unset($opts['context']);
2452
- //Get data
2453
  $out = $item->get_data($opts);
2454
  if ( !is_null($out) ) {
2455
- //Get specific member in value (e.g. value from a specific item element)
2456
  if ( isset($opts['element']) && is_array($out) && ( $el = $opts['element'] ) && isset($out[$el]) )
2457
  $out = $out[$el];
2458
  }
2459
 
2460
- //Format data based on context
2461
  $out = $item->preserve_special_chars($out, $context);
2462
  $out = $item->format($out, $context);
2463
- //Return data
2464
  return $out;
2465
  }
2466
 
@@ -2491,7 +313,7 @@ class SLB_Fields extends SLB_Field_Collection {
2491
  * @return string Placeholder output
2492
  */
2493
  function process_placeholder_loop($output, $item, $placeholder, $layout, $data) {
2494
- //Setup loop options
2495
  $attr_defaults = array (
2496
  'layout' => '',
2497
  'layout_data' => null,
@@ -2500,33 +322,33 @@ class SLB_Fields extends SLB_Field_Collection {
2500
  $attr = wp_parse_args($placeholder['attributes'], $attr_defaults);
2501
  if ( is_null($attr['layout_data']) )
2502
  $attr['layout_data'] =& $attr['layout'];
2503
- //Get data for loop
2504
  $path = explode('.', $attr['data']);
2505
  $loop_data = $item->get_member_value($path);
2506
 
2507
- //Check if data is callback
2508
  if ( is_callable($loop_data) )
2509
  $loop_data = call_user_func($loop_data);
2510
 
2511
- //Get item data
2512
  $data = $item->get_data();
2513
 
2514
- //Iterate over data and build output
2515
  $out = array();
2516
  if ( is_array($loop_data) && !empty($loop_data) ) {
2517
  foreach ( $loop_data as $value => $label ) {
2518
- //Load appropriate layout based on item value
2519
  $layout = ( ($data === 0 && $value === $data) xor $data == $value ) ? $attr['layout_data'] : $attr['layout'];
2520
- //Stop processing if no valid layout is returned
2521
  if ( empty($layout) )
2522
  continue;
2523
- //Prep extended item data
2524
  $data_ext = array('option_value' => $value, 'option_text' => $label);
2525
  $out[] = $item->build_layout($layout, $data_ext);
2526
  }
2527
  }
2528
 
2529
- //Return output
2530
  return implode($out);
2531
  }
2532
 
@@ -2559,32 +381,32 @@ class SLB_Fields extends SLB_Field_Collection {
2559
  'elements' => 'has_elements'
2560
  );
2561
 
2562
- //Stop execution if group does not exist
2563
  if ( $this->group_exists($group) && $group =& $this->get_group($group) ) {
2564
  $group_items = ( count($group->items) > 1 ) ? $classnames->multi : $classnames->single . ( ( ( $fs = array_keys($group->items) ) && ( $f =& $group->items[$fs[0]] ) && ( $els = $f->get_member_value('elements', '', null) ) && !empty($els) ) ? '_' . $classnames->elements : '' );
2565
  $classname = array($this->add_prefix('attributes_wrap'), $group_items);
2566
- $out[] = '<div class="' . implode(' ', $classname) . '">'; //Wrap all items in group
2567
 
2568
- //Build layout for each item in group
2569
  foreach ( array_keys($group->items) as $item_id ) {
2570
  $item =& $group->items[$item_id];
2571
  $item->set_caller($this);
2572
- //Start item output
2573
  $id = $this->add_prefix('field_' . $item->get_id());
2574
  $out[] = '<div id="' . $id . '_wrap" class=' . $this->add_prefix('attribute_wrap') . '>';
2575
- //Build item layout
2576
  $out[] = $item->build_layout();
2577
- //end item output
2578
  $out[] = '</div>';
2579
  $item->clear_caller();
2580
  }
2581
- $out[] = '</div>'; //Close items container
2582
- //Add description if exists
2583
  if ( !empty($group->description) )
2584
  $out[] = '<p class=' . $this->add_prefix('group_description') . '>' . $group->description . '</p>';
2585
  }
2586
 
2587
- //Return group output
2588
  return implode($out);
2589
  }
2590
  }
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  /**
3
  * Collection of default system-wide fields
4
  * @package Simple Lightbox
24
 
25
  protected function _hooks() {
26
  parent::_hooks();
27
+ // Init fields
28
  add_action('init', $this->m('register_types'));
29
+ // Init placeholders
30
  add_action('init', $this->m('register_placeholders'));
31
  }
32
 
38
  function register_types() {
39
  /* Field Types */
40
 
41
+ // Base
42
  $base = new SLB_Field_Type('base');
43
  $base->set_description(__('Default Element', 'simple-lightbox'));
44
  $base->set_property('tag', 'span');
49
  $base->set_layout('display', '{data context="display"}');
50
  $this->add($base);
51
 
52
+ // Base closed
53
  $base_closed = new SLB_Field_Type('base_closed');
54
  $base_closed->set_parent('base');
55
  $base_closed->set_description(__('Default Element (Closed Tag)', 'simple-lightbox'));
58
  $base_closed->set_layout('form', '{form_start ref_base="layout"}{data}{form_end ref_base="layout"}');
59
  $this->add($base_closed);
60
 
61
+ // Input
62
  $input = new SLB_Field_Type('input', 'base');
63
  $input->set_description(__('Default Input Element', 'simple-lightbox'));
64
  $input->set_property('tag', 'input');
66
  $input->set_property('value', '{data}', 'attr');
67
  $this->add($input);
68
 
69
+ // Text input
70
  $text = new SLB_Field_Type('text', 'input');
71
  $text->set_description(__('Text Box', 'simple-lightbox'));
72
  $text->set_property('size', 15, 'attr');
74
  $text->set_layout('form', '{label ref_base="layout"} {inherit}');
75
  $this->add($text);
76
 
77
+ // Checkbox
78
  $cb = new SLB_Field_Type('checkbox', 'input');
79
  $cb->set_property('type', 'checkbox');
80
  $cb->set_property('value', null);
82
  $cb->set_layout('form', '{label ref_base="layout"} <{form_attr ref_base="layout"} />');
83
  $this->add($cb);
84
 
85
+ // Textarea
86
  $ta = new SLB_Field_Type('textarea', 'base_closed');
87
  $ta->set_property('tag', 'textarea');
88
  $ta->set_property('cols', 40, 'attr');
89
  $ta->set_property('rows', 3, 'attr');
90
  $this->add($ta);
91
 
92
+ // Rich Text
93
  $rt = new SLB_Field_Type('richtext', 'textarea');
94
  $rt->set_property('class', 'theEditor {inherit}');
95
  $rt->set_layout('form', '<div class="rt_container">{inherit}</div>');
96
  $rt->add_action('admin_print_footer_scripts', 'wp_tiny_mce', 25);
97
  $this->add($rt);
98
 
99
+ // Hidden
100
  $hidden = new SLB_Field_Type('hidden');
101
  $hidden->set_parent('input');
102
  $hidden->set_description(__('Hidden Field', 'simple-lightbox'));
103
  $hidden->set_property('type', 'hidden');
104
  $this->add($hidden);
105
 
106
+ // Select
107
  $select = new SLB_Field_Type('select', 'base_closed');
108
  $select->set_description(__('Select tag', 'simple-lightbox'));
109
  $select->set_property('tag', 'select');
115
  $select->set_layout('option_data', '<{tag_option} value="{data_ext id="option_value"}" selected="selected">{data_ext id="option_text"}</{tag_option}>');
116
  $this->add($select);
117
 
118
+ // Span
119
  $span = new SLB_Field_Type('span', 'base_closed');
120
  $span->set_description(__('Inline wrapper', 'simple-lightbox'));
121
  $span->set_property('tag', 'span');
122
  $span->set_property('value', 'Hello there!');
123
  $this->add($span);
124
 
125
+ // Enable plugins to modify (add, remove, etc.) field types
126
+ $this->util->do_action_ref_array('register_fields', array($this), false);
127
 
128
+ // Signal completion of field registration
129
+ $this->util->do_action_ref_array('fields_registered', array($this), false);
130
  }
131
 
132
  /* Placeholder handlers */
133
 
134
  function register_placeholders() {
135
+ // Default placeholder handlers
136
  $this->register_placeholder('all', $this->m('process_placeholder_default'), 11);
137
  $this->register_placeholder('field_id', $this->m('process_placeholder_id'));
138
  $this->register_placeholder('field_name', $this->m('process_placeholder_name'));
142
  $this->register_placeholder('label', $this->m('process_placeholder_label'));
143
  $this->register_placeholder('checked', $this->m('process_placeholder_checked'));
144
 
145
+ // Allow other code to register placeholders
146
+ $this->util->do_action_ref_array('register_field_placeholders', array($this), false);
147
 
148
+ // Signal completion of field placeholder registration
149
+ $this->util->do_action_ref_array('field_placeholders_registered', array($this), false);
150
  }
151
 
152
  /**
181
  * @return string Value to use in place of current placeholder
182
  */
183
  function process_placeholder_default($output, $item, $placeholder, $layout, $data) {
184
+ // Validate parameters before processing
185
+ if ( empty($output) && ($item instanceof SLB_Field_Type) && is_array($placeholder) ) {
186
+ // Build path to replacement data
187
  $output = $item->get_member_value($placeholder);
188
 
189
+ // Check if value is group (properties, etc.)
190
+ // All groups must have additional attributes (beyond reserved attributes) that define how items in group are used
191
  if (is_array($output)
192
  && !empty($placeholder['attributes'])
193
  && is_array($placeholder['attributes'])
196
  ) {
197
  /* Targeted property is an array, but the placeholder contains additional options on how property is to be used */
198
 
199
+ // Find items matching criteria in $output
200
+ // Check for group criteria
201
  if ( 'properties' == $placeholder['tag'] && ($prop_group = $item->get_group($placeholder['attributes']['group'])) && !empty($prop_group) ) {
202
  /* Process group */
203
  $group_out = array();
204
+ // Iterate through properties in group and build string
205
  foreach ( array_keys($prop_group) as $prop_key ) {
206
  $prop_val = $item->get_property($prop_key);
207
  if ( !is_null($prop_val) )
209
  }
210
  $output = implode(' ', $group_out);
211
  }
212
+ } elseif ( is_object($output) && ($output instanceof $item->base_class) ) {
213
  /* Targeted property is actually a nested item */
214
+ // Set caller to current item
215
  $output->set_caller($item);
216
+ // Build layout for nested element
217
  $output = $output->build_layout($layout);
218
  }
219
  }
227
  * @return string Placeholder output
228
  */
229
  function process_placeholder_id($output, $item, $placeholder, $layout, $data) {
230
+ // Get attributes
231
  $args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_id'));
232
  return $item->get_id($args);
233
  }
239
  * @return string Placeholder output
240
  */
241
  function process_placeholder_name($output, $item, $placeholder, $layout, $data) {
242
+ // Get attributes
243
  $args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_name'));
244
  return $item->get_id($args);
245
  }
250
  * @return string Field label
251
  */
252
  function process_placeholder_label($output, $item, $placeholder, $layout, $data) {
253
+ // Check if item has label property (e.g. sub-elements)
254
  $out = $item->get_property('label');
255
+ // If property not set, use item title
256
  if ( empty($out) )
257
  $out = $item->get_title();
258
  return $out;
268
  'context' => '',
269
  );
270
  $opts = wp_parse_args($placeholder['attributes'], $attr_default);
271
+ // Save context to separate variable
272
  $context = $opts['context'];
273
  unset($opts['context']);
274
+ // Get data
275
  $out = $item->get_data($opts);
276
  if ( !is_null($out) ) {
277
+ // Get specific member in value (e.g. value from a specific item element)
278
  if ( isset($opts['element']) && is_array($out) && ( $el = $opts['element'] ) && isset($out[$el]) )
279
  $out = $out[$el];
280
  }
281
 
282
+ // Format data based on context
283
  $out = $item->preserve_special_chars($out, $context);
284
  $out = $item->format($out, $context);
285
+ // Return data
286
  return $out;
287
  }
288
 
313
  * @return string Placeholder output
314
  */
315
  function process_placeholder_loop($output, $item, $placeholder, $layout, $data) {
316
+ // Setup loop options
317
  $attr_defaults = array (
318
  'layout' => '',
319
  'layout_data' => null,
322
  $attr = wp_parse_args($placeholder['attributes'], $attr_defaults);
323
  if ( is_null($attr['layout_data']) )
324
  $attr['layout_data'] =& $attr['layout'];
325
+ // Get data for loop
326
  $path = explode('.', $attr['data']);
327
  $loop_data = $item->get_member_value($path);
328
 
329
+ // Check if data is callback
330
  if ( is_callable($loop_data) )
331
  $loop_data = call_user_func($loop_data);
332
 
333
+ // Get item data
334
  $data = $item->get_data();
335
 
336
+ // Iterate over data and build output
337
  $out = array();
338
  if ( is_array($loop_data) && !empty($loop_data) ) {
339
  foreach ( $loop_data as $value => $label ) {
340
+ // Load appropriate layout based on item value
341
  $layout = ( ($data === 0 && $value === $data) xor $data == $value ) ? $attr['layout_data'] : $attr['layout'];
342
+ // Stop processing if no valid layout is returned
343
  if ( empty($layout) )
344
  continue;
345
+ // Prep extended item data
346
  $data_ext = array('option_value' => $value, 'option_text' => $label);
347
  $out[] = $item->build_layout($layout, $data_ext);
348
  }
349
  }
350
 
351
+ // Return output
352
  return implode($out);
353
  }
354
 
381
  'elements' => 'has_elements'
382
  );
383
 
384
+ // Stop execution if group does not exist
385
  if ( $this->group_exists($group) && $group =& $this->get_group($group) ) {
386
  $group_items = ( count($group->items) > 1 ) ? $classnames->multi : $classnames->single . ( ( ( $fs = array_keys($group->items) ) && ( $f =& $group->items[$fs[0]] ) && ( $els = $f->get_member_value('elements', '', null) ) && !empty($els) ) ? '_' . $classnames->elements : '' );
387
  $classname = array($this->add_prefix('attributes_wrap'), $group_items);
388
+ $out[] = '<div class="' . implode(' ', $classname) . '">'; // Wrap all items in group
389
 
390
+ // Build layout for each item in group
391
  foreach ( array_keys($group->items) as $item_id ) {
392
  $item =& $group->items[$item_id];
393
  $item->set_caller($this);
394
+ // Start item output
395
  $id = $this->add_prefix('field_' . $item->get_id());
396
  $out[] = '<div id="' . $id . '_wrap" class=' . $this->add_prefix('attribute_wrap') . '>';
397
+ // Build item layout
398
  $out[] = $item->build_layout();
399
+ // end item output
400
  $out[] = '</div>';
401
  $item->clear_caller();
402
  }
403
+ $out[] = '</div>'; // Close items container
404
+ // Add description if exists
405
  if ( !empty($group->description) )
406
  $out[] = '<p class=' . $this->add_prefix('group_description') . '>' . $group->description . '</p>';
407
  }
408
 
409
+ // Return group output
410
  return implode($out);
411
  }
412
  }
includes/class.option.php ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Option object
4
+ * @package Simple Lightbox
5
+ * @subpackage Options
6
+ * @author Archetyped
7
+ */
8
+ class SLB_Option extends SLB_Field {
9
+
10
+ /* Properties */
11
+
12
+ public $hook_prefix = 'option';
13
+
14
+ /**
15
+ * Determines whether option will be sent to client
16
+ * @var bool
17
+ */
18
+ var $in_client = false;
19
+
20
+ /**
21
+ * Child mapping
22
+ * @see SLB_Field_Base::map
23
+ * @var array
24
+ */
25
+ var $map = array (
26
+ 'default' => 'data',
27
+ 'attr' => 'properties'
28
+ );
29
+
30
+ var $property_priority = array ('id', 'data', 'parent');
31
+
32
+ /* Init */
33
+
34
+ /**
35
+ * @see SLB_Field::__construct()
36
+ * @uses parent::__construct() to initialize instance
37
+ * @param $id
38
+ * @param $title
39
+ * @param $default
40
+ */
41
+ function __construct($id, $title = '', $default = '') {
42
+ // Normalize properties
43
+ $args = func_get_args();
44
+ $defaults = array ('title' => '', 'default' => '');
45
+ $props = $this->make_properties($args, $defaults);
46
+ // Validate
47
+ if ( is_scalar($id) )
48
+ $props['id'] = $id;
49
+ if ( !is_string($props['title']) )
50
+ $props['title'] = '';
51
+ // Send to parent constructor
52
+ parent::__construct($props);
53
+ }
54
+
55
+ /* Getters/Setters */
56
+
57
+ /**
58
+ * Retrieve default value for option
59
+ * @return mixed Default option value
60
+ */
61
+ function get_default($context = '') {
62
+ return $this->get_data($context, false);
63
+ }
64
+
65
+ /**
66
+ * Sets parent based on default value
67
+ */
68
+ function set_parent($parent = null) {
69
+ $p = $this->get_parent();
70
+ if ( empty($parent) && empty($p) ) {
71
+ $parent = 'text';
72
+ $d = $this->get_default();
73
+ if ( is_bool($d) )
74
+ $parent = 'checkbox';
75
+ $parent = 'option_' . $parent;
76
+ } elseif ( !empty($p) && !is_object($p) ) {
77
+ $parent =& $p;
78
+ }
79
+ parent::set_parent($parent);
80
+ }
81
+
82
+ /**
83
+ * Set in_client property
84
+ * @uses this::in_client
85
+ * @param bool Whether or not option should be included in client output (Default: false)
86
+ * @return void
87
+ */
88
+ function set_in_client($in_client = false) {
89
+ $this->in_client = !!$in_client;
90
+ }
91
+
92
+ /**
93
+ * Determines whether option should be included in client output
94
+ * @uses this::in_client
95
+ * @return bool TRUE if option is included in client output
96
+ */
97
+ function get_in_client() {
98
+ return $this->in_client;
99
+ }
100
+
101
+ /* Formatting */
102
+
103
+ /**
104
+ * Format data as string for browser output
105
+ * @see SLB_Field_Base::format()
106
+ * @param mixed $value Data to format
107
+ * @param string $context (optional) Current context
108
+ * @return string Formatted value
109
+ */
110
+ function format_display($value, $context = '') {
111
+ if ( !is_string($value) ) {
112
+ if ( is_bool($value) ) {
113
+ $value = ( $value ) ? __('Enabled', 'simple-lightbox') : __('Disabled', 'simple-lightbox');
114
+ }
115
+ elseif ( is_null($value) )
116
+ $value = '';
117
+ else
118
+ $value = strval($value);
119
+ } elseif ( empty($value) ) {
120
+ $value = 'empty';
121
+ }
122
+ return htmlentities($value);
123
+ }
124
+
125
+ /**
126
+ * Format data using same format as default value
127
+ * @see SLB_Field_Base::format()
128
+ * @param mixed $value Data to format
129
+ * @param string $context (optional) Current context
130
+ * @return mixed Formatted option value
131
+ */
132
+ function format_default($value, $context = '') {
133
+ // Get default value
134
+ $d = $this->get_default();
135
+ if ( empty($d) )
136
+ return $value;
137
+ if ( is_bool($d) )
138
+ $value = $this->format_bool($value);
139
+ elseif ( is_string($d) )
140
+ $value = $this->format_string($value);
141
+ return $value;
142
+ }
143
+
144
+ /**
145
+ * Format data as boolean (true/false)
146
+ * @see SLB_Field_Base::format()
147
+ * @param mixed $value Data to format
148
+ * @param string $context (optional) Current context
149
+ * @return bool Option value
150
+ */
151
+ function format_bool($value, $context = '') {
152
+ if ( !is_bool($value) )
153
+ $value = !!$value;
154
+ return $value;
155
+ }
156
+
157
+ /**
158
+ * Format data as string
159
+ * @see SLB_Field_Base::format()
160
+ * @param mixed $value Data to format
161
+ * @param string $context (optional) Current context
162
+ * @return string Option string value
163
+ */
164
+ function format_string($value, $context = '') {
165
+ if ( is_bool($value) ) {
166
+ $value = ( $value ) ? 'true' : 'false';
167
+ }
168
+ elseif ( is_object($value) ) {
169
+ $value = get_class($value);
170
+ }
171
+ elseif ( is_array($value) ) {
172
+ $value = implode(' ', $value);
173
+ }
174
+ else {
175
+ $value = strval($value);
176
+ }
177
+ return $value;
178
+ }
179
+ }
includes/class.options.php CHANGED
@@ -1,184 +1,4 @@
1
  <?php
2
-
3
- /**
4
- * Option object
5
- * @package Simple Lightbox
6
- * @subpackage Options
7
- * @author Archetyped
8
- */
9
- class SLB_Option extends SLB_Field {
10
-
11
- /* Properties */
12
-
13
- public $hook_prefix = 'option';
14
-
15
- /**
16
- * Determines whether option will be sent to client
17
- * @var bool
18
- */
19
- var $in_client = false;
20
-
21
- /**
22
- * Child mapping
23
- * @see SLB_Field_Base::map
24
- * @var array
25
- */
26
- var $map = array (
27
- 'default' => 'data',
28
- 'attr' => 'properties'
29
- );
30
-
31
- var $property_priority = array ('id', 'data', 'parent');
32
-
33
- /* Init */
34
-
35
- /**
36
- * @see SLB_Field::__construct()
37
- * @uses parent::__construct() to initialize instance
38
- * @param $id
39
- * @param $title
40
- * @param $default
41
- */
42
- function __construct($id, $title = '', $default = '') {
43
- //Normalize properties
44
- $args = func_get_args();
45
- $defaults = array ('title' => '', 'default' => '');
46
- $props = $this->make_properties($args, $defaults);
47
- //Validate
48
- if ( is_scalar($id) )
49
- $props['id'] = $id;
50
- if ( !is_string($props['title']) )
51
- $props['title'] = '';
52
- //Send to parent constructor
53
- parent::__construct($props);
54
- }
55
-
56
- /* Getters/Setters */
57
-
58
- /**
59
- * Retrieve default value for option
60
- * @return mixed Default option value
61
- */
62
- function get_default($context = '') {
63
- return $this->get_data($context, false);
64
- }
65
-
66
- /**
67
- * Sets parent based on default value
68
- */
69
- function set_parent($parent = null) {
70
- $p =& $this->get_parent();
71
- if ( empty($parent) && empty($p) ) {
72
- $parent = 'text';
73
- $d = $this->get_default();
74
- if ( is_bool($d) )
75
- $parent = 'checkbox';
76
- $parent = 'option_' . $parent;
77
- } elseif ( !empty($p) && !is_object($p) ) {
78
- $parent =& $p;
79
- }
80
- parent::set_parent($parent);
81
- }
82
-
83
- /**
84
- * Set in_client property
85
- * @uses this::in_client
86
- * @param bool Whether or not option should be included in client output (Default: false)
87
- * @return void
88
- */
89
- function set_in_client($in_client = false) {
90
- $this->in_client = !!$in_client;
91
- }
92
-
93
- /**
94
- * Determines whether option should be included in client output
95
- * @uses this::in_client
96
- * @return bool TRUE if option is included in client output
97
- */
98
- function get_in_client() {
99
- return $this->in_client;
100
- }
101
-
102
- /* Formatting */
103
-
104
- /**
105
- * Format data as string for browser output
106
- * @see SLB_Field_Base::format()
107
- * @param mixed $value Data to format
108
- * @param string $context (optional) Current context
109
- * @return string Formatted value
110
- */
111
- function format_display($value, $context = '') {
112
- if ( !is_string($value) ) {
113
- if ( is_bool($value) ) {
114
- $value = ( $value ) ? __('Enabled', 'simple-lightbox') : __('Disabled', 'simple-lightbox');
115
- }
116
- elseif ( is_null($value) )
117
- $value = '';
118
- else
119
- $value = strval($value);
120
- } elseif ( empty($value) ) {
121
- $value = 'empty';
122
- }
123
- return htmlentities($value);
124
- }
125
-
126
- /**
127
- * Format data using same format as default value
128
- * @see SLB_Field_Base::format()
129
- * @param mixed $value Data to format
130
- * @param string $context (optional) Current context
131
- * @return mixed Formatted option value
132
- */
133
- function format_default($value, $context = '') {
134
- //Get default value
135
- $d = $this->get_default();
136
- if ( empty($d) )
137
- return $value;
138
- if ( is_bool($d) )
139
- $value = $this->format_bool($value);
140
- elseif ( is_string($d) )
141
- $value = $this->format_string($value);
142
- return $value;
143
- }
144
-
145
- /**
146
- * Format data as boolean (true/false)
147
- * @see SLB_Field_Base::format()
148
- * @param mixed $value Data to format
149
- * @param string $context (optional) Current context
150
- * @return bool Option value
151
- */
152
- function format_bool($value, $context = '') {
153
- if ( !is_bool($value) )
154
- $value = !!$value;
155
- return $value;
156
- }
157
-
158
- /**
159
- * Format data as string
160
- * @see SLB_Field_Base::format()
161
- * @param mixed $value Data to format
162
- * @param string $context (optional) Current context
163
- * @return string Option string value
164
- */
165
- function format_string($value, $context = '') {
166
- if ( is_bool($value) ) {
167
- $value = ( $value ) ? 'true' : 'false';
168
- }
169
- elseif ( is_object($value) ) {
170
- $value = get_class($value);
171
- }
172
- elseif ( is_array($value) ) {
173
- $value = implode(' ', $value);
174
- }
175
- else {
176
- $value = strval($value);
177
- }
178
- return $value;
179
- }
180
- }
181
-
182
  /**
183
  * Options collection
184
  * @package Simple Lightbox
@@ -218,9 +38,9 @@ class SLB_Options extends SLB_Field_Collection {
218
  /* Init */
219
 
220
  function __construct($id = '', $props = array()) {
221
- //Validate arguments
222
  $args = func_get_args();
223
- //Set default ID
224
  if ( !$this->validate_id($id) ) {
225
  $id = 'options';
226
  }
@@ -232,12 +52,15 @@ class SLB_Options extends SLB_Field_Collection {
232
 
233
  protected function _hooks() {
234
  parent::_hooks();
235
- //Register fields
236
- add_action($this->add_prefix('register_fields'), $this->m('register_fields'));
237
- //Set option parents
238
- add_action($this->add_prefix('fields_registered'), $this->m('set_parents'));
239
- //Building
240
  $this->util->add_action('build_init', $this->m('build_init'));
 
 
 
241
  }
242
 
243
  /* Legacy/Migration */
@@ -253,21 +76,21 @@ class SLB_Options extends SLB_Field_Collection {
253
  if ( !$this->version_checked ) {
254
  $this->version_checked = true;
255
  $version_changed = false;
256
- //Get version from DB
257
  $vo = $this->get_version();
258
- //Get current version
259
  $vn = $this->util->get_plugin_version();
260
- //Compare versions
261
  if ( $vo != $vn ) {
262
- //Update saved version
263
  $this->set_version($vn);
264
- //Migrate old version to new version
265
  if ( strcasecmp($vo, $vn) < 0 ) {
266
- //Force full migration
267
  $version_changed = true;
268
  }
269
  }
270
- //Migrate
271
  $this->migrate($version_changed);
272
  }
273
 
@@ -306,30 +129,30 @@ class SLB_Options extends SLB_Field_Collection {
306
  if ( !$full && $this->items_migrated )
307
  return false;
308
 
309
- //Legacy options
310
  $d = null;
311
  $this->load_data();
312
 
313
- $items =& $this->get_items();
314
 
315
- //Migrate separate options to unified option
316
  if ( $full ) {
317
  foreach ( $items as $opt => $props ) {
318
  $oid = $this->add_prefix($opt);
319
  $o = get_option($oid, $d);
320
  if ( $o !== $d ) {
321
- //Migrate value to data array
322
  $this->set_data($opt, $o, false);
323
- //Delete legacy option
324
  delete_option($oid);
325
  }
326
  }
327
  }
328
 
329
- //Migrate legacy items
330
  if ( is_array($this->properties_init) && isset($this->properties_init['legacy']) && is_array($this->properties_init['legacy']) ) {
331
  $l =& $this->properties_init['legacy'];
332
- //Normalize legacy map
333
  foreach ( $l as $opt => $dest ) {
334
  if ( !is_array($dest) ) {
335
  if ( is_string($dest) )
@@ -344,37 +167,37 @@ class SLB_Options extends SLB_Field_Collection {
344
  foreach ( $l as $opt => $dest ) {
345
  $oid = $this->add_prefix($opt);
346
  $o = get_option($oid, $d);
347
- //Only migrate valid values
348
  if ( $o !== $d ) {
349
- //Process destinations
350
  foreach ( $dest as $id ) {
351
  $this->set_data($id, $o, false, true);
352
  }
353
  }
354
- //Remove legacy option
355
  delete_option($oid);
356
  }
357
  }
358
 
359
  /* Simple Migration (Internal options only) */
360
 
361
- //Get existing items that are also legacy items
362
  $opts = array_intersect_key($this->get_data(), $l);
363
  foreach ( $opts as $opt => $val ) {
364
  $d = $this->get_data($opt);
365
- //Migrate data from old option to new option
366
  $dest = $l[$opt];
367
- //Validate new options to send data to
368
  foreach ( $dest as $id ) {
369
  $this->set_data($id, $d, false, true);
370
  }
371
- //Remove legacy option
372
  $this->remove($opt, false);
373
  }
374
  }
375
- //Save changes
376
  $this->save();
377
- //Set flag
378
  $this->items_migrated = true;
379
  }
380
 
@@ -408,33 +231,41 @@ class SLB_Options extends SLB_Field_Collection {
408
  * @param SLB_Fields $fields Reference to global fields object
409
  * @return void
410
  */
411
- function register_fields(&$fields) {
412
- //Layouts
413
  $o = $this->get_field_elements();
 
414
 
415
- $form = $o->layout->opt_pre . $o->layout->label_ref . $o->layout->field_pre . $o->layout->form . $o->layout->field_post . $o->layout->opt_post;
 
 
 
 
 
 
 
416
 
417
- //Text input
418
  $otxt = new SLB_Field_Type('option_text', 'text');
419
  $otxt->set_property('class', '{inherit} code');
420
  $otxt->set_property('size', null);
421
  $otxt->set_property('value', '{data context="form"}');
422
- $otxt->set_layout('label', $o->layout->label);
423
  $otxt->set_layout('form', $form);
424
  $fields->add($otxt);
425
 
426
- //Checkbox
427
  $ocb = new SLB_Field_Type('option_checkbox', 'checkbox');
428
- $ocb->set_layout('label', $o->layout->label);
429
  $ocb->set_layout('form', $form);
430
  $fields->add($ocb);
431
 
432
- //Select
433
  $othm = new SLB_Field_Type('option_select', 'select');
434
- $othm->set_layout('label', $o->layout->label);
435
- $othm->set_layout('form_start', $o->layout->field_pre . '{inherit}');
436
- $othm->set_layout('form_end', '{inherit}' . $o->layout->field_post);
437
- $othm->set_layout('form', $o->layout->opt_pre . '{inherit}' . $o->layout->opt_post);
438
  $fields->add($othm);
439
  }
440
 
@@ -446,10 +277,10 @@ class SLB_Options extends SLB_Field_Collection {
446
  * @param object $fields Collection of default field types
447
  * @return void
448
  */
449
- function set_parents(&$fields) {
450
  if ( !is_admin() )
451
  return false;
452
- $items =& $this->get_items();
453
  foreach ( array_keys($items) as $opt ) {
454
  $items[$opt]->set_parent();
455
  }
@@ -470,41 +301,50 @@ class SLB_Options extends SLB_Field_Collection {
470
  * @param array $values (optional) Option values
471
  * @return array Full options data
472
  */
473
- function validate($values = null, $force_save = false) {
474
- if ( empty($values) && isset($_REQUEST[$this->add_prefix('options')]) ) {
475
- $values_orig = $values;
476
- if ( is_string($values_orig) )
477
- $force_save = true;
478
- $values = $_REQUEST[$this->add_prefix('options')];
479
  }
480
  if ( is_array($values) ) {
481
- //Format data based on option type (bool, string, etc.)
482
  foreach ( $values as $id => $val ) {
483
- //Get default
484
  $d = $this->get_default($id);
485
  if ( is_bool($d) && !empty($val) )
486
  $values[$id] = true;
487
  }
488
- //Merge in additional options that are not in post data
489
- //Missing options (e.g. disabled checkboxes) & defaults
490
- $items =& $this->get_items();
491
- foreach ( $items as $id => $opt ) {
492
- //Add options that were not included in form submission
493
- if ( !array_key_exists($id, $values) ) {
494
- if ( is_bool($opt->get_default()) )
495
- $values[$id] = false;
496
- else
497
- $values[$id] = $opt->get_default();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498
  }
499
  }
500
  }
501
 
502
- if ( $force_save ) {
503
- $this->set_data($values);
504
- $values = $values_orig;
505
- }
506
-
507
- //Return value
508
  return $values;
509
  }
510
 
@@ -516,19 +356,16 @@ class SLB_Options extends SLB_Field_Collection {
516
  * @return array Options data
517
  */
518
  function fetch_data($sanitize = true) {
519
- //Get data
520
  $data = get_option($this->get_key(), null);
521
  if ( $sanitize && is_array($data) ) {
522
- //Sanitize loaded data based on default values
523
  foreach ( $data as $id => $val ) {
524
  if ( $this->has($id) ) {
525
  $opt = $this->get($id);
526
  if ( is_bool($opt->get_default()) )
527
  $data[$id] = !!$val;
528
- }/* else {
529
- //Remove data that has no matching item
530
- unset($data[$id]);
531
- }*/
532
  }
533
  }
534
  return $data;
@@ -540,10 +377,10 @@ class SLB_Options extends SLB_Field_Collection {
540
  */
541
  function load_data() {
542
  if ( !$this->data_loaded ) {
543
- //Retrieve data
544
  $this->data = $this->fetch_data();
545
  $this->data_loaded = true;
546
- //Check update
547
  $this->check_update();
548
  }
549
  }
@@ -554,11 +391,11 @@ class SLB_Options extends SLB_Field_Collection {
554
  */
555
  function reset($hard = true) {
556
  $this->load_data();
557
- //Reset data
558
  if ( $hard ) {
559
  $this->data = null;
560
  }
561
- //Save
562
  $this->save();
563
  }
564
 
@@ -603,9 +440,9 @@ class SLB_Options extends SLB_Field_Collection {
603
  * @return SLB_Option Option instance
604
  */
605
  function &add($id, $properties = array(), $update = false) {
606
- //Create item
607
  $args = func_get_args();
608
- $ret =& call_user_func_array(array('parent', 'add'), $args);
609
  return $ret;
610
  }
611
 
@@ -661,7 +498,7 @@ class SLB_Options extends SLB_Field_Collection {
661
  * @return array Associative array of options
662
  */
663
  function build_client_output() {
664
- $items =& $this->get_items();
665
  $out = array();
666
  foreach ( $items as $option ) {
667
  if ( !$option->get_in_client() )
@@ -670,4 +507,135 @@ class SLB_Options extends SLB_Field_Collection {
670
  }
671
  return $out;
672
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
  }
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  /**
3
  * Options collection
4
  * @package Simple Lightbox
38
  /* Init */
39
 
40
  function __construct($id = '', $props = array()) {
41
+ // Validate arguments
42
  $args = func_get_args();
43
+ // Set default ID
44
  if ( !$this->validate_id($id) ) {
45
  $id = 'options';
46
  }
52
 
53
  protected function _hooks() {
54
  parent::_hooks();
55
+ // Register fields
56
+ $this->util->add_action('register_fields', $this->m('register_fields'), 10, 1, false);
57
+ // Set option parents
58
+ $this->util->add_action('fields_registered', $this->m('set_parents'), 10, 1, false);
59
+ // Building
60
  $this->util->add_action('build_init', $this->m('build_init'));
61
+ // Admin
62
+ $this->util->add_action('admin_page_render_content', $this->m('admin_page_render_content'), 10, 3, false);
63
+ $this->util->add_filter('admin_action_reset', $this->m('admin_action_reset'), 10, 3, false);
64
  }
65
 
66
  /* Legacy/Migration */
76
  if ( !$this->version_checked ) {
77
  $this->version_checked = true;
78
  $version_changed = false;
79
+ // Get version from DB
80
  $vo = $this->get_version();
81
+ // Get current version
82
  $vn = $this->util->get_plugin_version();
83
+ // Compare versions
84
  if ( $vo != $vn ) {
85
+ // Update saved version
86
  $this->set_version($vn);
87
+ // Migrate old version to new version
88
  if ( strcasecmp($vo, $vn) < 0 ) {
89
+ // Force full migration
90
  $version_changed = true;
91
  }
92
  }
93
+ // Migrate
94
  $this->migrate($version_changed);
95
  }
96
 
129
  if ( !$full && $this->items_migrated )
130
  return false;
131
 
132
+ // Legacy options
133
  $d = null;
134
  $this->load_data();
135
 
136
+ $items = $this->get_items();
137
 
138
+ // Migrate separate options to unified option
139
  if ( $full ) {
140
  foreach ( $items as $opt => $props ) {
141
  $oid = $this->add_prefix($opt);
142
  $o = get_option($oid, $d);
143
  if ( $o !== $d ) {
144
+ // Migrate value to data array
145
  $this->set_data($opt, $o, false);
146
+ // Delete legacy option
147
  delete_option($oid);
148
  }
149
  }
150
  }
151
 
152
+ // Migrate legacy items
153
  if ( is_array($this->properties_init) && isset($this->properties_init['legacy']) && is_array($this->properties_init['legacy']) ) {
154
  $l =& $this->properties_init['legacy'];
155
+ // Normalize legacy map
156
  foreach ( $l as $opt => $dest ) {
157
  if ( !is_array($dest) ) {
158
  if ( is_string($dest) )
167
  foreach ( $l as $opt => $dest ) {
168
  $oid = $this->add_prefix($opt);
169
  $o = get_option($oid, $d);
170
+ // Only migrate valid values
171
  if ( $o !== $d ) {
172
+ // Process destinations
173
  foreach ( $dest as $id ) {
174
  $this->set_data($id, $o, false, true);
175
  }
176
  }
177
+ // Remove legacy option
178
  delete_option($oid);
179
  }
180
  }
181
 
182
  /* Simple Migration (Internal options only) */
183
 
184
+ // Get existing items that are also legacy items
185
  $opts = array_intersect_key($this->get_data(), $l);
186
  foreach ( $opts as $opt => $val ) {
187
  $d = $this->get_data($opt);
188
+ // Migrate data from old option to new option
189
  $dest = $l[$opt];
190
+ // Validate new options to send data to
191
  foreach ( $dest as $id ) {
192
  $this->set_data($id, $d, false, true);
193
  }
194
+ // Remove legacy option
195
  $this->remove($opt, false);
196
  }
197
  }
198
+ // Save changes
199
  $this->save();
200
+ // Set flag
201
  $this->items_migrated = true;
202
  }
203
 
231
  * @param SLB_Fields $fields Reference to global fields object
232
  * @return void
233
  */
234
+ function register_fields($fields) {
235
+ // Layouts
236
  $o = $this->get_field_elements();
237
+ $l =& $o->layout;
238
 
239
+ $form = implode('', array (
240
+ $l->opt_pre,
241
+ $l->label_ref,
242
+ $l->field_pre,
243
+ $l->form,
244
+ $l->field_post,
245
+ $l->opt_post
246
+ ));
247
 
248
+ // Text input
249
  $otxt = new SLB_Field_Type('option_text', 'text');
250
  $otxt->set_property('class', '{inherit} code');
251
  $otxt->set_property('size', null);
252
  $otxt->set_property('value', '{data context="form"}');
253
+ $otxt->set_layout('label', $l->label);
254
  $otxt->set_layout('form', $form);
255
  $fields->add($otxt);
256
 
257
+ // Checkbox
258
  $ocb = new SLB_Field_Type('option_checkbox', 'checkbox');
259
+ $ocb->set_layout('label', $l->label);
260
  $ocb->set_layout('form', $form);
261
  $fields->add($ocb);
262
 
263
+ // Select
264
  $othm = new SLB_Field_Type('option_select', 'select');
265
+ $othm->set_layout('label', $l->label);
266
+ $othm->set_layout('form_start', $l->field_pre . '{inherit}');
267
+ $othm->set_layout('form_end', '{inherit}' . $l->field_post);
268
+ $othm->set_layout('form', $l->opt_pre . '{inherit}' . $l->opt_post);
269
  $fields->add($othm);
270
  }
271
 
277
  * @param object $fields Collection of default field types
278
  * @return void
279
  */
280
+ function set_parents($fields) {
281
  if ( !is_admin() )
282
  return false;
283
+ $items = &$this->get_items();
284
  foreach ( array_keys($items) as $opt ) {
285
  $items[$opt]->set_parent();
286
  }
301
  * @param array $values (optional) Option values
302
  * @return array Full options data
303
  */
304
+ function validate($values = null) {
305
+ $qvar = $this->get_id('formatted');
306
+ if ( empty($values) && isset($_REQUEST[$qvar]) ) {
307
+ $values = $_REQUEST[$qvar];
 
 
308
  }
309
  if ( is_array($values) ) {
310
+ // Format data based on option type (bool, string, etc.)
311
  foreach ( $values as $id => $val ) {
312
+ // Get default
313
  $d = $this->get_default($id);
314
  if ( is_bool($d) && !empty($val) )
315
  $values[$id] = true;
316
  }
317
+
318
+ // Merge in additional options that are not in post data
319
+ // Missing options (e.g. disabled checkboxes, empty fields, etc.)
320
+
321
+ // Get groups that were output in request
322
+ $qvar_groups = $qvar . '_groups';
323
+ if ( isset($_REQUEST[$qvar_groups]) ) {
324
+ $groups = explode( ',', implode(',', $_REQUEST[$qvar_groups]) );
325
+
326
+ // Get group items
327
+ $items = array();
328
+ $items_temp = null;
329
+ foreach ( $groups as $gid ) {
330
+ $items_temp = $this->get_group_items($gid);
331
+ $items = array_merge($items, $items_temp);
332
+ }
333
+ unset($items_temp);
334
+ $items = call_user_func_array('array_merge', $items);
335
+ foreach ( $items as $id => $opt ) {
336
+ // Add options that were not included in form submission
337
+ if ( !array_key_exists($id, $values) ) {
338
+ if ( is_bool($opt->get_default()) )
339
+ $values[$id] = false;
340
+ else
341
+ $values[$id] = $opt->get_default();
342
+ }
343
  }
344
  }
345
  }
346
 
347
+ // Return value
 
 
 
 
 
348
  return $values;
349
  }
350
 
356
  * @return array Options data
357
  */
358
  function fetch_data($sanitize = true) {
359
+ // Get data
360
  $data = get_option($this->get_key(), null);
361
  if ( $sanitize && is_array($data) ) {
362
+ // Sanitize loaded data based on default values
363
  foreach ( $data as $id => $val ) {
364
  if ( $this->has($id) ) {
365
  $opt = $this->get($id);
366
  if ( is_bool($opt->get_default()) )
367
  $data[$id] = !!$val;
368
+ }
 
 
 
369
  }
370
  }
371
  return $data;
377
  */
378
  function load_data() {
379
  if ( !$this->data_loaded ) {
380
+ // Retrieve data
381
  $this->data = $this->fetch_data();
382
  $this->data_loaded = true;
383
+ // Check update
384
  $this->check_update();
385
  }
386
  }
391
  */
392
  function reset($hard = true) {
393
  $this->load_data();
394
+ // Reset data
395
  if ( $hard ) {
396
  $this->data = null;
397
  }
398
+ // Save
399
  $this->save();
400
  }
401
 
440
  * @return SLB_Option Option instance
441
  */
442
  function &add($id, $properties = array(), $update = false) {
443
+ // Create item
444
  $args = func_get_args();
445
+ $ret = call_user_func_array(array('parent', 'add'), $args);
446
  return $ret;
447
  }
448
 
498
  * @return array Associative array of options
499
  */
500
  function build_client_output() {
501
+ $items = $this->get_items();
502
  $out = array();
503
  foreach ( $items as $option ) {
504
  if ( !$option->get_in_client() )
507
  }
508
  return $out;
509
  }
510
+
511
+ /* Admin */
512
+
513
+ /**
514
+ * Handles output building for options on admin pages
515
+ * @param obj|array $opts Options instance or Array of options instance and groups to build
516
+ * @param obj $page Admin Page instance
517
+ * @param obj $state Admin Page state properties
518
+ */
519
+ public function admin_page_render_content($opts, $page, $state) {
520
+ $groups = null;
521
+ if ( is_array($opts) && count($opts) == 2 ) {
522
+ $groups = $opts[1];
523
+ $opts = $opts[0];
524
+ }
525
+ if ( $opts === $this ) {
526
+ // Set build variables and callbacks
527
+ $this->set_build_var('admin_page', $page);
528
+ $this->set_build_var('admin_state', $state);
529
+ if ( !empty($groups) ) {
530
+ $this->set_build_var('groups', $groups);
531
+ }
532
+ $hooks = array (
533
+ 'filter' => array (
534
+ 'parse_build_vars' => array( $this->m('admin_parse_build_vars'), 10, 2 )
535
+ )
536
+ );
537
+
538
+ // Add hooks
539
+ foreach ( $hooks as $type => $hook ) {
540
+ $m = 'add_' . $type;
541
+ foreach ( $hook as $tag => $args ) {
542
+ array_unshift($args, $tag);
543
+ call_user_func_array($this->util->m($m), $args);
544
+ }
545
+ }
546
+
547
+ // Build output
548
+ $this->build(array('build_groups' => $this->m('admin_build_groups')));
549
+
550
+ // Remove hooks
551
+ foreach ( $hooks as $type => $hook ) {
552
+ $m = 'remove_' . $type;
553
+ foreach ( $hook as $tag => $args ) {
554
+ call_user_func($this->util->m($m), $tag, $args[0]);
555
+ }
556
+ }
557
+ // Clear custom build vars
558
+ $this->delete_build_var('admin_page');
559
+ $this->delete_build_var('admin_state');
560
+ }
561
+ }
562
+
563
+ /**
564
+ * Builds option groups output
565
+ */
566
+ public function admin_build_groups() {
567
+ $page = $this->get_build_var('admin_page');
568
+ $state = $this->get_build_var('admin_state');
569
+ $groups = $this->get_build_var('groups');
570
+
571
+ // Get all groups
572
+ $groups_all = $this->get_groups();
573
+ $groups_built = array();
574
+ if ( empty($groups) ) {
575
+ $groups = array_keys($groups_all);
576
+ }
577
+ // Iterate through groups
578
+ foreach ( $groups as $gid ) {
579
+ // Validate
580
+ if ( !isset($groups_all[$gid]) || !count($this->get_items($gid)) ) {
581
+ continue;
582
+ }
583
+ // Add meta box for each group
584
+ $g = $groups_all[$gid];
585
+ add_meta_box($g->id, $g->title, $this->m('admin_build_group'), $state->screen, $state->context, $state->priority, array('group' => $g->id, 'page' => $page));
586
+ $groups_built[] = $gid;
587
+ }
588
+
589
+ // Define groups built
590
+ if ( !empty($groups_built) ) {
591
+ echo $this->util->build_html_element(array(
592
+ 'tag' => 'input',
593
+ 'attributes' => array (
594
+ 'type' => 'hidden',
595
+ 'value' => implode(',', $groups_built),
596
+ 'name' => $this->get_id('formatted') . '_groups[]'
597
+ ),
598
+ ));
599
+ }
600
+ }
601
+
602
+ /**
603
+ * Group output handler for admin pages
604
+ * @param obj $obj Object passed by `do_meta_boxes()` call (Default: NULL)
605
+ * @param array $box Meta box properties
606
+ */
607
+ public function admin_build_group($obj, $box) {
608
+ $a = $box['args'];
609
+ $group = $a['group'];
610
+ $this->build_group($group);
611
+ }
612
+
613
+ /**
614
+ * Parse build vars
615
+ * @uses `options_parse_build_vars` filter hook
616
+ */
617
+ public function admin_parse_build_vars($vars, $opts) {
618
+ // Handle form submission
619
+ if ( isset($_REQUEST[$opts->get_id('formatted')]) ) {
620
+ $vars['validate_pre'] = $vars['save_pre'] = true;
621
+ }
622
+ return $vars;
623
+ }
624
+
625
+ /**
626
+ * Admin reset handler
627
+ * @param bool $res Current result
628
+ * @param obj $opts Options instance
629
+ * @param obj $reset Admin Reset instance
630
+ */
631
+ public function admin_action_reset($res, $opts, $reset) {
632
+ // Only process matching options instance
633
+ if ( $opts === $this ) {
634
+ // Reset options
635
+ $this->reset();
636
+ // Set result
637
+ $res = true;
638
+ }
639
+ return $res;
640
+ }
641
  }
includes/class.template_tags.php CHANGED
@@ -13,10 +13,10 @@ class SLB_Template_Tags extends SLB_Collection_Controller {
13
 
14
  public $hook_prefix = 'template_tags';
15
 
16
- //Use tag ID as key
17
  protected $key_prop = 'get_id';
18
 
19
- //Call $key_prop is a method to be called
20
  protected $key_call = true;
21
 
22
  /* Properties */
@@ -32,8 +32,8 @@ class SLB_Template_Tags extends SLB_Collection_Controller {
32
  protected function _hooks() {
33
  parent::_hooks();
34
  $this->util->add_action('init', $this->m('init_defaults'));
35
-
36
- add_action('wp_footer', $this->m('client_output'), $this->util->priority('client_footer_output'));
37
  }
38
 
39
  /* Collection Management */
@@ -48,7 +48,7 @@ class SLB_Template_Tags extends SLB_Collection_Controller {
48
  */
49
  public function add($id, $props = array()) {
50
  $o = ( is_string($id) ) ? new $this->item_type($id, $props) : $id;
51
- //Add to collection
52
  return parent::add($o);
53
  }
54
 
@@ -59,12 +59,19 @@ class SLB_Template_Tags extends SLB_Collection_Controller {
59
  * @param SLB_Template_Tags $tags Tags controller
60
  */
61
  public function init_defaults($tags) {
 
 
 
62
  $defaults = array (
63
  'item' => array (
64
- 'client_script' => $this->util->get_file_url('template-tags/item/tag.item.js'),
 
 
65
  ),
66
  'ui' => array (
67
- 'client_script' => $this->util->get_file_url('template-tags/ui/tag.ui.js'),
 
 
68
  ),
69
  );
70
  foreach ( $defaults as $id => $props ) {
@@ -75,27 +82,41 @@ class SLB_Template_Tags extends SLB_Collection_Controller {
75
  /* Output */
76
 
77
  /**
78
- * Client output
79
  */
80
  public function client_output() {
81
- //Stop if not enabled
82
- if ( !$this->has_parent() || !$this->get_parent()->is_enabled() ) {
83
- return;
84
  }
85
- $out = array();
86
- $out[] = '<!-- SLB-TPTG -->' . PHP_EOL;
 
 
 
 
 
 
 
87
  $code = array();
88
- //Load matched handlers
89
- foreach ( $this->get() as $id => $tag ) {
90
- //Define
 
 
 
 
91
  $params = array(
92
- sprintf("'%s'", $id),
93
- sprintf("'%s'", $tag->get_client_script('uri')),
94
  );
95
- $code[] = $this->util->call_client_method('View.add_template_tag_handler', $params, false);
 
 
 
 
 
 
96
  }
97
- $out[] = $this->util->build_script_element(implode('', $code), 'add_template_tags', true, true);
98
- $out[] = '<!-- /SLB-TPTG -->' . PHP_EOL;
99
- echo implode('', $out);
100
  }
101
  }
13
 
14
  public $hook_prefix = 'template_tags';
15
 
16
+ // Use tag ID as key
17
  protected $key_prop = 'get_id';
18
 
19
+ // Call $key_prop is a method to be called
20
  protected $key_call = true;
21
 
22
  /* Properties */
32
  protected function _hooks() {
33
  parent::_hooks();
34
  $this->util->add_action('init', $this->m('init_defaults'));
35
+ $this->util->add_action('footer', $this->m('client_output'), 1, 0, false);
36
+ $this->util->add_filter('footer_script', $this->m('client_output_script'), $this->util->priority('client_footer_output'), 1, false);
37
  }
38
 
39
  /* Collection Management */
48
  */
49
  public function add($id, $props = array()) {
50
  $o = ( is_string($id) ) ? new $this->item_type($id, $props) : $id;
51
+ // Add to collection
52
  return parent::add($o);
53
  }
54
 
59
  * @param SLB_Template_Tags $tags Tags controller
60
  */
61
  public function init_defaults($tags) {
62
+ $js_path = 'js/';
63
+ $js_path .= ( SLB_DEV ) ? 'dev' : 'prod';
64
+ $src_base = $this->util->get_file_url('template-tags', true);
65
  $defaults = array (
66
  'item' => array (
67
+ 'scripts' => array (
68
+ array ( 'base', "$src_base/item/$js_path/tag.item.js" ),
69
+ )
70
  ),
71
  'ui' => array (
72
+ 'scripts' => array (
73
+ array ( 'base', "$src_base/ui/$js_path/tag.ui.js" ),
74
+ )
75
  ),
76
  );
77
  foreach ( $defaults as $id => $props ) {
82
  /* Output */
83
 
84
  /**
85
+ * Build client output
86
  */
87
  public function client_output() {
88
+ // Load matched handlers
89
+ foreach ( $this->get() as $tag ) {
90
+ $tag->enqueue_scripts();
91
  }
92
+ }
93
+
94
+ /**
95
+ * Client output script
96
+ * @param array $commands Client script commands
97
+ * @return array Modified script commands
98
+ */
99
+ public function client_output_script($commands) {
100
+ $out = array('/* TPLT */');
101
  $code = array();
102
+
103
+ foreach ( $this->get() as $tag ) {
104
+ $styles = $tag->get_styles(array('uri_format'=>'full'));
105
+ if ( empty($styles) ) {
106
+ continue;
107
+ }
108
+ // Setup client parameters
109
  $params = array(
110
+ sprintf("'%s'", $tag->get_id()),
 
111
  );
112
+ $params[] = json_encode( array('styles' => array_values($styles)) );
113
+ // Extend handler in client
114
+ $code[] = $this->util->call_client_method('View.extend_template_tag_handler', $params, false);
115
+ }
116
+ if ( !empty($code) ) {
117
+ $out[] = implode('', $code);
118
+ $commands[] = implode(PHP_EOL, $out);
119
  }
120
+ return $commands;
 
 
121
  }
122
  }
includes/class.theme.php CHANGED
@@ -11,13 +11,20 @@ class SLB_Theme extends SLB_Component {
11
 
12
  protected $props_required = array('name');
13
 
 
 
 
 
 
 
14
  /* Get/Set */
15
 
16
  /**
17
  * Retrieve theme's ancestors
 
18
  * @return array Theme's ancestors (sorted by nearest to most distant ancestor)
19
  */
20
- public function get_ancestors() {
21
  $ret = array();
22
  /**
23
  * @var SLB_Theme
@@ -25,35 +32,34 @@ class SLB_Theme extends SLB_Component {
25
  $thm = $this;
26
  while ( $thm->has_parent() ) {
27
  $par = $thm->get_parent();
28
- //Add ancestor
29
  if ( $par->is_valid() && !in_array($par, $ret, true) ) {
30
  $ret[] = $par;
31
  }
32
- //Get next ancestor
33
  $thm = $par;
34
  }
 
 
 
 
35
  return $ret;
36
  }
37
 
38
- /* Style */
39
-
40
  /**
41
- * Set Theme style path
42
- * @see `add_style()`
43
  */
44
- public function set_client_style($src, $deps = array()) {
45
- if ( is_array($src) ) {
46
- list($src, $deps) = func_get_arg(0);
47
- }
48
- return $this->add_style('client', $src, $deps);
49
  }
50
 
51
  /**
52
- * Get Theme style path
53
- * @see `get_style()`
54
  */
55
- public function get_client_style($format = null) {
56
- return $this->get_style('client', $format);
57
  }
58
 
59
  /* Templates */
11
 
12
  protected $props_required = array('name');
13
 
14
+ /**
15
+ * Public flag
16
+ * @var bool
17
+ */
18
+ protected $public = true;
19
+
20
  /* Get/Set */
21
 
22
  /**
23
  * Retrieve theme's ancestors
24
+ * @param bool $sort_topdown (optional) Ancestor sorting (Default: Nearest to Farthest)
25
  * @return array Theme's ancestors (sorted by nearest to most distant ancestor)
26
  */
27
+ public function get_ancestors($sort_topdown = false) {
28
  $ret = array();
29
  /**
30
  * @var SLB_Theme
32
  $thm = $this;
33
  while ( $thm->has_parent() ) {
34
  $par = $thm->get_parent();
35
+ // Add ancestor
36
  if ( $par->is_valid() && !in_array($par, $ret, true) ) {
37
  $ret[] = $par;
38
  }
39
+ // Get next ancestor
40
  $thm = $par;
41
  }
42
+ // Sorting
43
+ if ( $sort_topdown ) {
44
+ $ret = array_reverse($ret);
45
+ }
46
  return $ret;
47
  }
48
 
 
 
49
  /**
50
+ * Set public flag
51
+ * @param bool $public
52
  */
53
+ public function set_public($public) {
54
+ $this->public = !!$public;
 
 
 
55
  }
56
 
57
  /**
58
+ * Get privacy state
59
+ * @return bool
60
  */
61
+ public function get_public() {
62
+ return !!$this->public;
63
  }
64
 
65
  /* Templates */
includes/class.themes.php CHANGED
@@ -25,11 +25,12 @@ class SLB_Themes extends SLB_Collection_Controller {
25
 
26
  protected function _hooks() {
27
  parent::_hooks();
28
- //Register themes
29
  $this->util->add_action('init', $this->m('init_defaults'), 1);
30
 
31
- //Client output
32
- add_action('wp_footer', $this->m('client_output'), $this->util->priority('client_footer_output'));
 
33
  }
34
 
35
  protected function _options() {
@@ -46,7 +47,7 @@ class SLB_Themes extends SLB_Collection_Controller {
46
  )
47
  );
48
 
49
- parent::_options($opts);
50
  }
51
 
52
  /**
@@ -54,17 +55,39 @@ class SLB_Themes extends SLB_Collection_Controller {
54
  * @param SLB_Themes $themes Themes controller
55
  */
56
  function init_defaults($themes) {
 
 
 
 
 
57
  $defaults = array (
 
 
 
 
 
 
 
 
 
 
 
58
  $this->get_default_id() => array (
59
  'name' => __('Default (Light)', 'simple-lightbox'),
60
- 'layout' => $this->util->get_file_url('themes/default/layout.html'),
61
- 'client_script' => $this->util->get_file_url('themes/default/client.js'),
62
- 'client_style' => $this->util->get_file_url('themes/default/css/style.css'),
 
 
 
 
63
  ),
64
  $this->add_prefix('black') => array (
65
  'name' => __('Default (Dark)', 'simple-lightbox'),
66
  'parent' => $this->get_default_id(),
67
- 'client_style' => $this->util->get_file_url('themes/black/css/style.css'),
 
 
68
  ),
69
  );
70
 
@@ -84,18 +107,58 @@ class SLB_Themes extends SLB_Collection_Controller {
84
  * @return object Current instance
85
  */
86
  public function add($id, $props = array()) {
87
- //Prepare parent
88
  if ( isset($props['parent']) && !($props['parent'] instanceof $this->item_type) ) {
89
  $pid = $props['parent'];
90
- $items = $this->get();
91
  if ( isset($items[$pid]) ) {
92
  $props['parent'] = $items[$pid];
93
  }
94
  }
95
  $o = ( is_string($id) ) ? new $this->item_type($id, $props) : $id;
96
- //Add to collection
97
  return parent::add($o);
98
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  /* Helpers */
101
 
@@ -116,9 +179,9 @@ class SLB_Themes extends SLB_Collection_Controller {
116
  * @return SLB_Theme Selected theme
117
  */
118
  protected function get_selected() {
119
- //Get themes
120
  $thms = $this->get();
121
- //Retrieve currently-selected theme
122
  $id = $this->options->get_value('theme_default');
123
  if ( !isset($thms[$id]) ) {
124
  $id = $this->get_default_id();
@@ -129,66 +192,72 @@ class SLB_Themes extends SLB_Collection_Controller {
129
  /* Output */
130
 
131
  /**
132
- * Client output
133
  */
134
  public function client_output() {
135
- //Stop if not enabled
136
- if ( !$this->has_parent() || !$this->get_parent()->is_enabled() ) {
137
- return;
138
- }
139
 
140
- //Theme
141
- /**
142
- * @var SLB_Theme
143
- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  $thm = $this->get_selected();
145
 
146
- //Process theme ancestors
147
- $thms = array_reverse($thm->get_ancestors());
148
  $thms[] = $thm;
149
 
150
- $id_fmt = 'add_theme_%s';
151
- $out = array();
152
- $out[] = '<!-- SLB-THM -->' . PHP_EOL;
153
  $code = array();
154
 
155
- //Build output for each theme
156
  foreach ( $thms as $thm ) {
157
- //Setup client parameters
158
  $params = array(
159
  sprintf("'%s'", $thm->get_id()),
160
  );
161
- //Theme properties
162
  $thm_props = array(
163
  'name' => $thm->get_name(),
164
  'parent' => ( $thm->has_parent() ) ? $thm->get_parent()->get_id() : '',
 
165
  );
166
  /* Optional properties */
167
- //Layout
168
- $uri = $thm->get_layout('uri');
169
- if ( !empty($uri) ) {
170
- $thm_props['layout_uri'] = $uri;
 
 
 
171
  }
172
- //Script
173
- $script = $thm->get_client_script('uri');
174
- if ( !empty($script) ) {
175
- $thm_props['client_script'] = $script;
176
- }
177
- //Style
178
- $style = $thm->get_client_style('uri');
179
- if ( !empty($style) ) {
180
- $thm_props['client_style'] = $style;
181
- }
182
- //Add properties to parameters
183
  $params[] = json_encode($thm_props);
184
 
185
- //Add theme to client
186
- $code[] = $this->util->call_client_method('View.add_theme', $params, false);
187
  }
188
 
189
- $out[] = $this->util->build_script_element(implode('', $code), 'add_themes', true, true);
190
- $out[] = '<!-- /SLB-THM -->' . PHP_EOL;
191
- echo implode('', $out);
 
 
192
  }
193
 
194
  /* Options */
@@ -199,24 +268,26 @@ class SLB_Themes extends SLB_Collection_Controller {
199
  * @return array Theme options
200
  */
201
  public function opt_get_field_values() {
202
- //Get themes
203
  $items = $this->get();
204
  $d = $this->get_default_id();
205
- //Pop out default theme
206
  if ( isset($items[$d]) ) {
207
  $itm_d = $items[$d];
208
  unset($items[$d]);
209
  }
210
 
211
- //Sort themes by name
212
- uasort($items, create_function('$a,$b', 'return strcmp($a->get_name(), $b->get_name());'));
 
 
213
 
214
- //Insert default theme at top of array
215
  if ( isset($itm_d) ) {
216
  $items = array( $d => $itm_d ) + $items;
217
  }
218
 
219
- //Build options
220
  foreach ( $items as $item ) {
221
  $items[$item->get_id()] = $item->get_name();
222
  }
25
 
26
  protected function _hooks() {
27
  parent::_hooks();
28
+ // Register themes
29
  $this->util->add_action('init', $this->m('init_defaults'), 1);
30
 
31
+ // Client output
32
+ $this->util->add_action('footer', $this->m('client_output'), 1, 0, false);
33
+ $this->util->add_filter('footer_script', $this->m('client_output_script'), $this->util->priority('client_footer_output'), 1, false);
34
  }
35
 
36
  protected function _options() {
47
  )
48
  );
49
 
50
+ parent::_set_options($opts);
51
  }
52
 
53
  /**
55
  * @param SLB_Themes $themes Themes controller
56
  */
57
  function init_defaults($themes) {
58
+ $js_path = 'js/';
59
+ $js_path .= ( SLB_DEV ) ? 'dev' : 'prod';
60
+ $scheme = is_ssl() ? 'https' : 'http';
61
+ $baseline = $this->add_prefix('baseline');
62
+ $src_base = $this->util->get_file_url('themes', true);
63
  $defaults = array (
64
+ $baseline => array (
65
+ 'name' => __('Baseline', 'simple-lightbox'),
66
+ 'public' => false,
67
+ 'layout' => "$src_base/baseline/layout.html",
68
+ 'scripts' => array (
69
+ array ( 'base', $src_base . "/baseline/$js_path/client.js" ),
70
+ ),
71
+ 'styles' => array (
72
+ array ( 'base', "$src_base/baseline/css/style.css" ),
73
+ ),
74
+ ),
75
  $this->get_default_id() => array (
76
  'name' => __('Default (Light)', 'simple-lightbox'),
77
+ 'parent' => $baseline,
78
+ 'scripts' => array (
79
+ array ( 'base', $src_base . "/default/$js_path/client.js" ),
80
+ ),
81
+ 'styles' => array (
82
+ array ( 'base', "$src_base/default/css/style.css" ),
83
+ ),
84
  ),
85
  $this->add_prefix('black') => array (
86
  'name' => __('Default (Dark)', 'simple-lightbox'),
87
  'parent' => $this->get_default_id(),
88
+ 'styles' => array (
89
+ array ( 'base', "$src_base/black/css/style.css" )
90
+ )
91
  ),
92
  );
93
 
107
  * @return object Current instance
108
  */
109
  public function add($id, $props = array()) {
110
+ // Prepare parent
111
  if ( isset($props['parent']) && !($props['parent'] instanceof $this->item_type) ) {
112
  $pid = $props['parent'];
113
+ $items = $this->get(array('include_private' => true));
114
  if ( isset($items[$pid]) ) {
115
  $props['parent'] = $items[$pid];
116
  }
117
  }
118
  $o = ( is_string($id) ) ? new $this->item_type($id, $props) : $id;
119
+ // Add to collection
120
  return parent::add($o);
121
  }
122
+
123
+ /**
124
+ * Get themes
125
+ * @param array $args (optional) Arguments
126
+ * @return array Themes
127
+ */
128
+ public function get($args = null) {
129
+ // Normalize arguments
130
+ $args_default = array(
131
+ 'include_public' => true,
132
+ 'include_private' => false,
133
+ );
134
+ $r = wp_parse_args($args, $args_default);
135
+ $r['include_public'] = !!$r['include_public'];
136
+ $r['include_private'] = !!$r['include_private'];
137
+
138
+ $items = parent::get($args);
139
+
140
+ if ( empty($items) )
141
+ return $items;
142
+
143
+ /* Process custom arguments */
144
+
145
+ // Filter
146
+ $items_exclude = array();
147
+ // Identify excluded themes
148
+ $filter_props = array('include_public' => true, 'include_private' => false);
149
+ foreach ( $filter_props as $filter_prop => $filter_value ) {
150
+ if ( !$r[ $filter_prop ] ) {
151
+ foreach ( $items as $id => $item ) {
152
+ if ( $item->get_public() == $filter_value ) {
153
+ $items_exclude[] = $id;
154
+ }
155
+ }
156
+ }
157
+ }
158
+ // Filter themes from collection
159
+ $items = array_diff_key($items, array_fill_keys($items_exclude, null));
160
+ return $items;
161
+ }
162
 
163
  /* Helpers */
164
 
179
  * @return SLB_Theme Selected theme
180
  */
181
  protected function get_selected() {
182
+ // Get themes
183
  $thms = $this->get();
184
+ // Retrieve currently-selected theme
185
  $id = $this->options->get_value('theme_default');
186
  if ( !isset($thms[$id]) ) {
187
  $id = $this->get_default_id();
192
  /* Output */
193
 
194
  /**
195
+ * Build client output
196
  */
197
  public function client_output() {
198
+ // Process active theme
199
+ $thm = $this->get_selected();
 
 
200
 
201
+ // Get theme ancestors
202
+ $thms = $thm->get_ancestors(true);
203
+ $thms[] = $thm;
204
+
205
+ foreach ( $thms as $thm ) {
206
+ // Load files
207
+ $thm->enqueue_scripts();
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Client output script
213
+ *
214
+ * @param array $commands Client script commands
215
+ * @return array Modified script commands
216
+ */
217
+ public function client_output_script($commands) {
218
+ // Theme
219
  $thm = $this->get_selected();
220
 
221
+ // Process theme ancestors
222
+ $thms = $thm->get_ancestors(true);
223
  $thms[] = $thm;
224
 
225
+ $out = array('/* THM */');
 
 
226
  $code = array();
227
 
228
+ // Build output for each theme
229
  foreach ( $thms as $thm ) {
230
+ // Setup client parameters
231
  $params = array(
232
  sprintf("'%s'", $thm->get_id()),
233
  );
234
+ // Theme properties
235
  $thm_props = array(
236
  'name' => $thm->get_name(),
237
  'parent' => ( $thm->has_parent() ) ? $thm->get_parent()->get_id() : '',
238
+ 'styles' => array_values($thm->get_styles(array('uri_format'=>'full'))),
239
  );
240
  /* Optional properties */
241
+ // Layout
242
+ $layout = $thm->get_layout('contents');
243
+ if ( !empty($layout) ) {
244
+ // Format
245
+ $layout = str_replace(array("\n", "\r", "\t"), '', $layout);
246
+ // Save
247
+ $thm_props['layout_raw'] = $layout;
248
  }
249
+ // Add properties to parameters
 
 
 
 
 
 
 
 
 
 
250
  $params[] = json_encode($thm_props);
251
 
252
+ // Add theme to client
253
+ $code[] = $this->util->call_client_method('View.extend_theme', $params, false);
254
  }
255
 
256
+ if ( !empty($code) ) {
257
+ $out[] = implode('', $code);
258
+ $commands[] = implode(PHP_EOL, $out);
259
+ }
260
+ return $commands;
261
  }
262
 
263
  /* Options */
268
  * @return array Theme options
269
  */
270
  public function opt_get_field_values() {
271
+ // Get themes
272
  $items = $this->get();
273
  $d = $this->get_default_id();
274
+ // Pop out default theme
275
  if ( isset($items[$d]) ) {
276
  $itm_d = $items[$d];
277
  unset($items[$d]);
278
  }
279
 
280
+ // Sort themes by name
281
+ uasort( $items, function( $a, $b ) {
282
+ return strcmp( $a->get_name(), $b->get_name() );
283
+ });
284
 
285
+ // Insert default theme at top of array
286
  if ( isset($itm_d) ) {
287
  $items = array( $d => $itm_d ) + $items;
288
  }
289
 
290
+ // Build options
291
  foreach ( $items as $item ) {
292
  $items[$item->get_id()] = $item->get_name();
293
  }
includes/class.utilities.php CHANGED
@@ -16,30 +16,40 @@ class SLB_Utilities {
16
  * Instance parent
17
  * @var object
18
  */
19
- var $parent = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  /**
22
- * Default plugin headers
23
- * @var array
24
  */
25
- private $plugin_headers = array (
26
- 'Name' => 'Plugin Name',
27
- 'PluginURI' => 'Plugin URI',
28
- 'Version' => 'Version',
29
- 'Description' => 'Description',
30
- 'Author' => 'Author',
31
- 'AuthorURI' => 'Author URI',
32
- 'TextDomain' => 'Text Domain',
33
- 'DomainPath' => 'Domain Path',
34
- 'Network' => 'Network',
35
- );
36
-
37
 
38
  /**
39
  * Standard hook priorities
40
  * @var array
41
  */
42
- private $priorities = array (
43
  'high' => 1,
44
  'low' => 99,
45
  'safe' => 15,
@@ -48,9 +58,10 @@ class SLB_Utilities {
48
 
49
  /* Constructors */
50
 
51
- function __construct(&$obj) {
52
- if ( is_object($obj) )
53
- $this->parent =& $obj;
 
54
  }
55
 
56
  /**
@@ -93,35 +104,47 @@ class SLB_Utilities {
93
  */
94
  function get_prefix($sep = null) {
95
  $sep = $this->get_sep($sep);
96
- $prefix = ( !empty($this->parent->prefix) ) ? $this->parent->prefix . $sep : '';
97
  return $prefix;
98
  }
99
 
100
  /**
101
  * Check if a string is prefixed
102
- * @param string $text Text to check for prefix
103
  * @param string $sep (optional) Separator used
104
  */
105
  function has_prefix($text, $sep = null) {
 
 
 
 
 
 
106
  return ( !empty($text) && stripos($text, $this->get_prefix($sep)) === 0 );
107
  }
108
 
109
  /**
110
  * Prepend plugin prefix to some text
111
- * @param string $text Text to add to prefix
112
  * @param string $sep (optional) Text used to separate prefix and text
113
  * @param bool $once (optional) Whether to add prefix to text that already contains a prefix or not
114
  * @return string Text with prefix prepended
115
  */
116
  function add_prefix($text, $sep = '_', $once = true) {
117
- if ( $once && $this->has_prefix($text, $sep) )
118
- return $text;
119
- return $this->get_prefix($sep) . $text;
 
 
 
 
 
 
120
  }
121
 
122
  /**
123
  * Prepend uppercased plugin prefix to some text
124
- * @param string $text Text to add to prefix
125
  * @param string $sep (optional) Text used to separate prefix and text
126
  * @param bool $once (optional) Whether to add prefix to text that already contains a prefix or not
127
  * @return string Text with prefix prepended
@@ -176,8 +199,8 @@ class SLB_Utilities {
176
  */
177
  public function priority($id = null) {
178
  $pri = 10;
179
- if ( !is_null($id) && array_key_exists($id, $this->priorities) ) {
180
- $pri = $this->priorities[$id];
181
  }
182
  return $pri;
183
  }
@@ -185,30 +208,44 @@ class SLB_Utilities {
185
  /* Wrapped Values */
186
 
187
  /**
188
- * Returns validated object of start/end wrapper values
 
 
 
189
  * @param string|array $start Start text (Can also be array defining start & end values)
190
  * @param string $end (optional) End text
191
  * If $end not defined, then $start is used
192
  * @return obj Wrapper
193
  */
194
  function get_wrapper($start = null, $end = null) {
195
- //Return pre-built wrapper
196
  if ( is_object($start) && isset($start->start) && isset($start->end) )
197
  return $start;
198
- //Default wrapper
199
- if ( is_null($start) && is_null($end) )
200
- $start = array('[', ']');
201
- $wrapper = compact('start', 'end');
202
- if ( is_array($start) && count($start) > 1 ) {
203
- $wrapper['start'] = $start[0];
204
- $wrapper['end'] = $start[1];
205
- }
206
- if ( !is_string($wrapper['start']) || empty($wrapper['start'] ) )
207
- $wrapper['start'] = '';
208
- if ( !is_string($wrapper['end']) || empty($wrapper['end']) )
209
- $wrapper['end'] = $wrapper['start'];
210
 
211
- return (object) $wrapper;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
213
 
214
  /**
@@ -221,11 +258,11 @@ class SLB_Utilities {
221
  function has_wrapper($text, $start = null, $end = null) {
222
  if ( !is_string($text) || empty($text) )
223
  return false;
224
- //Validate wrapper)
225
  $w = $this->get_wrapper($start, $end);
226
 
227
- //Check for wrapper
228
- return ( substr($text, 0, 1) == $w->start && substr($text, -1, 1) == $w->end ) ? true : false;
229
  }
230
 
231
  /**
@@ -248,7 +285,7 @@ class SLB_Utilities {
248
 
249
  /**
250
  * Add wrapper to specified text
251
- * @uses this->get_wrapper() to retrieve wrapper object
252
  * @param string $text Text to wrap
253
  * @param string|array $start (optional) Start text (Array defines both start/end text)
254
  * @param string $end (optional) End text
@@ -285,13 +322,14 @@ class SLB_Utilities {
285
  */
286
  function parse_client_files($files, $type = 'scripts') {
287
  if ( is_array($files) && !empty($files) ) {
288
- //Defaults
289
  $defaults = array(
290
  'file' => null,
291
  'deps' => array(),
292
  'callback' => null,
293
  'context' => array(),
294
  'enqueue' => true,
 
295
  );
296
  switch ( $type ) {
297
  case 'styles':
@@ -300,34 +338,43 @@ class SLB_Utilities {
300
  default:
301
  $defaults['in_footer'] = false;
302
  }
303
- //Iterate through files
 
 
 
 
304
  foreach ( $files as $h => $p ) {
305
  unset($file, $cb, $ctxs, $ctx);
306
- //Set ID
307
  $p['id'] = $this->add_prefix($h);
308
- //Type Validation
 
 
 
 
309
  foreach ( $defaults as $m => $d ) {
310
- //Check if value requires validation
311
  if ( !is_array($d) || !isset($p[$m]) || is_array($p[$m]) )
312
  continue;
313
- //Wrap value in array or destroy it
314
  if ( is_scalar($p[$m]) )
315
  $p[$m] = array($p[$m]);
316
  else
317
  unset($p[$m]);
318
  }
319
 
 
320
  $p = array_merge($defaults, $p);
321
 
322
  /* File name */
323
 
324
- //Validate file
325
  $file =& $p['file'];
326
 
327
- //Determine if filename or callback
328
  if ( !$this->is_file($file) )
329
- $file = $this->parse_client_file_callback($file);
330
- //Remove invalid file and move on to next
331
  if ( empty($file) ) {
332
  unset($files[$h]);
333
  continue;
@@ -335,7 +382,7 @@ class SLB_Utilities {
335
 
336
  /* Dependencies */
337
 
338
- //Format internal dependencies
339
  foreach ( $p['deps'] as $idx => $dep ) {
340
  if ( $this->has_wrapper($dep) ) {
341
  $dep = $this->remove_wrapper($dep);
@@ -345,50 +392,46 @@ class SLB_Utilities {
345
 
346
  /* Context */
347
 
348
- //Validate callback
349
  $cb =& $p['callback'];
350
- if ( !is_null($cb) ) {
351
- $cb = $this->parse_client_file_callback($cb);
352
- //Remove files with invalid callbacks (will never be loaded)
353
- if ( is_null($cb) ) {
354
- unset($files[$h]);
355
- continue;
356
- }
357
  }
358
 
359
- //Validate contexts
360
  $ctxs =& $p['context'];
361
  $ctxs = array_unique($ctxs);
362
  $has_contexts = ( count($ctxs) > 0 ) ? true : false;
363
  foreach ( $ctxs as $idx => $ctx ) {
364
- //Convert to array
365
  $ctx = array_values( array_slice( (array) $ctx, 0, 2 ) );
366
  switch ( count($ctx) ) {
367
  case 1 :
368
- //Simple context
369
  $ctx = $ctx[0];
370
  break;
371
  case 2 :
372
- //Context + Callback
373
- $ctx[1] = $this->parse_client_file_callback($ctx[1]);
374
- if ( !is_null($ctx[1]) ) {
375
  break;
376
  }
377
- //Continue to default case if callback is invalid
378
  default :
379
- //Context is invalid
380
  $ctx = false;
381
  break;
382
  }
383
 
384
- //Remove invalid contexts
385
  if ( empty($ctx) ) {
386
  unset($ctxs[$idx]);
387
  } else {
388
  $ctxs[$idx] = $ctx;
389
  }
390
  }
391
- //Remove file if all specified contexts invalid (no context is OK)
392
  if ( $has_contexts && empty($ctxs) ) {
393
  unset($files[$h]);
394
  continue;
@@ -397,30 +440,15 @@ class SLB_Utilities {
397
 
398
  /* Finalize Properties */
399
 
400
- //Convert properties to object
401
  $files[$h] = (object) $p;
402
  }
403
  }
404
- //Cast to object before returning
405
  $files = (object) $files;
406
  return $files;
407
  }
408
 
409
- /**
410
- * Parses callbacks set for client files
411
- * @param string $callback Callback value
412
- * > Values wrapped in square brackets (`[` & `]`) are internal methods (of parent object)
413
- * @return callback|null Validated callback (NULL if callback is invalid)
414
- */
415
- function parse_client_file_callback($callback) {
416
- if ( $this->has_wrapper($callback) ) {
417
- $callback = $this->m($this->parent, $this->remove_wrapper($callback));
418
- }
419
- if ( !is_callable($callback) )
420
- $callback = null;
421
- return $callback;
422
- }
423
-
424
  /**
425
  * Build JS client object
426
  * @param string (optional) $path Additional object path
@@ -444,7 +472,7 @@ class SLB_Utilities {
444
  * @return string JS expression to extend client object
445
  */
446
  function extend_client_object($obj, $data = null, $out = false) {
447
- //Validate parameters
448
  $args = func_get_args();
449
  switch ( count($args) ) {
450
  case 2:
@@ -459,14 +487,14 @@ class SLB_Utilities {
459
  $obj = null;
460
  break;
461
  }
462
- //Default client object
463
  if ( !is_string($obj) || empty($obj) )
464
  $obj = null;
465
- //Default data
466
  if ( is_array($data) ) {
467
  $data = (object)$data;
468
  }
469
- //Build expression
470
  if ( empty($data) || ( empty($obj) && is_scalar($data) ) ) {
471
  $ret = '';
472
  } else {
@@ -487,24 +515,24 @@ class SLB_Utilities {
487
  * If no command is specified the validation conditions are returned
488
  */
489
  public function validate_client_object($obj, $cmd = null) {
490
- //Build condition
 
 
 
491
  $sep = '.';
492
- $obj = trim( $this->get_client_object($obj) , $sep);
493
- $offset = 0;
494
- $len = strlen($obj);
495
- $pos = $len;
496
- $fmt = '(typeof %s != \'undefined\')';
497
- $objs = array();
498
- //Add segments to array (in reverse)
499
- do {
500
- $objs[] = sprintf($fmt, substr($obj, 0, $pos));
501
- $offset = $pos - $len - 1;
502
- } while ( $offset < $len && ( $pos = strrpos($obj, $sep, $offset) ) && $pos !== false );
503
- //Format condition
504
- $condition = implode(' && ', array_reverse($objs));
505
 
506
- //Wrap command in validation
507
- if ( is_string($cmd) && !empty($cmd) ) {
508
  $condition = sprintf('if ( %1$s ) { %2$s }', $condition, $cmd);
509
  }
510
  return $condition;
@@ -527,7 +555,7 @@ class SLB_Utilities {
527
  $encode = !!$encode;
528
  $validate = !!$validate;
529
 
530
- //Build parameters
531
  if ( !is_null($params) ) {
532
  if ( $encode ) {
533
  $params = json_encode($params);
@@ -571,17 +599,17 @@ class SLB_Utilities {
571
  return true;
572
  }
573
 
574
- /* Hooks */
575
 
576
  /**
577
  * Retrieve parent object
578
  * @return obj|bool Parent object (FALSE if no valid parent set)
579
  */
580
- function &get_parent() {
581
- if ( is_object($this->parent) )
582
- return $this->parent;
583
- else
584
- return false;
 
585
  }
586
 
587
  /**
@@ -592,9 +620,11 @@ class SLB_Utilities {
592
  * @return mixed Parent property value
593
  */
594
  function get_parent_property($prop, $default = '') {
595
- $p =& $this->get_parent();
596
  return ( !!$p && property_exists($p, $prop) ) ? $p->{$prop} : $default;
597
- }
 
 
598
 
599
  /**
600
  * Retrieve formatted name for internal hooks
@@ -602,14 +632,20 @@ class SLB_Utilities {
602
  * @uses self::get_parent_property() to retrieve hook prefix
603
  * @uses self::add_prefix()
604
  * @param string $tag Base tag
 
605
  * @return string Formatted hook
606
  */
607
- function get_hook($tag) {
608
- //Hook prefix
609
- $hook = $this->get_parent_property('hook_prefix', '');
 
 
 
 
 
610
  if ( !empty($hook) )
611
  $hook .= '_';
612
- //Prefix
613
  return $this->add_prefix($hook . $tag);
614
  }
615
 
@@ -618,19 +654,27 @@ class SLB_Utilities {
618
  * Namespaces $tag
619
  * @uses self::get_hook()
620
  * @see do_action()
 
621
  */
622
  function do_action($tag, $arg = '') {
 
 
 
 
 
 
623
  $args = func_get_args();
624
- $args[0] = $this->get_hook($tag);
625
  return call_user_func_array('do_action', $args);
626
  }
627
 
628
  /**
629
  * Run internal action passing arguments in array
630
  * @uses do_action_ref_array()
 
631
  */
632
- function do_action_ref_array($tag, $args) {
633
- return do_action_ref_array($this->get_hook($tag), $args);
634
  }
635
 
636
  /**
@@ -638,19 +682,27 @@ class SLB_Utilities {
638
  * Namespaces $tag
639
  * @uses self::get_hook()
640
  * @see apply_filters()
 
641
  */
642
  function apply_filters($tag, $value) {
 
 
 
 
 
 
643
  $args = func_get_args();
644
- $args[0] = $this->get_hook($tag);
645
  return call_user_func_array('apply_filters', $args);
646
  }
647
 
648
  /**
649
  * Run internal filter passing arguments in array
650
  * @uses apply_filters_ref_array()
 
651
  */
652
- function apply_filters_ref_array($tag, $args) {
653
- return apply_filters_ref_array($this->get_hook($tag), $args);
654
  }
655
 
656
  /**
@@ -658,9 +710,10 @@ class SLB_Utilities {
658
  * Namespaces $tag
659
  * @uses self::get_hook()
660
  * @see add_action()
 
661
  */
662
- function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
663
- return add_action($this->get_hook($tag), $function_to_add, $priority, $accepted_args);
664
  }
665
 
666
  /**
@@ -668,9 +721,10 @@ class SLB_Utilities {
668
  * Namespaces $tag
669
  * @uses self::get_hook()
670
  * @see add_filter()
 
671
  */
672
- function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
673
- return add_filter($this->get_hook($tag), $function_to_add, $priority, $accepted_args);
674
  }
675
 
676
  /**
@@ -678,9 +732,10 @@ class SLB_Utilities {
678
  * Namespaces $tag
679
  * @uses self::get_hook()
680
  * @uses remove_action()
 
681
  */
682
- function remove_action($tag, $function_to_remove, $priority = 10, $accepted_args = 1) {
683
- return remove_action($this->get_hook($tag), $function_to_remove, $priority, $accepted_args);
684
  }
685
 
686
  /**
@@ -688,9 +743,71 @@ class SLB_Utilities {
688
  * Namespaces $tag
689
  * @uses self::get_hook()
690
  * @uses remove_filter()
 
691
  */
692
- function remove_filter($tag, $function_to_remove, $priority = 10, $accepted_args = 1) {
693
- return remove_filter($this->get_hook($tag), $function_to_remove, $priority, $accepted_args);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
694
  }
695
 
696
  /* Meta */
@@ -777,37 +894,8 @@ class SLB_Utilities {
777
  return '_' . $this->add_prefix($text);
778
  }
779
 
780
- /*-** Request **-*/
781
-
782
- /**
783
- * Checks if the currently executing file matches specified file name
784
- * @param string $filename Filename to check for
785
- * @return bool TRUE if current page matches specified filename, FALSE otherwise
786
- */
787
- function is_current_file( $filename ) {
788
- return ( $filename == basename( $_SERVER['SCRIPT_NAME'] ) );
789
- }
790
-
791
- /**
792
- * Checks whether the current page is a management page
793
- * @return bool TRUE if current page is a management page, FALSE otherwise
794
- */
795
- function is_admin_management_page() {
796
- return ( is_admin()
797
- && ( $this->is_current_file('edit.php')
798
- || ( $this->is_current_file('admin.php')
799
- && isset($_GET['page'])
800
- && strpos($_GET['page'], 'cnr') === 0 )
801
- )
802
- );
803
- }
804
-
805
  /* Class */
806
 
807
- function is_a($obj, $class_name) {
808
- return ( is_object($obj) && is_a($obj, $this->add_prefix_uc($class_name)) ) ? true : false;
809
- }
810
-
811
  /**
812
  * Retrieve name of internal class
813
  * @param string $class Base name of class
@@ -824,38 +912,43 @@ class SLB_Utilities {
824
  * @return array Context
825
  */
826
  function get_context() {
827
- //Context
828
  static $ctx = null;
829
  if ( !is_array($ctx) ) {
830
- //Standard
831
  $ctx = array($this->build_context());
832
- //Action
833
  $action = $this->get_action();
834
  if ( !empty($action) ) {
835
  $ctx[] = $this->build_context('action', $action);
836
  }
837
- //Post type
838
  $post_type = $this->get_post_type();
839
  if ( !empty($action) ) {
840
  $ctx[] = $this->build_context('post-type', $post_type);
841
  }
842
- //Admin page
843
  if ( is_admin() ) {
844
  global $pagenow;
845
  $pg = $this->strip_file_extension($pagenow);
846
  $ctx[] = $this->build_context('page', $pg);
847
- //Query String
848
- parse_str($_SERVER['QUERY_STRING'], $qv);
849
- if ( isset($qv['page']) ) {
850
- $ctx[] = $this->build_context('page', $qv['page']);
 
 
 
 
 
851
  }
852
- //Action
853
  if ( !empty($action) ) {
854
  $ctx[] = $this->build_context('page', $pg, 'action', $action);
855
  $ctx[] = $this->build_context('post-type', $post_type, 'action', $action);
856
  }
857
  }
858
- //User
859
  $u = wp_get_current_user();
860
  $ctx[] = $this->build_context('user', ( $u->ID ) ? 'registered' : 'guest', false);
861
  }
@@ -875,16 +968,16 @@ class SLB_Utilities {
875
  */
876
  function build_context($context = null, $prefix = true) {
877
  $args = func_get_args();
878
- //Get prefix option
879
  if ( !empty($args) ) {
880
  $prefix = ( is_bool($args[count($args) - 1]) ) ? array_pop($args) : true;
881
  }
882
 
883
- //Validate
884
  $context = array_filter($args, 'is_string');
885
  $sep = '_';
886
 
887
- //Context Prefix
888
  if ( $prefix )
889
  array_unshift($context, ( is_admin() ) ? 'admin' : 'public' );
890
  return implode($sep, $context);
@@ -919,6 +1012,8 @@ class SLB_Utilities {
919
  $this->extend_client_object($ctx, true);
920
  }
921
 
 
 
922
  /**
923
  * Joins and normalizes the slashes in the paths passed to method
924
  * All forward/back slashes are converted to forward slashes
@@ -932,47 +1027,47 @@ class SLB_Utilities {
932
  $sl_f = '/';
933
  $sl_b = '\\';
934
  $parts = func_get_args();
935
- //Slash defaults (trailing, leading);
936
  $slashes = array(false, true);
937
  if ( func_num_args() > 1 ) {
938
- //Get last argument
939
  $arg_last = $parts[count($parts) - 1];
940
  if ( is_bool($arg_last) ) {
941
  $arg_last = array($arg_last);
942
  }
943
 
944
  if ( is_array($arg_last) && count($arg_last) > 0 && is_bool($arg_last[0]) ) {
945
- //Remove slash paramter from args array
946
  array_pop($parts);
947
- //Normalize slashes options
948
  if ( isset($arg_last[0]) )
949
  $slashes[0] = $arg_last[0];
950
  if ( isset($arg_last[1]) )
951
  $slashes[1] = $arg_last[1];
952
  }
953
  }
954
- //Extract to slash options local variables
955
  list($trailing_slash, $leading_slash) = $slashes;
956
 
957
- //Clean path segments
958
  foreach ( $parts as $key => $part ) {
959
- //Trim slashes/spaces
960
  $parts[$key] = trim($part, " " . $sl_f . $sl_b);
961
 
962
- //Verify path segment still contains value
963
  if ( empty($parts[$key]) ) {
964
  unset($parts[$key]);
965
  continue;
966
  }
967
  }
968
 
969
- //Join path parts together
970
  $parts = implode($sl_b, $parts);
971
  $parts = str_replace($sl_b, $sl_f, $parts);
972
- //Add trailing slash (if necessary)
973
  if ( $trailing_slash )
974
  $parts .= $sl_f;
975
- //Add leading slash (if necessary)
976
  $regex = '#^.+:[\\/]#';
977
  if ( $leading_slash && !preg_match($regex, $parts) ) {
978
  $parts = $sl_f . $parts;
@@ -983,11 +1078,12 @@ class SLB_Utilities {
983
  /**
984
  * Returns URL of file (assumes that it is in plugin directory)
985
  * @param string $file name of file get URL
 
986
  * @return string File URL
987
  */
988
- function get_file_url($file) {
989
  if ( is_string($file) && '' != trim($file) ) {
990
- $file = str_replace(' ', '%20', $this->normalize_path($this->get_url_base(), $file));
991
  }
992
  return $file;
993
  }
@@ -997,9 +1093,10 @@ class SLB_Utilities {
997
  * @param string $file file name
998
  * @return string File path
999
  */
1000
- function get_file_path($file) {
 
1001
  if ( is_string($file) && '' != trim($file) ) {
1002
- $file = $this->normalize_path($this->get_path_base(), $file);
1003
  }
1004
  return $file;
1005
  }
@@ -1020,6 +1117,15 @@ class SLB_Utilities {
1020
  return ( empty($ext) ) ? false : true;
1021
  }
1022
 
 
 
 
 
 
 
 
 
 
1023
  /**
1024
  * Retrieves file extension
1025
  * @param string $file file name/path
@@ -1029,8 +1135,13 @@ class SLB_Utilities {
1029
  function get_file_extension($file, $lowercase = true) {
1030
  $ret = '';
1031
  $sep = '.';
 
1032
  if ( !is_string($file) )
1033
  return $ret;
 
 
 
 
1034
  if ( ( $rpos = strrpos($file, $sep) ) > 0 )
1035
  $ret = substr($file, $rpos + 1);
1036
  if ( !!$lowercase )
@@ -1050,7 +1161,7 @@ class SLB_Utilities {
1050
  if ( !is_array($extension) )
1051
  $extension = array(strval($extension));
1052
  if ( !$case_sensitive ) {
1053
- //Normalize extensions
1054
  $extension = array_map('strtolower', $extension);
1055
  }
1056
  return ( in_array($this->get_file_extension($file, !$case_sensitive), $extension) ) ? true : false;
@@ -1077,12 +1188,28 @@ class SLB_Utilities {
1077
  * @uses normalize_path()
1078
  * @return string Base URL
1079
  */
1080
- function get_url_base() {
1081
- static $url_base = '';
1082
- if ( '' == $url_base ) {
1083
- $url_base = $this->normalize_path(plugins_url(), $this->get_plugin_base());
1084
  }
1085
- return $url_base;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1086
  }
1087
 
1088
  /**
@@ -1092,28 +1219,74 @@ class SLB_Utilities {
1092
  * @uses normalize_path()
1093
  * @return string Base path
1094
  */
1095
- function get_path_base() {
1096
- static $path_base = '';
1097
- if ( '' == $path_base ) {
1098
- $path_base = $this->normalize_path(WP_PLUGIN_DIR, $this->get_plugin_base());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1099
  }
1100
- return $path_base;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1101
  }
1102
 
1103
  /**
1104
  * Retrieve plugin's base directory
1105
  * @uses WP_PLUGIN_DIR
1106
- * @uses normalize_path()
 
1107
  * @return string Base directory
1108
  */
1109
- function get_plugin_base($trim = false) {
1110
- static $plugin_dir = '';
1111
- if ( '' == $plugin_dir ) {
1112
- $plugin_dir = str_replace($this->normalize_path(WP_PLUGIN_DIR), '', $this->normalize_path(dirname(dirname(__FILE__))));
1113
  }
1114
- if ( $trim )
1115
- $plugin_dir = trim($plugin_dir, ' \/');
1116
- return $plugin_dir;
1117
  }
1118
 
1119
  /**
@@ -1123,28 +1296,30 @@ class SLB_Utilities {
1123
  * @return string Base file path
1124
  */
1125
  function get_plugin_base_file() {
1126
- static $file = '';
1127
- if ( empty($file) ) {
1128
- $dir = @ opendir($this->get_path_base());
1129
  if ( $dir ) {
1130
  while ( ($ftemp = readdir($dir)) !== false ) {
1131
- //Only process PHP files
1132
  $ftemp = $this->get_file_path($ftemp);
1133
  if ( !$this->has_file_extension($ftemp, 'php') || !is_readable($ftemp) )
1134
  continue;
1135
- //Check for data
1136
- $data = get_file_data($ftemp, $this->plugin_headers);
1137
  if ( !empty($data['Name']) ) {
1138
- //Set base file
1139
- $file = $ftemp;
 
 
1140
  break;
1141
  }
1142
  }
1143
  }
1144
  @closedir($dir);
1145
  }
1146
- //Return
1147
- return $file;
1148
  }
1149
 
1150
  /**
@@ -1155,33 +1330,35 @@ class SLB_Utilities {
1155
  * @return string Internal plugin name
1156
  */
1157
  function get_plugin_base_name() {
1158
- static $name = false;
1159
- if ( !$name ) {
1160
- $file = $this->get_plugin_base_file();
1161
- $name = plugin_basename($file);
 
 
 
 
 
 
1162
  }
1163
- return $name;
1164
  }
1165
 
1166
  /**
1167
  * Retrieve plugin info
1168
  * Parses info comment in main plugin file
1169
- * @uses get_plugin_base_file()
1170
- */
1171
- function get_plugin_info($field = '') {
1172
- static $data = array();
1173
- $ret = '';
1174
- //Get plugin data
1175
- if ( empty($data) ) {
1176
- $file = $this->get_plugin_base_file();
1177
- $data = get_file_data($file, $this->plugin_headers);
1178
  }
1179
- //Return specified field
1180
  if ( !empty($field) ) {
1181
- if ( isset($data[$field]) )
1182
- $ret = $data[$field];
1183
- } else {
1184
- $ret = $data;
1185
  }
1186
  return $ret;
1187
  }
@@ -1193,33 +1370,17 @@ class SLB_Utilities {
1193
  * @return string Plugin version
1194
  */
1195
  function get_plugin_version($strip_desc = true) {
1196
- static $v = '';
1197
- //Retrieve version
1198
- if ( empty($v) ) {
1199
- $field = 'Version';
1200
- $v = $this->get_plugin_info($field);
1201
- }
1202
- //Format
1203
- $ret = $v;
1204
- if ( $strip_desc ) {
1205
  $ret = explode(' ', $ret, 2);
1206
  $ret = $ret[0];
1207
  }
1208
- //Return
1209
  return $ret;
1210
  }
1211
 
1212
- /**
1213
- * Retrieve plugin textdomain (for localization)
1214
- * @return string
1215
- */
1216
- function get_plugin_textdomain() {
1217
- static $dom = '';
1218
- if ( empty($dom) )
1219
- $dom = $this->get_plugin_base(true);
1220
- return $dom;
1221
- }
1222
-
1223
  /**
1224
  * Retrieve current post type based on URL query variables
1225
  * @return string|null Current post type
@@ -1243,15 +1404,15 @@ class SLB_Utilities {
1243
  function get_action($default = null) {
1244
  $action = '';
1245
 
1246
- //Check if action is set in URL
1247
  if ( isset($_GET['action']) )
1248
  $action = $_GET['action'];
1249
- //Otherwise, Determine action based on plugin plugin admin page suffix
1250
- elseif ( isset($_GET['page']) && ($pos = strrpos($_GET['page'], '-')) && $pos !== false && ( $pos != count($_GET['page']) - 1 ) )
1251
  $action = trim(substr($_GET['page'], $pos + 1), '-_');
1252
 
1253
- //Determine action for core admin pages
1254
- if ( ! isset($_GET['page']) || empty($action) ) {
1255
  $actions = array(
1256
  'add' => array('page-new', 'post-new'),
1257
  'edit-item' => array('page', 'post'),
@@ -1273,25 +1434,9 @@ class SLB_Utilities {
1273
 
1274
  /*-** General **-*/
1275
 
1276
- /**
1277
- * Checks if last parameter sent to a function is an array of options and returns it
1278
- * Calling function should use `func_get_args()` and pass the value to this method
1279
- * @param array $args Parameters passed to calling function
1280
- * @return array Options array (Default: empty array)
1281
- */
1282
- function func_get_options($args) {
1283
- $r = array();
1284
- if ( is_array($args) && !empty($args) ) {
1285
- $last = count($args) - 1;
1286
- if ( is_array($args[$last]) )
1287
- $r = $args[$last];
1288
- }
1289
- return $r;
1290
- }
1291
-
1292
  /**
1293
  * Checks if a property exists in a class or object
1294
- * (Compatibility method for PHP 4
1295
  * @param mixed $class Class or object to check
1296
  * @param string $property Name of property to look for in $class
1297
  */
@@ -1313,13 +1458,13 @@ class SLB_Utilities {
1313
  */
1314
  function &get_property(&$obj, $property) {
1315
  $property = trim($property);
1316
- //Object
1317
  if ( is_object($obj) )
1318
  return $obj->{$property};
1319
- //Array
1320
  if ( is_array($obj) )
1321
  return $obj[$property];
1322
- //Class
1323
  if ( is_string($obj) && class_exists($obj) ) {
1324
  $cvars = get_class_vars($obj);
1325
  if ( isset($cvars[$property]) )
@@ -1339,23 +1484,23 @@ class SLB_Utilities {
1339
  */
1340
  function array_remap($arr, $map = array(), $overwrite = false) {
1341
  if ( !empty($map) && is_array($map) ) {
1342
- //Iterate through mappings
1343
  foreach ( $map as $from => $to ) {
1344
  if ( !array_key_exists($from, $arr) )
1345
  continue;
1346
  $move = $overwrite;
1347
- //Only remap if parent property doesn't already exist in array
1348
  if ( !array_key_exists($to, $arr) )
1349
  $move = true;
1350
  if ( $move ) {
1351
- //Move member value to new key
1352
  $arr[$to] = $arr[$from];
1353
- //Remove source member
1354
  unset($arr[$from]);
1355
  }
1356
  }
1357
  }
1358
- //Return remapped properties
1359
  return $arr;
1360
  }
1361
 
@@ -1397,28 +1542,28 @@ class SLB_Utilities {
1397
  * @return array Merged array
1398
  */
1399
  function array_merge_recursive_distinct($arr1) {
1400
- //Get all arrays passed to function
1401
  $args = func_get_args();
1402
  if ( empty($args) )
1403
  return false;
1404
- //Return empty array if first parameter is not an array
1405
  if ( !is_array($args[0]) )
1406
  return array();
1407
- //Set first array as base array
1408
  $merged = $args[0];
1409
- //Iterate through arrays to merge
1410
  $arg_length = count($args);
1411
  for ( $x = 1; $x < $arg_length; $x++ ) {
1412
- //Skip if argument is not an array (only merge arrays)
1413
  if ( !is_array($args[$x]) )
1414
  continue;
1415
- //Iterate through argument items
1416
  foreach ( $args[$x] as $key => $val ) {
1417
- //Generate key for numeric indexes
1418
  if ( is_int($key) ) {
1419
- //Add new item to merged array
1420
  $merged[] = null;
1421
- //Get key of new item
1422
  $key = array_pop(array_keys($merged));
1423
  }
1424
  if ( !isset($merged[$key]) || !is_array($merged[$key]) || !is_array($val) ) {
@@ -1441,13 +1586,13 @@ class SLB_Utilities {
1441
  */
1442
  function array_replace_recursive($search, $arr_replace, $arr_subject) {
1443
  foreach ($arr_subject as $key => $val) {
1444
- //Skip element if key does not exist in the replacement array
1445
  if (!isset($arr_replace[$key]))
1446
  continue;
1447
- //If element values for both arrays are strings, replace text
1448
  if (is_string($val) && strpos($val, $search) !== false && is_string($arr_replace[$key]))
1449
  $arr_subject[$key] = str_replace($search, $arr_replace[$key], $val);
1450
- //If value in both arrays are arrays, recursively replace text
1451
  if (is_array($val) && is_array($arr_replace[$key]))
1452
  $arr_subject[$key] = $this->array_replace_recursive($search, $arr_replace[$key], $val);
1453
  }
@@ -1466,20 +1611,6 @@ class SLB_Utilities {
1466
  return eval('return isset($arr' . $f_path . ');');
1467
  }
1468
 
1469
- /**
1470
- * Returns value of item at specified path in array
1471
- * @param array $arr Array to get item from
1472
- * @param array $path Array of segments that form path to array (each array item is a deeper dimension in the array)
1473
- * @return mixed Value of item in array (Default: empty string)
1474
- */
1475
- function &get_array_item(&$arr, &$path) {
1476
- $item = '';
1477
- if ($this->array_item_isset($arr, $path)) {
1478
- eval('$item =& $arr' . $this->get_array_path($path) . ';');
1479
- }
1480
- return $item;
1481
- }
1482
-
1483
  /**
1484
  * Build formatted string based on array values
1485
  * Array values in formatted string will be ordered by index order
@@ -1488,14 +1619,14 @@ class SLB_Utilities {
1488
  * @return string Formatted string based on array values
1489
  */
1490
  function get_array_path($attribute = '', $format = null) {
1491
- //Formatted value
1492
  $fmtd = '';
1493
  if (!empty($attribute)) {
1494
- //Make sure attribute is array
1495
  if (!is_array($attribute)) {
1496
  $attribute = array($attribute);
1497
  }
1498
- //Format attribute
1499
  $format = strtolower($format);
1500
  switch ($format) {
1501
  case 'id':
@@ -1503,14 +1634,14 @@ class SLB_Utilities {
1503
  break;
1504
  case 'metadata':
1505
  case 'attribute':
1506
- //Join segments
1507
  $delim = '_';
1508
  $fmtd = implode($delim, $attribute);
1509
- //Replace white space and repeating delimiters
1510
  $fmtd = str_replace(' ', $delim, $fmtd);
1511
  while (strpos($fmtd, $delim.$delim) !== false)
1512
  $fmtd = str_replace($delim.$delim, $delim, $fmtd);
1513
- //Prefix formatted value with delimeter for metadata keys
1514
  if ('metadata' == $format)
1515
  $fmtd = $delim . $fmtd;
1516
  break;
@@ -1533,17 +1664,17 @@ class SLB_Utilities {
1533
  $path = array();
1534
  $args = func_get_args();
1535
 
1536
- //Iterate through parameters and build path
1537
  foreach ( $args as $arg ) {
1538
  if ( empty($arg) )
1539
  continue;
1540
 
1541
  if (is_array($arg)) {
1542
- //Recurse through array items to pull out any more arrays
1543
  foreach ($arg as $key => $val) {
1544
  $path = array_merge($path, $this->build_path($val));
1545
  }
1546
- //$path = array_merge($path, array_values($arg));
1547
  } elseif ( is_scalar($arg) ) {
1548
  $path[] = $arg;
1549
  }
@@ -1552,6 +1683,51 @@ class SLB_Utilities {
1552
  return $path;
1553
  }
1554
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1555
  /**
1556
  * Parse string of attributes into array
1557
  * For XML/XHTML tag attributes
@@ -1561,11 +1737,11 @@ class SLB_Utilities {
1561
  function parse_attribute_string($txt, $defaults = array()) {
1562
  $txt = trim($txt, ' >');
1563
  $matches = $attr = array();
1564
- //Strip tag
1565
  if ( $txt[0] == '<' && ($s = strpos($txt, ' ')) && $s !== false ) {
1566
  $txt = trim(substr($txt, $s + 1));
1567
  }
1568
- //Parse attributes
1569
  $rgx = "/\b(\w+.*?)=([\"'])(.*?)\\2(?:\s|$)/i";
1570
  preg_match_all($rgx, $txt, $matches);
1571
  if ( count($matches) > 3 ) {
@@ -1574,7 +1750,7 @@ class SLB_Utilities {
1574
  $attr[trim($val)] = trim($matches[3][$sub_idx]);
1575
  }
1576
  }
1577
- //Destroy parsing vars
1578
  unset($txt, $matches);
1579
 
1580
  return array_merge($defaults, $attr);
@@ -1587,9 +1763,10 @@ class SLB_Utilities {
1587
  */
1588
  function build_attribute_string($attr) {
1589
  $ret = '';
1590
- if ( is_object($attr) )
1591
  $attr = (array) $attr;
1592
- if ( is_array($attr) ) {
 
1593
  array_map('esc_attr', $attr);
1594
  $attr_str = array();
1595
  foreach ( $attr as $key => $val ) {
@@ -1600,6 +1777,36 @@ class SLB_Utilities {
1600
  return $ret;
1601
  }
1602
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1603
  function build_html_link($uri, $content, $attributes = array()) {
1604
  $attributes = array_merge(array('href' => $uri, 'title' => $content), $attributes);
1605
  return $this->build_html_element(array('tag' => 'a', 'wrap' => true, 'content' => $content, 'attributes' => $attributes));
@@ -1610,7 +1817,7 @@ class SLB_Utilities {
1610
  * @param $url Stylesheet URL
1611
  * @return string Stylesheet element
1612
  */
1613
- function build_stylesheet_element($url = '') {
1614
  $attributes = array('href' => $url, 'type' => 'text/css', 'rel' => 'stylesheet');
1615
  return $this->build_html_element(array('tag' => 'link', 'wrap' => false, 'attributes' => $attributes));
1616
  }
@@ -1624,9 +1831,9 @@ class SLB_Utilities {
1624
  * @param bool $wait_doc_ready (optional) Wait until document is fully loaded before executing commands? (Default: No)
1625
  */
1626
  function build_script_element($content = '', $id = '', $wrap_jquery = true, $wait_doc_ready = false) {
1627
- //Stop processing invalid content
1628
  if ( is_array($content) && !empty($content) ) {
1629
- $content = implode(PHP_EOL, $content);
1630
  }
1631
  if ( empty($content) || !is_string($content) ) {
1632
  return '';
@@ -1635,269 +1842,22 @@ class SLB_Utilities {
1635
  $start = array('/* <![CDATA[ */');
1636
  $end = array('/* ]]> */');
1637
  if ( $wrap_jquery ) {
1638
- $start[] = '(function($){';
1639
- $end[] = '})(jQuery);';
1640
 
1641
- //Add event handler (if necessary)
1642
  if ( $wait_doc_ready ) {
1643
  $start[] = '$(document).ready(function(){';
1644
  $end[] = '})';
1645
  }
1646
  }
1647
 
1648
- //Reverse order of end values
1649
  $end = array_reverse($end);
1650
  $content = implode('', array_merge($start, array($content), $end));
1651
  if ( is_string($id) && !empty($id) ) {
1652
  $attributes['id'] = $this->add_prefix($id);
1653
  }
1654
- return $this->build_html_element(array('tag' => 'script', 'content' => $content, 'attributes' => $attributes)) . PHP_EOL;
1655
- }
1656
-
1657
- /**
1658
- * Generate external script element
1659
- * @param $url Script URL
1660
- * @return string Script element
1661
- */
1662
- function build_ext_script_element($url = '') {
1663
- $attributes = array('src' => $url, 'type' => 'text/javascript');
1664
- return $this->build_html_element(array('tag' => 'script', 'attributes' => $attributes)) . PHP_EOL;
1665
- }
1666
-
1667
- /**
1668
- * Generate HTML element based on values
1669
- * @param $args Element arguments
1670
- * @return string Generated HTML element
1671
- */
1672
- function build_html_element($args) {
1673
- $defaults = array(
1674
- 'tag' => 'span',
1675
- 'wrap' => true,
1676
- 'content' => '',
1677
- 'attributes' => array()
1678
- );
1679
- $el_start = '<';
1680
- $el_end = '>';
1681
- $el_close = '/';
1682
- $args = wp_parse_args($args, $defaults);
1683
- //Collect attributes
1684
- $attr_exclude = array( 'content', 'tag', 'wrap', 'attributes' );
1685
- $attr_extra = array_diff_key($args, array_fill_keys($attr_exclude, null));
1686
- if ( count($attr_extra) ) {
1687
- //Merge attributes
1688
- $args['attributes'] = wp_parse_args($attr_extra, $args['attributes']);
1689
- //Remove attributes from top-level arguments
1690
- $args = array_diff_key($args, $attr_extra);
1691
- }
1692
- extract($args, EXTR_SKIP);
1693
- $content = trim($content);
1694
-
1695
-
1696
- if ( !$wrap && strlen($content) > 0 )
1697
- $wrap = true;
1698
-
1699
- $attributes = $this->build_attribute_string($attributes);
1700
- if ( strlen($attributes) > 0 )
1701
- $attributes = ' ' . $attributes;
1702
-
1703
- $ret = $el_start . $tag . $attributes;
1704
-
1705
- if ( $wrap )
1706
- $ret .= $el_end . $content . $el_start . $el_close . $tag;
1707
- else
1708
- $ret .= ' ' . $el_close;
1709
-
1710
- $ret .= $el_end;
1711
- return $ret;
1712
- }
1713
-
1714
- /*-** Admin **-*/
1715
-
1716
- /**
1717
- * Add submenu page in the admin menu
1718
- * Adds ability to set the position of the page in the menu
1719
- * @see add_submenu_page (Wraps functionality)
1720
- *
1721
- * @param $parent
1722
- * @param $page_title
1723
- * @param $menu_title
1724
- * @param $access_level
1725
- * @param $file
1726
- * @param $function
1727
- * @param int $pos Index position of menu page
1728
- *
1729
- * @global array $submenu Admin page submenus
1730
- */
1731
- function add_submenu_page($parent, $page_title, $menu_title, $capability, $file, $function = '', $pos = false) {
1732
- //Add submenu page as usual
1733
- $args = func_get_args();
1734
- $hookname = call_user_func_array('add_submenu_page', $args);
1735
- if ( is_int($pos) ) {
1736
- global $submenu;
1737
- //Get last submenu added
1738
- $parent = $this->get_submenu_parent_file($parent);
1739
- if ( isset($submenu[$parent]) ) {
1740
- $subs =& $submenu[$parent];
1741
-
1742
- //Make sure menu isn't already in the desired position
1743
- if ( $pos <= ( count($subs) - 1 ) ) {
1744
- //Get submenu that was just added
1745
- $sub = array_pop($subs);
1746
- //Insert into desired position
1747
- if ( 0 == $pos ) {
1748
- array_unshift($subs, $sub);
1749
- } else {
1750
- $top = array_slice($subs, 0, $pos);
1751
- $bottom = array_slice($subs, $pos);
1752
- array_push($top, $sub);
1753
- $subs = array_merge($top, $bottom);
1754
- }
1755
- }
1756
- }
1757
- }
1758
-
1759
- return $hookname;
1760
- }
1761
-
1762
- /**
1763
- * Remove admin submenu
1764
- * @param string $parent Submenu parent file
1765
- * @param string $file Submenu file name
1766
- * @return int|null Index of removed submenu (NULL if submenu not found)
1767
- *
1768
- * @global array $submenu
1769
- * @global array $_registered_pages
1770
- */
1771
- function remove_submenu_page($parent, $file) {
1772
- global $submenu, $_registered_pages;
1773
- $ret = null;
1774
-
1775
- $parent = $this->get_submenu_parent_file($parent);
1776
- $file = plugin_basename($file);
1777
- $file_index = 2;
1778
-
1779
- //Find submenu
1780
- if ( isset($submenu[$parent]) ) {
1781
- $subs =& $submenu[$parent];
1782
- for ($x = 0; $x < count($subs); $x++) {
1783
- if ( $subs[$x][$file_index] == $file ) {
1784
- //Remove matching submenu
1785
- $hookname = get_plugin_page_hookname($file, $parent);
1786
- remove_all_actions($hookname);
1787
- unset($_registered_pages[$hookname]);
1788
- unset($subs[$x]);
1789
- $subs = array_values($subs);
1790
- //Set index and stop processing
1791
- $ret = $x;
1792
- break;
1793
- }
1794
- }
1795
- }
1796
-
1797
- return $ret;
1798
- }
1799
-
1800
- /**
1801
- * Replace a submenu page
1802
- * Adds a submenu page in the place of an existing submenu page that has the same $file value
1803
- *
1804
- * @param $parent
1805
- * @param $page_title
1806
- * @param $menu_title
1807
- * @param $access_level
1808
- * @param $file
1809
- * @param $function
1810
- * @return string Hookname
1811
- *
1812
- * @global array $submenu
1813
- */
1814
- function replace_submenu_page($parent, $page_title, $menu_title, $access_level, $file, $function = '') {
1815
- global $submenu;
1816
- //Remove matching submenu (if exists)
1817
- $pos = $this->remove_submenu_page($parent, $file);
1818
- //Insert submenu page
1819
- $hookname = $this->add_submenu_page($parent, $page_title, $menu_title, $access_level, $file, $function, $pos);
1820
- return $hookname;
1821
- }
1822
-
1823
- /**
1824
- * Retrieves parent file for submenu
1825
- * @param string $parent Parent file
1826
- * @return string Formatted parent file name
1827
- *
1828
- * @global array $_wp_real_parent_file;
1829
- */
1830
- function get_submenu_parent_file($parent) {
1831
- global $_wp_real_parent_file;
1832
- $parent = plugin_basename($parent);
1833
- if ( isset($_wp_real_parent_file[$parent]) )
1834
- $parent = $_wp_real_parent_file[$parent];
1835
- return $parent;
1836
- }
1837
-
1838
- /* Shortcodes */
1839
-
1840
- /**
1841
- * Generate shortcode to be used in content
1842
- * @param string $tag Shortcode tag
1843
- * @param array $attr Associative array of attributes
1844
- * @return string Shortcode markup
1845
- */
1846
- public function make_shortcode($tag, $attr = array()) {
1847
- return '[' . $tag . ']';
1848
- }
1849
-
1850
- /**
1851
- * Build shortcode regex pattern for specific shortcode
1852
- * @uses $shortcode_tags
1853
- * @param string $tag Shortcode tag
1854
- * @return string Shortcode regex pattern
1855
- */
1856
- public function get_shortcode_regex($tag) {
1857
- global $shortcode_tags;
1858
- //Backup shortcodes
1859
- $tgs_temp = $shortcode_tags;
1860
- $ret = '';
1861
- if ( !is_string($tag) || empty($tag) ) {
1862
- return $ret;
1863
- }
1864
- //Modify
1865
- $shortcode_tags = array( $tag => null );
1866
- //Build pattern
1867
- $ret = get_shortcode_regex();
1868
- //Restore shortcodes
1869
- $shortcode_tags = $tgs_temp;
1870
-
1871
- return $ret;
1872
- }
1873
- /**
1874
- * Check if content contains shortcode
1875
- * @param string $tag Name of shortcode to check for
1876
- * @param string $content Content to check for shortcode
1877
- * @return bool TRUE if content contains shortcode
1878
- */
1879
- public function has_shortcode($content, $tag) {
1880
- $ptn = $this->get_shortcode_regex($tag);
1881
- $ret = ( is_string($content) && preg_match("/$ptn/s", $content) == 1 ) ? true : false;
1882
- return $ret;
1883
- }
1884
-
1885
- /**
1886
- * Add shortcode to content
1887
- * @param string $content Content to add shortcode to
1888
- * @param bool $in_footer (optional) Add shortcode to head or footer of content (Default: footer)
1889
- * @return string Modified content
1890
- */
1891
- public function add_shortcode($content, $tag, $attr = null, $in_footer = true) {
1892
- if ( !is_string($content) ) {
1893
- $content = '';
1894
- }
1895
- $sc = $this->make_shortcode($tag, $attr);
1896
- if ( !!$in_footer ) {
1897
- $content .= $sc;
1898
- } else {
1899
- $content = $sc . $content;
1900
- }
1901
- return $content;
1902
  }
1903
  }
16
  * Instance parent
17
  * @var object
18
  */
19
+ private $_parent = null;
20
+
21
+ /**
22
+ * Plugin Base
23
+ * @var string
24
+ */
25
+ private $_plugin = array(
26
+ 'base' => null,
27
+ 'file' => null,
28
+ 'name' => null,
29
+ 'data' => null,
30
+ 'uri' => null,
31
+ 'headers' => array (
32
+ 'Name' => 'Plugin Name',
33
+ 'PluginURI' => 'Plugin URI',
34
+ 'SupportURI' => 'Support URI',
35
+ 'Version' => 'Version',
36
+ 'Description' => 'Description',
37
+ 'Author' => 'Author',
38
+ 'AuthorURI' => 'Author URI',
39
+ )
40
+ );
41
 
42
  /**
43
+ * Plugin base path
44
+ * @var string
45
  */
46
+ private $_path_base = null;
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  /**
49
  * Standard hook priorities
50
  * @var array
51
  */
52
+ private $_priorities = array (
53
  'high' => 1,
54
  'low' => 99,
55
  'safe' => 15,
58
 
59
  /* Constructors */
60
 
61
+ function __construct($obj) {
62
+ if ( is_object($obj) ) {
63
+ $this->_parent = $obj;
64
+ }
65
  }
66
 
67
  /**
104
  */
105
  function get_prefix($sep = null) {
106
  $sep = $this->get_sep($sep);
107
+ $prefix = ( !empty($this->_parent->prefix) ) ? $this->_parent->prefix . $sep : '';
108
  return $prefix;
109
  }
110
 
111
  /**
112
  * Check if a string is prefixed
113
+ * @param string|array $text Text to check for prefix
114
  * @param string $sep (optional) Separator used
115
  */
116
  function has_prefix($text, $sep = null) {
117
+ if ( empty($text) )
118
+ return false;
119
+ if ( !is_array($text) )
120
+ $text = array($text);
121
+ $text = array_values($text);
122
+ $text = $text[0];
123
  return ( !empty($text) && stripos($text, $this->get_prefix($sep)) === 0 );
124
  }
125
 
126
  /**
127
  * Prepend plugin prefix to some text
128
+ * @param string|array $text Text to add to prefix
129
  * @param string $sep (optional) Text used to separate prefix and text
130
  * @param bool $once (optional) Whether to add prefix to text that already contains a prefix or not
131
  * @return string Text with prefix prepended
132
  */
133
  function add_prefix($text, $sep = '_', $once = true) {
134
+ // Normalize data type (array)
135
+ if ( empty($text) )
136
+ $text = array('');
137
+ if ( !is_array($text) )
138
+ $text = array($text);
139
+ // Add prefix (if necessary)
140
+ if ( !$once || ( $once && !$this->has_prefix($text, $sep) ) )
141
+ array_unshift($text, $this->get_prefix());
142
+ return implode($sep, $text);
143
  }
144
 
145
  /**
146
  * Prepend uppercased plugin prefix to some text
147
+ * @param string|array $text Text to add to prefix
148
  * @param string $sep (optional) Text used to separate prefix and text
149
  * @param bool $once (optional) Whether to add prefix to text that already contains a prefix or not
150
  * @return string Text with prefix prepended
199
  */
200
  public function priority($id = null) {
201
  $pri = 10;
202
+ if ( !is_null($id) && array_key_exists($id, $this->_priorities) ) {
203
+ $pri = $this->_priorities[$id];
204
  }
205
  return $pri;
206
  }
208
  /* Wrapped Values */
209
 
210
  /**
211
+ * Create wrapper object
212
+ * Properties
213
+ * > start
214
+ * > end
215
  * @param string|array $start Start text (Can also be array defining start & end values)
216
  * @param string $end (optional) End text
217
  * If $end not defined, then $start is used
218
  * @return obj Wrapper
219
  */
220
  function get_wrapper($start = null, $end = null) {
221
+ // Validate existing wrapper
222
  if ( is_object($start) && isset($start->start) && isset($start->end) )
223
  return $start;
 
 
 
 
 
 
 
 
 
 
 
 
224
 
225
+ // Initialize wrapper
226
+ $w = array (
227
+ 'start' => '[',
228
+ 'end' => ']',
229
+ );
230
+
231
+ if ( !empty($start) ) {
232
+ if ( is_string($start) ) {
233
+ $w['start'] = $start;
234
+ } elseif ( is_array($start) ) {
235
+ $start = array_values($start);
236
+ if ( is_string($start) ) {
237
+ $w['start'] = $start[0];
238
+ }
239
+ if ( isset($start[1]) && is_string($start[1]) ) {
240
+ $w['end'] = $start[1];
241
+ }
242
+ }
243
+ }
244
+ if ( is_string($end) ) {
245
+ $w['end'] = $end;
246
+ }
247
+
248
+ return (object) $w;
249
  }
250
 
251
  /**
258
  function has_wrapper($text, $start = null, $end = null) {
259
  if ( !is_string($text) || empty($text) )
260
  return false;
261
+ // Validate wrapper
262
  $w = $this->get_wrapper($start, $end);
263
 
264
+ // Check for wrapper
265
+ return ( substr($text, 0, strlen($w->start)) == $w->start && substr($text, -1, strlen($w->end)) == $w->end ) ? true : false;
266
  }
267
 
268
  /**
285
 
286
  /**
287
  * Add wrapper to specified text
288
+ * @uses Utilities::get_wrapper() to retrieve wrapper object
289
  * @param string $text Text to wrap
290
  * @param string|array $start (optional) Start text (Array defines both start/end text)
291
  * @param string $end (optional) End text
322
  */
323
  function parse_client_files($files, $type = 'scripts') {
324
  if ( is_array($files) && !empty($files) ) {
325
+ // Defaults
326
  $defaults = array(
327
  'file' => null,
328
  'deps' => array(),
329
  'callback' => null,
330
  'context' => array(),
331
  'enqueue' => true,
332
+ 'enqueued' => false
333
  );
334
  switch ( $type ) {
335
  case 'styles':
338
  default:
339
  $defaults['in_footer'] = false;
340
  }
341
+ // Iterate through files
342
+ /**
343
+ * $h (string) handle
344
+ * $p (array) properties
345
+ */
346
  foreach ( $files as $h => $p ) {
347
  unset($file, $cb, $ctxs, $ctx);
348
+ // Set ID
349
  $p['id'] = $this->add_prefix($h);
350
+ // Type Validation
351
+ /**
352
+ * $m (string) property name
353
+ * $d (mixed) default value
354
+ */
355
  foreach ( $defaults as $m => $d ) {
356
+ // Check if value requires validation
357
  if ( !is_array($d) || !isset($p[$m]) || is_array($p[$m]) )
358
  continue;
359
+ // Wrap value in array or destroy it
360
  if ( is_scalar($p[$m]) )
361
  $p[$m] = array($p[$m]);
362
  else
363
  unset($p[$m]);
364
  }
365
 
366
+ // Normalize file properties
367
  $p = array_merge($defaults, $p);
368
 
369
  /* File name */
370
 
371
+ // Validate file
372
  $file =& $p['file'];
373
 
374
+ // Determine if filename or callback
375
  if ( !$this->is_file($file) )
376
+ $file = ( is_callable($file) ) ? $file : null;
377
+ // Remove invalid file and move on to next
378
  if ( empty($file) ) {
379
  unset($files[$h]);
380
  continue;
382
 
383
  /* Dependencies */
384
 
385
+ // Format internal dependencies
386
  foreach ( $p['deps'] as $idx => $dep ) {
387
  if ( $this->has_wrapper($dep) ) {
388
  $dep = $this->remove_wrapper($dep);
392
 
393
  /* Context */
394
 
395
+ // Validate callback
396
  $cb =& $p['callback'];
397
+ if ( !is_null($cb) && !is_callable($cb) ) {
398
+ // Remove files with invalid callbacks (will never be loaded)
399
+ unset($files[$h]);
400
+ continue;
 
 
 
401
  }
402
 
403
+ // Validate contexts
404
  $ctxs =& $p['context'];
405
  $ctxs = array_unique($ctxs);
406
  $has_contexts = ( count($ctxs) > 0 ) ? true : false;
407
  foreach ( $ctxs as $idx => $ctx ) {
408
+ // Convert to array
409
  $ctx = array_values( array_slice( (array) $ctx, 0, 2 ) );
410
  switch ( count($ctx) ) {
411
  case 1 :
412
+ // Simple context
413
  $ctx = $ctx[0];
414
  break;
415
  case 2 :
416
+ // Context + Callback
417
+ if ( is_callable($ctx[1]) ) {
 
418
  break;
419
  }
420
+ // Continue to default case if callback is invalid
421
  default :
422
+ // Context is invalid
423
  $ctx = false;
424
  break;
425
  }
426
 
427
+ // Remove invalid contexts
428
  if ( empty($ctx) ) {
429
  unset($ctxs[$idx]);
430
  } else {
431
  $ctxs[$idx] = $ctx;
432
  }
433
  }
434
+ // Remove file if all specified contexts invalid (no context is OK)
435
  if ( $has_contexts && empty($ctxs) ) {
436
  unset($files[$h]);
437
  continue;
440
 
441
  /* Finalize Properties */
442
 
443
+ // Convert properties to object
444
  $files[$h] = (object) $p;
445
  }
446
  }
447
+ // Cast to object before returning
448
  $files = (object) $files;
449
  return $files;
450
  }
451
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
  /**
453
  * Build JS client object
454
  * @param string (optional) $path Additional object path
472
  * @return string JS expression to extend client object
473
  */
474
  function extend_client_object($obj, $data = null, $out = false) {
475
+ // Validate parameters
476
  $args = func_get_args();
477
  switch ( count($args) ) {
478
  case 2:
487
  $obj = null;
488
  break;
489
  }
490
+ // Default client object
491
  if ( !is_string($obj) || empty($obj) )
492
  $obj = null;
493
+ // Default data
494
  if ( is_array($data) ) {
495
  $data = (object)$data;
496
  }
497
+ // Build expression
498
  if ( empty($data) || ( empty($obj) && is_scalar($data) ) ) {
499
  $ret = '';
500
  } else {
515
  * If no command is specified the validation conditions are returned
516
  */
517
  public function validate_client_object($obj, $cmd = null) {
518
+ // Get base object
519
+ $base = $this->get_client_object();
520
+
521
+ // Build condition
522
  $sep = '.';
523
+ $obj = trim($obj, $sep);
524
+ // Strip base object
525
+ if ( 0 === strpos($obj, $base . $sep) ) {
526
+ $obj = substr($obj, strlen($base . $sep));
527
+ }
528
+ $fmt = '!!window.%1$s';
529
+ if ( !empty($obj) ) {
530
+ $fmt .= ' && %1$s.has_child(\'%2$s\')';
531
+ }
532
+ $condition = sprintf($fmt, $base, $obj);
 
 
 
533
 
534
+ // Wrap command in validation
535
+ if ( !empty($cmd) && is_string($cmd) ) {
536
  $condition = sprintf('if ( %1$s ) { %2$s }', $condition, $cmd);
537
  }
538
  return $condition;
555
  $encode = !!$encode;
556
  $validate = !!$validate;
557
 
558
+ // Build parameters
559
  if ( !is_null($params) ) {
560
  if ( $encode ) {
561
  $params = json_encode($params);
599
  return true;
600
  }
601
 
 
602
 
603
  /**
604
  * Retrieve parent object
605
  * @return obj|bool Parent object (FALSE if no valid parent set)
606
  */
607
+ function get_parent() {
608
+ if ( is_object($this->_parent) ) {
609
+ return $this->_parent;
610
+ } else {
611
+ return false;
612
+ }
613
  }
614
 
615
  /**
620
  * @return mixed Parent property value
621
  */
622
  function get_parent_property($prop, $default = '') {
623
+ $p = $this->get_parent();
624
  return ( !!$p && property_exists($p, $prop) ) ? $p->{$prop} : $default;
625
+ }
626
+
627
+ /* Hooks */
628
 
629
  /**
630
  * Retrieve formatted name for internal hooks
632
  * @uses self::get_parent_property() to retrieve hook prefix
633
  * @uses self::add_prefix()
634
  * @param string $tag Base tag
635
+ * @param bool|string $hook_prefix (optional) Secondary prefix to use for hook (Default: Use predefined hook name, FALSE: no secondary hook)
636
  * @return string Formatted hook
637
  */
638
+ function get_hook($tag, $hook_prefix = true) {
639
+ // Hook prefix
640
+ $hook = '';
641
+ if ( is_bool($hook_prefix) && $hook_prefix ) {
642
+ $hook = $this->get_parent_property('hook_prefix', '');
643
+ } elseif ( is_string($hook_prefix) ) {
644
+ $hook = $hook_prefix;
645
+ }
646
  if ( !empty($hook) )
647
  $hook .= '_';
648
+ // Prefix
649
  return $this->add_prefix($hook . $tag);
650
  }
651
 
654
  * Namespaces $tag
655
  * @uses self::get_hook()
656
  * @see do_action()
657
+ * @param string|array $tag Action hook. If array, get hook prefix
658
  */
659
  function do_action($tag, $arg = '') {
660
+ // Handle hook prefix
661
+ $hook_prefix = true;
662
+ if ( is_array($tag) ) {
663
+ $hook_prefix = $tag[1];
664
+ $tag = $tag[0];
665
+ }
666
  $args = func_get_args();
667
+ $args[0] = $this->get_hook($tag, $hook_prefix);
668
  return call_user_func_array('do_action', $args);
669
  }
670
 
671
  /**
672
  * Run internal action passing arguments in array
673
  * @uses do_action_ref_array()
674
+ * @param bool|string $hook_prefix (optional) Secondary prefix to use for hook (Default: Use predefined hook name, FALSE: no secondary hook)
675
  */
676
+ function do_action_ref_array($tag, $args, $hook_prefix = true) {
677
+ return do_action_ref_array($this->get_hook($tag, $hook_prefix), $args);
678
  }
679
 
680
  /**
682
  * Namespaces $tag
683
  * @uses self::get_hook()
684
  * @see apply_filters()
685
+ * @param string|array $tag Action hook. If array, get hook prefix
686
  */
687
  function apply_filters($tag, $value) {
688
+ // Handle hook prefix
689
+ $hook_prefix = true;
690
+ if ( is_array($tag) ) {
691
+ $hook_prefix = $tag[1];
692
+ $tag = $tag[0];
693
+ }
694
  $args = func_get_args();
695
+ $args[0] = $this->get_hook($tag, $hook_prefix);
696
  return call_user_func_array('apply_filters', $args);
697
  }
698
 
699
  /**
700
  * Run internal filter passing arguments in array
701
  * @uses apply_filters_ref_array()
702
+ * @param bool|string $hook_prefix (optional) Secondary prefix to use for hook (Default: Use predefined hook name, FALSE: no secondary hook)
703
  */
704
+ function apply_filters_ref_array($tag, $args, $hook_prefix = true) {
705
+ return apply_filters_ref_array($this->get_hook($tag, $hook_prefix), $args);
706
  }
707
 
708
  /**
710
  * Namespaces $tag
711
  * @uses self::get_hook()
712
  * @see add_action()
713
+ * @param bool|string $hook_prefix (optional) Secondary prefix to use for hook (Default: Use predefined hook name, FALSE: no secondary hook)
714
  */
715
+ function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1, $hook_prefix = true) {
716
+ return add_action($this->get_hook($tag, $hook_prefix), $function_to_add, $priority, $accepted_args);
717
  }
718
 
719
  /**
721
  * Namespaces $tag
722
  * @uses self::get_hook()
723
  * @see add_filter()
724
+ * @param bool|string $hook_prefix (optional) Secondary prefix to use for hook (Default: Use predefined hook name, FALSE: no secondary hook)
725
  */
726
+ function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1, $hook_prefix = true) {
727
+ return add_filter($this->get_hook($tag, $hook_prefix), $function_to_add, $priority, $accepted_args);
728
  }
729
 
730
  /**
732
  * Namespaces $tag
733
  * @uses self::get_hook()
734
  * @uses remove_action()
735
+ * @param bool|string $hook_prefix (optional) Secondary prefix to use for hook (Default: Use predefined hook name, FALSE: no secondary hook)
736
  */
737
+ function remove_action($tag, $function_to_remove, $priority = 10, $accepted_args = 1, $hook_prefix = true) {
738
+ return remove_action($this->get_hook($tag, $hook_prefix), $function_to_remove, $priority, $accepted_args);
739
  }
740
 
741
  /**
743
  * Namespaces $tag
744
  * @uses self::get_hook()
745
  * @uses remove_filter()
746
+ * @param bool|string $hook_prefix (optional) Secondary prefix to use for hook (Default: Use predefined hook name, FALSE: no secondary hook)
747
  */
748
+ function remove_filter($tag, $function_to_remove, $priority = 10, $accepted_args = 1, $hook_prefix = true) {
749
+ return remove_filter($this->get_hook($tag, $hook_prefix), $function_to_remove, $priority, $accepted_args);
750
+ }
751
+
752
+ /* Shortcode */
753
+
754
+ /**
755
+ * Process specific shortcode(s) in content
756
+ * Default: Process all existing shortcodes
757
+ * @uses $shortcode_tags - array tag => callback
758
+ * @uses do_shortcode()
759
+ *
760
+ * @param string $content Content to process for shortcodes
761
+ * @param string|array $shortcode Single tag or array of tags to process
762
+ * > Associative array sets temporary callbacks for shortcodes (`tag => callback`)
763
+ */
764
+ public function do_shortcode($content, $shortcode = null) {
765
+ global $shortcode_tags;
766
+
767
+ // Process custom shortcodes
768
+ if ( !is_null($shortcode) ) {
769
+ // Cast to array
770
+ $shortcode = (array) $shortcode;
771
+ // Backup and reset shortcode handlers
772
+ $tags_temp = $shortcode_tags;
773
+ $shortcode_tags = array();
774
+ // Register specified tags
775
+ foreach ( $shortcode as $key => $val ) {
776
+ if ( is_string($key) && is_callable($val) ) {
777
+ // Tag w/custom callback
778
+ $shortcode_tags[$key] = $val;
779
+ } elseif ( is_int($key) && is_string($val) && isset($tags_temp[$val]) ) {
780
+ // Tag with default callback
781
+ $shortcode_tags[$val] = $tags_temp[$val];
782
+ }
783
+ }
784
+ }
785
+
786
+ // Process shortcodes in content
787
+ $content = do_shortcode($content);
788
+
789
+ // Restore default shortcode handlers
790
+ if ( isset($tags_temp) ) {
791
+ $shortcode_tags = $tags_temp;
792
+ unset($tags_temp);
793
+ }
794
+
795
+ return $content;
796
+ }
797
+
798
+ /**
799
+ * Build shortcode tag
800
+ * @param string $tag Shortcode base
801
+ * @param array (optional) $attr Shortcode attributes
802
+ * @param string (optional) $content Shortcode content
803
+ * @return string Shortcode tag
804
+ */
805
+ public function make_shortcode($tag, $attr = null, $content = null) {
806
+ return $this->build_element(array (
807
+ 'tag' => $tag,
808
+ 'attributes' => $attr,
809
+ 'content' => $content,
810
+ ));
811
  }
812
 
813
  /* Meta */
894
  return '_' . $this->add_prefix($text);
895
  }
896
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
897
  /* Class */
898
 
 
 
 
 
899
  /**
900
  * Retrieve name of internal class
901
  * @param string $class Base name of class
912
  * @return array Context
913
  */
914
  function get_context() {
915
+ // Context
916
  static $ctx = null;
917
  if ( !is_array($ctx) ) {
918
+ // Standard
919
  $ctx = array($this->build_context());
920
+ // Action
921
  $action = $this->get_action();
922
  if ( !empty($action) ) {
923
  $ctx[] = $this->build_context('action', $action);
924
  }
925
+ // Post type
926
  $post_type = $this->get_post_type();
927
  if ( !empty($action) ) {
928
  $ctx[] = $this->build_context('post-type', $post_type);
929
  }
930
+ // Admin page
931
  if ( is_admin() ) {
932
  global $pagenow;
933
  $pg = $this->strip_file_extension($pagenow);
934
  $ctx[] = $this->build_context('page', $pg);
935
+ // Query String
936
+ if ( isset($_SERVER['QUERY_STRING']) ) {
937
+ parse_str($_SERVER['QUERY_STRING'], $qv);
938
+ if ( isset($qv['page']) ) {
939
+ $ctx[] = $this->build_context('page', $qv['page']);
940
+ if ( stripos($qv['page'], $this->get_prefix()) === 0 ) {
941
+ $ctx[] = $this->build_context('page', $this->get_prefix());
942
+ }
943
+ }
944
  }
945
+ // Action
946
  if ( !empty($action) ) {
947
  $ctx[] = $this->build_context('page', $pg, 'action', $action);
948
  $ctx[] = $this->build_context('post-type', $post_type, 'action', $action);
949
  }
950
  }
951
+ // User
952
  $u = wp_get_current_user();
953
  $ctx[] = $this->build_context('user', ( $u->ID ) ? 'registered' : 'guest', false);
954
  }
968
  */
969
  function build_context($context = null, $prefix = true) {
970
  $args = func_get_args();
971
+ // Get prefix option
972
  if ( !empty($args) ) {
973
  $prefix = ( is_bool($args[count($args) - 1]) ) ? array_pop($args) : true;
974
  }
975
 
976
+ // Validate
977
  $context = array_filter($args, 'is_string');
978
  $sep = '_';
979
 
980
+ // Context Prefix
981
  if ( $prefix )
982
  array_unshift($context, ( is_admin() ) ? 'admin' : 'public' );
983
  return implode($sep, $context);
1012
  $this->extend_client_object($ctx, true);
1013
  }
1014
 
1015
+ /* Path */
1016
+
1017
  /**
1018
  * Joins and normalizes the slashes in the paths passed to method
1019
  * All forward/back slashes are converted to forward slashes
1027
  $sl_f = '/';
1028
  $sl_b = '\\';
1029
  $parts = func_get_args();
1030
+ // Slash defaults (trailing, leading);
1031
  $slashes = array(false, true);
1032
  if ( func_num_args() > 1 ) {
1033
+ // Get last argument
1034
  $arg_last = $parts[count($parts) - 1];
1035
  if ( is_bool($arg_last) ) {
1036
  $arg_last = array($arg_last);
1037
  }
1038
 
1039
  if ( is_array($arg_last) && count($arg_last) > 0 && is_bool($arg_last[0]) ) {
1040
+ // Remove slash paramter from args array
1041
  array_pop($parts);
1042
+ // Normalize slashes options
1043
  if ( isset($arg_last[0]) )
1044
  $slashes[0] = $arg_last[0];
1045
  if ( isset($arg_last[1]) )
1046
  $slashes[1] = $arg_last[1];
1047
  }
1048
  }
1049
+ // Extract to slash options local variables
1050
  list($trailing_slash, $leading_slash) = $slashes;
1051
 
1052
+ // Clean path segments
1053
  foreach ( $parts as $key => $part ) {
1054
+ // Trim slashes/spaces
1055
  $parts[$key] = trim($part, " " . $sl_f . $sl_b);
1056
 
1057
+ // Verify path segment still contains value
1058
  if ( empty($parts[$key]) ) {
1059
  unset($parts[$key]);
1060
  continue;
1061
  }
1062
  }
1063
 
1064
+ // Join path parts together
1065
  $parts = implode($sl_b, $parts);
1066
  $parts = str_replace($sl_b, $sl_f, $parts);
1067
+ // Add trailing slash (if necessary)
1068
  if ( $trailing_slash )
1069
  $parts .= $sl_f;
1070
+ // Add leading slash (if necessary)
1071
  $regex = '#^.+:[\\/]#';
1072
  if ( $leading_slash && !preg_match($regex, $parts) ) {
1073
  $parts = $sl_f . $parts;
1078
  /**
1079
  * Returns URL of file (assumes that it is in plugin directory)
1080
  * @param string $file name of file get URL
1081
+ * @param string|bool $relative Path that URI should be relative to (Default: full path)
1082
  * @return string File URL
1083
  */
1084
+ function get_file_url($file, $relative = null) {
1085
  if ( is_string($file) && '' != trim($file) ) {
1086
+ $file = str_replace(' ', '%20', $this->normalize_path($this->get_url_base(false, $relative), $file));
1087
  }
1088
  return $file;
1089
  }
1093
  * @param string $file file name
1094
  * @return string File path
1095
  */
1096
+ function get_file_path($file, $relative = null) {
1097
+ // Build path
1098
  if ( is_string($file) && '' != trim($file) ) {
1099
+ $file = $this->normalize_path($this->get_path_base($relative), $file);
1100
  }
1101
  return $file;
1102
  }
1117
  return ( empty($ext) ) ? false : true;
1118
  }
1119
 
1120
+ /**
1121
+ * Check if string is valid URI
1122
+ * @param string $uri String to check
1123
+ * @return bool TRUE if string is valid URI
1124
+ */
1125
+ function is_uri($uri) {
1126
+ return ( preg_match('|^(https?:)?//|', $uri) ) ? true : false;
1127
+ }
1128
+
1129
  /**
1130
  * Retrieves file extension
1131
  * @param string $file file name/path
1135
  function get_file_extension($file, $lowercase = true) {
1136
  $ret = '';
1137
  $sep = '.';
1138
+ // Validate
1139
  if ( !is_string($file) )
1140
  return $ret;
1141
+ // Strip query string (if necessary)
1142
+ if ( ( $qpos = strpos($file, '?') ) && $qpos !== false ) {
1143
+ $file = substr($file, 0, $qpos);
1144
+ }
1145
  if ( ( $rpos = strrpos($file, $sep) ) > 0 )
1146
  $ret = substr($file, $rpos + 1);
1147
  if ( !!$lowercase )
1161
  if ( !is_array($extension) )
1162
  $extension = array(strval($extension));
1163
  if ( !$case_sensitive ) {
1164
+ // Normalize extensions
1165
  $extension = array_map('strtolower', $extension);
1166
  }
1167
  return ( in_array($this->get_file_extension($file, !$case_sensitive), $extension) ) ? true : false;
1188
  * @uses normalize_path()
1189
  * @return string Base URL
1190
  */
1191
+ function get_url_base($trailing_slash = false, $relative = null) {
1192
+ $ret = $this->_plugin['uri'];
1193
+ if ( empty($ret) ) {
1194
+ $ret = $this->normalize_path(plugins_url(), $this->get_plugin_base());
1195
  }
1196
+ // Trailing slash
1197
+ if ( !!$trailing_slash ) {
1198
+ $ret .= '/';
1199
+ }
1200
+ // Relative
1201
+ if ( !empty($relative) ) {
1202
+ // Default
1203
+ if ( is_bool($relative) ) {
1204
+ $relative = site_url();
1205
+ }
1206
+ // Custom
1207
+ if ( is_string($relative) ) {
1208
+ $ret = $this->get_relative_path($ret, $relative);
1209
+ }
1210
+ }
1211
+
1212
+ return $ret;
1213
  }
1214
 
1215
  /**
1219
  * @uses normalize_path()
1220
  * @return string Base path
1221
  */
1222
+ function get_path_base($relative = null) {
1223
+ $ret = $this->_path_base;
1224
+ if ( empty($ret) ) {
1225
+ // Get base directory of parent object
1226
+ if ( $this->get_parent() ) {
1227
+ $r = new ReflectionClass(get_class($this->get_parent()));
1228
+ $base = $r->getFileName();
1229
+ unset($r);
1230
+ } else {
1231
+ $base = __FILE__;
1232
+ }
1233
+ // Extract base path
1234
+ $base = $this->normalize_path($base);
1235
+ if ( 0 === strpos($base, $this->normalize_path(WP_PLUGIN_DIR)) ) {
1236
+ $end = strpos($base, '/', strlen(WP_PLUGIN_DIR) + 1);
1237
+ $base = substr($base, 0, $end);
1238
+ }
1239
+ $ret = $this->_path_base = $base;
1240
  }
1241
+ // Make relative path
1242
+ if ( !empty($relative) ) {
1243
+ // Default
1244
+ if ( is_bool($relative) ) {
1245
+ $relative = ABSPATH;
1246
+ }
1247
+ // Custom
1248
+ if ( is_string($relative) ) {
1249
+ $ret = $this->get_relative_path($ret, $relative);
1250
+ }
1251
+ }
1252
+ return $ret;
1253
+ }
1254
+
1255
+ /**
1256
+ * Retrieve relative path for absolute paths
1257
+ * @param string $path Path to modify
1258
+ * @param string $relative (optional) Base path to make $path relative to (Default: Site's base path)
1259
+ * @return string Relative path
1260
+ */
1261
+ function get_relative_path($path, $relative = true) {
1262
+ // Default base path
1263
+ if ( !is_string($relative) ) {
1264
+ $relative = ABSPATH;
1265
+ }
1266
+ if ( !empty($relative) && !empty($path) ) {
1267
+ $relative = $this->normalize_path($relative);
1268
+ $path = $this->normalize_path($path);
1269
+ // Strip base path
1270
+ if ( strpos($path, $relative) === 0 ) {
1271
+ $path = substr($path, strlen($relative));
1272
+ }
1273
+ }
1274
+ return $path;
1275
  }
1276
 
1277
  /**
1278
  * Retrieve plugin's base directory
1279
  * @uses WP_PLUGIN_DIR
1280
+ * @uses Utilities::get_path_base() to retrieve plugin base path
1281
+ * @uses Utilities::_plugin_base to save plugin base
1282
  * @return string Base directory
1283
  */
1284
+ function get_plugin_base() {
1285
+ $ret = $this->_plugin['base'];
1286
+ if ( empty($ret) ) {
1287
+ $ret = $this->_plugin['base'] = basename($this->get_path_base());
1288
  }
1289
+ return $ret;
 
 
1290
  }
1291
 
1292
  /**
1296
  * @return string Base file path
1297
  */
1298
  function get_plugin_base_file() {
1299
+ $ret = $this->_plugin['file'];
1300
+ if ( empty($ret) ) {
1301
+ $dir = @opendir($this->get_path_base());
1302
  if ( $dir ) {
1303
  while ( ($ftemp = readdir($dir)) !== false ) {
1304
+ // Only process PHP files
1305
  $ftemp = $this->get_file_path($ftemp);
1306
  if ( !$this->has_file_extension($ftemp, 'php') || !is_readable($ftemp) )
1307
  continue;
1308
+ // Check for data
1309
+ $data = get_file_data($ftemp, $this->_plugin['headers']);
1310
  if ( !empty($data['Name']) ) {
1311
+ // Set base file
1312
+ $ret = $ftemp;
1313
+ // Save plugin data
1314
+ $this->set_plugin_info($data);
1315
  break;
1316
  }
1317
  }
1318
  }
1319
  @closedir($dir);
1320
  }
1321
+ // Return
1322
+ return $ret;
1323
  }
1324
 
1325
  /**
1330
  * @return string Internal plugin name
1331
  */
1332
  function get_plugin_base_name() {
1333
+ $ret = $this->_plugin['name'];
1334
+ if ( empty($ret) ) {
1335
+ $ret = $this->_plugin['name'] = plugin_basename( $this->get_plugin_base_file() );
1336
+ }
1337
+ return $ret;
1338
+ }
1339
+
1340
+ private function set_plugin_info($data) {
1341
+ if ( is_array($data) ) {
1342
+ $this->_plugin['data'] = $data;
1343
  }
 
1344
  }
1345
 
1346
  /**
1347
  * Retrieve plugin info
1348
  * Parses info comment in main plugin file
1349
+ * @uses get_plugin_base_file() to retrieve plugin info
1350
+ * @return array|string Plugin info (specific value if field set)
1351
+ */
1352
+ function get_plugin_info($field = null) {
1353
+ $ret = $this->_plugin['data'];
1354
+ // Get plugin data
1355
+ if ( empty($ret) ) {
1356
+ $this->get_plugin_base_file();
1357
+ $ret = $this->_plugin['data'];
1358
  }
1359
+ // Return specified field
1360
  if ( !empty($field) ) {
1361
+ $ret = ( is_array($ret) && isset($ret[$field]) ) ? $ret[$field] : '';
 
 
 
1362
  }
1363
  return $ret;
1364
  }
1370
  * @return string Plugin version
1371
  */
1372
  function get_plugin_version($strip_desc = true) {
1373
+ // Retrieve version
1374
+ $ret = $this->get_plugin_info('Version');
1375
+ // Format
1376
+ if ( !empty($ret) && $strip_desc ) {
 
 
 
 
 
1377
  $ret = explode(' ', $ret, 2);
1378
  $ret = $ret[0];
1379
  }
1380
+ // Return
1381
  return $ret;
1382
  }
1383
 
 
 
 
 
 
 
 
 
 
 
 
1384
  /**
1385
  * Retrieve current post type based on URL query variables
1386
  * @return string|null Current post type
1404
  function get_action($default = null) {
1405
  $action = '';
1406
 
1407
+ // Check if action is set in URL
1408
  if ( isset($_GET['action']) )
1409
  $action = $_GET['action'];
1410
+ // Otherwise, Determine action based on plugin admin page suffix
1411
+ elseif ( isset($_GET['page']) && ($pos = strrpos($_GET['page'], '-')) && $pos !== false && ( $pos != strlen($_GET['page']) - 1 ) )
1412
  $action = trim(substr($_GET['page'], $pos + 1), '-_');
1413
 
1414
+ // Determine action for core admin pages
1415
+ if ( ( !isset($_GET['page']) || empty($action) ) && isset($_SERVER['SCRIPT_NAME']) ) {
1416
  $actions = array(
1417
  'add' => array('page-new', 'post-new'),
1418
  'edit-item' => array('page', 'post'),
1434
 
1435
  /*-** General **-*/
1436
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1437
  /**
1438
  * Checks if a property exists in a class or object
1439
+ * Compatibility method for PHP 4
1440
  * @param mixed $class Class or object to check
1441
  * @param string $property Name of property to look for in $class
1442
  */
1458
  */
1459
  function &get_property(&$obj, $property) {
1460
  $property = trim($property);
1461
+ // Object
1462
  if ( is_object($obj) )
1463
  return $obj->{$property};
1464
+ // Array
1465
  if ( is_array($obj) )
1466
  return $obj[$property];
1467
+ // Class
1468
  if ( is_string($obj) && class_exists($obj) ) {
1469
  $cvars = get_class_vars($obj);
1470
  if ( isset($cvars[$property]) )
1484
  */
1485
  function array_remap($arr, $map = array(), $overwrite = false) {
1486
  if ( !empty($map) && is_array($map) ) {
1487
+ // Iterate through mappings
1488
  foreach ( $map as $from => $to ) {
1489
  if ( !array_key_exists($from, $arr) )
1490
  continue;
1491
  $move = $overwrite;
1492
+ // Only remap if parent property doesn't already exist in array
1493
  if ( !array_key_exists($to, $arr) )
1494
  $move = true;
1495
  if ( $move ) {
1496
+ // Move member value to new key
1497
  $arr[$to] = $arr[$from];
1498
+ // Remove source member
1499
  unset($arr[$from]);
1500
  }
1501
  }
1502
  }
1503
+ // Return remapped properties
1504
  return $arr;
1505
  }
1506
 
1542
  * @return array Merged array
1543
  */
1544
  function array_merge_recursive_distinct($arr1) {
1545
+ // Get all arrays passed to function
1546
  $args = func_get_args();
1547
  if ( empty($args) )
1548
  return false;
1549
+ // Return empty array if first parameter is not an array
1550
  if ( !is_array($args[0]) )
1551
  return array();
1552
+ // Set first array as base array
1553
  $merged = $args[0];
1554
+ // Iterate through arrays to merge
1555
  $arg_length = count($args);
1556
  for ( $x = 1; $x < $arg_length; $x++ ) {
1557
+ // Skip if argument is not an array (only merge arrays)
1558
  if ( !is_array($args[$x]) )
1559
  continue;
1560
+ // Iterate through argument items
1561
  foreach ( $args[$x] as $key => $val ) {
1562
+ // Generate key for numeric indexes
1563
  if ( is_int($key) ) {
1564
+ // Add new item to merged array
1565
  $merged[] = null;
1566
+ // Get key of new item
1567
  $key = array_pop(array_keys($merged));
1568
  }
1569
  if ( !isset($merged[$key]) || !is_array($merged[$key]) || !is_array($val) ) {
1586
  */
1587
  function array_replace_recursive($search, $arr_replace, $arr_subject) {
1588
  foreach ($arr_subject as $key => $val) {
1589
+ // Skip element if key does not exist in the replacement array
1590
  if (!isset($arr_replace[$key]))
1591
  continue;
1592
+ // If element values for both arrays are strings, replace text
1593
  if (is_string($val) && strpos($val, $search) !== false && is_string($arr_replace[$key]))
1594
  $arr_subject[$key] = str_replace($search, $arr_replace[$key], $val);
1595
+ // If value in both arrays are arrays, recursively replace text
1596
  if (is_array($val) && is_array($arr_replace[$key]))
1597
  $arr_subject[$key] = $this->array_replace_recursive($search, $arr_replace[$key], $val);
1598
  }
1611
  return eval('return isset($arr' . $f_path . ');');
1612
  }
1613
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1614
  /**
1615
  * Build formatted string based on array values
1616
  * Array values in formatted string will be ordered by index order
1619
  * @return string Formatted string based on array values
1620
  */
1621
  function get_array_path($attribute = '', $format = null) {
1622
+ // Formatted value
1623
  $fmtd = '';
1624
  if (!empty($attribute)) {
1625
+ // Make sure attribute is array
1626
  if (!is_array($attribute)) {
1627
  $attribute = array($attribute);
1628
  }
1629
+ // Format attribute
1630
  $format = strtolower($format);
1631
  switch ($format) {
1632
  case 'id':
1634
  break;
1635
  case 'metadata':
1636
  case 'attribute':
1637
+ // Join segments
1638
  $delim = '_';
1639
  $fmtd = implode($delim, $attribute);
1640
+ // Replace white space and repeating delimiters
1641
  $fmtd = str_replace(' ', $delim, $fmtd);
1642
  while (strpos($fmtd, $delim.$delim) !== false)
1643
  $fmtd = str_replace($delim.$delim, $delim, $fmtd);
1644
+ // Prefix formatted value with delimeter for metadata keys
1645
  if ('metadata' == $format)
1646
  $fmtd = $delim . $fmtd;
1647
  break;
1664
  $path = array();
1665
  $args = func_get_args();
1666
 
1667
+ // Iterate through parameters and build path
1668
  foreach ( $args as $arg ) {
1669
  if ( empty($arg) )
1670
  continue;
1671
 
1672
  if (is_array($arg)) {
1673
+ // Recurse through array items to pull out any more arrays
1674
  foreach ($arg as $key => $val) {
1675
  $path = array_merge($path, $this->build_path($val));
1676
  }
1677
+ // $path = array_merge($path, array_values($arg));
1678
  } elseif ( is_scalar($arg) ) {
1679
  $path[] = $arg;
1680
  }
1683
  return $path;
1684
  }
1685
 
1686
+ /**
1687
+ * Build generic element
1688
+ * @param array $args
1689
+ * @return string Element output
1690
+ */
1691
+ public function build_element($args = array()) {
1692
+ $ret = '';
1693
+ $args_default = array(
1694
+ 'tag' => '',
1695
+ 'wrap' => false,
1696
+ 'content' => '',
1697
+ 'attributes' => array(),
1698
+ 'format' => array(),
1699
+ );
1700
+ $format_default = array(
1701
+ 'open' => '[%s]',
1702
+ 'close' => '[/%s]',
1703
+ );
1704
+ $args = wp_parse_args($args, $args_default);
1705
+ $args['format'] = wp_parse_args($args['format'], $format_default);
1706
+
1707
+ // Validate
1708
+ if ( !is_string($args['tag']) || empty($args['tag']) ) {
1709
+ return $ret;
1710
+ }
1711
+
1712
+ $args = (object) $args;
1713
+
1714
+ $args->attributes = $this->build_attribute_string($args->attributes);
1715
+ if ( strlen($args->attributes) > 0 ) {
1716
+ $args->attributes = ' ' . $args->attributes;
1717
+ }
1718
+
1719
+ // Build output
1720
+ $args->format = (object) $args->format;
1721
+ $ret = sprintf( $args->format->open, $args->tag . $args->attributes);
1722
+
1723
+ // Wrap content if necessary
1724
+ if ( $args->wrap || ( is_string($args->content) && !empty($args->content) ) ) {
1725
+ $ret .= $args->content . sprintf( $args->format->close, $args->tag);
1726
+ }
1727
+
1728
+ return $ret;
1729
+ }
1730
+
1731
  /**
1732
  * Parse string of attributes into array
1733
  * For XML/XHTML tag attributes
1737
  function parse_attribute_string($txt, $defaults = array()) {
1738
  $txt = trim($txt, ' >');
1739
  $matches = $attr = array();
1740
+ // Strip tag
1741
  if ( $txt[0] == '<' && ($s = strpos($txt, ' ')) && $s !== false ) {
1742
  $txt = trim(substr($txt, $s + 1));
1743
  }
1744
+ // Parse attributes
1745
  $rgx = "/\b(\w+.*?)=([\"'])(.*?)\\2(?:\s|$)/i";
1746
  preg_match_all($rgx, $txt, $matches);
1747
  if ( count($matches) > 3 ) {
1750
  $attr[trim($val)] = trim($matches[3][$sub_idx]);
1751
  }
1752
  }
1753
+ // Destroy parsing vars
1754
  unset($txt, $matches);
1755
 
1756
  return array_merge($defaults, $attr);
1763
  */
1764
  function build_attribute_string($attr) {
1765
  $ret = '';
1766
+ if ( is_object($attr) ) {
1767
  $attr = (array) $attr;
1768
+ }
1769
+ if ( is_array($attr) && !empty($attr) ) {
1770
  array_map('esc_attr', $attr);
1771
  $attr_str = array();
1772
  foreach ( $attr as $key => $val ) {
1777
  return $ret;
1778
  }
1779
 
1780
+ /* HTML */
1781
+
1782
+ /**
1783
+ * Generate HTML element based on values
1784
+ * @param $args Element arguments
1785
+ * @return string Generated HTML element
1786
+ */
1787
+ public function build_html_element($args) {
1788
+ $args_default = array(
1789
+ 'tag' => 'span',
1790
+ 'content' => '',
1791
+ 'attributes' => array()
1792
+ );
1793
+ $args = wp_parse_args($args, $args_default);
1794
+ $args['format'] = array(
1795
+ 'open' => '<%s>',
1796
+ 'close' => '</%s>',
1797
+ );
1798
+ // Build element
1799
+ return $this->build_element($args);
1800
+ }
1801
+
1802
+ /**
1803
+ * Build HTML link element
1804
+ * @uses build_html_element() to build link output
1805
+ * @param string $uri Link URI
1806
+ * @param string $content Link content
1807
+ * @param $array (optional) $attributes Additional link attributes
1808
+ * @return string HTML link element
1809
+ */
1810
  function build_html_link($uri, $content, $attributes = array()) {
1811
  $attributes = array_merge(array('href' => $uri, 'title' => $content), $attributes);
1812
  return $this->build_html_element(array('tag' => 'a', 'wrap' => true, 'content' => $content, 'attributes' => $attributes));
1817
  * @param $url Stylesheet URL
1818
  * @return string Stylesheet element
1819
  */
1820
+ function build_stylesheet_element($url) {
1821
  $attributes = array('href' => $url, 'type' => 'text/css', 'rel' => 'stylesheet');
1822
  return $this->build_html_element(array('tag' => 'link', 'wrap' => false, 'attributes' => $attributes));
1823
  }
1831
  * @param bool $wait_doc_ready (optional) Wait until document is fully loaded before executing commands? (Default: No)
1832
  */
1833
  function build_script_element($content = '', $id = '', $wrap_jquery = true, $wait_doc_ready = false) {
1834
+ // Stop processing invalid content
1835
  if ( is_array($content) && !empty($content) ) {
1836
+ $content = implode(PHP_EOL, $content);
1837
  }
1838
  if ( empty($content) || !is_string($content) ) {
1839
  return '';
1842
  $start = array('/* <![CDATA[ */');
1843
  $end = array('/* ]]> */');
1844
  if ( $wrap_jquery ) {
1845
+ $start[] = 'if ( !!window.jQuery ) {(function($){';
1846
+ $end[] = '})(jQuery);}';
1847
 
1848
+ // Add event handler (if necessary)
1849
  if ( $wait_doc_ready ) {
1850
  $start[] = '$(document).ready(function(){';
1851
  $end[] = '})';
1852
  }
1853
  }
1854
 
1855
+ // Reverse order of end values
1856
  $end = array_reverse($end);
1857
  $content = implode('', array_merge($start, array($content), $end));
1858
  if ( is_string($id) && !empty($id) ) {
1859
  $attributes['id'] = $this->add_prefix($id);
1860
  }
1861
+ return $this->build_html_element(array('tag' => 'script', 'content' => $content, 'wrap' => true, 'attributes' => $attributes)) . PHP_EOL;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1862
  }
1863
  }
l10n/simple-lightbox.pot CHANGED
@@ -1,218 +1,260 @@
1
- # Copyright (C) 2013
2
- # This file is distributed under the same license as the package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: \n"
6
- "Report-Msgid-Bugs-To: https://github.com/archetyped/simple-lightbox/wiki/Reporting-Issues\n"
7
- "POT-Creation-Date: 2013-04-16 00:22:28+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2013-MO-DA HO:MI+ZONE\n"
12
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
14
 
15
- #: includes/class.admin.php:686
16
- msgid "The settings have been reset"
17
  msgstr ""
18
 
19
- #: includes/class.admin.php:687
20
- msgid "<strong class=\"%1$s\">Notice:</strong> This update is a <strong class=\"%1$s\">Beta version</strong>. It is highly recommended that you test the update on a test server before updating the plugin on a production server."
21
  msgstr ""
22
 
23
- #: includes/class.admin.php:688 includes/class.admin.php:1394
24
- #: includes/class.admin.php:1443 includes/class.admin.php:1531
25
- msgid "Access Denied"
26
  msgstr ""
27
 
28
- #: includes/class.admin.php:1318
29
- msgid "Save Changes"
30
  msgstr ""
31
 
32
- #: includes/class.fields.php:2223
33
- msgid "Default Element"
34
  msgstr ""
35
 
36
- #: includes/class.fields.php:2235
37
- msgid "Default Element (Closed Tag)"
38
  msgstr ""
39
 
40
- #: includes/class.fields.php:2243
41
- msgid "Default Input Element"
42
  msgstr ""
43
 
44
- #: includes/class.fields.php:2251
45
- msgid "Text Box"
46
  msgstr ""
47
 
48
- #: includes/class.fields.php:2282
49
- msgid "Hidden Field"
50
  msgstr ""
51
 
52
- #: includes/class.fields.php:2288
53
- msgid "Select tag"
54
  msgstr ""
55
 
56
- #: includes/class.fields.php:2300
57
- msgid "Inline wrapper"
58
  msgstr ""
59
 
60
- #: includes/class.options.php:115
61
- msgid "Enabled"
62
  msgstr ""
63
 
64
- #: includes/class.options.php:115
65
- msgid "Disabled"
66
  msgstr ""
67
 
68
- #: includes/class.options.php:400
69
- msgid "Default"
70
  msgstr ""
71
 
72
- #: includes/class.themes.php:41
73
- msgid "Theme"
74
  msgstr ""
75
 
76
- #: includes/class.themes.php:61
77
- msgid "Default (Light)"
78
  msgstr ""
79
 
80
- #: includes/class.themes.php:67
81
- msgid "Default (Dark)"
82
  msgstr ""
83
 
84
- #: model.php:151
85
- msgid "Activation"
86
  msgstr ""
87
 
88
- #: model.php:152
89
- msgid "Grouping"
90
  msgstr ""
91
 
92
- #: model.php:153
93
- msgid "UI"
94
  msgstr ""
95
 
96
- #: model.php:154
97
- msgid "Labels"
98
  msgstr ""
99
 
100
- #: model.php:157
101
- msgid "Enable Lightbox Functionality"
102
  msgstr ""
103
 
104
- #: model.php:158
105
- msgid "Enable on Home page"
106
  msgstr ""
107
 
108
- #: model.php:159
109
- msgid "Enable on Posts"
110
  msgstr ""
111
 
112
- #: model.php:160
113
- msgid "Enable on Pages"
114
  msgstr ""
115
 
116
- #: model.php:161
117
- msgid "Enable on Archive Pages (tags, categories, etc.)"
118
  msgstr ""
119
 
120
- #: model.php:162
121
- msgid "Enable for Widgets"
122
  msgstr ""
123
 
124
- #: model.php:163
125
- msgid "Group items (for displaying as a slideshow)"
126
  msgstr ""
127
 
128
- #: model.php:164
129
- msgid "Group items by Post (e.g. on pages with multiple posts)"
130
  msgstr ""
131
 
132
- #: model.php:165
133
- msgid "Group gallery items separately"
134
  msgstr ""
135
 
136
- #: model.php:166
137
- msgid "Group widget items separately"
138
  msgstr ""
139
 
140
- #: model.php:167
141
- msgid "Resize lightbox to fit in window"
142
  msgstr ""
143
 
144
- #: model.php:168
145
- msgid "Enable animations"
146
  msgstr ""
147
 
148
- #: model.php:169
149
- msgid "Start Slideshow Automatically"
150
  msgstr ""
151
 
152
- #: model.php:170
153
- msgid "Slide Duration (Seconds)"
154
  msgstr ""
155
 
156
- #: model.php:171
157
- msgid "Loop through items"
158
  msgstr ""
159
 
160
- #: model.php:172
161
- msgid "Overlay Opacity (0 - 1)"
 
 
 
162
  msgstr ""
163
 
164
- #: model.php:173
165
- msgid "Loading indicator"
166
  msgstr ""
167
 
168
- #: model.php:174
169
- msgid "Close button"
170
  msgstr ""
171
 
172
- #: model.php:175
173
- msgid "Next Item button"
 
 
 
174
  msgstr ""
175
 
176
- #: model.php:176
177
- msgid "Previous Item button"
 
178
  msgstr ""
179
 
180
- #: model.php:177
181
- msgid "Start Slideshow button"
182
  msgstr ""
183
 
184
- #: model.php:178
185
- msgid "Stop Slideshow button"
186
  msgstr ""
187
 
188
- #: model.php:179
189
- msgid "Slideshow status format"
190
  msgstr ""
191
 
192
- #: model.php:228
193
- msgid "Lightbox"
194
  msgstr ""
195
 
196
- #: model.php:229
197
- msgid "Lightbox Settings"
198
  msgstr ""
199
 
200
- #: model.php:230
201
- msgid "Settings"
202
  msgstr ""
203
 
204
- #: model.php:234
205
- msgid "Reset"
206
  msgstr ""
207
 
208
- #: model.php:235
209
- msgid "Are you sure you want to reset settings?"
210
  msgstr ""
211
 
212
- #: model.php:236
213
- msgid "Settings have been reset"
214
  msgstr ""
215
 
216
- #: model.php:237
217
- msgid "Settings were not reset"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  msgstr ""
1
+ # Copyright (C) 2015 Simple Lightbox
2
+ # This file is distributed under the same license as the Simple Lightbox package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Simple Lightbox 2.5.1\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/simple-lightbox\n"
7
+ "POT-Creation-Date: 2015-09-24 03:20:40+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2015-MO-DA HO:MI+ZONE\n"
12
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
14
 
15
+ #: controller.php:240
16
+ msgid "Activation"
17
  msgstr ""
18
 
19
+ #: controller.php:241
20
+ msgid "Grouping"
21
  msgstr ""
22
 
23
+ #: controller.php:242
24
+ msgid "UI"
 
25
  msgstr ""
26
 
27
+ #: controller.php:243
28
+ msgid "Labels"
29
  msgstr ""
30
 
31
+ #: controller.php:246
32
+ msgid "Enable Lightbox Functionality"
33
  msgstr ""
34
 
35
+ #: controller.php:247
36
+ msgid "Enable on Home page"
37
  msgstr ""
38
 
39
+ #: controller.php:248
40
+ msgid "Enable on Single Posts"
41
  msgstr ""
42
 
43
+ #: controller.php:249
44
+ msgid "Enable on Pages"
45
  msgstr ""
46
 
47
+ #: controller.php:250
48
+ msgid "Enable on Archive Pages (tags, categories, etc.)"
49
  msgstr ""
50
 
51
+ #: controller.php:251
52
+ msgid "Enable for Widgets"
53
  msgstr ""
54
 
55
+ #: controller.php:252
56
+ msgid "Group items (for displaying as a slideshow)"
57
  msgstr ""
58
 
59
+ #: controller.php:253
60
+ msgid "Group items by Post (e.g. on pages with multiple posts)"
61
  msgstr ""
62
 
63
+ #: controller.php:254
64
+ msgid "Group gallery items separately"
65
  msgstr ""
66
 
67
+ #: controller.php:255
68
+ msgid "Group widget items separately"
69
  msgstr ""
70
 
71
+ #: controller.php:256
72
+ msgid "Resize lightbox to fit in window"
73
  msgstr ""
74
 
75
+ #: controller.php:257
76
+ msgid "Enable animations"
77
  msgstr ""
78
 
79
+ #: controller.php:258
80
+ msgid "Start Slideshow Automatically"
81
  msgstr ""
82
 
83
+ #: controller.php:259
84
+ msgid "Slide Duration (Seconds)"
85
  msgstr ""
86
 
87
+ #: controller.php:260
88
+ msgid "Loop through items"
89
  msgstr ""
90
 
91
+ #: controller.php:261
92
+ msgid "Overlay Opacity (0 - 1)"
93
  msgstr ""
94
 
95
+ #: controller.php:262
96
+ msgid "Enable default title"
97
  msgstr ""
98
 
99
+ #: controller.php:263
100
+ msgid "Loading indicator"
101
  msgstr ""
102
 
103
+ #: controller.php:264
104
+ msgid "Close button"
105
  msgstr ""
106
 
107
+ #: controller.php:265
108
+ msgid "Next Item button"
109
  msgstr ""
110
 
111
+ #: controller.php:266
112
+ msgid "Previous Item button"
113
  msgstr ""
114
 
115
+ #: controller.php:267
116
+ msgid "Start Slideshow button"
117
  msgstr ""
118
 
119
+ #: controller.php:268
120
+ msgid "Stop Slideshow button"
121
  msgstr ""
122
 
123
+ #: controller.php:269
124
+ msgid "Slideshow status format"
125
  msgstr ""
126
 
127
+ #: controller.php:319
128
+ msgid "Lightbox"
129
  msgstr ""
130
 
131
+ #: controller.php:320
132
+ msgid "Lightbox Settings"
133
  msgstr ""
134
 
135
+ #: controller.php:321
136
+ msgid "Settings"
137
  msgstr ""
138
 
139
+ #: controller.php:330
140
+ msgid "Feedback & Support"
141
  msgstr ""
142
 
143
+ #: controller.php:335
144
+ msgid "Reset"
145
  msgstr ""
146
 
147
+ #: controller.php:336
148
+ msgid "Are you sure you want to reset settings?"
149
  msgstr ""
150
 
151
+ #: controller.php:337
152
+ msgid "Settings have been reset"
153
  msgstr ""
154
 
155
+ #: controller.php:338
156
+ msgid "Settings were not reset"
157
  msgstr ""
158
 
159
+ #: controller.php:348
160
+ msgid ""
161
+ "<p>Simple Lightbox thrives on your feedback!</p><p>Click the button below to "
162
+ "<strong>get help</strong>, <strong>request a feature</strong>, or "
163
+ "<strong>provide some feedback</strong>!</p>"
164
  msgstr ""
165
 
166
+ #: controller.php:352
167
+ msgid "Get Support &amp; Provide Feedback"
168
  msgstr ""
169
 
170
+ #: includes/class.admin.php:644
171
+ msgid "The settings have been reset"
172
  msgstr ""
173
 
174
+ #: includes/class.admin.php:645
175
+ msgid ""
176
+ "<strong class=\"%1$s\">Notice:</strong> This update is a <strong class=\"%1$s"
177
+ "\">Beta version</strong>. It is highly recommended that you test the update "
178
+ "on a test server before updating the plugin on a production server."
179
  msgstr ""
180
 
181
+ #: includes/class.admin.php:646 includes/class.admin_action.php:45
182
+ #: includes/class.admin_page.php:146
183
+ msgid "Access Denied"
184
  msgstr ""
185
 
186
+ #: includes/class.fields.php:43
187
+ msgid "Default Element"
188
  msgstr ""
189
 
190
+ #: includes/class.fields.php:55
191
+ msgid "Default Element (Closed Tag)"
192
  msgstr ""
193
 
194
+ #: includes/class.fields.php:63
195
+ msgid "Default Input Element"
196
  msgstr ""
197
 
198
+ #: includes/class.fields.php:71
199
+ msgid "Text Box"
200
  msgstr ""
201
 
202
+ #: includes/class.fields.php:102
203
+ msgid "Hidden Field"
204
  msgstr ""
205
 
206
+ #: includes/class.fields.php:108
207
+ msgid "Select tag"
208
  msgstr ""
209
 
210
+ #: includes/class.fields.php:120
211
+ msgid "Inline wrapper"
212
  msgstr ""
213
 
214
+ #: includes/class.option.php:113
215
+ msgid "Enabled"
216
  msgstr ""
217
 
218
+ #: includes/class.option.php:113
219
+ msgid "Disabled"
220
  msgstr ""
221
 
222
+ #: includes/class.options.php:222
223
+ msgid "Default"
224
+ msgstr ""
225
+
226
+ #: includes/class.themes.php:40
227
+ msgid "Theme"
228
+ msgstr ""
229
+
230
+ #: includes/class.themes.php:65
231
+ msgid "Baseline"
232
+ msgstr ""
233
+
234
+ #: includes/class.themes.php:76
235
+ msgid "Default (Light)"
236
+ msgstr ""
237
+
238
+ #: includes/class.themes.php:87
239
+ msgid "Default (Dark)"
240
+ msgstr ""
241
+
242
+ #. Plugin Name of the plugin/theme
243
+ msgid "Simple Lightbox"
244
+ msgstr ""
245
+
246
+ #. Plugin URI of the plugin/theme
247
+ msgid "http://archetyped.com/tools/simple-lightbox/"
248
+ msgstr ""
249
+
250
+ #. Description of the plugin/theme
251
+ msgid "The highly customizable lightbox for WordPress"
252
+ msgstr ""
253
+
254
+ #. Author of the plugin/theme
255
+ msgid "Archetyped"
256
+ msgstr ""
257
+
258
+ #. Author URI of the plugin/theme
259
+ msgid "http://archetyped.com"
260
  msgstr ""
load.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* Constants */
4
+
5
+ if ( !defined('SLB_DEV') ) {
6
+ define('SLB_DEV', ( isset( $_REQUEST['slb_dev'] ) && !!$_REQUEST['slb_dev'] ) );
7
+ }
8
+
9
+ /* Class Management */
10
+
11
+ /**
12
+ * Class loading handler
13
+ * @param string $classname Class to load
14
+ */
15
+ function slb_autoload($classname) {
16
+ $prefix = 'slb_';
17
+ $cls = strtolower($classname);
18
+ // Remove prefix
19
+ if ( 0 !== strpos($cls, $prefix) ) {
20
+ return false;
21
+ }
22
+ // Format class for filename
23
+ $fn = 'class.' . substr($cls, strlen($prefix)) . '.php';
24
+ // Build path
25
+ $path = dirname(__FILE__) . '/' . "includes/" . $fn;
26
+ // Load file
27
+ if ( is_readable($path) ) {
28
+ require $path;
29
+ }
30
+ }
31
+ spl_autoload_register('slb_autoload');
32
+
33
+ /* Load Assets */
34
+
35
+ $path = dirname(__FILE__) . '/';
36
+ require_once $path . 'controller.php';
37
+ $GLOBALS['slb'] = new SLB_Lightbox();
38
+ require_once $path . 'functions.php';
main.php CHANGED
@@ -1,57 +1,48 @@
1
- <?php
2
- /*
 
 
 
 
 
 
 
 
3
  Plugin Name: Simple Lightbox
4
- Plugin URI: http://archetyped.com/lab/slb-2-0rc4/
5
  Description: The highly customizable lightbox for WordPress
6
- Version: 2.0
 
 
7
  Author: Archetyped
8
  Author URI: http://archetyped.com
9
- */
10
- /*
11
- Copyright 2013 Solomon Marchessault (sol@archetyped.com)
12
-
13
- This program is free software; you can redistribute it and/or
14
- modify it under the terms of the GNU General Public License
15
- as published by the Free Software Foundation; either version 2
16
- of the License, or (at your option) any later version.
17
-
18
- This program is distributed in the hope that it will be useful,
19
- but WITHOUT ANY WARRANTY; without even the implied warranty of
20
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
- GNU General Public License for more details.
22
-
23
- You should have received a copy of the GNU General Public License
24
- along with this program; if not, write to the Free Software
25
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26
  */
27
 
28
- /**
29
- * Class loading handler
30
- * @param string $classname Class to load
31
- */
32
- function slb_autoload($classname) {
33
- $prefix = 'SLB_';
34
- //Remove prefix
35
- if ( 0 !== strpos($classname, $prefix) ) {
36
- return false;
37
- }
38
- //Format class for filename
39
- $fn = 'class.' . strtolower( substr($classname, strlen($prefix)) ) . '.php';
40
- //Build path
41
- $path = dirname(__FILE__) . '/' . "includes/" . $fn;
42
- //Load file
43
- if ( is_readable($path) ) {
44
- require $path;
 
 
 
 
 
45
  }
 
46
  }
47
 
48
- spl_autoload_register('slb_autoload');
49
-
50
- require_once 'model.php';
51
-
52
- $slb = new SLB_Lightbox();
53
-
54
- function slb_register_theme($name, $title, $stylesheet_url, $layout) {
55
- global $slb;
56
- $slb->register_theme($name, $title, $stylesheet_url, $layout);
57
- }
1
+ <?php
2
+ /**
3
+ * Plugin entrypoint
4
+ *
5
+ * @package Simple Lightbox
6
+ * @author Archetyped <support@archetyped.com>
7
+ * @copyright 2018 Archetyped
8
+ */
9
+
10
+ /*
11
  Plugin Name: Simple Lightbox
12
+ Plugin URI: http://archetyped.com/tools/simple-lightbox/
13
  Description: The highly customizable lightbox for WordPress
14
+ Version: 2.7.1
15
+ Text Domain: simple-lightbox
16
+ Domain Path: /l10n
17
  Author: Archetyped
18
  Author URI: http://archetyped.com
19
+ Support URI: https://github.com/archetyped/simple-lightbox/wiki/Feedback-&-Support
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  */
21
 
22
+ require_once dirname( __FILE__ ) . '/includes/class-requirements-check.php';
23
+
24
+ /* @var array Plugin Requirements */
25
+ $slb_requirements = new SLB_Requirements_Check(
26
+ array(
27
+ 'name' => __( 'Simple Lightbox', 'simple-lightbox' ),
28
+ 'file' => __FILE__,
29
+ 'uri' => array(
30
+ 'reference' => 'https://github.com/archetyped/simple-lightbox/wiki/Requirements',
31
+ ),
32
+ )
33
+ );
34
+
35
+ // Check requirements before initializing plugin.
36
+ if ( $slb_requirements->passes() ) {
37
+ /**
38
+ * Initialize SLB
39
+ *
40
+ * @return void
41
+ */
42
+ function slb_init() {
43
+ require_once dirname( __FILE__ ) . '/load.php';
44
  }
45
+ add_action( 'init', 'slb_init', 1 );
46
  }
47
 
48
+ unset( $slb_requirements );
 
 
 
 
 
 
 
 
 
model.php DELETED
@@ -1,1097 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Model (Core functionality)
5
- * @package Simple Lightbox
6
- * @author Archetyped
7
- */
8
- class SLB_Lightbox extends SLB_Base {
9
-
10
- /*-** Properties **-*/
11
-
12
- protected $model = true;
13
-
14
- /* Files */
15
-
16
- var $scripts = array (
17
- 'core' => array (
18
- 'file' => 'client/js/lib.core.js',
19
- 'deps' => 'jquery',
20
- 'enqueue' => false,
21
- 'in_footer' => true,
22
- ),
23
- 'view' => array (
24
- 'file' => 'client/js/lib.view.js',
25
- 'deps' => array('jquery', '[core]'),
26
- 'context' => array( array('public', '[is_enabled]') ),
27
- 'in_footer' => true,
28
- ),
29
- );
30
-
31
- /**
32
- * Fields
33
- * @var SLB_Fields
34
- */
35
- public $fields = null;
36
-
37
- /**
38
- * Themes collection
39
- * @var SLB_Themes
40
- */
41
- var $themes = null;
42
-
43
- /**
44
- * Content types
45
- * @var SLB_Content_Handlers
46
- */
47
- var $handlers = null;
48
-
49
- /**
50
- * Template tags
51
- * @var SLB_Template_Tags
52
- */
53
- var $template_tags = null;
54
-
55
- /**
56
- * Properties for media attachments in current request
57
- * > Key (string) Attachment URI
58
- * > Value (assoc-array) Attachment properties (url, etc.)
59
- * > source: Source URL
60
- * @var array
61
- */
62
- var $media_items = array();
63
-
64
- /**
65
- * Raw media items
66
- * Used for populating media object on client-side
67
- * > Key: Item URI
68
- * > Value: Associative array of media properties
69
- * > type: Item type (Default: null)
70
- * > id: Item ID (Default: null)
71
- * @var array
72
- */
73
- private $media_items_raw = array();
74
-
75
- /* Widget properties */
76
-
77
- /**
78
- * Widget callback key
79
- * @var string
80
- */
81
- var $widget_callback = 'callback';
82
-
83
- /**
84
- * Key to use to store original callback
85
- * @var string
86
- */
87
- var $widget_callback_orig = 'callback_orig';
88
-
89
- /**
90
- * Used to track if widget is currently being processed or not
91
- * @var bool
92
- */
93
- var $widget_processing = false;
94
-
95
- /**
96
- * Constructor
97
- */
98
- function __construct() {
99
- parent::__construct();
100
- //Init instances
101
- $this->fields = new SLB_Fields();
102
- $this->themes = new SLB_Themes($this);
103
- if ( !is_admin() ) {
104
- $this->template_tags = new SLB_Template_Tags($this);
105
- }
106
- }
107
-
108
- /* Init */
109
-
110
- /**
111
- * Register hooks
112
- * @uses parent::_hooks()
113
- */
114
- protected function _hooks() {
115
- parent::_hooks();
116
-
117
- /* Admin */
118
- add_action('admin_menu', $this->m('admin_menus'));
119
-
120
- /* Client-side */
121
-
122
- //Init lightbox
123
- $priority = $this->util->priority('low');
124
- add_action('wp_footer', $this->m('client_init'), $this->util->priority('client_footer_output'));
125
- add_action('wp_footer', $this->m('client_footer'), $priority);
126
- //Link activation
127
- add_filter('the_content', $this->m('activate_links'), $priority);
128
- //Gallery wrapping
129
- add_filter('the_content', $this->m('gallery_wrap'), 1);
130
- add_filter('the_content', $this->m('gallery_unwrap'), $priority + 1);
131
-
132
- /* Widgets */
133
- add_filter('sidebars_widgets', $this->m('sidebars_widgets'));
134
- }
135
-
136
- /**
137
- * Init options
138
- *
139
- */
140
- protected function _options() {
141
- //Setup options
142
- $opts = array (
143
- 'groups' => array (
144
- 'activation' => array ( 'title' => __('Activation', 'simple-lightbox'), 'priority' => 10),
145
- 'grouping' => array ( 'title' => __('Grouping', 'simple-lightbox'), 'priority' => 20),
146
- 'ui' => array ( 'title' => __('UI', 'simple-lightbox'), 'priority' => 30),
147
- 'labels' => array ( 'title' => __('Labels', 'simple-lightbox'), 'priority' => 40),
148
- ),
149
- 'items' => array (
150
- 'enabled' => array('title' => __('Enable Lightbox Functionality', 'simple-lightbox'), 'default' => true, 'group' => array('activation', 10)),
151
- 'enabled_home' => array('title' => __('Enable on Home page', 'simple-lightbox'), 'default' => true, 'group' => array('activation', 20)),
152
- 'enabled_post' => array('title' => __('Enable on Posts', 'simple-lightbox'), 'default' => true, 'group' => array('activation', 30)),
153
- 'enabled_page' => array('title' => __('Enable on Pages', 'simple-lightbox'), 'default' => true, 'group' => array('activation', 40)),
154
- 'enabled_archive' => array('title' => __('Enable on Archive Pages (tags, categories, etc.)', 'simple-lightbox'), 'default' => true, 'group' => array('activation', 50)),
155
- 'enabled_widget' => array('title' => __('Enable for Widgets', 'simple-lightbox'), 'default' => false, 'group' => array('activation', 60)),
156
- 'group_links' => array('title' => __('Group items (for displaying as a slideshow)', 'simple-lightbox'), 'default' => true, 'group' => array('grouping', 10)),
157
- 'group_post' => array('title' => __('Group items by Post (e.g. on pages with multiple posts)', 'simple-lightbox'), 'default' => true, 'group' => array('grouping', 20)),
158
- 'group_gallery' => array('title' => __('Group gallery items separately', 'simple-lightbox'), 'default' => false, 'group' => array('grouping', 30)),
159
- 'group_widget' => array('title' => __('Group widget items separately', 'simple-lightbox'), 'default' => false, 'group' => array('grouping', 40)),
160
- 'ui_autofit' => array('title' => __('Resize lightbox to fit in window', 'simple-lightbox'), 'default' => true, 'group' => array('ui', 10), 'in_client' => true),
161
- 'ui_animate' => array('title' => __('Enable animations', 'simple-lightbox'), 'default' => true, 'group' => array('ui', 20), 'in_client' => true),
162
- 'slideshow_autostart' => array('title' => __('Start Slideshow Automatically', 'simple-lightbox'), 'default' => true, 'group' => array('ui', 30), 'in_client' => true),
163
- 'slideshow_duration' => array('title' => __('Slide Duration (Seconds)', 'simple-lightbox'), 'default' => '6', 'attr' => array('size' => 3, 'maxlength' => 3), 'group' => array('ui', 40), 'in_client' => true),
164
- 'group_loop' => array('title' => __('Loop through items', 'simple-lightbox'),'default' => true, 'group' => array('ui', 50), 'in_client' => true),
165
- 'ui_overlay_opacity' => array('title' => __('Overlay Opacity (0 - 1)', 'simple-lightbox'), 'default' => '0.8', 'attr' => array('size' => 3, 'maxlength' => 3), 'group' => array('ui', 60), 'in_client' => true),
166
- 'txt_loading' => array('title' => __('Loading indicator', 'simple-lightbox'), 'default' => 'Loading', 'group' => array('labels', 20)),
167
- 'txt_close' => array('title' => __('Close button', 'simple-lightbox'), 'default' => 'Close', 'group' => array('labels', 10)),
168
- 'txt_nav_next' => array('title' => __('Next Item button', 'simple-lightbox'), 'default' => 'Next', 'group' => array('labels', 30)),
169
- 'txt_nav_prev' => array('title' => __('Previous Item button', 'simple-lightbox'), 'default' => 'Previous', 'group' => array('labels', 40)),
170
- 'txt_slideshow_start' => array('title' => __('Start Slideshow button', 'simple-lightbox'), 'default' => 'Start slideshow', 'group' => array('labels', 50)),
171
- 'txt_slideshow_stop' => array('title' => __('Stop Slideshow button', 'simple-lightbox'),'default' => 'Stop slideshow', 'group' => array('labels', 60)),
172
- 'txt_group_status' => array('title' => __('Slideshow status format', 'simple-lightbox'), 'default' => 'Item %current% of %total%', 'group' => array('labels', 70))
173
- ),
174
- 'legacy' => array (
175
- 'header_activation' => null,
176
- 'header_enabled' => null,
177
- 'header_strings' => null,
178
- 'header_ui' => null,
179
- 'activate_attachments' => null,
180
- 'validate_links' => null,
181
- 'enabled_compat' => null,
182
- 'enabled_single' => array('enabled_post', 'enabled_page'),
183
- 'enabled_caption' => null,
184
- 'enabled_desc' => null,
185
- 'ui_enabled_caption' => null,
186
- 'ui_caption_src' => null,
187
- 'ui_enabled_desc' => null,
188
- 'caption_src' => null,
189
- 'animate' => 'ui_animate',
190
- 'overlay_opacity' => 'ui_overlay_opacity',
191
- 'loop' => 'group_loop',
192
- 'autostart' => 'slideshow_autostart',
193
- 'duration' => 'slideshow_duration',
194
- 'txt_numDisplayPrefix' => null,
195
- 'txt_numDisplaySeparator' => null,
196
- 'txt_closeLink' => 'txt_link_close',
197
- 'txt_nextLink' => 'txt_link_next',
198
- 'txt_prevLink' => 'txt_link_prev',
199
- 'txt_startSlideshow' => 'txt_slideshow_start',
200
- 'txt_stopSlideshow' => 'txt_slideshow_stop',
201
- 'txt_loadingMsg' => 'txt_loading',
202
- 'txt_link_next' => 'txt_nav_next',
203
- 'txt_link_prev' => 'txt_nav_prev',
204
- 'txt_link_close' => 'txt_close',
205
- )
206
- );
207
-
208
- parent::_options($opts);
209
- }
210
-
211
- /* Methods */
212
-
213
- /*-** Admin **-*/
214
-
215
- /**
216
- * Add admin menus
217
- * @uses this->admin->add_theme_page
218
- */
219
- function admin_menus() {
220
- $options_labels = array(
221
- 'menu' => __('Lightbox', 'simple-lightbox'),
222
- 'header' => __('Lightbox Settings', 'simple-lightbox'),
223
- 'plugin_action' => __('Settings', 'simple-lightbox')
224
- );
225
-
226
- $labels_reset = array (
227
- 'title' => __('Reset', 'simple-lightbox'),
228
- 'confirm' => __('Are you sure you want to reset settings?', 'simple-lightbox'),
229
- 'success' => __('Settings have been reset', 'simple-lightbox'),
230
- 'failure' => __('Settings were not reset', 'simple-lightbox')
231
- );
232
-
233
- $this->admin->add_theme_page('options', $options_labels, $this->options);
234
- $this->admin->add_reset('reset', $labels_reset, $this->options);
235
- }
236
-
237
- /*-** Functionality **-*/
238
-
239
- /**
240
- * Checks whether lightbox is currently enabled/disabled
241
- * @return bool TRUE if lightbox is currently enabled, FALSE otherwise
242
- */
243
- function is_enabled($check_request = true) {
244
- $ret = ( $this->options->get_bool('enabled') && !is_feed() ) ? true : false;
245
- if ( $ret && $check_request ) {
246
- $opt = '';
247
- //Determine option to check
248
- if ( is_home() )
249
- $opt = 'home';
250
- elseif ( is_singular() ) {
251
- $opt = ( is_page() ) ? 'page' : 'post';
252
- }
253
- elseif ( is_archive() || is_search() )
254
- $opt = 'archive';
255
- //Check option
256
- if ( !empty($opt) && ( $opt = 'enabled_' . $opt ) && $this->options->has($opt) ) {
257
- $ret = $this->options->get_bool($opt);
258
- }
259
- }
260
- return $ret;
261
- }
262
-
263
- /**
264
- * Scans post content for image links and activates them
265
- *
266
- * Lightbox will not be activated for feeds
267
- * @param $content
268
- * @return string Post content
269
- */
270
- function activate_links($content) {
271
- //Activate links only if enabled
272
- if ( !$this->is_enabled() ) {
273
- return $content;
274
- }
275
-
276
- $groups = array();
277
- $w = $this->group_get_wrapper();
278
- $g_ph_f = '[%s]';
279
-
280
- //Strip groups
281
- if ( $this->options->get_bool('group_gallery') ) {
282
- $groups = array();
283
- static $g_idx = 1;
284
- $g_end_idx = 0;
285
- //Iterate through galleries
286
- while ( ($g_start_idx = strpos($content, $w->open, $g_end_idx)) && $g_start_idx !== false
287
- && ($g_end_idx = strpos($content, $w->close, $g_start_idx)) && $g_end_idx != false ) {
288
- $g_start_idx += strlen($w->open);
289
- //Extract gallery content & save for processing
290
- $g_len = $g_end_idx - $g_start_idx;
291
- $groups[$g_idx] = substr($content, $g_start_idx, $g_len);
292
- //Replace content with placeholder
293
- $g_ph = sprintf($g_ph_f, $g_idx);
294
- $content = substr_replace($content, $g_ph, $g_start_idx, $g_len);
295
- //Increment gallery count
296
- $g_idx++;
297
- //Update end index
298
- $g_end_idx = $g_start_idx + strlen($w->open);
299
- }
300
- }
301
-
302
- //General link processing
303
- $content = $this->process_links($content);
304
-
305
- //Reintegrate Groups
306
- foreach ( $groups as $group => $g_content ) {
307
- $g_ph = $w->open . sprintf($g_ph_f, $group) . $w->close;
308
- //Skip group if placeholder does not exist in content
309
- if ( strpos($content, $g_ph) === false ) {
310
- continue;
311
- }
312
- //Replace placeholder with processed content
313
- $content = str_replace($g_ph, $w->open . $this->process_links($g_content, 'gallery_' . $group) . $w->close, $content);
314
- }
315
- return $content;
316
- }
317
-
318
- /**
319
- * Process links in content
320
- * @global obj $wpdb DB instance
321
- * @global obj $post Current post
322
- * @param string $content Text containing links
323
- * @param string (optional) $group Group to add links to (Default: none)
324
- * @return string Content with processed links
325
- */
326
- function process_links($content, $group = null) {
327
- //Validate
328
- if ( !is_string($content) || empty($content) ) {
329
- return $content;
330
- }
331
- //Extract links
332
- $links = $this->get_links($content, true);
333
- //Do not process content without links
334
- if ( empty($links) ) {
335
- return $content;
336
- }
337
- //Process links
338
- $protocol = array('http://', 'https://');
339
- $domain = str_replace($protocol, '', strtolower(get_bloginfo('url')));
340
- $qv_att = 'attachment_id';
341
-
342
- //Setup group properties
343
- $g_props = (object) array(
344
- 'enabled' => $this->options->get_bool('group_links'),
345
- 'attr' => 'group',
346
- 'base' => '',
347
- 'legacy_prefix' => 'lightbox[',
348
- 'legacy_suffix' => ']'
349
- );
350
- if ( $g_props->enabled ) {
351
- $g_props->base = ( is_scalar($group) ) ? trim(strval($group)) : '';
352
- }
353
-
354
- //Initialize content handlers
355
- if ( !( $this->handlers instanceof SLB_Content_Handlers ) ) {
356
- $this->handlers = new SLB_Content_Handlers($this);
357
- }
358
-
359
- //Iterate through links & add lightbox if necessary
360
- foreach ( $links as $link ) {
361
- //Init vars
362
- $pid = 0;
363
- $link_new = $link;
364
- $internal = false;
365
- $q = null;
366
- $uri = (object) array('raw' => '', 'base' => '', 'source' => '');
367
- $type = false;
368
-
369
- //Parse link attributes
370
- $attrs = $this->util->parse_attribute_string($link_new, array('href' => ''));
371
- $attrs_legacy = ( isset($attrs['rel']) && !empty($attrs['rel']) ) ? explode(' ', trim($attrs['rel'])) : array();
372
- //Get URI
373
- $uri->raw = $uri->base = $uri->source = $attrs['href'];
374
-
375
- //Stop processing invalid links
376
- if ( empty($uri->raw) //Empty
377
- || 0 === strpos($uri->raw, '#') //Invalid
378
- || $this->has_attribute($attrs, 'active', false) //Prev-processed
379
- || in_array($this->add_prefix('off'), $attrs_legacy) //Disabled
380
- ) {
381
- continue;
382
- }
383
-
384
- //Process legacy attributes
385
- if ( !empty($attrs_legacy) ) {
386
- //Group
387
- if ( $g_props->enabled ) {
388
- foreach ( $attrs_legacy as $attr ) {
389
- if ( 0 === strpos($attr, $g_props->legacy_prefix) && substr($attr, -1) == $g_props->legacy_suffix ) {
390
- $this->set_attribute($attrs, $g_props->attr, substr($attr, strlen($g_props->legacy_prefix), -1));
391
- break;
392
- }
393
- }
394
- unset($attr);
395
- }
396
- }
397
-
398
- //Check if item links to internal media (attachment)
399
- $uri_dom = str_replace($protocol, '', strtolower($uri->raw));
400
- if ( strpos($uri_dom, $domain) === 0 ) {
401
- //Save URL for further processing
402
- $internal = true;
403
- }
404
-
405
- //Sanitize URI
406
- $qpos = strpos($uri->raw, '?');
407
- if ( $qpos !== false ) {
408
- $uri->base = substr($uri->raw, 0, $qpos);
409
- if ( $internal ) {
410
- //Extract query string
411
- $q = substr($uri->raw, $qpos + 1);
412
- //Check for attachment ID
413
- if ( strpos($q, $qv_att . '=') !== false ) {
414
- //Parse query string
415
- wp_parse_str($q, $q);
416
- //Strip other variables from query string
417
- $uri->base = add_query_arg($qv_att, $q[$qv_att], $uri->base);
418
- }
419
- }
420
- }
421
-
422
- //Get source URI
423
- $uri->source = $uri->base;
424
- if ( $internal && is_local_attachment($uri->source) ) {
425
- $pid = url_to_postid($uri->base);
426
- $src = wp_get_attachment_url($pid);
427
- if ( !!$src ) {
428
- $uri->source = $src;
429
- }
430
- unset($src);
431
- }
432
-
433
- /* Determine link type */
434
-
435
- //Check if URI has already been processed
436
- if ( $this->media_item_cached($uri->base) ) {
437
- $i = $this->get_cached_media_item($uri->base);
438
- $type = $i->type;
439
- }
440
-
441
- //Get handler match
442
- else {
443
- $handler = $this->handlers->match($uri->source);
444
- if ( !!$handler ) {
445
- $type = $handler->get_id();
446
- }
447
- }
448
-
449
- //Stop processing if link type not valid
450
- if ( !$type ) {
451
- continue;
452
- }
453
-
454
- //Set group (if necessary)
455
- if ( $g_props->enabled ) {
456
- //Get preset group attribute
457
- $g = ( $this->has_attribute($attrs, $g_props->attr) ) ? $this->get_attribute($attrs, $g_props->attr) : '';
458
- if ( is_string($g) && ($g = trim($g)) && !empty($g) ) {
459
- $group = $g;
460
- } else {
461
- $group = $g_props->base;
462
- }
463
- //Group links by post?
464
- if ( !$this->widget_processing && $this->options->get_bool('group_post') ) {
465
- global $post;
466
- $group = ( !empty($group) ) ? implode('_', array($post->ID, $group)) : $post->ID;
467
- }
468
- //Default group
469
- if ( empty($group) ) {
470
- $group = $this->get_prefix();
471
- }
472
-
473
- //Set group attribute
474
- $this->set_attribute($attrs, $g_props->attr, $group);
475
- unset($g);
476
- }
477
-
478
- //Activate link
479
- $this->set_attribute($attrs, 'active');
480
-
481
- //Process internal links
482
- if ( $internal ) {
483
- //Mark as internal
484
- $this->set_attribute($attrs, 'internal', $pid);
485
- }
486
-
487
- //Cache item attributes
488
- $this->cache_media_item($uri, $type, $internal, $pid);
489
-
490
- //Update link in content
491
- $link_new = '<a ' . $this->util->build_attribute_string($attrs) . '>';
492
- $content = str_replace($link, $link_new, $content);
493
- }
494
- return $content;
495
- }
496
-
497
- /**
498
- * Retrieve HTML links in content
499
- * @param string $content Content to get links from
500
- * @param bool (optional) $unique Remove duplicates from returned links (Default: FALSE)
501
- * @return array Links in content
502
- */
503
- function get_links($content, $unique = false) {
504
- $rgx = "/\<a[^\>]+href=.*?\>/i";
505
- $links = array();
506
- preg_match_all($rgx, $content, $links);
507
- $links = $links[0];
508
- if ( $unique )
509
- $links = array_unique($links);
510
- return $links;
511
- }
512
-
513
- /**
514
- * Sets options/settings to initialize lightbox functionality on page load
515
- * @return void
516
- */
517
- function client_init() {
518
- if ( ! $this->is_enabled() ) {
519
- return;
520
- }
521
- echo PHP_EOL . '<!-- SLB -->' . PHP_EOL;
522
- //Get options
523
- $options = $this->options->build_client_output();
524
-
525
- //Load UI Strings
526
- if ( ($labels = $this->build_labels()) && !empty($labels) ) {
527
- $options['ui_labels'] = $labels;
528
- }
529
-
530
- //Build client output
531
- echo $this->util->build_script_element($this->util->call_client_method('View.init', $options), 'init', true, true);
532
- echo '<!-- /SLB -->' . PHP_EOL;
533
- }
534
-
535
- /**
536
- * Output code in footer
537
- * > Media attachment URLs
538
- * @uses `_wp_attached_file` to match attachment ID to URI
539
- * @uses `_wp_attachment_metadata` to retrieve attachment metadata
540
- */
541
- function client_footer() {
542
- echo '<!-- X-M -->';
543
- //Stop if not enabled
544
- if ( !$this->is_enabled() ) {
545
- return;
546
- }
547
- echo '<!-- SLB-M -->' . PHP_EOL;
548
-
549
- $client_out = array();
550
-
551
- /* Load cached media */
552
- if ( $this->has_cached_media_items() ) {
553
- global $wpdb;
554
-
555
- $this->media_items = array();
556
- $props = array('id', 'type', 'description', 'title', 'source', 'caption');
557
- $props = (object) array_combine($props, $props);
558
- $props_map = array('description' => 'post_content', 'title' => 'post_title', 'caption' => 'post_excerpt');
559
-
560
- //Separate media into buckets by type
561
- $m_bucket = array();
562
- $m_internals = array();
563
- $type = $id = null;
564
-
565
- $m_items = $this->media_items = $this->get_cached_media_items();
566
- foreach ( $m_items as $uri => $p ) {
567
- $type = $p->{$props->type};
568
- //Initialize bucket (if necessary)
569
- if ( !isset($m_bucket[$type]) ) {
570
- $m_bucket[$type] = array();
571
- }
572
- //Add item to bucket
573
- $m_bucket[$type][$uri] =& $m_items[$uri];
574
- //Set aside internal links for additional processing
575
- if ( $p->internal && !isset($m_internals[$uri]) ) {
576
- $m_internals[$uri] =& $m_items[$uri];
577
- }
578
- }
579
- unset($uri, $p);
580
-
581
- //Process internal links
582
- if ( !empty($m_internals) ) {
583
- $uris_base = array();
584
- $uri_prefix = wp_upload_dir();
585
- $uri_prefix = $this->util->normalize_path($uri_prefix['baseurl'], true);
586
- foreach ( $m_internals as $uri => $p ) {
587
- //Prepare internal links
588
- if ( !$p->id && strpos($p->source, $uri_prefix) === 0 ) {
589
- $uris_base[str_replace($uri_prefix, '', $p->source)] = $uri;
590
- }
591
- }
592
- unset($uri, $p);
593
-
594
- //Retrieve attachment IDs
595
- $uris_flat = "('" . implode("','", array_keys($uris_base)) . "')";
596
- $q = $wpdb->prepare("SELECT post_id, meta_value FROM $wpdb->postmeta WHERE `meta_key` = %s AND LOWER(`meta_value`) IN $uris_flat LIMIT %d", '_wp_attached_file', count($uris_base));
597
- $pids = $wpdb->get_results($q);
598
- //Match IDs to URIs
599
- if ( $pids ) {
600
- foreach ( $pids as $pd ) {
601
- $file =& $pd->meta_value;
602
- if ( isset($uris_base[$file]) ) {
603
- $m_internals[ $uris_base[$file] ]->{$props->id} = absint($pd->post_id);
604
- }
605
- }
606
- }
607
- //Destroy worker vars
608
- unset($uris_base, $uris_flat, $q, $pids, $pd);
609
- }
610
-
611
- //Process items with attachment IDs
612
- $pids = array();
613
- foreach ( $m_items as $uri => $p ) {
614
- //Add post ID to query
615
- if ( !!$p->id ) {
616
- //Create array for ID (support multiple URIs per ID)
617
- if ( !isset($pids[$p->id]) ) {
618
- $pids[$p->id] = array();
619
- }
620
- //Add URI to ID
621
- $pids[$p->id][] = $uri;
622
- }
623
- }
624
- unset($uri, $p);
625
-
626
- //Retrieve attachment properties
627
- if ( !empty($pids) ) {
628
- $pids_flat = array_keys($pids);
629
- //Retrieve attachment post data
630
- $atts = get_posts(array('post_type' => 'attachment', 'include' => $pids_flat));
631
-
632
- //Process attachments
633
- if ( $atts ) {
634
- //Retrieve attachment metadata
635
- $pids_flat = "('" . implode("','", $pids_flat) . "')";
636
- $atts_meta = $wpdb->get_results($wpdb->prepare("SELECT `post_id`,`meta_value` FROM $wpdb->postmeta WHERE `post_id` IN $pids_flat AND `meta_key` = %s LIMIT %d", '_wp_attachment_metadata', count($atts)));
637
- //Restructure metadata array by post ID
638
- if ( $atts_meta ) {
639
- $meta = array();
640
- foreach ( $atts_meta as $att_meta ) {
641
- $meta[$att_meta->post_id] = $att_meta->meta_value;
642
- }
643
- $atts_meta = $meta;
644
- unset($meta);
645
- } else {
646
- $atts_meta = array();
647
- }
648
- $props_size = array('file', 'width', 'height');
649
- $props_exclude = array('hwstring_small');
650
- foreach ( $atts as $att ) {
651
- //Set post data
652
- $m = array();
653
- //Remap post data to properties
654
- foreach ( $props_map as $prop_key => $prop_source ) {
655
- $m[$props->{$prop_key}] = $att->{$prop_source};
656
- }
657
- unset($prop_key, $prop_source);
658
-
659
- //Add metadata
660
- if ( isset($atts_meta[$att->ID]) && ($a = unserialize($atts_meta[$att->ID])) && is_array($a) ) {
661
- //Move original size into `sizes` array
662
- foreach ( $props_size as $d ) {
663
- if ( !isset($a[$d]) ) {
664
- continue;
665
- }
666
- $a['sizes']['original'][$d] = $a[$d];
667
- unset($a[$d]);
668
- }
669
-
670
- //Strip extraneous metadata
671
- foreach ( $props_exclude as $d ) {
672
- if ( isset($a[$d]) ) {
673
- unset($a[$d]);
674
- }
675
- }
676
-
677
- //Merge post data & meta data
678
- $m = array_merge($a, $m);
679
- //Destroy worker vars
680
- unset($a, $d);
681
- }
682
-
683
- //Save attachment data (post & meta) to original object(s)
684
- foreach ( $pids[$att->ID] as $uri ) {
685
- $this->media_items[$uri] = (object) array_merge( (array) $m_items[$uri], $m);
686
- }
687
- }
688
- }
689
- unset($atts, $atts_meta, $m, $a, $uri, $pids, $pids_flat);
690
- }
691
-
692
- //Build client output
693
- $obj = 'View.assets';
694
- $client_out[] = $this->util->extend_client_object($obj, $this->media_items);
695
- }
696
- if ( !empty($client_out) ) {
697
- echo $this->util->build_script_element($client_out, 'footer', true, true);
698
- }
699
- echo PHP_EOL . '<!-- /SLB-M -->' . PHP_EOL;
700
- }
701
-
702
- /*-** Media **-*/
703
-
704
- /**
705
- * Cache media properties for later processing
706
- * @param object $uri URI to cache
707
- * Members
708
- * > raw: Raw Link URI
709
- * > base: Sanitized URI
710
- * > source: Source URI (e.g. for attachment URIs)
711
- * @param string $type Media type (image, attachment, etc.)
712
- * @param int $id (optional) ID of media item (for internal items) (Default: NULL)
713
- */
714
- private function cache_media_item($uri, $type, $internal, $id = null) {
715
- //Validate
716
- if ( !is_object($uri) || !is_string($type) ) {
717
- return false;
718
- }
719
- if ( !$this->media_item_cached($uri->base) ) {
720
- //Set properties
721
- $i = array('type' => $type, 'source' => $uri->source, 'internal' => $internal, 'id' => null, '_entries' => array());
722
- //ID
723
- if ( is_numeric($id) && !!$id ) {
724
- $i['id'] = absint($id);
725
- }
726
- //Cache media item
727
- $this->media_items_raw[$uri->base] = (object) $i;
728
- }
729
- //Add URI variants
730
- $entries =& $this->media_items_raw[$uri->base]->_entries;
731
- if ( !in_array($uri->raw, $entries) ) {
732
- $entries[] = $uri->raw;
733
- }
734
- }
735
-
736
- /**
737
- * Checks if media item has already been cached
738
- * @param string $uri URI of media item
739
- * @return boolean Whether media item has been cached
740
- */
741
- private function media_item_cached($uri) {
742
- return ( is_string($uri) && isset($this->media_items_raw[$uri]) ) ? true : false;
743
- }
744
-
745
- /**
746
- * Retrieve cached media item
747
- * @param string $uri Media item URI
748
- * @return object|null Media item properties (NULL if not set)
749
- */
750
- private function get_cached_media_item($uri) {
751
- return ( $this->media_item_cached($uri) ) ? $this->media_items_raw[$uri] : null;
752
- }
753
-
754
- /**
755
- * Retrieve cached media items
756
- * @return array Cached media items
757
- */
758
- private function &get_cached_media_items() {
759
- return $this->media_items_raw;
760
- }
761
-
762
- /**
763
- * Check if media items have been cached
764
- * @return boolean
765
- */
766
- private function has_cached_media_items() {
767
- return ( empty($this->media_items_raw) ) ? false : true;
768
- }
769
-
770
- /*-** Theme **-*/
771
-
772
- /**
773
- * Retrieve theme
774
- * @param string $id ID of theme to retrieve
775
- * @return SLB_Theme Theme instance
776
- * @TODO Refactor
777
- */
778
- function get_theme($id = '') {
779
- //Default: Get current theme if no theme specified
780
- if ( !$this->themes->has_item($id) ) {
781
- $id = $this->options->get_value('theme');
782
- if ( !$this->themes->has_item($id) ) {
783
- $id = $this->themes->get_default_id();
784
- }
785
- }
786
- return $this->themes->get_item($id);
787
- }
788
-
789
- /*-** Grouping **-*/
790
-
791
- /**
792
- * Builds wrapper for grouping
793
- * @return object Wrapper properties
794
- * > open
795
- * > close
796
- */
797
- function group_get_wrapper() {
798
- static $wrapper = null;
799
- if ( is_null($wrapper) ) {
800
- $start = '<';
801
- $end = '>';
802
- $terminate = '/';
803
- $val = $this->add_prefix('group');
804
- //Build properties
805
- $wrapper = array(
806
- 'open' => $start . $val . $end,
807
- 'close' => $start . $terminate . $val . $end
808
- );
809
- //Convert to object
810
- $wrapper = (object) $wrapper;
811
- }
812
- return $wrapper;
813
- }
814
-
815
- /**
816
- * Wraps galleries for grouping
817
- * @uses `the_content` Filter hook
818
- * @uses gallery_wrap_callback to Wrap shortcodes for grouping
819
- * @param string $content Post content
820
- * @return string Modified post content
821
- */
822
- function gallery_wrap($content) {
823
- if ( !$this->is_enabled() )
824
- return $content;
825
- //Stop processing if option not enabled
826
- if ( !$this->options->get_bool('group_gallery') )
827
- return $content;
828
- global $shortcode_tags;
829
- //Save default shortcode handlers to temp variable
830
- $sc_temp = $shortcode_tags;
831
- //Find gallery shortcodes
832
- $shortcodes = array('gallery', 'nggallery');
833
- $m = $this->m('gallery_wrap_callback');
834
- $shortcode_tags = array();
835
- foreach ( $shortcodes as $tag ) {
836
- $shortcode_tags[$tag] = $m;
837
- }
838
- //Wrap gallery shortcodes
839
- $content = do_shortcode($content);
840
- //Restore default shortcode handlers
841
- $shortcode_tags = $sc_temp;
842
-
843
- return $content;
844
- }
845
-
846
- /**
847
- * Wraps gallery shortcodes for later processing
848
- * @param array $attr Shortcode attributes
849
- * @param string $content Content enclosed in shortcode
850
- * @param string $tag Shortcode name
851
- * @return string Wrapped gallery shortcode
852
- */
853
- function gallery_wrap_callback($attr, $content = null, $tag) {
854
- //Rebuild shortcode
855
- $sc = '[' . $tag . ' ' . $this->util->build_attribute_string($attr) . ']';
856
- if ( !empty($content) )
857
- $sc .= $content . '[/' . $tag .']';
858
- //Wrap shortcode
859
- $w = $this->group_get_wrapper();
860
- $sc = $w->open . $sc . $w->close;
861
- return $sc;
862
- }
863
-
864
- /**
865
- * Removes wrapping from galleries
866
- * @uses `the_content` filter hook
867
- * @param $content Post content
868
- * @return string Modified post content
869
- */
870
- function gallery_unwrap($content) {
871
- if ( !$this->is_enabled() )
872
- return $content;
873
- //Stop processing if option not enabled
874
- if ( !$this->options->get_bool('group_gallery') )
875
- return $content;
876
- $w = $this->group_get_wrapper();
877
- if ( strpos($content, $w->open) !== false ) {
878
- $content = str_replace($w->open, '', $content);
879
- $content = str_replace($w->close, '', $content);
880
- }
881
- return $content;
882
- }
883
-
884
- /*-** Widgets **-*/
885
-
886
- /**
887
- * Reroute widget display handlers to internal method
888
- * @param array $sidebar_widgets List of sidebars & their widgets
889
- * @uses WP Hook `sidebars_widgets` to intercept widget list
890
- * @global $wp_registered_widgets to reroute display callback
891
- * @return array Sidebars and widgets (unmodified)
892
- */
893
- function sidebars_widgets($sidebars_widgets) {
894
- global $wp_registered_widgets;
895
- static $widgets_processed = false;
896
- if ( is_admin() || empty($wp_registered_widgets) || $widgets_processed || !is_object($this->options) || !$this->is_enabled() || !$this->options->get_bool('enabled_widget') )
897
- return $sidebars_widgets;
898
- $widgets_processed = true;
899
- //Fetch active widgets from all sidebars
900
- foreach ( $sidebars_widgets as $sb => $ws ) {
901
- //Skip inactive widgets and empty sidebars
902
- if ( 'wp_inactive_widgets' == $sb || empty($ws) || !is_array($ws) )
903
- continue;
904
- foreach ( $ws as $w ) {
905
- if ( isset($wp_registered_widgets[$w]) && isset($wp_registered_widgets[$w][$this->widget_callback]) ) {
906
- $wref =& $wp_registered_widgets[$w];
907
- //Backup original callback
908
- $wref[$this->widget_callback_orig] = $wref[$this->widget_callback];
909
- //Reroute callback
910
- $wref[$this->widget_callback] = $this->m('widget_callback');
911
- unset($wref);
912
- }
913
- }
914
- }
915
-
916
- return $sidebars_widgets;
917
- }
918
-
919
- /**
920
- * Widget display handler
921
- * Original widget display handler is called inside of an output buffer & links in output are processed before sending to browser
922
- * @param array $args Widget instance properties
923
- * @param int (optional) $widget_args Additional widget args (usually the widget's instance number)
924
- * @see WP_Widget::display_callback() for more information
925
- * @see sidebars_widgets() for callback modification
926
- * @global $wp_registered_widgets
927
- * @uses widget_process_links() to Process links in widget content
928
- * @return void
929
- */
930
- function widget_callback($args, $widget_args = 1) {
931
- global $wp_registered_widgets;
932
- $wid = ( isset($args['widget_id']) ) ? $args['widget_id'] : false;
933
- //Stop processing if widget data invalid
934
- if ( !$wid || !isset($wp_registered_widgets[$wid]) || !($w =& $wp_registered_widgets[$wid]) || !isset($w['id']) || $wid != $w['id'] )
935
- return false;
936
- //Get original callback
937
- if ( !isset($w[$this->widget_callback_orig]) || !($cb = $w[$this->widget_callback_orig]) || !is_callable($cb) )
938
- return false;
939
- $params = func_get_args();
940
- $this->widget_processing = true;
941
- //Start output buffer
942
- ob_start();
943
- //Call original callback
944
- call_user_func_array($cb, $params);
945
- //Flush output buffer
946
- echo $this->widget_process_links(ob_get_clean(), $wid);
947
- $this->widget_processing = false;
948
- }
949
-
950
- /**
951
- * Process links in widget content
952
- * @param string $content Widget content
953
- * @return string Processed widget content
954
- * @uses process_links() to process links
955
- */
956
- function widget_process_links($content, $id) {
957
- $id = ( $this->options->get_bool('group_widget') ) ? "widget_$id" : null;
958
- return $this->process_links($content, $id);
959
- }
960
-
961
- /*-** Helpers **-*/
962
-
963
- /**
964
- * Build attribute name
965
- * Makes sure name is only prefixed once
966
- * @param string $name (optional) Attribute base name
967
- * @return string Formatted attribute name
968
- */
969
- function make_attribute_name($name = '') {
970
- //Validate
971
- if ( !is_string($name) ) {
972
- $name = '';
973
- } else {
974
- $name = trim($name);
975
- }
976
- //Setup
977
- $sep = '-';
978
- $top = 'data';
979
- //Generate valid name
980
- if ( strpos($name, $top . $sep . $this->get_prefix()) !== 0 ) {
981
- $name = $top . $sep . $this->add_prefix($name, $sep);
982
- }
983
- return $name;
984
- }
985
-
986
- /**
987
- * Set attribute to array
988
- * Attribute is added to array if it does not exist
989
- * @param array $attrs Array to add attribute to (Passed by reference)
990
- * @param string $name Name of attribute to add
991
- * @param string (optional) $value Attribute value
992
- * @return array Updated attribute array
993
- */
994
- function set_attribute(&$attrs, $name, $value = true) {
995
- //Validate
996
- $attrs = $this->get_attributes($attrs, false);
997
- if ( !is_string($name) || empty($name) ) {
998
- return $attrs;
999
- }
1000
- if ( !is_scalar($value) ) {
1001
- $value = true;
1002
- }
1003
- //Add attribute
1004
- $attrs = array_merge($attrs, array( $this->make_attribute_name($name) => strval($value) ));
1005
-
1006
- return $attrs;
1007
- }
1008
-
1009
- /**
1010
- * Convert attribute string into array
1011
- * @param string $attr_string Attribute string
1012
- * @param bool (optional) $internal Whether only internal attributes should be evaluated (Default: TRUE)
1013
- * @return array Attributes as associative array
1014
- */
1015
- function get_attributes($attr_string, $internal = true) {
1016
- if ( is_string($attr_string) ) {
1017
- $attr_string = $this->util->parse_attribute_string($attr_string);
1018
- }
1019
- $ret = ( is_array($attr_string) ) ? $attr_string : array();
1020
- //Filter out external attributes
1021
- if ( !empty($ret) && is_bool($internal) && $internal ) {
1022
- $ret_f = array();
1023
- foreach ( $ret as $key => $val ) {
1024
- if ( strpos($key, $this->make_attribute_name()) == 0 ) {
1025
- $ret_f[$key] = $val;
1026
- }
1027
- }
1028
- if ( !empty($ret_f) ) {
1029
- $ret = $ret_f;
1030
- }
1031
- }
1032
-
1033
- return $ret;
1034
- }
1035
-
1036
- /**
1037
- * Retrieve attribute value
1038
- * @param string|array $attrs Attributes to retrieve attribute value from
1039
- * @param string $attr Attribute name to retrieve
1040
- * @param bool (optional) $internal Whether only internal attributes should be evaluated (Default: TRUE)
1041
- * @return string|bool Attribute value (Default: FALSE)
1042
- */
1043
- function get_attribute($attrs, $attr, $internal = true) {
1044
- $ret = false;
1045
- //Validate
1046
- $attrs = $this->get_attributes($attrs, $internal);
1047
- if ( $internal ) {
1048
- $attr = $this->make_attribute_name($attr);
1049
- }
1050
- if ( isset($attrs[$attr]) ) {
1051
- $ret = $attrs[$attr];
1052
- }
1053
- return $ret;
1054
- }
1055
-
1056
- /**
1057
- * Checks if attribute exists
1058
- * If supplied, the attribute's value is also validated
1059
- * @param string|array $attrs Attributes to retrieve attribute value from
1060
- * @param string $attr Attribute name to retrieve
1061
- * @param mixed $value (optional) Attribute value to check for
1062
- * @param bool $internal (optional) Whether to check only internal attributes (Default: TRUE)
1063
- * @see get_attribute()
1064
- * @return bool Whether or not attribute (with matching value if specified) exists
1065
- */
1066
- function has_attribute($attrs, $attr, $value = null, $internal = true) {
1067
- $a = $this->get_attribute($attrs, $attr, $internal);
1068
- $ret = false;
1069
- if ( $a !== false ) {
1070
- $ret = true;
1071
- //Check value
1072
- if ( null != $value && is_scalar($value) ) {
1073
- $ret = ( $a == strval($value) ) ? true : false;
1074
- }
1075
- }
1076
- return $ret;
1077
- }
1078
-
1079
- /**
1080
- * Build JS object of UI strings when initializing lightbox
1081
- * @return array UI strings
1082
- */
1083
- function build_labels() {
1084
- $ret = array();
1085
- //Get all UI options
1086
- $prefix = 'txt_';
1087
- $opt_strings = array_filter(array_keys($this->options->get_items()), create_function('$opt', 'return ( strpos($opt, "' . $prefix . '") === 0 );'));
1088
- if ( count($opt_strings) ) {
1089
- //Build array of UI options
1090
- foreach ( $opt_strings as $key ) {
1091
- $name = substr($key, strlen($prefix));
1092
- $ret[$name] = $this->options->get_value($key);
1093
- }
1094
- }
1095
- return $ret;
1096
- }
1097
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
package-lock.json ADDED
@@ -0,0 +1,2941 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "simple-lightbox",
3
+ "version": "2.7.1",
4
+ "lockfileVersion": 1,
5
+ "requires": true,
6
+ "dependencies": {
7
+ "abbrev": {
8
+ "version": "1.1.1",
9
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
10
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
11
+ "dev": true
12
+ },
13
+ "amdefine": {
14
+ "version": "1.0.1",
15
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
16
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
17
+ "dev": true
18
+ },
19
+ "ansi-regex": {
20
+ "version": "2.1.1",
21
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
22
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
23
+ "dev": true
24
+ },
25
+ "ansi-styles": {
26
+ "version": "2.2.1",
27
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
28
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
29
+ "dev": true
30
+ },
31
+ "aproba": {
32
+ "version": "1.2.0",
33
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
34
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
35
+ "dev": true
36
+ },
37
+ "are-we-there-yet": {
38
+ "version": "1.1.4",
39
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
40
+ "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
41
+ "dev": true,
42
+ "requires": {
43
+ "delegates": "1.0.0",
44
+ "readable-stream": "2.3.6"
45
+ },
46
+ "dependencies": {
47
+ "isarray": {
48
+ "version": "1.0.0",
49
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
50
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
51
+ "dev": true
52
+ },
53
+ "readable-stream": {
54
+ "version": "2.3.6",
55
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
56
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
57
+ "dev": true,
58
+ "requires": {
59
+ "core-util-is": "1.0.2",
60
+ "inherits": "2.0.3",
61
+ "isarray": "1.0.0",
62
+ "process-nextick-args": "2.0.0",
63
+ "safe-buffer": "5.1.2",
64
+ "string_decoder": "1.1.1",
65
+ "util-deprecate": "1.0.2"
66
+ }
67
+ },
68
+ "string_decoder": {
69
+ "version": "1.1.1",
70
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
71
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
72
+ "dev": true,
73
+ "requires": {
74
+ "safe-buffer": "5.1.2"
75
+ }
76
+ }
77
+ }
78
+ },
79
+ "argparse": {
80
+ "version": "1.0.10",
81
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
82
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
83
+ "dev": true,
84
+ "requires": {
85
+ "sprintf-js": "1.0.3"
86
+ }
87
+ },
88
+ "array-differ": {
89
+ "version": "1.0.0",
90
+ "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz",
91
+ "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=",
92
+ "dev": true
93
+ },
94
+ "array-find-index": {
95
+ "version": "1.0.2",
96
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
97
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
98
+ "dev": true
99
+ },
100
+ "array-union": {
101
+ "version": "1.0.2",
102
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
103
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
104
+ "dev": true,
105
+ "requires": {
106
+ "array-uniq": "1.0.3"
107
+ }
108
+ },
109
+ "array-uniq": {
110
+ "version": "1.0.3",
111
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
112
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
113
+ "dev": true
114
+ },
115
+ "arrify": {
116
+ "version": "1.0.1",
117
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
118
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
119
+ "dev": true
120
+ },
121
+ "asn1": {
122
+ "version": "0.2.3",
123
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
124
+ "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
125
+ "dev": true
126
+ },
127
+ "assert-plus": {
128
+ "version": "0.2.0",
129
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
130
+ "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
131
+ "dev": true
132
+ },
133
+ "async": {
134
+ "version": "1.5.2",
135
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
136
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
137
+ "dev": true
138
+ },
139
+ "async-foreach": {
140
+ "version": "0.1.3",
141
+ "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
142
+ "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
143
+ "dev": true
144
+ },
145
+ "asynckit": {
146
+ "version": "0.4.0",
147
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
148
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
149
+ "dev": true
150
+ },
151
+ "aws-sign2": {
152
+ "version": "0.6.0",
153
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
154
+ "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
155
+ "dev": true
156
+ },
157
+ "aws4": {
158
+ "version": "1.7.0",
159
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
160
+ "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==",
161
+ "dev": true
162
+ },
163
+ "balanced-match": {
164
+ "version": "1.0.0",
165
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
166
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
167
+ "dev": true
168
+ },
169
+ "bcrypt-pbkdf": {
170
+ "version": "1.0.1",
171
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
172
+ "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
173
+ "dev": true,
174
+ "optional": true,
175
+ "requires": {
176
+ "tweetnacl": "0.14.5"
177
+ }
178
+ },
179
+ "beeper": {
180
+ "version": "1.1.1",
181
+ "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz",
182
+ "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=",
183
+ "dev": true
184
+ },
185
+ "block-stream": {
186
+ "version": "0.0.9",
187
+ "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
188
+ "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
189
+ "dev": true,
190
+ "requires": {
191
+ "inherits": "2.0.3"
192
+ }
193
+ },
194
+ "body-parser": {
195
+ "version": "1.14.2",
196
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.14.2.tgz",
197
+ "integrity": "sha1-EBXLH+LEQ4WCWVgdtTMy+NDPUPk=",
198
+ "dev": true,
199
+ "requires": {
200
+ "bytes": "2.2.0",
201
+ "content-type": "1.0.4",
202
+ "debug": "2.2.0",
203
+ "depd": "1.1.2",
204
+ "http-errors": "1.3.1",
205
+ "iconv-lite": "0.4.13",
206
+ "on-finished": "2.3.0",
207
+ "qs": "5.2.0",
208
+ "raw-body": "2.1.7",
209
+ "type-is": "1.6.16"
210
+ },
211
+ "dependencies": {
212
+ "iconv-lite": {
213
+ "version": "0.4.13",
214
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz",
215
+ "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=",
216
+ "dev": true
217
+ },
218
+ "qs": {
219
+ "version": "5.2.0",
220
+ "resolved": "https://registry.npmjs.org/qs/-/qs-5.2.0.tgz",
221
+ "integrity": "sha1-qfMRQq9GjLcrJbMBNrokVoNJFr4=",
222
+ "dev": true
223
+ }
224
+ }
225
+ },
226
+ "boom": {
227
+ "version": "2.10.1",
228
+ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
229
+ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
230
+ "dev": true,
231
+ "requires": {
232
+ "hoek": "2.16.3"
233
+ }
234
+ },
235
+ "brace-expansion": {
236
+ "version": "1.1.11",
237
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
238
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
239
+ "dev": true,
240
+ "requires": {
241
+ "balanced-match": "1.0.0",
242
+ "concat-map": "0.0.1"
243
+ }
244
+ },
245
+ "browserify-zlib": {
246
+ "version": "0.1.4",
247
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
248
+ "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
249
+ "dev": true,
250
+ "requires": {
251
+ "pako": "0.2.9"
252
+ }
253
+ },
254
+ "buffer-from": {
255
+ "version": "1.0.0",
256
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
257
+ "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==",
258
+ "dev": true
259
+ },
260
+ "builtin-modules": {
261
+ "version": "1.1.1",
262
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
263
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
264
+ "dev": true
265
+ },
266
+ "bytes": {
267
+ "version": "2.2.0",
268
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz",
269
+ "integrity": "sha1-/TVGSkA/b5EXwt42Cez/nK4ABYg=",
270
+ "dev": true
271
+ },
272
+ "cache-swap": {
273
+ "version": "0.3.0",
274
+ "resolved": "https://registry.npmjs.org/cache-swap/-/cache-swap-0.3.0.tgz",
275
+ "integrity": "sha1-HFQaoQilAQb2ML3Zj+HeyLoTP1E=",
276
+ "dev": true,
277
+ "requires": {
278
+ "graceful-fs": "4.1.11",
279
+ "mkdirp": "0.5.1",
280
+ "object-assign": "4.1.1",
281
+ "rimraf": "2.6.2"
282
+ },
283
+ "dependencies": {
284
+ "rimraf": {
285
+ "version": "2.6.2",
286
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
287
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
288
+ "dev": true,
289
+ "requires": {
290
+ "glob": "7.0.6"
291
+ }
292
+ }
293
+ }
294
+ },
295
+ "camelcase": {
296
+ "version": "2.1.1",
297
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
298
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
299
+ "dev": true
300
+ },
301
+ "camelcase-keys": {
302
+ "version": "2.1.0",
303
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
304
+ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
305
+ "dev": true,
306
+ "requires": {
307
+ "camelcase": "2.1.1",
308
+ "map-obj": "1.0.1"
309
+ }
310
+ },
311
+ "caseless": {
312
+ "version": "0.11.0",
313
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
314
+ "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=",
315
+ "dev": true
316
+ },
317
+ "chalk": {
318
+ "version": "1.1.3",
319
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
320
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
321
+ "dev": true,
322
+ "requires": {
323
+ "ansi-styles": "2.2.1",
324
+ "escape-string-regexp": "1.0.5",
325
+ "has-ansi": "2.0.0",
326
+ "strip-ansi": "3.0.1",
327
+ "supports-color": "2.0.0"
328
+ }
329
+ },
330
+ "cli": {
331
+ "version": "1.0.1",
332
+ "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
333
+ "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
334
+ "dev": true,
335
+ "requires": {
336
+ "exit": "0.1.2",
337
+ "glob": "7.1.2"
338
+ },
339
+ "dependencies": {
340
+ "glob": {
341
+ "version": "7.1.2",
342
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
343
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
344
+ "dev": true,
345
+ "requires": {
346
+ "fs.realpath": "1.0.0",
347
+ "inflight": "1.0.6",
348
+ "inherits": "2.0.3",
349
+ "minimatch": "3.0.4",
350
+ "once": "1.4.0",
351
+ "path-is-absolute": "1.0.1"
352
+ }
353
+ }
354
+ }
355
+ },
356
+ "cliui": {
357
+ "version": "3.2.0",
358
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
359
+ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
360
+ "dev": true,
361
+ "requires": {
362
+ "string-width": "1.0.2",
363
+ "strip-ansi": "3.0.1",
364
+ "wrap-ansi": "2.1.0"
365
+ }
366
+ },
367
+ "code-point-at": {
368
+ "version": "1.1.0",
369
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
370
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
371
+ "dev": true
372
+ },
373
+ "coffeescript": {
374
+ "version": "1.10.0",
375
+ "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz",
376
+ "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=",
377
+ "dev": true
378
+ },
379
+ "colors": {
380
+ "version": "1.1.2",
381
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
382
+ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
383
+ "dev": true
384
+ },
385
+ "combined-stream": {
386
+ "version": "1.0.6",
387
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
388
+ "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
389
+ "dev": true,
390
+ "requires": {
391
+ "delayed-stream": "1.0.0"
392
+ }
393
+ },
394
+ "commander": {
395
+ "version": "2.15.1",
396
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
397
+ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
398
+ "dev": true
399
+ },
400
+ "concat-map": {
401
+ "version": "0.0.1",
402
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
403
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
404
+ "dev": true
405
+ },
406
+ "concat-stream": {
407
+ "version": "1.6.2",
408
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
409
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
410
+ "dev": true,
411
+ "requires": {
412
+ "buffer-from": "1.0.0",
413
+ "inherits": "2.0.3",
414
+ "readable-stream": "2.3.6",
415
+ "typedarray": "0.0.6"
416
+ },
417
+ "dependencies": {
418
+ "isarray": {
419
+ "version": "1.0.0",
420
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
421
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
422
+ "dev": true
423
+ },
424
+ "readable-stream": {
425
+ "version": "2.3.6",
426
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
427
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
428
+ "dev": true,
429
+ "requires": {
430
+ "core-util-is": "1.0.2",
431
+ "inherits": "2.0.3",
432
+ "isarray": "1.0.0",
433
+ "process-nextick-args": "2.0.0",
434
+ "safe-buffer": "5.1.2",
435
+ "string_decoder": "1.1.1",
436
+ "util-deprecate": "1.0.2"
437
+ }
438
+ },
439
+ "string_decoder": {
440
+ "version": "1.1.1",
441
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
442
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
443
+ "dev": true,
444
+ "requires": {
445
+ "safe-buffer": "5.1.2"
446
+ }
447
+ }
448
+ }
449
+ },
450
+ "console-browserify": {
451
+ "version": "1.1.0",
452
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
453
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
454
+ "dev": true,
455
+ "requires": {
456
+ "date-now": "0.1.4"
457
+ }
458
+ },
459
+ "console-control-strings": {
460
+ "version": "1.1.0",
461
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
462
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
463
+ "dev": true
464
+ },
465
+ "content-type": {
466
+ "version": "1.0.4",
467
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
468
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
469
+ "dev": true
470
+ },
471
+ "core-util-is": {
472
+ "version": "1.0.2",
473
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
474
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
475
+ "dev": true
476
+ },
477
+ "cross-spawn": {
478
+ "version": "3.0.1",
479
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
480
+ "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
481
+ "dev": true,
482
+ "requires": {
483
+ "lru-cache": "4.1.2",
484
+ "which": "1.2.14"
485
+ },
486
+ "dependencies": {
487
+ "lru-cache": {
488
+ "version": "4.1.2",
489
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz",
490
+ "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==",
491
+ "dev": true,
492
+ "requires": {
493
+ "pseudomap": "1.0.2",
494
+ "yallist": "2.1.2"
495
+ }
496
+ }
497
+ }
498
+ },
499
+ "cryptiles": {
500
+ "version": "2.0.5",
501
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
502
+ "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
503
+ "dev": true,
504
+ "requires": {
505
+ "boom": "2.10.1"
506
+ }
507
+ },
508
+ "currently-unhandled": {
509
+ "version": "0.4.1",
510
+ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
511
+ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
512
+ "dev": true,
513
+ "requires": {
514
+ "array-find-index": "1.0.2"
515
+ }
516
+ },
517
+ "dashdash": {
518
+ "version": "1.14.1",
519
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
520
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
521
+ "dev": true,
522
+ "requires": {
523
+ "assert-plus": "1.0.0"
524
+ },
525
+ "dependencies": {
526
+ "assert-plus": {
527
+ "version": "1.0.0",
528
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
529
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
530
+ "dev": true
531
+ }
532
+ }
533
+ },
534
+ "date-now": {
535
+ "version": "0.1.4",
536
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
537
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
538
+ "dev": true
539
+ },
540
+ "date-time": {
541
+ "version": "1.1.0",
542
+ "resolved": "https://registry.npmjs.org/date-time/-/date-time-1.1.0.tgz",
543
+ "integrity": "sha1-GIdtC9pMGf5w3Tv0sDTygbEqQLY=",
544
+ "dev": true,
545
+ "requires": {
546
+ "time-zone": "0.1.0"
547
+ }
548
+ },
549
+ "dateformat": {
550
+ "version": "1.0.12",
551
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
552
+ "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
553
+ "dev": true,
554
+ "requires": {
555
+ "get-stdin": "4.0.1",
556
+ "meow": "3.7.0"
557
+ }
558
+ },
559
+ "debug": {
560
+ "version": "2.2.0",
561
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
562
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
563
+ "dev": true,
564
+ "requires": {
565
+ "ms": "0.7.1"
566
+ }
567
+ },
568
+ "decamelize": {
569
+ "version": "1.2.0",
570
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
571
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
572
+ "dev": true
573
+ },
574
+ "delayed-stream": {
575
+ "version": "1.0.0",
576
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
577
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
578
+ "dev": true
579
+ },
580
+ "delegates": {
581
+ "version": "1.0.0",
582
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
583
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
584
+ "dev": true
585
+ },
586
+ "depd": {
587
+ "version": "1.1.2",
588
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
589
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
590
+ "dev": true
591
+ },
592
+ "dom-serializer": {
593
+ "version": "0.1.0",
594
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
595
+ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
596
+ "dev": true,
597
+ "requires": {
598
+ "domelementtype": "1.1.3",
599
+ "entities": "1.1.1"
600
+ },
601
+ "dependencies": {
602
+ "domelementtype": {
603
+ "version": "1.1.3",
604
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
605
+ "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=",
606
+ "dev": true
607
+ },
608
+ "entities": {
609
+ "version": "1.1.1",
610
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
611
+ "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=",
612
+ "dev": true
613
+ }
614
+ }
615
+ },
616
+ "domelementtype": {
617
+ "version": "1.3.0",
618
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
619
+ "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=",
620
+ "dev": true
621
+ },
622
+ "domhandler": {
623
+ "version": "2.3.0",
624
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
625
+ "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
626
+ "dev": true,
627
+ "requires": {
628
+ "domelementtype": "1.3.0"
629
+ }
630
+ },
631
+ "domutils": {
632
+ "version": "1.5.1",
633
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
634
+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
635
+ "dev": true,
636
+ "requires": {
637
+ "dom-serializer": "0.1.0",
638
+ "domelementtype": "1.3.0"
639
+ }
640
+ },
641
+ "each-async": {
642
+ "version": "1.1.1",
643
+ "resolved": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz",
644
+ "integrity": "sha1-3uUim98KtrogEqOV4bhpq/iBNHM=",
645
+ "dev": true,
646
+ "requires": {
647
+ "onetime": "1.1.0",
648
+ "set-immediate-shim": "1.0.1"
649
+ }
650
+ },
651
+ "ecc-jsbn": {
652
+ "version": "0.1.1",
653
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
654
+ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
655
+ "dev": true,
656
+ "optional": true,
657
+ "requires": {
658
+ "jsbn": "0.1.1"
659
+ }
660
+ },
661
+ "ee-first": {
662
+ "version": "1.1.1",
663
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
664
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
665
+ "dev": true
666
+ },
667
+ "entities": {
668
+ "version": "1.0.0",
669
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
670
+ "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=",
671
+ "dev": true
672
+ },
673
+ "error-ex": {
674
+ "version": "1.3.1",
675
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
676
+ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
677
+ "dev": true,
678
+ "requires": {
679
+ "is-arrayish": "0.2.1"
680
+ }
681
+ },
682
+ "escape-string-regexp": {
683
+ "version": "1.0.5",
684
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
685
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
686
+ "dev": true
687
+ },
688
+ "esprima": {
689
+ "version": "2.7.3",
690
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
691
+ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
692
+ "dev": true
693
+ },
694
+ "eventemitter2": {
695
+ "version": "0.4.14",
696
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
697
+ "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
698
+ "dev": true
699
+ },
700
+ "exit": {
701
+ "version": "0.1.2",
702
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
703
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
704
+ "dev": true
705
+ },
706
+ "extend": {
707
+ "version": "3.0.1",
708
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
709
+ "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
710
+ "dev": true
711
+ },
712
+ "extsprintf": {
713
+ "version": "1.3.0",
714
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
715
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
716
+ "dev": true
717
+ },
718
+ "faye-websocket": {
719
+ "version": "0.10.0",
720
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
721
+ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
722
+ "dev": true,
723
+ "requires": {
724
+ "websocket-driver": "0.7.0"
725
+ }
726
+ },
727
+ "figures": {
728
+ "version": "1.7.0",
729
+ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
730
+ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
731
+ "dev": true,
732
+ "requires": {
733
+ "escape-string-regexp": "1.0.5",
734
+ "object-assign": "4.1.1"
735
+ }
736
+ },
737
+ "find-up": {
738
+ "version": "1.1.2",
739
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
740
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
741
+ "dev": true,
742
+ "requires": {
743
+ "path-exists": "2.1.0",
744
+ "pinkie-promise": "2.0.1"
745
+ }
746
+ },
747
+ "findup-sync": {
748
+ "version": "0.3.0",
749
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
750
+ "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
751
+ "dev": true,
752
+ "requires": {
753
+ "glob": "5.0.15"
754
+ },
755
+ "dependencies": {
756
+ "glob": {
757
+ "version": "5.0.15",
758
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
759
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
760
+ "dev": true,
761
+ "requires": {
762
+ "inflight": "1.0.6",
763
+ "inherits": "2.0.3",
764
+ "minimatch": "3.0.4",
765
+ "once": "1.4.0",
766
+ "path-is-absolute": "1.0.1"
767
+ }
768
+ }
769
+ }
770
+ },
771
+ "forever-agent": {
772
+ "version": "0.6.1",
773
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
774
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
775
+ "dev": true
776
+ },
777
+ "form-data": {
778
+ "version": "2.1.4",
779
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
780
+ "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
781
+ "dev": true,
782
+ "requires": {
783
+ "asynckit": "0.4.0",
784
+ "combined-stream": "1.0.6",
785
+ "mime-types": "2.1.18"
786
+ }
787
+ },
788
+ "fs.realpath": {
789
+ "version": "1.0.0",
790
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
791
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
792
+ "dev": true
793
+ },
794
+ "fstream": {
795
+ "version": "1.0.11",
796
+ "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
797
+ "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
798
+ "dev": true,
799
+ "requires": {
800
+ "graceful-fs": "4.1.11",
801
+ "inherits": "2.0.3",
802
+ "mkdirp": "0.5.1",
803
+ "rimraf": "2.2.8"
804
+ }
805
+ },
806
+ "gauge": {
807
+ "version": "2.7.4",
808
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
809
+ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
810
+ "dev": true,
811
+ "requires": {
812
+ "aproba": "1.2.0",
813
+ "console-control-strings": "1.1.0",
814
+ "has-unicode": "2.0.1",
815
+ "object-assign": "4.1.1",
816
+ "signal-exit": "3.0.2",
817
+ "string-width": "1.0.2",
818
+ "strip-ansi": "3.0.1",
819
+ "wide-align": "1.1.2"
820
+ }
821
+ },
822
+ "gaze": {
823
+ "version": "1.1.2",
824
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz",
825
+ "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=",
826
+ "dev": true,
827
+ "requires": {
828
+ "globule": "1.2.0"
829
+ }
830
+ },
831
+ "generate-function": {
832
+ "version": "2.0.0",
833
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
834
+ "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=",
835
+ "dev": true
836
+ },
837
+ "generate-object-property": {
838
+ "version": "1.2.0",
839
+ "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
840
+ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
841
+ "dev": true,
842
+ "requires": {
843
+ "is-property": "1.0.2"
844
+ }
845
+ },
846
+ "get-caller-file": {
847
+ "version": "1.0.2",
848
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
849
+ "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=",
850
+ "dev": true
851
+ },
852
+ "get-stdin": {
853
+ "version": "4.0.1",
854
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
855
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
856
+ "dev": true
857
+ },
858
+ "getobject": {
859
+ "version": "0.1.0",
860
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
861
+ "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=",
862
+ "dev": true
863
+ },
864
+ "getpass": {
865
+ "version": "0.1.7",
866
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
867
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
868
+ "dev": true,
869
+ "requires": {
870
+ "assert-plus": "1.0.0"
871
+ },
872
+ "dependencies": {
873
+ "assert-plus": {
874
+ "version": "1.0.0",
875
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
876
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
877
+ "dev": true
878
+ }
879
+ }
880
+ },
881
+ "glob": {
882
+ "version": "7.0.6",
883
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
884
+ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
885
+ "dev": true,
886
+ "requires": {
887
+ "fs.realpath": "1.0.0",
888
+ "inflight": "1.0.6",
889
+ "inherits": "2.0.3",
890
+ "minimatch": "3.0.4",
891
+ "once": "1.4.0",
892
+ "path-is-absolute": "1.0.1"
893
+ }
894
+ },
895
+ "globule": {
896
+ "version": "1.2.0",
897
+ "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz",
898
+ "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=",
899
+ "dev": true,
900
+ "requires": {
901
+ "glob": "7.1.2",
902
+ "lodash": "4.17.10",
903
+ "minimatch": "3.0.4"
904
+ },
905
+ "dependencies": {
906
+ "glob": {
907
+ "version": "7.1.2",
908
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
909
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
910
+ "dev": true,
911
+ "requires": {
912
+ "fs.realpath": "1.0.0",
913
+ "inflight": "1.0.6",
914
+ "inherits": "2.0.3",
915
+ "minimatch": "3.0.4",
916
+ "once": "1.4.0",
917
+ "path-is-absolute": "1.0.1"
918
+ }
919
+ }
920
+ }
921
+ },
922
+ "graceful-fs": {
923
+ "version": "4.1.11",
924
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
925
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
926
+ "dev": true
927
+ },
928
+ "grunt": {
929
+ "version": "1.0.2",
930
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.2.tgz",
931
+ "integrity": "sha1-TmpeaVtwRy/VME9fqeNCNoNqc7w=",
932
+ "dev": true,
933
+ "requires": {
934
+ "coffeescript": "1.10.0",
935
+ "dateformat": "1.0.12",
936
+ "eventemitter2": "0.4.14",
937
+ "exit": "0.1.2",
938
+ "findup-sync": "0.3.0",
939
+ "glob": "7.0.6",
940
+ "grunt-cli": "1.2.0",
941
+ "grunt-known-options": "1.1.0",
942
+ "grunt-legacy-log": "1.0.2",
943
+ "grunt-legacy-util": "1.0.0",
944
+ "iconv-lite": "0.4.21",
945
+ "js-yaml": "3.5.5",
946
+ "minimatch": "3.0.4",
947
+ "nopt": "3.0.6",
948
+ "path-is-absolute": "1.0.1",
949
+ "rimraf": "2.2.8"
950
+ },
951
+ "dependencies": {
952
+ "grunt-cli": {
953
+ "version": "1.2.0",
954
+ "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
955
+ "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
956
+ "dev": true,
957
+ "requires": {
958
+ "findup-sync": "0.3.0",
959
+ "grunt-known-options": "1.1.0",
960
+ "nopt": "3.0.6",
961
+ "resolve": "1.1.7"
962
+ }
963
+ }
964
+ }
965
+ },
966
+ "grunt-contrib-jshint": {
967
+ "version": "1.1.0",
968
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz",
969
+ "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=",
970
+ "dev": true,
971
+ "requires": {
972
+ "chalk": "1.1.3",
973
+ "hooker": "0.2.3",
974
+ "jshint": "2.9.5"
975
+ }
976
+ },
977
+ "grunt-contrib-uglify": {
978
+ "version": "3.3.0",
979
+ "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-3.3.0.tgz",
980
+ "integrity": "sha512-W9O7lJE3PlD8VCc5fyaf98QV7f5wEDiU4PBIh0+/6UBbk2LhgzEFS0/p+taH5UD3+PlEn7QPN0o06Z0To6SqXw==",
981
+ "dev": true,
982
+ "requires": {
983
+ "chalk": "1.1.3",
984
+ "maxmin": "1.1.0",
985
+ "uglify-js": "3.3.22",
986
+ "uri-path": "1.0.0"
987
+ }
988
+ },
989
+ "grunt-contrib-watch": {
990
+ "version": "1.0.1",
991
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.0.1.tgz",
992
+ "integrity": "sha512-8Zka/svGl6+ZwF7d6z/CfXwsb4cDODnajmZsY4nUAs9Ob0kJEcsLiDf5qm2HdDoEcm3NHjWCrFiWx+PZ2y4D7A==",
993
+ "dev": true,
994
+ "requires": {
995
+ "async": "1.5.2",
996
+ "gaze": "1.1.2",
997
+ "lodash": "4.17.10",
998
+ "tiny-lr": "0.2.1"
999
+ }
1000
+ },
1001
+ "grunt-known-options": {
1002
+ "version": "1.1.0",
1003
+ "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.0.tgz",
1004
+ "integrity": "sha1-pCdO6zL6dl2lp6OxcSYXzjsUQUk=",
1005
+ "dev": true
1006
+ },
1007
+ "grunt-legacy-log": {
1008
+ "version": "1.0.2",
1009
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-1.0.2.tgz",
1010
+ "integrity": "sha512-WdedTJ/6zCXnI/coaouzqvkI19uwqbcPkdsXiDRKJyB5rOUlOxnCnTVbpeUdEckKVir2uHF3rDBYppj2p6N3+g==",
1011
+ "dev": true,
1012
+ "requires": {
1013
+ "colors": "1.1.2",
1014
+ "grunt-legacy-log-utils": "1.0.0",
1015
+ "hooker": "0.2.3",
1016
+ "lodash": "4.17.10"
1017
+ }
1018
+ },
1019
+ "grunt-legacy-log-utils": {
1020
+ "version": "1.0.0",
1021
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-1.0.0.tgz",
1022
+ "integrity": "sha1-p7ji0Ps1taUPSvmG/BEnSevJbz0=",
1023
+ "dev": true,
1024
+ "requires": {
1025
+ "chalk": "1.1.3",
1026
+ "lodash": "4.3.0"
1027
+ },
1028
+ "dependencies": {
1029
+ "lodash": {
1030
+ "version": "4.3.0",
1031
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz",
1032
+ "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=",
1033
+ "dev": true
1034
+ }
1035
+ }
1036
+ },
1037
+ "grunt-legacy-util": {
1038
+ "version": "1.0.0",
1039
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.0.0.tgz",
1040
+ "integrity": "sha1-OGqnjcbtUJhsKxiVcmWxtIq7m4Y=",
1041
+ "dev": true,
1042
+ "requires": {
1043
+ "async": "1.5.2",
1044
+ "exit": "0.1.2",
1045
+ "getobject": "0.1.0",
1046
+ "hooker": "0.2.3",
1047
+ "lodash": "4.3.0",
1048
+ "underscore.string": "3.2.3",
1049
+ "which": "1.2.14"
1050
+ },
1051
+ "dependencies": {
1052
+ "lodash": {
1053
+ "version": "4.3.0",
1054
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz",
1055
+ "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=",
1056
+ "dev": true
1057
+ }
1058
+ }
1059
+ },
1060
+ "grunt-phplint": {
1061
+ "version": "0.1.0",
1062
+ "resolved": "https://registry.npmjs.org/grunt-phplint/-/grunt-phplint-0.1.0.tgz",
1063
+ "integrity": "sha1-bb4uauxTqiKc+sCtmnyZ4kGEhI0=",
1064
+ "dev": true,
1065
+ "requires": {
1066
+ "cache-swap": "0.3.0",
1067
+ "grunt": "0.4.5"
1068
+ },
1069
+ "dependencies": {
1070
+ "argparse": {
1071
+ "version": "0.1.16",
1072
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
1073
+ "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=",
1074
+ "dev": true,
1075
+ "requires": {
1076
+ "underscore": "1.7.0",
1077
+ "underscore.string": "2.4.0"
1078
+ },
1079
+ "dependencies": {
1080
+ "underscore.string": {
1081
+ "version": "2.4.0",
1082
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz",
1083
+ "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=",
1084
+ "dev": true
1085
+ }
1086
+ }
1087
+ },
1088
+ "async": {
1089
+ "version": "0.1.22",
1090
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz",
1091
+ "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=",
1092
+ "dev": true
1093
+ },
1094
+ "coffee-script": {
1095
+ "version": "1.3.3",
1096
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz",
1097
+ "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=",
1098
+ "dev": true
1099
+ },
1100
+ "colors": {
1101
+ "version": "0.6.2",
1102
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
1103
+ "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
1104
+ "dev": true
1105
+ },
1106
+ "dateformat": {
1107
+ "version": "1.0.2-1.2.3",
1108
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz",
1109
+ "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=",
1110
+ "dev": true
1111
+ },
1112
+ "esprima": {
1113
+ "version": "1.0.4",
1114
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz",
1115
+ "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=",
1116
+ "dev": true
1117
+ },
1118
+ "findup-sync": {
1119
+ "version": "0.1.3",
1120
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
1121
+ "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=",
1122
+ "dev": true,
1123
+ "requires": {
1124
+ "glob": "3.2.11",
1125
+ "lodash": "2.4.2"
1126
+ },
1127
+ "dependencies": {
1128
+ "glob": {
1129
+ "version": "3.2.11",
1130
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
1131
+ "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=",
1132
+ "dev": true,
1133
+ "requires": {
1134
+ "inherits": "2.0.3",
1135
+ "minimatch": "0.3.0"
1136
+ }
1137
+ },
1138
+ "lodash": {
1139
+ "version": "2.4.2",
1140
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
1141
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
1142
+ "dev": true
1143
+ },
1144
+ "minimatch": {
1145
+ "version": "0.3.0",
1146
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
1147
+ "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=",
1148
+ "dev": true,
1149
+ "requires": {
1150
+ "lru-cache": "2.7.3",
1151
+ "sigmund": "1.0.1"
1152
+ }
1153
+ }
1154
+ }
1155
+ },
1156
+ "glob": {
1157
+ "version": "3.1.21",
1158
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
1159
+ "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=",
1160
+ "dev": true,
1161
+ "requires": {
1162
+ "graceful-fs": "1.2.3",
1163
+ "inherits": "1.0.2",
1164
+ "minimatch": "0.2.14"
1165
+ },
1166
+ "dependencies": {
1167
+ "inherits": {
1168
+ "version": "1.0.2",
1169
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz",
1170
+ "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=",
1171
+ "dev": true
1172
+ }
1173
+ }
1174
+ },
1175
+ "graceful-fs": {
1176
+ "version": "1.2.3",
1177
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz",
1178
+ "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=",
1179
+ "dev": true
1180
+ },
1181
+ "grunt": {
1182
+ "version": "0.4.5",
1183
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
1184
+ "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=",
1185
+ "dev": true,
1186
+ "requires": {
1187
+ "async": "0.1.22",
1188
+ "coffee-script": "1.3.3",
1189
+ "colors": "0.6.2",
1190
+ "dateformat": "1.0.2-1.2.3",
1191
+ "eventemitter2": "0.4.14",
1192
+ "exit": "0.1.2",
1193
+ "findup-sync": "0.1.3",
1194
+ "getobject": "0.1.0",
1195
+ "glob": "3.1.21",
1196
+ "grunt-legacy-log": "0.1.3",
1197
+ "grunt-legacy-util": "0.2.0",
1198
+ "hooker": "0.2.3",
1199
+ "iconv-lite": "0.2.11",
1200
+ "js-yaml": "2.0.5",
1201
+ "lodash": "0.9.2",
1202
+ "minimatch": "0.2.14",
1203
+ "nopt": "1.0.10",
1204
+ "rimraf": "2.2.8",
1205
+ "underscore.string": "2.2.1",
1206
+ "which": "1.0.9"
1207
+ }
1208
+ },
1209
+ "grunt-legacy-log": {
1210
+ "version": "0.1.3",
1211
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz",
1212
+ "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=",
1213
+ "dev": true,
1214
+ "requires": {
1215
+ "colors": "0.6.2",
1216
+ "grunt-legacy-log-utils": "0.1.1",
1217
+ "hooker": "0.2.3",
1218
+ "lodash": "2.4.2",
1219
+ "underscore.string": "2.3.3"
1220
+ },
1221
+ "dependencies": {
1222
+ "lodash": {
1223
+ "version": "2.4.2",
1224
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
1225
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
1226
+ "dev": true
1227
+ },
1228
+ "underscore.string": {
1229
+ "version": "2.3.3",
1230
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz",
1231
+ "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=",
1232
+ "dev": true
1233
+ }
1234
+ }
1235
+ },
1236
+ "grunt-legacy-log-utils": {
1237
+ "version": "0.1.1",
1238
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz",
1239
+ "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=",
1240
+ "dev": true,
1241
+ "requires": {
1242
+ "colors": "0.6.2",
1243
+ "lodash": "2.4.2",
1244
+ "underscore.string": "2.3.3"
1245
+ },
1246
+ "dependencies": {
1247
+ "lodash": {
1248
+ "version": "2.4.2",
1249
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
1250
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
1251
+ "dev": true
1252
+ },
1253
+ "underscore.string": {
1254
+ "version": "2.3.3",
1255
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz",
1256
+ "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=",
1257
+ "dev": true
1258
+ }
1259
+ }
1260
+ },
1261
+ "grunt-legacy-util": {
1262
+ "version": "0.2.0",
1263
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz",
1264
+ "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=",
1265
+ "dev": true,
1266
+ "requires": {
1267
+ "async": "0.1.22",
1268
+ "exit": "0.1.2",
1269
+ "getobject": "0.1.0",
1270
+ "hooker": "0.2.3",
1271
+ "lodash": "0.9.2",
1272
+ "underscore.string": "2.2.1",
1273
+ "which": "1.0.9"
1274
+ }
1275
+ },
1276
+ "iconv-lite": {
1277
+ "version": "0.2.11",
1278
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz",
1279
+ "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=",
1280
+ "dev": true
1281
+ },
1282
+ "js-yaml": {
1283
+ "version": "2.0.5",
1284
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
1285
+ "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=",
1286
+ "dev": true,
1287
+ "requires": {
1288
+ "argparse": "0.1.16",
1289
+ "esprima": "1.0.4"
1290
+ }
1291
+ },
1292
+ "lodash": {
1293
+ "version": "0.9.2",
1294
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz",
1295
+ "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=",
1296
+ "dev": true
1297
+ },
1298
+ "minimatch": {
1299
+ "version": "0.2.14",
1300
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
1301
+ "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=",
1302
+ "dev": true,
1303
+ "requires": {
1304
+ "lru-cache": "2.7.3",
1305
+ "sigmund": "1.0.1"
1306
+ }
1307
+ },
1308
+ "nopt": {
1309
+ "version": "1.0.10",
1310
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
1311
+ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
1312
+ "dev": true,
1313
+ "requires": {
1314
+ "abbrev": "1.1.1"
1315
+ }
1316
+ },
1317
+ "underscore.string": {
1318
+ "version": "2.2.1",
1319
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz",
1320
+ "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=",
1321
+ "dev": true
1322
+ },
1323
+ "which": {
1324
+ "version": "1.0.9",
1325
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz",
1326
+ "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=",
1327
+ "dev": true
1328
+ }
1329
+ }
1330
+ },
1331
+ "grunt-sass": {
1332
+ "version": "2.1.0",
1333
+ "resolved": "https://registry.npmjs.org/grunt-sass/-/grunt-sass-2.1.0.tgz",
1334
+ "integrity": "sha512-XkexnQt/9rhReNd+Y7T0n/2g5FqYOQKfi2iSlpwDqvgs7EgEaGTxNhnWzHnbW5oNRvzL9AHopBG3AgRxL0d+DA==",
1335
+ "dev": true,
1336
+ "requires": {
1337
+ "each-async": "1.1.1",
1338
+ "node-sass": "4.9.0",
1339
+ "object-assign": "4.1.1"
1340
+ }
1341
+ },
1342
+ "gzip-size": {
1343
+ "version": "1.0.0",
1344
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz",
1345
+ "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=",
1346
+ "dev": true,
1347
+ "requires": {
1348
+ "browserify-zlib": "0.1.4",
1349
+ "concat-stream": "1.6.2"
1350
+ }
1351
+ },
1352
+ "har-validator": {
1353
+ "version": "2.0.6",
1354
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
1355
+ "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
1356
+ "dev": true,
1357
+ "requires": {
1358
+ "chalk": "1.1.3",
1359
+ "commander": "2.15.1",
1360
+ "is-my-json-valid": "2.17.2",
1361
+ "pinkie-promise": "2.0.1"
1362
+ }
1363
+ },
1364
+ "has-ansi": {
1365
+ "version": "2.0.0",
1366
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
1367
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
1368
+ "dev": true,
1369
+ "requires": {
1370
+ "ansi-regex": "2.1.1"
1371
+ }
1372
+ },
1373
+ "has-unicode": {
1374
+ "version": "2.0.1",
1375
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
1376
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
1377
+ "dev": true
1378
+ },
1379
+ "hawk": {
1380
+ "version": "3.1.3",
1381
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
1382
+ "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
1383
+ "dev": true,
1384
+ "requires": {
1385
+ "boom": "2.10.1",
1386
+ "cryptiles": "2.0.5",
1387
+ "hoek": "2.16.3",
1388
+ "sntp": "1.0.9"
1389
+ }
1390
+ },
1391
+ "hoek": {
1392
+ "version": "2.16.3",
1393
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
1394
+ "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
1395
+ "dev": true
1396
+ },
1397
+ "hooker": {
1398
+ "version": "0.2.3",
1399
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
1400
+ "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
1401
+ "dev": true
1402
+ },
1403
+ "hosted-git-info": {
1404
+ "version": "2.6.0",
1405
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz",
1406
+ "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==",
1407
+ "dev": true
1408
+ },
1409
+ "htmlparser2": {
1410
+ "version": "3.8.3",
1411
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
1412
+ "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
1413
+ "dev": true,
1414
+ "requires": {
1415
+ "domelementtype": "1.3.0",
1416
+ "domhandler": "2.3.0",
1417
+ "domutils": "1.5.1",
1418
+ "entities": "1.0.0",
1419
+ "readable-stream": "1.1.14"
1420
+ }
1421
+ },
1422
+ "http-errors": {
1423
+ "version": "1.3.1",
1424
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz",
1425
+ "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=",
1426
+ "dev": true,
1427
+ "requires": {
1428
+ "inherits": "2.0.3",
1429
+ "statuses": "1.5.0"
1430
+ }
1431
+ },
1432
+ "http-parser-js": {
1433
+ "version": "0.4.12",
1434
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.12.tgz",
1435
+ "integrity": "sha1-uc+/Sizybw/DSxDKFImid3HjR08=",
1436
+ "dev": true
1437
+ },
1438
+ "http-signature": {
1439
+ "version": "1.1.1",
1440
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
1441
+ "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
1442
+ "dev": true,
1443
+ "requires": {
1444
+ "assert-plus": "0.2.0",
1445
+ "jsprim": "1.4.1",
1446
+ "sshpk": "1.14.1"
1447
+ }
1448
+ },
1449
+ "iconv-lite": {
1450
+ "version": "0.4.21",
1451
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz",
1452
+ "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
1453
+ "dev": true,
1454
+ "requires": {
1455
+ "safer-buffer": "2.1.2"
1456
+ }
1457
+ },
1458
+ "in-publish": {
1459
+ "version": "2.0.0",
1460
+ "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
1461
+ "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=",
1462
+ "dev": true
1463
+ },
1464
+ "indent-string": {
1465
+ "version": "2.1.0",
1466
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
1467
+ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
1468
+ "dev": true,
1469
+ "requires": {
1470
+ "repeating": "2.0.1"
1471
+ }
1472
+ },
1473
+ "inflight": {
1474
+ "version": "1.0.6",
1475
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
1476
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
1477
+ "dev": true,
1478
+ "requires": {
1479
+ "once": "1.4.0",
1480
+ "wrappy": "1.0.2"
1481
+ }
1482
+ },
1483
+ "inherits": {
1484
+ "version": "2.0.3",
1485
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
1486
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
1487
+ "dev": true
1488
+ },
1489
+ "invert-kv": {
1490
+ "version": "1.0.0",
1491
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
1492
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
1493
+ "dev": true
1494
+ },
1495
+ "irregular-plurals": {
1496
+ "version": "1.4.0",
1497
+ "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.4.0.tgz",
1498
+ "integrity": "sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y=",
1499
+ "dev": true
1500
+ },
1501
+ "is-arrayish": {
1502
+ "version": "0.2.1",
1503
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
1504
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
1505
+ "dev": true
1506
+ },
1507
+ "is-builtin-module": {
1508
+ "version": "1.0.0",
1509
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
1510
+ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
1511
+ "dev": true,
1512
+ "requires": {
1513
+ "builtin-modules": "1.1.1"
1514
+ }
1515
+ },
1516
+ "is-finite": {
1517
+ "version": "1.0.2",
1518
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
1519
+ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
1520
+ "dev": true,
1521
+ "requires": {
1522
+ "number-is-nan": "1.0.1"
1523
+ }
1524
+ },
1525
+ "is-fullwidth-code-point": {
1526
+ "version": "1.0.0",
1527
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
1528
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
1529
+ "dev": true,
1530
+ "requires": {
1531
+ "number-is-nan": "1.0.1"
1532
+ }
1533
+ },
1534
+ "is-my-ip-valid": {
1535
+ "version": "1.0.0",
1536
+ "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz",
1537
+ "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==",
1538
+ "dev": true
1539
+ },
1540
+ "is-my-json-valid": {
1541
+ "version": "2.17.2",
1542
+ "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz",
1543
+ "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==",
1544
+ "dev": true,
1545
+ "requires": {
1546
+ "generate-function": "2.0.0",
1547
+ "generate-object-property": "1.2.0",
1548
+ "is-my-ip-valid": "1.0.0",
1549
+ "jsonpointer": "4.0.1",
1550
+ "xtend": "4.0.1"
1551
+ }
1552
+ },
1553
+ "is-property": {
1554
+ "version": "1.0.2",
1555
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
1556
+ "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
1557
+ "dev": true
1558
+ },
1559
+ "is-typedarray": {
1560
+ "version": "1.0.0",
1561
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
1562
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
1563
+ "dev": true
1564
+ },
1565
+ "is-utf8": {
1566
+ "version": "0.2.1",
1567
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
1568
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
1569
+ "dev": true
1570
+ },
1571
+ "isarray": {
1572
+ "version": "0.0.1",
1573
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
1574
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
1575
+ "dev": true
1576
+ },
1577
+ "isexe": {
1578
+ "version": "2.0.0",
1579
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
1580
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
1581
+ "dev": true
1582
+ },
1583
+ "isstream": {
1584
+ "version": "0.1.2",
1585
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
1586
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
1587
+ "dev": true
1588
+ },
1589
+ "js-base64": {
1590
+ "version": "2.4.3",
1591
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz",
1592
+ "integrity": "sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==",
1593
+ "dev": true
1594
+ },
1595
+ "js-yaml": {
1596
+ "version": "3.5.5",
1597
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz",
1598
+ "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=",
1599
+ "dev": true,
1600
+ "requires": {
1601
+ "argparse": "1.0.10",
1602
+ "esprima": "2.7.3"
1603
+ }
1604
+ },
1605
+ "jsbn": {
1606
+ "version": "0.1.1",
1607
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
1608
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
1609
+ "dev": true,
1610
+ "optional": true
1611
+ },
1612
+ "jshint": {
1613
+ "version": "2.9.5",
1614
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.5.tgz",
1615
+ "integrity": "sha1-HnJSkVzmgbQIJ+4UJIxG006apiw=",
1616
+ "dev": true,
1617
+ "requires": {
1618
+ "cli": "1.0.1",
1619
+ "console-browserify": "1.1.0",
1620
+ "exit": "0.1.2",
1621
+ "htmlparser2": "3.8.3",
1622
+ "lodash": "3.7.0",
1623
+ "minimatch": "3.0.4",
1624
+ "shelljs": "0.3.0",
1625
+ "strip-json-comments": "1.0.4"
1626
+ },
1627
+ "dependencies": {
1628
+ "lodash": {
1629
+ "version": "3.7.0",
1630
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz",
1631
+ "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU=",
1632
+ "dev": true
1633
+ }
1634
+ }
1635
+ },
1636
+ "jshint-stylish": {
1637
+ "version": "2.2.1",
1638
+ "resolved": "https://registry.npmjs.org/jshint-stylish/-/jshint-stylish-2.2.1.tgz",
1639
+ "integrity": "sha1-JCCCosA1rgP9gQROBXDMQgjPbmE=",
1640
+ "dev": true,
1641
+ "requires": {
1642
+ "beeper": "1.1.1",
1643
+ "chalk": "1.1.3",
1644
+ "log-symbols": "1.0.2",
1645
+ "plur": "2.1.2",
1646
+ "string-length": "1.0.1",
1647
+ "text-table": "0.2.0"
1648
+ }
1649
+ },
1650
+ "json-schema": {
1651
+ "version": "0.2.3",
1652
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
1653
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
1654
+ "dev": true
1655
+ },
1656
+ "json-stringify-safe": {
1657
+ "version": "5.0.1",
1658
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
1659
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
1660
+ "dev": true
1661
+ },
1662
+ "jsonpointer": {
1663
+ "version": "4.0.1",
1664
+ "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
1665
+ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
1666
+ "dev": true
1667
+ },
1668
+ "jsprim": {
1669
+ "version": "1.4.1",
1670
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
1671
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
1672
+ "dev": true,
1673
+ "requires": {
1674
+ "assert-plus": "1.0.0",
1675
+ "extsprintf": "1.3.0",
1676
+ "json-schema": "0.2.3",
1677
+ "verror": "1.10.0"
1678
+ },
1679
+ "dependencies": {
1680
+ "assert-plus": {
1681
+ "version": "1.0.0",
1682
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
1683
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
1684
+ "dev": true
1685
+ }
1686
+ }
1687
+ },
1688
+ "lcid": {
1689
+ "version": "1.0.0",
1690
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
1691
+ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
1692
+ "dev": true,
1693
+ "requires": {
1694
+ "invert-kv": "1.0.0"
1695
+ }
1696
+ },
1697
+ "livereload-js": {
1698
+ "version": "2.3.0",
1699
+ "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.3.0.tgz",
1700
+ "integrity": "sha512-j1R0/FeGa64Y+NmqfZhyoVRzcFlOZ8sNlKzHjh4VvLULFACZhn68XrX5DFg2FhMvSMJmROuFxRSa560ECWKBMg==",
1701
+ "dev": true
1702
+ },
1703
+ "load-grunt-tasks": {
1704
+ "version": "3.5.2",
1705
+ "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.5.2.tgz",
1706
+ "integrity": "sha1-ByhWEYD9IP+KaSdQWFL8WKrqDIg=",
1707
+ "dev": true,
1708
+ "requires": {
1709
+ "arrify": "1.0.1",
1710
+ "multimatch": "2.1.0",
1711
+ "pkg-up": "1.0.0",
1712
+ "resolve-pkg": "0.1.0"
1713
+ }
1714
+ },
1715
+ "load-json-file": {
1716
+ "version": "1.1.0",
1717
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
1718
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
1719
+ "dev": true,
1720
+ "requires": {
1721
+ "graceful-fs": "4.1.11",
1722
+ "parse-json": "2.2.0",
1723
+ "pify": "2.3.0",
1724
+ "pinkie-promise": "2.0.1",
1725
+ "strip-bom": "2.0.0"
1726
+ }
1727
+ },
1728
+ "lodash": {
1729
+ "version": "4.17.10",
1730
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
1731
+ "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
1732
+ "dev": true
1733
+ },
1734
+ "lodash.assign": {
1735
+ "version": "4.2.0",
1736
+ "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
1737
+ "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
1738
+ "dev": true
1739
+ },
1740
+ "lodash.clonedeep": {
1741
+ "version": "4.5.0",
1742
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
1743
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
1744
+ "dev": true
1745
+ },
1746
+ "lodash.mergewith": {
1747
+ "version": "4.6.1",
1748
+ "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
1749
+ "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==",
1750
+ "dev": true
1751
+ },
1752
+ "log-symbols": {
1753
+ "version": "1.0.2",
1754
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
1755
+ "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
1756
+ "dev": true,
1757
+ "requires": {
1758
+ "chalk": "1.1.3"
1759
+ }
1760
+ },
1761
+ "loud-rejection": {
1762
+ "version": "1.6.0",
1763
+ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
1764
+ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
1765
+ "dev": true,
1766
+ "requires": {
1767
+ "currently-unhandled": "0.4.1",
1768
+ "signal-exit": "3.0.2"
1769
+ }
1770
+ },
1771
+ "lru-cache": {
1772
+ "version": "2.7.3",
1773
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
1774
+ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=",
1775
+ "dev": true
1776
+ },
1777
+ "map-obj": {
1778
+ "version": "1.0.1",
1779
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
1780
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
1781
+ "dev": true
1782
+ },
1783
+ "maxmin": {
1784
+ "version": "1.1.0",
1785
+ "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz",
1786
+ "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=",
1787
+ "dev": true,
1788
+ "requires": {
1789
+ "chalk": "1.1.3",
1790
+ "figures": "1.7.0",
1791
+ "gzip-size": "1.0.0",
1792
+ "pretty-bytes": "1.0.4"
1793
+ }
1794
+ },
1795
+ "media-typer": {
1796
+ "version": "0.3.0",
1797
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1798
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
1799
+ "dev": true
1800
+ },
1801
+ "meow": {
1802
+ "version": "3.7.0",
1803
+ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
1804
+ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
1805
+ "dev": true,
1806
+ "requires": {
1807
+ "camelcase-keys": "2.1.0",
1808
+ "decamelize": "1.2.0",
1809
+ "loud-rejection": "1.6.0",
1810
+ "map-obj": "1.0.1",
1811
+ "minimist": "1.2.0",
1812
+ "normalize-package-data": "2.4.0",
1813
+ "object-assign": "4.1.1",
1814
+ "read-pkg-up": "1.0.1",
1815
+ "redent": "1.0.0",
1816
+ "trim-newlines": "1.0.0"
1817
+ }
1818
+ },
1819
+ "mime-db": {
1820
+ "version": "1.33.0",
1821
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
1822
+ "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
1823
+ "dev": true
1824
+ },
1825
+ "mime-types": {
1826
+ "version": "2.1.18",
1827
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
1828
+ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
1829
+ "dev": true,
1830
+ "requires": {
1831
+ "mime-db": "1.33.0"
1832
+ }
1833
+ },
1834
+ "minimatch": {
1835
+ "version": "3.0.4",
1836
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
1837
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
1838
+ "dev": true,
1839
+ "requires": {
1840
+ "brace-expansion": "1.1.11"
1841
+ }
1842
+ },
1843
+ "minimist": {
1844
+ "version": "1.2.0",
1845
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
1846
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
1847
+ "dev": true
1848
+ },
1849
+ "mkdirp": {
1850
+ "version": "0.5.1",
1851
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
1852
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
1853
+ "dev": true,
1854
+ "requires": {
1855
+ "minimist": "0.0.8"
1856
+ },
1857
+ "dependencies": {
1858
+ "minimist": {
1859
+ "version": "0.0.8",
1860
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
1861
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
1862
+ "dev": true
1863
+ }
1864
+ }
1865
+ },
1866
+ "ms": {
1867
+ "version": "0.7.1",
1868
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
1869
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=",
1870
+ "dev": true
1871
+ },
1872
+ "multimatch": {
1873
+ "version": "2.1.0",
1874
+ "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz",
1875
+ "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=",
1876
+ "dev": true,
1877
+ "requires": {
1878
+ "array-differ": "1.0.0",
1879
+ "array-union": "1.0.2",
1880
+ "arrify": "1.0.1",
1881
+ "minimatch": "3.0.4"
1882
+ }
1883
+ },
1884
+ "nan": {
1885
+ "version": "2.10.0",
1886
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
1887
+ "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
1888
+ "dev": true
1889
+ },
1890
+ "node-gyp": {
1891
+ "version": "3.6.2",
1892
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz",
1893
+ "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=",
1894
+ "dev": true,
1895
+ "requires": {
1896
+ "fstream": "1.0.11",
1897
+ "glob": "7.0.6",
1898
+ "graceful-fs": "4.1.11",
1899
+ "minimatch": "3.0.4",
1900
+ "mkdirp": "0.5.1",
1901
+ "nopt": "3.0.6",
1902
+ "npmlog": "4.1.2",
1903
+ "osenv": "0.1.5",
1904
+ "request": "2.79.0",
1905
+ "rimraf": "2.2.8",
1906
+ "semver": "5.3.0",
1907
+ "tar": "2.2.1",
1908
+ "which": "1.2.14"
1909
+ },
1910
+ "dependencies": {
1911
+ "semver": {
1912
+ "version": "5.3.0",
1913
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
1914
+ "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
1915
+ "dev": true
1916
+ }
1917
+ }
1918
+ },
1919
+ "node-sass": {
1920
+ "version": "4.9.0",
1921
+ "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.0.tgz",
1922
+ "integrity": "sha512-QFHfrZl6lqRU3csypwviz2XLgGNOoWQbo2GOvtsfQqOfL4cy1BtWnhx/XUeAO9LT3ahBzSRXcEO6DdvAH9DzSg==",
1923
+ "dev": true,
1924
+ "requires": {
1925
+ "async-foreach": "0.1.3",
1926
+ "chalk": "1.1.3",
1927
+ "cross-spawn": "3.0.1",
1928
+ "gaze": "1.1.2",
1929
+ "get-stdin": "4.0.1",
1930
+ "glob": "7.0.6",
1931
+ "in-publish": "2.0.0",
1932
+ "lodash.assign": "4.2.0",
1933
+ "lodash.clonedeep": "4.5.0",
1934
+ "lodash.mergewith": "4.6.1",
1935
+ "meow": "3.7.0",
1936
+ "mkdirp": "0.5.1",
1937
+ "nan": "2.10.0",
1938
+ "node-gyp": "3.6.2",
1939
+ "npmlog": "4.1.2",
1940
+ "request": "2.79.0",
1941
+ "sass-graph": "2.2.4",
1942
+ "stdout-stream": "1.4.0",
1943
+ "true-case-path": "1.0.2"
1944
+ }
1945
+ },
1946
+ "nopt": {
1947
+ "version": "3.0.6",
1948
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
1949
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
1950
+ "dev": true,
1951
+ "requires": {
1952
+ "abbrev": "1.1.1"
1953
+ }
1954
+ },
1955
+ "normalize-package-data": {
1956
+ "version": "2.4.0",
1957
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
1958
+ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
1959
+ "dev": true,
1960
+ "requires": {
1961
+ "hosted-git-info": "2.6.0",
1962
+ "is-builtin-module": "1.0.0",
1963
+ "semver": "5.5.0",
1964
+ "validate-npm-package-license": "3.0.3"
1965
+ }
1966
+ },
1967
+ "npmlog": {
1968
+ "version": "4.1.2",
1969
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
1970
+ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
1971
+ "dev": true,
1972
+ "requires": {
1973
+ "are-we-there-yet": "1.1.4",
1974
+ "console-control-strings": "1.1.0",
1975
+ "gauge": "2.7.4",
1976
+ "set-blocking": "2.0.0"
1977
+ }
1978
+ },
1979
+ "number-is-nan": {
1980
+ "version": "1.0.1",
1981
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
1982
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
1983
+ "dev": true
1984
+ },
1985
+ "oauth-sign": {
1986
+ "version": "0.8.2",
1987
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
1988
+ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
1989
+ "dev": true
1990
+ },
1991
+ "object-assign": {
1992
+ "version": "4.1.1",
1993
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1994
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
1995
+ "dev": true
1996
+ },
1997
+ "on-finished": {
1998
+ "version": "2.3.0",
1999
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
2000
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
2001
+ "dev": true,
2002
+ "requires": {
2003
+ "ee-first": "1.1.1"
2004
+ }
2005
+ },
2006
+ "once": {
2007
+ "version": "1.4.0",
2008
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
2009
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
2010
+ "dev": true,
2011
+ "requires": {
2012
+ "wrappy": "1.0.2"
2013
+ }
2014
+ },
2015
+ "onetime": {
2016
+ "version": "1.1.0",
2017
+ "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
2018
+ "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
2019
+ "dev": true
2020
+ },
2021
+ "os-homedir": {
2022
+ "version": "1.0.2",
2023
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
2024
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
2025
+ "dev": true
2026
+ },
2027
+ "os-locale": {
2028
+ "version": "1.4.0",
2029
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
2030
+ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
2031
+ "dev": true,
2032
+ "requires": {
2033
+ "lcid": "1.0.0"
2034
+ }
2035
+ },
2036
+ "os-tmpdir": {
2037
+ "version": "1.0.2",
2038
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
2039
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
2040
+ "dev": true
2041
+ },
2042
+ "osenv": {
2043
+ "version": "0.1.5",
2044
+ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
2045
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
2046
+ "dev": true,
2047
+ "requires": {
2048
+ "os-homedir": "1.0.2",
2049
+ "os-tmpdir": "1.0.2"
2050
+ }
2051
+ },
2052
+ "pako": {
2053
+ "version": "0.2.9",
2054
+ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
2055
+ "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
2056
+ "dev": true
2057
+ },
2058
+ "parse-json": {
2059
+ "version": "2.2.0",
2060
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
2061
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
2062
+ "dev": true,
2063
+ "requires": {
2064
+ "error-ex": "1.3.1"
2065
+ }
2066
+ },
2067
+ "parse-ms": {
2068
+ "version": "1.0.1",
2069
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz",
2070
+ "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=",
2071
+ "dev": true
2072
+ },
2073
+ "parseurl": {
2074
+ "version": "1.3.2",
2075
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
2076
+ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=",
2077
+ "dev": true
2078
+ },
2079
+ "path-exists": {
2080
+ "version": "2.1.0",
2081
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
2082
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
2083
+ "dev": true,
2084
+ "requires": {
2085
+ "pinkie-promise": "2.0.1"
2086
+ }
2087
+ },
2088
+ "path-is-absolute": {
2089
+ "version": "1.0.1",
2090
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
2091
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
2092
+ "dev": true
2093
+ },
2094
+ "path-type": {
2095
+ "version": "1.1.0",
2096
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
2097
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
2098
+ "dev": true,
2099
+ "requires": {
2100
+ "graceful-fs": "4.1.11",
2101
+ "pify": "2.3.0",
2102
+ "pinkie-promise": "2.0.1"
2103
+ }
2104
+ },
2105
+ "pify": {
2106
+ "version": "2.3.0",
2107
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
2108
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
2109
+ "dev": true
2110
+ },
2111
+ "pinkie": {
2112
+ "version": "2.0.4",
2113
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
2114
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
2115
+ "dev": true
2116
+ },
2117
+ "pinkie-promise": {
2118
+ "version": "2.0.1",
2119
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
2120
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
2121
+ "dev": true,
2122
+ "requires": {
2123
+ "pinkie": "2.0.4"
2124
+ }
2125
+ },
2126
+ "pkg-up": {
2127
+ "version": "1.0.0",
2128
+ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz",
2129
+ "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=",
2130
+ "dev": true,
2131
+ "requires": {
2132
+ "find-up": "1.1.2"
2133
+ }
2134
+ },
2135
+ "plur": {
2136
+ "version": "2.1.2",
2137
+ "resolved": "https://registry.npmjs.org/plur/-/plur-2.1.2.tgz",
2138
+ "integrity": "sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo=",
2139
+ "dev": true,
2140
+ "requires": {
2141
+ "irregular-plurals": "1.4.0"
2142
+ }
2143
+ },
2144
+ "pretty-bytes": {
2145
+ "version": "1.0.4",
2146
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz",
2147
+ "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=",
2148
+ "dev": true,
2149
+ "requires": {
2150
+ "get-stdin": "4.0.1",
2151
+ "meow": "3.7.0"
2152
+ }
2153
+ },
2154
+ "pretty-ms": {
2155
+ "version": "2.1.0",
2156
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-2.1.0.tgz",
2157
+ "integrity": "sha1-QlfCVt8/sLRR1q/6qwIYhBJpgdw=",
2158
+ "dev": true,
2159
+ "requires": {
2160
+ "is-finite": "1.0.2",
2161
+ "parse-ms": "1.0.1",
2162
+ "plur": "1.0.0"
2163
+ },
2164
+ "dependencies": {
2165
+ "plur": {
2166
+ "version": "1.0.0",
2167
+ "resolved": "https://registry.npmjs.org/plur/-/plur-1.0.0.tgz",
2168
+ "integrity": "sha1-24XGgU9eXlo7Se/CjWBP7GKXUVY=",
2169
+ "dev": true
2170
+ }
2171
+ }
2172
+ },
2173
+ "process-nextick-args": {
2174
+ "version": "2.0.0",
2175
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
2176
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
2177
+ "dev": true
2178
+ },
2179
+ "pseudomap": {
2180
+ "version": "1.0.2",
2181
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
2182
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
2183
+ "dev": true
2184
+ },
2185
+ "punycode": {
2186
+ "version": "1.4.1",
2187
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
2188
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
2189
+ "dev": true
2190
+ },
2191
+ "qs": {
2192
+ "version": "5.1.0",
2193
+ "resolved": "https://registry.npmjs.org/qs/-/qs-5.1.0.tgz",
2194
+ "integrity": "sha1-TZMuXH6kEcynajEtOaYGIA/VDNk=",
2195
+ "dev": true
2196
+ },
2197
+ "raw-body": {
2198
+ "version": "2.1.7",
2199
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz",
2200
+ "integrity": "sha1-rf6s4uT7MJgFgBTQjActzFl1h3Q=",
2201
+ "dev": true,
2202
+ "requires": {
2203
+ "bytes": "2.4.0",
2204
+ "iconv-lite": "0.4.13",
2205
+ "unpipe": "1.0.0"
2206
+ },
2207
+ "dependencies": {
2208
+ "bytes": {
2209
+ "version": "2.4.0",
2210
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz",
2211
+ "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=",
2212
+ "dev": true
2213
+ },
2214
+ "iconv-lite": {
2215
+ "version": "0.4.13",
2216
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz",
2217
+ "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=",
2218
+ "dev": true
2219
+ }
2220
+ }
2221
+ },
2222
+ "read-pkg": {
2223
+ "version": "1.1.0",
2224
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
2225
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
2226
+ "dev": true,
2227
+ "requires": {
2228
+ "load-json-file": "1.1.0",
2229
+ "normalize-package-data": "2.4.0",
2230
+ "path-type": "1.1.0"
2231
+ }
2232
+ },
2233
+ "read-pkg-up": {
2234
+ "version": "1.0.1",
2235
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
2236
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
2237
+ "dev": true,
2238
+ "requires": {
2239
+ "find-up": "1.1.2",
2240
+ "read-pkg": "1.1.0"
2241
+ }
2242
+ },
2243
+ "readable-stream": {
2244
+ "version": "1.1.14",
2245
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
2246
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
2247
+ "dev": true,
2248
+ "requires": {
2249
+ "core-util-is": "1.0.2",
2250
+ "inherits": "2.0.3",
2251
+ "isarray": "0.0.1",
2252
+ "string_decoder": "0.10.31"
2253
+ }
2254
+ },
2255
+ "redent": {
2256
+ "version": "1.0.0",
2257
+ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
2258
+ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
2259
+ "dev": true,
2260
+ "requires": {
2261
+ "indent-string": "2.1.0",
2262
+ "strip-indent": "1.0.1"
2263
+ }
2264
+ },
2265
+ "repeating": {
2266
+ "version": "2.0.1",
2267
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
2268
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
2269
+ "dev": true,
2270
+ "requires": {
2271
+ "is-finite": "1.0.2"
2272
+ }
2273
+ },
2274
+ "request": {
2275
+ "version": "2.79.0",
2276
+ "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz",
2277
+ "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=",
2278
+ "dev": true,
2279
+ "requires": {
2280
+ "aws-sign2": "0.6.0",
2281
+ "aws4": "1.7.0",
2282
+ "caseless": "0.11.0",
2283
+ "combined-stream": "1.0.6",
2284
+ "extend": "3.0.1",
2285
+ "forever-agent": "0.6.1",
2286
+ "form-data": "2.1.4",
2287
+ "har-validator": "2.0.6",
2288
+ "hawk": "3.1.3",
2289
+ "http-signature": "1.1.1",
2290
+ "is-typedarray": "1.0.0",
2291
+ "isstream": "0.1.2",
2292
+ "json-stringify-safe": "5.0.1",
2293
+ "mime-types": "2.1.18",
2294
+ "oauth-sign": "0.8.2",
2295
+ "qs": "6.3.2",
2296
+ "stringstream": "0.0.5",
2297
+ "tough-cookie": "2.3.4",
2298
+ "tunnel-agent": "0.4.3",
2299
+ "uuid": "3.2.1"
2300
+ },
2301
+ "dependencies": {
2302
+ "qs": {
2303
+ "version": "6.3.2",
2304
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz",
2305
+ "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=",
2306
+ "dev": true
2307
+ }
2308
+ }
2309
+ },
2310
+ "require-directory": {
2311
+ "version": "2.1.1",
2312
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
2313
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
2314
+ "dev": true
2315
+ },
2316
+ "require-main-filename": {
2317
+ "version": "1.0.1",
2318
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
2319
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
2320
+ "dev": true
2321
+ },
2322
+ "resolve": {
2323
+ "version": "1.1.7",
2324
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
2325
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
2326
+ "dev": true
2327
+ },
2328
+ "resolve-from": {
2329
+ "version": "2.0.0",
2330
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
2331
+ "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=",
2332
+ "dev": true
2333
+ },
2334
+ "resolve-pkg": {
2335
+ "version": "0.1.0",
2336
+ "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-0.1.0.tgz",
2337
+ "integrity": "sha1-AsyZNBDik2livZcWahsHfalyVTE=",
2338
+ "dev": true,
2339
+ "requires": {
2340
+ "resolve-from": "2.0.0"
2341
+ }
2342
+ },
2343
+ "rimraf": {
2344
+ "version": "2.2.8",
2345
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
2346
+ "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
2347
+ "dev": true
2348
+ },
2349
+ "safe-buffer": {
2350
+ "version": "5.1.2",
2351
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
2352
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
2353
+ "dev": true
2354
+ },
2355
+ "safer-buffer": {
2356
+ "version": "2.1.2",
2357
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
2358
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
2359
+ "dev": true
2360
+ },
2361
+ "sass-graph": {
2362
+ "version": "2.2.4",
2363
+ "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
2364
+ "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
2365
+ "dev": true,
2366
+ "requires": {
2367
+ "glob": "7.0.6",
2368
+ "lodash": "4.17.10",
2369
+ "scss-tokenizer": "0.2.3",
2370
+ "yargs": "7.1.0"
2371
+ }
2372
+ },
2373
+ "scss-tokenizer": {
2374
+ "version": "0.2.3",
2375
+ "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
2376
+ "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
2377
+ "dev": true,
2378
+ "requires": {
2379
+ "js-base64": "2.4.3",
2380
+ "source-map": "0.4.4"
2381
+ },
2382
+ "dependencies": {
2383
+ "source-map": {
2384
+ "version": "0.4.4",
2385
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
2386
+ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
2387
+ "dev": true,
2388
+ "requires": {
2389
+ "amdefine": "1.0.1"
2390
+ }
2391
+ }
2392
+ }
2393
+ },
2394
+ "semver": {
2395
+ "version": "5.5.0",
2396
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
2397
+ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
2398
+ "dev": true
2399
+ },
2400
+ "set-blocking": {
2401
+ "version": "2.0.0",
2402
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
2403
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
2404
+ "dev": true
2405
+ },
2406
+ "set-immediate-shim": {
2407
+ "version": "1.0.1",
2408
+ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
2409
+ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
2410
+ "dev": true
2411
+ },
2412
+ "shelljs": {
2413
+ "version": "0.3.0",
2414
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
2415
+ "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=",
2416
+ "dev": true
2417
+ },
2418
+ "sigmund": {
2419
+ "version": "1.0.1",
2420
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
2421
+ "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
2422
+ "dev": true
2423
+ },
2424
+ "signal-exit": {
2425
+ "version": "3.0.2",
2426
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
2427
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
2428
+ "dev": true
2429
+ },
2430
+ "sntp": {
2431
+ "version": "1.0.9",
2432
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
2433
+ "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
2434
+ "dev": true,
2435
+ "requires": {
2436
+ "hoek": "2.16.3"
2437
+ }
2438
+ },
2439
+ "source-map": {
2440
+ "version": "0.6.1",
2441
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
2442
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
2443
+ "dev": true
2444
+ },
2445
+ "spdx-correct": {
2446
+ "version": "3.0.0",
2447
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
2448
+ "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
2449
+ "dev": true,
2450
+ "requires": {
2451
+ "spdx-expression-parse": "3.0.0",
2452
+ "spdx-license-ids": "3.0.0"
2453
+ }
2454
+ },
2455
+ "spdx-exceptions": {
2456
+ "version": "2.1.0",
2457
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
2458
+ "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==",
2459
+ "dev": true
2460
+ },
2461
+ "spdx-expression-parse": {
2462
+ "version": "3.0.0",
2463
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
2464
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
2465
+ "dev": true,
2466
+ "requires": {
2467
+ "spdx-exceptions": "2.1.0",
2468
+ "spdx-license-ids": "3.0.0"
2469
+ }
2470
+ },
2471
+ "spdx-license-ids": {
2472
+ "version": "3.0.0",
2473
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz",
2474
+ "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==",
2475
+ "dev": true
2476
+ },
2477
+ "sprintf-js": {
2478
+ "version": "1.0.3",
2479
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
2480
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
2481
+ "dev": true
2482
+ },
2483
+ "sshpk": {
2484
+ "version": "1.14.1",
2485
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz",
2486
+ "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=",
2487
+ "dev": true,
2488
+ "requires": {
2489
+ "asn1": "0.2.3",
2490
+ "assert-plus": "1.0.0",
2491
+ "bcrypt-pbkdf": "1.0.1",
2492
+ "dashdash": "1.14.1",
2493
+ "ecc-jsbn": "0.1.1",
2494
+ "getpass": "0.1.7",
2495
+ "jsbn": "0.1.1",
2496
+ "tweetnacl": "0.14.5"
2497
+ },
2498
+ "dependencies": {
2499
+ "assert-plus": {
2500
+ "version": "1.0.0",
2501
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
2502
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
2503
+ "dev": true
2504
+ }
2505
+ }
2506
+ },
2507
+ "statuses": {
2508
+ "version": "1.5.0",
2509
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
2510
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
2511
+ "dev": true
2512
+ },
2513
+ "stdout-stream": {
2514
+ "version": "1.4.0",
2515
+ "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz",
2516
+ "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=",
2517
+ "dev": true,
2518
+ "requires": {
2519
+ "readable-stream": "2.3.6"
2520
+ },
2521
+ "dependencies": {
2522
+ "isarray": {
2523
+ "version": "1.0.0",
2524
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
2525
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
2526
+ "dev": true
2527
+ },
2528
+ "readable-stream": {
2529
+ "version": "2.3.6",
2530
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
2531
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
2532
+ "dev": true,
2533
+ "requires": {
2534
+ "core-util-is": "1.0.2",
2535
+ "inherits": "2.0.3",
2536
+ "isarray": "1.0.0",
2537
+ "process-nextick-args": "2.0.0",
2538
+ "safe-buffer": "5.1.2",
2539
+ "string_decoder": "1.1.1",
2540
+ "util-deprecate": "1.0.2"
2541
+ }
2542
+ },
2543
+ "string_decoder": {
2544
+ "version": "1.1.1",
2545
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
2546
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
2547
+ "dev": true,
2548
+ "requires": {
2549
+ "safe-buffer": "5.1.2"
2550
+ }
2551
+ }
2552
+ }
2553
+ },
2554
+ "string-length": {
2555
+ "version": "1.0.1",
2556
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz",
2557
+ "integrity": "sha1-VpcPscOFWOnnC3KL894mmsRa36w=",
2558
+ "dev": true,
2559
+ "requires": {
2560
+ "strip-ansi": "3.0.1"
2561
+ }
2562
+ },
2563
+ "string-width": {
2564
+ "version": "1.0.2",
2565
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
2566
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
2567
+ "dev": true,
2568
+ "requires": {
2569
+ "code-point-at": "1.1.0",
2570
+ "is-fullwidth-code-point": "1.0.0",
2571
+ "strip-ansi": "3.0.1"
2572
+ }
2573
+ },
2574
+ "string_decoder": {
2575
+ "version": "0.10.31",
2576
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
2577
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
2578
+ "dev": true
2579
+ },
2580
+ "stringstream": {
2581
+ "version": "0.0.5",
2582
+ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
2583
+ "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
2584
+ "dev": true
2585
+ },
2586
+ "strip-ansi": {
2587
+ "version": "3.0.1",
2588
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
2589
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
2590
+ "dev": true,
2591
+ "requires": {
2592
+ "ansi-regex": "2.1.1"
2593
+ }
2594
+ },
2595
+ "strip-bom": {
2596
+ "version": "2.0.0",
2597
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
2598
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
2599
+ "dev": true,
2600
+ "requires": {
2601
+ "is-utf8": "0.2.1"
2602
+ }
2603
+ },
2604
+ "strip-indent": {
2605
+ "version": "1.0.1",
2606
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
2607
+ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
2608
+ "dev": true,
2609
+ "requires": {
2610
+ "get-stdin": "4.0.1"
2611
+ }
2612
+ },
2613
+ "strip-json-comments": {
2614
+ "version": "1.0.4",
2615
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
2616
+ "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=",
2617
+ "dev": true
2618
+ },
2619
+ "supports-color": {
2620
+ "version": "2.0.0",
2621
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
2622
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
2623
+ "dev": true
2624
+ },
2625
+ "tar": {
2626
+ "version": "2.2.1",
2627
+ "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
2628
+ "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
2629
+ "dev": true,
2630
+ "requires": {
2631
+ "block-stream": "0.0.9",
2632
+ "fstream": "1.0.11",
2633
+ "inherits": "2.0.3"
2634
+ }
2635
+ },
2636
+ "text-table": {
2637
+ "version": "0.2.0",
2638
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
2639
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
2640
+ "dev": true
2641
+ },
2642
+ "time-grunt": {
2643
+ "version": "1.4.0",
2644
+ "resolved": "https://registry.npmjs.org/time-grunt/-/time-grunt-1.4.0.tgz",
2645
+ "integrity": "sha1-BiIT5mDJB+hvRAVWwB6mWXtxJCA=",
2646
+ "dev": true,
2647
+ "requires": {
2648
+ "chalk": "1.1.3",
2649
+ "date-time": "1.1.0",
2650
+ "figures": "1.7.0",
2651
+ "hooker": "0.2.3",
2652
+ "number-is-nan": "1.0.1",
2653
+ "pretty-ms": "2.1.0",
2654
+ "text-table": "0.2.0"
2655
+ }
2656
+ },
2657
+ "time-zone": {
2658
+ "version": "0.1.0",
2659
+ "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-0.1.0.tgz",
2660
+ "integrity": "sha1-Sncotqwo2w4Aj1FAQ/1VW9VXO0Y=",
2661
+ "dev": true
2662
+ },
2663
+ "tiny-lr": {
2664
+ "version": "0.2.1",
2665
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.2.1.tgz",
2666
+ "integrity": "sha1-s/26gC5dVqM8L28QeUsy5Hescp0=",
2667
+ "dev": true,
2668
+ "requires": {
2669
+ "body-parser": "1.14.2",
2670
+ "debug": "2.2.0",
2671
+ "faye-websocket": "0.10.0",
2672
+ "livereload-js": "2.3.0",
2673
+ "parseurl": "1.3.2",
2674
+ "qs": "5.1.0"
2675
+ }
2676
+ },
2677
+ "tough-cookie": {
2678
+ "version": "2.3.4",
2679
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
2680
+ "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
2681
+ "dev": true,
2682
+ "requires": {
2683
+ "punycode": "1.4.1"
2684
+ }
2685
+ },
2686
+ "trim-newlines": {
2687
+ "version": "1.0.0",
2688
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
2689
+ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
2690
+ "dev": true
2691
+ },
2692
+ "true-case-path": {
2693
+ "version": "1.0.2",
2694
+ "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz",
2695
+ "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=",
2696
+ "dev": true,
2697
+ "requires": {
2698
+ "glob": "6.0.4"
2699
+ },
2700
+ "dependencies": {
2701
+ "glob": {
2702
+ "version": "6.0.4",
2703
+ "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
2704
+ "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
2705
+ "dev": true,
2706
+ "requires": {
2707
+ "inflight": "1.0.6",
2708
+ "inherits": "2.0.3",
2709
+ "minimatch": "3.0.4",
2710
+ "once": "1.4.0",
2711
+ "path-is-absolute": "1.0.1"
2712
+ }
2713
+ }
2714
+ }
2715
+ },
2716
+ "tunnel-agent": {
2717
+ "version": "0.4.3",
2718
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
2719
+ "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=",
2720
+ "dev": true
2721
+ },
2722
+ "tweetnacl": {
2723
+ "version": "0.14.5",
2724
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
2725
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
2726
+ "dev": true,
2727
+ "optional": true
2728
+ },
2729
+ "type-is": {
2730
+ "version": "1.6.16",
2731
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
2732
+ "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
2733
+ "dev": true,
2734
+ "requires": {
2735
+ "media-typer": "0.3.0",
2736
+ "mime-types": "2.1.18"
2737
+ }
2738
+ },
2739
+ "typedarray": {
2740
+ "version": "0.0.6",
2741
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
2742
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
2743
+ "dev": true
2744
+ },
2745
+ "uglify-js": {
2746
+ "version": "3.3.22",
2747
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.22.tgz",
2748
+ "integrity": "sha512-tqw96rL6/BG+7LM5VItdhDjTQmL5zG/I0b2RqWytlgeHe2eydZHuBHdA9vuGpCDhH/ZskNGcqDhivoR2xt8RIw==",
2749
+ "dev": true,
2750
+ "requires": {
2751
+ "commander": "2.15.1",
2752
+ "source-map": "0.6.1"
2753
+ }
2754
+ },
2755
+ "underscore": {
2756
+ "version": "1.7.0",
2757
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz",
2758
+ "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=",
2759
+ "dev": true
2760
+ },
2761
+ "underscore.string": {
2762
+ "version": "3.2.3",
2763
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.2.3.tgz",
2764
+ "integrity": "sha1-gGmSYzZl1eX8tNsfs6hi62jp5to=",
2765
+ "dev": true
2766
+ },
2767
+ "unpipe": {
2768
+ "version": "1.0.0",
2769
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
2770
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
2771
+ "dev": true
2772
+ },
2773
+ "uri-path": {
2774
+ "version": "1.0.0",
2775
+ "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz",
2776
+ "integrity": "sha1-l0fwGDWJM8Md4PzP2C0TjmcmLjI=",
2777
+ "dev": true
2778
+ },
2779
+ "util-deprecate": {
2780
+ "version": "1.0.2",
2781
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
2782
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
2783
+ "dev": true
2784
+ },
2785
+ "uuid": {
2786
+ "version": "3.2.1",
2787
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
2788
+ "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
2789
+ "dev": true
2790
+ },
2791
+ "validate-npm-package-license": {
2792
+ "version": "3.0.3",
2793
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
2794
+ "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
2795
+ "dev": true,
2796
+ "requires": {
2797
+ "spdx-correct": "3.0.0",
2798
+ "spdx-expression-parse": "3.0.0"
2799
+ }
2800
+ },
2801
+ "verror": {
2802
+ "version": "1.10.0",
2803
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
2804
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
2805
+ "dev": true,
2806
+ "requires": {
2807
+ "assert-plus": "1.0.0",
2808
+ "core-util-is": "1.0.2",
2809
+ "extsprintf": "1.3.0"
2810
+ },
2811
+ "dependencies": {
2812
+ "assert-plus": {
2813
+ "version": "1.0.0",
2814
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
2815
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
2816
+ "dev": true
2817
+ }
2818
+ }
2819
+ },
2820
+ "websocket-driver": {
2821
+ "version": "0.7.0",
2822
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz",
2823
+ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=",
2824
+ "dev": true,
2825
+ "requires": {
2826
+ "http-parser-js": "0.4.12",
2827
+ "websocket-extensions": "0.1.3"
2828
+ }
2829
+ },
2830
+ "websocket-extensions": {
2831
+ "version": "0.1.3",
2832
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz",
2833
+ "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
2834
+ "dev": true
2835
+ },
2836
+ "which": {
2837
+ "version": "1.2.14",
2838
+ "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
2839
+ "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
2840
+ "dev": true,
2841
+ "requires": {
2842
+ "isexe": "2.0.0"
2843
+ }
2844
+ },
2845
+ "which-module": {
2846
+ "version": "1.0.0",
2847
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
2848
+ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
2849
+ "dev": true
2850
+ },
2851
+ "wide-align": {
2852
+ "version": "1.1.2",
2853
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
2854
+ "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
2855
+ "dev": true,
2856
+ "requires": {
2857
+ "string-width": "1.0.2"
2858
+ }
2859
+ },
2860
+ "wrap-ansi": {
2861
+ "version": "2.1.0",
2862
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
2863
+ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
2864
+ "dev": true,
2865
+ "requires": {
2866
+ "string-width": "1.0.2",
2867
+ "strip-ansi": "3.0.1"
2868
+ }
2869
+ },
2870
+ "wrappy": {
2871
+ "version": "1.0.2",
2872
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
2873
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
2874
+ "dev": true
2875
+ },
2876
+ "xtend": {
2877
+ "version": "4.0.1",
2878
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
2879
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
2880
+ "dev": true
2881
+ },
2882
+ "y18n": {
2883
+ "version": "3.2.1",
2884
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
2885
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
2886
+ "dev": true
2887
+ },
2888
+ "yallist": {
2889
+ "version": "2.1.2",
2890
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
2891
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
2892
+ "dev": true
2893
+ },
2894
+ "yargs": {
2895
+ "version": "7.1.0",
2896
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
2897
+ "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
2898
+ "dev": true,
2899
+ "requires": {
2900
+ "camelcase": "3.0.0",
2901
+ "cliui": "3.2.0",
2902
+ "decamelize": "1.2.0",
2903
+ "get-caller-file": "1.0.2",
2904
+ "os-locale": "1.4.0",
2905
+ "read-pkg-up": "1.0.1",
2906
+ "require-directory": "2.1.1",
2907
+ "require-main-filename": "1.0.1",
2908
+ "set-blocking": "2.0.0",
2909
+ "string-width": "1.0.2",
2910
+ "which-module": "1.0.0",
2911
+ "y18n": "3.2.1",
2912
+ "yargs-parser": "5.0.0"
2913
+ },
2914
+ "dependencies": {
2915
+ "camelcase": {
2916
+ "version": "3.0.0",
2917
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
2918
+ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
2919
+ "dev": true
2920
+ }
2921
+ }
2922
+ },
2923
+ "yargs-parser": {
2924
+ "version": "5.0.0",
2925
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
2926
+ "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
2927
+ "dev": true,
2928
+ "requires": {
2929
+ "camelcase": "3.0.0"
2930
+ },
2931
+ "dependencies": {
2932
+ "camelcase": {
2933
+ "version": "3.0.0",
2934
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
2935
+ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
2936
+ "dev": true
2937
+ }
2938
+ }
2939
+ }
2940
+ }
2941
+ }
package.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "simple-lightbox",
3
+ "version": "2.7.1",
4
+ "title": "Simple Lightbox",
5
+ "description": "The highly-customizable lightbox for WordPress",
6
+ "author": "Archetyped <support@archetyped.com>",
7
+ "license": "GPLv2",
8
+ "private": true,
9
+ "devDependencies": {
10
+ "grunt": "^1.0.1",
11
+ "grunt-contrib-jshint": "^1.0.0",
12
+ "grunt-contrib-uglify": "^3.3.0",
13
+ "grunt-contrib-watch": "^1.0.0",
14
+ "grunt-phplint": "0.1.0",
15
+ "grunt-sass": "^2.1.0",
16
+ "jshint-stylish": "^2.2.1",
17
+ "load-grunt-tasks": "^3.5.2",
18
+ "time-grunt": "^1.4.0"
19
+ }
20
+ }
readme.txt CHANGED
@@ -3,8 +3,9 @@ Contributors: Archetyped
3
  Donate link: http://gum.co/slb-donate
4
  License: GPLv2
5
  Tags: lightbox, gallery, photography, images, theme, template, style
6
- Requires at least: 3.6
7
- Tested up to: 3.6.1
 
8
  Stable tag: trunk
9
 
10
  The highly customizable lightbox for WordPress
@@ -13,6 +14,7 @@ The highly customizable lightbox for WordPress
13
  Simple Lightbox is a very simple and customizable lightbox that is easy to add to your WordPress website.
14
 
15
  #### Features
 
16
  Options for customizing the lightbox behavior are located in the **Appearance > Lightbox** admin menu (or just click the **Settings** link below the plugin's name when viewing the list of installed plugins)
17
 
18
  * Automatically activate links (no manual coding required)
@@ -30,7 +32,8 @@ Options for customizing the lightbox behavior are located in the **Appearance >
30
  * Group image links by Post (separate slideshow for each post on page)
31
 
32
  #### Usage
33
- 1. Insert links to images/image attachments into your posts/pages
 
34
 
35
  **That's it! The image will be displayed in a lightbox automatically.**
36
 
@@ -38,12 +41,13 @@ Options for customizing the lightbox behavior are located in the **Appearance >
38
 
39
  == Installation ==
40
 
41
- 1. Install and activate SLB
42
- 1. Verify that your site's theme uses the `wp_head()`, `wp_footer()`, & `the_content()` template tags (standard in any professional theme)
43
 
44
  == Upgrade Notice ==
45
 
46
- No upgrade notices
 
47
 
48
  == Frequently Asked Questions ==
49
 
@@ -51,152 +55,26 @@ Get more information on [Simple Lightbox's official page](http://archetyped.com/
51
 
52
  == Screenshots ==
53
 
54
- 1. Lightbox Customization Options
55
- 2. Light Theme
56
- 3. Dark Theme
57
 
58
  == Changelog ==
59
 
60
- = 2.0 =
61
- * Completely rewritten lightbox code
62
- * Add: Automatically resize lightbox to fit window
63
- * Add: APIs for third-party add-ons
64
- * Add: Flexible theme support
65
- * Add: Flexible content handler support
66
- * Add: Mobile-optimized responsive themes (2)
67
- * Optimize: PHP class autoloading
68
- * Optimize: Improved performance and compatibility
69
- * Optimize: Full internationalization support
70
-
71
- = 1.6 =
72
- * Add: Widget support
73
- * Add: WordPress 3.3 support
74
- * Add: Localization support
75
- * Add: Option to group gallery links separately (supports WordPress & NextGen galleries)
76
- * Add: Upgrade notice
77
- * Optimize: WP 3.3 compatibility
78
- * Optimize: Improved compatibility with URI case-sensitivity
79
- * Optimize: Activation processing
80
- * Optimize: Image grouping
81
- * Optimize: Image metadata loading performance
82
- * Optimize: File loading
83
- * Optimize: Improved safeguards against interference by bugs in other plugins
84
- * Optimize: Link processing performance
85
- * Optimize: Lightbox styling isolated from site styles
86
- * Optimize: Improved link processing performance
87
- * Optimize: Improved image metadata support
88
- * Optimize: Improved support for HTTP/HTTPS requests
89
- * Fix: SLB is not defined in JS (Jezz Hands)
90
- * Fix: Boolean case-sensitivity (78 Truths)
91
- * Fix: YouTube embed using iFrame overlaps lightbox (Elena in Hiding)
92
- * Fix: Issue when scanning links without valid URLs (McCloskey Iteration)
93
- * Fix: Image activation is case-sensitive (Sensitive Tanya)
94
- * Fix: Visible lightbox overlay edges when image larger than browser window (Chibi Overlay)
95
- * Fix: Options availability for some users
96
- * Fix: Inconsistent loading of image metadata
97
- * Fix: Links not fully processed when group is set manually
98
-
99
- = 1.5.6 =
100
- * Add: Display image description in lightbox (with HTML support)
101
- * Add: Support for W3 Total Cache plugin
102
- * Add: Initial support for NextGEN galleries
103
- * Update: **Important:** [System Requirements](http://wordpress.org/about/requirements/) aligned with WP 3.2.1
104
- * Optimize: Improved support for small images in default template
105
- * Optimize: Support for non-English text in user options
106
- * Optimize: Improved IE compatibility
107
- * Optimize: Improved data handling
108
- * Optimize: Skin loading performance
109
- * Optimize: Skin CSS Cleanup
110
- * Optimize: Caption support for galleries
111
- * Optimize: Options code cleanup (Juga Sweep)
112
- * Fix: User-defined UI text not used (Ivan gets Even (cooler))
113
- * Fix: Options reset after update (KRazy Donna)
114
-
115
- = 1.5.5.1 =
116
- * Fix: Disabled links not being disabled (Disabling Sascha)
117
-
118
- = 1.5.5 =
119
- * Add: Distinct link activation (will not affect other lightboxes)
120
- * Add: Backwards compatibility with legacy lightbox links (optional)
121
- * Add: Support for WordPress 3.2
122
- * Add: Support for links added after page load (e.g. via AJAX, etc.)
123
- * Add: Admin option to enable/disable attachment links
124
- * Add: Support for image attachment links
125
- * Update: Options management overhaul
126
- * Update: Additional WordPress 3.2 support (Gallery)
127
- * Update: Cache-management for enqueued files
128
- * Update: Improved UI consistency
129
- * Update: Improved compatibility for older versions of PHP
130
- * Update: Internal optimizations
131
- * Update: Improved URL handling
132
- * Fix: Improved options migration from old versions (Hutchison Migration)
133
- * Fix: XHTML Validation (Hajo Validation)
134
-
135
- = 1.5.4 =
136
- * Add: Optional Link validation
137
- * Add: Keyboard Navigation
138
- * Add: Option to enable/disable image caption
139
- * Add: `rel` attribute supported again
140
- * Add: Use `slb_off` in link's `rel` attribute to disable automatic activation for link
141
- * Fix: HTTPS compatibility (J&uuml;rgen Protocol)
142
- * Fix: Enabling SLB on Pages issue
143
- * Fix: Zmanu is_single
144
- * Fix: Image order is sometimes incorrect
145
- * Optimize: Filter double clicks
146
- * Optimize: Separate options to enable/disable SLB on Posts and Pages
147
- * Optimize: Better grouping support
148
-
149
- = 1.5.3 =
150
- * Fix: Caption may not display under certain circumstances (Caption Erin)
151
- * Fix: Images not grouped when "separate by post" option is activated (Logical Ross)
152
- * Update: Lightbox will not be activated for links that already have `rel` attribute set
153
-
154
- = 1.5.2 =
155
- * Fix: Slideshow loops out of control (Mirage of Wallentin)
156
- * Fix: Lightbox fails when group by posts disabled (Lange Find)
157
- * Add: Option to use the image's URI as caption when link title not set (Under UI options)
158
-
159
- = 1.5.1 =
160
- * Add: WP Gallery support
161
- * Fix: Navigation hidden when only one image
162
- * Fix: Use user-defined UI text
163
-
164
- = 1.5 =
165
- * Add: Theme support
166
- * Optimize: Javascript cleanup and file size reductions
167
- * Optimize: CSS cleanup
168
-
169
- = 1.4 =
170
- * Update: Integrated with jQuery
171
- * Optimize: Javascript filesize 9x smaller
172
- * Add: Close lightbox by clicking to left/right outside of image (an oft-requested feature)
173
-
174
- = 1.3.2 =
175
- * Add: Option to enable/disable lightbox resizing animation (thanks Maria!)
176
-
177
- = 1.3.1 =
178
- * Update: Utilities code (internal)
179
-
180
- = 1.3 =
181
- * Add: Customizable UI label text (close, next, and prev button images can be replaced in `images` directory)
182
- * Add: Group image links by Post (separate slideshow for each post)
183
- * Add: Reset settings link on plugin listings page
184
- * Optimize: Organized settings page
185
-
186
- = 1.2.1 =
187
- * Fixed: Image title given higher precedence than Image alt (more compatible w/WP workflow)
188
-
189
- = 1.2 =
190
- * Added: Option to group automatically activated links
191
- * Optimized: Lightbox caption retrieval
192
-
193
- = 1.1 =
194
- * Added: Enable/disable lightbox functionality by page type (Home, Pages/Posts, Archive, etc.)
195
- * Added: Automatically activate lightbox functionality for image links
196
- * Added: Link to settings menu on plugin listing page
197
- * Optimized: Options menu field building
198
- * Optimized: Loading of default values for plugin options
199
- * Optimized: General code optimizations
200
-
201
- = 1.0 =
202
- * Initial release
3
  Donate link: http://gum.co/slb-donate
4
  License: GPLv2
5
  Tags: lightbox, gallery, photography, images, theme, template, style
6
+ Requires at least: 5.2
7
+ Tested up to: 5.3
8
+ Requires PHP: 5.6.20
9
  Stable tag: trunk
10
 
11
  The highly customizable lightbox for WordPress
14
  Simple Lightbox is a very simple and customizable lightbox that is easy to add to your WordPress website.
15
 
16
  #### Features
17
+
18
  Options for customizing the lightbox behavior are located in the **Appearance > Lightbox** admin menu (or just click the **Settings** link below the plugin's name when viewing the list of installed plugins)
19
 
20
  * Automatically activate links (no manual coding required)
32
  * Group image links by Post (separate slideshow for each post on page)
33
 
34
  #### Usage
35
+
36
+ 1. Insert links to images/image attachments into your posts/pages
37
 
38
  **That's it! The image will be displayed in a lightbox automatically.**
39
 
41
 
42
  == Installation ==
43
 
44
+ 1. Install and activate SLB
45
+ 1. Verify that your site's theme uses the `wp_head()`, `wp_footer()`, & `the_content()` template tags (standard in any professional theme)
46
 
47
  == Upgrade Notice ==
48
 
49
+ = 2.7.0 =
50
+ Fixes & improvements. PHP 5.4+ Required.
51
 
52
  == Frequently Asked Questions ==
53
 
55
 
56
  == Screenshots ==
57
 
58
+ 1. Lightbox Customization Options
59
+ 2. Light Theme
60
+ 3. Dark Theme
61
 
62
  == Changelog ==
63
 
64
+ = 2.7.1 =
65
+
66
+ * Update: Confirm compatibility with WordPress 5.0+
67
+ * Optimize: Improved support for captions generated by Block Editor.
68
+
69
+ = 2.7.0 =
70
+
71
+ * Fix: Remove reference to deprecated `screen_icon()` function (The Icon of Finnegan Island)
72
+ * Fix: Reset settings link initializes plugin delete confirmation
73
+ * Add: Validate requirements before initialization.
74
+ * Optimize: PHP 7.2+ Compatibility
75
+ * Optimize: Internal code optimizations
76
+ * Themes
77
+ * Add: RTL Support
78
+ * Update: Load font locally
79
+
80
+ [See full changelog](https://github.com/archetyped/simple-lightbox/releases)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
template-tags/item/js/dev/tag.item.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ if ( !!window.SLB && SLB.has_child('View.extend_template_tag_handler') ) {(function() {
2
+ SLB.View.extend_template_tag_handler('item', {
3
+ /**
4
+ * Render Item tag
5
+ * @param obj item Content Item
6
+ * @param obj tag Tag instance
7
+ * @param obj dfr Promise to be resolved when tag is rendered
8
+ */
9
+ render: function(item, tag, dfr) {
10
+ // Build method name
11
+ var m = 'get_' + tag.get_prop();
12
+ // Get data
13
+ var ret = ( this.util.is_method(item, m) ) ? item[m]() : item.get_attribute(tag.get_prop(), '');
14
+ // Handle response
15
+ if ( this.util.is_promise(ret) ) {
16
+ ret.done(function(output) {
17
+ dfr.resolve(output);
18
+ });
19
+ } else {
20
+ dfr.resolve(ret);
21
+ }
22
+ return dfr.promise();
23
+ }
24
+ });
25
+ })();
26
+ }
template-tags/item/js/prod/tag.item.js ADDED
@@ -0,0 +1 @@
 
1
+ window.SLB&&SLB.has_child("View.extend_template_tag_handler")&&!function(){SLB.View.extend_template_tag_handler("item",{render:function(item,tag,dfr){var m="get_"+tag.get_prop(),ret=this.util.is_method(item,m)?item[m]():item.get_attribute(tag.get_prop(),"");return this.util.is_promise(ret)?ret.done(function(output){dfr.resolve(output)}):dfr.resolve(ret),dfr.promise()}})}();
template-tags/item/tag.item.js DELETED
@@ -1,17 +0,0 @@
1
- (function($) {
2
- return {
3
- render: function(item, tag) {
4
- var dfr = $.Deferred();
5
- var m = 'get_' + tag.get_prop();
6
- var ret = ( this.util.is_method(item, m) ) ? item[m]() : item.get_attribute(tag.get_prop(), '');
7
- if ( this.util.is_promise(ret) ) {
8
- ret.done(function(output) {
9
- dfr.resolve(output);
10
- });
11
- } else {
12
- dfr.resolve(ret);
13
- }
14
- return dfr.promise();
15
- }
16
- }
17
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
template-tags/ui/js/dev/tag.ui.js ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ if ( !!window.SLB && SLB.has_child('View.extend_template_tag_handler') ) {(function() {
2
+ SLB.View.extend_template_tag_handler('ui', {
3
+ _hooks : function() {
4
+ this.on('dom_init', function(ev) {
5
+ this.call_attribute('events_init', ev);
6
+ });
7
+ },
8
+ events_init: function(ev) {
9
+ var v = ev.data.template.get_theme().get_viewer();
10
+ var thm = v.get_theme();
11
+ // Add event handlers
12
+ v.on('events-complete', function(ev, v) {
13
+ // Register event handlers
14
+
15
+ /* Close */
16
+
17
+ // Close button
18
+ thm.dom_get_tag('ui', 'close').click(function() {
19
+ return v.close();
20
+ });
21
+
22
+ /* Navigation */
23
+
24
+ thm.dom_get_tag('ui', 'nav_next').click(function() {
25
+ v.item_next();
26
+ });
27
+ thm.dom_get_tag('ui', 'nav_prev').click(function() {
28
+ v.item_prev();
29
+ });
30
+
31
+ /* Slideshow */
32
+
33
+ thm.dom_get_tag('ui', 'slideshow_control').click(function() {
34
+ v.slideshow_toggle();
35
+ });
36
+ });
37
+
38
+ v.on('slideshow-toggle', function(ev, v) {
39
+ // Update slideshow control tag
40
+ var tags = thm.get_tags('ui', 'slideshow_control');
41
+ if ( tags.length ) {
42
+ // Renderer
43
+ var render_tag = function(tag) {
44
+ tag.render(v.get_item()).done(function(r) {
45
+ r.tag.dom_get().html(r.output);
46
+ });
47
+ };
48
+ // Process tags
49
+ for ( var x = 0; x < tags.length; x++ ) {
50
+ render_tag(tags[x]);
51
+ }
52
+ }
53
+ });
54
+ },
55
+ render: function(item, tag, dfr) {
56
+ // Process content
57
+ var ret = this.handle_prop(tag.get_prop(), item, tag);
58
+ if ( this.util.is_promise(ret) ) {
59
+ ret.done(function(output) {
60
+ dfr.resolve(output);
61
+ });
62
+ } else {
63
+ dfr.resolve(ret);
64
+ }
65
+ return dfr.promise();
66
+ },
67
+ props: {
68
+ 'slideshow_control': function(item) {
69
+ // Get slideshow status
70
+ var v = item.get_viewer();
71
+ var prop = ( v.slideshow_active() ) ? 'slideshow_stop' : 'slideshow_start';
72
+ return v.get_label(prop);
73
+ },
74
+ 'group_status': function(item) {
75
+ // Handle single items
76
+ if ( item.get_group().is_single() ) {
77
+ return '';
78
+ }
79
+ // Handle groups with multiple items
80
+ var out = item.get_viewer().get_label('group_status');
81
+ var key,
82
+ ph,
83
+ delim = '%',
84
+ handlers = {
85
+ current: function() {
86
+ return item.get_group(true).get_pos() + 1;
87
+ },
88
+ total: function() {
89
+ return item.get_group().get_size();
90
+ }
91
+ };
92
+ // Parse placeholders
93
+ for ( key in handlers ) {
94
+ // Build placeholder
95
+ ph = delim + key + delim;
96
+ // Replace placeholder
97
+ if ( -1 !== out.indexOf(ph) ) {
98
+ out = out.replace(new RegExp(ph, 'ig'), handlers[key]());
99
+ }
100
+ }
101
+ return out;
102
+ }
103
+ }
104
+ });
105
+ })();}
template-tags/ui/js/prod/tag.ui.js ADDED
@@ -0,0 +1 @@
 
1
+ window.SLB&&SLB.has_child("View.extend_template_tag_handler")&&!function(){SLB.View.extend_template_tag_handler("ui",{_hooks:function(){this.on("dom_init",function(ev){this.call_attribute("events_init",ev)})},events_init:function(ev){var v=ev.data.template.get_theme().get_viewer(),thm=v.get_theme();v.on("events-complete",function(ev,v){thm.dom_get_tag("ui","close").click(function(){return v.close()}),thm.dom_get_tag("ui","nav_next").click(function(){v.item_next()}),thm.dom_get_tag("ui","nav_prev").click(function(){v.item_prev()}),thm.dom_get_tag("ui","slideshow_control").click(function(){v.slideshow_toggle()})}),v.on("slideshow-toggle",function(ev,v){var tags=thm.get_tags("ui","slideshow_control");if(tags.length)for(var render_tag=function(tag){tag.render(v.get_item()).done(function(r){r.tag.dom_get().html(r.output)})},x=0;x<tags.length;x++)render_tag(tags[x])})},render:function(item,tag,dfr){var ret=this.handle_prop(tag.get_prop(),item,tag);return this.util.is_promise(ret)?ret.done(function(output){dfr.resolve(output)}):dfr.resolve(ret),dfr.promise()},props:{slideshow_control:function(item){var v=item.get_viewer(),prop=v.slideshow_active()?"slideshow_stop":"slideshow_start";return v.get_label(prop)},group_status:function(item){if(item.get_group().is_single())return"";var key,ph,out=item.get_viewer().get_label("group_status"),delim="%",handlers={current:function(){return item.get_group(!0).get_pos()+1},total:function(){return item.get_group().get_size()}};for(key in handlers)ph=delim+key+delim,-1!==out.indexOf(ph)&&(out=out.replace(new RegExp(ph,"ig"),handlers[key]()));return out}}})}();
template-tags/ui/tag.ui.js DELETED
@@ -1,108 +0,0 @@
1
- (function($) {
2
- return {
3
- init: function(item, tag, v) {
4
- //Add event handlers
5
- v.on('events-complete', function(ev, v) {
6
- //Register event handlers
7
-
8
- /* Close */
9
-
10
- var close = function() {
11
- return v.close();
12
- };
13
- //Close button
14
- v.get_theme().dom_get_tag('ui', 'close').click(close);
15
-
16
- /* Navigation */
17
-
18
- var nav_next = function() {
19
- v.item_next();
20
- };
21
-
22
- var nav_prev = function() {
23
- v.item_prev();
24
- };
25
-
26
- v.get_theme().dom_get_tag('ui', 'nav_next').click(nav_next);
27
- v.get_theme().dom_get_tag('ui', 'nav_prev').click(nav_prev);
28
-
29
- /* Slideshow */
30
-
31
- var slideshow_control = function() {
32
- v.slideshow_toggle();
33
- };
34
-
35
- v.get_theme().dom_get_tag('ui', 'slideshow_control').click(slideshow_control);
36
- });
37
-
38
- v.on('slideshow-toggle', function(ev, v) {
39
- //Update slideshow control tag
40
- var tags = v.get_theme().get_tags('ui', 'slideshow_control');
41
- if ( tags.length ) {
42
- for ( var x = 0; x < tags.length; x++ ) {
43
- tags[x].render(v.get_item()).done(function(r) {
44
- r.tag.dom_get().html(r.output);
45
- });
46
- }
47
- }
48
- });
49
- },
50
- render: function(item, tag) {
51
- //Initialize event handlers (once per viewer)
52
- var v = item.get_viewer();
53
- var st = ['events-init', tag.get_ns(), tag.get_name()].join('_');
54
- var fmt = function(output) {
55
- return output;
56
- };
57
- if ( !v.get_status(st) ) {
58
- v.set_status(st);
59
- this.call_attribute('init', item, tag, v);
60
- }
61
- //Process content
62
- var dfr = $.Deferred();
63
- var ret = this.handle_prop(tag.get_prop(), item, tag);
64
- if ( this.util.is_promise(ret) ) {
65
- ret.done(function(output) {
66
- dfr.resolve(fmt(output));
67
- });
68
- } else {
69
- dfr.resolve(fmt(ret));
70
- }
71
- return dfr.promise();
72
- },
73
- props: {
74
- 'slideshow_control': function(item, tag) {
75
- //Get slideshow status
76
- var prop = ( item.get_viewer().slideshow_active() ) ? 'slideshow_stop' : 'slideshow_start';
77
- return item.get_viewer().get_label(prop);
78
- },
79
- 'group_status': function(item, tag) {
80
- //Handle single items
81
- if ( item.get_group().is_single() ) {
82
- return '';
83
- }
84
- //Handle groups with multiple items
85
- var out = item.get_viewer().get_label('group_status');
86
- var key,
87
- ph,
88
- delim = '%',
89
- handlers = {
90
- current: function() {
91
- return item.get_group(true).get_pos() + 1;
92
- },
93
- total: function() {
94
- return item.get_group().get_size();
95
- }
96
- };
97
- //Parse placeholders
98
- for ( key in handlers ) {
99
- ph = delim + key + delim;
100
- if ( out.indexOf(ph) != -1 ) {
101
- out = out.replace(ph, handlers[key]());
102
- }
103
- }
104
- return out;
105
- }
106
- }
107
- }
108
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
themes/baseline/css/style.css ADDED
@@ -0,0 +1 @@
 
1
+ #slb_viewer_wrap .slb_theme_slb_baseline .slb_viewer_layout,#slb_viewer_wrap .slb_theme_slb_baseline .slb_container{box-sizing:border-box}#slb_viewer_wrap .slb_theme_slb_baseline{position:absolute;top:0;left:0;width:100%;z-index:99999;text-align:center;line-height:0;color:#000;font-family:arial, verdana, sans-serif;font-size:12px}#slb_viewer_wrap .slb_theme_slb_baseline *{margin:0;padding:0;line-height:1.4em;text-align:left;vertical-align:baseline;white-space:normal;outline:none;border:0px;background:none;opacity:1;width:auto;height:auto;position:static;float:none;clear:none}[dir="rtl"] #slb_viewer_wrap .slb_theme_slb_baseline *{text-align:right}#slb_viewer_wrap .slb_theme_slb_baseline a img{border:none}#slb_viewer_wrap .slb_theme_slb_baseline .slb_viewer_layout{z-index:2;position:absolute;width:100%;text-align:center}#slb_viewer_wrap .slb_theme_slb_baseline .slb_viewer_overlay{position:fixed;top:0;left:0;z-index:1;min-height:105%;min-width:100%;background-color:#000}#slb_viewer_wrap .slb_theme_slb_baseline .slb_container{position:relative;display:inline-block;background-color:#fff;margin:0 auto;padding:16px}#slb_viewer_wrap .slb_theme_slb_baseline .slb_loading{background:url("../images/loading.gif") center center no-repeat;position:absolute;left:0%;top:0;width:100%;height:100%;min-width:31px;min-height:31px;text-align:center;line-height:0;display:none}#slb_viewer_wrap .slb_theme_slb_baseline .slb_template_tag_ui{cursor:pointer}#slb_viewer_wrap .slb_theme_slb_baseline .slb_content{position:relative}#slb_viewer_wrap .slb_theme_slb_baseline .slb_details{margin:0 auto;text-align:left}#slb_viewer_wrap .slb_theme_slb_baseline .slb_details .inner{display:table;width:100%}#slb_viewer_wrap .slb_theme_slb_baseline .slb_details .slb_data{display:table-caption}#slb_viewer_wrap .slb_theme_slb_baseline .slb_template_tag_item_content>*{width:100%;height:100%}#slb_viewer_wrap .slb_theme_slb_baseline.item_single .slb_group_status,#slb_viewer_wrap .slb_theme_slb_baseline.item_single .slb_nav,#slb_viewer_wrap .slb_theme_slb_baseline.item_single .slb_slideshow{display:none}#slb_viewer_wrap .slb_theme_slb_baseline.loading .slb_loading{display:block}#slb_viewer_wrap .slb_theme_slb_baseline.loading .slb_template_tag_ui{opacity:0}@media screen and (max-width: 480px){#slb_viewer_wrap .slb_theme_slb_baseline .slb_theme_slb_baseline,#slb_viewer_wrap .slb_theme_slb_baseline .slb_viewer_layout,#slb_viewer_wrap .slb_theme_slb_baseline .slb_container{min-height:100%;min-width:320px;width:100%}#slb_viewer_wrap .slb_theme_slb_baseline .slb_viewer_layout{display:block}#slb_viewer_wrap .slb_theme_slb_baseline .slb_container{max-width:100%;margin:0;padding:5px;position:absolute;top:0;left:0}#slb_viewer_wrap .slb_theme_slb_baseline .slb_container .slb_content img,#slb_viewer_wrap .slb_theme_slb_baseline .slb_container .slb_content iframe,#slb_viewer_wrap .slb_theme_slb_baseline .slb_container .slb_content object,#slb_viewer_wrap .slb_theme_slb_baseline .slb_container .slb_content .slb_inner{max-width:100%}#slb_viewer_wrap .slb_theme_slb_baseline .slb_container .slb_content img{height:auto}}
themes/baseline/images/loading.gif ADDED
Binary file
themes/baseline/js/dev/client.js ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ if ( !!window.SLB && SLB.has_child('View.extend_theme') ) {(function() {
2
+ SLB.View.extend_theme('slb_baseline', {
3
+ 'breakpoints': {
4
+ 'small': 480,
5
+ 'large': 1024
6
+ },
7
+ /**
8
+ * Theme offsets
9
+ * Reports additional space required for theme UI
10
+ * @return obj Offset width/height values
11
+ */
12
+ 'offset': function() {
13
+ var o;
14
+ if ( document.documentElement.clientWidth > this.get_breakpoint('small') ) {
15
+ o = {'width': 32, 'height': 55};
16
+ } else {
17
+ o = {'width': 0, 'height': 0};
18
+ }
19
+ return o;
20
+ },
21
+ /**
22
+ * Theme margins
23
+ * Reports additional margins used for positioning viewer
24
+ * @return obj Margin width/height values
25
+ */
26
+ 'margin': function() {
27
+ var m;
28
+ if ( document.documentElement.clientWidth > this.get_breakpoint('small') ) {
29
+ m = {'height': 50, 'width': 20};
30
+ } else {
31
+ m = {'height': 0, 'width': 0};
32
+ }
33
+ return m;
34
+ }
35
+ });
36
+ })();
37
+ }
themes/baseline/js/prod/client.js ADDED
@@ -0,0 +1 @@
 
1
+ window.SLB&&SLB.has_child("View.extend_theme")&&!function(){SLB.View.extend_theme("slb_baseline",{breakpoints:{small:480,large:1024},offset:function(){var o;return o=document.documentElement.clientWidth>this.get_breakpoint("small")?{width:32,height:55}:{width:0,height:0}},margin:function(){var m;return m=document.documentElement.clientWidth>this.get_breakpoint("small")?{height:50,width:20}:{height:0,width:0}}})}();
themes/{default → baseline}/layout.html RENAMED
@@ -1,22 +1,22 @@
1
  <div class="slb_container">
2
  <div class="slb_content">
3
  {{item.content}}
4
- <span class="slb_nav">
5
- <div class="slb_prev">
6
  {{ui.nav_prev}}
7
- </div>
8
- <div class="slb_next">
9
  {{ui.nav_next}}
10
- </div>
11
- </span>
12
- <span class="slb_controls">
13
- <div class="slb_close">
14
  {{ui.close}}
15
- </div>
16
- <div class="slb_slideshow">
17
  {{ui.slideshow_control}}
18
- </div>
19
- </span>
20
  <div class="slb_loading">
21
  {{ui.loading}}
22
  </div>
@@ -31,11 +31,19 @@
31
  <span class="slb_group_status">
32
  {{ui.group_status}}
33
  </span>
34
- <span class="slb_data_desc">
35
  {{item.description}}
36
- </span>
37
  </div>
38
  </div>
 
 
 
 
 
 
 
 
39
  </div>
40
  </div>
41
  </div>
1
  <div class="slb_container">
2
  <div class="slb_content">
3
  {{item.content}}
4
+ <div class="slb_nav">
5
+ <span class="slb_prev">
6
  {{ui.nav_prev}}
7
+ </span>
8
+ <span class="slb_next">
9
  {{ui.nav_next}}
10
+ </span>
11
+ </div>
12
+ <div class="slb_controls">
13
+ <span class="slb_close">
14
  {{ui.close}}
15
+ </span>
16
+ <span class="slb_slideshow">
17
  {{ui.slideshow_control}}
18
+ </span>
19
+ </div>
20
  <div class="slb_loading">
21
  {{ui.loading}}
22
  </div>
31
  <span class="slb_group_status">
32
  {{ui.group_status}}
33
  </span>
34
+ <div class="slb_data_desc">
35
  {{item.description}}
36
+ </div>
37
  </div>
38
  </div>
39
+ <div class="slb_nav">
40
+ <span class="slb_prev">
41
+ {{ui.nav_prev}}
42
+ </span>
43
+ <span class="slb_next">
44
+ {{ui.nav_next}}
45
+ </span>
46
+ </div>
47
  </div>
48
  </div>
49
  </div>
themes/baseline/sass/style.scss ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //Imports
2
+
3
+ //Variables
4
+
5
+ //Mixins
6
+
7
+ %box-sizing-border-box {
8
+ box-sizing: border-box;
9
+ }
10
+
11
+ #slb_viewer_wrap {
12
+ .slb_theme_slb_baseline {
13
+ position: absolute;
14
+ top: 0;
15
+ left: 0;
16
+ width: 100%;
17
+ z-index: 99999;
18
+ text-align: center;
19
+ line-height: 0;
20
+ color:#000;
21
+ font: {
22
+ family: arial, verdana, sans-serif;
23
+ size: 12px;
24
+ }
25
+
26
+ //Reset
27
+ * {
28
+ margin: 0;
29
+ padding: 0;
30
+ line-height: 1.4em;
31
+ text-align: left;
32
+ vertical-align: baseline;
33
+ white-space: normal;
34
+ outline: none;
35
+ border: 0px;
36
+ background: none;
37
+ opacity: 1;
38
+ width: auto;
39
+ height: auto;
40
+ position: static;
41
+ float: none;
42
+ clear: none;
43
+ [dir="rtl"] & {
44
+ text-align: right;
45
+ }
46
+ }
47
+
48
+ //General
49
+
50
+ a img {
51
+ border: none;
52
+ }
53
+
54
+ .slb_viewer_layout {
55
+ @extend %box-sizing-border-box;
56
+ z-index: 2;
57
+ position: absolute;
58
+ width: 100%;
59
+ text-align: center;
60
+ }
61
+
62
+ .slb_viewer_overlay {
63
+ position: fixed;
64
+ top: 0;
65
+ left: 0;
66
+ z-index: 1;
67
+ min-height: 105%;
68
+ min-width: 100%;
69
+ background-color: #000;
70
+ }
71
+
72
+ .slb_container {
73
+ @extend %box-sizing-border-box;
74
+ position: relative;
75
+ display: inline-block;
76
+ background-color: #fff;
77
+ margin: 0 auto;
78
+ padding: 16px;
79
+ }
80
+
81
+ .slb_loading {
82
+ background: url('../images/loading.gif') center center no-repeat;
83
+ position: absolute;
84
+ left: 0%;
85
+ top: 0;
86
+ width: 100%;
87
+ height: 100%;
88
+ min-width: 31px;
89
+ min-height: 31px;
90
+ text-align: center;
91
+ line-height: 0;
92
+ display: none;
93
+ }
94
+
95
+ .slb_template_tag_ui {
96
+ cursor: pointer;
97
+ }
98
+
99
+ //UI
100
+
101
+ //Content
102
+ .slb_content {
103
+ position: relative;
104
+ }
105
+
106
+ .slb_details {
107
+ margin: 0 auto;
108
+ text-align: left;
109
+
110
+ .inner {
111
+ display: table;
112
+ width: 100%;
113
+ }
114
+ .slb_data {
115
+ display: table-caption;
116
+ }
117
+ }
118
+
119
+ .slb_template_tag_item_content > * {
120
+ width: 100%;
121
+ height: 100%;
122
+ }
123
+
124
+ /* Single */
125
+ &.item_single {
126
+ .slb_group_status,
127
+ .slb_nav,
128
+ .slb_slideshow {
129
+ display: none;
130
+ }
131
+ }
132
+ /* Loading */
133
+ &.loading {
134
+ .slb_loading {
135
+ display: block;
136
+ }
137
+
138
+ .slb_template_tag_ui {
139
+ opacity: 0;
140
+ }
141
+ }
142
+
143
+ //Media
144
+
145
+ //Small screen
146
+ @media screen and (max-width: 480px) {
147
+ %vsizing {
148
+ min-height: 100%;
149
+ min-width: 320px;
150
+ width: 100%;
151
+ }
152
+ @extend %vsizing;
153
+ .slb_viewer_layout {
154
+ @extend %vsizing;
155
+ display: block;
156
+ }
157
+ .slb_container {
158
+ @extend %vsizing;
159
+ max-width: 100%;
160
+ margin: 0;
161
+ padding: 5px;
162
+ position: absolute;
163
+ top: 0;
164
+ left: 0;
165
+ .slb_content {
166
+ img, iframe, object, .slb_inner {
167
+ max-width: 100%;
168
+ }
169
+ img {
170
+ height: auto;
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
themes/black/config.rb DELETED
@@ -1,23 +0,0 @@
1
- # Require any additional compass plugins here.
2
-
3
- # Set this to the root of your project when deployed:
4
- http_path = "/"
5
- css_dir = "css"
6
- sass_dir = "sass"
7
- images_dir = "images"
8
-
9
- # You can select your preferred output style here (can be overridden via the command line):
10
- output_style = :compressed
11
-
12
- # To enable relative paths to assets via compass helper functions. Uncomment:
13
- # relative_assets = true
14
-
15
- # To disable debugging comments that display the original location of your selectors. Uncomment:
16
- # line_comments = false
17
-
18
-
19
- # If you prefer the indented syntax, you might want to regenerate this
20
- # project again passing --syntax sass, or you can uncomment this:
21
- # preferred_syntax = :sass
22
- # and then run:
23
- # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
themes/black/css/style.css CHANGED
@@ -1 +1 @@
1
- #slb_viewer_wrap .slb_theme_slb_black a,#slb_viewer_wrap .slb_theme_slb_black a:hover{color:#fff}#slb_viewer_wrap .slb_theme_slb_black .slb_loading{background-image:url("../images/loading.gif")}#slb_viewer_wrap .slb_theme_slb_black .slb_container{background-color:#151515}#slb_viewer_wrap .slb_theme_slb_black .slb_prev a{background-image:url("../images/nav_prev.png")}#slb_viewer_wrap .slb_theme_slb_black .slb_next a{background-image:url("../images/nav_next.png")}#slb_viewer_wrap .slb_theme_slb_black .slb_data_title{color:#e3e3e3}#slb_viewer_wrap .slb_theme_slb_black .slb_data_desc{color:#cecece}#slb_viewer_wrap .slb_theme_slb_black .slb_group_status{color:#999}
1
+ #slb_viewer_wrap .slb_theme_slb_black a,#slb_viewer_wrap .slb_theme_slb_black a:hover{color:#fff}#slb_viewer_wrap .slb_theme_slb_black .slb_loading{background-image:url("../images/loading.gif")}#slb_viewer_wrap .slb_theme_slb_black .slb_container{background-color:#151515}#slb_viewer_wrap .slb_theme_slb_black .slb_content .slb_prev .slb_template_tag,[dir="rtl"] #slb_viewer_wrap .slb_theme_slb_black .slb_content .slb_next .slb_template_tag{background-image:url("../images/nav_prev.png")}#slb_viewer_wrap .slb_theme_slb_black .slb_content .slb_next .slb_template_tag,[dir="rtl"] #slb_viewer_wrap .slb_theme_slb_black .slb_content .slb_prev .slb_template_tag{background-image:url("../images/nav_next.png")}#slb_viewer_wrap .slb_theme_slb_black .slb_data_title{color:#e3e3e3}#slb_viewer_wrap .slb_theme_slb_black .slb_data_desc{color:#cecece}#slb_viewer_wrap .slb_theme_slb_black .slb_group_status{color:#999}
themes/black/sass/style.scss CHANGED
@@ -13,13 +13,16 @@
13
  .slb_container {
14
  background-color: #151515;
15
  }
16
-
17
- .slb_prev a {
18
- background-image: url('../images/nav_prev.png');
19
- }
20
-
21
- .slb_next a {
22
- background-image: url('../images/nav_next.png');
 
 
 
23
  }
24
 
25
  .slb_data_title {
13
  .slb_container {
14
  background-color: #151515;
15
  }
16
+ .slb_content {
17
+ .slb_prev .slb_template_tag,
18
+ [dir="rtl"] & .slb_next .slb_template_tag {
19
+ background-image: url('../images/nav_prev.png');
20
+ }
21
+
22
+ .slb_next .slb_template_tag,
23
+ [dir="rtl"] & .slb_prev .slb_template_tag {
24
+ background-image: url('../images/nav_next.png');
25
+ }
26
  }
27
 
28
  .slb_data_title {
themes/default/client.js DELETED
@@ -1,202 +0,0 @@
1
- (function($) {
2
- return {
3
- /**
4
- * State transition handlers
5
- */
6
- 'transition': {
7
- /**
8
- * Open event
9
- * @param View.Viewer v Viewer instance
10
- * @param jQuery.Deferred dfr Deferred instance to be resolved when animation is complete
11
- * @return jQuery.Promise Resolved when transition is complete
12
- */
13
- 'open': function(v, dfr) {
14
- var t = this;
15
- var d = v.dom_get(),
16
- l = v.get_layout().hide(),
17
- o = v.get_overlay().hide();
18
- var pos = {'top' : ''};
19
- var final = function() {
20
- //Show overlay
21
- o.fadeIn(function() {
22
- l.css(pos);
23
- dfr.resolve();
24
- });
25
- };
26
- //Clean UI
27
- d.find('.slb_content').css({width: '', height: ''}).find('.slb_template_tag').hide();
28
- d.find('.slb_details').height(0);
29
- //Show viewer DOM
30
- d.show(function() {
31
- if ( document.documentElement.clientWidth > 480 ) {
32
- /* Standard */
33
- //Center vertically
34
- var top_scr = $(document).scrollTop();
35
- pos.top = ( top_scr + $(window).height() / 2 ) - ( l.height() / 2 );
36
- if ( pos.top < top_scr ) {
37
- pos.top = top_scr;
38
- }
39
- } else {
40
- //Position at top
41
- /* Small screen */
42
- pos.top = $(document).scrollTop();
43
- }
44
- final();
45
- });
46
- return dfr.promise();
47
- },
48
- /**
49
- * Close event
50
- * @param View.Viewer v Viewer instance
51
- * @param jQuery.Deferred dfr Deferred instance to be resolved when animation is complete
52
- * @return jQuery.Promise Resolved when transition is complete
53
- */
54
- 'close': function(v, dfr) {
55
- var l = v.get_layout(),
56
- c = l.find('.slb_content');
57
- var t = this;
58
- var reset = function() {
59
- //Reset state
60
- c.width('').height('');
61
- l.css('opacity', '');
62
- dfr.resolve();
63
- }
64
- if ( v.animation_enabled() && document.documentElement.clientWidth > 480 ) { /* Standard */
65
- var lanim = {opacity: 0, top: $(document).scrollTop() + ( $(window).height() / 2 )},
66
- canim = {width: 0, height: 0};
67
- //Shrink & fade out viewer
68
- var pos = l.animate(lanim).promise();
69
- var size = ( $.isEmptyObject(canim) ) ? true : c.animate(canim).promise();
70
- $.when(pos, size).done(function() {
71
- //Fade out overlay
72
- v.get_overlay().fadeOut(function() {
73
- reset();
74
- });
75
- });
76
- } else {
77
- l.css('opacity', 0);
78
- reset();
79
- }
80
- return dfr.promise();
81
- },
82
- /**
83
- * Item loading event
84
- * @param View.Viewer v Viewer instance
85
- * @param jQuery.Deferred dfr Deferred instance to be resolved when animation is complete
86
- * @return jQuery.Promise Resolved when transition is complete
87
- */
88
- 'load': function(v, dfr) {
89
- v.get_layout().find('.slb_loading').show();
90
- if ( document.documentElement.clientWidth > 480 ) {
91
- return v.get_layout().fadeIn().promise()
92
- } else {
93
- v.get_layout().show();
94
- dfr.resolve();
95
- return dfr;
96
- }
97
- },
98
- /**
99
- * Item unloaded event
100
- * @param View.Viewer v Viewer instance
101
- * @param jQuery.Deferred dfr Deferred instance to be resolved when animation is complete
102
- * @return jQuery.Promise Resolved when transition is complete
103
- */
104
- 'unload': function(v, dfr) {
105
- var l = v.get_layout(),
106
- det = l.find('.slb_details'),
107
- cont = l.find('.slb_content .slb_template_tag');
108
- var props = {height: 0};
109
- if ( document.documentElement.clientWidth > 480 ) {
110
- //Hide details
111
- det.animate(props, 'slow');
112
- //Hide content
113
- cont.fadeOut();
114
- } else {
115
- det.css(props);
116
- cont.hide();
117
- }
118
- $.when(det.promise(), cont.promise()).done(function() {
119
- dfr.resolve();
120
- });
121
- return dfr.promise();
122
- },
123
- /**
124
- * Item loading completed event
125
- * @param View.Viewer v Viewer instance
126
- * @param jQuery.Deferred dfr Deferred instance to be resolved when animation is complete
127
- * @return jQuery.Promise Resolved when transition is complete
128
- */
129
- 'complete': function(v, dfr) {
130
- //Elements
131
- var l = v.get_layout(),
132
- loader = l.find('.slb_loading'),
133
- det = l.find('.slb_details'),
134
- det_data = det.find('.slb_data'),
135
- c = l.find('.slb_content'),
136
- c_tag = c.find('.slb_template_tag'),
137
- c_tag_cont = c.find('.slb_template_tag_item_content');
138
- //Transition
139
- if ( document.documentElement.clientWidth > 480 ) {
140
- //Resize viewer to fit item
141
- var dims = this.get_item_dimensions();
142
- //Show detail tags (container still hidden)
143
- det.find('.slb_template_tag').show();
144
- var top_scr = $(document).scrollTop();
145
- var pos = { 'top': top_scr + ( $(window).height() / 2 ) - ( this.get_dimensions().height / 2 ) };
146
- if ( pos.top < top_scr ) {
147
- pos.top = top_scr;
148
- }
149
- pos.top = pos.top || 0;
150
- //Resize container
151
- pos = l.animate(pos).promise();
152
- dims = c.animate(dims).promise();
153
- $.when(pos, dims).done(function() {
154
- //Hide loading indicator
155
- loader.fadeOut('fast', function() {
156
- //Display content
157
- c_tag_cont.fadeIn(function() {
158
- //Show UI
159
- c_tag.show();
160
- //Show details
161
- det.animate({height: det_data.outerHeight()}, 'slow').promise().done(function() {
162
- det.height('');
163
- dfr.resolve();
164
- });
165
- });
166
- });
167
- });
168
- } else {
169
- loader.hide();
170
- c_tag.show();
171
- det.height('');
172
- dfr.resolve();
173
- }
174
- return dfr.promise();
175
- }
176
- },
177
- /**
178
- * Theme offsets
179
- * Reports additional space required for theme UI
180
- */
181
- 'offset': function() {
182
- var dims = {'width': 0, 'height': 0};
183
- if ( document.documentElement.clientWidth > 480 ) {
184
- var d = this.get_viewer().get_layout().find('.slb_details');
185
- d.find('.slb_template_tag').show();
186
- $.extend(dims, {'width': 32, 'height': d.find('.slb_data').outerHeight()})
187
- }
188
- return dims;
189
- },
190
- /**
191
- * Theme margins
192
- * Reports additional margins used for positioning viewer
193
- */
194
- 'margin': function() {
195
- var m = {'height': 0, 'width': 0};
196
- if ( document.documentElement.clientWidth > 480 ) {
197
- $.extend(m, {'height': 50, 'width': 20});
198
- }
199
- return m;
200
- }
201
- };
202
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
themes/default/config.rb DELETED
@@ -1,23 +0,0 @@
1
- # Require any additional compass plugins here.
2
-
3
- # Set this to the root of your project when deployed:
4
- http_path = "/"
5
- css_dir = "css"
6
- sass_dir = "sass"
7
- images_dir = "images"
8
-
9
- # You can select your preferred output style here (can be overridden via the command line):
10
- output_style = :compressed
11
-
12
- # To enable relative paths to assets via compass helper functions. Uncomment:
13
- # relative_assets = true
14
-
15
- # To disable debugging comments that display the original location of your selectors. Uncomment:
16
- # line_comments = false
17
-
18
-
19
- # If you prefer the indented syntax, you might want to regenerate this
20
- # project again passing --syntax sass, or you can uncomment this:
21
- # preferred_syntax = :sass
22
- # and then run:
23
- # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
themes/default/css/style.css CHANGED
@@ -1 +1 @@
1
- @import url("//fonts.googleapis.com/css?family=Yanone+Kaffeesatz");#slb_viewer_wrap .slb_theme_slb_default{position:absolute;top:0;left:0;width:100%;z-index:99999;text-align:center;line-height:0;color:#000;font-family:arial, verdana, sans-serif;font-size:12px}#slb_viewer_wrap .slb_theme_slb_default *{margin:0;padding:0;line-height:1.4em;text-align:left;vertical-align:baseline;white-space:normal;outline:none;border:0px;background:none;opacity:1;width:auto;height:auto;position:static;float:none;clear:none}#slb_viewer_wrap .slb_theme_slb_default a,#slb_viewer_wrap .slb_theme_slb_default a:hover{border-bottom:none;color:#000;text-decoration:underline}#slb_viewer_wrap .slb_theme_slb_default a img{border:none}#slb_viewer_wrap .slb_theme_slb_default .slb_viewer_layout{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;z-index:2;position:absolute;top:20px;width:100%;text-align:center}#slb_viewer_wrap .slb_theme_slb_default .slb_viewer_overlay{position:fixed;top:0;left:0;z-index:1;min-height:105%;min-width:100%;background-color:#151410;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);opacity:0.8}#slb_viewer_wrap .slb_theme_slb_default .slb_container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:0 0 64px -40px #fcfcfc;-moz-box-shadow:0 0 64px -40px #fcfcfc;box-shadow:0 0 64px -40px #fcfcfc;position:relative;display:inline-block;background-color:#fff;margin:0 auto;border-radius:5px;padding:16px}#slb_viewer_wrap .slb_theme_slb_default .slb_loading{background:url("../images/loading.gif") center center no-repeat;position:absolute;left:0%;top:0;width:100%;height:100%;min-width:31px;min-height:31px;text-align:center;text-indent:-2000em;line-height:0;display:none}#slb_viewer_wrap .slb_theme_slb_default .slb_template_tag_ui{cursor:pointer;transition:opacity .5s}#slb_viewer_wrap .slb_theme_slb_default .slb_controls{position:absolute;top:8px;right:8px;width:75%;text-align:right}#slb_viewer_wrap .slb_theme_slb_default .slb_controls .slb_template_tag_ui{width:25px;height:25px;float:right;margin-left:2px;text-indent:-2000em;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=50);opacity:0.5}#slb_viewer_wrap .slb_theme_slb_default .slb_controls .slb_template_tag_ui:hover{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);opacity:0.8}#slb_viewer_wrap .slb_theme_slb_default .slb_controls .slb_slideshow .slb_template_tag{background:url("../images/ui_slideshow_play.png") 0 0 no-repeat}#slb_viewer_wrap .slb_theme_slb_default .slb_controls .slb_close .slb_template_tag{background:url("../images/ui_close.png") 0 0 no-repeat}#slb_viewer_wrap .slb_theme_slb_default.slideshow_active .slb_controls .slb_slideshow .slb_template_tag{background:url("../images/ui_slideshow_pause.png") 0 0 no-repeat}#slb_viewer_wrap .slb_theme_slb_default .slb_prev .slb_template_tag,#slb_viewer_wrap .slb_theme_slb_default .slb_next .slb_template_tag{position:absolute;top:20%;height:71%;width:45%;margin-left:4px;background-repeat:no-repeat;text-indent:-2000em;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=50);opacity:0.5}#slb_viewer_wrap .slb_theme_slb_default .slb_prev .slb_template_tag{background-image:url("../images/nav_prev.png");background-position:left 45%}#slb_viewer_wrap .slb_theme_slb_default .slb_next .slb_template_tag{right:4px;background-image:url("../images/nav_next.png");background-position:right 45%}#slb_viewer_wrap .slb_theme_slb_default .slb_prev .slb_template_tag:hover,#slb_viewer_wrap .slb_theme_slb_default .slb_next .slb_template_tag:hover{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);opacity:1}#slb_viewer_wrap .slb_theme_slb_default .slb_content{min-width:200px;min-height:200px;position:relative}#slb_viewer_wrap .slb_theme_slb_default .slb_details{margin:0 auto;line-height:1.4em;text-align:left;overflow:hidden;position:relative}#slb_viewer_wrap .slb_theme_slb_default .slb_details .inner{display:table;width:100%}#slb_viewer_wrap .slb_theme_slb_default .slb_details .slb_data{display:table-caption;caption-side:bottom}#slb_viewer_wrap .slb_theme_slb_default .slb_template_tag_item_content *{width:100%;height:100%}#slb_viewer_wrap .slb_theme_slb_default .slb_data_title,#slb_viewer_wrap .slb_theme_slb_default .slb_group_status{font-family:'Yanone Kaffeesatz', sans-serif;font-size:23px}#slb_viewer_wrap .slb_theme_slb_default .slb_group_status{color:#777;font-style:italic;font-size:18.4px}#slb_viewer_wrap .slb_theme_slb_default .slb_data_desc{display:block;margin-top:0.5em}#slb_viewer_wrap .slb_theme_slb_default.item_single .slb_group_status,#slb_viewer_wrap .slb_theme_slb_default.item_single .slb_nav,#slb_viewer_wrap .slb_theme_slb_default.item_single .slb_slideshow{display:none}#slb_viewer_wrap .slb_theme_slb_default.loading .slb_loading{display:block}#slb_viewer_wrap .slb_theme_slb_default.loading .slb_template_tag_ui{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);opacity:0}@media screen and (max-width: 480px){#slb_viewer_wrap .slb_theme_slb_default{min-height:100%;min-width:320px;width:100%}#slb_viewer_wrap .slb_theme_slb_default .slb_viewer_layout{min-height:100%;min-width:320px;width:100%;display:block}#slb_viewer_wrap .slb_theme_slb_default .slb_container{min-height:100%;min-width:320px;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;max-width:100%;border-radius:0;margin:0;padding:5px;position:absolute;top:0;left:0}#slb_viewer_wrap .slb_theme_slb_default .slb_container .slb_content img,#slb_viewer_wrap .slb_theme_slb_default .slb_container .slb_content iframe,#slb_viewer_wrap .slb_theme_slb_default .slb_container .slb_content object,#slb_viewer_wrap .slb_theme_slb_default .slb_container .slb_content .slb_inner{max-width:100%}#slb_viewer_wrap .slb_theme_slb_default .slb_container .slb_content img{height:auto}#slb_viewer_wrap .slb_theme_slb_default .slb_controls{top:3px;right:3px}#slb_viewer_wrap .slb_theme_slb_default .slb_prev .slb_template_tag,#slb_viewer_wrap .slb_theme_slb_default .slb_next .slb_template_tag{top:17%;height:79%}}
1
+ @font-face{font-family:'Yanone Kaffeesatz';font-style:normal;font-weight:400;src:url("../fonts/yanone-kaffeesatz-v9-latin-regular.eot");src:local("Yanone Kaffeesatz Regular"),local("YanoneKaffeesatz-Regular"),url("../fonts/yanone-kaffeesatz-v9-latin-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/yanone-kaffeesatz-v9-latin-regular.woff2") format("woff2"),url("../fonts/yanone-kaffeesatz-v9-latin-regular.woff") format("woff"),url("../fonts/yanone-kaffeesatz-v9-latin-regular.ttf") format("truetype"),url("../fonts/yanone-kaffeesatz-v9-latin-regular.svg#YanoneKaffeesatz") format("svg")}#slb_viewer_wrap .slb_theme_slb_default .slb_loading,#slb_viewer_wrap .slb_theme_slb_default .slb_controls .slb_template_tag_ui,#slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_prev .slb_template_tag,#slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_next .slb_template_tag{text-indent:100%;white-space:nowrap;overflow:hidden}#slb_viewer_wrap .slb_theme_slb_default a,#slb_viewer_wrap .slb_theme_slb_default a:hover{border-bottom:none;color:#000;text-decoration:underline}#slb_viewer_wrap .slb_theme_slb_default .slb_viewer_layout{top:20px}#slb_viewer_wrap .slb_theme_slb_default .slb_container{box-shadow:0 0 64px -40px #fcfcfc;border-radius:5px}#slb_viewer_wrap .slb_theme_slb_default .slb_template_tag_ui{transition:opacity .5s}#slb_viewer_wrap .slb_theme_slb_default .slb_controls{position:absolute;top:8px;right:8px;width:75%;text-align:right}[dir="rtl"] #slb_viewer_wrap .slb_theme_slb_default .slb_controls{right:inherit;left:0px}#slb_viewer_wrap .slb_theme_slb_default .slb_controls .slb_template_tag_ui{width:25px;height:25px;float:right;margin-left:2px;opacity:0.5}[dir="rtl"] #slb_viewer_wrap .slb_theme_slb_default .slb_controls .slb_template_tag_ui{float:left}#slb_viewer_wrap .slb_theme_slb_default .slb_controls .slb_template_tag_ui:hover{opacity:0.8}#slb_viewer_wrap .slb_theme_slb_default .slb_controls .slb_slideshow .slb_template_tag{background:url("../images/ui_slideshow_play.png") 0 0 no-repeat}#slb_viewer_wrap .slb_theme_slb_default .slb_controls .slb_close .slb_template_tag{background:url("../images/ui_close.png") 0 0 no-repeat}#slb_viewer_wrap .slb_theme_slb_default.slideshow_active .slb_controls .slb_slideshow .slb_template_tag{background:url("../images/ui_slideshow_pause.png") 0 0 no-repeat}#slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_prev .slb_template_tag,#slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_next .slb_template_tag{position:absolute;top:20%;height:71%;width:45%;min-width:25px;min-height:33px;background-repeat:no-repeat;opacity:0.5}#slb_viewer_wrap .slb_theme_slb_default .slb_content{min-height:58px;min-width:50px}#slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_prev .slb_template_tag,[dir="rtl"] #slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_next .slb_template_tag{left:4px;right:inherit;background-image:url("../images/nav_prev.png");background-position:left 45%}#slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_next .slb_template_tag,[dir="rtl"] #slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_prev .slb_template_tag{right:4px;left:inherit;background-image:url("../images/nav_next.png");background-position:right 45%}#slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_prev .slb_template_tag:hover,#slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_next .slb_template_tag:hover{opacity:1}#slb_viewer_wrap .slb_theme_slb_default .slb_details{line-height:1.4em;overflow:hidden;position:relative}#slb_viewer_wrap .slb_theme_slb_default .slb_details .slb_data{caption-side:bottom}#slb_viewer_wrap .slb_theme_slb_default .slb_details .slb_nav{display:none}#slb_viewer_wrap .slb_theme_slb_default .slb_data_title,#slb_viewer_wrap .slb_theme_slb_default .slb_group_status{font-family:'Yanone Kaffeesatz', arial, sans-serif;font-size:23px;margin-right:.2em;display:inline-block}[dir="rtl"] #slb_viewer_wrap .slb_theme_slb_default .slb_data_title,[dir="rtl"] #slb_viewer_wrap .slb_theme_slb_default .slb_group_status{margin-left:.2em;margin-right:0px}#slb_viewer_wrap .slb_theme_slb_default .slb_group_status{color:#777;font-style:italic;font-size:18.4px}#slb_viewer_wrap .slb_theme_slb_default .slb_data_desc{display:block;margin-top:0.5em}@media screen and (max-width: 480px){#slb_viewer_wrap .slb_theme_slb_default .slb_container{box-shadow:none;border-radius:0}#slb_viewer_wrap .slb_theme_slb_default .slb_controls{top:3px;right:3px}#slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_prev .slb_template_tag,#slb_viewer_wrap .slb_theme_slb_default .slb_content .slb_next .slb_template_tag{top:17%;height:79%}}
themes/default/fonts/OFL.txt ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) <dates>, <Copyright Holder> (<URL|email>),
2
+ with Reserved Font Name <Reserved Font Name>.
3
+ Copyright (c) <dates>, <additional Copyright Holder> (<URL|email>),
4
+ with Reserved Font Name <additional Reserved Font Name>.
5
+ Copyright (c) <dates>, <additional Copyright Holder> (<URL|email>).
6
+
7
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
8
+ This license is copied below, and is also available with a FAQ at:
9
+ http://scripts.sil.org/OFL
10
+
11
+
12
+ -----------------------------------------------------------
13
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
14
+ -----------------------------------------------------------
15
+
16
+ PREAMBLE
17
+ The goals of the Open Font License (OFL) are to stimulate worldwide
18
+ development of collaborative font projects, to support the font creation
19
+ efforts of academic and linguistic communities, and to provide a free and
20
+ open framework in which fonts may be shared and improved in partnership
21
+ with others.
22
+
23
+ The OFL allows the licensed fonts to be used, studied, modified and
24
+ redistributed freely as long as they are not sold by themselves. The
25
+ fonts, including any derivative works, can be bundled, embedded,
26
+ redistributed and/or sold with any software provided that any reserved
27
+ names are not used by derivative works. The fonts and derivatives,
28
+ however, cannot be released under any other type of license. The
29
+ requirement for fonts to remain under this license does not apply
30
+ to any document created using the fonts or their derivatives.
31
+
32
+ DEFINITIONS
33
+ "Font Software" refers to the set of files released by the Copyright
34
+ Holder(s) under this license and clearly marked as such. This may
35
+ include source files, build scripts and documentation.
36
+
37
+ "Reserved Font Name" refers to any names specified as such after the
38
+ copyright statement(s).
39
+
40
+ "Original Version" refers to the collection of Font Software components as
41
+ distributed by the Copyright Holder(s).
42
+
43
+ "Modified Version" refers to any derivative made by adding to, deleting,
44
+ or substituting -- in part or in whole -- any of the components of the
45
+ Original Version, by changing formats or by porting the Font Software to a
46
+ new environment.
47
+
48
+ "Author" refers to any designer, engineer, programmer, technical
49
+ writer or other person who contributed to the Font Software.
50
+
51
+ PERMISSION & CONDITIONS
52
+ Permission is hereby granted, free of charge, to any person obtaining
53
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
54
+ redistribute, and sell modified and unmodified copies of the Font
55
+ Software, subject to the following conditions:
56
+
57
+ 1) Neither the Font Software nor any of its individual components,
58
+ in Original or Modified Versions, may be sold by itself.
59
+
60
+ 2) Original or Modified Versions of the Font Software may be bundled,
61
+ redistributed and/or sold with any software, provided that each copy
62
+ contains the above copyright notice and this license. These can be
63
+ included either as stand-alone text files, human-readable headers or
64
+ in the appropriate machine-readable metadata fields within text or
65
+ binary files as long as those fields can be easily viewed by the user.
66
+
67
+ 3) No Modified Version of the Font Software may use the Reserved Font
68
+ Name(s) unless explicit written permission is granted by the corresponding
69
+ Copyright Holder. This restriction only applies to the primary font name as
70
+ presented to the users.
71
+
72
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
73
+ Software shall not be used to promote, endorse or advertise any
74
+ Modified Version, except to acknowledge the contribution(s) of the
75
+ Copyright Holder(s) and the Author(s) or with their explicit written
76
+ permission.
77
+
78
+ 5) The Font Software, modified or unmodified, in part or in whole,
79
+ must be distributed entirely under this license, and must not be
80
+ distributed under any other license. The requirement for fonts to
81
+ remain under this license does not apply to any document created
82
+ using the Font Software.
83
+
84
+ TERMINATION
85
+ This license becomes null and void if any of the above conditions are
86
+ not met.
87
+
88
+ DISCLAIMER
89
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
90
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
91
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
92
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
93
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
94
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
95
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
96
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
97
+ OTHER DEALINGS IN THE FONT SOFTWARE.
themes/default/fonts/yanone-kaffeesatz-v9-latin-regular.eot ADDED
Binary file
themes/default/fonts/yanone-kaffeesatz-v9-latin-regular.svg ADDED
@@ -0,0 +1,407 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <defs >
5
+ <font id="YanoneKaffeesatz" horiz-adv-x="384" ><font-face
6
+ font-family="Yanone Kaffeesatz"
7
+ units-per-em="1000"
8
+ panose-1="0 0 5 0 0 0 0 0 0 0"
9
+ ascent="957"
10
+ descent="-200"
11
+ alphabetic="0" />
12
+ <glyph unicode=" " glyph-name="space" horiz-adv-x="158" />
13
+ <glyph unicode="!" glyph-name="exclam" horiz-adv-x="195" d="M45 683Q45 694 53 699T83 705T155 706L139 251Q138 233 131 226T109 216T59 213L45 683ZM68 -7T55 7T42 50Q42 74 57 91T97 108Q153 108 153 50Q153 26 138 10T97 -7Q68 -7 55 7Z" />
14
+ <glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="263" d="M42 607T37 667T31 760Q31 781 34 782Q41 786 65 786Q95 786 110 783Q110 740 103 665T89 586Q82 585 67 584T45 583Q42 607 37 667ZM166 590T160 659T153 761Q153 781 156 782Q163 786 187
15
+ 786Q218 786 233 783Q233 742 225 666T212 586Q205 585 190 584T168 583Q166 590 160 659Z" />
16
+ <glyph unicode="#" glyph-name="numbersign" horiz-adv-x="418" d="M323 254H390Q390 213 388 202T374 190H320L316 123Q315 112 302 110T245 107L249 190H154L150 123Q149 112 136 110T79 107L83 190H28Q28 232 30 243T44 254H87L92 360H28Q28 402 30 413T44
17
+ 424H96L99 487Q100 498 114 500T170 503L166 424H262L265 487Q266 498 280 500T336 503L332 424H390Q390 383 388 372T374 360H328L323 254ZM157 254H253L258 360H162L157 254Z" />
18
+ <glyph unicode="$" glyph-name="dollar" horiz-adv-x="418" d="M363 20T237 -4V-91Q237 -110 221 -116T164 -122V-7Q128 -5 103 2T66 19T53 37Q53 46 59 68T74 102Q111 72 181 72Q228 72 251 94T274 158Q274 186 262 208T219 264L107 377Q53 434 53 488Q53 542
19
+ 86 574T181 615V704Q181 724 198 730T254 737V616Q304 613 330 602T357 581Q357 569 352 553T337 525Q326 533 299 539T239 546Q142 546 142 489Q142 469 152 452T185 409L306 288Q336 258 349 227T363 157Q363 20 237 -4Z" />
20
+ <glyph unicode="%" glyph-name="percent" horiz-adv-x="692" d="M162 -7T150 -3T127 8T116 23L520 709Q523 708 534 703T555 691T565 674Q565 667 397 382T166 -8Q162 -7 150 -3ZM132 339T104 353T60 408T45 525Q45 618 81 663T186 708Q233 708 261 692T304 636T319
21
+ 526Q319 339 180 339Q132 339 104 353ZM212 402T228 430T245 525Q245 574 239 600T220 636T186 646Q154 646 138 619T122 523Q122 473 128 447T148 412T182 402Q212 402 228 430ZM460 -9T431 5T387 60T372 177Q372 270 408 315T514 360Q561 360 589 344T632 288T647
22
+ 178Q647 -9 508 -9Q460 -9 431 5ZM539 52T555 80T572 175Q572 224 566 250T548 286T513 296Q481 296 465 269T448 173Q448 123 454 97T474 62T509 52Q539 52 555 80Z" />
23
+ <glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="502" d="M458 70T488 55Q485 31 468 11T435 -10Q420 -10 362 54Q306 -10 212 -10Q27 -10 27 170Q27 229 59 278T139 358Q61 494 61 568Q61 636 104 671T228 706Q293 706 335 692T377 659Q377 644 372
24
+ 627T357 600Q330 619 301 627T237 635Q193 635 170 620T146 558Q146 508 205 405T343 201Q351 241 351 291Q351 423 291 496Q332 504 405 504Q447 504 483 501Q483 499 484 490T485 472Q485 453 477 444T443 435L394 436Q411 405 420 374T429 281Q429 194 404 129Q458
25
+ 70 488 55ZM272 66T309 115Q242 196 174 300Q110 254 110 168Q110 124 133 95T207 66Q272 66 309 115Z" />
26
+ <glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="141" d="M42 607T37 667T31 760Q31 781 34 782Q41 786 65 786Q95 786 110 783Q110 740 103 665T89 586Q82 585 67 584T45 583Q42 607 37 667Z" />
27
+ <glyph unicode="(" glyph-name="parenleft" horiz-adv-x="292" d="M212 -196T172 -135T99 43T65 320Q65 470 98 578T172 744T226 803Q246 803 255 792T271 752Q218 680 183 573T147 318Q147 211 166 119T213 -38T271 -136Q265 -166 256 -181T227 -196Q212 -196 172 -135Z" />
28
+ <glyph unicode=")" glyph-name="parenright" horiz-adv-x="292" d="M47 -196T37 -181T21 -136Q71 -70 108 44T145 318Q145 460 110 573T21 752Q28 781 37 792T66 803Q79 803 119 746T193 581T227 320Q227 168 194 52T120 -130T65 -196Q47 -196 37 -181Z" />
29
+ <glyph unicode="*" glyph-name="asterisk" horiz-adv-x="470" d="M199 667Q198 678 206 683T229 688Q256 688 278 677L258 519Q256 510 252 506T239 501Q231 501 221 504L199 667ZM269 469T269 478Q269 489 282 495L431 575Q441 566 447 551T454 521Q454 498 439
30
+ 494L280 455Q269 469 269 478ZM16 503Q15 532 29 551T60 570Q63 570 69 568L199 487Q200 470 194 463T175 459L16 503ZM249 404Q246 410 246 414Q246 424 271 434L386 299Q390 294 390 288Q390 274 370 261T327 245L249 404ZM130 250T120 250Q107 250 92 266T66
31
+ 300L187 423Q195 431 202 431Q212 431 224 415L136 259Q130 250 120 250Z" />
32
+ <glyph unicode="+" glyph-name="plus" horiz-adv-x="418" d="M384 317Q384 275 382 261T368 247H245V123Q245 112 231 110T174 107V247H34Q34 290 36 303T51 317H174V441Q174 452 188 454T245 457V317H384Z" />
33
+ <glyph unicode="," glyph-name="comma" horiz-adv-x="183" d="M43 -103T38 -80Q54 -53 59 -36T67 5Q43 21 43 54Q43 77 57 91T94 106Q148 106 148 47Q148 13 133 -21T99 -79T70 -103Q43 -103 38 -80Z" />
34
+ <glyph unicode="-" glyph-name="hyphen" horiz-adv-x="278" d="M37 290T39 303T54 317H241Q241 275 239 261T225 247H37Q37 290 39 303Z" />
35
+ <glyph unicode="." glyph-name="period" horiz-adv-x="183" d="M63 -6T50 8T37 51Q37 75 52 91T93 108Q148 108 148 51Q148 26 133 10T93 -6Q63 -6 50 8Z" />
36
+ <glyph unicode="/" glyph-name="slash" horiz-adv-x="334" d="M235 743Q238 754 243 758T265 763T324 765L99 0Q96 -10 91 -14T69 -19T10 -20L235 743Z" />
37
+ <glyph unicode="0" glyph-name="zero" horiz-adv-x="418" d="M107 -9T68 65T29 296Q29 448 72 524T211 600Q306 600 347 532T389 320Q389 151 348 71T206 -9Q107 -9 68 65ZM260 64T282 124T305 321Q305 428 285 478T215 529Q160 529 138 469T116 290Q116 206 124
38
+ 157T153 86T208 64Q260 64 282 124Z" />
39
+ <glyph unicode="1" glyph-name="one" horiz-adv-x="418" d="M28 32T28 56Q28 67 72 71T173 76L184 517Q163 516 123 509T70 499Q58 519 58 552Q58 558 97 569T183 590T247 600Q263 600 270 595L257 76H331Q353 76 368 77T389 79Q389 32 387 16T373 0H30Q28 32 28 56Z" />
40
+ <glyph unicode="2" glyph-name="two" horiz-adv-x="418" d="M29 11T29 25Q29 45 33 56T54 96Q76 133 102 167T167 247Q227 317 255 362T283 454Q283 523 206 523Q166 523 125 513T59 488Q49 511 49 547Q49 559 73 571T139 592T226 600Q309 600 341 567T374 464Q374
41
+ 403 342 349T243 217Q171 131 136 76H271Q304 76 341 79T389 83Q389 34 386 17T372 0H33Q29 11 29 25Z" />
42
+ <glyph unicode="3" glyph-name="three" horiz-adv-x="418" d="M99 -100T72 -86T44 -51Q44 -27 49 -11T55 9Q90 -23 153 -23Q218 -23 254 25T291 150Q291 209 248 231T132 254L130 305Q194 336 228 376T263 458Q263 486 248 504T191 523Q111 523 53 487Q43 507
43
+ 43 546Q43 559 64 571T125 592T216 600Q290 600 322 568T355 482Q355 380 245 301Q305 295 341 259T378 152Q378 71 349 15T269 -71T152 -100Q99 -100 72 -86Z" />
44
+ <glyph unicode="4" glyph-name="four" horiz-adv-x="418" d="M235 131H54Q28 157 28 202Q28 227 65 311T152 478T237 592Q254 606 273 606Q297 606 319 595L316 206Q344 206 364 207T390 209Q391 208 392 202T394 186Q394 162 388 147T367 131H316V26Q316 12 313
45
+ 7T295 1T235 0V131ZM235 206L247 495Q208 450 161 361T98 206H235Z" />
46
+ <glyph unicode="5" glyph-name="five" horiz-adv-x="418" d="M111 -100T85 -93T47 -77T34 -59Q34 -19 43 7Q78 -25 141 -25Q218 -25 256 33T294 178Q294 259 255 289T139 319Q100 319 42 306L71 600H377Q377 568 375 554T363 534T335 528Q291 528 229 529T140
47
+ 532L124 381Q159 386 187 386Q380 386 380 194Q380 56 322 -22T148 -100Q111 -100 85 -93Z" />
48
+ <glyph unicode="6" glyph-name="six" horiz-adv-x="418" d="M313 439T352 392T391 245Q391 -8 202 -8Q128 -8 91 29T43 132T31 308Q31 702 259 702Q306 702 336 694T367 668Q367 636 359 611Q317 626 265 626Q119 626 108 367Q132 403 165 421T246 439Q313 439
49
+ 352 392ZM255 64T283 110T312 228Q312 295 289 330T223 365Q157 365 108 290V284Q106 169 129 117T205 64Q255 64 283 110Z" />
50
+ <glyph unicode="7" glyph-name="seven" horiz-adv-x="418" d="M115 -88T98 -84T80 -66Q93 35 128 149T208 365T294 531Q220 527 67 527Q45 527 45 600H405Q303 403 242 238T163 -88Q115 -88 98 -84Z" />
51
+ <glyph unicode="8" glyph-name="eight" horiz-adv-x="418" d="M362 293T377 259T392 182Q392 99 347 46T218 -8Q123 -8 78 45T32 183Q32 245 63 295T146 376Q101 407 81 429Q60 448 50 475T39 534Q39 617 87 661T210 706Q387 706 387 544Q387 485 358 444T279
52
+ 369Q317 339 333 323Q362 293 377 259ZM176 630T148 608T119 539Q119 489 159 457Q180 438 219 411Q313 469 313 541Q313 585 286 607T220 630Q176 630 148 608ZM259 69T286 100T313 183Q313 220 302 243T268 286Q248 307 210 333Q163 309 135 272T107 188Q107
53
+ 133 135 101T213 69Q259 69 286 100Z" />
54
+ <glyph unicode="9" glyph-name="nine" horiz-adv-x="418" d="M288 609T324 580T373 486T389 297Q387 92 327 -6T138 -104Q98 -104 74 -96T49 -71Q49 -58 51 -43T56 -17Q89 -27 129 -27Q220 -27 261 39T310 229Q286 193 254 175T174 157Q109 157 69 209T28 358Q28
55
+ 485 75 547T217 609Q288 609 324 580ZM263 231T312 306V310Q312 392 305 439T278 512T218 537Q165 537 136 495T107 368Q107 302 131 267T198 231Q263 231 312 306Z" />
56
+ <glyph unicode=":" glyph-name="colon" horiz-adv-x="183" d="M63 329T51 343T38 386Q38 410 53 426T93 443Q149 443 149 386Q149 361 134 345T93 329Q63 329 51 343ZM63 -6T51 8T38 51Q38 75 53 91T93 108Q149 108 149 51Q149 26 134 10T93 -6Q63 -6 51 8Z" />
57
+ <glyph unicode=";" glyph-name="semicolon" horiz-adv-x="183" d="M61 329T49 343T36 386Q36 410 51 426T91 443Q147 443 147 386Q147 361 132 345T91 329Q61 329 49 343ZM39 -103T34 -80Q50 -54 55 -37T63 5Q39 21 39 54Q39 77 53 91T90 106Q144 106 144 47Q144
58
+ 13 129 -21T95 -79T66 -103Q39 -103 34 -80Z" />
59
+ <glyph unicode="&lt;" glyph-name="less" horiz-adv-x="400" d="M318 96T297 103L52 211Q39 234 39 258Q39 289 65 302L329 430Q342 418 349 404T357 377Q357 359 343 353L144 255L360 159Q359 134 348 115T324 96Q318 96 297 103Z" />
60
+ <glyph unicode="=" glyph-name="equal" horiz-adv-x="418" d="M34 382T36 396T51 410H384Q384 367 382 353T368 339H34Q34 382 36 396ZM34 197T36 210T51 224H384Q384 182 382 168T368 154H34Q34 197 36 210Z" />
61
+ <glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="400" d="M62 97T55 112T46 139Q46 157 60 164L260 261L43 358Q44 383 56 401T80 420Q90 420 107 413L351 305Q364 285 364 258Q364 227 338 214L74 85Q62 97 55 112Z" />
62
+ <glyph unicode="?" glyph-name="question" horiz-adv-x="295" d="M92 210T89 219T82 244T79 278Q79 319 97 351Q109 374 124 396T143 424Q171 465 184 491T197 552Q197 629 115 629Q66 629 31 603Q19 639 19 667Q19 684 58 697T144 710Q209 710 246 675T283 570Q283
63
+ 512 264 473T209 384Q182 350 172 330Q160 309 160 283Q160 271 165 243Q167 234 168 227T166 217Q156 206 120 206Q109 206 93 208Q92 210 89 219ZM100 -6T88 8T75 51Q75 75 90 91T130 108Q186 108 186 51Q186 26 171 10T130 -6Q100 -6 88 8Z" />
64
+ <glyph unicode="@" glyph-name="at" horiz-adv-x="738" d="M442 -112T496 -101T602 -68Q623 -100 623 -126Q623 -141 592 -156T505 -180T375 -190Q268 -190 199 -159T89 -29T47 264Q47 479 139 581T394 683Q545 683 619 608T693 359Q693 145 644 74T533 2Q498
65
+ 2 471 25Q459 15 429 8T358 1Q319 1 293 12T252 57T237 151Q237 318 365 318Q403 318 423 307Q423 313 424 330T425 368Q426 401 411 413T364 426Q333 426 310 421T264 402Q256 419 256 448Q256 459 265 468Q281 479 312 486T391 494Q448 494 475 468T501 378L496
66
+ 177Q494 104 491 78Q508 72 524 72Q545 72 564 98T597 184T610 338Q610 441 590 500T523 585T393 611Q303 611 246 578T159 466T129 253Q129 106 155 27T233 -82T369 -112Q442 -112 496 -101ZM423 258Q402 261 388 261Q352 261 333 236T314 147Q314 98 327 80T368
67
+ 62Q390 62 404 74T419 114L423 258Z" />
68
+ <glyph unicode="A" glyph-name="A" horiz-adv-x="437" d="M433 6T426 2T396 -2Q369 -2 348 0L306 178H132L93 14Q90 5 84 2T55 -2Q34 -2 5 1L184 689Q187 703 214 703Q237 703 252 700L431 15Q433 6 426 2ZM288 251L254 395Q225 517 219 588Q208 499 183 395L149
69
+ 251H288Z" />
70
+ <glyph unicode="B" glyph-name="B" horiz-adv-x="377" d="M44 681Q44 691 49 695T70 700H169Q337 700 337 546Q337 470 308 430T242 384Q289 381 324 350T360 238Q360 118 307 59T170 0H44V681ZM175 414Q209 414 229 443T249 530Q249 629 175 629H125V414H175ZM171
71
+ 71Q212 71 238 110T265 231Q265 280 254 305T223 337T172 345H125V71H171Z" />
72
+ <glyph unicode="C" glyph-name="C" horiz-adv-x="341" d="M142 -10T107 13T51 109T30 321Q30 480 59 566T130 681T227 710Q277 710 299 694T321 655Q321 628 308 606Q277 631 243 631Q206 631 178 608T133 517T115 325Q115 227 127 173T161 99T215 79Q243 79 268
73
+ 88T308 111Q320 85 320 62Q320 32 285 11T196 -10Q142 -10 107 13Z" />
74
+ <glyph unicode="D" glyph-name="D" horiz-adv-x="388" d="M44 681Q44 691 50 695T71 700H169Q237 700 279 672T342 575T364 386Q364 242 342 158T276 37T169 0H44V681ZM171 77Q201 77 223 100T259 193T273 394Q273 485 262 535T229 606T171 626H125V77H171Z" />
75
+ <glyph unicode="E" glyph-name="E" horiz-adv-x="320" d="M125 73H297Q297 44 297 26T293 4T281 0H45V684Q45 688 48 694T61 700H301Q301 672 301 654T297 632T286 627H125V418H276Q276 391 276 373T272 350T261 344H125V73Z" />
76
+ <glyph unicode="F" glyph-name="F" horiz-adv-x="313" d="M301 700Q301 672 301 656T297 635T286 631H125V418H276Q276 391 276 373T272 350T261 344H125V16Q125 11 123 6T110 0H45V684Q45 688 48 694T61 700H301Z" />
77
+ <glyph unicode="G" glyph-name="G" horiz-adv-x="386" d="M148 -10T108 18T49 121T30 334Q30 485 55 567T127 679T245 710Q293 710 322 695T352 659Q352 635 341 607Q327 618 306 624T261 631Q208 631 177 605T131 515T116 340Q116 225 128 167T162 89T220 69Q244
78
+ 69 259 77T280 94V343H205V394Q205 410 224 410H355V35Q301 -10 218 -10Q148 -10 108 18Z" />
79
+ <glyph unicode="H" glyph-name="H" horiz-adv-x="410" d="M365 7T362 4T339 1T285 0V339H125V16Q125 7 122 4T98 1T44 0V684Q44 693 47 696T71 699T125 700V415H285V684Q285 693 288 696T311 699T365 700V16Q365 7 362 4Z" />
80
+ <glyph unicode="I" glyph-name="I" horiz-adv-x="169" d="M44 663Q44 683 48 690T66 698T126 700V16Q126 7 122 4T98 1T44 0V663Z" />
81
+ <glyph unicode="J" glyph-name="J" horiz-adv-x="300" d="M63 -9T39 8T14 50Q14 64 17 73T26 96Q41 82 63 73T109 63Q138 63 151 76T169 114T174 183V663Q174 684 177 690T195 698T256 700V165Q256 82 226 37T119 -9Q63 -9 39 8Z" />
82
+ <glyph unicode="K" glyph-name="K" horiz-adv-x="389" d="M371 701T371 686Q371 682 368 677L206 398Q326 307 367 18Q368 6 362 2T334 -2Q308 -2 278 1Q254 128 229 201T164 318Q151 333 124 345V16Q124 7 121 4T97 1T42 0V684Q42 693 45 696T69 699T124 700V398L284
83
+ 695Q313 701 335 701Q371 701 371 686Z" />
84
+ <glyph unicode="L" glyph-name="L" horiz-adv-x="314" d="M44 677Q44 690 48 694T66 699T125 700V76H297V16Q297 7 293 4T281 0H44V677Z" />
85
+ <glyph unicode="M" glyph-name="M" horiz-adv-x="489" d="M445 700V16Q445 7 442 4T418 1T363 0L366 245Q368 310 375 410T385 546L368 486Q329 342 319 315L266 161Q263 153 255 153H220L168 315Q155 354 104 541L103 540Q106 509 113 412T121 249L123 16Q123
86
+ 7 119 4T95 1T44 0V684Q44 694 48 697T60 700H118L214 396Q225 362 234 330T246 285Q249 297 257 329T275 393L364 684Q367 693 370 696T380 700H445Z" />
87
+ <glyph unicode="N" glyph-name="N" horiz-adv-x="444" d="M320 693T324 696T348 699T401 700V16Q401 0 384 0H324L209 275Q185 339 114 560Q118 517 121 416T124 259V16Q124 7 121 4T97 1T44 0V684Q44 700 60 700H121L234 430Q247 395 308 211Q312 199 330 146V149Q325
88
+ 188 323 290T320 448V684Q320 693 324 696Z" />
89
+ <glyph unicode="O" glyph-name="O" horiz-adv-x="429" d="M143 -10T105 21T48 131T30 354Q30 486 50 564T113 676T222 710Q288 710 326 678T381 569T399 357Q399 220 380 140T319 25T211 -10Q143 -10 105 21ZM251 73T272 101T302 190T312 355Q312 459 304 517T276
90
+ 601T220 627Q184 627 163 599T131 507T121 334Q121 238 129 182T158 99T214 73Q251 73 272 101Z" />
91
+ <glyph unicode="P" glyph-name="P" horiz-adv-x="361" d="M254 700T298 653T343 503Q343 387 293 336T160 284H124V16Q124 7 121 4T97 1T44 0V684Q44 693 47 696T60 700H159Q254 700 298 653ZM212 359T233 394T254 512Q254 569 236 599T174 629H124V359H168Q212
92
+ 359 233 394Z" />
93
+ <glyph unicode="Q" glyph-name="Q" horiz-adv-x="429" d="M404 -61T407 -74T410 -97Q410 -118 382 -127T320 -137Q269 -137 239 -107T190 -9Q131 -5 97 29T46 141T30 354Q30 486 50 564T113 676T222 710Q288 710 326 678T381 569T399 357Q399 194 371 111T282
94
+ 2Q288 -37 300 -53T335 -70Q372 -70 398 -53Q404 -61 407 -74ZM121 238T129 182T158 99T214 73Q251 73 272 101T302 190T312 355Q312 459 304 517T276 601T220 627Q184 627 163 599T131 507T121 334Q121 238 129 182Z" />
95
+ <glyph unicode="R" glyph-name="R" horiz-adv-x="372" d="M288 264T315 192T350 18Q350 6 343 2T309 -2Q298 -2 262 0Q255 110 231 179T180 285Q173 284 160 284H124V16Q124 7 121 4T97 1T44 0V684Q44 688 47 694T60 700H159Q254 700 298 653T343 503Q343 356
96
+ 256 306Q288 264 315 192ZM168 359Q212 359 233 393T254 506Q254 567 236 598T174 629H124V359H168Z" />
97
+ <glyph unicode="S" glyph-name="S" horiz-adv-x="358" d="M94 -10T60 8T25 49Q25 62 30 78T46 107Q85 72 149 72Q248 72 248 174Q248 210 235 238T189 306L89 423Q55 464 41 497T26 567Q26 632 71 671T198 710Q265 710 297 697T329 660Q329 635 309 614Q298 623
98
+ 271 630T211 638Q166 638 140 620T113 569Q113 543 124 522T159 470L268 347Q306 302 320 264T335 178Q335 93 294 42T157 -10Q94 -10 60 8Z" />
99
+ <glyph unicode="T" glyph-name="T" horiz-adv-x="373" d="M365 700V641Q365 631 361 628T348 624H225V16Q225 7 221 4T197 1T144 0V624H8V684Q8 700 25 700H365Z" />
100
+ <glyph unicode="U" glyph-name="U" horiz-adv-x="437" d="M148 -10T109 18T57 91T44 205V683Q44 692 47 695T71 699T127 700V202Q127 133 146 99T224 65Q249 65 274 70T312 82V683Q312 692 316 695T340 699T394 700V26Q373 12 326 1T224 -10Q148 -10 109 18Z" />
101
+ <glyph unicode="V" glyph-name="V" horiz-adv-x="420" d="M176 -7T165 8T146 51Q122 153 73 377T7 676Q5 686 5 689Q5 695 9 697T24 700H90L174 312Q187 246 207 117L214 69Q232 215 250 312L325 676Q328 689 332 694T346 700H416L262 -3Q233 -7 194 -7Q176 -7 165 8Z" />
102
+ <glyph unicode="W" glyph-name="W" horiz-adv-x="654" d="M650 700L518 -3Q509 -4 492 -6T451 -8Q419 -8 408 49L356 276Q345 334 325 503Q306 341 296 277L241 -3Q208 -7 167 -7Q140 -7 126 54Q105 158 63 380T7 676Q6 680 6 687Q6 694 10 697T24 700H90L163
103
+ 311Q178 220 193 88H198Q203 187 224 311L286 642Q289 656 292 661T307 666H364L435 311Q450 232 462 118L465 88H471Q482 220 496 311L561 676Q563 689 566 694T581 700H650Z" />
104
+ <glyph unicode="X" glyph-name="X" horiz-adv-x="424" d="M414 9T414 7Q414 0 403 0H315L261 135Q233 206 207 283Q178 198 151 135L97 10Q93 4 90 2T79 0H9L162 361L36 686Q33 694 37 696T66 699T131 700L172 586Q205 491 222 436Q248 520 271 586L310 687Q312
105
+ 694 316 696T341 699T397 700L268 368L412 13Q414 9 414 7Z" />
106
+ <glyph unicode="Y" glyph-name="Y" horiz-adv-x="364" d="M225 16Q225 7 222 4T198 1T144 0V285L6 687Q4 694 8 696T35 699T97 700L137 563Q152 516 168 446T187 353Q189 375 206 445T238 563L273 685Q275 693 279 696T304 699T360 700L225 290V16Z" />
107
+ <glyph unicode="Z" glyph-name="Z" horiz-adv-x="385" d="M19 11T19 31Q19 50 27 67L267 628H80Q49 628 37 625V683Q37 691 41 695T54 700H352Q360 689 360 674Q360 656 352 637L108 72H302Q343 72 361 80V16Q361 0 345 0H32Q19 11 19 31Z" />
108
+ <glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="386" d="M122 796Q122 808 125 812T140 819T184 821H377Q377 785 376 771T369 752T350 747H203V-123H376Q376 -161 374 -176T367 -195T349 -199H122V796Z" />
109
+ <glyph unicode="\" glyph-name="backslash" horiz-adv-x="329" d="M12 743L10 752Q10 761 25 763T91 765L317 -10L319 -18Q319 -26 304 -28T237 -30L12 743Z" />
110
+ <glyph unicode="]" glyph-name="bracketright" horiz-adv-x="386" d="M9 -164T10 -150T17 -131T36 -126H183V746H10Q10 784 12 799T19 818T37 822H264V-175Q264 -187 261 -191T246 -198T202 -200H9Q9 -164 10 -150Z" />
111
+ <glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="430" d="M69 208T59 220T48 249L220 398L366 270Q380 259 380 244Q380 230 369 219T341 206L212 294L110 218Q94 208 85 208Q69 208 59 220Z" />
112
+ <glyph unicode="_" glyph-name="underscore" horiz-adv-x="377" d="M14 -66T16 -52T30 -38H364Q364 -81 362 -95T347 -109H14Q14 -66 16 -52Z" />
113
+ <glyph unicode="`" glyph-name="grave" horiz-adv-x="269" d="M162 568T111 604T49 655Q53 677 63 696T86 716Q93 716 141 675T220 598Q219 595 215 584T204 563T189 553Q162 568 111 604Z" />
114
+ <glyph unicode="a" glyph-name="a" horiz-adv-x="354" d="M102 -10T66 20T29 142Q29 237 65 271T163 306Q210 306 236 293V372Q236 406 221 419T174 432Q138 432 101 426T41 409Q30 430 30 456Q30 476 41 483Q56 493 99 501T194 510Q259 510 288 482T317 388V335Q317
115
+ 115 311 24Q287 9 250 0T166 -10Q102 -10 66 20ZM196 58T211 63T233 74Q236 108 236 236Q217 246 189 246Q153 246 133 225T113 144Q113 93 129 76T180 58Q196 58 211 63Z" />
116
+ <glyph unicode="b" glyph-name="b" horiz-adv-x="385" d="M84 -10T48 20V698Q48 717 54 724T75 733T128 735V475Q173 510 235 510Q297 510 327 465T358 313Q358 120 303 55T164 -10Q84 -10 48 20ZM212 61T240 110T268 296Q268 374 251 405T198 437Q156 437 128
117
+ 409V75Q144 61 175 61Q212 61 240 110Z" />
118
+ <glyph unicode="c" glyph-name="c" horiz-adv-x="313" d="M128 -10T96 9T48 83T31 239Q31 400 81 455T210 510Q246 510 270 497T294 465Q294 442 285 420Q251 439 214 439Q170 439 142 399T114 238Q114 169 123 131T152 77T204 62Q227 62 248 68T284 85Q292 56
119
+ 292 41Q292 20 260 5T180 -10Q128 -10 96 9Z" />
120
+ <glyph unicode="d" glyph-name="d" horiz-adv-x="381" d="M142 -10T108 7T52 77T31 232Q31 373 73 441T184 510Q230 510 256 490V698Q256 717 262 724T284 733T337 735V20Q273 -10 193 -10Q142 -10 108 7ZM234 62T256 77V427Q239 440 210 440Q120 440 120 241Q120
121
+ 168 129 129T154 76T196 62Q234 62 256 77Z" />
122
+ <glyph unicode="e" glyph-name="e" horiz-adv-x="361" d="M267 68T321 100Q328 78 328 45Q328 22 288 6T190 -10Q139 -10 105 9T50 79T30 224Q30 341 54 404T117 488T207 510Q334 510 334 350Q334 270 323 220Q279 211 221 208T113 204Q115 150 125 121T155 80T207
123
+ 68Q267 68 321 100ZM162 439T140 402T114 268Q213 268 251 280Q256 303 256 340Q255 396 243 417T198 439Q162 439 140 402Z" />
124
+ <glyph unicode="f" glyph-name="f" horiz-adv-x="254" d="M82 432H13Q13 459 14 475T18 496T33 500H82V561Q82 665 129 702T249 739Q294 739 318 728T342 698Q342 675 333 654Q304 666 262 666Q215 666 189 646T162 567V500H252Q252 473 251 457T247 436T235 432H162V16Q162
125
+ 11 159 6T145 0H82V432Z" />
126
+ <glyph unicode="g" glyph-name="g" horiz-adv-x="394" d="M308 72T341 47T375 -34Q375 -104 326 -150T183 -197Q91 -197 57 -167T23 -88Q23 -54 44 -23T100 33Q37 50 38 87Q38 127 101 177Q39 217 39 327Q39 422 83 466T206 510Q229 510 256 502H375Q383 488 383
127
+ 464Q383 433 355 433Q342 433 319 438Q357 400 357 329Q357 246 318 200T195 154Q169 154 150 158Q123 131 123 114Q123 102 148 97T242 82Q308 72 341 47ZM167 451T146 422T124 329Q124 271 139 244T200 216Q239 216 257 249T276 348Q276 402 258 426T203 451Q167
128
+ 451 146 422ZM240 -123T266 -99T293 -45Q293 -22 283 -10T249 9T180 19L160 21Q137 3 122 -21T106 -69Q106 -96 124 -109T190 -123Q240 -123 266 -99Z" />
129
+ <glyph unicode="h" glyph-name="h" horiz-adv-x="380" d="M45 698Q45 717 51 724T72 733T125 735V471Q147 490 177 500T238 510Q299 510 320 479T342 378V16Q342 7 338 4T313 1T260 0V356Q260 394 248 414T201 435Q182 435 162 429T125 408V16Q125 7 122 4T99
130
+ 1T45 0V698Z" />
131
+ <glyph unicode="i" glyph-name="i" horiz-adv-x="175" d="M47 463Q47 482 53 489T75 498T128 500V16Q128 7 125 4T101 1T47 0V463ZM300 570T300 628Q300 692 346 692Q368 692 377 679T386 634Q386 604 374 587T340 570Q300 570 300 628Z" />
132
+ <glyph unicode="j" glyph-name="j" horiz-adv-x="184" d="M-50 -196T-76 -182T-103 -143Q-103 -117 -91 -102Q-53 -122 -6 -122Q31 -122 42 -101T54 -24V463Q54 482 60 489T81 498T134 500V-36Q134 -111 106 -153T4 -196Q-50 -196 -76 -182ZM306 570T306 628Q306
133
+ 692 352 692Q374 692 383 679T392 634Q392 604 380 587T346 570Q306 570 306 628Z" />
134
+ <glyph unicode="k" glyph-name="k" horiz-adv-x="376" d="M46 698Q46 717 52 724T74 733T127 735V464Q153 485 187 497T253 510Q338 510 338 424Q338 369 312 331T254 269Q293 252 320 209T354 100Q355 90 355 67Q355 43 352 16Q351 7 347 4T321 1T261 0Q265 29
135
+ 265 62Q265 93 260 123Q251 175 231 201T183 227Q176 227 172 226Q161 224 146 217T127 207V16Q127 7 124 4T101 1T46 0V698ZM249 314T249 397Q249 437 212 437Q166 437 127 389V265Q249 314 249 397Z" />
136
+ <glyph unicode="l" glyph-name="l" horiz-adv-x="173" d="M46 698Q46 717 52 724T74 733T127 735V16Q127 7 123 4T100 1T46 0V698Z" />
137
+ <glyph unicode="m" glyph-name="m" horiz-adv-x="564" d="M458 510T482 494T515 449T523 369V16Q523 7 519 4T495 1T442 0V370Q442 409 433 424T393 439Q356 439 325 420V16Q325 7 322 4T299 1T246 0H245V372Q245 400 242 413T229 433T195 440Q159 440 127 422V16Q127
138
+ 7 123 4T99 1T46 0V477Q119 510 197 510Q259 510 288 479Q313 493 346 501T410 510Q458 510 482 494Z" />
139
+ <glyph unicode="n" glyph-name="n" horiz-adv-x="383" d="M45 476Q76 490 121 500T204 510Q282 510 313 480T344 376V16Q344 7 340 4T316 1T261 0V352Q261 402 249 421T201 440Q183 440 162 436T126 422V16Q126 7 122 4T98 1T45 0V476Z" />
140
+ <glyph unicode="o" glyph-name="o" horiz-adv-x="376" d="M128 -10T95 9T45 84T27 250Q27 510 194 510Q249 510 282 488T332 409T349 252Q349 116 308 53T184 -10Q128 -10 95 9ZM222 58T241 100T261 252Q261 330 254 371T231 427T191 442Q154 442 135 400T115
141
+ 248Q115 168 123 127T146 72T186 58Q222 58 241 100Z" />
142
+ <glyph unicode="p" glyph-name="p" horiz-adv-x="382" d="M63 -197T55 -190T46 -162V470Q109 510 192 510Q272 510 313 464T355 302Q355 121 313 56T206 -10Q180 -10 160 -2T127 18V-191Q108 -197 85 -197Q63 -197 55 -190ZM266 62T266 293Q266 352 258 384T233
143
+ 428T188 440Q154 440 127 421V95Q147 62 181 62Q266 62 266 293Z" />
144
+ <glyph unicode="q" glyph-name="q" horiz-adv-x="387" d="M276 -198T261 -195V34Q231 -10 165 -10Q107 -10 69 38T30 218Q30 373 86 441T235 510Q269 510 294 503T342 479V-163Q342 -186 334 -191T294 -198Q276 -198 261 -195ZM215 60T233 73T261 112V426Q243
145
+ 440 215 440Q169 440 145 389T121 220Q121 127 141 94T195 61Q215 60 233 73Z" />
146
+ <glyph unicode="r" glyph-name="r" horiz-adv-x="263" d="M45 465Q109 510 181 510Q218 510 234 500T251 468Q251 443 242 421Q221 433 194 433Q155 433 128 410L127 16Q127 7 123 4T99 1T45 0V465Z" />
147
+ <glyph unicode="s" glyph-name="s" horiz-adv-x="346" d="M97 -10T64 3T30 40Q30 64 45 87Q86 63 144 63Q183 63 204 78T226 124Q226 146 217 165T186 203L89 286Q31 333 31 392Q31 447 72 478T195 510Q263 510 291 492T320 454Q320 445 315 434T303 413Q259 440
148
+ 204 440Q168 440 147 427T125 391Q125 376 133 363T165 329L258 254Q291 222 304 194T318 129Q318 63 274 27T152 -10Q97 -10 64 3Z" />
149
+ <glyph unicode="t" glyph-name="t" horiz-adv-x="292" d="M141 -10T121 7T94 56T87 139V431H19Q19 458 20 474T24 495T36 500H87V652Q87 666 93 671T114 678T167 680V500H271Q271 473 270 456T266 435T254 431H167V139Q167 98 173 79T204 59Q220 59 232 62T262
150
+ 74Q268 54 268 34Q268 10 243 0T182 -10Q141 -10 121 7Z" />
151
+ <glyph unicode="u" glyph-name="u" horiz-adv-x="389" d="M135 -10T103 8T58 61T45 156V484Q45 493 48 496T72 499T128 500V149Q128 98 141 79T196 60Q236 60 263 79V484Q263 493 267 496T290 499T344 500V26Q314 10 274 0T194 -10Q135 -10 103 8Z" />
152
+ <glyph unicode="v" glyph-name="v" horiz-adv-x="344" d="M143 -6T135 6T121 43L19 475Q17 485 17 487Q17 494 23 496T51 499T104 500L136 339Q150 274 161 193T176 86H178Q181 111 192 192T216 340L243 482Q245 492 249 495T273 499T328 500L213 -3Q198 -6 156
153
+ -6Q143 -6 135 6Z" />
154
+ <glyph unicode="w" glyph-name="w" horiz-adv-x="541" d="M448 492T452 495T476 499T526 500Q510 421 466 209T418 -3Q415 -4 401 -5T366 -6Q354 -6 342 7T324 46L311 114Q288 220 274 363Q256 205 237 113L214 -3Q207 -4 188 -5T147 -6Q123 -6 113 43Q95 120
155
+ 61 276T16 483Q14 492 19 495T46 499T103 500L134 337Q146 267 156 191T169 93H175Q194 229 203 278L235 469Q237 480 241 483T261 486H314L353 276Q364 228 373 170T384 93H389Q393 140 402 210T421 337L446 482Q448 492 452 495Z" />
156
+ <glyph unicode="x" glyph-name="x" horiz-adv-x="411" d="M395 9T395 8Q395 3 384 2T337 0H302L265 66Q243 107 199 201Q164 121 136 65L107 16Q103 8 96 5T68 1T15 0L157 260L35 487Q32 494 35 496T60 499T120 500L157 431Q185 379 211 317Q244 390 266 432L297
157
+ 486Q300 494 305 496T330 499T386 500L258 267L391 19Q395 9 395 8Z" />
158
+ <glyph unicode="y" glyph-name="y" horiz-adv-x="357" d="M-4 -193T-4 -154Q-4 -142 -1 -130T8 -109Q18 -116 35 -120T71 -124Q97 -124 115 -100T144 -16L24 478Q22 486 22 489Q22 497 35 498T102 500L157 260Q167 211 174 156T184 83Q186 99 192 154T208 261L253
159
+ 477Q255 490 259 494T279 499T340 500L217 -27Q202 -91 184 -127T140 -178T77 -193Q-4 -193 -4 -154Z" />
160
+ <glyph unicode="z" glyph-name="z" horiz-adv-x="335" d="M33 1T30 5T24 19T21 40Q21 58 28 70L223 432H120Q63 432 28 427Q25 444 25 465Q25 483 31 491T51 500H306Q307 499 310 493T314 475Q314 456 305 436L115 75H204Q270 75 310 85Q312 77 312 50Q312 23
161
+ 306 12T280 0H34Q33 1 30 5Z" />
162
+ <glyph unicode="{" glyph-name="braceleft" horiz-adv-x="277" d="M154 -150T122 -117T89 -15Q89 26 96 80Q97 89 100 120T103 182Q103 220 90 237T48 260Q41 275 41 293Q41 321 58 333Q74 343 83 352T97 378T103 427Q103 458 100 492T96 536Q89 590 89 626Q89
163
+ 686 130 714T244 744Q260 722 260 694Q260 683 256 676T245 666Q208 663 190 644T171 585Q171 556 176 511Q182 445 182 403Q182 357 169 335T128 300Q155 289 168 268T182 205Q182 164 176 100Q171 53 171 22Q171 -15 180 -35T206 -63T253 -76Q255 -102 255 -106Q255
164
+ -126 248 -138T227 -150Q154 -150 122 -117Z" />
165
+ <glyph unicode="|" glyph-name="bar" horiz-adv-x="324" d="M122 793Q122 805 126 809T145 814T203 815V-180Q203 -191 199 -194T175 -199T122 -200V793Z" />
166
+ <glyph unicode="}" glyph-name="braceright" horiz-adv-x="277" d="M18 -128T18 -100Q18 -75 33 -72Q106 -66 106 8Q106 28 102 84Q96 148 96 191Q96 237 109 259T149 294Q122 305 109 326T96 389Q96 430 102 494Q106 550 106 573Q106 610 97 629T71 657T25 670Q21
167
+ 686 21 701Q21 720 29 732T50 744Q123 744 155 711T188 609Q188 568 181 514Q180 505 177 474T174 412Q174 374 187 357T229 334Q236 317 236 301Q236 272 218 261Q201 250 193 242T180 216T174 167Q174 128 182 58Q188 -6 188 -32Q188 -92 146 -121T33 -150Q18
168
+ -128 18 -100Z" />
169
+ <glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="400" d="M237 212T221 218T185 237Q169 247 158 251T132 256Q115 256 102 250T72 232Q58 221 46 218Q32 217 22 230T11 266Q26 289 61 309T140 329Q164 329 178 324T212 306Q228 296 240 291T270 286Q309
170
+ 286 341 320Q347 325 357 325Q370 325 380 311T391 277Q373 252 340 232T263 212Q237 212 221 218Z" />
171
+ <glyph unicode="&#xa0;" glyph-name="uni00A0" horiz-adv-x="158" />
172
+ <glyph unicode="&#xa1;" glyph-name="exclamdown" horiz-adv-x="195" d="M46 394T46 451Q46 476 61 492T102 508Q132 508 144 494T157 451Q157 427 142 411T102 394Q46 394 46 451ZM61 256Q62 274 69 281T91 291T141 293L155 -174Q155 -185 147 -190T117 -196T45
173
+ -197L61 256Z" />
174
+ <glyph unicode="&#xa2;" glyph-name="cent" horiz-adv-x="418" d="M277 64T299 71T335 88Q343 68 343 37Q343 23 319 9T263 -9V-102Q263 -121 247 -127T190 -133V-7Q154 -1 131 20T96 91T83 228Q83 471 213 504V592Q213 611 230 618T286 626V508Q310 503 327 494T345
175
+ 475Q345 437 337 417Q305 435 269 435Q239 435 217 421T179 362T164 231Q164 156 174 121T202 75T257 64Q277 64 299 71Z" />
176
+ <glyph unicode="&#xa3;" glyph-name="sterling" horiz-adv-x="366" d="M322 74Q329 54 329 37Q329 0 277 0H89Q80 121 78 277H15Q15 320 17 333T31 347H78Q82 480 122 537T238 595Q280 595 309 582T338 542Q338 530 335 518T327 493Q294 514 256 514Q210 514 185
177
+ 479T158 347H288Q288 305 286 291T272 277H158Q160 146 165 80L322 74Z" />
178
+ <glyph unicode="&#xa4;" glyph-name="currency" horiz-adv-x="571" d="M560 49Q535 24 525 15T507 6Q504 6 498 10L417 91Q371 52 286 52Q201 52 154 90L62 -2Q39 21 30 32T20 51Q20 56 24 60L110 146Q87 198 87 276Q87 350 107 401L12 496Q37 521 47 530T65 539Q69
179
+ 539 73 535L147 461Q198 506 291 506Q380 506 427 463L510 546Q534 522 543 512T552 493Q552 489 548 485L466 403Q484 357 484 286Q484 202 460 148L560 49ZM348 121T377 162T407 288Q407 365 380 402T295 439Q230 439 199 398T167 271Q167 195 196 158T288 121Q348
180
+ 121 377 162Z" />
181
+ <glyph unicode="&#xa5;" glyph-name="yen" horiz-adv-x="418" d="M249 243H379Q379 205 373 191T347 176H249V16Q249 7 246 4T223 1T169 0V176H38Q38 215 44 229T70 243H169V284L149 341H38Q38 380 44 394T70 408H126L30 687Q28 694 32 696T59 699T122 700L161
182
+ 563Q179 505 193 449T209 372Q211 392 226 451T259 563L297 685Q300 693 304 696T329 699T384 700L288 408H378Q378 370 372 356T347 341H266L249 290V243Z" />
183
+ <glyph unicode="&#xa6;" glyph-name="brokenbar" horiz-adv-x="227" d="M90 326T73 330V711Q73 729 82 736T115 743Q135 743 153 737V354Q153 337 146 332T112 326Q90 326 73 330ZM89 -198T73 -193V192Q73 210 82 217T115 225Q135 225 153 219V-169Q153 -187 146
184
+ -192T112 -198Q89 -198 73 -193Z" />
185
+ <glyph unicode="&#xa7;" glyph-name="section" horiz-adv-x="348" d="M264 201Q284 174 292 155T301 112Q301 58 259 27T150 -4Q106 -4 78 9T43 39Q40 44 40 55Q40 83 56 105Q90 81 137 81Q171 81 190 94T209 128Q209 142 202 154T179 185Q179 185 141 223Q94
186
+ 268 72 296Q53 320 45 340T36 388Q36 415 49 442T91 489L83 499Q63 524 55 543T46 587Q46 641 88 672T198 703Q242 703 269 690T304 661Q307 652 307 644Q307 617 291 595Q259 618 209 618Q175 618 157 605T138 571Q138 557 144 546T167 515Q181 500 202 480Q221
187
+ 462 242 441T275 404Q295 380 303 360T311 312Q311 286 297 259T255 212L264 201ZM151 300T171 284T196 264Q214 271 225 287T236 318Q236 335 229 350T207 384Q196 399 176 415T151 435Q133 429 122 413T111 382Q111 363 118 348T140 315Q151 300 171 284Z" />
188
+ <glyph unicode="&#xa8;" glyph-name="dieresis" horiz-adv-x="269" d="M53 573T44 586T34 632Q34 659 45 674T78 689Q100 689 108 677T117 634Q117 604 106 588T73 572Q53 573 44 586ZM173 573T163 585T153 628Q153 689 197 689Q219 689 227 677T236 634Q234 572
189
+ 192 572Q173 573 163 585Z" />
190
+ <glyph unicode="&#xa9;" glyph-name="copyright" horiz-adv-x="471" d="M133 320T85 371T36 541Q36 652 89 706T242 760Q343 760 389 711T436 543Q436 435 384 378T234 320Q133 320 85 371ZM296 378T335 418T375 544Q375 628 342 666T241 705Q96 705 96 541Q96
191
+ 452 129 415T232 378Q296 378 335 418ZM211 416T193 424T166 456T157 531Q157 613 183 640T249 668Q264 668 279 663T294 652Q294 644 288 620Q268 628 254 628Q231 628 218 609T204 529Q204 497 208 482T222 462T247 457Q266 457 286 467Q289 461 291 452T293
192
+ 437Q293 430 277 423T243 416Q211 416 193 424Z" />
193
+ <glyph unicode="&#xaa;" glyph-name="ordfeminine" horiz-adv-x="323" d="M100 288T71 312T41 411Q41 478 69 506T150 534Q182 534 214 524V582Q214 615 198 627T152 639Q105 639 57 617Q47 630 47 652Q47 668 53 675Q85 710 176 710Q225 710 253 685T282 608V317Q259
194
+ 306 224 297T154 288Q100 288 71 312ZM179 346T193 349T214 358V468Q206 472 194 474T172 476Q138 476 122 464T105 409Q105 373 120 360T165 346Q179 346 193 349Z" />
195
+ <glyph unicode="&#xab;" glyph-name="guillemotleft" horiz-adv-x="416" d="M156 16T120 70T51 181T18 253Q18 268 50 322T118 430T164 500Q178 500 188 491T205 472T212 459Q212 442 186 397T132 309T94 253Q103 240 132 198T187 109T214 47Q213 43 207 32T191
196
+ 11T167 0Q156 16 120 70ZM326 38T291 86T223 185T190 251Q190 265 222 315T288 414T334 477Q347 477 357 469T374 451T381 439Q381 423 356 381T303 301T267 251Q276 240 303 203T357 125T383 69Q382 66 376 55T360 34T337 24Q326 38 291 86Z" />
197
+ <glyph unicode="&#xac;" glyph-name="logicalnot" horiz-adv-x="400" d="M358 111Q342 105 321 105Q304 105 293 111T282 131V232H40Q34 252 34 270Q34 287 40 297T60 308H358V111Z" />
198
+ <glyph unicode="&#xad;" glyph-name="uni00AD" horiz-adv-x="278" d="M37 290T39 303T54 317H241Q241 275 239 261T225 247H37Q37 290 39 303Z" />
199
+ <glyph unicode="&#xae;" glyph-name="registered" horiz-adv-x="471" d="M343 760T389 711T436 543Q436 435 384 378T234 320Q133 320 85 371T36 541Q36 652 89 706T242 760Q343 760 389 711ZM296 378T335 418T375 544Q375 628 342 666T241 705Q96 705 96 541Q96
200
+ 452 129 415T232 378Q296 378 335 418ZM288 517T300 492T315 426Q315 420 312 418T299 416Q281 416 274 417Q268 491 239 518Q235 517 227 517H214V425Q214 419 208 418T176 417V665Q176 666 177 668T183 670H227Q308 670 308 598Q308 550 274 529Q288 517 300
201
+ 492ZM219 551T230 551Q249 551 258 562T268 600Q268 618 260 627T232 637H214V552Q219 551 230 551Z" />
202
+ <glyph unicode="&#xaf;" glyph-name="overscore" horiz-adv-x="269" d="M1 601T1 624Q1 642 10 652T34 663H261Q268 646 268 629Q268 611 260 599T238 587H6Q1 601 1 624Z" />
203
+ <glyph unicode="&#xb0;" glyph-name="degree" horiz-adv-x="358" d="M129 553T107 573T84 649Q84 746 184 746Q233 746 254 725T276 650Q276 602 251 578T177 553Q129 553 107 573ZM223 604T223 650Q223 674 214 684T184 694Q161 694 150 684T138 648Q138 604
204
+ 179 604Q223 604 223 650Z" />
205
+ <glyph unicode="&#xb1;" glyph-name="plusminus" horiz-adv-x="419" d="M385 359Q385 317 383 303T369 289H246V165Q246 154 232 152T175 149V289H35Q35 332 37 345T52 359H175V483Q175 494 189 496T246 499V359H385ZM34 58T36 71T51 85H384Q384 43 382 29T368
206
+ 15H34Q34 58 36 71Z" />
207
+ <glyph unicode="&#xb2;" glyph-name="uni00B2" d="M41 301T41 315Q41 344 53 357Q77 385 138 444Q188 491 210 518T232 576Q232 600 219 614T168 628Q139 628 116 618T66 590Q57 606 54 616T51 639Q51 657 74 673T134 700T205 710Q274 710 302 683T330 604Q330
208
+ 556 300 515T212 416Q170 373 162 365H339Q344 342 344 330Q344 289 318 289H47Q41 301 41 315Z" />
209
+ <glyph unicode="&#xb3;" glyph-name="uni00B3" d="M304 503T322 478T340 423Q340 352 292 316T165 279Q126 279 98 286T57 305Q45 314 45 335Q45 347 50 359T64 379Q103 353 161 353Q205 353 231 369T257 416Q257 438 242 452T199 470Q181 473 128 473Q126 476
210
+ 123 489T119 510Q119 535 144 535Q171 535 197 532Q218 548 227 562T236 594Q236 612 221 625T171 638Q113 638 67 605Q51 622 51 645Q51 663 61 671Q79 688 116 699T204 710Q266 710 295 686T325 620Q325 592 312 565T273 516Q304 503 322 478Z" />
211
+ <glyph unicode="&#xb4;" glyph-name="acute" horiz-adv-x="271" d="M73 560T66 570T55 591T50 605Q82 641 129 681T184 722Q197 722 207 703T221 661Q209 648 158 611T81 560Q73 560 66 570Z" />
212
+ <glyph unicode="&#xb5;" glyph-name="uni00B5" horiz-adv-x="395" d="M70 -195T45 -190V470Q45 488 54 496T86 505Q110 505 125 500V131Q125 98 143 82T195 66Q237 66 269 81V470Q269 488 277 496T308 505Q334 505 349 500V24Q317 9 277 0T194 -10Q146 -10 125
213
+ 15V-163Q125 -195 80 -195Q70 -195 45 -190Z" />
214
+ <glyph unicode="&#xb6;" glyph-name="paragraph" horiz-adv-x="541" d="M289 -3T271 3V205Q257 198 235 194T191 190Q121 190 78 242T34 420Q34 569 86 639T231 710Q298 710 352 682V26Q352 -3 312 -3Q289 -3 271 3ZM430 -3T414 2V658Q414 676 423 683T456 690Q477
215
+ 690 495 684V26Q495 8 487 3T453 -3Q430 -3 414 2ZM226 255T243 258T271 269V630Q253 641 220 641Q169 641 143 588T116 428Q116 331 143 294T212 255Q226 255 243 258Z" />
216
+ <glyph unicode="&#xb7;" glyph-name="middot" horiz-adv-x="220" d="M79 212T66 226T53 269Q53 293 68 309T109 326Q165 326 165 269Q165 244 150 228T109 212Q79 212 66 226Z" />
217
+ <glyph unicode="&#xb8;" glyph-name="cedilla" horiz-adv-x="269" d="M177 -58T191 -74T206 -122Q206 -160 184 -183T126 -206Q107 -206 89 -200T70 -193Q71 -188 75 -172T83 -152Q86 -149 101 -156T128 -163Q159 -163 159 -129Q159 -95 129 -95Q113 -95 102 -97T88
218
+ -101L107 2H152L140 -59Q141 -58 157 -58Q177 -58 191 -74Z" />
219
+ <glyph unicode="&#xb9;" glyph-name="uni00B9" horiz-adv-x="382" d="M45 308T45 323Q45 345 50 355T72 365H166V622L58 600Q48 616 48 638Q48 666 74 676Q109 688 157 699T223 710Q241 710 247 707V365H334Q338 339 338 329Q338 307 333 298T313 289H49Q45 308 45 323Z" />
220
+ <glyph unicode="&#xba;" glyph-name="ordmasculine" horiz-adv-x="338" d="M121 289T93 307T50 372T35 499Q35 607 71 658T174 710Q220 710 247 692T289 628T303 501Q303 394 268 342T165 289Q121 289 93 307ZM200 356T217 389T235 499Q235 580 221 609T175 639Q139
221
+ 639 121 606T103 494Q103 414 118 385T166 356Q200 356 217 389Z" />
222
+ <glyph unicode="&#xbb;" glyph-name="guillemotright" horiz-adv-x="414" d="M244 1T233 6T211 21T200 46Q200 62 227 108T283 197T321 253Q312 266 284 308T229 396T203 459Q203 471 213 480T235 494T251 500Q262 484 297 431T364 323T396 253Q396 237 363 181T295
223
+ 71T248 0Q244 1 233 6ZM73 23T63 28T43 43T33 68Q33 84 59 124T112 202T149 250Q152 250 126 284Q84 343 60 382T35 439Q35 451 45 460T65 474T79 480Q90 465 125 416T192 316T224 250Q224 234 191 184T123 85T76 22Q73 23 63 28Z" />
224
+ <glyph unicode="&#xbc;" glyph-name="onequarter" horiz-adv-x="959" d="M41 307T41 323Q41 345 46 355T68 365H162V622L54 600Q44 616 44 638Q44 665 71 676Q105 688 153 699T219 710Q237 710 244 707V365H330Q334 348 334 329Q334 307 329 298T310 289H46Q41
225
+ 307 41 323ZM324 -10T309 -2T281 19L617 691Q625 710 645 710Q658 710 673 703T700 682L361 5Q355 -10 337 -10Q324 -10 309 -2ZM808 -6T792 -2V70H621Q607 85 607 110Q607 125 614 143Q632 202 677 275T764 390Q780 409 796 415T834 421Q853 421 868 416V147H927Q934
226
+ 130 934 109Q934 91 927 81T909 70H868V19Q868 -6 825 -6Q808 -6 792 -2ZM795 141L799 329Q766 303 731 247T682 141H795Z" />
227
+ <glyph unicode="&#xbd;" glyph-name="onehalf" horiz-adv-x="947" d="M41 307T41 323Q41 345 46 355T68 365H162V622L54 600Q44 616 44 638Q44 665 71 676Q105 688 153 699T219 710Q237 710 244 707V365H330Q334 348 334 329Q334 307 329 298T310 289H46Q41 307
228
+ 41 323ZM324 -10T309 -2T281 19L617 691Q625 710 645 710Q658 710 673 703T700 682L361 5Q355 -10 337 -10Q324 -10 309 -2ZM607 12T607 26Q607 55 619 68Q643 96 704 155Q754 202 776 229T798 287Q798 311 785 325T734 339Q706 339 682 329T632 301Q623 317 621
229
+ 327T618 350Q618 368 641 384T700 411T771 421Q840 421 868 394T896 315Q896 281 879 249T839 190T776 125Q735 84 728 76H905Q910 53 910 41Q910 0 884 0H613Q607 12 607 26Z" />
230
+ <glyph unicode="&#xbe;" glyph-name="threequarters" horiz-adv-x="961" d="M300 503T318 478T336 423Q336 352 288 316T161 279Q122 279 95 286T54 305Q41 314 41 335Q41 347 46 359T61 379Q98 353 158 353Q202 353 227 369T253 416Q253 438 238 452T196 470Q177
231
+ 473 124 473Q122 476 119 489T115 510Q115 535 140 535Q169 535 194 532Q215 547 223 561T232 594Q232 612 217 625T168 638Q109 638 64 605Q48 622 48 646Q48 664 57 671Q75 688 112 699T201 710Q262 710 291 686T321 620Q321 592 308 565T269 516Q300 503 318
232
+ 478ZM326 -10T311 -2T283 19L619 691Q627 710 647 710Q660 710 675 703T702 682L363 5Q357 -10 339 -10Q326 -10 311 -2ZM810 -6T794 -2V70H623Q609 85 609 110Q609 125 616 143Q634 202 679 275T766 390Q782 409 798 415T836 421Q855 421 870 416V147H929Q936
233
+ 130 936 109Q936 91 929 81T911 70H870V19Q870 -6 827 -6Q810 -6 794 -2ZM797 141L801 329Q768 303 733 247T684 141H797Z" />
234
+ <glyph unicode="&#xbf;" glyph-name="questiondown" horiz-adv-x="306" d="M138 392T125 407T111 450Q111 474 126 490T167 506Q197 506 210 492T223 450Q223 426 208 409T167 392Q138 392 125 407ZM89 -195T52 -160T14 -56Q14 3 33 43T89 131Q117 170 125 184Q137
235
+ 205 137 232Q137 242 132 272Q130 281 130 288T131 298Q141 308 176 308Q188 308 204 306Q205 304 208 295T215 270T219 236Q219 198 201 163Q186 135 156 93Q127 49 114 23T101 -37Q101 -114 182 -114Q228 -114 266 -89Q278 -119 278 -153Q278 -170 239 -182T153
236
+ -195Q89 -195 52 -160Z" />
237
+ <glyph unicode="&#xc0;" glyph-name="Agrave" horiz-adv-x="437" d="M433 6T426 2T396 -2Q369 -2 348 0L306 178H132L93 14Q90 5 84 2T55 -2Q34 -2 5 1L184 689Q187 703 214 703Q237 703 252 700L431 15Q433 6 426 2ZM288 251L254 395Q225 517 219 588Q208 499
238
+ 183 395L149 251H288ZM253 768T202 804T140 855Q144 877 154 896T177 916Q184 916 232 875T311 798Q310 795 306 784T295 763T280 753Q253 768 202 804Z" />
239
+ <glyph unicode="&#xc1;" glyph-name="Aacute" horiz-adv-x="437" d="M433 6T426 2T396 -2Q369 -2 348 0L306 178H132L93 14Q90 5 84 2T55 -2Q34 -2 5 1L184 689Q187 703 214 703Q237 703 252 700L431 15Q433 6 426 2ZM288 251L254 395Q225 517 219 588Q208 499
240
+ 183 395L149 251H288ZM163 760T156 770T145 791T140 805Q172 841 219 881T274 922Q287 922 297 903T311 861Q299 848 248 811T171 760Q163 760 156 770Z" />
241
+ <glyph unicode="&#xc2;" glyph-name="Acircumflex" horiz-adv-x="437" d="M433 6T426 2T396 -2Q369 -2 348 0L306 178H132L93 14Q90 5 84 2T55 -2Q34 -2 5 1L184 689Q187 703 214 703Q237 703 252 700L431 15Q433 6 426 2ZM288 251L254 395Q225 517 219 588Q208
242
+ 499 183 395L149 251H288ZM279 770T255 796T222 838Q218 832 203 814T172 779T147 762Q111 762 92 785L206 908Q214 911 223 911Q237 911 248 899Q255 892 278 867T323 817T345 784Q345 774 328 768T288 761Q279 770 255 796Z" />
243
+ <glyph unicode="&#xc3;" glyph-name="Atilde" horiz-adv-x="437" d="M433 6T426 2T396 -2Q369 -2 348 0L306 178H132L93 14Q90 5 84 2T55 -2Q34 -2 5 1L184 689Q187 703 214 703Q237 703 252 700L431 15Q433 6 426 2ZM288 251L254 395Q225 517 219 588Q208 499
244
+ 183 395L149 251H288ZM238 763T227 770T205 790Q195 801 188 806T170 811Q156 811 147 804T127 785T111 772Q101 772 93 781T79 799T72 812Q96 851 125 869T185 887Q200 887 209 881T230 863Q241 851 250 845T272 839Q292 839 302 846T322 866T337 878Q345 878
245
+ 351 868T361 847T366 833Q362 824 347 808T309 777T258 763Q238 763 227 770Z" />
246
+ <glyph unicode="&#xc4;" glyph-name="Adieresis" horiz-adv-x="437" d="M433 6T426 2T396 -2Q369 -2 348 0L306 178H132L93 14Q90 5 84 2T55 -2Q34 -2 5 1L184 689Q187 703 214 703Q237 703 252 700L431 15Q433 6 426 2ZM288 251L254 395Q225 517 219 588Q208
247
+ 499 183 395L149 251H288ZM137 773T128 786T118 832Q118 859 129 874T162 889Q184 889 192 877T201 834Q201 804 190 788T157 772Q137 773 128 786ZM257 773T247 785T237 828Q237 889 281 889Q303 889 311 877T320 834Q318 772 276 772Q257 773 247 785Z" />
248
+ <glyph unicode="&#xc5;" glyph-name="Aring" horiz-adv-x="437" d="M433 6T426 2T396 -2Q369 -2 348 0L306 178H132L93 14Q90 5 84 2T55 -2Q34 -2 5 1L184 689Q187 703 214 703Q237 703 252 700L431 15Q433 6 426 2ZM288 251L254 395Q225 517 219 588Q208 499
249
+ 183 395L149 251H288ZM169 743T146 764T123 839Q123 888 149 912T223 937Q271 937 293 916T315 840Q315 792 290 768T216 743Q169 743 146 764ZM259 797T259 840Q259 862 250 871T223 881Q180 881 180 838Q180 818 189 808T218 797Q259 797 259 840Z" />
250
+ <glyph unicode="&#xc6;" glyph-name="AE" horiz-adv-x="615" d="M420 73H592Q592 44 592 26T588 4T576 0H340V178H174L105 14Q102 5 95 2T68 -2Q45 -2 17 1L311 686Q317 700 337 700H596Q596 672 596 654T592 632T581 627H420V418H571Q571 391 571 373T567 350T556
251
+ 344H420V73ZM340 251V615Q304 490 264 392L205 251H340Z" />
252
+ <glyph unicode="&#xc7;" glyph-name="Ccedilla" horiz-adv-x="341" d="M243 79T268 88T308 111Q320 85 320 62Q320 35 293 15T220 -9L210 -59Q211 -58 227 -58Q247 -58 261 -74T276 -122Q276 -160 254 -183T196 -206Q177 -206 159 -200T140 -193Q141 -188 145
253
+ -172T153 -152Q156 -149 171 -156T198 -163Q229 -163 229 -129Q229 -95 199 -95Q183 -95 172 -97T158 -101L175 -9Q128 -6 97 21T48 120T30 321Q30 480 59 566T130 681T227 710Q277 710 299 694T321 655Q321 628 308 606Q277 631 243 631Q206 631 178 608T133 517T115
254
+ 325Q115 227 127 173T161 99T215 79Q243 79 268 88Z" />
255
+ <glyph unicode="&#xc8;" glyph-name="Egrave" horiz-adv-x="320" d="M125 73H297Q297 44 297 26T293 4T281 0H45V684Q45 688 48 694T61 700H301Q301 672 301 654T297 632T286 627H125V418H276Q276 391 276 373T272 350T261 344H125V73ZM204 768T153 804T91 855Q95
256
+ 877 105 896T128 916Q135 916 183 875T262 798Q261 795 257 784T246 763T231 753Q204 768 153 804Z" />
257
+ <glyph unicode="&#xc9;" glyph-name="Eacute" horiz-adv-x="320" d="M125 73H297Q297 44 297 26T293 4T281 0H45V684Q45 688 48 694T61 700H301Q301 672 301 654T297 632T286 627H125V418H276Q276 391 276 373T272 350T261 344H125V73ZM114 760T107 770T96 791T91
258
+ 805Q123 841 170 881T225 922Q238 922 248 903T262 861Q250 848 199 811T122 760Q114 760 107 770Z" />
259
+ <glyph unicode="&#xca;" glyph-name="Ecircumflex" horiz-adv-x="320" d="M125 73H297Q297 44 297 26T293 4T281 0H45V684Q45 688 48 694T61 700H301Q301 672 301 654T297 632T286 627H125V418H276Q276 391 276 373T272 350T261 344H125V73ZM230 770T206 796T173
260
+ 838Q169 832 154 814T123 779T98 762Q62 762 43 785L157 908Q165 911 174 911Q188 911 199 899Q206 892 229 867T274 817T296 784Q296 774 279 768T239 761Q230 770 206 796Z" />
261
+ <glyph unicode="&#xcb;" glyph-name="Edieresis" horiz-adv-x="320" d="M125 73H297Q297 44 297 26T293 4T281 0H45V684Q45 688 48 694T61 700H301Q301 672 301 654T297 632T286 627H125V418H276Q276 391 276 373T272 350T261 344H125V73ZM88 773T79 786T69 832Q69
262
+ 859 80 874T113 889Q135 889 143 877T152 834Q152 804 141 788T108 772Q88 773 79 786ZM208 773T198 785T188 828Q188 889 232 889Q254 889 262 877T271 834Q269 772 227 772Q208 773 198 785Z" />
263
+ <glyph unicode="&#xcc;" glyph-name="Igrave" horiz-adv-x="169" d="M44 663Q44 683 48 690T66 698T126 700V16Q126 7 122 4T98 1T44 0V663ZM119 768T68 804T6 855Q10 877 20 896T43 916Q50 916 98 875T177 798Q176 795 172 784T161 763T146 753Q119 768 68 804Z" />
264
+ <glyph unicode="&#xcd;" glyph-name="Iacute" horiz-adv-x="169" d="M44 663Q44 683 48 690T66 698T126 700V16Q126 7 122 4T98 1T44 0V663ZM29 760T22 770T11 791T6 805Q38 841 85 881T140 922Q153 922 163 903T177 861Q165 848 114 811T37 760Q29 760 22 770Z" />
265
+ <glyph unicode="&#xce;" glyph-name="Icircumflex" horiz-adv-x="169" d="M44 663Q44 683 48 690T66 698T126 700V16Q126 7 122 4T98 1T44 0V663ZM145 770T121 796T88 838Q84 832 69 814T38 779T13 762Q-23 762 -42 785L72 908Q80 911 89 911Q103 911 114 899Q121
266
+ 892 144 867T189 817T211 784Q211 774 194 768T154 761Q145 770 121 796Z" />
267
+ <glyph unicode="&#xcf;" glyph-name="Idieresis" horiz-adv-x="169" d="M44 663Q44 683 48 690T66 698T126 700V16Q126 7 122 4T98 1T44 0V663ZM3 773T-6 786T-16 832Q-16 859 -5 874T28 889Q50 889 58 877T67 834Q67 804 56 788T23 772Q3 773 -6 786ZM123 773T113
268
+ 785T103 828Q103 889 147 889Q169 889 177 877T186 834Q184 772 142 772Q123 773 113 785Z" />
269
+ <glyph unicode="&#xd0;" glyph-name="Eth" horiz-adv-x="388" d="M237 700T279 672T342 577T364 394Q364 247 342 160T276 37T169 0H44V331H5Q5 374 7 387T21 401H44V681Q44 691 50 695T71 700H169Q237 700 279 672ZM201 77T223 100T259 193T273 394Q273 485 262
270
+ 535T229 606T171 626H125V401H205Q205 359 203 345T188 331H125V77H171Q201 77 223 100Z" />
271
+ <glyph unicode="&#xd1;" glyph-name="Ntilde" horiz-adv-x="444" d="M320 693T324 696T348 699T401 700V16Q401 0 384 0H324L209 275Q185 339 114 560Q118 517 121 416T124 259V16Q124 7 121 4T97 1T44 0V684Q44 700 60 700H121L234 430Q247 395 308 211Q312 199
272
+ 330 146V149Q325 188 323 290T320 448V684Q320 693 324 696ZM241 763T230 770T208 790Q198 801 191 806T173 811Q159 811 150 804T130 785T114 772Q104 772 96 781T82 799T75 812Q99 851 128 869T188 887Q203 887 212 881T233 863Q244 851 253 845T275 839Q295
273
+ 839 305 846T325 866T340 878Q348 878 354 868T364 847T369 833Q365 824 350 808T312 777T261 763Q241 763 230 770Z" />
274
+ <glyph unicode="&#xd2;" glyph-name="Ograve" horiz-adv-x="429" d="M143 -10T105 21T48 131T30 354Q30 486 50 564T113 676T222 710Q288 710 326 678T381 569T399 357Q399 220 380 140T319 25T211 -10Q143 -10 105 21ZM251 73T272 101T302 190T312 355Q312 459
275
+ 304 517T276 601T220 627Q184 627 163 599T131 507T121 334Q121 238 129 182T158 99T214 73Q251 73 272 101ZM249 768T198 804T136 855Q140 877 150 896T173 916Q180 916 228 875T307 798Q306 795 302 784T291 763T276 753Q249 768 198 804Z" />
276
+ <glyph unicode="&#xd3;" glyph-name="Oacute" horiz-adv-x="429" d="M143 -10T105 21T48 131T30 354Q30 486 50 564T113 676T222 710Q288 710 326 678T381 569T399 357Q399 220 380 140T319 25T211 -10Q143 -10 105 21ZM251 73T272 101T302 190T312 355Q312 459
277
+ 304 517T276 601T220 627Q184 627 163 599T131 507T121 334Q121 238 129 182T158 99T214 73Q251 73 272 101ZM159 760T152 770T141 791T136 805Q168 841 215 881T270 922Q283 922 293 903T307 861Q295 848 244 811T167 760Q159 760 152 770Z" />
278
+ <glyph unicode="&#xd4;" glyph-name="Ocircumflex" horiz-adv-x="429" d="M143 -10T105 21T48 131T30 354Q30 486 50 564T113 676T222 710Q288 710 326 678T381 569T399 357Q399 220 380 140T319 25T211 -10Q143 -10 105 21ZM251 73T272 101T302 190T312 355Q312
279
+ 459 304 517T276 601T220 627Q184 627 163 599T131 507T121 334Q121 238 129 182T158 99T214 73Q251 73 272 101ZM275 770T251 796T218 838Q214 832 199 814T168 779T143 762Q107 762 88 785L202 908Q210 911 219 911Q233 911 244 899Q251 892 274 867T319 817T341
280
+ 784Q341 774 324 768T284 761Q275 770 251 796Z" />
281
+ <glyph unicode="&#xd5;" glyph-name="Otilde" horiz-adv-x="429" d="M143 -10T105 21T48 131T30 354Q30 486 50 564T113 676T222 710Q288 710 326 678T381 569T399 357Q399 220 380 140T319 25T211 -10Q143 -10 105 21ZM251 73T272 101T302 190T312 355Q312 459
282
+ 304 517T276 601T220 627Q184 627 163 599T131 507T121 334Q121 238 129 182T158 99T214 73Q251 73 272 101ZM234 763T223 770T201 790Q191 801 184 806T166 811Q152 811 143 804T123 785T107 772Q97 772 89 781T75 799T68 812Q92 851 121 869T181 887Q196 887
283
+ 205 881T226 863Q237 851 246 845T268 839Q288 839 298 846T318 866T333 878Q341 878 347 868T357 847T362 833Q358 824 343 808T305 777T254 763Q234 763 223 770Z" />
284
+ <glyph unicode="&#xd6;" glyph-name="Odieresis" horiz-adv-x="429" d="M143 -10T105 21T48 131T30 354Q30 486 50 564T113 676T222 710Q288 710 326 678T381 569T399 357Q399 220 380 140T319 25T211 -10Q143 -10 105 21ZM251 73T272 101T302 190T312 355Q312
285
+ 459 304 517T276 601T220 627Q184 627 163 599T131 507T121 334Q121 238 129 182T158 99T214 73Q251 73 272 101ZM133 773T124 786T114 832Q114 859 125 874T158 889Q180 889 188 877T197 834Q197 804 186 788T153 772Q133 773 124 786ZM253 773T243 785T233 828Q233
286
+ 889 277 889Q299 889 307 877T316 834Q314 772 272 772Q253 773 243 785Z" />
287
+ <glyph unicode="&#xd7;" glyph-name="multiply" horiz-adv-x="418" d="M257 281L345 183Q319 157 309 149T292 140Q290 140 284 144L209 228L133 144Q127 140 125 140Q118 140 108 148T72 183L160 281L71 381Q76 386 90 399T111 418T124 423Q129 423 133 419L209
288
+ 335L284 419Q288 423 293 423Q300 423 308 416T329 398T346 381L257 281Z" />
289
+ <glyph unicode="&#xd8;" glyph-name="Oslash" horiz-adv-x="436" d="M415 685T381 585Q403 506 403 357Q403 220 383 140T322 25T214 -10Q174 -10 145 0T94 35Q81 -1 73 -16Q59 -16 43 -9T24 9Q23 12 35 44T59 102Q33 183 33 354Q33 486 53 564T114 676T222 710Q262
290
+ 710 291 699T341 664Q359 712 363 719Q367 719 378 717T401 709T414 694Q415 685 381 585ZM186 628T165 600T134 507T124 334Q124 228 132 176L281 599Q261 628 222 628Q186 628 165 600ZM247 73T269 95T303 178T315 355Q315 463 307 516Q249 349 157 98Q176 73
291
+ 211 73Q247 73 269 95Z" />
292
+ <glyph unicode="&#xd9;" glyph-name="Ugrave" horiz-adv-x="437" d="M148 -10T109 18T57 91T44 205V683Q44 692 47 695T71 699T127 700V202Q127 133 146 99T224 65Q249 65 274 70T312 82V683Q312 692 316 695T340 699T394 700V26Q373 12 326 1T224 -10Q148 -10
293
+ 109 18ZM253 768T202 804T140 855Q144 877 154 896T177 916Q184 916 232 875T311 798Q310 795 306 784T295 763T280 753Q253 768 202 804Z" />
294
+ <glyph unicode="&#xda;" glyph-name="Uacute" horiz-adv-x="437" d="M148 -10T109 18T57 91T44 205V683Q44 692 47 695T71 699T127 700V202Q127 133 146 99T224 65Q249 65 274 70T312 82V683Q312 692 316 695T340 699T394 700V26Q373 12 326 1T224 -10Q148 -10
295
+ 109 18ZM163 760T156 770T145 791T140 805Q172 841 219 881T274 922Q287 922 297 903T311 861Q299 848 248 811T171 760Q163 760 156 770Z" />
296
+ <glyph unicode="&#xdb;" glyph-name="Ucircumflex" horiz-adv-x="437" d="M148 -10T109 18T57 91T44 205V683Q44 692 47 695T71 699T127 700V202Q127 133 146 99T224 65Q249 65 274 70T312 82V683Q312 692 316 695T340 699T394 700V26Q373 12 326 1T224 -10Q148
297
+ -10 109 18ZM279 770T255 796T222 838Q218 832 203 814T172 779T147 762Q111 762 92 785L206 908Q214 911 223 911Q237 911 248 899Q255 892 278 867T323 817T345 784Q345 774 328 768T288 761Q279 770 255 796Z" />
298
+ <glyph unicode="&#xdc;" glyph-name="Udieresis" horiz-adv-x="437" d="M148 -10T109 18T57 91T44 205V683Q44 692 47 695T71 699T127 700V202Q127 133 146 99T224 65Q249 65 274 70T312 82V683Q312 692 316 695T340 699T394 700V26Q373 12 326 1T224 -10Q148
299
+ -10 109 18ZM137 773T128 786T118 832Q118 859 129 874T162 889Q184 889 192 877T201 834Q201 804 190 788T157 772Q137 773 128 786ZM257 773T247 785T237 828Q237 889 281 889Q303 889 311 877T320 834Q318 772 276 772Q257 773 247 785Z" />
300
+ <glyph unicode="&#xdd;" glyph-name="Yacute" horiz-adv-x="364" d="M225 16Q225 7 222 4T198 1T144 0V285L6 687Q4 694 8 696T35 699T97 700L137 563Q152 516 168 446T187 353Q189 375 206 445T238 563L273 685Q275 693 279 696T304 699T360 700L225 290V16ZM126
301
+ 760T119 770T108 791T103 805Q135 841 182 881T237 922Q250 922 260 903T274 861Q262 848 211 811T134 760Q126 760 119 770Z" />
302
+ <glyph unicode="&#xde;" glyph-name="Thorn" horiz-adv-x="361" d="M44 680Q44 693 54 696T98 700H124V566H159Q254 566 298 519T343 369Q343 253 293 202T160 150H124V16Q124 7 120 4T96 1T44 0V680ZM212 225T233 260T254 378Q254 435 236 465T174 495Q139 495
303
+ 124 493V228Q131 228 144 227T168 225Q212 225 233 260Z" />
304
+ <glyph unicode="&#xdf;" glyph-name="germandbls" horiz-adv-x="465" d="M65 -9T52 -4T39 2Q40 4 42 13T47 38T50 74L48 550Q48 629 90 667T208 705Q277 705 322 685T388 631T409 561Q409 529 394 510T348 467Q323 449 312 436T300 403Q300 385 313 368T355 325Q387
305
+ 294 407 270T441 213T456 138Q456 70 416 31T306 -9Q269 -9 242 -2T201 15T187 36Q187 69 208 90Q217 81 238 72T288 63Q324 63 345 82T366 137Q366 172 347 201T288 270Q250 309 231 337T212 399Q212 432 228 452T276 497Q300 515 311 528T323 560Q323 589 295
306
+ 609T214 630Q166 630 148 601T129 504L131 44Q131 14 122 3T84 -9Q65 -9 52 -4Z" />
307
+ <glyph unicode="&#xe0;" glyph-name="agrave" horiz-adv-x="354" d="M102 -10T66 20T29 142Q29 237 65 271T163 306Q210 306 236 293V372Q236 406 221 419T174 432Q138 432 101 426T41 409Q30 430 30 456Q30 476 41 483Q56 493 99 501T194 510Q259 510 288 482T317
308
+ 388V335Q317 115 311 24Q287 9 250 0T166 -10Q102 -10 66 20ZM196 58T211 63T233 74Q236 108 236 236Q217 246 189 246Q153 246 133 225T113 144Q113 93 129 76T180 58Q196 58 211 63ZM209 568T158 604T96 655Q100 677 110 696T133 716Q140 716 188 675T267 598Q266
309
+ 595 262 584T251 563T236 553Q209 568 158 604Z" />
310
+ <glyph unicode="&#xe1;" glyph-name="aacute" horiz-adv-x="354" d="M102 -10T66 20T29 142Q29 237 65 271T163 306Q210 306 236 293V372Q236 406 221 419T174 432Q138 432 101 426T41 409Q30 430 30 456Q30 476 41 483Q56 493 99 501T194 510Q259 510 288 482T317
311
+ 388V335Q317 115 311 24Q287 9 250 0T166 -10Q102 -10 66 20ZM196 58T211 63T233 74Q236 108 236 236Q217 246 189 246Q153 246 133 225T113 144Q113 93 129 76T180 58Q196 58 211 63ZM119 560T112 570T101 591T96 605Q128 641 175 681T230 722Q243 722 253 703T267
312
+ 661Q255 648 204 611T127 560Q119 560 112 570Z" />
313
+ <glyph unicode="&#xe2;" glyph-name="acircumflex" horiz-adv-x="354" d="M102 -10T66 20T29 142Q29 237 65 271T163 306Q210 306 236 293V372Q236 406 221 419T174 432Q138 432 101 426T41 409Q30 430 30 456Q30 476 41 483Q56 493 99 501T194 510Q259 510 288
314
+ 482T317 388V335Q317 115 311 24Q287 9 250 0T166 -10Q102 -10 66 20ZM196 58T211 63T233 74Q236 108 236 236Q217 246 189 246Q153 246 133 225T113 144Q113 93 129 76T180 58Q196 58 211 63ZM235 570T211 596T178 638Q174 632 159 614T128 579T103 562Q67 562
315
+ 48 585L162 708Q170 711 179 711Q193 711 204 699Q211 692 234 667T279 617T301 584Q301 574 284 568T244 561Q235 570 211 596Z" />
316
+ <glyph unicode="&#xe3;" glyph-name="atilde" horiz-adv-x="354" d="M102 -10T66 20T29 142Q29 237 65 271T163 306Q210 306 236 293V372Q236 406 221 419T174 432Q138 432 101 426T41 409Q30 430 30 456Q30 476 41 483Q56 493 99 501T194 510Q259 510 288 482T317
317
+ 388V335Q317 115 311 24Q287 9 250 0T166 -10Q102 -10 66 20ZM196 58T211 63T233 74Q236 108 236 236Q217 246 189 246Q153 246 133 225T113 144Q113 93 129 76T180 58Q196 58 211 63ZM194 563T183 570T161 590Q151 601 144 606T126 611Q112 611 103 604T83 585T67
318
+ 572Q57 572 49 581T35 599T28 612Q52 651 81 669T141 687Q156 687 165 681T186 663Q197 651 206 645T228 639Q248 639 258 646T278 666T293 678Q301 678 307 668T317 647T322 633Q318 624 303 608T265 577T214 563Q194 563 183 570Z" />
319
+ <glyph unicode="&#xe4;" glyph-name="adieresis" horiz-adv-x="354" d="M102 -10T66 20T29 142Q29 237 65 271T163 306Q210 306 236 293V372Q236 406 221 419T174 432Q138 432 101 426T41 409Q30 430 30 456Q30 476 41 483Q56 493 99 501T194 510Q259 510 288
320
+ 482T317 388V335Q317 115 311 24Q287 9 250 0T166 -10Q102 -10 66 20ZM196 58T211 63T233 74Q236 108 236 236Q217 246 189 246Q153 246 133 225T113 144Q113 93 129 76T180 58Q196 58 211 63ZM93 573T84 586T74 632Q74 659 85 674T118 689Q140 689 148 677T157
321
+ 634Q157 604 146 588T113 572Q93 573 84 586ZM213 573T203 585T193 628Q193 689 237 689Q259 689 267 677T276 634Q274 572 232 572Q213 573 203 585Z" />
322
+ <glyph unicode="&#xe5;" glyph-name="aring" horiz-adv-x="354" d="M102 -10T66 20T29 142Q29 237 65 271T163 306Q210 306 236 293V372Q236 406 221 419T174 432Q138 432 101 426T41 409Q30 430 30 456Q30 476 41 483Q56 493 99 501T194 510Q259 510 288 482T317
323
+ 388V335Q317 115 311 24Q287 9 250 0T166 -10Q102 -10 66 20ZM196 58T211 63T233 74Q236 108 236 236Q217 246 189 246Q153 246 133 225T113 144Q113 93 129 76T180 58Q196 58 211 63ZM125 543T102 564T79 639Q79 688 105 712T179 737Q227 737 249 716T271 640Q271
324
+ 592 246 568T172 543Q125 543 102 564ZM215 597T215 640Q215 662 206 671T179 681Q136 681 136 638Q136 618 145 608T174 597Q215 597 215 640Z" />
325
+ <glyph unicode="&#xe6;" glyph-name="ae" horiz-adv-x="583" d="M478 69T532 100Q539 79 539 28Q539 21 520 13T468 -2T401 -9Q367 -9 344 -3T302 20Q255 -9 164 -9Q122 -9 94 1T47 46T29 148Q29 319 160 319Q210 319 236 306V378Q236 412 218 425T168 438Q93
326
+ 438 42 411Q32 429 32 459Q32 466 35 472T42 483Q58 494 98 502T197 510Q238 510 262 497T296 453Q317 487 346 498T418 510Q545 510 545 351Q545 270 534 220Q497 214 445 214Q383 214 324 225Q324 157 333 124T360 80T410 69Q478 69 532 100ZM376 439T354 406T326
327
+ 289Q388 277 426 277Q446 277 462 280Q467 303 467 342Q466 397 454 418T409 439Q376 439 354 406ZM236 250Q217 258 189 258Q154 258 134 235T113 149Q113 110 121 90T142 64T178 58Q195 58 212 62T236 74V250Z" />
328
+ <glyph unicode="&#xe7;" glyph-name="ccedilla" horiz-adv-x="313" d="M227 62T248 68T284 85Q292 56 292 41Q292 20 262 6T184 -10L174 -59Q175 -58 191 -58Q211 -58 225 -74T240 -122Q240 -160 218 -183T160 -206Q141 -206 123 -200T104 -193Q105 -188 109 -172T117
329
+ -152Q120 -149 135 -156T162 -163Q193 -163 193 -129Q193 -95 163 -95Q147 -95 136 -97T122 -101L139 -7Q102 -1 79 23T43 99T31 239Q31 400 81 455T210 510Q246 510 270 497T294 465Q294 442 285 420Q251 439 214 439Q170 439 142 399T114 238Q114 169 123 131T152
330
+ 77T204 62Q227 62 248 68Z" />
331
+ <glyph unicode="&#xe8;" glyph-name="egrave" horiz-adv-x="361" d="M267 68T321 100Q328 78 328 45Q328 22 288 6T190 -10Q139 -10 105 9T50 79T30 224Q30 341 54 404T117 488T207 510Q334 510 334 350Q334 270 323 220Q279 211 221 208T113 204Q115 150 125
332
+ 121T155 80T207 68Q267 68 321 100ZM162 439T140 402T114 268Q213 268 251 280Q256 303 256 340Q255 396 243 417T198 439Q162 439 140 402ZM223 568T172 604T110 655Q114 677 124 696T147 716Q154 716 202 675T281 598Q280 595 276 584T265 563T250 553Q223 568
333
+ 172 604Z" />
334
+ <glyph unicode="&#xe9;" glyph-name="eacute" horiz-adv-x="361" d="M267 68T321 100Q328 78 328 45Q328 22 288 6T190 -10Q139 -10 105 9T50 79T30 224Q30 341 54 404T117 488T207 510Q334 510 334 350Q334 270 323 220Q279 211 221 208T113 204Q115 150 125
335
+ 121T155 80T207 68Q267 68 321 100ZM162 439T140 402T114 268Q213 268 251 280Q256 303 256 340Q255 396 243 417T198 439Q162 439 140 402ZM133 560T126 570T115 591T110 605Q142 641 189 681T244 722Q257 722 267 703T281 661Q269 648 218 611T141 560Q133 560
336
+ 126 570Z" />
337
+ <glyph unicode="&#xea;" glyph-name="ecircumflex" horiz-adv-x="361" d="M267 68T321 100Q328 78 328 45Q328 22 288 6T190 -10Q139 -10 105 9T50 79T30 224Q30 341 54 404T117 488T207 510Q334 510 334 350Q334 270 323 220Q279 211 221 208T113 204Q115 150
338
+ 125 121T155 80T207 68Q267 68 321 100ZM162 439T140 402T114 268Q213 268 251 280Q256 303 256 340Q255 396 243 417T198 439Q162 439 140 402ZM249 570T225 596T192 638Q188 632 173 614T142 579T117 562Q81 562 62 585L176 708Q184 711 193 711Q207 711 218
339
+ 699Q225 692 248 667T293 617T315 584Q315 574 298 568T258 561Q249 570 225 596Z" />
340
+ <glyph unicode="&#xeb;" glyph-name="edieresis" horiz-adv-x="361" d="M267 68T321 100Q328 78 328 45Q328 22 288 6T190 -10Q139 -10 105 9T50 79T30 224Q30 341 54 404T117 488T207 510Q334 510 334 350Q334 270 323 220Q279 211 221 208T113 204Q115 150 125
341
+ 121T155 80T207 68Q267 68 321 100ZM162 439T140 402T114 268Q213 268 251 280Q256 303 256 340Q255 396 243 417T198 439Q162 439 140 402ZM107 573T98 586T88 632Q88 659 99 674T132 689Q154 689 162 677T171 634Q171 604 160 588T127 572Q107 573 98 586ZM227
342
+ 573T217 585T207 628Q207 689 251 689Q273 689 281 677T290 634Q288 572 246 572Q227 573 217 585Z" />
343
+ <glyph unicode="&#xec;" glyph-name="igrave" horiz-adv-x="175" d="M47 463Q47 482 53 489T75 498T128 500V16Q128 7 125 4T101 1T47 0V463ZM377 568T326 604T264 655Q268 677 278 696T301 716Q308 716 356 675T435 598Q434 595 430 584T419 563T404 553Q377
344
+ 568 326 604Z" />
345
+ <glyph unicode="&#xed;" glyph-name="iacute" horiz-adv-x="175" d="M47 463Q47 482 53 489T75 498T128 500V16Q128 7 125 4T101 1T47 0V463ZM31 560T24 570T13 591T8 605Q40 641 87 681T142 722Q155 722 165 703T179 661Q167 648 116 611T39 560Q31 560 24 570Z" />
346
+ <glyph unicode="&#xee;" glyph-name="icircumflex" horiz-adv-x="175" d="M47 463Q47 482 53 489T75 498T128 500V16Q128 7 125 4T101 1T47 0V463ZM147 570T123 596T90 638Q86 632 71 614T40 579T15 562Q-21 562 -40 585L74 708Q82 711 91 711Q105 711 116 699Q123
347
+ 692 146 667T191 617T213 584Q213 574 196 568T156 561Q147 570 123 596Z" />
348
+ <glyph unicode="&#xef;" glyph-name="idieresis" horiz-adv-x="175" d="M47 463Q47 482 53 489T75 498T128 500V16Q128 7 125 4T101 1T47 0V463ZM261 573T252 586T242 632Q242 659 253 674T286 689Q308 689 316 677T325 634Q325 604 314 588T281 572Q261 573 252
349
+ 586ZM381 573T371 585T361 628Q361 689 405 689Q427 689 435 677T444 634Q442 572 400 572Q381 573 371 585Z" />
350
+ <glyph unicode="&#xf0;" glyph-name="eth" horiz-adv-x="408" d="M380 504T380 289Q380 149 344 70T209 -10Q116 -10 73 51T30 257Q30 378 73 443T190 509Q244 509 278 480Q260 547 224 602L129 565Q121 585 116 600T111 623Q111 633 119 637L177 659Q163 675
351
+ 125 707Q134 724 150 734T183 744Q203 744 213 732Q233 716 257 690L326 717Q334 697 339 682T344 659Q344 649 336 646L300 632Q380 504 380 289ZM263 58T280 116T298 286Q298 349 293 388Q264 437 212 437Q163 437 138 383T112 244Q112 58 207 58Q263 58 280
352
+ 116Z" />
353
+ <glyph unicode="&#xf1;" glyph-name="ntilde" horiz-adv-x="383" d="M45 476Q76 490 121 500T204 510Q282 510 313 480T344 376V16Q344 7 340 4T316 1T261 0V352Q261 402 249 421T201 440Q183 440 162 436T126 422V16Q126 7 122 4T98 1T45 0V476ZM211 563T200
354
+ 570T178 590Q168 601 161 606T143 611Q129 611 120 604T100 585T84 572Q74 572 66 581T52 599T45 612Q69 651 98 669T158 687Q173 687 182 681T203 663Q214 651 223 645T245 639Q265 639 275 646T295 666T310 678Q318 678 324 668T334 647T339 633Q335 624 320
355
+ 608T282 577T231 563Q211 563 200 570Z" />
356
+ <glyph unicode="&#xf2;" glyph-name="ograve" horiz-adv-x="376" d="M128 -10T95 9T45 84T27 250Q27 510 194 510Q249 510 282 488T332 409T349 252Q349 116 308 53T184 -10Q128 -10 95 9ZM222 58T241 100T261 252Q261 330 254 371T231 427T191 442Q154 442 135
357
+ 400T115 248Q115 168 123 127T146 72T186 58Q222 58 241 100ZM224 568T173 604T111 655Q115 677 125 696T148 716Q155 716 203 675T282 598Q281 595 277 584T266 563T251 553Q224 568 173 604Z" />
358
+ <glyph unicode="&#xf3;" glyph-name="oacute" horiz-adv-x="376" d="M128 -10T95 9T45 84T27 250Q27 510 194 510Q249 510 282 488T332 409T349 252Q349 116 308 53T184 -10Q128 -10 95 9ZM222 58T241 100T261 252Q261 330 254 371T231 427T191 442Q154 442 135
359
+ 400T115 248Q115 168 123 127T146 72T186 58Q222 58 241 100ZM134 560T127 570T116 591T111 605Q143 641 190 681T245 722Q258 722 268 703T282 661Q270 648 219 611T142 560Q134 560 127 570Z" />
360
+ <glyph unicode="&#xf4;" glyph-name="ocircumflex" horiz-adv-x="376" d="M128 -10T95 9T45 84T27 250Q27 510 194 510Q249 510 282 488T332 409T349 252Q349 116 308 53T184 -10Q128 -10 95 9ZM222 58T241 100T261 252Q261 330 254 371T231 427T191 442Q154 442
361
+ 135 400T115 248Q115 168 123 127T146 72T186 58Q222 58 241 100ZM250 570T226 596T193 638Q189 632 174 614T143 579T118 562Q82 562 63 585L177 708Q185 711 194 711Q208 711 219 699Q226 692 249 667T294 617T316 584Q316 574 299 568T259 561Q250 570 226 596Z"
362
+ />
363
+ <glyph unicode="&#xf5;" glyph-name="otilde" horiz-adv-x="376" d="M128 -10T95 9T45 84T27 250Q27 510 194 510Q249 510 282 488T332 409T349 252Q349 116 308 53T184 -10Q128 -10 95 9ZM222 58T241 100T261 252Q261 330 254 371T231 427T191 442Q154 442 135
364
+ 400T115 248Q115 168 123 127T146 72T186 58Q222 58 241 100ZM209 563T198 570T176 590Q166 601 159 606T141 611Q127 611 118 604T98 585T82 572Q72 572 64 581T50 599T43 612Q67 651 96 669T156 687Q171 687 180 681T201 663Q212 651 221 645T243 639Q263 639
365
+ 273 646T293 666T308 678Q316 678 322 668T332 647T337 633Q333 624 318 608T280 577T229 563Q209 563 198 570Z" />
366
+ <glyph unicode="&#xf6;" glyph-name="odieresis" horiz-adv-x="376" d="M128 -10T95 9T45 84T27 250Q27 510 194 510Q249 510 282 488T332 409T349 252Q349 116 308 53T184 -10Q128 -10 95 9ZM222 58T241 100T261 252Q261 330 254 371T231 427T191 442Q154 442
367
+ 135 400T115 248Q115 168 123 127T146 72T186 58Q222 58 241 100ZM108 573T99 586T89 632Q89 659 100 674T133 689Q155 689 163 677T172 634Q172 604 161 588T128 572Q108 573 99 586ZM228 573T218 585T208 628Q208 689 252 689Q274 689 282 677T291 634Q289 572
368
+ 247 572Q228 573 218 585Z" />
369
+ <glyph unicode="&#xf7;" glyph-name="divide" horiz-adv-x="418" d="M181 376T170 389T159 427Q159 448 172 463T208 478Q258 478 258 427Q258 405 245 391T208 376Q181 376 170 389ZM34 290T36 303T51 317H384Q384 275 382 261T368 247H34Q34 290 36 303ZM181
370
+ 81T170 94T159 132Q159 153 172 168T208 183Q258 183 258 132Q258 110 245 96T208 81Q181 81 170 94Z" />
371
+ <glyph unicode="&#xf8;" glyph-name="oslash" horiz-adv-x="412" d="M403 484T388 454T356 395Q369 343 369 252Q369 116 329 53T207 -10Q167 -10 140 -2T93 29L62 -26Q46 -23 32 -14T17 8Q17 14 63 92Q48 149 48 250Q48 510 213 510Q250 510 277 501T323 468Q351
372
+ 515 357 522Q360 521 371 517T393 506T403 489Q403 484 388 454ZM174 440T155 399T135 248Q135 216 137 176L266 404Q257 425 244 432T211 440Q174 440 155 399ZM243 58T262 100T282 252Q282 298 279 331Q275 323 194 179L150 101Q158 76 172 67T207 58Q243 58
373
+ 262 100Z" />
374
+ <glyph unicode="&#xf9;" glyph-name="ugrave" horiz-adv-x="389" d="M135 -10T103 8T58 61T45 156V484Q45 493 48 496T72 499T128 500V149Q128 98 141 79T196 60Q236 60 263 79V484Q263 493 267 496T290 499T344 500V26Q314 10 274 0T194 -10Q135 -10 103 8ZM229
375
+ 568T178 604T116 655Q120 677 130 696T153 716Q160 716 208 675T287 598Q286 595 282 584T271 563T256 553Q229 568 178 604Z" />
376
+ <glyph unicode="&#xfa;" glyph-name="uacute" horiz-adv-x="389" d="M135 -10T103 8T58 61T45 156V484Q45 493 48 496T72 499T128 500V149Q128 98 141 79T196 60Q236 60 263 79V484Q263 493 267 496T290 499T344 500V26Q314 10 274 0T194 -10Q135 -10 103 8ZM139
377
+ 560T132 570T121 591T116 605Q148 641 195 681T250 722Q263 722 273 703T287 661Q275 648 224 611T147 560Q139 560 132 570Z" />
378
+ <glyph unicode="&#xfb;" glyph-name="ucircumflex" horiz-adv-x="389" d="M135 -10T103 8T58 61T45 156V484Q45 493 48 496T72 499T128 500V149Q128 98 141 79T196 60Q236 60 263 79V484Q263 493 267 496T290 499T344 500V26Q314 10 274 0T194 -10Q135 -10 103
379
+ 8ZM255 570T231 596T198 638Q194 632 179 614T148 579T123 562Q87 562 68 585L182 708Q190 711 199 711Q213 711 224 699Q231 692 254 667T299 617T321 584Q321 574 304 568T264 561Q255 570 231 596Z" />
380
+ <glyph unicode="&#xfc;" glyph-name="udieresis" horiz-adv-x="389" d="M135 -10T103 8T58 61T45 156V484Q45 493 48 496T72 499T128 500V149Q128 98 141 79T196 60Q236 60 263 79V484Q263 493 267 496T290 499T344 500V26Q314 10 274 0T194 -10Q135 -10 103 8ZM113
381
+ 573T104 586T94 632Q94 659 105 674T138 689Q160 689 168 677T177 634Q177 604 166 588T133 572Q113 573 104 586ZM233 573T223 585T213 628Q213 689 257 689Q279 689 287 677T296 634Q294 572 252 572Q233 573 223 585Z" />
382
+ <glyph unicode="&#xfd;" glyph-name="yacute" horiz-adv-x="357" d="M-4 -193T-4 -154Q-4 -142 -1 -130T8 -109Q18 -116 35 -120T71 -124Q97 -124 115 -100T144 -16L24 478Q22 486 22 489Q22 497 35 498T102 500L157 260Q167 211 174 156T184 83Q186 99 192 154T208
383
+ 261L253 477Q255 490 259 494T279 499T340 500L217 -27Q202 -91 184 -127T140 -178T77 -193Q-4 -193 -4 -154ZM122 560T115 570T104 591T99 605Q131 641 178 681T233 722Q246 722 256 703T270 661Q258 648 207 611T130 560Q122 560 115 570Z" />
384
+ <glyph unicode="&#xfe;" glyph-name="thorn" d="M273 509T314 463T355 300Q355 123 309 57T203 -10Q153 -10 127 16V-194Q108 -200 79 -200Q58 -200 52 -194T46 -166V698Q46 717 52 724T74 733T127 735V502Q156 509 192 509Q273 509 314 463ZM212 60T239 109T266
385
+ 293Q266 354 257 385T232 428T188 439Q154 439 127 418V88Q143 59 178 59Q212 60 239 109Z" />
386
+ <glyph unicode="&#xff;" glyph-name="ydieresis" horiz-adv-x="357" d="M-4 -193T-4 -154Q-4 -142 -1 -130T8 -109Q18 -116 35 -120T71 -124Q97 -124 115 -100T144 -16L24 478Q22 486 22 489Q22 497 35 498T102 500L157 260Q167 211 174 156T184 83Q186 99 192
387
+ 154T208 261L253 477Q255 490 259 494T279 499T340 500L217 -27Q202 -91 184 -127T140 -178T77 -193Q-4 -193 -4 -154ZM96 573T87 586T77 632Q77 659 88 674T121 689Q143 689 151 677T160 634Q160 604 149 588T116 572Q96 573 87 586ZM216 573T206 585T196 628Q196
388
+ 689 240 689Q262 689 270 677T279 634Q277 572 235 572Q216 573 206 585Z" />
389
+ <glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="424" d="M37 290T39 303T54 317H387Q387 275 385 261T371 247H37Q37 290 39 303Z" />
390
+ <glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="700" d="M6 290T8 303T22 317H694Q694 275 692 261T678 247H6Q6 290 8 303Z" />
391
+ <glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="174" d="M34 575T34 634Q34 668 48 705T80 769T108 795Q135 795 140 772Q127 747 122 725T115 676Q138 661 138 627Q138 605 124 590T88 575Q34 575 34 634Z" />
392
+ <glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="174" d="M41 572T34 595Q48 621 52 642T59 692Q36 706 36 740Q36 763 50 777T86 792Q141 792 141 733Q141 699 127 662T94 598T66 572Q41 572 34 595Z" />
393
+ <glyph unicode="&#x201a;" glyph-name="quotesinglbase" horiz-adv-x="170" d="M41 -114T34 -91Q48 -66 52 -45T59 5Q36 19 36 54Q36 76 50 91T86 106Q141 106 141 47Q141 13 127 -24T94 -88T66 -114Q41 -114 34 -91Z" />
394
+ <glyph unicode="&#x201c;" glyph-name="quotedblleft" horiz-adv-x="324" d="M34 575T34 634Q34 668 48 705T80 769T108 795Q135 795 140 772Q127 747 122 725T115 676Q138 661 138 627Q138 605 124 590T88 575Q34 575 34 634ZM184 575T184 634Q184 668 198 705T230
395
+ 769T258 795Q285 795 290 772Q276 747 272 726T265 676Q288 661 288 627Q288 605 274 590T238 575Q184 575 184 634Z" />
396
+ <glyph unicode="&#x201d;" glyph-name="quotedblright" horiz-adv-x="324" d="M41 572T34 595Q48 621 52 642T59 692Q36 706 36 740Q36 763 50 777T86 792Q141 792 141 733Q141 699 127 662T94 598T66 572Q41 572 34 595ZM189 572T184 595Q198 621 202 642T209
397
+ 692Q186 706 186 740Q186 763 200 777T236 792Q291 792 291 733Q291 699 277 662T244 598T216 572Q189 572 184 595Z" />
398
+ <glyph unicode="&#x201e;" glyph-name="quotedblbase" horiz-adv-x="321" d="M41 -114T34 -91Q48 -66 52 -45T59 5Q36 19 36 54Q36 76 50 91T86 106Q141 106 141 47Q141 13 127 -24T94 -88T66 -114Q41 -114 34 -91ZM189 -114T184 -91Q198 -66 202 -45T209 5Q186
399
+ 19 186 54Q186 76 200 91T236 106Q291 106 291 47Q291 13 277 -24T244 -88T216 -114Q189 -114 184 -91Z" />
400
+ <glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="223" d="M75 208T58 227T41 284Q41 316 61 338T114 361Q153 361 171 341T189 284Q189 251 169 230T114 208Q75 208 58 227Z" />
401
+ <glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="247" d="M156 16T120 69T51 178T18 250Q18 265 50 320T118 430T164 500Q178 500 188 491T205 472T212 459Q212 442 186 396T132 306T94 250Q103 238 132 196T187 109T214 47Q213 43 207 32T191
402
+ 11T167 0Q156 16 120 69Z" />
403
+ <glyph unicode="&#x203a;" glyph-name="guilsinglright" horiz-adv-x="247" d="M77 1T66 6T44 21T33 46Q33 62 59 107T115 195T153 250Q144 263 116 306T61 395T35 459Q35 471 45 480T67 494T83 500Q94 484 129 430T196 321T229 250Q229 234 196 179T128 70T81
404
+ 0Q77 1 66 6Z" />
405
+ </font>
406
+ </defs>
407
+ </svg>
themes/default/fonts/yanone-kaffeesatz-v9-latin-regular.ttf ADDED
Binary file
themes/default/fonts/yanone-kaffeesatz-v9-latin-regular.woff ADDED
Binary file
themes/default/fonts/yanone-kaffeesatz-v9-latin-regular.woff2 ADDED
Binary file
themes/default/js/dev/client.js ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ if ( !!window.SLB && SLB.has_child('View.extend_theme') ) {(function($) {
2
+ SLB.View.extend_theme('slb_default', {
3
+ /**
4
+ * Transition event handlers
5
+ */
6
+ 'transition': {
7
+ /**
8
+ * Open event
9
+ * @param View.Viewer v Viewer instance
10
+ * @param deferred dfr Resolved when transition is complete
11
+ * @return promise Resolved when transition is complete
12
+ */
13
+ 'open': function(v, dfr) {
14
+ // Reset layout and overlay state on open
15
+ var l = v.get_layout().hide(),
16
+ o = v.get_overlay().hide();
17
+
18
+ // Clean UI
19
+ var thm = this;
20
+ var d = v.dom_get();
21
+ d.find('.slb_content').css({width: '', height: ''}).find(this.get_tag_selector()).hide();
22
+ d.find('.slb_details').height(0);
23
+ // Show viewer DOM
24
+ d.show({'always': function() {
25
+ var pos = {'top_base': $(document).scrollTop()};
26
+ if ( document.documentElement.clientWidth > thm.get_breakpoint('small') ) {
27
+ /* Standard screen */
28
+ // Center vertically
29
+ pos.top = ( pos.top_base + $(window).height() / 2 ) - ( l.height() / 2 );
30
+ if ( pos.top < pos.top_base ) {
31
+ pos.top = pos.top_base;
32
+ }
33
+ } else {
34
+ /* Small screen */
35
+ // Position at top
36
+ pos.top = pos.top_base;
37
+ }
38
+ // Show overlay
39
+ o.fadeIn({'always': function() {
40
+ // Position lightbox
41
+ l.css(pos);
42
+ dfr.resolve();
43
+ }});
44
+ }});
45
+ return dfr.promise();
46
+ },
47
+ /**
48
+ * Close event
49
+ * @param View.Viewer v Viewer instance
50
+ * @param jQuery.Deferred dfr Deferred instance to be resolved when animation is complete
51
+ * @return jQuery.Promise Resolved when transition is complete
52
+ */
53
+ 'close': function(v, dfr) {
54
+ // Viewer elements
55
+ var l = v.get_layout(),
56
+ c = l.find('.slb_content');
57
+ // Reset procedure
58
+ var reset = function() {
59
+ // Reset state
60
+ c.width('').height('');
61
+ l.css('opacity', '');
62
+ dfr.resolve();
63
+ };
64
+ if ( v.animation_enabled() && document.documentElement.clientWidth > this.get_breakpoint('small') ) {
65
+ /* Standard */
66
+ var anims = {
67
+ 'layout': { opacity: 0, top: $(document).scrollTop() + ( $(window).height() / 2 ) },
68
+ 'content': { width: 0, height: 0 },
69
+ 'speed': 'fast'
70
+ };
71
+ // Shrink & fade out viewer
72
+ var pos = l.animate(anims.layout, anims.speed).promise();
73
+ var size = c.animate(anims.content, anims.speed).promise();
74
+ $.when(pos, size).done(function() {
75
+ // Fade out overlay
76
+ v.get_overlay().fadeOut({'always': function() {
77
+ reset();
78
+ }});
79
+ });
80
+ } else {
81
+ l.css('opacity', 0);
82
+ reset();
83
+ }
84
+ return dfr.promise();
85
+ },
86
+ /**
87
+ * Item loading event
88
+ * @param View.Viewer v Viewer instance
89
+ * @param deferred dfr Resolved when animation is complete
90
+ * @return promise Resolved when transition is complete
91
+ */
92
+ 'load': function(v) {
93
+ v.get_layout().find('.slb_loading').show();
94
+ if ( document.documentElement.clientWidth > this.get_breakpoint('small') ) {
95
+ return v.get_layout().fadeIn().promise();
96
+ } else {
97
+ return v.get_layout().show().promise();
98
+ }
99
+ },
100
+ /**
101
+ * Item unloaded event
102
+ * @param View.Viewer v Viewer instance
103
+ * @param jQuery.Deferred dfr Deferred instance to be resolved when animation is complete
104
+ * @return jQuery.Promise Resolved when transition is complete
105
+ */
106
+ 'unload': function(v, dfr) {
107
+ var l = v.get_layout(),
108
+ det = l.find('.slb_details'),
109
+ cont = l.find('.slb_content ' + this.get_tag_selector());
110
+ var props = {height: 0};
111
+ // Hide details
112
+ det.css(props);
113
+ // Hide content
114
+ cont.hide();
115
+ // Finish
116
+ $.when(det.promise(), cont.promise()).done(function() {
117
+ dfr.resolve();
118
+ });
119
+ return dfr.promise();
120
+ },
121
+ /**
122
+ * Item loading completed event
123
+ * @param View.Viewer v Viewer instance
124
+ * @param jQuery.Deferred dfr Deferred instance to be resolved when animation is complete
125
+ * @return jQuery.Promise Resolved when transition is complete
126
+ */
127
+ 'complete': function(v, dfr) {
128
+ // Elements
129
+ var l = v.get_layout(),
130
+ loader = l.find('.slb_loading'),
131
+ det = l.find('.slb_details'),
132
+ det_data = det.find('.slb_data'),
133
+ c = l.find('.slb_content'),
134
+ c_tag = c.find( this.get_tag_selector() );
135
+ // Transition
136
+ if ( document.documentElement.clientWidth > this.get_breakpoint('small') ) {
137
+ var spd = 'fast';
138
+ // Resize viewer to fit item
139
+ var dims_item = this.get_item_dimensions();
140
+ // Determine details height
141
+ det.width(dims_item.width);
142
+ var dims_det = {'height': det_data.outerHeight()};
143
+ // Reset width
144
+ det.width('');
145
+ // Determine vertical positioning (centered)
146
+ var pos = { 'top_base': $(document).scrollTop() };
147
+ // Center vertically
148
+ pos.top = pos.top_base + ( $(window).height() / 2 ) - ( ( dims_det.height + dims_item.height ) / 2 );
149
+ if ( pos.top < pos.top_base ) {
150
+ pos.top = pos.top_base;
151
+ }
152
+
153
+ // Position/Resize viewer
154
+ pos = l.animate(pos, spd).promise();
155
+ dims_item = c.animate(dims_item, spd).promise();
156
+ // Display elements
157
+ var thm = this;
158
+ $.when(pos, dims_item).done(function() {
159
+ // Hide loading indicator
160
+ loader.fadeOut(spd, function() {
161
+ // Display content
162
+ c.find( thm.get_tag_selector('item', 'content') ).fadeIn(function() {
163
+ // Show UI
164
+ c_tag.show();
165
+ // Show details
166
+ det.animate({'height': det_data.outerHeight()}, 'slow').promise().done(function() {
167
+ det.height('');
168
+ dfr.resolve();
169
+ });
170
+ });
171
+ });
172
+ });
173
+ } else {
174
+ loader.hide();
175
+ c_tag.show();
176
+ det.height('');
177
+ dfr.resolve();
178
+ }
179
+ return dfr.promise();
180
+ }
181
+ }
182
+ });
183
+ })(jQuery);
184
+ }
themes/default/js/prod/client.js ADDED
@@ -0,0 +1 @@
 
1
+ window.SLB&&SLB.has_child("View.extend_theme")&&!function($){SLB.View.extend_theme("slb_default",{transition:{open:function(v,dfr){var l=v.get_layout().hide(),o=v.get_overlay().hide(),thm=this,d=v.dom_get();return d.find(".slb_content").css({width:"",height:""}).find(this.get_tag_selector()).hide(),d.find(".slb_details").height(0),d.show({always:function(){var pos={top_base:$(document).scrollTop()};document.documentElement.clientWidth>thm.get_breakpoint("small")?(pos.top=pos.top_base+$(window).height()/2-l.height()/2,pos.top<pos.top_base&&(pos.top=pos.top_base)):pos.top=pos.top_base,o.fadeIn({always:function(){l.css(pos),dfr.resolve()}})}}),dfr.promise()},close:function(v,dfr){var l=v.get_layout(),c=l.find(".slb_content"),reset=function(){c.width("").height(""),l.css("opacity",""),dfr.resolve()};if(v.animation_enabled()&&document.documentElement.clientWidth>this.get_breakpoint("small")){var anims={layout:{opacity:0,top:$(document).scrollTop()+$(window).height()/2},content:{width:0,height:0},speed:"fast"},pos=l.animate(anims.layout,anims.speed).promise(),size=c.animate(anims.content,anims.speed).promise();$.when(pos,size).done(function(){v.get_overlay().fadeOut({always:function(){reset()}})})}else l.css("opacity",0),reset();return dfr.promise()},load:function(v){return v.get_layout().find(".slb_loading").show(),document.documentElement.clientWidth>this.get_breakpoint("small")?v.get_layout().fadeIn().promise():v.get_layout().show().promise()},unload:function(v,dfr){var l=v.get_layout(),det=l.find(".slb_details"),cont=l.find(".slb_content "+this.get_tag_selector()),props={height:0};return det.css(props),cont.hide(),$.when(det.promise(),cont.promise()).done(function(){dfr.resolve()}),dfr.promise()},complete:function(v,dfr){var l=v.get_layout(),loader=l.find(".slb_loading"),det=l.find(".slb_details"),det_data=det.find(".slb_data"),c=l.find(".slb_content"),c_tag=c.find(this.get_tag_selector());if(document.documentElement.clientWidth>this.get_breakpoint("small")){var spd="fast",dims_item=this.get_item_dimensions();det.width(dims_item.width);var dims_det={height:det_data.outerHeight()};det.width("");var pos={top_base:$(document).scrollTop()};pos.top=pos.top_base+$(window).height()/2-(dims_det.height+dims_item.height)/2,pos.top<pos.top_base&&(pos.top=pos.top_base),pos=l.animate(pos,spd).promise(),dims_item=c.animate(dims_item,spd).promise();var thm=this;$.when(pos,dims_item).done(function(){loader.fadeOut(spd,function(){c.find(thm.get_tag_selector("item","content")).fadeIn(function(){c_tag.show(),det.animate({height:det_data.outerHeight()},"slow").promise().done(function(){det.height(""),dfr.resolve()})})})})}else loader.hide(),c_tag.show(),det.height(""),dfr.resolve();return dfr.promise()}}})}(jQuery);
themes/default/sass/_fonts.scss ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* yanone-kaffeesatz-regular - latin */
2
+ @font-face {
3
+ font-family: 'Yanone Kaffeesatz';
4
+ font-style: normal;
5
+ font-weight: 400;
6
+ src: url('../fonts/yanone-kaffeesatz-v9-latin-regular.eot'); /* IE9 Compat Modes */
7
+ src: local('Yanone Kaffeesatz Regular'), local('YanoneKaffeesatz-Regular'),
8
+ url('../fonts/yanone-kaffeesatz-v9-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
9
+ url('../fonts/yanone-kaffeesatz-v9-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
10
+ url('../fonts/yanone-kaffeesatz-v9-latin-regular.woff') format('woff'), /* Modern Browsers */
11
+ url('../fonts/yanone-kaffeesatz-v9-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
12
+ url('../fonts/yanone-kaffeesatz-v9-latin-regular.svg#YanoneKaffeesatz') format('svg'); /* Legacy iOS */
13
+ }
themes/default/sass/style.scss CHANGED
@@ -1,54 +1,25 @@
1
- //Imports
2
 
3
- @import "compass";
4
- @import url('//fonts.googleapis.com/css?family=Yanone+Kaffeesatz');
 
 
5
 
6
- //Mixins
7
 
8
- @mixin box-sizing-border {
9
- @include box-sizing(border-box);
10
- }
11
 
12
- @mixin min-dims($width: 200px, $height: 200px) {
13
- min-width: $width;
14
- min-height: $height;
 
15
  }
16
 
17
  #slb_viewer_wrap {
18
  .slb_theme_slb_default {
19
- position: absolute;
20
- top: 0;
21
- left: 0;
22
- width: 100%;
23
- z-index: 99999;
24
- text-align: center;
25
- line-height: 0;
26
- color:#000;
27
- font: {
28
- family: arial, verdana, sans-serif;
29
- size: 12px;
30
- }
31
-
32
- /* Reset */
33
- * {
34
- margin: 0;
35
- padding: 0;
36
- line-height: 1.4em;
37
- text-align: left;
38
- vertical-align: baseline;
39
- white-space: normal;
40
- outline: none;
41
- border: 0px;
42
- background: none;
43
- opacity: 1;
44
- width: auto;
45
- height: auto;
46
- position: static;
47
- float: none;
48
- clear: none;
49
- }
50
-
51
- /* General */
52
  a,
53
  a:hover {
54
  border-bottom:none;
@@ -56,80 +27,49 @@
56
  text-decoration:underline;
57
  }
58
 
59
- a img {
60
- border: none;
61
- }
62
-
63
  .slb_viewer_layout {
64
- @include box-sizing-border;
65
- z-index: 2;
66
- position: absolute;
67
  top: 20px;
68
- width: 100%;
69
- text-align: center;
70
- }
71
-
72
- .slb_viewer_overlay {
73
- position: fixed;
74
- top: 0;
75
- left: 0;
76
- z-index: 1;
77
- min-height: 105%;
78
- min-width: 100%;
79
- background-color: #151410;
80
- @include opacity(0.8);
81
  }
82
 
83
  .slb_container {
84
- @include box-sizing-border;
85
- @include box-shadow(0 0 64px -40px #fcfcfc);
86
- position: relative;
87
- display: inline-block;
88
- background-color: #fff;
89
- margin: 0 auto;
90
  border-radius: 5px;
91
- padding: 16px;
92
  }
93
 
94
  .slb_loading {
95
- background: url('../images/loading.gif') center center no-repeat;
96
- position: absolute;
97
- left: 0%;
98
- top: 0;
99
- width: 100%;
100
- height: 100%;
101
- min-width: 31px;
102
- min-height: 31px;
103
- text-align: center;
104
- text-indent: -2000em;
105
- line-height: 0;
106
- display: none;
107
  }
108
 
109
  .slb_template_tag_ui {
110
- cursor: pointer;
111
  transition: opacity .5s;
112
  }
113
 
114
- /* UI */
115
  .slb_controls {
116
  position: absolute;
117
  top: 8px;
118
  right: 8px;
119
  width: 75%;
120
  text-align: right;
 
 
 
 
121
 
122
  .slb_template_tag_ui {
123
- width: 25px;
124
- height: 25px;
 
125
  float: right;
126
  margin-left: 2px;
127
- text-indent: -2000em;
128
- @include opacity(.5);
 
 
129
  }
130
 
131
  .slb_template_tag_ui:hover {
132
- @include opacity(.8);
133
  }
134
 
135
  .slb_slideshow .slb_template_tag {
@@ -145,74 +85,80 @@
145
  background: url('../images/ui_slideshow_pause.png') 0 0 no-repeat;
146
  }
147
 
148
- /* Navigation */
149
  $ui_nav_pos: 45%;
150
  %ui_nav {
 
151
  position: absolute;
152
  top: 20%;
153
  height: 71%;
154
  width: 45%;
155
- margin-left: 4px;
 
156
  background-repeat: no-repeat;
157
- text-indent: -2000em;
158
- @include opacity(.5);
159
- }
160
-
161
- .slb_prev .slb_template_tag {
162
- background-image: url('../images/nav_prev.png');
163
- background-position: left $ui_nav_pos;
164
- }
165
-
166
- .slb_next .slb_template_tag {
167
- right: 4px;
168
- background-image: url('../images/nav_next.png');
169
- background-position: right $ui_nav_pos;
170
- }
171
-
172
- .slb_prev, .slb_next {
173
- .slb_template_tag {
174
- @extend %ui_nav;
175
- &:hover { @include opacity(1); }
176
- }
177
  }
178
 
179
- /* Content */
180
  .slb_content {
181
- @include min-dims;
182
- position: relative;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  }
184
 
185
  .slb_details {
186
- margin: 0 auto;
187
  line-height: 1.4em;
188
- text-align: left;
189
  overflow: hidden;
190
  position: relative;
191
- .inner {
192
- display: table;
193
- width: 100%;
194
- }
195
  .slb_data {
196
- display: table-caption;
197
  caption-side: bottom;
198
  }
 
 
 
 
199
  }
200
 
201
- .slb_template_tag_item_content * {
202
- width: 100%;
203
- height: 100%;
204
- }
205
-
206
- /* Title */
207
  $title-size: 23px;
208
 
209
- .slb_data_title {
210
- font-family: 'Yanone Kaffeesatz', sans-serif;
211
  font-size: $title-size;
 
 
 
 
 
 
212
  }
213
 
214
  .slb_group_status {
215
- @extend .slb_data_title;
216
  color: #777;
217
  font-style: italic;
218
  font-size: $title-size * .8;
@@ -223,62 +169,13 @@
223
  margin-top: 0.5em;
224
  }
225
 
226
- /* Single */
227
- &.item_single {
228
- .slb_group_status,
229
- .slb_nav,
230
- .slb_slideshow {
231
- display: none;
232
- }
233
- }
234
- /* Loading */
235
- &.loading {
236
- .slb_loading {
237
- display: block;
238
- }
239
- .slb_details,
240
- .slb_template_tag {
241
- //display: none;
242
- }
243
-
244
- .slb_template_tag_ui {
245
- @include opacity(0);
246
- }
247
- }
248
-
249
  //Media
250
 
251
  //Small screen
252
  @media screen and (max-width: 480px) {
253
- @mixin vsizing {
254
- min-height: 100%;
255
- min-width: 320px;
256
- width: 100%;
257
- }
258
- @include vsizing;
259
- .slb_viewer_layout {
260
- @include vsizing;
261
- display: block;
262
- }
263
  .slb_container {
264
- @include vsizing;
265
- @include box-sizing-border;
266
- @include box-shadow(none);
267
- max-width: 100%;
268
  border-radius: 0;
269
- margin: 0;
270
- padding: 5px;
271
- position: absolute;
272
- top: 0;
273
- left: 0;
274
- .slb_content {
275
- img, iframe, object, .slb_inner {
276
- max-width: 100%;
277
- }
278
- img {
279
- height: auto;
280
- }
281
- }
282
  }
283
  .slb_controls {
284
  top: 3px;
1
+ // Variables
2
 
3
+ $nav_width: 25px;
4
+ $nav_height: 33px;
5
+ $ui_controls_width: 25px;
6
+ $ui_controls_height: 25px;
7
 
8
+ // Fonts
9
 
10
+ @import "fonts";
11
+
12
+ // Placeholders
13
 
14
+ %hide-text {
15
+ text-indent: 100%;
16
+ white-space: nowrap;
17
+ overflow: hidden;
18
  }
19
 
20
  #slb_viewer_wrap {
21
  .slb_theme_slb_default {
22
+ //General
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  a,
24
  a:hover {
25
  border-bottom:none;
27
  text-decoration:underline;
28
  }
29
 
 
 
 
 
30
  .slb_viewer_layout {
 
 
 
31
  top: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  }
33
 
34
  .slb_container {
35
+ box-shadow: 0 0 64px -40px #fcfcfc;
 
 
 
 
 
36
  border-radius: 5px;
 
37
  }
38
 
39
  .slb_loading {
40
+ @extend %hide-text;
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
 
43
  .slb_template_tag_ui {
 
44
  transition: opacity .5s;
45
  }
46
 
47
+ //UI
48
  .slb_controls {
49
  position: absolute;
50
  top: 8px;
51
  right: 8px;
52
  width: 75%;
53
  text-align: right;
54
+ [dir="rtl"] & {
55
+ right: inherit;
56
+ left: 0px;
57
+ }
58
 
59
  .slb_template_tag_ui {
60
+ @extend %hide-text;
61
+ width: $ui_controls_width;
62
+ height: $ui_controls_height;
63
  float: right;
64
  margin-left: 2px;
65
+ opacity: 0.5;
66
+ [dir="rtl"] & {
67
+ float: left;
68
+ }
69
  }
70
 
71
  .slb_template_tag_ui:hover {
72
+ opacity: 0.8;
73
  }
74
 
75
  .slb_slideshow .slb_template_tag {
85
  background: url('../images/ui_slideshow_pause.png') 0 0 no-repeat;
86
  }
87
 
88
+ //Navigation
89
  $ui_nav_pos: 45%;
90
  %ui_nav {
91
+ @extend %hide-text;
92
  position: absolute;
93
  top: 20%;
94
  height: 71%;
95
  width: 45%;
96
+ min-width: $nav_width;
97
+ min-height: $nav_height;
98
  background-repeat: no-repeat;
99
+ opacity: 0.5;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  }
101
 
102
+ //Content
103
  .slb_content {
104
+ min-height: $nav_height + $ui_controls_height;
105
+ min-width: $nav_width * 2;
106
+
107
+ .slb_prev .slb_template_tag,
108
+ [dir="rtl"] & .slb_next .slb_template_tag {
109
+ left: 4px;
110
+ right: inherit;
111
+ background-image: url('../images/nav_prev.png');
112
+ background-position: left $ui_nav_pos;
113
+ }
114
+
115
+ .slb_next .slb_template_tag,
116
+ [dir="rtl"] & .slb_prev .slb_template_tag {
117
+ right: 4px;
118
+ left: inherit;
119
+ background-image: url('../images/nav_next.png');
120
+ background-position: right $ui_nav_pos;
121
+ }
122
+
123
+ .slb_prev, .slb_next {
124
+ .slb_template_tag {
125
+ @extend %ui_nav;
126
+ &:hover {
127
+ opacity: 1
128
+ }
129
+ }
130
+ }
131
  }
132
 
133
  .slb_details {
 
134
  line-height: 1.4em;
 
135
  overflow: hidden;
136
  position: relative;
137
+
 
 
 
138
  .slb_data {
 
139
  caption-side: bottom;
140
  }
141
+
142
+ .slb_nav {
143
+ display: none;
144
+ }
145
  }
146
 
147
+ //Title
 
 
 
 
 
148
  $title-size: 23px;
149
 
150
+ .slb_data_title, .slb_group_status {
151
+ font-family: 'Yanone Kaffeesatz', arial, sans-serif;
152
  font-size: $title-size;
153
+ margin-right: .2em;
154
+ display: inline-block;
155
+ [dir="rtl"] & {
156
+ margin-left: .2em;
157
+ margin-right: 0px;
158
+ }
159
  }
160
 
161
  .slb_group_status {
 
162
  color: #777;
163
  font-style: italic;
164
  font-size: $title-size * .8;
169
  margin-top: 0.5em;
170
  }
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  //Media
173
 
174
  //Small screen
175
  @media screen and (max-width: 480px) {
 
 
 
 
 
 
 
 
 
 
176
  .slb_container {
177
+ box-shadow: none;
 
 
 
178
  border-radius: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  }
180
  .slb_controls {
181
  top: 3px;