Strong Testimonials - Version 2.50.4

Version Description

Download this release

Release Info

Developer giucu91
Plugin Icon 128x128 Strong Testimonials
Version 2.50.4
Comparing to
See all releases

Code changes from version 2.50.3 to 2.50.4

Files changed (75) hide show
  1. admin/about/class-strong-testimonials-welcome.php +286 -0
  2. admin/admin.php +1 -1
  3. admin/class-strong-testimonials-admin-scripts.php +12 -1
  4. admin/class-strong-testimonials-post-editor.php +7 -5
  5. admin/css/selectize.default.css +408 -0
  6. admin/img/banner.png +0 -0
  7. admin/img/features/1.svg +1 -0
  8. admin/img/features/10.svg +1 -0
  9. admin/img/features/11.svg +1 -0
  10. admin/img/features/12.svg +1 -0
  11. admin/img/features/13.svg +1 -0
  12. admin/img/features/14.svg +1 -0
  13. admin/img/features/15.svg +1 -0
  14. admin/img/features/2.svg +1 -0
  15. admin/img/features/3.svg +1 -0
  16. admin/img/features/4.svg +1 -0
  17. admin/img/features/5.svg +1 -0
  18. admin/img/features/6.svg +1 -0
  19. admin/img/features/7.svg +1 -0
  20. admin/img/features/8.svg +1 -0
  21. admin/img/features/9.svg +1 -0
  22. admin/img/features/email.svg +21 -0
  23. admin/img/features/filter.svg +21 -0
  24. admin/img/features/infinitescroll.svg +15 -0
  25. admin/img/features/mailchimp.svg +34 -0
  26. admin/img/features/rolemanagement.svg +17 -0
  27. admin/img/mascot-2.svg +1 -0
  28. admin/img/star.svg +12 -0
  29. admin/img/testimonial-image-1.jpg +0 -0
  30. admin/img/testimonial-image-2.jpeg +0 -0
  31. admin/js/admin-form.js +1 -1
  32. admin/js/lib/are-you-sure/jquery.are-you-sure.js +9 -9
  33. admin/js/selectize.js +3891 -0
  34. admin/js/strong-testimonials-elementor-editor.js +43 -0
  35. admin/partials/views/group-compat.php +1 -1
  36. admin/partials/views/group-extra.php +1 -1
  37. admin/partials/views/group-fields.php +5 -5
  38. admin/partials/views/group-form.php +1 -1
  39. admin/partials/views/group-query.php +1 -1
  40. admin/partials/views/group-slideshow.php +1 -1
  41. admin/partials/views/group-style.php +1 -1
  42. admin/partials/views/option-title.php +1 -1
  43. admin/partials/views/view-shortcode.php +1 -1
  44. admin/settings/partials/general.php +17 -15
  45. admin/uninstall/class-strong-testimonials-uninstall.php +3 -1
  46. assets/css/admin-global.min.css +1 -0
  47. assets/css/admin-welcome.css +326 -0
  48. assets/css/admin-welcome.min.css +1 -0
  49. assets/css/admin.css +9 -205
  50. assets/css/admin.min.css +1 -0
  51. assets/css/blocks.css +13 -0
  52. assets/css/blocks.min.css +1 -0
  53. assets/js/admin-js.js +67 -68
  54. assets/js/blocks-js.js +452 -0
  55. assets/src/js/admin.js +5 -5
  56. assets/src/js/blocks.js +50 -0
  57. assets/src/js/components/edit.js +109 -0
  58. assets/src/js/components/inspector.js +78 -0
  59. assets/src/scss/blocks.scss +19 -0
  60. changelog.txt +6 -0
  61. includes/class-strong-gutemberg.php +56 -0
  62. includes/class-strong-view-display.php +7 -6
  63. includes/class-strong-view-slideshow.php +7 -5
  64. includes/elementor/class-strong-elementor-check.php +120 -0
  65. includes/elementor/class-strong-elementor-widget-activation.php +85 -0
  66. includes/elementor/class-strong-elementor.php +64 -0
  67. includes/functions-image.php +11 -9
  68. includes/functions.php +12 -0
  69. includes/strong-testimonials-beaver-block/class-strong-beaver-block.php +40 -0
  70. includes/strong-testimonials-beaver-block/class-strong-beaver.php +27 -0
  71. includes/strong-testimonials-beaver-block/includes/frontend.php +13 -0
  72. public/js/lib/form-validation/form-validation.js +1 -1
  73. public/js/lib/form-validation/form-validation.min.js +1 -1
  74. readme.txt +2 -2
  75. strong-testimonials.php +15 -16
admin/about/class-strong-testimonials-welcome.php ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Strong_Testimonials_Welcome {
4
+
5
+ public function __construct() {
6
+ add_action( 'admin_menu', array( $this, 'register' ) );
7
+ add_action( 'admin_head', array( $this, 'hide_menu' ) );
8
+ }
9
+
10
+ public function hide_menu() {
11
+ remove_submenu_page( 'index.php', 'wpmtst-getting-started' );
12
+ }
13
+
14
+ public function register() {
15
+
16
+ add_dashboard_page(
17
+ esc_html__( 'Welcome to Strong Testimonials', 'strong-testimonials' ),
18
+ esc_html__( 'Welcome to Strong Testimonials', 'strong-testimonials' ),
19
+ 'manage_options',
20
+ 'wpmtst-getting-started',
21
+ array( $this, 'output' )
22
+ );
23
+ }
24
+
25
+ public function output() {
26
+ ?>
27
+ <div id="wpmtst-welcome">
28
+
29
+ <div class="container">
30
+
31
+ <div class="hero features">
32
+
33
+ <div class="mascot">
34
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/mascot-2.svg" alt="<?php esc_attr_e( 'Strong Testimonials Mascot', 'strong-testimonials' ); ?>">
35
+ </div>
36
+
37
+ <div class="block">
38
+ <h1><?php esc_html_e( 'Welcome to Strong Testimonials', 'strong-testimonials' ); ?></h1>
39
+ <h6><?php esc_html_e( 'Thank you for choosing Strong Testimonials - Build trust and credibility with your products.', 'strong-testimonials' ); ?></h6>
40
+ </div>
41
+
42
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/banner.png" alt="<?php esc_attr_e( 'Watch how to', 'strong-testimonials' ); ?>" class="video-thumbnail">
43
+
44
+ <div class="block">
45
+
46
+ <div class="feature-list clear">
47
+
48
+ <div class="feature-block first">
49
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/7.svg">
50
+ <h5><?php esc_html_e( 'Increase Conversions', 'strong-testimonials' ); ?></h5>
51
+ <p><?php esc_html_e( 'Make customers 63% more likely to purchase with testimonials that drive sales.', 'strong-testimonials' ); ?></p>
52
+ </div>
53
+
54
+ <div class="feature-block last">
55
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/8.svg">
56
+ <h5><?php esc_html_e( 'Collect Testimonials', 'strong-testimonials' ); ?></h5>
57
+ <p><?php esc_html_e( 'Easily collect testimonials from customers by creating forms.', 'strong-testimonials' ); ?></p>
58
+ </div>
59
+
60
+ <div class="feature-block first">
61
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/5.svg">
62
+ <h5><?php esc_html_e( 'Multiple Layouts', 'strong-testimonials' ); ?></h5>
63
+ <p><?php esc_html_e( 'Choose from four unique layouts for your testimonials.', 'strong-testimonials' ); ?></p>
64
+ </div>
65
+
66
+ <div class="feature-block last">
67
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/14.svg">
68
+ <h5><?php esc_html_e( 'SEO-friendly', 'strong-testimonials' ); ?></h5>
69
+ <p><?php esc_html_e( 'SEO-friendly testimonials that your customers and Search Engines can understand.', 'strong-testimonials' ); ?></p>
70
+ </div>
71
+
72
+ </div>
73
+
74
+ <div class="button-wrap clear">
75
+ <div class="left">
76
+ <a href="<?php echo esc_url( admin_url( 'edit.php?post_type=wpm-testimonial' ) ); ?>" class="wpmtst-btn wpmtst-btn-block wpmtst-btn-lg wpmtst-btn-purple">
77
+ <?php esc_html_e( 'Start Adding Testimonials', 'strong-testimonials' ); ?>
78
+ </a>
79
+ </div>
80
+ <div class="right">
81
+ <a href="https://strongtestimonials.com/docs"
82
+ class="wpmtst-btn wpmtst-btn-block wpmtst-btn-lg wpmtst-btn-orange" target="_blank">
83
+ <?php esc_html_e( 'Read the Docs', 'strong-testimonials' ); ?>
84
+ </a>
85
+ </div>
86
+ </div>
87
+
88
+ </div>
89
+
90
+ </div><!-- hero -->
91
+
92
+ <div class="features">
93
+
94
+ <div class="block">
95
+
96
+ <h1><?php esc_html_e( 'Strong Testimonials Extensions', 'strong-testimonials' ); ?></h1>
97
+ <h6><?php esc_html_e( 'Sales copy grabs attention - testimonials drive sales.', 'strong-testimonials' ); ?></h6>
98
+
99
+ <div class="feature-list clear">
100
+
101
+ <div class="feature-block first">
102
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/1.svg">
103
+ <h5><?php esc_html_e( 'Pro Templates', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
104
+ <p><?php esc_html_e( 'Create beautiful testimonial designs with a number of predesigned and easy-to-use premium templates.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/pro-templates"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
105
+ </div>
106
+
107
+ <div class="feature-block last">
108
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/infinitescroll.svg">
109
+ <h5><?php esc_html_e( 'Infinite Scroll', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
110
+ <p><?php esc_html_e( 'Change properties of the testimonial post type: labels, permalink structure, admin options and post editor features.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/infinite-scroll/"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
111
+ </div>
112
+
113
+ <div class="feature-block first">
114
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/12.svg">
115
+ <h5><?php esc_html_e( 'Testimonial Assignment', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
116
+ <p><?php esc_html_e( 'Assign testimonials to custom post types for easy management and filtering.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/assignment"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
117
+ </div>
118
+
119
+ <div class="feature-block last">
120
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/11.svg">
121
+ <h5><?php esc_html_e( 'Custom Properties', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
122
+ <p><?php esc_html_e( 'Change properties of the testimonial post type: labels, permalink structure, admin options and post editor features.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/properties"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
123
+ </div>
124
+
125
+ <div class="feature-block first">
126
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/6.svg">
127
+ <h5><?php esc_html_e( 'Advanced View Settings', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
128
+ <p><?php esc_html_e( 'Customize your testimonials beyond star ratings, reorder fields and more.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/advanced-views"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
129
+ </div>
130
+
131
+ <div class="feature-block last">
132
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/9.svg">
133
+ <h5><?php esc_html_e( 'Multiple Submission Forms', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
134
+ <p><?php esc_html_e( 'Easily collect testimonials from customers by creating and customizing multiple forms at once.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/multiple-forms"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
135
+ </div>
136
+
137
+ <div class="feature-block first">
138
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/10.svg">
139
+ <h5><?php esc_html_e( 'Custom Form Fields', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
140
+ <p><?php esc_html_e( 'Enhance your submission forms to both collect and display additional information.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/custom-fields"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
141
+ </div>
142
+
143
+ <div class="feature-block last">
144
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/14.svg">
145
+ <h5><?php esc_html_e( 'SEO-friendly Testimonials', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
146
+ <p><?php esc_html_e( 'Take full advantage of your testimonials with our Schema.org Markup extension.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/review-markup"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
147
+ </div>
148
+
149
+ <div class="feature-block first">
150
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/13.svg">
151
+ <h5><?php esc_html_e( 'Spam Control', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
152
+ <p><?php esc_html_e( 'Protect your testimonial submission forms from spam and other types of automated abuse.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/captcha"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
153
+ </div>
154
+
155
+ <div class="feature-block last">
156
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/15.svg">
157
+ <h5><?php esc_html_e( 'Testimonial Importer', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
158
+ <p><?php esc_html_e( 'Import reviews from 3rd party sites like: Facebook, Google, Yelp, Zomato & WooCommerce', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/importer"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
159
+ </div>
160
+
161
+ <div class="feature-block first">
162
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/email.svg">
163
+ <h5><?php esc_html_e( 'Enhanced Emails', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
164
+ <p><?php esc_html_e( 'We added the option to send an email to the customer upon new testimonial submission. Also, to send an email to the customer when the testimonial is approved.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/enhanced-emails/"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
165
+ </div>
166
+
167
+ <div class="feature-block last">
168
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/filter.svg">
169
+ <h5><?php esc_html_e( 'Filters', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
170
+ <p><?php esc_html_e( 'Now you can use categories to group your testimonials and have your clients read reviews grouped by service/product type.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/filters/"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
171
+ </div>
172
+
173
+ <div class="feature-block first">
174
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/rolemanagement.svg">
175
+ <h5><?php esc_html_e( 'Role Management', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
176
+ <p><?php esc_html_e( 'We’re giving power back to the users and admins can decide which user roles are worthy of adding, editing, or removing testimonials.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/role-management/"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
177
+ </div>
178
+
179
+ <div class="feature-block last">
180
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/features/mailchimp.svg">
181
+ <h5><?php esc_html_e( 'Mailchimp integration', 'strong-testimonials' ); ?><div class="pro-label">PRO</div></h5>
182
+ <p><?php esc_html_e( 'Now you can subscribe your customers to a Mailchimp list.', 'strong-testimonials' ); ?><br/><a target="_blank" href="https://strongtestimonials.com/extensions/importer"><?php esc_html_e( 'Learn More', 'strong-testimonials' ); ?></a></p>
183
+ </div>
184
+
185
+ </div><!-- feature-list -->
186
+
187
+ </div>
188
+
189
+ </div><!-- features -->
190
+
191
+ <div class="upgrade">
192
+
193
+ <div class="block clear">
194
+
195
+ <h1><?php esc_html_e( 'Upgrade to PRO', 'strong-testimonials' ); ?></h1>
196
+
197
+ <div class="left">
198
+ <ul>
199
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( '1 Year of Free Updates', 'strong-testimonials' ); ?></li>
200
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Pro Templates', 'strong-testimonials' ); ?></li>
201
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Testimonial Assignment', 'strong-testimonials' ); ?></li>
202
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Testimonial Importer', 'strong-testimonials' ); ?></li>
203
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'SEO-friendly Testimonials', 'strong-testimonials' ); ?></li>
204
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Country Selector', 'strong-testimonials' ); ?></li>
205
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Infinite Scroll', 'strong-testimonials' ); ?></li>
206
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Filters', 'strong-testimonials' ); ?></li>
207
+ </ul>
208
+ </div>
209
+
210
+ <div class="right">
211
+ <ul>
212
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( '1 Year of Support', 'strong-testimonials' ); ?></li>
213
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Spam Control', 'strong-testimonials' ); ?></li>
214
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Advanced View Settings', 'strong-testimonials' ); ?></li>
215
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Multiple Submission Forms', 'strong-testimonials' ); ?></li>
216
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Custom Form Fields', 'strong-testimonials' ); ?></li>
217
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Custom properties', 'strong-testimonials' ); ?></li>
218
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Enhanced Emails', 'strong-testimonials' ); ?></li>
219
+ <li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Role Management', 'strong-testimonials' ); ?></li>
220
+ </ul>
221
+ </div>
222
+
223
+ <a href="https://strongtestimonials.com/pricing/" target="_blank"
224
+ class="wpmtst-btn wpmtst-btn-lg wpmtst-btn-white">
225
+ <?php esc_html_e( 'Upgrade Now', 'strong-testimonials' ); ?>
226
+ </a>
227
+
228
+ </div>
229
+
230
+ </div><!-- upgrade -->
231
+
232
+ <div class="testimonials">
233
+
234
+ <div class="block clear">
235
+
236
+ <div class="testimonial-block left">
237
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/testimonial-image-1.jpg">
238
+ <p><?php esc_html_e( 'Strong Testimonials is my new, go-to resource for creating pages with multiple staff bios or testimonials. It’s extremely easy to use, update, and customize, and that makes it an invaluable asset. Highly recommend!', 'strong-testimonials' ); ?>
239
+ <div style="background-image: url(<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/star.svg)" class="testimonial-stars"></div>
240
+ <p><strong><?php esc_html_e( 'Ryan Haught' ); ?></strong><br/><?php esc_html_e( 'Digital Marketer at Heaven’s Family' ); ?></p>
241
+ </div>
242
+
243
+ <div class="testimonial-block right">
244
+ <img src="<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/testimonial-image-2.jpeg">
245
+ <p><?php esc_html_e( 'I have used various testimonials plugins. What I get here for free is just amazing. The support is great. And updates fresh. Looking at the ability to get reviews indexed by Google is more than enough reason to get an upgrade.', 'strong-testimonials' ); ?>
246
+ <div style="background-image: url(<?php echo esc_attr( WPMTST_ADMIN_URL ); ?>/img/star.svg)" class="testimonial-stars"></div>
247
+ <p><strong><?php esc_html_e( 'Johan Horak' ); ?></strong><br/><?php esc_html_e( 'Marketing at CapeHolidays' ); ?></p>
248
+ </div>
249
+
250
+ </div>
251
+
252
+ </div><!-- testimonials -->
253
+
254
+ <div class="footer">
255
+
256
+ <div class="block clear">
257
+
258
+ <div class="button-wrap clear">
259
+ <div class="left">
260
+ <a href="<?php echo esc_url( admin_url( 'edit.php?post_type=wpm-testimonial' ) ); ?>"
261
+ class="wpmtst-btn wpmtst-btn-block wpmtst-btn-lg wpmtst-btn-orange">
262
+ <?php esc_html_e( 'Start Adding Testimonials', 'strong-testimonials' ); ?>
263
+ </a>
264
+ </div>
265
+ <div class="right">
266
+ <a href="https://strongtestimonials.com/pricing/" target="_blank"
267
+ class="wpmtst-btn wpmtst-btn-block wpmtst-btn-lg wpmtst-btn-purple">
268
+ <?php esc_html_e( 'Upgrade to Pro', 'strong-testimonials' ); ?>
269
+ </a>
270
+ </div>
271
+ </div>
272
+
273
+ </div>
274
+
275
+ </div><!-- footer -->
276
+
277
+ </div><!-- container -->
278
+
279
+ </div><!-- wpmtst welcome -->
280
+ <?php
281
+ }
282
+
283
+
284
+ }
285
+
286
+ new Strong_Testimonials_Welcome();
admin/admin.php CHANGED
@@ -92,7 +92,7 @@ add_action( 'admin_action_captcha-options-changed', 'wpmtst_action_captcha_optio
92
  function wpmtst_activation_redirect() {
93
  if ( get_option( 'wpmtst_do_activation_redirect', false ) ) {
94
  delete_option( 'wpmtst_do_activation_redirect' );
95
- wp_redirect( admin_url( 'edit.php?post_type=wpm-testimonial' ) );
96
  exit;
97
  }
98
  }
92
  function wpmtst_activation_redirect() {
93
  if ( get_option( 'wpmtst_do_activation_redirect', false ) ) {
94
  delete_option( 'wpmtst_do_activation_redirect' );
95
+ wp_redirect( admin_url( 'index.php?page=wpmtst-getting-started' ) );
96
  exit;
97
  }
98
  }
admin/class-strong-testimonials-admin-scripts.php CHANGED
@@ -64,7 +64,13 @@ class Strong_Testimonials_Admin_Scripts {
64
  WPMTST_ASSETS_CSS . 'admin-global.css',
65
  array(),
66
  $plugin_version );
67
-
 
 
 
 
 
 
68
  wp_register_style(
69
  'wpmtst-admin-style',
70
  WPMTST_ASSETS_CSS . 'admin.css',
@@ -230,6 +236,7 @@ class Strong_Testimonials_Admin_Scripts {
230
  * Enqueue global admin scripts.
231
  */
232
  public static function admin_enqueue_scripts() {
 
233
  $plugin_version = get_option( 'wpmtst_plugin_version' );
234
 
235
  wp_enqueue_style( 'wpmtst-admin-global-style' );
@@ -248,6 +255,10 @@ class Strong_Testimonials_Admin_Scripts {
248
  'templateTagTitle' => __( 'click to insert into message at caret', 'strong-testimonials' ),
249
  )
250
  );
 
 
 
 
251
  }
252
 
253
  /**
64
  WPMTST_ASSETS_CSS . 'admin-global.css',
65
  array(),
66
  $plugin_version );
67
+
68
+ wp_register_style(
69
+ 'wpmtst-admin-welcome-style',
70
+ WPMTST_ASSETS_CSS . 'admin-welcome.css',
71
+ array(),
72
+ $plugin_version );
73
+
74
  wp_register_style(
75
  'wpmtst-admin-style',
76
  WPMTST_ASSETS_CSS . 'admin.css',
236
  * Enqueue global admin scripts.
237
  */
238
  public static function admin_enqueue_scripts() {
239
+ $screen = get_current_screen();
240
  $plugin_version = get_option( 'wpmtst_plugin_version' );
241
 
242
  wp_enqueue_style( 'wpmtst-admin-global-style' );
255
  'templateTagTitle' => __( 'click to insert into message at caret', 'strong-testimonials' ),
256
  )
257
  );
258
+
259
+ if ( $screen->id === 'dashboard_page_wpmtst-getting-started' ) {
260
+ wp_enqueue_style( 'wpmtst-admin-welcome-style' );
261
+ }
262
  }
263
 
264
  /**
admin/class-strong-testimonials-post-editor.php CHANGED
@@ -318,7 +318,9 @@ class Strong_Testimonials_Post_Editor {
318
  // Determine whether to update or delete.
319
  // Similar to wpmtst_ajax_edit_rating() in admin-ajax.php.
320
  $custom_fields['nofollow']['input_type'] = '';
321
-
 
 
322
  foreach ( $custom as $key => $value ) {
323
  $action = 'update';
324
  $sanitized_value = '';
@@ -329,12 +331,12 @@ class Strong_Testimonials_Post_Editor {
329
  }
330
  }
331
 
332
- // Data Sanitization
333
- if ( 'text' == $custom_fields[ $key ]['input_type'] ) {
334
  $sanitized_value = wp_filter_post_kses( $value );
335
- }elseif ( 'email' == $custom_fields[ $key ]['input_type'] ) {
336
  $sanitized_value = sanitize_email( $value );
337
- }elseif ( 'url' == $custom_fields[ $key ]['input_type'] ) {
338
  $sanitized_value = sanitize_text_field( $value );
339
  }else{
340
  $sanitized_value = sanitize_text_field( $value );
318
  // Determine whether to update or delete.
319
  // Similar to wpmtst_ajax_edit_rating() in admin-ajax.php.
320
  $custom_fields['nofollow']['input_type'] = '';
321
+ $custom_fields['noopener']['input_type'] = '';
322
+ $custom_fields['noreferrer']['input_type'] = '';
323
+
324
  foreach ( $custom as $key => $value ) {
325
  $action = 'update';
326
  $sanitized_value = '';
331
  }
332
  }
333
 
334
+ // Data Sanitizationva
335
+ if ( isset($custom_fields[ $key ]['input_type']) && 'text' == $custom_fields[ $key ]['input_type'] ) {
336
  $sanitized_value = wp_filter_post_kses( $value );
337
+ }elseif ( isset($custom_fields[ $key ]['input_type']) && 'email' == $custom_fields[ $key ]['input_type'] ) {
338
  $sanitized_value = sanitize_email( $value );
339
+ }elseif ( isset($custom_fields[ $key ]['input_type']) && 'url' == $custom_fields[ $key ]['input_type'] ) {
340
  $sanitized_value = sanitize_text_field( $value );
341
  }else{
342
  $sanitized_value = sanitize_text_field( $value );
admin/css/selectize.default.css ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * selectize.default.css (v0.12.6) - Default Theme
3
+ * Copyright (c) 2013–2015 Brian Reavis & contributors
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6
+ * file except in compliance with the License. You may obtain a copy of the License at:
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software distributed under
10
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ * ANY KIND, either express or implied. See the License for the specific language
12
+ * governing permissions and limitations under the License.
13
+ *
14
+ * @author Brian Reavis <brian@thirdroute.com>
15
+ */
16
+ .selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
17
+ visibility: visible !important;
18
+ background: #f2f2f2 !important;
19
+ background: rgba(0, 0, 0, 0.06) !important;
20
+ border: 0 none !important;
21
+ -webkit-box-shadow: inset 0 0 12px 4px #fff;
22
+ box-shadow: inset 0 0 12px 4px #fff;
23
+ }
24
+ .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
25
+ content: '!';
26
+ visibility: hidden;
27
+ }
28
+ .selectize-control.plugin-drag_drop .ui-sortable-helper {
29
+ -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
30
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
31
+ }
32
+ .selectize-dropdown-header {
33
+ position: relative;
34
+ padding: 5px 8px;
35
+ border-bottom: 1px solid #d0d0d0;
36
+ background: #f8f8f8;
37
+ -webkit-border-radius: 3px 3px 0 0;
38
+ -moz-border-radius: 3px 3px 0 0;
39
+ border-radius: 3px 3px 0 0;
40
+ }
41
+ .selectize-dropdown-header-close {
42
+ position: absolute;
43
+ right: 8px;
44
+ top: 50%;
45
+ color: #303030;
46
+ opacity: 0.4;
47
+ margin-top: -12px;
48
+ line-height: 20px;
49
+ font-size: 20px !important;
50
+ }
51
+ .selectize-dropdown-header-close:hover {
52
+ color: #000000;
53
+ }
54
+ .selectize-dropdown.plugin-optgroup_columns .optgroup {
55
+ border-right: 1px solid #f2f2f2;
56
+ border-top: 0 none;
57
+ float: left;
58
+ -webkit-box-sizing: border-box;
59
+ -moz-box-sizing: border-box;
60
+ box-sizing: border-box;
61
+ }
62
+ .selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
63
+ border-right: 0 none;
64
+ }
65
+ .selectize-dropdown.plugin-optgroup_columns .optgroup:before {
66
+ display: none;
67
+ }
68
+ .selectize-dropdown.plugin-optgroup_columns .optgroup-header {
69
+ border-top: 0 none;
70
+ }
71
+ .selectize-control.plugin-remove_button [data-value] {
72
+ position: relative;
73
+ padding-right: 24px !important;
74
+ }
75
+ .selectize-control.plugin-remove_button [data-value] .remove {
76
+ z-index: 1;
77
+ /* fixes ie bug (see #392) */
78
+ position: absolute;
79
+ top: 0;
80
+ right: 0;
81
+ bottom: 0;
82
+ width: 17px;
83
+ text-align: center;
84
+ font-weight: bold;
85
+ font-size: 12px;
86
+ color: inherit;
87
+ text-decoration: none;
88
+ vertical-align: middle;
89
+ display: inline-block;
90
+ padding: 2px 0 0 0;
91
+ border-left: 1px solid #0073bb;
92
+ -webkit-border-radius: 0 2px 2px 0;
93
+ -moz-border-radius: 0 2px 2px 0;
94
+ border-radius: 0 2px 2px 0;
95
+ -webkit-box-sizing: border-box;
96
+ -moz-box-sizing: border-box;
97
+ box-sizing: border-box;
98
+ }
99
+ .selectize-control.plugin-remove_button [data-value] .remove:hover {
100
+ background: rgba(0, 0, 0, 0.05);
101
+ }
102
+ .selectize-control.plugin-remove_button [data-value].active .remove {
103
+ border-left-color: #00578d;
104
+ }
105
+ .selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
106
+ background: none;
107
+ }
108
+ .selectize-control.plugin-remove_button .disabled [data-value] .remove {
109
+ border-left-color: #aaaaaa;
110
+ }
111
+ .selectize-control.plugin-remove_button .remove-single {
112
+ position: absolute;
113
+ right: 0;
114
+ top: 0;
115
+ font-size: 23px;
116
+ }
117
+ .selectize-control {
118
+ position: relative;
119
+ }
120
+ .selectize-dropdown,
121
+ .selectize-input,
122
+ .selectize-input input {
123
+ color: #303030;
124
+ font-family: inherit;
125
+ font-size: 13px;
126
+ line-height: 18px;
127
+ -webkit-font-smoothing: inherit;
128
+ }
129
+ .selectize-input,
130
+ .selectize-control.single .selectize-input.input-active {
131
+ background: #fff;
132
+ cursor: text;
133
+ display: inline-block;
134
+ }
135
+ .selectize-input {
136
+ border: 1px solid #d0d0d0;
137
+ padding: 8px 8px;
138
+ display: inline-block;
139
+ width: 100%;
140
+ overflow: hidden;
141
+ position: relative;
142
+ z-index: 1;
143
+ -webkit-box-sizing: border-box;
144
+ -moz-box-sizing: border-box;
145
+ box-sizing: border-box;
146
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
147
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
148
+ -webkit-border-radius: 3px;
149
+ -moz-border-radius: 3px;
150
+ border-radius: 3px;
151
+ }
152
+ .selectize-control.multi .selectize-input.has-items {
153
+ padding: 5px 8px 2px;
154
+ }
155
+ .selectize-input.full {
156
+ background-color: #fff;
157
+ }
158
+ .selectize-input.disabled,
159
+ .selectize-input.disabled * {
160
+ cursor: default !important;
161
+ }
162
+ .selectize-input.focus {
163
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
164
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
165
+ }
166
+ .selectize-input.dropdown-active {
167
+ -webkit-border-radius: 3px 3px 0 0;
168
+ -moz-border-radius: 3px 3px 0 0;
169
+ border-radius: 3px 3px 0 0;
170
+ }
171
+ .selectize-input > * {
172
+ vertical-align: baseline;
173
+ display: -moz-inline-stack;
174
+ display: inline-block;
175
+ zoom: 1;
176
+ *display: inline;
177
+ }
178
+ .selectize-control.multi .selectize-input > div {
179
+ cursor: pointer;
180
+ margin: 0 3px 3px 0;
181
+ padding: 2px 6px;
182
+ background: #1da7ee;
183
+ color: #fff;
184
+ border: 1px solid #0073bb;
185
+ }
186
+ .selectize-control.multi .selectize-input > div.active {
187
+ background: #92c836;
188
+ color: #fff;
189
+ border: 1px solid #00578d;
190
+ }
191
+ .selectize-control.multi .selectize-input.disabled > div,
192
+ .selectize-control.multi .selectize-input.disabled > div.active {
193
+ color: #ffffff;
194
+ background: #d2d2d2;
195
+ border: 1px solid #aaaaaa;
196
+ }
197
+ .selectize-input > input {
198
+ display: inline-block !important;
199
+ padding: 0 !important;
200
+ min-height: 0 !important;
201
+ max-height: none !important;
202
+ max-width: 100% !important;
203
+ margin: 0 1px !important;
204
+ text-indent: 0 !important;
205
+ border: 0 none !important;
206
+ background: none !important;
207
+ line-height: inherit !important;
208
+ -webkit-user-select: auto !important;
209
+ -webkit-box-shadow: none !important;
210
+ box-shadow: none !important;
211
+ }
212
+ .selectize-input > input::-ms-clear {
213
+ display: none;
214
+ }
215
+ .selectize-input > input:focus {
216
+ outline: none !important;
217
+ }
218
+ .selectize-input::after {
219
+ content: ' ';
220
+ display: block;
221
+ clear: left;
222
+ }
223
+ .selectize-input.dropdown-active::before {
224
+ content: ' ';
225
+ display: block;
226
+ position: absolute;
227
+ background: #f0f0f0;
228
+ height: 1px;
229
+ bottom: 0;
230
+ left: 0;
231
+ right: 0;
232
+ }
233
+ .selectize-dropdown {
234
+ position: absolute;
235
+ z-index: 10;
236
+ border: 1px solid #d0d0d0;
237
+ background: #fff;
238
+ margin: -1px 0 0 0;
239
+ border-top: 0 none;
240
+ -webkit-box-sizing: border-box;
241
+ -moz-box-sizing: border-box;
242
+ box-sizing: border-box;
243
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
244
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
245
+ -webkit-border-radius: 0 0 3px 3px;
246
+ -moz-border-radius: 0 0 3px 3px;
247
+ border-radius: 0 0 3px 3px;
248
+ }
249
+ .selectize-dropdown [data-selectable] {
250
+ cursor: pointer;
251
+ overflow: hidden;
252
+ }
253
+ .selectize-dropdown [data-selectable] .highlight {
254
+ background: rgba(125, 168, 208, 0.2);
255
+ -webkit-border-radius: 1px;
256
+ -moz-border-radius: 1px;
257
+ border-radius: 1px;
258
+ }
259
+ .selectize-dropdown .option,
260
+ .selectize-dropdown .optgroup-header {
261
+ padding: 5px 8px;
262
+ }
263
+ .selectize-dropdown .option,
264
+ .selectize-dropdown [data-disabled],
265
+ .selectize-dropdown [data-disabled] [data-selectable].option {
266
+ cursor: inherit;
267
+ opacity: 0.5;
268
+ }
269
+ .selectize-dropdown [data-selectable].option {
270
+ opacity: 1;
271
+ }
272
+ .selectize-dropdown .optgroup:first-child .optgroup-header {
273
+ border-top: 0 none;
274
+ }
275
+ .selectize-dropdown .optgroup-header {
276
+ color: #303030;
277
+ background: #fff;
278
+ cursor: default;
279
+ }
280
+ .selectize-dropdown .active {
281
+ background-color: #f5fafd;
282
+ color: #495c68;
283
+ }
284
+ .selectize-dropdown .active.create {
285
+ color: #495c68;
286
+ }
287
+ .selectize-dropdown .create {
288
+ color: rgba(48, 48, 48, 0.5);
289
+ }
290
+ .selectize-dropdown-content {
291
+ overflow-y: auto;
292
+ overflow-x: hidden;
293
+ max-height: 200px;
294
+ -webkit-overflow-scrolling: touch;
295
+ }
296
+ .selectize-control.single .selectize-input,
297
+ .selectize-control.single .selectize-input input {
298
+ cursor: pointer;
299
+ }
300
+ .selectize-control.single .selectize-input.input-active,
301
+ .selectize-control.single .selectize-input.input-active input {
302
+ cursor: text;
303
+ }
304
+ .selectize-control.single .selectize-input:after {
305
+ content: ' ';
306
+ display: block;
307
+ position: absolute;
308
+ top: 50%;
309
+ right: 15px;
310
+ margin-top: -3px;
311
+ width: 0;
312
+ height: 0;
313
+ border-style: solid;
314
+ border-width: 5px 5px 0 5px;
315
+ border-color: #808080 transparent transparent transparent;
316
+ }
317
+ /* Cutomization for select */
318
+ .elementor-control-modula_gallery_select .selectize-control.single .selectize-input:after {
319
+ display:none;
320
+ }
321
+
322
+ .selectize-control.single .selectize-input.dropdown-active:after {
323
+ margin-top: -4px;
324
+ border-width: 0 5px 5px 5px;
325
+ border-color: transparent transparent #808080 transparent;
326
+ }
327
+ .selectize-control.rtl.single .selectize-input:after {
328
+ left: 15px;
329
+ right: auto;
330
+ }
331
+ .selectize-control.rtl .selectize-input > input {
332
+ margin: 0 4px 0 -2px !important;
333
+ }
334
+ .selectize-control .selectize-input.disabled {
335
+ opacity: 0.5;
336
+ background-color: #fafafa;
337
+ }
338
+ .selectize-control.multi .selectize-input.has-items {
339
+ padding-left: 5px;
340
+ padding-right: 5px;
341
+ }
342
+ .selectize-control.multi .selectize-input.disabled [data-value] {
343
+ color: #999;
344
+ text-shadow: none;
345
+ background: none;
346
+ -webkit-box-shadow: none;
347
+ box-shadow: none;
348
+ }
349
+ .selectize-control.multi .selectize-input.disabled [data-value],
350
+ .selectize-control.multi .selectize-input.disabled [data-value] .remove {
351
+ border-color: #e6e6e6;
352
+ }
353
+ .selectize-control.multi .selectize-input.disabled [data-value] .remove {
354
+ background: none;
355
+ }
356
+ .selectize-control.multi .selectize-input [data-value] {
357
+ text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3);
358
+ -webkit-border-radius: 3px;
359
+ -moz-border-radius: 3px;
360
+ border-radius: 3px;
361
+ background-color: #1b9dec;
362
+ background-image: -moz-linear-gradient(top, #1da7ee, #178ee9);
363
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9));
364
+ background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9);
365
+ background-image: -o-linear-gradient(top, #1da7ee, #178ee9);
366
+ background-image: linear-gradient(to bottom, #1da7ee, #178ee9);
367
+ background-repeat: repeat-x;
368
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0);
369
+ -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
370
+ box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
371
+ }
372
+ .selectize-control.multi .selectize-input [data-value].active {
373
+ background-color: #0085d4;
374
+ background-image: -moz-linear-gradient(top, #008fd8, #0075cf);
375
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf));
376
+ background-image: -webkit-linear-gradient(top, #008fd8, #0075cf);
377
+ background-image: -o-linear-gradient(top, #008fd8, #0075cf);
378
+ background-image: linear-gradient(to bottom, #008fd8, #0075cf);
379
+ background-repeat: repeat-x;
380
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0);
381
+ }
382
+ .selectize-control.single .selectize-input {
383
+ -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
384
+ box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
385
+ background-color: #f9f9f9;
386
+ background-image: -moz-linear-gradient(top, #fefefe, #f2f2f2);
387
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f2f2f2));
388
+ background-image: -webkit-linear-gradient(top, #fefefe, #f2f2f2);
389
+ background-image: -o-linear-gradient(top, #fefefe, #f2f2f2);
390
+ background-image: linear-gradient(to bottom, #fefefe, #f2f2f2);
391
+ background-repeat: repeat-x;
392
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffefefe', endColorstr='#fff2f2f2', GradientType=0);
393
+ }
394
+ .selectize-control.single .selectize-input,
395
+ .selectize-dropdown.single {
396
+ border-color: #b8b8b8;
397
+ }
398
+ .selectize-dropdown .optgroup-header {
399
+ padding-top: 7px;
400
+ font-weight: bold;
401
+ font-size: 0.85em;
402
+ }
403
+ .selectize-dropdown .optgroup {
404
+ border-top: 1px solid #f0f0f0;
405
+ }
406
+ .selectize-dropdown .optgroup:first-child {
407
+ border-top: 0 none;
408
+ }
admin/img/banner.png ADDED
Binary file
admin/img/features/1.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 65 65"><title>service-1</title><g id="group-11svg"><path id="path-1" d="M65,57a8,8,0,0,1-8,8H14a8,8,0,0,1-8-8V13a8,8,0,0,1,8-8H57a8,8,0,0,1,8,8Z" style="fill:#a999f6"/><path id="path-2" d="M52.5,63H9.5A9.56,9.56,0,0,1,0,53.5V9.5A9.5,9.5,0,0,1,9.5,0h43A9.5,9.5,0,0,1,62,9.5v44A9.56,9.56,0,0,1,52.5,63ZM9.5,3A6.49,6.49,0,0,0,3,9.5v44A6.49,6.49,0,0,0,9.5,60h43A6.49,6.49,0,0,0,59,53.5V9.5A6.49,6.49,0,0,0,52.5,3Z"/><path id="path-5" d="M32.8,25.3c-3.8,0-7-5.2-7-8.4,0-3.4,3.3-6.4,7-6.4s7,3,7,6.4S36.6,25.3,32.8,25.3Zm0-11.8c-2,0-4,1.7-4,3.4,0,2.2,2.4,5.4,4,5.4s4-3.2,4-5.4A4.1,4.1,0,0,0,32.8,13.5Z"/><path id="path-6" d="M41.2,36a1.42,1.42,0,0,1-1.4-.7,7.61,7.61,0,0,0-14.1.1,1.7,1.7,0,0,1-1.3,1l-2.3.1a1.5,1.5,0,0,1-1.1-.4,1.61,1.61,0,0,1-.5-1.1,12.2,12.2,0,1,1,24.4,0,1.37,1.37,0,0,1-1.5,1.3L41.2,36ZM32.8,25.7A9,9,0,0,0,24,32.4a10.59,10.59,0,0,1,17.6,0A9.12,9.12,0,0,0,32.8,25.7Z"/><path id="path-7" d="M43,46H22a1.5,1.5,0,0,1,0-3H43a1.5,1.5,0,0,1,0,3Z"/><path id="path-8" d="M43,53H22a1.5,1.5,0,0,1,0-3H43a1.5,1.5,0,0,1,0,3Z"/></g></svg>
admin/img/features/10.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 64"><title>Group 48</title><g id="group-48svg"><path id="path-1" d="M58,56a8,8,0,0,1-8,8H15a8,8,0,0,1-8-8V12a8,8,0,0,1,8-8H50a8,8,0,0,1,8,8Z" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-2" d="M44.5,63H9.5A9.51,9.51,0,0,1,0,53.5V9.5A9.51,9.51,0,0,1,9.5,0h35A9.51,9.51,0,0,1,54,9.5v44A9.51,9.51,0,0,1,44.5,63ZM9.5,3A6.51,6.51,0,0,0,3,9.5v44A6.51,6.51,0,0,0,9.5,60h35A6.51,6.51,0,0,0,51,53.5V9.5A6.51,6.51,0,0,0,44.5,3Z" style="fill-rule:evenodd"/><path id="path-3" d="M46,14H10a1.5,1.5,0,0,1,0-3H46a1.5,1.5,0,0,1,0,3Z" style="fill-rule:evenodd"/><path id="path-4" d="M46,22H10a1.5,1.5,0,0,1,0-3H46a1.5,1.5,0,0,1,0,3Z" style="fill-rule:evenodd"/><path id="path-5" d="M30.06,31.81l2.32,4.7,5.18.76-3.75,3.66.89,5.17-4.64-2.44L25.42,46.1l.88-5.17-3.75-3.66,5.19-.76Z" style="fill:#fff;fill-rule:evenodd"/><path id="path-6" d="M22.42,46.6a1.53,1.53,0,0,1-.89-.29,1.5,1.5,0,0,1-.59-1.47l.75-4.39L18.5,37.34a1.48,1.48,0,0,1-.38-1.53,1.52,1.52,0,0,1,1.21-1l4.41-.63,2-4a1.51,1.51,0,0,1,1.35-.84h0a1.5,1.5,0,0,1,1.34.84l2,4,4.41.63a1.51,1.51,0,0,1,1.21,1,1.48,1.48,0,0,1-.38,1.53l-3.19,3.11.76,4.39a1.52,1.52,0,0,1-.6,1.47,1.48,1.48,0,0,1-1.58.11l-3.94-2.07-3.95,2.07A1.45,1.45,0,0,1,22.42,46.6Zm.35-9.28,1.58,1.53a1.52,1.52,0,0,1,.43,1.33l-.37,2.17,1.95-1a1.49,1.49,0,0,1,1.39,0l1.95,1-.37-2.17a1.52,1.52,0,0,1,.43-1.33l1.58-1.53L29.16,37A1.5,1.5,0,0,1,28,36.18l-1-2-1,2A1.5,1.5,0,0,1,25,37Z" style="fill-rule:evenodd"/></g></svg>
admin/img/features/11.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 65 57.05"><title>service-10</title><g id="group-24svg"><path id="path-1" d="M65,49.05a8,8,0,0,1-8,8H15a8,8,0,0,1-8-8V13a8,8,0,0,1,8-8H57a8,8,0,0,1,8,8Z" transform="translate(0 -0.01)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-2" d="M65,18V12.3C65,8,61.31,5,57.06,5H14.73C10.48,5,7,8,7,12.3V18Z" transform="translate(0 -0.01)" style="fill:#8479d1;fill-rule:evenodd"/><path id="path-3" d="M51.5,55.05H9.5A9.51,9.51,0,0,1,0,45.54v-36A9.51,9.51,0,0,1,9.5,0h42A9.51,9.51,0,0,1,61,9.52v36A9.51,9.51,0,0,1,51.5,55.05ZM9.5,3A6.51,6.51,0,0,0,3,9.52v36a6.51,6.51,0,0,0,6.5,6.51h42A6.51,6.51,0,0,0,58,45.54v-36A6.51,6.51,0,0,0,51.5,3Z" transform="translate(0 -0.01)" style="fill-rule:evenodd"/><path id="path-4" d="M59.5,17H1.5A1.5,1.5,0,0,1,0,15.52V9.41A9.54,9.54,0,0,1,9.44,0H51.76A9.44,9.44,0,0,1,61,9.41v6.11A1.5,1.5,0,0,1,59.5,17ZM3,14H58V9.41A6.4,6.4,0,0,0,51.76,3H9.44A6.59,6.59,0,0,0,3,9.41Z" transform="translate(0 -0.01)" style="fill-rule:evenodd"/><path id="path-5" d="M13,8.91a1.7,1.7,0,1,1-1.7-1.7A1.7,1.7,0,0,1,13,8.91Z" transform="translate(0 -0.01)" style="fill-rule:evenodd"/><path id="path-6" d="M18.62,8.91a1.7,1.7,0,1,1-1.7-1.7A1.7,1.7,0,0,1,18.62,8.91Z" transform="translate(0 -0.01)" style="fill-rule:evenodd"/><path id="path-7" d="M24.28,8.91a1.7,1.7,0,1,1-1.7-1.7A1.7,1.7,0,0,1,24.28,8.91Z" transform="translate(0 -0.01)" style="fill-rule:evenodd"/><path id="path-8" d="M41.5,54.55a1.5,1.5,0,0,1-1.5-1.5V16a1.5,1.5,0,1,1,3,0v37A1.5,1.5,0,0,1,41.5,54.55Z" transform="translate(0 -0.01)" style="fill-rule:evenodd"/><path id="path-9" d="M55,26H47a1.5,1.5,0,0,1,0-3h8a1.5,1.5,0,0,1,0,3Z" transform="translate(0 -0.01)" style="fill-rule:evenodd"/><path id="path-10" d="M55,35H47a1.5,1.5,0,0,1,0-3h8a1.5,1.5,0,0,1,0,3Z" transform="translate(0 -0.01)" style="fill-rule:evenodd"/><path id="path-11" d="M55,44H47a1.5,1.5,0,0,1,0-3h8a1.5,1.5,0,0,1,0,3Z" transform="translate(0 -0.01)" style="fill-rule:evenodd"/></g></svg>
admin/img/features/12.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 63 63.05"><title>service-12</title><g id="group-38svg"><path id="path-1" d="M52.78,26.5a1.51,1.51,0,0,1-1.46-1.16A4.32,4.32,0,0,0,47.21,22h-14a4.22,4.22,0,0,0-4.12,3.24,1.5,1.5,0,1,1-2.92-.7,7.22,7.22,0,0,1,7-5.54h14a7.35,7.35,0,0,1,7,5.64,1.5,1.5,0,0,1-1.11,1.81A1.41,1.41,0,0,1,52.78,26.5Z" transform="translate(0 0.01)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-2" d="M34.84,49.08a1.52,1.52,0,0,1-.88-.29,1.5,1.5,0,0,1-.33-2.1l.45-.61a7.12,7.12,0,0,1,6-3.06h.31a7.17,7.17,0,0,1,6,3l.41.51a1.5,1.5,0,1,1-2.34,1.88L44,47.9a.41.41,0,0,1-.07-.11A4.16,4.16,0,0,0,40.37,46h-.31a4.13,4.13,0,0,0-3.52,1.77l-.48.67A1.53,1.53,0,0,1,34.84,49.08Z" transform="translate(0 0.01)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-3" d="M39.27,44.52A1.43,1.43,0,0,1,37.92,43V21a1.36,1.36,0,1,1,2.7,0V43A1.43,1.43,0,0,1,39.27,44.52Z" transform="translate(0 0.01)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-4" d="M53.13,63H47.91a1.5,1.5,0,0,1,0-3h5.22a7.71,7.71,0,0,0,2.29-.35,1.5,1.5,0,0,1,.89,2.87A10.61,10.61,0,0,1,53.13,63ZM39.91,63h-8a1.5,1.5,0,1,1,0-3h8a1.5,1.5,0,0,1,0,3Zm-16,0h-8a1.5,1.5,0,1,1,0-3h8a1.5,1.5,0,0,1,0,3ZM7.92,63l-.22,0A8.73,8.73,0,0,1,.56,57.22a1.5,1.5,0,1,1,2.82-1A5.78,5.78,0,0,0,8.12,60a1.5,1.5,0,0,1-.2,3ZM61.28,57.2a1.57,1.57,0,0,1-.37-.05,1.49,1.49,0,0,1-1.08-1.82,5.9,5.9,0,0,0,.17-1.4V47.71a1.5,1.5,0,0,1,3,0v6.22a9,9,0,0,1-.26,2.14A1.51,1.51,0,0,1,61.28,57.2ZM1.5,50.26A1.5,1.5,0,0,1,0,48.76v-8a1.5,1.5,0,0,1,3,0v8A1.5,1.5,0,0,1,1.5,50.26Zm60-9A1.51,1.51,0,0,1,60,39.7v-8a1.5,1.5,0,0,1,3,0v8A1.51,1.51,0,0,1,61.5,41.21Zm-60-7A1.5,1.5,0,0,1,0,32.75v-8a1.5,1.5,0,0,1,3,0v8A1.5,1.5,0,0,1,1.5,34.25Zm60-9.06a1.5,1.5,0,0,1-1.5-1.5v-8a1.5,1.5,0,1,1,3,0v8A1.5,1.5,0,0,1,61.5,25.19Zm-.32-16a1.5,1.5,0,0,1-1.44-1.07,7.44,7.44,0,0,0-4.35-4.72A1.5,1.5,0,1,1,56.48.63a10.5,10.5,0,0,1,6.14,6.66,1.5,1.5,0,0,1-1,1.86A1.52,1.52,0,0,1,61.18,9.21ZM48,3H40a1.5,1.5,0,0,1,0-3h8a1.5,1.5,0,0,1,0,3ZM32,3H24a1.5,1.5,0,0,1,0-3h8a1.5,1.5,0,0,1,0,3Z" transform="translate(0 0.01)" style="fill-rule:evenodd"/><path id="path-5" d="M15.31,4.07a2.08,2.08,0,0,0-.86-.15H4.52A1.82,1.82,0,0,0,2.71,5.75v10a1.88,1.88,0,0,0,.12.83A1.74,1.74,0,0,0,5.7,17l3.62-3.63,6.42,6.42a1.93,1.93,0,0,0,2.73-2.72L12,10.62l3.7-3.7A1.73,1.73,0,0,0,15.31,4.07Z" transform="translate(0 0.01)" style="fill:#a999f6;fill-rule:evenodd"/></g></svg>
admin/img/features/13.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 65 57.05"><title>service-13</title><g id="group-8svg"><path id="path-1" d="M65,49a8,8,0,0,1-8,8H15a8,8,0,0,1-8-8V13a8,8,0,0,1,8-8H57a8,8,0,0,1,8,8Z" transform="translate(0 0)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-2" d="M65,18V12.18C65,7.93,61.31,5,57.06,5H14.73C10.48,5,7,7.93,7,12.18V18Z" transform="translate(0 0)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-3" d="M51.5,55H9.5A9.52,9.52,0,0,1,0,45.53V9.5A9.51,9.51,0,0,1,9.5,0h42A9.51,9.51,0,0,1,61,9.5v36A9.52,9.52,0,0,1,51.5,55ZM9.5,3A6.51,6.51,0,0,0,3,9.5v36A6.51,6.51,0,0,0,9.5,52h42A6.51,6.51,0,0,0,58,45.53V9.5A6.51,6.51,0,0,0,51.5,3Z" transform="translate(0 0)" style="fill-rule:evenodd"/><path id="path-4" d="M59.5,17H1.5A1.5,1.5,0,0,1,0,15.51V9.3A9.5,9.5,0,0,1,9.44,0H51.77A9.27,9.27,0,0,1,61,9.3v6.21A1.5,1.5,0,0,1,59.5,17ZM3,14H58V9.3A6.34,6.34,0,0,0,51.77,3H9.44A6.54,6.54,0,0,0,3,9.3Z" transform="translate(0 0)" style="fill-rule:evenodd"/><path id="path-5" d="M13,8.8a1.7,1.7,0,1,1-1.7-1.7A1.7,1.7,0,0,1,13,8.8Z" transform="translate(0 0)" style="fill-rule:evenodd"/><path id="path-6" d="M18.62,8.8a1.7,1.7,0,1,1-1.7-1.7A1.7,1.7,0,0,1,18.62,8.8Z" transform="translate(0 0)" style="fill-rule:evenodd"/><path id="path-7" d="M24.28,8.8a1.7,1.7,0,1,1-1.7-1.7A1.7,1.7,0,0,1,24.28,8.8Z" transform="translate(0 0)" style="fill-rule:evenodd"/><path id="path-8" d="M27.61,39h-.06a1.54,1.54,0,0,1-1.08-.58l-3.33-3.92a1.51,1.51,0,0,1,2.28-2l2.27,2.66,6.64-6.64a1.49,1.49,0,0,1,2.12,0,1.51,1.51,0,0,1,0,2.12l-7.78,7.84A1.56,1.56,0,0,1,27.61,39Z" transform="translate(0 0)" style="fill-rule:evenodd"/></g></svg>
admin/img/features/14.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 45 43.8"><title>ratings</title><g id="group-13svg"><path id="path-1" d="M45.1,18.8,34.4,28.6l2,14.4L23.8,35.8l-13,6.3,2.9-14.2L3.7,17.5l14.4-1.6L24.9,3l6,13.2Z" transform="translate(-0.1 0.8)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-2" d="M7.9,41.1c-.1,0-.1-.1-.2-.1a1.24,1.24,0,0,1-.5-1.5L10,26.1.5,16.2a1.4,1.4,0,0,1-.3-1.5,1.72,1.72,0,0,1,1.2-1L15,12.1,21.4,0a1.6,1.6,0,0,1,1.4-.8,1.42,1.42,0,0,1,1.3.9l5.7,12.5L43.3,15a1.5,1.5,0,0,1,1.2,1.1,1.44,1.44,0,0,1-.4,1.5L34,26.9l1.9,13.6a1.45,1.45,0,0,1-.6,1.4,1.81,1.81,0,0,1-1.6.1L21.8,35.2l-12.3,6A2.1,2.1,0,0,1,7.9,41.1ZM4.7,16.3l7.9,8.3a1.68,1.68,0,0,1,.4,1.3L10.7,37.1l10.3-5a1.7,1.7,0,0,1,1.4,0l10,5.7L30.8,26.4a1.3,1.3,0,0,1,.5-1.3l8.5-7.7-11.3-2a1.44,1.44,0,0,1-1.1-.9L22.6,4.1,17.2,14.2A1.74,1.74,0,0,1,16,15Z" transform="translate(-0.1 0.8)" style="fill-rule:evenodd"/></g></svg>
admin/img/features/15.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 61.01 63.52"><title>Group 20</title><g id="group-20svg"><path id="path-1" d="M41.63,12.93a1.54,1.54,0,0,1-1.05-.43,1.5,1.5,0,0,1,0-2.12l3.83-4L40.56,2.54A1.5,1.5,0,1,1,42.69.43l4.85,4.92a1.5,1.5,0,0,1,0,2.1l-4.85,5A1.53,1.53,0,0,1,41.63,12.93Z" transform="translate(-0.02 0.02)" style="fill-rule:evenodd"/><path id="path-2" d="M1.52,7.47C.69,7.47,0,7,0,6.21a1.58,1.58,0,0,1,1.49-1.6l45-.15h0A1.7,1.7,0,0,1,48,6.18a1.39,1.39,0,0,1-1.5,1.37l-45-.08Z" transform="translate(-0.02 0.02)" style="fill-rule:evenodd"/><path id="path-3" d="M39.25,63.51H19.65a7.31,7.31,0,0,1-7.17-7.33L6.78,26.63A7.16,7.16,0,0,1,14,19.47h20.3" transform="translate(-0.02 0.02)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-4" d="M61,55.47a8,8,0,0,1-8,8H33.91a8,8,0,0,1-8-8l-3-28a8,8,0,0,1,8-8H50a8,8,0,0,1,8,8Z" transform="translate(-0.02 0.02)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-5" d="M48,62.5H28.91a9.32,9.32,0,0,1-9.54-9.19l-3-28.11a.76.76,0,0,1,0-.15,9.57,9.57,0,0,1,9.53-9.58H45A9.56,9.56,0,0,1,54.51,25l3,28.1c0,.06,0,.11,0,.16A9.31,9.31,0,0,1,48,62.5ZM19.36,25l3,28.09a.92.92,0,0,1,0,.16,6.34,6.34,0,0,0,6.54,6.28H48a6.35,6.35,0,0,0,6.54-6.2l-3-28.1c0-.05,0-.1,0-.15A6.56,6.56,0,0,0,45,18.47H25.89A6.56,6.56,0,0,0,19.36,25Z" transform="translate(-0.02 0.02)" style="fill-rule:evenodd"/><path id="path-6" d="M34.25,62.5H14.65A8.62,8.62,0,0,1,6,54.08L.31,24.54a1.42,1.42,0,0,1,0-.29A8.73,8.73,0,0,1,9,15.47h20.3a1.5,1.5,0,1,1,0,3H9a5.72,5.72,0,0,0-5.66,5.65L9,53.65a1.27,1.27,0,0,1,0,.28,5.56,5.56,0,0,0,5.67,5.57h19.6a1.5,1.5,0,0,1,0,3Z" transform="translate(-0.02 0.02)" style="fill-rule:evenodd"/></g></svg>
admin/img/features/2.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 65"><title>service-2</title><g id="group-54svg"><path id="path-1" d="M58,57a8,8,0,0,1-8,8H14a8,8,0,0,1-8-8V13a8,8,0,0,1,8-8H50a8,8,0,0,1,8,8Z" style="fill:#a999f6"/><path id="path-2" d="M45.5,63H9.5A9.56,9.56,0,0,1,0,53.5V9.5A9.56,9.56,0,0,1,9.5,0h36A9.56,9.56,0,0,1,55,9.5v44A9.56,9.56,0,0,1,45.5,63ZM9.5,3A6.49,6.49,0,0,0,3,9.5v44A6.49,6.49,0,0,0,9.5,60h36A6.49,6.49,0,0,0,52,53.5V9.5A6.49,6.49,0,0,0,45.5,3Z"/><path id="path-3" d="M5,20H3a1.5,1.5,0,0,1,0-3H5a1.5,1.5,0,0,1,0,3Z"/><path id="path-4" d="M52,20H5a1.5,1.5,0,0,1,0-3H52a1.5,1.5,0,0,1,0,3Z"/><path id="path-5" d="M54,20H52a1.5,1.5,0,0,1,0-3h2a1.5,1.5,0,0,1,0,3Z"/><path id="path-6" d="M17.2,11.1a2.58,2.58,0,0,1-2.6,2.6A2.52,2.52,0,0,1,12,11.1a2.6,2.6,0,0,1,5.2,0Z"/><path id="path-7" d="M26.4,11.1a2.6,2.6,0,1,1-2.6-2.6A2.65,2.65,0,0,1,26.4,11.1Z"/><path id="path-8" d="M34.6,11.1A2.6,2.6,0,1,1,32,8.5,2.65,2.65,0,0,1,34.6,11.1Z"/><path id="path-9" d="M37.9,45.4a1.21,1.21,0,0,1-1.2-.7,1.48,1.48,0,0,1,.4-2.1l7.6-5.2-7.5-3.8a1.5,1.5,0,1,1,1.3-2.7l7.7,4a2.64,2.64,0,0,1,1.5,2.3,2.71,2.71,0,0,1-1.1,2.5l-8,5.4A.78.78,0,0,1,37.9,45.4Z"/><path id="path-10" d="M20.7,49.4a1.14,1.14,0,0,1-.8-.3L12,43.7a2.9,2.9,0,0,1-1.2-2.5,3.09,3.09,0,0,1,1.5-2.3l7.7-4a1.5,1.5,0,0,1,1.3,2.7l-7.5,3.8,7.7,5.2a1.48,1.48,0,0,1,.4,2.1A1.21,1.21,0,0,1,20.7,49.4Z" style="fill:#020202"/><path id="path-11" d="M26.4,52.3a.6.6,0,0,1-.4-.1,1.54,1.54,0,0,1-1-1.9l6.6-21.1a1.52,1.52,0,1,1,2.9.9L27.9,51.2A1.5,1.5,0,0,1,26.4,52.3Z" style="fill:#050505"/></g></svg>
admin/img/features/3.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 62.41 56.4"><title>service-3</title><g id="group-16svg"><path id="path-1" d="M15.5,3.5V34.3A15.1,15.1,0,0,0,30.9,18.9,15.1,15.1,0,0,0,15.5,3.5Z" style="fill:#a999f6"/><path id="path-2" d="M30.3,11.4A29.44,29.44,0,0,1,31.8,19a29.44,29.44,0,0,1-1.5,7.6A7.3,7.3,0,0,0,37.9,19,7.24,7.24,0,0,0,30.3,11.4Z" style="fill:#a999f6"/><path id="path-3" d="M13,33.9a1.54,1.54,0,0,1-1.5-1.5V1.5A1.54,1.54,0,0,1,13,0,16.57,16.57,0,0,1,29.9,16.9,16.59,16.59,0,0,1,13,33.9ZM14.5,3.1V30.8A13.51,13.51,0,0,0,26.9,17,13.54,13.54,0,0,0,14.5,3.1Z"/><path id="path-4" d="M27.3,26a1.43,1.43,0,0,1-1.2-.6,1.4,1.4,0,0,1-.2-1.4,28.54,28.54,0,0,0,1.4-7.1,29.73,29.73,0,0,0-1.4-7.1,1.4,1.4,0,0,1,.2-1.4,1.43,1.43,0,0,1,1.2-.6,8.77,8.77,0,0,1,9.1,9.1A8.77,8.77,0,0,1,27.3,26Zm2.1-14.8a26.08,26.08,0,0,1,.9,5.8,27,27,0,0,1-.9,5.8,5.79,5.79,0,0,0,4-5.8A5.79,5.79,0,0,0,29.4,11.2Z"/><path id="path-5" d="M12.5,11.9H1.5a1.5,1.5,0,0,1,0-3h11a1.5,1.5,0,0,1,0,3Z"/><path id="path-6" d="M12.5,23.9H1.5a1.5,1.5,0,0,1,0-3h11a1.5,1.5,0,0,1,0,3Z"/><path id="path-7" d="M61,56.4a1.54,1.54,0,0,1-1.5-1.5V46.1c0-2.5-1.6-5.2-4.2-5.2h-.1c-4.4-.1-7.8-3.2-7.8-7.2v-10A4.54,4.54,0,0,0,43,18.9H35.4a1.5,1.5,0,0,1,0-3H43a7.54,7.54,0,0,1,7.4,7.8V33.8c0,2.7,2.6,4.2,5,4.2h.1c4.3.2,6.9,4.2,6.9,8.2V55A1.36,1.36,0,0,1,61,56.4Z"/></g></svg>
admin/img/features/4.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 64"><title>service-4</title><g id="group-59svg"><path id="path-1" d="M58,56a8,8,0,0,1-8,8H15a8,8,0,0,1-8-8V12a8,8,0,0,1,8-8H50a8,8,0,0,1,8,8Z" style="fill:#a999f6"/><path id="path-2" d="M44.5,63H9.5A9.56,9.56,0,0,1,0,53.5V9.5A9.56,9.56,0,0,1,9.5,0h35A9.56,9.56,0,0,1,54,9.5v44A9.56,9.56,0,0,1,44.5,63ZM9.5,3A6.49,6.49,0,0,0,3,9.5v44A6.49,6.49,0,0,0,9.5,60h35A6.49,6.49,0,0,0,51,53.5V9.5A6.49,6.49,0,0,0,44.5,3Z"/><path id="path-3" d="M46,14H10a1.5,1.5,0,0,1,0-3H46a1.5,1.5,0,0,1,0,3Z"/><path id="path-4" d="M46,22H10a1.5,1.5,0,0,1,0-3H46a1.5,1.5,0,0,1,0,3Z"/><path id="path-5" d="M19.5,34.5a6,6,0,0,1,1-3.2,8.38,8.38,0,0,1,2.9-2.7,9.36,9.36,0,0,1,4.5-1.1,9.28,9.28,0,0,1,4.2.9,6.65,6.65,0,0,1,2.8,2.4,5.58,5.58,0,0,1,1,3.3,5.14,5.14,0,0,1-.6,2.5,6.23,6.23,0,0,1-1.4,1.8c-.5.5-1.5,1.4-2.8,2.6a3.82,3.82,0,0,0-.9.9c-.2.3-.4.5-.5.7l-.3.6a5.31,5.31,0,0,0-.3,1.1,1.88,1.88,0,0,1-2,1.7,1.68,1.68,0,0,1-1.4-.6,2.2,2.2,0,0,1-.6-1.7,6.05,6.05,0,0,1,.4-2.4,7.84,7.84,0,0,1,1.1-1.8c.5-.5,1.1-1.1,1.9-1.8s1.2-1.1,1.5-1.4a4.44,4.44,0,0,0,.8-1.1,2.77,2.77,0,0,0,.3-1.3,2.7,2.7,0,0,0-1-2.2,3.66,3.66,0,0,0-2.5-.9,3.67,3.67,0,0,0-2.7.9,6.84,6.84,0,0,0-1.5,2.7c-.4,1.2-1.1,1.9-2.1,1.9a2.13,2.13,0,0,1-1.6-.7A5.43,5.43,0,0,1,19.5,34.5Zm8.1,18.1a2.41,2.41,0,0,1-1.8-.7,2.2,2.2,0,0,1-.8-1.8,2.5,2.5,0,1,1,5,0,2.14,2.14,0,0,1-.7,1.8A2.34,2.34,0,0,1,27.6,52.6Z"/></g></svg>
admin/img/features/5.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 61 60"><title>service-5</title><g id="group-25svg"><path id="path-1" d="M61,52a8,8,0,0,1-8,8H13a8,8,0,0,1-8-8V12a8,8,0,0,1,8-8H53a8,8,0,0,1,8,8Z" style="fill:#a999f6"/><path id="path-2" d="M12.7,60H26V4H12.7C8.3,4,4,7.8,4,12.2v40C4,56.6,8.3,60,12.7,60Z" style="fill:#a999f6"/><path id="path-3" d="M61,44H45V30H61Z" style="fill:#6254c6"/><path id="path-4" d="M53.2,59H45V44H61v7.2A7.68,7.68,0,0,1,53.2,59Z" style="fill:#3626aa"/><path id="path-5" d="M53.2,4H45V31H61V11.7A7.6,7.6,0,0,0,53.2,4Z" style="fill:#8479d1"/><path id="path-6" d="M49.5,59H9.5A9.5,9.5,0,0,1,0,49.5V9.5A9.56,9.56,0,0,1,9.5,0h40A9.56,9.56,0,0,1,59,9.5v40A9.5,9.5,0,0,1,49.5,59ZM9.5,3A6.49,6.49,0,0,0,3,9.5v40A6.49,6.49,0,0,0,9.5,56h40A6.49,6.49,0,0,0,56,49.5V9.5A6.49,6.49,0,0,0,49.5,3Z"/><path id="path-7" d="M40.5,58.5A1.54,1.54,0,0,1,39,57V2a1.5,1.5,0,0,1,3,0V57A1.47,1.47,0,0,1,40.5,58.5Z"/><path id="path-8" d="M23.5,58.5A1.54,1.54,0,0,1,22,57V2a1.5,1.5,0,0,1,3,0V57A1.47,1.47,0,0,1,23.5,58.5Z"/><path id="path-9" d="M57,30H41a1.5,1.5,0,0,1,0-3H57a1.5,1.5,0,0,1,0,3Z"/><path id="path-10" d="M57,43H41a1.5,1.5,0,0,1,0-3H57a1.5,1.5,0,0,1,0,3Z"/></g></svg>
admin/img/features/6.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 63.36 63.2"><title>service-6</title><g id="group-4svg"><path id="path-1" d="M24.9,63.2,39.1,51a4.19,4.19,0,0,1,2.8-1l3.6-.2c16.8,0,17.3-12,16.9-12H48.8a4,4,0,0,1-2.6-.7l-3-2a4.35,4.35,0,0,1-1.9-3.2V24.8a4.22,4.22,0,0,1,1.4-2.9L46,18.3a4.41,4.41,0,0,1,3.2-1.5H62.4c.4,0,.8-.1.9-.4a.76.76,0,0,0-.1-1A21.31,21.31,0,0,0,45.4,6.5a21.83,21.83,0,0,0-20.5,15c-.1.2-.3.7-.7,5.3a9.41,9.41,0,0,1-3.5,6.5L3.5,47.3Z" transform="translate(-0.08)" style="fill:#a999f6"/><path id="path-2" d="M22.9,59.7a1.61,1.61,0,0,1-1.1-.5,1.46,1.46,0,0,1,.2-2.1L36.2,44.9A5.94,5.94,0,0,1,40,43.5h3.6c5.9,0,10.3-1.5,13-4.4A9.7,9.7,0,0,0,59,34.7H46.8a5.5,5.5,0,0,1-3.5-1.2l-2.7-2.1a5.64,5.64,0,0,1-2.2-4.5V19.8a5.61,5.61,0,0,1,1.5-3.9l3.2-3.4a5.57,5.57,0,0,1,4.2-1.7H59.8A20.1,20.1,0,0,0,43.5,3,20.46,20.46,0,0,0,24.4,16.9a36.55,36.55,0,0,0-.6,4.9,11,11,0,0,1-4,7.5L2.5,43.5a1.55,1.55,0,0,1-2.1-.2,1.55,1.55,0,0,1,.2-2.1L17.9,27a7.9,7.9,0,0,0,2.9-5.5c.4-4.7.6-5.2.7-5.6A23.6,23.6,0,0,1,43.5,0a22.69,22.69,0,0,1,19,9.6,3.07,3.07,0,0,1,.2,2.8,2.76,2.76,0,0,1-2.3,1.5H47.3a2.48,2.48,0,0,0-2,.8L42.1,18a2.41,2.41,0,0,0-.7,1.8v7.1a3,3,0,0,0,1,2.2l2.7,2.1a2.72,2.72,0,0,0,1.7.6H60.5c.2,0,1.6.1,1.6,1.9a12.35,12.35,0,0,1-3.3,7.5q-4.95,5.4-15.3,5.4H39.9a2.41,2.41,0,0,0-1.8.7l-14.2,12A1.43,1.43,0,0,1,22.9,59.7Z" transform="translate(-0.08)"/></g></svg>
admin/img/features/7.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 63 47.4"><title>service-7</title><g id="group-30svg"><path id="path-1" d="M59,46.18a27,27,0,0,0,3.64-13.59,28.07,28.07,0,0,0-56.13,0,27.28,27.28,0,0,0,3.61,13.63Z" transform="translate(0.01 0.22)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-2" d="M29.44,32.45,42.8,15,33.73,35a2.52,2.52,0,0,1-4.59-2A2.86,2.86,0,0,1,29.44,32.45Z" transform="translate(0.01 0.22)" style="fill-rule:evenodd"/><path id="path-3" d="M57.65,47.18H5.36a1.49,1.49,0,0,1-1.28-.72A29.51,29.51,0,0,1,0,31.24a31.5,31.5,0,1,1,63,.09h0a29,29,0,0,1-4.07,15.13A1.52,1.52,0,0,1,57.65,47.18Zm-51.43-3H56.79A26.2,26.2,0,0,0,60,31.33a28.5,28.5,0,1,0-57-.09H3A26.79,26.79,0,0,0,6.22,44.18Z" transform="translate(0.01 0.22)" style="fill-rule:evenodd"/><path id="path-4" d="M31.15,9.65a1.5,1.5,0,0,1-1.5-1.5v-6a1.5,1.5,0,1,1,3,0v6A1.5,1.5,0,0,1,31.15,9.65Z" transform="translate(0.01 0.22)" style="fill-rule:evenodd"/><path id="path-5" d="M8.65,30.17h-7a1.51,1.51,0,0,1,0-3h7a1.51,1.51,0,0,1,0,3Z" transform="translate(0.01 0.22)" style="fill-rule:evenodd"/><path id="path-6" d="M60.65,30.17h-7a1.51,1.51,0,0,1,0-3h7a1.51,1.51,0,0,1,0,3Z" transform="translate(0.01 0.22)" style="fill-rule:evenodd"/></g></svg>
admin/img/features/8.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 66.17"><title>service-8</title><g id="group-18svg"><path id="path-1" d="M44.13,6h-27C12,6,7,10.37,7,15.55V66.17L18.53,52h25.6C49.31,52,52,47.73,52,42.55v-27C52,10.37,49.31,6,44.13,6Z" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-2" d="M1.5,63.67a1.69,1.69,0,0,1-.5-.08,1.51,1.51,0,0,1-1-1.42V11.55C0,5.4,5.2,0,11.14,0h27C44.54,0,48,6,48,11.55v27C48,44.7,43.94,49,38.13,49H13.28L2.7,63.07A1.51,1.51,0,0,1,1.5,63.67ZM11.14,3C6.88,3,3,7.08,3,11.55V57.68L11.34,46.6a1.47,1.47,0,0,1,1.19-.6h25.6C42.3,46,45,43.07,45,38.55v-27C45,7.41,42.59,3,38.13,3Z" style="fill-rule:evenodd"/></g><path d="M16,23.27a4.28,4.28,0,1,1-4.28,4.28l0-.61a8.57,8.57,0,0,1,8.57-8.56v2.44a6.11,6.11,0,0,0-4.33,1.79,8.17,8.17,0,0,0-.61.71A4.91,4.91,0,0,1,16,23.27Zm11,0a4.28,4.28,0,1,1-4.29,4.28l0-.61a8.57,8.57,0,0,1,8.57-8.56v2.44A6.12,6.12,0,0,0,27,22.61a8.17,8.17,0,0,0-.61.71A5.06,5.06,0,0,1,27,23.27Z"/></svg>
admin/img/features/9.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 66.32 69.11"><title>Group 31</title><g id="group-31svg"><path id="path-1" d="M33.79,69H15.49c-3.93,0-7.13-3.66-7.13-7.59V22.14c0-3.93,3.2-6.7,7.13-6.7H41.38c3.95,0,7.16,2.75,7.16,6.7V50.65Z" transform="translate(-3 0.09)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-2" d="M47.47,52.05H37.36c-2,0-4,1.6-4,3.57V68.57Z" transform="translate(-3 0.09)" style="fill:#8479d1;fill-rule:evenodd"/><path id="path-3" d="M30.67,67.23H11.92c-4.75,0-8.92-4-8.92-8.48V19.46C3,14.94,7.17,11,11.92,11H37.81c4.59,0,8,3.64,8,8.48V48a1.33,1.33,0,0,1-.28.82L31.73,66.71A1.35,1.35,0,0,1,30.67,67.23ZM11.92,13.66c-3.33,0-6.24,2.71-6.24,5.8V58.75c0,3.09,2.91,5.8,6.24,5.8H30l13.17-17V19.46a5.47,5.47,0,0,0-5.37-5.8Z" transform="translate(-3 0.09)" style="fill-rule:evenodd"/><path id="path-4" d="M30.23,67a1.42,1.42,0,0,1-.45-.08,1.34,1.34,0,0,1-.89-1.26V52.93c0-2.88,3.13-5.34,5.68-5.34h9.62a1.33,1.33,0,0,1,1,2.19l-14,16.78A1.34,1.34,0,0,1,30.23,67Zm4.34-16.77c-1.29,0-3,1.51-3,2.66V62l9.77-11.73Z" transform="translate(-3 0.09)" style="fill-rule:evenodd"/><path id="path-5" d="M40.5,30.62H26.21a1.34,1.34,0,0,1,0-2.68H40.5a1.34,1.34,0,1,1,0,2.68Z" transform="translate(-3 0.09)" style="fill-rule:evenodd"/><path id="path-6" d="M40.5,36.87H26.21a1.34,1.34,0,0,1,0-2.68H40.5a1.34,1.34,0,1,1,0,2.68Z" transform="translate(-3 0.09)" style="fill-rule:evenodd"/><path id="path-7" d="M40.5,43.12H26.21a1.34,1.34,0,0,1,0-2.68H40.5a1.34,1.34,0,1,1,0,2.68Z" transform="translate(-3 0.09)" style="fill-rule:evenodd"/></g><g id="group-31svg-2" data-name="group-31svg"><path id="path-1-2" data-name="path-1" d="M52.8,64.91H32.3c-4.4,0-8-4.1-8-8.5v-44c0-4.4,3.58-7.5,8-7.5h29c4.42,0,8,3.08,8,7.5V44.34Z" transform="translate(-3 0.09)" style="fill:#a999f6;fill-rule:evenodd"/><path id="path-2-2" data-name="path-2" d="M68.12,45.91H56.8c-2.21,0-4.48,1.79-4.48,4v14.5Z" transform="translate(-3 0.09)" style="fill:#8479d1;fill-rule:evenodd"/><path id="path-3-2" data-name="path-3" d="M49.3,62.91h-21c-5.32,0-10-4.44-10-9.5v-44c0-5.06,4.66-9.5,10-9.5h29c5.14,0,9,4.08,9,9.5V41.34a1.49,1.49,0,0,1-.32.92L50.49,62.33A1.48,1.48,0,0,1,49.3,62.91Zm-21-60c-3.72,0-7,3-7,6.5v44c0,3.46,3.26,6.5,7,6.5H48.57L63.32,40.83V9.41c0-3.71-2.59-6.5-6-6.5Z" transform="translate(-3 0.09)" style="fill-rule:evenodd"/><path id="path-4-2" data-name="path-4" d="M48.82,62.7a1.48,1.48,0,0,1-.51-.09,1.5,1.5,0,0,1-1-1.41V46.89c0-3.22,3.49-6,6.36-6H64.45a1.5,1.5,0,0,1,1.16,2.46L50,62.16A1.5,1.5,0,0,1,48.82,62.7Zm4.86-18.79c-1.45,0-3.36,1.7-3.36,3V57.05L61.25,43.91Z" transform="translate(-3 0.09)" style="fill-rule:evenodd"/><path id="path-5-2" data-name="path-5" d="M60.32,21.91h-16a1.5,1.5,0,0,1,0-3h16a1.5,1.5,0,0,1,0,3Z" transform="translate(-3 0.09)" style="fill-rule:evenodd"/><path id="path-6-2" data-name="path-6" d="M60.32,28.91h-16a1.5,1.5,0,0,1,0-3h16a1.5,1.5,0,0,1,0,3Z" transform="translate(-3 0.09)" style="fill-rule:evenodd"/><path id="path-7-2" data-name="path-7" d="M60.32,35.91h-16a1.5,1.5,0,1,1,0-3h16a1.5,1.5,0,0,1,0,3Z" transform="translate(-3 0.09)" style="fill-rule:evenodd"/></g></svg>
admin/img/features/email.svg ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 65 57" style="enable-background:new 0 0 65 57;" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#A999F6;}
7
+ </style>
8
+ <g>
9
+ <g>
10
+ <path class="st0" d="M64.5,13.1v34.1c0,2.8-2.3,5.1-5.1,5.1h-48c-2.8,0-5.1-2.3-5.1-5.1V13.1c0-2.8,2.3-5.1,5.1-5.1h48
11
+ C62.2,8,64.5,10.3,64.5,13.1z"/>
12
+ </g>
13
+ </g>
14
+ <g>
15
+ <g>
16
+ <path d="M56.6,2.6H5.5C2.4,2.6,0,5,0,8v36.4c0,3,2.4,5.5,5.5,5.5h51.1c3,0,5.5-2.4,5.5-5.5V8C62,5,59.6,2.6,56.6,2.6z M55.8,6.2
17
+ L31.1,30.9L6.2,6.2H55.8z M3.6,43.6V8.8l17.5,17.4L3.6,43.6z M6.2,46.2l17.5-17.5l6.1,6.1c0.7,0.7,1.9,0.7,2.6,0l6-6l17.4,17.4
18
+ H6.2z M58.4,43.6L41,26.2L58.4,8.8V43.6z"/>
19
+ </g>
20
+ </g>
21
+ </svg>
admin/img/features/filter.svg ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 65 57" style="enable-background:new 0 0 65 57;" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#A999F6;}
7
+ </style>
8
+ <g>
9
+ <g>
10
+ <path class="st0" d="M61.2,7.2v9.2c0,0.5-0.2,1-0.6,1.4L42.7,33.9v15.8c0,0.7-0.4,1.3-1,1.6l-11.1,5.5C30.4,57,30.1,57,29.8,57
11
+ c-1,0-1.8-0.8-1.8-1.8V33.9L10.1,17.8c-0.4-0.3-0.6-0.9-0.6-1.4V7.2c0-1,0.8-1.8,1.8-1.8h48C60.4,5.3,61.2,6.1,61.2,7.2z"/>
12
+ </g>
13
+ </g>
14
+ <g>
15
+ <g>
16
+ <path d="M58.1,0H5.8c-1.1,0-2,0.9-2,2v10.1c0,0.6,0.2,1.1,0.7,1.5l19.4,17.5v23.2c0,1.1,0.9,2,2,2c0.3,0,0.6-0.1,0.9-0.2l12.1-6
17
+ c0.7-0.3,1.1-1,1.1-1.8V31.1l19.4-17.5c0.4-0.4,0.7-0.9,0.7-1.5V2C60.1,0.9,59.2,0,58.1,0z M56.1,11.2L36.6,28.7
18
+ c-0.4,0.4-0.7,0.9-0.7,1.5V47l-8,4V30.2c0-0.6-0.2-1.1-0.7-1.5L7.8,11.2V4h48.3V11.2z"/>
19
+ </g>
20
+ </g>
21
+ </svg>
admin/img/features/infinitescroll.svg ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 65 57" style="enable-background:new 0 0 65 57;" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#A999F6;}
7
+ </style>
8
+ <path class="st0" d="M50.9,18v26.7c0,6.5-5,11.8-11.1,11.8h-8.5c-6.1,0-11.1-5.3-11.1-11.8V18c0-6.5,5-11.8,11.1-11.8h8.5
9
+ C45.9,6.2,50.9,11.5,50.9,18z"/>
10
+ <path d="M31.2,45.9L21.8,36l3-2.9l6.3,6.7l6.3-6.7l3.1,2.9L31.2,45.9z M48.6,41.9V13.5C48.6,6.5,43,0.9,36,0.9h-9.6
11
+ c-6.9,0-12.6,5.6-12.6,12.6v28.5c0,6.9,5.6,12.6,12.6,12.6H36C43,54.5,48.6,48.9,48.6,41.9z M36,5.1c4.6,0,8.4,3.8,8.4,8.4v28.5
12
+ c0,4.6-3.8,8.4-8.4,8.4h-9.6c-4.6,0-8.4-3.8-8.4-8.4V13.5c0-4.6,3.8-8.4,8.4-8.4H36z M31.2,9.4c-1.2,0-2.1,0.9-2.1,2.1
13
+ s0.9,2.1,2.1,2.1s2.1-0.9,2.1-2.1S32.4,9.4,31.2,9.4z M31.2,17.8c-1.2,0-2.1,0.9-2.1,2.1s0.9,2.1,2.1,2.1s2.1-0.9,2.1-2.1
14
+ S32.4,17.8,31.2,17.8z M31.2,26.1c-1.2,0-2.1,0.9-2.1,2.1s0.9,2.1,2.1,2.1s2.1-0.9,2.1-2.1S32.4,26.1,31.2,26.1z"/>
15
+ </svg>
admin/img/features/mailchimp.svg ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 65 57" style="enable-background:new 0 0 65 57;" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#A999F6;}
7
+ </style>
8
+ <path class="st0" d="M59.6,40.1c0,2.2-0.9,4.6-1.1,5.2C51.9,61.1,26.3,61,19.3,44.8c-3.8,0-7.8-3.3-8.2-7.4c-0.2-1.4,0-2.8,0.5-4.1
9
+ l-1.8-1.5C1.6,25,27.3-3.6,35.5,3.5c0.1,0,2.8,2.7,2.8,2.7l1.5-0.6c7.2-3,13.1-1.5,13.1,3.2c0,2.5-1.6,5.4-4.1,8
10
+ c3.2,3,2.4,8.7,2.6,10.1c0.9,0.2,3.7,0.9,5,2.3c2.2,2.3,0.7,4.8,0.4,5.3c0.5,1.4,0.4,1.1,0.8,2.5C58.4,37,59.6,37.9,59.6,40.1z"/>
11
+ <path d="M44.1,25.8c0.4,0,0.8,0,1.1,0c0.2-0.5,0.2-1.3,0.1-2.1c-0.3-1.3-0.6-2.1-1.4-2c-0.8,0.1-0.8,1.1-0.5,2.4
12
+ C43.5,24.8,43.8,25.4,44.1,25.8L44.1,25.8z M37.6,26.8c0.5,0.2,0.9,0.4,1,0.3c0.2-0.2-0.4-1.1-1.5-1.6c-1.2-0.5-2.6-0.3-3.7,0.4
13
+ c-0.4,0.3-0.7,0.6-0.7,0.9c0.1,0.5,1.2-0.3,2.8-0.4C36.3,26.3,37,26.6,37.6,26.8L37.6,26.8z M36.5,27.5c-1.1,0.2-1.8,0.8-1.6,1.2
14
+ c0.1,0,0.1,0.1,0.6-0.1c0.7-0.3,1.5-0.4,2.3-0.2c0.4,0,0.5,0.1,0.6-0.1C38.5,28,37.6,27.3,36.5,27.5L36.5,27.5z M43.1,29.5
15
+ c0.4-0.8-1.3-1.7-1.7-0.9C40.9,29.5,42.6,30.4,43.1,29.5L43.1,29.5z M45,27c-0.9,0-1,1.9,0,1.9C45.9,29,45.9,27.1,45,27L45,27z
16
+ M18.3,36.7c-0.2,0-0.7,0.2-1-0.3c-0.6-1,1.4-2.5,0.4-4.4c-1.1-2.1-3.4-1.6-4.3-0.7c-1.1,1.2-1.1,2.9-0.6,2.9
17
+ c0.5,0.1,0.5-0.8,0.9-1.4c0.5-0.7,1.4-0.9,2.2-0.5c0,0,0,0,0,0c1.4,0.9,0.2,2.2,0.3,3.5c0.2,2,2.2,2,2.6,1.1c0.1-0.1,0-0.2,0-0.3
18
+ C18.7,36.8,18.8,36.5,18.3,36.7L18.3,36.7z M54.8,34.6c-0.4-1.4-0.3-1.1-0.8-2.5c0.3-0.4,1.9-2.9-0.4-5.3c-1.3-1.3-4.1-2-5-2.3
19
+ c-0.2-1.4,0.6-7.1-2.6-10.1c2.5-2.6,4.1-5.5,4.1-8c0-4.8-5.9-6.2-13.1-3.2l-1.5,0.6c0,0-2.8-2.7-2.8-2.7C24.5-6-1.2,22.6,7,29.5
20
+ L8.8,31c-0.5,1.3-0.7,2.7-0.5,4.1c0.4,4.1,4.4,7.4,8.2,7.3c7,16.2,32.6,16.2,39.2,0.4c0.2-0.5,1.1-3,1.1-5.2
21
+ C56.8,35.5,55.6,34.6,54.8,34.6L54.8,34.6z M16.3,40.4c-2.8-0.1-5.8-2.6-6.1-5.5c-0.8-7.5,9-9.2,10.2-1.5C21,37,19.9,40.5,16.3,40.4
22
+ L16.3,40.4z M14.1,26.5c-1.8,0.4-3.5,1.4-4.5,2.9c-0.6-0.5-1.7-1.5-1.9-1.8c-1.6-3,1.7-8.9,4.1-12.2C17.5,7.2,26.5,1,30.7,2.1
23
+ c0.7,0.2,2.9,2.8,2.9,2.8s-4.2,2.3-8,5.5C20.4,14.4,16.4,20.2,14.1,26.5L14.1,26.5z M43.2,38.8c0,0-4.3,0.6-8.5-0.9
24
+ c0.8-2.5,3.3,0.7,11.7-1.7c1.9-0.5,4.3-1.6,6.2-3.1c0.4,0.9,0.7,1.9,0.9,3c0.4-0.1,1.7-0.1,1.4,2.2c-0.4,2.4-1.4,4.4-3.2,6.2
25
+ c-1.1,1.2-2.4,2.1-3.8,2.8c-0.8,0.4-1.6,0.8-2.5,1c-6.5,2.1-13.2-0.2-15.3-5.2c-0.2-0.4-0.3-0.8-0.4-1.2c-0.9-3.3-0.1-7.3,2.3-9.8
26
+ c0.1-0.2,0.3-0.3,0.3-0.6c0-0.2-0.1-0.4-0.2-0.6c-0.9-1.2-3.8-3.3-3.2-7.4c0.4-2.9,3-5,5.4-4.9l0.6,0c1,0.1,1.9,0.2,2.8,0.2
27
+ c1.4,0.1,2.7-0.1,4.2-1.4c0.5-0.4,0.9-0.8,1.6-0.9c0.6-0.1,1.2,0,1.7,0.3c1.2,0.8,1.4,2.8,1.5,4.2c0,0.8,0.1,2.8,0.2,3.4
28
+ c0.1,1.3,0.4,1.5,1.1,1.7c0.4,0.1,0.7,0.2,1.3,0.4c1.6,0.5,2.6,0.9,3.2,1.5c0.3,0.3,0.5,0.7,0.6,1.1c0.2,1.4-1.1,3.1-4.4,4.6
29
+ c-5.7,2.6-11.4,1.8-12.2,1.7c-2.5-0.3-3.8,2.8-2.4,5c2.8,4.1,14.9,2.4,18.4-2.6c0.1-0.1,0-0.2-0.1-0.1c-5.1,3.5-11.8,4.7-15.6,3.2
30
+ c-0.6-0.2-1.8-0.8-1.9-2c5.3,1.6,8.6,0.1,8.6,0.1S43.5,38.8,43.2,38.8L43.2,38.8z M24.7,15.3c2-2.4,4.5-4.4,6.8-5.6c0,0,0.1,0,0.1,0
31
+ c0,0,0,0.1,0,0.1c-0.2,0.3-0.5,1-0.6,1.5c0,0,0,0.1,0.1,0.1c0,0,0.1,0,0.1,0c1.4-1,3.8-2,6-2.1c0.1,0,0.1,0,0.1,0.1c0,0,0,0.1,0,0.1
32
+ c-0.4,0.3-0.7,0.6-0.9,0.9c0,0,0,0.1,0,0.1c0,0,0,0,0.1,0c1.5,0,3.6,0.5,5,1.3c0.1,0.1,0,0.2-0.1,0.2c-8.5-1.9-15,2.3-16.4,3.3
33
+ c0,0-0.1,0-0.1,0C24.7,15.4,24.7,15.4,24.7,15.3L24.7,15.3z"/>
34
+ </svg>
admin/img/features/rolemanagement.svg ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 65 57" style="enable-background:new 0 0 65 57;" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#A999F6;}
7
+ </style>
8
+ <path class="st0" d="M59.2,30.4c0,6.7-2.6,13.1-7.4,17.9c-4.8,4.8-11.1,7.4-17.9,7.4c-6.7,0-13.1-2.6-17.9-7.4
9
+ c-4.8-4.8-7.4-11.1-7.4-17.9c0-6.7,2.6-13.1,7.4-17.9C20.9,7.8,27.2,5.2,34,5.2c6.7,0,13.1,2.6,17.9,7.4
10
+ C56.6,17.4,59.2,23.7,59.2,30.4z"/>
11
+ <path d="M49.7,8.7c-5-5-11.7-7.8-18.8-7.8c-7.1,0-13.8,2.8-18.8,7.8S4.2,20.4,4.2,27.5c0,7.1,2.8,13.8,7.8,18.8s11.7,7.8,18.8,7.8
12
+ c7.1,0,13.8-2.8,18.8-7.8s7.8-11.7,7.8-18.8C57.5,20.4,54.7,13.7,49.7,8.7z M17.6,46.9c1.1-6.4,6.7-11.1,13.3-11.1
13
+ c6.6,0,12.2,4.7,13.3,11.1c-3.8,2.6-8.4,4.1-13.3,4.1S21.4,49.5,17.6,46.9z M22.4,24.1c0-4.7,3.8-8.5,8.5-8.5s8.5,3.8,8.5,8.5
14
+ s-3.8,8.5-8.5,8.5S22.4,28.8,22.4,24.1z M46.8,44.7c-0.8-3-2.5-5.7-4.8-7.8c-1.4-1.3-3.1-2.3-4.8-3c3.2-2.1,5.3-5.6,5.3-9.7
15
+ c0-6.4-5.2-11.6-11.6-11.6s-11.6,5.2-11.6,11.6c0,4.1,2.1,7.6,5.3,9.7c-1.8,0.7-3.4,1.7-4.8,3c-2.3,2.1-4,4.8-4.8,7.8
16
+ c-4.6-4.3-7.5-10.4-7.5-17.2C7.4,14.5,17.9,4,30.9,4s23.5,10.5,23.5,23.5C54.4,34.3,51.5,40.4,46.8,44.7z"/>
17
+ </svg>
admin/img/mascot-2.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 124.74 126.98"><title>mascot-2</title><g style="isolation:isolate"><g id="Layer_1" data-name="Layer 1"><path d="M89.54,113c-.57,4.93-12.32,6.45-16.87,8s-2.46,3.78-6.06,6c-.2-9.85-11.19-6.63-22-6.63S37,113.72,37,113.72s-.76,2.08-4.17,2.65c2.46-1.33,1.52-3.79-3.79-4.93s-7.39-6.06-5.69-12.88c-1.14,1.51-2.84,3.41-5.49,2.27,4.16-3.22,1.32-5.31-3.79-7.58C6,89.65,6.9,83.59,12.59,75.82,10.32,77.14,8,74.48,8,74.48c1.33,1,5.68-1.32,2.09-5.3s-7.78-4.74-7.58-12.7C3.11,45.67,16.38,33,32.68,22.75,49.17,12.32,50.5,8.34,50.5,8.34s1.32.76,1.7,3.41a6.59,6.59,0,0,0,2.28-5.69s2.08.38,2.84,5.12C59.59,3.6,68.31,0,68.31,0s-1.83,7.41,2.08,10.17c.44-1.31.72-2,.72-2s-1.3.72,7.53,5.5,58.42,29.17,44.09,47.85c-3.48,5.65-1.09,7.16,4.55,6.73a7.44,7.44,0,0,1-5.64,3.69s5.86,6.51-1.08,12.16-20.42,10.2-12.39,14.55c-3.47,1.95-4.33.22-4.33.22s3.9,8.9-4.35,10.85C93,111,91,111.86,92.76,115.12,91.67,116.21,90,113.84,89.54,113Z" transform="translate(-2.54)" style="fill:#782600"/><path d="M111.7,58.51a1.11,1.11,0,0,0,0-.13Z" transform="translate(-2.54)" style="fill:#782600"/><path d="M71.05,8.31l.06-.17S71,8.18,71.05,8.31Z" transform="translate(-2.54)" style="fill:#782600"/><path d="M37,113.82l0-.1Z" transform="translate(-2.54)" style="fill:#782600"/><path d="M127.28,68.22c-5.64.43-8-1.08-4.55-6.73,14.33-18.68-35.26-43.07-44.09-47.85C71.92,10,71.07,8.72,71.05,8.31c-.11.28-.35.92-.66,1.86C66.48,7.41,68.31,0,68.31,0L68.2.05c-5,6.38.62,13.38,4.65,14.89-2.6-2-1.16-3.76-1.16-3.76a70.44,70.44,0,0,0,7.53,4.92C91.09,22.19,84.43,30,84.43,30s3.48-2,3.19-.29,2,6.09,3.47,4,3.19-2.31,2,0S91.67,37.53,97.46,37c19.11-1.45,23.75,15.92,20.85,19.11s-2.89,8.1,1.74,12.16c-4.6,0-7.92-8.56-8.35-9.71a11.84,11.84,0,0,1-.63,8.84c1.44-4-5.5-9.84-10.71-7.24s-44.3,11-58.78,7.53-20.27,3.18-19.69,9c-2.89-2.31-2-8.1-2-8.1s-1.45,2.89-6.08,4.92c2-1.74,4.63-5.79-1.74-10.42-9.26-6.08-3.47-20.56,7.24-21.72s12.45.58,12.16-2-1.45-5.8,1.74-4.06,2,0,1.45-7.53a12.87,12.87,0,0,1,1.83-7.39q-1.76,1.16-3.79,2.46C16.38,33,3.11,45.67,2.55,56.48c-.2,8,4,8.72,7.58,12.7S9.37,75.44,8,74.48c0,0,2.28,2.66,4.55,1.34C6.9,83.59,6,89.65,14.11,93.25c5.11,2.27,8,4.36,3.79,7.58,2.65,1.14,4.35-.76,5.49-2.27-1.7,6.82.38,11.75,5.69,12.88s6.25,3.6,3.79,4.93c3-.49,3.92-2.12,4.13-2.55l0-.1,0,.1c-.35.83-2.4,6.53,7.62,6.53,10.8,0,21.79-3.22,22,6.63,3.6-2.27,1.51-4.54,6.06-6s16.3-3,16.87-8c.48.88,2.13,3.25,3.22,2.16-1.74-3.26.21-4.12,6.73-5.43,8.25-2,4.35-10.85,4.35-10.85s.86,1.73,4.33-.22c-8-4.35,5.43-8.91,12.39-14.55s1.08-12.16,1.08-12.16A7.44,7.44,0,0,0,127.28,68.22Z" transform="translate(-2.54)" style="fill:#782600;opacity:0.5;mix-blend-mode:multiply"/><path d="M42.44,30.21a49.67,49.67,0,0,1-14.3,18C26.3,49.68,34.8,51,38.87,56.83c-3.2-.86-4.2-.37-4.2-.37s3.46,1.36,4.94,3.45c0,0-11.68-2.26-15.16,4.94-2,4.06,2.83,12.81,12.94,18.24,10.23,5.67,15,7.63,16,11.09s-4.07,3-4.07,3a11.13,11.13,0,0,0,8.14,1.35,7.78,7.78,0,0,0,1.6,5.43s2-3.7,3.83-4.07c1.35-.37-1.86,3.08-1.86,3.08s6.78,1.11,10.48-2.22c0,2.58,3.7.86,5-2.71,5.43.61,10.23-2.59,10.23-2.59s-4.43-2-3.82-3.57,3.7-9.74,13.81-15.41,15.65-10.36,9.74-16.52S93.22,54,91.26,55a38.92,38.92,0,0,1,4.68-2.59,14.72,14.72,0,0,0-9.37,1.36c7.52-6.78,7.27-5.92,4.93-11S78.8,26.38,77.69,26.51,67.09,33,66,40.81c-1.84-5.3-3.32-4.93-6.28,6.65-2.59-2.34-5.92-9.74-5.92-9.74a6.36,6.36,0,0,0-1.23,4.44C50,35.88,44.17,30.45,42.44,30.21Z" transform="translate(-2.54)" style="fill:#782600;opacity:0.5;mix-blend-mode:multiply"/><path d="M44.41,33.07A45.61,45.61,0,0,1,31.28,49.59c-1.7,1.36,6.11,2.6,9.85,7.92-3-.79-3.85-.33-3.85-.33s3.17,1.24,4.53,3.16c0,0-10.72-2.07-13.92,4.53-1.82,3.74,2.6,11.77,11.88,16.75,9.4,5.21,13.8,7,14.71,10.19s-3.73,2.71-3.73,2.71a10.18,10.18,0,0,0,7.47,1.25,7.11,7.11,0,0,0,1.47,5S61.5,97.35,63.2,97c1.24-.34-1.7,2.82-1.7,2.82s6.23,1,9.62-2c0,2.37,3.4.79,4.64-2.49a15.72,15.72,0,0,0,9.39-2.38s-4.07-1.81-3.51-3.29A27.65,27.65,0,0,1,94.32,75.51c9.28-5.2,14.37-9.51,8.94-15.17s-12.22-5.43-14-4.52a35.71,35.71,0,0,1,4.3-2.38,13.61,13.61,0,0,0-8.6,1.24c6.9-6.22,6.67-5.43,4.52-10.07s-11.65-15-12.67-14.94S67,35.67,66,42.8c-1.7-4.86-3.06-4.52-5.78,6.11C57.88,46.76,54.82,40,54.82,40a5.84,5.84,0,0,0-1.13,4.08C51.31,38.28,46,33.3,44.41,33.07Z" transform="translate(-2.54)" style="fill:#f91"/><g style="opacity:0.5;mix-blend-mode:multiply"><path d="M103.26,60.34c-5.43-5.66-12.22-5.43-14-4.52a35.71,35.71,0,0,1,4.3-2.38,13.61,13.61,0,0,0-8.6,1.24c6.9-6.22,6.67-5.43,4.52-10.07s-11.65-15-12.67-14.94S67,35.67,66,42.8c-1.57-4.5-2.86-4.54-5.19,3.9v0s1.83-3.25,2.94-3.35,1.42,4.36,1.42,4.36S69.36,35.27,75.54,34,89.73,46,85.88,51.07,79.8,61.72,74.73,59.89c-4.25-1.31-5.07-1.11-5.07-1.11s7.44,1.95,9.85,3.36a12.26,12.26,0,0,1,5.87-5S83.45,59.39,83.86,61c4.66-3.25,10.33-5.27,14.8-1.62s3,8.72-1.32,10.84c-5.43,2.66-14.9,7.3-16,3.76-3.14-9.84-10.64-12.78-10.64-12.78s6.08,5.78,5.57,8.11c-5.77-2.43-18.65-3.14-25.85,2,1.22-4.26,5.88-9.43,5.88-9.43a37.15,37.15,0,0,0-7.4,8.62c-3.14,5.17-4.66,10.54-9.83,7.4S27.59,67.8,33.17,64.15a9.7,9.7,0,0,1,10.24-.71,13.7,13.7,0,0,0-.31-2.13c1.93.31,2.32,3,2.32,3A33.06,33.06,0,0,1,52.23,61c-5,1.32-5.27,1.32-7.42-2.77-2.23-3.17-11.55-7.84-9.22-10.49,4-4.12,7.21-9.32,10.28-10.37s9.32,11,9.32,11-.21-4.24.21-4.34,2.86,2.54,2.86,2.54l.05-.12A57.66,57.66,0,0,1,54.82,40a5.84,5.84,0,0,0-1.13,4.08c-2.38-5.77-7.7-10.75-9.28-11A45.61,45.61,0,0,1,31.28,49.59c-1.7,1.36,6.11,2.6,9.85,7.92-3-.79-3.85-.33-3.85-.33s3.17,1.24,4.53,3.16c0,0-10.72-2.07-13.92,4.53-1.82,3.74,2.6,11.77,11.88,16.75,9.4,5.21,13.8,7,14.71,10.19s-3.73,2.71-3.73,2.71a10.18,10.18,0,0,0,7.47,1.25,7.11,7.11,0,0,0,1.47,5S61.5,97.35,63.2,97c1.24-.34-1.7,2.82-1.7,2.82s6.23,1,9.62-2c0,2.37,3.4.79,4.64-2.49a15.72,15.72,0,0,0,9.39-2.38s-4.07-1.81-3.51-3.29A27.65,27.65,0,0,1,94.32,75.51C103.6,70.31,108.69,66,103.26,60.34ZM81.73,92.64c-4.54,2.13-6.27-.27-6.27-.27s-2.14,5.47-2.93,4.54-.14-3.47-.14-3.47-3.33,4.4-8,5.07A6.34,6.34,0,0,0,65.72,94s-4.54,0-6,3.73c-.26-1.86,1.07-4.53,1.07-4.53s-1.33,1.6-5.61,1.2a2.33,2.33,0,0,0,1.61-2.94c4.8,2.41,7.74.27,13.08-.53s7.6-1.47,11.2-4C79.46,89.17,78.53,91.84,81.73,92.64Z" transform="translate(-2.54)" style="fill:#c97106"/></g><path d="M61.36,69.66s8.92-1.74,14.71.93,4.29,7.3-3.82,11.82-12.44,4.11-18.19.69C47.22,79.05,44.21,72.91,61.36,69.66Z" transform="translate(-2.54)" style="fill:#573220"/><path d="M58.89,82.15S56.67,78,52.83,77.6c-2.53-.4-2.63,1.11-.1,1.82S57.48,80.22,58.89,82.15Z" transform="translate(-2.54)" style="fill:#573220;mix-blend-mode:multiply"/><path d="M67.38,80.73s1.82-3.33,4.54-3.23,4.25-.71,3.43-1.52-4.94-1-7,1.92S67.38,80.73,67.38,80.73Z" transform="translate(-2.54)" style="fill:#573220;mix-blend-mode:multiply"/><path d="M52.25,58c0,1.53-.59,2.75-1.23,2.72S49.91,59.42,50,57.88s.59-2.75,1.23-2.72S52.29,56.47,52.25,58Z" transform="translate(-2.54)"/><path d="M52,57.09c0,.61-.19,1.1-.47,1.11s-.53-.48-.56-1.08.19-1.09.47-1.1S51.92,56.49,52,57.09Z" transform="translate(-2.54)" style="fill:#fff"/><path d="M73.5,55.23c0,1.66-.65,3-1.33,2.94S71,56.76,71,55.1s.64-3,1.33-3S73.54,53.56,73.5,55.23Z" transform="translate(-2.54)"/><path d="M73.17,54.24c0,.65-.21,1.19-.51,1.19s-.57-.51-.6-1.16.2-1.18.51-1.19S73.14,53.59,73.17,54.24Z" transform="translate(-2.54)" style="fill:#fff"/><path d="M53.17,55.08s-5.36-6.18-6.55-4.87-2.91,4.27-1.13,4.51S53.87,55.73,53.17,55.08Z" transform="translate(-2.54)" style="fill:#c97106;opacity:0.5;mix-blend-mode:multiply"/><path d="M66.75,51.3a53.69,53.69,0,0,1,3.4-5.87c1.52-2.09,2.61-3,4.34-2s4.57,4.71,4.21,5.36-2.76-.65-5.07-1.3S66.83,52.67,66.75,51.3Z" transform="translate(-2.54)" style="fill:#c97106;opacity:0.5;mix-blend-mode:multiply"/><path d="M53,54s-5.36-6.19-6.54-4.88-2.91,4.28-1.13,4.52S53.75,54.63,53,54Z" transform="translate(-2.54)" style="fill:#762b04"/><path d="M66.62,50.19A54.71,54.71,0,0,1,70,44.33c1.52-2.1,2.6-3,4.34-2s4.56,4.71,4.2,5.36-2.75-.65-5.06-1.3S66.7,51.57,66.62,50.19Z" transform="translate(-2.54)" style="fill:#762b04"/><path d="M45.42,64.31s16.07-10.2,34.09-2.17C66.7,55.62,56.06,57.8,45.42,64.31Z" transform="translate(-2.54)" style="fill:#c97106;mix-blend-mode:multiply"/><path d="M52.42,82.07s-4.05,5,.85,7.13S62.85,92,65.51,90.59s8.51-.32,11.92-4,4.46-6.81,1.59-9.8C77.32,79,64.33,91.87,52.42,82.07Z" transform="translate(-2.54)" style="fill:#fed57b"/><path d="M54,84.38a18.12,18.12,0,0,0,6,1.91c3.48.46,2.57,3.3-.17,3.66S53.13,88,52.58,86,53.31,83.91,54,84.38Z" transform="translate(-2.54)" style="fill:#fed57b;mix-blend-mode:screen"/><path d="M67.21,85.55s5.92-1.76,9.44-5.18c1.94-2.22,3.51,5.08-3.61,7.31C67.49,89,65.64,86.47,67.21,85.55Z" transform="translate(-2.54)" style="fill:#fed57b;mix-blend-mode:screen"/><path d="M63.87,85.66a4.13,4.13,0,0,0,1.34,3c-.88.26-2.17,2.63-3.71,2.68a8,8,0,0,0,3.71-.57,23.3,23.3,0,0,1,5.12-1.18c1.34-.26,1.44-.42,1.44-.42a10.36,10.36,0,0,1-5.26-.62A6.87,6.87,0,0,1,63.87,85.66Z" transform="translate(-2.54)" style="fill:#573220"/><path d="M55.7,87.11a6.39,6.39,0,0,1-.75.32c-.25.08-.54.2-.89.3l-.56.15-.63.14c-.43.1-.91.17-1.42.25s-1.05.13-1.61.14c-.28,0-.57,0-.86,0l-.87,0h-.45l-.44-.05-.89-.09c-.59-.07-1.18-.2-1.76-.29s-1.13-.3-1.67-.44-1-.36-1.53-.53-.91-.39-1.33-.57A7.93,7.93,0,0,1,39,85.9l-.82-.46L37.42,85l.74.34.85.39c.33.16.72.28,1.13.45s.85.35,1.34.49l1.52.47,1.65.38c.58.09,1.15.21,1.73.28l.88.1.44,0,.43,0L49,88c.28,0,.56,0,.84,0,.55,0,1.08,0,1.59-.07s1-.1,1.41-.16.84-.14,1.19-.22.65-.16.9-.21C55.42,87.22,55.7,87.11,55.7,87.11Z" transform="translate(-2.54)"/><path d="M56.06,88l-.6.15-1.6.4L49,89.71c-.88.21-1.74.47-2.57.66l-1.17.34L44.23,91c-.32.09-.61.21-.87.29l-.68.22-.6.19.56-.27c.18-.07.4-.17.65-.29s.54-.26.87-.37l1-.36c.37-.14.76-.29,1.17-.41.81-.23,1.68-.49,2.57-.71L50.24,89c.44-.11.87-.19,1.29-.28.83-.16,1.61-.3,2.29-.41s1.23-.18,1.62-.24Z" transform="translate(-2.54)"/><path d="M56.71,89s-.25.11-.7.27-1.07.46-1.82.79c-.37.15-.77.35-1.2.55s-.85.43-1.31.66-.91.48-1.38.74-.95.52-1.41.81-.93.56-1.37.86l-.66.43-.63.46-.61.44c-.2.14-.39.29-.57.44a13,13,0,0,0-1,.82c-.31.27-.6.52-.85.72l-.62.61-.55.51.48-.58c.16-.17.36-.4.57-.65l.8-.79c.29-.29.63-.58,1-.89l.55-.48.6-.46.63-.47.66-.46c.44-.3.91-.6,1.38-.88s1-.55,1.42-.81,1-.49,1.42-.72.92-.42,1.35-.6.85-.35,1.24-.49c.77-.3,1.42-.49,1.88-.63S56.71,89,56.71,89Z" transform="translate(-2.54)"/><path d="M71.7,84.11,74.23,83c.76-.31,1.64-.71,2.58-1.11s2-.86,3-1.32l1.48-.69c.48-.26,1-.46,1.42-.71s.9-.46,1.31-.7.8-.44,1.13-.67A10.61,10.61,0,0,0,86,77.2a6.51,6.51,0,0,0,.68-.53l.55-.52-.49.58c-.16.18-.39.36-.64.6a8.93,8.93,0,0,1-.89.7c-.33.25-.72.48-1.12.74s-.83.51-1.28.76c-.9.51-1.9,1-2.9,1.46s-2,.9-3,1.28-1.86.71-2.64,1-1.44.48-1.9.63Z" transform="translate(-2.54)"/><path d="M71.48,85.33l.79-.24c.51-.13,1.23-.34,2.1-.55s1.89-.43,3-.65l1.7-.31,1.77-.27c.59-.07,1.18-.16,1.77-.21S83.77,83,84.33,83s1.1-.06,1.62,0,1,0,1.45,0l.65,0a5,5,0,0,1,.58.1,6.82,6.82,0,0,1,.92.18l.79.25-.8-.17a5.76,5.76,0,0,0-.93-.1,11.12,11.12,0,0,0-1.21-.06q-.68,0-1.44,0c-.51,0-1,.07-1.6.11s-1.13.12-1.71.18-1.17.14-1.76.22c-1.18.15-2.36.35-3.46.53l-1.59.27-1.42.25C72.66,85.1,71.48,85.33,71.48,85.33Z" transform="translate(-2.54)"/><path d="M72.2,86.69l.84-.17c.53-.09,1.29-.23,2.22-.36s2-.28,3.15-.38,2.4-.21,3.64-.26c.62,0,1.24,0,1.85,0s1.22,0,1.8,0,1.15.07,1.68.1,1,.1,1.5.15.88.15,1.26.21l.52.1.43.12.83.23-.85-.15-.43-.08-.53-.06c-.37,0-.79-.1-1.25-.12s-1-.08-1.5-.09-1.09,0-1.67,0-1.17,0-1.77,0L82.07,86c-1.24,0-2.47.12-3.62.21s-2.24.16-3.16.24Z" transform="translate(-2.54)"/><path d="M32.94,32.91a28.26,28.26,0,0,1-.26-10.16c.8-6.36-7.57-7.9-13-.71-4.29,5.64-6.44,15.44-1.21,16.51A11.52,11.52,0,0,0,27,37.21l2.41-1.07L28.1,30.5A33.56,33.56,0,0,1,32.94,32.91Z" transform="translate(-2.54)" style="fill:#f91"/><path d="M20.64,26.44a5.72,5.72,0,0,1,3.19-2.51c3.65-1,7,5.86,4.33,6.62.93,4.07-5.48,6.55-7.82,3.36C18.84,31.87,19.19,28.69,20.64,26.44Z" transform="translate(-2.54)" style="fill:#d35e00"/><path d="M23.83,23.93a5.72,5.72,0,0,0-3.19,2.51.16.16,0,0,1,0,.07c1.54-.92,3.36-1.45,5-.48,3.67,2.12,2.49,9.62-4.7,8.49,2.66,2.2,8.09-.2,7.23-4C30.81,29.79,27.48,22.94,23.83,23.93Z" transform="translate(-2.54)" style="fill:#d35e00;mix-blend-mode:multiply"/><g style="opacity:0.5;mix-blend-mode:multiply"><path d="M32.94,32.91a28.26,28.26,0,0,1-.26-10.16c.52-4.14-2.85-6.24-6.77-5.21,8.57-1.1,5.24,10.63,3.25,7s-7-3.93-10.16,2.5,3.32,11.61-1.25,9.12c-3.22-1.75-1.59-7.33-.35-10.46-2.61,5.45-3.17,12,1,12.82A11.52,11.52,0,0,0,27,37.21l2.41-1.07L28.1,30.5A33.56,33.56,0,0,1,32.94,32.91Z" transform="translate(-2.54)" style="fill:#c97106"/></g><path d="M25.31,20.84s-5.62.23-7.86,9c-1.12,2.36-2.25-2.13,2.8-8S29.79,19.61,25.31,20.84Z" transform="translate(-2.54)" style="fill:#fff;opacity:0.5;mix-blend-mode:screen"/><path d="M87.7,26.58A17.68,17.68,0,0,0,88.06,18c-1-4.77,4-11.12,13.16-6.8s11.62,11.87,9.28,16c-1.86,3.27-5.66,4.77-8.75,4.94s-3.45,1.06-3.45,1.06l-2.82.09L98,30.82l-4.15-.08,1.67-2.65-4.77,1.15a15.89,15.89,0,0,0-1.5-5A7.41,7.41,0,0,1,87.7,26.58Z" transform="translate(-2.54)" style="fill:#f91"/><path d="M91.67,25.51c-.57-2-.61-5.91.19-7.78,3-6.91,15.72,1.91,15.16,7.49-.3,2.9-8.6,5.53-13.13,5.52,2-2.33,1.67-2.65,1.67-2.65l-2.88.32A19.3,19.3,0,0,1,91.67,25.51Z" transform="translate(-2.54)" style="fill:#d35e00"/><g style="mix-blend-mode:multiply"><path d="M96.88,19.57c3.21-2.4,7.08.47,9.57,3C104,17.37,94.38,11.84,91.86,17.73c-.8,1.87-.76,5.8-.19,7.78a19.3,19.3,0,0,0,1,2.9l2.88-.32s.31.32-1.67,2.65a24.66,24.66,0,0,0,10.91-3l-.1-.15C97.53,31.73,91.67,23.49,96.88,19.57Z" transform="translate(-2.54)" style="fill:#c97106"/></g><g style="opacity:0.5;mix-blend-mode:multiply"><path d="M106.44,28.26c2.17-1.73,3.69-4.34-.22-8.9-7.38-8.69-15.42-3.47-13.46-6.95,1.46-2.31,5.06-2.08,8.38-1.24C92,6.92,87.09,13.26,88.06,18a17.68,17.68,0,0,1-.36,8.57,7.41,7.41,0,0,0,1.59-2.38,15.89,15.89,0,0,1,1.5,5l4.77-1.15-1.67,2.65,4.15.08L95.48,33.3l2.82-.09s.36-.88,3.45-1.06,6.89-1.67,8.75-4.94c1.17-2.09,1.14-5-.36-8a6,6,0,0,0,.2.6C113.82,28.26,104.27,30,106.44,28.26Z" transform="translate(-2.54)" style="fill:#c97106"/></g><path d="M100.34,12.69s6.87,2.74,9.48,9c1.5,3.62,1.25-3-3.86-7.1S96.73,11.2,100.34,12.69Z" transform="translate(-2.54)" style="fill:#fff;opacity:0.5;mix-blend-mode:screen"/><path d="M57.14,68.22A38,38,0,0,1,70,67.35c6.08.87-3.48-8.9-6.95-8.69S54.32,67.78,57.14,68.22Z" transform="translate(-2.54)" style="fill:#f91;opacity:0.5;mix-blend-mode:screen"/><path d="M62.14,67.57a23.69,23.69,0,0,1,5.21-.43c1.74.21-3.26-8.26-4.34-8.48S62.14,67.57,62.14,67.57Z" transform="translate(-2.54)" style="fill:#fff;opacity:0.5;mix-blend-mode:screen"/><path d="M52.94,60.55A30.92,30.92,0,0,1,71.65,59C64.54,56.49,59.6,57.31,52.94,60.55Z" transform="translate(-2.54)" style="fill:#fff"/><g style="opacity:0.5;mix-blend-mode:multiply"><path d="M78.66,72.39a1.55,1.55,0,0,1-1.54,2.13c-3.91.22-8.47-1.31-11.72,3.9S60.62,78,53.89,77.13a11.3,11.3,0,0,1-5-1.62c-1.08,2.62,1.49,5.41,5.17,7.59,5.75,3.42,10.08,3.83,18.19-.69C78.75,78.79,81,75.09,78.66,72.39Z" transform="translate(-2.54)" style="fill:#573220"/></g><path d="M74.25,70.9c-.06.31-.74.43-1.53.26s-1.36-.55-1.3-.86.76-.42,1.54-.25S74.32,70.6,74.25,70.9Z" transform="translate(-2.54)" style="fill:#fff;mix-blend-mode:screen"/><path d="M50.46,74.42A37.71,37.71,0,0,1,69.22,70.7C72.62,71.1,64.21,66.5,50.46,74.42Z" transform="translate(-2.54)" style="fill:#fff;mix-blend-mode:screen"/><path d="M37,113.82l0-.1Z" transform="translate(-2.54)" style="fill:none"/><path d="M119.52,70.56c3.81,6.35,1.27,13-10.49,16.21s-6.36,9.53-6.36,9.53A11.52,11.52,0,0,1,98.23,96c7.3,5.72,1.59,9.22-5.08,9.85s-2.23,6.36-2.23,6.36l-3.5-4.14c-.31,7.63-15.56,6.36-19.69,12.71-1-4.44-2.54-12.71-10.49-8.89S41.36,114.41,42,105.51c-1.59,5.09-6,4.13-6,4.13s6.67-6.67-2.55-4.44-7.62-13.67-7.62-13.67c-1.27,5.72-4.14,4.77-4.14,4.77s5.09-8-4.44-9.85c-4.79-1-7-3-8-4.88-2.13,5-1.07,9,4.94,11.68,5.11,2.27,8,4.36,3.79,7.58,2.65,1.14,4.35-.76,5.49-2.27-1.7,6.82.38,11.75,5.69,12.88s6.25,3.6,3.79,4.93c3-.49,3.92-2.12,4.13-2.55l0-.1,0,.1c-.35.83-2.4,6.53,7.62,6.53,10.8,0,21.79-3.22,22,6.63,3.6-2.27,1.51-4.54,6.06-6s16.3-3,16.87-8c.48.88,2.13,3.25,3.22,2.16-1.74-3.26.21-4.12,6.73-5.43,8.25-2,4.35-10.85,4.35-10.85s.86,1.73,4.33-.22c-8-4.35,5.43-8.91,12.39-14.55s1.08-12.16,1.08-12.16a7.48,7.48,0,0,0,5.26-3.07A15.62,15.62,0,0,1,119.52,70.56Z" transform="translate(-2.54)" style="fill:#782600;opacity:0.5;mix-blend-mode:multiply"/><path d="M65.5,3.52s-8.89,7.94-5.4,16.2C57.56,17.18,55,9.56,65.5,3.52Z" transform="translate(-2.54)" style="fill:#782600;mix-blend-mode:screen"/><path d="M55,8s0,6.67-5.09,8.9C54.7,18.45,57.88,12.1,55,8Z" transform="translate(-2.54)" style="fill:#782600;mix-blend-mode:screen"/><path d="M49.93,11.46s-1.59,4.45-9.53,8C47.71,19.72,50.25,14,49.93,11.46Z" transform="translate(-2.54)" style="fill:#782600;mix-blend-mode:screen"/></g></g></svg>
admin/img/star.svg ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="-274 399.8 53 43.1" style="enable-background:new -274 399.8 53 43.1;" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#5333ED;}
7
+ </style>
8
+ <g>
9
+ <path class="st0" d="M-234.8,415h-11.5l-3.6-11c-1.4-4.3-3.7-4.3-5.1,0l-3.6,11h-11.5c-4.5,0-5.3,2.2-1.6,4.8l9.3,6.8l-3.6,11
10
+ c-1.4,4.3,0.4,5.7,4.1,3l9.3-6.8l9.3,6.8c3.7,2.7,5.5,1.3,4.1-3l-3.6-11l9.3-6.8C-229.6,417.1-230.3,415-234.8,415z"/>
11
+ </g>
12
+ </svg>
admin/img/testimonial-image-1.jpg ADDED
Binary file
admin/img/testimonial-image-2.jpeg ADDED
Binary file
admin/js/admin-form.js CHANGED
@@ -19,7 +19,7 @@ jQuery(document).ready(function ($) {
19
  $notifyFields.slideDown();
20
  }
21
 
22
- $notifyAdmin.change(function (e) {
23
  if ($(this).is(':checked')) {
24
  $notifyFields.slideDown();
25
  $(this).blur();
19
  $notifyFields.slideDown();
20
  }
21
 
22
+ $notifyAdmin.on( 'change', function (e) {
23
  if ($(this).is(':checked')) {
24
  $notifyFields.slideDown();
25
  $(this).blur();
admin/js/lib/are-you-sure/jquery.are-you-sure.js CHANGED
@@ -115,8 +115,8 @@
115
  var initForm = function($form) {
116
  var fields = $form.find(settings.fieldSelector);
117
  $(fields).each(function() { storeOrigValue($(this)); });
118
- $(fields).unbind(settings.fieldEvents, checkForm);
119
- $(fields).bind(settings.fieldEvents, checkForm);
120
  $form.data("ays-orig-field-count", $(fields).length);
121
  setDirtyStatus($form, false);
122
  };
@@ -142,7 +142,7 @@
142
  var $field = $(this);
143
  if (!$field.data('ays-orig')) {
144
  storeOrigValue($field);
145
- $field.bind(settings.fieldEvents, checkForm);
146
  }
147
  });
148
  // Check for changes while we're here
@@ -155,7 +155,7 @@
155
 
156
  if (!settings.silent && !window.aysUnloadSet) {
157
  window.aysUnloadSet = true;
158
- $(window).bind('beforeunload', function() {
159
  $dirtyForms = $("form").filter('.' + settings.dirtyClass);
160
  if ($dirtyForms.length == 0) {
161
  return;
@@ -178,14 +178,14 @@
178
  }
179
  var $form = $(this);
180
 
181
- $form.submit(function() {
182
  $form.removeClass(settings.dirtyClass);
183
  });
184
- $form.bind('reset', function() { setDirtyStatus($form, false); });
185
  // Add a custom events
186
- $form.bind('rescan.areYouSure', rescan);
187
- $form.bind('reinitialize.areYouSure', reinitialize);
188
- $form.bind('checkform.areYouSure', checkForm);
189
  initForm($form);
190
  });
191
  };
115
  var initForm = function($form) {
116
  var fields = $form.find(settings.fieldSelector);
117
  $(fields).each(function() { storeOrigValue($(this)); });
118
+ $(fields).off(settings.fieldEvents, checkForm);
119
+ $(fields).on(settings.fieldEvents, checkForm);
120
  $form.data("ays-orig-field-count", $(fields).length);
121
  setDirtyStatus($form, false);
122
  };
142
  var $field = $(this);
143
  if (!$field.data('ays-orig')) {
144
  storeOrigValue($field);
145
+ $field.on(settings.fieldEvents, checkForm);
146
  }
147
  });
148
  // Check for changes while we're here
155
 
156
  if (!settings.silent && !window.aysUnloadSet) {
157
  window.aysUnloadSet = true;
158
+ $(window).on('beforeunload', function() {
159
  $dirtyForms = $("form").filter('.' + settings.dirtyClass);
160
  if ($dirtyForms.length == 0) {
161
  return;
178
  }
179
  var $form = $(this);
180
 
181
+ $form.on('submit', function() {
182
  $form.removeClass(settings.dirtyClass);
183
  });
184
+ $form.on('reset', function() { setDirtyStatus($form, false); });
185
  // Add a custom events
186
+ $form.on('rescan.areYouSure', rescan);
187
+ $form.on('reinitialize.areYouSure', reinitialize);
188
+ $form.on('checkform.areYouSure', checkForm);
189
  initForm($form);
190
  });
191
  };
admin/js/selectize.js ADDED
@@ -0,0 +1,3891 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * sifter.js
3
+ * Copyright (c) 2013 Brian Reavis & contributors
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6
+ * file except in compliance with the License. You may obtain a copy of the License at:
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software distributed under
10
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ * ANY KIND, either express or implied. See the License for the specific language
12
+ * governing permissions and limitations under the License.
13
+ *
14
+ * @author Brian Reavis <brian@thirdroute.com>
15
+ */
16
+
17
+ (function(root, factory) {
18
+ if (typeof define === 'function' && define.amd) {
19
+ define('sifter', factory);
20
+ } else if (typeof exports === 'object') {
21
+ module.exports = factory();
22
+ } else {
23
+ root.Sifter = factory();
24
+ }
25
+ }(this, function() {
26
+
27
+ /**
28
+ * Textually searches arrays and hashes of objects
29
+ * by property (or multiple properties). Designed
30
+ * specifically for autocomplete.
31
+ *
32
+ * @constructor
33
+ * @param {array|object} items
34
+ * @param {object} items
35
+ */
36
+ var Sifter = function(items, settings) {
37
+ this.items = items;
38
+ this.settings = settings || {diacritics: true};
39
+ };
40
+
41
+ /**
42
+ * Splits a search string into an array of individual
43
+ * regexps to be used to match results.
44
+ *
45
+ * @param {string} query
46
+ * @returns {array}
47
+ */
48
+ Sifter.prototype.tokenize = function(query) {
49
+ query = trim(String(query || '').toLowerCase());
50
+ if (!query || !query.length) return [];
51
+
52
+ var i, n, regex, letter;
53
+ var tokens = [];
54
+ var words = query.split(/ +/);
55
+
56
+ for (i = 0, n = words.length; i < n; i++) {
57
+ regex = escape_regex(words[i]);
58
+ if (this.settings.diacritics) {
59
+ for (letter in DIACRITICS) {
60
+ if (DIACRITICS.hasOwnProperty(letter)) {
61
+ regex = regex.replace(new RegExp(letter, 'g'), DIACRITICS[letter]);
62
+ }
63
+ }
64
+ }
65
+ tokens.push({
66
+ string : words[i],
67
+ regex : new RegExp(regex, 'i')
68
+ });
69
+ }
70
+
71
+ return tokens;
72
+ };
73
+
74
+ /**
75
+ * Iterates over arrays and hashes.
76
+ *
77
+ * ```
78
+ * this.iterator(this.items, function(item, id) {
79
+ * // invoked for each item
80
+ * });
81
+ * ```
82
+ *
83
+ * @param {array|object} object
84
+ */
85
+ Sifter.prototype.iterator = function(object, callback) {
86
+ var iterator;
87
+ if (is_array(object)) {
88
+ iterator = Array.prototype.forEach || function(callback) {
89
+ for (var i = 0, n = this.length; i < n; i++) {
90
+ callback(this[i], i, this);
91
+ }
92
+ };
93
+ } else {
94
+ iterator = function(callback) {
95
+ for (var key in this) {
96
+ if (this.hasOwnProperty(key)) {
97
+ callback(this[key], key, this);
98
+ }
99
+ }
100
+ };
101
+ }
102
+
103
+ iterator.apply(object, [callback]);
104
+ };
105
+
106
+ /**
107
+ * Returns a function to be used to score individual results.
108
+ *
109
+ * Good matches will have a higher score than poor matches.
110
+ * If an item is not a match, 0 will be returned by the function.
111
+ *
112
+ * @param {object|string} search
113
+ * @param {object} options (optional)
114
+ * @returns {function}
115
+ */
116
+ Sifter.prototype.getScoreFunction = function(search, options) {
117
+ var self, fields, tokens, token_count, nesting;
118
+
119
+ self = this;
120
+ search = self.prepareSearch(search, options);
121
+ tokens = search.tokens;
122
+ fields = search.options.fields;
123
+ token_count = tokens.length;
124
+ nesting = search.options.nesting;
125
+
126
+ /**
127
+ * Calculates how close of a match the
128
+ * given value is against a search token.
129
+ *
130
+ * @param {mixed} value
131
+ * @param {object} token
132
+ * @return {number}
133
+ */
134
+ var scoreValue = function(value, token) {
135
+ var score, pos;
136
+
137
+ if (!value) return 0;
138
+ value = String(value || '');
139
+ pos = value.search(token.regex);
140
+ if (pos === -1) return 0;
141
+ score = token.string.length / value.length;
142
+ if (pos === 0) score += 0.5;
143
+ return score;
144
+ };
145
+
146
+ /**
147
+ * Calculates the score of an object
148
+ * against the search query.
149
+ *
150
+ * @param {object} token
151
+ * @param {object} data
152
+ * @return {number}
153
+ */
154
+ var scoreObject = (function() {
155
+ var field_count = fields.length;
156
+ if (!field_count) {
157
+ return function() { return 0; };
158
+ }
159
+ if (field_count === 1) {
160
+ return function(token, data) {
161
+ return scoreValue(getattr(data, fields[0], nesting), token);
162
+ };
163
+ }
164
+ return function(token, data) {
165
+ for (var i = 0, sum = 0; i < field_count; i++) {
166
+ sum += scoreValue(getattr(data, fields[i], nesting), token);
167
+ }
168
+ return sum / field_count;
169
+ };
170
+ })();
171
+
172
+ if (!token_count) {
173
+ return function() { return 0; };
174
+ }
175
+ if (token_count === 1) {
176
+ return function(data) {
177
+ return scoreObject(tokens[0], data);
178
+ };
179
+ }
180
+
181
+ if (search.options.conjunction === 'and') {
182
+ return function(data) {
183
+ var score;
184
+ for (var i = 0, sum = 0; i < token_count; i++) {
185
+ score = scoreObject(tokens[i], data);
186
+ if (score <= 0) return 0;
187
+ sum += score;
188
+ }
189
+ return sum / token_count;
190
+ };
191
+ } else {
192
+ return function(data) {
193
+ for (var i = 0, sum = 0; i < token_count; i++) {
194
+ sum += scoreObject(tokens[i], data);
195
+ }
196
+ return sum / token_count;
197
+ };
198
+ }
199
+ };
200
+
201
+ /**
202
+ * Returns a function that can be used to compare two
203
+ * results, for sorting purposes. If no sorting should
204
+ * be performed, `null` will be returned.
205
+ *
206
+ * @param {string|object} search
207
+ * @param {object} options
208
+ * @return function(a,b)
209
+ */
210
+ Sifter.prototype.getSortFunction = function(search, options) {
211
+ var i, n, self, field, fields, fields_count, multiplier, multipliers, get_field, implicit_score, sort;
212
+
213
+ self = this;
214
+ search = self.prepareSearch(search, options);
215
+ sort = (!search.query && options.sort_empty) || options.sort;
216
+
217
+ /**
218
+ * Fetches the specified sort field value
219
+ * from a search result item.
220
+ *
221
+ * @param {string} name
222
+ * @param {object} result
223
+ * @return {mixed}
224
+ */
225
+ get_field = function(name, result) {
226
+ if (name === '$score') return result.score;
227
+ return getattr(self.items[result.id], name, options.nesting);
228
+ };
229
+
230
+ // parse options
231
+ fields = [];
232
+ if (sort) {
233
+ for (i = 0, n = sort.length; i < n; i++) {
234
+ if (search.query || sort[i].field !== '$score') {
235
+ fields.push(sort[i]);
236
+ }
237
+ }
238
+ }
239
+
240
+ // the "$score" field is implied to be the primary
241
+ // sort field, unless it's manually specified
242
+ if (search.query) {
243
+ implicit_score = true;
244
+ for (i = 0, n = fields.length; i < n; i++) {
245
+ if (fields[i].field === '$score') {
246
+ implicit_score = false;
247
+ break;
248
+ }
249
+ }
250
+ if (implicit_score) {
251
+ fields.unshift({field: '$score', direction: 'desc'});
252
+ }
253
+ } else {
254
+ for (i = 0, n = fields.length; i < n; i++) {
255
+ if (fields[i].field === '$score') {
256
+ fields.splice(i, 1);
257
+ break;
258
+ }
259
+ }
260
+ }
261
+
262
+ multipliers = [];
263
+ for (i = 0, n = fields.length; i < n; i++) {
264
+ multipliers.push(fields[i].direction === 'desc' ? -1 : 1);
265
+ }
266
+
267
+ // build function
268
+ fields_count = fields.length;
269
+ if (!fields_count) {
270
+ return null;
271
+ } else if (fields_count === 1) {
272
+ field = fields[0].field;
273
+ multiplier = multipliers[0];
274
+ return function(a, b) {
275
+ return multiplier * cmp(
276
+ get_field(field, a),
277
+ get_field(field, b)
278
+ );
279
+ };
280
+ } else {
281
+ return function(a, b) {
282
+ var i, result, a_value, b_value, field;
283
+ for (i = 0; i < fields_count; i++) {
284
+ field = fields[i].field;
285
+ result = multipliers[i] * cmp(
286
+ get_field(field, a),
287
+ get_field(field, b)
288
+ );
289
+ if (result) return result;
290
+ }
291
+ return 0;
292
+ };
293
+ }
294
+ };
295
+
296
+ /**
297
+ * Parses a search query and returns an object
298
+ * with tokens and fields ready to be populated
299
+ * with results.
300
+ *
301
+ * @param {string} query
302
+ * @param {object} options
303
+ * @returns {object}
304
+ */
305
+ Sifter.prototype.prepareSearch = function(query, options) {
306
+ if (typeof query === 'object') return query;
307
+
308
+ options = extend({}, options);
309
+
310
+ var option_fields = options.fields;
311
+ var option_sort = options.sort;
312
+ var option_sort_empty = options.sort_empty;
313
+
314
+ if (option_fields && !is_array(option_fields)) options.fields = [option_fields];
315
+ if (option_sort && !is_array(option_sort)) options.sort = [option_sort];
316
+ if (option_sort_empty && !is_array(option_sort_empty)) options.sort_empty = [option_sort_empty];
317
+
318
+ return {
319
+ options : options,
320
+ query : String(query || '').toLowerCase(),
321
+ tokens : this.tokenize(query),
322
+ total : 0,
323
+ items : []
324
+ };
325
+ };
326
+
327
+ /**
328
+ * Searches through all items and returns a sorted array of matches.
329
+ *
330
+ * The `options` parameter can contain:
331
+ *
332
+ * - fields {string|array}
333
+ * - sort {array}
334
+ * - score {function}
335
+ * - filter {bool}
336
+ * - limit {integer}
337
+ *
338
+ * Returns an object containing:
339
+ *
340
+ * - options {object}
341
+ * - query {string}
342
+ * - tokens {array}
343
+ * - total {int}
344
+ * - items {array}
345
+ *
346
+ * @param {string} query
347
+ * @param {object} options
348
+ * @returns {object}
349
+ */
350
+ Sifter.prototype.search = function(query, options) {
351
+ var self = this, value, score, search, calculateScore;
352
+ var fn_sort;
353
+ var fn_score;
354
+
355
+ search = this.prepareSearch(query, options);
356
+ options = search.options;
357
+ query = search.query;
358
+
359
+ // generate result scoring function
360
+ fn_score = options.score || self.getScoreFunction(search);
361
+
362
+ // perform search and sort
363
+ if (query.length) {
364
+ self.iterator(self.items, function(item, id) {
365
+ score = fn_score(item);
366
+ if (options.filter === false || score > 0) {
367
+ search.items.push({'score': score, 'id': id});
368
+ }
369
+ });
370
+ } else {
371
+ self.iterator(self.items, function(item, id) {
372
+ search.items.push({'score': 1, 'id': id});
373
+ });
374
+ }
375
+
376
+ fn_sort = self.getSortFunction(search, options);
377
+ if (fn_sort) search.items.sort(fn_sort);
378
+
379
+ // apply limits
380
+ search.total = search.items.length;
381
+ if (typeof options.limit === 'number') {
382
+ search.items = search.items.slice(0, options.limit);
383
+ }
384
+
385
+ return search;
386
+ };
387
+
388
+ // utilities
389
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
390
+
391
+ var cmp = function(a, b) {
392
+ if (typeof a === 'number' && typeof b === 'number') {
393
+ return a > b ? 1 : (a < b ? -1 : 0);
394
+ }
395
+ a = asciifold(String(a || ''));
396
+ b = asciifold(String(b || ''));
397
+ if (a > b) return 1;
398
+ if (b > a) return -1;
399
+ return 0;
400
+ };
401
+
402
+ var extend = function(a, b) {
403
+ var i, n, k, object;
404
+ for (i = 1, n = arguments.length; i < n; i++) {
405
+ object = arguments[i];
406
+ if (!object) continue;
407
+ for (k in object) {
408
+ if (object.hasOwnProperty(k)) {
409
+ a[k] = object[k];
410
+ }
411
+ }
412
+ }
413
+ return a;
414
+ };
415
+
416
+ /**
417
+ * A property getter resolving dot-notation
418
+ * @param {Object} obj The root object to fetch property on
419
+ * @param {String} name The optionally dotted property name to fetch
420
+ * @param {Boolean} nesting Handle nesting or not
421
+ * @return {Object} The resolved property value
422
+ */
423
+ var getattr = function(obj, name, nesting) {
424
+ if (!obj || !name) return;
425
+ if (!nesting) return obj[name];
426
+ var names = name.split(".");
427
+ while(names.length && (obj = obj[names.shift()]));
428
+ return obj;
429
+ };
430
+
431
+ var trim = function(str) {
432
+ return (str + '').replace(/^\s+|\s+$|/g, '');
433
+ };
434
+
435
+ var escape_regex = function(str) {
436
+ return (str + '').replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
437
+ };
438
+
439
+ var is_array = Array.isArray || (typeof $ !== 'undefined' && $.isArray) || function(object) {
440
+ return Object.prototype.toString.call(object) === '[object Array]';
441
+ };
442
+
443
+ var DIACRITICS = {
444
+ 'a': '[aḀḁĂăÂâǍǎȺⱥȦȧẠạÄäÀàÁáĀāÃãÅåąĄÃąĄ]',
445
+ 'b': '[b␢βΒB฿𐌁ᛒ]',
446
+ 'c': '[cĆćĈĉČčĊċC̄c̄ÇçḈḉȻȼƇƈɕᴄCc]',
447
+ 'd': '[dĎďḊḋḐḑḌḍḒḓḎḏĐđD̦d̦ƉɖƊɗƋƌᵭᶁᶑȡᴅDdð]',
448
+ 'e': '[eÉéÈèÊêḘḙĚěĔĕẼẽḚḛẺẻĖėËëĒēȨȩĘęᶒɆɇȄȅẾếỀềỄễỂểḜḝḖḗḔḕȆȇẸẹỆệⱸᴇEeɘǝƏƐε]',
449
+ 'f': '[fƑƒḞḟ]',
450
+ 'g': '[gɢ₲ǤǥĜĝĞğĢģƓɠĠġ]',
451
+ 'h': '[hĤĥĦħḨḩẖẖḤḥḢḣɦʰǶƕ]',
452
+ 'i': '[iÍíÌìĬĭÎîǏǐÏïḮḯĨĩĮįĪīỈỉȈȉȊȋỊịḬḭƗɨɨ̆ᵻᶖİiIıɪIi]',
453
+ 'j': '[jȷĴĵɈɉʝɟʲ]',
454
+ 'k': '[kƘƙꝀꝁḰḱǨǩḲḳḴḵκϰ₭]',
455
+ 'l': '[lŁłĽľĻļĹĺḶḷḸḹḼḽḺḻĿŀȽƚⱠⱡⱢɫɬᶅɭȴʟLl]',
456
+ 'n': '[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲȠƞᵰᶇɳȵɴNnŊŋ]',
457
+ 'o': '[oØøÖöÓóÒòÔôǑǒŐőŎŏȮȯỌọƟɵƠơỎỏŌōÕõǪǫȌȍՕօ]',
458
+ 'p': '[pṔṕṖṗⱣᵽƤƥᵱ]',
459
+ 'q': '[qꝖꝗʠɊɋꝘꝙq̃]',
460
+ 'r': '[rŔŕɌɍŘřŖŗṘṙȐȑȒȓṚṛⱤɽ]',
461
+ 's': '[sŚśṠṡṢṣꞨꞩŜŝŠšŞşȘșS̈s̈]',
462
+ 't': '[tŤťṪṫŢţṬṭƮʈȚțṰṱṮṯƬƭ]',
463
+ 'u': '[uŬŭɄʉỤụÜüÚúÙùÛûǓǔŰűŬŭƯưỦủŪūŨũŲųȔȕ∪]',
464
+ 'v': '[vṼṽṾṿƲʋꝞꝟⱱʋ]',
465
+ 'w': '[wẂẃẀẁŴŵẄẅẆẇẈẉ]',
466
+ 'x': '[xẌẍẊẋχ]',
467
+ 'y': '[yÝýỲỳŶŷŸÿỸỹẎẏỴỵɎɏƳƴ]',
468
+ 'z': '[zŹźẐẑŽžŻżẒẓẔẕƵƶ]'
469
+ };
470
+
471
+ var asciifold = (function() {
472
+ var i, n, k, chunk;
473
+ var foreignletters = '';
474
+ var lookup = {};
475
+ for (k in DIACRITICS) {
476
+ if (DIACRITICS.hasOwnProperty(k)) {
477
+ chunk = DIACRITICS[k].substring(2, DIACRITICS[k].length - 1);
478
+ foreignletters += chunk;
479
+ for (i = 0, n = chunk.length; i < n; i++) {
480
+ lookup[chunk.charAt(i)] = k;
481
+ }
482
+ }
483
+ }
484
+ var regexp = new RegExp('[' + foreignletters + ']', 'g');
485
+ return function(str) {
486
+ return str.replace(regexp, function(foreignletter) {
487
+ return lookup[foreignletter];
488
+ }).toLowerCase();
489
+ };
490
+ })();
491
+
492
+
493
+ // export
494
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
495
+
496
+ return Sifter;
497
+ }));
498
+
499
+
500
+
501
+ /**
502
+ * microplugin.js
503
+ * Copyright (c) 2013 Brian Reavis & contributors
504
+ *
505
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
506
+ * file except in compliance with the License. You may obtain a copy of the License at:
507
+ * http://www.apache.org/licenses/LICENSE-2.0
508
+ *
509
+ * Unless required by applicable law or agreed to in writing, software distributed under
510
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
511
+ * ANY KIND, either express or implied. See the License for the specific language
512
+ * governing permissions and limitations under the License.
513
+ *
514
+ * @author Brian Reavis <brian@thirdroute.com>
515
+ */
516
+
517
+ (function(root, factory) {
518
+ if (typeof define === 'function' && define.amd) {
519
+ define('microplugin', factory);
520
+ } else if (typeof exports === 'object') {
521
+ module.exports = factory();
522
+ } else {
523
+ root.MicroPlugin = factory();
524
+ }
525
+ }(this, function() {
526
+ var MicroPlugin = {};
527
+
528
+ MicroPlugin.mixin = function(Interface) {
529
+ Interface.plugins = {};
530
+
531
+ /**
532
+ * Initializes the listed plugins (with options).
533
+ * Acceptable formats:
534
+ *
535
+ * List (without options):
536
+ * ['a', 'b', 'c']
537
+ *
538
+ * List (with options):
539
+ * [{'name': 'a', options: {}}, {'name': 'b', options: {}}]
540
+ *
541
+ * Hash (with options):
542
+ * {'a': { ... }, 'b': { ... }, 'c': { ... }}
543
+ *
544
+ * @param {mixed} plugins
545
+ */
546
+ Interface.prototype.initializePlugins = function(plugins) {
547
+ var i, n, key;
548
+ var self = this;
549
+ var queue = [];
550
+
551
+ self.plugins = {
552
+ names : [],
553
+ settings : {},
554
+ requested : {},
555
+ loaded : {}
556
+ };
557
+
558
+ if (utils.isArray(plugins)) {
559
+ for (i = 0, n = plugins.length; i < n; i++) {
560
+ if (typeof plugins[i] === 'string') {
561
+ queue.push(plugins[i]);
562
+ } else {
563
+ self.plugins.settings[plugins[i].name] = plugins[i].options;
564
+ queue.push(plugins[i].name);
565
+ }
566
+ }
567
+ } else if (plugins) {
568
+ for (key in plugins) {
569
+ if (plugins.hasOwnProperty(key)) {
570
+ self.plugins.settings[key] = plugins[key];
571
+ queue.push(key);
572
+ }
573
+ }
574
+ }
575
+
576
+ while (queue.length) {
577
+ self.require(queue.shift());
578
+ }
579
+ };
580
+
581
+ Interface.prototype.loadPlugin = function(name) {
582
+ var self = this;
583
+ var plugins = self.plugins;
584
+ var plugin = Interface.plugins[name];
585
+
586
+ if (!Interface.plugins.hasOwnProperty(name)) {
587
+ throw new Error('Unable to find "' + name + '" plugin');
588
+ }
589
+
590
+ plugins.requested[name] = true;
591
+ plugins.loaded[name] = plugin.fn.apply(self, [self.plugins.settings[name] || {}]);
592
+ plugins.names.push(name);
593
+ };
594
+
595
+ /**
596
+ * Initializes a plugin.
597
+ *
598
+ * @param {string} name
599
+ */
600
+ Interface.prototype.require = function(name) {
601
+ var self = this;
602
+ var plugins = self.plugins;
603
+
604
+ if (!self.plugins.loaded.hasOwnProperty(name)) {
605
+ if (plugins.requested[name]) {
606
+ throw new Error('Plugin has circular dependency ("' + name + '")');
607
+ }
608
+ self.loadPlugin(name);
609
+ }
610
+
611
+ return plugins.loaded[name];
612
+ };
613
+
614
+ /**
615
+ * Registers a plugin.
616
+ *
617
+ * @param {string} name
618
+ * @param {function} fn
619
+ */
620
+ Interface.define = function(name, fn) {
621
+ Interface.plugins[name] = {
622
+ 'name' : name,
623
+ 'fn' : fn
624
+ };
625
+ };
626
+ };
627
+
628
+ var utils = {
629
+ isArray: Array.isArray || function(vArg) {
630
+ return Object.prototype.toString.call(vArg) === '[object Array]';
631
+ }
632
+ };
633
+
634
+ return MicroPlugin;
635
+ }));
636
+
637
+ /**
638
+ * selectize.js (v0.12.6)
639
+ * Copyright (c) 2013–2015 Brian Reavis & contributors
640
+ *
641
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
642
+ * file except in compliance with the License. You may obtain a copy of the License at:
643
+ * http://www.apache.org/licenses/LICENSE-2.0
644
+ *
645
+ * Unless required by applicable law or agreed to in writing, software distributed under
646
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
647
+ * ANY KIND, either express or implied. See the License for the specific language
648
+ * governing permissions and limitations under the License.
649
+ *
650
+ * @author Brian Reavis <brian@thirdroute.com>
651
+ */
652
+
653
+ /*jshint curly:false */
654
+ /*jshint browser:true */
655
+
656
+ (function(root, factory) {
657
+ if (typeof define === 'function' && define.amd) {
658
+ define('selectize', ['jquery','sifter','microplugin'], factory);
659
+ } else if (typeof exports === 'object') {
660
+ module.exports = factory(require('jquery'), require('sifter'), require('microplugin'));
661
+ } else {
662
+ root.Selectize = factory(root.jQuery, root.Sifter, root.MicroPlugin);
663
+ }
664
+ }(this, function($, Sifter, MicroPlugin) {
665
+ 'use strict';
666
+
667
+ var highlight = function($element, pattern) {
668
+ if (typeof pattern === 'string' && !pattern.length) return;
669
+ var regex = (typeof pattern === 'string') ? new RegExp(pattern, 'i') : pattern;
670
+
671
+ var highlight = function(node) {
672
+ var skip = 0;
673
+ // Wrap matching part of text node with highlighting <span>, e.g.
674
+ // Soccer -> <span class="highlight">Soc</span>cer for regex = /soc/i
675
+ if (node.nodeType === 3) {
676
+ var pos = node.data.search(regex);
677
+ if (pos >= 0 && node.data.length > 0) {
678
+ var match = node.data.match(regex);
679
+ var spannode = document.createElement('span');
680
+ spannode.className = 'highlight';
681
+ var middlebit = node.splitText(pos);
682
+ var endbit = middlebit.splitText(match[0].length);
683
+ var middleclone = middlebit.cloneNode(true);
684
+ spannode.appendChild(middleclone);
685
+ middlebit.parentNode.replaceChild(spannode, middlebit);
686
+ skip = 1;
687
+ }
688
+ }
689
+ // Recurse element node, looking for child text nodes to highlight, unless element
690
+ // is childless, <script>, <style>, or already highlighted: <span class="hightlight">
691
+ else if (node.nodeType === 1 && node.childNodes && !/(script|style)/i.test(node.tagName) && ( node.className !== 'highlight' || node.tagName !== 'SPAN' )) {
692
+ for (var i = 0; i < node.childNodes.length; ++i) {
693
+ i += highlight(node.childNodes[i]);
694
+ }
695
+ }
696
+ return skip;
697
+ };
698
+
699
+ return $element.each(function() {
700
+ highlight(this);
701
+ });
702
+ };
703
+
704
+ /**
705
+ * removeHighlight fn copied from highlight v5 and
706
+ * edited to remove with() and pass js strict mode
707
+ */
708
+ $.fn.removeHighlight = function() {
709
+ return this.find("span.highlight").each(function() {
710
+ this.parentNode.firstChild.nodeName;
711
+ var parent = this.parentNode;
712
+ parent.replaceChild(this.firstChild, this);
713
+ parent.normalize();
714
+ }).end();
715
+ };
716
+
717
+
718
+ var MicroEvent = function() {};
719
+ MicroEvent.prototype = {
720
+ on: function(event, fct){
721
+ this._events = this._events || {};
722
+ this._events[event] = this._events[event] || [];
723
+ this._events[event].push(fct);
724
+ },
725
+ off: function(event, fct){
726
+ var n = arguments.length;
727
+ if (n === 0) return delete this._events;
728
+ if (n === 1) return delete this._events[event];
729
+
730
+ this._events = this._events || {};
731
+ if (event in this._events === false) return;
732
+ this._events[event].splice(this._events[event].indexOf(fct), 1);
733
+ },
734
+ trigger: function(event /* , args... */){
735
+ this._events = this._events || {};
736
+ if (event in this._events === false) return;
737
+ for (var i = 0; i < this._events[event].length; i++){
738
+ this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
739
+ }
740
+ }
741
+ };
742
+
743
+ /**
744
+ * Mixin will delegate all MicroEvent.js function in the destination object.
745
+ *
746
+ * - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent
747
+ *
748
+ * @param {object} the object which will support MicroEvent
749
+ */
750
+ MicroEvent.mixin = function(destObject){
751
+ var props = ['on', 'off', 'trigger'];
752
+ for (var i = 0; i < props.length; i++){
753
+ destObject.prototype[props[i]] = MicroEvent.prototype[props[i]];
754
+ }
755
+ };
756
+
757
+ var IS_MAC = /Mac/.test(navigator.userAgent);
758
+
759
+ var KEY_A = 65;
760
+ var KEY_COMMA = 188;
761
+ var KEY_RETURN = 13;
762
+ var KEY_ESC = 27;
763
+ var KEY_LEFT = 37;
764
+ var KEY_UP = 38;
765
+ var KEY_P = 80;
766
+ var KEY_RIGHT = 39;
767
+ var KEY_DOWN = 40;
768
+ var KEY_N = 78;
769
+ var KEY_BACKSPACE = 8;
770
+ var KEY_DELETE = 46;
771
+ var KEY_SHIFT = 16;
772
+ var KEY_CMD = IS_MAC ? 91 : 17;
773
+ var KEY_CTRL = IS_MAC ? 18 : 17;
774
+ var KEY_TAB = 9;
775
+
776
+ var TAG_SELECT = 1;
777
+ var TAG_INPUT = 2;
778
+
779
+ // for now, android support in general is too spotty to support validity
780
+ var SUPPORTS_VALIDITY_API = !/android/i.test(window.navigator.userAgent) && !!document.createElement('input').validity;
781
+
782
+
783
+ var isset = function(object) {
784
+ return typeof object !== 'undefined';
785
+ };
786
+
787
+ /**
788
+ * Converts a scalar to its best string representation
789
+ * for hash keys and HTML attribute values.
790
+ *
791
+ * Transformations:
792
+ * 'str' -> 'str'
793
+ * null -> ''
794
+ * undefined -> ''
795
+ * true -> '1'
796
+ * false -> '0'
797
+ * 0 -> '0'
798
+ * 1 -> '1'
799
+ *
800
+ * @param {string} value
801
+ * @returns {string|null}
802
+ */
803
+ var hash_key = function(value) {
804
+ if (typeof value === 'undefined' || value === null) return null;
805
+ if (typeof value === 'boolean') return value ? '1' : '0';
806
+ return value + '';
807
+ };
808
+
809
+ /**
810
+ * Escapes a string for use within HTML.
811
+ *
812
+ * @param {string} str
813
+ * @returns {string}
814
+ */
815
+ var escape_html = function(str) {
816
+ return (str + '')
817
+ .replace(/&/g, '&amp;')
818
+ .replace(/</g, '&lt;')
819
+ .replace(/>/g, '&gt;')
820
+ .replace(/"/g, '&quot;');
821
+ };
822
+
823
+ /**
824
+ * Escapes "$" characters in replacement strings.
825
+ *
826
+ * @param {string} str
827
+ * @returns {string}
828
+ */
829
+ var escape_replace = function(str) {
830
+ return (str + '').replace(/\$/g, '$$$$');
831
+ };
832
+
833
+ var hook = {};
834
+
835
+ /**
836
+ * Wraps `method` on `self` so that `fn`
837
+ * is invoked before the original method.
838
+ *
839
+ * @param {object} self
840
+ * @param {string} method
841
+ * @param {function} fn
842
+ */
843
+ hook.before = function(self, method, fn) {
844
+ var original = self[method];
845
+ self[method] = function() {
846
+ fn.apply(self, arguments);
847
+ return original.apply(self, arguments);
848
+ };
849
+ };
850
+
851
+ /**
852
+ * Wraps `method` on `self` so that `fn`
853
+ * is invoked after the original method.
854
+ *
855
+ * @param {object} self
856
+ * @param {string} method
857
+ * @param {function} fn
858
+ */
859
+ hook.after = function(self, method, fn) {
860
+ var original = self[method];
861
+ self[method] = function() {
862
+ var result = original.apply(self, arguments);
863
+ fn.apply(self, arguments);
864
+ return result;
865
+ };
866
+ };
867
+
868
+ /**
869
+ * Wraps `fn` so that it can only be invoked once.
870
+ *
871
+ * @param {function} fn
872
+ * @returns {function}
873
+ */
874
+ var once = function(fn) {
875
+ var called = false;
876
+ return function() {
877
+ if (called) return;
878
+ called = true;
879
+ fn.apply(this, arguments);
880
+ };
881
+ };
882
+
883
+ /**
884
+ * Wraps `fn` so that it can only be called once
885
+ * every `delay` milliseconds (invoked on the falling edge).
886
+ *
887
+ * @param {function} fn
888
+ * @param {int} delay
889
+ * @returns {function}
890
+ */
891
+ var debounce = function(fn, delay) {
892
+ var timeout;
893
+ return function() {
894
+ var self = this;
895
+ var args = arguments;
896
+ window.clearTimeout(timeout);
897
+ timeout = window.setTimeout(function() {
898
+ fn.apply(self, args);
899
+ }, delay);
900
+ };
901
+ };
902
+
903
+ /**
904
+ * Debounce all fired events types listed in `types`
905
+ * while executing the provided `fn`.
906
+ *
907
+ * @param {object} self
908
+ * @param {array} types
909
+ * @param {function} fn
910
+ */
911
+ var debounce_events = function(self, types, fn) {
912
+ var type;
913
+ var trigger = self.trigger;
914
+ var event_args = {};
915
+
916
+ // override trigger method
917
+ self.trigger = function() {
918
+ var type = arguments[0];
919
+ if (types.indexOf(type) !== -1) {
920
+ event_args[type] = arguments;
921
+ } else {
922
+ return trigger.apply(self, arguments);
923
+ }
924
+ };
925
+
926
+ // invoke provided function
927
+ fn.apply(self, []);
928
+ self.trigger = trigger;
929
+
930
+ // trigger queued events
931
+ for (type in event_args) {
932
+ if (event_args.hasOwnProperty(type)) {
933
+ trigger.apply(self, event_args[type]);
934
+ }
935
+ }
936
+ };
937
+
938
+ /**
939
+ * A workaround for http://bugs.jquery.com/ticket/6696
940
+ *
941
+ * @param {object} $parent - Parent element to listen on.
942
+ * @param {string} event - Event name.
943
+ * @param {string} selector - Descendant selector to filter by.
944
+ * @param {function} fn - Event handler.
945
+ */
946
+ var watchChildEvent = function($parent, event, selector, fn) {
947
+ $parent.on(event, selector, function(e) {
948
+ var child = e.target;
949
+ while (child && child.parentNode !== $parent[0]) {
950
+ child = child.parentNode;
951
+ }
952
+ e.currentTarget = child;
953
+ return fn.apply(this, [e]);
954
+ });
955
+ };
956
+
957
+ /**
958
+ * Determines the current selection within a text input control.
959
+ * Returns an object containing:
960
+ * - start
961
+ * - length
962
+ *
963
+ * @param {object} input
964
+ * @returns {object}
965
+ */
966
+ var getSelection = function(input) {
967
+ var result = {};
968
+ if ('selectionStart' in input) {
969
+ result.start = input.selectionStart;
970
+ result.length = input.selectionEnd - result.start;
971
+ } else if (document.selection) {
972
+ input.focus();
973
+ var sel = document.selection.createRange();
974
+ var selLen = document.selection.createRange().text.length;
975
+ sel.moveStart('character', -input.value.length);
976
+ result.start = sel.text.length - selLen;
977
+ result.length = selLen;
978
+ }
979
+ return result;
980
+ };
981
+
982
+ /**
983
+ * Copies CSS properties from one element to another.
984
+ *
985
+ * @param {object} $from
986
+ * @param {object} $to
987
+ * @param {array} properties
988
+ */
989
+ var transferStyles = function($from, $to, properties) {
990
+ var i, n, styles = {};
991
+ if (properties) {
992
+ for (i = 0, n = properties.length; i < n; i++) {
993
+ styles[properties[i]] = $from.css(properties[i]);
994
+ }
995
+ } else {
996
+ styles = $from.css();
997
+ }
998
+ $to.css(styles);
999
+ };
1000
+
1001
+ /**
1002
+ * Measures the width of a string within a
1003
+ * parent element (in pixels).
1004
+ *
1005
+ * @param {string} str
1006
+ * @param {object} $parent
1007
+ * @returns {int}
1008
+ */
1009
+ var measureString = function(str, $parent) {
1010
+ if (!str) {
1011
+ return 0;
1012
+ }
1013
+
1014
+ if (!Selectize.$testInput) {
1015
+ Selectize.$testInput = $('<span />').css({
1016
+ position: 'absolute',
1017
+ top: -99999,
1018
+ left: -99999,
1019
+ width: 'auto',
1020
+ padding: 0,
1021
+ whiteSpace: 'pre'
1022
+ }).appendTo('body');
1023
+ }
1024
+
1025
+ Selectize.$testInput.text(str);
1026
+
1027
+ transferStyles($parent, Selectize.$testInput, [
1028
+ 'letterSpacing',
1029
+ 'fontSize',
1030
+ 'fontFamily',
1031
+ 'fontWeight',
1032
+ 'textTransform'
1033
+ ]);
1034
+
1035
+ return Selectize.$testInput.width();
1036
+ };
1037
+
1038
+ /**
1039
+ * Sets up an input to grow horizontally as the user
1040
+ * types. If the value is changed manually, you can
1041
+ * trigger the "update" handler to resize:
1042
+ *
1043
+ * $input.trigger('update');
1044
+ *
1045
+ * @param {object} $input
1046
+ */
1047
+ var autoGrow = function($input) {
1048
+ var currentWidth = null;
1049
+
1050
+ var update = function(e, options) {
1051
+ var value, keyCode, printable, placeholder, width;
1052
+ var shift, character, selection;
1053
+ e = e || window.event || {};
1054
+ options = options || {};
1055
+
1056
+ if (e.metaKey || e.altKey) return;
1057
+ if (!options.force && $input.data('grow') === false) return;
1058
+
1059
+ value = $input.val();
1060
+ if (e.type && e.type.toLowerCase() === 'keydown') {
1061
+ keyCode = e.keyCode;
1062
+ printable = (
1063
+ (keyCode >= 48 && keyCode <= 57) || // 0-9
1064
+ (keyCode >= 65 && keyCode <= 90) || // a-z
1065
+ (keyCode >= 96 && keyCode <= 111) || // numpad 0-9, numeric operators
1066
+ (keyCode >= 186 && keyCode <= 222) || // semicolon, equal, comma, dash, etc.
1067
+ keyCode === 32 // space
1068
+ );
1069
+
1070
+ if (keyCode === KEY_DELETE || keyCode === KEY_BACKSPACE) {
1071
+ selection = getSelection($input[0]);
1072
+ if (selection.length) {
1073
+ value = value.substring(0, selection.start) + value.substring(selection.start + selection.length);
1074
+ } else if (keyCode === KEY_BACKSPACE && selection.start) {
1075
+ value = value.substring(0, selection.start - 1) + value.substring(selection.start + 1);
1076
+ } else if (keyCode === KEY_DELETE && typeof selection.start !== 'undefined') {
1077
+ value = value.substring(0, selection.start) + value.substring(selection.start + 1);
1078
+ }
1079
+ } else if (printable) {
1080
+ shift = e.shiftKey;
1081
+ character = String.fromCharCode(e.keyCode);
1082
+ if (shift) character = character.toUpperCase();
1083
+ else character = character.toLowerCase();
1084
+ value += character;
1085
+ }
1086
+ }
1087
+
1088
+ placeholder = $input.attr('placeholder');
1089
+ if (!value && placeholder) {
1090
+ value = placeholder;
1091
+ }
1092
+
1093
+ width = measureString(value, $input) + 4;
1094
+ if (width !== currentWidth) {
1095
+ currentWidth = width;
1096
+ $input.width(width);
1097
+ $input.triggerHandler('resize');
1098
+ }
1099
+ };
1100
+
1101
+ $input.on('keydown keyup update blur', update);
1102
+ update();
1103
+ };
1104
+
1105
+ var domToString = function(d) {
1106
+ var tmp = document.createElement('div');
1107
+
1108
+ tmp.appendChild(d.cloneNode(true));
1109
+
1110
+ return tmp.innerHTML;
1111
+ };
1112
+
1113
+ var logError = function(message, options){
1114
+ if(!options) options = {};
1115
+ var component = "Selectize";
1116
+
1117
+ console.error(component + ": " + message)
1118
+
1119
+ if(options.explanation){
1120
+ // console.group is undefined in <IE11
1121
+ if(console.group) console.group();
1122
+ console.error(options.explanation);
1123
+ if(console.group) console.groupEnd();
1124
+ }
1125
+ }
1126
+
1127
+
1128
+ var Selectize = function($input, settings) {
1129
+ var key, i, n, dir, input, self = this;
1130
+ input = $input[0];
1131
+ input.selectize = self;
1132
+
1133
+ // detect rtl environment
1134
+ var computedStyle = window.getComputedStyle && window.getComputedStyle(input, null);
1135
+ dir = computedStyle ? computedStyle.getPropertyValue('direction') : input.currentStyle && input.currentStyle.direction;
1136
+ dir = dir || $input.parents('[dir]:first').attr('dir') || '';
1137
+
1138
+ // setup default state
1139
+ $.extend(self, {
1140
+ order : 0,
1141
+ settings : settings,
1142
+ $input : $input,
1143
+ tabIndex : $input.attr('tabindex') || '',
1144
+ tagType : input.tagName.toLowerCase() === 'select' ? TAG_SELECT : TAG_INPUT,
1145
+ rtl : /rtl/i.test(dir),
1146
+
1147
+ eventNS : '.selectize' + (++Selectize.count),
1148
+ highlightedValue : null,
1149
+ isBlurring : false,
1150
+ isOpen : false,
1151
+ isDisabled : false,
1152
+ isRequired : $input.is('[required]'),
1153
+ isInvalid : false,
1154
+ isLocked : false,
1155
+ isFocused : false,
1156
+ isInputHidden : false,
1157
+ isSetup : false,
1158
+ isShiftDown : false,
1159
+ isCmdDown : false,
1160
+ isCtrlDown : false,
1161
+ ignoreFocus : false,
1162
+ ignoreBlur : false,
1163
+ ignoreHover : false,
1164
+ hasOptions : false,
1165
+ currentResults : null,
1166
+ lastValue : '',
1167
+ caretPos : 0,
1168
+ loading : 0,
1169
+ loadedSearches : {},
1170
+
1171
+ $activeOption : null,
1172
+ $activeItems : [],
1173
+
1174
+ optgroups : {},
1175
+ options : {},
1176
+ userOptions : {},
1177
+ items : [],
1178
+ renderCache : {},
1179
+ onSearchChange : settings.loadThrottle === null ? self.onSearchChange : debounce(self.onSearchChange, settings.loadThrottle)
1180
+ });
1181
+
1182
+ // search system
1183
+ self.sifter = new Sifter(this.options, {diacritics: settings.diacritics});
1184
+
1185
+ // build options table
1186
+ if (self.settings.options) {
1187
+ for (i = 0, n = self.settings.options.length; i < n; i++) {
1188
+ self.registerOption(self.settings.options[i]);
1189
+ }
1190
+ delete self.settings.options;
1191
+ }
1192
+
1193
+ // build optgroup table
1194
+ if (self.settings.optgroups) {
1195
+ for (i = 0, n = self.settings.optgroups.length; i < n; i++) {
1196
+ self.registerOptionGroup(self.settings.optgroups[i]);
1197
+ }
1198
+ delete self.settings.optgroups;
1199
+ }
1200
+
1201
+ // option-dependent defaults
1202
+ self.settings.mode = self.settings.mode || (self.settings.maxItems === 1 ? 'single' : 'multi');
1203
+ if (typeof self.settings.hideSelected !== 'boolean') {
1204
+ self.settings.hideSelected = self.settings.mode === 'multi';
1205
+ }
1206
+
1207
+ self.initializePlugins(self.settings.plugins);
1208
+ self.setupCallbacks();
1209
+ self.setupTemplates();
1210
+ self.setup();
1211
+ };
1212
+
1213
+ // mixins
1214
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1215
+
1216
+ MicroEvent.mixin(Selectize);
1217
+
1218
+ if(typeof MicroPlugin !== "undefined"){
1219
+ MicroPlugin.mixin(Selectize);
1220
+ }else{
1221
+ logError("Dependency MicroPlugin is missing",
1222
+ {explanation:
1223
+ "Make sure you either: (1) are using the \"standalone\" "+
1224
+ "version of Selectize, or (2) require MicroPlugin before you "+
1225
+ "load Selectize."}
1226
+ );
1227
+ }
1228
+
1229
+
1230
+ // methods
1231
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1232
+
1233
+ $.extend(Selectize.prototype, {
1234
+
1235
+ /**
1236
+ * Creates all elements and sets up event bindings.
1237
+ */
1238
+ setup: function() {
1239
+ var self = this;
1240
+ var settings = self.settings;
1241
+ var eventNS = self.eventNS;
1242
+ var $window = $(window);
1243
+ var $document = $(document);
1244
+ var $input = self.$input;
1245
+
1246
+ var $wrapper;
1247
+ var $control;
1248
+ var $control_input;
1249
+ var $dropdown;
1250
+ var $dropdown_content;
1251
+ var $dropdown_parent;
1252
+ var inputMode;
1253
+ var timeout_blur;
1254
+ var timeout_focus;
1255
+ var classes;
1256
+ var classes_plugins;
1257
+ var inputId;
1258
+
1259
+ inputMode = self.settings.mode;
1260
+ classes = $input.attr('class') || '';
1261
+
1262
+ $wrapper = $('<div>').addClass(settings.wrapperClass).addClass(classes).addClass(inputMode);
1263
+ $control = $('<div>').addClass(settings.inputClass).addClass('items').appendTo($wrapper);
1264
+ $control_input = $('<input type="text" autocomplete="off" />').appendTo($control).attr('tabindex', $input.is(':disabled') ? '-1' : self.tabIndex);
1265
+ $dropdown_parent = $(settings.dropdownParent || $wrapper);
1266
+ $dropdown = $('<div>').addClass(settings.dropdownClass).addClass(inputMode).hide().appendTo($dropdown_parent);
1267
+ $dropdown_content = $('<div>').addClass(settings.dropdownContentClass).appendTo($dropdown);
1268
+
1269
+ if(inputId = $input.attr('id')) {
1270
+ $control_input.attr('id', inputId + '-selectized');
1271
+ $("label[for='"+inputId+"']").attr('for', inputId + '-selectized');
1272
+ }
1273
+
1274
+ if(self.settings.copyClassesToDropdown) {
1275
+ $dropdown.addClass(classes);
1276
+ }
1277
+
1278
+ $wrapper.css({
1279
+ width: $input[0].style.width
1280
+ });
1281
+
1282
+ if (self.plugins.names.length) {
1283
+ classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-');
1284
+ $wrapper.addClass(classes_plugins);
1285
+ $dropdown.addClass(classes_plugins);
1286
+ }
1287
+
1288
+ if ((settings.maxItems === null || settings.maxItems > 1) && self.tagType === TAG_SELECT) {
1289
+ $input.attr('multiple', 'multiple');
1290
+ }
1291
+
1292
+ if (self.settings.placeholder) {
1293
+ $control_input.attr('placeholder', settings.placeholder);
1294
+ }
1295
+
1296
+ // if splitOn was not passed in, construct it from the delimiter to allow pasting universally
1297
+ if (!self.settings.splitOn && self.settings.delimiter) {
1298
+ var delimiterEscaped = self.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
1299
+ self.settings.splitOn = new RegExp('\\s*' + delimiterEscaped + '+\\s*');
1300
+ }
1301
+
1302
+ if ($input.attr('autocorrect')) {
1303
+ $control_input.attr('autocorrect', $input.attr('autocorrect'));
1304
+ }
1305
+
1306
+ if ($input.attr('autocapitalize')) {
1307
+ $control_input.attr('autocapitalize', $input.attr('autocapitalize'));
1308
+ }
1309
+ $control_input[0].type = $input[0].type;
1310
+
1311
+ self.$wrapper = $wrapper;
1312
+ self.$control = $control;
1313
+ self.$control_input = $control_input;
1314
+ self.$dropdown = $dropdown;
1315
+ self.$dropdown_content = $dropdown_content;
1316
+
1317
+ $dropdown.on('mouseenter mousedown click', '[data-disabled]>[data-selectable]', function(e) { e.stopImmediatePropagation(); });
1318
+ $dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); });
1319
+ $dropdown.on('mousedown click', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); });
1320
+ watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); });
1321
+ autoGrow($control_input);
1322
+
1323
+ $control.on({
1324
+ mousedown : function() { return self.onMouseDown.apply(self, arguments); },
1325
+ click : function() { return self.onClick.apply(self, arguments); }
1326
+ });
1327
+
1328
+ $control_input.on({
1329
+ mousedown : function(e) { e.stopPropagation(); },
1330
+ keydown : function() { return self.onKeyDown.apply(self, arguments); },
1331
+ keyup : function() { return self.onKeyUp.apply(self, arguments); },
1332
+ keypress : function() { return self.onKeyPress.apply(self, arguments); },
1333
+ resize : function() { self.positionDropdown.apply(self, []); },
1334
+ blur : function() { return self.onBlur.apply(self, arguments); },
1335
+ focus : function() { self.ignoreBlur = false; return self.onFocus.apply(self, arguments); },
1336
+ paste : function() { return self.onPaste.apply(self, arguments); }
1337
+ });
1338
+
1339
+ $document.on('keydown' + eventNS, function(e) {
1340
+ self.isCmdDown = e[IS_MAC ? 'metaKey' : 'ctrlKey'];
1341
+ self.isCtrlDown = e[IS_MAC ? 'altKey' : 'ctrlKey'];
1342
+ self.isShiftDown = e.shiftKey;
1343
+ });
1344
+
1345
+ $document.on('keyup' + eventNS, function(e) {
1346
+ if (e.keyCode === KEY_CTRL) self.isCtrlDown = false;
1347
+ if (e.keyCode === KEY_SHIFT) self.isShiftDown = false;
1348
+ if (e.keyCode === KEY_CMD) self.isCmdDown = false;
1349
+ });
1350
+
1351
+ $document.on('mousedown' + eventNS, function(e) {
1352
+ if (self.isFocused) {
1353
+ // prevent events on the dropdown scrollbar from causing the control to blur
1354
+ if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) {
1355
+ return false;
1356
+ }
1357
+ // blur on click outside
1358
+ if (!self.$control.has(e.target).length && e.target !== self.$control[0]) {
1359
+ self.blur(e.target);
1360
+ }
1361
+ }
1362
+ });
1363
+
1364
+ $window.on(['scroll' + eventNS, 'resize' + eventNS].join(' '), function() {
1365
+ if (self.isOpen) {
1366
+ self.positionDropdown.apply(self, arguments);
1367
+ }
1368
+ });
1369
+ $window.on('mousemove' + eventNS, function() {
1370
+ self.ignoreHover = false;
1371
+ });
1372
+
1373
+ // store original children and tab index so that they can be
1374
+ // restored when the destroy() method is called.
1375
+ this.revertSettings = {
1376
+ $children : $input.children().detach(),
1377
+ tabindex : $input.attr('tabindex')
1378
+ };
1379
+
1380
+ $input.attr('tabindex', -1).hide().after(self.$wrapper);
1381
+
1382
+ if ($.isArray(settings.items)) {
1383
+ self.setValue(settings.items);
1384
+ delete settings.items;
1385
+ }
1386
+
1387
+ // feature detect for the validation API
1388
+ if (SUPPORTS_VALIDITY_API) {
1389
+ $input.on('invalid' + eventNS, function(e) {
1390
+ e.preventDefault();
1391
+ self.isInvalid = true;
1392
+ self.refreshState();
1393
+ });
1394
+ }
1395
+
1396
+ self.updateOriginalInput();
1397
+ self.refreshItems();
1398
+ self.refreshState();
1399
+ self.updatePlaceholder();
1400
+ self.isSetup = true;
1401
+
1402
+ if ($input.is(':disabled')) {
1403
+ self.disable();
1404
+ }
1405
+
1406
+ self.on('change', this.onChange);
1407
+
1408
+ $input.data('selectize', self);
1409
+ $input.addClass('selectized');
1410
+ self.trigger('initialize');
1411
+
1412
+ // preload options
1413
+ if (settings.preload === true) {
1414
+ self.onSearchChange('');
1415
+ }
1416
+
1417
+ },
1418
+
1419
+ /**
1420
+ * Sets up default rendering functions.
1421
+ */
1422
+ setupTemplates: function() {
1423
+ var self = this;
1424
+ var field_label = self.settings.labelField;
1425
+ var field_optgroup = self.settings.optgroupLabelField;
1426
+
1427
+ var templates = {
1428
+ 'optgroup': function(data) {
1429
+ return '<div class="optgroup">' + data.html + '</div>';
1430
+ },
1431
+ 'optgroup_header': function(data, escape) {
1432
+ return '<div class="optgroup-header">' + escape(data[field_optgroup]) + '</div>';
1433
+ },
1434
+ 'option': function(data, escape) {
1435
+ return '<div class="option">' + escape(data[field_label]) + '</div>';
1436
+ },
1437
+ 'item': function(data, escape) {
1438
+ return '<div class="item">' + escape(data[field_label]) + '</div>';
1439
+ },
1440
+ 'option_create': function(data, escape) {
1441
+ return '<div class="create">Add <strong>' + escape(data.input) + '</strong>&hellip;</div>';
1442
+ }
1443
+ };
1444
+
1445
+ self.settings.render = $.extend({}, templates, self.settings.render);
1446
+ },
1447
+
1448
+ /**
1449
+ * Maps fired events to callbacks provided
1450
+ * in the settings used when creating the control.
1451
+ */
1452
+ setupCallbacks: function() {
1453
+ var key, fn, callbacks = {
1454
+ 'initialize' : 'onInitialize',
1455
+ 'change' : 'onChange',
1456
+ 'item_add' : 'onItemAdd',
1457
+ 'item_remove' : 'onItemRemove',
1458
+ 'clear' : 'onClear',
1459
+ 'option_add' : 'onOptionAdd',
1460
+ 'option_remove' : 'onOptionRemove',
1461
+ 'option_clear' : 'onOptionClear',
1462
+ 'optgroup_add' : 'onOptionGroupAdd',
1463
+ 'optgroup_remove' : 'onOptionGroupRemove',
1464
+ 'optgroup_clear' : 'onOptionGroupClear',
1465
+ 'dropdown_open' : 'onDropdownOpen',
1466
+ 'dropdown_close' : 'onDropdownClose',
1467
+ 'type' : 'onType',
1468
+ 'load' : 'onLoad',
1469
+ 'focus' : 'onFocus',
1470
+ 'blur' : 'onBlur'
1471
+ };
1472
+
1473
+ for (key in callbacks) {
1474
+ if (callbacks.hasOwnProperty(key)) {
1475
+ fn = this.settings[callbacks[key]];
1476
+ if (fn) this.on(key, fn);
1477
+ }
1478
+ }
1479
+ },
1480
+
1481
+ /**
1482
+ * Triggered when the main control element
1483
+ * has a click event.
1484
+ *
1485
+ * @param {object} e
1486
+ * @return {boolean}
1487
+ */
1488
+ onClick: function(e) {
1489
+ var self = this;
1490
+
1491
+ // necessary for mobile webkit devices (manual focus triggering
1492
+ // is ignored unless invoked within a click event)
1493
+ // also necessary to reopen a dropdown that has been closed by
1494
+ // closeAfterSelect
1495
+ if (!self.isFocused || !self.isOpen) {
1496
+ self.focus();
1497
+ e.preventDefault();
1498
+ }
1499
+ },
1500
+
1501
+ /**
1502
+ * Triggered when the main control element
1503
+ * has a mouse down event.
1504
+ *
1505
+ * @param {object} e
1506
+ * @return {boolean}
1507
+ */
1508
+ onMouseDown: function(e) {
1509
+ var self = this;
1510
+ var defaultPrevented = e.isDefaultPrevented();
1511
+ var $target = $(e.target);
1512
+
1513
+ if (self.isFocused) {
1514
+ // retain focus by preventing native handling. if the
1515
+ // event target is the input it should not be modified.
1516
+ // otherwise, text selection within the input won't work.
1517
+ if (e.target !== self.$control_input[0]) {
1518
+ if (self.settings.mode === 'single') {
1519
+ // toggle dropdown
1520
+ self.isOpen ? self.close() : self.open();
1521
+ } else if (!defaultPrevented) {
1522
+ self.setActiveItem(null);
1523
+ }
1524
+ return false;
1525
+ }
1526
+ } else {
1527
+ // give control focus
1528
+ if (!defaultPrevented) {
1529
+ window.setTimeout(function() {
1530
+ self.focus();
1531
+ }, 0);
1532
+ }
1533
+ }
1534
+ },
1535
+
1536
+ /**
1537
+ * Triggered when the value of the control has been changed.
1538
+ * This should propagate the event to the original DOM
1539
+ * input / select element.
1540
+ */
1541
+ onChange: function() {
1542
+ this.$input.trigger('change');
1543
+ },
1544
+
1545
+ /**
1546
+ * Triggered on <input> paste.
1547
+ *
1548
+ * @param {object} e
1549
+ * @returns {boolean}
1550
+ */
1551
+ onPaste: function(e) {
1552
+ var self = this;
1553
+
1554
+ if (self.isFull() || self.isInputHidden || self.isLocked) {
1555
+ e.preventDefault();
1556
+ return;
1557
+ }
1558
+
1559
+ // If a regex or string is included, this will split the pasted
1560
+ // input and create Items for each separate value
1561
+ if (self.settings.splitOn) {
1562
+
1563
+ // Wait for pasted text to be recognized in value
1564
+ setTimeout(function() {
1565
+ var pastedText = self.$control_input.val();
1566
+ if(!pastedText.match(self.settings.splitOn)){ return }
1567
+
1568
+ var splitInput = $.trim(pastedText).split(self.settings.splitOn);
1569
+ for (var i = 0, n = splitInput.length; i < n; i++) {
1570
+ self.createItem(splitInput[i]);
1571
+ }
1572
+ }, 0);
1573
+ }
1574
+ },
1575
+
1576
+ /**
1577
+ * Triggered on <input> keypress.
1578
+ *
1579
+ * @param {object} e
1580
+ * @returns {boolean}
1581
+ */
1582
+ onKeyPress: function(e) {
1583
+ if (this.isLocked) return e && e.preventDefault();
1584
+ var character = String.fromCharCode(e.keyCode || e.which);
1585
+ if (this.settings.create && this.settings.mode === 'multi' && character === this.settings.delimiter) {
1586
+ this.createItem();
1587
+ e.preventDefault();
1588
+ return false;
1589
+ }
1590
+ },
1591
+
1592
+ /**
1593
+ * Triggered on <input> keydown.
1594
+ *
1595
+ * @param {object} e
1596
+ * @returns {boolean}
1597
+ */
1598
+ onKeyDown: function(e) {
1599
+ var isInput = e.target === this.$control_input[0];
1600
+ var self = this;
1601
+
1602
+ if (self.isLocked) {
1603
+ if (e.keyCode !== KEY_TAB) {
1604
+ e.preventDefault();
1605
+ }
1606
+ return;
1607
+ }
1608
+
1609
+ switch (e.keyCode) {
1610
+ case KEY_A:
1611
+ if (self.isCmdDown) {
1612
+ self.selectAll();
1613
+ return;
1614
+ }
1615
+ break;
1616
+ case KEY_ESC:
1617
+ if (self.isOpen) {
1618
+ e.preventDefault();
1619
+ e.stopPropagation();
1620
+ self.close();
1621
+ }
1622
+ return;
1623
+ case KEY_N:
1624
+ if (!e.ctrlKey || e.altKey) break;
1625
+ case KEY_DOWN:
1626
+ if (!self.isOpen && self.hasOptions) {
1627
+ self.open();
1628
+ } else if (self.$activeOption) {
1629
+ self.ignoreHover = true;
1630
+ var $next = self.getAdjacentOption(self.$activeOption, 1);
1631
+ if ($next.length) self.setActiveOption($next, true, true);
1632
+ }
1633
+ e.preventDefault();
1634
+ return;
1635
+ case KEY_P:
1636
+ if (!e.ctrlKey || e.altKey) break;
1637
+ case KEY_UP:
1638
+ if (self.$activeOption) {
1639
+ self.ignoreHover = true;
1640
+ var $prev = self.getAdjacentOption(self.$activeOption, -1);
1641
+ if ($prev.length) self.setActiveOption($prev, true, true);
1642
+ }
1643
+ e.preventDefault();
1644
+ return;
1645
+ case KEY_RETURN:
1646
+ if (self.isOpen && self.$activeOption) {
1647
+ self.onOptionSelect({currentTarget: self.$activeOption});
1648
+ e.preventDefault();
1649
+ }
1650
+ return;
1651
+ case KEY_LEFT:
1652
+ self.advanceSelection(-1, e);
1653
+ return;
1654
+ case KEY_RIGHT:
1655
+ self.advanceSelection(1, e);
1656
+ return;
1657
+ case KEY_TAB:
1658
+ if (self.settings.selectOnTab && self.isOpen && self.$activeOption) {
1659
+ self.onOptionSelect({currentTarget: self.$activeOption});
1660
+
1661
+ // Default behaviour is to jump to the next field, we only want this
1662
+ // if the current field doesn't accept any more entries
1663
+ if (!self.isFull()) {
1664
+ e.preventDefault();
1665
+ }
1666
+ }
1667
+ if (self.settings.create && self.createItem()) {
1668
+ e.preventDefault();
1669
+ }
1670
+ return;
1671
+ case KEY_BACKSPACE:
1672
+ case KEY_DELETE:
1673
+ self.deleteSelection(e);
1674
+ return;
1675
+ }
1676
+
1677
+ if ((self.isFull() || self.isInputHidden) && !(IS_MAC ? e.metaKey : e.ctrlKey)) {
1678
+ e.preventDefault();
1679
+ return;
1680
+ }
1681
+ },
1682
+
1683
+ /**
1684
+ * Triggered on <input> keyup.
1685
+ *
1686
+ * @param {object} e
1687
+ * @returns {boolean}
1688
+ */
1689
+ onKeyUp: function(e) {
1690
+ var self = this;
1691
+
1692
+ if (self.isLocked) return e && e.preventDefault();
1693
+ var value = self.$control_input.val() || '';
1694
+ if (self.lastValue !== value) {
1695
+ self.lastValue = value;
1696
+ self.onSearchChange(value);
1697
+ self.refreshOptions();
1698
+ self.trigger('type', value);
1699
+ }
1700
+ },
1701
+
1702
+ /**
1703
+ * Invokes the user-provide option provider / loader.
1704
+ *
1705
+ * Note: this function is debounced in the Selectize
1706
+ * constructor (by `settings.loadThrottle` milliseconds)
1707
+ *
1708
+ * @param {string} value
1709
+ */
1710
+ onSearchChange: function(value) {
1711
+ var self = this;
1712
+ var fn = self.settings.load;
1713
+ if (!fn) return;
1714
+ if (self.loadedSearches.hasOwnProperty(value)) return;
1715
+ self.loadedSearches[value] = true;
1716
+ self.load(function(callback) {
1717
+ fn.apply(self, [value, callback]);
1718
+ });
1719
+ },
1720
+
1721
+ /**
1722
+ * Triggered on <input> focus.
1723
+ *
1724
+ * @param {object} e (optional)
1725
+ * @returns {boolean}
1726
+ */
1727
+ onFocus: function(e) {
1728
+ var self = this;
1729
+ var wasFocused = self.isFocused;
1730
+
1731
+ if (self.isDisabled) {
1732
+ self.blur();
1733
+ e && e.preventDefault();
1734
+ return false;
1735
+ }
1736
+
1737
+ if (self.ignoreFocus) return;
1738
+ self.isFocused = true;
1739
+ if (self.settings.preload === 'focus') self.onSearchChange('');
1740
+
1741
+ if (!wasFocused) self.trigger('focus');
1742
+
1743
+ if (!self.$activeItems.length) {
1744
+ self.showInput();
1745
+ self.setActiveItem(null);
1746
+ self.refreshOptions(!!self.settings.openOnFocus);
1747
+ }
1748
+
1749
+ self.refreshState();
1750
+ },
1751
+
1752
+ /**
1753
+ * Triggered on <input> blur.
1754
+ *
1755
+ * @param {object} e
1756
+ * @param {Element} dest
1757
+ */
1758
+ onBlur: function(e, dest) {
1759
+ var self = this;
1760
+ if (!self.isFocused) return;
1761
+ self.isFocused = false;
1762
+
1763
+ if (self.ignoreFocus) {
1764
+ return;
1765
+ } else if (!self.ignoreBlur && document.activeElement === self.$dropdown_content[0]) {
1766
+ // necessary to prevent IE closing the dropdown when the scrollbar is clicked
1767
+ self.ignoreBlur = true;
1768
+ self.onFocus(e);
1769
+ return;
1770
+ }
1771
+
1772
+ var deactivate = function() {
1773
+ self.close();
1774
+ self.setTextboxValue('');
1775
+ self.setActiveItem(null);
1776
+ self.setActiveOption(null);
1777
+ self.setCaret(self.items.length);
1778
+ self.refreshState();
1779
+
1780
+ // IE11 bug: element still marked as active
1781
+ dest && dest.focus && dest.focus();
1782
+
1783
+ self.isBlurring = false;
1784
+ self.ignoreFocus = false;
1785
+ self.trigger('blur');
1786
+ };
1787
+
1788
+ self.isBlurring = true;
1789
+ self.ignoreFocus = true;
1790
+ if (self.settings.create && self.settings.createOnBlur) {
1791
+ self.createItem(null, false, deactivate);
1792
+ } else {
1793
+ deactivate();
1794
+ }
1795
+ },
1796
+
1797
+ /**
1798
+ * Triggered when the user rolls over
1799
+ * an option in the autocomplete dropdown menu.
1800
+ *
1801
+ * @param {object} e
1802
+ * @returns {boolean}
1803
+ */
1804
+ onOptionHover: function(e) {
1805
+ if (this.ignoreHover) return;
1806
+ this.setActiveOption(e.currentTarget, false);
1807
+ },
1808
+
1809
+ /**
1810
+ * Triggered when the user clicks on an option
1811
+ * in the autocomplete dropdown menu.
1812
+ *
1813
+ * @param {object} e
1814
+ * @returns {boolean}
1815
+ */
1816
+ onOptionSelect: function(e) {
1817
+ var value, $target, $option, self = this;
1818
+
1819
+ if (e.preventDefault) {
1820
+ e.preventDefault();
1821
+ e.stopPropagation();
1822
+ }
1823
+
1824
+ $target = $(e.currentTarget);
1825
+ if ($target.hasClass('create')) {
1826
+ self.createItem(null, function() {
1827
+ if (self.settings.closeAfterSelect) {
1828
+ self.close();
1829
+ }
1830
+ });
1831
+ } else {
1832
+ value = $target.attr('data-value');
1833
+ if (typeof value !== 'undefined') {
1834
+ self.lastQuery = null;
1835
+ self.setTextboxValue('');
1836
+ self.addItem(value);
1837
+ if (self.settings.closeAfterSelect) {
1838
+ self.close();
1839
+ } else if (!self.settings.hideSelected && e.type && /mouse/.test(e.type)) {
1840
+ self.setActiveOption(self.getOption(value));
1841
+ }
1842
+ }
1843
+ }
1844
+ },
1845
+
1846
+ /**
1847
+ * Triggered when the user clicks on an item
1848
+ * that has been selected.
1849
+ *
1850
+ * @param {object} e
1851
+ * @returns {boolean}
1852
+ */
1853
+ onItemSelect: function(e) {
1854
+ var self = this;
1855
+
1856
+ if (self.isLocked) return;
1857
+ if (self.settings.mode === 'multi') {
1858
+ e.preventDefault();
1859
+ self.setActiveItem(e.currentTarget, e);
1860
+ }
1861
+ },
1862
+
1863
+ /**
1864
+ * Invokes the provided method that provides
1865
+ * results to a callback---which are then added
1866
+ * as options to the control.
1867
+ *
1868
+ * @param {function} fn
1869
+ */
1870
+ load: function(fn) {
1871
+ var self = this;
1872
+ var $wrapper = self.$wrapper.addClass(self.settings.loadingClass);
1873
+
1874
+ self.loading++;
1875
+ fn.apply(self, [function(results) {
1876
+ self.loading = Math.max(self.loading - 1, 0);
1877
+ if (results && results.length) {
1878
+ self.addOption(results);
1879
+ self.refreshOptions(self.isFocused && !self.isInputHidden);
1880
+ }
1881
+ if (!self.loading) {
1882
+ $wrapper.removeClass(self.settings.loadingClass);
1883
+ }
1884
+ self.trigger('load', results);
1885
+ }]);
1886
+ },
1887
+
1888
+ /**
1889
+ * Sets the input field of the control to the specified value.
1890
+ *
1891
+ * @param {string} value
1892
+ */
1893
+ setTextboxValue: function(value) {
1894
+ var $input = this.$control_input;
1895
+ var changed = $input.val() !== value;
1896
+ if (changed) {
1897
+ $input.val(value).triggerHandler('update');
1898
+ this.lastValue = value;
1899
+ }
1900
+ },
1901
+
1902
+ /**
1903
+ * Returns the value of the control. If multiple items
1904
+ * can be selected (e.g. <select multiple>), this returns
1905
+ * an array. If only one item can be selected, this
1906
+ * returns a string.
1907
+ *
1908
+ * @returns {mixed}
1909
+ */
1910
+ getValue: function() {
1911
+ if (this.tagType === TAG_SELECT && this.$input.attr('multiple')) {
1912
+ return this.items;
1913
+ } else {
1914
+ return this.items.join(this.settings.delimiter);
1915
+ }
1916
+ },
1917
+
1918
+ /**
1919
+ * Resets the selected items to the given value.
1920
+ *
1921
+ * @param {mixed} value
1922
+ */
1923
+ setValue: function(value, silent) {
1924
+ var events = silent ? [] : ['change'];
1925
+
1926
+ debounce_events(this, events, function() {
1927
+ this.clear(silent);
1928
+ this.addItems(value, silent);
1929
+ });
1930
+ },
1931
+
1932
+ /**
1933
+ * Sets the selected item.
1934
+ *
1935
+ * @param {object} $item
1936
+ * @param {object} e (optional)
1937
+ */
1938
+ setActiveItem: function($item, e) {
1939
+ var self = this;
1940
+ var eventName;
1941
+ var i, idx, begin, end, item, swap;
1942
+ var $last;
1943
+
1944
+ if (self.settings.mode === 'single') return;
1945
+ $item = $($item);
1946
+
1947
+ // clear the active selection
1948
+ if (!$item.length) {
1949
+ $(self.$activeItems).removeClass('active');
1950
+ self.$activeItems = [];
1951
+ if (self.isFocused) {
1952
+ self.showInput();
1953
+ }
1954
+ return;
1955
+ }
1956
+
1957
+ // modify selection
1958
+ eventName = e && e.type.toLowerCase();
1959
+
1960
+ if (eventName === 'mousedown' && self.isShiftDown && self.$activeItems.length) {
1961
+ $last = self.$control.children('.active:last');
1962
+ begin = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$last[0]]);
1963
+ end = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$item[0]]);
1964
+ if (begin > end) {
1965
+ swap = begin;
1966
+ begin = end;
1967
+ end = swap;
1968
+ }
1969
+ for (i = begin; i <= end; i++) {
1970
+ item = self.$control[0].childNodes[i];
1971
+ if (self.$activeItems.indexOf(item) === -1) {
1972
+ $(item).addClass('active');
1973
+ self.$activeItems.push(item);
1974
+ }
1975
+ }
1976
+ e.preventDefault();
1977
+ } else if ((eventName === 'mousedown' && self.isCtrlDown) || (eventName === 'keydown' && this.isShiftDown)) {
1978
+ if ($item.hasClass('active')) {
1979
+ idx = self.$activeItems.indexOf($item[0]);
1980
+ self.$activeItems.splice(idx, 1);
1981
+ $item.removeClass('active');
1982
+ } else {
1983
+ self.$activeItems.push($item.addClass('active')[0]);
1984
+ }
1985
+ } else {
1986
+ $(self.$activeItems).removeClass('active');
1987
+ self.$activeItems = [$item.addClass('active')[0]];
1988
+ }
1989
+
1990
+ // ensure control has focus
1991
+ self.hideInput();
1992
+ if (!this.isFocused) {
1993
+ self.focus();
1994
+ }
1995
+ },
1996
+
1997
+ /**
1998
+ * Sets the selected item in the dropdown menu
1999
+ * of available options.
2000
+ *
2001
+ * @param {object} $object
2002
+ * @param {boolean} scroll
2003
+ * @param {boolean} animate
2004
+ */
2005
+ setActiveOption: function($option, scroll, animate) {
2006
+ var height_menu, height_item, y;
2007
+ var scroll_top, scroll_bottom;
2008
+ var self = this;
2009
+
2010
+ if (self.$activeOption) self.$activeOption.removeClass('active');
2011
+ self.$activeOption = null;
2012
+
2013
+ $option = $($option);
2014
+ if (!$option.length) return;
2015
+
2016
+ self.$activeOption = $option.addClass('active');
2017
+
2018
+ if (scroll || !isset(scroll)) {
2019
+
2020
+ height_menu = self.$dropdown_content.height();
2021
+ height_item = self.$activeOption.outerHeight(true);
2022
+ scroll = self.$dropdown_content.scrollTop() || 0;
2023
+ y = self.$activeOption.offset().top - self.$dropdown_content.offset().top + scroll;
2024
+ scroll_top = y;
2025
+ scroll_bottom = y - height_menu + height_item;
2026
+
2027
+ if (y + height_item > height_menu + scroll) {
2028
+ self.$dropdown_content.stop().animate({scrollTop: scroll_bottom}, animate ? self.settings.scrollDuration : 0);
2029
+ } else if (y < scroll) {
2030
+ self.$dropdown_content.stop().animate({scrollTop: scroll_top}, animate ? self.settings.scrollDuration : 0);
2031
+ }
2032
+
2033
+ }
2034
+ },
2035
+
2036
+ /**
2037
+ * Selects all items (CTRL + A).
2038
+ */
2039
+ selectAll: function() {
2040
+ var self = this;
2041
+ if (self.settings.mode === 'single') return;
2042
+
2043
+ self.$activeItems = Array.prototype.slice.apply(self.$control.children(':not(input)').addClass('active'));
2044
+ if (self.$activeItems.length) {
2045
+ self.hideInput();
2046
+ self.close();
2047
+ }
2048
+ self.focus();
2049
+ },
2050
+
2051
+ /**
2052
+ * Hides the input element out of view, while
2053
+ * retaining its focus.
2054
+ */
2055
+ hideInput: function() {
2056
+ var self = this;
2057
+
2058
+ self.setTextboxValue('');
2059
+ self.$control_input.css({opacity: 0, position: 'absolute', left: self.rtl ? 10000 : -10000});
2060
+ self.isInputHidden = true;
2061
+ },
2062
+
2063
+ /**
2064
+ * Restores input visibility.
2065
+ */
2066
+ showInput: function() {
2067
+ this.$control_input.css({opacity: 1, position: 'relative', left: 0});
2068
+ this.isInputHidden = false;
2069
+ },
2070
+
2071
+ /**
2072
+ * Gives the control focus.
2073
+ */
2074
+ focus: function() {
2075
+ var self = this;
2076
+ if (self.isDisabled) return;
2077
+
2078
+ self.ignoreFocus = true;
2079
+ self.$control_input[0].focus();
2080
+ window.setTimeout(function() {
2081
+ self.ignoreFocus = false;
2082
+ self.onFocus();
2083
+ }, 0);
2084
+ },
2085
+
2086
+ /**
2087
+ * Forces the control out of focus.
2088
+ *
2089
+ * @param {Element} dest
2090
+ */
2091
+ blur: function(dest) {
2092
+ this.$control_input[0].blur();
2093
+ this.onBlur(null, dest);
2094
+ },
2095
+
2096
+ /**
2097
+ * Returns a function that scores an object
2098
+ * to show how good of a match it is to the
2099
+ * provided query.
2100
+ *
2101
+ * @param {string} query
2102
+ * @param {object} options
2103
+ * @return {function}
2104
+ */
2105
+ getScoreFunction: function(query) {
2106
+ return this.sifter.getScoreFunction(query, this.getSearchOptions());
2107
+ },
2108
+
2109
+ /**
2110
+ * Returns search options for sifter (the system
2111
+ * for scoring and sorting results).
2112
+ *
2113
+ * @see https://github.com/brianreavis/sifter.js
2114
+ * @return {object}
2115
+ */
2116
+ getSearchOptions: function() {
2117
+ var settings = this.settings;
2118
+ var sort = settings.sortField;
2119
+ if (typeof sort === 'string') {
2120
+ sort = [{field: sort}];
2121
+ }
2122
+
2123
+ return {
2124
+ fields : settings.searchField,
2125
+ conjunction : settings.searchConjunction,
2126
+ sort : sort,
2127
+ nesting : settings.nesting
2128
+ };
2129
+ },
2130
+
2131
+ /**
2132
+ * Searches through available options and returns
2133
+ * a sorted array of matches.
2134
+ *
2135
+ * Returns an object containing:
2136
+ *
2137
+ * - query {string}
2138
+ * - tokens {array}
2139
+ * - total {int}
2140
+ * - items {array}
2141
+ *
2142
+ * @param {string} query
2143
+ * @returns {object}
2144
+ */
2145
+ search: function(query) {
2146
+ var i, value, score, result, calculateScore;
2147
+ var self = this;
2148
+ var settings = self.settings;
2149
+ var options = this.getSearchOptions();
2150
+
2151
+ // validate user-provided result scoring function
2152
+ if (settings.score) {
2153
+ calculateScore = self.settings.score.apply(this, [query]);
2154
+ if (typeof calculateScore !== 'function') {
2155
+ throw new Error('Selectize "score" setting must be a function that returns a function');
2156
+ }
2157
+ }
2158
+
2159
+ // perform search
2160
+ if (query !== self.lastQuery) {
2161
+ self.lastQuery = query;
2162
+ result = self.sifter.search(query, $.extend(options, {score: calculateScore}));
2163
+ self.currentResults = result;
2164
+ } else {
2165
+ result = $.extend(true, {}, self.currentResults);
2166
+ }
2167
+
2168
+ // filter out selected items
2169
+ if (settings.hideSelected) {
2170
+ for (i = result.items.length - 1; i >= 0; i--) {
2171
+ if (self.items.indexOf(hash_key(result.items[i].id)) !== -1) {
2172
+ result.items.splice(i, 1);
2173
+ }
2174
+ }
2175
+ }
2176
+
2177
+ return result;
2178
+ },
2179
+
2180
+ /**
2181
+ * Refreshes the list of available options shown
2182
+ * in the autocomplete dropdown menu.
2183
+ *
2184
+ * @param {boolean} triggerDropdown
2185
+ */
2186
+ refreshOptions: function(triggerDropdown) {
2187
+ var i, j, k, n, groups, groups_order, option, option_html, optgroup, optgroups, html, html_children, has_create_option;
2188
+ var $active, $active_before, $create;
2189
+
2190
+ if (typeof triggerDropdown === 'undefined') {
2191
+ triggerDropdown = true;
2192
+ }
2193
+
2194
+ var self = this;
2195
+ var query = $.trim(self.$control_input.val());
2196
+ var results = self.search(query);
2197
+ var $dropdown_content = self.$dropdown_content;
2198
+ var active_before = self.$activeOption && hash_key(self.$activeOption.attr('data-value'));
2199
+
2200
+ // build markup
2201
+ n = results.items.length;
2202
+ if (typeof self.settings.maxOptions === 'number') {
2203
+ n = Math.min(n, self.settings.maxOptions);
2204
+ }
2205
+
2206
+ // render and group available options individually
2207
+ groups = {};
2208
+ groups_order = [];
2209
+
2210
+ for (i = 0; i < n; i++) {
2211
+ option = self.options[results.items[i].id];
2212
+ option_html = self.render('option', option);
2213
+ optgroup = option[self.settings.optgroupField] || '';
2214
+ optgroups = $.isArray(optgroup) ? optgroup : [optgroup];
2215
+
2216
+ for (j = 0, k = optgroups && optgroups.length; j < k; j++) {
2217
+ optgroup = optgroups[j];
2218
+ if (!self.optgroups.hasOwnProperty(optgroup)) {
2219
+ optgroup = '';
2220
+ }
2221
+ if (!groups.hasOwnProperty(optgroup)) {
2222
+ groups[optgroup] = document.createDocumentFragment();
2223
+ groups_order.push(optgroup);
2224
+ }
2225
+ groups[optgroup].appendChild(option_html);
2226
+ }
2227
+ }
2228
+
2229
+ // sort optgroups
2230
+ if (this.settings.lockOptgroupOrder) {
2231
+ groups_order.sort(function(a, b) {
2232
+ var a_order = self.optgroups[a].$order || 0;
2233
+ var b_order = self.optgroups[b].$order || 0;
2234
+ return a_order - b_order;
2235
+ });
2236
+ }
2237
+
2238
+ // render optgroup headers & join groups
2239
+ html = document.createDocumentFragment();
2240
+ for (i = 0, n = groups_order.length; i < n; i++) {
2241
+ optgroup = groups_order[i];
2242
+ if (self.optgroups.hasOwnProperty(optgroup) && groups[optgroup].childNodes.length) {
2243
+ // render the optgroup header and options within it,
2244
+ // then pass it to the wrapper template
2245
+ html_children = document.createDocumentFragment();
2246
+ html_children.appendChild(self.render('optgroup_header', self.optgroups[optgroup]));
2247
+ html_children.appendChild(groups[optgroup]);
2248
+
2249
+ html.appendChild(self.render('optgroup', $.extend({}, self.optgroups[optgroup], {
2250
+ html: domToString(html_children),
2251
+ dom: html_children
2252
+ })));
2253
+ } else {
2254
+ html.appendChild(groups[optgroup]);
2255
+ }
2256
+ }
2257
+
2258
+ $dropdown_content.html(html);
2259
+
2260
+ // highlight matching terms inline
2261
+ if (self.settings.highlight) {
2262
+ $dropdown_content.removeHighlight();
2263
+ if (results.query.length && results.tokens.length) {
2264
+ for (i = 0, n = results.tokens.length; i < n; i++) {
2265
+ highlight($dropdown_content, results.tokens[i].regex);
2266
+ }
2267
+ }
2268
+ }
2269
+
2270
+ // add "selected" class to selected options
2271
+ if (!self.settings.hideSelected) {
2272
+ for (i = 0, n = self.items.length; i < n; i++) {
2273
+ self.getOption(self.items[i]).addClass('selected');
2274
+ }
2275
+ }
2276
+
2277
+ // add create option
2278
+ has_create_option = self.canCreate(query);
2279
+ if (has_create_option) {
2280
+ $dropdown_content.prepend(self.render('option_create', {input: query}));
2281
+ $create = $($dropdown_content[0].childNodes[0]);
2282
+ }
2283
+
2284
+ // activate
2285
+ self.hasOptions = results.items.length > 0 || has_create_option;
2286
+ if (self.hasOptions) {
2287
+ if (results.items.length > 0) {
2288
+ $active_before = active_before && self.getOption(active_before);
2289
+ if ($active_before && $active_before.length) {
2290
+ $active = $active_before;
2291
+ } else if (self.settings.mode === 'single' && self.items.length) {
2292
+ $active = self.getOption(self.items[0]);
2293
+ }
2294
+ if (!$active || !$active.length) {
2295
+ if ($create && !self.settings.addPrecedence) {
2296
+ $active = self.getAdjacentOption($create, 1);
2297
+ } else {
2298
+ $active = $dropdown_content.find('[data-selectable]:first');
2299
+ }
2300
+ }
2301
+ } else {
2302
+ $active = $create;
2303
+ }
2304
+ self.setActiveOption($active);
2305
+ if (triggerDropdown && !self.isOpen) { self.open(); }
2306
+ } else {
2307
+ self.setActiveOption(null);
2308
+ if (triggerDropdown && self.isOpen) { self.close(); }
2309
+ }
2310
+ },
2311
+
2312
+ /**
2313
+ * Adds an available option. If it already exists,
2314
+ * nothing will happen. Note: this does not refresh
2315
+ * the options list dropdown (use `refreshOptions`
2316
+ * for that).
2317
+ *
2318
+ * Usage:
2319
+ *
2320
+ * this.addOption(data)
2321
+ *
2322
+ * @param {object|array} data
2323
+ */
2324
+ addOption: function(data) {
2325
+ var i, n, value, self = this;
2326
+
2327
+ if ($.isArray(data)) {
2328
+ for (i = 0, n = data.length; i < n; i++) {
2329
+ self.addOption(data[i]);
2330
+ }
2331
+ return;
2332
+ }
2333
+
2334
+ if (value = self.registerOption(data)) {
2335
+ self.userOptions[value] = true;
2336
+ self.lastQuery = null;
2337
+ self.trigger('option_add', value, data);
2338
+ }
2339
+ },
2340
+
2341
+ /**
2342
+ * Registers an option to the pool of options.
2343
+ *
2344
+ * @param {object} data
2345
+ * @return {boolean|string}
2346
+ */
2347
+ registerOption: function(data) {
2348
+ var key = hash_key(data[this.settings.valueField]);
2349
+ if (typeof key === 'undefined' || key === null || this.options.hasOwnProperty(key)) return false;
2350
+ data.$order = data.$order || ++this.order;
2351
+ this.options[key] = data;
2352
+ return key;
2353
+ },
2354
+
2355
+ /**
2356
+ * Registers an option group to the pool of option groups.
2357
+ *
2358
+ * @param {object} data
2359
+ * @return {boolean|string}
2360
+ */
2361
+ registerOptionGroup: function(data) {
2362
+ var key = hash_key(data[this.settings.optgroupValueField]);
2363
+ if (!key) return false;
2364
+
2365
+ data.$order = data.$order || ++this.order;
2366
+ this.optgroups[key] = data;
2367
+ return key;
2368
+ },
2369
+
2370
+ /**
2371
+ * Registers a new optgroup for options
2372
+ * to be bucketed into.
2373
+ *
2374
+ * @param {string} id
2375
+ * @param {object} data
2376
+ */
2377
+ addOptionGroup: function(id, data) {
2378
+ data[this.settings.optgroupValueField] = id;
2379
+ if (id = this.registerOptionGroup(data)) {
2380
+ this.trigger('optgroup_add', id, data);
2381
+ }
2382
+ },
2383
+
2384
+ /**
2385
+ * Removes an existing option group.
2386
+ *
2387
+ * @param {string} id
2388
+ */
2389
+ removeOptionGroup: function(id) {
2390
+ if (this.optgroups.hasOwnProperty(id)) {
2391
+ delete this.optgroups[id];
2392
+ this.renderCache = {};
2393
+ this.trigger('optgroup_remove', id);
2394
+ }
2395
+ },
2396
+
2397
+ /**
2398
+ * Clears all existing option groups.
2399
+ */
2400
+ clearOptionGroups: function() {
2401
+ this.optgroups = {};
2402
+ this.renderCache = {};
2403
+ this.trigger('optgroup_clear');
2404
+ },
2405
+
2406
+ /**
2407
+ * Updates an option available for selection. If
2408
+ * it is visible in the selected items or options
2409
+ * dropdown, it will be re-rendered automatically.
2410
+ *
2411
+ * @param {string} value
2412
+ * @param {object} data
2413
+ */
2414
+ updateOption: function(value, data) {
2415
+ var self = this;
2416
+ var $item, $item_new;
2417
+ var value_new, index_item, cache_items, cache_options, order_old;
2418
+
2419
+ value = hash_key(value);
2420
+ value_new = hash_key(data[self.settings.valueField]);
2421
+
2422
+ // sanity checks
2423
+ if (value === null) return;
2424
+ if (!self.options.hasOwnProperty(value)) return;
2425
+ if (typeof value_new !== 'string') throw new Error('Value must be set in option data');
2426
+
2427
+ order_old = self.options[value].$order;
2428
+
2429
+ // update references
2430
+ if (value_new !== value) {
2431
+ delete self.options[value];
2432
+ index_item = self.items.indexOf(value);
2433
+ if (index_item !== -1) {
2434
+ self.items.splice(index_item, 1, value_new);
2435
+ }
2436
+ }
2437
+ data.$order = data.$order || order_old;
2438
+ self.options[value_new] = data;
2439
+
2440
+ // invalidate render cache
2441
+ cache_items = self.renderCache['item'];
2442
+ cache_options = self.renderCache['option'];
2443
+
2444
+ if (cache_items) {
2445
+ delete cache_items[value];
2446
+ delete cache_items[value_new];
2447
+ }
2448
+ if (cache_options) {
2449
+ delete cache_options[value];
2450
+ delete cache_options[value_new];
2451
+ }
2452
+
2453
+ // update the item if it's selected
2454
+ if (self.items.indexOf(value_new) !== -1) {
2455
+ $item = self.getItem(value);
2456
+ $item_new = $(self.render('item', data));
2457
+ if ($item.hasClass('active')) $item_new.addClass('active');
2458
+ $item.replaceWith($item_new);
2459
+ }
2460
+
2461
+ // invalidate last query because we might have updated the sortField
2462
+ self.lastQuery = null;
2463
+
2464
+ // update dropdown contents
2465
+ if (self.isOpen) {
2466
+ self.refreshOptions(false);
2467
+ }
2468
+ },
2469
+
2470
+ /**
2471
+ * Removes a single option.
2472
+ *
2473
+ * @param {string} value
2474
+ * @param {boolean} silent
2475
+ */
2476
+ removeOption: function(value, silent) {
2477
+ var self = this;
2478
+ value = hash_key(value);
2479
+
2480
+ var cache_items = self.renderCache['item'];
2481
+ var cache_options = self.renderCache['option'];
2482
+ if (cache_items) delete cache_items[value];
2483
+ if (cache_options) delete cache_options[value];
2484
+
2485
+ delete self.userOptions[value];
2486
+ delete self.options[value];
2487
+ self.lastQuery = null;
2488
+ self.trigger('option_remove', value);
2489
+ self.removeItem(value, silent);
2490
+ },
2491
+
2492
+ /**
2493
+ * Clears all options.
2494
+ */
2495
+ clearOptions: function() {
2496
+ var self = this;
2497
+
2498
+ self.loadedSearches = {};
2499
+ self.userOptions = {};
2500
+ self.renderCache = {};
2501
+ var options = self.options;
2502
+ $.each(self.options, function(key, value) {
2503
+ if(self.items.indexOf(key) == -1) {
2504
+ delete options[key];
2505
+ }
2506
+ });
2507
+ self.options = self.sifter.items = options;
2508
+ self.lastQuery = null;
2509
+ self.trigger('option_clear');
2510
+ },
2511
+
2512
+ /**
2513
+ * Returns the jQuery element of the option
2514
+ * matching the given value.
2515
+ *
2516
+ * @param {string} value
2517
+ * @returns {object}
2518
+ */
2519
+ getOption: function(value) {
2520
+ return this.getElementWithValue(value, this.$dropdown_content.find('[data-selectable]'));
2521
+ },
2522
+
2523
+ /**
2524
+ * Returns the jQuery element of the next or
2525
+ * previous selectable option.
2526
+ *
2527
+ * @param {object} $option
2528
+ * @param {int} direction can be 1 for next or -1 for previous
2529
+ * @return {object}
2530
+ */
2531
+ getAdjacentOption: function($option, direction) {
2532
+ var $options = this.$dropdown.find('[data-selectable]');
2533
+ var index = $options.index($option) + direction;
2534
+
2535
+ return index >= 0 && index < $options.length ? $options.eq(index) : $();
2536
+ },
2537
+
2538
+ /**
2539
+ * Finds the first element with a "data-value" attribute
2540
+ * that matches the given value.
2541
+ *
2542
+ * @param {mixed} value
2543
+ * @param {object} $els
2544
+ * @return {object}
2545
+ */
2546
+ getElementWithValue: function(value, $els) {
2547
+ value = hash_key(value);
2548
+
2549
+ if (typeof value !== 'undefined' && value !== null) {
2550
+ for (var i = 0, n = $els.length; i < n; i++) {
2551
+ if ($els[i].getAttribute('data-value') === value) {
2552
+ return $($els[i]);
2553
+ }
2554
+ }
2555
+ }
2556
+
2557
+ return $();
2558
+ },
2559
+
2560
+ /**
2561
+ * Returns the jQuery element of the item
2562
+ * matching the given value.
2563
+ *
2564
+ * @param {string} value
2565
+ * @returns {object}
2566
+ */
2567
+ getItem: function(value) {
2568
+ return this.getElementWithValue(value, this.$control.children());
2569
+ },
2570
+
2571
+ /**
2572
+ * "Selects" multiple items at once. Adds them to the list
2573
+ * at the current caret position.
2574
+ *
2575
+ * @param {string} value
2576
+ * @param {boolean} silent
2577
+ */
2578
+ addItems: function(values, silent) {
2579
+ this.buffer = document.createDocumentFragment();
2580
+
2581
+ var childNodes = this.$control[0].childNodes;
2582
+ for (var i = 0; i < childNodes.length; i++) {
2583
+ this.buffer.appendChild(childNodes[i]);
2584
+ }
2585
+
2586
+ var items = $.isArray(values) ? values : [values];
2587
+ for (var i = 0, n = items.length; i < n; i++) {
2588
+ this.isPending = (i < n - 1);
2589
+ this.addItem(items[i], silent);
2590
+ }
2591
+
2592
+ var control = this.$control[0];
2593
+ control.insertBefore(this.buffer, control.firstChild);
2594
+
2595
+ this.buffer = null;
2596
+ },
2597
+
2598
+ /**
2599
+ * "Selects" an item. Adds it to the list
2600
+ * at the current caret position.
2601
+ *
2602
+ * @param {string} value
2603
+ * @param {boolean} silent
2604
+ */
2605
+ addItem: function(value, silent) {
2606
+ var events = silent ? [] : ['change'];
2607
+
2608
+ debounce_events(this, events, function() {
2609
+ var $item, $option, $options;
2610
+ var self = this;
2611
+ var inputMode = self.settings.mode;
2612
+ var i, active, value_next, wasFull;
2613
+ value = hash_key(value);
2614
+
2615
+ if (self.items.indexOf(value) !== -1) {
2616
+ if (inputMode === 'single') self.close();
2617
+ return;
2618
+ }
2619
+
2620
+ if (!self.options.hasOwnProperty(value)) return;
2621
+ if (inputMode === 'single') self.clear(silent);
2622
+ if (inputMode === 'multi' && self.isFull()) return;
2623
+
2624
+ $item = $(self.render('item', self.options[value]));
2625
+ wasFull = self.isFull();
2626
+ self.items.splice(self.caretPos, 0, value);
2627
+ self.insertAtCaret($item);
2628
+ if (!self.isPending || (!wasFull && self.isFull())) {
2629
+ self.refreshState();
2630
+ }
2631
+
2632
+ if (self.isSetup) {
2633
+ $options = self.$dropdown_content.find('[data-selectable]');
2634
+
2635
+ // update menu / remove the option (if this is not one item being added as part of series)
2636
+ if (!self.isPending) {
2637
+ $option = self.getOption(value);
2638
+ value_next = self.getAdjacentOption($option, 1).attr('data-value');
2639
+ self.refreshOptions(self.isFocused && inputMode !== 'single');
2640
+ if (value_next) {
2641
+ self.setActiveOption(self.getOption(value_next));
2642
+ }
2643
+ }
2644
+
2645
+ // hide the menu if the maximum number of items have been selected or no options are left
2646
+ if (!$options.length || self.isFull()) {
2647
+ self.close();
2648
+ } else if (!self.isPending) {
2649
+ self.positionDropdown();
2650
+ }
2651
+
2652
+ self.updatePlaceholder();
2653
+ self.trigger('item_add', value, $item);
2654
+
2655
+ if (!self.isPending) {
2656
+ self.updateOriginalInput({silent: silent});
2657
+ }
2658
+ }
2659
+ });
2660
+ },
2661
+
2662
+ /**
2663
+ * Removes the selected item matching
2664
+ * the provided value.
2665
+ *
2666
+ * @param {string} value
2667
+ */
2668
+ removeItem: function(value, silent) {
2669
+ var self = this;
2670
+ var $item, i, idx;
2671
+
2672
+ $item = (value instanceof $) ? value : self.getItem(value);
2673
+ value = hash_key($item.attr('data-value'));
2674
+ i = self.items.indexOf(value);
2675
+
2676
+ if (i !== -1) {
2677
+ $item.remove();
2678
+ if ($item.hasClass('active')) {
2679
+ idx = self.$activeItems.indexOf($item[0]);
2680
+ self.$activeItems.splice(idx, 1);
2681
+ }
2682
+
2683
+ self.items.splice(i, 1);
2684
+ self.lastQuery = null;
2685
+ if (!self.settings.persist && self.userOptions.hasOwnProperty(value)) {
2686
+ self.removeOption(value, silent);
2687
+ }
2688
+
2689
+ if (i < self.caretPos) {
2690
+ self.setCaret(self.caretPos - 1);
2691
+ }
2692
+
2693
+ self.refreshState();
2694
+ self.updatePlaceholder();
2695
+ self.updateOriginalInput({silent: silent});
2696
+ self.positionDropdown();
2697
+ self.trigger('item_remove', value, $item);
2698
+ }
2699
+ },
2700
+
2701
+ /**
2702
+ * Invokes the `create` method provided in the
2703
+ * selectize options that should provide the data
2704
+ * for the new item, given the user input.
2705
+ *
2706
+ * Once this completes, it will be added
2707
+ * to the item list.
2708
+ *
2709
+ * @param {string} value
2710
+ * @param {boolean} [triggerDropdown]
2711
+ * @param {function} [callback]
2712
+ * @return {boolean}
2713
+ */
2714
+ createItem: function(input, triggerDropdown) {
2715
+ var self = this;
2716
+ var caret = self.caretPos;
2717
+ input = input || $.trim(self.$control_input.val() || '');
2718
+
2719
+ var callback = arguments[arguments.length - 1];
2720
+ if (typeof callback !== 'function') callback = function() {};
2721
+
2722
+ if (typeof triggerDropdown !== 'boolean') {
2723
+ triggerDropdown = true;
2724
+ }
2725
+
2726
+ if (!self.canCreate(input)) {
2727
+ callback();
2728
+ return false;
2729
+ }
2730
+
2731
+ self.lock();
2732
+
2733
+ var setup = (typeof self.settings.create === 'function') ? this.settings.create : function(input) {
2734
+ var data = {};
2735
+ data[self.settings.labelField] = input;
2736
+ data[self.settings.valueField] = input;
2737
+ return data;
2738
+ };
2739
+
2740
+ var create = once(function(data) {
2741
+ self.unlock();
2742
+
2743
+ if (!data || typeof data !== 'object') return callback();
2744
+ var value = hash_key(data[self.settings.valueField]);
2745
+ if (typeof value !== 'string') return callback();
2746
+
2747
+ self.setTextboxValue('');
2748
+ self.addOption(data);
2749
+ self.setCaret(caret);
2750
+ self.addItem(value);
2751
+ self.refreshOptions(triggerDropdown && self.settings.mode !== 'single');
2752
+ callback(data);
2753
+ });
2754
+
2755
+ var output = setup.apply(this, [input, create]);
2756
+ if (typeof output !== 'undefined') {
2757
+ create(output);
2758
+ }
2759
+
2760
+ return true;
2761
+ },
2762
+
2763
+ /**
2764
+ * Re-renders the selected item lists.
2765
+ */
2766
+ refreshItems: function() {
2767
+ this.lastQuery = null;
2768
+
2769
+ if (this.isSetup) {
2770
+ this.addItem(this.items);
2771
+ }
2772
+
2773
+ this.refreshState();
2774
+ this.updateOriginalInput();
2775
+ },
2776
+
2777
+ /**
2778
+ * Updates all state-dependent attributes
2779
+ * and CSS classes.
2780
+ */
2781
+ refreshState: function() {
2782
+ this.refreshValidityState();
2783
+ this.refreshClasses();
2784
+ },
2785
+
2786
+ /**
2787
+ * Update the `required` attribute of both input and control input.
2788
+ *
2789
+ * The `required` property needs to be activated on the control input
2790
+ * for the error to be displayed at the right place. `required` also
2791
+ * needs to be temporarily deactivated on the input since the input is
2792
+ * hidden and can't show errors.
2793
+ */
2794
+ refreshValidityState: function() {
2795
+ if (!this.isRequired) return false;
2796
+
2797
+ var invalid = !this.items.length;
2798
+
2799
+ this.isInvalid = invalid;
2800
+ this.$control_input.prop('required', invalid);
2801
+ this.$input.prop('required', !invalid);
2802
+ },
2803
+
2804
+ /**
2805
+ * Updates all state-dependent CSS classes.
2806
+ */
2807
+ refreshClasses: function() {
2808
+ var self = this;
2809
+ var isFull = self.isFull();
2810
+ var isLocked = self.isLocked;
2811
+
2812
+ self.$wrapper
2813
+ .toggleClass('rtl', self.rtl);
2814
+
2815
+ self.$control
2816
+ .toggleClass('focus', self.isFocused)
2817
+ .toggleClass('disabled', self.isDisabled)
2818
+ .toggleClass('required', self.isRequired)
2819
+ .toggleClass('invalid', self.isInvalid)
2820
+ .toggleClass('locked', isLocked)
2821
+ .toggleClass('full', isFull).toggleClass('not-full', !isFull)
2822
+ .toggleClass('input-active', self.isFocused && !self.isInputHidden)
2823
+ .toggleClass('dropdown-active', self.isOpen)
2824
+ .toggleClass('has-options', !$.isEmptyObject(self.options))
2825
+ .toggleClass('has-items', self.items.length > 0);
2826
+
2827
+ self.$control_input.data('grow', !isFull && !isLocked);
2828
+ },
2829
+
2830
+ /**
2831
+ * Determines whether or not more items can be added
2832
+ * to the control without exceeding the user-defined maximum.
2833
+ *
2834
+ * @returns {boolean}
2835
+ */
2836
+ isFull: function() {
2837
+ return this.settings.maxItems !== null && this.items.length >= this.settings.maxItems;
2838
+ },
2839
+
2840
+ /**
2841
+ * Refreshes the original <select> or <input>
2842
+ * element to reflect the current state.
2843
+ */
2844
+ updateOriginalInput: function(opts) {
2845
+ var i, n, options, label, self = this;
2846
+ opts = opts || {};
2847
+
2848
+ if (self.tagType === TAG_SELECT) {
2849
+ options = [];
2850
+ for (i = 0, n = self.items.length; i < n; i++) {
2851
+ label = self.options[self.items[i]][self.settings.labelField] || '';
2852
+ options.push('<option value="' + escape_html(self.items[i]) + '" selected="selected">' + escape_html(label) + '</option>');
2853
+ }
2854
+ if (!options.length && !this.$input.attr('multiple')) {
2855
+ options.push('<option value="" selected="selected"></option>');
2856
+ }
2857
+ self.$input.html(options.join(''));
2858
+ } else {
2859
+ self.$input.val(self.getValue());
2860
+ self.$input.attr('value',self.$input.val());
2861
+ }
2862
+
2863
+ if (self.isSetup) {
2864
+ if (!opts.silent) {
2865
+ self.trigger('change', self.$input.val());
2866
+ }
2867
+ }
2868
+ },
2869
+
2870
+ /**
2871
+ * Shows/hide the input placeholder depending
2872
+ * on if there items in the list already.
2873
+ */
2874
+ updatePlaceholder: function() {
2875
+ if (!this.settings.placeholder) return;
2876
+ var $input = this.$control_input;
2877
+
2878
+ if (this.items.length) {
2879
+ $input.removeAttr('placeholder');
2880
+ } else {
2881
+ $input.attr('placeholder', this.settings.placeholder);
2882
+ }
2883
+ $input.triggerHandler('update', {force: true});
2884
+ },
2885
+
2886
+ /**
2887
+ * Shows the autocomplete dropdown containing
2888
+ * the available options.
2889
+ */
2890
+ open: function() {
2891
+ var self = this;
2892
+
2893
+ if (self.isLocked || self.isOpen || (self.settings.mode === 'multi' && self.isFull())) return;
2894
+ self.focus();
2895
+ self.isOpen = true;
2896
+ self.refreshState();
2897
+ self.$dropdown.css({visibility: 'hidden', display: 'block'});
2898
+ self.positionDropdown();
2899
+ self.$dropdown.css({visibility: 'visible'});
2900
+ self.trigger('dropdown_open', self.$dropdown);
2901
+ },
2902
+
2903
+ /**
2904
+ * Closes the autocomplete dropdown menu.
2905
+ */
2906
+ close: function() {
2907
+ var self = this;
2908
+ var trigger = self.isOpen;
2909
+
2910
+ if (self.settings.mode === 'single' && self.items.length) {
2911
+ self.hideInput();
2912
+
2913
+ // Do not trigger blur while inside a blur event,
2914
+ // this fixes some weird tabbing behavior in FF and IE.
2915
+ // See #1164
2916
+ if (!self.isBlurring) {
2917
+ self.$control_input.blur(); // close keyboard on iOS
2918
+ }
2919
+ }
2920
+
2921
+ self.isOpen = false;
2922
+ self.$dropdown.hide();
2923
+ self.setActiveOption(null);
2924
+ self.refreshState();
2925
+
2926
+ if (trigger) self.trigger('dropdown_close', self.$dropdown);
2927
+ },
2928
+
2929
+ /**
2930
+ * Calculates and applies the appropriate
2931
+ * position of the dropdown.
2932
+ */
2933
+ positionDropdown: function() {
2934
+ var $control = this.$control;
2935
+ var offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position();
2936
+ offset.top += $control.outerHeight(true);
2937
+
2938
+ this.$dropdown.css({
2939
+ width : $control[0].getBoundingClientRect().width,
2940
+ top : offset.top,
2941
+ left : offset.left
2942
+ });
2943
+ },
2944
+
2945
+ /**
2946
+ * Resets / clears all selected items
2947
+ * from the control.
2948
+ *
2949
+ * @param {boolean} silent
2950
+ */
2951
+ clear: function(silent) {
2952
+ var self = this;
2953
+
2954
+ if (!self.items.length) return;
2955
+ self.$control.children(':not(input)').remove();
2956
+ self.items = [];
2957
+ self.lastQuery = null;
2958
+ self.setCaret(0);
2959
+ self.setActiveItem(null);
2960
+ self.updatePlaceholder();
2961
+ self.updateOriginalInput({silent: silent});
2962
+ self.refreshState();
2963
+ self.showInput();
2964
+ self.trigger('clear');
2965
+ },
2966
+
2967
+ /**
2968
+ * A helper method for inserting an element
2969
+ * at the current caret position.
2970
+ *
2971
+ * @param {object} $el
2972
+ */
2973
+ insertAtCaret: function($el) {
2974
+ var caret = Math.min(this.caretPos, this.items.length);
2975
+ var el = $el[0];
2976
+ var target = this.buffer || this.$control[0];
2977
+
2978
+ if (caret === 0) {
2979
+ target.insertBefore(el, target.firstChild);
2980
+ } else {
2981
+ target.insertBefore(el, target.childNodes[caret]);
2982
+ }
2983
+
2984
+ this.setCaret(caret + 1);
2985
+ },
2986
+
2987
+ /**
2988
+ * Removes the current selected item(s).
2989
+ *
2990
+ * @param {object} e (optional)
2991
+ * @returns {boolean}
2992
+ */
2993
+ deleteSelection: function(e) {
2994
+ var i, n, direction, selection, values, caret, option_select, $option_select, $tail;
2995
+ var self = this;
2996
+
2997
+ direction = (e && e.keyCode === KEY_BACKSPACE) ? -1 : 1;
2998
+ selection = getSelection(self.$control_input[0]);
2999
+
3000
+ if (self.$activeOption && !self.settings.hideSelected) {
3001
+ option_select = self.getAdjacentOption(self.$activeOption, -1).attr('data-value');
3002
+ }
3003
+
3004
+ // determine items that will be removed
3005
+ values = [];
3006
+
3007
+ if (self.$activeItems.length) {
3008
+ $tail = self.$control.children('.active:' + (direction > 0 ? 'last' : 'first'));
3009
+ caret = self.$control.children(':not(input)').index($tail);
3010
+ if (direction > 0) { caret++; }
3011
+
3012
+ for (i = 0, n = self.$activeItems.length; i < n; i++) {
3013
+ values.push($(self.$activeItems[i]).attr('data-value'));
3014
+ }
3015
+ if (e) {
3016
+ e.preventDefault();
3017
+ e.stopPropagation();
3018
+ }
3019
+ } else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) {
3020
+ if (direction < 0 && selection.start === 0 && selection.length === 0) {
3021
+ values.push(self.items[self.caretPos - 1]);
3022
+ } else if (direction > 0 && selection.start === self.$control_input.val().length) {
3023
+ values.push(self.items[self.caretPos]);
3024
+ }
3025
+ }
3026
+
3027
+ // allow the callback to abort
3028
+ if (!values.length || (typeof self.settings.onDelete === 'function' && self.settings.onDelete.apply(self, [values]) === false)) {
3029
+ return false;
3030
+ }
3031
+
3032
+ // perform removal
3033
+ if (typeof caret !== 'undefined') {
3034
+ self.setCaret(caret);
3035
+ }
3036
+ while (values.length) {
3037
+ self.removeItem(values.pop());
3038
+ }
3039
+
3040
+ self.showInput();
3041
+ self.positionDropdown();
3042
+ self.refreshOptions(true);
3043
+
3044
+ // select previous option
3045
+ if (option_select) {
3046
+ $option_select = self.getOption(option_select);
3047
+ if ($option_select.length) {
3048
+ self.setActiveOption($option_select);
3049
+ }
3050
+ }
3051
+
3052
+ return true;
3053
+ },
3054
+
3055
+ /**
3056
+ * Selects the previous / next item (depending
3057
+ * on the `direction` argument).
3058
+ *
3059
+ * > 0 - right
3060
+ * < 0 - left
3061
+ *
3062
+ * @param {int} direction
3063
+ * @param {object} e (optional)
3064
+ */
3065
+ advanceSelection: function(direction, e) {
3066
+ var tail, selection, idx, valueLength, cursorAtEdge, $tail;
3067
+ var self = this;
3068
+
3069
+ if (direction === 0) return;
3070
+ if (self.rtl) direction *= -1;
3071
+
3072
+ tail = direction > 0 ? 'last' : 'first';
3073
+ selection = getSelection(self.$control_input[0]);
3074
+
3075
+ if (self.isFocused && !self.isInputHidden) {
3076
+ valueLength = self.$control_input.val().length;
3077
+ cursorAtEdge = direction < 0
3078
+ ? selection.start === 0 && selection.length === 0
3079
+ : selection.start === valueLength;
3080
+
3081
+ if (cursorAtEdge && !valueLength) {
3082
+ self.advanceCaret(direction, e);
3083
+ }
3084
+ } else {
3085
+ $tail = self.$control.children('.active:' + tail);
3086
+ if ($tail.length) {
3087
+ idx = self.$control.children(':not(input)').index($tail);
3088
+ self.setActiveItem(null);
3089
+ self.setCaret(direction > 0 ? idx + 1 : idx);
3090
+ }
3091
+ }
3092
+ },
3093
+
3094
+ /**
3095
+ * Moves the caret left / right.
3096
+ *
3097
+ * @param {int} direction
3098
+ * @param {object} e (optional)
3099
+ */
3100
+ advanceCaret: function(direction, e) {
3101
+ var self = this, fn, $adj;
3102
+
3103
+ if (direction === 0) return;
3104
+
3105
+ fn = direction > 0 ? 'next' : 'prev';
3106
+ if (self.isShiftDown) {
3107
+ $adj = self.$control_input[fn]();
3108
+ if ($adj.length) {
3109
+ self.hideInput();
3110
+ self.setActiveItem($adj);
3111
+ e && e.preventDefault();
3112
+ }
3113
+ } else {
3114
+ self.setCaret(self.caretPos + direction);
3115
+ }
3116
+ },
3117
+
3118
+ /**
3119
+ * Moves the caret to the specified index.
3120
+ *
3121
+ * @param {int} i
3122
+ */
3123
+ setCaret: function(i) {
3124
+ var self = this;
3125
+
3126
+ if (self.settings.mode === 'single') {
3127
+ i = self.items.length;
3128
+ } else {
3129
+ i = Math.max(0, Math.min(self.items.length, i));
3130
+ }
3131
+
3132
+ if(!self.isPending) {
3133
+ // the input must be moved by leaving it in place and moving the
3134
+ // siblings, due to the fact that focus cannot be restored once lost
3135
+ // on mobile webkit devices
3136
+ var j, n, fn, $children, $child;
3137
+ $children = self.$control.children(':not(input)');
3138
+ for (j = 0, n = $children.length; j < n; j++) {
3139
+ $child = $($children[j]).detach();
3140
+ if (j < i) {
3141
+ self.$control_input.before($child);
3142
+ } else {
3143
+ self.$control.append($child);
3144
+ }
3145
+ }
3146
+ }
3147
+
3148
+ self.caretPos = i;
3149
+ },
3150
+
3151
+ /**
3152
+ * Disables user input on the control. Used while
3153
+ * items are being asynchronously created.
3154
+ */
3155
+ lock: function() {
3156
+ this.close();
3157
+ this.isLocked = true;
3158
+ this.refreshState();
3159
+ },
3160
+
3161
+ /**
3162
+ * Re-enables user input on the control.
3163
+ */
3164
+ unlock: function() {
3165
+ this.isLocked = false;
3166
+ this.refreshState();
3167
+ },
3168
+
3169
+ /**
3170
+ * Disables user input on the control completely.
3171
+ * While disabled, it cannot receive focus.
3172
+ */
3173
+ disable: function() {
3174
+ var self = this;
3175
+ self.$input.prop('disabled', true);
3176
+ self.$control_input.prop('disabled', true).prop('tabindex', -1);
3177
+ self.isDisabled = true;
3178
+ self.lock();
3179
+ },
3180
+
3181
+ /**
3182
+ * Enables the control so that it can respond
3183
+ * to focus and user input.
3184
+ */
3185
+ enable: function() {
3186
+ var self = this;
3187
+ self.$input.prop('disabled', false);
3188
+ self.$control_input.prop('disabled', false).prop('tabindex', self.tabIndex);
3189
+ self.isDisabled = false;
3190
+ self.unlock();
3191
+ },
3192
+
3193
+ /**
3194
+ * Completely destroys the control and
3195
+ * unbinds all event listeners so that it can
3196
+ * be garbage collected.
3197
+ */
3198
+ destroy: function() {
3199
+ var self = this;
3200
+ var eventNS = self.eventNS;
3201
+ var revertSettings = self.revertSettings;
3202
+
3203
+ self.trigger('destroy');
3204
+ self.off();
3205
+ self.$wrapper.remove();
3206
+ self.$dropdown.remove();
3207
+
3208
+ self.$input
3209
+ .html('')
3210
+ .append(revertSettings.$children)
3211
+ .removeAttr('tabindex')
3212
+ .removeClass('selectized')
3213
+ .attr({tabindex: revertSettings.tabindex})
3214
+ .show();
3215
+
3216
+ self.$control_input.removeData('grow');
3217
+ self.$input.removeData('selectize');
3218
+
3219
+ if (--Selectize.count == 0 && Selectize.$testInput) {
3220
+ Selectize.$testInput.remove();
3221
+ Selectize.$testInput = undefined;
3222
+ }
3223
+
3224
+ $(window).off(eventNS);
3225
+ $(document).off(eventNS);
3226
+ $(document.body).off(eventNS);
3227
+
3228
+ delete self.$input[0].selectize;
3229
+ },
3230
+
3231
+ /**
3232
+ * A helper method for rendering "item" and
3233
+ * "option" templates, given the data.
3234
+ *
3235
+ * @param {string} templateName
3236
+ * @param {object} data
3237
+ * @returns {string}
3238
+ */
3239
+ render: function(templateName, data) {
3240
+ var value, id, label;
3241
+ var html = '';
3242
+ var cache = false;
3243
+ var self = this;
3244
+ var regex_tag = /^[\t \r\n]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i;
3245
+
3246
+ if (templateName === 'option' || templateName === 'item') {
3247
+ value = hash_key(data[self.settings.valueField]);
3248
+ cache = !!value;
3249
+ }
3250
+
3251
+ // pull markup from cache if it exists
3252
+ if (cache) {
3253
+ if (!isset(self.renderCache[templateName])) {
3254
+ self.renderCache[templateName] = {};
3255
+ }
3256
+ if (self.renderCache[templateName].hasOwnProperty(value)) {
3257
+ return self.renderCache[templateName][value];
3258
+ }
3259
+ }
3260
+
3261
+ // render markup
3262
+ html = $(self.settings.render[templateName].apply(this, [data, escape_html]));
3263
+
3264
+ // add mandatory attributes
3265
+ if (templateName === 'option' || templateName === 'option_create') {
3266
+ if (!data[self.settings.disabledField]) {
3267
+ html.attr('data-selectable', '');
3268
+ }
3269
+ }
3270
+ else if (templateName === 'optgroup') {
3271
+ id = data[self.settings.optgroupValueField] || '';
3272
+ html.attr('data-group', id);
3273
+ if(data[self.settings.disabledField]) {
3274
+ html.attr('data-disabled', '');
3275
+ }
3276
+ }
3277
+ if (templateName === 'option' || templateName === 'item') {
3278
+ html.attr('data-value', value || '');
3279
+ }
3280
+
3281
+ // update cache
3282
+ if (cache) {
3283
+ self.renderCache[templateName][value] = html[0];
3284
+ }
3285
+
3286
+ return html[0];
3287
+ },
3288
+
3289
+ /**
3290
+ * Clears the render cache for a template. If
3291
+ * no template is given, clears all render
3292
+ * caches.
3293
+ *
3294
+ * @param {string} templateName
3295
+ */
3296
+ clearCache: function(templateName) {
3297
+ var self = this;
3298
+ if (typeof templateName === 'undefined') {
3299
+ self.renderCache = {};
3300
+ } else {
3301
+ delete self.renderCache[templateName];
3302
+ }
3303
+ },
3304
+
3305
+ /**
3306
+ * Determines whether or not to display the
3307
+ * create item prompt, given a user input.
3308
+ *
3309
+ * @param {string} input
3310
+ * @return {boolean}
3311
+ */
3312
+ canCreate: function(input) {
3313
+ var self = this;
3314
+ if (!self.settings.create) return false;
3315
+ var filter = self.settings.createFilter;
3316
+ return input.length
3317
+ && (typeof filter !== 'function' || filter.apply(self, [input]))
3318
+ && (typeof filter !== 'string' || new RegExp(filter).test(input))
3319
+ && (!(filter instanceof RegExp) || filter.test(input));
3320
+ }
3321
+
3322
+ });
3323
+
3324
+
3325
+ Selectize.count = 0;
3326
+ Selectize.defaults = {
3327
+ options: [],
3328
+ optgroups: [],
3329
+
3330
+ plugins: [],
3331
+ delimiter: ',',
3332
+ splitOn: null, // regexp or string for splitting up values from a paste command
3333
+ persist: true,
3334
+ diacritics: true,
3335
+ create: false,
3336
+ createOnBlur: false,
3337
+ createFilter: null,
3338
+ highlight: true,
3339
+ openOnFocus: true,
3340
+ maxOptions: 1000,
3341
+ maxItems: null,
3342
+ hideSelected: null,
3343
+ addPrecedence: false,
3344
+ selectOnTab: false,
3345
+ preload: false,
3346
+ allowEmptyOption: false,
3347
+ closeAfterSelect: false,
3348
+
3349
+ scrollDuration: 60,
3350
+ loadThrottle: 300,
3351
+ loadingClass: 'loading',
3352
+
3353
+ dataAttr: 'data-data',
3354
+ optgroupField: 'optgroup',
3355
+ valueField: 'value',
3356
+ labelField: 'text',
3357
+ disabledField: 'disabled',
3358
+ optgroupLabelField: 'label',
3359
+ optgroupValueField: 'value',
3360
+ lockOptgroupOrder: false,
3361
+
3362
+ sortField: '$order',
3363
+ searchField: ['text'],
3364
+ searchConjunction: 'and',
3365
+
3366
+ mode: null,
3367
+ wrapperClass: 'selectize-control',
3368
+ inputClass: 'selectize-input',
3369
+ dropdownClass: 'selectize-dropdown',
3370
+ dropdownContentClass: 'selectize-dropdown-content',
3371
+
3372
+ dropdownParent: null,
3373
+
3374
+ copyClassesToDropdown: true,
3375
+
3376
+ /*
3377
+ load : null, // function(query, callback) { ... }
3378
+ score : null, // function(search) { ... }
3379
+ onInitialize : null, // function() { ... }
3380
+ onChange : null, // function(value) { ... }
3381
+ onItemAdd : null, // function(value, $item) { ... }
3382
+ onItemRemove : null, // function(value) { ... }
3383
+ onClear : null, // function() { ... }
3384
+ onOptionAdd : null, // function(value, data) { ... }
3385
+ onOptionRemove : null, // function(value) { ... }
3386
+ onOptionClear : null, // function() { ... }
3387
+ onOptionGroupAdd : null, // function(id, data) { ... }
3388
+ onOptionGroupRemove : null, // function(id) { ... }
3389
+ onOptionGroupClear : null, // function() { ... }
3390
+ onDropdownOpen : null, // function($dropdown) { ... }
3391
+ onDropdownClose : null, // function($dropdown) { ... }
3392
+ onType : null, // function(str) { ... }
3393
+ onDelete : null, // function(values) { ... }
3394
+ */
3395
+
3396
+ render: {
3397
+ /*
3398
+ item: null,
3399
+ optgroup: null,
3400
+ optgroup_header: null,
3401
+ option: null,
3402
+ option_create: null
3403
+ */
3404
+ }
3405
+ };
3406
+
3407
+
3408
+ $.fn.selectize = function(settings_user) {
3409
+ var defaults = $.fn.selectize.defaults;
3410
+ var settings = $.extend({}, defaults, settings_user);
3411
+ var attr_data = settings.dataAttr;
3412
+ var field_label = settings.labelField;
3413
+ var field_value = settings.valueField;
3414
+ var field_disabled = settings.disabledField;
3415
+ var field_optgroup = settings.optgroupField;
3416
+ var field_optgroup_label = settings.optgroupLabelField;
3417
+ var field_optgroup_value = settings.optgroupValueField;
3418
+
3419
+ /**
3420
+ * Initializes selectize from a <input type="text"> element.
3421
+ *
3422
+ * @param {object} $input
3423
+ * @param {object} settings_element
3424
+ */
3425
+ var init_textbox = function($input, settings_element) {
3426
+ var i, n, values, option;
3427
+
3428
+ var data_raw = $input.attr(attr_data);
3429
+
3430
+ if (!data_raw) {
3431
+ var value = $.trim($input.val() || '');
3432
+ if (!settings.allowEmptyOption && !value.length) return;
3433
+ values = value.split(settings.delimiter);
3434
+ for (i = 0, n = values.length; i < n; i++) {
3435
+ option = {};
3436
+ option[field_label] = values[i];
3437
+ option[field_value] = values[i];
3438
+ settings_element.options.push(option);
3439
+ }
3440
+ settings_element.items = values;
3441
+ } else {
3442
+ settings_element.options = JSON.parse(data_raw);
3443
+ for (i = 0, n = settings_element.options.length; i < n; i++) {
3444
+ settings_element.items.push(settings_element.options[i][field_value]);
3445
+ }
3446
+ }
3447
+ };
3448
+
3449
+ /**
3450
+ * Initializes selectize from a <select> element.
3451
+ *
3452
+ * @param {object} $input
3453
+ * @param {object} settings_element
3454
+ */
3455
+ var init_select = function($input, settings_element) {
3456
+ var i, n, tagName, $children, order = 0;
3457
+ var options = settings_element.options;
3458
+ var optionsMap = {};
3459
+
3460
+ var readData = function($el) {
3461
+ var data = attr_data && $el.attr(attr_data);
3462
+ if (typeof data === 'string' && data.length) {
3463
+ return JSON.parse(data);
3464
+ }
3465
+ return null;
3466
+ };
3467
+
3468
+ var addOption = function($option, group) {
3469
+ $option = $($option);
3470
+
3471
+ var value = hash_key($option.val());
3472
+ if (!value && !settings.allowEmptyOption) return;
3473
+
3474
+ // if the option already exists, it's probably been
3475
+ // duplicated in another optgroup. in this case, push
3476
+ // the current group to the "optgroup" property on the
3477
+ // existing option so that it's rendered in both places.
3478
+ if (optionsMap.hasOwnProperty(value)) {
3479
+ if (group) {
3480
+ var arr = optionsMap[value][field_optgroup];
3481
+ if (!arr) {
3482
+ optionsMap[value][field_optgroup] = group;
3483
+ } else if (!$.isArray(arr)) {
3484
+ optionsMap[value][field_optgroup] = [arr, group];
3485
+ } else {
3486
+ arr.push(group);
3487
+ }
3488
+ }
3489
+ return;
3490
+ }
3491
+
3492
+ var option = readData($option) || {};
3493
+ option[field_label] = option[field_label] || $option.text();
3494
+ option[field_value] = option[field_value] || value;
3495
+ option[field_disabled] = option[field_disabled] || $option.prop('disabled');
3496
+ option[field_optgroup] = option[field_optgroup] || group;
3497
+
3498
+ optionsMap[value] = option;
3499
+ options.push(option);
3500
+
3501
+ if ($option.is(':selected')) {
3502
+ settings_element.items.push(value);
3503
+ }
3504
+ };
3505
+
3506
+ var addGroup = function($optgroup) {
3507
+ var i, n, id, optgroup, $options;
3508
+
3509
+ $optgroup = $($optgroup);
3510
+ id = $optgroup.attr('label');
3511
+
3512
+ if (id) {
3513
+ optgroup = readData($optgroup) || {};
3514
+ optgroup[field_optgroup_label] = id;
3515
+ optgroup[field_optgroup_value] = id;
3516
+ optgroup[field_disabled] = $optgroup.prop('disabled');
3517
+ settings_element.optgroups.push(optgroup);
3518
+ }
3519
+
3520
+ $options = $('option', $optgroup);
3521
+ for (i = 0, n = $options.length; i < n; i++) {
3522
+ addOption($options[i], id);
3523
+ }
3524
+ };
3525
+
3526
+ settings_element.maxItems = $input.attr('multiple') ? null : 1;
3527
+
3528
+ $children = $input.children();
3529
+ for (i = 0, n = $children.length; i < n; i++) {
3530
+ tagName = $children[i].tagName.toLowerCase();
3531
+ if (tagName === 'optgroup') {
3532
+ addGroup($children[i]);
3533
+ } else if (tagName === 'option') {
3534
+ addOption($children[i]);
3535
+ }
3536
+ }
3537
+ };
3538
+
3539
+ return this.each(function() {
3540
+ if (this.selectize) return;
3541
+
3542
+ var instance;
3543
+ var $input = $(this);
3544
+ var tag_name = this.tagName.toLowerCase();
3545
+ var placeholder = $input.attr('placeholder') || $input.attr('data-placeholder');
3546
+ if (!placeholder && !settings.allowEmptyOption) {
3547
+ placeholder = $input.children('option[value=""]').text();
3548
+ }
3549
+
3550
+ var settings_element = {
3551
+ 'placeholder' : placeholder,
3552
+ 'options' : [],
3553
+ 'optgroups' : [],
3554
+ 'items' : []
3555
+ };
3556
+
3557
+ if (tag_name === 'select') {
3558
+ init_select($input, settings_element);
3559
+ } else {
3560
+ init_textbox($input, settings_element);
3561
+ }
3562
+
3563
+ instance = new Selectize($input, $.extend(true, {}, defaults, settings_element, settings_user));
3564
+ });
3565
+ };
3566
+
3567
+ $.fn.selectize.defaults = Selectize.defaults;
3568
+ $.fn.selectize.support = {
3569
+ validity: SUPPORTS_VALIDITY_API
3570
+ };
3571
+
3572
+
3573
+ Selectize.define('drag_drop', function(options) {
3574
+ if (!$.fn.sortable) throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".');
3575
+ if (this.settings.mode !== 'multi') return;
3576
+ var self = this;
3577
+
3578
+ self.lock = (function() {
3579
+ var original = self.lock;
3580
+ return function() {
3581
+ var sortable = self.$control.data('sortable');
3582
+ if (sortable) sortable.disable();
3583
+ return original.apply(self, arguments);
3584
+ };
3585
+ })();
3586
+
3587
+ self.unlock = (function() {
3588
+ var original = self.unlock;
3589
+ return function() {
3590
+ var sortable = self.$control.data('sortable');
3591
+ if (sortable) sortable.enable();
3592
+ return original.apply(self, arguments);
3593
+ };
3594
+ })();
3595
+
3596
+ self.setup = (function() {
3597
+ var original = self.setup;
3598
+ return function() {
3599
+ original.apply(this, arguments);
3600
+
3601
+ var $control = self.$control.sortable({
3602
+ items: '[data-value]',
3603
+ forcePlaceholderSize: true,
3604
+ disabled: self.isLocked,
3605
+ start: function(e, ui) {
3606
+ ui.placeholder.css('width', ui.helper.css('width'));
3607
+ $control.css({overflow: 'visible'});
3608
+ },
3609
+ stop: function() {
3610
+ $control.css({overflow: 'hidden'});
3611
+ var active = self.$activeItems ? self.$activeItems.slice() : null;
3612
+ var values = [];
3613
+ $control.children('[data-value]').each(function() {
3614
+ values.push($(this).attr('data-value'));
3615
+ });
3616
+ self.setValue(values);
3617
+ self.setActiveItem(active);
3618
+ }
3619
+ });
3620
+ };
3621
+ })();
3622
+
3623
+ });
3624
+
3625
+ Selectize.define('dropdown_header', function(options) {
3626
+ var self = this;
3627
+
3628
+ options = $.extend({
3629
+ title : 'Untitled',
3630
+ headerClass : 'selectize-dropdown-header',
3631
+ titleRowClass : 'selectize-dropdown-header-title',
3632
+ labelClass : 'selectize-dropdown-header-label',
3633
+ closeClass : 'selectize-dropdown-header-close',
3634
+
3635
+ html: function(data) {
3636
+ return (
3637
+ '<div class="' + data.headerClass + '">' +
3638
+ '<div class="' + data.titleRowClass + '">' +
3639
+ '<span class="' + data.labelClass + '">' + data.title + '</span>' +
3640
+ '<a href="javascript:void(0)" class="' + data.closeClass + '">&times;</a>' +
3641
+ '</div>' +
3642
+ '</div>'
3643
+ );
3644
+ }
3645
+ }, options);
3646
+
3647
+ self.setup = (function() {
3648
+ var original = self.setup;
3649
+ return function() {
3650
+ original.apply(self, arguments);
3651
+ self.$dropdown_header = $(options.html(options));
3652
+ self.$dropdown.prepend(self.$dropdown_header);
3653
+ };
3654
+ })();
3655
+
3656
+ });
3657
+
3658
+ Selectize.define('optgroup_columns', function(options) {
3659
+ var self = this;
3660
+
3661
+ options = $.extend({
3662
+ equalizeWidth : true,
3663
+ equalizeHeight : true
3664
+ }, options);
3665
+
3666
+ this.getAdjacentOption = function($option, direction) {
3667
+ var $options = $option.closest('[data-group]').find('[data-selectable]');
3668
+ var index = $options.index($option) + direction;
3669
+
3670
+ return index >= 0 && index < $options.length ? $options.eq(index) : $();
3671
+ };
3672
+
3673
+ this.onKeyDown = (function() {
3674
+ var original = self.onKeyDown;
3675
+ return function(e) {
3676
+ var index, $option, $options, $optgroup;
3677
+
3678
+ if (this.isOpen && (e.keyCode === KEY_LEFT || e.keyCode === KEY_RIGHT)) {
3679
+ self.ignoreHover = true;
3680
+ $optgroup = this.$activeOption.closest('[data-group]');
3681
+ index = $optgroup.find('[data-selectable]').index(this.$activeOption);
3682
+
3683
+ if(e.keyCode === KEY_LEFT) {
3684
+ $optgroup = $optgroup.prev('[data-group]');
3685
+ } else {
3686
+ $optgroup = $optgroup.next('[data-group]');
3687
+ }
3688
+
3689
+ $options = $optgroup.find('[data-selectable]');
3690
+ $option = $options.eq(Math.min($options.length - 1, index));
3691
+ if ($option.length) {
3692
+ this.setActiveOption($option);
3693
+ }
3694
+ return;
3695
+ }
3696
+
3697
+ return original.apply(this, arguments);
3698
+ };
3699
+ })();
3700
+
3701
+ var getScrollbarWidth = function() {
3702
+ var div;
3703
+ var width = getScrollbarWidth.width;
3704
+ var doc = document;
3705
+
3706
+ if (typeof width === 'undefined') {
3707
+ div = doc.createElement('div');
3708
+ div.innerHTML = '<div style="width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;"><div style="width:1px;height:100px;"></div></div>';
3709
+ div = div.firstChild;
3710
+ doc.body.appendChild(div);
3711
+ width = getScrollbarWidth.width = div.offsetWidth - div.clientWidth;
3712
+ doc.body.removeChild(div);
3713
+ }
3714
+ return width;
3715
+ };
3716
+
3717
+ var equalizeSizes = function() {
3718
+ var i, n, height_max, width, width_last, width_parent, $optgroups;
3719
+
3720
+ $optgroups = $('[data-group]', self.$dropdown_content);
3721
+ n = $optgroups.length;
3722
+ if (!n || !self.$dropdown_content.width()) return;
3723
+
3724
+ if (options.equalizeHeight) {
3725
+ height_max = 0;
3726
+ for (i = 0; i < n; i++) {
3727
+ height_max = Math.max(height_max, $optgroups.eq(i).height());
3728
+ }
3729
+ $optgroups.css({height: height_max});
3730
+ }
3731
+
3732
+ if (options.equalizeWidth) {
3733
+ width_parent = self.$dropdown_content.innerWidth() - getScrollbarWidth();
3734
+ width = Math.round(width_parent / n);
3735
+ $optgroups.css({width: width});
3736
+ if (n > 1) {
3737
+ width_last = width_parent - width * (n - 1);
3738
+ $optgroups.eq(n - 1).css({width: width_last});
3739
+ }
3740
+ }
3741
+ };
3742
+
3743
+ if (options.equalizeHeight || options.equalizeWidth) {
3744
+ hook.after(this, 'positionDropdown', equalizeSizes);
3745
+ hook.after(this, 'refreshOptions', equalizeSizes);
3746
+ }
3747
+
3748
+
3749
+ });
3750
+
3751
+ Selectize.define('remove_button', function(options) {
3752
+ options = $.extend({
3753
+ label : '&times;',
3754
+ title : 'Remove',
3755
+ className : 'remove',
3756
+ append : true
3757
+ }, options);
3758
+
3759
+ var singleClose = function(thisRef, options) {
3760
+
3761
+ options.className = 'remove-single';
3762
+
3763
+ var self = thisRef;
3764
+ var html = '<a href="javascript:void(0)" class="' + options.className + '" tabindex="-1" title="' + escape_html(options.title) + '">' + options.label + '</a>';
3765
+
3766
+ /**
3767
+ * Appends an element as a child (with raw HTML).
3768
+ *
3769
+ * @param {string} html_container
3770
+ * @param {string} html_element
3771
+ * @return {string}
3772
+ */
3773
+ var append = function(html_container, html_element) {
3774
+ return $('<span>').append(html_container)
3775
+ .append(html_element);
3776
+ };
3777
+
3778
+ thisRef.setup = (function() {
3779
+ var original = self.setup;
3780
+ return function() {
3781
+ // override the item rendering method to add the button to each
3782
+ if (options.append) {
3783
+ var id = $(self.$input.context).attr('id');
3784
+ var selectizer = $('#'+id);
3785
+
3786
+ var render_item = self.settings.render.item;
3787
+ self.settings.render.item = function(data) {
3788
+ return append(render_item.apply(thisRef, arguments), html);
3789
+ };
3790
+ }
3791
+
3792
+ original.apply(thisRef, arguments);
3793
+
3794
+ // add event listener
3795
+ thisRef.$control.on('click', '.' + options.className, function(e) {
3796
+ e.preventDefault();
3797
+ if (self.isLocked) return;
3798
+
3799
+ self.clear();
3800
+ });
3801
+
3802
+ };
3803
+ })();
3804
+ };
3805
+
3806
+ var multiClose = function(thisRef, options) {
3807
+
3808
+ var self = thisRef;
3809
+ var html = '<a href="javascript:void(0)" class="' + options.className + '" tabindex="-1" title="' + escape_html(options.title) + '">' + options.label + '</a>';
3810
+
3811
+ /**
3812
+ * Appends an element as a child (with raw HTML).
3813
+ *
3814
+ * @param {string} html_container
3815
+ * @param {string} html_element
3816
+ * @return {string}
3817
+ */
3818
+ var append = function(html_container, html_element) {
3819
+ var pos = html_container.search(/(<\/[^>]+>\s*)$/);
3820
+ return html_container.substring(0, pos) + html_element + html_container.substring(pos);
3821
+ };
3822
+
3823
+ thisRef.setup = (function() {
3824
+ var original = self.setup;
3825
+ return function() {
3826
+ // override the item rendering method to add the button to each
3827
+ if (options.append) {
3828
+ var render_item = self.settings.render.item;
3829
+ self.settings.render.item = function(data) {
3830
+ return append(render_item.apply(thisRef, arguments), html);
3831
+ };
3832
+ }
3833
+
3834
+ original.apply(thisRef, arguments);
3835
+
3836
+ // add event listener
3837
+ thisRef.$control.on('click', '.' + options.className, function(e) {
3838
+ e.preventDefault();
3839
+ if (self.isLocked) return;
3840
+
3841
+ var $item = $(e.currentTarget).parent();
3842
+ self.setActiveItem($item);
3843
+ if (self.deleteSelection()) {
3844
+ self.setCaret(self.items.length);
3845
+ }
3846
+ });
3847
+
3848
+ };
3849
+ })();
3850
+ };
3851
+
3852
+ if (this.settings.mode === 'single') {
3853
+ singleClose(this, options);
3854
+ return;
3855
+ } else {
3856
+ multiClose(this, options);
3857
+ }
3858
+ });
3859
+
3860
+
3861
+ Selectize.define('restore_on_backspace', function(options) {
3862
+ var self = this;
3863
+
3864
+ options.text = options.text || function(option) {
3865
+ return option[this.settings.labelField];
3866
+ };
3867
+
3868
+ this.onKeyDown = (function() {
3869
+ var original = self.onKeyDown;
3870
+ return function(e) {
3871
+ var index, option;
3872
+ if (e.keyCode === KEY_BACKSPACE && this.$control_input.val() === '' && !this.$activeItems.length) {
3873
+ index = this.caretPos - 1;
3874
+ if (index >= 0 && index < this.items.length) {
3875
+ option = this.options[this.items[index]];
3876
+ if (this.deleteSelection(e)) {
3877
+ this.setTextboxValue(options.text.apply(this, [option]));
3878
+ this.refreshOptions(true);
3879
+ }
3880
+ e.preventDefault();
3881
+ return;
3882
+ }
3883
+ }
3884
+ return original.apply(this, arguments);
3885
+ };
3886
+ })();
3887
+ });
3888
+
3889
+
3890
+ return Selectize;
3891
+ }));
admin/js/strong-testimonials-elementor-editor.js ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+
3
+ elementor.hooks.addAction('panel/open_editor/widget/strong_testimonials_elementor_views', function (panel, model, view) {
4
+
5
+ // Get search input
6
+ search_input = panel.$el.find('select[data-setting="strong_testimonials_views_select"]');
7
+
8
+ var search_input_active = model.attributes.settings.attributes.strong_testimonials_views_select;
9
+
10
+ var search_val, timer, selective_input;
11
+ // Initialize the selectize
12
+ search_input.selectize({
13
+ create: false,
14
+ maxItems: 1,
15
+ closeAfterSelect: false,
16
+
17
+ valueField: 'ID',
18
+ labelField: 'post_title',
19
+ searchField:'post_title',
20
+
21
+ load: function(query, callback) {
22
+ jQuery.ajax({
23
+ url: strongAjax.ajax_url,
24
+ type: 'POST',
25
+ dataType: 'json',
26
+ data: {
27
+ action: 'strong_testimonials_elementor_ajax_search',
28
+ },
29
+ error: function() {
30
+ callback();
31
+ },
32
+ success: function( response ) {
33
+ callback( response.data );
34
+ }
35
+
36
+ });
37
+
38
+ }
39
+ });
40
+
41
+ });
42
+
43
+ });
admin/partials/views/group-compat.php CHANGED
@@ -4,7 +4,7 @@ $then_classes = array(
4
  apply_filters( 'wpmtst_view_section', '', 'compat' ),
5
  );
6
  ?>
7
- <div class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
8
  <h3>
9
  <?php /* translators: On the Views admin screen. */ ?>
10
  <?php _e( 'Compatibility', 'strong-testimonials' ); ?>
4
  apply_filters( 'wpmtst_view_section', '', 'compat' ),
5
  );
6
  ?>
7
+ <div class="<?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>" style="display: none;">
8
  <h3>
9
  <?php /* translators: On the Views admin screen. */ ?>
10
  <?php _e( 'Compatibility', 'strong-testimonials' ); ?>
admin/partials/views/group-extra.php CHANGED
@@ -8,7 +8,7 @@ $then_classes = array(
8
  apply_filters( 'wpmtst_view_section', '', 'extra' ),
9
  );
10
  ?>
11
- <div class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: On the Views admin screen. */ ?>
14
  <?php _e( 'Extra', 'strong-testimonials' ); ?>
8
  apply_filters( 'wpmtst_view_section', '', 'extra' ),
9
  );
10
  ?>
11
+ <div class="<?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: On the Views admin screen. */ ?>
14
  <?php _e( 'Extra', 'strong-testimonials' ); ?>
admin/partials/views/group-fields.php CHANGED
@@ -8,7 +8,7 @@ $then_classes = array(
8
  apply_filters( 'wpmtst_view_section', '', 'fields' ),
9
  );
10
  ?>
11
- <div class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: On the Views admin screen. */ ?>
14
  <?php _e( 'Fields', 'strong-testimonials' ); ?>
@@ -25,7 +25,7 @@ $then_classes = array(
25
  apply_filters( 'wpmtst_view_section', '', 'title' ),
26
  );
27
  ?>
28
- <tr class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
29
  <?php include( 'option-title.php' ); ?>
30
  </tr>
31
 
@@ -39,7 +39,7 @@ $then_classes = array(
39
  apply_filters( 'wpmtst_view_section', '', 'thumbnail' ),
40
  );
41
  ?>
42
- <tr class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
43
  <?php include( 'option-thumbnail.php' ); ?>
44
  </tr>
45
 
@@ -53,7 +53,7 @@ $then_classes = array(
53
  apply_filters( 'wpmtst_view_section', '', 'content' ),
54
  );
55
  ?>
56
- <tr class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
57
  <?php include( 'option-content.php' ); ?>
58
  </tr>
59
 
@@ -67,7 +67,7 @@ $then_classes = array(
67
  apply_filters( 'wpmtst_view_section', '', 'client-section' ),
68
  );
69
  ?>
70
- <tr class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
71
  <?php include( 'option-client-section.php' ); ?>
72
  </tr>
73
 
8
  apply_filters( 'wpmtst_view_section', '', 'fields' ),
9
  );
10
  ?>
11
+ <div class="<?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: On the Views admin screen. */ ?>
14
  <?php _e( 'Fields', 'strong-testimonials' ); ?>
25
  apply_filters( 'wpmtst_view_section', '', 'title' ),
26
  );
27
  ?>
28
+ <tr class="<?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>" style="display: none;">
29
  <?php include( 'option-title.php' ); ?>
30
  </tr>
31
 
39
  apply_filters( 'wpmtst_view_section', '', 'thumbnail' ),
40
  );
41
  ?>
42
+ <tr class="<?php echo esc_attr( implode( ' ' ,array_filter( $then_classes ) ) ); ?>" style="display: none;">
43
  <?php include( 'option-thumbnail.php' ); ?>
44
  </tr>
45
 
53
  apply_filters( 'wpmtst_view_section', '', 'content' ),
54
  );
55
  ?>
56
+ <tr class="<?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>" style="display: none;">
57
  <?php include( 'option-content.php' ); ?>
58
  </tr>
59
 
67
  apply_filters( 'wpmtst_view_section', '', 'client-section' ),
68
  );
69
  ?>
70
+ <tr class="<?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>" style="display: none;">
71
  <?php include( 'option-client-section.php' ); ?>
72
  </tr>
73
 
admin/partials/views/group-form.php CHANGED
@@ -8,7 +8,7 @@ $then_classes = array(
8
  apply_filters( 'wpmtst_view_section', '', 'form' ),
9
  );
10
  ?>
11
- <div class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: On the Views admin screen. */ ?>
14
  <?php _e( 'Actions', 'strong-testimonials' ); ?>
8
  apply_filters( 'wpmtst_view_section', '', 'form' ),
9
  );
10
  ?>
11
+ <div class="<?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: On the Views admin screen. */ ?>
14
  <?php _e( 'Actions', 'strong-testimonials' ); ?>
admin/partials/views/group-query.php CHANGED
@@ -8,7 +8,7 @@ $then_classes = array(
8
  apply_filters( 'wpmtst_view_section', '', 'select' ),
9
  );
10
  ?>
11
- <div class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: On the Views admin screen. */ ?>
14
  <?php _e( 'Query', 'strong-testimonials' ); ?>
8
  apply_filters( 'wpmtst_view_section', '', 'select' ),
9
  );
10
  ?>
11
+ <div class="<?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: On the Views admin screen. */ ?>
14
  <?php _e( 'Query', 'strong-testimonials' ); ?>
admin/partials/views/group-slideshow.php CHANGED
@@ -8,7 +8,7 @@ $then_classes = array(
8
  apply_filters( 'wpmtst_view_section', '', 'slideshow' ),
9
  );
10
  ?>
11
- <div class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: In the view editor. */ ?>
14
  <?php _e( 'Slideshow', 'strong-testimonials' ); ?>
8
  apply_filters( 'wpmtst_view_section', '', 'slideshow' ),
9
  );
10
  ?>
11
+ <div class="<?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: In the view editor. */ ?>
14
  <?php _e( 'Slideshow', 'strong-testimonials' ); ?>
admin/partials/views/group-style.php CHANGED
@@ -8,7 +8,7 @@ $then_classes = array(
8
  apply_filters( 'wpmtst_view_section', '', 'style' ),
9
  );
10
  ?>
11
- <div class="<?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: On the Views admin screen. */ ?>
14
  <?php _e( 'Style', 'strong-testimonials' ); ?>
8
  apply_filters( 'wpmtst_view_section', '', 'style' ),
9
  );
10
  ?>
11
+ <div class="<?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>" style="display: none;">
12
  <h3>
13
  <?php /* translators: On the Views admin screen. */ ?>
14
  <?php _e( 'Style', 'strong-testimonials' ); ?>
admin/partials/views/option-title.php CHANGED
@@ -33,7 +33,7 @@ if ( '0' == $view['title_link'] ) {
33
  <?php printf( _x( 'Link to %s', 'The name of this post type. "Testimonial" by default.', 'strong-testimonials' ), strtolower( apply_filters( 'wpmtst_cpt_singular_name', __( 'Testimonial', 'strong-testimonials' ) ) ) ); ?>
34
  </label>
35
  <div class="wpmtst-tooltip"><span>[?]</span>
36
- <div class="wpmtst-tooltip-content"><?php echo esc_html__('"Full testimonial" option doesn\'s work if "Disable permalinks for testimonials" from "Settings" page in enabled.','strong-testimonials'); ?></div>
37
  </div>
38
 
39
  <select name="view[data][title_link]">
33
  <?php printf( _x( 'Link to %s', 'The name of this post type. "Testimonial" by default.', 'strong-testimonials' ), strtolower( apply_filters( 'wpmtst_cpt_singular_name', __( 'Testimonial', 'strong-testimonials' ) ) ) ); ?>
34
  </label>
35
  <div class="wpmtst-tooltip"><span>[?]</span>
36
+ <div class="wpmtst-tooltip-content"><?php echo esc_html__('"Full testimonial" option doesn\'s work if "Disable permalinks for testimonials" from "Settings" page is enabled.','strong-testimonials'); ?></div>
37
  </div>
38
 
39
  <select name="view[data][title_link]">
admin/partials/views/view-shortcode.php CHANGED
@@ -20,7 +20,7 @@ $then_classes = array(
20
  );
21
  ?>
22
 
23
- <div class="table-row form-view-shortcode <?php echo esc_attr( implode( array_filter( $then_classes ), ' ' ) ); ?>">
24
  <div class="table-cell">
25
  <label for="view-shortcode">
26
  <?php _e( 'Shortcode', 'strong-testimonials' ); ?>
20
  );
21
  ?>
22
 
23
+ <div class="table-row form-view-shortcode <?php echo esc_attr( implode( ' ', array_filter( $then_classes ) ) ); ?>">
24
  <div class="table-cell">
25
  <label for="view-shortcode">
26
  <?php _e( 'Shortcode', 'strong-testimonials' ); ?>
admin/settings/partials/general.php CHANGED
@@ -230,21 +230,23 @@ $options = get_option( 'wpmtst_options' );
230
  </fieldset>
231
  </td>
232
  </tr>
233
-
234
- <tr valign="top">
235
- <th scope="row">
236
- <?php _e( 'Lazy Loading', 'strong-testimonials' ); ?>
237
- </th>
238
- <td>
239
- <fieldset>
240
- <label>
241
- <input type="checkbox" name="wpmtst_options[lazyload]" <?php checked( $options['lazyload'] ); ?>>
242
- <?php printf( __( 'Enable the Lazy Loading functionality.', 'strong-testimonials' ) ); ?>
243
- <?php _e( 'Off by default.', 'strong-testimonials' ); ?>
244
- </label>
245
- </fieldset>
246
- </td>
247
- </tr>
 
 
248
 
249
  <?php if ( wpmtst_is_plugin_active( 'lazy-loading-responsive-images' ) ) : ?>
250
  <tr valign="top">
230
  </fieldset>
231
  </td>
232
  </tr>
233
+
234
+ <?php if( !function_exists( 'wp_lazy_loading_enabled' ) || !apply_filters( 'wp_lazy_loading_enabled', true ) ) : ?>
235
+ <tr valign="top">
236
+ <th scope="row">
237
+ <?php _e( 'Lazy Loading', 'strong-testimonials' ); ?>
238
+ </th>
239
+ <td>
240
+ <fieldset>
241
+ <label>
242
+ <input type="checkbox" name="wpmtst_options[lazyload]" <?php checked( $options['lazyload'] ); ?>>
243
+ <?php printf( __( 'Enable the Lazy Loading functionality.', 'strong-testimonials' ) ); ?>
244
+ <?php _e( 'Off by default.', 'strong-testimonials' ); ?>
245
+ </label>
246
+ </fieldset>
247
+ </td>
248
+ </tr>
249
+ <?php endif; ?>
250
 
251
  <?php if ( wpmtst_is_plugin_active( 'lazy-loading-responsive-images' ) ) : ?>
252
  <tr valign="top">
admin/uninstall/class-strong-testimonials-uninstall.php CHANGED
@@ -15,7 +15,9 @@ class Strong_Testimonials_Uninstall {
15
  $this,
16
  'filter_action_links'
17
  ) );
18
- add_action( 'admin_footer-plugins.php', array( $this, 'add_uninstall_form' ), 16 );
 
 
19
  add_action( 'wp_ajax_st_uninstall_plugin', array( $this, 'st_uninstall_plugin' ) );
20
  add_action( 'admin_enqueue_scripts', array( $this, 'uninstall_scripts' ) );
21
  }
15
  $this,
16
  'filter_action_links'
17
  ) );
18
+ if (!is_network_admin()) {
19
+ add_action( 'admin_footer-plugins.php', array( $this, 'add_uninstall_form' ), 16 );
20
+ }
21
  add_action( 'wp_ajax_st_uninstall_plugin', array( $this, 'st_uninstall_plugin' ) );
22
  add_action( 'admin_enqueue_scripts', array( $this, 'uninstall_scripts' ) );
23
  }
assets/css/admin-global.min.css ADDED
@@ -0,0 +1 @@
 
1
+ #menu-posts-wpm-testimonial .wp-submenu-wrap>li:last-child a{color:#FBC556}.wpmtst-tooltip{position:relative;display:inline-block}.wpmtst-tooltip>span{cursor:pointer;color:#0073aa;text-decoration:underline;margin-left:5px}.wpmtst-tooltip .wpmtst-tooltip-content{display:none;position:absolute;top:50%;right:-10px;transform:translate(100% ,-50%);width:300px;background:#000;color:#fff;padding:10px;box-sizing:border-box;z-index:99}.wpmtst-tooltip .wpmtst-tooltip-content:before{content:'';width:0;height:0;border-style:solid;border-width:8px 8px 8px 0;border-color:transparent #000 transparent transparent;display:block;position:absolute;top:50%;left:-8px;transform:translateY(-50%)}.wpmtst-tooltip:hover .wpmtst-tooltip-content{display:block}.wpmtst-inline-block{display:inline-block}.wpmts-settings-columns{display:flex}.wpmts-settings-columns>form{flex-grow:1}.wpmts-settings-columns .wpmtst-settings-upsell{width:25%;padding-left:30px;flex-grow:0;flex-shrink:0}.wpmtst-settings-upsell .wpmtst-alert ul{list-style:none;padding:0;margin:0}.wpmtst-settings-upsell .wpmtst-alert ul li{margin-bottom:10px}.wpmtst-alert{padding:20px;background:#f4daa4;color:#8d6e30;position:relative}span.wpmtst-upsell-badge{font-size:10px;background:#f4daa4;padding:2px 5px;display:inline-block;margin-left:10px;border-radius:10px}
assets/css/admin-welcome.css ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #wpcontent {
2
+ padding-left: 0 !important;
3
+ position: relative;
4
+ }
5
+
6
+ #wpmtst-welcome {
7
+ padding-top: 80px;
8
+ }
9
+
10
+ #wpmtst-welcome *, #wpmtst-welcome *::before, #wpmtst-welcome *::after {
11
+ -webkit-box-sizing: border-box;
12
+ -moz-box-sizing: border-box;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ #wpmtst-welcome .clear:before {
17
+ content: " ";
18
+ display: table;
19
+ }
20
+
21
+ #wpmtst-welcome .clear:after {
22
+ clear: both;
23
+ content: " ";
24
+ display: table;
25
+ }
26
+
27
+ #wpmtst-welcome .container {
28
+ margin: 0 auto;
29
+ max-width: 800px;
30
+ padding: 0;
31
+ }
32
+
33
+ #wpmtst-welcome .block {
34
+ padding: 40px;
35
+ }
36
+
37
+ #wpmtst-welcome img {
38
+ max-width: 100%;
39
+ height: auto;
40
+ }
41
+
42
+ #wpmtst-welcome h1 {
43
+ color: #222;
44
+ font-size: 24px;
45
+ text-align: center;
46
+ margin: 0 0 16px 0;
47
+ }
48
+
49
+ #wpmtst-welcome h5 {
50
+ color: #222;
51
+ font-size: 16px;
52
+ margin: 0 0 8px 0;
53
+ }
54
+
55
+ #wpmtst-welcome h6 {
56
+ font-size: 16px;
57
+ font-weight: 400;
58
+ line-height: 1.6;
59
+ text-align: center;
60
+ margin: 0;
61
+ }
62
+
63
+ #wpmtst-welcome .wpmtst-btn-lg {
64
+ font-size: 16px;
65
+ font-weight: 600;
66
+ padding: 16px 28px;
67
+ }
68
+
69
+ #wpmtst-welcome .wpmtst-btn-block {
70
+ display: block;
71
+ width: 100%;
72
+ }
73
+
74
+ #wpmtst-welcome .wpmtst-btn {
75
+ border: 0;
76
+ border-radius: 40px;
77
+ cursor: pointer;
78
+ display: inline-block;
79
+ margin: 0;
80
+ text-decoration: none;
81
+ text-align: center;
82
+ vertical-align: middle;
83
+ white-space: nowrap;
84
+ box-shadow: none;
85
+ }
86
+
87
+ #wpmtst-welcome .wpmtst-btn-purple {
88
+ background-color: #5D3CE4;
89
+ border-color: #5D3CE4;
90
+ color: #fff;
91
+ }
92
+
93
+ #wpmtst-welcome .wpmtst-btn-orange {
94
+ background-color: #FBC555;
95
+ border-color: #FBC555;
96
+ color: #fff;
97
+ }
98
+
99
+ #wpmtst-welcome .wpmtst-btn-white {
100
+ background-color: #fff;
101
+ border-color: #fff;
102
+ color: #222;
103
+ }
104
+
105
+
106
+ #wpmtst-welcome .button-wrap {
107
+ max-width: 590px;
108
+ margin: 0 auto 0 auto;
109
+ }
110
+
111
+ #wpmtst-welcome .button-wrap .left {
112
+ margin-bottom: 10px;
113
+ }
114
+
115
+ @media screen and (min-width: 720px) {
116
+
117
+ #wpmtst-welcome .button-wrap .left {
118
+ margin-bottom: 0px;
119
+ float: left;
120
+ width: 50%;
121
+ padding-right: 20px;
122
+ }
123
+
124
+ #wpmtst-welcome .button-wrap .right {
125
+ float: right;
126
+ width: 50%;
127
+ padding-left: 20px;
128
+ }
129
+
130
+ }
131
+
132
+
133
+
134
+ /* hero section */
135
+ #wpmtst-welcome .hero {
136
+ background-color: #fff;
137
+ border-radius: 10px;
138
+ margin-bottom: 30px;
139
+ position: relative;
140
+ box-shadow: -3px 2px 70px 0px rgba(128, 144, 174, 0.1);
141
+ padding-top: 40px;
142
+ }
143
+
144
+ #wpmtst-welcome .hero .mascot {
145
+ background-color: #fff;
146
+ border-radius: 50%;
147
+ height: 90px;
148
+ width: 90px;
149
+ padding: 28px 28px;
150
+ position: absolute;
151
+ top: -45px;
152
+ left: 50%;
153
+ margin-left: -45px;
154
+ }
155
+
156
+ #wpmtst-welcome .hero .video-thumbnail {
157
+ display: block;
158
+ margin: 0;
159
+ width: 100%;
160
+ }
161
+
162
+ #wpmtst-welcome .hero .button-wrap {
163
+ margin-top: 20px;
164
+ }
165
+
166
+
167
+ /* features section */
168
+ #wpmtst-welcome .features {
169
+ background-color: #fff;
170
+ border-top-left-radius: 10px;
171
+ border-top-right-radius: 10px;
172
+ position: relative;
173
+ box-shadow: -3px 2px 70px 0px rgba(128, 144, 174, 0.1);
174
+ padding-top: 20px;
175
+ }
176
+
177
+ #wpmtst-welcome .features .feature-list {
178
+ margin-top: 40px;
179
+ }
180
+
181
+ #wpmtst-welcome .features .feature-block {
182
+ margin-bottom: 30px;
183
+ }
184
+
185
+ @media screen and (min-width: 720px) {
186
+
187
+ #wpmtst-welcome .features .feature-block {
188
+ margin-bottom: 0px;
189
+ float: left;
190
+ width: 50%;
191
+ padding-bottom: 32px;
192
+ overflow: auto;
193
+ }
194
+
195
+ #wpmtst-welcome .features .feature-block.first {
196
+ padding-right: 22px;
197
+ clear: both;
198
+ }
199
+
200
+ #wpmtst-welcome .features .feature-block.last {
201
+ padding-left: 22px;
202
+ }
203
+
204
+ }
205
+
206
+ #wpmtst-welcome .features .feature-block img {
207
+ float: left;
208
+ max-width: 44px;
209
+ max-height: 44px;
210
+ }
211
+
212
+ #wpmtst-welcome .features .feature-block h5 {
213
+ margin-left: 70px;
214
+ }
215
+
216
+ #wpmtst-welcome .features .feature-block p {
217
+ margin-left: 70px;
218
+ }
219
+
220
+ #wpmtst-welcome .features .pro-label {
221
+ display: inline-block;
222
+ line-height: 1;
223
+ color: #fff;
224
+ background-color: #5333ED;
225
+ padding: 3px 6px;
226
+ font-size: 0.75rem;
227
+ border-radius: 20px;
228
+ vertical-align: text-top;
229
+ margin-left: 20px;
230
+ }
231
+
232
+ #wpmtst-welcome .features .feature-list a {
233
+ color: #5333ED;
234
+ }
235
+
236
+ /* upgrade section */
237
+ #wpmtst-welcome .upgrade {
238
+ background-color: #5333ED;
239
+ color: rgba(255,255,255,0.7);
240
+ text-align: center;
241
+ }
242
+
243
+ #wpmtst-welcome .upgrade h1{
244
+ color: #fff;
245
+ }
246
+
247
+ @media screen and (min-width: 720px) {
248
+
249
+ #wpmtst-welcome .upgrade .left{
250
+ float: left;
251
+ width: 50%;
252
+ padding-left: 20%;
253
+ margin-bottom: 30px;
254
+ text-align: left;
255
+ }
256
+
257
+ #wpmtst-welcome .upgrade .right{
258
+ float: left;
259
+ width: 50%;
260
+ padding-right: 20%;
261
+ margin-bottom: 30px;
262
+ text-align: left;
263
+ }
264
+
265
+ }
266
+
267
+ /* testimonials section */
268
+ #wpmtst-welcome .testimonials {
269
+ background-color: #fafafa;
270
+
271
+ position: relative;
272
+ box-shadow: -3px 2px 70px 0px rgba(128, 144, 174, 0.1);
273
+ }
274
+
275
+ #wpmtst-welcome .testimonials .testimonial-block {
276
+ padding: 0 16px 32px;
277
+ overflow: auto;
278
+ margin-top: 40px;
279
+ text-align: center;
280
+ }
281
+
282
+ @media screen and (min-width: 720px) {
283
+
284
+ #wpmtst-welcome .testimonials .testimonial-block {
285
+ float: left;
286
+ width: 50%;
287
+ }
288
+
289
+ }
290
+
291
+ #wpmtst-welcome .testimonials .testimonial-block p {
292
+ font-size: 14px;
293
+ font-weight: 400;
294
+ line-height: 1.6;
295
+ }
296
+
297
+ #wpmtst-welcome .testimonials .testimonial-block img {
298
+ max-width: 60px;
299
+ max-height: 60px;
300
+ border-radius: 50%;
301
+ filter: grayscale(100%);
302
+ box-shadow: 0 0 12px rgba(0,0,0,0.15);
303
+ }
304
+
305
+
306
+ #wpmtst-welcome .testimonials .testimonial-block .testimonial-stars {
307
+ width: 94px;
308
+ height: 20px;
309
+ background-repeat: repeat-x;
310
+ background-position: 50% 50%;
311
+ background-size: 20%;
312
+ position: relative;
313
+ display:inline-block;
314
+ }
315
+
316
+ /* testimonials footer */
317
+ #wpmtst-welcome .footer {
318
+ background-color: #fff;
319
+ border-bottom-left-radius: 10px;
320
+ border-bottom-right-radius: 10px;
321
+ margin-bottom: 30px;
322
+ position: relative;
323
+ box-shadow: -3px 2px 70px 0px rgba(128, 144, 174, 0.1);
324
+ }
325
+
326
+
assets/css/admin-welcome.min.css ADDED
@@ -0,0 +1 @@
 
1
+ #wpmtst-welcome .clear:after,#wpmtst-welcome .clear:before{content:" ";display:table}#wpcontent{padding-left:0!important;position:relative}#wpmtst-welcome{padding-top:80px}#wpmtst-welcome *,#wpmtst-welcome ::after,#wpmtst-welcome ::before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#wpmtst-welcome .clear:after{clear:both}#wpmtst-welcome .container{margin:0 auto;max-width:800px;padding:0}#wpmtst-welcome .block{padding:40px}#wpmtst-welcome img{max-width:100%;height:auto}#wpmtst-welcome h1{color:#222;font-size:24px;text-align:center;margin:0 0 16px}#wpmtst-welcome h5{color:#222;font-size:16px;margin:0 0 8px}#wpmtst-welcome h6{font-size:16px;font-weight:400;line-height:1.6;text-align:center;margin:0}#wpmtst-welcome .wpmtst-btn-lg{font-size:16px;font-weight:600;padding:16px 28px}#wpmtst-welcome .wpmtst-btn-block{display:block;width:100%}#wpmtst-welcome .wpmtst-btn{border:0;border-radius:40px;cursor:pointer;display:inline-block;margin:0;text-decoration:none;text-align:center;vertical-align:middle;white-space:nowrap;box-shadow:none}#wpmtst-welcome .features,#wpmtst-welcome .hero,#wpmtst-welcome .testimonials{box-shadow:-3px 2px 70px 0 rgba(128,144,174,.1)}#wpmtst-welcome .wpmtst-btn-purple{background-color:#5D3CE4;border-color:#5D3CE4;color:#fff}#wpmtst-welcome .wpmtst-btn-orange{background-color:#FBC555;border-color:#FBC555;color:#fff}#wpmtst-welcome .wpmtst-btn-white{background-color:#fff;border-color:#fff;color:#222}#wpmtst-welcome .button-wrap{max-width:590px;margin:0 auto}#wpmtst-welcome .button-wrap .left{margin-bottom:10px}@media screen and (min-width:720px){#wpmtst-welcome .button-wrap .left{margin-bottom:0;float:left;width:50%;padding-right:20px}#wpmtst-welcome .button-wrap .right{float:right;width:50%;padding-left:20px}}#wpmtst-welcome .hero{background-color:#fff;border-radius:10px;margin-bottom:30px;position:relative;padding-top:40px}#wpmtst-welcome .hero .mascot{background-color:#fff;border-radius:50%;height:90px;width:90px;padding:28px;position:absolute;top:-45px;left:50%;margin-left:-45px}#wpmtst-welcome .hero .video-thumbnail{display:block;margin:0;width:100%}#wpmtst-welcome .hero .button-wrap{margin-top:20px}#wpmtst-welcome .features{background-color:#fff;border-top-left-radius:10px;border-top-right-radius:10px;position:relative;padding-top:20px}#wpmtst-welcome .features .feature-list{margin-top:40px}#wpmtst-welcome .features .feature-block{margin-bottom:30px}@media screen and (min-width:720px){#wpmtst-welcome .features .feature-block{margin-bottom:0;float:left;width:50%;padding-bottom:32px;overflow:auto}#wpmtst-welcome .features .feature-block.first{padding-right:22px;clear:both}#wpmtst-welcome .features .feature-block.last{padding-left:22px}}#wpmtst-welcome .features .feature-block img{float:left;max-width:44px;max-height:44px}#wpmtst-welcome .features .feature-block h5,#wpmtst-welcome .features .feature-block p{margin-left:70px}#wpmtst-welcome .features .pro-label{display:inline-block;line-height:1;color:#fff;background-color:#5333ED;padding:3px 6px;font-size:.75rem;border-radius:20px;vertical-align:text-top;margin-left:20px}#wpmtst-welcome .features .feature-list a{color:#5333ED}#wpmtst-welcome .upgrade{background-color:#5333ED;color:rgba(255,255,255,.7);text-align:center}#wpmtst-welcome .upgrade h1{color:#fff}@media screen and (min-width:720px){#wpmtst-welcome .upgrade .left{float:left;width:50%;padding-left:20%;margin-bottom:30px;text-align:left}#wpmtst-welcome .upgrade .right{float:left;width:50%;padding-right:20%;margin-bottom:30px;text-align:left}#wpmtst-welcome .testimonials .testimonial-block{float:left;width:50%}}#wpmtst-welcome .testimonials{background-color:#fafafa;position:relative}#wpmtst-welcome .testimonials .testimonial-block{padding:0 16px 32px;overflow:auto;margin-top:40px;text-align:center}#wpmtst-welcome .testimonials .testimonial-block p{font-size:14px;font-weight:400;line-height:1.6}#wpmtst-welcome .testimonials .testimonial-block img{max-width:60px;max-height:60px;border-radius:50%;filter:grayscale(100%);box-shadow:0 0 12px rgba(0,0,0,.15)}#wpmtst-welcome .testimonials .testimonial-block .testimonial-stars{width:94px;height:20px;background-repeat:repeat-x;background-position:50% 50%;background-size:20%;position:relative;display:inline-block}#wpmtst-welcome .footer{background-color:#fff;border-bottom-left-radius:10px;border-bottom-right-radius:10px;margin-bottom:30px;position:relative;box-shadow:-3px 2px 70px 0 rgba(128,144,174,.1)}
assets/css/admin.css CHANGED
@@ -647,210 +647,14 @@ ul.standard {
647
  margin-left: 1rem;
648
  float: right; }
649
 
650
- /* notices */
651
- .wpmtst-notice-wrap {
652
- border: none;
653
- padding: 0;
654
- box-shadow: none;
655
- background: transparent;
656
- }
657
-
658
- .wpmtst-notice {
659
- padding: 2rem;
660
- }
661
-
662
- .wpmtst-notice--feedback {
663
- background: #fff;
664
- border-radius: 10px;
665
- border: 3px solid transparent;
666
- box-shadow: -3px 2px 70px 0px rgba(128, 144, 174, 0.16);
667
- padding-right: 140px !important;
668
- box-sizing: border-box;
669
- position: relative;
670
- overflow: hidden;
671
- margin-bottom: 20px;
672
- }
673
-
674
- .wpmtst-notice--feedback p {
675
- color: #888;
676
- }
677
-
678
- .wpmtst-notice--feedback__bg {
679
- background: url(../img/notice-bg-1.png) no-repeat 50% 50%;
680
- width: 400px;
681
- height: 400px;
682
- position: absolute;
683
- right: -225px;
684
- top: -100px;
685
- background-size: 100%;
686
- }
687
-
688
- .wpmtst-notice--upsell {
689
- background: #fbc556;
690
- border-radius: 10px;
691
- border: 3px solid transparent;
692
- box-shadow: none;
693
- padding-right: 140px !important;
694
- box-sizing: border-box;
695
- position: relative;
696
- overflow: hidden;
697
- margin-bottom: 20px;
698
- }
699
-
700
- .wpm-testimonial_page_about-strong-testimonials .wpmtst-notice--upsell .button {
701
- padding: 0 1.5rem;
702
- height: 2.5rem;
703
- line-height: 2.5rem;
704
- }
705
-
706
- .wpmtst-notice--upsell p {
707
- color: #8d6e30;
708
- }
709
-
710
- .wpmtst-notice--upsell__bg {
711
- background: url(../img/notice-bg-2.png) no-repeat 50% 50%;
712
- width: 380px;
713
- height: 400px;
714
- position: absolute;
715
- right: -260px;
716
- top: -80px;
717
- background-size: 100%;
718
- }
719
-
720
- @media screen and (min-width: 1100px) {
721
- .wpmtst-notice-wrap {
722
- display: flex;
723
- justify-content: space-between;
724
- }
725
-
726
- .edit-php.post-type-wpm-testimonial .wpmtst-notice--feedback,
727
- .wpm-testimonial_page_testimonial-views .wpmtst-notice--feedback {
728
- width: calc(50% - 15px);
729
- }
730
- .edit-php.post-type-wpm-testimonial .wpmtst-notice--upsell,
731
- .wpm-testimonial_page_testimonial-views .wpmtst-notice--upsell {
732
- width: calc(50% - 15px);
733
- }
734
- }
735
-
736
- @media screen and (min-width: 1400px) {
737
- .wpmtst-notice--feedback {
738
- padding-right: 320px !important;
739
- }
740
- .wpmtst-notice--feedback p {
741
- font-size: 14px;
742
- font-weight: 600;
743
- }
744
- .wpmtst-notice--feedback__bg {
745
- right: -60px;
746
- }
747
- .wpmtst-notice--upsell {
748
- padding-right: 320px !important;
749
- }
750
- .wpmtst-notice--upsell p {
751
- font-size: 14px;
752
- font-weight: 600;
753
- }
754
- .wpmtst-notice--upsell__bg {
755
- right: -60px;
756
- }
757
- }
758
-
759
-
760
- /* addons box */
761
- .wpmtst-addons-container {
762
- display: flex;
763
- justify-content: space-between;
764
- flex-wrap: wrap;
765
- }
766
-
767
- .wpmtst-addon {
768
- width: 100%;
769
- margin-bottom: 1rem;
770
- background-color: #fff;
771
- border: 1px solid #ddd;
772
- box-sizing: border-box;
773
- }
774
-
775
- @media screen and (min-width: 1100px) {
776
- .wpmtst-addons-container {
777
- margin-right: -20px;
778
- }
779
- .wpmtst-addon {
780
- width: calc( 50% - 20px );
781
- margin-right: 20px;
782
- }
783
- }
784
-
785
- @media screen and (min-width: 1400px) {
786
- .wpmtst-addon {
787
- width: calc( 33% - 20px );
788
- }
789
- }
790
-
791
-
792
- .wpmtst-addon .wpmtst-addon-box {
793
- display: flex;
794
- padding: 20px 20px 10px;
795
- text-align: left;
796
- }
797
- .wpmtst-addon .wpmtst-addon-box .wpmtst-addon-content {
798
- padding-left: 20px;
799
- }
800
- .wpmtst-addon .wpmtst-addon-box img {
801
- display: block;
802
- max-width: 128px;
803
- max-height: 128px;
804
- }
805
- .wpmtst-addon .wpmtst-addon-box h3 {
806
- margin-top: 0;
807
- text-align: left;
808
- }
809
- .wpmtst-addon .wpmtst-addon-actions {
810
- padding: 12px 20px;
811
- background-color: #fafafa;
812
- border-top: 1px solid #ddd;
813
- overflow: hidden;
814
- text-align: center;
815
- }
816
-
817
- /* alerts */
818
- #wpmtst-importer-upsell.postbox .inside {
819
- margin: 0;
820
- padding: 0;
821
- }
822
 
823
- #wpmtst-importer-upsell .wpmtst-alert {
824
- padding: 10px;
825
- }
826
 
827
- #wpmtst-importer-upsell .wpmtst-alert > *:last-child {
828
- text-align: center;
829
- }
830
-
831
- #wpmtst-importer-upsell .hndle {
832
- display: none;
833
- }
834
-
835
- .wpmtst-alert {
836
- padding: 20px;
837
- background: #f4daa4;
838
- color: #8d6e30;
839
- position: relative;
840
- }
841
-
842
- .wpmtst-alert ul {
843
- list-style: circle;
844
- padding-left: 30px;
845
- }
846
-
847
- .wpmtst-alert__upgrade-btn {
848
- position: absolute;
849
- right: 0.5rem;
850
- top: 50%;
851
- transform: translateY(-50%) !important;
852
- }
853
-
854
- .wpmtst-alert > *:last-child {
855
- margin-bottom: 0 !important;
856
- }
647
  margin-left: 1rem;
648
  float: right; }
649
 
650
+ .edit-php.post-type-wpm-testimonial .wpmtst-notice {
651
+ padding: 2.5rem 2rem;
652
+ border: 2px solid #7F22DE; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
653
 
654
+ .edit-php.post-type-wpm-testimonial .wpmtst-notice img {
655
+ max-width: 100%; }
 
656
 
657
+ .edit-php.post-type-wpm-testimonial .wpmst-mascot {
658
+ width: 120px;
659
+ margin-right: 2rem;
660
+ float: left; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/css/admin.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .error-message,.has-input,.required-phrase:before,.stackem .notice,.stackem label,.stackem label.inline,.stackem span.link,label.required:after{display:inline-block}.icon-blue{color:#0073aa}.error{color:red!important}.required-phrase{color:red;font-style:normal;font-weight:400;font-size:.9em}.required-phrase:before,label.required:after{content:'*';color:red;font-size:27px;line-height:19px;height:19px;font-weight:600;vertical-align:middle;position:relative;top:1px}.intro p,.wrap.wpmtst p{font-size:14px}.stackem label,.wp-core-ui .button-primary.active,.wp-core-ui .button-primary.active:focus,.wp-core-ui .button-primary.active:hover,.wp-core-ui .button-primary:active{vertical-align:baseline}.error-message{margin-left:.5em}button.nogo{margin:0 3px}li.warning{background:#ffffe0}.between-inputs{margin-left:.3em;margin-right:.3em}.wrap.wpmtst h2{margin:2em 0 1em}.wrap.wpmtst .notice p{font-size:13px}.wpmtst .list-wrap{padding:.5em}a.widget-action:focus{outline:0}input#post_name{width:98%}fieldset>div{min-height:30px}div.help{margin-left:0}div.help.minor{font-size:.9em;text-align:left}.wpmtst i.fa.example{color:#0073aa}.stackem ul{margin:14px 0 0}.stackem li{margin-top:16px;margin-bottom:16px}.stackem label{line-height:28px;margin-right:.5em}.stackem p.description{margin-top:0;margin-left:22px}.stackem p.description.warning{color:#CD0000}.stackem .notice{background:0 0;border:none;box-shadow:none;font-size:.9em;font-style:italic;margin:0 .2em;padding:0;color:#868686}.stackem span.link{margin:0 .3em;font-size:.9em;line-height:28px}.stackem label.disabled{color:#888}div.radio{line-height:1.5em}div.radio:hover{color:#000}#help-section,#screenshot-screen-options{display:none}.screenshot>div{display:inline-block;box-shadow:inset 0 0 40px rgba(0,0,0,.8)}.help-text{padding:.5em 0}.wpmtst .form-table{vertical-align:middle}.wpmtst .form-table td{padding-top:15px}.wpmtst .form-table td:nth-child(1){width:200px}.wpmtst .form-table .parent{border-bottom:0;vertical-align:top}.wpmtst .form-table .child{border-top:0}.wpmtst .form-table td p:last-child{margin-bottom:0}.wpmtst .form-table input[type=button]{line-height:26px;height:28px}.wpmtst select{min-width:120px}.wpmtst option{padding-left:5px}.wpmtst .form-table div.box{border:1px solid #DDD;display:inline-block;padding:10px}.wpmtst .form-table .row{line-height:2.5em}.wpmtst .form-table .alpha{width:120px}.wpmtst .form-table .alpha.alpha-110{width:110px}.wpmtst .form-table .alpha+div{display:inline-block}.wpmtst label.success-action{display:inline-block;width:11em;padding:10px 0}.wpmtst .form-table.compact th{vertical-align:top}.wpmtst .form-table.compact td{vertical-align:middle}.wpmtst .form-table.compact td.actions{width:100px;vertical-align:bottom}.wpmtst .form-table.compact td.actions input.button{margin-right:0;margin-bottom:3px}.wpmtst .form-table.compact input[type=text]{width:100%}.wpmtst .form-table.compact input.error{border-color:red;border-radius:2px;box-shadow:none}.wpmtst .form-table.compact label.error{color:red;display:block}.code{background:#FFF}.code.wide{padding:2px 5px;margin:3px;border-radius:2px}.submit-buttons input.button{margin-right:10px}.custom-input{line-height:1.3em}.indent{margin-left:2em}.outdent{margin-left:-2em}.hilite{background:#ffffe0;border-radius:4px;display:inline-block;padding:.25em .75em;box-shadow:2px 2px 4px rgba(0,0,0,.5)}p.description.hilite{margin-bottom:2em}ul.compact{margin:0}ul.compact li{display:inline-block;margin:6px 30px 6px 0}#licenses-form .form-table{width:auto;max-width:1024px}#licenses-form .form-table td,#licenses-form .form-table th{vertical-align:top}#licenses-form th.for-license-key,#licenses-form th.for-license-status{width:350px}#licenses-form .form-table th{padding:15px}#licenses-form .form-table td{white-space:nowrap;padding:15px}#licenses-form .form-table td:first-child,#licenses-form .form-table th:first-child{padding-left:0}#licenses-form .form-table input[type=text]{font-family:Consolas,Monaco,monospace;width:100%}#licenses-form .form-table label{line-height:27px;padding-left:0;text-indent:0}#licenses-form .doing-ajax:before{display:inline-block;font:400 22px/29px dashicons;content:"\F463";text-align:center;vertical-align:middle;-webkit-animation-name:rotate;-webkit-animation-duration:1.5s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear;-moz-animation-name:rotate;-moz-animation-duration:1.5s;-moz-animation-iteration-count:infinite;-moz-animation-timing-function:linear;animation-name:rotate;animation-duration:1.5s;animation-iteration-count:infinite;animation-timing-function:linear}.form-table.shortcodes td.shortcode,td.column-id,td.column-shortcode{font-family:Consolas,Monaco,monospace}#licenses-form span.license-status{display:inline-block;font-size:.9em;line-height:28px;height:27px;margin:1px 10px 1px 0;padding:0 8px;text-transform:uppercase;border-radius:3px;vertical-align:middle}#licenses-form span.active{background:green;color:#FFF}#licenses-form span.inactive{background:#999;color:#FFF}#licenses-form span.ib{display:inline-block;vertical-align:middle}.activation-error{color:red}.form-table td .description,.form-table th .description{font-size:14px;font-style:italic}.description.inline{display:inline-block;margin-left:1em}.description a{text-decoration:underline}.description a:hover{text-decoration:none}.radio .description{padding-left:20px}li.checkbox{margin-bottom:10px}.checkbox .description{padding-left:24px}ul.description{font-style:italic;color:#666;margin-top:4px;list-style-type:disc;margin-left:21px;margin-bottom:0}.wpmtst-widget-form{margin-bottom:1em}.widget-inside p{margin:.5em 0}input[disabled],input[readonly]{color:#999;background:rgba(0,0,0,.04)}.form-table.shortcodes tbody tr:hover,.form-table.shortcodes tr.important{background:#FFF}.widefat th.column-ID{width:2.2em}td.column-id,td.column-shortcode{white-space:nowrap}th.sortable.column-id a span{float:right}td.column-id{text-align:right}.column-rating{width:auto!important}.wp-list-table.fixed.wpm-testimonial_page_testimonial-views{table-layout:auto}.wpmtst2>h1{margin-bottom:15px}@media screen and (max-width:782px){.wpm-testimonial_page_testimonial-settings .form-table td input[type=text]{display:inline-block;width:auto}}@keyframes rotate{from{transform:rotate(0)}to{transform:rotate(360deg)}}.form-table.shortcodes th{padding-left:10px;width:auto}.form-table.shortcodes td{border:1px solid #DDD}.form-table.shortcodes td p{margin:1em 0}.form-table.shortcodes td p:first-child{margin-top:0}.form-table td p:first-child,.wrap h2.nav-tab-wrapper{margin-top:6px}.form-table.shortcodes td p:last-child{margin-bottom:0}.form-table.shortcodes tr.important td{border:1px solid #CCC}.slug-example{font-weight:700;color:#0073aa}.form-table.width-auto{width:auto}.current-dashicon{display:inline-block;background:#0073aa;color:#FFF;border-radius:2px;text-align:center;vertical-align:middle;width:28px;height:28px;position:relative;top:-3px}.current-dashicon .dashicons{vertical-align:middle;line-height:1.3}.wp-core-ui input[type=reset],.wp-core-ui input[type=reset]:active,.wp-core-ui input[type=reset]:focus,.wp-core-ui input[type=reset]:hover{background:#F7F7F7;border:1px solid #CCC;box-shadow:0 1px 0 #CCC;padding:0 10px 1px;width:auto}.wp-core-ui input[type=reset]:focus,.wp-core-ui input[type=reset]:hover{background:#fafafa;border-color:#999;color:#23282d}.wp-core-ui input[type=reset]:focus{border-color:#5b9dd9;box-shadow:0 0 3px rgba(0,115,170,.8)}.wp-core-ui input[type=reset]:active{background:#eee;border-color:#999;box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5);transform:translateY(1px)}input.button.restore-default{font:400 20px/1 dashicons;padding:1px 3px}.wpmtst .subsubsub{float:none;margin-left:16px}.regular-text.half{width:12.5em}.regular-text.third{width:20em}input.code{font-size:13px;font-family:Consolas,Monaco,monospace}.tab-header{padding:1em 0}.striped>tbody>tr.readonly{background:#ffffe0}ul.standard{list-style:disc;padding-left:1.5em}.form-table p.error{margin:.5em 0}.wpmtst.shortcode{vertical-align:middle}.wpmtst.shortcodes.has-stars table{margin-bottom:1em}.wpmtst.shortcodes.has-stars td:nth-child(2){width:70%}.wpmtst.shortcodes td{padding:10px}.wpmtst.shortcodes tr.important td{border:1px solid #DDD}.wpmtst.shortcodes tr:hover{background:#FFF}.wpmtst.shortcodes td.has-inner{padding:0}.wpmtst.shortcodes table.inner{margin:0;vertical-align:middle;width:100%}.wpmtst.shortcodes table.inner,.wpmtst.shortcodes table.inner td,.wpmtst.shortcodes table.inner tr{border:0}.wpmtst.shortcodes table.inner tr:first-child td{border-bottom:1px solid #DDD}.wp-list-table .type-wpm-testimonial .row-actions span.id span{color:#777}.wpm-testimonial_page_about-strong-testimonials .wpmst-mascot{width:160px;margin-left:1rem;float:right}.edit-php.post-type-wpm-testimonial .wpmtst-notice{padding:2.5rem 2rem;border:2px solid #7F22DE}.edit-php.post-type-wpm-testimonial .wpmtst-notice img{max-width:100%}.edit-php.post-type-wpm-testimonial .wpmst-mascot{width:120px;margin-right:2rem;float:left}
assets/css/blocks.css ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ div.st-block-preview {
2
+ background: white;
3
+ padding: 50px 25px;
4
+ border: 1px solid black; }
5
+
6
+ div.st-view-select label.components-input-control__label {
7
+ margin-bottom: 10px !important;
8
+ font-weight: 700; }
9
+
10
+ div.st-view-select div.components-input-control__container select.components-select-control__input {
11
+ height: -webkit-fit-content;
12
+ height: -moz-fit-content;
13
+ height: fit-content; }
assets/css/blocks.min.css ADDED
@@ -0,0 +1 @@
 
1
+ div.st-block-preview{background:#fff;padding:50px 25px;border:1px solid #000}div.st-view-select{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:100px}div.st-view-select label.components-input-control__label{margin-bottom:10px!important;font-weight:700}div.st-view-select div.components-input-control__container select.components-select-control__input{height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}
assets/js/admin-js.js CHANGED
@@ -63,11 +63,68 @@
63
  /******/ __webpack_require__.p = "";
64
  /******/
65
  /******/ // Load entry module and return exports
66
- /******/ return __webpack_require__(__webpack_require__.s = 2);
67
  /******/ })
68
  /************************************************************************/
69
  /******/ ([
70
- /* 0 */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  /***/ (function(module, exports, __webpack_require__) {
72
 
73
  "use strict";
@@ -116,8 +173,7 @@ var Notice = function () {
116
  exports.default = Notice;
117
 
118
  /***/ }),
119
- /* 1 */,
120
- /* 2 */
121
  /***/ (function(module, exports, __webpack_require__) {
122
 
123
  "use strict";
@@ -125,11 +181,11 @@ exports.default = Notice;
125
 
126
  var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
127
 
128
- var _notice = __webpack_require__(0);
129
 
130
  var _notice2 = _interopRequireDefault(_notice);
131
 
132
- var _AddonsPage = __webpack_require__(4);
133
 
134
  var _AddonsPage2 = _interopRequireDefault(_AddonsPage);
135
 
@@ -173,30 +229,30 @@ jQuery(document).ready(function ($) {
173
 
174
  // Add protocol if missing
175
  // Thanks http://stackoverflow.com/a/36429927/51600
176
- $('input[type=url]').change(function () {
177
  if (this.value.length && !/^https*:\/\//.test(this.value)) {
178
  this.value = 'http://' + this.value;
179
  }
180
  });
181
 
182
- $('ul.ui-tabs-nav li a').click(function () {
183
  $(this).blur();
184
  });
185
 
186
- $('.focus-next-field').change(function (e) {
187
  if ($(e.target).is(':checked')) {
188
  $(e.target).parent().next().find('input').focus().select();
189
  }
190
  });
191
 
192
  // toggle screenshots
193
- $('#toggle-screen-options').add('#screenshot-screen-options').click(function (e) {
194
  $(this).blur();
195
  $('#screenshot-screen-options').slideToggle();
196
  });
197
 
198
  // toggle screenshots
199
- $('#toggle-help').click(function (e) {
200
  $(this).toggleClass('closed open').blur();
201
  $('#help-section').slideToggle();
202
  });
@@ -271,62 +327,5 @@ var WPMTST_Admin = function () {
271
 
272
  window.WPMTST_Admin = new WPMTST_Admin();
273
 
274
- /***/ }),
275
- /* 3 */,
276
- /* 4 */
277
- /***/ (function(module, exports, __webpack_require__) {
278
-
279
- "use strict";
280
-
281
-
282
- Object.defineProperty(exports, "__esModule", {
283
- value: true
284
- });
285
-
286
- var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
287
-
288
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
289
-
290
- var AddonsPage = function () {
291
- function AddonsPage() {
292
- var _this = this;
293
-
294
- _classCallCheck(this, AddonsPage);
295
-
296
- if (!jQuery('body').hasClass('wpm-testimonial_page_strong-testimonials-addons')) {
297
- return;
298
- }
299
-
300
- this.reloadButton = jQuery('#wpmtst-reload-extensions');
301
-
302
- //events
303
- this.reloadButton.on('click', function (e) {
304
- return _this.onReloadExtensionsClick(e);
305
- });
306
- }
307
-
308
- _createClass(AddonsPage, [{
309
- key: 'onReloadExtensionsClick',
310
- value: function onReloadExtensionsClick(e) {
311
- e.preventDefault();
312
-
313
- this.reloadButton.addClass('updating-message');
314
-
315
- jQuery.ajax({
316
- type: "POST",
317
- data: { action: "wpmtst_reload_extensions", nonce: this.reloadButton.data('nonce') },
318
- url: ajaxurl,
319
- success: function success(response) {
320
- location.reload();
321
- }
322
- });
323
- }
324
- }]);
325
-
326
- return AddonsPage;
327
- }();
328
-
329
- exports.default = AddonsPage;
330
-
331
  /***/ })
332
  /******/ ]);
63
  /******/ __webpack_require__.p = "";
64
  /******/
65
  /******/ // Load entry module and return exports
66
+ /******/ return __webpack_require__(__webpack_require__.s = 3);
67
  /******/ })
68
  /************************************************************************/
69
  /******/ ([
70
+ /* 0 */,
71
+ /* 1 */
72
+ /***/ (function(module, exports, __webpack_require__) {
73
+
74
+ "use strict";
75
+
76
+
77
+ Object.defineProperty(exports, "__esModule", {
78
+ value: true
79
+ });
80
+
81
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
82
+
83
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
84
+
85
+ var AddonsPage = function () {
86
+ function AddonsPage() {
87
+ var _this = this;
88
+
89
+ _classCallCheck(this, AddonsPage);
90
+
91
+ if (!jQuery('body').hasClass('wpm-testimonial_page_strong-testimonials-addons')) {
92
+ return;
93
+ }
94
+
95
+ this.reloadButton = jQuery('#wpmtst-reload-extensions');
96
+
97
+ //events
98
+ this.reloadButton.on('click', function (e) {
99
+ return _this.onReloadExtensionsClick(e);
100
+ });
101
+ }
102
+
103
+ _createClass(AddonsPage, [{
104
+ key: 'onReloadExtensionsClick',
105
+ value: function onReloadExtensionsClick(e) {
106
+ e.preventDefault();
107
+
108
+ this.reloadButton.addClass('updating-message');
109
+
110
+ jQuery.ajax({
111
+ type: "POST",
112
+ data: { action: "wpmtst_reload_extensions", nonce: this.reloadButton.data('nonce') },
113
+ url: ajaxurl,
114
+ success: function success(response) {
115
+ location.reload();
116
+ }
117
+ });
118
+ }
119
+ }]);
120
+
121
+ return AddonsPage;
122
+ }();
123
+
124
+ exports.default = AddonsPage;
125
+
126
+ /***/ }),
127
+ /* 2 */
128
  /***/ (function(module, exports, __webpack_require__) {
129
 
130
  "use strict";
173
  exports.default = Notice;
174
 
175
  /***/ }),
176
+ /* 3 */
 
177
  /***/ (function(module, exports, __webpack_require__) {
178
 
179
  "use strict";
181
 
182
  var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
183
 
184
+ var _notice = __webpack_require__(2);
185
 
186
  var _notice2 = _interopRequireDefault(_notice);
187
 
188
+ var _AddonsPage = __webpack_require__(1);
189
 
190
  var _AddonsPage2 = _interopRequireDefault(_AddonsPage);
191
 
229
 
230
  // Add protocol if missing
231
  // Thanks http://stackoverflow.com/a/36429927/51600
232
+ $('input[type=url]').on('change', function () {
233
  if (this.value.length && !/^https*:\/\//.test(this.value)) {
234
  this.value = 'http://' + this.value;
235
  }
236
  });
237
 
238
+ $('ul.ui-tabs-nav li a').on('click', function () {
239
  $(this).blur();
240
  });
241
 
242
+ $('.focus-next-field').on('change', function (e) {
243
  if ($(e.target).is(':checked')) {
244
  $(e.target).parent().next().find('input').focus().select();
245
  }
246
  });
247
 
248
  // toggle screenshots
249
+ $('#toggle-screen-options').add('#screenshot-screen-options').on('click', function (e) {
250
  $(this).blur();
251
  $('#screenshot-screen-options').slideToggle();
252
  });
253
 
254
  // toggle screenshots
255
+ $('#toggle-help').on('click', function (e) {
256
  $(this).toggleClass('closed open').blur();
257
  $('#help-section').slideToggle();
258
  });
327
 
328
  window.WPMTST_Admin = new WPMTST_Admin();
329
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  /***/ })
331
  /******/ ]);
assets/js/blocks-js.js ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******/ (function(modules) { // webpackBootstrap
2
+ /******/ // The module cache
3
+ /******/ var installedModules = {};
4
+ /******/
5
+ /******/ // The require function
6
+ /******/ function __webpack_require__(moduleId) {
7
+ /******/
8
+ /******/ // Check if module is in cache
9
+ /******/ if(installedModules[moduleId]) {
10
+ /******/ return installedModules[moduleId].exports;
11
+ /******/ }
12
+ /******/ // Create a new module (and put it into the cache)
13
+ /******/ var module = installedModules[moduleId] = {
14
+ /******/ i: moduleId,
15
+ /******/ l: false,
16
+ /******/ exports: {}
17
+ /******/ };
18
+ /******/
19
+ /******/ // Execute the module function
20
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21
+ /******/
22
+ /******/ // Flag the module as loaded
23
+ /******/ module.l = true;
24
+ /******/
25
+ /******/ // Return the exports of the module
26
+ /******/ return module.exports;
27
+ /******/ }
28
+ /******/
29
+ /******/
30
+ /******/ // expose the modules object (__webpack_modules__)
31
+ /******/ __webpack_require__.m = modules;
32
+ /******/
33
+ /******/ // expose the module cache
34
+ /******/ __webpack_require__.c = installedModules;
35
+ /******/
36
+ /******/ // identity function for calling harmony imports with the correct context
37
+ /******/ __webpack_require__.i = function(value) { return value; };
38
+ /******/
39
+ /******/ // define getter function for harmony exports
40
+ /******/ __webpack_require__.d = function(exports, name, getter) {
41
+ /******/ if(!__webpack_require__.o(exports, name)) {
42
+ /******/ Object.defineProperty(exports, name, {
43
+ /******/ configurable: false,
44
+ /******/ enumerable: true,
45
+ /******/ get: getter
46
+ /******/ });
47
+ /******/ }
48
+ /******/ };
49
+ /******/
50
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
51
+ /******/ __webpack_require__.n = function(module) {
52
+ /******/ var getter = module && module.__esModule ?
53
+ /******/ function getDefault() { return module['default']; } :
54
+ /******/ function getModuleExports() { return module; };
55
+ /******/ __webpack_require__.d(getter, 'a', getter);
56
+ /******/ return getter;
57
+ /******/ };
58
+ /******/
59
+ /******/ // Object.prototype.hasOwnProperty.call
60
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
61
+ /******/
62
+ /******/ // __webpack_public_path__
63
+ /******/ __webpack_require__.p = "";
64
+ /******/
65
+ /******/ // Load entry module and return exports
66
+ /******/ return __webpack_require__(__webpack_require__.s = 4);
67
+ /******/ })
68
+ /************************************************************************/
69
+ /******/ ({
70
+
71
+ /***/ 0:
72
+ /***/ (function(module, exports, __webpack_require__) {
73
+
74
+ "use strict";
75
+
76
+
77
+ Object.defineProperty(exports, "__esModule", {
78
+ value: true
79
+ });
80
+
81
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
82
+
83
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
84
+
85
+ var _inspector = __webpack_require__(8);
86
+
87
+ var _inspector2 = _interopRequireDefault(_inspector);
88
+
89
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
90
+
91
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
92
+
93
+ function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
94
+
95
+ function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
96
+
97
+ /**
98
+ * Wordpress deps
99
+ */
100
+
101
+ var __ = wp.i18n.__;
102
+ var _wp$element = wp.element,
103
+ Component = _wp$element.Component,
104
+ Fragment = _wp$element.Fragment;
105
+ var withSelect = wp.data.withSelect;
106
+ var _wp$components = wp.components,
107
+ SelectControl = _wp$components.SelectControl,
108
+ Spinner = _wp$components.Spinner,
109
+ Toolbar = _wp$components.Toolbar,
110
+ Button = _wp$components.Button;
111
+ var BlockControls = wp.blockEditor.BlockControls;
112
+
113
+ var StrongTestimonialViewEdit = function (_Component) {
114
+ _inherits(StrongTestimonialViewEdit, _Component);
115
+
116
+ function StrongTestimonialViewEdit(props) {
117
+ _classCallCheck(this, StrongTestimonialViewEdit);
118
+
119
+ var _this = _possibleConstructorReturn(this, (StrongTestimonialViewEdit.__proto__ || Object.getPrototypeOf(StrongTestimonialViewEdit)).apply(this, arguments));
120
+
121
+ _this.props.attributes.status = 'ready';
122
+ _this.props.attributes.views = st_views.views;
123
+ return _this;
124
+ }
125
+
126
+ _createClass(StrongTestimonialViewEdit, [{
127
+ key: 'onIdChange',
128
+ value: function onIdChange(id) {
129
+ this.props.setAttributes({ status: 'ready', id: id, views: st_views.views });
130
+ }
131
+ }, {
132
+ key: 'selectOptions',
133
+ value: function selectOptions() {
134
+ var options = [];
135
+
136
+ st_views.views.forEach(function (view) {
137
+ options.push({ value: view.id, label: view.name });
138
+ });
139
+
140
+ return options;
141
+ }
142
+ }, {
143
+ key: 'render',
144
+ value: function render() {
145
+ var _this2 = this;
146
+
147
+ var _props = this.props,
148
+ attributes = _props.attributes,
149
+ setAttributes = _props.setAttributes;
150
+ var id = attributes.id,
151
+ views = attributes.views,
152
+ status = attributes.status,
153
+ testimonials = attributes.testimonials,
154
+ mode = attributes.mode;
155
+
156
+ var blockControls = React.createElement(
157
+ BlockControls,
158
+ null,
159
+ views.length > 0 && React.createElement(
160
+ Toolbar,
161
+ null,
162
+ React.createElement(Button, { label: __('Edit View'), icon: 'edit', target: '_blank' })
163
+ )
164
+ );
165
+ if (status === 'loading') {
166
+ return [React.createElement(
167
+ Fragment,
168
+ null,
169
+ React.createElement(
170
+ 'div',
171
+ { className: 'st-block-preview' },
172
+ React.createElement(
173
+ 'div',
174
+ { className: 'st-block-preview__content' },
175
+ React.createElement(
176
+ 'div',
177
+ { className: 'st-block-preview__logo' },
178
+ ' '
179
+ ),
180
+ React.createElement(Spinner, null)
181
+ )
182
+ )
183
+ )];
184
+ }
185
+
186
+ return [React.createElement(
187
+ Fragment,
188
+ null,
189
+ React.createElement(_inspector2.default, _extends({ onIdChange: function onIdChange(id) {
190
+ return _this2.onIdChange(id);
191
+ } }, this.props)),
192
+ React.createElement(
193
+ 'div',
194
+ { className: 'st-block-preview' },
195
+ React.createElement(
196
+ 'div',
197
+ { 'class': 'st-block-preview__content' },
198
+ React.createElement('div', { className: 'st-block-preview__logo' }),
199
+ views.length === 0 && React.createElement(
200
+ Fragment,
201
+ null,
202
+ React.createElement(
203
+ 'h6',
204
+ null,
205
+ __("You don't seem to have any views.")
206
+ ),
207
+ React.createElement(
208
+ Button,
209
+ {
210
+ href: st_views.adminURL + 'edit.php?post_type=wpm-testimonial&page=testimonial-views&action=add',
211
+ target: '_blank',
212
+ isDefault: true
213
+ },
214
+ __('Add New View')
215
+ )
216
+ ),
217
+ st_views.views.length > 0 && React.createElement(
218
+ Fragment,
219
+ null,
220
+ React.createElement(SelectControl, {
221
+ label: 'Select a view:',
222
+ className: 'st-view-select',
223
+ value: id,
224
+ options: this.selectOptions(),
225
+ onChange: function onChange(id) {
226
+ return setAttributes({ id: id });
227
+ }
228
+ }),
229
+ id != 0 && React.createElement(
230
+ Button,
231
+ {
232
+ target: '_blank',
233
+ href: st_views.adminURL + 'edit.php?post_type=wpm-testimonial&page=testimonial-views&action=edit&id=' + id,
234
+ isSecondary: true
235
+ },
236
+ __('Edit Settings')
237
+ )
238
+ )
239
+ )
240
+ )
241
+ )];
242
+ }
243
+ }]);
244
+
245
+ return StrongTestimonialViewEdit;
246
+ }(Component);
247
+
248
+ exports.default = StrongTestimonialViewEdit;
249
+
250
+ /***/ }),
251
+
252
+ /***/ 4:
253
+ /***/ (function(module, exports, __webpack_require__) {
254
+
255
+ "use strict";
256
+
257
+
258
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
259
+
260
+ var _edit = __webpack_require__(0);
261
+
262
+ var _edit2 = _interopRequireDefault(_edit);
263
+
264
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
265
+
266
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
267
+
268
+ /**
269
+ * Import wp deps
270
+ */
271
+
272
+ var __ = wp.i18n.__;
273
+ var registerBlockType = wp.blocks.registerBlockType;
274
+
275
+ var StrongTestimonialView = function () {
276
+ function StrongTestimonialView() {
277
+ _classCallCheck(this, StrongTestimonialView);
278
+
279
+ this.registerBlock();
280
+ }
281
+
282
+ _createClass(StrongTestimonialView, [{
283
+ key: 'registerBlock',
284
+ value: function registerBlock() {
285
+
286
+ this.blockName = 'strongtestimonials/view';
287
+
288
+ this.blockAttributes = {
289
+ id: {
290
+ type: 'number',
291
+ default: 0
292
+ },
293
+ mode: {
294
+ type: 'string',
295
+ default: 'display'
296
+ }
297
+ };
298
+
299
+ registerBlockType(this.blockName, {
300
+ title: 'Strong Testimonial View',
301
+ description: __('Render ST View', 'strong-testimonials'),
302
+ icon: 'editor-quote',
303
+ category: 'common',
304
+ supports: {
305
+ html: false,
306
+ customClassName: false
307
+ },
308
+
309
+ attributes: this.blockAttributes,
310
+ edit: _edit2.default,
311
+ save: function save() {
312
+ return null;
313
+ }
314
+ });
315
+ }
316
+ }]);
317
+
318
+ return StrongTestimonialView;
319
+ }();
320
+
321
+ var strongTestimonialsView = new StrongTestimonialView();
322
+
323
+ /***/ }),
324
+
325
+ /***/ 8:
326
+ /***/ (function(module, exports, __webpack_require__) {
327
+
328
+ "use strict";
329
+
330
+
331
+ Object.defineProperty(exports, "__esModule", {
332
+ value: true
333
+ });
334
+
335
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
336
+
337
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
338
+
339
+ function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
340
+
341
+ function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
342
+
343
+ /**
344
+ * WordPress dependencies
345
+ */
346
+ var __ = wp.i18n.__;
347
+ var _wp$element = wp.element,
348
+ Component = _wp$element.Component,
349
+ Fragment = _wp$element.Fragment;
350
+ var InspectorControls = wp.blockEditor.InspectorControls;
351
+ var _wp$components = wp.components,
352
+ SelectControl = _wp$components.SelectControl,
353
+ Button = _wp$components.Button,
354
+ PanelBody = _wp$components.PanelBody,
355
+ PanelRow = _wp$components.PanelRow;
356
+
357
+ /**
358
+ * Inspector controls
359
+ */
360
+
361
+ var Inspector = function (_Component) {
362
+ _inherits(Inspector, _Component);
363
+
364
+ function Inspector(props) {
365
+ _classCallCheck(this, Inspector);
366
+
367
+ return _possibleConstructorReturn(this, (Inspector.__proto__ || Object.getPrototypeOf(Inspector)).apply(this, arguments));
368
+ }
369
+
370
+ _createClass(Inspector, [{
371
+ key: 'selectOptions',
372
+ value: function selectOptions() {
373
+ var options = [{ value: 0, label: __('none') }];
374
+
375
+ this.props.attributes.views.forEach(function (view) {
376
+ options.push({ value: view.id, label: view.name });
377
+ });
378
+
379
+ return options;
380
+ }
381
+ }, {
382
+ key: 'render',
383
+ value: function render() {
384
+ var _props = this.props,
385
+ attributes = _props.attributes,
386
+ setAttributes = _props.setAttributes;
387
+ var id = attributes.id,
388
+ views = attributes.views,
389
+ testimonials = attributes.testimonials;
390
+
391
+ return React.createElement(
392
+ Fragment,
393
+ null,
394
+ React.createElement(
395
+ InspectorControls,
396
+ null,
397
+ React.createElement(
398
+ PanelBody,
399
+ { title: __('View Settings'), initialOpen: true },
400
+ views.length === 0 && React.createElement(
401
+ Fragment,
402
+ null,
403
+ React.createElement(
404
+ 'p',
405
+ null,
406
+ __("You don't seem to have any views.")
407
+ ),
408
+ React.createElement(
409
+ Button,
410
+ {
411
+ href: st_views.adminURL + 'edit.php?post_type=wpm-testimonial&page=testimonial-views&action=add',
412
+ target: '_blank',
413
+ isDefault: true
414
+ },
415
+ __('Add New View')
416
+ )
417
+ ),
418
+ views.length > 0 && React.createElement(
419
+ Fragment,
420
+ null,
421
+ React.createElement(SelectControl, {
422
+ label: __('Select View'),
423
+ value: id,
424
+ options: this.selectOptions(),
425
+ onChange: function onChange(id) {
426
+ return setAttributes({ id: id });
427
+ }
428
+ }),
429
+ id != 0 && React.createElement(
430
+ Button,
431
+ {
432
+ target: '_blank',
433
+ href: st_views.adminURL + 'edit.php?post_type=wpm-testimonial&page=testimonial-views&action=edit&id=' + id,
434
+ isSecondary: true
435
+ },
436
+ __('Edit View')
437
+ )
438
+ )
439
+ )
440
+ )
441
+ );
442
+ }
443
+ }]);
444
+
445
+ return Inspector;
446
+ }(Component);
447
+
448
+ exports.default = Inspector;
449
+
450
+ /***/ })
451
+
452
+ /******/ });
assets/src/js/admin.js CHANGED
@@ -34,30 +34,30 @@ jQuery(document).ready(function ($) {
34
 
35
  // Add protocol if missing
36
  // Thanks http://stackoverflow.com/a/36429927/51600
37
- $('input[type=url]').change(function () {
38
  if (this.value.length && !/^https*:\/\//.test(this.value)) {
39
  this.value = 'http://' + this.value;
40
  }
41
  });
42
 
43
- $('ul.ui-tabs-nav li a').click(function () {
44
  $(this).blur();
45
  });
46
 
47
- $('.focus-next-field').change(function (e) {
48
  if ($(e.target).is(':checked')) {
49
  $(e.target).parent().next().find('input').focus().select();
50
  }
51
  });
52
 
53
  // toggle screenshots
54
- $('#toggle-screen-options').add('#screenshot-screen-options').click(function (e) {
55
  $(this).blur();
56
  $('#screenshot-screen-options').slideToggle();
57
  });
58
 
59
  // toggle screenshots
60
- $('#toggle-help').click(function (e) {
61
  $(this).toggleClass('closed open').blur();
62
  $('#help-section').slideToggle();
63
  });
34
 
35
  // Add protocol if missing
36
  // Thanks http://stackoverflow.com/a/36429927/51600
37
+ $('input[type=url]').on( 'change', function () {
38
  if (this.value.length && !/^https*:\/\//.test(this.value)) {
39
  this.value = 'http://' + this.value;
40
  }
41
  });
42
 
43
+ $('ul.ui-tabs-nav li a').on('click', function () {
44
  $(this).blur();
45
  });
46
 
47
+ $('.focus-next-field').on( 'change', function (e) {
48
  if ($(e.target).is(':checked')) {
49
  $(e.target).parent().next().find('input').focus().select();
50
  }
51
  });
52
 
53
  // toggle screenshots
54
+ $('#toggle-screen-options').add('#screenshot-screen-options').on( 'click', function (e) {
55
  $(this).blur();
56
  $('#screenshot-screen-options').slideToggle();
57
  });
58
 
59
  // toggle screenshots
60
+ $('#toggle-help').on( 'click', function (e) {
61
  $(this).toggleClass('closed open').blur();
62
  $('#help-section').slideToggle();
63
  });
assets/src/js/blocks.js ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Edit from './components/edit';
2
+
3
+ /**
4
+ * Import wp deps
5
+ */
6
+
7
+ const { __ } = wp.i18n;
8
+ const { registerBlockType } = wp.blocks;
9
+
10
+ class StrongTestimonialView {
11
+
12
+ constructor() {
13
+ this.registerBlock();
14
+ }
15
+
16
+ registerBlock() {
17
+
18
+ this.blockName = 'strongtestimonials/view';
19
+
20
+ this.blockAttributes = {
21
+ id: {
22
+ type: 'number',
23
+ default: 0,
24
+ },
25
+ mode: {
26
+ type: 'string',
27
+ default: 'display',
28
+ }
29
+ };
30
+
31
+ registerBlockType( this.blockName , {
32
+ title: 'Strong Testimonial View',
33
+ description: __( 'Render ST View', 'strong-testimonials'),
34
+ icon: 'editor-quote',
35
+ category: 'common',
36
+ supports: {
37
+ html: false,
38
+ customClassName: false,
39
+ },
40
+
41
+ attributes: this.blockAttributes,
42
+ edit: Edit,
43
+ save: () => {
44
+ return null;
45
+ },
46
+ });
47
+ }
48
+ }
49
+
50
+ let strongTestimonialsView = new StrongTestimonialView();
assets/src/js/components/edit.js ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Inspector from './inspector';
2
+
3
+ /**
4
+ * Wordpress deps
5
+ */
6
+
7
+ const { __ } = wp.i18n;
8
+ const { Component, Fragment } = wp.element;
9
+ const { withSelect } = wp.data;
10
+ const { SelectControl, Spinner, Toolbar, Button } = wp.components;
11
+ const { BlockControls } = wp.blockEditor;
12
+
13
+ export default class StrongTestimonialViewEdit extends Component {
14
+ constructor(props) {
15
+ super(...arguments);
16
+ this.props.attributes.status = 'ready';
17
+ this.props.attributes.views = st_views.views;
18
+ }
19
+
20
+ onIdChange(id) {
21
+ this.props.setAttributes({ status: 'ready', id: id, views: st_views.views });
22
+ }
23
+
24
+ selectOptions() {
25
+ let options = [];
26
+
27
+ st_views.views.forEach(function(view) {
28
+ options.push({ value: view.id, label: view.name });
29
+ });
30
+
31
+ return options;
32
+ }
33
+
34
+ render() {
35
+ const { attributes, setAttributes } = this.props;
36
+ const { id, views, status, testimonials, mode } = attributes;
37
+ const blockControls = (
38
+ <BlockControls>
39
+ {views.length > 0 && (
40
+ <Toolbar>
41
+ <Button label={__('Edit View')} icon="edit" target="_blank" />
42
+ </Toolbar>
43
+ )}
44
+ </BlockControls>
45
+ );
46
+ if (status === 'loading') {
47
+ return [
48
+ <Fragment>
49
+ <div className="st-block-preview">
50
+ <div className="st-block-preview__content">
51
+ <div className="st-block-preview__logo"> </div>
52
+ <Spinner />
53
+ </div>
54
+ </div>
55
+ </Fragment>
56
+ ];
57
+ }
58
+
59
+ return [
60
+ <Fragment>
61
+ <Inspector onIdChange={(id) => this.onIdChange(id)} {...this.props} />
62
+ <div className="st-block-preview">
63
+ <div class="st-block-preview__content">
64
+ <div className="st-block-preview__logo" />
65
+ {views.length === 0 && (
66
+ <Fragment>
67
+ <h6>{__("You don't seem to have any views.")}</h6>
68
+ <Button
69
+ href={
70
+ st_views.adminURL +
71
+ 'edit.php?post_type=wpm-testimonial&page=testimonial-views&action=add'
72
+ }
73
+ target="_blank"
74
+ isDefault
75
+ >
76
+ {__('Add New View')}
77
+ </Button>
78
+ </Fragment>
79
+ )}
80
+ {st_views.views.length > 0 && (
81
+ <Fragment>
82
+ <SelectControl
83
+ label="Select a view:"
84
+ className="st-view-select"
85
+ value={id}
86
+ options={this.selectOptions()}
87
+ onChange={(id) => setAttributes({ id })}
88
+ />
89
+ {id != 0 && (
90
+ <Button
91
+ target="_blank"
92
+ href={
93
+ st_views.adminURL +
94
+ 'edit.php?post_type=wpm-testimonial&page=testimonial-views&action=edit&id=' +
95
+ id
96
+ }
97
+ isSecondary
98
+ >
99
+ {__('Edit Settings')}
100
+ </Button>
101
+ )}
102
+ </Fragment>
103
+ )}
104
+ </div>
105
+ </div>
106
+ </Fragment>
107
+ ];
108
+ }
109
+ }
assets/src/js/components/inspector.js ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ const { __ } = wp.i18n;
5
+ const { Component, Fragment } = wp.element;
6
+ const { InspectorControls } = wp.blockEditor;
7
+ const { SelectControl, Button, PanelBody, PanelRow } = wp.components;
8
+
9
+ /**
10
+ * Inspector controls
11
+ */
12
+ export default class Inspector extends Component {
13
+ constructor(props) {
14
+ super(...arguments);
15
+ }
16
+
17
+ selectOptions() {
18
+ let options = [ { value: 0, label: __('none') } ];
19
+
20
+ this.props.attributes.views.forEach(function(view) {
21
+ options.push({ value: view.id, label: view.name });
22
+ });
23
+
24
+ return options;
25
+ }
26
+
27
+ render() {
28
+ const { attributes, setAttributes } = this.props;
29
+ const { id, views, testimonials } = attributes;
30
+ return (
31
+ <Fragment>
32
+ <InspectorControls>
33
+ <PanelBody title={__('View Settings')} initialOpen={true}>
34
+ {views.length === 0 && (
35
+ <Fragment>
36
+ <p>{__("You don't seem to have any views.")}</p>
37
+ <Button
38
+ href={
39
+ st_views.adminURL +
40
+ 'edit.php?post_type=wpm-testimonial&page=testimonial-views&action=add'
41
+ }
42
+ target="_blank"
43
+ isDefault
44
+ >
45
+ {__('Add New View')}
46
+ </Button>
47
+ </Fragment>
48
+ )}
49
+
50
+ {views.length > 0 && (
51
+ <Fragment>
52
+ <SelectControl
53
+ label={__('Select View')}
54
+ value={id}
55
+ options={this.selectOptions()}
56
+ onChange={(id) => setAttributes({ id })}
57
+ />
58
+ {id != 0 && (
59
+ <Button
60
+ target="_blank"
61
+ href={
62
+ st_views.adminURL +
63
+ 'edit.php?post_type=wpm-testimonial&page=testimonial-views&action=edit&id=' +
64
+ id
65
+ }
66
+ isSecondary
67
+ >
68
+ {__('Edit View')}
69
+ </Button>
70
+ )}
71
+ </Fragment>
72
+ )}
73
+ </PanelBody>
74
+ </InspectorControls>
75
+ </Fragment>
76
+ );
77
+ }
78
+ }
assets/src/scss/blocks.scss ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ div.st-block-preview {
2
+ background: white;
3
+ padding: 50px 25px;
4
+ border: 1px solid black;
5
+ }
6
+
7
+ div.st-view-select {
8
+ width: fit-content;
9
+ min-width: 100px;
10
+ }
11
+
12
+ div.st-view-select label.components-input-control__label {
13
+ margin-bottom: 10px!important;
14
+ font-weight: 700;
15
+ }
16
+
17
+ div.st-view-select div.components-input-control__container select.components-select-control__input{
18
+ height: fit-content;
19
+ }
changelog.txt CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  = 2.50.3 =
2
  * Fixed upsells option
3
  * Fixed Slideshow Stretch behaviour.
1
+ = 2.50.4 =
2
+ * Added Welcome Banner when Strong Testimonials is activated.
3
+ * Removed uninstall message that was appearing in WP dashboard when using multisite network.
4
+ * Removed Lazy Loading Setting for WP versions higher than 5.5 when the core LazyLoad is enabled.
5
+ * Fixed typo from Fields.
6
+
7
  = 2.50.3 =
8
  * Fixed upsells option
9
  * Fixed Slideshow Stretch behaviour.
includes/class-strong-gutemberg.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class that handles all the assets and includes for every block
5
+ *
6
+ * @since 2.40.5
7
+ */
8
+
9
+ class Strong_Gutemberg {
10
+
11
+ public function __construct() {
12
+ add_action( 'init', array( $this,'register_block_type') );
13
+ add_action( 'init', array( $this,'generate_js_vars') );
14
+ }
15
+
16
+ public function register_block_type() {
17
+
18
+ wp_register_script( 'st-block-js', WPMTST_URL . 'assets/js/blocks-js.js', array( 'wp-i18n', 'wp-element', 'wp-editor', 'wp-blocks', 'wp-components', 'wp-api', 'wp-data'), WPMTST_VERSION );
19
+
20
+ wp_register_style( 'st-block-css', WPMTST_URL . 'assets/css/blocks.css', array(), WPMTST_VERSION );
21
+
22
+ register_block_type( 'strongtestimonials/view', array(
23
+ 'render_callback' => array( $this, 'render_view' ),
24
+ 'editor_script' => 'st-block-js',
25
+ 'editor_style' => 'st-block-css',
26
+ ));
27
+
28
+ }
29
+
30
+ public function generate_js_vars() {
31
+
32
+ wp_localize_script(
33
+ 'st-block-js', 'st_views', array(
34
+ 'adminURL' => admin_url(),
35
+ 'ajaxURL' => admin_url( 'admin-ajax.php' ),
36
+ 'views' => wpmtst_unserialize_views( wpmtst_get_views() ),
37
+ )
38
+ );
39
+ }
40
+
41
+ public function render_view( $attributes ) {
42
+
43
+ if( 0 == count( $attributes ) ) {
44
+ return;
45
+ }
46
+
47
+ if( '0' == $attributes['id'] ) {
48
+ return;
49
+ }
50
+
51
+ return "[testimonial_view id={$attributes['id']}]";
52
+ }
53
+
54
+ }
55
+
56
+ new Strong_Gutemberg();
includes/class-strong-view-display.php CHANGED
@@ -525,12 +525,13 @@ class Strong_View_Display extends Strong_View {
525
  * @since 2.40.4
526
  */
527
  public function has_lazyload() {
528
-
529
- $options = get_option( 'wpmtst_options' );
530
- if ( isset( $options['lazyload'] ) && $options['lazyload'] ) {
531
- WPMST()->render->add_style( 'wpmtst-lazyload-css' );
532
- WPMST()->render->add_script( 'wpmtst-lozad' );
533
- WPMST()->render->add_script( 'wpmtst-lozad-load' );
 
534
  }
535
  }
536
 
525
  * @since 2.40.4
526
  */
527
  public function has_lazyload() {
528
+ if( !function_exists( 'wp_lazy_loading_enabled' ) || !apply_filters( 'wp_lazy_loading_enabled', true ) ) {
529
+ $options = get_option( 'wpmtst_options' );
530
+ if ( isset( $options['lazyload'] ) && $options['lazyload'] ) {
531
+ WPMST()->render->add_style( 'wpmtst-lazyload-css' );
532
+ WPMST()->render->add_script( 'wpmtst-lozad' );
533
+ WPMST()->render->add_script( 'wpmtst-lozad-load' );
534
+ }
535
  }
536
  }
537
 
includes/class-strong-view-slideshow.php CHANGED
@@ -484,11 +484,13 @@ class Strong_View_Slideshow extends Strong_View_Display {
484
  * @since 2.40.4
485
  */
486
  public function has_lazyload() {
487
- $options = get_option( 'wpmtst_options' );
488
- if ( isset( $options['lazyload'] ) && $options['lazyload'] ) {
489
- WPMST()->render->add_style( 'wpmtst-lazyload-css' );
490
- WPMST()->render->add_script( 'wpmtst-lozad' );
491
- WPMST()->render->add_script( 'wpmtst-lozad-load' );
 
 
492
  }
493
  }
494
 
484
  * @since 2.40.4
485
  */
486
  public function has_lazyload() {
487
+ if( !function_exists( 'wp_lazy_loading_enabled' ) || !apply_filters( 'wp_lazy_loading_enabled', true ) ) {
488
+ $options = get_option( 'wpmtst_options' );
489
+ if ( isset( $options['lazyload'] ) && $options['lazyload'] ) {
490
+ WPMST()->render->add_style( 'wpmtst-lazyload-css' );
491
+ WPMST()->render->add_script( 'wpmtst-lozad' );
492
+ WPMST()->render->add_script( 'wpmtst-lozad-load' );
493
+ }
494
  }
495
  }
496
 
includes/elementor/class-strong-elementor-check.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if (!defined('ABSPATH'))
3
+ exit; // Exit if accessed directly
4
+
5
+ final class Strong_Testimonials_Elementor_Check {
6
+
7
+ /**
8
+ * Plugin Version
9
+ *
10
+ * @since 2.40.5
11
+ * @var string The plugin version.
12
+ */
13
+ const VERSION = '2.40.5';
14
+
15
+ /**
16
+ * Minimum Elementor Version
17
+ *
18
+ * @since 2.40.5
19
+ * @var string Minimum Elementor version required to run the elementor block.
20
+ */
21
+ const MINIMUM_ELEMENTOR_VERSION = '2.4.5';
22
+
23
+ /**
24
+ * Minimum PHP Version
25
+ *
26
+ * @since 2.40.5
27
+ * @var string Minimum PHP version required to run the elementor block.
28
+ */
29
+ const MINIMUM_PHP_VERSION = '7.0';
30
+
31
+ /**
32
+ * Constructor
33
+ *
34
+ * @since 2.40.5
35
+ * @access public
36
+ */
37
+ public function __construct() {
38
+
39
+ // Init Plugin
40
+ add_action('plugins_loaded', array($this, 'init'));
41
+ }
42
+
43
+ public function init() {
44
+
45
+ if ( ! did_action( 'elementor/loaded' ) ) {
46
+ return;
47
+ }
48
+
49
+ // Check for required Elementor version
50
+ if (!version_compare(ELEMENTOR_VERSION, self::MINIMUM_ELEMENTOR_VERSION, '>=')) {
51
+ add_action('admin_notices', array($this, 'admin_notice_minimum_elementor_version'));
52
+ return;
53
+ }
54
+
55
+ // Check for required PHP version
56
+ if (version_compare(PHP_VERSION, self::MINIMUM_PHP_VERSION, '<')) {
57
+ add_action('admin_notices', array($this, 'admin_notice_minimum_php_version'));
58
+ return;
59
+ }
60
+
61
+ add_action('elementor/widgets/widgets_registered', array( $this, 'remove_strong_testimonials_widget' ), 15);
62
+
63
+ // Once we get here, We have passed all validation checks so we can safely include our elementor block activation
64
+ require_once( WPMTST_INC.'elementor/class-strong-elementor-widget-activation.php' );
65
+ }
66
+
67
+
68
+ /**
69
+ * Admin notice
70
+ *
71
+ * Warning when the site doesn't have a minimum required Elementor version.
72
+ *
73
+ * @since 2.40.5
74
+ * @access public
75
+ */
76
+ public function admin_notice_minimum_elementor_version() {
77
+ if (isset($_GET['activate'])) {
78
+ unset($_GET['activate']);
79
+ }
80
+
81
+ $message = sprintf(
82
+ esc_html__('"%1$s" requires "%2$s" version %3$s or greater.', 'strong-testimonials'),
83
+ '<strong>' . esc_html__('Strong Testimonials Elementor widget', 'strong-testimonials') . '</strong>',
84
+ '<strong>' . esc_html__('Elementor', 'strong-testimonials') . '</strong>',
85
+ self::MINIMUM_ELEMENTOR_VERSION
86
+ );
87
+
88
+ printf('<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message);
89
+ }
90
+
91
+ /**
92
+ * Admin notice
93
+ *
94
+ * Warning when the site doesn't have a minimum required PHP version.
95
+ *
96
+ * @since 1.0.0
97
+ * @access public
98
+ */
99
+ public function admin_notice_minimum_php_version() {
100
+ if (isset($_GET['activate'])) {
101
+ unset($_GET['activate']);
102
+ }
103
+
104
+ $message = sprintf(
105
+ esc_html__('"%1$s" requires "%2$s" version %3$s or greater.', 'strong-testimonials'),
106
+ '<strong>' . esc_html__('Strong Testimonials Elementor widget', 'strong-testimonials') . '</strong>',
107
+ '<strong>' . esc_html__('PHP', 'strong-testimonials') . '</strong>',
108
+ self::MINIMUM_PHP_VERSION
109
+ );
110
+
111
+ printf('<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message);
112
+ }
113
+
114
+ /* Remove WordPress widget because we have a dedicated Elementor Widget */
115
+ public function remove_strong_testimonials_widget( $widget_manager ){
116
+ $widget_manager->unregister_widget_type( 'strong-testimonials-view-widget' );
117
+ }
118
+ }
119
+
120
+ new Strong_Testimonials_Elementor_Check();
includes/elementor/class-strong-elementor-widget-activation.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace ElementorStrongTestimonials;
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ } // Exit if accessed directly
7
+
8
+ class Strong_Testimonials_Elementor_Widget_Activation {
9
+
10
+ private static $_instance = null;
11
+
12
+ public static function instance() {
13
+ if ( is_null( self::$_instance ) ) {
14
+ self::$_instance = new self();
15
+ }
16
+
17
+ return self::$_instance;
18
+ }
19
+
20
+ private function include_widgets_files() {
21
+ require_once WPMTST_INC . 'elementor/class-strong-elementor.php' ;
22
+ }
23
+
24
+ /**
25
+ * Register Widgets
26
+ *
27
+ * Register new Elementor widgets.
28
+ *
29
+ * @since 1.2.0
30
+ * @access public
31
+ */
32
+ public function register_widgets() {
33
+ $this->include_widgets_files();
34
+ // Register Widgets
35
+ \Elementor\Plugin::instance()->widgets_manager->register_widget_type( new Widgets\Strong_Testimonials_Elementor_Widget() );
36
+ }
37
+
38
+ public function __construct() {
39
+
40
+ // Register widgets
41
+ add_action( 'elementor/widgets/widgets_registered', array( $this, 'register_widgets' ) );
42
+
43
+ // Enqueue needed scripts for elementor Editor
44
+ add_action( 'elementor/editor/before_enqueue_scripts', array($this, 'strong_testimonials_elementor_enqueue_editor_scripts' ));
45
+
46
+ // Enqueue needed scripts and styles in Elementor preview
47
+ add_action( 'elementor/preview/enqueue_scripts', array( $this, 'strong_testimonials_elementor_enqueue_scripts' ) );
48
+ add_action('wp_ajax_strong_testimonials_elementor_ajax_search',array($this,'strong_testimonials_elementor_ajax_search'));
49
+
50
+ }
51
+
52
+ public function strong_testimonials_elementor_enqueue_editor_scripts() {
53
+
54
+ wp_enqueue_script( 'strong-testimonials-elementor-editor', WPMTST_URL . 'admin/js/strong-testimonials-elementor-editor.js', null, WPMTST_VERSION, true );
55
+ wp_localize_script('strong-testimonials-elementor-editor', 'strongAjax', array(
56
+ 'ajax_url' => admin_url( 'admin-ajax.php' )
57
+ ));
58
+
59
+ wp_enqueue_script( 'st-selectize', WPMTST_URL . 'admin/js/selectize.js', null, WPMTST_VERSION, true );
60
+ wp_enqueue_style( 'st-selectize-css', WPMTST_URL . 'admin/css/selectize.default.css' );
61
+ }
62
+
63
+ /**
64
+ * Enqueue scripts in Elementor preview
65
+ */
66
+ public function strong_testimonials_elementor_enqueue_scripts() {
67
+
68
+
69
+ }
70
+
71
+ public function strong_testimonials_elementor_ajax_search() {
72
+
73
+ if( 'strong_testimonials_elementor_ajax_search' == $_POST['action'] ) {
74
+ $galleries = wpmtst_unserialize_views( wpmtst_get_views() );
75
+
76
+ wp_send_json_success( $galleries );
77
+ } else {
78
+ wp_send_json_error();
79
+ }
80
+ die();
81
+ }
82
+ }
83
+
84
+ // Instantiate Plugin Class
85
+ Strong_Testimonials_Elementor_Widget_Activation::instance();
includes/elementor/class-strong-elementor.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Handles the testimonial widget
5
+ */
6
+
7
+ namespace ElementorStrongTestimonials\Widgets;
8
+
9
+ use Elementor\Widget_Base;
10
+ use Elementor\Controls_Manager;
11
+
12
+ if( !defined( 'ABSPATH') ) {
13
+ exit;
14
+ }
15
+
16
+ class Strong_Testimonials_Elementor_Widget extends \Elementor\Widget_Base {
17
+
18
+ public function get_name() {
19
+ return 'strong_testimonials_elementor_views';
20
+ }
21
+
22
+ public function get_title() {
23
+ return esc_html__( 'Strong Testimonials', 'strong-testimonials');
24
+ }
25
+
26
+ public function get_icon() {
27
+ return 'eicon-editor-quote';
28
+ }
29
+
30
+ public function get_cattegories() {
31
+ return array( 'general' );
32
+ }
33
+
34
+ protected function _register_controls() {
35
+ $this->start_controls_section(
36
+ 'content_section',
37
+ array(
38
+ 'label' => esc_html__( 'Content', 'strong-testimonials'),
39
+ 'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
40
+ )
41
+ );
42
+
43
+ $this->add_control(
44
+ 'strong_testimonials_views_select',
45
+ array(
46
+ 'label' => esc_html__( 'Select/Search View', 'strong-testimonials'),
47
+ 'type' => \Elementor\Controls_Manager::SELECT,
48
+ 'options' => get_formatted_views(),
49
+ 'default' => 'none',
50
+ )
51
+ );
52
+
53
+ $this->end_controls_section();
54
+
55
+ }
56
+
57
+ protected function render() {
58
+ $settings = $this->get_settings_for_display();
59
+ $view_id = $settings['strong_testimonials_views_select'];
60
+ if( 'none' != $view_id ){
61
+ echo "[testimonial_view id={$view_id}]";
62
+ }
63
+ }
64
+ }
includes/functions-image.php CHANGED
@@ -124,15 +124,17 @@ add_action( 'init', 'wpmtst_lazyload_check' );
124
  * @return array
125
  */
126
  function wpmtst_add_lazyload( $attr, $attachment, $size ) {
127
- $options = get_option( 'wpmtst_options' );
128
-
129
- if ( isset( $options['lazyload'] ) && $options['lazyload']) {
130
- if ( 'wpm-testimonial' == get_post_type( $attachment->post_parent ) && !is_admin() ) {
131
- $attr['class'] .= ' lazy-load';
132
- $attr['data-src'] = $attr['src'];
133
- $attr['data-srcset'] = $attr['srcset'];
134
- unset($attr['src']);
135
- unset($attr['srcset']);
 
 
136
  }
137
  }
138
 
124
  * @return array
125
  */
126
  function wpmtst_add_lazyload( $attr, $attachment, $size ) {
127
+ if( !function_exists( 'wp_lazy_loading_enabled' ) || !apply_filters( 'wp_lazy_loading_enabled', true ) ) {
128
+ $options = get_option( 'wpmtst_options' );
129
+
130
+ if ( isset( $options['lazyload'] ) && $options['lazyload']) {
131
+ if ( 'wpm-testimonial' == get_post_type( $attachment->post_parent ) && !is_admin() ) {
132
+ $attr['class'] .= ' lazy-load';
133
+ $attr['data-src'] = $attr['src'];
134
+ $attr['data-srcset'] = $attr['srcset'];
135
+ unset($attr['src']);
136
+ unset($attr['srcset']);
137
+ }
138
  }
139
  }
140
 
includes/functions.php CHANGED
@@ -896,3 +896,15 @@ if ( ! function_exists( 'wpmtst_current_url' ) ) {
896
  return home_url( add_query_arg( array(), $wp->request ) );
897
  }
898
  }
 
 
 
 
 
 
 
 
 
 
 
 
896
  return home_url( add_query_arg( array(), $wp->request ) );
897
  }
898
  }
899
+ if ( ! function_exists( 'get_formatted_views' ) ) {
900
+
901
+ function get_formatted_views() {
902
+ $views = wpmtst_get_views() ;
903
+
904
+ $view_array = array( 'none' => esc_html__( 'None', 'strong-testimonials'));
905
+ foreach( $views as $view ) {
906
+ $view_array[$view['id']] = esc_html__( $view['name'] );
907
+ }
908
+ return $view_array;
909
+ }
910
+ }
includes/strong-testimonials-beaver-block/class-strong-beaver-block.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly.
3
+ if (!defined('ABSPATH')) {
4
+ exit;
5
+ }
6
+
7
+ class Strong_Testimonials_Beaver_Block extends FLBuilderModule {
8
+ public function __construct() {
9
+ parent::__construct(array(
10
+ 'name' => esc_html__( 'Strong Testimonials', 'strong-testimonials' ),
11
+ 'description' => esc_html__( 'A block for Strong Testimonials Views', 'strong-testimonials' ),
12
+ 'category' => esc_html__( 'Strong Testimonials', 'strong-testimonials' ),
13
+ 'icon' => 'format-image.svg',
14
+ 'dir' => WPMTST_DIR . 'includes/strong-testimonials-beaver-block/',
15
+ 'url' => WPMTST_URL . 'includes/strong-testimonials-beaver-block/',
16
+ 'partial_refresh' => true,
17
+ ));
18
+ }
19
+ }
20
+
21
+ FLBuilder::register_module('Strong_Testimonials_Beaver_Block', array(
22
+ 'strong_testimonials' => array(
23
+ 'title' => esc_html__( 'Strong Testimonials', 'strong-testimonials' ),
24
+ 'sections' => array(
25
+ 'strong_testimonials_view_section' => array(
26
+ 'title' => esc_html__( 'Select the Strong Testimonials View you want', 'strong-testimonials' ),
27
+ 'fields' => array(
28
+ 'strong_testimonials_view_select' => array(
29
+ 'type' => 'select',
30
+ 'label' => esc_html__( 'Select Strong Testimonials View', 'strong-testimonials' ),
31
+ 'default' => 'none',
32
+ 'options' => get_formatted_views(),
33
+ )
34
+ )
35
+ )
36
+ )
37
+ )
38
+ ));
39
+
40
+
includes/strong-testimonials-beaver-block/class-strong-beaver.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly.
3
+ if (!defined('ABSPATH')) {
4
+ exit;
5
+ }
6
+
7
+ class Strong_Testimonials_Beaver {
8
+
9
+ /**
10
+ * Strong_Testimonials_Beaver constructor.
11
+ */
12
+ public function __construct() {
13
+ add_action('init', array($this, 'include_beaver_block'));
14
+ }
15
+
16
+ /**
17
+ * Include ST Beaver Block
18
+ */
19
+ public function include_beaver_block() {
20
+ if (class_exists('FLBuilder')) {
21
+ require_once WPMTST_DIR . 'includes/strong-testimonials-beaver-block/class-strong-beaver-block.php' ;
22
+ }
23
+ }
24
+
25
+ }
26
+
27
+ $strong_beaver = new Strong_Testimonials_Beaver();
includes/strong-testimonials-beaver-block/includes/frontend.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly.
3
+ if (!defined('ABSPATH')) {
4
+ exit;
5
+ }
6
+
7
+ $view_id = $settings->strong_testimonials_view_select;
8
+ if ('none' != $view_id) {
9
+ echo do_shortcode("[testimonial_view id='{$view_id}']");
10
+
11
+ } else {
12
+ echo esc_html__( 'No view was selected', 'strong-testimonials' );
13
+ }
public/js/lib/form-validation/form-validation.js CHANGED
@@ -76,7 +76,7 @@ var strongValidation = {
76
 
77
  // Add protocol if missing
78
  // Thanks http://stackoverflow.com/a/36429927/51600
79
- jQuery('input[type=url]').change(function () {
80
  if (this.value.length && !/^https*:\/\//.test(this.value)) {
81
  this.value = 'https://' + this.value;
82
  }
76
 
77
  // Add protocol if missing
78
  // Thanks http://stackoverflow.com/a/36429927/51600
79
+ jQuery('input[type=url]').on( 'change', function () {
80
  if (this.value.length && !/^https*:\/\//.test(this.value)) {
81
  this.value = 'https://' + this.value;
82
  }
public/js/lib/form-validation/form-validation.min.js CHANGED
@@ -1 +1 @@
1
- var strongValidation={defaults:{ajaxUrl:"",display:{successMessage:!1},scroll:{onError:!0,onErrorOffset:100,onSuccess:!0,onSuccessOffset:100},fields:{}},settings:{},setOpts:function(e){this.settings=jQuery.extend({},this.defaults,e)},rules:{},setRules:function(){for(var e=0;e<this.settings.fields.length;e++)"rating"===this.settings.fields[e].type&&1===this.settings.fields[e].required&&(this.rules[this.settings.fields[e].name]={ratingRequired:!0})},init:function(){var e={};void 0!==window.strongForm&&(e=window.strongForm),this.setOpts(e),this.settings.display.successMessage?this.scrollOnSuccess():(this.setRules(),this.changeEvents(),this.customValidators(),this.validateForm())},changeEvents:function(){jQuery('input[type="text"], input[type="url"], input[type="email"], textarea',"#wpmtst-submission-form").on("change blur",function(e){e.target.value=e.target.value.trim()}),jQuery("input[type=url]").change(function(){this.value.length&&!/^https*:\/\//.test(this.value)&&(this.value="https://"+this.value)});for(var e=document.getElementsByClassName("strong-rating"),s=0;s<e.length;s++)e[s].addEventListener("click",this.handleRadioEvent,!0),e[s].addEventListener("keyup",this.handleRadioEvent,!0),e[s].addEventListener("change",function(){jQuery(this).valid()},!0)},handleRadioEvent:function(e){if(e.keyCode>=48&&e.keyCode<=53){var s=e.keyCode-48;jQuery(this).find('input[type="radio"][value='+s+"]").click()}},customValidators:function(){jQuery.validator.addMethod("ratingRequired",function(e,s){return jQuery(s).find("input:checked").val()>0},jQuery.validator.messages.required)},validateForm:function(){jQuery("#wpmtst-submission-form").validate({onfocusout:!1,focusInvalid:!1,invalidHandler:function(e,s){if(s.numberOfInvalids())if(strongValidation.settings.scroll.onError){if(void 0!==s.errorList[0]){var t=jQuery(s.errorList[0].element),i=t.closest(".form-field").offset().top-strongValidation.settings.scroll.onErrorOffset;jQuery("html, body").animate({scrollTop:i},800,function(){t.focus()})}}else s.errorList[0].element.focus()},submitHandler:function(e){if(strongValidation.disableForm(),""!==strongValidation.settings.ajaxUrl){window.onbeforeunload=function(){return"Please wait while the form is submitted."};var s={url:strongValidation.settings.ajaxUrl,data:{action:"wpmtst_form2"},success:strongValidation.showResponse};jQuery(e).ajaxSubmit(s)}else e.submit()},rules:strongValidation.rules,errorPlacement:function(e,s){e.appendTo(s.closest("div.form-field"))},highlight:function(e,s,t){"checkbox"===e.type?jQuery(e).closest(".field-wrap").addClass(s).removeClass(t):"rating"===jQuery(e).data("fieldType")?jQuery(e).closest(".field-wrap").addClass(s).removeClass(t):jQuery(e).addClass(s).removeClass(t)},unhighlight:function(e,s,t){"checkbox"===e.type?jQuery(e).closest(".field-wrap").removeClass(s).addClass(t):"rating"===jQuery(e).data("fieldType")?jQuery(e).closest(".field-wrap").removeClass(s).addClass(t):jQuery(e).removeClass(s).addClass(t)}})},showResponse:function(e){window.onbeforeunload=null,strongValidation.enableForm();var s=JSON.parse(e);if(s.success)jQuery("#wpmtst-form").html(s.message),strongValidation.scrollOnSuccess();else for(var t in s.errors)s.errors.hasOwnProperty(t)&&jQuery("#wpmtst-submission-form").children(".field-"+t).find("span.error").remove().end().append('<span class="error">'+s.errors[t]+"</span>")},scrollOnSuccess:function(){var e,s;strongValidation.settings.scroll.onSuccess&&((e=jQuery(".wpmtst-testimonial-success").offset())&&(s=e.top-strongValidation.settings.scroll.onSuccessOffset,jQuery("#wpadminbar").length&&(s-=32),jQuery("html, body").animate({scrollTop:s},800)))},disableForm:function(){jQuery(".strong-form-wait").show(),jQuery("#wpmtst_submit_testimonial").prop("disabled",!0)},enableForm:function(){jQuery(".strong-form-wait").hide(),jQuery("#wpmtst_submit_testimonial").prop("disabled",!1)}};
1
+ var strongValidation={defaults:{ajaxUrl:"",display:{successMessage:!1},scroll:{onError:!0,onErrorOffset:100,onSuccess:!0,onSuccessOffset:100},fields:{}},settings:{},setOpts:function(a){this.settings=jQuery.extend({},this.defaults,a)},rules:{},setRules:function(){for(var a=0;a<this.settings.fields.length;a++)"rating"===this.settings.fields[a].type&&1===this.settings.fields[a].required&&(this.rules[this.settings.fields[a].name]={ratingRequired:!0})},init:function(){var a={};"undefined"!=typeof window.strongForm&&(a=window.strongForm),this.setOpts(a),this.settings.display.successMessage?this.scrollOnSuccess():(this.setRules(),this.changeEvents(),this.customValidators(),this.validateForm())},changeEvents:function(){jQuery('input[type="text"], input[type="url"], input[type="email"], textarea',"#wpmtst-submission-form").on("change blur",function(a){a.target.value=a.target.value.trim()}),jQuery("input[type=url]").on("change",function(){this.value.length&&!/^https*:\/\//.test(this.value)&&(this.value="https://"+this.value)});for(var a=document.getElementsByClassName("strong-rating"),b=0;b<a.length;b++)a[b].addEventListener("click",this.handleRadioEvent,!0),a[b].addEventListener("keyup",this.handleRadioEvent,!0),a[b].addEventListener("change",function(){jQuery(this).valid()},!0)},handleRadioEvent:function(a){if(a.keyCode>=48&&a.keyCode<=53){var b=a.keyCode-48;jQuery(this).find('input[type="radio"][value='+b+"]").click()}},customValidators:function(){jQuery.validator.addMethod("ratingRequired",function(a,b){return jQuery(b).find("input:checked").val()>0},jQuery.validator.messages.required)},validateForm:function(){var a=jQuery("#wpmtst-submission-form");a.validate({onfocusout:!1,focusInvalid:!1,invalidHandler:function(a,b){var c=b.numberOfInvalids();if(c)if(strongValidation.settings.scroll.onError){if("undefined"!=typeof b.errorList[0]){var d=jQuery(b.errorList[0].element),e=d.closest(".form-field").offset(),f=e.top-strongValidation.settings.scroll.onErrorOffset;jQuery("html, body").animate({scrollTop:f},800,function(){d.focus()})}}else b.errorList[0].element.focus()},submitHandler:function(a){if(strongValidation.disableForm(),""!==strongValidation.settings.ajaxUrl){window.onbeforeunload=function(){return"Please wait while the form is submitted."};var b={url:strongValidation.settings.ajaxUrl,data:{action:"wpmtst_form2"},success:strongValidation.showResponse};jQuery(a).ajaxSubmit(b)}else a.submit()},rules:strongValidation.rules,errorPlacement:function(a,b){a.appendTo(b.closest("div.form-field"))},highlight:function(a,b,c){"checkbox"===a.type?jQuery(a).closest(".field-wrap").addClass(b).removeClass(c):"rating"===jQuery(a).data("fieldType")?jQuery(a).closest(".field-wrap").addClass(b).removeClass(c):jQuery(a).addClass(b).removeClass(c)},unhighlight:function(a,b,c){"checkbox"===a.type?jQuery(a).closest(".field-wrap").removeClass(b).addClass(c):"rating"===jQuery(a).data("fieldType")?jQuery(a).closest(".field-wrap").removeClass(b).addClass(c):jQuery(a).removeClass(b).addClass(c)}})},showResponse:function(a){window.onbeforeunload=null,strongValidation.enableForm();var b=JSON.parse(a);if(b.success)jQuery("#wpmtst-form").html(b.message),strongValidation.scrollOnSuccess();else for(var c in b.errors)b.errors.hasOwnProperty(c)&&jQuery("#wpmtst-submission-form").children(".field-"+c).find("span.error").remove().end().append('<span class="error">'+b.errors[c]+"</span>")},scrollOnSuccess:function(){if(strongValidation.settings.scroll.onSuccess){var a,b;a=jQuery(".wpmtst-testimonial-success").offset(),a&&(b=a.top-strongValidation.settings.scroll.onSuccessOffset,jQuery("#wpadminbar").length&&(b-=32),jQuery("html, body").animate({scrollTop:b},800))}},disableForm:function(){jQuery(".strong-form-wait").show(),jQuery("#wpmtst_submit_testimonial").prop("disabled",!0)},enableForm:function(){jQuery(".strong-form-wait").hide(),jQuery("#wpmtst_submit_testimonial").prop("disabled",!1)}},strongValidation={defaults:{ajaxUrl:"",display:{successMessage:!1},scroll:{onError:!0,onErrorOffset:100,onSuccess:!0,onSuccessOffset:100},fields:{}},settings:{},setOpts:function(a){this.settings=jQuery.extend({},this.defaults,a)},rules:{},setRules:function(){for(var a=0;a<this.settings.fields.length;a++)"rating"===this.settings.fields[a].type&&1===this.settings.fields[a].required&&(this.rules[this.settings.fields[a].name]={ratingRequired:!0})},init:function(){var a={};void 0!==window.strongForm&&(a=window.strongForm),this.setOpts(a),this.settings.display.successMessage?this.scrollOnSuccess():(this.setRules(),this.changeEvents(),this.customValidators(),this.validateForm())},changeEvents:function(){jQuery('input[type="text"], input[type="url"], input[type="email"], textarea',"#wpmtst-submission-form").on("change blur",function(a){a.target.value=a.target.value.trim()}),jQuery("input[type=url]").change(function(){this.value.length&&!/^https*:\/\//.test(this.value)&&(this.value="https://"+this.value)});for(var a=document.getElementsByClassName("strong-rating"),b=0;b<a.length;b++)a[b].addEventListener("click",this.handleRadioEvent,!0),a[b].addEventListener("keyup",this.handleRadioEvent,!0),a[b].addEventListener("change",function(){jQuery(this).valid()},!0)},handleRadioEvent:function(a){if(a.keyCode>=48&&a.keyCode<=53){var b=a.keyCode-48;jQuery(this).find('input[type="radio"][value='+b+"]").click()}},customValidators:function(){jQuery.validator.addMethod("ratingRequired",function(a,b){return jQuery(b).find("input:checked").val()>0},jQuery.validator.messages.required)},validateForm:function(){jQuery("#wpmtst-submission-form").validate({onfocusout:!1,focusInvalid:!1,invalidHandler:function(a,b){if(b.numberOfInvalids())if(strongValidation.settings.scroll.onError){if(void 0!==b.errorList[0]){var c=jQuery(b.errorList[0].element),d=c.closest(".form-field").offset().top-strongValidation.settings.scroll.onErrorOffset;jQuery("html, body").animate({scrollTop:d},800,function(){c.focus()})}}else b.errorList[0].element.focus()},submitHandler:function(a){if(strongValidation.disableForm(),""!==strongValidation.settings.ajaxUrl){window.onbeforeunload=function(){return"Please wait while the form is submitted."};var b={url:strongValidation.settings.ajaxUrl,data:{action:"wpmtst_form2"},success:strongValidation.showResponse};jQuery(a).ajaxSubmit(b)}else a.submit()},rules:strongValidation.rules,errorPlacement:function(a,b){a.appendTo(b.closest("div.form-field"))},highlight:function(a,b,c){"checkbox"===a.type?jQuery(a).closest(".field-wrap").addClass(b).removeClass(c):"rating"===jQuery(a).data("fieldType")?jQuery(a).closest(".field-wrap").addClass(b).removeClass(c):jQuery(a).addClass(b).removeClass(c)},unhighlight:function(a,b,c){"checkbox"===a.type?jQuery(a).closest(".field-wrap").removeClass(b).addClass(c):"rating"===jQuery(a).data("fieldType")?jQuery(a).closest(".field-wrap").removeClass(b).addClass(c):jQuery(a).removeClass(b).addClass(c)}})},showResponse:function(a){window.onbeforeunload=null,strongValidation.enableForm();var b=JSON.parse(a);if(b.success)jQuery("#wpmtst-form").html(b.message),strongValidation.scrollOnSuccess();else for(var c in b.errors)b.errors.hasOwnProperty(c)&&jQuery("#wpmtst-submission-form").children(".field-"+c).find("span.error").remove().end().append('<span class="error">'+b.errors[c]+"</span>")},scrollOnSuccess:function(){var a,b;strongValidation.settings.scroll.onSuccess&&(a=jQuery(".wpmtst-testimonial-success").offset())&&(b=a.top-strongValidation.settings.scroll.onSuccessOffset,jQuery("#wpadminbar").length&&(b-=32),jQuery("html, body").animate({scrollTop:b},800))},disableForm:function(){jQuery(".strong-form-wait").show(),jQuery("#wpmtst_submit_testimonial").prop("disabled",!0)},enableForm:function(){jQuery(".strong-form-wait").hide(),jQuery("#wpmtst_submit_testimonial").prop("disabled",!1)}};
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: wpchill,silkalns,cdillon27
3
  Tags: testimonials, testimonial slider, testimonial form, star ratings
4
  Requires at least: 5.2
5
  Requires PHP: 5.6
6
- Tested up to: 5.5
7
- Stable tag: 2.50.3
8
  License: GPLv3 or later
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
3
  Tags: testimonials, testimonial slider, testimonial form, star ratings
4
  Requires at least: 5.2
5
  Requires PHP: 5.6
6
+ Tested up to: 5.6
7
+ Stable tag: 2.50.4
8
  License: GPLv3 or later
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
strong-testimonials.php CHANGED
@@ -5,7 +5,7 @@
5
  * Description: Collect and display your testimonials or reviews.
6
  * Author: WPChill
7
  * Author URI: https://wpchill.com/
8
- * Version: 2.50.3
9
  * Text Domain: strong-testimonials
10
  * Domain Path: /languages
11
  * Requires: 4.6 or higher
@@ -24,7 +24,6 @@
24
  * NOTE:
25
  * Chris Dillon ownership rights were ceased on: 01/20/2019 06:52:23 PM when ownership was turned over to MachoThemes
26
  * MachoThemes ownership started on: 01/20/2019 06:52:24 PM
27
- * WPChill ownership started on: 5th of November 2020. WPChill is a restructure and rebrand of MachoThemes.
28
  *
29
  * This program is free software; you can redistribute it and/or modify
30
  * it under the terms of the GNU General Public License as published by
@@ -46,7 +45,7 @@ if ( ! defined( 'ABSPATH' ) ) {
46
  exit;
47
  }
48
 
49
- define( 'WPMTST_VERSION', '2.50.3' );
50
  define( 'WPMTST_PLUGIN', plugin_basename( __FILE__ ) ); // strong-testimonials/strong-testimonials.php
51
  define( 'WPMTST', dirname( WPMTST_PLUGIN ) ); // strong-testimonials
52
  defined( 'WPMTST_STORE_URL' ) || define( 'WPMTST_STORE_URL', 'https://strongtestimonials.com' );
@@ -231,6 +230,9 @@ final class Strong_Testimonials {
231
  require_once WPMTST_INC . 'class-strong-testimonials-privacy.php';
232
 
233
  require_once WPMTST_INC . 'class-strong-testimonials-shortcode.php';
 
 
 
234
  require_once WPMTST_INC . 'class-strong-testimonials-shortcode-count.php';
235
  require_once WPMTST_INC . 'class-strong-testimonials-shortcode-average.php';
236
  require_once WPMTST_INC . 'class-strong-testimonials-render.php';
@@ -272,8 +274,9 @@ final class Strong_Testimonials {
272
  require_once WPMTST_ADMIN . 'settings/class-strong-testimonials-settings-general.php';
273
  require_once WPMTST_ADMIN . 'settings/class-strong-testimonials-settings-form.php';
274
  require_once WPMTST_ADMIN . 'settings/class-strong-testimonials-settings-compat.php';
275
- require_once WPMTST_ADMIN . 'settings/class-strong-testimonials-form.php';
276
 
 
277
  require_once WPMTST_ADMIN . 'class-strong-testimonials-addons.php';
278
  require_once WPMTST_ADMIN . 'class-strong-testimonials-defaults.php';
279
  require_once WPMTST_ADMIN . 'class-strong-testimonials-list-table.php';
@@ -328,8 +331,7 @@ final class Strong_Testimonials {
328
  * Plugin setup.
329
  */
330
  add_action( 'init', array( $this, 'l10n_check' ) );
331
- //@todo : delete commented line. For the moment let it be
332
- //add_action( 'init', array( $this, 'reorder_check' ) );
333
 
334
  /**
335
  * Theme support for thumbnails.
@@ -398,16 +400,13 @@ final class Strong_Testimonials {
398
  /**
399
  * Load reorder class if enabled.
400
  */
401
- //@todo : delete commented lines. For the moment let it be
402
- /*public function reorder_check() {
403
- $options = get_option( 'wpmtst_options' );
404
- if ( isset( $options['reorder'] ) && $options['reorder'] ) {
405
- require_once WPMTST_INC . 'class-strong-testimonials-order.php';
406
- }
407
- }*/
408
-
409
-
410
-
411
  /**
412
  * Get att(s).
413
  *
5
  * Description: Collect and display your testimonials or reviews.
6
  * Author: WPChill
7
  * Author URI: https://wpchill.com/
8
+ * Version: 2.50.4
9
  * Text Domain: strong-testimonials
10
  * Domain Path: /languages
11
  * Requires: 4.6 or higher
24
  * NOTE:
25
  * Chris Dillon ownership rights were ceased on: 01/20/2019 06:52:23 PM when ownership was turned over to MachoThemes
26
  * MachoThemes ownership started on: 01/20/2019 06:52:24 PM
 
27
  *
28
  * This program is free software; you can redistribute it and/or modify
29
  * it under the terms of the GNU General Public License as published by
45
  exit;
46
  }
47
 
48
+ define( 'WPMTST_VERSION', '2.50.4' );
49
  define( 'WPMTST_PLUGIN', plugin_basename( __FILE__ ) ); // strong-testimonials/strong-testimonials.php
50
  define( 'WPMTST', dirname( WPMTST_PLUGIN ) ); // strong-testimonials
51
  defined( 'WPMTST_STORE_URL' ) || define( 'WPMTST_STORE_URL', 'https://strongtestimonials.com' );
230
  require_once WPMTST_INC . 'class-strong-testimonials-privacy.php';
231
 
232
  require_once WPMTST_INC . 'class-strong-testimonials-shortcode.php';
233
+ require_once WPMTST_INC . 'class-strong-gutemberg.php';
234
+ require_once WPMTST_INC . 'elementor/class-strong-elementor-check.php';
235
+ require_once WPMTST_INC . 'strong-testimonials-beaver-block/class-strong-beaver.php';
236
  require_once WPMTST_INC . 'class-strong-testimonials-shortcode-count.php';
237
  require_once WPMTST_INC . 'class-strong-testimonials-shortcode-average.php';
238
  require_once WPMTST_INC . 'class-strong-testimonials-render.php';
274
  require_once WPMTST_ADMIN . 'settings/class-strong-testimonials-settings-general.php';
275
  require_once WPMTST_ADMIN . 'settings/class-strong-testimonials-settings-form.php';
276
  require_once WPMTST_ADMIN . 'settings/class-strong-testimonials-settings-compat.php';
277
+ require_once WPMTST_ADMIN . 'settings/class-strong-testimonials-form.php';
278
 
279
+ require_once WPMTST_ADMIN . 'about/class-strong-testimonials-welcome.php';
280
  require_once WPMTST_ADMIN . 'class-strong-testimonials-addons.php';
281
  require_once WPMTST_ADMIN . 'class-strong-testimonials-defaults.php';
282
  require_once WPMTST_ADMIN . 'class-strong-testimonials-list-table.php';
331
  * Plugin setup.
332
  */
333
  add_action( 'init', array( $this, 'l10n_check' ) );
334
+ //add_action( 'init', array( $this, 'reorder_check' ) );
 
335
 
336
  /**
337
  * Theme support for thumbnails.
400
  /**
401
  * Load reorder class if enabled.
402
  */
403
+ // public function reorder_check() {
404
+ // $options = get_option( 'wpmtst_options' );
405
+ // if ( isset( $options['reorder'] ) && $options['reorder'] ) {
406
+ // require_once WPMTST_INC . 'class-strong-testimonials-order.php';
407
+ // }
408
+ // }
409
+
 
 
 
410
  /**
411
  * Get att(s).
412
  *