Version Description
- Feature: added new field types: Heading, Input, Textarea, Select, Select2, Checkbox, Radio
- Feature: added Avatar field which lets your users manage their avatar on your website from the Profile Builder forms
- Feature: added Email Customizer feature which lets you edit all the emails that the plugin sends with custom content and tags for the defined fields
- Fix: issue with licence activation not working on multisite
Download this release
Release Info
Developer | raster02 |
Plugin | User registration & user profile – Profile Builder |
Version | 3.8.1 |
Comparing to | |
See all releases |
Code changes from version 3.8.0 to 3.8.1
- admin/add-ons.php +7 -7
- admin/advanced-settings/includes/fields/remove-all-fields-from-backend.php +8 -0
- admin/advanced-settings/includes/forms/back-end-validation.php +4 -0
- admin/advanced-settings/includes/views/view-fields.php +40 -43
- admin/basic-info.php +9 -12
- admin/general-settings.php +43 -1
- admin/manage-fields.php +59 -12
- admin/register-version.php +1 -1
- assets/lib/Mustache/Autoloader.php +75 -0
- assets/lib/Mustache/Compiler.php +476 -0
- assets/lib/Mustache/Context.php +149 -0
- assets/lib/Mustache/Engine.php +748 -0
- assets/lib/Mustache/Exception.php +18 -0
- assets/lib/Mustache/Exception/InvalidArgumentException.php +18 -0
- assets/lib/Mustache/Exception/LogicException.php +18 -0
- assets/lib/Mustache/Exception/RuntimeException.php +18 -0
- assets/lib/Mustache/Exception/SyntaxException.php +29 -0
- assets/lib/Mustache/Exception/UnknownFilterException.php +29 -0
- assets/lib/Mustache/Exception/UnknownHelperException.php +29 -0
- assets/lib/Mustache/Exception/UnknownTemplateException.php +29 -0
- assets/lib/Mustache/HelperCollection.php +170 -0
- assets/lib/Mustache/LambdaHelper.php +49 -0
- assets/lib/Mustache/Loader.php +28 -0
- assets/lib/Mustache/Loader/ArrayLoader.php +78 -0
- assets/lib/Mustache/Loader/CascadingLoader.php +69 -0
- assets/lib/Mustache/Loader/FilesystemLoader.php +124 -0
- assets/lib/Mustache/Loader/InlineLoader.php +123 -0
- assets/lib/Mustache/Loader/MutableLoader.php +32 -0
- assets/lib/Mustache/Loader/StringLoader.php +40 -0
- assets/lib/Mustache/Logger.php +135 -0
- assets/lib/Mustache/Logger/AbstractLogger.php +121 -0
- assets/lib/Mustache/Logger/StreamLogger.php +193 -0
- assets/lib/Mustache/Parser.php +194 -0
- assets/lib/Mustache/Template.php +177 -0
- assets/lib/Mustache/Tokenizer.php +278 -0
- assets/lib/class-mustache-templates/class-mustache-templates.css +193 -0
- assets/lib/class-mustache-templates/class-mustache-templates.js +80 -0
- assets/lib/class-mustache-templates/class-mustache-templates.php +604 -0
- assets/lib/codemirror/LICENSE +19 -0
- assets/lib/codemirror/README.md +11 -0
- assets/lib/codemirror/addon/comment/comment.js +145 -0
- assets/lib/codemirror/addon/comment/continuecomment.js +54 -0
- assets/lib/codemirror/addon/dialog/dialog.css +32 -0
- assets/lib/codemirror/addon/dialog/dialog.js +80 -0
- assets/lib/codemirror/addon/display/fullscreen.css +6 -0
- assets/lib/codemirror/addon/display/fullscreen.js +30 -0
- assets/lib/codemirror/addon/display/placeholder.js +54 -0
- assets/lib/codemirror/addon/edit/closebrackets.js +82 -0
- assets/lib/codemirror/addon/edit/closetag.js +87 -0
- assets/lib/codemirror/addon/edit/continuelist.js +25 -0
- assets/lib/codemirror/addon/edit/matchbrackets.js +86 -0
- assets/lib/codemirror/addon/edit/matchtags.js +56 -0
- assets/lib/codemirror/addon/edit/trailingspace.js +15 -0
- assets/lib/codemirror/addon/fold/brace-fold.js +93 -0
- assets/lib/codemirror/addon/fold/comment-fold.js +40 -0
- assets/lib/codemirror/addon/fold/foldcode.js +73 -0
- assets/lib/codemirror/addon/fold/foldgutter.js +122 -0
- assets/lib/codemirror/addon/fold/indent-fold.js +12 -0
- assets/lib/codemirror/addon/fold/xml-fold.js +167 -0
- assets/lib/codemirror/addon/hint/anyword-hint.js +34 -0
- assets/lib/codemirror/addon/hint/css-hint.js +50 -0
- assets/lib/codemirror/addon/hint/html-hint.js +337 -0
- assets/lib/codemirror/addon/hint/javascript-hint.js +146 -0
- assets/lib/codemirror/addon/hint/pig-hint.js +119 -0
- assets/lib/codemirror/addon/hint/python-hint.js +95 -0
- assets/lib/codemirror/addon/hint/show-hint.css +38 -0
- assets/lib/codemirror/addon/hint/show-hint.js +274 -0
- assets/lib/codemirror/addon/hint/xml-hint.js +68 -0
- assets/lib/codemirror/addon/lint/coffeescript-lint.js +25 -0
- assets/lib/codemirror/addon/lint/css-lint.js +17 -0
- assets/lib/codemirror/addon/lint/javascript-lint.js +124 -0
- assets/lib/codemirror/addon/lint/json-lint.js +15 -0
- assets/lib/codemirror/addon/lint/lint.css +72 -0
- assets/lib/codemirror/addon/lint/lint.js +203 -0
- assets/lib/codemirror/addon/merge/dep/diff_match_patch.js +50 -0
- assets/lib/codemirror/addon/merge/merge.css +92 -0
- assets/lib/codemirror/addon/merge/merge.js +473 -0
- assets/lib/codemirror/addon/mode/loadmode.js +51 -0
- assets/lib/codemirror/addon/mode/multiplex.js +101 -0
- assets/lib/codemirror/addon/mode/multiplex_test.js +30 -0
- assets/lib/codemirror/addon/mode/overlay.js +59 -0
- assets/lib/codemirror/addon/runmode/colorize.js +29 -0
- assets/lib/codemirror/addon/runmode/runmode-standalone.js +132 -0
- assets/lib/codemirror/addon/runmode/runmode.js +56 -0
- assets/lib/codemirror/addon/runmode/runmode.node.js +103 -0
- assets/lib/codemirror/addon/scroll/scrollpastend.js +34 -0
- assets/lib/codemirror/addon/search/match-highlighter.js +91 -0
- assets/lib/codemirror/addon/search/search.js +131 -0
- assets/lib/codemirror/addon/search/searchcursor.js +143 -0
- assets/lib/codemirror/addon/selection/active-line.js +39 -0
- assets/lib/codemirror/addon/selection/mark-selection.js +108 -0
- assets/lib/codemirror/addon/tern/tern.css +85 -0
- assets/lib/codemirror/addon/tern/tern.js +631 -0
- assets/lib/codemirror/addon/tern/worker.js +39 -0
- assets/lib/codemirror/lib/codemirror.css +273 -0
- assets/lib/codemirror/lib/codemirror.js +5887 -0
- assets/lib/codemirror/mode/css/css.js +627 -0
- assets/lib/codemirror/mode/css/index.html +70 -0
- assets/lib/codemirror/mode/css/scss.html +157 -0
- assets/lib/codemirror/mode/css/scss_test.js +80 -0
- assets/lib/codemirror/mode/css/test.js +126 -0
- assets/lib/codemirror/mode/htmlembedded/htmlembedded.js +73 -0
- assets/lib/codemirror/mode/htmlembedded/index.html +60 -0
- assets/lib/codemirror/mode/htmlmixed/htmlmixed.js +104 -0
- assets/lib/codemirror/mode/htmlmixed/index.html +85 -0
- assets/lib/codemirror/mode/http/http.js +98 -0
- assets/lib/codemirror/mode/http/index.html +45 -0
- assets/lib/codemirror/mode/index.html +110 -0
- assets/lib/codemirror/mode/javascript/index.html +107 -0
- assets/lib/codemirror/mode/javascript/javascript.js +480 -0
- assets/lib/codemirror/mode/javascript/test.js +10 -0
- assets/lib/codemirror/mode/javascript/typescript.html +61 -0
- assets/lib/codemirror/mode/meta.js +87 -0
- assets/lib/codemirror/mode/php/index.html +62 -0
- assets/lib/codemirror/mode/php/php.js +132 -0
- assets/lib/codemirror/mode/xml/index.html +57 -0
- assets/lib/codemirror/mode/xml/xml.js +341 -0
- assets/lib/wck-api/wordpress-creation-kit.php +42 -6
- features/email-customizer/admin-email-customizer.php +205 -0
- features/email-customizer/email-customizer.php +1286 -0
- features/email-customizer/user-email-customizer.php +409 -0
- features/functions.php +12 -12
- front-end/default-fields/avatar/avatar.php +263 -0
- front-end/default-fields/checkbox/checkbox.php +122 -0
- front-end/default-fields/default-fields.php +16 -1
- front-end/default-fields/fields-functions.php +66 -0
- front-end/default-fields/heading/heading.php +23 -0
- front-end/default-fields/input/input.php +71 -0
- front-end/default-fields/radio/radio.php +95 -0
- front-end/default-fields/select/select.php +105 -0
- front-end/default-fields/select2/select2.css +21 -0
- front-end/default-fields/select2/select2.js +23 -0
- front-end/default-fields/select2/select2.php +152 -0
- front-end/default-fields/textarea/textarea.php +78 -0
- front-end/default-fields/upload/upload.css +69 -0
- front-end/default-fields/upload/upload.js +242 -0
- front-end/default-fields/upload/upload_helper_functions.php +380 -0
- front-end/login.php +6 -2
- index.php +27 -14
- readme.txt +10 -4
- translation/profile-builder.catalog.php +19 -20
- translation/profile-builder.pot +508 -512
admin/add-ons.php
CHANGED
@@ -61,13 +61,13 @@ function wppb_add_ons_content() {
|
|
61 |
'icon' => 'pro_user_listing.png',
|
62 |
'doc_url' => 'https://www.cozmoslabs.com/docs/profile-builder-2/modules/user-listing/?utm_source=wpbackend&utm_medium=clientsite&utm_content=add-on-page&utm_campaign=PBPro',
|
63 |
),
|
64 |
-
array( 'slug' => 'wppb_emailCustomizer',
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
),
|
71 |
array( 'slug' => 'wppb_customRedirect',
|
72 |
'type' => 'add-on',
|
73 |
'name' => __( 'Custom Redirects', 'profile-builder' ),
|
61 |
'icon' => 'pro_user_listing.png',
|
62 |
'doc_url' => 'https://www.cozmoslabs.com/docs/profile-builder-2/modules/user-listing/?utm_source=wpbackend&utm_medium=clientsite&utm_content=add-on-page&utm_campaign=PBPro',
|
63 |
),
|
64 |
+
// array( 'slug' => 'wppb_emailCustomizer',
|
65 |
+
// 'type' => 'add-on',
|
66 |
+
// 'name' => __( 'Email Customizer', 'profile-builder' ),
|
67 |
+
// 'description' => __( 'Simple to use customization of the WordPress Registration Emails', 'profile-builder' ),
|
68 |
+
// 'icon' => 'pro_email_customizer.png',
|
69 |
+
// 'doc_url' => 'https://www.cozmoslabs.com/docs/profile-builder-2/modules/user-email-customizer/?utm_source=wpbackend&utm_medium=clientsite&utm_content=add-on-page&utm_campaign=PBPro',
|
70 |
+
// ),
|
71 |
array( 'slug' => 'wppb_customRedirect',
|
72 |
'type' => 'add-on',
|
73 |
'name' => __( 'Custom Redirects', 'profile-builder' ),
|
admin/advanced-settings/includes/fields/remove-all-fields-from-backend.php
CHANGED
@@ -2,9 +2,17 @@
|
|
2 |
/* Hide all profile fields in backend */
|
3 |
add_action('admin_init', 'wppb_toolbox_remove_all_fields_from_backend');
|
4 |
function wppb_toolbox_remove_all_fields_from_backend(){
|
|
|
5 |
remove_action( 'show_user_profile', 'display_profile_extra_fields_in_admin', 10 );
|
6 |
remove_action( 'edit_user_profile', 'display_profile_extra_fields_in_admin', 10 );
|
7 |
remove_action( 'personal_options_update', 'save_profile_extra_fields_in_admin', 10 );
|
8 |
remove_action( 'edit_user_profile_update', 'save_profile_extra_fields_in_admin', 10 );
|
9 |
remove_action( 'user_profile_update_errors', 'wppb_validate_backend_fields', 10, 3 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
}
|
2 |
/* Hide all profile fields in backend */
|
3 |
add_action('admin_init', 'wppb_toolbox_remove_all_fields_from_backend');
|
4 |
function wppb_toolbox_remove_all_fields_from_backend(){
|
5 |
+
// old
|
6 |
remove_action( 'show_user_profile', 'display_profile_extra_fields_in_admin', 10 );
|
7 |
remove_action( 'edit_user_profile', 'display_profile_extra_fields_in_admin', 10 );
|
8 |
remove_action( 'personal_options_update', 'save_profile_extra_fields_in_admin', 10 );
|
9 |
remove_action( 'edit_user_profile_update', 'save_profile_extra_fields_in_admin', 10 );
|
10 |
remove_action( 'user_profile_update_errors', 'wppb_validate_backend_fields', 10, 3 );
|
11 |
+
|
12 |
+
// new
|
13 |
+
remove_action( 'show_user_profile', 'wppb_display_fields_in_admin', 10 );
|
14 |
+
remove_action( 'edit_user_profile', 'wppb_display_fields_in_admin', 10 );
|
15 |
+
remove_action( 'personal_options_update', 'wppb_save_fields_in_admin', 10 );
|
16 |
+
remove_action( 'edit_user_profile_update', 'wppb_save_fields_in_admin', 10 );
|
17 |
+
remove_action( 'user_profile_update_errors', 'wppb_validate_fields_in_admin', 10, 3 );
|
18 |
}
|
admin/advanced-settings/includes/forms/back-end-validation.php
CHANGED
@@ -2,5 +2,9 @@
|
|
2 |
|
3 |
add_action ('user_profile_update_errors', 'wppb_toolbox_remove_backend_profile_validation', 5);
|
4 |
function wppb_toolbox_remove_backend_profile_validation(){
|
|
|
5 |
remove_action( 'user_profile_update_errors', 'wppb_validate_backend_fields', 10, 3);
|
|
|
|
|
|
|
6 |
}
|
2 |
|
3 |
add_action ('user_profile_update_errors', 'wppb_toolbox_remove_backend_profile_validation', 5);
|
4 |
function wppb_toolbox_remove_backend_profile_validation(){
|
5 |
+
// old
|
6 |
remove_action( 'user_profile_update_errors', 'wppb_validate_backend_fields', 10, 3);
|
7 |
+
|
8 |
+
// new
|
9 |
+
remove_action( 'user_profile_update_errors', 'wppb_validate_fields_in_admin', 10, 3);
|
10 |
}
|
admin/advanced-settings/includes/views/view-fields.php
CHANGED
@@ -255,57 +255,54 @@
|
|
255 |
</tr>
|
256 |
<?php endif; ?>
|
257 |
|
258 |
-
|
259 |
-
|
260 |
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
|
274 |
-
<?php if ( defined( 'WPPB_PAID_PLUGIN_DIR' ) && file_exists( WPPB_PAID_PLUGIN_DIR . '/front-end/extra-fields/extra-fields.php' ) ) : ?>
|
275 |
-
<tr>
|
276 |
-
<th><?php esc_html_e( 'Remove All Extra Fields from Backend edit profile page.', 'profile-builder' ); ?></th>
|
277 |
|
278 |
-
|
279 |
-
|
280 |
-
<?php esc_html_e( 'Yes', 'profile-builder' ); ?>
|
281 |
-
</label>
|
282 |
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
</ul>
|
288 |
-
</td>
|
289 |
-
</tr>
|
290 |
-
<?php endif; ?>
|
291 |
|
292 |
-
|
293 |
-
|
294 |
-
|
|
|
|
|
|
|
|
|
295 |
|
296 |
-
|
297 |
-
|
298 |
-
<?php esc_html_e( 'Yes', 'profile-builder' ); ?>
|
299 |
-
</label>
|
300 |
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
|
|
|
|
|
|
|
|
309 |
|
310 |
</table>
|
311 |
|
255 |
</tr>
|
256 |
<?php endif; ?>
|
257 |
|
258 |
+
<tr>
|
259 |
+
<th><?php esc_html_e( 'Show the Password field visibility toggle', 'profile-builder' ); ?></th>
|
260 |
|
261 |
+
<td>
|
262 |
+
<label><input type="checkbox" name="wppb_toolbox_fields_settings[password-visibility-hide]"<?php echo ( ( isset( $settings['password-visibility-hide'] ) && ( $settings['password-visibility-hide'] == 'yes' ) ) ? ' checked' : '' ); ?> value="yes">
|
263 |
+
<?php esc_html_e( 'Yes', 'profile-builder' ); ?>
|
264 |
+
</label>
|
265 |
|
266 |
+
<ul>
|
267 |
+
<li class="description">
|
268 |
+
<?php esc_html_e( 'Activating this option will show a visibility toggle button for all Password fields.', 'profile-builder' ); ?>
|
269 |
+
</li>
|
270 |
+
</ul>
|
271 |
+
</td>
|
272 |
+
</tr>
|
273 |
|
|
|
|
|
|
|
274 |
|
275 |
+
<tr>
|
276 |
+
<th><?php esc_html_e( 'Remove All Extra Fields from Backend edit profile page.', 'profile-builder' ); ?></th>
|
|
|
|
|
277 |
|
278 |
+
<td>
|
279 |
+
<label><input type="checkbox" name="wppb_toolbox_fields_settings[remove-all-fields-from-backend]"<?php echo ( ( isset( $settings['remove-all-fields-from-backend'] ) && ( $settings['remove-all-fields-from-backend'] == 'yes' ) ) ? ' checked' : '' ); ?> value="yes">
|
280 |
+
<?php esc_html_e( 'Yes', 'profile-builder' ); ?>
|
281 |
+
</label>
|
|
|
|
|
|
|
|
|
282 |
|
283 |
+
<ul>
|
284 |
+
<li class="description">
|
285 |
+
<?php esc_html_e( 'If you activate this option, it will remove all custom fields from the backend profile page created with Profile Builder.', 'profile-builder' ); ?>
|
286 |
+
</li>
|
287 |
+
</ul>
|
288 |
+
</td>
|
289 |
+
</tr>
|
290 |
|
291 |
+
<tr>
|
292 |
+
<th><?php esc_html_e( 'Update database entries when changing meta key', 'profile-builder' ); ?></th>
|
|
|
|
|
293 |
|
294 |
+
<td>
|
295 |
+
<label><input type="checkbox" name="wppb_toolbox_fields_settings[update-db-meta-keys]"<?php echo ( ( isset( $settings['update-db-meta-keys'] ) && ( $settings['update-db-meta-keys'] == 'yes' ) ) ? ' checked' : '' ); ?> value="yes">
|
296 |
+
<?php esc_html_e( 'Yes', 'profile-builder' ); ?>
|
297 |
+
</label>
|
298 |
+
|
299 |
+
<ul>
|
300 |
+
<li class="description">
|
301 |
+
<?php esc_html_e( 'If you activate this option, when changing the meta key of a field, existing entries from the database will be updated as well.', 'profile-builder' ); ?>
|
302 |
+
</li>
|
303 |
+
</ul>
|
304 |
+
</td>
|
305 |
+
</tr>
|
306 |
|
307 |
</table>
|
308 |
|
admin/basic-info.php
CHANGED
@@ -87,6 +87,10 @@ function wppb_basic_info_content() {
|
|
87 |
<h3><?php esc_html_e( 'Content Restriction', 'profile-builder' ); ?></h3>
|
88 |
<p><?php esc_html_e( 'Restrict users from accessing certain pages, posts or custom post types based on user role or logged-in status.', 'profile-builder' ); ?></p>
|
89 |
</div>
|
|
|
|
|
|
|
|
|
90 |
<div>
|
91 |
<h3><?php esc_html_e( 'Minimum Password Length and Strength Meter', 'profile-builder' ); ?></h3>
|
92 |
<p><?php esc_html_e( 'Eliminate weak passwords altogether by setting a minimum password length and enforcing a certain password strength.', 'profile-builder' ); ?></p>
|
@@ -118,27 +122,24 @@ function wppb_basic_info_content() {
|
|
118 |
<p><a href="admin.php?page=manage-fields" class="button"><?php esc_html_e( 'Get started with extra fields', 'profile-builder' ); ?></a></p>
|
119 |
<?php } ?>
|
120 |
<ul style="float: left; margin-right: 50px;">
|
121 |
-
<li><?php esc_html_e( 'Avatar Upload', 'profile-builder' ); ?></li>
|
122 |
<li><?php esc_html_e( 'Generic Uploads', 'profile-builder' ); ?></li>
|
123 |
<li><?php esc_html_e( 'Agree To Terms Checkbox', 'profile-builder' ); ?></li>
|
124 |
<li><?php esc_html_e( 'Datepicker', 'profile-builder' ); ?> </li>
|
125 |
<li><?php esc_html_e( 'Timepicker', 'profile-builder' ); ?> </li>
|
126 |
<li><?php esc_html_e( 'Colorpicker', 'profile-builder' ); ?> </li>
|
127 |
-
<li><?php esc_html_e( 'reCAPTCHA', 'profile-builder' ); ?></li>
|
128 |
<li><?php esc_html_e( 'Country Select', 'profile-builder' ); ?></li>
|
129 |
<li><?php esc_html_e( 'Currency Select', 'profile-builder' ); ?></li>
|
130 |
<li><?php esc_html_e( 'Timezone Select', 'profile-builder' ); ?></li>
|
131 |
</ul>
|
132 |
|
133 |
<ul style="float: left;">
|
134 |
-
|
|
|
|
|
|
|
135 |
<li><?php esc_html_e( 'Number', 'profile-builder' ); ?></li>
|
136 |
-
<li><?php esc_html_e( 'Checkbox', 'profile-builder' ); ?></li>
|
137 |
-
<li><?php esc_html_e( 'Select', 'profile-builder' ); ?></li>
|
138 |
-
<li><?php esc_html_e( 'Radio Buttons', 'profile-builder' ); ?></li>
|
139 |
-
<li><?php esc_html_e( 'Textarea', 'profile-builder' ); ?></li>
|
140 |
<li><?php esc_html_e( 'Validation', 'profile-builder' ); ?></li>
|
141 |
-
<li><?php esc_html_e( '
|
142 |
<li><?php esc_html_e( 'HTML', 'profile-builder' ); ?></li>
|
143 |
</ul>
|
144 |
</div>
|
@@ -166,10 +167,6 @@ function wppb_basic_info_content() {
|
|
166 |
<p><?php printf( esc_html__( 'To create a page containing the users registered to this current site/blog, insert the following shortcode in a page of your chosing: %s.', 'profile-builder' ), '<strong class="nowrap">[wppb-list-users]</strong>' ); ?></p>
|
167 |
<?php endif;?>
|
168 |
</div>
|
169 |
-
<div>
|
170 |
-
<h3><?php esc_html_e( 'Email Customizer', 'profile-builder' ); ?></h3>
|
171 |
-
<p><?php esc_html_e( 'Personalize all emails sent to your users or admins. On registration, email confirmation, admin approval / un-approval.', 'profile-builder' ); ?></p>
|
172 |
-
</div>
|
173 |
<div>
|
174 |
<h3><?php esc_html_e( 'Custom Redirects', 'profile-builder' ); ?></h3>
|
175 |
<p><?php esc_html_e( 'Keep your users out of the WordPress dashboard, redirect them to the front-page after login or registration, everything is just a few clicks away.', 'profile-builder' ); ?></p>
|
87 |
<h3><?php esc_html_e( 'Content Restriction', 'profile-builder' ); ?></h3>
|
88 |
<p><?php esc_html_e( 'Restrict users from accessing certain pages, posts or custom post types based on user role or logged-in status.', 'profile-builder' ); ?></p>
|
89 |
</div>
|
90 |
+
<div>
|
91 |
+
<h3><?php esc_html_e( 'Email Customizer', 'profile-builder' ); ?></h3>
|
92 |
+
<p><?php esc_html_e( 'Personalize all emails sent to your users or admins. On registration, email confirmation, admin approval / un-approval.', 'profile-builder' ); ?></p>
|
93 |
+
</div>
|
94 |
<div>
|
95 |
<h3><?php esc_html_e( 'Minimum Password Length and Strength Meter', 'profile-builder' ); ?></h3>
|
96 |
<p><?php esc_html_e( 'Eliminate weak passwords altogether by setting a minimum password length and enforcing a certain password strength.', 'profile-builder' ); ?></p>
|
122 |
<p><a href="admin.php?page=manage-fields" class="button"><?php esc_html_e( 'Get started with extra fields', 'profile-builder' ); ?></a></p>
|
123 |
<?php } ?>
|
124 |
<ul style="float: left; margin-right: 50px;">
|
|
|
125 |
<li><?php esc_html_e( 'Generic Uploads', 'profile-builder' ); ?></li>
|
126 |
<li><?php esc_html_e( 'Agree To Terms Checkbox', 'profile-builder' ); ?></li>
|
127 |
<li><?php esc_html_e( 'Datepicker', 'profile-builder' ); ?> </li>
|
128 |
<li><?php esc_html_e( 'Timepicker', 'profile-builder' ); ?> </li>
|
129 |
<li><?php esc_html_e( 'Colorpicker', 'profile-builder' ); ?> </li>
|
|
|
130 |
<li><?php esc_html_e( 'Country Select', 'profile-builder' ); ?></li>
|
131 |
<li><?php esc_html_e( 'Currency Select', 'profile-builder' ); ?></li>
|
132 |
<li><?php esc_html_e( 'Timezone Select', 'profile-builder' ); ?></li>
|
133 |
</ul>
|
134 |
|
135 |
<ul style="float: left;">
|
136 |
+
<li><?php esc_html_e( 'Map', 'profile-builder' ); ?></li>
|
137 |
+
<li><?php esc_html_e( 'Select 2 Multiple', 'profile-builder' ); ?></li>
|
138 |
+
<li><?php esc_html_e( 'Phone', 'profile-builder' ); ?></li>
|
139 |
+
<li><?php esc_html_e( 'Hidden Input', 'profile-builder' ); ?></li>
|
140 |
<li><?php esc_html_e( 'Number', 'profile-builder' ); ?></li>
|
|
|
|
|
|
|
|
|
141 |
<li><?php esc_html_e( 'Validation', 'profile-builder' ); ?></li>
|
142 |
+
<li><?php esc_html_e( 'Select CPT', 'profile-builder' ); ?></li>
|
143 |
<li><?php esc_html_e( 'HTML', 'profile-builder' ); ?></li>
|
144 |
</ul>
|
145 |
</div>
|
167 |
<p><?php printf( esc_html__( 'To create a page containing the users registered to this current site/blog, insert the following shortcode in a page of your chosing: %s.', 'profile-builder' ), '<strong class="nowrap">[wppb-list-users]</strong>' ); ?></p>
|
168 |
<?php endif;?>
|
169 |
</div>
|
|
|
|
|
|
|
|
|
170 |
<div>
|
171 |
<h3><?php esc_html_e( 'Custom Redirects', 'profile-builder' ); ?></h3>
|
172 |
<p><?php esc_html_e( 'Keep your users out of the WordPress dashboard, redirect them to the front-page after login or registration, everything is just a few clicks away.', 'profile-builder' ); ?></p>
|
admin/general-settings.php
CHANGED
@@ -31,7 +31,13 @@ function wppb_get_settings_pages(){
|
|
31 |
}
|
32 |
|
33 |
//add sub-pages here for email customizer
|
34 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
if( ( isset($wppb_module_settings['wppb_emailCustomizerAdmin']) && $wppb_module_settings['wppb_emailCustomizerAdmin'] == 'show' ) || ( isset($wppb_module_settings['wppb_emailCustomizer']) && $wppb_module_settings['wppb_emailCustomizer'] == 'show') ){
|
36 |
$settings_pages['pages']['user-email-customizer'] = __( 'Email Customizer', 'profile-builder' );
|
37 |
$settings_pages['sub-pages']['user-email-customizer']['user-email-customizer'] = __( 'User Emails', 'profile-builder' );
|
@@ -339,6 +345,42 @@ function wppb_general_settings_content() {
|
|
339 |
</td>
|
340 |
</tr>
|
341 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
342 |
<?php do_action( 'wppb_extra_general_settings', $wppb_generalSettings ); ?>
|
343 |
</table>
|
344 |
|
31 |
}
|
32 |
|
33 |
//add sub-pages here for email customizer
|
34 |
+
if( file_exists( WPPB_PLUGIN_DIR . '/features/email-customizer/email-customizer.php' ) ){
|
35 |
+
|
36 |
+
$settings_pages['pages']['user-email-customizer'] = __( 'Email Customizer', 'profile-builder' );
|
37 |
+
$settings_pages['sub-pages']['user-email-customizer']['user-email-customizer'] = __( 'User Emails', 'profile-builder' );
|
38 |
+
$settings_pages['sub-pages']['user-email-customizer']['admin-email-customizer'] = __( 'Administrator Emails', 'profile-builder' );
|
39 |
+
|
40 |
+
} else if ( defined( 'WPPB_PAID_PLUGIN_DIR' ) && file_exists( WPPB_PAID_PLUGIN_DIR . '/add-ons/add-ons.php' ) ) {
|
41 |
if( ( isset($wppb_module_settings['wppb_emailCustomizerAdmin']) && $wppb_module_settings['wppb_emailCustomizerAdmin'] == 'show' ) || ( isset($wppb_module_settings['wppb_emailCustomizer']) && $wppb_module_settings['wppb_emailCustomizer'] == 'show') ){
|
42 |
$settings_pages['pages']['user-email-customizer'] = __( 'Email Customizer', 'profile-builder' );
|
43 |
$settings_pages['sub-pages']['user-email-customizer']['user-email-customizer'] = __( 'User Emails', 'profile-builder' );
|
345 |
</td>
|
346 |
</tr>
|
347 |
|
348 |
+
<tr>
|
349 |
+
<th scope="row">
|
350 |
+
<?php esc_html_e( 'Select Recover Password Page:', 'profile-builder' ); ?>
|
351 |
+
</th>
|
352 |
+
<td>
|
353 |
+
<select name="wppb_general_settings[lost_password_page]" class="wppb-select">
|
354 |
+
<option value=""> <?php esc_html_e( 'None', 'profile-builder' ); ?></option>
|
355 |
+
<?php
|
356 |
+
$args = array(
|
357 |
+
'post_type' => 'page',
|
358 |
+
'post_status' => 'publish',
|
359 |
+
'numberposts' => -1,
|
360 |
+
'orderby' => 'name',
|
361 |
+
'order' => 'ASC'
|
362 |
+
);
|
363 |
+
$pages = get_posts( $args );
|
364 |
+
|
365 |
+
foreach ( $pages as $key => $value ){
|
366 |
+
echo '<option value="'.esc_attr( $value->guid ).'"';
|
367 |
+
if ( $wppb_generalSettings['lost_password_page'] == $value->guid )
|
368 |
+
echo ' selected';
|
369 |
+
|
370 |
+
echo '>' . esc_html( $value->post_title ) . '</option>';
|
371 |
+
}
|
372 |
+
|
373 |
+
?>
|
374 |
+
|
375 |
+
</select>
|
376 |
+
|
377 |
+
<ul>
|
378 |
+
<li class="description"><?php printf( esc_html__( 'Select the page which contains the %1$s[wppb-recover-password]%2$s shortcode.', 'profile-builder' ), '<strong>','</strong>' ) ?> </li>
|
379 |
+
</ul>
|
380 |
+
|
381 |
+
</td>
|
382 |
+
</tr>
|
383 |
+
|
384 |
<?php do_action( 'wppb_extra_general_settings', $wppb_generalSettings ); ?>
|
385 |
</table>
|
386 |
|
admin/manage-fields.php
CHANGED
@@ -53,12 +53,23 @@ function wppb_populate_manage_fields(){
|
|
53 |
'standard' =>
|
54 |
array(
|
55 |
'label' => __('Standard', 'profile-builder'),
|
56 |
-
'options' => array(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
),
|
58 |
'advanced' =>
|
59 |
array(
|
60 |
'label' => __('Advanced', 'profile-builder'),
|
61 |
-
'options' => array(
|
|
|
|
|
62 |
),
|
63 |
'other' =>
|
64 |
array(
|
@@ -83,20 +94,20 @@ function wppb_populate_manage_fields(){
|
|
83 |
}
|
84 |
|
85 |
if( PROFILE_BUILDER != 'Profile Builder Free' ) {
|
86 |
-
$manage_field_types['optgroups']['standard']['options'][] = 'Heading';
|
87 |
-
$manage_field_types['optgroups']['standard']['options'][] = 'Input';
|
88 |
$manage_field_types['optgroups']['standard']['options'][] = 'Number';
|
89 |
$manage_field_types['optgroups']['standard']['options'][] = 'Input (Hidden)';
|
90 |
$manage_field_types['optgroups']['standard']['options'][] = 'Language';
|
91 |
-
$manage_field_types['optgroups']['standard']['options'][] = 'Textarea';
|
92 |
$manage_field_types['optgroups']['standard']['options'][] = 'WYSIWYG';
|
93 |
-
$manage_field_types['optgroups']['standard']['options'][] = 'Select';
|
94 |
$manage_field_types['optgroups']['standard']['options'][] = 'Select (Multiple)';
|
95 |
-
$manage_field_types['optgroups']['standard']['options'][] = 'Checkbox';
|
96 |
-
$manage_field_types['optgroups']['standard']['options'][] = 'Radio';
|
97 |
$manage_field_types['optgroups']['standard']['options'][] = 'HTML';
|
98 |
$manage_field_types['optgroups']['standard']['options'][] = 'Upload';
|
99 |
-
$manage_field_types['optgroups']['standard']['options'][] = 'Avatar';
|
100 |
|
101 |
$manage_field_types['optgroups']['advanced']['options'][] = 'Phone';
|
102 |
$manage_field_types['optgroups']['advanced']['options'][] = 'Select (Country)';
|
@@ -113,11 +124,12 @@ function wppb_populate_manage_fields(){
|
|
113 |
$manage_field_types['optgroups']['other']['options'][] = 'Email';
|
114 |
$manage_field_types['optgroups']['other']['options'][] = 'URL';
|
115 |
|
116 |
-
$manage_field_types['optgroups']['other']['options'][] = 'Select2';
|
117 |
$manage_field_types['optgroups']['other']['options'][] = 'Select2 (Multiple)';
|
118 |
|
119 |
$manage_field_types['optgroups']['other']['options'][] = 'Honeypot';
|
120 |
-
|
|
|
121 |
|
122 |
$manage_field_types['optgroups']['other']['options'][] = 'Email Confirmation';
|
123 |
|
@@ -130,7 +142,42 @@ function wppb_populate_manage_fields(){
|
|
130 |
|
131 |
$manage_field_types = apply_filters( 'wppb_all_manage_fields_types', $manage_field_types );
|
132 |
|
133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
$field_description = __('Choose one of the supported field types','profile-builder');
|
135 |
if( PROFILE_BUILDER == 'Profile Builder Free' ) {
|
136 |
$field_description .= sprintf( __('. Extra Field Types are available in <a href="%s">Basic or PRO versions</a>.' , 'profile-builder'), esc_url( 'https://www.cozmoslabs.com/wordpress-profile-builder/?utm_source=wpbackend&utm_medium=clientsite&utm_content=manage-fields-link&utm_campaign=PBFree' ) );
|
53 |
'standard' =>
|
54 |
array(
|
55 |
'label' => __('Standard', 'profile-builder'),
|
56 |
+
'options' => array(
|
57 |
+
// since 3.8.1
|
58 |
+
'Avatar',
|
59 |
+
'Checkbox',
|
60 |
+
'Heading',
|
61 |
+
'Input',
|
62 |
+
'Radio',
|
63 |
+
'Select',
|
64 |
+
'Textarea',
|
65 |
+
),
|
66 |
),
|
67 |
'advanced' =>
|
68 |
array(
|
69 |
'label' => __('Advanced', 'profile-builder'),
|
70 |
+
'options' => array(
|
71 |
+
'Select2' // since 3.8.1
|
72 |
+
),
|
73 |
),
|
74 |
'other' =>
|
75 |
array(
|
94 |
}
|
95 |
|
96 |
if( PROFILE_BUILDER != 'Profile Builder Free' ) {
|
97 |
+
// $manage_field_types['optgroups']['standard']['options'][] = 'Heading';
|
98 |
+
// $manage_field_types['optgroups']['standard']['options'][] = 'Input';
|
99 |
$manage_field_types['optgroups']['standard']['options'][] = 'Number';
|
100 |
$manage_field_types['optgroups']['standard']['options'][] = 'Input (Hidden)';
|
101 |
$manage_field_types['optgroups']['standard']['options'][] = 'Language';
|
102 |
+
// $manage_field_types['optgroups']['standard']['options'][] = 'Textarea';
|
103 |
$manage_field_types['optgroups']['standard']['options'][] = 'WYSIWYG';
|
104 |
+
// $manage_field_types['optgroups']['standard']['options'][] = 'Select';
|
105 |
$manage_field_types['optgroups']['standard']['options'][] = 'Select (Multiple)';
|
106 |
+
// $manage_field_types['optgroups']['standard']['options'][] = 'Checkbox';
|
107 |
+
// $manage_field_types['optgroups']['standard']['options'][] = 'Radio';
|
108 |
$manage_field_types['optgroups']['standard']['options'][] = 'HTML';
|
109 |
$manage_field_types['optgroups']['standard']['options'][] = 'Upload';
|
110 |
+
// $manage_field_types['optgroups']['standard']['options'][] = 'Avatar';
|
111 |
|
112 |
$manage_field_types['optgroups']['advanced']['options'][] = 'Phone';
|
113 |
$manage_field_types['optgroups']['advanced']['options'][] = 'Select (Country)';
|
124 |
$manage_field_types['optgroups']['other']['options'][] = 'Email';
|
125 |
$manage_field_types['optgroups']['other']['options'][] = 'URL';
|
126 |
|
127 |
+
// $manage_field_types['optgroups']['other']['options'][] = 'Select2';
|
128 |
$manage_field_types['optgroups']['other']['options'][] = 'Select2 (Multiple)';
|
129 |
|
130 |
$manage_field_types['optgroups']['other']['options'][] = 'Honeypot';
|
131 |
+
|
132 |
+
}
|
133 |
|
134 |
$manage_field_types['optgroups']['other']['options'][] = 'Email Confirmation';
|
135 |
|
142 |
|
143 |
$manage_field_types = apply_filters( 'wppb_all_manage_fields_types', $manage_field_types );
|
144 |
|
145 |
+
if( PROFILE_BUILDER == 'Profile Builder Free' ) {
|
146 |
+
|
147 |
+
$manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'Number', 'disabled' => true );
|
148 |
+
$manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'Input (Hidden)', 'disabled' => true );
|
149 |
+
$manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'Language', 'disabled' => true );
|
150 |
+
$manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'WYSIWYG', 'disabled' => true );
|
151 |
+
$manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'Select (Multiple)', 'disabled' => true );
|
152 |
+
$manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'HTML', 'disabled' => true );
|
153 |
+
$manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'Upload', 'disabled' => true );
|
154 |
+
|
155 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Phone', 'disabled' => true );
|
156 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Select (Country)', 'disabled' => true );
|
157 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Select (Timezone)', 'disabled' => true );
|
158 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Select (Currency)', 'disabled' => true );
|
159 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Select (CPT)', 'disabled' => true );
|
160 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Checkbox (Terms and Conditions)', 'disabled' => true );
|
161 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Datepicker', 'disabled' => true );
|
162 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Timepicker', 'disabled' => true );
|
163 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Colorpicker', 'disabled' => true );
|
164 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Validation', 'disabled' => true );
|
165 |
+
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Map', 'disabled' => true );
|
166 |
+
|
167 |
+
$manage_field_types['optgroups']['other']['options'][] = array( 'field_name' => 'WooCommerce Customer Billing Address', 'disabled' => true );
|
168 |
+
$manage_field_types['optgroups']['other']['options'][] = array( 'field_name' => 'WooCommerce Customer Shipping Address', 'disabled' => true );
|
169 |
+
$manage_field_types['optgroups']['other']['options'][] = array( 'field_name' => 'MailChimp Subscribe', 'disabled' => true );
|
170 |
+
$manage_field_types['optgroups']['other']['options'][] = array( 'field_name' => 'Email', 'disabled' => true );
|
171 |
+
$manage_field_types['optgroups']['other']['options'][] = array( 'field_name' => 'URL', 'disabled' => true );
|
172 |
+
$manage_field_types['optgroups']['other']['options'][] = array( 'field_name' => 'Select2 (Multiple)', 'disabled' => true );
|
173 |
+
$manage_field_types['optgroups']['other']['options'][] = array( 'field_name' => 'Honeypot', 'disabled' => true );
|
174 |
+
|
175 |
+
}
|
176 |
+
|
177 |
+
if( !is_plugin_active( 'paid-member-subscriptions/index.php' ) )
|
178 |
+
$manage_field_types['optgroups']['other']['options'][] = array( 'field_name' => 'Subscription Plans', 'disabled' => true );
|
179 |
+
|
180 |
+
// Free to Pro call to action on Manage Fields page
|
181 |
$field_description = __('Choose one of the supported field types','profile-builder');
|
182 |
if( PROFILE_BUILDER == 'Profile Builder Free' ) {
|
183 |
$field_description .= sprintf( __('. Extra Field Types are available in <a href="%s">Basic or PRO versions</a>.' , 'profile-builder'), esc_url( 'https://www.cozmoslabs.com/wordpress-profile-builder/?utm_source=wpbackend&utm_medium=clientsite&utm_content=manage-fields-link&utm_campaign=PBFree' ) );
|
admin/register-version.php
CHANGED
@@ -57,7 +57,7 @@ function wppb_serial_form(){
|
|
57 |
<p><?php esc_html_e( 'If you register this version of Profile Builder, you\'ll receive information regarding upgrades, patches, and technical support.', 'profile-builder' ) ?></p>
|
58 |
|
59 |
<div class="wppb-serial-wrap">
|
60 |
-
<form method="post" action="options.php">
|
61 |
<?php settings_fields( 'wppb_license_key' ); ?>
|
62 |
|
63 |
<label for="wppb_license_key"><?php esc_html_e( 'License key', 'profile-builder' ); ?></label>
|
57 |
<p><?php esc_html_e( 'If you register this version of Profile Builder, you\'ll receive information regarding upgrades, patches, and technical support.', 'profile-builder' ) ?></p>
|
58 |
|
59 |
<div class="wppb-serial-wrap">
|
60 |
+
<form method="post" action="<?php echo !is_multisite() ? 'options.php' : 'edit.php'; ?>">
|
61 |
<?php settings_fields( 'wppb_license_key' ); ?>
|
62 |
|
63 |
<label for="wppb_license_key"><?php esc_html_e( 'License key', 'profile-builder' ); ?></label>
|
assets/lib/Mustache/Autoloader.php
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache class autoloader.
|
14 |
+
*/
|
15 |
+
class Mustache_Autoloader
|
16 |
+
{
|
17 |
+
|
18 |
+
private $baseDir;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Autoloader constructor.
|
22 |
+
*
|
23 |
+
* @param string $baseDir Mustache library base directory (default: dirname(__FILE__).'/..')
|
24 |
+
*/
|
25 |
+
public function __construct($baseDir = null)
|
26 |
+
{
|
27 |
+
if ($baseDir === null) {
|
28 |
+
$baseDir = dirname(__FILE__).'/..';
|
29 |
+
}
|
30 |
+
|
31 |
+
// realpath doesn't always work, for example, with stream URIs
|
32 |
+
$realDir = realpath($baseDir);
|
33 |
+
if (is_dir($realDir)) {
|
34 |
+
$this->baseDir = $realDir;
|
35 |
+
} else {
|
36 |
+
$this->baseDir = $baseDir;
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Register a new instance as an SPL autoloader.
|
42 |
+
*
|
43 |
+
* @param string $baseDir Mustache library base directory (default: dirname(__FILE__).'/..')
|
44 |
+
*
|
45 |
+
* @return Mustache_Autoloader Registered Autoloader instance
|
46 |
+
*/
|
47 |
+
public static function register($baseDir = null)
|
48 |
+
{
|
49 |
+
$loader = new self($baseDir);
|
50 |
+
spl_autoload_register(array($loader, 'autoload'));
|
51 |
+
|
52 |
+
return $loader;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Autoload Mustache classes.
|
57 |
+
*
|
58 |
+
* @param string $class
|
59 |
+
*/
|
60 |
+
public function autoload($class)
|
61 |
+
{
|
62 |
+
if ($class[0] === '\\') {
|
63 |
+
$class = substr($class, 1);
|
64 |
+
}
|
65 |
+
|
66 |
+
if (strpos($class, 'Mustache') !== 0) {
|
67 |
+
return;
|
68 |
+
}
|
69 |
+
|
70 |
+
$file = sprintf('%s/%s.php', $this->baseDir, str_replace('_', '/', $class));
|
71 |
+
if (is_file($file)) {
|
72 |
+
require $file;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
}
|
assets/lib/Mustache/Compiler.php
ADDED
@@ -0,0 +1,476 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache Compiler class.
|
14 |
+
*
|
15 |
+
* This class is responsible for turning a Mustache token parse tree into normal PHP source code.
|
16 |
+
*/
|
17 |
+
class Mustache_Compiler
|
18 |
+
{
|
19 |
+
|
20 |
+
private $sections;
|
21 |
+
private $source;
|
22 |
+
private $indentNextLine;
|
23 |
+
private $customEscape;
|
24 |
+
private $entityFlags;
|
25 |
+
private $charset;
|
26 |
+
private $strictCallables;
|
27 |
+
private $pragmas;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Compile a Mustache token parse tree into PHP source code.
|
31 |
+
*
|
32 |
+
* @param string $source Mustache Template source code
|
33 |
+
* @param string $tree Parse tree of Mustache tokens
|
34 |
+
* @param string $name Mustache Template class name
|
35 |
+
* @param bool $customEscape (default: false)
|
36 |
+
* @param int $entityFlags (default: ENT_COMPAT)
|
37 |
+
* @param string $charset (default: 'UTF-8')
|
38 |
+
* @param bool $strictCallables (default: false)
|
39 |
+
*
|
40 |
+
* @return string Generated PHP source code
|
41 |
+
*/
|
42 |
+
public function compile($source, array $tree, $name, $customEscape = false, $charset = 'UTF-8', $strictCallables = false, $entityFlags = ENT_COMPAT)
|
43 |
+
{
|
44 |
+
$this->pragmas = array();
|
45 |
+
$this->sections = array();
|
46 |
+
$this->source = $source;
|
47 |
+
$this->indentNextLine = true;
|
48 |
+
$this->customEscape = $customEscape;
|
49 |
+
$this->entityFlags = $entityFlags;
|
50 |
+
$this->charset = $charset;
|
51 |
+
$this->strictCallables = $strictCallables;
|
52 |
+
|
53 |
+
return $this->writeCode($tree, $name);
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Helper function for walking the Mustache token parse tree.
|
58 |
+
*
|
59 |
+
* @throws Mustache_Exception_SyntaxException upon encountering unknown token types.
|
60 |
+
*
|
61 |
+
* @param array $tree Parse tree of Mustache tokens
|
62 |
+
* @param int $level (default: 0)
|
63 |
+
*
|
64 |
+
* @return string Generated PHP source code
|
65 |
+
*/
|
66 |
+
private function walk(array $tree, $level = 0)
|
67 |
+
{
|
68 |
+
$code = '';
|
69 |
+
$level++;
|
70 |
+
foreach ($tree as $node) {
|
71 |
+
switch ($node[Mustache_Tokenizer::TYPE]) {
|
72 |
+
case Mustache_Tokenizer::T_PRAGMA:
|
73 |
+
$this->pragmas[$node[Mustache_Tokenizer::NAME]] = true;
|
74 |
+
break;
|
75 |
+
|
76 |
+
case Mustache_Tokenizer::T_SECTION:
|
77 |
+
$code .= $this->section(
|
78 |
+
$node[Mustache_Tokenizer::NODES],
|
79 |
+
$node[Mustache_Tokenizer::NAME],
|
80 |
+
$node[Mustache_Tokenizer::INDEX],
|
81 |
+
$node[Mustache_Tokenizer::END],
|
82 |
+
$node[Mustache_Tokenizer::OTAG],
|
83 |
+
$node[Mustache_Tokenizer::CTAG],
|
84 |
+
$level
|
85 |
+
);
|
86 |
+
break;
|
87 |
+
|
88 |
+
case Mustache_Tokenizer::T_INVERTED:
|
89 |
+
$code .= $this->invertedSection(
|
90 |
+
$node[Mustache_Tokenizer::NODES],
|
91 |
+
$node[Mustache_Tokenizer::NAME],
|
92 |
+
$level
|
93 |
+
);
|
94 |
+
break;
|
95 |
+
|
96 |
+
case Mustache_Tokenizer::T_PARTIAL:
|
97 |
+
case Mustache_Tokenizer::T_PARTIAL_2:
|
98 |
+
$code .= $this->partial(
|
99 |
+
$node[Mustache_Tokenizer::NAME],
|
100 |
+
isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '',
|
101 |
+
$level
|
102 |
+
);
|
103 |
+
break;
|
104 |
+
|
105 |
+
case Mustache_Tokenizer::T_UNESCAPED:
|
106 |
+
case Mustache_Tokenizer::T_UNESCAPED_2:
|
107 |
+
$code .= $this->variable($node[Mustache_Tokenizer::NAME], false, $level);
|
108 |
+
break;
|
109 |
+
|
110 |
+
case Mustache_Tokenizer::T_COMMENT:
|
111 |
+
break;
|
112 |
+
|
113 |
+
case Mustache_Tokenizer::T_ESCAPED:
|
114 |
+
$code .= $this->variable($node[Mustache_Tokenizer::NAME], true, $level);
|
115 |
+
break;
|
116 |
+
|
117 |
+
case Mustache_Tokenizer::T_TEXT:
|
118 |
+
$code .= $this->text($node[Mustache_Tokenizer::VALUE], $level);
|
119 |
+
break;
|
120 |
+
|
121 |
+
default:
|
122 |
+
throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node);
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
return $code;
|
127 |
+
}
|
128 |
+
|
129 |
+
const KLASS = '<?php
|
130 |
+
|
131 |
+
class %s extends Mustache_Template
|
132 |
+
{
|
133 |
+
private $lambdaHelper;%s
|
134 |
+
|
135 |
+
public function renderInternal(Mustache_Context $context, $indent = \'\')
|
136 |
+
{
|
137 |
+
$this->lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context);
|
138 |
+
$buffer = \'\';
|
139 |
+
%s
|
140 |
+
|
141 |
+
return $buffer;
|
142 |
+
}
|
143 |
+
%s
|
144 |
+
}';
|
145 |
+
|
146 |
+
const KLASS_NO_LAMBDAS = '<?php
|
147 |
+
|
148 |
+
class %s extends Mustache_Template
|
149 |
+
{%s
|
150 |
+
public function renderInternal(Mustache_Context $context, $indent = \'\')
|
151 |
+
{
|
152 |
+
$buffer = \'\';
|
153 |
+
%s
|
154 |
+
|
155 |
+
return $buffer;
|
156 |
+
}
|
157 |
+
}';
|
158 |
+
|
159 |
+
const STRICT_CALLABLE = 'protected $strictCallables = true;';
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Generate Mustache Template class PHP source.
|
163 |
+
*
|
164 |
+
* @param array $tree Parse tree of Mustache tokens
|
165 |
+
* @param string $name Mustache Template class name
|
166 |
+
*
|
167 |
+
* @return string Generated PHP source code
|
168 |
+
*/
|
169 |
+
private function writeCode($tree, $name)
|
170 |
+
{
|
171 |
+
$code = $this->walk($tree);
|
172 |
+
$sections = implode("\n", $this->sections);
|
173 |
+
$klass = empty($this->sections) ? self::KLASS_NO_LAMBDAS : self::KLASS;
|
174 |
+
$callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : '';
|
175 |
+
|
176 |
+
return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections);
|
177 |
+
}
|
178 |
+
|
179 |
+
const SECTION_CALL = '
|
180 |
+
// %s section
|
181 |
+
$buffer .= $this->section%s($context, $indent, $context->%s(%s));
|
182 |
+
';
|
183 |
+
|
184 |
+
const SECTION = '
|
185 |
+
private function section%s(Mustache_Context $context, $indent, $value)
|
186 |
+
{
|
187 |
+
$buffer = \'\';
|
188 |
+
if (%s) {
|
189 |
+
$source = %s;
|
190 |
+
$buffer .= $this->mustache
|
191 |
+
->loadLambda((string) call_user_func($value, $source, $this->lambdaHelper)%s)
|
192 |
+
->renderInternal($context);
|
193 |
+
} elseif (!empty($value)) {
|
194 |
+
$values = $this->isIterable($value) ? $value : array($value);
|
195 |
+
foreach ($values as $value) {
|
196 |
+
$context->push($value);%s
|
197 |
+
$context->pop();
|
198 |
+
}
|
199 |
+
}
|
200 |
+
|
201 |
+
return $buffer;
|
202 |
+
}';
|
203 |
+
|
204 |
+
/**
|
205 |
+
* Generate Mustache Template section PHP source.
|
206 |
+
*
|
207 |
+
* @param array $nodes Array of child tokens
|
208 |
+
* @param string $id Section name
|
209 |
+
* @param int $start Section start offset
|
210 |
+
* @param int $end Section end offset
|
211 |
+
* @param string $otag Current Mustache opening tag
|
212 |
+
* @param string $ctag Current Mustache closing tag
|
213 |
+
* @param int $level
|
214 |
+
*
|
215 |
+
* @return string Generated section PHP source code
|
216 |
+
*/
|
217 |
+
private function section($nodes, $id, $start, $end, $otag, $ctag, $level)
|
218 |
+
{
|
219 |
+
$method = $this->getFindMethod($id);
|
220 |
+
$id = var_export($id, true);
|
221 |
+
$source = var_export(substr($this->source, $start, $end - $start), true);
|
222 |
+
$callable = $this->getCallable();
|
223 |
+
|
224 |
+
if ($otag !== '{{' || $ctag !== '}}') {
|
225 |
+
$delims = ', '.var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true);
|
226 |
+
} else {
|
227 |
+
$delims = '';
|
228 |
+
}
|
229 |
+
|
230 |
+
$key = ucfirst(md5($delims."\n".$source));
|
231 |
+
|
232 |
+
if (!isset($this->sections[$key])) {
|
233 |
+
$this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $delims, $this->walk($nodes, 2));
|
234 |
+
}
|
235 |
+
|
236 |
+
return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $key, $method, $id);
|
237 |
+
}
|
238 |
+
|
239 |
+
const INVERTED_SECTION = '
|
240 |
+
// %s inverted section
|
241 |
+
$value = $context->%s(%s);
|
242 |
+
if (empty($value)) {
|
243 |
+
%s
|
244 |
+
}';
|
245 |
+
|
246 |
+
/**
|
247 |
+
* Generate Mustache Template inverted section PHP source.
|
248 |
+
*
|
249 |
+
* @param array $nodes Array of child tokens
|
250 |
+
* @param string $id Section name
|
251 |
+
* @param int $level
|
252 |
+
*
|
253 |
+
* @return string Generated inverted section PHP source code
|
254 |
+
*/
|
255 |
+
private function invertedSection($nodes, $id, $level)
|
256 |
+
{
|
257 |
+
$method = $this->getFindMethod($id);
|
258 |
+
$id = var_export($id, true);
|
259 |
+
|
260 |
+
return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $this->walk($nodes, $level));
|
261 |
+
}
|
262 |
+
|
263 |
+
const PARTIAL = '
|
264 |
+
if ($partial = $this->mustache->loadPartial(%s)) {
|
265 |
+
$buffer .= $partial->renderInternal($context, %s);
|
266 |
+
}
|
267 |
+
';
|
268 |
+
|
269 |
+
/**
|
270 |
+
* Generate Mustache Template partial call PHP source.
|
271 |
+
*
|
272 |
+
* @param string $id Partial name
|
273 |
+
* @param string $indent Whitespace indent to apply to partial
|
274 |
+
* @param int $level
|
275 |
+
*
|
276 |
+
* @return string Generated partial call PHP source code
|
277 |
+
*/
|
278 |
+
private function partial($id, $indent, $level)
|
279 |
+
{
|
280 |
+
return sprintf(
|
281 |
+
$this->prepare(self::PARTIAL, $level),
|
282 |
+
var_export($id, true),
|
283 |
+
var_export($indent, true)
|
284 |
+
);
|
285 |
+
}
|
286 |
+
|
287 |
+
const VARIABLE = '
|
288 |
+
$value = $this->resolveValue($context->%s(%s), $context, $indent);%s
|
289 |
+
$buffer .= %s%s;
|
290 |
+
';
|
291 |
+
|
292 |
+
/**
|
293 |
+
* Generate Mustache Template variable interpolation PHP source.
|
294 |
+
*
|
295 |
+
* @param string $id Variable name
|
296 |
+
* @param boolean $escape Escape the variable value for output?
|
297 |
+
* @param int $level
|
298 |
+
*
|
299 |
+
* @return string Generated variable interpolation PHP source
|
300 |
+
*/
|
301 |
+
private function variable($id, $escape, $level)
|
302 |
+
{
|
303 |
+
$filters = '';
|
304 |
+
|
305 |
+
if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) {
|
306 |
+
list($id, $filters) = $this->getFilters($id, $level);
|
307 |
+
}
|
308 |
+
|
309 |
+
$method = $this->getFindMethod($id);
|
310 |
+
$id = ($method !== 'last') ? var_export($id, true) : '';
|
311 |
+
$value = $escape ? $this->getEscape() : '$value';
|
312 |
+
|
313 |
+
return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value);
|
314 |
+
}
|
315 |
+
|
316 |
+
/**
|
317 |
+
* Generate Mustache Template variable filtering PHP source.
|
318 |
+
*
|
319 |
+
* @param string $id Variable name
|
320 |
+
* @param int $level
|
321 |
+
*
|
322 |
+
* @return string Generated variable filtering PHP source
|
323 |
+
*/
|
324 |
+
private function getFilters($id, $level)
|
325 |
+
{
|
326 |
+
$filters = array_map('trim', explode('|', $id));
|
327 |
+
$id = array_shift($filters);
|
328 |
+
|
329 |
+
return array($id, $this->getFilter($filters, $level));
|
330 |
+
}
|
331 |
+
|
332 |
+
const FILTER = '
|
333 |
+
$filter = $context->%s(%s);
|
334 |
+
if (!(%s)) {
|
335 |
+
throw new Mustache_Exception_UnknownFilterException(%s);
|
336 |
+
}
|
337 |
+
$value = call_user_func($filter, $value);%s
|
338 |
+
';
|
339 |
+
|
340 |
+
/**
|
341 |
+
* Generate PHP source for a single filter.
|
342 |
+
*
|
343 |
+
* @param array $filters
|
344 |
+
* @param int $level
|
345 |
+
*
|
346 |
+
* @return string Generated filter PHP source
|
347 |
+
*/
|
348 |
+
private function getFilter(array $filters, $level)
|
349 |
+
{
|
350 |
+
if (empty($filters)) {
|
351 |
+
return '';
|
352 |
+
}
|
353 |
+
|
354 |
+
$name = array_shift($filters);
|
355 |
+
$method = $this->getFindMethod($name);
|
356 |
+
$filter = ($method !== 'last') ? var_export($name, true) : '';
|
357 |
+
$callable = $this->getCallable('$filter');
|
358 |
+
$msg = var_export($name, true);
|
359 |
+
|
360 |
+
return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilter($filters, $level));
|
361 |
+
}
|
362 |
+
|
363 |
+
const LINE = '$buffer .= "\n";';
|
364 |
+
const TEXT = '$buffer .= %s%s;';
|
365 |
+
|
366 |
+
/**
|
367 |
+
* Generate Mustache Template output Buffer call PHP source.
|
368 |
+
*
|
369 |
+
* @param string $text
|
370 |
+
* @param int $level
|
371 |
+
*
|
372 |
+
* @return string Generated output Buffer call PHP source
|
373 |
+
*/
|
374 |
+
private function text($text, $level)
|
375 |
+
{
|
376 |
+
$indentNextLine = (substr($text, -1) === "\n");
|
377 |
+
$code = sprintf($this->prepare(self::TEXT, $level), $this->flushIndent(), var_export($text, true));
|
378 |
+
$this->indentNextLine = $indentNextLine;
|
379 |
+
|
380 |
+
return $code;
|
381 |
+
}
|
382 |
+
|
383 |
+
/**
|
384 |
+
* Prepare PHP source code snippet for output.
|
385 |
+
*
|
386 |
+
* @param string $text
|
387 |
+
* @param int $bonus Additional indent level (default: 0)
|
388 |
+
* @param boolean $prependNewline Prepend a newline to the snippet? (default: true)
|
389 |
+
* @param boolean $appendNewline Append a newline to the snippet? (default: false)
|
390 |
+
*
|
391 |
+
* @return string PHP source code snippet
|
392 |
+
*/
|
393 |
+
private function prepare($text, $bonus = 0, $prependNewline = true, $appendNewline = false)
|
394 |
+
{
|
395 |
+
$text = ($prependNewline ? "\n" : '').trim($text);
|
396 |
+
if ($prependNewline) {
|
397 |
+
$bonus++;
|
398 |
+
}
|
399 |
+
if ($appendNewline) {
|
400 |
+
$text .= "\n";
|
401 |
+
}
|
402 |
+
|
403 |
+
return preg_replace("/\n( {8})?/", "\n".str_repeat(" ", $bonus * 4), $text);
|
404 |
+
}
|
405 |
+
|
406 |
+
const DEFAULT_ESCAPE = 'htmlspecialchars(%s, %s, %s)';
|
407 |
+
const CUSTOM_ESCAPE = 'call_user_func($this->mustache->getEscape(), %s)';
|
408 |
+
|
409 |
+
/**
|
410 |
+
* Get the current escaper.
|
411 |
+
*
|
412 |
+
* @param string $value (default: '$value')
|
413 |
+
*
|
414 |
+
* @return string Either a custom callback, or an inline call to `htmlspecialchars`
|
415 |
+
*/
|
416 |
+
private function getEscape($value = '$value')
|
417 |
+
{
|
418 |
+
if ($this->customEscape) {
|
419 |
+
return sprintf(self::CUSTOM_ESCAPE, $value);
|
420 |
+
} else {
|
421 |
+
return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->entityFlags, true), var_export($this->charset, true));
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
/**
|
426 |
+
* Select the appropriate Context `find` method for a given $id.
|
427 |
+
*
|
428 |
+
* The return value will be one of `find`, `findDot` or `last`.
|
429 |
+
*
|
430 |
+
* @see Mustache_Context::find
|
431 |
+
* @see Mustache_Context::findDot
|
432 |
+
* @see Mustache_Context::last
|
433 |
+
*
|
434 |
+
* @param string $id Variable name
|
435 |
+
*
|
436 |
+
* @return string `find` method name
|
437 |
+
*/
|
438 |
+
private function getFindMethod($id)
|
439 |
+
{
|
440 |
+
if ($id === '.') {
|
441 |
+
return 'last';
|
442 |
+
} elseif (strpos($id, '.') === false) {
|
443 |
+
return 'find';
|
444 |
+
} else {
|
445 |
+
return 'findDot';
|
446 |
+
}
|
447 |
+
}
|
448 |
+
|
449 |
+
const IS_CALLABLE = '!is_string(%s) && is_callable(%s)';
|
450 |
+
const STRICT_IS_CALLABLE = 'is_object(%s) && is_callable(%s)';
|
451 |
+
|
452 |
+
private function getCallable($variable = '$value')
|
453 |
+
{
|
454 |
+
$tpl = $this->strictCallables ? self::STRICT_IS_CALLABLE : self::IS_CALLABLE;
|
455 |
+
|
456 |
+
return sprintf($tpl, $variable, $variable);
|
457 |
+
}
|
458 |
+
|
459 |
+
const LINE_INDENT = '$indent . ';
|
460 |
+
|
461 |
+
/**
|
462 |
+
* Get the current $indent prefix to write to the buffer.
|
463 |
+
*
|
464 |
+
* @return string "$indent . " or ""
|
465 |
+
*/
|
466 |
+
private function flushIndent()
|
467 |
+
{
|
468 |
+
if (!$this->indentNextLine) {
|
469 |
+
return '';
|
470 |
+
}
|
471 |
+
|
472 |
+
$this->indentNextLine = false;
|
473 |
+
|
474 |
+
return self::LINE_INDENT;
|
475 |
+
}
|
476 |
+
}
|
assets/lib/Mustache/Context.php
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache Template rendering Context.
|
14 |
+
*/
|
15 |
+
class Mustache_Context
|
16 |
+
{
|
17 |
+
private $stack = array();
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Mustache rendering Context constructor.
|
21 |
+
*
|
22 |
+
* @param mixed $context Default rendering context (default: null)
|
23 |
+
*/
|
24 |
+
public function __construct($context = null)
|
25 |
+
{
|
26 |
+
if ($context !== null) {
|
27 |
+
$this->stack = array($context);
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Push a new Context frame onto the stack.
|
33 |
+
*
|
34 |
+
* @param mixed $value Object or array to use for context
|
35 |
+
*/
|
36 |
+
public function push($value)
|
37 |
+
{
|
38 |
+
array_push($this->stack, $value);
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Pop the last Context frame from the stack.
|
43 |
+
*
|
44 |
+
* @return mixed Last Context frame (object or array)
|
45 |
+
*/
|
46 |
+
public function pop()
|
47 |
+
{
|
48 |
+
return array_pop($this->stack);
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Get the last Context frame.
|
53 |
+
*
|
54 |
+
* @return mixed Last Context frame (object or array)
|
55 |
+
*/
|
56 |
+
public function last()
|
57 |
+
{
|
58 |
+
return end($this->stack);
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Find a variable in the Context stack.
|
63 |
+
*
|
64 |
+
* Starting with the last Context frame (the context of the innermost section), and working back to the top-level
|
65 |
+
* rendering context, look for a variable with the given name:
|
66 |
+
*
|
67 |
+
* * If the Context frame is an associative array which contains the key $id, returns the value of that element.
|
68 |
+
* * If the Context frame is an object, this will check first for a public method, then a public property named
|
69 |
+
* $id. Failing both of these, it will try `__isset` and `__get` magic methods.
|
70 |
+
* * If a value named $id is not found in any Context frame, returns an empty string.
|
71 |
+
*
|
72 |
+
* @param string $id Variable name
|
73 |
+
*
|
74 |
+
* @return mixed Variable value, or '' if not found
|
75 |
+
*/
|
76 |
+
public function find($id)
|
77 |
+
{
|
78 |
+
return $this->findVariableInStack($id, $this->stack);
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Find a 'dot notation' variable in the Context stack.
|
83 |
+
*
|
84 |
+
* Note that dot notation traversal bubbles through scope differently than the regular find method. After finding
|
85 |
+
* the initial chunk of the dotted name, each subsequent chunk is searched for only within the value of the previous
|
86 |
+
* result. For example, given the following context stack:
|
87 |
+
*
|
88 |
+
* $data = array(
|
89 |
+
* 'name' => 'Fred',
|
90 |
+
* 'child' => array(
|
91 |
+
* 'name' => 'Bob'
|
92 |
+
* ),
|
93 |
+
* );
|
94 |
+
*
|
95 |
+
* ... and the Mustache following template:
|
96 |
+
*
|
97 |
+
* {{ child.name }}
|
98 |
+
*
|
99 |
+
* ... the `name` value is only searched for within the `child` value of the global Context, not within parent
|
100 |
+
* Context frames.
|
101 |
+
*
|
102 |
+
* @param string $id Dotted variable selector
|
103 |
+
*
|
104 |
+
* @return mixed Variable value, or '' if not found
|
105 |
+
*/
|
106 |
+
public function findDot($id)
|
107 |
+
{
|
108 |
+
$chunks = explode('.', $id);
|
109 |
+
$first = array_shift($chunks);
|
110 |
+
$value = $this->findVariableInStack($first, $this->stack);
|
111 |
+
|
112 |
+
foreach ($chunks as $chunk) {
|
113 |
+
if ($value === '') {
|
114 |
+
return $value;
|
115 |
+
}
|
116 |
+
|
117 |
+
$value = $this->findVariableInStack($chunk, array($value));
|
118 |
+
}
|
119 |
+
|
120 |
+
return $value;
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Helper function to find a variable in the Context stack.
|
125 |
+
*
|
126 |
+
* @see Mustache_Context::find
|
127 |
+
*
|
128 |
+
* @param string $id Variable name
|
129 |
+
* @param array $stack Context stack
|
130 |
+
*
|
131 |
+
* @return mixed Variable value, or '' if not found
|
132 |
+
*/
|
133 |
+
private function findVariableInStack($id, array $stack)
|
134 |
+
{
|
135 |
+
for ($i = count($stack) - 1; $i >= 0; $i--) {
|
136 |
+
if (is_object($stack[$i]) && !$stack[$i] instanceof Closure) {
|
137 |
+
if (method_exists($stack[$i], $id)) {
|
138 |
+
return $stack[$i]->$id();
|
139 |
+
} elseif (isset($stack[$i]->$id)) {
|
140 |
+
return $stack[$i]->$id;
|
141 |
+
}
|
142 |
+
} elseif (is_array($stack[$i]) && array_key_exists($id, $stack[$i])) {
|
143 |
+
return $stack[$i][$id];
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
return '';
|
148 |
+
}
|
149 |
+
}
|
assets/lib/Mustache/Engine.php
ADDED
@@ -0,0 +1,748 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* A Mustache implementation in PHP.
|
14 |
+
*
|
15 |
+
* {@link http://defunkt.github.com/mustache}
|
16 |
+
*
|
17 |
+
* Mustache is a framework-agnostic logic-less templating language. It enforces separation of view
|
18 |
+
* logic from template files. In fact, it is not even possible to embed logic in the template.
|
19 |
+
*
|
20 |
+
* This is very, very rad.
|
21 |
+
*
|
22 |
+
* @author Justin Hileman {@link http://justinhileman.com}
|
23 |
+
*/
|
24 |
+
class Mustache_Engine
|
25 |
+
{
|
26 |
+
const VERSION = '2.4.1';
|
27 |
+
const SPEC_VERSION = '1.1.2';
|
28 |
+
|
29 |
+
const PRAGMA_FILTERS = 'FILTERS';
|
30 |
+
|
31 |
+
// Template cache
|
32 |
+
private $templates = array();
|
33 |
+
|
34 |
+
// Environment
|
35 |
+
private $templateClassPrefix = '__Mustache_';
|
36 |
+
private $cache = null;
|
37 |
+
private $cacheFileMode = null;
|
38 |
+
private $loader;
|
39 |
+
private $partialsLoader;
|
40 |
+
private $helpers;
|
41 |
+
private $escape;
|
42 |
+
private $entityFlags = ENT_COMPAT;
|
43 |
+
private $charset = 'UTF-8';
|
44 |
+
private $logger;
|
45 |
+
private $strictCallables = false;
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Mustache class constructor.
|
49 |
+
*
|
50 |
+
* Passing an $options array allows overriding certain Mustache options during instantiation:
|
51 |
+
*
|
52 |
+
* $options = array(
|
53 |
+
* // The class prefix for compiled templates. Defaults to '__Mustache_'.
|
54 |
+
* 'template_class_prefix' => '__MyTemplates_',
|
55 |
+
*
|
56 |
+
* // A cache directory for compiled templates. Mustache will not cache templates unless this is set
|
57 |
+
* 'cache' => dirname(__FILE__).'/tmp/cache/mustache',
|
58 |
+
*
|
59 |
+
* // Override default permissions for cache files. Defaults to using the system-defined umask. It is
|
60 |
+
* // *strongly* recommended that you configure your umask properly rather than overriding permissions here.
|
61 |
+
* 'cache_file_mode' => 0666,
|
62 |
+
*
|
63 |
+
* // A Mustache template loader instance. Uses a StringLoader if not specified.
|
64 |
+
* 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'),
|
65 |
+
*
|
66 |
+
* // A Mustache loader instance for partials.
|
67 |
+
* 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'),
|
68 |
+
*
|
69 |
+
* // An array of Mustache partials. Useful for quick-and-dirty string template loading, but not as
|
70 |
+
* // efficient or lazy as a Filesystem (or database) loader.
|
71 |
+
* 'partials' => array('foo' => file_get_contents(dirname(__FILE__).'/views/partials/foo.mustache')),
|
72 |
+
*
|
73 |
+
* // An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order
|
74 |
+
* // sections), or any other valid Mustache context value. They will be prepended to the context stack,
|
75 |
+
* // so they will be available in any template loaded by this Mustache instance.
|
76 |
+
* 'helpers' => array('i18n' => function($text) {
|
77 |
+
* // do something translatey here...
|
78 |
+
* }),
|
79 |
+
*
|
80 |
+
* // An 'escape' callback, responsible for escaping double-mustache variables.
|
81 |
+
* 'escape' => function($value) {
|
82 |
+
* return htmlspecialchars($buffer, ENT_COMPAT, 'UTF-8');
|
83 |
+
* },
|
84 |
+
*
|
85 |
+
* // Type argument for `htmlspecialchars`. Defaults to ENT_COMPAT. You may prefer ENT_QUOTES.
|
86 |
+
* 'entity_flags' => ENT_QUOTES,
|
87 |
+
*
|
88 |
+
* // Character set for `htmlspecialchars`. Defaults to 'UTF-8'. Use 'UTF-8'.
|
89 |
+
* 'charset' => 'ISO-8859-1',
|
90 |
+
*
|
91 |
+
* // A Mustache Logger instance. No logging will occur unless this is set. Using a PSR-3 compatible
|
92 |
+
* // logging library -- such as Monolog -- is highly recommended. A simple stream logger implementation is
|
93 |
+
* // available as well:
|
94 |
+
* 'logger' => new Mustache_Logger_StreamLogger('php://stderr'),
|
95 |
+
*
|
96 |
+
* // Only treat Closure instances and invokable classes as callable. If true, values like
|
97 |
+
* // `array('ClassName', 'methodName')` and `array($classInstance, 'methodName')`, which are traditionally
|
98 |
+
* // "callable" in PHP, are not called to resolve variables for interpolation or section contexts. This
|
99 |
+
* // helps protect against arbitrary code execution when user input is passed directly into the template.
|
100 |
+
* // This currently defaults to false, but will default to true in v3.0.
|
101 |
+
* 'strict_callables' => true,
|
102 |
+
* );
|
103 |
+
*
|
104 |
+
* @throws Mustache_Exception_InvalidArgumentException If `escape` option is not callable.
|
105 |
+
*
|
106 |
+
* @param array $options (default: array())
|
107 |
+
*/
|
108 |
+
public function __construct(array $options = array())
|
109 |
+
{
|
110 |
+
if (isset($options['template_class_prefix'])) {
|
111 |
+
$this->templateClassPrefix = $options['template_class_prefix'];
|
112 |
+
}
|
113 |
+
|
114 |
+
if (isset($options['cache'])) {
|
115 |
+
$this->cache = $options['cache'];
|
116 |
+
}
|
117 |
+
|
118 |
+
if (isset($options['cache_file_mode'])) {
|
119 |
+
$this->cacheFileMode = $options['cache_file_mode'];
|
120 |
+
}
|
121 |
+
|
122 |
+
if (isset($options['loader'])) {
|
123 |
+
$this->setLoader($options['loader']);
|
124 |
+
}
|
125 |
+
|
126 |
+
if (isset($options['partials_loader'])) {
|
127 |
+
$this->setPartialsLoader($options['partials_loader']);
|
128 |
+
}
|
129 |
+
|
130 |
+
if (isset($options['partials'])) {
|
131 |
+
$this->setPartials($options['partials']);
|
132 |
+
}
|
133 |
+
|
134 |
+
if (isset($options['helpers'])) {
|
135 |
+
$this->setHelpers($options['helpers']);
|
136 |
+
}
|
137 |
+
|
138 |
+
if (isset($options['escape'])) {
|
139 |
+
if (!is_callable($options['escape'])) {
|
140 |
+
throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "escape" option must be callable');
|
141 |
+
}
|
142 |
+
|
143 |
+
$this->escape = $options['escape'];
|
144 |
+
}
|
145 |
+
|
146 |
+
if (isset($options['entity_flags'])) {
|
147 |
+
$this->entityFlags = $options['entity_flags'];
|
148 |
+
}
|
149 |
+
|
150 |
+
if (isset($options['charset'])) {
|
151 |
+
$this->charset = $options['charset'];
|
152 |
+
}
|
153 |
+
|
154 |
+
if (isset($options['logger'])) {
|
155 |
+
$this->setLogger($options['logger']);
|
156 |
+
}
|
157 |
+
|
158 |
+
if (isset($options['strict_callables'])) {
|
159 |
+
$this->strictCallables = $options['strict_callables'];
|
160 |
+
}
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Shortcut 'render' invocation.
|
165 |
+
*
|
166 |
+
* Equivalent to calling `$mustache->loadTemplate($template)->render($context);`
|
167 |
+
*
|
168 |
+
* @see Mustache_Engine::loadTemplate
|
169 |
+
* @see Mustache_Template::render
|
170 |
+
*
|
171 |
+
* @param string $template
|
172 |
+
* @param mixed $context (default: array())
|
173 |
+
*
|
174 |
+
* @return string Rendered template
|
175 |
+
*/
|
176 |
+
public function render($template, $context = array())
|
177 |
+
{
|
178 |
+
return $this->loadTemplate($template)->render($context);
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Get the current Mustache escape callback.
|
183 |
+
*
|
184 |
+
* @return mixed Callable or null
|
185 |
+
*/
|
186 |
+
public function getEscape()
|
187 |
+
{
|
188 |
+
return $this->escape;
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Get the current Mustache entitity type to escape.
|
193 |
+
*
|
194 |
+
* @return int
|
195 |
+
*/
|
196 |
+
public function getEntityFlags()
|
197 |
+
{
|
198 |
+
return $this->entityFlags;
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Get the current Mustache character set.
|
203 |
+
*
|
204 |
+
* @return string
|
205 |
+
*/
|
206 |
+
public function getCharset()
|
207 |
+
{
|
208 |
+
return $this->charset;
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Set the Mustache template Loader instance.
|
213 |
+
*
|
214 |
+
* @param Mustache_Loader $loader
|
215 |
+
*/
|
216 |
+
public function setLoader(Mustache_Loader $loader)
|
217 |
+
{
|
218 |
+
$this->loader = $loader;
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Get the current Mustache template Loader instance.
|
223 |
+
*
|
224 |
+
* If no Loader instance has been explicitly specified, this method will instantiate and return
|
225 |
+
* a StringLoader instance.
|
226 |
+
*
|
227 |
+
* @return Mustache_Loader
|
228 |
+
*/
|
229 |
+
public function getLoader()
|
230 |
+
{
|
231 |
+
if (!isset($this->loader)) {
|
232 |
+
$this->loader = new Mustache_Loader_StringLoader;
|
233 |
+
}
|
234 |
+
|
235 |
+
return $this->loader;
|
236 |
+
}
|
237 |
+
|
238 |
+
/**
|
239 |
+
* Set the Mustache partials Loader instance.
|
240 |
+
*
|
241 |
+
* @param Mustache_Loader $partialsLoader
|
242 |
+
*/
|
243 |
+
public function setPartialsLoader(Mustache_Loader $partialsLoader)
|
244 |
+
{
|
245 |
+
$this->partialsLoader = $partialsLoader;
|
246 |
+
}
|
247 |
+
|
248 |
+
/**
|
249 |
+
* Get the current Mustache partials Loader instance.
|
250 |
+
*
|
251 |
+
* If no Loader instance has been explicitly specified, this method will instantiate and return
|
252 |
+
* an ArrayLoader instance.
|
253 |
+
*
|
254 |
+
* @return Mustache_Loader
|
255 |
+
*/
|
256 |
+
public function getPartialsLoader()
|
257 |
+
{
|
258 |
+
if (!isset($this->partialsLoader)) {
|
259 |
+
$this->partialsLoader = new Mustache_Loader_ArrayLoader;
|
260 |
+
}
|
261 |
+
|
262 |
+
return $this->partialsLoader;
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Set partials for the current partials Loader instance.
|
267 |
+
*
|
268 |
+
* @throws Mustache_Exception_RuntimeException If the current Loader instance is immutable
|
269 |
+
*
|
270 |
+
* @param array $partials (default: array())
|
271 |
+
*/
|
272 |
+
public function setPartials(array $partials = array())
|
273 |
+
{
|
274 |
+
if (!isset($this->partialsLoader)) {
|
275 |
+
$this->partialsLoader = new Mustache_Loader_ArrayLoader;
|
276 |
+
}
|
277 |
+
|
278 |
+
if (!$this->partialsLoader instanceof Mustache_Loader_MutableLoader) {
|
279 |
+
throw new Mustache_Exception_RuntimeException('Unable to set partials on an immutable Mustache Loader instance');
|
280 |
+
}
|
281 |
+
|
282 |
+
$this->partialsLoader->setTemplates($partials);
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* Set an array of Mustache helpers.
|
287 |
+
*
|
288 |
+
* An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order sections), or
|
289 |
+
* any other valid Mustache context value. They will be prepended to the context stack, so they will be available in
|
290 |
+
* any template loaded by this Mustache instance.
|
291 |
+
*
|
292 |
+
* @throws Mustache_Exception_InvalidArgumentException if $helpers is not an array or Traversable
|
293 |
+
*
|
294 |
+
* @param array|Traversable $helpers
|
295 |
+
*/
|
296 |
+
public function setHelpers($helpers)
|
297 |
+
{
|
298 |
+
if (!is_array($helpers) && !$helpers instanceof Traversable) {
|
299 |
+
throw new Mustache_Exception_InvalidArgumentException('setHelpers expects an array of helpers');
|
300 |
+
}
|
301 |
+
|
302 |
+
$this->getHelpers()->clear();
|
303 |
+
|
304 |
+
foreach ($helpers as $name => $helper) {
|
305 |
+
$this->addHelper($name, $helper);
|
306 |
+
}
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* Get the current set of Mustache helpers.
|
311 |
+
*
|
312 |
+
* @see Mustache_Engine::setHelpers
|
313 |
+
*
|
314 |
+
* @return Mustache_HelperCollection
|
315 |
+
*/
|
316 |
+
public function getHelpers()
|
317 |
+
{
|
318 |
+
if (!isset($this->helpers)) {
|
319 |
+
$this->helpers = new Mustache_HelperCollection;
|
320 |
+
}
|
321 |
+
|
322 |
+
return $this->helpers;
|
323 |
+
}
|
324 |
+
|
325 |
+
/**
|
326 |
+
* Add a new Mustache helper.
|
327 |
+
*
|
328 |
+
* @see Mustache_Engine::setHelpers
|
329 |
+
*
|
330 |
+
* @param string $name
|
331 |
+
* @param mixed $helper
|
332 |
+
*/
|
333 |
+
public function addHelper($name, $helper)
|
334 |
+
{
|
335 |
+
$this->getHelpers()->add($name, $helper);
|
336 |
+
}
|
337 |
+
|
338 |
+
/**
|
339 |
+
* Get a Mustache helper by name.
|
340 |
+
*
|
341 |
+
* @see Mustache_Engine::setHelpers
|
342 |
+
*
|
343 |
+
* @param string $name
|
344 |
+
*
|
345 |
+
* @return mixed Helper
|
346 |
+
*/
|
347 |
+
public function getHelper($name)
|
348 |
+
{
|
349 |
+
return $this->getHelpers()->get($name);
|
350 |
+
}
|
351 |
+
|
352 |
+
/**
|
353 |
+
* Check whether this Mustache instance has a helper.
|
354 |
+
*
|
355 |
+
* @see Mustache_Engine::setHelpers
|
356 |
+
*
|
357 |
+
* @param string $name
|
358 |
+
*
|
359 |
+
* @return boolean True if the helper is present
|
360 |
+
*/
|
361 |
+
public function hasHelper($name)
|
362 |
+
{
|
363 |
+
return $this->getHelpers()->has($name);
|
364 |
+
}
|
365 |
+
|
366 |
+
/**
|
367 |
+
* Remove a helper by name.
|
368 |
+
*
|
369 |
+
* @see Mustache_Engine::setHelpers
|
370 |
+
*
|
371 |
+
* @param string $name
|
372 |
+
*/
|
373 |
+
public function removeHelper($name)
|
374 |
+
{
|
375 |
+
$this->getHelpers()->remove($name);
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* Set the Mustache Logger instance.
|
380 |
+
*
|
381 |
+
* @throws Mustache_Exception_InvalidArgumentException If logger is not an instance of Mustache_Logger or Psr\Log\LoggerInterface.
|
382 |
+
*
|
383 |
+
* @param Mustache_Logger|Psr\Log\LoggerInterface $logger
|
384 |
+
*/
|
385 |
+
public function setLogger($logger = null)
|
386 |
+
{
|
387 |
+
if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) {
|
388 |
+
throw new Mustache_Exception_InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.');
|
389 |
+
}
|
390 |
+
|
391 |
+
$this->logger = $logger;
|
392 |
+
}
|
393 |
+
|
394 |
+
/**
|
395 |
+
* Get the current Mustache Logger instance.
|
396 |
+
*
|
397 |
+
* @return Mustache_Logger|Psr\Log\LoggerInterface
|
398 |
+
*/
|
399 |
+
public function getLogger()
|
400 |
+
{
|
401 |
+
return $this->logger;
|
402 |
+
}
|
403 |
+
|
404 |
+
/**
|
405 |
+
* Set the Mustache Tokenizer instance.
|
406 |
+
*
|
407 |
+
* @param Mustache_Tokenizer $tokenizer
|
408 |
+
*/
|
409 |
+
public function setTokenizer(Mustache_Tokenizer $tokenizer)
|
410 |
+
{
|
411 |
+
$this->tokenizer = $tokenizer;
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* Get the current Mustache Tokenizer instance.
|
416 |
+
*
|
417 |
+
* If no Tokenizer instance has been explicitly specified, this method will instantiate and return a new one.
|
418 |
+
*
|
419 |
+
* @return Mustache_Tokenizer
|
420 |
+
*/
|
421 |
+
public function getTokenizer()
|
422 |
+
{
|
423 |
+
if (!isset($this->tokenizer)) {
|
424 |
+
$this->tokenizer = new Mustache_Tokenizer;
|
425 |
+
}
|
426 |
+
|
427 |
+
return $this->tokenizer;
|
428 |
+
}
|
429 |
+
|
430 |
+
/**
|
431 |
+
* Set the Mustache Parser instance.
|
432 |
+
*
|
433 |
+
* @param Mustache_Parser $parser
|
434 |
+
*/
|
435 |
+
public function setParser(Mustache_Parser $parser)
|
436 |
+
{
|
437 |
+
$this->parser = $parser;
|
438 |
+
}
|
439 |
+
|
440 |
+
/**
|
441 |
+
* Get the current Mustache Parser instance.
|
442 |
+
*
|
443 |
+
* If no Parser instance has been explicitly specified, this method will instantiate and return a new one.
|
444 |
+
*
|
445 |
+
* @return Mustache_Parser
|
446 |
+
*/
|
447 |
+
public function getParser()
|
448 |
+
{
|
449 |
+
if (!isset($this->parser)) {
|
450 |
+
$this->parser = new Mustache_Parser;
|
451 |
+
}
|
452 |
+
|
453 |
+
return $this->parser;
|
454 |
+
}
|
455 |
+
|
456 |
+
/**
|
457 |
+
* Set the Mustache Compiler instance.
|
458 |
+
*
|
459 |
+
* @param Mustache_Compiler $compiler
|
460 |
+
*/
|
461 |
+
public function setCompiler(Mustache_Compiler $compiler)
|
462 |
+
{
|
463 |
+
$this->compiler = $compiler;
|
464 |
+
}
|
465 |
+
|
466 |
+
/**
|
467 |
+
* Get the current Mustache Compiler instance.
|
468 |
+
*
|
469 |
+
* If no Compiler instance has been explicitly specified, this method will instantiate and return a new one.
|
470 |
+
*
|
471 |
+
* @return Mustache_Compiler
|
472 |
+
*/
|
473 |
+
public function getCompiler()
|
474 |
+
{
|
475 |
+
if (!isset($this->compiler)) {
|
476 |
+
$this->compiler = new Mustache_Compiler;
|
477 |
+
}
|
478 |
+
|
479 |
+
return $this->compiler;
|
480 |
+
}
|
481 |
+
|
482 |
+
/**
|
483 |
+
* Helper method to generate a Mustache template class.
|
484 |
+
*
|
485 |
+
* @param string $source
|
486 |
+
*
|
487 |
+
* @return string Mustache Template class name
|
488 |
+
*/
|
489 |
+
public function getTemplateClassName($source)
|
490 |
+
{
|
491 |
+
return $this->templateClassPrefix . md5(sprintf(
|
492 |
+
'version:%s,escape:%s,entity_flags:%d,charset:%s,strict_callables:%s,source:%s',
|
493 |
+
self::VERSION,
|
494 |
+
isset($this->escape) ? 'custom' : 'default',
|
495 |
+
$this->entityFlags,
|
496 |
+
$this->charset,
|
497 |
+
$this->strictCallables ? 'true' : 'false',
|
498 |
+
$source
|
499 |
+
));
|
500 |
+
}
|
501 |
+
|
502 |
+
/**
|
503 |
+
* Load a Mustache Template by name.
|
504 |
+
*
|
505 |
+
* @param string $name
|
506 |
+
*
|
507 |
+
* @return Mustache_Template
|
508 |
+
*/
|
509 |
+
public function loadTemplate($name)
|
510 |
+
{
|
511 |
+
return $this->loadSource($this->getLoader()->load($name));
|
512 |
+
}
|
513 |
+
|
514 |
+
/**
|
515 |
+
* Load a Mustache partial Template by name.
|
516 |
+
*
|
517 |
+
* This is a helper method used internally by Template instances for loading partial templates. You can most likely
|
518 |
+
* ignore it completely.
|
519 |
+
*
|
520 |
+
* @param string $name
|
521 |
+
*
|
522 |
+
* @return Mustache_Template
|
523 |
+
*/
|
524 |
+
public function loadPartial($name)
|
525 |
+
{
|
526 |
+
try {
|
527 |
+
if (isset($this->partialsLoader)) {
|
528 |
+
$loader = $this->partialsLoader;
|
529 |
+
} elseif (isset($this->loader) && !$this->loader instanceof Mustache_Loader_StringLoader) {
|
530 |
+
$loader = $this->loader;
|
531 |
+
} else {
|
532 |
+
throw new Mustache_Exception_UnknownTemplateException($name);
|
533 |
+
}
|
534 |
+
|
535 |
+
return $this->loadSource($loader->load($name));
|
536 |
+
} catch (Mustache_Exception_UnknownTemplateException $e) {
|
537 |
+
// If the named partial cannot be found, log then return null.
|
538 |
+
$this->log(
|
539 |
+
Mustache_Logger::WARNING,
|
540 |
+
'Partial not found: "{name}"',
|
541 |
+
array('name' => $e->getTemplateName())
|
542 |
+
);
|
543 |
+
}
|
544 |
+
}
|
545 |
+
|
546 |
+
/**
|
547 |
+
* Load a Mustache lambda Template by source.
|
548 |
+
*
|
549 |
+
* This is a helper method used by Template instances to generate subtemplates for Lambda sections. You can most
|
550 |
+
* likely ignore it completely.
|
551 |
+
*
|
552 |
+
* @param string $source
|
553 |
+
* @param string $delims (default: null)
|
554 |
+
*
|
555 |
+
* @return Mustache_Template
|
556 |
+
*/
|
557 |
+
public function loadLambda($source, $delims = null)
|
558 |
+
{
|
559 |
+
if ($delims !== null) {
|
560 |
+
$source = $delims . "\n" . $source;
|
561 |
+
}
|
562 |
+
|
563 |
+
return $this->loadSource($source);
|
564 |
+
}
|
565 |
+
|
566 |
+
/**
|
567 |
+
* Instantiate and return a Mustache Template instance by source.
|
568 |
+
*
|
569 |
+
* @see Mustache_Engine::loadTemplate
|
570 |
+
* @see Mustache_Engine::loadPartial
|
571 |
+
* @see Mustache_Engine::loadLambda
|
572 |
+
*
|
573 |
+
* @param string $source
|
574 |
+
*
|
575 |
+
* @return Mustache_Template
|
576 |
+
*/
|
577 |
+
private function loadSource($source)
|
578 |
+
{
|
579 |
+
$className = $this->getTemplateClassName($source);
|
580 |
+
|
581 |
+
if (!isset($this->templates[$className])) {
|
582 |
+
if (!class_exists($className, false)) {
|
583 |
+
if ($fileName = $this->getCacheFilename($source)) {
|
584 |
+
if (!is_file($fileName)) {
|
585 |
+
$this->log(
|
586 |
+
Mustache_Logger::DEBUG,
|
587 |
+
'Writing "{className}" class to template cache: "{fileName}"',
|
588 |
+
array('className' => $className, 'fileName' => $fileName)
|
589 |
+
);
|
590 |
+
|
591 |
+
$this->writeCacheFile($fileName, $this->compile($source));
|
592 |
+
}
|
593 |
+
|
594 |
+
require_once $fileName;
|
595 |
+
} else {
|
596 |
+
$this->log(
|
597 |
+
Mustache_Logger::WARNING,
|
598 |
+
'Template cache disabled, evaluating "{className}" class at runtime',
|
599 |
+
array('className' => $className)
|
600 |
+
);
|
601 |
+
|
602 |
+
eval('?>'.$this->compile($source));
|
603 |
+
}
|
604 |
+
}
|
605 |
+
|
606 |
+
$this->log(
|
607 |
+
Mustache_Logger::DEBUG,
|
608 |
+
'Instantiating template: "{className}"',
|
609 |
+
array('className' => $className)
|
610 |
+
);
|
611 |
+
|
612 |
+
$this->templates[$className] = new $className($this);
|
613 |
+
}
|
614 |
+
|
615 |
+
return $this->templates[$className];
|
616 |
+
}
|
617 |
+
|
618 |
+
/**
|
619 |
+
* Helper method to tokenize a Mustache template.
|
620 |
+
*
|
621 |
+
* @see Mustache_Tokenizer::scan
|
622 |
+
*
|
623 |
+
* @param string $source
|
624 |
+
*
|
625 |
+
* @return array Tokens
|
626 |
+
*/
|
627 |
+
private function tokenize($source)
|
628 |
+
{
|
629 |
+
return $this->getTokenizer()->scan($source);
|
630 |
+
}
|
631 |
+
|
632 |
+
/**
|
633 |
+
* Helper method to parse a Mustache template.
|
634 |
+
*
|
635 |
+
* @see Mustache_Parser::parse
|
636 |
+
*
|
637 |
+
* @param string $source
|
638 |
+
*
|
639 |
+
* @return array Token tree
|
640 |
+
*/
|
641 |
+
private function parse($source)
|
642 |
+
{
|
643 |
+
return $this->getParser()->parse($this->tokenize($source));
|
644 |
+
}
|
645 |
+
|
646 |
+
/**
|
647 |
+
* Helper method to compile a Mustache template.
|
648 |
+
*
|
649 |
+
* @see Mustache_Compiler::compile
|
650 |
+
*
|
651 |
+
* @param string $source
|
652 |
+
*
|
653 |
+
* @return string generated Mustache template class code
|
654 |
+
*/
|
655 |
+
private function compile($source)
|
656 |
+
{
|
657 |
+
$tree = $this->parse($source);
|
658 |
+
$name = $this->getTemplateClassName($source);
|
659 |
+
|
660 |
+
$this->log(
|
661 |
+
Mustache_Logger::INFO,
|
662 |
+
'Compiling template to "{className}" class',
|
663 |
+
array('className' => $name)
|
664 |
+
);
|
665 |
+
|
666 |
+
return $this->getCompiler()->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags);
|
667 |
+
}
|
668 |
+
|
669 |
+
/**
|
670 |
+
* Helper method to generate a Mustache Template class cache filename.
|
671 |
+
*
|
672 |
+
* @param string $source
|
673 |
+
*
|
674 |
+
* @return string Mustache Template class cache filename
|
675 |
+
*/
|
676 |
+
private function getCacheFilename($source)
|
677 |
+
{
|
678 |
+
if ($this->cache) {
|
679 |
+
return sprintf('%s/%s.php', $this->cache, $this->getTemplateClassName($source));
|
680 |
+
}
|
681 |
+
}
|
682 |
+
|
683 |
+
/**
|
684 |
+
* Helper method to dump a generated Mustache Template subclass to the file cache.
|
685 |
+
*
|
686 |
+
* @throws Mustache_Exception_RuntimeException if unable to create the cache directory or write to $fileName.
|
687 |
+
*
|
688 |
+
* @param string $fileName
|
689 |
+
* @param string $source
|
690 |
+
*
|
691 |
+
* @codeCoverageIgnore
|
692 |
+
*/
|
693 |
+
private function writeCacheFile($fileName, $source)
|
694 |
+
{
|
695 |
+
$dirName = dirname($fileName);
|
696 |
+
if (!is_dir($dirName)) {
|
697 |
+
$this->log(
|
698 |
+
Mustache_Logger::INFO,
|
699 |
+
'Creating Mustache template cache directory: "{dirName}"',
|
700 |
+
array('dirName' => $dirName)
|
701 |
+
);
|
702 |
+
|
703 |
+
@mkdir($dirName, 0777, true);
|
704 |
+
if (!is_dir($dirName)) {
|
705 |
+
throw new Mustache_Exception_RuntimeException(sprintf('Failed to create cache directory "%s".', $dirName));
|
706 |
+
}
|
707 |
+
|
708 |
+
}
|
709 |
+
|
710 |
+
$this->log(
|
711 |
+
Mustache_Logger::DEBUG,
|
712 |
+
'Caching compiled template to "{fileName}"',
|
713 |
+
array('fileName' => $fileName)
|
714 |
+
);
|
715 |
+
|
716 |
+
$tempFile = tempnam($dirName, basename($fileName));
|
717 |
+
if (false !== @file_put_contents($tempFile, $source)) {
|
718 |
+
if (@rename($tempFile, $fileName)) {
|
719 |
+
$mode = isset($this->cacheFileMode) ? $this->cacheFileMode : (0666 & ~umask());
|
720 |
+
@chmod($fileName, $mode);
|
721 |
+
|
722 |
+
return;
|
723 |
+
}
|
724 |
+
|
725 |
+
$this->log(
|
726 |
+
Mustache_Logger::ERROR,
|
727 |
+
'Unable to rename Mustache temp cache file: "{tempName}" -> "{fileName}"',
|
728 |
+
array('tempName' => $tempFile, 'fileName' => $fileName)
|
729 |
+
);
|
730 |
+
}
|
731 |
+
|
732 |
+
throw new Mustache_Exception_RuntimeException(sprintf('Failed to write cache file "%s".', $fileName));
|
733 |
+
}
|
734 |
+
|
735 |
+
/**
|
736 |
+
* Add a log record if logging is enabled.
|
737 |
+
*
|
738 |
+
* @param integer $level The logging level
|
739 |
+
* @param string $message The log message
|
740 |
+
* @param array $context The log context
|
741 |
+
*/
|
742 |
+
private function log($level, $message, array $context = array())
|
743 |
+
{
|
744 |
+
if (isset($this->logger)) {
|
745 |
+
$this->logger->log($level, $message, $context);
|
746 |
+
}
|
747 |
+
}
|
748 |
+
}
|
assets/lib/Mustache/Exception.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2013 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* A Mustache Exception interface.
|
14 |
+
*/
|
15 |
+
interface Mustache_Exception
|
16 |
+
{
|
17 |
+
// This space intentionally left blank.
|
18 |
+
}
|
assets/lib/Mustache/Exception/InvalidArgumentException.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2013 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Invalid argument exception.
|
14 |
+
*/
|
15 |
+
class Mustache_Exception_InvalidArgumentException extends InvalidArgumentException implements Mustache_Exception
|
16 |
+
{
|
17 |
+
// This space intentionally left blank.
|
18 |
+
}
|
assets/lib/Mustache/Exception/LogicException.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2013 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Logic exception.
|
14 |
+
*/
|
15 |
+
class Mustache_Exception_LogicException extends LogicException implements Mustache_Exception
|
16 |
+
{
|
17 |
+
// This space intentionally left blank.
|
18 |
+
}
|
assets/lib/Mustache/Exception/RuntimeException.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2013 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Runtime exception.
|
14 |
+
*/
|
15 |
+
class Mustache_Exception_RuntimeException extends RuntimeException implements Mustache_Exception
|
16 |
+
{
|
17 |
+
// This space intentionally left blank.
|
18 |
+
}
|
assets/lib/Mustache/Exception/SyntaxException.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2013 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache syntax exception.
|
14 |
+
*/
|
15 |
+
class Mustache_Exception_SyntaxException extends LogicException implements Mustache_Exception
|
16 |
+
{
|
17 |
+
protected $token;
|
18 |
+
|
19 |
+
public function __construct($msg, array $token)
|
20 |
+
{
|
21 |
+
$this->token = $token;
|
22 |
+
parent::__construct($msg);
|
23 |
+
}
|
24 |
+
|
25 |
+
public function getToken()
|
26 |
+
{
|
27 |
+
return $this->token;
|
28 |
+
}
|
29 |
+
}
|
assets/lib/Mustache/Exception/UnknownFilterException.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2013 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Unknown filter exception.
|
14 |
+
*/
|
15 |
+
class Mustache_Exception_UnknownFilterException extends UnexpectedValueException implements Mustache_Exception
|
16 |
+
{
|
17 |
+
protected $filterName;
|
18 |
+
|
19 |
+
public function __construct($filterName)
|
20 |
+
{
|
21 |
+
$this->filterName = $filterName;
|
22 |
+
parent::__construct(sprintf('Unknown filter: %s', $filterName));
|
23 |
+
}
|
24 |
+
|
25 |
+
public function getFilterName()
|
26 |
+
{
|
27 |
+
return $this->filterName;
|
28 |
+
}
|
29 |
+
}
|
assets/lib/Mustache/Exception/UnknownHelperException.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2013 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Unknown helper exception.
|
14 |
+
*/
|
15 |
+
class Mustache_Exception_UnknownHelperException extends InvalidArgumentException implements Mustache_Exception
|
16 |
+
{
|
17 |
+
protected $helperName;
|
18 |
+
|
19 |
+
public function __construct($helperName)
|
20 |
+
{
|
21 |
+
$this->helperName = $helperName;
|
22 |
+
parent::__construct(sprintf('Unknown helper: %s', $helperName));
|
23 |
+
}
|
24 |
+
|
25 |
+
public function getHelperName()
|
26 |
+
{
|
27 |
+
return $this->helperName;
|
28 |
+
}
|
29 |
+
}
|
assets/lib/Mustache/Exception/UnknownTemplateException.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2013 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Unknown template exception.
|
14 |
+
*/
|
15 |
+
class Mustache_Exception_UnknownTemplateException extends InvalidArgumentException implements Mustache_Exception
|
16 |
+
{
|
17 |
+
protected $templateName;
|
18 |
+
|
19 |
+
public function __construct($templateName)
|
20 |
+
{
|
21 |
+
$this->templateName = $templateName;
|
22 |
+
parent::__construct(sprintf('Unknown template: %s', $templateName));
|
23 |
+
}
|
24 |
+
|
25 |
+
public function getTemplateName()
|
26 |
+
{
|
27 |
+
return $this->templateName;
|
28 |
+
}
|
29 |
+
}
|
assets/lib/Mustache/HelperCollection.php
ADDED
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* A collection of helpers for a Mustache instance.
|
14 |
+
*/
|
15 |
+
class Mustache_HelperCollection
|
16 |
+
{
|
17 |
+
private $helpers = array();
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Helper Collection constructor.
|
21 |
+
*
|
22 |
+
* Optionally accepts an array (or Traversable) of `$name => $helper` pairs.
|
23 |
+
*
|
24 |
+
* @throws Mustache_Exception_InvalidArgumentException if the $helpers argument isn't an array or Traversable
|
25 |
+
*
|
26 |
+
* @param array|Traversable $helpers (default: null)
|
27 |
+
*/
|
28 |
+
public function __construct($helpers = null)
|
29 |
+
{
|
30 |
+
if ($helpers !== null) {
|
31 |
+
if (!is_array($helpers) && !$helpers instanceof Traversable) {
|
32 |
+
throw new Mustache_Exception_InvalidArgumentException('HelperCollection constructor expects an array of helpers');
|
33 |
+
}
|
34 |
+
|
35 |
+
foreach ($helpers as $name => $helper) {
|
36 |
+
$this->add($name, $helper);
|
37 |
+
}
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Magic mutator.
|
43 |
+
*
|
44 |
+
* @see Mustache_HelperCollection::add
|
45 |
+
*
|
46 |
+
* @param string $name
|
47 |
+
* @param mixed $helper
|
48 |
+
*/
|
49 |
+
public function __set($name, $helper)
|
50 |
+
{
|
51 |
+
$this->add($name, $helper);
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Add a helper to this collection.
|
56 |
+
*
|
57 |
+
* @param string $name
|
58 |
+
* @param mixed $helper
|
59 |
+
*/
|
60 |
+
public function add($name, $helper)
|
61 |
+
{
|
62 |
+
$this->helpers[$name] = $helper;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Magic accessor.
|
67 |
+
*
|
68 |
+
* @see Mustache_HelperCollection::get
|
69 |
+
*
|
70 |
+
* @param string $name
|
71 |
+
*
|
72 |
+
* @return mixed Helper
|
73 |
+
*/
|
74 |
+
public function __get($name)
|
75 |
+
{
|
76 |
+
return $this->get($name);
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Get a helper by name.
|
81 |
+
*
|
82 |
+
* @throws Mustache_Exception_UnknownHelperException If helper does not exist.
|
83 |
+
*
|
84 |
+
* @param string $name
|
85 |
+
*
|
86 |
+
* @return mixed Helper
|
87 |
+
*/
|
88 |
+
public function get($name)
|
89 |
+
{
|
90 |
+
if (!$this->has($name)) {
|
91 |
+
throw new Mustache_Exception_UnknownHelperException($name);
|
92 |
+
}
|
93 |
+
|
94 |
+
return $this->helpers[$name];
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Magic isset().
|
99 |
+
*
|
100 |
+
* @see Mustache_HelperCollection::has
|
101 |
+
*
|
102 |
+
* @param string $name
|
103 |
+
*
|
104 |
+
* @return boolean True if helper is present
|
105 |
+
*/
|
106 |
+
public function __isset($name)
|
107 |
+
{
|
108 |
+
return $this->has($name);
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Check whether a given helper is present in the collection.
|
113 |
+
*
|
114 |
+
* @param string $name
|
115 |
+
*
|
116 |
+
* @return boolean True if helper is present
|
117 |
+
*/
|
118 |
+
public function has($name)
|
119 |
+
{
|
120 |
+
return array_key_exists($name, $this->helpers);
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Magic unset().
|
125 |
+
*
|
126 |
+
* @see Mustache_HelperCollection::remove
|
127 |
+
*
|
128 |
+
* @param string $name
|
129 |
+
*/
|
130 |
+
public function __unset($name)
|
131 |
+
{
|
132 |
+
$this->remove($name);
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Check whether a given helper is present in the collection.
|
137 |
+
*
|
138 |
+
* @throws Mustache_Exception_UnknownHelperException if the requested helper is not present.
|
139 |
+
*
|
140 |
+
* @param string $name
|
141 |
+
*/
|
142 |
+
public function remove($name)
|
143 |
+
{
|
144 |
+
if (!$this->has($name)) {
|
145 |
+
throw new Mustache_Exception_UnknownHelperException($name);
|
146 |
+
}
|
147 |
+
|
148 |
+
unset($this->helpers[$name]);
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Clear the helper collection.
|
153 |
+
*
|
154 |
+
* Removes all helpers from this collection
|
155 |
+
*/
|
156 |
+
public function clear()
|
157 |
+
{
|
158 |
+
$this->helpers = array();
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Check whether the helper collection is empty.
|
163 |
+
*
|
164 |
+
* @return boolean True if the collection is empty
|
165 |
+
*/
|
166 |
+
public function isEmpty()
|
167 |
+
{
|
168 |
+
return empty($this->helpers);
|
169 |
+
}
|
170 |
+
}
|
assets/lib/Mustache/LambdaHelper.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache Lambda Helper.
|
14 |
+
*
|
15 |
+
* Passed as the second argument to section lambdas (higher order sections),
|
16 |
+
* giving them access to a `render` method for rendering a string with the
|
17 |
+
* current context.
|
18 |
+
*/
|
19 |
+
class Mustache_LambdaHelper
|
20 |
+
{
|
21 |
+
private $mustache;
|
22 |
+
private $context;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Mustache Lambda Helper constructor.
|
26 |
+
*
|
27 |
+
* @param Mustache_Engine $mustache Mustache engine instance.
|
28 |
+
* @param Mustache_Context $context Rendering context.
|
29 |
+
*/
|
30 |
+
public function __construct(Mustache_Engine $mustache, Mustache_Context $context)
|
31 |
+
{
|
32 |
+
$this->mustache = $mustache;
|
33 |
+
$this->context = $context;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Render a string as a Mustache template with the current rendering context.
|
38 |
+
*
|
39 |
+
* @param string $string
|
40 |
+
*
|
41 |
+
* @return Rendered template.
|
42 |
+
*/
|
43 |
+
public function render($string)
|
44 |
+
{
|
45 |
+
return $this->mustache
|
46 |
+
->loadLambda((string) $string)
|
47 |
+
->renderInternal($this->context);
|
48 |
+
}
|
49 |
+
}
|
assets/lib/Mustache/Loader.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache Template Loader interface.
|
14 |
+
*/
|
15 |
+
interface Mustache_Loader
|
16 |
+
{
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Load a Template by name.
|
20 |
+
*
|
21 |
+
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
|
22 |
+
*
|
23 |
+
* @param string $name
|
24 |
+
*
|
25 |
+
* @return string Mustache Template source
|
26 |
+
*/
|
27 |
+
public function load($name);
|
28 |
+
}
|
assets/lib/Mustache/Loader/ArrayLoader.php
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache Template array Loader implementation.
|
14 |
+
*
|
15 |
+
* An ArrayLoader instance loads Mustache Template source by name from an initial array:
|
16 |
+
*
|
17 |
+
* $loader = new ArrayLoader(
|
18 |
+
* 'foo' => '{{ bar }}',
|
19 |
+
* 'baz' => 'Hey {{ qux }}!'
|
20 |
+
* );
|
21 |
+
*
|
22 |
+
* $tpl = $loader->load('foo'); // '{{ bar }}'
|
23 |
+
*
|
24 |
+
* The ArrayLoader is used internally as a partials loader by Mustache_Engine instance when an array of partials
|
25 |
+
* is set. It can also be used as a quick-and-dirty Template loader.
|
26 |
+
*/
|
27 |
+
class Mustache_Loader_ArrayLoader implements Mustache_Loader, Mustache_Loader_MutableLoader
|
28 |
+
{
|
29 |
+
|
30 |
+
/**
|
31 |
+
* ArrayLoader constructor.
|
32 |
+
*
|
33 |
+
* @param array $templates Associative array of Template source (default: array())
|
34 |
+
*/
|
35 |
+
public function __construct(array $templates = array())
|
36 |
+
{
|
37 |
+
$this->templates = $templates;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Load a Template.
|
42 |
+
*
|
43 |
+
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
|
44 |
+
*
|
45 |
+
* @param string $name
|
46 |
+
*
|
47 |
+
* @return string Mustache Template source
|
48 |
+
*/
|
49 |
+
public function load($name)
|
50 |
+
{
|
51 |
+
if (!isset($this->templates[$name])) {
|
52 |
+
throw new Mustache_Exception_UnknownTemplateException($name);
|
53 |
+
}
|
54 |
+
|
55 |
+
return $this->templates[$name];
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Set an associative array of Template sources for this loader.
|
60 |
+
*
|
61 |
+
* @param array $templates
|
62 |
+
*/
|
63 |
+
public function setTemplates(array $templates)
|
64 |
+
{
|
65 |
+
$this->templates = $templates;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Set a Template source by name.
|
70 |
+
*
|
71 |
+
* @param string $name
|
72 |
+
* @param string $template Mustache Template source
|
73 |
+
*/
|
74 |
+
public function setTemplate($name, $template)
|
75 |
+
{
|
76 |
+
$this->templates[$name] = $template;
|
77 |
+
}
|
78 |
+
}
|
assets/lib/Mustache/Loader/CascadingLoader.php
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2013 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* A Mustache Template cascading loader implementation, which delegates to other
|
14 |
+
* Loader instances.
|
15 |
+
*/
|
16 |
+
class Mustache_Loader_CascadingLoader implements Mustache_Loader
|
17 |
+
{
|
18 |
+
private $loaders;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Construct a CascadingLoader with an array of loaders:
|
22 |
+
*
|
23 |
+
* $loader = new Mustache_Loader_CascadingLoader(array(
|
24 |
+
* new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__),
|
25 |
+
* new Mustache_Loader_FilesystemLoader(__DIR__.'/templates')
|
26 |
+
* ));
|
27 |
+
*
|
28 |
+
* @param array $loaders An array of Mustache Loader instances
|
29 |
+
*/
|
30 |
+
public function __construct(array $loaders = array())
|
31 |
+
{
|
32 |
+
$this->loaders = array();
|
33 |
+
foreach ($loaders as $loader) {
|
34 |
+
$this->addLoader($loader);
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Add a Loader instance.
|
40 |
+
*
|
41 |
+
* @param Mustache_Loader $loader A Mustache Loader instance
|
42 |
+
*/
|
43 |
+
public function addLoader(Mustache_Loader $loader)
|
44 |
+
{
|
45 |
+
$this->loaders[] = $loader;
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Load a Template by name.
|
50 |
+
*
|
51 |
+
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
|
52 |
+
*
|
53 |
+
* @param string $name
|
54 |
+
*
|
55 |
+
* @return string Mustache Template source
|
56 |
+
*/
|
57 |
+
public function load($name)
|
58 |
+
{
|
59 |
+
foreach ($this->loaders as $loader) {
|
60 |
+
try {
|
61 |
+
return $loader->load($name);
|
62 |
+
} catch (Mustache_Exception_UnknownTemplateException $e) {
|
63 |
+
// do nothing, check the next loader.
|
64 |
+
}
|
65 |
+
}
|
66 |
+
|
67 |
+
throw new Mustache_Exception_UnknownTemplateException($name);
|
68 |
+
}
|
69 |
+
}
|
assets/lib/Mustache/Loader/FilesystemLoader.php
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache Template filesystem Loader implementation.
|
14 |
+
*
|
15 |
+
* A FilesystemLoader instance loads Mustache Template source from the filesystem by name:
|
16 |
+
*
|
17 |
+
* $loader = new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views');
|
18 |
+
* $tpl = $loader->load('foo'); // equivalent to `file_get_contents(dirname(__FILE__).'/views/foo.mustache');
|
19 |
+
*
|
20 |
+
* This is probably the most useful Mustache Loader implementation. It can be used for partials and normal Templates:
|
21 |
+
*
|
22 |
+
* $m = new Mustache(array(
|
23 |
+
* 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'),
|
24 |
+
* 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'),
|
25 |
+
* ));
|
26 |
+
*/
|
27 |
+
class Mustache_Loader_FilesystemLoader implements Mustache_Loader
|
28 |
+
{
|
29 |
+
private $baseDir;
|
30 |
+
private $extension = '.mustache';
|
31 |
+
private $templates = array();
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Mustache filesystem Loader constructor.
|
35 |
+
*
|
36 |
+
* Passing an $options array allows overriding certain Loader options during instantiation:
|
37 |
+
*
|
38 |
+
* $options = array(
|
39 |
+
* // The filename extension used for Mustache templates. Defaults to '.mustache'
|
40 |
+
* 'extension' => '.ms',
|
41 |
+
* );
|
42 |
+
*
|
43 |
+
* @throws Mustache_Exception_RuntimeException if $baseDir does not exist.
|
44 |
+
*
|
45 |
+
* @param string $baseDir Base directory containing Mustache template files.
|
46 |
+
* @param array $options Array of Loader options (default: array())
|
47 |
+
*/
|
48 |
+
public function __construct($baseDir, array $options = array())
|
49 |
+
{
|
50 |
+
$this->baseDir = $baseDir;
|
51 |
+
|
52 |
+
if (strpos($this->baseDir, '://') === -1) {
|
53 |
+
$this->baseDir = realpath($this->baseDir);
|
54 |
+
}
|
55 |
+
|
56 |
+
if (!is_dir($this->baseDir)) {
|
57 |
+
throw new Mustache_Exception_RuntimeException(sprintf('FilesystemLoader baseDir must be a directory: %s', $baseDir));
|
58 |
+
}
|
59 |
+
|
60 |
+
if (array_key_exists('extension', $options)) {
|
61 |
+
if (empty($options['extension'])) {
|
62 |
+
$this->extension = '';
|
63 |
+
} else {
|
64 |
+
$this->extension = '.' . ltrim($options['extension'], '.');
|
65 |
+
}
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Load a Template by name.
|
71 |
+
*
|
72 |
+
* $loader = new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views');
|
73 |
+
* $loader->load('admin/dashboard'); // loads "./views/admin/dashboard.mustache";
|
74 |
+
*
|
75 |
+
* @param string $name
|
76 |
+
*
|
77 |
+
* @return string Mustache Template source
|
78 |
+
*/
|
79 |
+
public function load($name)
|
80 |
+
{
|
81 |
+
if (!isset($this->templates[$name])) {
|
82 |
+
$this->templates[$name] = $this->loadFile($name);
|
83 |
+
}
|
84 |
+
|
85 |
+
return $this->templates[$name];
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Helper function for loading a Mustache file by name.
|
90 |
+
*
|
91 |
+
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
|
92 |
+
*
|
93 |
+
* @param string $name
|
94 |
+
*
|
95 |
+
* @return string Mustache Template source
|
96 |
+
*/
|
97 |
+
protected function loadFile($name)
|
98 |
+
{
|
99 |
+
$fileName = $this->getFileName($name);
|
100 |
+
|
101 |
+
if (!file_exists($fileName)) {
|
102 |
+
throw new Mustache_Exception_UnknownTemplateException($name);
|
103 |
+
}
|
104 |
+
|
105 |
+
return file_get_contents($fileName);
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Helper function for getting a Mustache template file name.
|
110 |
+
*
|
111 |
+
* @param string $name
|
112 |
+
*
|
113 |
+
* @return string Template file name
|
114 |
+
*/
|
115 |
+
protected function getFileName($name)
|
116 |
+
{
|
117 |
+
$fileName = $this->baseDir . '/' . $name;
|
118 |
+
if (substr($fileName, 0 - strlen($this->extension)) !== $this->extension) {
|
119 |
+
$fileName .= $this->extension;
|
120 |
+
}
|
121 |
+
|
122 |
+
return $fileName;
|
123 |
+
}
|
124 |
+
}
|
assets/lib/Mustache/Loader/InlineLoader.php
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2013 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* A Mustache Template loader for inline templates.
|
14 |
+
*
|
15 |
+
* With the InlineLoader, templates can be defined at the end of any PHP source
|
16 |
+
* file:
|
17 |
+
*
|
18 |
+
* $loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__);
|
19 |
+
* $hello = $loader->load('hello');
|
20 |
+
* $goodbye = $loader->load('goodbye');
|
21 |
+
*
|
22 |
+
* __halt_compiler();
|
23 |
+
*
|
24 |
+
* @@ hello
|
25 |
+
* Hello, {{ planet }}!
|
26 |
+
*
|
27 |
+
* @@ goodbye
|
28 |
+
* Goodbye, cruel {{ planet }}
|
29 |
+
*
|
30 |
+
* Templates are deliniated by lines containing only `@@ name`.
|
31 |
+
*
|
32 |
+
* The InlineLoader is well-suited to micro-frameworks such as Silex:
|
33 |
+
*
|
34 |
+
* $app->register(new MustacheServiceProvider, array(
|
35 |
+
* 'mustache.loader' => new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__)
|
36 |
+
* ));
|
37 |
+
*
|
38 |
+
* $app->get('/{name}', function($name) use ($app) {
|
39 |
+
* return $app['mustache']->render('hello', compact('name'));
|
40 |
+
* })
|
41 |
+
* ->value('name', 'world');
|
42 |
+
*
|
43 |
+
* // ...
|
44 |
+
*
|
45 |
+
* __halt_compiler();
|
46 |
+
*
|
47 |
+
* @@ hello
|
48 |
+
* Hello, {{ name }}!
|
49 |
+
*
|
50 |
+
*/
|
51 |
+
class Mustache_Loader_InlineLoader implements Mustache_Loader
|
52 |
+
{
|
53 |
+
protected $fileName;
|
54 |
+
protected $offset;
|
55 |
+
protected $templates;
|
56 |
+
|
57 |
+
/**
|
58 |
+
* The InlineLoader requires a filename and offset to process templates.
|
59 |
+
* The magic constants `__FILE__` and `__COMPILER_HALT_OFFSET__` are usually
|
60 |
+
* perfectly suited to the job:
|
61 |
+
*
|
62 |
+
* $loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__);
|
63 |
+
*
|
64 |
+
* Note that this only works if the loader is instantiated inside the same
|
65 |
+
* file as the inline templates. If the templates are located in another
|
66 |
+
* file, it would be necessary to manually specify the filename and offset.
|
67 |
+
*
|
68 |
+
* @param string $fileName The file to parse for inline templates
|
69 |
+
* @param int $offset A string offset for the start of the templates.
|
70 |
+
* This usually coincides with the `__halt_compiler`
|
71 |
+
* call, and the `__COMPILER_HALT_OFFSET__`.
|
72 |
+
*/
|
73 |
+
public function __construct($fileName, $offset)
|
74 |
+
{
|
75 |
+
if (!is_file($fileName)) {
|
76 |
+
throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid filename.');
|
77 |
+
}
|
78 |
+
|
79 |
+
if (!is_int($offset) || $offset < 0) {
|
80 |
+
throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid file offset.');
|
81 |
+
}
|
82 |
+
|
83 |
+
$this->fileName = $fileName;
|
84 |
+
$this->offset = $offset;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Load a Template by name.
|
89 |
+
*
|
90 |
+
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
|
91 |
+
*
|
92 |
+
* @param string $name
|
93 |
+
*
|
94 |
+
* @return string Mustache Template source
|
95 |
+
*/
|
96 |
+
public function load($name)
|
97 |
+
{
|
98 |
+
$this->loadTemplates();
|
99 |
+
|
100 |
+
if (!array_key_exists($name, $this->templates)) {
|
101 |
+
throw new Mustache_Exception_UnknownTemplateException($name);
|
102 |
+
}
|
103 |
+
|
104 |
+
return $this->templates[$name];
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Parse and load templates from the end of a source file.
|
109 |
+
*/
|
110 |
+
protected function loadTemplates()
|
111 |
+
{
|
112 |
+
if ($this->templates === null) {
|
113 |
+
$this->templates = array();
|
114 |
+
$data = file_get_contents($this->fileName, false, null, $this->offset);
|
115 |
+
foreach (preg_split("/^@@(?= [\w\d\.]+$)/m", $data, -1) as $chunk) {
|
116 |
+
if (trim($chunk)) {
|
117 |
+
list($name, $content) = explode("\n", $chunk, 2);
|
118 |
+
$this->templates[trim($name)] = trim($content);
|
119 |
+
}
|
120 |
+
}
|
121 |
+
}
|
122 |
+
}
|
123 |
+
}
|
assets/lib/Mustache/Loader/MutableLoader.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache Template mutable Loader interface.
|
14 |
+
*/
|
15 |
+
interface Mustache_Loader_MutableLoader
|
16 |
+
{
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Set an associative array of Template sources for this loader.
|
20 |
+
*
|
21 |
+
* @param array $templates
|
22 |
+
*/
|
23 |
+
public function setTemplates(array $templates);
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Set a Template source by name.
|
27 |
+
*
|
28 |
+
* @param string $name
|
29 |
+
* @param string $template Mustache Template source
|
30 |
+
*/
|
31 |
+
public function setTemplate($name, $template);
|
32 |
+
}
|
assets/lib/Mustache/Loader/StringLoader.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache Template string Loader implementation.
|
14 |
+
*
|
15 |
+
* A StringLoader instance is essentially a noop. It simply passes the 'name' argument straight through:
|
16 |
+
*
|
17 |
+
* $loader = new StringLoader;
|
18 |
+
* $tpl = $loader->load('{{ foo }}'); // '{{ foo }}'
|
19 |
+
*
|
20 |
+
* This is the default Template Loader instance used by Mustache:
|
21 |
+
*
|
22 |
+
* $m = new Mustache;
|
23 |
+
* $tpl = $m->loadTemplate('{{ foo }}');
|
24 |
+
* echo $tpl->render(array('foo' => 'bar')); // "bar"
|
25 |
+
*/
|
26 |
+
class Mustache_Loader_StringLoader implements Mustache_Loader
|
27 |
+
{
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Load a Template by source.
|
31 |
+
*
|
32 |
+
* @param string $name Mustache Template source
|
33 |
+
*
|
34 |
+
* @return string Mustache Template source
|
35 |
+
*/
|
36 |
+
public function load($name)
|
37 |
+
{
|
38 |
+
return $name;
|
39 |
+
}
|
40 |
+
}
|
assets/lib/Mustache/Logger.php
ADDED
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Describes a Mustache logger instance
|
14 |
+
*
|
15 |
+
* This is identical to the Psr\Log\LoggerInterface.
|
16 |
+
*
|
17 |
+
* The message MUST be a string or object implementing __toString().
|
18 |
+
*
|
19 |
+
* The message MAY contain placeholders in the form: {foo} where foo
|
20 |
+
* will be replaced by the context data in key "foo".
|
21 |
+
*
|
22 |
+
* The context array can contain arbitrary data, the only assumption that
|
23 |
+
* can be made by implementors is that if an Exception instance is given
|
24 |
+
* to produce a stack trace, it MUST be in a key named "exception".
|
25 |
+
*
|
26 |
+
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
|
27 |
+
* for the full interface specification.
|
28 |
+
*/
|
29 |
+
interface Mustache_Logger
|
30 |
+
{
|
31 |
+
/**
|
32 |
+
* Psr\Log compatible log levels
|
33 |
+
*/
|
34 |
+
const EMERGENCY = 'emergency';
|
35 |
+
const ALERT = 'alert';
|
36 |
+
const CRITICAL = 'critical';
|
37 |
+
const ERROR = 'error';
|
38 |
+
const WARNING = 'warning';
|
39 |
+
const NOTICE = 'notice';
|
40 |
+
const INFO = 'info';
|
41 |
+
const DEBUG = 'debug';
|
42 |
+
|
43 |
+
/**
|
44 |
+
* System is unusable.
|
45 |
+
*
|
46 |
+
* @param string $message
|
47 |
+
* @param array $context
|
48 |
+
* @return null
|
49 |
+
*/
|
50 |
+
public function emergency($message, array $context = array());
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Action must be taken immediately.
|
54 |
+
*
|
55 |
+
* Example: Entire website down, database unavailable, etc. This should
|
56 |
+
* trigger the SMS alerts and wake you up.
|
57 |
+
*
|
58 |
+
* @param string $message
|
59 |
+
* @param array $context
|
60 |
+
* @return null
|
61 |
+
*/
|
62 |
+
public function alert($message, array $context = array());
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Critical conditions.
|
66 |
+
*
|
67 |
+
* Example: Application component unavailable, unexpected exception.
|
68 |
+
*
|
69 |
+
* @param string $message
|
70 |
+
* @param array $context
|
71 |
+
* @return null
|
72 |
+
*/
|
73 |
+
public function critical($message, array $context = array());
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Runtime errors that do not require immediate action but should typically
|
77 |
+
* be logged and monitored.
|
78 |
+
*
|
79 |
+
* @param string $message
|
80 |
+
* @param array $context
|
81 |
+
* @return null
|
82 |
+
*/
|
83 |
+
public function error($message, array $context = array());
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Exceptional occurrences that are not errors.
|
87 |
+
*
|
88 |
+
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
89 |
+
* that are not necessarily wrong.
|
90 |
+
*
|
91 |
+
* @param string $message
|
92 |
+
* @param array $context
|
93 |
+
* @return null
|
94 |
+
*/
|
95 |
+
public function warning($message, array $context = array());
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Normal but significant events.
|
99 |
+
*
|
100 |
+
* @param string $message
|
101 |
+
* @param array $context
|
102 |
+
* @return null
|
103 |
+
*/
|
104 |
+
public function notice($message, array $context = array());
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Interesting events.
|
108 |
+
*
|
109 |
+
* Example: User logs in, SQL logs.
|
110 |
+
*
|
111 |
+
* @param string $message
|
112 |
+
* @param array $context
|
113 |
+
* @return null
|
114 |
+
*/
|
115 |
+
public function info($message, array $context = array());
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Detailed debug information.
|
119 |
+
*
|
120 |
+
* @param string $message
|
121 |
+
* @param array $context
|
122 |
+
* @return null
|
123 |
+
*/
|
124 |
+
public function debug($message, array $context = array());
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Logs with an arbitrary level.
|
128 |
+
*
|
129 |
+
* @param mixed $level
|
130 |
+
* @param string $message
|
131 |
+
* @param array $context
|
132 |
+
* @return null
|
133 |
+
*/
|
134 |
+
public function log($level, $message, array $context = array());
|
135 |
+
}
|
assets/lib/Mustache/Logger/AbstractLogger.php
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* This is a simple Logger implementation that other Loggers can inherit from.
|
14 |
+
*
|
15 |
+
* This is identical to the Psr\Log\AbstractLogger.
|
16 |
+
*
|
17 |
+
* It simply delegates all log-level-specific methods to the `log` method to
|
18 |
+
* reduce boilerplate code that a simple Logger that does the same thing with
|
19 |
+
* messages regardless of the error level has to implement.
|
20 |
+
*/
|
21 |
+
abstract class Mustache_Logger_AbstractLogger implements Mustache_Logger
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* System is unusable.
|
25 |
+
*
|
26 |
+
* @param string $message
|
27 |
+
* @param array $context
|
28 |
+
*/
|
29 |
+
public function emergency($message, array $context = array())
|
30 |
+
{
|
31 |
+
$this->log(Mustache_Logger::EMERGENCY, $message, $context);
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Action must be taken immediately.
|
36 |
+
*
|
37 |
+
* Example: Entire website down, database unavailable, etc. This should
|
38 |
+
* trigger the SMS alerts and wake you up.
|
39 |
+
*
|
40 |
+
* @param string $message
|
41 |
+
* @param array $context
|
42 |
+
*/
|
43 |
+
public function alert($message, array $context = array())
|
44 |
+
{
|
45 |
+
$this->log(Mustache_Logger::ALERT, $message, $context);
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Critical conditions.
|
50 |
+
*
|
51 |
+
* Example: Application component unavailable, unexpected exception.
|
52 |
+
*
|
53 |
+
* @param string $message
|
54 |
+
* @param array $context
|
55 |
+
*/
|
56 |
+
public function critical($message, array $context = array())
|
57 |
+
{
|
58 |
+
$this->log(Mustache_Logger::CRITICAL, $message, $context);
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Runtime errors that do not require immediate action but should typically
|
63 |
+
* be logged and monitored.
|
64 |
+
*
|
65 |
+
* @param string $message
|
66 |
+
* @param array $context
|
67 |
+
*/
|
68 |
+
public function error($message, array $context = array())
|
69 |
+
{
|
70 |
+
$this->log(Mustache_Logger::ERROR, $message, $context);
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Exceptional occurrences that are not errors.
|
75 |
+
*
|
76 |
+
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
77 |
+
* that are not necessarily wrong.
|
78 |
+
*
|
79 |
+
* @param string $message
|
80 |
+
* @param array $context
|
81 |
+
*/
|
82 |
+
public function warning($message, array $context = array())
|
83 |
+
{
|
84 |
+
$this->log(Mustache_Logger::WARNING, $message, $context);
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Normal but significant events.
|
89 |
+
*
|
90 |
+
* @param string $message
|
91 |
+
* @param array $context
|
92 |
+
*/
|
93 |
+
public function notice($message, array $context = array())
|
94 |
+
{
|
95 |
+
$this->log(Mustache_Logger::NOTICE, $message, $context);
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Interesting events.
|
100 |
+
*
|
101 |
+
* Example: User logs in, SQL logs.
|
102 |
+
*
|
103 |
+
* @param string $message
|
104 |
+
* @param array $context
|
105 |
+
*/
|
106 |
+
public function info($message, array $context = array())
|
107 |
+
{
|
108 |
+
$this->log(Mustache_Logger::INFO, $message, $context);
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Detailed debug information.
|
113 |
+
*
|
114 |
+
* @param string $message
|
115 |
+
* @param array $context
|
116 |
+
*/
|
117 |
+
public function debug($message, array $context = array())
|
118 |
+
{
|
119 |
+
$this->log(Mustache_Logger::DEBUG, $message, $context);
|
120 |
+
}
|
121 |
+
}
|
assets/lib/Mustache/Logger/StreamLogger.php
ADDED
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* A Mustache Stream Logger.
|
14 |
+
*
|
15 |
+
* The Stream Logger wraps a file resource instance (such as a stream) or a
|
16 |
+
* stream URL. All log messages over the threshold level will be appended to
|
17 |
+
* this stream.
|
18 |
+
*
|
19 |
+
* Hint: Try `php://stderr` for your stream URL.
|
20 |
+
*/
|
21 |
+
class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
|
22 |
+
{
|
23 |
+
protected static $levels = array(
|
24 |
+
self::DEBUG => 100,
|
25 |
+
self::INFO => 200,
|
26 |
+
self::NOTICE => 250,
|
27 |
+
self::WARNING => 300,
|
28 |
+
self::ERROR => 400,
|
29 |
+
self::CRITICAL => 500,
|
30 |
+
self::ALERT => 550,
|
31 |
+
self::EMERGENCY => 600,
|
32 |
+
);
|
33 |
+
|
34 |
+
protected $stream = null;
|
35 |
+
protected $url = null;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @throws InvalidArgumentException if the logging level is unknown.
|
39 |
+
*
|
40 |
+
* @param string $stream Resource instance or URL
|
41 |
+
* @param integer $level The minimum logging level at which this handler will be triggered
|
42 |
+
*/
|
43 |
+
public function __construct($stream, $level = Mustache_Logger::ERROR)
|
44 |
+
{
|
45 |
+
$this->setLevel($level);
|
46 |
+
|
47 |
+
if (is_resource($stream)) {
|
48 |
+
$this->stream = $stream;
|
49 |
+
} else {
|
50 |
+
$this->url = $stream;
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Close stream resources.
|
56 |
+
*/
|
57 |
+
public function __destruct()
|
58 |
+
{
|
59 |
+
if (is_resource($this->stream)) {
|
60 |
+
fclose($this->stream);
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Set the minimum logging level.
|
66 |
+
*
|
67 |
+
* @throws Mustache_Exception_InvalidArgumentException if the logging level is unknown.
|
68 |
+
*
|
69 |
+
* @param integer $level The minimum logging level which will be written
|
70 |
+
*/
|
71 |
+
public function setLevel($level)
|
72 |
+
{
|
73 |
+
if (!array_key_exists($level, self::$levels)) {
|
74 |
+
throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected logging level: %s', $level));
|
75 |
+
}
|
76 |
+
|
77 |
+
$this->level = $level;
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Get the current minimum logging level.
|
82 |
+
*
|
83 |
+
* @return integer
|
84 |
+
*/
|
85 |
+
public function getLevel()
|
86 |
+
{
|
87 |
+
return $this->level;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Logs with an arbitrary level.
|
92 |
+
*
|
93 |
+
* @throws Mustache_Exception_InvalidArgumentException if the logging level is unknown.
|
94 |
+
*
|
95 |
+
* @param mixed $level
|
96 |
+
* @param string $message
|
97 |
+
* @param array $context
|
98 |
+
*/
|
99 |
+
public function log($level, $message, array $context = array())
|
100 |
+
{
|
101 |
+
if (!array_key_exists($level, self::$levels)) {
|
102 |
+
throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected logging level: %s', $level));
|
103 |
+
}
|
104 |
+
|
105 |
+
if (self::$levels[$level] >= self::$levels[$this->level]) {
|
106 |
+
$this->writeLog($level, $message, $context);
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Write a record to the log.
|
112 |
+
*
|
113 |
+
* @throws Mustache_Exception_LogicException If neither a stream resource nor url is present.
|
114 |
+
* @throws Mustache_Exception_RuntimeException If the stream url cannot be opened.
|
115 |
+
*
|
116 |
+
* @param integer $level The logging level
|
117 |
+
* @param string $message The log message
|
118 |
+
* @param array $context The log context
|
119 |
+
*/
|
120 |
+
protected function writeLog($level, $message, array $context = array())
|
121 |
+
{
|
122 |
+
if (!is_resource($this->stream)) {
|
123 |
+
if (!isset($this->url)) {
|
124 |
+
throw new Mustache_Exception_LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
|
125 |
+
}
|
126 |
+
|
127 |
+
$this->stream = fopen($this->url, 'a');
|
128 |
+
if (!is_resource($this->stream)) {
|
129 |
+
// @codeCoverageIgnoreStart
|
130 |
+
throw new Mustache_Exception_RuntimeException(sprintf('The stream or file "%s" could not be opened.', $this->url));
|
131 |
+
// @codeCoverageIgnoreEnd
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
fwrite($this->stream, self::formatLine($level, $message, $context));
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Gets the name of the logging level.
|
140 |
+
*
|
141 |
+
* @throws InvalidArgumentException if the logging level is unknown.
|
142 |
+
*
|
143 |
+
* @param integer $level
|
144 |
+
*
|
145 |
+
* @return string
|
146 |
+
*/
|
147 |
+
protected static function getLevelName($level)
|
148 |
+
{
|
149 |
+
return strtoupper($level);
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Format a log line for output.
|
154 |
+
*
|
155 |
+
* @param integer $level The logging level
|
156 |
+
* @param string $message The log message
|
157 |
+
* @param array $context The log context
|
158 |
+
*
|
159 |
+
* @return string
|
160 |
+
*/
|
161 |
+
protected static function formatLine($level, $message, array $context = array())
|
162 |
+
{
|
163 |
+
return sprintf(
|
164 |
+
"%s: %s\n",
|
165 |
+
self::getLevelName($level),
|
166 |
+
self::interpolateMessage($message, $context)
|
167 |
+
);
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Interpolate context values into the message placeholders.
|
172 |
+
*
|
173 |
+
* @param string $message
|
174 |
+
* @param array $context
|
175 |
+
*
|
176 |
+
* @return string
|
177 |
+
*/
|
178 |
+
protected static function interpolateMessage($message, array $context = array())
|
179 |
+
{
|
180 |
+
if (strpos($message, '{') === false) {
|
181 |
+
return $message;
|
182 |
+
}
|
183 |
+
|
184 |
+
// build a replacement array with braces around the context keys
|
185 |
+
$replace = array();
|
186 |
+
foreach ($context as $key => $val) {
|
187 |
+
$replace['{' . $key . '}'] = $val;
|
188 |
+
}
|
189 |
+
|
190 |
+
// interpolate replacement values into the the message and return
|
191 |
+
return strtr($message, $replace);
|
192 |
+
}
|
193 |
+
}
|
assets/lib/Mustache/Parser.php
ADDED
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache Parser class.
|
14 |
+
*
|
15 |
+
* This class is responsible for turning a set of Mustache tokens into a parse tree.
|
16 |
+
*/
|
17 |
+
class Mustache_Parser
|
18 |
+
{
|
19 |
+
private $lineNum;
|
20 |
+
private $lineTokens;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Process an array of Mustache tokens and convert them into a parse tree.
|
24 |
+
*
|
25 |
+
* @param array $tokens Set of Mustache tokens
|
26 |
+
*
|
27 |
+
* @return array Mustache token parse tree
|
28 |
+
*/
|
29 |
+
public function parse(array $tokens = array())
|
30 |
+
{
|
31 |
+
$this->lineNum = -1;
|
32 |
+
$this->lineTokens = 0;
|
33 |
+
|
34 |
+
return $this->buildTree($tokens);
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Helper method for recursively building a parse tree.
|
39 |
+
*
|
40 |
+
* @throws Mustache_Exception_SyntaxException when nesting errors or mismatched section tags are encountered.
|
41 |
+
*
|
42 |
+
* @param array &$tokens Set of Mustache tokens
|
43 |
+
* @param array $parent Parent token (default: null)
|
44 |
+
*
|
45 |
+
* @return array Mustache Token parse tree
|
46 |
+
*/
|
47 |
+
private function buildTree(array &$tokens, array $parent = null)
|
48 |
+
{
|
49 |
+
$nodes = array();
|
50 |
+
|
51 |
+
while (!empty($tokens)) {
|
52 |
+
$token = array_shift($tokens);
|
53 |
+
|
54 |
+
if ($token[Mustache_Tokenizer::LINE] === $this->lineNum) {
|
55 |
+
$this->lineTokens++;
|
56 |
+
} else {
|
57 |
+
$this->lineNum = $token[Mustache_Tokenizer::LINE];
|
58 |
+
$this->lineTokens = 0;
|
59 |
+
}
|
60 |
+
|
61 |
+
switch ($token[Mustache_Tokenizer::TYPE]) {
|
62 |
+
case Mustache_Tokenizer::T_DELIM_CHANGE:
|
63 |
+
$this->clearStandaloneLines($nodes, $tokens);
|
64 |
+
break;
|
65 |
+
|
66 |
+
case Mustache_Tokenizer::T_SECTION:
|
67 |
+
case Mustache_Tokenizer::T_INVERTED:
|
68 |
+
$this->clearStandaloneLines($nodes, $tokens);
|
69 |
+
$nodes[] = $this->buildTree($tokens, $token);
|
70 |
+
break;
|
71 |
+
|
72 |
+
case Mustache_Tokenizer::T_END_SECTION:
|
73 |
+
if (!isset($parent)) {
|
74 |
+
$msg = sprintf('Unexpected closing tag: /%s', $token[Mustache_Tokenizer::NAME]);
|
75 |
+
throw new Mustache_Exception_SyntaxException($msg, $token);
|
76 |
+
}
|
77 |
+
|
78 |
+
if ($token[Mustache_Tokenizer::NAME] !== $parent[Mustache_Tokenizer::NAME]) {
|
79 |
+
$msg = sprintf('Nesting error: %s vs. %s', $parent[Mustache_Tokenizer::NAME], $token[Mustache_Tokenizer::NAME]);
|
80 |
+
throw new Mustache_Exception_SyntaxException($msg, $token);
|
81 |
+
}
|
82 |
+
|
83 |
+
$this->clearStandaloneLines($nodes, $tokens);
|
84 |
+
$parent[Mustache_Tokenizer::END] = $token[Mustache_Tokenizer::INDEX];
|
85 |
+
$parent[Mustache_Tokenizer::NODES] = $nodes;
|
86 |
+
|
87 |
+
return $parent;
|
88 |
+
break;
|
89 |
+
|
90 |
+
case Mustache_Tokenizer::T_PARTIAL:
|
91 |
+
case Mustache_Tokenizer::T_PARTIAL_2:
|
92 |
+
// store the whitespace prefix for laters!
|
93 |
+
if ($indent = $this->clearStandaloneLines($nodes, $tokens)) {
|
94 |
+
$token[Mustache_Tokenizer::INDENT] = $indent[Mustache_Tokenizer::VALUE];
|
95 |
+
}
|
96 |
+
$nodes[] = $token;
|
97 |
+
break;
|
98 |
+
|
99 |
+
case Mustache_Tokenizer::T_PRAGMA:
|
100 |
+
case Mustache_Tokenizer::T_COMMENT:
|
101 |
+
$this->clearStandaloneLines($nodes, $tokens);
|
102 |
+
$nodes[] = $token;
|
103 |
+
break;
|
104 |
+
|
105 |
+
default:
|
106 |
+
$nodes[] = $token;
|
107 |
+
break;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
if (isset($parent)) {
|
112 |
+
$msg = sprintf('Missing closing tag: %s', $parent[Mustache_Tokenizer::NAME]);
|
113 |
+
throw new Mustache_Exception_SyntaxException($msg, $parent);
|
114 |
+
}
|
115 |
+
|
116 |
+
return $nodes;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Clear standalone line tokens.
|
121 |
+
*
|
122 |
+
* Returns a whitespace token for indenting partials, if applicable.
|
123 |
+
*
|
124 |
+
* @param array $nodes Parsed nodes.
|
125 |
+
* @param array $tokens Tokens to be parsed.
|
126 |
+
*
|
127 |
+
* @return array Resulting indent token, if any.
|
128 |
+
*/
|
129 |
+
private function clearStandaloneLines(array &$nodes, array &$tokens)
|
130 |
+
{
|
131 |
+
if ($this->lineTokens > 1) {
|
132 |
+
// this is the third or later node on this line, so it can't be standalone
|
133 |
+
return;
|
134 |
+
}
|
135 |
+
|
136 |
+
$prev = null;
|
137 |
+
if ($this->lineTokens === 1) {
|
138 |
+
// this is the second node on this line, so it can't be standalone
|
139 |
+
// unless the previous node is whitespace.
|
140 |
+
if ($prev = end($nodes)) {
|
141 |
+
if (!$this->tokenIsWhitespace($prev)) {
|
142 |
+
return;
|
143 |
+
}
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
$next = null;
|
148 |
+
if ($next = reset($tokens)) {
|
149 |
+
// If we're on a new line, bail.
|
150 |
+
if ($next[Mustache_Tokenizer::LINE] !== $this->lineNum) {
|
151 |
+
return;
|
152 |
+
}
|
153 |
+
|
154 |
+
// If the next token isn't whitespace, bail.
|
155 |
+
if (!$this->tokenIsWhitespace($next)) {
|
156 |
+
return;
|
157 |
+
}
|
158 |
+
|
159 |
+
if (count($tokens) !== 1) {
|
160 |
+
// Unless it's the last token in the template, the next token
|
161 |
+
// must end in newline for this to be standalone.
|
162 |
+
if (substr($next[Mustache_Tokenizer::VALUE], -1) !== "\n") {
|
163 |
+
return;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
// Discard the whitespace suffix
|
168 |
+
array_shift($tokens);
|
169 |
+
}
|
170 |
+
|
171 |
+
if ($prev) {
|
172 |
+
// Return the whitespace prefix, if any
|
173 |
+
return array_pop($nodes);
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Check whether token is a whitespace token.
|
179 |
+
*
|
180 |
+
* True if token type is T_TEXT and value is all whitespace characters.
|
181 |
+
*
|
182 |
+
* @param array $token
|
183 |
+
*
|
184 |
+
* @return boolean True if token is a whitespace token
|
185 |
+
*/
|
186 |
+
private function tokenIsWhitespace(array $token)
|
187 |
+
{
|
188 |
+
if ($token[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_TEXT) {
|
189 |
+
return preg_match('/^\s*$/', $token[Mustache_Tokenizer::VALUE]);
|
190 |
+
}
|
191 |
+
|
192 |
+
return false;
|
193 |
+
}
|
194 |
+
}
|
assets/lib/Mustache/Template.php
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Abstract Mustache Template class.
|
14 |
+
*
|
15 |
+
* @abstract
|
16 |
+
*/
|
17 |
+
abstract class Mustache_Template
|
18 |
+
{
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var Mustache_Engine
|
22 |
+
*/
|
23 |
+
protected $mustache;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var boolean
|
27 |
+
*/
|
28 |
+
protected $strictCallables = false;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Mustache Template constructor.
|
32 |
+
*
|
33 |
+
* @param Mustache_Engine $mustache
|
34 |
+
*/
|
35 |
+
public function __construct(Mustache_Engine $mustache)
|
36 |
+
{
|
37 |
+
$this->mustache = $mustache;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Mustache Template instances can be treated as a function and rendered by simply calling them:
|
42 |
+
*
|
43 |
+
* $m = new Mustache_Engine;
|
44 |
+
* $tpl = $m->loadTemplate('Hello, {{ name }}!');
|
45 |
+
* echo $tpl(array('name' => 'World')); // "Hello, World!"
|
46 |
+
*
|
47 |
+
* @see Mustache_Template::render
|
48 |
+
*
|
49 |
+
* @param mixed $context Array or object rendering context (default: array())
|
50 |
+
*
|
51 |
+
* @return string Rendered template
|
52 |
+
*/
|
53 |
+
public function __invoke($context = array())
|
54 |
+
{
|
55 |
+
return $this->render($context);
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Render this template given the rendering context.
|
60 |
+
*
|
61 |
+
* @param mixed $context Array or object rendering context (default: array())
|
62 |
+
*
|
63 |
+
* @return string Rendered template
|
64 |
+
*/
|
65 |
+
public function render($context = array())
|
66 |
+
{
|
67 |
+
return $this->renderInternal($this->prepareContextStack($context));
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Internal rendering method implemented by Mustache Template concrete subclasses.
|
72 |
+
*
|
73 |
+
* This is where the magic happens :)
|
74 |
+
*
|
75 |
+
* NOTE: This method is not part of the Mustache.php public API.
|
76 |
+
*
|
77 |
+
* @param Mustache_Context $context
|
78 |
+
* @param string $indent (default: '')
|
79 |
+
*
|
80 |
+
* @return string Rendered template
|
81 |
+
*/
|
82 |
+
abstract public function renderInternal(Mustache_Context $context, $indent = '');
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Tests whether a value should be iterated over (e.g. in a section context).
|
86 |
+
*
|
87 |
+
* In most languages there are two distinct array types: list and hash (or whatever you want to call them). Lists
|
88 |
+
* should be iterated, hashes should be treated as objects. Mustache follows this paradigm for Ruby, Javascript,
|
89 |
+
* Java, Python, etc.
|
90 |
+
*
|
91 |
+
* PHP, however, treats lists and hashes as one primitive type: array. So Mustache.php needs a way to distinguish
|
92 |
+
* between between a list of things (numeric, normalized array) and a set of variables to be used as section context
|
93 |
+
* (associative array). In other words, this will be iterated over:
|
94 |
+
*
|
95 |
+
* $items = array(
|
96 |
+
* array('name' => 'foo'),
|
97 |
+
* array('name' => 'bar'),
|
98 |
+
* array('name' => 'baz'),
|
99 |
+
* );
|
100 |
+
*
|
101 |
+
* ... but this will be used as a section context block:
|
102 |
+
*
|
103 |
+
* $items = array(
|
104 |
+
* 1 => array('name' => 'foo'),
|
105 |
+
* 'banana' => array('name' => 'bar'),
|
106 |
+
* 42 => array('name' => 'baz'),
|
107 |
+
* );
|
108 |
+
*
|
109 |
+
* @param mixed $value
|
110 |
+
*
|
111 |
+
* @return boolean True if the value is 'iterable'
|
112 |
+
*/
|
113 |
+
protected function isIterable($value)
|
114 |
+
{
|
115 |
+
if (is_object($value)) {
|
116 |
+
return $value instanceof Traversable;
|
117 |
+
} elseif (is_array($value)) {
|
118 |
+
$i = 0;
|
119 |
+
foreach ($value as $k => $v) {
|
120 |
+
if ($k !== $i++) {
|
121 |
+
return false;
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
return true;
|
126 |
+
} else {
|
127 |
+
return false;
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Helper method to prepare the Context stack.
|
133 |
+
*
|
134 |
+
* Adds the Mustache HelperCollection to the stack's top context frame if helpers are present.
|
135 |
+
*
|
136 |
+
* @param mixed $context Optional first context frame (default: null)
|
137 |
+
*
|
138 |
+
* @return Mustache_Context
|
139 |
+
*/
|
140 |
+
protected function prepareContextStack($context = null)
|
141 |
+
{
|
142 |
+
$stack = new Mustache_Context;
|
143 |
+
|
144 |
+
$helpers = $this->mustache->getHelpers();
|
145 |
+
if (!$helpers->isEmpty()) {
|
146 |
+
$stack->push($helpers);
|
147 |
+
}
|
148 |
+
|
149 |
+
if (!empty($context)) {
|
150 |
+
$stack->push($context);
|
151 |
+
}
|
152 |
+
|
153 |
+
return $stack;
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Resolve a context value.
|
158 |
+
*
|
159 |
+
* Invoke the value if it is callable, otherwise return the value.
|
160 |
+
*
|
161 |
+
* @param mixed $value
|
162 |
+
* @param Mustache_Context $context
|
163 |
+
* @param string $indent
|
164 |
+
*
|
165 |
+
* @return string
|
166 |
+
*/
|
167 |
+
protected function resolveValue($value, Mustache_Context $context, $indent = '')
|
168 |
+
{
|
169 |
+
if (($this->strictCallables ? is_object($value) : !is_string($value)) && is_callable($value)) {
|
170 |
+
return $this->mustache
|
171 |
+
->loadLambda((string) call_user_func($value))
|
172 |
+
->renderInternal($context, $indent);
|
173 |
+
}
|
174 |
+
|
175 |
+
return $value;
|
176 |
+
}
|
177 |
+
}
|
assets/lib/Mustache/Tokenizer.php
ADDED
@@ -0,0 +1,278 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Mustache.php.
|
5 |
+
*
|
6 |
+
* (c) 2012 Justin Hileman
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Mustache Tokenizer class.
|
14 |
+
*
|
15 |
+
* This class is responsible for turning raw template source into a set of Mustache tokens.
|
16 |
+
*/
|
17 |
+
class Mustache_Tokenizer
|
18 |
+
{
|
19 |
+
|
20 |
+
// Finite state machine states
|
21 |
+
const IN_TEXT = 0;
|
22 |
+
const IN_TAG_TYPE = 1;
|
23 |
+
const IN_TAG = 2;
|
24 |
+
|
25 |
+
// Token types
|
26 |
+
const T_SECTION = '#';
|
27 |
+
const T_INVERTED = '^';
|
28 |
+
const T_END_SECTION = '/';
|
29 |
+
const T_COMMENT = '!';
|
30 |
+
const T_PARTIAL = '>';
|
31 |
+
const T_PARTIAL_2 = '<';
|
32 |
+
const T_DELIM_CHANGE = '=';
|
33 |
+
const T_ESCAPED = '_v';
|
34 |
+
const T_UNESCAPED = '{';
|
35 |
+
const T_UNESCAPED_2 = '&';
|
36 |
+
const T_TEXT = '_t';
|
37 |
+
const T_PRAGMA = '%';
|
38 |
+
|
39 |
+
// Valid token types
|
40 |
+
private static $tagTypes = array(
|
41 |
+
self::T_SECTION => true,
|
42 |
+
self::T_INVERTED => true,
|
43 |
+
self::T_END_SECTION => true,
|
44 |
+
self::T_COMMENT => true,
|
45 |
+
self::T_PARTIAL => true,
|
46 |
+
self::T_PARTIAL_2 => true,
|
47 |
+
self::T_DELIM_CHANGE => true,
|
48 |
+
self::T_ESCAPED => true,
|
49 |
+
self::T_UNESCAPED => true,
|
50 |
+
self::T_UNESCAPED_2 => true,
|
51 |
+
self::T_PRAGMA => true,
|
52 |
+
);
|
53 |
+
|
54 |
+
// Interpolated tags
|
55 |
+
private static $interpolatedTags = array(
|
56 |
+
self::T_ESCAPED => true,
|
57 |
+
self::T_UNESCAPED => true,
|
58 |
+
self::T_UNESCAPED_2 => true,
|
59 |
+
);
|
60 |
+
|
61 |
+
// Token properties
|
62 |
+
const TYPE = 'type';
|
63 |
+
const NAME = 'name';
|
64 |
+
const OTAG = 'otag';
|
65 |
+
const CTAG = 'ctag';
|
66 |
+
const LINE = 'line';
|
67 |
+
const INDEX = 'index';
|
68 |
+
const END = 'end';
|
69 |
+
const INDENT = 'indent';
|
70 |
+
const NODES = 'nodes';
|
71 |
+
const VALUE = 'value';
|
72 |
+
|
73 |
+
private $state;
|
74 |
+
private $tagType;
|
75 |
+
private $tag;
|
76 |
+
private $buffer;
|
77 |
+
private $tokens;
|
78 |
+
private $seenTag;
|
79 |
+
private $line;
|
80 |
+
private $otag;
|
81 |
+
private $ctag;
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Scan and tokenize template source.
|
85 |
+
*
|
86 |
+
* @param string $text Mustache template source to tokenize
|
87 |
+
* @param string $delimiters Optionally, pass initial opening and closing delimiters (default: null)
|
88 |
+
*
|
89 |
+
* @return array Set of Mustache tokens
|
90 |
+
*/
|
91 |
+
public function scan($text, $delimiters = null)
|
92 |
+
{
|
93 |
+
$this->reset();
|
94 |
+
|
95 |
+
if ($delimiters = trim($delimiters)) {
|
96 |
+
list($otag, $ctag) = explode(' ', $delimiters);
|
97 |
+
$this->otag = $otag;
|
98 |
+
$this->ctag = $ctag;
|
99 |
+
}
|
100 |
+
|
101 |
+
$len = strlen($text);
|
102 |
+
for ($i = 0; $i < $len; $i++) {
|
103 |
+
switch ($this->state) {
|
104 |
+
case self::IN_TEXT:
|
105 |
+
if ($this->tagChange($this->otag, $text, $i)) {
|
106 |
+
$i--;
|
107 |
+
$this->flushBuffer();
|
108 |
+
$this->state = self::IN_TAG_TYPE;
|
109 |
+
} else {
|
110 |
+
$char = substr($text, $i, 1);
|
111 |
+
$this->buffer .= $char;
|
112 |
+
if ($char == "\n") {
|
113 |
+
$this->flushBuffer();
|
114 |
+
$this->line++;
|
115 |
+
}
|
116 |
+
}
|
117 |
+
break;
|
118 |
+
|
119 |
+
case self::IN_TAG_TYPE:
|
120 |
+
$i += strlen($this->otag) - 1;
|
121 |
+
$char = substr($text, $i + 1, 1);
|
122 |
+
if (isset(self::$tagTypes[$char])) {
|
123 |
+
$tag = $char;
|
124 |
+
$this->tagType = $tag;
|
125 |
+
} else {
|
126 |
+
$tag = null;
|
127 |
+
$this->tagType = self::T_ESCAPED;
|
128 |
+
}
|
129 |
+
|
130 |
+
if ($this->tagType === self::T_DELIM_CHANGE) {
|
131 |
+
$i = $this->changeDelimiters($text, $i);
|
132 |
+
$this->state = self::IN_TEXT;
|
133 |
+
} elseif ($this->tagType === self::T_PRAGMA) {
|
134 |
+
$i = $this->addPragma($text, $i);
|
135 |
+
$this->state = self::IN_TEXT;
|
136 |
+
} else {
|
137 |
+
if ($tag !== null) {
|
138 |
+
$i++;
|
139 |
+
}
|
140 |
+
$this->state = self::IN_TAG;
|
141 |
+
}
|
142 |
+
$this->seenTag = $i;
|
143 |
+
break;
|
144 |
+
|
145 |
+
default:
|
146 |
+
if ($this->tagChange($this->ctag, $text, $i)) {
|
147 |
+
$this->tokens[] = array(
|
148 |
+
self::TYPE => $this->tagType,
|
149 |
+
self::NAME => trim($this->buffer),
|
150 |
+
self::OTAG => $this->otag,
|
151 |
+
self::CTAG => $this->ctag,
|
152 |
+
self::LINE => $this->line,
|
153 |
+
self::INDEX => ($this->tagType == self::T_END_SECTION) ? $this->seenTag - strlen($this->otag) : $i + strlen($this->ctag)
|
154 |
+
);
|
155 |
+
|
156 |
+
$this->buffer = '';
|
157 |
+
$i += strlen($this->ctag) - 1;
|
158 |
+
$this->state = self::IN_TEXT;
|
159 |
+
if ($this->tagType == self::T_UNESCAPED) {
|
160 |
+
if ($this->ctag == '}}') {
|
161 |
+
$i++;
|
162 |
+
} else {
|
163 |
+
// Clean up `{{{ tripleStache }}}` style tokens.
|
164 |
+
$lastName = $this->tokens[count($this->tokens) - 1][self::NAME];
|
165 |
+
if (substr($lastName, -1) === '}') {
|
166 |
+
$this->tokens[count($this->tokens) - 1][self::NAME] = trim(substr($lastName, 0, -1));
|
167 |
+
}
|
168 |
+
}
|
169 |
+
}
|
170 |
+
} else {
|
171 |
+
$this->buffer .= substr($text, $i, 1);
|
172 |
+
}
|
173 |
+
break;
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
$this->flushBuffer();
|
178 |
+
|
179 |
+
return $this->tokens;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Helper function to reset tokenizer internal state.
|
184 |
+
*/
|
185 |
+
private function reset()
|
186 |
+
{
|
187 |
+
$this->state = self::IN_TEXT;
|
188 |
+
$this->tagType = null;
|
189 |
+
$this->tag = null;
|
190 |
+
$this->buffer = '';
|
191 |
+
$this->tokens = array();
|
192 |
+
$this->seenTag = false;
|
193 |
+
$this->line = 0;
|
194 |
+
$this->otag = '{{';
|
195 |
+
$this->ctag = '}}';
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Flush the current buffer to a token.
|
200 |
+
*/
|
201 |
+
private function flushBuffer()
|
202 |
+
{
|
203 |
+
if (!empty($this->buffer)) {
|
204 |
+
$this->tokens[] = array(
|
205 |
+
self::TYPE => self::T_TEXT,
|
206 |
+
self::LINE => $this->line,
|
207 |
+
self::VALUE => $this->buffer
|
208 |
+
);
|
209 |
+
$this->buffer = '';
|
210 |
+
}
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Change the current Mustache delimiters. Set new `otag` and `ctag` values.
|
215 |
+
*
|
216 |
+
* @param string $text Mustache template source
|
217 |
+
* @param int $index Current tokenizer index
|
218 |
+
*
|
219 |
+
* @return int New index value
|
220 |
+
*/
|
221 |
+
private function changeDelimiters($text, $index)
|
222 |
+
{
|
223 |
+
$startIndex = strpos($text, '=', $index) + 1;
|
224 |
+
$close = '='.$this->ctag;
|
225 |
+
$closeIndex = strpos($text, $close, $index);
|
226 |
+
|
227 |
+
list($otag, $ctag) = explode(' ', trim(substr($text, $startIndex, $closeIndex - $startIndex)));
|
228 |
+
$this->otag = $otag;
|
229 |
+
$this->ctag = $ctag;
|
230 |
+
|
231 |
+
$this->tokens[] = array(
|
232 |
+
self::TYPE => self::T_DELIM_CHANGE,
|
233 |
+
self::LINE => $this->line,
|
234 |
+
);
|
235 |
+
|
236 |
+
return $closeIndex + strlen($close) - 1;
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Add pragma token.
|
241 |
+
*
|
242 |
+
* Pragmas are hoisted to the front of the template, so all pragma tokens
|
243 |
+
* will appear at the front of the token list.
|
244 |
+
*
|
245 |
+
* @param string $text
|
246 |
+
* @param int $index
|
247 |
+
*
|
248 |
+
* @return int New index value
|
249 |
+
*/
|
250 |
+
private function addPragma($text, $index)
|
251 |
+
{
|
252 |
+
$end = strpos($text, $this->ctag, $index);
|
253 |
+
$pragma = trim(substr($text, $index + 2, $end - $index - 2));
|
254 |
+
|
255 |
+
// Pragmas are hoisted to the front of the template.
|
256 |
+
array_unshift($this->tokens, array(
|
257 |
+
self::TYPE => self::T_PRAGMA,
|
258 |
+
self::NAME => $pragma,
|
259 |
+
self::LINE => 0,
|
260 |
+
));
|
261 |
+
|
262 |
+
return $end + strlen($this->ctag) - 1;
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Test whether it's time to change tags.
|
267 |
+
*
|
268 |
+
* @param string $tag Current tag name
|
269 |
+
* @param string $text Mustache template source
|
270 |
+
* @param int $index Current tokenizer index
|
271 |
+
*
|
272 |
+
* @return boolean True if this is a closing section tag
|
273 |
+
*/
|
274 |
+
private function tagChange($tag, $text, $index)
|
275 |
+
{
|
276 |
+
return substr($text, $index, strlen($tag)) === $tag;
|
277 |
+
}
|
278 |
+
}
|
assets/lib/class-mustache-templates/class-mustache-templates.css
ADDED
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Mustache Library for Creating */
|
2 |
+
|
3 |
+
/*--------------------------------------------------------------
|
4 |
+
>>> TABLE OF CONTENTS:
|
5 |
+
----------------------------------------------------------------
|
6 |
+
1.0 - Basic Structure
|
7 |
+
2.0 - Textarea Codemirror Styling
|
8 |
+
3.0 - Other Form Elements
|
9 |
+
4.0 - Save Button
|
10 |
+
5.0 - Media Queries
|
11 |
+
6.0 - Other
|
12 |
+
--------------------------------------------------------------*/
|
13 |
+
|
14 |
+
/*--------------------------------------------------------------
|
15 |
+
1.0 Basic Structure
|
16 |
+
--------------------------------------------------------------*/
|
17 |
+
|
18 |
+
.side .form-table th {
|
19 |
+
width: 20%;
|
20 |
+
font-weight: bold;
|
21 |
+
text-align: left;
|
22 |
+
padding-left: 0;
|
23 |
+
}
|
24 |
+
|
25 |
+
.wck-post-body{
|
26 |
+
clear: left;
|
27 |
+
float: left;
|
28 |
+
margin-right: -2000px;
|
29 |
+
width: 100%;
|
30 |
+
}
|
31 |
+
|
32 |
+
.metabox-holder .column-1 {
|
33 |
+
margin-right:300px;
|
34 |
+
}
|
35 |
+
.metabox-holder .column-2 {
|
36 |
+
float: right;
|
37 |
+
width: 280px;
|
38 |
+
clear:right;
|
39 |
+
position:relative;
|
40 |
+
}
|
41 |
+
.metabox-holder .column-3 {
|
42 |
+
clear: both;
|
43 |
+
margin-right:300px;
|
44 |
+
}
|
45 |
+
/*--------------------------------------------------------------
|
46 |
+
2.0 Textarea Codemirror styling
|
47 |
+
--------------------------------------------------------------*/
|
48 |
+
.textarea .stp-extra{
|
49 |
+
border:1px solid #ccc;
|
50 |
+
border-left:0;
|
51 |
+
float: left;
|
52 |
+
overflow: auto;
|
53 |
+
width: 39%;
|
54 |
+
height:400px;
|
55 |
+
}
|
56 |
+
.textarea .stp-extra h4 {
|
57 |
+
height: 20px;
|
58 |
+
margin: 1px 0;
|
59 |
+
padding:10px 20px;
|
60 |
+
overflow: hidden;
|
61 |
+
cursor:pointer;
|
62 |
+
background:#F1F1F1;
|
63 |
+
}
|
64 |
+
.textarea .stp-extra h4:hover{
|
65 |
+
background:#FCFCFC;
|
66 |
+
}
|
67 |
+
.textarea .stp-extra pre {
|
68 |
+
display: block;
|
69 |
+
height: 253px;
|
70 |
+
margin: 12px 0;
|
71 |
+
max-height: 253px;
|
72 |
+
overflow: auto;
|
73 |
+
padding-left: 20px;
|
74 |
+
vertical-align: top;
|
75 |
+
width: auto;
|
76 |
+
}
|
77 |
+
.textarea label.wppb_mustache_label {
|
78 |
+
display: none;
|
79 |
+
}
|
80 |
+
.cm-s-default.CodeMirror {
|
81 |
+
border: 1px solid #CCCCCC;
|
82 |
+
float: left;
|
83 |
+
font-family: Monaco,Menlo,"Andale Mono","lucida console","Courier New",monospace !important;
|
84 |
+
line-height: 1.4em !important;
|
85 |
+
width: 60%;
|
86 |
+
}
|
87 |
+
.cm-s-default {
|
88 |
+
min-height: 400px;
|
89 |
+
}
|
90 |
+
.mustache-box{
|
91 |
+
table-layout:fixed;
|
92 |
+
}
|
93 |
+
/*--------------------------------------------------------------
|
94 |
+
3.0 Other Form Elements
|
95 |
+
--------------------------------------------------------------*/
|
96 |
+
.text label.wppb_mustache_label {
|
97 |
+
display: inline-block;
|
98 |
+
min-width: 150px;
|
99 |
+
font-weight: bold;
|
100 |
+
}
|
101 |
+
|
102 |
+
.checkbox label.wppb_mustache_label {
|
103 |
+
font-weight: bold;
|
104 |
+
margin-right: 8px;
|
105 |
+
}
|
106 |
+
|
107 |
+
.mustache-box input[type="text"]{
|
108 |
+
width:400px;
|
109 |
+
min-width: auto;
|
110 |
+
}
|
111 |
+
|
112 |
+
.mustache-box .description{
|
113 |
+
display: block;
|
114 |
+
}
|
115 |
+
|
116 |
+
.mustache-box.form-table td.header{
|
117 |
+
padding-top: 0;
|
118 |
+
padding-bottom: 0;
|
119 |
+
}
|
120 |
+
|
121 |
+
/*--------------------------------------------------------------
|
122 |
+
4.0 Save Button
|
123 |
+
--------------------------------------------------------------*/
|
124 |
+
.mustache-save{
|
125 |
+
width: 100%;
|
126 |
+
text-align: center;
|
127 |
+
}
|
128 |
+
|
129 |
+
#page-save-metabox .hndle{
|
130 |
+
background: #333333;
|
131 |
+
color: #fff;
|
132 |
+
}
|
133 |
+
#page-save-metabox .inside{
|
134 |
+
padding: 30px;
|
135 |
+
}
|
136 |
+
|
137 |
+
/*--------------------------------------------------------------
|
138 |
+
5.0 Media Queries
|
139 |
+
--------------------------------------------------------------*/
|
140 |
+
@media screen and ( max-width: 878px ) {
|
141 |
+
.wck-post-body{
|
142 |
+
float:none;
|
143 |
+
margin-right: 0;
|
144 |
+
}
|
145 |
+
|
146 |
+
.metabox-holder .column-2{
|
147 |
+
float:none;
|
148 |
+
width:100%;
|
149 |
+
}
|
150 |
+
|
151 |
+
.metabox-holder .column-1{
|
152 |
+
margin-right:0;
|
153 |
+
}
|
154 |
+
|
155 |
+
.textarea .stp-extra{
|
156 |
+
float:none;
|
157 |
+
width:auto;
|
158 |
+
border:1px solid #ccc;
|
159 |
+
border-top:0;
|
160 |
+
}
|
161 |
+
.cm-s-default.CodeMirror{
|
162 |
+
float:none;
|
163 |
+
width: auto;
|
164 |
+
}
|
165 |
+
#page-save-metabox .inside{
|
166 |
+
text-align: right;
|
167 |
+
}
|
168 |
+
.mustache-save{
|
169 |
+
width: auto;
|
170 |
+
text-align: center;
|
171 |
+
}
|
172 |
+
|
173 |
+
}
|
174 |
+
|
175 |
+
/*--------------------------------------------------------------
|
176 |
+
6.0 Other
|
177 |
+
--------------------------------------------------------------*/
|
178 |
+
/* show email toggle checkbox inline for User/Admin Email Customizers */
|
179 |
+
.profile-builder_page_user-email-customizer .wck-post-body .postbox:not(:first-child) tr:nth-child(1), .profile-builder_page_admin-email-customizer .wck-post-body .postbox:not(:first-child) tr:nth-child(1) {
|
180 |
+
width: 50%;
|
181 |
+
display: inline-block;
|
182 |
+
}
|
183 |
+
|
184 |
+
.profile-builder_page_user-email-customizer .wck-post-body .postbox:not(:first-child) tr:nth-child(2), .profile-builder_page_admin-email-customizer .wck-post-body .postbox:not(:first-child) tr:nth-child(2) {
|
185 |
+
text-align: left;
|
186 |
+
width: 50%;
|
187 |
+
display: inline-block;
|
188 |
+
}
|
189 |
+
|
190 |
+
.profile-builder_page_user-email-customizer .wck-post-body .postbox:not(:first-child) tr:nth-child(2) td, .profile-builder_page_admin-email-customizer .wck-post-body .postbox:not(:first-child) tr:nth-child(2) td {
|
191 |
+
float: right;
|
192 |
+
padding-right: 20px;
|
193 |
+
}
|
assets/lib/class-mustache-templates/class-mustache-templates.js
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineMode("mustache", function(config, parserConfig) {
|
2 |
+
var mustacheOverlay = {
|
3 |
+
token: function(stream, state) {
|
4 |
+
var ch;
|
5 |
+
if (stream.match("{{")) {
|
6 |
+
while ((ch = stream.next()) != null)
|
7 |
+
if (ch == "}" && stream.next() == "}") break;
|
8 |
+
stream.eat("}");
|
9 |
+
return "mustache";
|
10 |
+
}
|
11 |
+
while (stream.next() != null && !stream.match("{{", false)) {}
|
12 |
+
return null;
|
13 |
+
}
|
14 |
+
};
|
15 |
+
return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "text/html"), mustacheOverlay);
|
16 |
+
});
|
17 |
+
|
18 |
+
|
19 |
+
jQuery(function(){
|
20 |
+
var wck_stp_textareas = ["wppb_mustache_template"];
|
21 |
+
var length = wck_stp_textareas.length;
|
22 |
+
element = null;
|
23 |
+
|
24 |
+
for ( var i=0; i < length; i++ ){
|
25 |
+
element = wck_stp_textareas[i];
|
26 |
+
|
27 |
+
if ( jQuery( 'textarea[class="' + element + '"]' ).length > 0 ){
|
28 |
+
jQuery( 'textarea[class|="' + element + '"]' ).each( function(){
|
29 |
+
var editor = CodeMirror.fromTextArea( this, {
|
30 |
+
mode: "mustache",
|
31 |
+
lineNumbers: true,
|
32 |
+
//lineWrapping:true,
|
33 |
+
extraKeys: {
|
34 |
+
"F11": function(cm) {
|
35 |
+
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
|
36 |
+
},
|
37 |
+
"Esc": function(cm) {
|
38 |
+
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
|
39 |
+
}
|
40 |
+
}
|
41 |
+
});
|
42 |
+
});
|
43 |
+
}
|
44 |
+
}
|
45 |
+
})
|
46 |
+
|
47 |
+
jQuery(function() {
|
48 |
+
jQuery( ".stp-extra" ).accordion();
|
49 |
+
});
|
50 |
+
|
51 |
+
//add hidden input with off value for checkboxes
|
52 |
+
jQuery(function() {
|
53 |
+
|
54 |
+
jQuery('form .wck-post-body input[type="checkbox"]').each( function() {
|
55 |
+
|
56 |
+
if ( !jQuery(this).is(':checked') ) {
|
57 |
+
var wppb_mustache_checkbox_off = document.createElement('input');
|
58 |
+
wppb_mustache_checkbox_off.type = 'hidden';
|
59 |
+
wppb_mustache_checkbox_off.value = 'off';
|
60 |
+
wppb_mustache_checkbox_off.name = jQuery(this).attr('name');
|
61 |
+
|
62 |
+
jQuery(this).after(wppb_mustache_checkbox_off);
|
63 |
+
}
|
64 |
+
|
65 |
+
});
|
66 |
+
|
67 |
+
jQuery('form .wck-post-body input[type="checkbox"]').on( 'change', function() {
|
68 |
+
var wppb_mustache_checkbox_off = document.createElement('input');
|
69 |
+
wppb_mustache_checkbox_off.type = 'hidden';
|
70 |
+
wppb_mustache_checkbox_off.value = 'off';
|
71 |
+
wppb_mustache_checkbox_off.name = jQuery(this).attr('name');
|
72 |
+
|
73 |
+
if ( jQuery(this).is(':checked') )
|
74 |
+
jQuery( '.' + jQuery(this).attr('name') + ' input[type="hidden"]' ).remove();
|
75 |
+
else
|
76 |
+
jQuery(this).after(wppb_mustache_checkbox_off);
|
77 |
+
|
78 |
+
});
|
79 |
+
|
80 |
+
});
|
assets/lib/class-mustache-templates/class-mustache-templates.php
ADDED
@@ -0,0 +1,604 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @param array() $mustache_vars is the array containing the names of the variable that must be proccessed by mustache in the template. The array must be in this form:
|
4 |
+
* array( array( 'name' => '', 'type' => '' ) ... ), and for loop tags it also contains a 'children' element which contains other mustache_vars array( array( 'name' => 'users', 'type' => 'loop_tag', 'children' => $merge_tags )
|
5 |
+
* @param string $template the template that needs to be procesed
|
6 |
+
* @param array $extra_values in this array we can pass any variables that are required for that specific implementation of the mustache processing system
|
7 |
+
*
|
8 |
+
*/
|
9 |
+
class PB_Mustache_Generate_Template{
|
10 |
+
var $mustache_vars = array( );
|
11 |
+
var $template = '';
|
12 |
+
var $extra_values = array( );
|
13 |
+
|
14 |
+
/**
|
15 |
+
* constructor for the class
|
16 |
+
*
|
17 |
+
* @param array $mustache_vars the array of variables
|
18 |
+
* @param string $template the html template
|
19 |
+
* @param $extra_values in this array we can pass any variables that are required for that specific implementation of the mustache processing system
|
20 |
+
*
|
21 |
+
* @since 2.0.0
|
22 |
+
*
|
23 |
+
*/
|
24 |
+
function __construct( $mustache_vars, $template, $extra_values ){
|
25 |
+
|
26 |
+
// Include Mustache Templates
|
27 |
+
if( !class_exists( 'Mustache_Autoloader' ) ){
|
28 |
+
|
29 |
+
if( !defined( 'WPPB_PAID_PLUGIN_DIR' ) || ( defined( 'WPPB_PAID_PLUGIN_DIR' ) && defined( 'PROFILE_BUILDER_PAID_VERSION' ) ) ){
|
30 |
+
if( file_exists( WPPB_PLUGIN_DIR . '/assets/lib/Mustache/Autoloader.php' ) )
|
31 |
+
require_once( WPPB_PLUGIN_DIR.'/assets/lib/Mustache/Autoloader.php' );
|
32 |
+
} else if( defined( 'WPPB_PAID_PLUGIN_DIR' ) ){
|
33 |
+
if( file_exists( WPPB_PAID_PLUGIN_DIR . '/assets/lib/Mustache/Autoloader.php' ) )
|
34 |
+
require_once( WPPB_PAID_PLUGIN_DIR.'/assets/lib/Mustache/Autoloader.php' );
|
35 |
+
}
|
36 |
+
|
37 |
+
}
|
38 |
+
|
39 |
+
Mustache_Autoloader::register();
|
40 |
+
|
41 |
+
$this->mustache_vars = $mustache_vars;
|
42 |
+
$this->template = $template;
|
43 |
+
$this->extra_values = $extra_values;
|
44 |
+
|
45 |
+
$this->process_mustache_vars();
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* construct the mustache_vars_array from $mustache_vars through filters
|
50 |
+
*
|
51 |
+
* @since 2.0.0
|
52 |
+
*
|
53 |
+
*/
|
54 |
+
function process_mustache_vars(){
|
55 |
+
if( !empty( $this->mustache_vars ) ){
|
56 |
+
foreach( $this->mustache_vars as $var ){
|
57 |
+
foreach( $var['variables'] as $variables ){
|
58 |
+
if( empty( $variables['children'] ) )
|
59 |
+
$variables['children'] = array();
|
60 |
+
$this->mustache_vars_array[ $variables['name'] ] = apply_filters( 'mustache_variable_'. $variables['type'], '', $variables['name'], $variables['children'], $this->extra_values );
|
61 |
+
}
|
62 |
+
}
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Process the mustache template from the html template and the processed mustache variable array.
|
69 |
+
*
|
70 |
+
* @since 2.0.0
|
71 |
+
*
|
72 |
+
* @return $content the proccessed template ready for output.
|
73 |
+
*/
|
74 |
+
function process_template(){
|
75 |
+
$m = new Mustache_Engine;
|
76 |
+
try {
|
77 |
+
if( !empty( $this->mustache_vars_array ) ){
|
78 |
+
foreach( $this->mustache_vars_array as $key => $value ){
|
79 |
+
if( is_string( $value ) ) {
|
80 |
+
$this->mustache_vars_array[$key] = str_replace('[', '[', $value);
|
81 |
+
$this->mustache_vars_array[$key] = str_replace(']', ']', $value);
|
82 |
+
}
|
83 |
+
}
|
84 |
+
}
|
85 |
+
$content = do_shortcode( $m->render( $this->template, $this->mustache_vars_array ) );
|
86 |
+
} catch (Exception $e) {
|
87 |
+
$content = $e->getMessage();
|
88 |
+
}
|
89 |
+
|
90 |
+
return apply_filters( 'wppb_mustache_template', $content );
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Handle toString method
|
95 |
+
*
|
96 |
+
* @since 2.0.0
|
97 |
+
*
|
98 |
+
* @return string $html html for the template.
|
99 |
+
*/
|
100 |
+
public function __toString() {
|
101 |
+
$html = $this->process_template();
|
102 |
+
return "{$html}";
|
103 |
+
}
|
104 |
+
|
105 |
+
|
106 |
+
}
|
107 |
+
|
108 |
+
|
109 |
+
class PB_Mustache_Generate_Admin_Box{
|
110 |
+
|
111 |
+
var $id; // string meta box id
|
112 |
+
var $title; // string title
|
113 |
+
var $page; // string|array post type to add meta box to
|
114 |
+
var $priority;
|
115 |
+
var $mustache_vars;
|
116 |
+
var $default_value;
|
117 |
+
|
118 |
+
|
119 |
+
/**
|
120 |
+
* constructor for the class
|
121 |
+
*
|
122 |
+
* @param string $id the meta box id
|
123 |
+
* @param string $title the title of the metabox
|
124 |
+
* @param string|array $page post type to add meta box to
|
125 |
+
* @param string $priority the priority within the context where the boxes should show ('high', 'core', 'default' or 'low')
|
126 |
+
* @param array $mustache_vars is the array containing the names of the variable that must be proccessed by mustache in the template. The array must be in this form:
|
127 |
+
* array( array( 'name' => '', 'type' => '' ) ... ), and for loop tags it also contains a 'children' element which contains other mustache_vars array( array( 'name' => 'users', 'type' => 'loop_tag', 'children' => $merge_tags )
|
128 |
+
* @param string $default_value the default template that populates the codemirror box.
|
129 |
+
*
|
130 |
+
* @since 2.0.0
|
131 |
+
*
|
132 |
+
*/
|
133 |
+
function __construct( $id, $title, $page, $priority, $mustache_vars, $default_value = '', $fields = array() ){
|
134 |
+
|
135 |
+
$this->mustache_vars = $mustache_vars;
|
136 |
+
|
137 |
+
$this->id = $id;
|
138 |
+
$this->title = $title;
|
139 |
+
$this->page = $page;
|
140 |
+
$this->priority = $priority;
|
141 |
+
$this->default_value = $default_value;
|
142 |
+
|
143 |
+
if(!is_array($this->page)) {
|
144 |
+
$this->page = array($this->page);
|
145 |
+
}
|
146 |
+
|
147 |
+
if( empty( $fields ) ){
|
148 |
+
$this->fields = array(
|
149 |
+
array( // Textarea
|
150 |
+
'label' => '', // <label>
|
151 |
+
'desc' => '', // description
|
152 |
+
'id' => $id, // field id and name
|
153 |
+
'type' => 'textarea', // type of field
|
154 |
+
'default' => $default_value, // type of field
|
155 |
+
)
|
156 |
+
);
|
157 |
+
}
|
158 |
+
else{
|
159 |
+
$this->fields = $fields;
|
160 |
+
}
|
161 |
+
|
162 |
+
$this->save_default_values();
|
163 |
+
|
164 |
+
if( defined( 'DOING_AJAX' ) && DOING_AJAX )
|
165 |
+
return;
|
166 |
+
|
167 |
+
add_action( 'add_meta_boxes', array( $this, 'add_box' ) );
|
168 |
+
add_action( 'save_post', array( $this, 'save_box' ), 11, 2);
|
169 |
+
add_action( 'wp_insert_post', array( $this, 'save_box' ), 11, 2);
|
170 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'wppb_print_mustache_script' ) );
|
171 |
+
add_action( 'admin_head', array( $this, 'wppb_print_codemirror_script' ) );
|
172 |
+
|
173 |
+
add_action( 'wck_before_meta_boxes', array( $this, 'wppb_mustache_page_before' ) );
|
174 |
+
add_action( 'wck_after_meta_boxes', array( $this, 'wppb_mustache_page_after' ) );
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Function that print the required scripts for the mustache templates
|
179 |
+
*
|
180 |
+
* @param string $hook the page hook
|
181 |
+
*
|
182 |
+
* @since 2.0.0
|
183 |
+
*
|
184 |
+
*/
|
185 |
+
function wppb_print_mustache_script( $hook ){
|
186 |
+
if ( isset( $_GET['post_type'] ) || isset( $_GET['post'] ) || isset( $_GET['page'] ) ){
|
187 |
+
if ( isset( $_GET['post_type'] ) )
|
188 |
+
$post_type = sanitize_text_field( $_GET['post_type'] );
|
189 |
+
|
190 |
+
elseif ( isset( $_GET['post'] ) )
|
191 |
+
$post_type = get_post_type( absint( $_GET['post'] ) );
|
192 |
+
else if( isset( $_GET['page'] ) ){
|
193 |
+
$screen = get_current_screen();
|
194 |
+
$post_type = $screen->id;
|
195 |
+
}
|
196 |
+
|
197 |
+
if ( ( $this->page[0] == $post_type ) ){
|
198 |
+
if( file_exists( WPPB_PLUGIN_DIR . 'assets/lib/codemirror/lib/codemirror.css' ) )
|
199 |
+
wp_enqueue_style( 'codemirror-style', WPPB_PLUGIN_URL . 'assets/lib/codemirror/lib/codemirror.css', false, PROFILE_BUILDER_VERSION );
|
200 |
+
else if( defined( 'WPPB_PAID_PLUGIN_URL' ) )
|
201 |
+
wp_enqueue_style( 'codemirror-style', WPPB_PAID_PLUGIN_URL . 'assets/lib/codemirror/lib/codemirror.css', false, PROFILE_BUILDER_VERSION );
|
202 |
+
|
203 |
+
if( !wp_script_is( 'codemirror', 'registered' ) && !wp_script_is( 'codemirror', 'enqueued' ) ) {
|
204 |
+
|
205 |
+
if( file_exists( WPPB_PLUGIN_DIR . 'assets/lib/codemirror/lib/codemirror.js' ) ){
|
206 |
+
wp_enqueue_script('codemirror', WPPB_PLUGIN_URL . 'assets/lib/codemirror/lib/codemirror.js', array(), PROFILE_BUILDER_VERSION);
|
207 |
+
wp_enqueue_script('codemirror-mode-overlay-js', WPPB_PLUGIN_URL . 'assets/lib/codemirror/addon/mode/overlay.js', array(), '1.0');
|
208 |
+
wp_enqueue_script('codemirror-mode-xml-js', WPPB_PLUGIN_URL . 'assets/lib/codemirror/mode/xml/xml.js', array(), '1.0');
|
209 |
+
wp_enqueue_script('codemirror-fullscreen-js', WPPB_PLUGIN_URL . 'assets/lib/codemirror/addon/display/fullscreen.js', array(), '1.0');
|
210 |
+
} else if ( defined( 'WPPB_PAID_PLUGIN_URL' ) ){
|
211 |
+
wp_enqueue_script('codemirror', WPPB_PAID_PLUGIN_URL . 'assets/lib/codemirror/lib/codemirror.js', array(), PROFILE_BUILDER_VERSION);
|
212 |
+
wp_enqueue_script('codemirror-mode-overlay-js', WPPB_PAID_PLUGIN_URL . 'assets/lib/codemirror/addon/mode/overlay.js', array(), '1.0');
|
213 |
+
wp_enqueue_script('codemirror-mode-xml-js', WPPB_PAID_PLUGIN_URL . 'assets/lib/codemirror/mode/xml/xml.js', array(), '1.0');
|
214 |
+
wp_enqueue_script('codemirror-fullscreen-js', WPPB_PAID_PLUGIN_URL . 'assets/lib/codemirror/addon/display/fullscreen.js', array(), '1.0');
|
215 |
+
}
|
216 |
+
|
217 |
+
}
|
218 |
+
|
219 |
+
wp_enqueue_script('jquery-ui-accordion');
|
220 |
+
}
|
221 |
+
}
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Function that prints the codemirror initialization and the css
|
226 |
+
*
|
227 |
+
* @param string $hook the page hook
|
228 |
+
*
|
229 |
+
* @since 2.0.0
|
230 |
+
*
|
231 |
+
*/
|
232 |
+
function wppb_print_codemirror_script(){
|
233 |
+
global $printed_codemirror_scripts;
|
234 |
+
|
235 |
+
if( $printed_codemirror_scripts )
|
236 |
+
return;
|
237 |
+
|
238 |
+
$post_type = NULL;
|
239 |
+
|
240 |
+
if ( isset( $_GET['post_type'] ) || isset( $_GET['post'] ) || isset( $_GET['page'] ) ){
|
241 |
+
if ( isset( $_GET['post_type'] ) )
|
242 |
+
$post_type = sanitize_text_field( $_GET['post_type'] );
|
243 |
+
elseif ( isset( $_GET['post'] ) )
|
244 |
+
$post_type = get_post_type( absint( $_GET['post'] ) );
|
245 |
+
else if( isset( $_GET['page'] ) ){
|
246 |
+
$screen = get_current_screen();
|
247 |
+
if( $screen !== null && is_object( $screen ) ) {
|
248 |
+
$post_type = $screen->id;
|
249 |
+
}
|
250 |
+
}
|
251 |
+
|
252 |
+
if ( ( $this->page[0] == $post_type ) ){
|
253 |
+
if( file_exists( WPPB_PLUGIN_DIR . 'assets/lib/class-mustache-templates/class-mustache-templates.css' ) )
|
254 |
+
wp_enqueue_style( 'class-mustache-css', WPPB_PLUGIN_URL . 'assets/lib/class-mustache-templates/class-mustache-templates.css', false, PROFILE_BUILDER_VERSION );
|
255 |
+
else if( defined( 'WPPB_PAID_PLUGIN_URL' ) )
|
256 |
+
wp_enqueue_style( 'class-mustache-css', WPPB_PAID_PLUGIN_URL . 'assets/lib/class-mustache-templates/class-mustache-templates.css', false, PROFILE_BUILDER_VERSION );
|
257 |
+
|
258 |
+
if( wp_script_is( 'codemirror-mode-overlay-js', 'enqueued' ) ) {
|
259 |
+
if( file_exists( WPPB_PLUGIN_DIR . 'assets/lib/class-mustache-templates/class-mustache-templates.css' ) )
|
260 |
+
wp_enqueue_script( 'class-mustache-js', WPPB_PLUGIN_URL . 'assets/lib/class-mustache-templates/class-mustache-templates.js', array("jquery"), PROFILE_BUILDER_VERSION );
|
261 |
+
else if( defined( 'WPPB_PAID_PLUGIN_URL' ) )
|
262 |
+
wp_enqueue_script( 'class-mustache-js', WPPB_PAID_PLUGIN_URL . 'assets/lib/class-mustache-templates/class-mustache-templates.js', array("jquery"), PROFILE_BUILDER_VERSION );
|
263 |
+
}
|
264 |
+
|
265 |
+
$printed_codemirror_scripts = true;
|
266 |
+
}
|
267 |
+
}
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* Function that adds the mustache metabox
|
272 |
+
*
|
273 |
+
* @since 2.0.0
|
274 |
+
*
|
275 |
+
*/
|
276 |
+
function add_box() {
|
277 |
+
global $post_type;
|
278 |
+
|
279 |
+
foreach ( $this->page as $page ) {
|
280 |
+
add_meta_box( $this->id, $this->title, array( $this, 'meta_box_callback' ), $page, 'normal', $this->priority, array( 'post_type' => $post_type ) );
|
281 |
+
|
282 |
+
/* if it's not a post type add a side metabox with a save button */
|
283 |
+
if( isset( $_GET['page'] ) )
|
284 |
+
add_meta_box( 'page-save-metabox', __( 'Save', 'profile-builder' ), array( $this, 'page_save_meta_box' ), $page, 'side' );
|
285 |
+
}
|
286 |
+
}
|
287 |
+
|
288 |
+
/**
|
289 |
+
* Function output the content for the metabox
|
290 |
+
*
|
291 |
+
*
|
292 |
+
* @since 2.0.0
|
293 |
+
*
|
294 |
+
*/
|
295 |
+
function meta_box_callback( $post, $metabox ) {
|
296 |
+
global $post;
|
297 |
+
if( isset( $_GET['page'] ) )
|
298 |
+
$post_type = NULL;
|
299 |
+
else
|
300 |
+
$post_type = $metabox['args']['post_type'];
|
301 |
+
|
302 |
+
/* save default values in the database as post meta for post types */
|
303 |
+
if( !empty( $this->fields ) && !empty( $post->ID ) ){
|
304 |
+
foreach( $this->fields as $field ){
|
305 |
+
if( !empty( $field['default'] ) ){
|
306 |
+
/* see if we have an option with this name, if we have don't do anything */
|
307 |
+
$field_saved_value = get_post_meta( $post->ID, $field['id'], true );
|
308 |
+
if( empty( $field_saved_value ) ){
|
309 |
+
add_post_meta( $post->ID, $field['id'], $field['default'], true );
|
310 |
+
}
|
311 |
+
}
|
312 |
+
}
|
313 |
+
}
|
314 |
+
|
315 |
+
// Use nonce for verification
|
316 |
+
echo '<input type="hidden" name="' . esc_attr( $post_type ) . '_meta_box_nonce" value="' . esc_attr( wp_create_nonce( basename( __FILE__) ) ) . '" />';
|
317 |
+
|
318 |
+
// Begin the field table and loop
|
319 |
+
echo '<table class="form-table meta_box mustache-box">';
|
320 |
+
foreach ( $this->fields as $field ) {
|
321 |
+
|
322 |
+
// get data for this field
|
323 |
+
extract( $field );
|
324 |
+
if ( !empty( $desc ) )
|
325 |
+
$desc = '<span class="description">' . $desc . '</span>';
|
326 |
+
|
327 |
+
// get value of this field if it exists for this post
|
328 |
+
if( isset( $_GET['page'] ) ){
|
329 |
+
$meta = get_option( $id );
|
330 |
+
}
|
331 |
+
else{
|
332 |
+
$meta = get_post_meta( $post->ID, $id, true );
|
333 |
+
}
|
334 |
+
|
335 |
+
//try to set a default value
|
336 |
+
if( empty( $meta ) && !empty( $default ) ){
|
337 |
+
$meta = $default;
|
338 |
+
}
|
339 |
+
|
340 |
+
|
341 |
+
// begin a table row with
|
342 |
+
echo '<tr>
|
343 |
+
<td class="' . esc_attr( $id ) . ' ' . esc_attr( $type ) . '">';
|
344 |
+
if( $type != 'header' && !empty( $label ) ){
|
345 |
+
echo '<label for="' . esc_attr( $id ) . '" class="wppb_mustache_label">' . esc_html( $label ) . '</label>';
|
346 |
+
}
|
347 |
+
switch( $type ) {
|
348 |
+
// text
|
349 |
+
case 'text':
|
350 |
+
echo '<input type="text" name="' . esc_attr( $id ) . '" id="' . esc_attr( $id ) . '" value="' . esc_attr( $meta ) . '" size="30" />
|
351 |
+
<br />' . wp_kses_post( $desc ) ;
|
352 |
+
break;
|
353 |
+
// textarea
|
354 |
+
case 'textarea':
|
355 |
+
|
356 |
+
echo '<textarea name="' . esc_attr( $id ) . '" id="' . esc_attr( $id ) . '" cols="220" rows="4" class="wppb_mustache_template">' . esc_textarea( $meta ) . '</textarea>';
|
357 |
+
echo wp_kses_post( $desc ) ;
|
358 |
+
break;
|
359 |
+
// checkbox
|
360 |
+
case 'checkbox':
|
361 |
+
echo '<input type="checkbox" name="' . esc_attr( $id ) . '" id="' . esc_attr( $id ) . '"' . checked( esc_attr( $meta ), 'on', false ) . ' value="on" />
|
362 |
+
<label for="' . esc_attr( $id ) . '">' . esc_html( $desc ) . '</label>';
|
363 |
+
break;
|
364 |
+
// select
|
365 |
+
case 'select':
|
366 |
+
echo '<select name="' . esc_attr( $id ) . '" id="' . esc_attr( $id ) . '">';
|
367 |
+
foreach ( $options as $option )
|
368 |
+
echo '<option' . selected( esc_attr( $meta ), $option['value'], false ) . ' value="' . esc_attr( $option['value'] ) . '">' . esc_html( $option['label'] ) . '</option>';
|
369 |
+
echo '</select><br />' . wp_kses_post( $desc );
|
370 |
+
break;
|
371 |
+
// radio
|
372 |
+
case 'radio':
|
373 |
+
foreach ( $options as $option )
|
374 |
+
echo '<input type="radio" name="' . esc_attr( $id ) . '" id="' . esc_attr( $id ) . '-' . esc_attr( $option['value'] ) . '" value="' . esc_attr( $option['value'] ) . '"' . checked( esc_attr( $meta ), $option['value'], false ) . ' />
|
375 |
+
<label for="' . esc_attr( $id ) . '-' . esc_attr( $option['value'] ) . '">' . esc_html( $option['label'] ) . '</label><br />';
|
376 |
+
echo '' . wp_kses_post( $desc );
|
377 |
+
break;
|
378 |
+
// checkbox_group
|
379 |
+
case 'checkbox_group':
|
380 |
+
foreach ( $options as $option )
|
381 |
+
echo '<input type="checkbox" value="' . esc_attr( $option['value'] ) . '" name="' . $id . '[]" id="' . $id . '-' . $option['value'] . '"' , is_array( $meta ) && in_array( $option['value'], $meta ) ? ' checked="checked"' : '' , ' />
|
382 |
+
<label for="' . esc_attr( $id ) . '-' . esc_attr( $option['value'] ) . '">' . esc_html( $option['label'] ) . '</label><br />';
|
383 |
+
echo '' . wp_kses_post( $desc );
|
384 |
+
break;
|
385 |
+
// text
|
386 |
+
case 'header':
|
387 |
+
echo '<h4>'. esc_html( $default ) .'</h4>';
|
388 |
+
break;
|
389 |
+
} //end switch
|
390 |
+
|
391 |
+
if( $type == 'textarea' ){
|
392 |
+
?>
|
393 |
+
<div class="stp-extra">
|
394 |
+
<?php
|
395 |
+
if( !empty( $this->mustache_vars ) ){
|
396 |
+
foreach( $this->mustache_vars as $mustache_var_group ){
|
397 |
+
?>
|
398 |
+
<h4><?php echo esc_html( $mustache_var_group['group-title'] ); ?></h4>
|
399 |
+
<pre><?php do_action( 'wppb_before_mustache_vars_display', $mustache_var_group, $id, $post_type ); $this->display_mustache_available_vars( $mustache_var_group['variables'], 0 ); ?></pre>
|
400 |
+
<?php
|
401 |
+
}
|
402 |
+
}
|
403 |
+
?>
|
404 |
+
</div>
|
405 |
+
<?php
|
406 |
+
}
|
407 |
+
echo '</td></tr>';
|
408 |
+
} // end foreach
|
409 |
+
echo '</table>'; // end table
|
410 |
+
}
|
411 |
+
|
412 |
+
/**
|
413 |
+
* Function that ads a side metabox on pages ( not post types ) with a save button
|
414 |
+
*
|
415 |
+
*
|
416 |
+
* @since 2.0.0
|
417 |
+
*
|
418 |
+
*/
|
419 |
+
function page_save_meta_box() {
|
420 |
+
?>
|
421 |
+
<input type="submit" value="<?php esc_html_e( 'Save Changes', 'profile-builder' ); ?>" class="button button-primary button-large mustache-save">
|
422 |
+
<?php
|
423 |
+
}
|
424 |
+
|
425 |
+
/**
|
426 |
+
* Function that ads a form start on pages ( not post types ) and also the 'save_post' action
|
427 |
+
*
|
428 |
+
*
|
429 |
+
* @since 2.0.0
|
430 |
+
*
|
431 |
+
*/
|
432 |
+
function wppb_mustache_page_before( $hook ){
|
433 |
+
global $started_mustache_form;
|
434 |
+
|
435 |
+
if( $started_mustache_form )
|
436 |
+
return;
|
437 |
+
|
438 |
+
if( isset( $_GET['page'] ) && $hook == $this->page[0] ){
|
439 |
+
/* if we are saving do the action 'save_post' */
|
440 |
+
if( isset( $_GET['mustache_action'] ) && $_GET['mustache_action'] == 'save' ) {
|
441 |
+
/* to avoid conflicts with other plugins we send an empty post object as the second parameter with a fake post type */
|
442 |
+
$post = new WP_Post( new stdClass() );
|
443 |
+
$post->post_type = 'wppb-mustache-settings-page';
|
444 |
+
remove_all_actions('save_post', 10 );//remove all actions that could run on the save_post hook on priority 10 than could expect a numeric post id instead of a string like $this->id. Ex: add_action( 'save_post', array( $this, 'refresh_post_word_count' ) ); in WPML translation management
|
445 |
+
remove_all_actions('save_post', PHP_INT_MAX );//same for PHP_INT_MAX priority: add_action( 'save_post', array( $this, 'queue_save_post_actions' ), PHP_INT_MAX, 2 );
|
446 |
+
do_action( 'save_post', $this->id, $post, false );
|
447 |
+
}
|
448 |
+
|
449 |
+
echo '<form action="'. esc_url( add_query_arg( array('mustache_action' => 'save') ) ) .'" method="post">';
|
450 |
+
$started_mustache_form = true;
|
451 |
+
}
|
452 |
+
}
|
453 |
+
|
454 |
+
/**
|
455 |
+
* Function that ads a form end on pages ( not post types )
|
456 |
+
*
|
457 |
+
*
|
458 |
+
* @since 2.0.0
|
459 |
+
*
|
460 |
+
*/
|
461 |
+
function wppb_mustache_page_after( $hook ){
|
462 |
+
global $ended_mustache_form;
|
463 |
+
if( $ended_mustache_form )
|
464 |
+
return;
|
465 |
+
|
466 |
+
if( isset( $_GET['page'] ) && $hook == $this->page[0] ){
|
467 |
+
echo '</form>';
|
468 |
+
$ended_mustache_form = true;
|
469 |
+
}
|
470 |
+
}
|
471 |
+
|
472 |
+
|
473 |
+
/**
|
474 |
+
* Function that transforms and echoes the $mustache_vars into despayable forms. It takes care of nested levels and adds {{}} as well
|
475 |
+
*
|
476 |
+
* @param array $mustache_vars is the array containing the names of the variable that must be proccessed by mustache in the template. The array must be in this form:
|
477 |
+
* array( array( 'name' => '', 'type' => '' ) ... ), and for loop tags it also contains a 'children' element which contains other mustache_vars array( array( 'name' => 'users', 'type' => 'loop_tag', 'children' => $merge_tags )
|
478 |
+
* @param int $level the current level of the nested variables.
|
479 |
+
*
|
480 |
+
* @since 2.0.0
|
481 |
+
*
|
482 |
+
*/
|
483 |
+
function display_mustache_available_vars( $mustache_vars, $level ){
|
484 |
+
if( !empty( $mustache_vars ) ){
|
485 |
+
foreach( $mustache_vars as $var ){
|
486 |
+
if ( $var[ 'name' ] != 'password' ) {
|
487 |
+
if ( empty( $var[ 'children' ] ) ) {
|
488 |
+
echo str_repeat( " ", $level );//phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
489 |
+
if ( !empty( $var[ 'label' ] ) )
|
490 |
+
echo esc_html( apply_filters( 'wppb_variable_label', $var[ 'label' ] . ':' ) );
|
491 |
+
if ( !empty( $var[ 'unescaped' ] ) && $var[ 'unescaped' ] === true )
|
492 |
+
echo '{{{';
|
493 |
+
else
|
494 |
+
echo '{{';
|
495 |
+
echo esc_html( $var[ 'name' ] );
|
496 |
+
if ( !empty( $var[ 'unescaped' ] ) && $var[ 'unescaped' ] === true )
|
497 |
+
echo '}}}';
|
498 |
+
else
|
499 |
+
echo '}}';
|
500 |
+
echo PHP_EOL;
|
501 |
+
} else {
|
502 |
+
echo str_repeat( " ", $level ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
503 |
+
echo '{{#' . esc_html( $var[ 'name' ] ) . '}}' . PHP_EOL;
|
504 |
+
$level++;
|
505 |
+
$this->display_mustache_available_vars( $var[ 'children' ], $level );
|
506 |
+
$level--;
|
507 |
+
echo str_repeat( " ", $level ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
508 |
+
echo '{{/' . esc_html( $var[ 'name' ] ) . '}}' . PHP_EOL;
|
509 |
+
}
|
510 |
+
}
|
511 |
+
}
|
512 |
+
}
|
513 |
+
}
|
514 |
+
|
515 |
+
/**
|
516 |
+
* Function that saves the values from the metabox
|
517 |
+
*
|
518 |
+
* @param int $post_id the post id
|
519 |
+
* @param post object $post
|
520 |
+
*
|
521 |
+
* @since 2.0.0
|
522 |
+
*
|
523 |
+
*/
|
524 |
+
function save_box( $post_id, $post ){
|
525 |
+
global $post_type;
|
526 |
+
/* addition to save as option if we are not on a post type */
|
527 |
+
if( !is_numeric( $post_id ) && isset( $_POST['_meta_box_nonce'] ) && @wp_verify_nonce( sanitize_text_field( $_POST['_meta_box_nonce'] ), basename( __FILE__ ) ) ){
|
528 |
+
foreach ( $this->fields as $field ) {
|
529 |
+
|
530 |
+
if ( isset( $_POST[$field['id']] ) ) {
|
531 |
+
update_option( $field['id'], wp_unslash( $_POST[$field['id']] ) );//phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
532 |
+
}
|
533 |
+
|
534 |
+
}
|
535 |
+
}
|
536 |
+
|
537 |
+
// verify nonce
|
538 |
+
if ( ! ( in_array( $post_type, $this->page ) && isset( $_POST[$post_type . '_meta_box_nonce'] ) && @wp_verify_nonce( sanitize_text_field( $_POST[$post_type . '_meta_box_nonce'] ), basename( __FILE__ ) ) ) )
|
539 |
+
return $post_id;
|
540 |
+
// check autosave
|
541 |
+
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
|
542 |
+
return $post_id;
|
543 |
+
// check permissions
|
544 |
+
if ( !current_user_can( 'edit_page', $post_id ) )
|
545 |
+
return $post_id;
|
546 |
+
|
547 |
+
// loop through fields and save the data
|
548 |
+
foreach ( $this->fields as $field ) {
|
549 |
+
if( $field['type'] == 'tax_select' ) {
|
550 |
+
// save taxonomies
|
551 |
+
if ( isset( $_POST[$field['id']] ) )
|
552 |
+
$term = $_POST[$field['id']]; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
553 |
+
wp_set_object_terms( $post_id, $term, $field['id'] );
|
554 |
+
}
|
555 |
+
else {
|
556 |
+
// save the rest
|
557 |
+
$old = get_post_meta( $post_id, $field['id'], true );
|
558 |
+
if ( isset( $_POST[$field['id']] ) ) {
|
559 |
+
$new = $_POST[$field['id']]; /* phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized */
|
560 |
+
} else {
|
561 |
+
$new = '';
|
562 |
+
}
|
563 |
+
|
564 |
+
if ( $new && $new != $old ) {
|
565 |
+
if ( is_array( $new ) ) {
|
566 |
+
foreach ( $new as &$item ) {
|
567 |
+
//$item = esc_attr( $item );
|
568 |
+
}
|
569 |
+
unset( $item );
|
570 |
+
} else {
|
571 |
+
//$new = esc_attr( $new );
|
572 |
+
}
|
573 |
+
update_post_meta( $post_id, $field['id'], $new );
|
574 |
+
} elseif ( '' == $new && $old ) {
|
575 |
+
delete_post_meta( $post_id, $field['id'], $old );
|
576 |
+
}
|
577 |
+
}
|
578 |
+
} // end foreach
|
579 |
+
}
|
580 |
+
|
581 |
+
|
582 |
+
/**
|
583 |
+
* Function that saves the default values for each field in the database for options
|
584 |
+
*
|
585 |
+
*
|
586 |
+
* @since 2.0.0
|
587 |
+
*
|
588 |
+
*/
|
589 |
+
|
590 |
+
function save_default_values(){
|
591 |
+
/* only do it on pages where we save as options and we have fields */
|
592 |
+
if( !empty( $this->fields ) && !post_type_exists($this->page[0]) ){
|
593 |
+
foreach( $this->fields as $field ){
|
594 |
+
if( !empty( $field['default'] ) ){
|
595 |
+
/* see if we have an option with this name, if we have don't do anything */
|
596 |
+
$field_saved_value = get_option( $field['id'] );
|
597 |
+
if( empty( $field_saved_value ) ) {
|
598 |
+
update_option( $field['id'], $field['default'] );
|
599 |
+
}
|
600 |
+
}
|
601 |
+
}
|
602 |
+
}
|
603 |
+
}
|
604 |
+
}
|
assets/lib/codemirror/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Copyright (C) 2013 by Marijn Haverbeke <marijnh@gmail.com> and others
|
2 |
+
|
3 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4 |
+
of this software and associated documentation files (the "Software"), to deal
|
5 |
+
in the Software without restriction, including without limitation the rights
|
6 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7 |
+
copies of the Software, and to permit persons to whom the Software is
|
8 |
+
furnished to do so, subject to the following conditions:
|
9 |
+
|
10 |
+
The above copyright notice and this permission notice shall be included in
|
11 |
+
all copies or substantial portions of the Software.
|
12 |
+
|
13 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19 |
+
THE SOFTWARE.
|
assets/lib/codemirror/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# CodeMirror
|
2 |
+
[![Build Status](https://secure.travis-ci.org/marijnh/CodeMirror.png?branch=master)](http://travis-ci.org/marijnh/CodeMirror)
|
3 |
+
[![NPM version](https://badge.fury.io/js/codemirror.png)](http://badge.fury.io/js/codemirror)
|
4 |
+
|
5 |
+
CodeMirror is a JavaScript component that provides a code editor in
|
6 |
+
the browser. When a mode is available for the language you are coding
|
7 |
+
in, it will color your code, and optionally help with indentation.
|
8 |
+
|
9 |
+
The project page is http://codemirror.net
|
10 |
+
The manual is at http://codemirror.net/doc/manual.html
|
11 |
+
The contributing guidelines are in [CONTRIBUTING.md](https://github.com/marijnh/CodeMirror/blob/master/CONTRIBUTING.md)
|
assets/lib/codemirror/addon/comment/comment.js
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
var noOptions = {};
|
5 |
+
var nonWS = /[^\s\u00a0]/;
|
6 |
+
var Pos = CodeMirror.Pos;
|
7 |
+
|
8 |
+
function firstNonWS(str) {
|
9 |
+
var found = str.search(nonWS);
|
10 |
+
return found == -1 ? 0 : found;
|
11 |
+
}
|
12 |
+
|
13 |
+
CodeMirror.commands.toggleComment = function(cm) {
|
14 |
+
var from = cm.getCursor("start"), to = cm.getCursor("end");
|
15 |
+
cm.uncomment(from, to) || cm.lineComment(from, to);
|
16 |
+
};
|
17 |
+
|
18 |
+
CodeMirror.defineExtension("lineComment", function(from, to, options) {
|
19 |
+
if (!options) options = noOptions;
|
20 |
+
var self = this, mode = self.getModeAt(from);
|
21 |
+
var commentString = options.lineComment || mode.lineComment;
|
22 |
+
if (!commentString) {
|
23 |
+
if (options.blockCommentStart || mode.blockCommentStart) {
|
24 |
+
options.fullLines = true;
|
25 |
+
self.blockComment(from, to, options);
|
26 |
+
}
|
27 |
+
return;
|
28 |
+
}
|
29 |
+
var firstLine = self.getLine(from.line);
|
30 |
+
if (firstLine == null) return;
|
31 |
+
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
|
32 |
+
var pad = options.padding == null ? " " : options.padding;
|
33 |
+
var blankLines = options.commentBlankLines || from.line == to.line;
|
34 |
+
|
35 |
+
self.operation(function() {
|
36 |
+
if (options.indent) {
|
37 |
+
var baseString = firstLine.slice(0, firstNonWS(firstLine));
|
38 |
+
for (var i = from.line; i < end; ++i) {
|
39 |
+
var line = self.getLine(i), cut = baseString.length;
|
40 |
+
if (!blankLines && !nonWS.test(line)) continue;
|
41 |
+
if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
|
42 |
+
self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
|
43 |
+
}
|
44 |
+
} else {
|
45 |
+
for (var i = from.line; i < end; ++i) {
|
46 |
+
if (blankLines || nonWS.test(self.getLine(i)))
|
47 |
+
self.replaceRange(commentString + pad, Pos(i, 0));
|
48 |
+
}
|
49 |
+
}
|
50 |
+
});
|
51 |
+
});
|
52 |
+
|
53 |
+
CodeMirror.defineExtension("blockComment", function(from, to, options) {
|
54 |
+
if (!options) options = noOptions;
|
55 |
+
var self = this, mode = self.getModeAt(from);
|
56 |
+
var startString = options.blockCommentStart || mode.blockCommentStart;
|
57 |
+
var endString = options.blockCommentEnd || mode.blockCommentEnd;
|
58 |
+
if (!startString || !endString) {
|
59 |
+
if ((options.lineComment || mode.lineComment) && options.fullLines != false)
|
60 |
+
self.lineComment(from, to, options);
|
61 |
+
return;
|
62 |
+
}
|
63 |
+
|
64 |
+
var end = Math.min(to.line, self.lastLine());
|
65 |
+
if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
|
66 |
+
|
67 |
+
var pad = options.padding == null ? " " : options.padding;
|
68 |
+
if (from.line > end) return;
|
69 |
+
|
70 |
+
self.operation(function() {
|
71 |
+
if (options.fullLines != false) {
|
72 |
+
var lastLineHasText = nonWS.test(self.getLine(end));
|
73 |
+
self.replaceRange(pad + endString, Pos(end));
|
74 |
+
self.replaceRange(startString + pad, Pos(from.line, 0));
|
75 |
+
var lead = options.blockCommentLead || mode.blockCommentLead;
|
76 |
+
if (lead != null) for (var i = from.line + 1; i <= end; ++i)
|
77 |
+
if (i != end || lastLineHasText)
|
78 |
+
self.replaceRange(lead + pad, Pos(i, 0));
|
79 |
+
} else {
|
80 |
+
self.replaceRange(endString, to);
|
81 |
+
self.replaceRange(startString, from);
|
82 |
+
}
|
83 |
+
});
|
84 |
+
});
|
85 |
+
|
86 |
+
CodeMirror.defineExtension("uncomment", function(from, to, options) {
|
87 |
+
if (!options) options = noOptions;
|
88 |
+
var self = this, mode = self.getModeAt(from);
|
89 |
+
var end = Math.min(to.line, self.lastLine()), start = Math.min(from.line, end);
|
90 |
+
|
91 |
+
// Try finding line comments
|
92 |
+
var lineString = options.lineComment || mode.lineComment, lines = [];
|
93 |
+
var pad = options.padding == null ? " " : options.padding, didSomething;
|
94 |
+
lineComment: {
|
95 |
+
if (!lineString) break lineComment;
|
96 |
+
for (var i = start; i <= end; ++i) {
|
97 |
+
var line = self.getLine(i);
|
98 |
+
var found = line.indexOf(lineString);
|
99 |
+
if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment;
|
100 |
+
if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
|
101 |
+
lines.push(line);
|
102 |
+
}
|
103 |
+
self.operation(function() {
|
104 |
+
for (var i = start; i <= end; ++i) {
|
105 |
+
var line = lines[i - start];
|
106 |
+
var pos = line.indexOf(lineString), endPos = pos + lineString.length;
|
107 |
+
if (pos < 0) continue;
|
108 |
+
if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
|
109 |
+
didSomething = true;
|
110 |
+
self.replaceRange("", Pos(i, pos), Pos(i, endPos));
|
111 |
+
}
|
112 |
+
});
|
113 |
+
if (didSomething) return true;
|
114 |
+
}
|
115 |
+
|
116 |
+
// Try block comments
|
117 |
+
var startString = options.blockCommentStart || mode.blockCommentStart;
|
118 |
+
var endString = options.blockCommentEnd || mode.blockCommentEnd;
|
119 |
+
if (!startString || !endString) return false;
|
120 |
+
var lead = options.blockCommentLead || mode.blockCommentLead;
|
121 |
+
var startLine = self.getLine(start), endLine = end == start ? startLine : self.getLine(end);
|
122 |
+
var open = startLine.indexOf(startString), close = endLine.lastIndexOf(endString);
|
123 |
+
if (close == -1 && start != end) {
|
124 |
+
endLine = self.getLine(--end);
|
125 |
+
close = endLine.lastIndexOf(endString);
|
126 |
+
}
|
127 |
+
if (open == -1 || close == -1) return false;
|
128 |
+
|
129 |
+
self.operation(function() {
|
130 |
+
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
|
131 |
+
Pos(end, close + endString.length));
|
132 |
+
var openEnd = open + startString.length;
|
133 |
+
if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
|
134 |
+
self.replaceRange("", Pos(start, open), Pos(start, openEnd));
|
135 |
+
if (lead) for (var i = start + 1; i <= end; ++i) {
|
136 |
+
var line = self.getLine(i), found = line.indexOf(lead);
|
137 |
+
if (found == -1 || nonWS.test(line.slice(0, found))) continue;
|
138 |
+
var foundEnd = found + lead.length;
|
139 |
+
if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
|
140 |
+
self.replaceRange("", Pos(i, found), Pos(i, foundEnd));
|
141 |
+
}
|
142 |
+
});
|
143 |
+
return true;
|
144 |
+
});
|
145 |
+
})();
|
assets/lib/codemirror/addon/comment/continuecomment.js
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
var modes = ["clike", "css", "javascript"];
|
3 |
+
for (var i = 0; i < modes.length; ++i)
|
4 |
+
CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "});
|
5 |
+
|
6 |
+
function continueComment(cm) {
|
7 |
+
var pos = cm.getCursor(), token = cm.getTokenAt(pos);
|
8 |
+
if (token.type != "comment") return CodeMirror.Pass;
|
9 |
+
var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode;
|
10 |
+
|
11 |
+
var insert;
|
12 |
+
if (mode.blockCommentStart && mode.blockCommentContinue) {
|
13 |
+
var end = token.string.indexOf(mode.blockCommentEnd);
|
14 |
+
var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found;
|
15 |
+
if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) {
|
16 |
+
// Comment ended, don't continue it
|
17 |
+
} else if (token.string.indexOf(mode.blockCommentStart) == 0) {
|
18 |
+
insert = full.slice(0, token.start);
|
19 |
+
if (!/^\s*$/.test(insert)) {
|
20 |
+
insert = "";
|
21 |
+
for (var i = 0; i < token.start; ++i) insert += " ";
|
22 |
+
}
|
23 |
+
} else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 &&
|
24 |
+
found + mode.blockCommentContinue.length > token.start &&
|
25 |
+
/^\s*$/.test(full.slice(0, found))) {
|
26 |
+
insert = full.slice(0, found);
|
27 |
+
}
|
28 |
+
if (insert != null) insert += mode.blockCommentContinue;
|
29 |
+
}
|
30 |
+
if (insert == null && mode.lineComment) {
|
31 |
+
var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment);
|
32 |
+
if (found > -1) {
|
33 |
+
insert = line.slice(0, found);
|
34 |
+
if (/\S/.test(insert)) insert = null;
|
35 |
+
else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0];
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
if (insert != null)
|
40 |
+
cm.replaceSelection("\n" + insert, "end");
|
41 |
+
else
|
42 |
+
return CodeMirror.Pass;
|
43 |
+
}
|
44 |
+
|
45 |
+
CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
|
46 |
+
if (prev && prev != CodeMirror.Init)
|
47 |
+
cm.removeKeyMap("continueComment");
|
48 |
+
if (val) {
|
49 |
+
var map = {name: "continueComment"};
|
50 |
+
map[typeof val == "string" ? val : "Enter"] = continueComment;
|
51 |
+
cm.addKeyMap(map);
|
52 |
+
}
|
53 |
+
});
|
54 |
+
})();
|
assets/lib/codemirror/addon/dialog/dialog.css
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.CodeMirror-dialog {
|
2 |
+
position: absolute;
|
3 |
+
left: 0; right: 0;
|
4 |
+
background: white;
|
5 |
+
z-index: 15;
|
6 |
+
padding: .1em .8em;
|
7 |
+
overflow: hidden;
|
8 |
+
color: #333;
|
9 |
+
}
|
10 |
+
|
11 |
+
.CodeMirror-dialog-top {
|
12 |
+
border-bottom: 1px solid #eee;
|
13 |
+
top: 0;
|
14 |
+
}
|
15 |
+
|
16 |
+
.CodeMirror-dialog-bottom {
|
17 |
+
border-top: 1px solid #eee;
|
18 |
+
bottom: 0;
|
19 |
+
}
|
20 |
+
|
21 |
+
.CodeMirror-dialog input {
|
22 |
+
border: none;
|
23 |
+
outline: none;
|
24 |
+
background: transparent;
|
25 |
+
width: 20em;
|
26 |
+
color: inherit;
|
27 |
+
font-family: monospace;
|
28 |
+
}
|
29 |
+
|
30 |
+
.CodeMirror-dialog button {
|
31 |
+
font-size: 70%;
|
32 |
+
}
|
assets/lib/codemirror/addon/dialog/dialog.js
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Open simple dialogs on top of an editor. Relies on dialog.css.
|
2 |
+
|
3 |
+
(function() {
|
4 |
+
function dialogDiv(cm, template, bottom) {
|
5 |
+
var wrap = cm.getWrapperElement();
|
6 |
+
var dialog;
|
7 |
+
dialog = wrap.appendChild(document.createElement("div"));
|
8 |
+
if (bottom) {
|
9 |
+
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
|
10 |
+
} else {
|
11 |
+
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
|
12 |
+
}
|
13 |
+
dialog.innerHTML = template;
|
14 |
+
return dialog;
|
15 |
+
}
|
16 |
+
|
17 |
+
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
|
18 |
+
var dialog = dialogDiv(this, template, options && options.bottom);
|
19 |
+
var closed = false, me = this;
|
20 |
+
function close() {
|
21 |
+
if (closed) return;
|
22 |
+
closed = true;
|
23 |
+
dialog.parentNode.removeChild(dialog);
|
24 |
+
}
|
25 |
+
var inp = dialog.getElementsByTagName("input")[0], button;
|
26 |
+
if (inp) {
|
27 |
+
CodeMirror.on(inp, "keydown", function(e) {
|
28 |
+
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
|
29 |
+
if (e.keyCode == 13 || e.keyCode == 27) {
|
30 |
+
CodeMirror.e_stop(e);
|
31 |
+
close();
|
32 |
+
me.focus();
|
33 |
+
if (e.keyCode == 13) callback(inp.value);
|
34 |
+
}
|
35 |
+
});
|
36 |
+
if (options && options.onKeyUp) {
|
37 |
+
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
|
38 |
+
}
|
39 |
+
if (options && options.value) inp.value = options.value;
|
40 |
+
inp.focus();
|
41 |
+
CodeMirror.on(inp, "blur", close);
|
42 |
+
} else if (button = dialog.getElementsByTagName("button")[0]) {
|
43 |
+
CodeMirror.on(button, "click", function() {
|
44 |
+
close();
|
45 |
+
me.focus();
|
46 |
+
});
|
47 |
+
button.focus();
|
48 |
+
CodeMirror.on(button, "blur", close);
|
49 |
+
}
|
50 |
+
return close;
|
51 |
+
});
|
52 |
+
|
53 |
+
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
|
54 |
+
var dialog = dialogDiv(this, template, options && options.bottom);
|
55 |
+
var buttons = dialog.getElementsByTagName("button");
|
56 |
+
var closed = false, me = this, blurring = 1;
|
57 |
+
function close() {
|
58 |
+
if (closed) return;
|
59 |
+
closed = true;
|
60 |
+
dialog.parentNode.removeChild(dialog);
|
61 |
+
me.focus();
|
62 |
+
}
|
63 |
+
buttons[0].focus();
|
64 |
+
for (var i = 0; i < buttons.length; ++i) {
|
65 |
+
var b = buttons[i];
|
66 |
+
(function(callback) {
|
67 |
+
CodeMirror.on(b, "click", function(e) {
|
68 |
+
CodeMirror.e_preventDefault(e);
|
69 |
+
close();
|
70 |
+
if (callback) callback(me);
|
71 |
+
});
|
72 |
+
})(callbacks[i]);
|
73 |
+
CodeMirror.on(b, "blur", function() {
|
74 |
+
--blurring;
|
75 |
+
setTimeout(function() { if (blurring <= 0) close(); }, 200);
|
76 |
+
});
|
77 |
+
CodeMirror.on(b, "focus", function() { ++blurring; });
|
78 |
+
}
|
79 |
+
});
|
80 |
+
})();
|
assets/lib/codemirror/addon/display/fullscreen.css
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.CodeMirror-fullscreen {
|
2 |
+
position: fixed;
|
3 |
+
top: 0; left: 0; right: 0; bottom: 0;
|
4 |
+
height: auto;
|
5 |
+
z-index: 9;
|
6 |
+
}
|
assets/lib/codemirror/addon/display/fullscreen.js
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
CodeMirror.defineOption("fullScreen", false, function(cm, val, old) {
|
5 |
+
if (old == CodeMirror.Init) old = false;
|
6 |
+
if (!old == !val) return;
|
7 |
+
if (val) setFullscreen(cm);
|
8 |
+
else setNormal(cm);
|
9 |
+
});
|
10 |
+
|
11 |
+
function setFullscreen(cm) {
|
12 |
+
var wrap = cm.getWrapperElement();
|
13 |
+
cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
|
14 |
+
width: wrap.style.width, height: wrap.style.height};
|
15 |
+
wrap.style.width = wrap.style.height = "";
|
16 |
+
wrap.className += " CodeMirror-fullscreen";
|
17 |
+
document.documentElement.style.overflow = "hidden";
|
18 |
+
cm.refresh();
|
19 |
+
}
|
20 |
+
|
21 |
+
function setNormal(cm) {
|
22 |
+
var wrap = cm.getWrapperElement();
|
23 |
+
wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
|
24 |
+
document.documentElement.style.overflow = "";
|
25 |
+
var info = cm.state.fullScreenRestore;
|
26 |
+
wrap.style.width = info.width; wrap.style.height = info.height;
|
27 |
+
window.scrollTo(info.scrollLeft, info.scrollTop);
|
28 |
+
cm.refresh();
|
29 |
+
}
|
30 |
+
})();
|
assets/lib/codemirror/addon/display/placeholder.js
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
|
3 |
+
var prev = old && old != CodeMirror.Init;
|
4 |
+
if (val && !prev) {
|
5 |
+
cm.on("focus", onFocus);
|
6 |
+
cm.on("blur", onBlur);
|
7 |
+
cm.on("change", onChange);
|
8 |
+
onChange(cm);
|
9 |
+
} else if (!val && prev) {
|
10 |
+
cm.off("focus", onFocus);
|
11 |
+
cm.off("blur", onBlur);
|
12 |
+
cm.off("change", onChange);
|
13 |
+
clearPlaceholder(cm);
|
14 |
+
var wrapper = cm.getWrapperElement();
|
15 |
+
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
|
16 |
+
}
|
17 |
+
|
18 |
+
if (val && !cm.hasFocus()) onBlur(cm);
|
19 |
+
});
|
20 |
+
|
21 |
+
function clearPlaceholder(cm) {
|
22 |
+
if (cm.state.placeholder) {
|
23 |
+
cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
|
24 |
+
cm.state.placeholder = null;
|
25 |
+
}
|
26 |
+
}
|
27 |
+
function setPlaceholder(cm) {
|
28 |
+
clearPlaceholder(cm);
|
29 |
+
var elt = cm.state.placeholder = document.createElement("pre");
|
30 |
+
elt.style.cssText = "height: 0; overflow: visible";
|
31 |
+
elt.className = "CodeMirror-placeholder";
|
32 |
+
elt.appendChild(document.createTextNode(cm.getOption("placeholder")));
|
33 |
+
cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
|
34 |
+
}
|
35 |
+
|
36 |
+
function onFocus(cm) {
|
37 |
+
clearPlaceholder(cm);
|
38 |
+
}
|
39 |
+
function onBlur(cm) {
|
40 |
+
if (isEmpty(cm)) setPlaceholder(cm);
|
41 |
+
}
|
42 |
+
function onChange(cm) {
|
43 |
+
var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
|
44 |
+
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
|
45 |
+
|
46 |
+
if (cm.hasFocus()) return;
|
47 |
+
if (empty) setPlaceholder(cm);
|
48 |
+
else clearPlaceholder(cm);
|
49 |
+
}
|
50 |
+
|
51 |
+
function isEmpty(cm) {
|
52 |
+
return (cm.lineCount() === 1) && (cm.getLine(0) === "");
|
53 |
+
}
|
54 |
+
})();
|
assets/lib/codemirror/addon/edit/closebrackets.js
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
var DEFAULT_BRACKETS = "()[]{}''\"\"";
|
3 |
+
var DEFAULT_EXPLODE_ON_ENTER = "[]{}";
|
4 |
+
var SPACE_CHAR_REGEX = /\s/;
|
5 |
+
|
6 |
+
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
|
7 |
+
if (old != CodeMirror.Init && old)
|
8 |
+
cm.removeKeyMap("autoCloseBrackets");
|
9 |
+
if (!val) return;
|
10 |
+
var pairs = DEFAULT_BRACKETS, explode = DEFAULT_EXPLODE_ON_ENTER;
|
11 |
+
if (typeof val == "string") pairs = val;
|
12 |
+
else if (typeof val == "object") {
|
13 |
+
if (val.pairs != null) pairs = val.pairs;
|
14 |
+
if (val.explode != null) explode = val.explode;
|
15 |
+
}
|
16 |
+
var map = buildKeymap(pairs);
|
17 |
+
if (explode) map.Enter = buildExplodeHandler(explode);
|
18 |
+
cm.addKeyMap(map);
|
19 |
+
});
|
20 |
+
|
21 |
+
function charsAround(cm, pos) {
|
22 |
+
var str = cm.getRange(CodeMirror.Pos(pos.line, pos.ch - 1),
|
23 |
+
CodeMirror.Pos(pos.line, pos.ch + 1));
|
24 |
+
return str.length == 2 ? str : null;
|
25 |
+
}
|
26 |
+
|
27 |
+
function buildKeymap(pairs) {
|
28 |
+
var map = {
|
29 |
+
name : "autoCloseBrackets",
|
30 |
+
Backspace: function(cm) {
|
31 |
+
if (cm.somethingSelected()) return CodeMirror.Pass;
|
32 |
+
var cur = cm.getCursor(), around = charsAround(cm, cur);
|
33 |
+
if (around && pairs.indexOf(around) % 2 == 0)
|
34 |
+
cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1));
|
35 |
+
else
|
36 |
+
return CodeMirror.Pass;
|
37 |
+
}
|
38 |
+
};
|
39 |
+
var closingBrackets = "";
|
40 |
+
for (var i = 0; i < pairs.length; i += 2) (function(left, right) {
|
41 |
+
if (left != right) closingBrackets += right;
|
42 |
+
function surround(cm) {
|
43 |
+
var selection = cm.getSelection();
|
44 |
+
cm.replaceSelection(left + selection + right);
|
45 |
+
}
|
46 |
+
function maybeOverwrite(cm) {
|
47 |
+
var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1));
|
48 |
+
if (ahead != right || cm.somethingSelected()) return CodeMirror.Pass;
|
49 |
+
else cm.execCommand("goCharRight");
|
50 |
+
}
|
51 |
+
map["'" + left + "'"] = function(cm) {
|
52 |
+
if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment")
|
53 |
+
return CodeMirror.Pass;
|
54 |
+
if (cm.somethingSelected()) return surround(cm);
|
55 |
+
if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return;
|
56 |
+
var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1);
|
57 |
+
var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch), curChar = cur.ch > 0 ? line.charAt(cur.ch - 1) : "";
|
58 |
+
if (left == right && CodeMirror.isWordChar(curChar))
|
59 |
+
return CodeMirror.Pass;
|
60 |
+
if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar))
|
61 |
+
cm.replaceSelection(left + right, {head: ahead, anchor: ahead});
|
62 |
+
else
|
63 |
+
return CodeMirror.Pass;
|
64 |
+
};
|
65 |
+
if (left != right) map["'" + right + "'"] = maybeOverwrite;
|
66 |
+
})(pairs.charAt(i), pairs.charAt(i + 1));
|
67 |
+
return map;
|
68 |
+
}
|
69 |
+
|
70 |
+
function buildExplodeHandler(pairs) {
|
71 |
+
return function(cm) {
|
72 |
+
var cur = cm.getCursor(), around = charsAround(cm, cur);
|
73 |
+
if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
74 |
+
cm.operation(function() {
|
75 |
+
var newPos = CodeMirror.Pos(cur.line + 1, 0);
|
76 |
+
cm.replaceSelection("\n\n", {anchor: newPos, head: newPos}, "+input");
|
77 |
+
cm.indentLine(cur.line + 1, null, true);
|
78 |
+
cm.indentLine(cur.line + 2, null, true);
|
79 |
+
});
|
80 |
+
};
|
81 |
+
}
|
82 |
+
})();
|
assets/lib/codemirror/addon/edit/closetag.js
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Tag-closer extension for CodeMirror.
|
3 |
+
*
|
4 |
+
* This extension adds an "autoCloseTags" option that can be set to
|
5 |
+
* either true to get the default behavior, or an object to further
|
6 |
+
* configure its behavior.
|
7 |
+
*
|
8 |
+
* These are supported options:
|
9 |
+
*
|
10 |
+
* `whenClosing` (default true)
|
11 |
+
* Whether to autoclose when the '/' of a closing tag is typed.
|
12 |
+
* `whenOpening` (default true)
|
13 |
+
* Whether to autoclose the tag when the final '>' of an opening
|
14 |
+
* tag is typed.
|
15 |
+
* `dontCloseTags` (default is empty tags for HTML, none for XML)
|
16 |
+
* An array of tag names that should not be autoclosed.
|
17 |
+
* `indentTags` (default is block tags for HTML, none for XML)
|
18 |
+
* An array of tag names that should, when opened, cause a
|
19 |
+
* blank line to be added inside the tag, and the blank line and
|
20 |
+
* closing line to be indented.
|
21 |
+
*
|
22 |
+
* See demos/closetag.html for a usage example.
|
23 |
+
*/
|
24 |
+
|
25 |
+
(function() {
|
26 |
+
CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
|
27 |
+
if (val && (old == CodeMirror.Init || !old)) {
|
28 |
+
var map = {name: "autoCloseTags"};
|
29 |
+
if (typeof val != "object" || val.whenClosing)
|
30 |
+
map["'/'"] = function(cm) { return autoCloseSlash(cm); };
|
31 |
+
if (typeof val != "object" || val.whenOpening)
|
32 |
+
map["'>'"] = function(cm) { return autoCloseGT(cm); };
|
33 |
+
cm.addKeyMap(map);
|
34 |
+
} else if (!val && (old != CodeMirror.Init && old)) {
|
35 |
+
cm.removeKeyMap("autoCloseTags");
|
36 |
+
}
|
37 |
+
});
|
38 |
+
|
39 |
+
var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
|
40 |
+
"source", "track", "wbr"];
|
41 |
+
var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
|
42 |
+
"h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
|
43 |
+
|
44 |
+
function autoCloseGT(cm) {
|
45 |
+
var pos = cm.getCursor(), tok = cm.getTokenAt(pos);
|
46 |
+
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
|
47 |
+
if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
|
48 |
+
|
49 |
+
var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html";
|
50 |
+
var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
|
51 |
+
var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
|
52 |
+
|
53 |
+
var tagName = state.tagName;
|
54 |
+
if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
|
55 |
+
var lowerTagName = tagName.toLowerCase();
|
56 |
+
// Don't process the '>' at the end of an end-tag or self-closing tag
|
57 |
+
if (tok.type == "tag" && state.type == "closeTag" ||
|
58 |
+
tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
|
59 |
+
dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1)
|
60 |
+
return CodeMirror.Pass;
|
61 |
+
|
62 |
+
var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1;
|
63 |
+
var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1);
|
64 |
+
cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "</" + tagName + ">",
|
65 |
+
{head: curPos, anchor: curPos});
|
66 |
+
if (doIndent) {
|
67 |
+
cm.indentLine(pos.line + 1);
|
68 |
+
cm.indentLine(pos.line + 2);
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
function autoCloseSlash(cm) {
|
73 |
+
var pos = cm.getCursor(), tok = cm.getTokenAt(pos);
|
74 |
+
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
|
75 |
+
if (tok.string.charAt(0) != "<" || inner.mode.name != "xml") return CodeMirror.Pass;
|
76 |
+
|
77 |
+
var tagName = state.context && state.context.tagName;
|
78 |
+
if (tagName) cm.replaceSelection("/" + tagName + ">", "end");
|
79 |
+
}
|
80 |
+
|
81 |
+
function indexOf(collection, elt) {
|
82 |
+
if (collection.indexOf) return collection.indexOf(elt);
|
83 |
+
for (var i = 0, e = collection.length; i < e; ++i)
|
84 |
+
if (collection[i] == elt) return i;
|
85 |
+
return -1;
|
86 |
+
}
|
87 |
+
})();
|
assets/lib/codemirror/addon/edit/continuelist.js
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
'use strict';
|
3 |
+
|
4 |
+
var listRE = /^(\s*)([*+-]|(\d+)\.)(\s*)/,
|
5 |
+
unorderedBullets = '*+-';
|
6 |
+
|
7 |
+
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
|
8 |
+
var pos = cm.getCursor(),
|
9 |
+
inList = cm.getStateAfter(pos.line).list,
|
10 |
+
match;
|
11 |
+
|
12 |
+
if (!inList || !(match = cm.getLine(pos.line).match(listRE))) {
|
13 |
+
cm.execCommand('newlineAndIndent');
|
14 |
+
return;
|
15 |
+
}
|
16 |
+
|
17 |
+
var indent = match[1], after = match[4];
|
18 |
+
var bullet = unorderedBullets.indexOf(match[2]) >= 0
|
19 |
+
? match[2]
|
20 |
+
: (parseInt(match[3], 10) + 1) + '.';
|
21 |
+
|
22 |
+
cm.replaceSelection('\n' + indent + bullet + after, 'end');
|
23 |
+
};
|
24 |
+
|
25 |
+
}());
|
assets/lib/codemirror/addon/edit/matchbrackets.js
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
|
3 |
+
(document.documentMode == null || document.documentMode < 8);
|
4 |
+
|
5 |
+
var Pos = CodeMirror.Pos;
|
6 |
+
|
7 |
+
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
|
8 |
+
function findMatchingBracket(cm, where, strict) {
|
9 |
+
var state = cm.state.matchBrackets;
|
10 |
+
var maxScanLen = (state && state.maxScanLineLength) || 10000;
|
11 |
+
|
12 |
+
var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
|
13 |
+
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
|
14 |
+
if (!match) return null;
|
15 |
+
var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
|
16 |
+
if (strict && forward != (pos == cur.ch)) return null;
|
17 |
+
var style = cm.getTokenTypeAt(Pos(cur.line, pos + 1));
|
18 |
+
|
19 |
+
var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
|
20 |
+
function scan(line, lineNo, start) {
|
21 |
+
if (!line.text) return;
|
22 |
+
var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
|
23 |
+
if (line.text.length > maxScanLen) return null;
|
24 |
+
if (start != null) pos = start + d;
|
25 |
+
for (; pos != end; pos += d) {
|
26 |
+
var ch = line.text.charAt(pos);
|
27 |
+
if (re.test(ch) && cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style) {
|
28 |
+
var match = matching[ch];
|
29 |
+
if (match.charAt(1) == ">" == forward) stack.push(ch);
|
30 |
+
else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
|
31 |
+
else if (!stack.length) return {pos: pos, match: true};
|
32 |
+
}
|
33 |
+
}
|
34 |
+
}
|
35 |
+
for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
|
36 |
+
if (i == cur.line) found = scan(line, i, pos);
|
37 |
+
else found = scan(cm.getLineHandle(i), i);
|
38 |
+
if (found) break;
|
39 |
+
}
|
40 |
+
return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos),
|
41 |
+
match: found && found.match, forward: forward};
|
42 |
+
}
|
43 |
+
|
44 |
+
function matchBrackets(cm, autoclear) {
|
45 |
+
// Disable brace matching in long lines, since it'll cause hugely slow updates
|
46 |
+
var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
|
47 |
+
var found = findMatchingBracket(cm);
|
48 |
+
if (!found || cm.getLine(found.from.line).length > maxHighlightLen ||
|
49 |
+
found.to && cm.getLine(found.to.line).length > maxHighlightLen)
|
50 |
+
return;
|
51 |
+
|
52 |
+
var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
53 |
+
var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style});
|
54 |
+
var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style});
|
55 |
+
// Kludge to work around the IE bug from issue #1193, where text
|
56 |
+
// input stops going to the textare whever this fires.
|
57 |
+
if (ie_lt8 && cm.state.focused) cm.display.input.focus();
|
58 |
+
var clear = function() {
|
59 |
+
cm.operation(function() { one.clear(); two && two.clear(); });
|
60 |
+
};
|
61 |
+
if (autoclear) setTimeout(clear, 800);
|
62 |
+
else return clear;
|
63 |
+
}
|
64 |
+
|
65 |
+
var currentlyHighlighted = null;
|
66 |
+
function doMatchBrackets(cm) {
|
67 |
+
cm.operation(function() {
|
68 |
+
if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
|
69 |
+
if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
|
70 |
+
});
|
71 |
+
}
|
72 |
+
|
73 |
+
CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
|
74 |
+
if (old && old != CodeMirror.Init)
|
75 |
+
cm.off("cursorActivity", doMatchBrackets);
|
76 |
+
if (val) {
|
77 |
+
cm.state.matchBrackets = typeof val == "object" ? val : {};
|
78 |
+
cm.on("cursorActivity", doMatchBrackets);
|
79 |
+
}
|
80 |
+
});
|
81 |
+
|
82 |
+
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
|
83 |
+
CodeMirror.defineExtension("findMatchingBracket", function(pos, strict){
|
84 |
+
return findMatchingBracket(this, pos, strict);
|
85 |
+
});
|
86 |
+
})();
|
assets/lib/codemirror/addon/edit/matchtags.js
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
|
5 |
+
if (old && old != CodeMirror.Init) {
|
6 |
+
cm.off("cursorActivity", doMatchTags);
|
7 |
+
cm.off("viewportChange", maybeUpdateMatch);
|
8 |
+
clear(cm);
|
9 |
+
}
|
10 |
+
if (val) {
|
11 |
+
cm.state.matchBothTags = typeof val == "object" && val.bothTags;
|
12 |
+
cm.on("cursorActivity", doMatchTags);
|
13 |
+
cm.on("viewportChange", maybeUpdateMatch);
|
14 |
+
doMatchTags(cm);
|
15 |
+
}
|
16 |
+
});
|
17 |
+
|
18 |
+
function clear(cm) {
|
19 |
+
if (cm.state.tagHit) cm.state.tagHit.clear();
|
20 |
+
if (cm.state.tagOther) cm.state.tagOther.clear();
|
21 |
+
cm.state.tagHit = cm.state.tagOther = null;
|
22 |
+
}
|
23 |
+
|
24 |
+
function doMatchTags(cm) {
|
25 |
+
cm.state.failedTagMatch = false;
|
26 |
+
cm.operation(function() {
|
27 |
+
clear(cm);
|
28 |
+
if (cm.somethingSelected()) return;
|
29 |
+
var cur = cm.getCursor(), range = cm.getViewport();
|
30 |
+
range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
|
31 |
+
var match = CodeMirror.findMatchingTag(cm, cur, range);
|
32 |
+
if (!match) return;
|
33 |
+
if (cm.state.matchBothTags) {
|
34 |
+
var hit = match.at == "open" ? match.open : match.close;
|
35 |
+
if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
|
36 |
+
}
|
37 |
+
var other = match.at == "close" ? match.open : match.close;
|
38 |
+
if (other)
|
39 |
+
cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
|
40 |
+
else
|
41 |
+
cm.state.failedTagMatch = true;
|
42 |
+
});
|
43 |
+
}
|
44 |
+
|
45 |
+
function maybeUpdateMatch(cm) {
|
46 |
+
if (cm.state.failedTagMatch) doMatchTags(cm);
|
47 |
+
}
|
48 |
+
|
49 |
+
CodeMirror.commands.toMatchingTag = function(cm) {
|
50 |
+
var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
|
51 |
+
if (found) {
|
52 |
+
var other = found.at == "close" ? found.open : found.close;
|
53 |
+
if (other) cm.setSelection(other.to, other.from);
|
54 |
+
}
|
55 |
+
};
|
56 |
+
})();
|
assets/lib/codemirror/addon/edit/trailingspace.js
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
|
2 |
+
if (prev == CodeMirror.Init) prev = false;
|
3 |
+
if (prev && !val)
|
4 |
+
cm.removeOverlay("trailingspace");
|
5 |
+
else if (!prev && val)
|
6 |
+
cm.addOverlay({
|
7 |
+
token: function(stream) {
|
8 |
+
for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
|
9 |
+
if (i > stream.pos) { stream.pos = i; return null; }
|
10 |
+
stream.pos = l;
|
11 |
+
return "trailingspace";
|
12 |
+
},
|
13 |
+
name: "trailingspace"
|
14 |
+
});
|
15 |
+
});
|
assets/lib/codemirror/addon/fold/brace-fold.js
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.registerHelper("fold", "brace", function(cm, start) {
|
2 |
+
var line = start.line, lineText = cm.getLine(line);
|
3 |
+
var startCh, tokenType;
|
4 |
+
|
5 |
+
function findOpening(openCh) {
|
6 |
+
for (var at = start.ch, pass = 0;;) {
|
7 |
+
var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1);
|
8 |
+
if (found == -1) {
|
9 |
+
if (pass == 1) break;
|
10 |
+
pass = 1;
|
11 |
+
at = lineText.length;
|
12 |
+
continue;
|
13 |
+
}
|
14 |
+
if (pass == 1 && found < start.ch) break;
|
15 |
+
tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1));
|
16 |
+
if (!/^(comment|string)/.test(tokenType)) return found + 1;
|
17 |
+
at = found - 1;
|
18 |
+
}
|
19 |
+
}
|
20 |
+
|
21 |
+
var startToken = "{", endToken = "}", startCh = findOpening("{");
|
22 |
+
if (startCh == null) {
|
23 |
+
startToken = "[", endToken = "]";
|
24 |
+
startCh = findOpening("[");
|
25 |
+
}
|
26 |
+
|
27 |
+
if (startCh == null) return;
|
28 |
+
var count = 1, lastLine = cm.lastLine(), end, endCh;
|
29 |
+
outer: for (var i = line; i <= lastLine; ++i) {
|
30 |
+
var text = cm.getLine(i), pos = i == line ? startCh : 0;
|
31 |
+
for (;;) {
|
32 |
+
var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
|
33 |
+
if (nextOpen < 0) nextOpen = text.length;
|
34 |
+
if (nextClose < 0) nextClose = text.length;
|
35 |
+
pos = Math.min(nextOpen, nextClose);
|
36 |
+
if (pos == text.length) break;
|
37 |
+
if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) {
|
38 |
+
if (pos == nextOpen) ++count;
|
39 |
+
else if (!--count) { end = i; endCh = pos; break outer; }
|
40 |
+
}
|
41 |
+
++pos;
|
42 |
+
}
|
43 |
+
}
|
44 |
+
if (end == null || line == end && endCh == startCh) return;
|
45 |
+
return {from: CodeMirror.Pos(line, startCh),
|
46 |
+
to: CodeMirror.Pos(end, endCh)};
|
47 |
+
});
|
48 |
+
CodeMirror.braceRangeFinder = CodeMirror.fold.brace; // deprecated
|
49 |
+
|
50 |
+
CodeMirror.registerHelper("fold", "import", function(cm, start) {
|
51 |
+
function hasImport(line) {
|
52 |
+
if (line < cm.firstLine() || line > cm.lastLine()) return null;
|
53 |
+
var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
|
54 |
+
if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
|
55 |
+
if (start.type != "keyword" || start.string != "import") return null;
|
56 |
+
// Now find closing semicolon, return its position
|
57 |
+
for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
|
58 |
+
var text = cm.getLine(i), semi = text.indexOf(";");
|
59 |
+
if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
var start = start.line, has = hasImport(start), prev;
|
64 |
+
if (!has || hasImport(start - 1) || ((prev = hasImport(start - 2)) && prev.end.line == start - 1))
|
65 |
+
return null;
|
66 |
+
for (var end = has.end;;) {
|
67 |
+
var next = hasImport(end.line + 1);
|
68 |
+
if (next == null) break;
|
69 |
+
end = next.end;
|
70 |
+
}
|
71 |
+
return {from: cm.clipPos(CodeMirror.Pos(start, has.startCh + 1)), to: end};
|
72 |
+
});
|
73 |
+
CodeMirror.importRangeFinder = CodeMirror.fold["import"]; // deprecated
|
74 |
+
|
75 |
+
CodeMirror.registerHelper("fold", "include", function(cm, start) {
|
76 |
+
function hasInclude(line) {
|
77 |
+
if (line < cm.firstLine() || line > cm.lastLine()) return null;
|
78 |
+
var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
|
79 |
+
if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
|
80 |
+
if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
|
81 |
+
}
|
82 |
+
|
83 |
+
var start = start.line, has = hasInclude(start);
|
84 |
+
if (has == null || hasInclude(start - 1) != null) return null;
|
85 |
+
for (var end = start;;) {
|
86 |
+
var next = hasInclude(end + 1);
|
87 |
+
if (next == null) break;
|
88 |
+
++end;
|
89 |
+
}
|
90 |
+
return {from: CodeMirror.Pos(start, has + 1),
|
91 |
+
to: cm.clipPos(CodeMirror.Pos(end))};
|
92 |
+
});
|
93 |
+
CodeMirror.includeRangeFinder = CodeMirror.fold.include; // deprecated
|
assets/lib/codemirror/addon/fold/comment-fold.js
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.registerHelper("fold", "comment", function(cm, start) {
|
2 |
+
var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;
|
3 |
+
if (!startToken || !endToken) return;
|
4 |
+
var line = start.line, lineText = cm.getLine(line);
|
5 |
+
|
6 |
+
var startCh;
|
7 |
+
for (var at = start.ch, pass = 0;;) {
|
8 |
+
var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1);
|
9 |
+
if (found == -1) {
|
10 |
+
if (pass == 1) return;
|
11 |
+
pass = 1;
|
12 |
+
at = lineText.length;
|
13 |
+
continue;
|
14 |
+
}
|
15 |
+
if (pass == 1 && found < start.ch) return;
|
16 |
+
if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)))) {
|
17 |
+
startCh = found + startToken.length;
|
18 |
+
break;
|
19 |
+
}
|
20 |
+
at = found - 1;
|
21 |
+
}
|
22 |
+
|
23 |
+
var depth = 1, lastLine = cm.lastLine(), end, endCh;
|
24 |
+
outer: for (var i = line; i <= lastLine; ++i) {
|
25 |
+
var text = cm.getLine(i), pos = i == line ? startCh : 0;
|
26 |
+
for (;;) {
|
27 |
+
var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
|
28 |
+
if (nextOpen < 0) nextOpen = text.length;
|
29 |
+
if (nextClose < 0) nextClose = text.length;
|
30 |
+
pos = Math.min(nextOpen, nextClose);
|
31 |
+
if (pos == text.length) break;
|
32 |
+
if (pos == nextOpen) ++depth;
|
33 |
+
else if (!--depth) { end = i; endCh = pos; break outer; }
|
34 |
+
++pos;
|
35 |
+
}
|
36 |
+
}
|
37 |
+
if (end == null || line == end && endCh == startCh) return;
|
38 |
+
return {from: CodeMirror.Pos(line, startCh),
|
39 |
+
to: CodeMirror.Pos(end, endCh)};
|
40 |
+
});
|
assets/lib/codemirror/addon/fold/foldcode.js
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
function doFold(cm, pos, options) {
|
5 |
+
var finder = options && (options.call ? options : options.rangeFinder);
|
6 |
+
if (!finder) finder = cm.getHelper(pos, "fold");
|
7 |
+
if (!finder) return;
|
8 |
+
if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
|
9 |
+
var minSize = options && options.minFoldSize || 0;
|
10 |
+
|
11 |
+
function getRange(allowFolded) {
|
12 |
+
var range = finder(cm, pos);
|
13 |
+
if (!range || range.to.line - range.from.line < minSize) return null;
|
14 |
+
var marks = cm.findMarksAt(range.from);
|
15 |
+
for (var i = 0; i < marks.length; ++i) {
|
16 |
+
if (marks[i].__isFold) {
|
17 |
+
if (!allowFolded) return null;
|
18 |
+
range.cleared = true;
|
19 |
+
marks[i].clear();
|
20 |
+
}
|
21 |
+
}
|
22 |
+
return range;
|
23 |
+
}
|
24 |
+
|
25 |
+
var range = getRange(true);
|
26 |
+
if (options && options.scanUp) while (!range && pos.line > cm.firstLine()) {
|
27 |
+
pos = CodeMirror.Pos(pos.line - 1, 0);
|
28 |
+
range = getRange(false);
|
29 |
+
}
|
30 |
+
if (!range || range.cleared) return;
|
31 |
+
|
32 |
+
var myWidget = makeWidget(options);
|
33 |
+
CodeMirror.on(myWidget, "mousedown", function() { myRange.clear(); });
|
34 |
+
var myRange = cm.markText(range.from, range.to, {
|
35 |
+
replacedWith: myWidget,
|
36 |
+
clearOnEnter: true,
|
37 |
+
__isFold: true
|
38 |
+
});
|
39 |
+
myRange.on("clear", function(from, to) {
|
40 |
+
CodeMirror.signal(cm, "unfold", cm, from, to);
|
41 |
+
});
|
42 |
+
CodeMirror.signal(cm, "fold", cm, range.from, range.to);
|
43 |
+
}
|
44 |
+
|
45 |
+
function makeWidget(options) {
|
46 |
+
var widget = (options && options.widget) || "\u2194";
|
47 |
+
if (typeof widget == "string") {
|
48 |
+
var text = document.createTextNode(widget);
|
49 |
+
widget = document.createElement("span");
|
50 |
+
widget.appendChild(text);
|
51 |
+
widget.className = "CodeMirror-foldmarker";
|
52 |
+
}
|
53 |
+
return widget;
|
54 |
+
}
|
55 |
+
|
56 |
+
// Clumsy backwards-compatible interface
|
57 |
+
CodeMirror.newFoldFunction = function(rangeFinder, widget) {
|
58 |
+
return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
|
59 |
+
};
|
60 |
+
|
61 |
+
// New-style interface
|
62 |
+
CodeMirror.defineExtension("foldCode", function(pos, options) { doFold(this, pos, options); });
|
63 |
+
|
64 |
+
CodeMirror.registerHelper("fold", "combine", function() {
|
65 |
+
var funcs = Array.prototype.slice.call(arguments, 0);
|
66 |
+
return function(cm, start) {
|
67 |
+
for (var i = 0; i < funcs.length; ++i) {
|
68 |
+
var found = funcs[i](cm, start);
|
69 |
+
if (found) return found;
|
70 |
+
}
|
71 |
+
};
|
72 |
+
});
|
73 |
+
})();
|
assets/lib/codemirror/addon/fold/foldgutter.js
ADDED
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
|
5 |
+
if (old && old != CodeMirror.Init) {
|
6 |
+
cm.clearGutter(cm.state.foldGutter.options.gutter);
|
7 |
+
cm.state.foldGutter = null;
|
8 |
+
cm.off("gutterClick", onGutterClick);
|
9 |
+
cm.off("change", onChange);
|
10 |
+
cm.off("viewportChange", onViewportChange);
|
11 |
+
cm.off("fold", onFold);
|
12 |
+
cm.off("unfold", onFold);
|
13 |
+
}
|
14 |
+
if (val) {
|
15 |
+
cm.state.foldGutter = new State(parseOptions(val));
|
16 |
+
updateInViewport(cm);
|
17 |
+
cm.on("gutterClick", onGutterClick);
|
18 |
+
cm.on("change", onChange);
|
19 |
+
cm.on("viewportChange", onViewportChange);
|
20 |
+
cm.on("fold", onFold);
|
21 |
+
cm.on("unfold", onFold);
|
22 |
+
}
|
23 |
+
});
|
24 |
+
|
25 |
+
var Pos = CodeMirror.Pos;
|
26 |
+
|
27 |
+
function State(options) {
|
28 |
+
this.options = options;
|
29 |
+
this.from = this.to = 0;
|
30 |
+
}
|
31 |
+
|
32 |
+
function parseOptions(opts) {
|
33 |
+
if (opts === true) opts = {};
|
34 |
+
if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
|
35 |
+
if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
|
36 |
+
if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
|
37 |
+
return opts;
|
38 |
+
}
|
39 |
+
|
40 |
+
function isFolded(cm, line) {
|
41 |
+
var marks = cm.findMarksAt(Pos(line));
|
42 |
+
for (var i = 0; i < marks.length; ++i)
|
43 |
+
if (marks[i].__isFold && marks[i].find().from.line == line) return true;
|
44 |
+
}
|
45 |
+
|
46 |
+
function marker(spec) {
|
47 |
+
if (typeof spec == "string") {
|
48 |
+
var elt = document.createElement("div");
|
49 |
+
elt.className = spec;
|
50 |
+
return elt;
|
51 |
+
} else {
|
52 |
+
return spec.cloneNode(true);
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
function updateFoldInfo(cm, from, to) {
|
57 |
+
var opts = cm.state.foldGutter.options, cur = from;
|
58 |
+
cm.eachLine(from, to, function(line) {
|
59 |
+
var mark = null;
|
60 |
+
if (isFolded(cm, cur)) {
|
61 |
+
mark = marker(opts.indicatorFolded);
|
62 |
+
} else {
|
63 |
+
var pos = Pos(cur, 0), func = opts.rangeFinder || cm.getHelper(pos, "fold");
|
64 |
+
var range = func && func(cm, pos);
|
65 |
+
if (range && range.from.line + 1 < range.to.line)
|
66 |
+
mark = marker(opts.indicatorOpen);
|
67 |
+
}
|
68 |
+
cm.setGutterMarker(line, opts.gutter, mark);
|
69 |
+
++cur;
|
70 |
+
});
|
71 |
+
}
|
72 |
+
|
73 |
+
function updateInViewport(cm) {
|
74 |
+
var vp = cm.getViewport(), state = cm.state.foldGutter;
|
75 |
+
if (!state) return;
|
76 |
+
cm.operation(function() {
|
77 |
+
updateFoldInfo(cm, vp.from, vp.to);
|
78 |
+
});
|
79 |
+
state.from = vp.from; state.to = vp.to;
|
80 |
+
}
|
81 |
+
|
82 |
+
function onGutterClick(cm, line, gutter) {
|
83 |
+
var opts = cm.state.foldGutter.options;
|
84 |
+
if (gutter != opts.gutter) return;
|
85 |
+
cm.foldCode(Pos(line, 0), opts.rangeFinder);
|
86 |
+
}
|
87 |
+
|
88 |
+
function onChange(cm) {
|
89 |
+
var state = cm.state.foldGutter;
|
90 |
+
state.from = state.to = 0;
|
91 |
+
clearTimeout(state.changeUpdate);
|
92 |
+
state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, 600);
|
93 |
+
}
|
94 |
+
|
95 |
+
function onViewportChange(cm) {
|
96 |
+
var state = cm.state.foldGutter;
|
97 |
+
clearTimeout(state.changeUpdate);
|
98 |
+
state.changeUpdate = setTimeout(function() {
|
99 |
+
var vp = cm.getViewport();
|
100 |
+
if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
|
101 |
+
updateInViewport(cm);
|
102 |
+
} else {
|
103 |
+
cm.operation(function() {
|
104 |
+
if (vp.from < state.from) {
|
105 |
+
updateFoldInfo(cm, vp.from, state.from);
|
106 |
+
state.from = vp.from;
|
107 |
+
}
|
108 |
+
if (vp.to > state.to) {
|
109 |
+
updateFoldInfo(cm, state.to, vp.to);
|
110 |
+
state.to = vp.to;
|
111 |
+
}
|
112 |
+
});
|
113 |
+
}
|
114 |
+
}, 400);
|
115 |
+
}
|
116 |
+
|
117 |
+
function onFold(cm, from) {
|
118 |
+
var state = cm.state.foldGutter, line = from.line;
|
119 |
+
if (line >= state.from && line < state.to)
|
120 |
+
updateFoldInfo(cm, line, line + 1);
|
121 |
+
}
|
122 |
+
})();
|
assets/lib/codemirror/addon/fold/indent-fold.js
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.registerHelper("fold", "indent", function(cm, start) {
|
2 |
+
var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line);
|
3 |
+
var myIndent = CodeMirror.countColumn(firstLine, null, tabSize);
|
4 |
+
for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) {
|
5 |
+
var curLine = cm.getLine(i);
|
6 |
+
if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent &&
|
7 |
+
CodeMirror.countColumn(cm.getLine(i-1), null, tabSize) > myIndent)
|
8 |
+
return {from: CodeMirror.Pos(start.line, firstLine.length),
|
9 |
+
to: CodeMirror.Pos(i, curLine.length)};
|
10 |
+
}
|
11 |
+
});
|
12 |
+
CodeMirror.indentRangeFinder = CodeMirror.fold.indent; // deprecated
|
assets/lib/codemirror/addon/fold/xml-fold.js
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
var Pos = CodeMirror.Pos;
|
5 |
+
function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
|
6 |
+
|
7 |
+
var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
|
8 |
+
var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
|
9 |
+
var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
|
10 |
+
|
11 |
+
function Iter(cm, line, ch, range) {
|
12 |
+
this.line = line; this.ch = ch;
|
13 |
+
this.cm = cm; this.text = cm.getLine(line);
|
14 |
+
this.min = range ? range.from : cm.firstLine();
|
15 |
+
this.max = range ? range.to - 1 : cm.lastLine();
|
16 |
+
}
|
17 |
+
|
18 |
+
function tagAt(iter, ch) {
|
19 |
+
var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
|
20 |
+
return type && /\btag\b/.test(type);
|
21 |
+
}
|
22 |
+
|
23 |
+
function nextLine(iter) {
|
24 |
+
if (iter.line >= iter.max) return;
|
25 |
+
iter.ch = 0;
|
26 |
+
iter.text = iter.cm.getLine(++iter.line);
|
27 |
+
return true;
|
28 |
+
}
|
29 |
+
function prevLine(iter) {
|
30 |
+
if (iter.line <= iter.min) return;
|
31 |
+
iter.text = iter.cm.getLine(--iter.line);
|
32 |
+
iter.ch = iter.text.length;
|
33 |
+
return true;
|
34 |
+
}
|
35 |
+
|
36 |
+
function toTagEnd(iter) {
|
37 |
+
for (;;) {
|
38 |
+
var gt = iter.text.indexOf(">", iter.ch);
|
39 |
+
if (gt == -1) { if (nextLine(iter)) continue; else return; }
|
40 |
+
if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
|
41 |
+
var lastSlash = iter.text.lastIndexOf("/", gt);
|
42 |
+
var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
|
43 |
+
iter.ch = gt + 1;
|
44 |
+
return selfClose ? "selfClose" : "regular";
|
45 |
+
}
|
46 |
+
}
|
47 |
+
function toTagStart(iter) {
|
48 |
+
for (;;) {
|
49 |
+
var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
|
50 |
+
if (lt == -1) { if (prevLine(iter)) continue; else return; }
|
51 |
+
if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
|
52 |
+
xmlTagStart.lastIndex = lt;
|
53 |
+
iter.ch = lt;
|
54 |
+
var match = xmlTagStart.exec(iter.text);
|
55 |
+
if (match && match.index == lt) return match;
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
function toNextTag(iter) {
|
60 |
+
for (;;) {
|
61 |
+
xmlTagStart.lastIndex = iter.ch;
|
62 |
+
var found = xmlTagStart.exec(iter.text);
|
63 |
+
if (!found) { if (nextLine(iter)) continue; else return; }
|
64 |
+
if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
|
65 |
+
iter.ch = found.index + found[0].length;
|
66 |
+
return found;
|
67 |
+
}
|
68 |
+
}
|
69 |
+
function toPrevTag(iter) {
|
70 |
+
for (;;) {
|
71 |
+
var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
|
72 |
+
if (gt == -1) { if (prevLine(iter)) continue; else return; }
|
73 |
+
if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
|
74 |
+
var lastSlash = iter.text.lastIndexOf("/", gt);
|
75 |
+
var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
|
76 |
+
iter.ch = gt + 1;
|
77 |
+
return selfClose ? "selfClose" : "regular";
|
78 |
+
}
|
79 |
+
}
|
80 |
+
|
81 |
+
function findMatchingClose(iter, tag) {
|
82 |
+
var stack = [];
|
83 |
+
for (;;) {
|
84 |
+
var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
|
85 |
+
if (!next || !(end = toTagEnd(iter))) return;
|
86 |
+
if (end == "selfClose") continue;
|
87 |
+
if (next[1]) { // closing tag
|
88 |
+
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
|
89 |
+
stack.length = i;
|
90 |
+
break;
|
91 |
+
}
|
92 |
+
if (i < 0 && (!tag || tag == next[2])) return {
|
93 |
+
tag: next[2],
|
94 |
+
from: Pos(startLine, startCh),
|
95 |
+
to: Pos(iter.line, iter.ch)
|
96 |
+
};
|
97 |
+
} else { // opening tag
|
98 |
+
stack.push(next[2]);
|
99 |
+
}
|
100 |
+
}
|
101 |
+
}
|
102 |
+
function findMatchingOpen(iter, tag) {
|
103 |
+
var stack = [];
|
104 |
+
for (;;) {
|
105 |
+
var prev = toPrevTag(iter);
|
106 |
+
if (!prev) return;
|
107 |
+
if (prev == "selfClose") { toTagStart(iter); continue; }
|
108 |
+
var endLine = iter.line, endCh = iter.ch;
|
109 |
+
var start = toTagStart(iter);
|
110 |
+
if (!start) return;
|
111 |
+
if (start[1]) { // closing tag
|
112 |
+
stack.push(start[2]);
|
113 |
+
} else { // opening tag
|
114 |
+
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
|
115 |
+
stack.length = i;
|
116 |
+
break;
|
117 |
+
}
|
118 |
+
if (i < 0 && (!tag || tag == start[2])) return {
|
119 |
+
tag: start[2],
|
120 |
+
from: Pos(iter.line, iter.ch),
|
121 |
+
to: Pos(endLine, endCh)
|
122 |
+
};
|
123 |
+
}
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
CodeMirror.registerHelper("fold", "xml", function(cm, start) {
|
128 |
+
var iter = new Iter(cm, start.line, 0);
|
129 |
+
for (;;) {
|
130 |
+
var openTag = toNextTag(iter), end;
|
131 |
+
if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
|
132 |
+
if (!openTag[1] && end != "selfClose") {
|
133 |
+
var start = Pos(iter.line, iter.ch);
|
134 |
+
var close = findMatchingClose(iter, openTag[2]);
|
135 |
+
return close && {from: start, to: close.from};
|
136 |
+
}
|
137 |
+
}
|
138 |
+
});
|
139 |
+
CodeMirror.tagRangeFinder = CodeMirror.fold.xml; // deprecated
|
140 |
+
|
141 |
+
CodeMirror.findMatchingTag = function(cm, pos, range) {
|
142 |
+
var iter = new Iter(cm, pos.line, pos.ch, range);
|
143 |
+
if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
|
144 |
+
var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
|
145 |
+
var start = end && toTagStart(iter);
|
146 |
+
if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return;
|
147 |
+
var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
|
148 |
+
|
149 |
+
if (start[1]) { // closing tag
|
150 |
+
return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
|
151 |
+
} else { // opening tag
|
152 |
+
iter = new Iter(cm, to.line, to.ch, range);
|
153 |
+
return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
|
154 |
+
}
|
155 |
+
};
|
156 |
+
|
157 |
+
CodeMirror.findEnclosingTag = function(cm, pos, range) {
|
158 |
+
var iter = new Iter(cm, pos.line, pos.ch, range);
|
159 |
+
for (;;) {
|
160 |
+
var open = findMatchingOpen(iter);
|
161 |
+
if (!open) break;
|
162 |
+
var forward = new Iter(cm, pos.line, pos.ch, range);
|
163 |
+
var close = findMatchingClose(forward, open.tag);
|
164 |
+
if (close) return {open: open, close: close};
|
165 |
+
}
|
166 |
+
};
|
167 |
+
})();
|
assets/lib/codemirror/addon/hint/anyword-hint.js
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
var WORD = /[\w$]+/, RANGE = 500;
|
5 |
+
|
6 |
+
CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
|
7 |
+
var word = options && options.word || WORD;
|
8 |
+
var range = options && options.range || RANGE;
|
9 |
+
var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
|
10 |
+
var start = cur.ch, end = start;
|
11 |
+
while (end < curLine.length && word.test(curLine.charAt(end))) ++end;
|
12 |
+
while (start && word.test(curLine.charAt(start - 1))) --start;
|
13 |
+
var curWord = start != end && curLine.slice(start, end);
|
14 |
+
|
15 |
+
var list = [], seen = {};
|
16 |
+
function scan(dir) {
|
17 |
+
var line = cur.line, end = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
|
18 |
+
for (; line != end; line += dir) {
|
19 |
+
var text = editor.getLine(line), m;
|
20 |
+
var re = new RegExp(word.source, "g");
|
21 |
+
while (m = re.exec(text)) {
|
22 |
+
if (line == cur.line && m[0] === curWord) continue;
|
23 |
+
if ((!curWord || m[0].indexOf(curWord) == 0) && !seen.hasOwnProperty(m[0])) {
|
24 |
+
seen[m[0]] = true;
|
25 |
+
list.push(m[0]);
|
26 |
+
}
|
27 |
+
}
|
28 |
+
}
|
29 |
+
}
|
30 |
+
scan(-1);
|
31 |
+
scan(1);
|
32 |
+
return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
|
33 |
+
});
|
34 |
+
})();
|
assets/lib/codemirror/addon/hint/css-hint.js
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function () {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
function getHints(cm) {
|
5 |
+
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
|
6 |
+
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
|
7 |
+
if (inner.mode.name != "css") return;
|
8 |
+
|
9 |
+
// If it's not a 'word-style' token, ignore the token.
|
10 |
+
if (!/^[\w$_-]*$/.test(token.string)) {
|
11 |
+
token = {
|
12 |
+
start: cur.ch, end: cur.ch, string: "", state: token.state,
|
13 |
+
type: null
|
14 |
+
};
|
15 |
+
var stack = token.state.stack;
|
16 |
+
var lastToken = stack && stack.length > 0 ? stack[stack.length - 1] : "";
|
17 |
+
if (token.string == ":" || lastToken.indexOf("property") == 0)
|
18 |
+
token.type = "variable";
|
19 |
+
else if (token.string == "{" || lastToken.indexOf("rule") == 0)
|
20 |
+
token.type = "property";
|
21 |
+
}
|
22 |
+
|
23 |
+
if (!token.type)
|
24 |
+
return;
|
25 |
+
|
26 |
+
var spec = CodeMirror.resolveMode("text/css");
|
27 |
+
var keywords = null;
|
28 |
+
if (token.type.indexOf("property") == 0)
|
29 |
+
keywords = spec.propertyKeywords;
|
30 |
+
else if (token.type.indexOf("variable") == 0)
|
31 |
+
keywords = spec.valueKeywords;
|
32 |
+
|
33 |
+
if (!keywords)
|
34 |
+
return;
|
35 |
+
|
36 |
+
var result = [];
|
37 |
+
for (var name in keywords) {
|
38 |
+
if (name.indexOf(token.string) == 0 /* > -1 */)
|
39 |
+
result.push(name);
|
40 |
+
}
|
41 |
+
|
42 |
+
return {
|
43 |
+
list: result,
|
44 |
+
from: CodeMirror.Pos(cur.line, token.start),
|
45 |
+
to: CodeMirror.Pos(cur.line, token.end)
|
46 |
+
};
|
47 |
+
}
|
48 |
+
|
49 |
+
CodeMirror.registerHelper("hint", "css", getHints);
|
50 |
+
})();
|
assets/lib/codemirror/addon/hint/html-hint.js
ADDED
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function () {
|
2 |
+
var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");
|
3 |
+
var targets = ["_blank", "_self", "_top", "_parent"];
|
4 |
+
var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
|
5 |
+
var methods = ["get", "post", "put", "delete"];
|
6 |
+
var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
|
7 |
+
var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech",
|
8 |
+
"3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait",
|
9 |
+
"orientation:landscape", "device-height: [X]", "device-width: [X]"];
|
10 |
+
var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags
|
11 |
+
|
12 |
+
var data = {
|
13 |
+
a: {
|
14 |
+
attrs: {
|
15 |
+
href: null, ping: null, type: null,
|
16 |
+
media: media,
|
17 |
+
target: targets,
|
18 |
+
hreflang: langs
|
19 |
+
}
|
20 |
+
},
|
21 |
+
abbr: s,
|
22 |
+
acronym: s,
|
23 |
+
address: s,
|
24 |
+
applet: s,
|
25 |
+
area: {
|
26 |
+
attrs: {
|
27 |
+
alt: null, coords: null, href: null, target: null, ping: null,
|
28 |
+
media: media, hreflang: langs, type: null,
|
29 |
+
shape: ["default", "rect", "circle", "poly"]
|
30 |
+
}
|
31 |
+
},
|
32 |
+
article: s,
|
33 |
+
aside: s,
|
34 |
+
audio: {
|
35 |
+
attrs: {
|
36 |
+
src: null, mediagroup: null,
|
37 |
+
crossorigin: ["anonymous", "use-credentials"],
|
38 |
+
preload: ["none", "metadata", "auto"],
|
39 |
+
autoplay: ["", "autoplay"],
|
40 |
+
loop: ["", "loop"],
|
41 |
+
controls: ["", "controls"]
|
42 |
+
}
|
43 |
+
},
|
44 |
+
b: s,
|
45 |
+
base: { attrs: { href: null, target: targets } },
|
46 |
+
basefont: s,
|
47 |
+
bdi: s,
|
48 |
+
bdo: s,
|
49 |
+
big: s,
|
50 |
+
blockquote: { attrs: { cite: null } },
|
51 |
+
body: s,
|
52 |
+
br: s,
|
53 |
+
button: {
|
54 |
+
attrs: {
|
55 |
+
form: null, formaction: null, name: null, value: null,
|
56 |
+
autofocus: ["", "autofocus"],
|
57 |
+
disabled: ["", "autofocus"],
|
58 |
+
formenctype: encs,
|
59 |
+
formmethod: methods,
|
60 |
+
formnovalidate: ["", "novalidate"],
|
61 |
+
formtarget: targets,
|
62 |
+
type: ["submit", "reset", "button"]
|
63 |
+
}
|
64 |
+
},
|
65 |
+
canvas: { attrs: { width: null, height: null } },
|
66 |
+
caption: s,
|
67 |
+
center: s,
|
68 |
+
cite: s,
|
69 |
+
code: s,
|
70 |
+
col: { attrs: { span: null } },
|
71 |
+
colgroup: { attrs: { span: null } },
|
72 |
+
command: {
|
73 |
+
attrs: {
|
74 |
+
type: ["command", "checkbox", "radio"],
|
75 |
+
label: null, icon: null, radiogroup: null, command: null, title: null,
|
76 |
+
disabled: ["", "disabled"],
|
77 |
+
checked: ["", "checked"]
|
78 |
+
}
|
79 |
+
},
|
80 |
+
data: { attrs: { value: null } },
|
81 |
+
datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } },
|
82 |
+
datalist: { attrs: { data: null } },
|
83 |
+
dd: s,
|
84 |
+
del: { attrs: { cite: null, datetime: null } },
|
85 |
+
details: { attrs: { open: ["", "open"] } },
|
86 |
+
dfn: s,
|
87 |
+
dir: s,
|
88 |
+
div: s,
|
89 |
+
dl: s,
|
90 |
+
dt: s,
|
91 |
+
em: s,
|
92 |
+
embed: { attrs: { src: null, type: null, width: null, height: null } },
|
93 |
+
eventsource: { attrs: { src: null } },
|
94 |
+
fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } },
|
95 |
+
figcaption: s,
|
96 |
+
figure: s,
|
97 |
+
font: s,
|
98 |
+
footer: s,
|
99 |
+
form: {
|
100 |
+
attrs: {
|
101 |
+
action: null, name: null,
|
102 |
+
"accept-charset": charsets,
|
103 |
+
autocomplete: ["on", "off"],
|
104 |
+
enctype: encs,
|
105 |
+
method: methods,
|
106 |
+
novalidate: ["", "novalidate"],
|
107 |
+
target: targets
|
108 |
+
}
|
109 |
+
},
|
110 |
+
frame: s,
|
111 |
+
frameset: s,
|
112 |
+
h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,
|
113 |
+
head: {
|
114 |
+
attrs: {},
|
115 |
+
children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
|
116 |
+
},
|
117 |
+
header: s,
|
118 |
+
hgroup: s,
|
119 |
+
hr: s,
|
120 |
+
html: {
|
121 |
+
attrs: { manifest: null },
|
122 |
+
children: ["head", "body"]
|
123 |
+
},
|
124 |
+
i: s,
|
125 |
+
iframe: {
|
126 |
+
attrs: {
|
127 |
+
src: null, srcdoc: null, name: null, width: null, height: null,
|
128 |
+
sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
|
129 |
+
seamless: ["", "seamless"]
|
130 |
+
}
|
131 |
+
},
|
132 |
+
img: {
|
133 |
+
attrs: {
|
134 |
+
alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
|
135 |
+
crossorigin: ["anonymous", "use-credentials"]
|
136 |
+
}
|
137 |
+
},
|
138 |
+
input: {
|
139 |
+
attrs: {
|
140 |
+
alt: null, dirname: null, form: null, formaction: null,
|
141 |
+
height: null, list: null, max: null, maxlength: null, min: null,
|
142 |
+
name: null, pattern: null, placeholder: null, size: null, src: null,
|
143 |
+
step: null, value: null, width: null,
|
144 |
+
accept: ["audio/*", "video/*", "image/*"],
|
145 |
+
autocomplete: ["on", "off"],
|
146 |
+
autofocus: ["", "autofocus"],
|
147 |
+
checked: ["", "checked"],
|
148 |
+
disabled: ["", "disabled"],
|
149 |
+
formenctype: encs,
|
150 |
+
formmethod: methods,
|
151 |
+
formnovalidate: ["", "novalidate"],
|
152 |
+
formtarget: targets,
|
153 |
+
multiple: ["", "multiple"],
|
154 |
+
readonly: ["", "readonly"],
|
155 |
+
required: ["", "required"],
|
156 |
+
type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
|
157 |
+
"week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
|
158 |
+
"file", "submit", "image", "reset", "button"]
|
159 |
+
}
|
160 |
+
},
|
161 |
+
ins: { attrs: { cite: null, datetime: null } },
|
162 |
+
kbd: s,
|
163 |
+
keygen: {
|
164 |
+
attrs: {
|
165 |
+
challenge: null, form: null, name: null,
|
166 |
+
autofocus: ["", "autofocus"],
|
167 |
+
disabled: ["", "disabled"],
|
168 |
+
keytype: ["RSA"]
|
169 |
+
}
|
170 |
+
},
|
171 |
+
label: { attrs: { "for": null, form: null } },
|
172 |
+
legend: s,
|
173 |
+
li: { attrs: { value: null } },
|
174 |
+
link: {
|
175 |
+
attrs: {
|
176 |
+
href: null, type: null,
|
177 |
+
hreflang: langs,
|
178 |
+
media: media,
|
179 |
+
sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
|
180 |
+
}
|
181 |
+
},
|
182 |
+
map: { attrs: { name: null } },
|
183 |
+
mark: s,
|
184 |
+
menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
|
185 |
+
meta: {
|
186 |
+
attrs: {
|
187 |
+
content: null,
|
188 |
+
charset: charsets,
|
189 |
+
name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
|
190 |
+
"http-equiv": ["content-language", "content-type", "default-style", "refresh"]
|
191 |
+
}
|
192 |
+
},
|
193 |
+
meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
|
194 |
+
nav: s,
|
195 |
+
noframes: s,
|
196 |
+
noscript: s,
|
197 |
+
object: {
|
198 |
+
attrs: {
|
199 |
+
data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
|
200 |
+
typemustmatch: ["", "typemustmatch"]
|
201 |
+
}
|
202 |
+
},
|
203 |
+
ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } },
|
204 |
+
optgroup: { attrs: { disabled: ["", "disabled"], label: null } },
|
205 |
+
option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } },
|
206 |
+
output: { attrs: { "for": null, form: null, name: null } },
|
207 |
+
p: s,
|
208 |
+
param: { attrs: { name: null, value: null } },
|
209 |
+
pre: s,
|
210 |
+
progress: { attrs: { value: null, max: null } },
|
211 |
+
q: { attrs: { cite: null } },
|
212 |
+
rp: s,
|
213 |
+
rt: s,
|
214 |
+
ruby: s,
|
215 |
+
s: s,
|
216 |
+
samp: s,
|
217 |
+
script: {
|
218 |
+
attrs: {
|
219 |
+
type: ["text/javascript"],
|
220 |
+
src: null,
|
221 |
+
async: ["", "async"],
|
222 |
+
defer: ["", "defer"],
|
223 |
+
charset: charsets
|
224 |
+
}
|
225 |
+
},
|
226 |
+
section: s,
|
227 |
+
select: {
|
228 |
+
attrs: {
|
229 |
+
form: null, name: null, size: null,
|
230 |
+
autofocus: ["", "autofocus"],
|
231 |
+
disabled: ["", "disabled"],
|
232 |
+
multiple: ["", "multiple"]
|
233 |
+
}
|
234 |
+
},
|
235 |
+
small: s,
|
236 |
+
source: { attrs: { src: null, type: null, media: null } },
|
237 |
+
span: s,
|
238 |
+
strike: s,
|
239 |
+
strong: s,
|
240 |
+
style: {
|
241 |
+
attrs: {
|
242 |
+
type: ["text/css"],
|
243 |
+
media: media,
|
244 |
+
scoped: null
|
245 |
+
}
|
246 |
+
},
|
247 |
+
sub: s,
|
248 |
+
summary: s,
|
249 |
+
sup: s,
|
250 |
+
table: s,
|
251 |
+
tbody: s,
|
252 |
+
td: { attrs: { colspan: null, rowspan: null, headers: null } },
|
253 |
+
textarea: {
|
254 |
+
attrs: {
|
255 |
+
dirname: null, form: null, maxlength: null, name: null, placeholder: null,
|
256 |
+
rows: null, cols: null,
|
257 |
+
autofocus: ["", "autofocus"],
|
258 |
+
disabled: ["", "disabled"],
|
259 |
+
readonly: ["", "readonly"],
|
260 |
+
required: ["", "required"],
|
261 |
+
wrap: ["soft", "hard"]
|
262 |
+
}
|
263 |
+
},
|
264 |
+
tfoot: s,
|
265 |
+
th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
|
266 |
+
thead: s,
|
267 |
+
time: { attrs: { datetime: null } },
|
268 |
+
title: s,
|
269 |
+
tr: s,
|
270 |
+
track: {
|
271 |
+
attrs: {
|
272 |
+
src: null, label: null, "default": null,
|
273 |
+
kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
|
274 |
+
srclang: langs
|
275 |
+
}
|
276 |
+
},
|
277 |
+
tt: s,
|
278 |
+
u: s,
|
279 |
+
ul: s,
|
280 |
+
"var": s,
|
281 |
+
video: {
|
282 |
+
attrs: {
|
283 |
+
src: null, poster: null, width: null, height: null,
|
284 |
+
crossorigin: ["anonymous", "use-credentials"],
|
285 |
+
preload: ["auto", "metadata", "none"],
|
286 |
+
autoplay: ["", "autoplay"],
|
287 |
+
mediagroup: ["movie"],
|
288 |
+
muted: ["", "muted"],
|
289 |
+
controls: ["", "controls"]
|
290 |
+
}
|
291 |
+
},
|
292 |
+
wbr: s
|
293 |
+
};
|
294 |
+
|
295 |
+
var globalAttrs = {
|
296 |
+
accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
|
297 |
+
"class": null,
|
298 |
+
contenteditable: ["true", "false"],
|
299 |
+
contextmenu: null,
|
300 |
+
dir: ["ltr", "rtl", "auto"],
|
301 |
+
draggable: ["true", "false", "auto"],
|
302 |
+
dropzone: ["copy", "move", "link", "string:", "file:"],
|
303 |
+
hidden: ["hidden"],
|
304 |
+
id: null,
|
305 |
+
inert: ["inert"],
|
306 |
+
itemid: null,
|
307 |
+
itemprop: null,
|
308 |
+
itemref: null,
|
309 |
+
itemscope: ["itemscope"],
|
310 |
+
itemtype: null,
|
311 |
+
lang: ["en", "es"],
|
312 |
+
spellcheck: ["true", "false"],
|
313 |
+
style: null,
|
314 |
+
tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
|
315 |
+
title: null,
|
316 |
+
translate: ["yes", "no"],
|
317 |
+
onclick: null,
|
318 |
+
rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"]
|
319 |
+
};
|
320 |
+
function populate(obj) {
|
321 |
+
for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))
|
322 |
+
obj.attrs[attr] = globalAttrs[attr];
|
323 |
+
}
|
324 |
+
|
325 |
+
populate(s);
|
326 |
+
for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)
|
327 |
+
populate(data[tag]);
|
328 |
+
|
329 |
+
CodeMirror.htmlSchema = data;
|
330 |
+
function htmlHint(cm, options) {
|
331 |
+
var local = {schemaInfo: data};
|
332 |
+
if (options) for (var opt in options) local[opt] = options[opt];
|
333 |
+
return CodeMirror.hint.xml(cm, local);
|
334 |
+
}
|
335 |
+
CodeMirror.htmlHint = htmlHint; // deprecated
|
336 |
+
CodeMirror.registerHelper("hint", "html", htmlHint);
|
337 |
+
})();
|
assets/lib/codemirror/addon/hint/javascript-hint.js
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function () {
|
2 |
+
var Pos = CodeMirror.Pos;
|
3 |
+
|
4 |
+
function forEach(arr, f) {
|
5 |
+
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
|
6 |
+
}
|
7 |
+
|
8 |
+
function arrayContains(arr, item) {
|
9 |
+
if (!Array.prototype.indexOf) {
|
10 |
+
var i = arr.length;
|
11 |
+
while (i--) {
|
12 |
+
if (arr[i] === item) {
|
13 |
+
return true;
|
14 |
+
}
|
15 |
+
}
|
16 |
+
return false;
|
17 |
+
}
|
18 |
+
return arr.indexOf(item) != -1;
|
19 |
+
}
|
20 |
+
|
21 |
+
function scriptHint(editor, keywords, getToken, options) {
|
22 |
+
// Find the token at the cursor
|
23 |
+
var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
|
24 |
+
token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;
|
25 |
+
|
26 |
+
// If it's not a 'word-style' token, ignore the token.
|
27 |
+
if (!/^[\w$_]*$/.test(token.string)) {
|
28 |
+
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
|
29 |
+
type: token.string == "." ? "property" : null};
|
30 |
+
}
|
31 |
+
// If it is a property, find out what it is a property of.
|
32 |
+
while (tprop.type == "property") {
|
33 |
+
tprop = getToken(editor, Pos(cur.line, tprop.start));
|
34 |
+
if (tprop.string != ".") return;
|
35 |
+
tprop = getToken(editor, Pos(cur.line, tprop.start));
|
36 |
+
if (tprop.string == ')') {
|
37 |
+
var level = 1;
|
38 |
+
do {
|
39 |
+
tprop = getToken(editor, Pos(cur.line, tprop.start));
|
40 |
+
switch (tprop.string) {
|
41 |
+
case ')': level++; break;
|
42 |
+
case '(': level--; break;
|
43 |
+
default: break;
|
44 |
+
}
|
45 |
+
} while (level > 0);
|
46 |
+
tprop = getToken(editor, Pos(cur.line, tprop.start));
|
47 |
+
if (tprop.type.indexOf("variable") === 0)
|
48 |
+
tprop.type = "function";
|
49 |
+
else return; // no clue
|
50 |
+
}
|
51 |
+
if (!context) var context = [];
|
52 |
+
context.push(tprop);
|
53 |
+
}
|
54 |
+
return {list: getCompletions(token, context, keywords, options),
|
55 |
+
from: Pos(cur.line, token.start),
|
56 |
+
to: Pos(cur.line, token.end)};
|
57 |
+
}
|
58 |
+
|
59 |
+
function javascriptHint(editor, options) {
|
60 |
+
return scriptHint(editor, javascriptKeywords,
|
61 |
+
function (e, cur) {return e.getTokenAt(cur);},
|
62 |
+
options);
|
63 |
+
};
|
64 |
+
CodeMirror.javascriptHint = javascriptHint; // deprecated
|
65 |
+
CodeMirror.registerHelper("hint", "javascript", javascriptHint);
|
66 |
+
|
67 |
+
function getCoffeeScriptToken(editor, cur) {
|
68 |
+
// This getToken, it is for coffeescript, imitates the behavior of
|
69 |
+
// getTokenAt method in javascript.js, that is, returning "property"
|
70 |
+
// type and treat "." as indepenent token.
|
71 |
+
var token = editor.getTokenAt(cur);
|
72 |
+
if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
|
73 |
+
token.end = token.start;
|
74 |
+
token.string = '.';
|
75 |
+
token.type = "property";
|
76 |
+
}
|
77 |
+
else if (/^\.[\w$_]*$/.test(token.string)) {
|
78 |
+
token.type = "property";
|
79 |
+
token.start++;
|
80 |
+
token.string = token.string.replace(/\./, '');
|
81 |
+
}
|
82 |
+
return token;
|
83 |
+
}
|
84 |
+
|
85 |
+
function coffeescriptHint(editor, options) {
|
86 |
+
return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
|
87 |
+
}
|
88 |
+
CodeMirror.coffeescriptHint = coffeescriptHint; // deprecated
|
89 |
+
CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint);
|
90 |
+
|
91 |
+
var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
|
92 |
+
"toUpperCase toLowerCase split concat match replace search").split(" ");
|
93 |
+
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
|
94 |
+
"lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
|
95 |
+
var funcProps = "prototype apply call bind".split(" ");
|
96 |
+
var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " +
|
97 |
+
"if in instanceof new null return switch throw true try typeof var void while with").split(" ");
|
98 |
+
var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
|
99 |
+
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
|
100 |
+
|
101 |
+
function getCompletions(token, context, keywords, options) {
|
102 |
+
var found = [], start = token.string;
|
103 |
+
function maybeAdd(str) {
|
104 |
+
if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
|
105 |
+
}
|
106 |
+
function gatherCompletions(obj) {
|
107 |
+
if (typeof obj == "string") forEach(stringProps, maybeAdd);
|
108 |
+
else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
|
109 |
+
else if (obj instanceof Function) forEach(funcProps, maybeAdd);
|
110 |
+
for (var name in obj) maybeAdd(name);
|
111 |
+
}
|
112 |
+
|
113 |
+
if (context) {
|
114 |
+
// If this is a property, see if it belongs to some object we can
|
115 |
+
// find in the current environment.
|
116 |
+
var obj = context.pop(), base;
|
117 |
+
if (obj.type.indexOf("variable") === 0) {
|
118 |
+
if (options && options.additionalContext)
|
119 |
+
base = options.additionalContext[obj.string];
|
120 |
+
base = base || window[obj.string];
|
121 |
+
} else if (obj.type == "string") {
|
122 |
+
base = "";
|
123 |
+
} else if (obj.type == "atom") {
|
124 |
+
base = 1;
|
125 |
+
} else if (obj.type == "function") {
|
126 |
+
if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
|
127 |
+
(typeof window.jQuery == 'function'))
|
128 |
+
base = window.jQuery();
|
129 |
+
else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
|
130 |
+
base = window._();
|
131 |
+
}
|
132 |
+
while (base != null && context.length)
|
133 |
+
base = base[context.pop().string];
|
134 |
+
if (base != null) gatherCompletions(base);
|
135 |
+
}
|
136 |
+
else {
|
137 |
+
// If not, just look in the window object and any local scope
|
138 |
+
// (reading into JS mode internals to get at the local and global variables)
|
139 |
+
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
|
140 |
+
for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
|
141 |
+
gatherCompletions(window);
|
142 |
+
forEach(keywords, maybeAdd);
|
143 |
+
}
|
144 |
+
return found;
|
145 |
+
}
|
146 |
+
})();
|
assets/lib/codemirror/addon/hint/pig-hint.js
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function () {
|
2 |
+
function forEach(arr, f) {
|
3 |
+
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
|
4 |
+
}
|
5 |
+
|
6 |
+
function arrayContains(arr, item) {
|
7 |
+
if (!Array.prototype.indexOf) {
|
8 |
+
var i = arr.length;
|
9 |
+
while (i--) {
|
10 |
+
if (arr[i] === item) {
|
11 |
+
return true;
|
12 |
+
}
|
13 |
+
}
|
14 |
+
return false;
|
15 |
+
}
|
16 |
+
return arr.indexOf(item) != -1;
|
17 |
+
}
|
18 |
+
|
19 |
+
function scriptHint(editor, _keywords, getToken) {
|
20 |
+
// Find the token at the cursor
|
21 |
+
var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
|
22 |
+
// If it's not a 'word-style' token, ignore the token.
|
23 |
+
|
24 |
+
if (!/^[\w$_]*$/.test(token.string)) {
|
25 |
+
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
|
26 |
+
className: token.string == ":" ? "pig-type" : null};
|
27 |
+
}
|
28 |
+
|
29 |
+
if (!context) var context = [];
|
30 |
+
context.push(tprop);
|
31 |
+
|
32 |
+
var completionList = getCompletions(token, context);
|
33 |
+
completionList = completionList.sort();
|
34 |
+
//prevent autocomplete for last word, instead show dropdown with one word
|
35 |
+
if(completionList.length == 1) {
|
36 |
+
completionList.push(" ");
|
37 |
+
}
|
38 |
+
|
39 |
+
return {list: completionList,
|
40 |
+
from: CodeMirror.Pos(cur.line, token.start),
|
41 |
+
to: CodeMirror.Pos(cur.line, token.end)};
|
42 |
+
}
|
43 |
+
|
44 |
+
function pigHint(editor) {
|
45 |
+
return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);});
|
46 |
+
}
|
47 |
+
CodeMirror.pigHint = pigHint; // deprecated
|
48 |
+
CodeMirror.registerHelper("hint", "pig", hinter);
|
49 |
+
|
50 |
+
var pigKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP "
|
51 |
+
+ "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL "
|
52 |
+
+ "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE "
|
53 |
+
+ "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE "
|
54 |
+
+ "NEQ MATCHES TRUE FALSE";
|
55 |
+
var pigKeywordsU = pigKeywords.split(" ");
|
56 |
+
var pigKeywordsL = pigKeywords.toLowerCase().split(" ");
|
57 |
+
|
58 |
+
var pigTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP";
|
59 |
+
var pigTypesU = pigTypes.split(" ");
|
60 |
+
var pigTypesL = pigTypes.toLowerCase().split(" ");
|
61 |
+
|
62 |
+
var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL "
|
63 |
+
+ "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS "
|
64 |
+
+ "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG "
|
65 |
+
+ "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN "
|
66 |
+
+ "INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER "
|
67 |
+
+ "ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS "
|
68 |
+
+ "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA "
|
69 |
+
+ "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE "
|
70 |
+
+ "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG "
|
71 |
+
+ "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER";
|
72 |
+
var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" ");
|
73 |
+
var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" ");
|
74 |
+
var pigBuiltinsC = ("BagSize BinStorage Bloom BuildBloom ConstantSize CubeDimensions DoubleAbs "
|
75 |
+
+ "DoubleAvg DoubleBase DoubleMax DoubleMin DoubleRound DoubleSum FloatAbs FloatAvg FloatMax "
|
76 |
+
+ "FloatMin FloatRound FloatSum GenericInvoker IntAbs IntAvg IntMax IntMin IntSum "
|
77 |
+
+ "InvokeForDouble InvokeForFloat InvokeForInt InvokeForLong InvokeForString Invoker "
|
78 |
+
+ "IsEmpty JsonLoader JsonMetadata JsonStorage LongAbs LongAvg LongMax LongMin LongSum MapSize "
|
79 |
+
+ "MonitoredUDF Nondeterministic OutputSchema PigStorage PigStreaming StringConcat StringMax "
|
80 |
+
+ "StringMin StringSize TextLoader TupleSize Utf8StorageConverter").split(" ").join("() ").split(" ");
|
81 |
+
|
82 |
+
function getCompletions(token, context) {
|
83 |
+
var found = [], start = token.string;
|
84 |
+
function maybeAdd(str) {
|
85 |
+
if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
|
86 |
+
}
|
87 |
+
|
88 |
+
function gatherCompletions(obj) {
|
89 |
+
if(obj == ":") {
|
90 |
+
forEach(pigTypesL, maybeAdd);
|
91 |
+
}
|
92 |
+
else {
|
93 |
+
forEach(pigBuiltinsU, maybeAdd);
|
94 |
+
forEach(pigBuiltinsL, maybeAdd);
|
95 |
+
forEach(pigBuiltinsC, maybeAdd);
|
96 |
+
forEach(pigTypesU, maybeAdd);
|
97 |
+
forEach(pigTypesL, maybeAdd);
|
98 |
+
forEach(pigKeywordsU, maybeAdd);
|
99 |
+
forEach(pigKeywordsL, maybeAdd);
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
if (context) {
|
104 |
+
// If this is a property, see if it belongs to some object we can
|
105 |
+
// find in the current environment.
|
106 |
+
var obj = context.pop(), base;
|
107 |
+
|
108 |
+
if (obj.type == "variable")
|
109 |
+
base = obj.string;
|
110 |
+
else if(obj.type == "variable-3")
|
111 |
+
base = ":" + obj.string;
|
112 |
+
|
113 |
+
while (base != null && context.length)
|
114 |
+
base = base[context.pop().string];
|
115 |
+
if (base != null) gatherCompletions(base);
|
116 |
+
}
|
117 |
+
return found;
|
118 |
+
}
|
119 |
+
})();
|
assets/lib/codemirror/addon/hint/python-hint.js
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function () {
|
2 |
+
function forEach(arr, f) {
|
3 |
+
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
|
4 |
+
}
|
5 |
+
|
6 |
+
function arrayContains(arr, item) {
|
7 |
+
if (!Array.prototype.indexOf) {
|
8 |
+
var i = arr.length;
|
9 |
+
while (i--) {
|
10 |
+
if (arr[i] === item) {
|
11 |
+
return true;
|
12 |
+
}
|
13 |
+
}
|
14 |
+
return false;
|
15 |
+
}
|
16 |
+
return arr.indexOf(item) != -1;
|
17 |
+
}
|
18 |
+
|
19 |
+
function scriptHint(editor, _keywords, getToken) {
|
20 |
+
// Find the token at the cursor
|
21 |
+
var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
|
22 |
+
// If it's not a 'word-style' token, ignore the token.
|
23 |
+
|
24 |
+
if (!/^[\w$_]*$/.test(token.string)) {
|
25 |
+
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
|
26 |
+
className: token.string == ":" ? "python-type" : null};
|
27 |
+
}
|
28 |
+
|
29 |
+
if (!context) var context = [];
|
30 |
+
context.push(tprop);
|
31 |
+
|
32 |
+
var completionList = getCompletions(token, context);
|
33 |
+
completionList = completionList.sort();
|
34 |
+
//prevent autocomplete for last word, instead show dropdown with one word
|
35 |
+
if(completionList.length == 1) {
|
36 |
+
completionList.push(" ");
|
37 |
+
}
|
38 |
+
|
39 |
+
return {list: completionList,
|
40 |
+
from: CodeMirror.Pos(cur.line, token.start),
|
41 |
+
to: CodeMirror.Pos(cur.line, token.end)};
|
42 |
+
}
|
43 |
+
|
44 |
+
function pythonHint(editor) {
|
45 |
+
return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);});
|
46 |
+
}
|
47 |
+
CodeMirror.pythonHint = pythonHint; // deprecated
|
48 |
+
CodeMirror.registerHelper("hint", "python", pythonHint);
|
49 |
+
|
50 |
+
var pythonKeywords = "and del from not while as elif global or with assert else if pass yield"
|
51 |
+
+ "break except import print class exec in raise continue finally is return def for lambda try";
|
52 |
+
var pythonKeywordsL = pythonKeywords.split(" ");
|
53 |
+
var pythonKeywordsU = pythonKeywords.toUpperCase().split(" ");
|
54 |
+
|
55 |
+
var pythonBuiltins = "abs divmod input open staticmethod all enumerate int ord str "
|
56 |
+
+ "any eval isinstance pow sum basestring execfile issubclass print super"
|
57 |
+
+ "bin file iter property tuple bool filter len range type"
|
58 |
+
+ "bytearray float list raw_input unichr callable format locals reduce unicode"
|
59 |
+
+ "chr frozenset long reload vars classmethod getattr map repr xrange"
|
60 |
+
+ "cmp globals max reversed zip compile hasattr memoryview round __import__"
|
61 |
+
+ "complex hash min set apply delattr help next setattr buffer"
|
62 |
+
+ "dict hex object slice coerce dir id oct sorted intern ";
|
63 |
+
var pythonBuiltinsL = pythonBuiltins.split(" ").join("() ").split(" ");
|
64 |
+
var pythonBuiltinsU = pythonBuiltins.toUpperCase().split(" ").join("() ").split(" ");
|
65 |
+
|
66 |
+
function getCompletions(token, context) {
|
67 |
+
var found = [], start = token.string;
|
68 |
+
function maybeAdd(str) {
|
69 |
+
if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
|
70 |
+
}
|
71 |
+
|
72 |
+
function gatherCompletions(_obj) {
|
73 |
+
forEach(pythonBuiltinsL, maybeAdd);
|
74 |
+
forEach(pythonBuiltinsU, maybeAdd);
|
75 |
+
forEach(pythonKeywordsL, maybeAdd);
|
76 |
+
forEach(pythonKeywordsU, maybeAdd);
|
77 |
+
}
|
78 |
+
|
79 |
+
if (context) {
|
80 |
+
// If this is a property, see if it belongs to some object we can
|
81 |
+
// find in the current environment.
|
82 |
+
var obj = context.pop(), base;
|
83 |
+
|
84 |
+
if (obj.type == "variable")
|
85 |
+
base = obj.string;
|
86 |
+
else if(obj.type == "variable-3")
|
87 |
+
base = ":" + obj.string;
|
88 |
+
|
89 |
+
while (base != null && context.length)
|
90 |
+
base = base[context.pop().string];
|
91 |
+
if (base != null) gatherCompletions(base);
|
92 |
+
}
|
93 |
+
return found;
|
94 |
+
}
|
95 |
+
})();
|
assets/lib/codemirror/addon/hint/show-hint.css
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.CodeMirror-hints {
|
2 |
+
position: absolute;
|
3 |
+
z-index: 10;
|
4 |
+
overflow: hidden;
|
5 |
+
list-style: none;
|
6 |
+
|
7 |
+
margin: 0;
|
8 |
+
padding: 2px;
|
9 |
+
|
10 |
+
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
11 |
+
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
12 |
+
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
13 |
+
border-radius: 3px;
|
14 |
+
border: 1px solid silver;
|
15 |
+
|
16 |
+
background: white;
|
17 |
+
font-size: 90%;
|
18 |
+
font-family: monospace;
|
19 |
+
|
20 |
+
max-height: 20em;
|
21 |
+
overflow-y: auto;
|
22 |
+
}
|
23 |
+
|
24 |
+
.CodeMirror-hint {
|
25 |
+
margin: 0;
|
26 |
+
padding: 0 4px;
|
27 |
+
border-radius: 2px;
|
28 |
+
max-width: 19em;
|
29 |
+
overflow: hidden;
|
30 |
+
white-space: pre;
|
31 |
+
color: black;
|
32 |
+
cursor: pointer;
|
33 |
+
}
|
34 |
+
|
35 |
+
.CodeMirror-hint-active {
|
36 |
+
background: #08f;
|
37 |
+
color: white;
|
38 |
+
}
|
assets/lib/codemirror/addon/hint/show-hint.js
ADDED
@@ -0,0 +1,274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
CodeMirror.showHint = function(cm, getHints, options) {
|
5 |
+
// We want a single cursor position.
|
6 |
+
if (cm.somethingSelected()) return;
|
7 |
+
if (getHints == null) getHints = cm.getHelper(cm.getCursor(), "hint");
|
8 |
+
if (getHints == null) return;
|
9 |
+
|
10 |
+
if (cm.state.completionActive) cm.state.completionActive.close();
|
11 |
+
|
12 |
+
var completion = cm.state.completionActive = new Completion(cm, getHints, options || {});
|
13 |
+
CodeMirror.signal(cm, "startCompletion", cm);
|
14 |
+
if (completion.options.async)
|
15 |
+
getHints(cm, function(hints) { completion.showHints(hints); }, completion.options);
|
16 |
+
else
|
17 |
+
return completion.showHints(getHints(cm, completion.options));
|
18 |
+
};
|
19 |
+
|
20 |
+
function Completion(cm, getHints, options) {
|
21 |
+
this.cm = cm;
|
22 |
+
this.getHints = getHints;
|
23 |
+
this.options = options;
|
24 |
+
this.widget = this.onClose = null;
|
25 |
+
}
|
26 |
+
|
27 |
+
Completion.prototype = {
|
28 |
+
close: function() {
|
29 |
+
if (!this.active()) return;
|
30 |
+
this.cm.state.completionActive = null;
|
31 |
+
|
32 |
+
if (this.widget) this.widget.close();
|
33 |
+
if (this.onClose) this.onClose();
|
34 |
+
CodeMirror.signal(this.cm, "endCompletion", this.cm);
|
35 |
+
},
|
36 |
+
|
37 |
+
active: function() {
|
38 |
+
return this.cm.state.completionActive == this;
|
39 |
+
},
|
40 |
+
|
41 |
+
pick: function(data, i) {
|
42 |
+
var completion = data.list[i];
|
43 |
+
if (completion.hint) completion.hint(this.cm, data, completion);
|
44 |
+
else this.cm.replaceRange(getText(completion), data.from, data.to);
|
45 |
+
this.close();
|
46 |
+
},
|
47 |
+
|
48 |
+
showHints: function(data) {
|
49 |
+
if (!data || !data.list.length || !this.active()) return this.close();
|
50 |
+
|
51 |
+
if (this.options.completeSingle != false && data.list.length == 1)
|
52 |
+
this.pick(data, 0);
|
53 |
+
else
|
54 |
+
this.showWidget(data);
|
55 |
+
},
|
56 |
+
|
57 |
+
showWidget: function(data) {
|
58 |
+
this.widget = new Widget(this, data);
|
59 |
+
CodeMirror.signal(data, "shown");
|
60 |
+
|
61 |
+
var debounce = null, completion = this, finished;
|
62 |
+
var closeOn = this.options.closeCharacters || /[\s()\[\]{};:>,]/;
|
63 |
+
var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length;
|
64 |
+
|
65 |
+
function done() {
|
66 |
+
if (finished) return;
|
67 |
+
finished = true;
|
68 |
+
completion.close();
|
69 |
+
completion.cm.off("cursorActivity", activity);
|
70 |
+
if (data) CodeMirror.signal(data, "close");
|
71 |
+
}
|
72 |
+
|
73 |
+
function update() {
|
74 |
+
if (finished) return;
|
75 |
+
CodeMirror.signal(data, "update");
|
76 |
+
if (completion.options.async)
|
77 |
+
completion.getHints(completion.cm, finishUpdate, completion.options);
|
78 |
+
else
|
79 |
+
finishUpdate(completion.getHints(completion.cm, completion.options));
|
80 |
+
}
|
81 |
+
function finishUpdate(data_) {
|
82 |
+
data = data_;
|
83 |
+
if (finished) return;
|
84 |
+
if (!data || !data.list.length) return done();
|
85 |
+
completion.widget = new Widget(completion, data);
|
86 |
+
}
|
87 |
+
|
88 |
+
function activity() {
|
89 |
+
clearTimeout(debounce);
|
90 |
+
var pos = completion.cm.getCursor(), line = completion.cm.getLine(pos.line);
|
91 |
+
if (pos.line != startPos.line || line.length - pos.ch != startLen - startPos.ch ||
|
92 |
+
pos.ch < startPos.ch || completion.cm.somethingSelected() ||
|
93 |
+
(pos.ch && closeOn.test(line.charAt(pos.ch - 1)))) {
|
94 |
+
completion.close();
|
95 |
+
} else {
|
96 |
+
debounce = setTimeout(update, 170);
|
97 |
+
if (completion.widget) completion.widget.close();
|
98 |
+
}
|
99 |
+
}
|
100 |
+
this.cm.on("cursorActivity", activity);
|
101 |
+
this.onClose = done;
|
102 |
+
}
|
103 |
+
};
|
104 |
+
|
105 |
+
function getText(completion) {
|
106 |
+
if (typeof completion == "string") return completion;
|
107 |
+
else return completion.text;
|
108 |
+
}
|
109 |
+
|
110 |
+
function buildKeyMap(options, handle) {
|
111 |
+
var baseMap = {
|
112 |
+
Up: function() {handle.moveFocus(-1);},
|
113 |
+
Down: function() {handle.moveFocus(1);},
|
114 |
+
PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
|
115 |
+
PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
|
116 |
+
Home: function() {handle.setFocus(0);},
|
117 |
+
End: function() {handle.setFocus(handle.length - 1);},
|
118 |
+
Enter: handle.pick,
|
119 |
+
Tab: handle.pick,
|
120 |
+
Esc: handle.close
|
121 |
+
};
|
122 |
+
var ourMap = options.customKeys ? {} : baseMap;
|
123 |
+
function addBinding(key, val) {
|
124 |
+
var bound;
|
125 |
+
if (typeof val != "string")
|
126 |
+
bound = function(cm) { return val(cm, handle); };
|
127 |
+
// This mechanism is deprecated
|
128 |
+
else if (baseMap.hasOwnProperty(val))
|
129 |
+
bound = baseMap[val];
|
130 |
+
else
|
131 |
+
bound = val;
|
132 |
+
ourMap[key] = bound;
|
133 |
+
}
|
134 |
+
if (options.customKeys)
|
135 |
+
for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key))
|
136 |
+
addBinding(key, options.customKeys[key]);
|
137 |
+
if (options.extraKeys)
|
138 |
+
for (var key in options.extraKeys) if (options.extraKeys.hasOwnProperty(key))
|
139 |
+
addBinding(key, options.extraKeys[key]);
|
140 |
+
return ourMap;
|
141 |
+
}
|
142 |
+
|
143 |
+
function Widget(completion, data) {
|
144 |
+
this.completion = completion;
|
145 |
+
this.data = data;
|
146 |
+
var widget = this, cm = completion.cm, options = completion.options;
|
147 |
+
|
148 |
+
var hints = this.hints = document.createElement("ul");
|
149 |
+
hints.className = "CodeMirror-hints";
|
150 |
+
this.selectedHint = 0;
|
151 |
+
|
152 |
+
var completions = data.list;
|
153 |
+
for (var i = 0; i < completions.length; ++i) {
|
154 |
+
var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
|
155 |
+
var className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active");
|
156 |
+
if (cur.className != null) className = cur.className + " " + className;
|
157 |
+
elt.className = className;
|
158 |
+
if (cur.render) cur.render(elt, data, cur);
|
159 |
+
else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
|
160 |
+
elt.hintId = i;
|
161 |
+
}
|
162 |
+
|
163 |
+
var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null);
|
164 |
+
var left = pos.left, top = pos.bottom, below = true;
|
165 |
+
hints.style.left = left + "px";
|
166 |
+
hints.style.top = top + "px";
|
167 |
+
// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
|
168 |
+
var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
|
169 |
+
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
|
170 |
+
(options.container || document.body).appendChild(hints);
|
171 |
+
var box = hints.getBoundingClientRect();
|
172 |
+
var overlapX = box.right - winW, overlapY = box.bottom - winH;
|
173 |
+
if (overlapX > 0) {
|
174 |
+
if (box.right - box.left > winW) {
|
175 |
+
hints.style.width = (winW - 5) + "px";
|
176 |
+
overlapX -= (box.right - box.left) - winW;
|
177 |
+
}
|
178 |
+
hints.style.left = (left = pos.left - overlapX) + "px";
|
179 |
+
}
|
180 |
+
if (overlapY > 0) {
|
181 |
+
var height = box.bottom - box.top;
|
182 |
+
if (box.top - (pos.bottom - pos.top) - height > 0) {
|
183 |
+
overlapY = height + (pos.bottom - pos.top);
|
184 |
+
below = false;
|
185 |
+
} else if (height > winH) {
|
186 |
+
hints.style.height = (winH - 5) + "px";
|
187 |
+
overlapY -= height - winH;
|
188 |
+
}
|
189 |
+
hints.style.top = (top = pos.bottom - overlapY) + "px";
|
190 |
+
}
|
191 |
+
|
192 |
+
cm.addKeyMap(this.keyMap = buildKeyMap(options, {
|
193 |
+
moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
|
194 |
+
setFocus: function(n) { widget.changeActive(n); },
|
195 |
+
menuSize: function() { return widget.screenAmount(); },
|
196 |
+
length: completions.length,
|
197 |
+
close: function() { completion.close(); },
|
198 |
+
pick: function() { widget.pick(); }
|
199 |
+
}));
|
200 |
+
|
201 |
+
if (options.closeOnUnfocus !== false) {
|
202 |
+
var closingOnBlur;
|
203 |
+
cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
|
204 |
+
cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
|
205 |
+
}
|
206 |
+
|
207 |
+
var startScroll = cm.getScrollInfo();
|
208 |
+
cm.on("scroll", this.onScroll = function() {
|
209 |
+
var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
|
210 |
+
var newTop = top + startScroll.top - curScroll.top;
|
211 |
+
var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
|
212 |
+
if (!below) point += hints.offsetHeight;
|
213 |
+
if (point <= editor.top || point >= editor.bottom) return completion.close();
|
214 |
+
hints.style.top = newTop + "px";
|
215 |
+
hints.style.left = (left + startScroll.left - curScroll.left) + "px";
|
216 |
+
});
|
217 |
+
|
218 |
+
CodeMirror.on(hints, "dblclick", function(e) {
|
219 |
+
var t = e.target || e.srcElement;
|
220 |
+
if (t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
|
221 |
+
});
|
222 |
+
CodeMirror.on(hints, "click", function(e) {
|
223 |
+
var t = e.target || e.srcElement;
|
224 |
+
if (t.hintId != null) widget.changeActive(t.hintId);
|
225 |
+
});
|
226 |
+
CodeMirror.on(hints, "mousedown", function() {
|
227 |
+
setTimeout(function(){cm.focus();}, 20);
|
228 |
+
});
|
229 |
+
|
230 |
+
CodeMirror.signal(data, "select", completions[0], hints.firstChild);
|
231 |
+
return true;
|
232 |
+
}
|
233 |
+
|
234 |
+
Widget.prototype = {
|
235 |
+
close: function() {
|
236 |
+
if (this.completion.widget != this) return;
|
237 |
+
this.completion.widget = null;
|
238 |
+
this.hints.parentNode.removeChild(this.hints);
|
239 |
+
this.completion.cm.removeKeyMap(this.keyMap);
|
240 |
+
|
241 |
+
var cm = this.completion.cm;
|
242 |
+
if (this.completion.options.closeOnUnfocus !== false) {
|
243 |
+
cm.off("blur", this.onBlur);
|
244 |
+
cm.off("focus", this.onFocus);
|
245 |
+
}
|
246 |
+
cm.off("scroll", this.onScroll);
|
247 |
+
},
|
248 |
+
|
249 |
+
pick: function() {
|
250 |
+
this.completion.pick(this.data, this.selectedHint);
|
251 |
+
},
|
252 |
+
|
253 |
+
changeActive: function(i, avoidWrap) {
|
254 |
+
if (i >= this.data.list.length)
|
255 |
+
i = avoidWrap ? this.data.list.length - 1 : 0;
|
256 |
+
else if (i < 0)
|
257 |
+
i = avoidWrap ? 0 : this.data.list.length - 1;
|
258 |
+
if (this.selectedHint == i) return;
|
259 |
+
var node = this.hints.childNodes[this.selectedHint];
|
260 |
+
node.className = node.className.replace(" CodeMirror-hint-active", "");
|
261 |
+
node = this.hints.childNodes[this.selectedHint = i];
|
262 |
+
node.className += " CodeMirror-hint-active";
|
263 |
+
if (node.offsetTop < this.hints.scrollTop)
|
264 |
+
this.hints.scrollTop = node.offsetTop - 3;
|
265 |
+
else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
|
266 |
+
this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
|
267 |
+
CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
|
268 |
+
},
|
269 |
+
|
270 |
+
screenAmount: function() {
|
271 |
+
return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
|
272 |
+
}
|
273 |
+
};
|
274 |
+
})();
|
assets/lib/codemirror/addon/hint/xml-hint.js
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
var Pos = CodeMirror.Pos;
|
5 |
+
|
6 |
+
function getHints(cm, options) {
|
7 |
+
var tags = options && options.schemaInfo;
|
8 |
+
var quote = (options && options.quoteChar) || '"';
|
9 |
+
if (!tags) return;
|
10 |
+
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
|
11 |
+
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
|
12 |
+
if (inner.mode.name != "xml") return;
|
13 |
+
var result = [], replaceToken = false, prefix;
|
14 |
+
var isTag = token.string.charAt(0) == "<";
|
15 |
+
if (!inner.state.tagName || isTag) { // Tag completion
|
16 |
+
if (isTag) {
|
17 |
+
prefix = token.string.slice(1);
|
18 |
+
replaceToken = true;
|
19 |
+
}
|
20 |
+
var cx = inner.state.context, curTag = cx && tags[cx.tagName];
|
21 |
+
var childList = cx ? curTag && curTag.children : tags["!top"];
|
22 |
+
if (childList) {
|
23 |
+
for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].indexOf(prefix) == 0)
|
24 |
+
result.push("<" + childList[i]);
|
25 |
+
} else {
|
26 |
+
for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.indexOf(prefix) == 0))
|
27 |
+
result.push("<" + name);
|
28 |
+
}
|
29 |
+
if (cx && (!prefix || ("/" + cx.tagName).indexOf(prefix) == 0))
|
30 |
+
result.push("</" + cx.tagName + ">");
|
31 |
+
} else {
|
32 |
+
// Attribute completion
|
33 |
+
var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs;
|
34 |
+
if (!attrs) return;
|
35 |
+
if (token.type == "string" || token.string == "=") { // A value
|
36 |
+
var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
|
37 |
+
Pos(cur.line, token.type == "string" ? token.start : token.end));
|
38 |
+
var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues;
|
39 |
+
if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return;
|
40 |
+
if (token.type == "string") {
|
41 |
+
prefix = token.string;
|
42 |
+
if (/['"]/.test(token.string.charAt(0))) {
|
43 |
+
quote = token.string.charAt(0);
|
44 |
+
prefix = token.string.slice(1);
|
45 |
+
}
|
46 |
+
replaceToken = true;
|
47 |
+
}
|
48 |
+
for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].indexOf(prefix) == 0)
|
49 |
+
result.push(quote + atValues[i] + quote);
|
50 |
+
} else { // An attribute name
|
51 |
+
if (token.type == "attribute") {
|
52 |
+
prefix = token.string;
|
53 |
+
replaceToken = true;
|
54 |
+
}
|
55 |
+
for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.indexOf(prefix) == 0))
|
56 |
+
result.push(attr);
|
57 |
+
}
|
58 |
+
}
|
59 |
+
return {
|
60 |
+
list: result,
|
61 |
+
from: replaceToken ? Pos(cur.line, token.start) : cur,
|
62 |
+
to: replaceToken ? Pos(cur.line, token.end) : cur
|
63 |
+
};
|
64 |
+
}
|
65 |
+
|
66 |
+
CodeMirror.xmlHint = getHints; // deprecated
|
67 |
+
CodeMirror.registerHelper("hint", "xml", getHints);
|
68 |
+
})();
|
assets/lib/codemirror/addon/lint/coffeescript-lint.js
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js
|
2 |
+
|
3 |
+
CodeMirror.registerHelper("lint", "coffeescript", function(text) {
|
4 |
+
var found = [];
|
5 |
+
var parseError = function(err) {
|
6 |
+
var loc = err.lineNumber;
|
7 |
+
found.push({from: CodeMirror.Pos(loc-1, 0),
|
8 |
+
to: CodeMirror.Pos(loc, 0),
|
9 |
+
severity: err.level,
|
10 |
+
message: err.message});
|
11 |
+
};
|
12 |
+
try {
|
13 |
+
var res = coffeelint.lint(text);
|
14 |
+
for(var i = 0; i < res.length; i++) {
|
15 |
+
parseError(res[i]);
|
16 |
+
}
|
17 |
+
} catch(e) {
|
18 |
+
found.push({from: CodeMirror.Pos(e.location.first_line, 0),
|
19 |
+
to: CodeMirror.Pos(e.location.last_line, e.location.last_column),
|
20 |
+
severity: 'error',
|
21 |
+
message: e.message});
|
22 |
+
}
|
23 |
+
return found;
|
24 |
+
});
|
25 |
+
CodeMirror.coffeeValidator = CodeMirror.lint.coffeescript; // deprecated
|
assets/lib/codemirror/addon/lint/css-lint.js
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Depends on csslint.js from https://github.com/stubbornella/csslint
|
2 |
+
|
3 |
+
CodeMirror.registerHelper("lint", "css", function(text) {
|
4 |
+
var found = [];
|
5 |
+
var results = CSSLint.verify(text), messages = results.messages, message = null;
|
6 |
+
for ( var i = 0; i < messages.length; i++) {
|
7 |
+
message = messages[i];
|
8 |
+
var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;
|
9 |
+
found.push({
|
10 |
+
from: CodeMirror.Pos(startLine, startCol),
|
11 |
+
to: CodeMirror.Pos(endLine, endCol),
|
12 |
+
message: message.message,
|
13 |
+
severity : message.type
|
14 |
+
});
|
15 |
+
}
|
16 |
+
return found;
|
17 |
+
});
|
assets/lib/codemirror/addon/lint/javascript-lint.js
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
|
3 |
+
var bogus = [ "Dangerous comment" ];
|
4 |
+
|
5 |
+
var warnings = [ [ "Expected '{'",
|
6 |
+
"Statement body should be inside '{ }' braces." ] ];
|
7 |
+
|
8 |
+
var errors = [ "Missing semicolon", "Extra comma", "Missing property name",
|
9 |
+
"Unmatched ", " and instead saw", " is not defined",
|
10 |
+
"Unclosed string", "Stopping, unable to continue" ];
|
11 |
+
|
12 |
+
function validator(text, options) {
|
13 |
+
JSHINT(text, options);
|
14 |
+
var errors = JSHINT.data().errors, result = [];
|
15 |
+
if (errors) parseErrors(errors, result);
|
16 |
+
return result;
|
17 |
+
}
|
18 |
+
|
19 |
+
CodeMirror.registerHelper("lint", "javascript", validator);
|
20 |
+
CodeMirror.javascriptValidator = CodeMirror.lint.javascript; // deprecated
|
21 |
+
|
22 |
+
function cleanup(error) {
|
23 |
+
// All problems are warnings by default
|
24 |
+
fixWith(error, warnings, "warning", true);
|
25 |
+
fixWith(error, errors, "error");
|
26 |
+
|
27 |
+
return isBogus(error) ? null : error;
|
28 |
+
}
|
29 |
+
|
30 |
+
function fixWith(error, fixes, severity, force) {
|
31 |
+
var description, fix, find, replace, found;
|
32 |
+
|
33 |
+
description = error.description;
|
34 |
+
|
35 |
+
for ( var i = 0; i < fixes.length; i++) {
|
36 |
+
fix = fixes[i];
|
37 |
+
find = (typeof fix === "string" ? fix : fix[0]);
|
38 |
+
replace = (typeof fix === "string" ? null : fix[1]);
|
39 |
+
found = description.indexOf(find) !== -1;
|
40 |
+
|
41 |
+
if (force || found) {
|
42 |
+
error.severity = severity;
|
43 |
+
}
|
44 |
+
if (found && replace) {
|
45 |
+
error.description = replace;
|
46 |
+
}
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
function isBogus(error) {
|
51 |
+
var description = error.description;
|
52 |
+
for ( var i = 0; i < bogus.length; i++) {
|
53 |
+
if (description.indexOf(bogus[i]) !== -1) {
|
54 |
+
return true;
|
55 |
+
}
|
56 |
+
}
|
57 |
+
return false;
|
58 |
+
}
|
59 |
+
|
60 |
+
function parseErrors(errors, output) {
|
61 |
+
for ( var i = 0; i < errors.length; i++) {
|
62 |
+
var error = errors[i];
|
63 |
+
if (error) {
|
64 |
+
var linetabpositions, index;
|
65 |
+
|
66 |
+
linetabpositions = [];
|
67 |
+
|
68 |
+
// This next block is to fix a problem in jshint. Jshint
|
69 |
+
// replaces
|
70 |
+
// all tabs with spaces then performs some checks. The error
|
71 |
+
// positions (character/space) are then reported incorrectly,
|
72 |
+
// not taking the replacement step into account. Here we look
|
73 |
+
// at the evidence line and try to adjust the character position
|
74 |
+
// to the correct value.
|
75 |
+
if (error.evidence) {
|
76 |
+
// Tab positions are computed once per line and cached
|
77 |
+
var tabpositions = linetabpositions[error.line];
|
78 |
+
if (!tabpositions) {
|
79 |
+
var evidence = error.evidence;
|
80 |
+
tabpositions = [];
|
81 |
+
// ugggh phantomjs does not like this
|
82 |
+
// forEachChar(evidence, function(item, index) {
|
83 |
+
Array.prototype.forEach.call(evidence, function(item,
|
84 |
+
index) {
|
85 |
+
if (item === '\t') {
|
86 |
+
// First col is 1 (not 0) to match error
|
87 |
+
// positions
|
88 |
+
tabpositions.push(index + 1);
|
89 |
+
}
|
90 |
+
});
|
91 |
+
linetabpositions[error.line] = tabpositions;
|
92 |
+
}
|
93 |
+
if (tabpositions.length > 0) {
|
94 |
+
var pos = error.character;
|
95 |
+
tabpositions.forEach(function(tabposition) {
|
96 |
+
if (pos > tabposition) pos -= 1;
|
97 |
+
});
|
98 |
+
error.character = pos;
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
var start = error.character - 1, end = start + 1;
|
103 |
+
if (error.evidence) {
|
104 |
+
index = error.evidence.substring(start).search(/.\b/);
|
105 |
+
if (index > -1) {
|
106 |
+
end += index;
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
// Convert to format expected by validation service
|
111 |
+
error.description = error.reason;// + "(jshint)";
|
112 |
+
error.start = error.character;
|
113 |
+
error.end = end;
|
114 |
+
error = cleanup(error);
|
115 |
+
|
116 |
+
if (error)
|
117 |
+
output.push({message: error.description,
|
118 |
+
severity: error.severity,
|
119 |
+
from: CodeMirror.Pos(error.line - 1, start),
|
120 |
+
to: CodeMirror.Pos(error.line - 1, end)});
|
121 |
+
}
|
122 |
+
}
|
123 |
+
}
|
124 |
+
})();
|
assets/lib/codemirror/addon/lint/json-lint.js
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Depends on jsonlint.js from https://github.com/zaach/jsonlint
|
2 |
+
|
3 |
+
CodeMirror.registerHelper("lint", "json", function(text) {
|
4 |
+
var found = [];
|
5 |
+
jsonlint.parseError = function(str, hash) {
|
6 |
+
var loc = hash.loc;
|
7 |
+
found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
|
8 |
+
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
|
9 |
+
message: str});
|
10 |
+
};
|
11 |
+
try { jsonlint.parse(text); }
|
12 |
+
catch(e) {}
|
13 |
+
return found;
|
14 |
+
});
|
15 |
+
CodeMirror.jsonValidator = CodeMirror.lint.json; // deprecated
|
assets/lib/codemirror/addon/lint/lint.css
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* The lint marker gutter */
|
2 |
+
.CodeMirror-lint-markers {
|
3 |
+
width: 16px;
|
4 |
+
}
|
5 |
+
|
6 |
+
.CodeMirror-lint-tooltip {
|
7 |
+
background-color: infobackground;
|
8 |
+
border: 1px solid black;
|
9 |
+
border-radius: 4px 4px 4px 4px;
|
10 |
+
color: infotext;
|
11 |
+
font-family: monospace;
|
12 |
+
font-size: 10pt;
|
13 |
+
overflow: hidden;
|
14 |
+
padding: 2px 5px;
|
15 |
+
position: fixed;
|
16 |
+
white-space: pre;
|
17 |
+
z-index: 100;
|
18 |
+
max-width: 600px;
|
19 |
+
opacity: 0;
|
20 |
+
transition: opacity .4s;
|
21 |
+
-moz-transition: opacity .4s;
|
22 |
+
-webkit-transition: opacity .4s;
|
23 |
+
-o-transition: opacity .4s;
|
24 |
+
-ms-transition: opacity .4s;
|
25 |
+
}
|
26 |
+
|
27 |
+
.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
|
28 |
+
background-position: left bottom;
|
29 |
+
background-repeat: repeat-x;
|
30 |
+
}
|
31 |
+
|
32 |
+
.CodeMirror-lint-mark-error {
|
33 |
+
background-image:
|
34 |
+
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
|
35 |
+
;
|
36 |
+
}
|
37 |
+
|
38 |
+
.CodeMirror-lint-mark-warning {
|
39 |
+
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
|
40 |
+
}
|
41 |
+
|
42 |
+
.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
|
43 |
+
background-position: center center;
|
44 |
+
background-repeat: no-repeat;
|
45 |
+
cursor: pointer;
|
46 |
+
display: inline-block;
|
47 |
+
height: 16px;
|
48 |
+
width: 16px;
|
49 |
+
vertical-align: middle;
|
50 |
+
position: relative;
|
51 |
+
}
|
52 |
+
|
53 |
+
.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
|
54 |
+
padding-left: 18px;
|
55 |
+
background-position: top left;
|
56 |
+
background-repeat: no-repeat;
|
57 |
+
}
|
58 |
+
|
59 |
+
.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
|
60 |
+
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
|
61 |
+
}
|
62 |
+
|
63 |
+
.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
|
64 |
+
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
|
65 |
+
}
|
66 |
+
|
67 |
+
.CodeMirror-lint-marker-multiple {
|
68 |
+
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
|
69 |
+
background-repeat: no-repeat;
|
70 |
+
background-position: right bottom;
|
71 |
+
width: 100%; height: 100%;
|
72 |
+
}
|
assets/lib/codemirror/addon/lint/lint.js
ADDED
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
var GUTTER_ID = "CodeMirror-lint-markers";
|
4 |
+
var SEVERITIES = /^(?:error|warning)$/;
|
5 |
+
|
6 |
+
function showTooltip(e, content) {
|
7 |
+
var tt = document.createElement("div");
|
8 |
+
tt.className = "CodeMirror-lint-tooltip";
|
9 |
+
tt.appendChild(content.cloneNode(true));
|
10 |
+
document.body.appendChild(tt);
|
11 |
+
|
12 |
+
function position(e) {
|
13 |
+
if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
|
14 |
+
tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
|
15 |
+
tt.style.left = (e.clientX + 5) + "px";
|
16 |
+
}
|
17 |
+
CodeMirror.on(document, "mousemove", position);
|
18 |
+
position(e);
|
19 |
+
if (tt.style.opacity != null) tt.style.opacity = 1;
|
20 |
+
return tt;
|
21 |
+
}
|
22 |
+
function rm(elt) {
|
23 |
+
if (elt.parentNode) elt.parentNode.removeChild(elt);
|
24 |
+
}
|
25 |
+
function hideTooltip(tt) {
|
26 |
+
if (!tt.parentNode) return;
|
27 |
+
if (tt.style.opacity == null) rm(tt);
|
28 |
+
tt.style.opacity = 0;
|
29 |
+
setTimeout(function() { rm(tt); }, 600);
|
30 |
+
}
|
31 |
+
|
32 |
+
function showTooltipFor(e, content, node) {
|
33 |
+
var tooltip = showTooltip(e, content);
|
34 |
+
function hide() {
|
35 |
+
CodeMirror.off(node, "mouseout", hide);
|
36 |
+
if (tooltip) { hideTooltip(tooltip); tooltip = null; }
|
37 |
+
}
|
38 |
+
var poll = setInterval(function() {
|
39 |
+
if (tooltip) for (var n = node;; n = n.parentNode) {
|
40 |
+
if (n == document.body) return;
|
41 |
+
if (!n) { hide(); break; }
|
42 |
+
}
|
43 |
+
if (!tooltip) return clearInterval(poll);
|
44 |
+
}, 400);
|
45 |
+
CodeMirror.on(node, "mouseout", hide);
|
46 |
+
}
|
47 |
+
|
48 |
+
function LintState(cm, options, hasGutter) {
|
49 |
+
this.marked = [];
|
50 |
+
this.options = options;
|
51 |
+
this.timeout = null;
|
52 |
+
this.hasGutter = hasGutter;
|
53 |
+
this.onMouseOver = function(e) { onMouseOver(cm, e); };
|
54 |
+
}
|
55 |
+
|
56 |
+
function parseOptions(cm, options) {
|
57 |
+
if (options instanceof Function) return {getAnnotations: options};
|
58 |
+
if (!options || options === true) options = {};
|
59 |
+
if (!options.getAnnotations) options.getAnnotations = cm.getHelper(CodeMirror.Pos(0, 0), "lint");
|
60 |
+
if (!options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)");
|
61 |
+
return options;
|
62 |
+
}
|
63 |
+
|
64 |
+
function clearMarks(cm) {
|
65 |
+
var state = cm.state.lint;
|
66 |
+
if (state.hasGutter) cm.clearGutter(GUTTER_ID);
|
67 |
+
for (var i = 0; i < state.marked.length; ++i)
|
68 |
+
state.marked[i].clear();
|
69 |
+
state.marked.length = 0;
|
70 |
+
}
|
71 |
+
|
72 |
+
function makeMarker(labels, severity, multiple, tooltips) {
|
73 |
+
var marker = document.createElement("div"), inner = marker;
|
74 |
+
marker.className = "CodeMirror-lint-marker-" + severity;
|
75 |
+
if (multiple) {
|
76 |
+
inner = marker.appendChild(document.createElement("div"));
|
77 |
+
inner.className = "CodeMirror-lint-marker-multiple";
|
78 |
+
}
|
79 |
+
|
80 |
+
if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
|
81 |
+
showTooltipFor(e, labels, inner);
|
82 |
+
});
|
83 |
+
|
84 |
+
return marker;
|
85 |
+
}
|
86 |
+
|
87 |
+
function getMaxSeverity(a, b) {
|
88 |
+
if (a == "error") return a;
|
89 |
+
else return b;
|
90 |
+
}
|
91 |
+
|
92 |
+
function groupByLine(annotations) {
|
93 |
+
var lines = [];
|
94 |
+
for (var i = 0; i < annotations.length; ++i) {
|
95 |
+
var ann = annotations[i], line = ann.from.line;
|
96 |
+
(lines[line] || (lines[line] = [])).push(ann);
|
97 |
+
}
|
98 |
+
return lines;
|
99 |
+
}
|
100 |
+
|
101 |
+
function annotationTooltip(ann) {
|
102 |
+
var severity = ann.severity;
|
103 |
+
if (!SEVERITIES.test(severity)) severity = "error";
|
104 |
+
var tip = document.createElement("div");
|
105 |
+
tip.className = "CodeMirror-lint-message-" + severity;
|
106 |
+
tip.appendChild(document.createTextNode(ann.message));
|
107 |
+
return tip;
|
108 |
+
}
|
109 |
+
|
110 |
+
function startLinting(cm) {
|
111 |
+
var state = cm.state.lint, options = state.options;
|
112 |
+
if (options.async)
|
113 |
+
options.getAnnotations(cm, updateLinting, options);
|
114 |
+
else
|
115 |
+
updateLinting(cm, options.getAnnotations(cm.getValue(), options));
|
116 |
+
}
|
117 |
+
|
118 |
+
function updateLinting(cm, annotationsNotSorted) {
|
119 |
+
clearMarks(cm);
|
120 |
+
var state = cm.state.lint, options = state.options;
|
121 |
+
|
122 |
+
var annotations = groupByLine(annotationsNotSorted);
|
123 |
+
|
124 |
+
for (var line = 0; line < annotations.length; ++line) {
|
125 |
+
var anns = annotations[line];
|
126 |
+
if (!anns) continue;
|
127 |
+
|
128 |
+
var maxSeverity = null;
|
129 |
+
var tipLabel = state.hasGutter && document.createDocumentFragment();
|
130 |
+
|
131 |
+
for (var i = 0; i < anns.length; ++i) {
|
132 |
+
var ann = anns[i];
|
133 |
+
var severity = ann.severity;
|
134 |
+
if (!SEVERITIES.test(severity)) severity = "error";
|
135 |
+
maxSeverity = getMaxSeverity(maxSeverity, severity);
|
136 |
+
|
137 |
+
if (options.formatAnnotation) ann = options.formatAnnotation(ann);
|
138 |
+
if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
|
139 |
+
|
140 |
+
if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
|
141 |
+
className: "CodeMirror-lint-mark-" + severity,
|
142 |
+
__annotation: ann
|
143 |
+
}));
|
144 |
+
}
|
145 |
+
|
146 |
+
if (state.hasGutter)
|
147 |
+
cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1,
|
148 |
+
state.options.tooltips));
|
149 |
+
}
|
150 |
+
if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
|
151 |
+
}
|
152 |
+
|
153 |
+
function onChange(cm) {
|
154 |
+
var state = cm.state.lint;
|
155 |
+
clearTimeout(state.timeout);
|
156 |
+
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
|
157 |
+
}
|
158 |
+
|
159 |
+
function popupSpanTooltip(ann, e) {
|
160 |
+
var target = e.target || e.srcElement;
|
161 |
+
showTooltipFor(e, annotationTooltip(ann), target);
|
162 |
+
}
|
163 |
+
|
164 |
+
// When the mouseover fires, the cursor might not actually be over
|
165 |
+
// the character itself yet. These pairs of x,y offsets are used to
|
166 |
+
// probe a few nearby points when no suitable marked range is found.
|
167 |
+
var nearby = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0];
|
168 |
+
|
169 |
+
function onMouseOver(cm, e) {
|
170 |
+
if (!/\bCodeMirror-lint-mark-/.test((e.target || e.srcElement).className)) return;
|
171 |
+
for (var i = 0; i < nearby.length; i += 2) {
|
172 |
+
var spans = cm.findMarksAt(cm.coordsChar({left: e.clientX + nearby[i],
|
173 |
+
top: e.clientY + nearby[i + 1]}));
|
174 |
+
for (var j = 0; j < spans.length; ++j) {
|
175 |
+
var span = spans[j], ann = span.__annotation;
|
176 |
+
if (ann) return popupSpanTooltip(ann, e);
|
177 |
+
}
|
178 |
+
}
|
179 |
+
}
|
180 |
+
|
181 |
+
function optionHandler(cm, val, old) {
|
182 |
+
if (old && old != CodeMirror.Init) {
|
183 |
+
clearMarks(cm);
|
184 |
+
cm.off("change", onChange);
|
185 |
+
CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
|
186 |
+
delete cm.state.lint;
|
187 |
+
}
|
188 |
+
|
189 |
+
if (val) {
|
190 |
+
var gutters = cm.getOption("gutters"), hasLintGutter = false;
|
191 |
+
for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
|
192 |
+
var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
|
193 |
+
cm.on("change", onChange);
|
194 |
+
if (state.options.tooltips != false)
|
195 |
+
CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
|
196 |
+
|
197 |
+
startLinting(cm);
|
198 |
+
}
|
199 |
+
}
|
200 |
+
|
201 |
+
CodeMirror.defineOption("lintWith", false, optionHandler); // deprecated
|
202 |
+
CodeMirror.defineOption("lint", false, optionHandler); // deprecated
|
203 |
+
})();
|
assets/lib/codemirror/addon/merge/dep/diff_match_patch.js
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// From https://code.google.com/p/google-diff-match-patch/ , licensed under the Apache License 2.0
|
2 |
+
(function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32}
|
3 |
+
diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a,
|
4 |
+
b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a};
|
5 |
+
diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,b,
|
6 |
+
d):this.diff_bisect_(a,b,d)};
|
7 |
+
diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b);a=d.chars1;b=d.chars2;d=d.lineArray;a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push([0,""]);for(var e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case 1:e++;g+=a[b][1];break;case -1:d++;f+=a[b][1];break;case 0:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,0,d[e]);b+=d.length}d=e=0;g=f=""}b++}a.pop();return a};
|
8 |
+
diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=f,h=2*f,j=Array(h),i=Array(h),k=0;k<h;k++)j[k]=-1,i[k]=-1;j[g+1]=0;i[g+1]=0;for(var k=d-e,q=0!=k%2,r=0,t=0,p=0,w=0,v=0;v<f&&!((new Date).getTime()>c);v++){for(var n=-v+r;n<=v-t;n+=2){var l=g+n,m;m=n==-v||n!=v&&j[l-1]<j[l+1]?j[l+1]:j[l-1]+1;for(var s=m-n;m<d&&s<e&&a.charAt(m)==b.charAt(s);)m++,s++;j[l]=m;if(m>d)t+=2;else if(s>e)r+=2;else if(q&&(l=g+k-n,0<=l&&l<h&&-1!=i[l])){var u=d-i[l];if(m>=
|
9 |
+
u)return this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]<i[l+1]?i[l+1]:i[l-1]+1;for(m=u-n;u<d&&m<e&&a.charAt(d-u-1)==b.charAt(e-m-1);)u++,m++;i[l]=u;if(u>d)w+=2;else if(m>e)p+=2;else if(!q&&(l=g+k-n,0<=l&&(l<h&&-1!=j[l])&&(m=j[l],s=g+m-l,u=d-u,m>=u)))return this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]};
|
10 |
+
diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)};
|
11 |
+
diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;f<a.length-1;){f=a.indexOf("\n",c);-1==f&&(f=a.length-1);var r=a.substring(c,f+1),c=f+1;(e.hasOwnProperty?e.hasOwnProperty(r):void 0!==e[r])?b+=String.fromCharCode(e[r]):(b+=String.fromCharCode(g),e[r]=g,d[g++]=r)}return b}var d=[],e={};d[0]="";var f=c(a),g=c(b);return{chars1:f,chars2:g,lineArray:d}};
|
12 |
+
diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
|
13 |
+
diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
|
14 |
+
diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;for(var d=0,e=1;;){var f=a.substring(c-e),f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
|
15 |
+
diff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,j,n,l;-1!=(e=b.indexOf(d,e+1));){var m=f.diff_commonPrefix(a.substring(c),b.substring(e)),s=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<s+m&&(g=b.substring(e-s,e)+b.substring(e,e+m),h=a.substring(0,c-s),j=a.substring(c+m),n=b.substring(0,e-s),l=b.substring(e+m))}return 2*g.length>=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null;
|
16 |
+
var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4)),d=c(d,e,Math.ceil(d.length/2)),h;if(!g&&!d)return null;h=d?g?g[4].length>d[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]};
|
17 |
+
diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f<a.length;)0==a[f][0]?(c[d++]=f,g=j,h=i,i=j=0,e=a[f][1]):(1==a[f][0]?j+=a[f][1].length:i+=a[f][1].length,e&&(e.length<=Math.max(g,h)&&e.length<=Math.max(j,i))&&(a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,d--,f=0<d?c[d-1]:-1,i=j=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<a.length;){if(-1==a[f-1][0]&&1==a[f][0]){b=a[f-1][1];c=a[f][1];
|
18 |
+
d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}};
|
19 |
+
diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_);
|
20 |
+
return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(0==a[c-1][0]&&0==a[c+1][0]){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g)var h=e.substring(e.length-g),d=d.substring(0,d.length-g),e=h+e.substring(0,e.length-g),f=h+f;for(var g=d,h=e,j=f,i=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){var d=d+e.charAt(0),e=e.substring(1)+f.charAt(0),f=f.substring(1),k=b(d,e)+b(e,f);k>=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]=
|
21 |
+
h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
|
22 |
+
diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;f<a.length;){if(0==a[f][0])a[f][1].length<this.Diff_EditCost&&(j||i)?(c[d++]=f,g=j,h=i,e=a[f][1]):(d=0,e=null),j=i=!1;else if(-1==a[f][0]?i=!0:j=!0,e&&(g&&h&&j&&i||e.length<this.Diff_EditCost/2&&3==g+h+j+i))a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,e=null,g&&h?(j=i=!0,d=0):(d--,f=0<d?c[d-1]:-1,j=i=!1),b=!0;f++}b&&this.diff_cleanupMerge(a)};
|
23 |
+
diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push([0,""]);for(var b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case 1:d++;f+=a[b][1];b++;break;case -1:c++;e+=a[b][1];b++;break;case 0:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&0==a[b-c-d-1][0]?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,[0,f.substring(0,g)]),b++),f=f.substring(g),e=e.substring(g)),g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-
|
24 |
+
g),e=e.substring(0,e.length-g))),0===c?a.splice(b-d,c+d,[1,f]):0===d?a.splice(b-c,c+d,[-1,e]):a.splice(b-c-d,c+d,[-1,e],[1,f]),b=b-c-d+(c?1:0)+(d?1:0)+1):0!==b&&0==a[b-1][0]?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)0==a[b-1][0]&&0==a[b+1][0]&&(a[b][1].substring(a[b][1].length-a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,
|
25 |
+
a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};diff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){1!==a[g][0]&&(c+=a[g][1].length);-1!==a[g][0]&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)};
|
26 |
+
diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var h=a[g][0],j=a[g][1],j=j.replace(c,"&").replace(d,"<").replace(e,">").replace(f,"¶<br>");switch(h){case 1:b[g]='<ins style="background:#e6ffe6;">'+j+"</ins>";break;case -1:b[g]='<del style="background:#ffe6e6;">'+j+"</del>";break;case 0:b[g]="<span>"+j+"</span>"}}return b.join("")};
|
27 |
+
diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)-1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][0],g=a[e][1];switch(f){case 1:c+=g.length;break;case -1:d+=g.length;break;case 0:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
|
28 |
+
diff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case 1:b[c]="+"+encodeURI(a[c][1]);break;case -1:b[c]="-"+a[c][1].length;break;case 0:b[c]="="+a[c][1].length}return b.join("\t").replace(/%20/g," ")};
|
29 |
+
diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case "+":try{c[d++]=[1,decodeURI(h)]}catch(j){throw Error("Illegal escape in diff_fromDelta: "+h);}break;case "-":case "=":var i=parseInt(h,10);if(isNaN(i)||0>i)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=i);"="==f[g].charAt(0)?c[d++]=[0,h]:c[d++]=[-1,h];break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+
|
30 |
+
f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
|
31 |
+
diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return!f.Match_Distance?g?1:e:e+g/f.Match_Distance}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<<b.length-1,h=-1,i,k,q=b.length+a.length,r,t=0;t<b.length;t++){i=0;for(k=q;i<k;)d(t,c+
|
32 |
+
k)<=g?i=k:q=k,k=Math.floor((q-i)/2+i);q=k;i=Math.max(1,c-k+1);var p=Math.min(c+k,a.length)+b.length;k=Array(p+2);for(k[p+1]=(1<<t)-1;p>=i;p--){var w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else break}if(d(t+1,c)>g)break;r=k}return h};
|
33 |
+
diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};
|
34 |
+
diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift([0,c]);(d=b.substring(a.start2+a.length1,a.start2+a.length1+d))&&a.diffs.push([0,d]);a.start1-=c.length;a.start2-=c.length;a.length1+=
|
35 |
+
c.length+d.length;a.length2+=c.length+d.length}};
|
36 |
+
diff_match_patch.prototype.patch_make=function(a,b,c){var d;if("string"==typeof a&&"string"==typeof b&&"undefined"==typeof c)d=a,b=this.diff_main(d,b,!0),2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b));else if(a&&"object"==typeof a&&"undefined"==typeof b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else if("string"==typeof a&&b&&"object"==typeof b&&"undefined"==typeof c)d=a;else if("string"==typeof a&&"string"==typeof b&&c&&"object"==typeof c)d=a,b=c;else throw Error("Unknown call format to patch_make.");
|
37 |
+
if(0===b.length)return[];c=[];a=new diff_match_patch.patch_obj;for(var e=0,f=0,g=0,h=d,j=0;j<b.length;j++){var i=b[j][0],k=b[j][1];!e&&0!==i&&(a.start1=f,a.start2=g);switch(i){case 1:a.diffs[e++]=b[j];a.length2+=k.length;d=d.substring(0,g)+k+d.substring(g);break;case -1:a.length1+=k.length;a.diffs[e++]=b[j];d=d.substring(0,g)+d.substring(g+k.length);break;case 0:k.length<=2*this.Patch_Margin&&e&&b.length!=j+1?(a.diffs[e++]=b[j],a.length1+=k.length,a.length2+=k.length):k.length>=2*this.Patch_Margin&&
|
38 |
+
e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=d.diffs[f].slice();e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};
|
39 |
+
diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];a=this.patch_deepCopy(a);var c=this.patch_addPadding(a);b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),j,i=-1;if(h.length>this.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g);
|
40 |
+
if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;i<a[f].diffs.length;i++){var q=a[f].diffs[i];0!==q[0]&&(k=this.diff_xIndex(g,h));1===q[0]?b=b.substring(0,
|
41 |
+
j+k)+q[1]+b.substring(j+k):-1===q[0]&&(b=b.substring(0,j+k)+b.substring(j+this.diff_xIndex(g,h+q[1].length)));-1!==q[0]&&(h+=q[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
|
42 |
+
diff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;var d=a[0],e=d.diffs;if(0==e.length||0!=e[0][0])e.unshift([0,c]),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0,
|
43 |
+
c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
|
44 |
+
diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,j=!0;h.start1=e-g.length;h.start2=f-g.length;""!==g&&(h.length1=h.length2=g.length,h.diffs.push([0,g]));for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){var g=d.diffs[0][0],i=d.diffs[0][1];1===g?(h.length2+=i.length,f+=i.length,h.diffs.push(d.diffs.shift()),
|
45 |
+
j=!1):-1===g&&1==h.diffs.length&&0==h.diffs[0][0]&&i.length>2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&&
|
46 |
+
(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
|
47 |
+
diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;a=a.split("\n");for(var c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid patch string: "+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
|
48 |
+
parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in patch_fromText: "+g);}if("-"==e)f.diffs.push([-1,g]);else if("+"==e)f.diffs.push([1,g]);else if(" "==e)f.diffs.push([0,g]);else if("@"==e)break;else if(""!==e)throw Error('Invalid patch mode "'+e+'" in: '+g);c++}}return b};diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
|
49 |
+
diff_match_patch.patch_obj.prototype.toString=function(){var a,b;a=0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1;b=0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2;a=["@@ -"+a+" +"+b+" @@\n"];var c;for(b=0;b<this.diffs.length;b++){switch(this.diffs[b][0]){case 1:c="+";break;case -1:c="-";break;case 0:c=" "}a[b+1]=c+encodeURI(this.diffs[b][1])+"\n"}return a.join("").replace(/%20/g," ")};
|
50 |
+
this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=-1;this.DIFF_INSERT=1;this.DIFF_EQUAL=0;})()
|
assets/lib/codemirror/addon/merge/merge.css
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.CodeMirror-merge {
|
2 |
+
position: relative;
|
3 |
+
border: 1px solid #ddd;
|
4 |
+
white-space: pre;
|
5 |
+
}
|
6 |
+
|
7 |
+
.CodeMirror-merge, .CodeMirror-merge .CodeMirror {
|
8 |
+
height: 350px;
|
9 |
+
}
|
10 |
+
|
11 |
+
.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; }
|
12 |
+
.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; }
|
13 |
+
.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }
|
14 |
+
.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }
|
15 |
+
|
16 |
+
.CodeMirror-merge-pane {
|
17 |
+
display: inline-block;
|
18 |
+
white-space: normal;
|
19 |
+
vertical-align: top;
|
20 |
+
}
|
21 |
+
.CodeMirror-merge-pane-rightmost {
|
22 |
+
position: absolute;
|
23 |
+
right: 0px;
|
24 |
+
z-index: 1;
|
25 |
+
}
|
26 |
+
|
27 |
+
.CodeMirror-merge-gap {
|
28 |
+
z-index: 2;
|
29 |
+
display: inline-block;
|
30 |
+
height: 100%;
|
31 |
+
-moz-box-sizing: border-box;
|
32 |
+
box-sizing: border-box;
|
33 |
+
overflow: hidden;
|
34 |
+
border-left: 1px solid #ddd;
|
35 |
+
border-right: 1px solid #ddd;
|
36 |
+
position: relative;
|
37 |
+
background: #f8f8f8;
|
38 |
+
}
|
39 |
+
|
40 |
+
.CodeMirror-merge-scrolllock-wrap {
|
41 |
+
position: absolute;
|
42 |
+
bottom: 0; left: 50%;
|
43 |
+
}
|
44 |
+
.CodeMirror-merge-scrolllock {
|
45 |
+
position: relative;
|
46 |
+
left: -50%;
|
47 |
+
cursor: pointer;
|
48 |
+
color: #555;
|
49 |
+
line-height: 1;
|
50 |
+
}
|
51 |
+
|
52 |
+
.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {
|
53 |
+
position: absolute;
|
54 |
+
left: 0; top: 0;
|
55 |
+
right: 0; bottom: 0;
|
56 |
+
line-height: 1;
|
57 |
+
}
|
58 |
+
|
59 |
+
.CodeMirror-merge-copy {
|
60 |
+
position: absolute;
|
61 |
+
cursor: pointer;
|
62 |
+
color: #44c;
|
63 |
+
}
|
64 |
+
|
65 |
+
.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
|
66 |
+
.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
|
67 |
+
|
68 |
+
.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {
|
69 |
+
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
|
70 |
+
background-position: bottom left;
|
71 |
+
background-repeat: repeat-x;
|
72 |
+
}
|
73 |
+
|
74 |
+
.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {
|
75 |
+
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
|
76 |
+
background-position: bottom left;
|
77 |
+
background-repeat: repeat-x;
|
78 |
+
}
|
79 |
+
|
80 |
+
.CodeMirror-merge-r-chunk { background: #ffffe0; }
|
81 |
+
.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; }
|
82 |
+
.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; }
|
83 |
+
.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; }
|
84 |
+
|
85 |
+
.CodeMirror-merge-l-chunk { background: #eef; }
|
86 |
+
.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }
|
87 |
+
.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }
|
88 |
+
.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }
|
89 |
+
|
90 |
+
.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
|
91 |
+
.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
|
92 |
+
.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
|
assets/lib/codemirror/addon/merge/merge.js
ADDED
@@ -0,0 +1,473 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
var Pos = CodeMirror.Pos;
|
5 |
+
var svgNS = "http://www.w3.org/2000/svg";
|
6 |
+
|
7 |
+
function DiffView(mv, type) {
|
8 |
+
this.mv = mv;
|
9 |
+
this.type = type;
|
10 |
+
this.classes = type == "left"
|
11 |
+
? {chunk: "CodeMirror-merge-l-chunk",
|
12 |
+
start: "CodeMirror-merge-l-chunk-start",
|
13 |
+
end: "CodeMirror-merge-l-chunk-end",
|
14 |
+
insert: "CodeMirror-merge-l-inserted",
|
15 |
+
del: "CodeMirror-merge-l-deleted",
|
16 |
+
connect: "CodeMirror-merge-l-connect"}
|
17 |
+
: {chunk: "CodeMirror-merge-r-chunk",
|
18 |
+
start: "CodeMirror-merge-r-chunk-start",
|
19 |
+
end: "CodeMirror-merge-r-chunk-end",
|
20 |
+
insert: "CodeMirror-merge-r-inserted",
|
21 |
+
del: "CodeMirror-merge-r-deleted",
|
22 |
+
connect: "CodeMirror-merge-r-connect"};
|
23 |
+
}
|
24 |
+
|
25 |
+
DiffView.prototype = {
|
26 |
+
constructor: DiffView,
|
27 |
+
init: function(pane, orig, options) {
|
28 |
+
this.edit = this.mv.edit;
|
29 |
+
this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: true}, copyObj(options)));
|
30 |
+
|
31 |
+
this.diff = getDiff(orig, options.value);
|
32 |
+
this.diffOutOfDate = false;
|
33 |
+
|
34 |
+
this.showDifferences = options.showDifferences !== false;
|
35 |
+
this.forceUpdate = registerUpdate(this);
|
36 |
+
setScrollLock(this, true, false);
|
37 |
+
registerScroll(this);
|
38 |
+
},
|
39 |
+
setShowDifferences: function(val) {
|
40 |
+
val = val !== false;
|
41 |
+
if (val != this.showDifferences) {
|
42 |
+
this.showDifferences = val;
|
43 |
+
this.forceUpdate("full");
|
44 |
+
}
|
45 |
+
}
|
46 |
+
};
|
47 |
+
|
48 |
+
function registerUpdate(dv) {
|
49 |
+
var edit = {from: 0, to: 0, marked: []};
|
50 |
+
var orig = {from: 0, to: 0, marked: []};
|
51 |
+
var debounceChange;
|
52 |
+
function update(mode) {
|
53 |
+
if (mode == "full") {
|
54 |
+
if (dv.svg) clear(dv.svg);
|
55 |
+
clear(dv.copyButtons);
|
56 |
+
clearMarks(dv.edit, edit.marked, dv.classes);
|
57 |
+
clearMarks(dv.orig, orig.marked, dv.classes);
|
58 |
+
edit.from = edit.to = orig.from = orig.to = 0;
|
59 |
+
}
|
60 |
+
if (dv.diffOutOfDate) {
|
61 |
+
dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue());
|
62 |
+
dv.diffOutOfDate = false;
|
63 |
+
CodeMirror.signal(dv.edit, "updateDiff", dv.diff);
|
64 |
+
}
|
65 |
+
if (dv.showDifferences) {
|
66 |
+
updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes);
|
67 |
+
updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);
|
68 |
+
}
|
69 |
+
drawConnectors(dv);
|
70 |
+
}
|
71 |
+
function set(slow) {
|
72 |
+
clearTimeout(debounceChange);
|
73 |
+
debounceChange = setTimeout(update, slow == true ? 250 : 100);
|
74 |
+
}
|
75 |
+
function change() {
|
76 |
+
if (!dv.diffOutOfDate) {
|
77 |
+
dv.diffOutOfDate = true;
|
78 |
+
edit.from = edit.to = orig.from = orig.to = 0;
|
79 |
+
}
|
80 |
+
set(true);
|
81 |
+
}
|
82 |
+
dv.edit.on("change", change);
|
83 |
+
dv.orig.on("change", change);
|
84 |
+
dv.edit.on("viewportChange", set);
|
85 |
+
dv.orig.on("viewportChange", set);
|
86 |
+
update();
|
87 |
+
return update;
|
88 |
+
}
|
89 |
+
|
90 |
+
function registerScroll(dv) {
|
91 |
+
dv.edit.on("scroll", function() {
|
92 |
+
syncScroll(dv, DIFF_INSERT) && drawConnectors(dv);
|
93 |
+
});
|
94 |
+
dv.orig.on("scroll", function() {
|
95 |
+
syncScroll(dv, DIFF_DELETE) && drawConnectors(dv);
|
96 |
+
});
|
97 |
+
}
|
98 |
+
|
99 |
+
function syncScroll(dv, type) {
|
100 |
+
// Change handler will do a refresh after a timeout when diff is out of date
|
101 |
+
if (dv.diffOutOfDate) return false;
|
102 |
+
if (!dv.lockScroll) return true;
|
103 |
+
var editor, other, now = +new Date;
|
104 |
+
if (type == DIFF_INSERT) { editor = dv.edit; other = dv.orig; }
|
105 |
+
else { editor = dv.orig; other = dv.edit; }
|
106 |
+
// Don't take action if the position of this editor was recently set
|
107 |
+
// (to prevent feedback loops)
|
108 |
+
if (editor.state.scrollSetBy == dv && (editor.state.scrollSetAt || 0) + 50 > now) return false;
|
109 |
+
|
110 |
+
var sInfo = editor.getScrollInfo(), halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen;
|
111 |
+
var mid = editor.lineAtHeight(midY, "local");
|
112 |
+
var around = chunkBoundariesAround(dv.diff, mid, type == DIFF_INSERT);
|
113 |
+
var off = getOffsets(editor, type == DIFF_INSERT ? around.edit : around.orig);
|
114 |
+
var offOther = getOffsets(other, type == DIFF_INSERT ? around.orig : around.edit);
|
115 |
+
var ratio = (midY - off.top) / (off.bot - off.top);
|
116 |
+
var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top);
|
117 |
+
|
118 |
+
var botDist, mix;
|
119 |
+
// Some careful tweaking to make sure no space is left out of view
|
120 |
+
// when scrolling to top or bottom.
|
121 |
+
if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) {
|
122 |
+
targetPos = targetPos * mix + sInfo.top * (1 - mix);
|
123 |
+
} else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) {
|
124 |
+
var otherInfo = other.getScrollInfo();
|
125 |
+
var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos;
|
126 |
+
if (botDistOther > botDist && (mix = botDist / halfScreen) < 1)
|
127 |
+
targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix);
|
128 |
+
}
|
129 |
+
|
130 |
+
other.scrollTo(sInfo.left, targetPos);
|
131 |
+
other.state.scrollSetAt = now;
|
132 |
+
other.state.scrollSetBy = dv;
|
133 |
+
return true;
|
134 |
+
}
|
135 |
+
|
136 |
+
function getOffsets(editor, around) {
|
137 |
+
var bot = around.after;
|
138 |
+
if (bot == null) bot = editor.lastLine() + 1;
|
139 |
+
return {top: editor.heightAtLine(around.before || 0, "local"),
|
140 |
+
bot: editor.heightAtLine(bot, "local")};
|
141 |
+
}
|
142 |
+
|
143 |
+
function setScrollLock(dv, val, action) {
|
144 |
+
dv.lockScroll = val;
|
145 |
+
if (val && action != false) syncScroll(dv, DIFF_INSERT) && drawConnectors(dv);
|
146 |
+
dv.lockButton.innerHTML = val ? "\u21db\u21da" : "\u21db \u21da";
|
147 |
+
}
|
148 |
+
|
149 |
+
// Updating the marks for editor content
|
150 |
+
|
151 |
+
function clearMarks(editor, arr, classes) {
|
152 |
+
for (var i = 0; i < arr.length; ++i) {
|
153 |
+
var mark = arr[i];
|
154 |
+
if (mark instanceof CodeMirror.TextMarker) {
|
155 |
+
mark.clear();
|
156 |
+
} else {
|
157 |
+
editor.removeLineClass(mark, "background", classes.chunk);
|
158 |
+
editor.removeLineClass(mark, "background", classes.start);
|
159 |
+
editor.removeLineClass(mark, "background", classes.end);
|
160 |
+
}
|
161 |
+
}
|
162 |
+
arr.length = 0;
|
163 |
+
}
|
164 |
+
|
165 |
+
// FIXME maybe add a margin around viewport to prevent too many updates
|
166 |
+
function updateMarks(editor, diff, state, type, classes) {
|
167 |
+
var vp = editor.getViewport();
|
168 |
+
editor.operation(function() {
|
169 |
+
if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
|
170 |
+
clearMarks(editor, state.marked, classes);
|
171 |
+
markChanges(editor, diff, type, state.marked, vp.from, vp.to, classes);
|
172 |
+
state.from = vp.from; state.to = vp.to;
|
173 |
+
} else {
|
174 |
+
if (vp.from < state.from) {
|
175 |
+
markChanges(editor, diff, type, state.marked, vp.from, state.from, classes);
|
176 |
+
state.from = vp.from;
|
177 |
+
}
|
178 |
+
if (vp.to > state.to) {
|
179 |
+
markChanges(editor, diff, type, state.marked, state.to, vp.to, classes);
|
180 |
+
state.to = vp.to;
|
181 |
+
}
|
182 |
+
}
|
183 |
+
});
|
184 |
+
}
|
185 |
+
|
186 |
+
function markChanges(editor, diff, type, marks, from, to, classes) {
|
187 |
+
var pos = Pos(0, 0);
|
188 |
+
var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1));
|
189 |
+
var cls = type == DIFF_DELETE ? classes.del : classes.insert;
|
190 |
+
function markChunk(start, end) {
|
191 |
+
var bfrom = Math.max(from, start), bto = Math.min(to, end);
|
192 |
+
for (var i = bfrom; i < bto; ++i) {
|
193 |
+
var line = editor.addLineClass(i, "background", classes.chunk);
|
194 |
+
if (i == start) editor.addLineClass(line, "background", classes.start);
|
195 |
+
if (i == end - 1) editor.addLineClass(line, "background", classes.end);
|
196 |
+
marks.push(line);
|
197 |
+
}
|
198 |
+
// When the chunk is empty, make sure a horizontal line shows up
|
199 |
+
if (start == end && bfrom == end && bto == end) {
|
200 |
+
if (bfrom)
|
201 |
+
marks.push(editor.addLineClass(bfrom - 1, "background", classes.end));
|
202 |
+
else
|
203 |
+
marks.push(editor.addLineClass(bfrom, "background", classes.start));
|
204 |
+
}
|
205 |
+
}
|
206 |
+
|
207 |
+
var chunkStart = 0;
|
208 |
+
for (var i = 0; i < diff.length; ++i) {
|
209 |
+
var part = diff[i], tp = part[0], str = part[1];
|
210 |
+
if (tp == DIFF_EQUAL) {
|
211 |
+
var cleanFrom = pos.line + (startOfLineClean(diff, i) ? 0 : 1);
|
212 |
+
moveOver(pos, str);
|
213 |
+
var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0);
|
214 |
+
if (cleanTo > cleanFrom) {
|
215 |
+
if (i) markChunk(chunkStart, cleanFrom);
|
216 |
+
chunkStart = cleanTo;
|
217 |
+
}
|
218 |
+
} else {
|
219 |
+
if (tp == type) {
|
220 |
+
var end = moveOver(pos, str, true);
|
221 |
+
var a = posMax(top, pos), b = posMin(bot, end);
|
222 |
+
if (!posEq(a, b))
|
223 |
+
marks.push(editor.markText(a, b, {className: cls}));
|
224 |
+
pos = end;
|
225 |
+
}
|
226 |
+
}
|
227 |
+
}
|
228 |
+
if (chunkStart <= pos.line) markChunk(chunkStart, pos.line + 1);
|
229 |
+
}
|
230 |
+
|
231 |
+
// Updating the gap between editor and original
|
232 |
+
|
233 |
+
function drawConnectors(dv) {
|
234 |
+
if (!dv.showDifferences) return;
|
235 |
+
|
236 |
+
if (dv.svg) {
|
237 |
+
clear(dv.svg);
|
238 |
+
var w = dv.gap.offsetWidth;
|
239 |
+
attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight);
|
240 |
+
}
|
241 |
+
clear(dv.copyButtons);
|
242 |
+
|
243 |
+
var flip = dv.type == "left";
|
244 |
+
var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
|
245 |
+
var sTopEdit = dv.edit.getScrollInfo().top, sTopOrig = dv.orig.getScrollInfo().top;
|
246 |
+
iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) {
|
247 |
+
if (topEdit > vpEdit.to || botEdit < vpEdit.from ||
|
248 |
+
topOrig > vpOrig.to || botOrig < vpOrig.from)
|
249 |
+
return;
|
250 |
+
var topLpx = dv.orig.heightAtLine(topOrig, "local") - sTopOrig, top = topLpx;
|
251 |
+
if (dv.svg) {
|
252 |
+
var topRpx = dv.edit.heightAtLine(topEdit, "local") - sTopEdit;
|
253 |
+
if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; }
|
254 |
+
var botLpx = dv.orig.heightAtLine(botOrig, "local") - sTopOrig;
|
255 |
+
var botRpx = dv.edit.heightAtLine(botEdit, "local") - sTopEdit;
|
256 |
+
if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; }
|
257 |
+
var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx;
|
258 |
+
var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx;
|
259 |
+
attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")),
|
260 |
+
"d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z",
|
261 |
+
"class", dv.classes.connect);
|
262 |
+
}
|
263 |
+
var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc",
|
264 |
+
"CodeMirror-merge-copy"));
|
265 |
+
copy.title = "Revert chunk";
|
266 |
+
copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig};
|
267 |
+
copy.style.top = top + "px";
|
268 |
+
});
|
269 |
+
}
|
270 |
+
|
271 |
+
function copyChunk(dv, chunk) {
|
272 |
+
if (dv.diffOutOfDate) return;
|
273 |
+
dv.edit.replaceRange(dv.orig.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)),
|
274 |
+
Pos(chunk.topEdit, 0), Pos(chunk.botEdit, 0));
|
275 |
+
}
|
276 |
+
|
277 |
+
// Merge view, containing 0, 1, or 2 diff views.
|
278 |
+
|
279 |
+
var MergeView = CodeMirror.MergeView = function(node, options) {
|
280 |
+
if (!(this instanceof MergeView)) return new MergeView(node, options);
|
281 |
+
|
282 |
+
var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight;
|
283 |
+
var hasLeft = origLeft != null, hasRight = origRight != null;
|
284 |
+
var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0);
|
285 |
+
var wrap = [], left = this.left = null, right = this.right = null;
|
286 |
+
|
287 |
+
if (hasLeft) {
|
288 |
+
left = this.left = new DiffView(this, "left");
|
289 |
+
var leftPane = elt("div", null, "CodeMirror-merge-pane");
|
290 |
+
wrap.push(leftPane);
|
291 |
+
wrap.push(buildGap(left));
|
292 |
+
}
|
293 |
+
|
294 |
+
var editPane = elt("div", null, "CodeMirror-merge-pane");
|
295 |
+
wrap.push(editPane);
|
296 |
+
|
297 |
+
if (hasRight) {
|
298 |
+
right = this.right = new DiffView(this, "right");
|
299 |
+
wrap.push(buildGap(right));
|
300 |
+
var rightPane = elt("div", null, "CodeMirror-merge-pane");
|
301 |
+
wrap.push(rightPane);
|
302 |
+
}
|
303 |
+
|
304 |
+
(hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost";
|
305 |
+
|
306 |
+
wrap.push(elt("div", null, null, "height: 0; clear: both;"));
|
307 |
+
var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane"));
|
308 |
+
this.edit = CodeMirror(editPane, copyObj(options));
|
309 |
+
|
310 |
+
if (left) left.init(leftPane, origLeft, options);
|
311 |
+
if (right) right.init(rightPane, origRight, options);
|
312 |
+
|
313 |
+
var onResize = function() {
|
314 |
+
if (left) drawConnectors(left);
|
315 |
+
if (right) drawConnectors(right);
|
316 |
+
};
|
317 |
+
CodeMirror.on(window, "resize", onResize);
|
318 |
+
var resizeInterval = setInterval(function() {
|
319 |
+
for (var p = wrapElt.parentNode; p && p != document.body; p = p.parentNode) {}
|
320 |
+
if (!p) { clearInterval(resizeInterval); CodeMirror.off(window, "resize", onResize); }
|
321 |
+
}, 5000);
|
322 |
+
};
|
323 |
+
|
324 |
+
function buildGap(dv) {
|
325 |
+
var lock = dv.lockButton = elt("div", null, "CodeMirror-merge-scrolllock");
|
326 |
+
lock.title = "Toggle locked scrolling";
|
327 |
+
var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap");
|
328 |
+
CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); });
|
329 |
+
dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type);
|
330 |
+
CodeMirror.on(dv.copyButtons, "click", function(e) {
|
331 |
+
var node = e.target || e.srcElement;
|
332 |
+
if (node.chunk) copyChunk(dv, node.chunk);
|
333 |
+
});
|
334 |
+
var gapElts = [dv.copyButtons, lockWrap];
|
335 |
+
var svg = document.createElementNS && document.createElementNS(svgNS, "svg");
|
336 |
+
if (svg && !svg.createSVGRect) svg = null;
|
337 |
+
dv.svg = svg;
|
338 |
+
if (svg) gapElts.push(svg);
|
339 |
+
|
340 |
+
return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap");
|
341 |
+
}
|
342 |
+
|
343 |
+
MergeView.prototype = {
|
344 |
+
constuctor: MergeView,
|
345 |
+
editor: function() { return this.edit; },
|
346 |
+
rightOriginal: function() { return this.right && this.right.orig; },
|
347 |
+
leftOriginal: function() { return this.left && this.left.orig; },
|
348 |
+
setShowDifferences: function(val) {
|
349 |
+
if (this.right) this.right.setShowDifferences(val);
|
350 |
+
if (this.left) this.left.setShowDifferences(val);
|
351 |
+
}
|
352 |
+
};
|
353 |
+
|
354 |
+
// Operations on diffs
|
355 |
+
|
356 |
+
var dmp = new diff_match_patch();
|
357 |
+
function getDiff(a, b) {
|
358 |
+
var diff = dmp.diff_main(a, b);
|
359 |
+
dmp.diff_cleanupSemantic(diff);
|
360 |
+
// The library sometimes leaves in empty parts, which confuse the algorithm
|
361 |
+
for (var i = 0; i < diff.length; ++i) {
|
362 |
+
var part = diff[i];
|
363 |
+
if (!part[1]) {
|
364 |
+
diff.splice(i--, 1);
|
365 |
+
} else if (i && diff[i - 1][0] == part[0]) {
|
366 |
+
diff.splice(i--, 1);
|
367 |
+
diff[i][1] += part[1];
|
368 |
+
}
|
369 |
+
}
|
370 |
+
return diff;
|
371 |
+
}
|
372 |
+
|
373 |
+
function iterateChunks(diff, f) {
|
374 |
+
var startEdit = 0, startOrig = 0;
|
375 |
+
var edit = Pos(0, 0), orig = Pos(0, 0);
|
376 |
+
for (var i = 0; i < diff.length; ++i) {
|
377 |
+
var part = diff[i], tp = part[0];
|
378 |
+
if (tp == DIFF_EQUAL) {
|
379 |
+
var startOff = startOfLineClean(diff, i) ? 0 : 1;
|
380 |
+
var cleanFromEdit = edit.line + startOff, cleanFromOrig = orig.line + startOff;
|
381 |
+
moveOver(edit, part[1], null, orig);
|
382 |
+
var endOff = endOfLineClean(diff, i) ? 1 : 0;
|
383 |
+
var cleanToEdit = edit.line + endOff, cleanToOrig = orig.line + endOff;
|
384 |
+
if (cleanToEdit > cleanFromEdit) {
|
385 |
+
if (i) f(startOrig, cleanFromOrig, startEdit, cleanFromEdit);
|
386 |
+
startEdit = cleanToEdit; startOrig = cleanToOrig;
|
387 |
+
}
|
388 |
+
} else {
|
389 |
+
moveOver(tp == DIFF_INSERT ? edit : orig, part[1]);
|
390 |
+
}
|
391 |
+
}
|
392 |
+
if (startEdit <= edit.line || startOrig <= orig.line)
|
393 |
+
f(startOrig, orig.line + 1, startEdit, edit.line + 1);
|
394 |
+
}
|
395 |
+
|
396 |
+
function endOfLineClean(diff, i) {
|
397 |
+
if (i == diff.length - 1) return true;
|
398 |
+
var next = diff[i + 1][1];
|
399 |
+
if (next.length == 1 || next.charCodeAt(0) != 10) return false;
|
400 |
+
if (i == diff.length - 2) return true;
|
401 |
+
next = diff[i + 2][1];
|
402 |
+
return next.length > 1 && next.charCodeAt(0) == 10;
|
403 |
+
}
|
404 |
+
|
405 |
+
function startOfLineClean(diff, i) {
|
406 |
+
if (i == 0) return true;
|
407 |
+
var last = diff[i - 1][1];
|
408 |
+
if (last.charCodeAt(last.length - 1) != 10) return false;
|
409 |
+
if (i == 1) return true;
|
410 |
+
last = diff[i - 2][1];
|
411 |
+
return last.charCodeAt(last.length - 1) == 10;
|
412 |
+
}
|
413 |
+
|
414 |
+
function chunkBoundariesAround(diff, n, nInEdit) {
|
415 |
+
var beforeE, afterE, beforeO, afterO;
|
416 |
+
iterateChunks(diff, function(fromOrig, toOrig, fromEdit, toEdit) {
|
417 |
+
var fromLocal = nInEdit ? fromEdit : fromOrig;
|
418 |
+
var toLocal = nInEdit ? toEdit : toOrig;
|
419 |
+
if (afterE == null) {
|
420 |
+
if (fromLocal > n) { afterE = fromEdit; afterO = fromOrig; }
|
421 |
+
else if (toLocal > n) { afterE = toEdit; afterO = toOrig; }
|
422 |
+
}
|
423 |
+
if (toLocal <= n) { beforeE = toEdit; beforeO = toOrig; }
|
424 |
+
else if (fromLocal <= n) { beforeE = fromEdit; beforeO = fromOrig; }
|
425 |
+
});
|
426 |
+
return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}};
|
427 |
+
}
|
428 |
+
|
429 |
+
// General utilities
|
430 |
+
|
431 |
+
function elt(tag, content, className, style) {
|
432 |
+
var e = document.createElement(tag);
|
433 |
+
if (className) e.className = className;
|
434 |
+
if (style) e.style.cssText = style;
|
435 |
+
if (typeof content == "string") e.appendChild(document.createTextNode(content));
|
436 |
+
else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
|
437 |
+
return e;
|
438 |
+
}
|
439 |
+
|
440 |
+
function clear(node) {
|
441 |
+
for (var count = node.childNodes.length; count > 0; --count)
|
442 |
+
node.removeChild(node.firstChild);
|
443 |
+
}
|
444 |
+
|
445 |
+
function attrs(elt) {
|
446 |
+
for (var i = 1; i < arguments.length; i += 2)
|
447 |
+
elt.setAttribute(arguments[i], arguments[i+1]);
|
448 |
+
}
|
449 |
+
|
450 |
+
function copyObj(obj, target) {
|
451 |
+
if (!target) target = {};
|
452 |
+
for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
|
453 |
+
return target;
|
454 |
+
}
|
455 |
+
|
456 |
+
function moveOver(pos, str, copy, other) {
|
457 |
+
var out = copy ? Pos(pos.line, pos.ch) : pos, at = 0;
|
458 |
+
for (;;) {
|
459 |
+
var nl = str.indexOf("\n", at);
|
460 |
+
if (nl == -1) break;
|
461 |
+
++out.line;
|
462 |
+
if (other) ++other.line;
|
463 |
+
at = nl + 1;
|
464 |
+
}
|
465 |
+
out.ch = (at ? 0 : out.ch) + (str.length - at);
|
466 |
+
if (other) other.ch = (at ? 0 : other.ch) + (str.length - at);
|
467 |
+
return out;
|
468 |
+
}
|
469 |
+
|
470 |
+
function posMin(a, b) { return (a.line - b.line || a.ch - b.ch) < 0 ? a : b; }
|
471 |
+
function posMax(a, b) { return (a.line - b.line || a.ch - b.ch) > 0 ? a : b; }
|
472 |
+
function posEq(a, b) { return a.line == b.line && a.ch == b.ch; }
|
473 |
+
})();
|
assets/lib/codemirror/addon/mode/loadmode.js
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js";
|
3 |
+
|
4 |
+
var loading = {};
|
5 |
+
function splitCallback(cont, n) {
|
6 |
+
var countDown = n;
|
7 |
+
return function() { if (--countDown == 0) cont(); };
|
8 |
+
}
|
9 |
+
function ensureDeps(mode, cont) {
|
10 |
+
var deps = CodeMirror.modes[mode].dependencies;
|
11 |
+
if (!deps) return cont();
|
12 |
+
var missing = [];
|
13 |
+
for (var i = 0; i < deps.length; ++i) {
|
14 |
+
if (!CodeMirror.modes.hasOwnProperty(deps[i]))
|
15 |
+
missing.push(deps[i]);
|
16 |
+
}
|
17 |
+
if (!missing.length) return cont();
|
18 |
+
var split = splitCallback(cont, missing.length);
|
19 |
+
for (var i = 0; i < missing.length; ++i)
|
20 |
+
CodeMirror.requireMode(missing[i], split);
|
21 |
+
}
|
22 |
+
|
23 |
+
CodeMirror.requireMode = function(mode, cont) {
|
24 |
+
if (typeof mode != "string") mode = mode.name;
|
25 |
+
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont);
|
26 |
+
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);
|
27 |
+
|
28 |
+
var script = document.createElement("script");
|
29 |
+
script.src = CodeMirror.modeURL.replace(/%N/g, mode);
|
30 |
+
var others = document.getElementsByTagName("script")[0];
|
31 |
+
others.parentNode.insertBefore(script, others);
|
32 |
+
var list = loading[mode] = [cont];
|
33 |
+
var count = 0, poll = setInterval(function() {
|
34 |
+
if (++count > 100) return clearInterval(poll);
|
35 |
+
if (CodeMirror.modes.hasOwnProperty(mode)) {
|
36 |
+
clearInterval(poll);
|
37 |
+
loading[mode] = null;
|
38 |
+
ensureDeps(mode, function() {
|
39 |
+
for (var i = 0; i < list.length; ++i) list[i]();
|
40 |
+
});
|
41 |
+
}
|
42 |
+
}, 200);
|
43 |
+
};
|
44 |
+
|
45 |
+
CodeMirror.autoLoadMode = function(instance, mode) {
|
46 |
+
if (!CodeMirror.modes.hasOwnProperty(mode))
|
47 |
+
CodeMirror.requireMode(mode, function() {
|
48 |
+
instance.setOption("mode", instance.getOption("mode"));
|
49 |
+
});
|
50 |
+
};
|
51 |
+
}());
|
assets/lib/codemirror/addon/mode/multiplex.js
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.multiplexingMode = function(outer /*, others */) {
|
2 |
+
// Others should be {open, close, mode [, delimStyle] [, innerStyle]} objects
|
3 |
+
var others = Array.prototype.slice.call(arguments, 1);
|
4 |
+
var n_others = others.length;
|
5 |
+
|
6 |
+
function indexOf(string, pattern, from) {
|
7 |
+
if (typeof pattern == "string") return string.indexOf(pattern, from);
|
8 |
+
var m = pattern.exec(from ? string.slice(from) : string);
|
9 |
+
return m ? m.index + from : -1;
|
10 |
+
}
|
11 |
+
|
12 |
+
return {
|
13 |
+
startState: function() {
|
14 |
+
return {
|
15 |
+
outer: CodeMirror.startState(outer),
|
16 |
+
innerActive: null,
|
17 |
+
inner: null
|
18 |
+
};
|
19 |
+
},
|
20 |
+
|
21 |
+
copyState: function(state) {
|
22 |
+
return {
|
23 |
+
outer: CodeMirror.copyState(outer, state.outer),
|
24 |
+
innerActive: state.innerActive,
|
25 |
+
inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner)
|
26 |
+
};
|
27 |
+
},
|
28 |
+
|
29 |
+
token: function(stream, state) {
|
30 |
+
if (!state.innerActive) {
|
31 |
+
var cutOff = Infinity, oldContent = stream.string;
|
32 |
+
for (var i = 0; i < n_others; ++i) {
|
33 |
+
var other = others[i];
|
34 |
+
var found = indexOf(oldContent, other.open, stream.pos);
|
35 |
+
if (found == stream.pos) {
|
36 |
+
stream.match(other.open);
|
37 |
+
state.innerActive = other;
|
38 |
+
state.inner = CodeMirror.startState(other.mode, outer.indent ? outer.indent(state.outer, "") : 0);
|
39 |
+
return other.delimStyle;
|
40 |
+
} else if (found != -1 && found < cutOff) {
|
41 |
+
cutOff = found;
|
42 |
+
}
|
43 |
+
}
|
44 |
+
if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff);
|
45 |
+
var outerToken = outer.token(stream, state.outer);
|
46 |
+
if (cutOff != Infinity) stream.string = oldContent;
|
47 |
+
return outerToken;
|
48 |
+
} else {
|
49 |
+
var curInner = state.innerActive, oldContent = stream.string;
|
50 |
+
var found = indexOf(oldContent, curInner.close, stream.pos);
|
51 |
+
if (found == stream.pos) {
|
52 |
+
stream.match(curInner.close);
|
53 |
+
state.innerActive = state.inner = null;
|
54 |
+
return curInner.delimStyle;
|
55 |
+
}
|
56 |
+
if (found > -1) stream.string = oldContent.slice(0, found);
|
57 |
+
var innerToken = curInner.mode.token(stream, state.inner);
|
58 |
+
if (found > -1) stream.string = oldContent;
|
59 |
+
var cur = stream.current(), found = cur.indexOf(curInner.close);
|
60 |
+
if (found > -1) stream.backUp(cur.length - found);
|
61 |
+
|
62 |
+
if (curInner.innerStyle) {
|
63 |
+
if (innerToken) innerToken = innerToken + ' ' + curInner.innerStyle;
|
64 |
+
else innerToken = curInner.innerStyle;
|
65 |
+
}
|
66 |
+
|
67 |
+
return innerToken;
|
68 |
+
}
|
69 |
+
},
|
70 |
+
|
71 |
+
indent: function(state, textAfter) {
|
72 |
+
var mode = state.innerActive ? state.innerActive.mode : outer;
|
73 |
+
if (!mode.indent) return CodeMirror.Pass;
|
74 |
+
return mode.indent(state.innerActive ? state.inner : state.outer, textAfter);
|
75 |
+
},
|
76 |
+
|
77 |
+
blankLine: function(state) {
|
78 |
+
var mode = state.innerActive ? state.innerActive.mode : outer;
|
79 |
+
if (mode.blankLine) {
|
80 |
+
mode.blankLine(state.innerActive ? state.inner : state.outer);
|
81 |
+
}
|
82 |
+
if (!state.innerActive) {
|
83 |
+
for (var i = 0; i < n_others; ++i) {
|
84 |
+
var other = others[i];
|
85 |
+
if (other.open === "\n") {
|
86 |
+
state.innerActive = other;
|
87 |
+
state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0);
|
88 |
+
}
|
89 |
+
}
|
90 |
+
} else if (state.innerActive.close === "\n") {
|
91 |
+
state.innerActive = state.inner = null;
|
92 |
+
}
|
93 |
+
},
|
94 |
+
|
95 |
+
electricChars: outer.electricChars,
|
96 |
+
|
97 |
+
innerMode: function(state) {
|
98 |
+
return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer};
|
99 |
+
}
|
100 |
+
};
|
101 |
+
};
|
assets/lib/codemirror/addon/mode/multiplex_test.js
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
CodeMirror.defineMode("markdown_with_stex", function(){
|
3 |
+
var inner = CodeMirror.getMode({}, "stex");
|
4 |
+
var outer = CodeMirror.getMode({}, "markdown");
|
5 |
+
|
6 |
+
var innerOptions = {
|
7 |
+
open: '$',
|
8 |
+
close: '$',
|
9 |
+
mode: inner,
|
10 |
+
delimStyle: 'delim',
|
11 |
+
innerStyle: 'inner'
|
12 |
+
};
|
13 |
+
|
14 |
+
return CodeMirror.multiplexingMode(outer, innerOptions);
|
15 |
+
});
|
16 |
+
|
17 |
+
var mode = CodeMirror.getMode({}, "markdown_with_stex");
|
18 |
+
|
19 |
+
function MT(name) {
|
20 |
+
test.mode(
|
21 |
+
name,
|
22 |
+
mode,
|
23 |
+
Array.prototype.slice.call(arguments, 1),
|
24 |
+
'multiplexing');
|
25 |
+
}
|
26 |
+
|
27 |
+
MT(
|
28 |
+
"stexInsideMarkdown",
|
29 |
+
"[strong **Equation:**] [delim $][inner&tag \\pi][delim $]");
|
30 |
+
})();
|
assets/lib/codemirror/addon/mode/overlay.js
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Utility function that allows modes to be combined. The mode given
|
2 |
+
// as the base argument takes care of most of the normal mode
|
3 |
+
// functionality, but a second (typically simple) mode is used, which
|
4 |
+
// can override the style of text. Both modes get to parse all of the
|
5 |
+
// text, but when both assign a non-null style to a piece of code, the
|
6 |
+
// overlay wins, unless the combine argument was true, in which case
|
7 |
+
// the styles are combined.
|
8 |
+
|
9 |
+
// overlayParser is the old, deprecated name
|
10 |
+
CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, combine) {
|
11 |
+
return {
|
12 |
+
startState: function() {
|
13 |
+
return {
|
14 |
+
base: CodeMirror.startState(base),
|
15 |
+
overlay: CodeMirror.startState(overlay),
|
16 |
+
basePos: 0, baseCur: null,
|
17 |
+
overlayPos: 0, overlayCur: null
|
18 |
+
};
|
19 |
+
},
|
20 |
+
copyState: function(state) {
|
21 |
+
return {
|
22 |
+
base: CodeMirror.copyState(base, state.base),
|
23 |
+
overlay: CodeMirror.copyState(overlay, state.overlay),
|
24 |
+
basePos: state.basePos, baseCur: null,
|
25 |
+
overlayPos: state.overlayPos, overlayCur: null
|
26 |
+
};
|
27 |
+
},
|
28 |
+
|
29 |
+
token: function(stream, state) {
|
30 |
+
if (stream.start == state.basePos) {
|
31 |
+
state.baseCur = base.token(stream, state.base);
|
32 |
+
state.basePos = stream.pos;
|
33 |
+
}
|
34 |
+
if (stream.start == state.overlayPos) {
|
35 |
+
stream.pos = stream.start;
|
36 |
+
state.overlayCur = overlay.token(stream, state.overlay);
|
37 |
+
state.overlayPos = stream.pos;
|
38 |
+
}
|
39 |
+
stream.pos = Math.min(state.basePos, state.overlayPos);
|
40 |
+
if (stream.eol()) state.basePos = state.overlayPos = 0;
|
41 |
+
|
42 |
+
if (state.overlayCur == null) return state.baseCur;
|
43 |
+
if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur;
|
44 |
+
else return state.overlayCur;
|
45 |
+
},
|
46 |
+
|
47 |
+
indent: base.indent && function(state, textAfter) {
|
48 |
+
return base.indent(state.base, textAfter);
|
49 |
+
},
|
50 |
+
electricChars: base.electricChars,
|
51 |
+
|
52 |
+
innerMode: function(state) { return {state: state.base, mode: base}; },
|
53 |
+
|
54 |
+
blankLine: function(state) {
|
55 |
+
if (base.blankLine) base.blankLine(state.base);
|
56 |
+
if (overlay.blankLine) overlay.blankLine(state.overlay);
|
57 |
+
}
|
58 |
+
};
|
59 |
+
};
|
assets/lib/codemirror/addon/runmode/colorize.js
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.colorize = (function() {
|
2 |
+
|
3 |
+
var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/;
|
4 |
+
|
5 |
+
function textContent(node, out) {
|
6 |
+
if (node.nodeType == 3) return out.push(node.nodeValue);
|
7 |
+
for (var ch = node.firstChild; ch; ch = ch.nextSibling) {
|
8 |
+
textContent(ch, out);
|
9 |
+
if (isBlock.test(node.nodeType)) out.push("\n");
|
10 |
+
}
|
11 |
+
}
|
12 |
+
|
13 |
+
return function(collection, defaultMode) {
|
14 |
+
if (!collection) collection = document.body.getElementsByTagName("pre");
|
15 |
+
|
16 |
+
for (var i = 0; i < collection.length; ++i) {
|
17 |
+
var node = collection[i];
|
18 |
+
var mode = node.getAttribute("data-lang") || defaultMode;
|
19 |
+
if (!mode) continue;
|
20 |
+
|
21 |
+
var text = [];
|
22 |
+
textContent(node, text);
|
23 |
+
node.innerHTML = "";
|
24 |
+
CodeMirror.runMode(text.join(""), mode, node);
|
25 |
+
|
26 |
+
node.className += " cm-s-default";
|
27 |
+
}
|
28 |
+
};
|
29 |
+
})();
|
assets/lib/codemirror/addon/runmode/runmode-standalone.js
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Just enough of CodeMirror to run runMode under node.js */
|
2 |
+
|
3 |
+
window.CodeMirror = {};
|
4 |
+
|
5 |
+
function splitLines(string){ return string.split(/\r?\n|\r/); };
|
6 |
+
|
7 |
+
function StringStream(string) {
|
8 |
+
this.pos = this.start = 0;
|
9 |
+
this.string = string;
|
10 |
+
}
|
11 |
+
StringStream.prototype = {
|
12 |
+
eol: function() {return this.pos >= this.string.length;},
|
13 |
+
sol: function() {return this.pos == 0;},
|
14 |
+
peek: function() {return this.string.charAt(this.pos) || null;},
|
15 |
+
next: function() {
|
16 |
+
if (this.pos < this.string.length)
|
17 |
+
return this.string.charAt(this.pos++);
|
18 |
+
},
|
19 |
+
eat: function(match) {
|
20 |
+
var ch = this.string.charAt(this.pos);
|
21 |
+
if (typeof match == "string") var ok = ch == match;
|
22 |
+
else var ok = ch && (match.test ? match.test(ch) : match(ch));
|
23 |
+
if (ok) {++this.pos; return ch;}
|
24 |
+
},
|
25 |
+
eatWhile: function(match) {
|
26 |
+
var start = this.pos;
|
27 |
+
while (this.eat(match)){}
|
28 |
+
return this.pos > start;
|
29 |
+
},
|
30 |
+
eatSpace: function() {
|
31 |
+
var start = this.pos;
|
32 |
+
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
|
33 |
+
return this.pos > start;
|
34 |
+
},
|
35 |
+
skipToEnd: function() {this.pos = this.string.length;},
|
36 |
+
skipTo: function(ch) {
|
37 |
+
var found = this.string.indexOf(ch, this.pos);
|
38 |
+
if (found > -1) {this.pos = found; return true;}
|
39 |
+
},
|
40 |
+
backUp: function(n) {this.pos -= n;},
|
41 |
+
column: function() {return this.start;},
|
42 |
+
indentation: function() {return 0;},
|
43 |
+
match: function(pattern, consume, caseInsensitive) {
|
44 |
+
if (typeof pattern == "string") {
|
45 |
+
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
|
46 |
+
var substr = this.string.substr(this.pos, pattern.length);
|
47 |
+
if (cased(substr) == cased(pattern)) {
|
48 |
+
if (consume !== false) this.pos += pattern.length;
|
49 |
+
return true;
|
50 |
+
}
|
51 |
+
} else {
|
52 |
+
var match = this.string.slice(this.pos).match(pattern);
|
53 |
+
if (match && match.index > 0) return null;
|
54 |
+
if (match && consume !== false) this.pos += match[0].length;
|
55 |
+
return match;
|
56 |
+
}
|
57 |
+
},
|
58 |
+
current: function(){return this.string.slice(this.start, this.pos);}
|
59 |
+
};
|
60 |
+
CodeMirror.StringStream = StringStream;
|
61 |
+
|
62 |
+
CodeMirror.startState = function (mode, a1, a2) {
|
63 |
+
return mode.startState ? mode.startState(a1, a2) : true;
|
64 |
+
};
|
65 |
+
|
66 |
+
var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
|
67 |
+
CodeMirror.defineMode = function (name, mode) { modes[name] = mode; };
|
68 |
+
CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; };
|
69 |
+
CodeMirror.getMode = function (options, spec) {
|
70 |
+
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
|
71 |
+
spec = mimeModes[spec];
|
72 |
+
if (typeof spec == "string")
|
73 |
+
var mname = spec, config = {};
|
74 |
+
else if (spec != null)
|
75 |
+
var mname = spec.name, config = spec;
|
76 |
+
var mfactory = modes[mname];
|
77 |
+
if (!mfactory) throw new Error("Unknown mode: " + spec);
|
78 |
+
return mfactory(options, config || {});
|
79 |
+
};
|
80 |
+
|
81 |
+
CodeMirror.runMode = function (string, modespec, callback, options) {
|
82 |
+
var mode = CodeMirror.getMode({ indentUnit: 2 }, modespec);
|
83 |
+
|
84 |
+
if (callback.nodeType == 1) {
|
85 |
+
var tabSize = (options && options.tabSize) || 4;
|
86 |
+
var node = callback, col = 0;
|
87 |
+
node.innerHTML = "";
|
88 |
+
callback = function (text, style) {
|
89 |
+
if (text == "\n") {
|
90 |
+
node.appendChild(document.createElement("br"));
|
91 |
+
col = 0;
|
92 |
+
return;
|
93 |
+
}
|
94 |
+
var content = "";
|
95 |
+
// replace tabs
|
96 |
+
for (var pos = 0; ;) {
|
97 |
+
var idx = text.indexOf("\t", pos);
|
98 |
+
if (idx == -1) {
|
99 |
+
content += text.slice(pos);
|
100 |
+
col += text.length - pos;
|
101 |
+
break;
|
102 |
+
} else {
|
103 |
+
col += idx - pos;
|
104 |
+
content += text.slice(pos, idx);
|
105 |
+
var size = tabSize - col % tabSize;
|
106 |
+
col += size;
|
107 |
+
for (var i = 0; i < size; ++i) content += " ";
|
108 |
+
pos = idx + 1;
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
if (style) {
|
113 |
+
var sp = node.appendChild(document.createElement("span"));
|
114 |
+
sp.className = "cm-" + style.replace(/ +/g, " cm-");
|
115 |
+
sp.appendChild(document.createTextNode(content));
|
116 |
+
} else {
|
117 |
+
node.appendChild(document.createTextNode(content));
|
118 |
+
}
|
119 |
+
};
|
120 |
+
}
|
121 |
+
|
122 |
+
var lines = splitLines(string), state = CodeMirror.startState(mode);
|
123 |
+
for (var i = 0, e = lines.length; i < e; ++i) {
|
124 |
+
if (i) callback("\n");
|
125 |
+
var stream = new CodeMirror.StringStream(lines[i]);
|
126 |
+
while (!stream.eol()) {
|
127 |
+
var style = mode.token(stream, state);
|
128 |
+
callback(stream.current(), style, i, stream.start, state);
|
129 |
+
stream.start = stream.pos;
|
130 |
+
}
|
131 |
+
}
|
132 |
+
};
|
assets/lib/codemirror/addon/runmode/runmode.js
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.runMode = function(string, modespec, callback, options) {
|
2 |
+
var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
|
3 |
+
var ie = /MSIE \d/.test(navigator.userAgent);
|
4 |
+
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
|
5 |
+
|
6 |
+
if (callback.nodeType == 1) {
|
7 |
+
var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
|
8 |
+
var node = callback, col = 0;
|
9 |
+
node.innerHTML = "";
|
10 |
+
callback = function(text, style) {
|
11 |
+
if (text == "\n") {
|
12 |
+
// Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
|
13 |
+
// Emitting a carriage return makes everything ok.
|
14 |
+
node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text));
|
15 |
+
col = 0;
|
16 |
+
return;
|
17 |
+
}
|
18 |
+
var content = "";
|
19 |
+
// replace tabs
|
20 |
+
for (var pos = 0;;) {
|
21 |
+
var idx = text.indexOf("\t", pos);
|
22 |
+
if (idx == -1) {
|
23 |
+
content += text.slice(pos);
|
24 |
+
col += text.length - pos;
|
25 |
+
break;
|
26 |
+
} else {
|
27 |
+
col += idx - pos;
|
28 |
+
content += text.slice(pos, idx);
|
29 |
+
var size = tabSize - col % tabSize;
|
30 |
+
col += size;
|
31 |
+
for (var i = 0; i < size; ++i) content += " ";
|
32 |
+
pos = idx + 1;
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
36 |
+
if (style) {
|
37 |
+
var sp = node.appendChild(document.createElement("span"));
|
38 |
+
sp.className = "cm-" + style.replace(/ +/g, " cm-");
|
39 |
+
sp.appendChild(document.createTextNode(content));
|
40 |
+
} else {
|
41 |
+
node.appendChild(document.createTextNode(content));
|
42 |
+
}
|
43 |
+
};
|
44 |
+
}
|
45 |
+
|
46 |
+
var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode);
|
47 |
+
for (var i = 0, e = lines.length; i < e; ++i) {
|
48 |
+
if (i) callback("\n");
|
49 |
+
var stream = new CodeMirror.StringStream(lines[i]);
|
50 |
+
while (!stream.eol()) {
|
51 |
+
var style = mode.token(stream, state);
|
52 |
+
callback(stream.current(), style, i, stream.start, state);
|
53 |
+
stream.start = stream.pos;
|
54 |
+
}
|
55 |
+
}
|
56 |
+
};
|
assets/lib/codemirror/addon/runmode/runmode.node.js
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Just enough of CodeMirror to run runMode under node.js */
|
2 |
+
|
3 |
+
function splitLines(string){ return string.split(/\r?\n|\r/); };
|
4 |
+
|
5 |
+
function StringStream(string) {
|
6 |
+
this.pos = this.start = 0;
|
7 |
+
this.string = string;
|
8 |
+
}
|
9 |
+
StringStream.prototype = {
|
10 |
+
eol: function() {return this.pos >= this.string.length;},
|
11 |
+
sol: function() {return this.pos == 0;},
|
12 |
+
peek: function() {return this.string.charAt(this.pos) || null;},
|
13 |
+
next: function() {
|
14 |
+
if (this.pos < this.string.length)
|
15 |
+
return this.string.charAt(this.pos++);
|
16 |
+
},
|
17 |
+
eat: function(match) {
|
18 |
+
var ch = this.string.charAt(this.pos);
|
19 |
+
if (typeof match == "string") var ok = ch == match;
|
20 |
+
else var ok = ch && (match.test ? match.test(ch) : match(ch));
|
21 |
+
if (ok) {++this.pos; return ch;}
|
22 |
+
},
|
23 |
+
eatWhile: function(match) {
|
24 |
+
var start = this.pos;
|
25 |
+
while (this.eat(match)){}
|
26 |
+
return this.pos > start;
|
27 |
+
},
|
28 |
+
eatSpace: function() {
|
29 |
+
var start = this.pos;
|
30 |
+
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
|
31 |
+
return this.pos > start;
|
32 |
+
},
|
33 |
+
skipToEnd: function() {this.pos = this.string.length;},
|
34 |
+
skipTo: function(ch) {
|
35 |
+
var found = this.string.indexOf(ch, this.pos);
|
36 |
+
if (found > -1) {this.pos = found; return true;}
|
37 |
+
},
|
38 |
+
backUp: function(n) {this.pos -= n;},
|
39 |
+
column: function() {return this.start;},
|
40 |
+
indentation: function() {return 0;},
|
41 |
+
match: function(pattern, consume, caseInsensitive) {
|
42 |
+
if (typeof pattern == "string") {
|
43 |
+
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
|
44 |
+
var substr = this.string.substr(this.pos, pattern.length);
|
45 |
+
if (cased(substr) == cased(pattern)) {
|
46 |
+
if (consume !== false) this.pos += pattern.length;
|
47 |
+
return true;
|
48 |
+
}
|
49 |
+
} else {
|
50 |
+
var match = this.string.slice(this.pos).match(pattern);
|
51 |
+
if (match && match.index > 0) return null;
|
52 |
+
if (match && consume !== false) this.pos += match[0].length;
|
53 |
+
return match;
|
54 |
+
}
|
55 |
+
},
|
56 |
+
current: function(){return this.string.slice(this.start, this.pos);}
|
57 |
+
};
|
58 |
+
exports.StringStream = StringStream;
|
59 |
+
|
60 |
+
exports.startState = function(mode, a1, a2) {
|
61 |
+
return mode.startState ? mode.startState(a1, a2) : true;
|
62 |
+
};
|
63 |
+
|
64 |
+
var modes = exports.modes = {}, mimeModes = exports.mimeModes = {};
|
65 |
+
exports.defineMode = function(name, mode) {
|
66 |
+
if (arguments.length > 2) {
|
67 |
+
mode.dependencies = [];
|
68 |
+
for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
|
69 |
+
}
|
70 |
+
modes[name] = mode;
|
71 |
+
};
|
72 |
+
exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; };
|
73 |
+
|
74 |
+
exports.defineMode("null", function() {
|
75 |
+
return {token: function(stream) {stream.skipToEnd();}};
|
76 |
+
});
|
77 |
+
exports.defineMIME("text/plain", "null");
|
78 |
+
|
79 |
+
exports.getMode = function(options, spec) {
|
80 |
+
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
|
81 |
+
spec = mimeModes[spec];
|
82 |
+
if (typeof spec == "string")
|
83 |
+
var mname = spec, config = {};
|
84 |
+
else if (spec != null)
|
85 |
+
var mname = spec.name, config = spec;
|
86 |
+
var mfactory = modes[mname];
|
87 |
+
if (!mfactory) throw new Error("Unknown mode: " + spec);
|
88 |
+
return mfactory(options, config || {});
|
89 |
+
};
|
90 |
+
|
91 |
+
exports.runMode = function(string, modespec, callback) {
|
92 |
+
var mode = exports.getMode({indentUnit: 2}, modespec);
|
93 |
+
var lines = splitLines(string), state = exports.startState(mode);
|
94 |
+
for (var i = 0, e = lines.length; i < e; ++i) {
|
95 |
+
if (i) callback("\n");
|
96 |
+
var stream = new exports.StringStream(lines[i]);
|
97 |
+
while (!stream.eol()) {
|
98 |
+
var style = mode.token(stream, state);
|
99 |
+
callback(stream.current(), style, i, stream.start, state);
|
100 |
+
stream.start = stream.pos;
|
101 |
+
}
|
102 |
+
}
|
103 |
+
};
|
assets/lib/codemirror/addon/scroll/scrollpastend.js
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) {
|
5 |
+
if (old && old != CodeMirror.Init) {
|
6 |
+
cm.off("change", onChange);
|
7 |
+
cm.display.lineSpace.parentNode.style.paddingBottom = "";
|
8 |
+
cm.state.scrollPastEndPadding = null;
|
9 |
+
}
|
10 |
+
if (val) {
|
11 |
+
cm.on("change", onChange);
|
12 |
+
updateBottomMargin(cm);
|
13 |
+
}
|
14 |
+
});
|
15 |
+
|
16 |
+
function onChange(cm, change) {
|
17 |
+
if (CodeMirror.changeEnd(change).line == cm.lastLine())
|
18 |
+
updateBottomMargin(cm);
|
19 |
+
}
|
20 |
+
|
21 |
+
function updateBottomMargin(cm) {
|
22 |
+
var padding = "";
|
23 |
+
if (cm.lineCount() > 1) {
|
24 |
+
var totalH = cm.display.scroller.clientHeight - 30,
|
25 |
+
lastLineH = cm.getLineHandle(cm.lastLine()).height;
|
26 |
+
padding = (totalH - lastLineH) + "px";
|
27 |
+
}
|
28 |
+
if (cm.state.scrollPastEndPadding != padding) {
|
29 |
+
cm.state.scrollPastEndPadding = padding;
|
30 |
+
cm.display.lineSpace.parentNode.style.paddingBottom = padding;
|
31 |
+
cm.setSize();
|
32 |
+
}
|
33 |
+
}
|
34 |
+
})();
|
assets/lib/codemirror/addon/search/match-highlighter.js
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Highlighting text that matches the selection
|
2 |
+
//
|
3 |
+
// Defines an option highlightSelectionMatches, which, when enabled,
|
4 |
+
// will style strings that match the selection throughout the
|
5 |
+
// document.
|
6 |
+
//
|
7 |
+
// The option can be set to true to simply enable it, or to a
|
8 |
+
// {minChars, style, showToken} object to explicitly configure it.
|
9 |
+
// minChars is the minimum amount of characters that should be
|
10 |
+
// selected for the behavior to occur, and style is the token style to
|
11 |
+
// apply to the matches. This will be prefixed by "cm-" to create an
|
12 |
+
// actual CSS class name. showToken, when enabled, will cause the
|
13 |
+
// current token to be highlighted when nothing is selected.
|
14 |
+
|
15 |
+
(function() {
|
16 |
+
var DEFAULT_MIN_CHARS = 2;
|
17 |
+
var DEFAULT_TOKEN_STYLE = "matchhighlight";
|
18 |
+
var DEFAULT_DELAY = 100;
|
19 |
+
|
20 |
+
function State(options) {
|
21 |
+
if (typeof options == "object") {
|
22 |
+
this.minChars = options.minChars;
|
23 |
+
this.style = options.style;
|
24 |
+
this.showToken = options.showToken;
|
25 |
+
this.delay = options.delay;
|
26 |
+
}
|
27 |
+
if (this.style == null) this.style = DEFAULT_TOKEN_STYLE;
|
28 |
+
if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS;
|
29 |
+
if (this.delay == null) this.delay = DEFAULT_DELAY;
|
30 |
+
this.overlay = this.timeout = null;
|
31 |
+
}
|
32 |
+
|
33 |
+
CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
|
34 |
+
if (old && old != CodeMirror.Init) {
|
35 |
+
var over = cm.state.matchHighlighter.overlay;
|
36 |
+
if (over) cm.removeOverlay(over);
|
37 |
+
clearTimeout(cm.state.matchHighlighter.timeout);
|
38 |
+
cm.state.matchHighlighter = null;
|
39 |
+
cm.off("cursorActivity", cursorActivity);
|
40 |
+
}
|
41 |
+
if (val) {
|
42 |
+
cm.state.matchHighlighter = new State(val);
|
43 |
+
highlightMatches(cm);
|
44 |
+
cm.on("cursorActivity", cursorActivity);
|
45 |
+
}
|
46 |
+
});
|
47 |
+
|
48 |
+
function cursorActivity(cm) {
|
49 |
+
var state = cm.state.matchHighlighter;
|
50 |
+
clearTimeout(state.timeout);
|
51 |
+
state.timeout = setTimeout(function() {highlightMatches(cm);}, state.delay);
|
52 |
+
}
|
53 |
+
|
54 |
+
function highlightMatches(cm) {
|
55 |
+
cm.operation(function() {
|
56 |
+
var state = cm.state.matchHighlighter;
|
57 |
+
if (state.overlay) {
|
58 |
+
cm.removeOverlay(state.overlay);
|
59 |
+
state.overlay = null;
|
60 |
+
}
|
61 |
+
if (!cm.somethingSelected() && state.showToken) {
|
62 |
+
var re = state.showToken === true ? /[\w$]/ : state.showToken;
|
63 |
+
var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
|
64 |
+
while (start && re.test(line.charAt(start - 1))) --start;
|
65 |
+
while (end < line.length && re.test(line.charAt(end))) ++end;
|
66 |
+
if (start < end)
|
67 |
+
cm.addOverlay(state.overlay = makeOverlay(line.slice(start, end), re, state.style));
|
68 |
+
return;
|
69 |
+
}
|
70 |
+
if (cm.getCursor("head").line != cm.getCursor("anchor").line) return;
|
71 |
+
var selection = cm.getSelection().replace(/^\s+|\s+$/g, "");
|
72 |
+
if (selection.length >= state.minChars)
|
73 |
+
cm.addOverlay(state.overlay = makeOverlay(selection, false, state.style));
|
74 |
+
});
|
75 |
+
}
|
76 |
+
|
77 |
+
function boundariesAround(stream, re) {
|
78 |
+
return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
|
79 |
+
(stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
|
80 |
+
}
|
81 |
+
|
82 |
+
function makeOverlay(query, hasBoundary, style) {
|
83 |
+
return {token: function(stream) {
|
84 |
+
if (stream.match(query) &&
|
85 |
+
(!hasBoundary || boundariesAround(stream, hasBoundary)))
|
86 |
+
return style;
|
87 |
+
stream.next();
|
88 |
+
stream.skipTo(query.charAt(0)) || stream.skipToEnd();
|
89 |
+
}};
|
90 |
+
}
|
91 |
+
})();
|
assets/lib/codemirror/addon/search/search.js
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Define search commands. Depends on dialog.js or another
|
2 |
+
// implementation of the openDialog method.
|
3 |
+
|
4 |
+
// Replace works a little oddly -- it will do the replace on the next
|
5 |
+
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
|
6 |
+
// replace by making sure the match is no longer selected when hitting
|
7 |
+
// Ctrl-G.
|
8 |
+
|
9 |
+
(function() {
|
10 |
+
function searchOverlay(query) {
|
11 |
+
if (typeof query == "string") return {token: function(stream) {
|
12 |
+
if (stream.match(query)) return "searching";
|
13 |
+
stream.next();
|
14 |
+
stream.skipTo(query.charAt(0)) || stream.skipToEnd();
|
15 |
+
}};
|
16 |
+
return {token: function(stream) {
|
17 |
+
if (stream.match(query)) return "searching";
|
18 |
+
while (!stream.eol()) {
|
19 |
+
stream.next();
|
20 |
+
if (stream.match(query, false)) break;
|
21 |
+
}
|
22 |
+
}};
|
23 |
+
}
|
24 |
+
|
25 |
+
function SearchState() {
|
26 |
+
this.posFrom = this.posTo = this.query = null;
|
27 |
+
this.overlay = null;
|
28 |
+
}
|
29 |
+
function getSearchState(cm) {
|
30 |
+
return cm.state.search || (cm.state.search = new SearchState());
|
31 |
+
}
|
32 |
+
function getSearchCursor(cm, query, pos) {
|
33 |
+
// Heuristic: if the query string is all lowercase, do a case insensitive search.
|
34 |
+
return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase());
|
35 |
+
}
|
36 |
+
function dialog(cm, text, shortText, f) {
|
37 |
+
if (cm.openDialog) cm.openDialog(text, f);
|
38 |
+
else f(prompt(shortText, ""));
|
39 |
+
}
|
40 |
+
function confirmDialog(cm, text, shortText, fs) {
|
41 |
+
if (cm.openConfirm) cm.openConfirm(text, fs);
|
42 |
+
else if (confirm(shortText)) fs[0]();
|
43 |
+
}
|
44 |
+
function parseQuery(query) {
|
45 |
+
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
|
46 |
+
return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query;
|
47 |
+
}
|
48 |
+
var queryDialog =
|
49 |
+
'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
|
50 |
+
function doSearch(cm, rev) {
|
51 |
+
var state = getSearchState(cm);
|
52 |
+
if (state.query) return findNext(cm, rev);
|
53 |
+
dialog(cm, queryDialog, "Search for:", function(query) {
|
54 |
+
cm.operation(function() {
|
55 |
+
if (!query || state.query) return;
|
56 |
+
state.query = parseQuery(query);
|
57 |
+
cm.removeOverlay(state.overlay);
|
58 |
+
state.overlay = searchOverlay(state.query);
|
59 |
+
cm.addOverlay(state.overlay);
|
60 |
+
state.posFrom = state.posTo = cm.getCursor();
|
61 |
+
findNext(cm, rev);
|
62 |
+
});
|
63 |
+
});
|
64 |
+
}
|
65 |
+
function findNext(cm, rev) {cm.operation(function() {
|
66 |
+
var state = getSearchState(cm);
|
67 |
+
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
|
68 |
+
if (!cursor.find(rev)) {
|
69 |
+
cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
|
70 |
+
if (!cursor.find(rev)) return;
|
71 |
+
}
|
72 |
+
cm.setSelection(cursor.from(), cursor.to());
|
73 |
+
state.posFrom = cursor.from(); state.posTo = cursor.to();
|
74 |
+
});}
|
75 |
+
function clearSearch(cm) {cm.operation(function() {
|
76 |
+
var state = getSearchState(cm);
|
77 |
+
if (!state.query) return;
|
78 |
+
state.query = null;
|
79 |
+
cm.removeOverlay(state.overlay);
|
80 |
+
});}
|
81 |
+
|
82 |
+
var replaceQueryDialog =
|
83 |
+
'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
|
84 |
+
var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
|
85 |
+
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
|
86 |
+
function replace(cm, all) {
|
87 |
+
dialog(cm, replaceQueryDialog, "Replace:", function(query) {
|
88 |
+
if (!query) return;
|
89 |
+
query = parseQuery(query);
|
90 |
+
dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
|
91 |
+
if (all) {
|
92 |
+
cm.operation(function() {
|
93 |
+
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
|
94 |
+
if (typeof query != "string") {
|
95 |
+
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
|
96 |
+
cursor.replace(text.replace(/\$(\d)/, function(_, i) {return match[i];}));
|
97 |
+
} else cursor.replace(text);
|
98 |
+
}
|
99 |
+
});
|
100 |
+
} else {
|
101 |
+
clearSearch(cm);
|
102 |
+
var cursor = getSearchCursor(cm, query, cm.getCursor());
|
103 |
+
var advance = function() {
|
104 |
+
var start = cursor.from(), match;
|
105 |
+
if (!(match = cursor.findNext())) {
|
106 |
+
cursor = getSearchCursor(cm, query);
|
107 |
+
if (!(match = cursor.findNext()) ||
|
108 |
+
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
|
109 |
+
}
|
110 |
+
cm.setSelection(cursor.from(), cursor.to());
|
111 |
+
confirmDialog(cm, doReplaceConfirm, "Replace?",
|
112 |
+
[function() {doReplace(match);}, advance]);
|
113 |
+
};
|
114 |
+
var doReplace = function(match) {
|
115 |
+
cursor.replace(typeof query == "string" ? text :
|
116 |
+
text.replace(/\$(\d)/, function(_, i) {return match[i];}));
|
117 |
+
advance();
|
118 |
+
};
|
119 |
+
advance();
|
120 |
+
}
|
121 |
+
});
|
122 |
+
});
|
123 |
+
}
|
124 |
+
|
125 |
+
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
|
126 |
+
CodeMirror.commands.findNext = doSearch;
|
127 |
+
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
|
128 |
+
CodeMirror.commands.clearSearch = clearSearch;
|
129 |
+
CodeMirror.commands.replace = replace;
|
130 |
+
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
|
131 |
+
})();
|
assets/lib/codemirror/addon/search/searchcursor.js
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function(){
|
2 |
+
var Pos = CodeMirror.Pos;
|
3 |
+
|
4 |
+
function SearchCursor(doc, query, pos, caseFold) {
|
5 |
+
this.atOccurrence = false; this.doc = doc;
|
6 |
+
if (caseFold == null && typeof query == "string") caseFold = false;
|
7 |
+
|
8 |
+
pos = pos ? doc.clipPos(pos) : Pos(0, 0);
|
9 |
+
this.pos = {from: pos, to: pos};
|
10 |
+
|
11 |
+
// The matches method is filled in based on the type of query.
|
12 |
+
// It takes a position and a direction, and returns an object
|
13 |
+
// describing the next occurrence of the query, or null if no
|
14 |
+
// more matches were found.
|
15 |
+
if (typeof query != "string") { // Regexp match
|
16 |
+
if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g");
|
17 |
+
this.matches = function(reverse, pos) {
|
18 |
+
if (reverse) {
|
19 |
+
query.lastIndex = 0;
|
20 |
+
var line = doc.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start;
|
21 |
+
for (;;) {
|
22 |
+
query.lastIndex = cutOff;
|
23 |
+
var newMatch = query.exec(line);
|
24 |
+
if (!newMatch) break;
|
25 |
+
match = newMatch;
|
26 |
+
start = match.index;
|
27 |
+
cutOff = match.index + (match[0].length || 1);
|
28 |
+
if (cutOff == line.length) break;
|
29 |
+
}
|
30 |
+
var matchLen = (match && match[0].length) || 0;
|
31 |
+
if (!matchLen) {
|
32 |
+
if (start == 0 && line.length == 0) {match = undefined;}
|
33 |
+
else if (start != doc.getLine(pos.line).length) {
|
34 |
+
matchLen++;
|
35 |
+
}
|
36 |
+
}
|
37 |
+
} else {
|
38 |
+
query.lastIndex = pos.ch;
|
39 |
+
var line = doc.getLine(pos.line), match = query.exec(line);
|
40 |
+
var matchLen = (match && match[0].length) || 0;
|
41 |
+
var start = match && match.index;
|
42 |
+
if (start + matchLen != line.length && !matchLen) matchLen = 1;
|
43 |
+
}
|
44 |
+
if (match && matchLen)
|
45 |
+
return {from: Pos(pos.line, start),
|
46 |
+
to: Pos(pos.line, start + matchLen),
|
47 |
+
match: match};
|
48 |
+
};
|
49 |
+
} else { // String query
|
50 |
+
if (caseFold) query = query.toLowerCase();
|
51 |
+
var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
|
52 |
+
var target = query.split("\n");
|
53 |
+
// Different methods for single-line and multi-line queries
|
54 |
+
if (target.length == 1) {
|
55 |
+
if (!query.length) {
|
56 |
+
// Empty string would match anything and never progress, so
|
57 |
+
// we define it to match nothing instead.
|
58 |
+
this.matches = function() {};
|
59 |
+
} else {
|
60 |
+
this.matches = function(reverse, pos) {
|
61 |
+
var line = fold(doc.getLine(pos.line)), len = query.length, match;
|
62 |
+
if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
|
63 |
+
: (match = line.indexOf(query, pos.ch)) != -1)
|
64 |
+
return {from: Pos(pos.line, match),
|
65 |
+
to: Pos(pos.line, match + len)};
|
66 |
+
};
|
67 |
+
}
|
68 |
+
} else {
|
69 |
+
this.matches = function(reverse, pos) {
|
70 |
+
var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(doc.getLine(ln));
|
71 |
+
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
|
72 |
+
if (reverse ? offsetA > pos.ch || offsetA != match.length
|
73 |
+
: offsetA < pos.ch || offsetA != line.length - match.length)
|
74 |
+
return;
|
75 |
+
for (;;) {
|
76 |
+
if (reverse ? !ln : ln == doc.lineCount() - 1) return;
|
77 |
+
line = fold(doc.getLine(ln += reverse ? -1 : 1));
|
78 |
+
match = target[reverse ? --idx : ++idx];
|
79 |
+
if (idx > 0 && idx < target.length - 1) {
|
80 |
+
if (line != match) return;
|
81 |
+
else continue;
|
82 |
+
}
|
83 |
+
var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
|
84 |
+
if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
|
85 |
+
return;
|
86 |
+
var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB);
|
87 |
+
return {from: reverse ? end : start, to: reverse ? start : end};
|
88 |
+
}
|
89 |
+
};
|
90 |
+
}
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
SearchCursor.prototype = {
|
95 |
+
findNext: function() {return this.find(false);},
|
96 |
+
findPrevious: function() {return this.find(true);},
|
97 |
+
|
98 |
+
find: function(reverse) {
|
99 |
+
var self = this, pos = this.doc.clipPos(reverse ? this.pos.from : this.pos.to);
|
100 |
+
function savePosAndFail(line) {
|
101 |
+
var pos = Pos(line, 0);
|
102 |
+
self.pos = {from: pos, to: pos};
|
103 |
+
self.atOccurrence = false;
|
104 |
+
return false;
|
105 |
+
}
|
106 |
+
|
107 |
+
for (;;) {
|
108 |
+
if (this.pos = this.matches(reverse, pos)) {
|
109 |
+
if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
|
110 |
+
this.atOccurrence = true;
|
111 |
+
return this.pos.match || true;
|
112 |
+
}
|
113 |
+
if (reverse) {
|
114 |
+
if (!pos.line) return savePosAndFail(0);
|
115 |
+
pos = Pos(pos.line-1, this.doc.getLine(pos.line-1).length);
|
116 |
+
}
|
117 |
+
else {
|
118 |
+
var maxLine = this.doc.lineCount();
|
119 |
+
if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
|
120 |
+
pos = Pos(pos.line + 1, 0);
|
121 |
+
}
|
122 |
+
}
|
123 |
+
},
|
124 |
+
|
125 |
+
from: function() {if (this.atOccurrence) return this.pos.from;},
|
126 |
+
to: function() {if (this.atOccurrence) return this.pos.to;},
|
127 |
+
|
128 |
+
replace: function(newText) {
|
129 |
+
if (!this.atOccurrence) return;
|
130 |
+
var lines = CodeMirror.splitLines(newText);
|
131 |
+
this.doc.replaceRange(lines, this.pos.from, this.pos.to);
|
132 |
+
this.pos.to = Pos(this.pos.from.line + lines.length - 1,
|
133 |
+
lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0));
|
134 |
+
}
|
135 |
+
};
|
136 |
+
|
137 |
+
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
|
138 |
+
return new SearchCursor(this.doc, query, pos, caseFold);
|
139 |
+
});
|
140 |
+
CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
|
141 |
+
return new SearchCursor(this, query, pos, caseFold);
|
142 |
+
});
|
143 |
+
})();
|
assets/lib/codemirror/addon/selection/active-line.js
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Because sometimes you need to style the cursor's line.
|
2 |
+
//
|
3 |
+
// Adds an option 'styleActiveLine' which, when enabled, gives the
|
4 |
+
// active line's wrapping <div> the CSS class "CodeMirror-activeline",
|
5 |
+
// and gives its background <div> the class "CodeMirror-activeline-background".
|
6 |
+
|
7 |
+
(function() {
|
8 |
+
"use strict";
|
9 |
+
var WRAP_CLASS = "CodeMirror-activeline";
|
10 |
+
var BACK_CLASS = "CodeMirror-activeline-background";
|
11 |
+
|
12 |
+
CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
|
13 |
+
var prev = old && old != CodeMirror.Init;
|
14 |
+
if (val && !prev) {
|
15 |
+
updateActiveLine(cm);
|
16 |
+
cm.on("cursorActivity", updateActiveLine);
|
17 |
+
} else if (!val && prev) {
|
18 |
+
cm.off("cursorActivity", updateActiveLine);
|
19 |
+
clearActiveLine(cm);
|
20 |
+
delete cm.state.activeLine;
|
21 |
+
}
|
22 |
+
});
|
23 |
+
|
24 |
+
function clearActiveLine(cm) {
|
25 |
+
if ("activeLine" in cm.state) {
|
26 |
+
cm.removeLineClass(cm.state.activeLine, "wrap", WRAP_CLASS);
|
27 |
+
cm.removeLineClass(cm.state.activeLine, "background", BACK_CLASS);
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
function updateActiveLine(cm) {
|
32 |
+
var line = cm.getLineHandleVisualStart(cm.getCursor().line);
|
33 |
+
if (cm.state.activeLine == line) return;
|
34 |
+
clearActiveLine(cm);
|
35 |
+
cm.addLineClass(line, "wrap", WRAP_CLASS);
|
36 |
+
cm.addLineClass(line, "background", BACK_CLASS);
|
37 |
+
cm.state.activeLine = line;
|
38 |
+
}
|
39 |
+
})();
|
assets/lib/codemirror/addon/selection/mark-selection.js
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Because sometimes you need to mark the selected *text*.
|
2 |
+
//
|
3 |
+
// Adds an option 'styleSelectedText' which, when enabled, gives
|
4 |
+
// selected text the CSS class given as option value, or
|
5 |
+
// "CodeMirror-selectedtext" when the value is not a string.
|
6 |
+
|
7 |
+
(function() {
|
8 |
+
"use strict";
|
9 |
+
|
10 |
+
CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
|
11 |
+
var prev = old && old != CodeMirror.Init;
|
12 |
+
if (val && !prev) {
|
13 |
+
cm.state.markedSelection = [];
|
14 |
+
cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext";
|
15 |
+
reset(cm);
|
16 |
+
cm.on("cursorActivity", onCursorActivity);
|
17 |
+
cm.on("change", onChange);
|
18 |
+
} else if (!val && prev) {
|
19 |
+
cm.off("cursorActivity", onCursorActivity);
|
20 |
+
cm.off("change", onChange);
|
21 |
+
clear(cm);
|
22 |
+
cm.state.markedSelection = cm.state.markedSelectionStyle = null;
|
23 |
+
}
|
24 |
+
});
|
25 |
+
|
26 |
+
function onCursorActivity(cm) {
|
27 |
+
cm.operation(function() { update(cm); });
|
28 |
+
}
|
29 |
+
|
30 |
+
function onChange(cm) {
|
31 |
+
if (cm.state.markedSelection.length)
|
32 |
+
cm.operation(function() { clear(cm); });
|
33 |
+
}
|
34 |
+
|
35 |
+
var CHUNK_SIZE = 8;
|
36 |
+
var Pos = CodeMirror.Pos;
|
37 |
+
|
38 |
+
function cmp(pos1, pos2) {
|
39 |
+
return pos1.line - pos2.line || pos1.ch - pos2.ch;
|
40 |
+
}
|
41 |
+
|
42 |
+
function coverRange(cm, from, to, addAt) {
|
43 |
+
if (cmp(from, to) == 0) return;
|
44 |
+
var array = cm.state.markedSelection;
|
45 |
+
var cls = cm.state.markedSelectionStyle;
|
46 |
+
for (var line = from.line;;) {
|
47 |
+
var start = line == from.line ? from : Pos(line, 0);
|
48 |
+
var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line;
|
49 |
+
var end = atEnd ? to : Pos(endLine, 0);
|
50 |
+
var mark = cm.markText(start, end, {className: cls});
|
51 |
+
if (addAt == null) array.push(mark);
|
52 |
+
else array.splice(addAt++, 0, mark);
|
53 |
+
if (atEnd) break;
|
54 |
+
line = endLine;
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
function clear(cm) {
|
59 |
+
var array = cm.state.markedSelection;
|
60 |
+
for (var i = 0; i < array.length; ++i) array[i].clear();
|
61 |
+
array.length = 0;
|
62 |
+
}
|
63 |
+
|
64 |
+
function reset(cm) {
|
65 |
+
clear(cm);
|
66 |
+
var from = cm.getCursor("start"), to = cm.getCursor("end");
|
67 |
+
coverRange(cm, from, to);
|
68 |
+
}
|
69 |
+
|
70 |
+
function update(cm) {
|
71 |
+
var from = cm.getCursor("start"), to = cm.getCursor("end");
|
72 |
+
if (cmp(from, to) == 0) return clear(cm);
|
73 |
+
|
74 |
+
var array = cm.state.markedSelection;
|
75 |
+
if (!array.length) return coverRange(cm, from, to);
|
76 |
+
|
77 |
+
var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
|
78 |
+
if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE ||
|
79 |
+
cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
|
80 |
+
return reset(cm);
|
81 |
+
|
82 |
+
while (cmp(from, coverStart.from) > 0) {
|
83 |
+
array.shift().clear();
|
84 |
+
coverStart = array[0].find();
|
85 |
+
}
|
86 |
+
if (cmp(from, coverStart.from) < 0) {
|
87 |
+
if (coverStart.to.line - from.line < CHUNK_SIZE) {
|
88 |
+
array.shift().clear();
|
89 |
+
coverRange(cm, from, coverStart.to, 0);
|
90 |
+
} else {
|
91 |
+
coverRange(cm, from, coverStart.from, 0);
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
while (cmp(to, coverEnd.to) < 0) {
|
96 |
+
array.pop().clear();
|
97 |
+
coverEnd = array[array.length - 1].find();
|
98 |
+
}
|
99 |
+
if (cmp(to, coverEnd.to) > 0) {
|
100 |
+
if (to.line - coverEnd.from.line < CHUNK_SIZE) {
|
101 |
+
array.pop().clear();
|
102 |
+
coverRange(cm, coverEnd.from, to);
|
103 |
+
} else {
|
104 |
+
coverRange(cm, coverEnd.to, to);
|
105 |
+
}
|
106 |
+
}
|
107 |
+
}
|
108 |
+
})();
|
assets/lib/codemirror/addon/tern/tern.css
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.CodeMirror-Tern-completion {
|
2 |
+
padding-left: 22px;
|
3 |
+
position: relative;
|
4 |
+
}
|
5 |
+
.CodeMirror-Tern-completion:before {
|
6 |
+
position: absolute;
|
7 |
+
left: 2px;
|
8 |
+
bottom: 2px;
|
9 |
+
border-radius: 50%;
|
10 |
+
font-size: 12px;
|
11 |
+
font-weight: bold;
|
12 |
+
height: 15px;
|
13 |
+
width: 15px;
|
14 |
+
line-height: 16px;
|
15 |
+
text-align: center;
|
16 |
+
color: white;
|
17 |
+
-moz-box-sizing: border-box;
|
18 |
+
box-sizing: border-box;
|
19 |
+
}
|
20 |
+
.CodeMirror-Tern-completion-unknown:before {
|
21 |
+
content: "?";
|
22 |
+
background: #4bb;
|
23 |
+
}
|
24 |
+
.CodeMirror-Tern-completion-object:before {
|
25 |
+
content: "O";
|
26 |
+
background: #77c;
|
27 |
+
}
|
28 |
+
.CodeMirror-Tern-completion-fn:before {
|
29 |
+
content: "F";
|
30 |
+
background: #7c7;
|
31 |
+
}
|
32 |
+
.CodeMirror-Tern-completion-array:before {
|
33 |
+
content: "A";
|
34 |
+
background: #c66;
|
35 |
+
}
|
36 |
+
.CodeMirror-Tern-completion-number:before {
|
37 |
+
content: "1";
|
38 |
+
background: #999;
|
39 |
+
}
|
40 |
+
.CodeMirror-Tern-completion-string:before {
|
41 |
+
content: "S";
|
42 |
+
background: #999;
|
43 |
+
}
|
44 |
+
.CodeMirror-Tern-completion-bool:before {
|
45 |
+
content: "B";
|
46 |
+
background: #999;
|
47 |
+
}
|
48 |
+
|
49 |
+
.CodeMirror-Tern-completion-guess {
|
50 |
+
color: #999;
|
51 |
+
}
|
52 |
+
|
53 |
+
.CodeMirror-Tern-tooltip {
|
54 |
+
border: 1px solid silver;
|
55 |
+
border-radius: 3px;
|
56 |
+
color: #444;
|
57 |
+
padding: 2px 5px;
|
58 |
+
font-size: 90%;
|
59 |
+
font-family: monospace;
|
60 |
+
background-color: white;
|
61 |
+
white-space: pre-wrap;
|
62 |
+
|
63 |
+
max-width: 40em;
|
64 |
+
position: absolute;
|
65 |
+
z-index: 10;
|
66 |
+
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
67 |
+
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
68 |
+
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
|
69 |
+
|
70 |
+
transition: opacity 1s;
|
71 |
+
-moz-transition: opacity 1s;
|
72 |
+
-webkit-transition: opacity 1s;
|
73 |
+
-o-transition: opacity 1s;
|
74 |
+
-ms-transition: opacity 1s;
|
75 |
+
}
|
76 |
+
|
77 |
+
.CodeMirror-Tern-hint-doc {
|
78 |
+
max-width: 25em;
|
79 |
+
}
|
80 |
+
|
81 |
+
.CodeMirror-Tern-fname { color: black; }
|
82 |
+
.CodeMirror-Tern-farg { color: #70a; }
|
83 |
+
.CodeMirror-Tern-farg-current { text-decoration: underline; }
|
84 |
+
.CodeMirror-Tern-type { color: #07c; }
|
85 |
+
.CodeMirror-Tern-fhint-guess { opacity: .7; }
|
assets/lib/codemirror/addon/tern/tern.js
ADDED
@@ -0,0 +1,631 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Glue code between CodeMirror and Tern.
|
2 |
+
//
|
3 |
+
// Create a CodeMirror.TernServer to wrap an actual Tern server,
|
4 |
+
// register open documents (CodeMirror.Doc instances) with it, and
|
5 |
+
// call its methods to activate the assisting functions that Tern
|
6 |
+
// provides.
|
7 |
+
//
|
8 |
+
// Options supported (all optional):
|
9 |
+
// * defs: An array of JSON definition data structures.
|
10 |
+
// * plugins: An object mapping plugin names to configuration
|
11 |
+
// options.
|
12 |
+
// * getFile: A function(name, c) that can be used to access files in
|
13 |
+
// the project that haven't been loaded yet. Simply do c(null) to
|
14 |
+
// indicate that a file is not available.
|
15 |
+
// * fileFilter: A function(value, docName, doc) that will be applied
|
16 |
+
// to documents before passing them on to Tern.
|
17 |
+
// * switchToDoc: A function(name) that should, when providing a
|
18 |
+
// multi-file view, switch the view or focus to the named file.
|
19 |
+
// * showError: A function(editor, message) that can be used to
|
20 |
+
// override the way errors are displayed.
|
21 |
+
// * completionTip: Customize the content in tooltips for completions.
|
22 |
+
// Is passed a single argument—the completion's data as returned by
|
23 |
+
// Tern—and may return a string, DOM node, or null to indicate that
|
24 |
+
// no tip should be shown. By default the docstring is shown.
|
25 |
+
// * typeTip: Like completionTip, but for the tooltips shown for type
|
26 |
+
// queries.
|
27 |
+
// * responseFilter: A function(doc, query, request, error, data) that
|
28 |
+
// will be applied to the Tern responses before treating them
|
29 |
+
//
|
30 |
+
//
|
31 |
+
// It is possible to run the Tern server in a web worker by specifying
|
32 |
+
// these additional options:
|
33 |
+
// * useWorker: Set to true to enable web worker mode. You'll probably
|
34 |
+
// want to feature detect the actual value you use here, for example
|
35 |
+
// !!window.Worker.
|
36 |
+
// * workerScript: The main script of the worker. Point this to
|
37 |
+
// wherever you are hosting worker.js from this directory.
|
38 |
+
// * workerDeps: An array of paths pointing (relative to workerScript)
|
39 |
+
// to the Acorn and Tern libraries and any Tern plugins you want to
|
40 |
+
// load. Or, if you minified those into a single script and included
|
41 |
+
// them in the workerScript, simply leave this undefined.
|
42 |
+
|
43 |
+
(function() {
|
44 |
+
"use strict";
|
45 |
+
|
46 |
+
CodeMirror.TernServer = function(options) {
|
47 |
+
var self = this;
|
48 |
+
this.options = options || {};
|
49 |
+
var plugins = this.options.plugins || (this.options.plugins = {});
|
50 |
+
if (!plugins.doc_comment) plugins.doc_comment = true;
|
51 |
+
if (this.options.useWorker) {
|
52 |
+
this.server = new WorkerServer(this);
|
53 |
+
} else {
|
54 |
+
this.server = new tern.Server({
|
55 |
+
getFile: function(name, c) { return getFile(self, name, c); },
|
56 |
+
async: true,
|
57 |
+
defs: this.options.defs || [],
|
58 |
+
plugins: plugins
|
59 |
+
});
|
60 |
+
}
|
61 |
+
this.docs = Object.create(null);
|
62 |
+
this.trackChange = function(doc, change) { trackChange(self, doc, change); };
|
63 |
+
|
64 |
+
this.cachedArgHints = null;
|
65 |
+
this.activeArgHints = null;
|
66 |
+
this.jumpStack = [];
|
67 |
+
};
|
68 |
+
|
69 |
+
CodeMirror.TernServer.prototype = {
|
70 |
+
addDoc: function(name, doc) {
|
71 |
+
var data = {doc: doc, name: name, changed: null};
|
72 |
+
this.server.addFile(name, docValue(this, data));
|
73 |
+
CodeMirror.on(doc, "change", this.trackChange);
|
74 |
+
return this.docs[name] = data;
|
75 |
+
},
|
76 |
+
|
77 |
+
delDoc: function(name) {
|
78 |
+
var found = this.docs[name];
|
79 |
+
if (!found) return;
|
80 |
+
CodeMirror.off(found.doc, "change", this.trackChange);
|
81 |
+
delete this.docs[name];
|
82 |
+
this.server.delFile(name);
|
83 |
+
},
|
84 |
+
|
85 |
+
hideDoc: function(name) {
|
86 |
+
closeArgHints(this);
|
87 |
+
var found = this.docs[name];
|
88 |
+
if (found && found.changed) sendDoc(this, found);
|
89 |
+
},
|
90 |
+
|
91 |
+
complete: function(cm) {
|
92 |
+
var self = this;
|
93 |
+
CodeMirror.showHint(cm, function(cm, c) { return hint(self, cm, c); }, {async: true});
|
94 |
+
},
|
95 |
+
|
96 |
+
getHint: function(cm, c) { return hint(this, cm, c); },
|
97 |
+
|
98 |
+
showType: function(cm) { showType(this, cm); },
|
99 |
+
|
100 |
+
updateArgHints: function(cm) { updateArgHints(this, cm); },
|
101 |
+
|
102 |
+
jumpToDef: function(cm) { jumpToDef(this, cm); },
|
103 |
+
|
104 |
+
jumpBack: function(cm) { jumpBack(this, cm); },
|
105 |
+
|
106 |
+
rename: function(cm) { rename(this, cm); },
|
107 |
+
|
108 |
+
request: function (cm, query, c) {
|
109 |
+
var self = this;
|
110 |
+
var doc = findDoc(this, cm.getDoc());
|
111 |
+
var request = buildRequest(this, doc, query);
|
112 |
+
|
113 |
+
this.server.request(request, function (error, data) {
|
114 |
+
if (!error && self.options.responseFilter)
|
115 |
+
data = self.options.responseFilter(doc, query, request, error, data);
|
116 |
+
c(error, data);
|
117 |
+
});
|
118 |
+
}
|
119 |
+
};
|
120 |
+
|
121 |
+
var Pos = CodeMirror.Pos;
|
122 |
+
var cls = "CodeMirror-Tern-";
|
123 |
+
var bigDoc = 250;
|
124 |
+
|
125 |
+
function getFile(ts, name, c) {
|
126 |
+
var buf = ts.docs[name];
|
127 |
+
if (buf)
|
128 |
+
c(docValue(ts, buf));
|
129 |
+
else if (ts.options.getFile)
|
130 |
+
ts.options.getFile(name, c);
|
131 |
+
else
|
132 |
+
c(null);
|
133 |
+
}
|
134 |
+
|
135 |
+
function findDoc(ts, doc, name) {
|
136 |
+
for (var n in ts.docs) {
|
137 |
+
var cur = ts.docs[n];
|
138 |
+
if (cur.doc == doc) return cur;
|
139 |
+
}
|
140 |
+
if (!name) for (var i = 0;; ++i) {
|
141 |
+
n = "[doc" + (i || "") + "]";
|
142 |
+
if (!ts.docs[n]) { name = n; break; }
|
143 |
+
}
|
144 |
+
return ts.addDoc(name, doc);
|
145 |
+
}
|
146 |
+
|
147 |
+
function trackChange(ts, doc, change) {
|
148 |
+
var data = findDoc(ts, doc);
|
149 |
+
|
150 |
+
var argHints = ts.cachedArgHints;
|
151 |
+
if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) <= 0)
|
152 |
+
ts.cachedArgHints = null;
|
153 |
+
|
154 |
+
var changed = data.changed;
|
155 |
+
if (changed == null)
|
156 |
+
data.changed = changed = {from: change.from.line, to: change.from.line};
|
157 |
+
var end = change.from.line + (change.text.length - 1);
|
158 |
+
if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end);
|
159 |
+
if (end >= changed.to) changed.to = end + 1;
|
160 |
+
if (changed.from > change.from.line) changed.from = change.from.line;
|
161 |
+
|
162 |
+
if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() {
|
163 |
+
if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data);
|
164 |
+
}, 200);
|
165 |
+
}
|
166 |
+
|
167 |
+
function sendDoc(ts, doc) {
|
168 |
+
ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) {
|
169 |
+
if (error) console.error(error);
|
170 |
+
else doc.changed = null;
|
171 |
+
});
|
172 |
+
}
|
173 |
+
|
174 |
+
// Completion
|
175 |
+
|
176 |
+
function hint(ts, cm, c) {
|
177 |
+
ts.request(cm, {type: "completions", types: true, docs: true, urls: true}, function(error, data) {
|
178 |
+
if (error) return showError(ts, cm, error);
|
179 |
+
var completions = [], after = "";
|
180 |
+
var from = data.start, to = data.end;
|
181 |
+
if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" &&
|
182 |
+
cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]")
|
183 |
+
after = "\"]";
|
184 |
+
|
185 |
+
for (var i = 0; i < data.completions.length; ++i) {
|
186 |
+
var completion = data.completions[i], className = typeToIcon(completion.type);
|
187 |
+
if (data.guess) className += " " + cls + "guess";
|
188 |
+
completions.push({text: completion.name + after,
|
189 |
+
displayText: completion.name,
|
190 |
+
className: className,
|
191 |
+
data: completion});
|
192 |
+
}
|
193 |
+
|
194 |
+
var obj = {from: from, to: to, list: completions};
|
195 |
+
var tooltip = null;
|
196 |
+
CodeMirror.on(obj, "close", function() { remove(tooltip); });
|
197 |
+
CodeMirror.on(obj, "update", function() { remove(tooltip); });
|
198 |
+
CodeMirror.on(obj, "select", function(cur, node) {
|
199 |
+
remove(tooltip);
|
200 |
+
var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc;
|
201 |
+
if (content) {
|
202 |
+
tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset,
|
203 |
+
node.getBoundingClientRect().top + window.pageYOffset, content);
|
204 |
+
tooltip.className += " " + cls + "hint-doc";
|
205 |
+
}
|
206 |
+
});
|
207 |
+
c(obj);
|
208 |
+
});
|
209 |
+
}
|
210 |
+
|
211 |
+
function typeToIcon(type) {
|
212 |
+
var suffix;
|
213 |
+
if (type == "?") suffix = "unknown";
|
214 |
+
else if (type == "number" || type == "string" || type == "bool") suffix = type;
|
215 |
+
else if (/^fn\(/.test(type)) suffix = "fn";
|
216 |
+
else if (/^\[/.test(type)) suffix = "array";
|
217 |
+
else suffix = "object";
|
218 |
+
return cls + "completion " + cls + "completion-" + suffix;
|
219 |
+
}
|
220 |
+
|
221 |
+
// Type queries
|
222 |
+
|
223 |
+
function showType(ts, cm) {
|
224 |
+
ts.request(cm, "type", function(error, data) {
|
225 |
+
if (error) return showError(ts, cm, error);
|
226 |
+
if (ts.options.typeTip) {
|
227 |
+
var tip = ts.options.typeTip(data);
|
228 |
+
} else {
|
229 |
+
var tip = elt("span", null, elt("strong", null, data.type || "not found"));
|
230 |
+
if (data.doc)
|
231 |
+
tip.appendChild(document.createTextNode(" — " + data.doc));
|
232 |
+
if (data.url) {
|
233 |
+
tip.appendChild(document.createTextNode(" "));
|
234 |
+
tip.appendChild(elt("a", null, "[docs]")).href = data.url;
|
235 |
+
}
|
236 |
+
}
|
237 |
+
tempTooltip(cm, tip);
|
238 |
+
});
|
239 |
+
}
|
240 |
+
|
241 |
+
// Maintaining argument hints
|
242 |
+
|
243 |
+
function updateArgHints(ts, cm) {
|
244 |
+
closeArgHints(ts);
|
245 |
+
|
246 |
+
if (cm.somethingSelected()) return;
|
247 |
+
var state = cm.getTokenAt(cm.getCursor()).state;
|
248 |
+
var inner = CodeMirror.innerMode(cm.getMode(), state);
|
249 |
+
if (inner.mode.name != "javascript") return;
|
250 |
+
var lex = inner.state.lexical;
|
251 |
+
if (lex.info != "call") return;
|
252 |
+
|
253 |
+
var ch, pos = lex.pos || 0, tabSize = cm.getOption("tabSize");
|
254 |
+
for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {
|
255 |
+
var str = cm.getLine(line), extra = 0;
|
256 |
+
for (var pos = 0;;) {
|
257 |
+
var tab = str.indexOf("\t", pos);
|
258 |
+
if (tab == -1) break;
|
259 |
+
extra += tabSize - (tab + extra) % tabSize - 1;
|
260 |
+
pos = tab + 1;
|
261 |
+
}
|
262 |
+
ch = lex.column - extra;
|
263 |
+
if (str.charAt(ch) == "(") {found = true; break;}
|
264 |
+
}
|
265 |
+
if (!found) return;
|
266 |
+
|
267 |
+
var start = Pos(line, ch);
|
268 |
+
var cache = ts.cachedArgHints;
|
269 |
+
if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0)
|
270 |
+
return showArgHints(ts, cm, pos);
|
271 |
+
|
272 |
+
ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) {
|
273 |
+
if (error || !data.type || !(/^fn\(/).test(data.type)) return;
|
274 |
+
ts.cachedArgHints = {
|
275 |
+
start: pos,
|
276 |
+
type: parseFnType(data.type),
|
277 |
+
name: data.exprName || data.name || "fn",
|
278 |
+
guess: data.guess,
|
279 |
+
doc: cm.getDoc()
|
280 |
+
};
|
281 |
+
showArgHints(ts, cm, pos);
|
282 |
+
});
|
283 |
+
}
|
284 |
+
|
285 |
+
function showArgHints(ts, cm, pos) {
|
286 |
+
closeArgHints(ts);
|
287 |
+
|
288 |
+
var cache = ts.cachedArgHints, tp = cache.type;
|
289 |
+
var tip = elt("span", cache.guess ? cls + "fhint-guess" : null,
|
290 |
+
elt("span", cls + "fname", cache.name), "(");
|
291 |
+
for (var i = 0; i < tp.args.length; ++i) {
|
292 |
+
if (i) tip.appendChild(document.createTextNode(", "));
|
293 |
+
var arg = tp.args[i];
|
294 |
+
tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?"));
|
295 |
+
if (arg.type != "?") {
|
296 |
+
tip.appendChild(document.createTextNode(":\u00a0"));
|
297 |
+
tip.appendChild(elt("span", cls + "type", arg.type));
|
298 |
+
}
|
299 |
+
}
|
300 |
+
tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")"));
|
301 |
+
if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype));
|
302 |
+
var place = cm.cursorCoords(null, "page");
|
303 |
+
ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip);
|
304 |
+
}
|
305 |
+
|
306 |
+
function parseFnType(text) {
|
307 |
+
var args = [], pos = 3;
|
308 |
+
|
309 |
+
function skipMatching(upto) {
|
310 |
+
var depth = 0, start = pos;
|
311 |
+
for (;;) {
|
312 |
+
var next = text.charAt(pos);
|
313 |
+
if (upto.test(next) && !depth) return text.slice(start, pos);
|
314 |
+
if (/[{\[\(]/.test(next)) ++depth;
|
315 |
+
else if (/[}\]\)]/.test(next)) --depth;
|
316 |
+
++pos;
|
317 |
+
}
|
318 |
+
}
|
319 |
+
|
320 |
+
// Parse arguments
|
321 |
+
if (text.charAt(pos) != ")") for (;;) {
|
322 |
+
var name = text.slice(pos).match(/^([^, \(\[\{]+): /);
|
323 |
+
if (name) {
|
324 |
+
pos += name[0].length;
|
325 |
+
name = name[1];
|
326 |
+
}
|
327 |
+
args.push({name: name, type: skipMatching(/[\),]/)});
|
328 |
+
if (text.charAt(pos) == ")") break;
|
329 |
+
pos += 2;
|
330 |
+
}
|
331 |
+
|
332 |
+
var rettype = text.slice(pos).match(/^\) -> (.*)$/);
|
333 |
+
|
334 |
+
return {args: args, rettype: rettype && rettype[1]};
|
335 |
+
}
|
336 |
+
|
337 |
+
// Moving to the definition of something
|
338 |
+
|
339 |
+
function jumpToDef(ts, cm) {
|
340 |
+
function inner(varName) {
|
341 |
+
var req = {type: "definition", variable: varName || null};
|
342 |
+
var doc = findDoc(ts, cm.getDoc());
|
343 |
+
ts.server.request(buildRequest(ts, doc, req), function(error, data) {
|
344 |
+
if (error) return showError(ts, cm, error);
|
345 |
+
if (!data.file && data.url) { window.open(data.url); return; }
|
346 |
+
|
347 |
+
if (data.file) {
|
348 |
+
var localDoc = ts.docs[data.file], found;
|
349 |
+
if (localDoc && (found = findContext(localDoc.doc, data))) {
|
350 |
+
ts.jumpStack.push({file: doc.name,
|
351 |
+
start: cm.getCursor("from"),
|
352 |
+
end: cm.getCursor("to")});
|
353 |
+
moveTo(ts, doc, localDoc, found.start, found.end);
|
354 |
+
return;
|
355 |
+
}
|
356 |
+
}
|
357 |
+
showError(ts, cm, "Could not find a definition.");
|
358 |
+
});
|
359 |
+
}
|
360 |
+
|
361 |
+
if (!atInterestingExpression(cm))
|
362 |
+
dialog(cm, "Jump to variable", function(name) { if (name) inner(name); });
|
363 |
+
else
|
364 |
+
inner();
|
365 |
+
}
|
366 |
+
|
367 |
+
function jumpBack(ts, cm) {
|
368 |
+
var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file];
|
369 |
+
if (!doc) return;
|
370 |
+
moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end);
|
371 |
+
}
|
372 |
+
|
373 |
+
function moveTo(ts, curDoc, doc, start, end) {
|
374 |
+
doc.doc.setSelection(end, start);
|
375 |
+
if (curDoc != doc && ts.options.switchToDoc) {
|
376 |
+
closeArgHints(ts);
|
377 |
+
ts.options.switchToDoc(doc.name);
|
378 |
+
}
|
379 |
+
}
|
380 |
+
|
381 |
+
// The {line,ch} representation of positions makes this rather awkward.
|
382 |
+
function findContext(doc, data) {
|
383 |
+
var before = data.context.slice(0, data.contextOffset).split("\n");
|
384 |
+
var startLine = data.start.line - (before.length - 1);
|
385 |
+
var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length);
|
386 |
+
|
387 |
+
var text = doc.getLine(startLine).slice(start.ch);
|
388 |
+
for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur)
|
389 |
+
text += "\n" + doc.getLine(cur);
|
390 |
+
if (text.slice(0, data.context.length) == data.context) return data;
|
391 |
+
|
392 |
+
var cursor = doc.getSearchCursor(data.context, 0, false);
|
393 |
+
var nearest, nearestDist = Infinity;
|
394 |
+
while (cursor.findNext()) {
|
395 |
+
var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000;
|
396 |
+
if (!dist) dist = Math.abs(from.ch - start.ch);
|
397 |
+
if (dist < nearestDist) { nearest = from; nearestDist = dist; }
|
398 |
+
}
|
399 |
+
if (!nearest) return null;
|
400 |
+
|
401 |
+
if (before.length == 1)
|
402 |
+
nearest.ch += before[0].length;
|
403 |
+
else
|
404 |
+
nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length);
|
405 |
+
if (data.start.line == data.end.line)
|
406 |
+
var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch));
|
407 |
+
else
|
408 |
+
var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch);
|
409 |
+
return {start: nearest, end: end};
|
410 |
+
}
|
411 |
+
|
412 |
+
function atInterestingExpression(cm) {
|
413 |
+
var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos);
|
414 |
+
if (tok.start < pos.ch && (tok.type == "comment" || tok.type == "string")) return false;
|
415 |
+
return /\w/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1));
|
416 |
+
}
|
417 |
+
|
418 |
+
// Variable renaming
|
419 |
+
|
420 |
+
function rename(ts, cm) {
|
421 |
+
var token = cm.getTokenAt(cm.getCursor());
|
422 |
+
if (!/\w/.test(token.string)) showError(ts, cm, "Not at a variable");
|
423 |
+
dialog(cm, "New name for " + token.string, function(newName) {
|
424 |
+
ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) {
|
425 |
+
if (error) return showError(ts, cm, error);
|
426 |
+
applyChanges(ts, data.changes);
|
427 |
+
});
|
428 |
+
});
|
429 |
+
}
|
430 |
+
|
431 |
+
var nextChangeOrig = 0;
|
432 |
+
function applyChanges(ts, changes) {
|
433 |
+
var perFile = Object.create(null);
|
434 |
+
for (var i = 0; i < changes.length; ++i) {
|
435 |
+
var ch = changes[i];
|
436 |
+
(perFile[ch.file] || (perFile[ch.file] = [])).push(ch);
|
437 |
+
}
|
438 |
+
for (var file in perFile) {
|
439 |
+
var known = ts.docs[file], chs = perFile[file];;
|
440 |
+
if (!known) continue;
|
441 |
+
chs.sort(function(a, b) { return cmpPos(b, a); });
|
442 |
+
var origin = "*rename" + (++nextChangeOrig);
|
443 |
+
for (var i = 0; i < chs.length; ++i) {
|
444 |
+
var ch = chs[i];
|
445 |
+
known.doc.replaceRange(ch.text, ch.start, ch.end, origin);
|
446 |
+
}
|
447 |
+
}
|
448 |
+
}
|
449 |
+
|
450 |
+
// Generic request-building helper
|
451 |
+
|
452 |
+
function buildRequest(ts, doc, query) {
|
453 |
+
var files = [], offsetLines = 0, allowFragments = !query.fullDocs;
|
454 |
+
if (!allowFragments) delete query.fullDocs;
|
455 |
+
if (typeof query == "string") query = {type: query};
|
456 |
+
query.lineCharPositions = true;
|
457 |
+
if (query.end == null) {
|
458 |
+
query.end = doc.doc.getCursor("end");
|
459 |
+
if (doc.doc.somethingSelected())
|
460 |
+
query.start = doc.doc.getCursor("start");
|
461 |
+
}
|
462 |
+
var startPos = query.start || query.end;
|
463 |
+
|
464 |
+
if (doc.changed) {
|
465 |
+
if (doc.doc.lineCount() > bigDoc && allowFragments !== false &&
|
466 |
+
doc.changed.to - doc.changed.from < 100 &&
|
467 |
+
doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {
|
468 |
+
files.push(getFragmentAround(doc, startPos, query.end));
|
469 |
+
query.file = "#0";
|
470 |
+
var offsetLines = files[0].offsetLines;
|
471 |
+
if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch);
|
472 |
+
query.end = Pos(query.end.line - offsetLines, query.end.ch);
|
473 |
+
} else {
|
474 |
+
files.push({type: "full",
|
475 |
+
name: doc.name,
|
476 |
+
text: docValue(ts, doc)});
|
477 |
+
query.file = doc.name;
|
478 |
+
doc.changed = null;
|
479 |
+
}
|
480 |
+
} else {
|
481 |
+
query.file = doc.name;
|
482 |
+
}
|
483 |
+
for (var name in ts.docs) {
|
484 |
+
var cur = ts.docs[name];
|
485 |
+
if (cur.changed && cur != doc) {
|
486 |
+
files.push({type: "full", name: cur.name, text: docValue(ts, cur)});
|
487 |
+
cur.changed = null;
|
488 |
+
}
|
489 |
+
}
|
490 |
+
|
491 |
+
return {query: query, files: files};
|
492 |
+
}
|
493 |
+
|
494 |
+
function getFragmentAround(data, start, end) {
|
495 |
+
var doc = data.doc;
|
496 |
+
var minIndent = null, minLine = null, endLine, tabSize = 4;
|
497 |
+
for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {
|
498 |
+
var line = doc.getLine(p), fn = line.search(/\bfunction\b/);
|
499 |
+
if (fn < 0) continue;
|
500 |
+
var indent = CodeMirror.countColumn(line, null, tabSize);
|
501 |
+
if (minIndent != null && minIndent <= indent) continue;
|
502 |
+
minIndent = indent;
|
503 |
+
minLine = p;
|
504 |
+
}
|
505 |
+
if (minLine == null) minLine = min;
|
506 |
+
var max = Math.min(doc.lastLine(), end.line + 20);
|
507 |
+
if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize))
|
508 |
+
endLine = max;
|
509 |
+
else for (endLine = end.line + 1; endLine < max; ++endLine) {
|
510 |
+
var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize);
|
511 |
+
if (indent <= minIndent) break;
|
512 |
+
}
|
513 |
+
var from = Pos(minLine, 0);
|
514 |
+
|
515 |
+
return {type: "part",
|
516 |
+
name: data.name,
|
517 |
+
offsetLines: from.line,
|
518 |
+
text: doc.getRange(from, Pos(endLine, 0))};
|
519 |
+
}
|
520 |
+
|
521 |
+
// Generic utilities
|
522 |
+
|
523 |
+
function cmpPos(a, b) { return a.line - b.line || a.ch - b.ch; }
|
524 |
+
|
525 |
+
function elt(tagname, cls /*, ... elts*/) {
|
526 |
+
var e = document.createElement(tagname);
|
527 |
+
if (cls) e.className = cls;
|
528 |
+
for (var i = 2; i < arguments.length; ++i) {
|
529 |
+
var elt = arguments[i];
|
530 |
+
if (typeof elt == "string") elt = document.createTextNode(elt);
|
531 |
+
e.appendChild(elt);
|
532 |
+
}
|
533 |
+
return e;
|
534 |
+
}
|
535 |
+
|
536 |
+
function dialog(cm, text, f) {
|
537 |
+
if (cm.openDialog)
|
538 |
+
cm.openDialog(text + ": <input type=text>", f);
|
539 |
+
else
|
540 |
+
f(prompt(text, ""));
|
541 |
+
}
|
542 |
+
|
543 |
+
// Tooltips
|
544 |
+
|
545 |
+
function tempTooltip(cm, content) {
|
546 |
+
var where = cm.cursorCoords();
|
547 |
+
var tip = makeTooltip(where.right + 1, where.bottom, content);
|
548 |
+
function clear() {
|
549 |
+
if (!tip.parentNode) return;
|
550 |
+
cm.off("cursorActivity", clear);
|
551 |
+
fadeOut(tip);
|
552 |
+
}
|
553 |
+
setTimeout(clear, 1700);
|
554 |
+
cm.on("cursorActivity", clear);
|
555 |
+
}
|
556 |
+
|
557 |
+
function makeTooltip(x, y, content) {
|
558 |
+
var node = elt("div", cls + "tooltip", content);
|
559 |
+
node.style.left = x + "px";
|
560 |
+
node.style.top = y + "px";
|
561 |
+
document.body.appendChild(node);
|
562 |
+
return node;
|
563 |
+
}
|
564 |
+
|
565 |
+
function remove(node) {
|
566 |
+
var p = node && node.parentNode;
|
567 |
+
if (p) p.removeChild(node);
|
568 |
+
}
|
569 |
+
|
570 |
+
function fadeOut(tooltip) {
|
571 |
+
tooltip.style.opacity = "0";
|
572 |
+
setTimeout(function() { remove(tooltip); }, 1100);
|
573 |
+
}
|
574 |
+
|
575 |
+
function showError(ts, cm, msg) {
|
576 |
+
if (ts.options.showError)
|
577 |
+
ts.options.showError(cm, msg);
|
578 |
+
else
|
579 |
+
tempTooltip(cm, String(msg));
|
580 |
+
}
|
581 |
+
|
582 |
+
function closeArgHints(ts) {
|
583 |
+
if (ts.activeArgHints) { remove(ts.activeArgHints); ts.activeArgHints = null; }
|
584 |
+
}
|
585 |
+
|
586 |
+
function docValue(ts, doc) {
|
587 |
+
var val = doc.doc.getValue();
|
588 |
+
if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc);
|
589 |
+
return val;
|
590 |
+
}
|
591 |
+
|
592 |
+
// Worker wrapper
|
593 |
+
|
594 |
+
function WorkerServer(ts) {
|
595 |
+
var worker = new Worker(ts.options.workerScript);
|
596 |
+
worker.postMessage({type: "init",
|
597 |
+
defs: ts.options.defs,
|
598 |
+
plugins: ts.options.plugins,
|
599 |
+
scripts: ts.options.workerDeps});
|
600 |
+
var msgId = 0, pending = {};
|
601 |
+
|
602 |
+
function send(data, c) {
|
603 |
+
if (c) {
|
604 |
+
data.id = ++msgId;
|
605 |
+
pending[msgId] = c;
|
606 |
+
}
|
607 |
+
worker.postMessage(data);
|
608 |
+
}
|
609 |
+
worker.onmessage = function(e) {
|
610 |
+
var data = e.data;
|
611 |
+
if (data.type == "getFile") {
|
612 |
+
getFile(ts, name, function(err, text) {
|
613 |
+
send({type: "getFile", err: String(err), text: text, id: data.id});
|
614 |
+
});
|
615 |
+
} else if (data.type == "debug") {
|
616 |
+
console.log(data.message);
|
617 |
+
} else if (data.id && pending[data.id]) {
|
618 |
+
pending[data.id](data.err, data.body);
|
619 |
+
delete pending[data.id];
|
620 |
+
}
|
621 |
+
};
|
622 |
+
worker.onerror = function(e) {
|
623 |
+
for (var id in pending) pending[id](e);
|
624 |
+
pending = {};
|
625 |
+
};
|
626 |
+
|
627 |
+
this.addFile = function(name, text) { send({type: "add", name: name, text: text}); };
|
628 |
+
this.delFile = function(name) { send({type: "del", name: name}); };
|
629 |
+
this.request = function(body, c) { send({type: "req", body: body}, c); };
|
630 |
+
}
|
631 |
+
})();
|
assets/lib/codemirror/addon/tern/worker.js
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
var server;
|
2 |
+
|
3 |
+
this.onmessage = function(e) {
|
4 |
+
var data = e.data;
|
5 |
+
switch (data.type) {
|
6 |
+
case "init": return startServer(data.defs, data.plugins, data.scripts);
|
7 |
+
case "add": return server.addFile(data.name, data.text);
|
8 |
+
case "del": return server.delFile(data.name);
|
9 |
+
case "req": return server.request(data.body, function(err, reqData) {
|
10 |
+
postMessage({id: data.id, body: reqData, err: err && String(err)});
|
11 |
+
});
|
12 |
+
case "getFile":
|
13 |
+
var c = pending[data.id];
|
14 |
+
delete pending[data.id];
|
15 |
+
return c(data.err, data.text);
|
16 |
+
default: throw new Error("Unknown message type: " + data.type);
|
17 |
+
}
|
18 |
+
};
|
19 |
+
|
20 |
+
var nextId = 0, pending = {};
|
21 |
+
function getFile(file, c) {
|
22 |
+
postMessage({type: "getFile", name: file, id: ++nextId});
|
23 |
+
pending[nextId] = c;
|
24 |
+
}
|
25 |
+
|
26 |
+
function startServer(defs, plugins, scripts) {
|
27 |
+
if (scripts) importScripts.apply(null, scripts);
|
28 |
+
|
29 |
+
server = new tern.Server({
|
30 |
+
getFile: getFile,
|
31 |
+
async: true,
|
32 |
+
defs: defs,
|
33 |
+
plugins: plugins
|
34 |
+
});
|
35 |
+
}
|
36 |
+
|
37 |
+
var console = {
|
38 |
+
log: function(v) { postMessage({type: "debug", message: v}); }
|
39 |
+
};
|
assets/lib/codemirror/lib/codemirror.css
ADDED
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* BASICS */
|
2 |
+
|
3 |
+
.CodeMirror {
|
4 |
+
/* Set height, width, borders, and global font properties here */
|
5 |
+
font-family: monospace;
|
6 |
+
height: 300px;
|
7 |
+
}
|
8 |
+
.CodeMirror-scroll {
|
9 |
+
/* Set scrolling behaviour here */
|
10 |
+
overflow: auto;
|
11 |
+
}
|
12 |
+
|
13 |
+
/* PADDING */
|
14 |
+
|
15 |
+
.CodeMirror-lines {
|
16 |
+
padding: 4px 0; /* Vertical padding around content */
|
17 |
+
}
|
18 |
+
.CodeMirror pre {
|
19 |
+
padding: 0 4px; /* Horizontal padding of content */
|
20 |
+
}
|
21 |
+
|
22 |
+
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
23 |
+
background-color: white; /* The little square between H and V scrollbars */
|
24 |
+
}
|
25 |
+
|
26 |
+
/* GUTTER */
|
27 |
+
|
28 |
+
.CodeMirror-gutters {
|
29 |
+
border-right: 1px solid #ddd;
|
30 |
+
background-color: #f7f7f7;
|
31 |
+
white-space: nowrap;
|
32 |
+
}
|
33 |
+
.CodeMirror-linenumbers {}
|
34 |
+
.CodeMirror-linenumber {
|
35 |
+
padding: 0 3px 0 5px;
|
36 |
+
min-width: 20px;
|
37 |
+
text-align: right;
|
38 |
+
color: #999;
|
39 |
+
}
|
40 |
+
|
41 |
+
/* CURSOR */
|
42 |
+
|
43 |
+
.CodeMirror div.CodeMirror-cursor {
|
44 |
+
border-left: 1px solid black;
|
45 |
+
z-index: 3;
|
46 |
+
}
|
47 |
+
/* Shown when moving in bi-directional text */
|
48 |
+
.CodeMirror div.CodeMirror-secondarycursor {
|
49 |
+
border-left: 1px solid silver;
|
50 |
+
}
|
51 |
+
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
|
52 |
+
width: auto;
|
53 |
+
border: 0;
|
54 |
+
background: #7e7;
|
55 |
+
z-index: 1;
|
56 |
+
}
|
57 |
+
/* Can style cursor different in overwrite (non-insert) mode */
|
58 |
+
.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
|
59 |
+
|
60 |
+
.cm-tab { display: inline-block; }
|
61 |
+
|
62 |
+
/* DEFAULT THEME */
|
63 |
+
|
64 |
+
.cm-s-default .cm-keyword {color: #708;}
|
65 |
+
.cm-s-default .cm-atom {color: #219;}
|
66 |
+
.cm-s-default .cm-number {color: #164;}
|
67 |
+
.cm-s-default .cm-def {color: #00f;}
|
68 |
+
.cm-s-default .cm-variable {color: black;}
|
69 |
+
.cm-s-default .cm-variable-2 {color: #05a;}
|
70 |
+
.cm-s-default .cm-variable-3 {color: #085;}
|
71 |
+
.cm-s-default .cm-property {color: black;}
|
72 |
+
.cm-s-default .cm-operator {color: black;}
|
73 |
+
.cm-s-default .cm-comment {color: #a50;}
|
74 |
+
.cm-s-default .cm-string {color: #a11;}
|
75 |
+
.cm-s-default .cm-string-2 {color: #f50;}
|
76 |
+
.cm-s-default .cm-meta {color: #555;}
|
77 |
+
.cm-s-default .cm-error {color: #f00;}
|
78 |
+
.cm-s-default .cm-qualifier {color: #555;}
|
79 |
+
.cm-s-default .cm-builtin {color: #30a;}
|
80 |
+
.cm-s-default .cm-bracket {color: #997;}
|
81 |
+
.cm-s-default .cm-tag {color: #170;}
|
82 |
+
.cm-s-default .cm-attribute {color: #00c;}
|
83 |
+
.cm-s-default .cm-header {color: blue;}
|
84 |
+
.cm-s-default .cm-quote {color: #090;}
|
85 |
+
.cm-s-default .cm-hr {color: #999;}
|
86 |
+
.cm-s-default .cm-link {color: #00c;}
|
87 |
+
|
88 |
+
.cm-negative {color: #d44;}
|
89 |
+
.cm-positive {color: #292;}
|
90 |
+
.cm-header, .cm-strong {font-weight: bold;}
|
91 |
+
.cm-em {font-style: italic;}
|
92 |
+
.cm-link {text-decoration: underline;}
|
93 |
+
|
94 |
+
.cm-invalidchar {color: #f00;}
|
95 |
+
|
96 |
+
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
97 |
+
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
98 |
+
.CodeMirror-activeline-background {background: #e8f2ff;}
|
99 |
+
|
100 |
+
/* STOP */
|
101 |
+
|
102 |
+
/* The rest of this file contains styles related to the mechanics of
|
103 |
+
the editor. You probably shouldn't touch them. */
|
104 |
+
|
105 |
+
.CodeMirror {
|
106 |
+
line-height: 1;
|
107 |
+
position: relative;
|
108 |
+
overflow: hidden;
|
109 |
+
background: white;
|
110 |
+
color: black;
|
111 |
+
}
|
112 |
+
|
113 |
+
.CodeMirror-scroll {
|
114 |
+
/* 30px is the magic margin used to hide the element's real scrollbars */
|
115 |
+
/* See overflow: hidden in .CodeMirror */
|
116 |
+
margin-bottom: -30px; margin-right: -30px;
|
117 |
+
padding-bottom: 30px; padding-right: 30px;
|
118 |
+
height: 100%;
|
119 |
+
outline: none; /* Prevent dragging from highlighting the element */
|
120 |
+
position: relative;
|
121 |
+
-moz-box-sizing: content-box;
|
122 |
+
box-sizing: content-box;
|
123 |
+
}
|
124 |
+
.CodeMirror-sizer {
|
125 |
+
position: relative;
|
126 |
+
}
|
127 |
+
|
128 |
+
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
129 |
+
before actuall scrolling happens, thus preventing shaking and
|
130 |
+
flickering artifacts. */
|
131 |
+
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
132 |
+
position: absolute;
|
133 |
+
z-index: 6;
|
134 |
+
display: none;
|
135 |
+
}
|
136 |
+
.CodeMirror-vscrollbar {
|
137 |
+
right: 0; top: 0;
|
138 |
+
overflow-x: hidden;
|
139 |
+
overflow-y: scroll;
|
140 |
+
}
|
141 |
+
.CodeMirror-hscrollbar {
|
142 |
+
bottom: 0; left: 0;
|
143 |
+
overflow-y: hidden;
|
144 |
+
overflow-x: scroll;
|
145 |
+
}
|
146 |
+
.CodeMirror-scrollbar-filler {
|
147 |
+
right: 0; bottom: 0;
|
148 |
+
}
|
149 |
+
.CodeMirror-gutter-filler {
|
150 |
+
left: 0; bottom: 0;
|
151 |
+
}
|
152 |
+
|
153 |
+
.CodeMirror-gutters {
|
154 |
+
position: absolute; left: 0; top: 0;
|
155 |
+
padding-bottom: 30px;
|
156 |
+
z-index: 3;
|
157 |
+
}
|
158 |
+
.CodeMirror-gutter {
|
159 |
+
white-space: normal;
|
160 |
+
height: 100%;
|
161 |
+
-moz-box-sizing: content-box;
|
162 |
+
box-sizing: content-box;
|
163 |
+
padding-bottom: 30px;
|
164 |
+
margin-bottom: -32px;
|
165 |
+
display: inline-block;
|
166 |
+
/* Hack to make IE7 behave */
|
167 |
+
*zoom:1;
|
168 |
+
*display:inline;
|
169 |
+
}
|
170 |
+
.CodeMirror-gutter-elt {
|
171 |
+
position: absolute;
|
172 |
+
cursor: default;
|
173 |
+
z-index: 4;
|
174 |
+
}
|
175 |
+
|
176 |
+
.CodeMirror-lines {
|
177 |
+
cursor: text;
|
178 |
+
}
|
179 |
+
.CodeMirror pre {
|
180 |
+
/* Reset some styles that the rest of the page might have set */
|
181 |
+
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
|
182 |
+
border-width: 0;
|
183 |
+
background: transparent;
|
184 |
+
font-family: inherit;
|
185 |
+
font-size: inherit;
|
186 |
+
margin: 0;
|
187 |
+
white-space: pre;
|
188 |
+
word-wrap: normal;
|
189 |
+
line-height: inherit;
|
190 |
+
color: inherit;
|
191 |
+
z-index: 2;
|
192 |
+
position: relative;
|
193 |
+
overflow: visible;
|
194 |
+
}
|
195 |
+
.CodeMirror-wrap pre {
|
196 |
+
word-wrap: break-word;
|
197 |
+
white-space: pre-wrap;
|
198 |
+
word-break: normal;
|
199 |
+
}
|
200 |
+
.CodeMirror-code pre {
|
201 |
+
border-right: 30px solid transparent;
|
202 |
+
width: -webkit-fit-content;
|
203 |
+
width: -moz-fit-content;
|
204 |
+
width: fit-content;
|
205 |
+
}
|
206 |
+
.CodeMirror-wrap .CodeMirror-code pre {
|
207 |
+
border-right: none;
|
208 |
+
width: auto;
|
209 |
+
}
|
210 |
+
.CodeMirror-linebackground {
|
211 |
+
position: absolute;
|
212 |
+
left: 0; right: 0; top: 0; bottom: 0;
|
213 |
+
z-index: 0;
|
214 |
+
}
|
215 |
+
|
216 |
+
.CodeMirror-linewidget {
|
217 |
+
position: relative;
|
218 |
+
z-index: 2;
|
219 |
+
overflow: auto;
|
220 |
+
}
|
221 |
+
|
222 |
+
.CodeMirror-widget {}
|
223 |
+
|
224 |
+
.CodeMirror-wrap .CodeMirror-scroll {
|
225 |
+
overflow-x: hidden;
|
226 |
+
}
|
227 |
+
|
228 |
+
.CodeMirror-measure {
|
229 |
+
position: absolute;
|
230 |
+
width: 100%;
|
231 |
+
height: 0;
|
232 |
+
overflow: hidden;
|
233 |
+
visibility: hidden;
|
234 |
+
}
|
235 |
+
.CodeMirror-measure pre { position: static; }
|
236 |
+
|
237 |
+
.CodeMirror div.CodeMirror-cursor {
|
238 |
+
position: absolute;
|
239 |
+
visibility: hidden;
|
240 |
+
border-right: none;
|
241 |
+
width: 0;
|
242 |
+
}
|
243 |
+
.CodeMirror-focused div.CodeMirror-cursor {
|
244 |
+
visibility: visible;
|
245 |
+
}
|
246 |
+
|
247 |
+
.CodeMirror-selected { background: #d9d9d9; }
|
248 |
+
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
249 |
+
|
250 |
+
.cm-searching {
|
251 |
+
background: #ffa;
|
252 |
+
background: rgba(255, 255, 0, .4);
|
253 |
+
}
|
254 |
+
|
255 |
+
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
|
256 |
+
.CodeMirror span { *vertical-align: text-bottom; }
|
257 |
+
|
258 |
+
@media print {
|
259 |
+
/* Hide the cursor when printing */
|
260 |
+
.CodeMirror div.CodeMirror-cursor {
|
261 |
+
visibility: hidden;
|
262 |
+
}
|
263 |
+
}
|
264 |
+
|
265 |
+
|
266 |
+
/* fullscreen adapted code */
|
267 |
+
.CodeMirror-fullscreen {
|
268 |
+
position: fixed;
|
269 |
+
top: 0; left: 0; right: 0; bottom: 0;
|
270 |
+
height: auto;
|
271 |
+
width:auto !important;
|
272 |
+
z-index: 999999;
|
273 |
+
}
|
assets/lib/codemirror/lib/codemirror.js
ADDED
@@ -0,0 +1,5887 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// CodeMirror version 3.17
|
2 |
+
//
|
3 |
+
// CodeMirror is the only global var we claim
|
4 |
+
window.CodeMirror = (function() {
|
5 |
+
"use strict";
|
6 |
+
|
7 |
+
// BROWSER SNIFFING
|
8 |
+
|
9 |
+
// Crude, but necessary to handle a number of hard-to-feature-detect
|
10 |
+
// bugs and behavior differences.
|
11 |
+
var gecko = /gecko\/\d/i.test(navigator.userAgent);
|
12 |
+
var ie = /MSIE \d/.test(navigator.userAgent);
|
13 |
+
var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
|
14 |
+
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
|
15 |
+
var webkit = /WebKit\//.test(navigator.userAgent);
|
16 |
+
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
|
17 |
+
var chrome = /Chrome\//.test(navigator.userAgent);
|
18 |
+
var opera = /Opera\//.test(navigator.userAgent);
|
19 |
+
var safari = /Apple Computer/.test(navigator.vendor);
|
20 |
+
var khtml = /KHTML\//.test(navigator.userAgent);
|
21 |
+
var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
|
22 |
+
var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
|
23 |
+
var phantom = /PhantomJS/.test(navigator.userAgent);
|
24 |
+
|
25 |
+
var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
|
26 |
+
// This is woefully incomplete. Suggestions for alternative methods welcome.
|
27 |
+
var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
|
28 |
+
var mac = ios || /Mac/.test(navigator.platform);
|
29 |
+
var windows = /win/i.test(navigator.platform);
|
30 |
+
|
31 |
+
var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
|
32 |
+
if (opera_version) opera_version = Number(opera_version[1]);
|
33 |
+
if (opera_version && opera_version >= 15) { opera = false; webkit = true; }
|
34 |
+
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
|
35 |
+
var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
|
36 |
+
var captureMiddleClick = gecko || (ie && !ie_lt9);
|
37 |
+
|
38 |
+
// Optimize some code when these features are not used
|
39 |
+
var sawReadOnlySpans = false, sawCollapsedSpans = false;
|
40 |
+
|
41 |
+
// CONSTRUCTOR
|
42 |
+
|
43 |
+
function CodeMirror(place, options) {
|
44 |
+
if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
|
45 |
+
|
46 |
+
this.options = options = options || {};
|
47 |
+
// Determine effective options based on given values and defaults.
|
48 |
+
for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
|
49 |
+
options[opt] = defaults[opt];
|
50 |
+
setGuttersForLineNumbers(options);
|
51 |
+
|
52 |
+
var docStart = typeof options.value == "string" ? 0 : options.value.first;
|
53 |
+
var display = this.display = makeDisplay(place, docStart);
|
54 |
+
display.wrapper.CodeMirror = this;
|
55 |
+
updateGutters(this);
|
56 |
+
if (options.autofocus && !mobile) focusInput(this);
|
57 |
+
|
58 |
+
this.state = {keyMaps: [],
|
59 |
+
overlays: [],
|
60 |
+
modeGen: 0,
|
61 |
+
overwrite: false, focused: false,
|
62 |
+
suppressEdits: false, pasteIncoming: false,
|
63 |
+
draggingText: false,
|
64 |
+
highlight: new Delayed()};
|
65 |
+
|
66 |
+
themeChanged(this);
|
67 |
+
if (options.lineWrapping)
|
68 |
+
this.display.wrapper.className += " CodeMirror-wrap";
|
69 |
+
|
70 |
+
var doc = options.value;
|
71 |
+
if (typeof doc == "string") doc = new Doc(options.value, options.mode);
|
72 |
+
operation(this, attachDoc)(this, doc);
|
73 |
+
|
74 |
+
// Override magic textarea content restore that IE sometimes does
|
75 |
+
// on our hidden textarea on reload
|
76 |
+
if (ie) setTimeout(bind(resetInput, this, true), 20);
|
77 |
+
|
78 |
+
registerEventHandlers(this);
|
79 |
+
// IE throws unspecified error in certain cases, when
|
80 |
+
// trying to access activeElement before onload
|
81 |
+
var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
|
82 |
+
if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
|
83 |
+
else onBlur(this);
|
84 |
+
|
85 |
+
operation(this, function() {
|
86 |
+
for (var opt in optionHandlers)
|
87 |
+
if (optionHandlers.propertyIsEnumerable(opt))
|
88 |
+
optionHandlers[opt](this, options[opt], Init);
|
89 |
+
for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
|
90 |
+
})();
|
91 |
+
}
|
92 |
+
|
93 |
+
// DISPLAY CONSTRUCTOR
|
94 |
+
|
95 |
+
function makeDisplay(place, docStart) {
|
96 |
+
var d = {};
|
97 |
+
|
98 |
+
var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;");
|
99 |
+
if (webkit) input.style.width = "1000px";
|
100 |
+
else input.setAttribute("wrap", "off");
|
101 |
+
// if border: 0; -- iOS fails to open keyboard (issue #1287)
|
102 |
+
if (ios) input.style.border = "1px solid black";
|
103 |
+
input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
|
104 |
+
|
105 |
+
// Wraps and hides input textarea
|
106 |
+
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
|
107 |
+
// The actual fake scrollbars.
|
108 |
+
d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
|
109 |
+
d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
|
110 |
+
d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
|
111 |
+
d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
|
112 |
+
// DIVs containing the selection and the actual code
|
113 |
+
d.lineDiv = elt("div", null, "CodeMirror-code");
|
114 |
+
d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
|
115 |
+
// Blinky cursor, and element used to ensure cursor fits at the end of a line
|
116 |
+
d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
|
117 |
+
// Secondary cursor, shown when on a 'jump' in bi-directional text
|
118 |
+
d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
|
119 |
+
// Used to measure text size
|
120 |
+
d.measure = elt("div", null, "CodeMirror-measure");
|
121 |
+
// Wraps everything that needs to exist inside the vertically-padded coordinate system
|
122 |
+
d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
|
123 |
+
null, "position: relative; outline: none");
|
124 |
+
// Moved around its parent to cover visible view
|
125 |
+
d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
|
126 |
+
// Set to the height of the text, causes scrolling
|
127 |
+
d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
|
128 |
+
// D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
|
129 |
+
d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
|
130 |
+
// Will contain the gutters, if any
|
131 |
+
d.gutters = elt("div", null, "CodeMirror-gutters");
|
132 |
+
d.lineGutter = null;
|
133 |
+
// Provides scrolling
|
134 |
+
d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
|
135 |
+
d.scroller.setAttribute("tabIndex", "-1");
|
136 |
+
// The element in which the editor lives.
|
137 |
+
d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
|
138 |
+
d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
|
139 |
+
// Work around IE7 z-index bug
|
140 |
+
if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
|
141 |
+
if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
|
142 |
+
|
143 |
+
// Needed to hide big blue blinking cursor on Mobile Safari
|
144 |
+
if (ios) input.style.width = "0px";
|
145 |
+
if (!webkit) d.scroller.draggable = true;
|
146 |
+
// Needed to handle Tab key in KHTML
|
147 |
+
if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
|
148 |
+
// Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
|
149 |
+
else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
|
150 |
+
|
151 |
+
// Current visible range (may be bigger than the view window).
|
152 |
+
d.viewOffset = d.lastSizeC = 0;
|
153 |
+
d.showingFrom = d.showingTo = docStart;
|
154 |
+
|
155 |
+
// Used to only resize the line number gutter when necessary (when
|
156 |
+
// the amount of lines crosses a boundary that makes its width change)
|
157 |
+
d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
|
158 |
+
// See readInput and resetInput
|
159 |
+
d.prevInput = "";
|
160 |
+
// Set to true when a non-horizontal-scrolling widget is added. As
|
161 |
+
// an optimization, widget aligning is skipped when d is false.
|
162 |
+
d.alignWidgets = false;
|
163 |
+
// Flag that indicates whether we currently expect input to appear
|
164 |
+
// (after some event like 'keypress' or 'input') and are polling
|
165 |
+
// intensively.
|
166 |
+
d.pollingFast = false;
|
167 |
+
// Self-resetting timeout for the poller
|
168 |
+
d.poll = new Delayed();
|
169 |
+
|
170 |
+
d.cachedCharWidth = d.cachedTextHeight = null;
|
171 |
+
d.measureLineCache = [];
|
172 |
+
d.measureLineCachePos = 0;
|
173 |
+
|
174 |
+
// Tracks when resetInput has punted to just putting a short
|
175 |
+
// string instead of the (large) selection.
|
176 |
+
d.inaccurateSelection = false;
|
177 |
+
|
178 |
+
// Tracks the maximum line length so that the horizontal scrollbar
|
179 |
+
// can be kept static when scrolling.
|
180 |
+
d.maxLine = null;
|
181 |
+
d.maxLineLength = 0;
|
182 |
+
d.maxLineChanged = false;
|
183 |
+
|
184 |
+
// Used for measuring wheel scrolling granularity
|
185 |
+
d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
|
186 |
+
|
187 |
+
return d;
|
188 |
+
}
|
189 |
+
|
190 |
+
// STATE UPDATES
|
191 |
+
|
192 |
+
// Used to get the editor into a consistent state again when options change.
|
193 |
+
|
194 |
+
function loadMode(cm) {
|
195 |
+
cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
|
196 |
+
cm.doc.iter(function(line) {
|
197 |
+
if (line.stateAfter) line.stateAfter = null;
|
198 |
+
if (line.styles) line.styles = null;
|
199 |
+
});
|
200 |
+
cm.doc.frontier = cm.doc.first;
|
201 |
+
startWorker(cm, 100);
|
202 |
+
cm.state.modeGen++;
|
203 |
+
if (cm.curOp) regChange(cm);
|
204 |
+
}
|
205 |
+
|
206 |
+
function wrappingChanged(cm) {
|
207 |
+
if (cm.options.lineWrapping) {
|
208 |
+
cm.display.wrapper.className += " CodeMirror-wrap";
|
209 |
+
cm.display.sizer.style.minWidth = "";
|
210 |
+
} else {
|
211 |
+
cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
|
212 |
+
computeMaxLength(cm);
|
213 |
+
}
|
214 |
+
estimateLineHeights(cm);
|
215 |
+
regChange(cm);
|
216 |
+
clearCaches(cm);
|
217 |
+
setTimeout(function(){updateScrollbars(cm);}, 100);
|
218 |
+
}
|
219 |
+
|
220 |
+
function estimateHeight(cm) {
|
221 |
+
var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
|
222 |
+
var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
|
223 |
+
return function(line) {
|
224 |
+
if (lineIsHidden(cm.doc, line))
|
225 |
+
return 0;
|
226 |
+
else if (wrapping)
|
227 |
+
return (Math.ceil(line.text.length / perLine) || 1) * th;
|
228 |
+
else
|
229 |
+
return th;
|
230 |
+
};
|
231 |
+
}
|
232 |
+
|
233 |
+
function estimateLineHeights(cm) {
|
234 |
+
var doc = cm.doc, est = estimateHeight(cm);
|
235 |
+
doc.iter(function(line) {
|
236 |
+
var estHeight = est(line);
|
237 |
+
if (estHeight != line.height) updateLineHeight(line, estHeight);
|
238 |
+
});
|
239 |
+
}
|
240 |
+
|
241 |
+
function keyMapChanged(cm) {
|
242 |
+
var map = keyMap[cm.options.keyMap], style = map.style;
|
243 |
+
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
|
244 |
+
(style ? " cm-keymap-" + style : "");
|
245 |
+
cm.state.disableInput = map.disableInput;
|
246 |
+
}
|
247 |
+
|
248 |
+
function themeChanged(cm) {
|
249 |
+
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
|
250 |
+
cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
|
251 |
+
clearCaches(cm);
|
252 |
+
}
|
253 |
+
|
254 |
+
function guttersChanged(cm) {
|
255 |
+
updateGutters(cm);
|
256 |
+
regChange(cm);
|
257 |
+
setTimeout(function(){alignHorizontally(cm);}, 20);
|
258 |
+
}
|
259 |
+
|
260 |
+
function updateGutters(cm) {
|
261 |
+
var gutters = cm.display.gutters, specs = cm.options.gutters;
|
262 |
+
removeChildren(gutters);
|
263 |
+
for (var i = 0; i < specs.length; ++i) {
|
264 |
+
var gutterClass = specs[i];
|
265 |
+
var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
|
266 |
+
if (gutterClass == "CodeMirror-linenumbers") {
|
267 |
+
cm.display.lineGutter = gElt;
|
268 |
+
gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
|
269 |
+
}
|
270 |
+
}
|
271 |
+
gutters.style.display = i ? "" : "none";
|
272 |
+
}
|
273 |
+
|
274 |
+
function lineLength(doc, line) {
|
275 |
+
if (line.height == 0) return 0;
|
276 |
+
var len = line.text.length, merged, cur = line;
|
277 |
+
while (merged = collapsedSpanAtStart(cur)) {
|
278 |
+
var found = merged.find();
|
279 |
+
cur = getLine(doc, found.from.line);
|
280 |
+
len += found.from.ch - found.to.ch;
|
281 |
+
}
|
282 |
+
cur = line;
|
283 |
+
while (merged = collapsedSpanAtEnd(cur)) {
|
284 |
+
var found = merged.find();
|
285 |
+
len -= cur.text.length - found.from.ch;
|
286 |
+
cur = getLine(doc, found.to.line);
|
287 |
+
len += cur.text.length - found.to.ch;
|
288 |
+
}
|
289 |
+
return len;
|
290 |
+
}
|
291 |
+
|
292 |
+
function computeMaxLength(cm) {
|
293 |
+
var d = cm.display, doc = cm.doc;
|
294 |
+
d.maxLine = getLine(doc, doc.first);
|
295 |
+
d.maxLineLength = lineLength(doc, d.maxLine);
|
296 |
+
d.maxLineChanged = true;
|
297 |
+
doc.iter(function(line) {
|
298 |
+
var len = lineLength(doc, line);
|
299 |
+
if (len > d.maxLineLength) {
|
300 |
+
d.maxLineLength = len;
|
301 |
+
d.maxLine = line;
|
302 |
+
}
|
303 |
+
});
|
304 |
+
}
|
305 |
+
|
306 |
+
// Make sure the gutters options contains the element
|
307 |
+
// "CodeMirror-linenumbers" when the lineNumbers option is true.
|
308 |
+
function setGuttersForLineNumbers(options) {
|
309 |
+
var found = indexOf(options.gutters, "CodeMirror-linenumbers");
|
310 |
+
if (found == -1 && options.lineNumbers) {
|
311 |
+
options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
|
312 |
+
} else if (found > -1 && !options.lineNumbers) {
|
313 |
+
options.gutters = options.gutters.slice(0);
|
314 |
+
options.gutters.splice(i, 1);
|
315 |
+
}
|
316 |
+
}
|
317 |
+
|
318 |
+
// SCROLLBARS
|
319 |
+
|
320 |
+
// Re-synchronize the fake scrollbars with the actual size of the
|
321 |
+
// content. Optionally force a scrollTop.
|
322 |
+
function updateScrollbars(cm) {
|
323 |
+
var d = cm.display, docHeight = cm.doc.height;
|
324 |
+
var totalHeight = docHeight + paddingVert(d);
|
325 |
+
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
|
326 |
+
d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
|
327 |
+
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
|
328 |
+
var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
|
329 |
+
var needsV = scrollHeight > (d.scroller.clientHeight + 1);
|
330 |
+
if (needsV) {
|
331 |
+
d.scrollbarV.style.display = "block";
|
332 |
+
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
|
333 |
+
d.scrollbarV.firstChild.style.height =
|
334 |
+
(scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
|
335 |
+
} else {
|
336 |
+
d.scrollbarV.style.display = "";
|
337 |
+
d.scrollbarV.firstChild.style.height = "0";
|
338 |
+
}
|
339 |
+
if (needsH) {
|
340 |
+
d.scrollbarH.style.display = "block";
|
341 |
+
d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
|
342 |
+
d.scrollbarH.firstChild.style.width =
|
343 |
+
(d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
|
344 |
+
} else {
|
345 |
+
d.scrollbarH.style.display = "";
|
346 |
+
d.scrollbarH.firstChild.style.width = "0";
|
347 |
+
}
|
348 |
+
if (needsH && needsV) {
|
349 |
+
d.scrollbarFiller.style.display = "block";
|
350 |
+
d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
|
351 |
+
} else d.scrollbarFiller.style.display = "";
|
352 |
+
if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
|
353 |
+
d.gutterFiller.style.display = "block";
|
354 |
+
d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px";
|
355 |
+
d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
|
356 |
+
} else d.gutterFiller.style.display = "";
|
357 |
+
|
358 |
+
if (mac_geLion && scrollbarWidth(d.measure) === 0)
|
359 |
+
d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
|
360 |
+
}
|
361 |
+
|
362 |
+
function visibleLines(display, doc, viewPort) {
|
363 |
+
var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
|
364 |
+
if (typeof viewPort == "number") top = viewPort;
|
365 |
+
else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
|
366 |
+
top = Math.floor(top - paddingTop(display));
|
367 |
+
var bottom = Math.ceil(top + height);
|
368 |
+
return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
|
369 |
+
}
|
370 |
+
|
371 |
+
// LINE NUMBERS
|
372 |
+
|
373 |
+
function alignHorizontally(cm) {
|
374 |
+
var display = cm.display;
|
375 |
+
if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
|
376 |
+
var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
|
377 |
+
var gutterW = display.gutters.offsetWidth, l = comp + "px";
|
378 |
+
for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
|
379 |
+
for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
|
380 |
+
}
|
381 |
+
if (cm.options.fixedGutter)
|
382 |
+
display.gutters.style.left = (comp + gutterW) + "px";
|
383 |
+
}
|
384 |
+
|
385 |
+
function maybeUpdateLineNumberWidth(cm) {
|
386 |
+
if (!cm.options.lineNumbers) return false;
|
387 |
+
var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
|
388 |
+
if (last.length != display.lineNumChars) {
|
389 |
+
var test = display.measure.appendChild(elt("div", [elt("div", last)],
|
390 |
+
"CodeMirror-linenumber CodeMirror-gutter-elt"));
|
391 |
+
var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
|
392 |
+
display.lineGutter.style.width = "";
|
393 |
+
display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
|
394 |
+
display.lineNumWidth = display.lineNumInnerWidth + padding;
|
395 |
+
display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
|
396 |
+
display.lineGutter.style.width = display.lineNumWidth + "px";
|
397 |
+
return true;
|
398 |
+
}
|
399 |
+
return false;
|
400 |
+
}
|
401 |
+
|
402 |
+
function lineNumberFor(options, i) {
|
403 |
+
return String(options.lineNumberFormatter(i + options.firstLineNumber));
|
404 |
+
}
|
405 |
+
function compensateForHScroll(display) {
|
406 |
+
return getRect(display.scroller).left - getRect(display.sizer).left;
|
407 |
+
}
|
408 |
+
|
409 |
+
// DISPLAY DRAWING
|
410 |
+
|
411 |
+
function updateDisplay(cm, changes, viewPort, forced) {
|
412 |
+
var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
|
413 |
+
var visible = visibleLines(cm.display, cm.doc, viewPort);
|
414 |
+
for (var first = true;; first = false) {
|
415 |
+
var oldWidth = cm.display.scroller.clientWidth;
|
416 |
+
if (!updateDisplayInner(cm, changes, visible, forced)) break;
|
417 |
+
updated = true;
|
418 |
+
changes = [];
|
419 |
+
updateSelection(cm);
|
420 |
+
updateScrollbars(cm);
|
421 |
+
if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) {
|
422 |
+
forced = true;
|
423 |
+
continue;
|
424 |
+
}
|
425 |
+
forced = false;
|
426 |
+
|
427 |
+
// Clip forced viewport to actual scrollable area
|
428 |
+
if (viewPort)
|
429 |
+
viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
|
430 |
+
typeof viewPort == "number" ? viewPort : viewPort.top);
|
431 |
+
visible = visibleLines(cm.display, cm.doc, viewPort);
|
432 |
+
if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
|
433 |
+
break;
|
434 |
+
}
|
435 |
+
|
436 |
+
if (updated) {
|
437 |
+
signalLater(cm, "update", cm);
|
438 |
+
if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
|
439 |
+
signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
|
440 |
+
}
|
441 |
+
return updated;
|
442 |
+
}
|
443 |
+
|
444 |
+
// Uses a set of changes plus the current scroll position to
|
445 |
+
// determine which DOM updates have to be made, and makes the
|
446 |
+
// updates.
|
447 |
+
function updateDisplayInner(cm, changes, visible, forced) {
|
448 |
+
var display = cm.display, doc = cm.doc;
|
449 |
+
if (!display.wrapper.clientWidth) {
|
450 |
+
display.showingFrom = display.showingTo = doc.first;
|
451 |
+
display.viewOffset = 0;
|
452 |
+
return;
|
453 |
+
}
|
454 |
+
|
455 |
+
// Bail out if the visible area is already rendered and nothing changed.
|
456 |
+
if (!forced && changes.length == 0 &&
|
457 |
+
visible.from > display.showingFrom && visible.to < display.showingTo)
|
458 |
+
return;
|
459 |
+
|
460 |
+
if (maybeUpdateLineNumberWidth(cm))
|
461 |
+
changes = [{from: doc.first, to: doc.first + doc.size}];
|
462 |
+
var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
|
463 |
+
display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
|
464 |
+
|
465 |
+
// Used to determine which lines need their line numbers updated
|
466 |
+
var positionsChangedFrom = Infinity;
|
467 |
+
if (cm.options.lineNumbers)
|
468 |
+
for (var i = 0; i < changes.length; ++i)
|
469 |
+
if (changes[i].diff && changes[i].from < positionsChangedFrom) { positionsChangedFrom = changes[i].from; }
|
470 |
+
|
471 |
+
var end = doc.first + doc.size;
|
472 |
+
var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
|
473 |
+
var to = Math.min(end, visible.to + cm.options.viewportMargin);
|
474 |
+
if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
|
475 |
+
if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
|
476 |
+
if (sawCollapsedSpans) {
|
477 |
+
from = lineNo(visualLine(doc, getLine(doc, from)));
|
478 |
+
while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
|
479 |
+
}
|
480 |
+
|
481 |
+
// Create a range of theoretically intact lines, and punch holes
|
482 |
+
// in that using the change info.
|
483 |
+
var intact = [{from: Math.max(display.showingFrom, doc.first),
|
484 |
+
to: Math.min(display.showingTo, end)}];
|
485 |
+
if (intact[0].from >= intact[0].to) intact = [];
|
486 |
+
else intact = computeIntact(intact, changes);
|
487 |
+
// When merged lines are present, we might have to reduce the
|
488 |
+
// intact ranges because changes in continued fragments of the
|
489 |
+
// intact lines do require the lines to be redrawn.
|
490 |
+
if (sawCollapsedSpans)
|
491 |
+
for (var i = 0; i < intact.length; ++i) {
|
492 |
+
var range = intact[i], merged;
|
493 |
+
while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
|
494 |
+
var newTo = merged.find().from.line;
|
495 |
+
if (newTo > range.from) range.to = newTo;
|
496 |
+
else { intact.splice(i--, 1); break; }
|
497 |
+
}
|
498 |
+
}
|
499 |
+
|
500 |
+
// Clip off the parts that won't be visible
|
501 |
+
var intactLines = 0;
|
502 |
+
for (var i = 0; i < intact.length; ++i) {
|
503 |
+
var range = intact[i];
|
504 |
+
if (range.from < from) range.from = from;
|
505 |
+
if (range.to > to) range.to = to;
|
506 |
+
if (range.from >= range.to) intact.splice(i--, 1);
|
507 |
+
else intactLines += range.to - range.from;
|
508 |
+
}
|
509 |
+
if (!forced && intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
|
510 |
+
updateViewOffset(cm);
|
511 |
+
return;
|
512 |
+
}
|
513 |
+
intact.sort(function(a, b) {return a.from - b.from;});
|
514 |
+
|
515 |
+
// Avoid crashing on IE's "unspecified error" when in iframes
|
516 |
+
try {
|
517 |
+
var focused = document.activeElement;
|
518 |
+
} catch(e) {}
|
519 |
+
if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
|
520 |
+
patchDisplay(cm, from, to, intact, positionsChangedFrom);
|
521 |
+
display.lineDiv.style.display = "";
|
522 |
+
if (focused && document.activeElement != focused && focused.offsetHeight) focused.focus();
|
523 |
+
|
524 |
+
var different = from != display.showingFrom || to != display.showingTo ||
|
525 |
+
display.lastSizeC != display.wrapper.clientHeight;
|
526 |
+
// This is just a bogus formula that detects when the editor is
|
527 |
+
// resized or the font size changes.
|
528 |
+
if (different) {
|
529 |
+
display.lastSizeC = display.wrapper.clientHeight;
|
530 |
+
startWorker(cm, 400);
|
531 |
+
}
|
532 |
+
display.showingFrom = from; display.showingTo = to;
|
533 |
+
|
534 |
+
updateHeightsInViewport(cm);
|
535 |
+
updateViewOffset(cm);
|
536 |
+
|
537 |
+
return true;
|
538 |
+
}
|
539 |
+
|
540 |
+
function updateHeightsInViewport(cm) {
|
541 |
+
var display = cm.display;
|
542 |
+
var prevBottom = display.lineDiv.offsetTop;
|
543 |
+
for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
|
544 |
+
if (ie_lt8) {
|
545 |
+
var bot = node.offsetTop + node.offsetHeight;
|
546 |
+
height = bot - prevBottom;
|
547 |
+
prevBottom = bot;
|
548 |
+
} else {
|
549 |
+
var box = getRect(node);
|
550 |
+
height = box.bottom - box.top;
|
551 |
+
}
|
552 |
+
var diff = node.lineObj.height - height;
|
553 |
+
if (height < 2) height = textHeight(display);
|
554 |
+
if (diff > .001 || diff < -.001) {
|
555 |
+
updateLineHeight(node.lineObj, height);
|
556 |
+
var widgets = node.lineObj.widgets;
|
557 |
+
if (widgets) for (var i = 0; i < widgets.length; ++i)
|
558 |
+
widgets[i].height = widgets[i].node.offsetHeight;
|
559 |
+
}
|
560 |
+
}
|
561 |
+
}
|
562 |
+
|
563 |
+
function updateViewOffset(cm) {
|
564 |
+
var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
|
565 |
+
// Position the mover div to align with the current virtual scroll position
|
566 |
+
cm.display.mover.style.top = off + "px";
|
567 |
+
}
|
568 |
+
|
569 |
+
function computeIntact(intact, changes) {
|
570 |
+
for (var i = 0, l = changes.length || 0; i < l; ++i) {
|
571 |
+
var change = changes[i], intact2 = [], diff = change.diff || 0;
|
572 |
+
for (var j = 0, l2 = intact.length; j < l2; ++j) {
|
573 |
+
var range = intact[j];
|
574 |
+
if (change.to <= range.from && change.diff) {
|
575 |
+
intact2.push({from: range.from + diff, to: range.to + diff});
|
576 |
+
} else if (change.to <= range.from || change.from >= range.to) {
|
577 |
+
intact2.push(range);
|
578 |
+
} else {
|
579 |
+
if (change.from > range.from)
|
580 |
+
intact2.push({from: range.from, to: change.from});
|
581 |
+
if (change.to < range.to)
|
582 |
+
intact2.push({from: change.to + diff, to: range.to + diff});
|
583 |
+
}
|
584 |
+
}
|
585 |
+
intact = intact2;
|
586 |
+
}
|
587 |
+
return intact;
|
588 |
+
}
|
589 |
+
|
590 |
+
function getDimensions(cm) {
|
591 |
+
var d = cm.display, left = {}, width = {};
|
592 |
+
for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
|
593 |
+
left[cm.options.gutters[i]] = n.offsetLeft;
|
594 |
+
width[cm.options.gutters[i]] = n.offsetWidth;
|
595 |
+
}
|
596 |
+
return {fixedPos: compensateForHScroll(d),
|
597 |
+
gutterTotalWidth: d.gutters.offsetWidth,
|
598 |
+
gutterLeft: left,
|
599 |
+
gutterWidth: width,
|
600 |
+
wrapperWidth: d.wrapper.clientWidth};
|
601 |
+
}
|
602 |
+
|
603 |
+
function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
|
604 |
+
var dims = getDimensions(cm);
|
605 |
+
var display = cm.display, lineNumbers = cm.options.lineNumbers;
|
606 |
+
if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
|
607 |
+
removeChildren(display.lineDiv);
|
608 |
+
var container = display.lineDiv, cur = container.firstChild;
|
609 |
+
|
610 |
+
function rm(node) {
|
611 |
+
var next = node.nextSibling;
|
612 |
+
if (webkit && mac && cm.display.currentWheelTarget == node) {
|
613 |
+
node.style.display = "none";
|
614 |
+
node.lineObj = null;
|
615 |
+
} else {
|
616 |
+
node.parentNode.removeChild(node);
|
617 |
+
}
|
618 |
+
return next;
|
619 |
+
}
|
620 |
+
|
621 |
+
var nextIntact = intact.shift(), lineN = from;
|
622 |
+
cm.doc.iter(from, to, function(line) {
|
623 |
+
if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
|
624 |
+
if (lineIsHidden(cm.doc, line)) {
|
625 |
+
if (line.height != 0) updateLineHeight(line, 0);
|
626 |
+
if (line.widgets && cur && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
|
627 |
+
var w = line.widgets[i];
|
628 |
+
if (w.showIfHidden) {
|
629 |
+
var prev = cur.previousSibling;
|
630 |
+
if (/pre/i.test(prev.nodeName)) {
|
631 |
+
var wrap = elt("div", null, null, "position: relative");
|
632 |
+
prev.parentNode.replaceChild(wrap, prev);
|
633 |
+
wrap.appendChild(prev);
|
634 |
+
prev = wrap;
|
635 |
+
}
|
636 |
+
var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
|
637 |
+
if (!w.handleMouseEvents) wnode.ignoreEvents = true;
|
638 |
+
positionLineWidget(w, wnode, prev, dims);
|
639 |
+
}
|
640 |
+
}
|
641 |
+
} else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
|
642 |
+
// This line is intact. Skip to the actual node. Update its
|
643 |
+
// line number if needed.
|
644 |
+
while (cur.lineObj != line) cur = rm(cur);
|
645 |
+
if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
|
646 |
+
setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
|
647 |
+
cur = cur.nextSibling;
|
648 |
+
} else {
|
649 |
+
// For lines with widgets, make an attempt to find and reuse
|
650 |
+
// the existing element, so that widgets aren't needlessly
|
651 |
+
// removed and re-inserted into the dom
|
652 |
+
if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
|
653 |
+
if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
|
654 |
+
// This line needs to be generated.
|
655 |
+
var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
|
656 |
+
if (lineNode != reuse) {
|
657 |
+
container.insertBefore(lineNode, cur);
|
658 |
+
} else {
|
659 |
+
while (cur != reuse) cur = rm(cur);
|
660 |
+
cur = cur.nextSibling;
|
661 |
+
}
|
662 |
+
|
663 |
+
lineNode.lineObj = line;
|
664 |
+
}
|
665 |
+
++lineN;
|
666 |
+
});
|
667 |
+
while (cur) cur = rm(cur);
|
668 |
+
}
|
669 |
+
|
670 |
+
function buildLineElement(cm, line, lineNo, dims, reuse) {
|
671 |
+
var built = buildLineContent(cm, line), lineElement = built.pre;
|
672 |
+
var markers = line.gutterMarkers, display = cm.display, wrap;
|
673 |
+
|
674 |
+
var bgClass = built.bgClass ? built.bgClass + " " + (line.bgClass || "") : line.bgClass;
|
675 |
+
if (!cm.options.lineNumbers && !markers && !bgClass && !line.wrapClass && !line.widgets)
|
676 |
+
return lineElement;
|
677 |
+
|
678 |
+
// Lines with gutter elements, widgets or a background class need
|
679 |
+
// to be wrapped again, and have the extra elements added to the
|
680 |
+
// wrapper div
|
681 |
+
|
682 |
+
if (reuse) {
|
683 |
+
reuse.alignable = null;
|
684 |
+
var isOk = true, widgetsSeen = 0, insertBefore = null;
|
685 |
+
for (var n = reuse.firstChild, next; n; n = next) {
|
686 |
+
next = n.nextSibling;
|
687 |
+
if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
|
688 |
+
reuse.removeChild(n);
|
689 |
+
} else {
|
690 |
+
for (var i = 0; i < line.widgets.length; ++i) {
|
691 |
+
var widget = line.widgets[i];
|
692 |
+
if (widget.node == n.firstChild) {
|
693 |
+
if (!widget.above && !insertBefore) insertBefore = n;
|
694 |
+
positionLineWidget(widget, n, reuse, dims);
|
695 |
+
++widgetsSeen;
|
696 |
+
break;
|
697 |
+
}
|
698 |
+
}
|
699 |
+
if (i == line.widgets.length) { isOk = false; break; }
|
700 |
+
}
|
701 |
+
}
|
702 |
+
reuse.insertBefore(lineElement, insertBefore);
|
703 |
+
if (isOk && widgetsSeen == line.widgets.length) {
|
704 |
+
wrap = reuse;
|
705 |
+
reuse.className = line.wrapClass || "";
|
706 |
+
}
|
707 |
+
}
|
708 |
+
if (!wrap) {
|
709 |
+
wrap = elt("div", null, line.wrapClass, "position: relative");
|
710 |
+
wrap.appendChild(lineElement);
|
711 |
+
}
|
712 |
+
// Kludge to make sure the styled element lies behind the selection (by z-index)
|
713 |
+
if (bgClass)
|
714 |
+
wrap.insertBefore(elt("div", null, bgClass + " CodeMirror-linebackground"), wrap.firstChild);
|
715 |
+
if (cm.options.lineNumbers || markers) {
|
716 |
+
var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
|
717 |
+
(cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
|
718 |
+
wrap.firstChild);
|
719 |
+
if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
|
720 |
+
if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
|
721 |
+
wrap.lineNumber = gutterWrap.appendChild(
|
722 |
+
elt("div", lineNumberFor(cm.options, lineNo),
|
723 |
+
"CodeMirror-linenumber CodeMirror-gutter-elt",
|
724 |
+
"left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
|
725 |
+
+ display.lineNumInnerWidth + "px"));
|
726 |
+
if (markers)
|
727 |
+
for (var k = 0; k < cm.options.gutters.length; ++k) {
|
728 |
+
var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
|
729 |
+
if (found)
|
730 |
+
gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
|
731 |
+
dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
|
732 |
+
}
|
733 |
+
}
|
734 |
+
if (ie_lt8) wrap.style.zIndex = 2;
|
735 |
+
if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
|
736 |
+
var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
|
737 |
+
if (!widget.handleMouseEvents) node.ignoreEvents = true;
|
738 |
+
positionLineWidget(widget, node, wrap, dims);
|
739 |
+
if (widget.above)
|
740 |
+
wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
|
741 |
+
else
|
742 |
+
wrap.appendChild(node);
|
743 |
+
signalLater(widget, "redraw");
|
744 |
+
}
|
745 |
+
return wrap;
|
746 |
+
}
|
747 |
+
|
748 |
+
function positionLineWidget(widget, node, wrap, dims) {
|
749 |
+
if (widget.noHScroll) {
|
750 |
+
(wrap.alignable || (wrap.alignable = [])).push(node);
|
751 |
+
var width = dims.wrapperWidth;
|
752 |
+
node.style.left = dims.fixedPos + "px";
|
753 |
+
if (!widget.coverGutter) {
|
754 |
+
width -= dims.gutterTotalWidth;
|
755 |
+
node.style.paddingLeft = dims.gutterTotalWidth + "px";
|
756 |
+
}
|
757 |
+
node.style.width = width + "px";
|
758 |
+
}
|
759 |
+
if (widget.coverGutter) {
|
760 |
+
node.style.zIndex = 5;
|
761 |
+
node.style.position = "relative";
|
762 |
+
if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
|
763 |
+
}
|
764 |
+
}
|
765 |
+
|
766 |
+
// SELECTION / CURSOR
|
767 |
+
|
768 |
+
function updateSelection(cm) {
|
769 |
+
var display = cm.display;
|
770 |
+
var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
|
771 |
+
if (collapsed || cm.options.showCursorWhenSelecting)
|
772 |
+
updateSelectionCursor(cm);
|
773 |
+
else
|
774 |
+
display.cursor.style.display = display.otherCursor.style.display = "none";
|
775 |
+
if (!collapsed)
|
776 |
+
updateSelectionRange(cm);
|
777 |
+
else
|
778 |
+
display.selectionDiv.style.display = "none";
|
779 |
+
|
780 |
+
// Move the hidden textarea near the cursor to prevent scrolling artifacts
|
781 |
+
if (cm.options.moveInputWithCursor) {
|
782 |
+
var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
|
783 |
+
var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
|
784 |
+
display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
|
785 |
+
headPos.top + lineOff.top - wrapOff.top)) + "px";
|
786 |
+
display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
|
787 |
+
headPos.left + lineOff.left - wrapOff.left)) + "px";
|
788 |
+
}
|
789 |
+
}
|
790 |
+
|
791 |
+
// No selection, plain cursor
|
792 |
+
function updateSelectionCursor(cm) {
|
793 |
+
var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
|
794 |
+
display.cursor.style.left = pos.left + "px";
|
795 |
+
display.cursor.style.top = pos.top + "px";
|
796 |
+
display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
|
797 |
+
display.cursor.style.display = "";
|
798 |
+
|
799 |
+
if (pos.other) {
|
800 |
+
display.otherCursor.style.display = "";
|
801 |
+
display.otherCursor.style.left = pos.other.left + "px";
|
802 |
+
display.otherCursor.style.top = pos.other.top + "px";
|
803 |
+
display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
|
804 |
+
} else { display.otherCursor.style.display = "none"; }
|
805 |
+
}
|
806 |
+
|
807 |
+
// Highlight selection
|
808 |
+
function updateSelectionRange(cm) {
|
809 |
+
var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
|
810 |
+
var fragment = document.createDocumentFragment();
|
811 |
+
var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
|
812 |
+
|
813 |
+
function add(left, top, width, bottom) {
|
814 |
+
if (top < 0) top = 0;
|
815 |
+
fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
|
816 |
+
"px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
|
817 |
+
"px; height: " + (bottom - top) + "px"));
|
818 |
+
}
|
819 |
+
|
820 |
+
function drawForLine(line, fromArg, toArg) {
|
821 |
+
var lineObj = getLine(doc, line);
|
822 |
+
var lineLen = lineObj.text.length;
|
823 |
+
var start, end;
|
824 |
+
function coords(ch, bias) {
|
825 |
+
return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
|
826 |
+
}
|
827 |
+
|
828 |
+
iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
|
829 |
+
var leftPos = coords(from, "left"), rightPos, left, right;
|
830 |
+
if (from == to) {
|
831 |
+
rightPos = leftPos;
|
832 |
+
left = right = leftPos.left;
|
833 |
+
} else {
|
834 |
+
rightPos = coords(to - 1, "right");
|
835 |
+
if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
|
836 |
+
left = leftPos.left;
|
837 |
+
right = rightPos.right;
|
838 |
+
}
|
839 |
+
if (fromArg == null && from == 0) left = pl;
|
840 |
+
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
|
841 |
+
add(left, leftPos.top, null, leftPos.bottom);
|
842 |
+
left = pl;
|
843 |
+
if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
|
844 |
+
}
|
845 |
+
if (toArg == null && to == lineLen) right = clientWidth;
|
846 |
+
if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
|
847 |
+
start = leftPos;
|
848 |
+
if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
|
849 |
+
end = rightPos;
|
850 |
+
if (left < pl + 1) left = pl;
|
851 |
+
add(left, rightPos.top, right - left, rightPos.bottom);
|
852 |
+
});
|
853 |
+
return {start: start, end: end};
|
854 |
+
}
|
855 |
+
|
856 |
+
if (sel.from.line == sel.to.line) {
|
857 |
+
drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
|
858 |
+
} else {
|
859 |
+
var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
|
860 |
+
var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
|
861 |
+
var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
|
862 |
+
var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
|
863 |
+
if (singleVLine) {
|
864 |
+
if (leftEnd.top < rightStart.top - 2) {
|
865 |
+
add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
|
866 |
+
add(pl, rightStart.top, rightStart.left, rightStart.bottom);
|
867 |
+
} else {
|
868 |
+
add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
|
869 |
+
}
|
870 |
+
}
|
871 |
+
if (leftEnd.bottom < rightStart.top)
|
872 |
+
add(pl, leftEnd.bottom, null, rightStart.top);
|
873 |
+
}
|
874 |
+
|
875 |
+
removeChildrenAndAdd(display.selectionDiv, fragment);
|
876 |
+
display.selectionDiv.style.display = "";
|
877 |
+
}
|
878 |
+
|
879 |
+
// Cursor-blinking
|
880 |
+
function restartBlink(cm) {
|
881 |
+
if (!cm.state.focused) return;
|
882 |
+
var display = cm.display;
|
883 |
+
clearInterval(display.blinker);
|
884 |
+
var on = true;
|
885 |
+
display.cursor.style.visibility = display.otherCursor.style.visibility = "";
|
886 |
+
if (cm.options.cursorBlinkRate > 0)
|
887 |
+
display.blinker = setInterval(function() {
|
888 |
+
display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
|
889 |
+
}, cm.options.cursorBlinkRate);
|
890 |
+
}
|
891 |
+
|
892 |
+
// HIGHLIGHT WORKER
|
893 |
+
|
894 |
+
function startWorker(cm, time) {
|
895 |
+
if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
|
896 |
+
cm.state.highlight.set(time, bind(highlightWorker, cm));
|
897 |
+
}
|
898 |
+
|
899 |
+
function highlightWorker(cm) {
|
900 |
+
var doc = cm.doc;
|
901 |
+
if (doc.frontier < doc.first) doc.frontier = doc.first;
|
902 |
+
if (doc.frontier >= cm.display.showingTo) return;
|
903 |
+
var end = +new Date + cm.options.workTime;
|
904 |
+
var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
|
905 |
+
var changed = [], prevChange;
|
906 |
+
doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
|
907 |
+
if (doc.frontier >= cm.display.showingFrom) { // Visible
|
908 |
+
var oldStyles = line.styles;
|
909 |
+
line.styles = highlightLine(cm, line, state);
|
910 |
+
var ischange = !oldStyles || oldStyles.length != line.styles.length;
|
911 |
+
for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
|
912 |
+
if (ischange) {
|
913 |
+
if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
|
914 |
+
else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
|
915 |
+
}
|
916 |
+
line.stateAfter = copyState(doc.mode, state);
|
917 |
+
} else {
|
918 |
+
processLine(cm, line, state);
|
919 |
+
line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
|
920 |
+
}
|
921 |
+
++doc.frontier;
|
922 |
+
if (+new Date > end) {
|
923 |
+
startWorker(cm, cm.options.workDelay);
|
924 |
+
return true;
|
925 |
+
}
|
926 |
+
});
|
927 |
+
if (changed.length)
|
928 |
+
operation(cm, function() {
|
929 |
+
for (var i = 0; i < changed.length; ++i)
|
930 |
+
regChange(this, changed[i].start, changed[i].end);
|
931 |
+
})();
|
932 |
+
}
|
933 |
+
|
934 |
+
// Finds the line to start with when starting a parse. Tries to
|
935 |
+
// find a line with a stateAfter, so that it can start with a
|
936 |
+
// valid state. If that fails, it returns the line with the
|
937 |
+
// smallest indentation, which tends to need the least context to
|
938 |
+
// parse correctly.
|
939 |
+
function findStartLine(cm, n, precise) {
|
940 |
+
var minindent, minline, doc = cm.doc, maxScan = cm.doc.mode.innerMode ? 1000 : 100;
|
941 |
+
for (var search = n, lim = n - maxScan; search > lim; --search) {
|
942 |
+
if (search <= doc.first) return doc.first;
|
943 |
+
var line = getLine(doc, search - 1);
|
944 |
+
if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
|
945 |
+
var indented = countColumn(line.text, null, cm.options.tabSize);
|
946 |
+
if (minline == null || minindent > indented) {
|
947 |
+
minline = search - 1;
|
948 |
+
minindent = indented;
|
949 |
+
}
|
950 |
+
}
|
951 |
+
return minline;
|
952 |
+
}
|
953 |
+
|
954 |
+
function getStateBefore(cm, n, precise) {
|
955 |
+
var doc = cm.doc, display = cm.display;
|
956 |
+
if (!doc.mode.startState) return true;
|
957 |
+
var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
|
958 |
+
if (!state) state = startState(doc.mode);
|
959 |
+
else state = copyState(doc.mode, state);
|
960 |
+
doc.iter(pos, n, function(line) {
|
961 |
+
processLine(cm, line, state);
|
962 |
+
var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
|
963 |
+
line.stateAfter = save ? copyState(doc.mode, state) : null;
|
964 |
+
++pos;
|
965 |
+
});
|
966 |
+
return state;
|
967 |
+
}
|
968 |
+
|
969 |
+
// POSITION MEASUREMENT
|
970 |
+
|
971 |
+
function paddingTop(display) {return display.lineSpace.offsetTop;}
|
972 |
+
function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
|
973 |
+
function paddingLeft(display) {
|
974 |
+
var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
|
975 |
+
return e.offsetLeft;
|
976 |
+
}
|
977 |
+
|
978 |
+
function measureChar(cm, line, ch, data, bias) {
|
979 |
+
var dir = -1;
|
980 |
+
data = data || measureLine(cm, line);
|
981 |
+
if (data.crude) {
|
982 |
+
var left = data.left + ch * data.width;
|
983 |
+
return {left: left, right: left + data.width, top: data.top, bottom: data.bottom};
|
984 |
+
}
|
985 |
+
|
986 |
+
for (var pos = ch;; pos += dir) {
|
987 |
+
var r = data[pos];
|
988 |
+
if (r) break;
|
989 |
+
if (dir < 0 && pos == 0) dir = 1;
|
990 |
+
}
|
991 |
+
bias = pos > ch ? "left" : pos < ch ? "right" : bias;
|
992 |
+
if (bias == "left" && r.leftSide) r = r.leftSide;
|
993 |
+
else if (bias == "right" && r.rightSide) r = r.rightSide;
|
994 |
+
return {left: pos < ch ? r.right : r.left,
|
995 |
+
right: pos > ch ? r.left : r.right,
|
996 |
+
top: r.top,
|
997 |
+
bottom: r.bottom};
|
998 |
+
}
|
999 |
+
|
1000 |
+
function findCachedMeasurement(cm, line) {
|
1001 |
+
var cache = cm.display.measureLineCache;
|
1002 |
+
for (var i = 0; i < cache.length; ++i) {
|
1003 |
+
var memo = cache[i];
|
1004 |
+
if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
|
1005 |
+
cm.display.scroller.clientWidth == memo.width &&
|
1006 |
+
memo.classes == line.textClass + "|" + line.wrapClass)
|
1007 |
+
return memo;
|
1008 |
+
}
|
1009 |
+
}
|
1010 |
+
|
1011 |
+
function clearCachedMeasurement(cm, line) {
|
1012 |
+
var exists = findCachedMeasurement(cm, line);
|
1013 |
+
if (exists) exists.text = exists.measure = exists.markedSpans = null;
|
1014 |
+
}
|
1015 |
+
|
1016 |
+
function measureLine(cm, line) {
|
1017 |
+
// First look in the cache
|
1018 |
+
var cached = findCachedMeasurement(cm, line);
|
1019 |
+
if (cached) return cached.measure;
|
1020 |
+
|
1021 |
+
// Failing that, recompute and store result in cache
|
1022 |
+
var measure = measureLineInner(cm, line);
|
1023 |
+
var cache = cm.display.measureLineCache;
|
1024 |
+
var memo = {text: line.text, width: cm.display.scroller.clientWidth,
|
1025 |
+
markedSpans: line.markedSpans, measure: measure,
|
1026 |
+
classes: line.textClass + "|" + line.wrapClass};
|
1027 |
+
if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
|
1028 |
+
else cache.push(memo);
|
1029 |
+
return measure;
|
1030 |
+
}
|
1031 |
+
|
1032 |
+
function measureLineInner(cm, line) {
|
1033 |
+
if (!cm.options.lineWrapping && line.text.length >= cm.options.crudeMeasuringFrom)
|
1034 |
+
return crudelyMeasureLine(cm, line);
|
1035 |
+
|
1036 |
+
var display = cm.display, measure = emptyArray(line.text.length);
|
1037 |
+
var pre = buildLineContent(cm, line, measure, true).pre;
|
1038 |
+
|
1039 |
+
// IE does not cache element positions of inline elements between
|
1040 |
+
// calls to getBoundingClientRect. This makes the loop below,
|
1041 |
+
// which gathers the positions of all the characters on the line,
|
1042 |
+
// do an amount of layout work quadratic to the number of
|
1043 |
+
// characters. When line wrapping is off, we try to improve things
|
1044 |
+
// by first subdividing the line into a bunch of inline blocks, so
|
1045 |
+
// that IE can reuse most of the layout information from caches
|
1046 |
+
// for those blocks. This does interfere with line wrapping, so it
|
1047 |
+
// doesn't work when wrapping is on, but in that case the
|
1048 |
+
// situation is slightly better, since IE does cache line-wrapping
|
1049 |
+
// information and only recomputes per-line.
|
1050 |
+
if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
|
1051 |
+
var fragment = document.createDocumentFragment();
|
1052 |
+
var chunk = 10, n = pre.childNodes.length;
|
1053 |
+
for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
|
1054 |
+
var wrap = elt("div", null, null, "display: inline-block");
|
1055 |
+
for (var j = 0; j < chunk && n; ++j) {
|
1056 |
+
wrap.appendChild(pre.firstChild);
|
1057 |
+
--n;
|
1058 |
+
}
|
1059 |
+
fragment.appendChild(wrap);
|
1060 |
+
}
|
1061 |
+
pre.appendChild(fragment);
|
1062 |
+
}
|
1063 |
+
|
1064 |
+
removeChildrenAndAdd(display.measure, pre);
|
1065 |
+
|
1066 |
+
var outer = getRect(display.lineDiv);
|
1067 |
+
var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
|
1068 |
+
// Work around an IE7/8 bug where it will sometimes have randomly
|
1069 |
+
// replaced our pre with a clone at this point.
|
1070 |
+
if (ie_lt9 && display.measure.first != pre)
|
1071 |
+
removeChildrenAndAdd(display.measure, pre);
|
1072 |
+
|
1073 |
+
function measureRect(rect) {
|
1074 |
+
var top = rect.top - outer.top, bot = rect.bottom - outer.top;
|
1075 |
+
if (bot > maxBot) bot = maxBot;
|
1076 |
+
if (top < 0) top = 0;
|
1077 |
+
for (var i = vranges.length - 2; i >= 0; i -= 2) {
|
1078 |
+
var rtop = vranges[i], rbot = vranges[i+1];
|
1079 |
+
if (rtop > bot || rbot < top) continue;
|
1080 |
+
if (rtop <= top && rbot >= bot ||
|
1081 |
+
top <= rtop && bot >= rbot ||
|
1082 |
+
Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
|
1083 |
+
vranges[i] = Math.min(top, rtop);
|
1084 |
+
vranges[i+1] = Math.max(bot, rbot);
|
1085 |
+
break;
|
1086 |
+
}
|
1087 |
+
}
|
1088 |
+
if (i < 0) { i = vranges.length; vranges.push(top, bot); }
|
1089 |
+
return {left: rect.left - outer.left,
|
1090 |
+
right: rect.right - outer.left,
|
1091 |
+
top: i, bottom: null};
|
1092 |
+
}
|
1093 |
+
function finishRect(rect) {
|
1094 |
+
rect.bottom = vranges[rect.top+1];
|
1095 |
+
rect.top = vranges[rect.top];
|
1096 |
+
}
|
1097 |
+
|
1098 |
+
for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
|
1099 |
+
var node = cur, rect = null;
|
1100 |
+
// A widget might wrap, needs special care
|
1101 |
+
if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
|
1102 |
+
if (cur.firstChild.nodeType == 1) node = cur.firstChild;
|
1103 |
+
var rects = node.getClientRects();
|
1104 |
+
if (rects.length > 1) {
|
1105 |
+
rect = data[i] = measureRect(rects[0]);
|
1106 |
+
rect.rightSide = measureRect(rects[rects.length - 1]);
|
1107 |
+
}
|
1108 |
+
}
|
1109 |
+
if (!rect) rect = data[i] = measureRect(getRect(node));
|
1110 |
+
if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
|
1111 |
+
if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
|
1112 |
+
}
|
1113 |
+
removeChildren(cm.display.measure);
|
1114 |
+
for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
|
1115 |
+
finishRect(cur);
|
1116 |
+
if (cur.leftSide) finishRect(cur.leftSide);
|
1117 |
+
if (cur.rightSide) finishRect(cur.rightSide);
|
1118 |
+
}
|
1119 |
+
return data;
|
1120 |
+
}
|
1121 |
+
|
1122 |
+
function crudelyMeasureLine(cm, line) {
|
1123 |
+
var copy = new Line(line.text.slice(0, 100), null);
|
1124 |
+
if (line.textClass) copy.textClass = line.textClass;
|
1125 |
+
var measure = measureLineInner(cm, copy);
|
1126 |
+
var left = measureChar(cm, copy, 0, measure, "left");
|
1127 |
+
var right = measureChar(cm, copy, 99, measure, "right");
|
1128 |
+
return {crude: true, top: left.top, left: left.left, bottom: left.bottom, width: (right.right - left.left) / 100};
|
1129 |
+
}
|
1130 |
+
|
1131 |
+
function measureLineWidth(cm, line) {
|
1132 |
+
var hasBadSpan = false;
|
1133 |
+
if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
|
1134 |
+
var sp = line.markedSpans[i];
|
1135 |
+
if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
|
1136 |
+
}
|
1137 |
+
var cached = !hasBadSpan && findCachedMeasurement(cm, line);
|
1138 |
+
if (cached || line.text.length >= cm.options.crudeMeasuringFrom)
|
1139 |
+
return measureChar(cm, line, line.text.length, cached && cached.measure, "right").right;
|
1140 |
+
|
1141 |
+
var pre = buildLineContent(cm, line, null, true).pre;
|
1142 |
+
var end = pre.appendChild(zeroWidthElement(cm.display.measure));
|
1143 |
+
removeChildrenAndAdd(cm.display.measure, pre);
|
1144 |
+
return getRect(end).right - getRect(cm.display.lineDiv).left;
|
1145 |
+
}
|
1146 |
+
|
1147 |
+
function clearCaches(cm) {
|
1148 |
+
cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
|
1149 |
+
cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
|
1150 |
+
if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
|
1151 |
+
cm.display.lineNumChars = null;
|
1152 |
+
}
|
1153 |
+
|
1154 |
+
function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
|
1155 |
+
function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
|
1156 |
+
|
1157 |
+
// Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
|
1158 |
+
function intoCoordSystem(cm, lineObj, rect, context) {
|
1159 |
+
if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
|
1160 |
+
var size = widgetHeight(lineObj.widgets[i]);
|
1161 |
+
rect.top += size; rect.bottom += size;
|
1162 |
+
}
|
1163 |
+
if (context == "line") return rect;
|
1164 |
+
if (!context) context = "local";
|
1165 |
+
var yOff = heightAtLine(cm, lineObj);
|
1166 |
+
if (context == "local") yOff += paddingTop(cm.display);
|
1167 |
+
else yOff -= cm.display.viewOffset;
|
1168 |
+
if (context == "page" || context == "window") {
|
1169 |
+
var lOff = getRect(cm.display.lineSpace);
|
1170 |
+
yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
|
1171 |
+
var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
|
1172 |
+
rect.left += xOff; rect.right += xOff;
|
1173 |
+
}
|
1174 |
+
rect.top += yOff; rect.bottom += yOff;
|
1175 |
+
return rect;
|
1176 |
+
}
|
1177 |
+
|
1178 |
+
// Context may be "window", "page", "div", or "local"/null
|
1179 |
+
// Result is in "div" coords
|
1180 |
+
function fromCoordSystem(cm, coords, context) {
|
1181 |
+
if (context == "div") return coords;
|
1182 |
+
var left = coords.left, top = coords.top;
|
1183 |
+
// First move into "page" coordinate system
|
1184 |
+
if (context == "page") {
|
1185 |
+
left -= pageScrollX();
|
1186 |
+
top -= pageScrollY();
|
1187 |
+
} else if (context == "local" || !context) {
|
1188 |
+
var localBox = getRect(cm.display.sizer);
|
1189 |
+
left += localBox.left;
|
1190 |
+
top += localBox.top;
|
1191 |
+
}
|
1192 |
+
|
1193 |
+
var lineSpaceBox = getRect(cm.display.lineSpace);
|
1194 |
+
return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
|
1195 |
+
}
|
1196 |
+
|
1197 |
+
function charCoords(cm, pos, context, lineObj, bias) {
|
1198 |
+
if (!lineObj) lineObj = getLine(cm.doc, pos.line);
|
1199 |
+
return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
|
1200 |
+
}
|
1201 |
+
|
1202 |
+
function cursorCoords(cm, pos, context, lineObj, measurement) {
|
1203 |
+
lineObj = lineObj || getLine(cm.doc, pos.line);
|
1204 |
+
if (!measurement) measurement = measureLine(cm, lineObj);
|
1205 |
+
function get(ch, right) {
|
1206 |
+
var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
|
1207 |
+
if (right) m.left = m.right; else m.right = m.left;
|
1208 |
+
return intoCoordSystem(cm, lineObj, m, context);
|
1209 |
+
}
|
1210 |
+
function getBidi(ch, partPos) {
|
1211 |
+
var part = order[partPos], right = part.level % 2;
|
1212 |
+
if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
|
1213 |
+
part = order[--partPos];
|
1214 |
+
ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
|
1215 |
+
right = true;
|
1216 |
+
} else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
|
1217 |
+
part = order[++partPos];
|
1218 |
+
ch = bidiLeft(part) - part.level % 2;
|
1219 |
+
right = false;
|
1220 |
+
}
|
1221 |
+
if (right && ch == part.to && ch > part.from) return get(ch - 1);
|
1222 |
+
return get(ch, right);
|
1223 |
+
}
|
1224 |
+
var order = getOrder(lineObj), ch = pos.ch;
|
1225 |
+
if (!order) return get(ch);
|
1226 |
+
var partPos = getBidiPartAt(order, ch);
|
1227 |
+
var val = getBidi(ch, partPos);
|
1228 |
+
if (bidiOther != null) val.other = getBidi(ch, bidiOther);
|
1229 |
+
return val;
|
1230 |
+
}
|
1231 |
+
|
1232 |
+
function PosWithInfo(line, ch, outside, xRel) {
|
1233 |
+
var pos = new Pos(line, ch);
|
1234 |
+
pos.xRel = xRel;
|
1235 |
+
if (outside) pos.outside = true;
|
1236 |
+
return pos;
|
1237 |
+
}
|
1238 |
+
|
1239 |
+
// Coords must be lineSpace-local
|
1240 |
+
function coordsChar(cm, x, y) {
|
1241 |
+
var doc = cm.doc;
|
1242 |
+
y += cm.display.viewOffset;
|
1243 |
+
if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
|
1244 |
+
var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
|
1245 |
+
if (lineNo > last)
|
1246 |
+
return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
|
1247 |
+
if (x < 0) x = 0;
|
1248 |
+
|
1249 |
+
for (;;) {
|
1250 |
+
var lineObj = getLine(doc, lineNo);
|
1251 |
+
var found = coordsCharInner(cm, lineObj, lineNo, x, y);
|
1252 |
+
var merged = collapsedSpanAtEnd(lineObj);
|
1253 |
+
var mergedPos = merged && merged.find();
|
1254 |
+
if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
|
1255 |
+
lineNo = mergedPos.to.line;
|
1256 |
+
else
|
1257 |
+
return found;
|
1258 |
+
}
|
1259 |
+
}
|
1260 |
+
|
1261 |
+
function coordsCharInner(cm, lineObj, lineNo, x, y) {
|
1262 |
+
var innerOff = y - heightAtLine(cm, lineObj);
|
1263 |
+
var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
|
1264 |
+
var measurement = measureLine(cm, lineObj);
|
1265 |
+
|
1266 |
+
function getX(ch) {
|
1267 |
+
var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
|
1268 |
+
lineObj, measurement);
|
1269 |
+
wrongLine = true;
|
1270 |
+
if (innerOff > sp.bottom) return sp.left - adjust;
|
1271 |
+
else if (innerOff < sp.top) return sp.left + adjust;
|
1272 |
+
else wrongLine = false;
|
1273 |
+
return sp.left;
|
1274 |
+
}
|
1275 |
+
|
1276 |
+
var bidi = getOrder(lineObj), dist = lineObj.text.length;
|
1277 |
+
var from = lineLeft(lineObj), to = lineRight(lineObj);
|
1278 |
+
var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
|
1279 |
+
|
1280 |
+
if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
|
1281 |
+
// Do a binary search between these bounds.
|
1282 |
+
for (;;) {
|
1283 |
+
if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
|
1284 |
+
var ch = x < fromX || x - fromX <= toX - x ? from : to;
|
1285 |
+
var xDiff = x - (ch == from ? fromX : toX);
|
1286 |
+
while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
|
1287 |
+
var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
|
1288 |
+
xDiff < 0 ? -1 : xDiff ? 1 : 0);
|
1289 |
+
return pos;
|
1290 |
+
}
|
1291 |
+
var step = Math.ceil(dist / 2), middle = from + step;
|
1292 |
+
if (bidi) {
|
1293 |
+
middle = from;
|
1294 |
+
for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
|
1295 |
+
}
|
1296 |
+
var middleX = getX(middle);
|
1297 |
+
if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
|
1298 |
+
else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
|
1299 |
+
}
|
1300 |
+
}
|
1301 |
+
|
1302 |
+
var measureText;
|
1303 |
+
function textHeight(display) {
|
1304 |
+
if (display.cachedTextHeight != null) return display.cachedTextHeight;
|
1305 |
+
if (measureText == null) {
|
1306 |
+
measureText = elt("pre");
|
1307 |
+
// Measure a bunch of lines, for browsers that compute
|
1308 |
+
// fractional heights.
|
1309 |
+
for (var i = 0; i < 49; ++i) {
|
1310 |
+
measureText.appendChild(document.createTextNode("x"));
|
1311 |
+
measureText.appendChild(elt("br"));
|
1312 |
+
}
|
1313 |
+
measureText.appendChild(document.createTextNode("x"));
|
1314 |
+
}
|
1315 |
+
removeChildrenAndAdd(display.measure, measureText);
|
1316 |
+
var height = measureText.offsetHeight / 50;
|
1317 |
+
if (height > 3) display.cachedTextHeight = height;
|
1318 |
+
removeChildren(display.measure);
|
1319 |
+
return height || 1;
|
1320 |
+
}
|
1321 |
+
|
1322 |
+
function charWidth(display) {
|
1323 |
+
if (display.cachedCharWidth != null) return display.cachedCharWidth;
|
1324 |
+
var anchor = elt("span", "x");
|
1325 |
+
var pre = elt("pre", [anchor]);
|
1326 |
+
removeChildrenAndAdd(display.measure, pre);
|
1327 |
+
var width = anchor.offsetWidth;
|
1328 |
+
if (width > 2) display.cachedCharWidth = width;
|
1329 |
+
return width || 10;
|
1330 |
+
}
|
1331 |
+
|
1332 |
+
// OPERATIONS
|
1333 |
+
|
1334 |
+
// Operations are used to wrap changes in such a way that each
|
1335 |
+
// change won't have to update the cursor and display (which would
|
1336 |
+
// be awkward, slow, and error-prone), but instead updates are
|
1337 |
+
// batched and then all combined and executed at once.
|
1338 |
+
|
1339 |
+
var nextOpId = 0;
|
1340 |
+
function startOperation(cm) {
|
1341 |
+
cm.curOp = {
|
1342 |
+
// An array of ranges of lines that have to be updated. See
|
1343 |
+
// updateDisplay.
|
1344 |
+
changes: [],
|
1345 |
+
forceUpdate: false,
|
1346 |
+
updateInput: null,
|
1347 |
+
userSelChange: null,
|
1348 |
+
textChanged: null,
|
1349 |
+
selectionChanged: false,
|
1350 |
+
cursorActivity: false,
|
1351 |
+
updateMaxLine: false,
|
1352 |
+
updateScrollPos: false,
|
1353 |
+
id: ++nextOpId
|
1354 |
+
};
|
1355 |
+
if (!delayedCallbackDepth++) delayedCallbacks = [];
|
1356 |
+
}
|
1357 |
+
|
1358 |
+
function endOperation(cm) {
|
1359 |
+
var op = cm.curOp, doc = cm.doc, display = cm.display;
|
1360 |
+
cm.curOp = null;
|
1361 |
+
|
1362 |
+
if (op.updateMaxLine) computeMaxLength(cm);
|
1363 |
+
if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) {
|
1364 |
+
var width = measureLineWidth(cm, display.maxLine);
|
1365 |
+
display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
|
1366 |
+
display.maxLineChanged = false;
|
1367 |
+
var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
|
1368 |
+
if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
|
1369 |
+
setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
|
1370 |
+
}
|
1371 |
+
var newScrollPos, updated;
|
1372 |
+
if (op.updateScrollPos) {
|
1373 |
+
newScrollPos = op.updateScrollPos;
|
1374 |
+
} else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
|
1375 |
+
var coords = cursorCoords(cm, doc.sel.head);
|
1376 |
+
newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
|
1377 |
+
}
|
1378 |
+
if (op.changes.length || op.forceUpdate || newScrollPos && newScrollPos.scrollTop != null) {
|
1379 |
+
updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop, op.forceUpdate);
|
1380 |
+
if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
|
1381 |
+
}
|
1382 |
+
if (!updated && op.selectionChanged) updateSelection(cm);
|
1383 |
+
if (op.updateScrollPos) {
|
1384 |
+
display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
|
1385 |
+
display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
|
1386 |
+
alignHorizontally(cm);
|
1387 |
+
if (op.scrollToPos)
|
1388 |
+
scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
|
1389 |
+
} else if (newScrollPos) {
|
1390 |
+
scrollCursorIntoView(cm);
|
1391 |
+
}
|
1392 |
+
if (op.selectionChanged) restartBlink(cm);
|
1393 |
+
|
1394 |
+
if (cm.state.focused && op.updateInput)
|
1395 |
+
resetInput(cm, op.userSelChange);
|
1396 |
+
|
1397 |
+
var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
|
1398 |
+
if (hidden) for (var i = 0; i < hidden.length; ++i)
|
1399 |
+
if (!hidden[i].lines.length) signal(hidden[i], "hide");
|
1400 |
+
if (unhidden) for (var i = 0; i < unhidden.length; ++i)
|
1401 |
+
if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
|
1402 |
+
|
1403 |
+
var delayed;
|
1404 |
+
if (!--delayedCallbackDepth) {
|
1405 |
+
delayed = delayedCallbacks;
|
1406 |
+
delayedCallbacks = null;
|
1407 |
+
}
|
1408 |
+
if (op.textChanged)
|
1409 |
+
signal(cm, "change", cm, op.textChanged);
|
1410 |
+
if (op.cursorActivity) signal(cm, "cursorActivity", cm);
|
1411 |
+
if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
|
1412 |
+
}
|
1413 |
+
|
1414 |
+
// Wraps a function in an operation. Returns the wrapped function.
|
1415 |
+
function operation(cm1, f) {
|
1416 |
+
return function() {
|
1417 |
+
var cm = cm1 || this, withOp = !cm.curOp;
|
1418 |
+
if (withOp) startOperation(cm);
|
1419 |
+
try { var result = f.apply(cm, arguments); }
|
1420 |
+
finally { if (withOp) endOperation(cm); }
|
1421 |
+
return result;
|
1422 |
+
};
|
1423 |
+
}
|
1424 |
+
function docOperation(f) {
|
1425 |
+
return function() {
|
1426 |
+
var withOp = this.cm && !this.cm.curOp, result;
|
1427 |
+
if (withOp) startOperation(this.cm);
|
1428 |
+
try { result = f.apply(this, arguments); }
|
1429 |
+
finally { if (withOp) endOperation(this.cm); }
|
1430 |
+
return result;
|
1431 |
+
};
|
1432 |
+
}
|
1433 |
+
function runInOp(cm, f) {
|
1434 |
+
var withOp = !cm.curOp, result;
|
1435 |
+
if (withOp) startOperation(cm);
|
1436 |
+
try { result = f(); }
|
1437 |
+
finally { if (withOp) endOperation(cm); }
|
1438 |
+
return result;
|
1439 |
+
}
|
1440 |
+
|
1441 |
+
function regChange(cm, from, to, lendiff) {
|
1442 |
+
if (from == null) from = cm.doc.first;
|
1443 |
+
if (to == null) to = cm.doc.first + cm.doc.size;
|
1444 |
+
cm.curOp.changes.push({from: from, to: to, diff: lendiff});
|
1445 |
+
}
|
1446 |
+
|
1447 |
+
// INPUT HANDLING
|
1448 |
+
|
1449 |
+
function slowPoll(cm) {
|
1450 |
+
if (cm.display.pollingFast) return;
|
1451 |
+
cm.display.poll.set(cm.options.pollInterval, function() {
|
1452 |
+
readInput(cm);
|
1453 |
+
if (cm.state.focused) slowPoll(cm);
|
1454 |
+
});
|
1455 |
+
}
|
1456 |
+
|
1457 |
+
function fastPoll(cm) {
|
1458 |
+
var missed = false;
|
1459 |
+
cm.display.pollingFast = true;
|
1460 |
+
function p() {
|
1461 |
+
var changed = readInput(cm);
|
1462 |
+
if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
|
1463 |
+
else {cm.display.pollingFast = false; slowPoll(cm);}
|
1464 |
+
}
|
1465 |
+
cm.display.poll.set(20, p);
|
1466 |
+
}
|
1467 |
+
|
1468 |
+
// prevInput is a hack to work with IME. If we reset the textarea
|
1469 |
+
// on every change, that breaks IME. So we look for changes
|
1470 |
+
// compared to the previous content instead. (Modern browsers have
|
1471 |
+
// events that indicate IME taking place, but these are not widely
|
1472 |
+
// supported or compatible enough yet to rely on.)
|
1473 |
+
function readInput(cm) {
|
1474 |
+
var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
|
1475 |
+
if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
|
1476 |
+
if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
|
1477 |
+
input.value = input.value.substring(0, input.value.length - 1);
|
1478 |
+
cm.state.fakedLastChar = false;
|
1479 |
+
}
|
1480 |
+
var text = input.value;
|
1481 |
+
if (text == prevInput && posEq(sel.from, sel.to)) return false;
|
1482 |
+
if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
|
1483 |
+
resetInput(cm, true);
|
1484 |
+
return false;
|
1485 |
+
}
|
1486 |
+
|
1487 |
+
var withOp = !cm.curOp;
|
1488 |
+
if (withOp) startOperation(cm);
|
1489 |
+
sel.shift = false;
|
1490 |
+
var same = 0, l = Math.min(prevInput.length, text.length);
|
1491 |
+
while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
|
1492 |
+
var from = sel.from, to = sel.to;
|
1493 |
+
if (same < prevInput.length)
|
1494 |
+
from = Pos(from.line, from.ch - (prevInput.length - same));
|
1495 |
+
else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
|
1496 |
+
to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
|
1497 |
+
|
1498 |
+
var updateInput = cm.curOp.updateInput;
|
1499 |
+
var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
|
1500 |
+
origin: cm.state.pasteIncoming ? "paste" : "+input"};
|
1501 |
+
makeChange(cm.doc, changeEvent, "end");
|
1502 |
+
cm.curOp.updateInput = updateInput;
|
1503 |
+
signalLater(cm, "inputRead", cm, changeEvent);
|
1504 |
+
|
1505 |
+
if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
|
1506 |
+
else cm.display.prevInput = text;
|
1507 |
+
if (withOp) endOperation(cm);
|
1508 |
+
cm.state.pasteIncoming = false;
|
1509 |
+
return true;
|
1510 |
+
}
|
1511 |
+
|
1512 |
+
function resetInput(cm, user) {
|
1513 |
+
var minimal, selected, doc = cm.doc;
|
1514 |
+
if (!posEq(doc.sel.from, doc.sel.to)) {
|
1515 |
+
cm.display.prevInput = "";
|
1516 |
+
minimal = hasCopyEvent &&
|
1517 |
+
(doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
|
1518 |
+
var content = minimal ? "-" : selected || cm.getSelection();
|
1519 |
+
cm.display.input.value = content;
|
1520 |
+
if (cm.state.focused) selectInput(cm.display.input);
|
1521 |
+
if (ie && !ie_lt9) cm.display.inputHasSelection = content;
|
1522 |
+
} else if (user) {
|
1523 |
+
cm.display.prevInput = cm.display.input.value = "";
|
1524 |
+
if (ie && !ie_lt9) cm.display.inputHasSelection = null;
|
1525 |
+
}
|
1526 |
+
cm.display.inaccurateSelection = minimal;
|
1527 |
+
}
|
1528 |
+
|
1529 |
+
function focusInput(cm) {
|
1530 |
+
if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
|
1531 |
+
cm.display.input.focus();
|
1532 |
+
}
|
1533 |
+
|
1534 |
+
function isReadOnly(cm) {
|
1535 |
+
return cm.options.readOnly || cm.doc.cantEdit;
|
1536 |
+
}
|
1537 |
+
|
1538 |
+
// EVENT HANDLERS
|
1539 |
+
|
1540 |
+
function registerEventHandlers(cm) {
|
1541 |
+
var d = cm.display;
|
1542 |
+
on(d.scroller, "mousedown", operation(cm, onMouseDown));
|
1543 |
+
if (ie)
|
1544 |
+
on(d.scroller, "dblclick", operation(cm, function(e) {
|
1545 |
+
if (signalDOMEvent(cm, e)) return;
|
1546 |
+
var pos = posFromMouse(cm, e);
|
1547 |
+
if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
|
1548 |
+
e_preventDefault(e);
|
1549 |
+
var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
|
1550 |
+
extendSelection(cm.doc, word.from, word.to);
|
1551 |
+
}));
|
1552 |
+
else
|
1553 |
+
on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
|
1554 |
+
on(d.lineSpace, "selectstart", function(e) {
|
1555 |
+
if (!eventInWidget(d, e)) e_preventDefault(e);
|
1556 |
+
});
|
1557 |
+
// Gecko browsers fire contextmenu *after* opening the menu, at
|
1558 |
+
// which point we can't mess with it anymore. Context menu is
|
1559 |
+
// handled in onMouseDown for Gecko.
|
1560 |
+
if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
|
1561 |
+
|
1562 |
+
on(d.scroller, "scroll", function() {
|
1563 |
+
if (d.scroller.clientHeight) {
|
1564 |
+
setScrollTop(cm, d.scroller.scrollTop);
|
1565 |
+
setScrollLeft(cm, d.scroller.scrollLeft, true);
|
1566 |
+
signal(cm, "scroll", cm);
|
1567 |
+
}
|
1568 |
+
});
|
1569 |
+
on(d.scrollbarV, "scroll", function() {
|
1570 |
+
if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
|
1571 |
+
});
|
1572 |
+
on(d.scrollbarH, "scroll", function() {
|
1573 |
+
if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
|
1574 |
+
});
|
1575 |
+
|
1576 |
+
on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
|
1577 |
+
on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
|
1578 |
+
|
1579 |
+
function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
|
1580 |
+
on(d.scrollbarH, "mousedown", reFocus);
|
1581 |
+
on(d.scrollbarV, "mousedown", reFocus);
|
1582 |
+
// Prevent wrapper from ever scrolling
|
1583 |
+
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
|
1584 |
+
|
1585 |
+
var resizeTimer;
|
1586 |
+
function onResize() {
|
1587 |
+
if (resizeTimer == null) resizeTimer = setTimeout(function() {
|
1588 |
+
resizeTimer = null;
|
1589 |
+
// Might be a text scaling operation, clear size caches.
|
1590 |
+
d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
|
1591 |
+
clearCaches(cm);
|
1592 |
+
runInOp(cm, bind(regChange, cm));
|
1593 |
+
}, 100);
|
1594 |
+
}
|
1595 |
+
on(window, "resize", onResize);
|
1596 |
+
// Above handler holds on to the editor and its data structures.
|
1597 |
+
// Here we poll to unregister it when the editor is no longer in
|
1598 |
+
// the document, so that it can be garbage-collected.
|
1599 |
+
function unregister() {
|
1600 |
+
for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
|
1601 |
+
if (p) setTimeout(unregister, 5000);
|
1602 |
+
else off(window, "resize", onResize);
|
1603 |
+
}
|
1604 |
+
setTimeout(unregister, 5000);
|
1605 |
+
|
1606 |
+
on(d.input, "keyup", operation(cm, function(e) {
|
1607 |
+
if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1608 |
+
if (e.keyCode == 16) cm.doc.sel.shift = false;
|
1609 |
+
}));
|
1610 |
+
on(d.input, "input", function() {
|
1611 |
+
if (ie && !ie_lt9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null;
|
1612 |
+
fastPoll(cm);
|
1613 |
+
});
|
1614 |
+
on(d.input, "keydown", operation(cm, onKeyDown));
|
1615 |
+
on(d.input, "keypress", operation(cm, onKeyPress));
|
1616 |
+
on(d.input, "focus", bind(onFocus, cm));
|
1617 |
+
on(d.input, "blur", bind(onBlur, cm));
|
1618 |
+
|
1619 |
+
function drag_(e) {
|
1620 |
+
if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
|
1621 |
+
e_stop(e);
|
1622 |
+
}
|
1623 |
+
if (cm.options.dragDrop) {
|
1624 |
+
on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
|
1625 |
+
on(d.scroller, "dragenter", drag_);
|
1626 |
+
on(d.scroller, "dragover", drag_);
|
1627 |
+
on(d.scroller, "drop", operation(cm, onDrop));
|
1628 |
+
}
|
1629 |
+
on(d.scroller, "paste", function(e) {
|
1630 |
+
if (eventInWidget(d, e)) return;
|
1631 |
+
focusInput(cm);
|
1632 |
+
fastPoll(cm);
|
1633 |
+
});
|
1634 |
+
on(d.input, "paste", function() {
|
1635 |
+
// Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
|
1636 |
+
// Add a char to the end of textarea before paste occur so that
|
1637 |
+
// selection doesn't span to the end of textarea.
|
1638 |
+
if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
|
1639 |
+
var start = d.input.selectionStart, end = d.input.selectionEnd;
|
1640 |
+
d.input.value += "$";
|
1641 |
+
d.input.selectionStart = start;
|
1642 |
+
d.input.selectionEnd = end;
|
1643 |
+
cm.state.fakedLastChar = true;
|
1644 |
+
}
|
1645 |
+
cm.state.pasteIncoming = true;
|
1646 |
+
fastPoll(cm);
|
1647 |
+
});
|
1648 |
+
|
1649 |
+
function prepareCopy() {
|
1650 |
+
if (d.inaccurateSelection) {
|
1651 |
+
d.prevInput = "";
|
1652 |
+
d.inaccurateSelection = false;
|
1653 |
+
d.input.value = cm.getSelection();
|
1654 |
+
selectInput(d.input);
|
1655 |
+
}
|
1656 |
+
}
|
1657 |
+
on(d.input, "cut", prepareCopy);
|
1658 |
+
on(d.input, "copy", prepareCopy);
|
1659 |
+
|
1660 |
+
// Needed to handle Tab key in KHTML
|
1661 |
+
if (khtml) on(d.sizer, "mouseup", function() {
|
1662 |
+
if (document.activeElement == d.input) d.input.blur();
|
1663 |
+
focusInput(cm);
|
1664 |
+
});
|
1665 |
+
}
|
1666 |
+
|
1667 |
+
function eventInWidget(display, e) {
|
1668 |
+
for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
|
1669 |
+
if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
|
1670 |
+
}
|
1671 |
+
}
|
1672 |
+
|
1673 |
+
function posFromMouse(cm, e, liberal) {
|
1674 |
+
var display = cm.display;
|
1675 |
+
if (!liberal) {
|
1676 |
+
var target = e_target(e);
|
1677 |
+
if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
|
1678 |
+
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
|
1679 |
+
target == display.scrollbarFiller || target == display.gutterFiller) return null;
|
1680 |
+
}
|
1681 |
+
var x, y, space = getRect(display.lineSpace);
|
1682 |
+
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
|
1683 |
+
try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
|
1684 |
+
return coordsChar(cm, x - space.left, y - space.top);
|
1685 |
+
}
|
1686 |
+
|
1687 |
+
var lastClick, lastDoubleClick;
|
1688 |
+
function onMouseDown(e) {
|
1689 |
+
if (signalDOMEvent(this, e)) return;
|
1690 |
+
var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
|
1691 |
+
sel.shift = e.shiftKey;
|
1692 |
+
|
1693 |
+
if (eventInWidget(display, e)) {
|
1694 |
+
if (!webkit) {
|
1695 |
+
display.scroller.draggable = false;
|
1696 |
+
setTimeout(function(){display.scroller.draggable = true;}, 100);
|
1697 |
+
}
|
1698 |
+
return;
|
1699 |
+
}
|
1700 |
+
if (clickInGutter(cm, e)) return;
|
1701 |
+
var start = posFromMouse(cm, e);
|
1702 |
+
|
1703 |
+
switch (e_button(e)) {
|
1704 |
+
case 3:
|
1705 |
+
if (captureMiddleClick) onContextMenu.call(cm, cm, e);
|
1706 |
+
return;
|
1707 |
+
case 2:
|
1708 |
+
if (webkit) cm.state.lastMiddleDown = +new Date;
|
1709 |
+
if (start) extendSelection(cm.doc, start);
|
1710 |
+
setTimeout(bind(focusInput, cm), 20);
|
1711 |
+
e_preventDefault(e);
|
1712 |
+
return;
|
1713 |
+
}
|
1714 |
+
// For button 1, if it was clicked inside the editor
|
1715 |
+
// (posFromMouse returning non-null), we have to adjust the
|
1716 |
+
// selection.
|
1717 |
+
if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
|
1718 |
+
|
1719 |
+
if (!cm.state.focused) onFocus(cm);
|
1720 |
+
|
1721 |
+
var now = +new Date, type = "single";
|
1722 |
+
if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
|
1723 |
+
type = "triple";
|
1724 |
+
e_preventDefault(e);
|
1725 |
+
setTimeout(bind(focusInput, cm), 20);
|
1726 |
+
selectLine(cm, start.line);
|
1727 |
+
} else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
|
1728 |
+
type = "double";
|
1729 |
+
lastDoubleClick = {time: now, pos: start};
|
1730 |
+
e_preventDefault(e);
|
1731 |
+
var word = findWordAt(getLine(doc, start.line).text, start);
|
1732 |
+
extendSelection(cm.doc, word.from, word.to);
|
1733 |
+
} else { lastClick = {time: now, pos: start}; }
|
1734 |
+
|
1735 |
+
var last = start;
|
1736 |
+
if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
|
1737 |
+
!posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
|
1738 |
+
var dragEnd = operation(cm, function(e2) {
|
1739 |
+
if (webkit) display.scroller.draggable = false;
|
1740 |
+
cm.state.draggingText = false;
|
1741 |
+
off(document, "mouseup", dragEnd);
|
1742 |
+
off(display.scroller, "drop", dragEnd);
|
1743 |
+
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
|
1744 |
+
e_preventDefault(e2);
|
1745 |
+
extendSelection(cm.doc, start);
|
1746 |
+
focusInput(cm);
|
1747 |
+
}
|
1748 |
+
});
|
1749 |
+
// Let the drag handler handle this.
|
1750 |
+
if (webkit) display.scroller.draggable = true;
|
1751 |
+
cm.state.draggingText = dragEnd;
|
1752 |
+
// IE's approach to draggable
|
1753 |
+
if (display.scroller.dragDrop) display.scroller.dragDrop();
|
1754 |
+
on(document, "mouseup", dragEnd);
|
1755 |
+
on(display.scroller, "drop", dragEnd);
|
1756 |
+
return;
|
1757 |
+
}
|
1758 |
+
e_preventDefault(e);
|
1759 |
+
if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
|
1760 |
+
|
1761 |
+
var startstart = sel.from, startend = sel.to, lastPos = start;
|
1762 |
+
|
1763 |
+
function doSelect(cur) {
|
1764 |
+
if (posEq(lastPos, cur)) return;
|
1765 |
+
lastPos = cur;
|
1766 |
+
|
1767 |
+
if (type == "single") {
|
1768 |
+
extendSelection(cm.doc, clipPos(doc, start), cur);
|
1769 |
+
return;
|
1770 |
+
}
|
1771 |
+
|
1772 |
+
startstart = clipPos(doc, startstart);
|
1773 |
+
startend = clipPos(doc, startend);
|
1774 |
+
if (type == "double") {
|
1775 |
+
var word = findWordAt(getLine(doc, cur.line).text, cur);
|
1776 |
+
if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
|
1777 |
+
else extendSelection(cm.doc, startstart, word.to);
|
1778 |
+
} else if (type == "triple") {
|
1779 |
+
if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
|
1780 |
+
else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
|
1781 |
+
}
|
1782 |
+
}
|
1783 |
+
|
1784 |
+
var editorSize = getRect(display.wrapper);
|
1785 |
+
// Used to ensure timeout re-tries don't fire when another extend
|
1786 |
+
// happened in the meantime (clearTimeout isn't reliable -- at
|
1787 |
+
// least on Chrome, the timeouts still happen even when cleared,
|
1788 |
+
// if the clear happens after their scheduled firing time).
|
1789 |
+
var counter = 0;
|
1790 |
+
|
1791 |
+
function extend(e) {
|
1792 |
+
var curCount = ++counter;
|
1793 |
+
var cur = posFromMouse(cm, e, true);
|
1794 |
+
if (!cur) return;
|
1795 |
+
if (!posEq(cur, last)) {
|
1796 |
+
if (!cm.state.focused) onFocus(cm);
|
1797 |
+
last = cur;
|
1798 |
+
doSelect(cur);
|
1799 |
+
var visible = visibleLines(display, doc);
|
1800 |
+
if (cur.line >= visible.to || cur.line < visible.from)
|
1801 |
+
setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
|
1802 |
+
} else {
|
1803 |
+
var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
|
1804 |
+
if (outside) setTimeout(operation(cm, function() {
|
1805 |
+
if (counter != curCount) return;
|
1806 |
+
display.scroller.scrollTop += outside;
|
1807 |
+
extend(e);
|
1808 |
+
}), 50);
|
1809 |
+
}
|
1810 |
+
}
|
1811 |
+
|
1812 |
+
function done(e) {
|
1813 |
+
counter = Infinity;
|
1814 |
+
e_preventDefault(e);
|
1815 |
+
focusInput(cm);
|
1816 |
+
off(document, "mousemove", move);
|
1817 |
+
off(document, "mouseup", up);
|
1818 |
+
}
|
1819 |
+
|
1820 |
+
var move = operation(cm, function(e) {
|
1821 |
+
if (!ie && !e_button(e)) done(e);
|
1822 |
+
else extend(e);
|
1823 |
+
});
|
1824 |
+
var up = operation(cm, done);
|
1825 |
+
on(document, "mousemove", move);
|
1826 |
+
on(document, "mouseup", up);
|
1827 |
+
}
|
1828 |
+
|
1829 |
+
function gutterEvent(cm, e, type, prevent, signalfn) {
|
1830 |
+
try { var mX = e.clientX, mY = e.clientY; }
|
1831 |
+
catch(e) { return false; }
|
1832 |
+
if (mX >= Math.floor(getRect(cm.display.gutters).right)) return false;
|
1833 |
+
if (prevent) e_preventDefault(e);
|
1834 |
+
|
1835 |
+
var display = cm.display;
|
1836 |
+
var lineBox = getRect(display.lineDiv);
|
1837 |
+
|
1838 |
+
if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
|
1839 |
+
mY -= lineBox.top - display.viewOffset;
|
1840 |
+
|
1841 |
+
for (var i = 0; i < cm.options.gutters.length; ++i) {
|
1842 |
+
var g = display.gutters.childNodes[i];
|
1843 |
+
if (g && getRect(g).right >= mX) {
|
1844 |
+
var line = lineAtHeight(cm.doc, mY);
|
1845 |
+
var gutter = cm.options.gutters[i];
|
1846 |
+
signalfn(cm, type, cm, line, gutter, e);
|
1847 |
+
return e_defaultPrevented(e);
|
1848 |
+
}
|
1849 |
+
}
|
1850 |
+
}
|
1851 |
+
|
1852 |
+
function contextMenuInGutter(cm, e) {
|
1853 |
+
if (!hasHandler(cm, "gutterContextMenu")) return false;
|
1854 |
+
return gutterEvent(cm, e, "gutterContextMenu", false, signal);
|
1855 |
+
}
|
1856 |
+
|
1857 |
+
function clickInGutter(cm, e) {
|
1858 |
+
return gutterEvent(cm, e, "gutterClick", true, signalLater);
|
1859 |
+
}
|
1860 |
+
|
1861 |
+
// Kludge to work around strange IE behavior where it'll sometimes
|
1862 |
+
// re-fire a series of drag-related events right after the drop (#1551)
|
1863 |
+
var lastDrop = 0;
|
1864 |
+
|
1865 |
+
function onDrop(e) {
|
1866 |
+
var cm = this;
|
1867 |
+
if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
|
1868 |
+
return;
|
1869 |
+
e_preventDefault(e);
|
1870 |
+
if (ie) lastDrop = +new Date;
|
1871 |
+
var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
|
1872 |
+
if (!pos || isReadOnly(cm)) return;
|
1873 |
+
if (files && files.length && window.FileReader && window.File) {
|
1874 |
+
var n = files.length, text = Array(n), read = 0;
|
1875 |
+
var loadFile = function(file, i) {
|
1876 |
+
var reader = new FileReader;
|
1877 |
+
reader.onload = function() {
|
1878 |
+
text[i] = reader.result;
|
1879 |
+
if (++read == n) {
|
1880 |
+
pos = clipPos(cm.doc, pos);
|
1881 |
+
makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
|
1882 |
+
}
|
1883 |
+
};
|
1884 |
+
reader.readAsText(file);
|
1885 |
+
};
|
1886 |
+
for (var i = 0; i < n; ++i) loadFile(files[i], i);
|
1887 |
+
} else {
|
1888 |
+
// Don't do a replace if the drop happened inside of the selected text.
|
1889 |
+
if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
|
1890 |
+
cm.state.draggingText(e);
|
1891 |
+
// Ensure the editor is re-focused
|
1892 |
+
setTimeout(bind(focusInput, cm), 20);
|
1893 |
+
return;
|
1894 |
+
}
|
1895 |
+
try {
|
1896 |
+
var text = e.dataTransfer.getData("Text");
|
1897 |
+
if (text) {
|
1898 |
+
var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
|
1899 |
+
setSelection(cm.doc, pos, pos);
|
1900 |
+
if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
|
1901 |
+
cm.replaceSelection(text, null, "paste");
|
1902 |
+
focusInput(cm);
|
1903 |
+
onFocus(cm);
|
1904 |
+
}
|
1905 |
+
}
|
1906 |
+
catch(e){}
|
1907 |
+
}
|
1908 |
+
}
|
1909 |
+
|
1910 |
+
function onDragStart(cm, e) {
|
1911 |
+
if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
|
1912 |
+
if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
|
1913 |
+
|
1914 |
+
var txt = cm.getSelection();
|
1915 |
+
e.dataTransfer.setData("Text", txt);
|
1916 |
+
|
1917 |
+
// Use dummy image instead of default browsers image.
|
1918 |
+
// Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
|
1919 |
+
if (e.dataTransfer.setDragImage && !safari) {
|
1920 |
+
var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
|
1921 |
+
img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
1922 |
+
if (opera) {
|
1923 |
+
img.width = img.height = 1;
|
1924 |
+
cm.display.wrapper.appendChild(img);
|
1925 |
+
// Force a relayout, or Opera won't use our image for some obscure reason
|
1926 |
+
img._top = img.offsetTop;
|
1927 |
+
}
|
1928 |
+
e.dataTransfer.setDragImage(img, 0, 0);
|
1929 |
+
if (opera) img.parentNode.removeChild(img);
|
1930 |
+
}
|
1931 |
+
}
|
1932 |
+
|
1933 |
+
function setScrollTop(cm, val) {
|
1934 |
+
if (Math.abs(cm.doc.scrollTop - val) < 2) return;
|
1935 |
+
cm.doc.scrollTop = val;
|
1936 |
+
if (!gecko) updateDisplay(cm, [], val);
|
1937 |
+
if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
|
1938 |
+
if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
|
1939 |
+
if (gecko) updateDisplay(cm, []);
|
1940 |
+
startWorker(cm, 100);
|
1941 |
+
}
|
1942 |
+
function setScrollLeft(cm, val, isScroller) {
|
1943 |
+
if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
|
1944 |
+
val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
|
1945 |
+
cm.doc.scrollLeft = val;
|
1946 |
+
alignHorizontally(cm);
|
1947 |
+
if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
|
1948 |
+
if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
|
1949 |
+
}
|
1950 |
+
|
1951 |
+
// Since the delta values reported on mouse wheel events are
|
1952 |
+
// unstandardized between browsers and even browser versions, and
|
1953 |
+
// generally horribly unpredictable, this code starts by measuring
|
1954 |
+
// the scroll effect that the first few mouse wheel events have,
|
1955 |
+
// and, from that, detects the way it can convert deltas to pixel
|
1956 |
+
// offsets afterwards.
|
1957 |
+
//
|
1958 |
+
// The reason we want to know the amount a wheel event will scroll
|
1959 |
+
// is that it gives us a chance to update the display before the
|
1960 |
+
// actual scrolling happens, reducing flickering.
|
1961 |
+
|
1962 |
+
var wheelSamples = 0, wheelPixelsPerUnit = null;
|
1963 |
+
// Fill in a browser-detected starting value on browsers where we
|
1964 |
+
// know one. These don't have to be accurate -- the result of them
|
1965 |
+
// being wrong would just be a slight flicker on the first wheel
|
1966 |
+
// scroll (if it is large enough).
|
1967 |
+
if (ie) wheelPixelsPerUnit = -.53;
|
1968 |
+
else if (gecko) wheelPixelsPerUnit = 15;
|
1969 |
+
else if (chrome) wheelPixelsPerUnit = -.7;
|
1970 |
+
else if (safari) wheelPixelsPerUnit = -1/3;
|
1971 |
+
|
1972 |
+
function onScrollWheel(cm, e) {
|
1973 |
+
var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
|
1974 |
+
if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
|
1975 |
+
if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
|
1976 |
+
else if (dy == null) dy = e.wheelDelta;
|
1977 |
+
|
1978 |
+
var display = cm.display, scroll = display.scroller;
|
1979 |
+
// Quit if there's nothing to scroll here
|
1980 |
+
if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
|
1981 |
+
dy && scroll.scrollHeight > scroll.clientHeight)) return;
|
1982 |
+
|
1983 |
+
// Webkit browsers on OS X abort momentum scrolls when the target
|
1984 |
+
// of the scroll event is removed from the scrollable element.
|
1985 |
+
// This hack (see related code in patchDisplay) makes sure the
|
1986 |
+
// element is kept around.
|
1987 |
+
if (dy && mac && webkit) {
|
1988 |
+
for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
|
1989 |
+
if (cur.lineObj) {
|
1990 |
+
cm.display.currentWheelTarget = cur;
|
1991 |
+
break;
|
1992 |
+
}
|
1993 |
+
}
|
1994 |
+
}
|
1995 |
+
|
1996 |
+
// On some browsers, horizontal scrolling will cause redraws to
|
1997 |
+
// happen before the gutter has been realigned, causing it to
|
1998 |
+
// wriggle around in a most unseemly way. When we have an
|
1999 |
+
// estimated pixels/delta value, we just handle horizontal
|
2000 |
+
// scrolling entirely here. It'll be slightly off from native, but
|
2001 |
+
// better than glitching out.
|
2002 |
+
if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
|
2003 |
+
if (dy)
|
2004 |
+
setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
|
2005 |
+
setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
|
2006 |
+
e_preventDefault(e);
|
2007 |
+
display.wheelStartX = null; // Abort measurement, if in progress
|
2008 |
+
return;
|
2009 |
+
}
|
2010 |
+
|
2011 |
+
if (dy && wheelPixelsPerUnit != null) {
|
2012 |
+
var pixels = dy * wheelPixelsPerUnit;
|
2013 |
+
var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
|
2014 |
+
if (pixels < 0) top = Math.max(0, top + pixels - 50);
|
2015 |
+
else bot = Math.min(cm.doc.height, bot + pixels + 50);
|
2016 |
+
updateDisplay(cm, [], {top: top, bottom: bot});
|
2017 |
+
}
|
2018 |
+
|
2019 |
+
if (wheelSamples < 20) {
|
2020 |
+
if (display.wheelStartX == null) {
|
2021 |
+
display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
|
2022 |
+
display.wheelDX = dx; display.wheelDY = dy;
|
2023 |
+
setTimeout(function() {
|
2024 |
+
if (display.wheelStartX == null) return;
|
2025 |
+
var movedX = scroll.scrollLeft - display.wheelStartX;
|
2026 |
+
var movedY = scroll.scrollTop - display.wheelStartY;
|
2027 |
+
var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
|
2028 |
+
(movedX && display.wheelDX && movedX / display.wheelDX);
|
2029 |
+
display.wheelStartX = display.wheelStartY = null;
|
2030 |
+
if (!sample) return;
|
2031 |
+
wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
|
2032 |
+
++wheelSamples;
|
2033 |
+
}, 200);
|
2034 |
+
} else {
|
2035 |
+
display.wheelDX += dx; display.wheelDY += dy;
|
2036 |
+
}
|
2037 |
+
}
|
2038 |
+
}
|
2039 |
+
|
2040 |
+
function doHandleBinding(cm, bound, dropShift) {
|
2041 |
+
if (typeof bound == "string") {
|
2042 |
+
bound = commands[bound];
|
2043 |
+
if (!bound) return false;
|
2044 |
+
}
|
2045 |
+
// Ensure previous input has been read, so that the handler sees a
|
2046 |
+
// consistent view of the document
|
2047 |
+
if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
|
2048 |
+
var doc = cm.doc, prevShift = doc.sel.shift, done = false;
|
2049 |
+
try {
|
2050 |
+
if (isReadOnly(cm)) cm.state.suppressEdits = true;
|
2051 |
+
if (dropShift) doc.sel.shift = false;
|
2052 |
+
done = bound(cm) != Pass;
|
2053 |
+
} finally {
|
2054 |
+
doc.sel.shift = prevShift;
|
2055 |
+
cm.state.suppressEdits = false;
|
2056 |
+
}
|
2057 |
+
return done;
|
2058 |
+
}
|
2059 |
+
|
2060 |
+
function allKeyMaps(cm) {
|
2061 |
+
var maps = cm.state.keyMaps.slice(0);
|
2062 |
+
if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
|
2063 |
+
maps.push(cm.options.keyMap);
|
2064 |
+
return maps;
|
2065 |
+
}
|
2066 |
+
|
2067 |
+
var maybeTransition;
|
2068 |
+
function handleKeyBinding(cm, e) {
|
2069 |
+
// Handle auto keymap transitions
|
2070 |
+
var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
|
2071 |
+
clearTimeout(maybeTransition);
|
2072 |
+
if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
|
2073 |
+
if (getKeyMap(cm.options.keyMap) == startMap) {
|
2074 |
+
cm.options.keyMap = (next.call ? next.call(null, cm) : next);
|
2075 |
+
keyMapChanged(cm);
|
2076 |
+
}
|
2077 |
+
}, 50);
|
2078 |
+
|
2079 |
+
var name = keyName(e, true), handled = false;
|
2080 |
+
if (!name) return false;
|
2081 |
+
var keymaps = allKeyMaps(cm);
|
2082 |
+
|
2083 |
+
if (e.shiftKey) {
|
2084 |
+
// First try to resolve full name (including 'Shift-'). Failing
|
2085 |
+
// that, see if there is a cursor-motion command (starting with
|
2086 |
+
// 'go') bound to the keyname without 'Shift-'.
|
2087 |
+
handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
|
2088 |
+
|| lookupKey(name, keymaps, function(b) {
|
2089 |
+
if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
|
2090 |
+
return doHandleBinding(cm, b);
|
2091 |
+
});
|
2092 |
+
} else {
|
2093 |
+
handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
|
2094 |
+
}
|
2095 |
+
|
2096 |
+
if (handled) {
|
2097 |
+
e_preventDefault(e);
|
2098 |
+
restartBlink(cm);
|
2099 |
+
if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
|
2100 |
+
signalLater(cm, "keyHandled", cm, name, e);
|
2101 |
+
}
|
2102 |
+
return handled;
|
2103 |
+
}
|
2104 |
+
|
2105 |
+
function handleCharBinding(cm, e, ch) {
|
2106 |
+
var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
|
2107 |
+
function(b) { return doHandleBinding(cm, b, true); });
|
2108 |
+
if (handled) {
|
2109 |
+
e_preventDefault(e);
|
2110 |
+
restartBlink(cm);
|
2111 |
+
signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
|
2112 |
+
}
|
2113 |
+
return handled;
|
2114 |
+
}
|
2115 |
+
|
2116 |
+
var lastStoppedKey = null;
|
2117 |
+
function onKeyDown(e) {
|
2118 |
+
var cm = this;
|
2119 |
+
if (!cm.state.focused) onFocus(cm);
|
2120 |
+
if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
2121 |
+
if (ie && e.keyCode == 27) e.returnValue = false;
|
2122 |
+
var code = e.keyCode;
|
2123 |
+
// IE does strange things with escape.
|
2124 |
+
cm.doc.sel.shift = code == 16 || e.shiftKey;
|
2125 |
+
// First give onKeyEvent option a chance to handle this.
|
2126 |
+
var handled = handleKeyBinding(cm, e);
|
2127 |
+
if (opera) {
|
2128 |
+
lastStoppedKey = handled ? code : null;
|
2129 |
+
// Opera has no cut event... we try to at least catch the key combo
|
2130 |
+
if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
|
2131 |
+
cm.replaceSelection("");
|
2132 |
+
}
|
2133 |
+
}
|
2134 |
+
|
2135 |
+
function onKeyPress(e) {
|
2136 |
+
var cm = this;
|
2137 |
+
if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
2138 |
+
var keyCode = e.keyCode, charCode = e.charCode;
|
2139 |
+
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
|
2140 |
+
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
|
2141 |
+
var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
|
2142 |
+
if (this.options.electricChars && this.doc.mode.electricChars &&
|
2143 |
+
this.options.smartIndent && !isReadOnly(this) &&
|
2144 |
+
this.doc.mode.electricChars.indexOf(ch) > -1)
|
2145 |
+
setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
|
2146 |
+
if (handleCharBinding(cm, e, ch)) return;
|
2147 |
+
if (ie && !ie_lt9) cm.display.inputHasSelection = null;
|
2148 |
+
fastPoll(cm);
|
2149 |
+
}
|
2150 |
+
|
2151 |
+
function onFocus(cm) {
|
2152 |
+
if (cm.options.readOnly == "nocursor") return;
|
2153 |
+
if (!cm.state.focused) {
|
2154 |
+
signal(cm, "focus", cm);
|
2155 |
+
cm.state.focused = true;
|
2156 |
+
if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
|
2157 |
+
cm.display.wrapper.className += " CodeMirror-focused";
|
2158 |
+
if (!cm.curOp) {
|
2159 |
+
resetInput(cm, true);
|
2160 |
+
if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730
|
2161 |
+
}
|
2162 |
+
}
|
2163 |
+
slowPoll(cm);
|
2164 |
+
restartBlink(cm);
|
2165 |
+
}
|
2166 |
+
function onBlur(cm) {
|
2167 |
+
if (cm.state.focused) {
|
2168 |
+
signal(cm, "blur", cm);
|
2169 |
+
cm.state.focused = false;
|
2170 |
+
cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
|
2171 |
+
}
|
2172 |
+
clearInterval(cm.display.blinker);
|
2173 |
+
setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
|
2174 |
+
}
|
2175 |
+
|
2176 |
+
var detectingSelectAll;
|
2177 |
+
function onContextMenu(cm, e) {
|
2178 |
+
if (signalDOMEvent(cm, e, "contextmenu")) return;
|
2179 |
+
var display = cm.display, sel = cm.doc.sel;
|
2180 |
+
if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return;
|
2181 |
+
|
2182 |
+
var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
|
2183 |
+
if (!pos || opera) return; // Opera is difficult.
|
2184 |
+
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
|
2185 |
+
operation(cm, setSelection)(cm.doc, pos, pos);
|
2186 |
+
|
2187 |
+
var oldCSS = display.input.style.cssText;
|
2188 |
+
display.inputDiv.style.position = "absolute";
|
2189 |
+
display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
|
2190 |
+
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
|
2191 |
+
"border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
|
2192 |
+
focusInput(cm);
|
2193 |
+
resetInput(cm, true);
|
2194 |
+
// Adds "Select all" to context menu in FF
|
2195 |
+
if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
|
2196 |
+
|
2197 |
+
function prepareSelectAllHack() {
|
2198 |
+
if (display.input.selectionStart != null) {
|
2199 |
+
var extval = display.input.value = "\u200b" + (posEq(sel.from, sel.to) ? "" : display.input.value);
|
2200 |
+
display.prevInput = "\u200b";
|
2201 |
+
display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
|
2202 |
+
}
|
2203 |
+
}
|
2204 |
+
function rehide() {
|
2205 |
+
display.inputDiv.style.position = "relative";
|
2206 |
+
display.input.style.cssText = oldCSS;
|
2207 |
+
if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
|
2208 |
+
slowPoll(cm);
|
2209 |
+
|
2210 |
+
// Try to detect the user choosing select-all
|
2211 |
+
if (display.input.selectionStart != null) {
|
2212 |
+
if (!ie || ie_lt9) prepareSelectAllHack();
|
2213 |
+
clearTimeout(detectingSelectAll);
|
2214 |
+
var i = 0, poll = function(){
|
2215 |
+
if (display.prevInput == " " && display.input.selectionStart == 0)
|
2216 |
+
operation(cm, commands.selectAll)(cm);
|
2217 |
+
else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
|
2218 |
+
else resetInput(cm);
|
2219 |
+
};
|
2220 |
+
detectingSelectAll = setTimeout(poll, 200);
|
2221 |
+
}
|
2222 |
+
}
|
2223 |
+
|
2224 |
+
if (ie && !ie_lt9) prepareSelectAllHack();
|
2225 |
+
if (captureMiddleClick) {
|
2226 |
+
e_stop(e);
|
2227 |
+
var mouseup = function() {
|
2228 |
+
off(window, "mouseup", mouseup);
|
2229 |
+
setTimeout(rehide, 20);
|
2230 |
+
};
|
2231 |
+
on(window, "mouseup", mouseup);
|
2232 |
+
} else {
|
2233 |
+
setTimeout(rehide, 50);
|
2234 |
+
}
|
2235 |
+
}
|
2236 |
+
|
2237 |
+
// UPDATING
|
2238 |
+
|
2239 |
+
var changeEnd = CodeMirror.changeEnd = function(change) {
|
2240 |
+
if (!change.text) return change.to;
|
2241 |
+
return Pos(change.from.line + change.text.length - 1,
|
2242 |
+
lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
|
2243 |
+
};
|
2244 |
+
|
2245 |
+
// Make sure a position will be valid after the given change.
|
2246 |
+
function clipPostChange(doc, change, pos) {
|
2247 |
+
if (!posLess(change.from, pos)) return clipPos(doc, pos);
|
2248 |
+
var diff = (change.text.length - 1) - (change.to.line - change.from.line);
|
2249 |
+
if (pos.line > change.to.line + diff) {
|
2250 |
+
var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
|
2251 |
+
if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
|
2252 |
+
return clipToLen(pos, getLine(doc, preLine).text.length);
|
2253 |
+
}
|
2254 |
+
if (pos.line == change.to.line + diff)
|
2255 |
+
return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
|
2256 |
+
getLine(doc, change.to.line).text.length - change.to.ch);
|
2257 |
+
var inside = pos.line - change.from.line;
|
2258 |
+
return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
|
2259 |
+
}
|
2260 |
+
|
2261 |
+
// Hint can be null|"end"|"start"|"around"|{anchor,head}
|
2262 |
+
function computeSelAfterChange(doc, change, hint) {
|
2263 |
+
if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
|
2264 |
+
return {anchor: clipPostChange(doc, change, hint.anchor),
|
2265 |
+
head: clipPostChange(doc, change, hint.head)};
|
2266 |
+
|
2267 |
+
if (hint == "start") return {anchor: change.from, head: change.from};
|
2268 |
+
|
2269 |
+
var end = changeEnd(change);
|
2270 |
+
if (hint == "around") return {anchor: change.from, head: end};
|
2271 |
+
if (hint == "end") return {anchor: end, head: end};
|
2272 |
+
|
2273 |
+
// hint is null, leave the selection alone as much as possible
|
2274 |
+
var adjustPos = function(pos) {
|
2275 |
+
if (posLess(pos, change.from)) return pos;
|
2276 |
+
if (!posLess(change.to, pos)) return end;
|
2277 |
+
|
2278 |
+
var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
|
2279 |
+
if (pos.line == change.to.line) ch += end.ch - change.to.ch;
|
2280 |
+
return Pos(line, ch);
|
2281 |
+
};
|
2282 |
+
return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
|
2283 |
+
}
|
2284 |
+
|
2285 |
+
function filterChange(doc, change, update) {
|
2286 |
+
var obj = {
|
2287 |
+
canceled: false,
|
2288 |
+
from: change.from,
|
2289 |
+
to: change.to,
|
2290 |
+
text: change.text,
|
2291 |
+
origin: change.origin,
|
2292 |
+
cancel: function() { this.canceled = true; }
|
2293 |
+
};
|
2294 |
+
if (update) obj.update = function(from, to, text, origin) {
|
2295 |
+
if (from) this.from = clipPos(doc, from);
|
2296 |
+
if (to) this.to = clipPos(doc, to);
|
2297 |
+
if (text) this.text = text;
|
2298 |
+
if (origin !== undefined) this.origin = origin;
|
2299 |
+
};
|
2300 |
+
signal(doc, "beforeChange", doc, obj);
|
2301 |
+
if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
|
2302 |
+
|
2303 |
+
if (obj.canceled) return null;
|
2304 |
+
return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
|
2305 |
+
}
|
2306 |
+
|
2307 |
+
// Replace the range from from to to by the strings in replacement.
|
2308 |
+
// change is a {from, to, text [, origin]} object
|
2309 |
+
function makeChange(doc, change, selUpdate, ignoreReadOnly) {
|
2310 |
+
if (doc.cm) {
|
2311 |
+
if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
|
2312 |
+
if (doc.cm.state.suppressEdits) return;
|
2313 |
+
}
|
2314 |
+
|
2315 |
+
if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
|
2316 |
+
change = filterChange(doc, change, true);
|
2317 |
+
if (!change) return;
|
2318 |
+
}
|
2319 |
+
|
2320 |
+
// Possibly split or suppress the update based on the presence
|
2321 |
+
// of read-only spans in its range.
|
2322 |
+
var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
|
2323 |
+
if (split) {
|
2324 |
+
for (var i = split.length - 1; i >= 1; --i)
|
2325 |
+
makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
|
2326 |
+
if (split.length)
|
2327 |
+
makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
|
2328 |
+
} else {
|
2329 |
+
makeChangeNoReadonly(doc, change, selUpdate);
|
2330 |
+
}
|
2331 |
+
}
|
2332 |
+
|
2333 |
+
function makeChangeNoReadonly(doc, change, selUpdate) {
|
2334 |
+
if (change.text.length == 1 && change.text[0] == "" && posEq(change.from, change.to)) return;
|
2335 |
+
var selAfter = computeSelAfterChange(doc, change, selUpdate);
|
2336 |
+
addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
|
2337 |
+
|
2338 |
+
makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
|
2339 |
+
var rebased = [];
|
2340 |
+
|
2341 |
+
linkedDocs(doc, function(doc, sharedHist) {
|
2342 |
+
if (!sharedHist && indexOf(rebased, doc.history) == -1) {
|
2343 |
+
rebaseHist(doc.history, change);
|
2344 |
+
rebased.push(doc.history);
|
2345 |
+
}
|
2346 |
+
makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
|
2347 |
+
});
|
2348 |
+
}
|
2349 |
+
|
2350 |
+
function makeChangeFromHistory(doc, type) {
|
2351 |
+
if (doc.cm && doc.cm.state.suppressEdits) return;
|
2352 |
+
|
2353 |
+
var hist = doc.history;
|
2354 |
+
var event = (type == "undo" ? hist.done : hist.undone).pop();
|
2355 |
+
if (!event) return;
|
2356 |
+
|
2357 |
+
var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
|
2358 |
+
anchorAfter: event.anchorBefore, headAfter: event.headBefore,
|
2359 |
+
generation: hist.generation};
|
2360 |
+
(type == "undo" ? hist.undone : hist.done).push(anti);
|
2361 |
+
hist.generation = event.generation || ++hist.maxGeneration;
|
2362 |
+
|
2363 |
+
var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
|
2364 |
+
|
2365 |
+
for (var i = event.changes.length - 1; i >= 0; --i) {
|
2366 |
+
var change = event.changes[i];
|
2367 |
+
change.origin = type;
|
2368 |
+
if (filter && !filterChange(doc, change, false)) {
|
2369 |
+
(type == "undo" ? hist.done : hist.undone).length = 0;
|
2370 |
+
return;
|
2371 |
+
}
|
2372 |
+
|
2373 |
+
anti.changes.push(historyChangeFromChange(doc, change));
|
2374 |
+
|
2375 |
+
var after = i ? computeSelAfterChange(doc, change, null)
|
2376 |
+
: {anchor: event.anchorBefore, head: event.headBefore};
|
2377 |
+
makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
|
2378 |
+
var rebased = [];
|
2379 |
+
|
2380 |
+
linkedDocs(doc, function(doc, sharedHist) {
|
2381 |
+
if (!sharedHist && indexOf(rebased, doc.history) == -1) {
|
2382 |
+
rebaseHist(doc.history, change);
|
2383 |
+
rebased.push(doc.history);
|
2384 |
+
}
|
2385 |
+
makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
|
2386 |
+
});
|
2387 |
+
}
|
2388 |
+
}
|
2389 |
+
|
2390 |
+
function shiftDoc(doc, distance) {
|
2391 |
+
function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
|
2392 |
+
doc.first += distance;
|
2393 |
+
if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
|
2394 |
+
doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
|
2395 |
+
doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
|
2396 |
+
}
|
2397 |
+
|
2398 |
+
function makeChangeSingleDoc(doc, change, selAfter, spans) {
|
2399 |
+
if (doc.cm && !doc.cm.curOp)
|
2400 |
+
return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
|
2401 |
+
|
2402 |
+
if (change.to.line < doc.first) {
|
2403 |
+
shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
|
2404 |
+
return;
|
2405 |
+
}
|
2406 |
+
if (change.from.line > doc.lastLine()) return;
|
2407 |
+
|
2408 |
+
// Clip the change to the size of this doc
|
2409 |
+
if (change.from.line < doc.first) {
|
2410 |
+
var shift = change.text.length - 1 - (doc.first - change.from.line);
|
2411 |
+
shiftDoc(doc, shift);
|
2412 |
+
change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
|
2413 |
+
text: [lst(change.text)], origin: change.origin};
|
2414 |
+
}
|
2415 |
+
var last = doc.lastLine();
|
2416 |
+
if (change.to.line > last) {
|
2417 |
+
change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
|
2418 |
+
text: [change.text[0]], origin: change.origin};
|
2419 |
+
}
|
2420 |
+
|
2421 |
+
change.removed = getBetween(doc, change.from, change.to);
|
2422 |
+
|
2423 |
+
if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
|
2424 |
+
if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
|
2425 |
+
else updateDoc(doc, change, spans, selAfter);
|
2426 |
+
}
|
2427 |
+
|
2428 |
+
function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
|
2429 |
+
var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
|
2430 |
+
|
2431 |
+
var recomputeMaxLength = false, checkWidthStart = from.line;
|
2432 |
+
if (!cm.options.lineWrapping) {
|
2433 |
+
checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
|
2434 |
+
doc.iter(checkWidthStart, to.line + 1, function(line) {
|
2435 |
+
if (line == display.maxLine) {
|
2436 |
+
recomputeMaxLength = true;
|
2437 |
+
return true;
|
2438 |
+
}
|
2439 |
+
});
|
2440 |
+
}
|
2441 |
+
|
2442 |
+
if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head))
|
2443 |
+
cm.curOp.cursorActivity = true;
|
2444 |
+
|
2445 |
+
updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
|
2446 |
+
|
2447 |
+
if (!cm.options.lineWrapping) {
|
2448 |
+
doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
|
2449 |
+
var len = lineLength(doc, line);
|
2450 |
+
if (len > display.maxLineLength) {
|
2451 |
+
display.maxLine = line;
|
2452 |
+
display.maxLineLength = len;
|
2453 |
+
display.maxLineChanged = true;
|
2454 |
+
recomputeMaxLength = false;
|
2455 |
+
}
|
2456 |
+
});
|
2457 |
+
if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
|
2458 |
+
}
|
2459 |
+
|
2460 |
+
// Adjust frontier, schedule worker
|
2461 |
+
doc.frontier = Math.min(doc.frontier, from.line);
|
2462 |
+
startWorker(cm, 400);
|
2463 |
+
|
2464 |
+
var lendiff = change.text.length - (to.line - from.line) - 1;
|
2465 |
+
// Remember that these lines changed, for updating the display
|
2466 |
+
regChange(cm, from.line, to.line + 1, lendiff);
|
2467 |
+
|
2468 |
+
if (hasHandler(cm, "change")) {
|
2469 |
+
var changeObj = {from: from, to: to,
|
2470 |
+
text: change.text,
|
2471 |
+
removed: change.removed,
|
2472 |
+
origin: change.origin};
|
2473 |
+
if (cm.curOp.textChanged) {
|
2474 |
+
for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
|
2475 |
+
cur.next = changeObj;
|
2476 |
+
} else cm.curOp.textChanged = changeObj;
|
2477 |
+
}
|
2478 |
+
}
|
2479 |
+
|
2480 |
+
function replaceRange(doc, code, from, to, origin) {
|
2481 |
+
if (!to) to = from;
|
2482 |
+
if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
|
2483 |
+
if (typeof code == "string") code = splitLines(code);
|
2484 |
+
makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
|
2485 |
+
}
|
2486 |
+
|
2487 |
+
// POSITION OBJECT
|
2488 |
+
|
2489 |
+
function Pos(line, ch) {
|
2490 |
+
if (!(this instanceof Pos)) return new Pos(line, ch);
|
2491 |
+
this.line = line; this.ch = ch;
|
2492 |
+
}
|
2493 |
+
CodeMirror.Pos = Pos;
|
2494 |
+
|
2495 |
+
function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
|
2496 |
+
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
|
2497 |
+
function copyPos(x) {return Pos(x.line, x.ch);}
|
2498 |
+
|
2499 |
+
// SELECTION
|
2500 |
+
|
2501 |
+
function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
|
2502 |
+
function clipPos(doc, pos) {
|
2503 |
+
if (pos.line < doc.first) return Pos(doc.first, 0);
|
2504 |
+
var last = doc.first + doc.size - 1;
|
2505 |
+
if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
|
2506 |
+
return clipToLen(pos, getLine(doc, pos.line).text.length);
|
2507 |
+
}
|
2508 |
+
function clipToLen(pos, linelen) {
|
2509 |
+
var ch = pos.ch;
|
2510 |
+
if (ch == null || ch > linelen) return Pos(pos.line, linelen);
|
2511 |
+
else if (ch < 0) return Pos(pos.line, 0);
|
2512 |
+
else return pos;
|
2513 |
+
}
|
2514 |
+
function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
|
2515 |
+
|
2516 |
+
// If shift is held, this will move the selection anchor. Otherwise,
|
2517 |
+
// it'll set the whole selection.
|
2518 |
+
function extendSelection(doc, pos, other, bias) {
|
2519 |
+
if (doc.sel.shift || doc.sel.extend) {
|
2520 |
+
var anchor = doc.sel.anchor;
|
2521 |
+
if (other) {
|
2522 |
+
var posBefore = posLess(pos, anchor);
|
2523 |
+
if (posBefore != posLess(other, anchor)) {
|
2524 |
+
anchor = pos;
|
2525 |
+
pos = other;
|
2526 |
+
} else if (posBefore != posLess(pos, other)) {
|
2527 |
+
pos = other;
|
2528 |
+
}
|
2529 |
+
}
|
2530 |
+
setSelection(doc, anchor, pos, bias);
|
2531 |
+
} else {
|
2532 |
+
setSelection(doc, pos, other || pos, bias);
|
2533 |
+
}
|
2534 |
+
if (doc.cm) doc.cm.curOp.userSelChange = true;
|
2535 |
+
}
|
2536 |
+
|
2537 |
+
function filterSelectionChange(doc, anchor, head) {
|
2538 |
+
var obj = {anchor: anchor, head: head};
|
2539 |
+
signal(doc, "beforeSelectionChange", doc, obj);
|
2540 |
+
if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
|
2541 |
+
obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
|
2542 |
+
return obj;
|
2543 |
+
}
|
2544 |
+
|
2545 |
+
// Update the selection. Last two args are only used by
|
2546 |
+
// updateDoc, since they have to be expressed in the line
|
2547 |
+
// numbers before the update.
|
2548 |
+
function setSelection(doc, anchor, head, bias, checkAtomic) {
|
2549 |
+
if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
|
2550 |
+
var filtered = filterSelectionChange(doc, anchor, head);
|
2551 |
+
head = filtered.head;
|
2552 |
+
anchor = filtered.anchor;
|
2553 |
+
}
|
2554 |
+
|
2555 |
+
var sel = doc.sel;
|
2556 |
+
sel.goalColumn = null;
|
2557 |
+
if (bias == null) bias = posLess(head, sel.head) ? -1 : 1;
|
2558 |
+
// Skip over atomic spans.
|
2559 |
+
if (checkAtomic || !posEq(anchor, sel.anchor))
|
2560 |
+
anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
|
2561 |
+
if (checkAtomic || !posEq(head, sel.head))
|
2562 |
+
head = skipAtomic(doc, head, bias, checkAtomic != "push");
|
2563 |
+
|
2564 |
+
if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
|
2565 |
+
|
2566 |
+
sel.anchor = anchor; sel.head = head;
|
2567 |
+
var inv = posLess(head, anchor);
|
2568 |
+
sel.from = inv ? head : anchor;
|
2569 |
+
sel.to = inv ? anchor : head;
|
2570 |
+
|
2571 |
+
if (doc.cm)
|
2572 |
+
doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
|
2573 |
+
doc.cm.curOp.cursorActivity = true;
|
2574 |
+
|
2575 |
+
signalLater(doc, "cursorActivity", doc);
|
2576 |
+
}
|
2577 |
+
|
2578 |
+
function reCheckSelection(cm) {
|
2579 |
+
setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
|
2580 |
+
}
|
2581 |
+
|
2582 |
+
function skipAtomic(doc, pos, bias, mayClear) {
|
2583 |
+
var flipped = false, curPos = pos;
|
2584 |
+
var dir = bias || 1;
|
2585 |
+
doc.cantEdit = false;
|
2586 |
+
search: for (;;) {
|
2587 |
+
var line = getLine(doc, curPos.line);
|
2588 |
+
if (line.markedSpans) {
|
2589 |
+
for (var i = 0; i < line.markedSpans.length; ++i) {
|
2590 |
+
var sp = line.markedSpans[i], m = sp.marker;
|
2591 |
+
if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
|
2592 |
+
(sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
|
2593 |
+
if (mayClear) {
|
2594 |
+
signal(m, "beforeCursorEnter");
|
2595 |
+
if (m.explicitlyCleared) {
|
2596 |
+
if (!line.markedSpans) break;
|
2597 |
+
else {--i; continue;}
|
2598 |
+
}
|
2599 |
+
}
|
2600 |
+
if (!m.atomic) continue;
|
2601 |
+
var newPos = m.find()[dir < 0 ? "from" : "to"];
|
2602 |
+
if (posEq(newPos, curPos)) {
|
2603 |
+
newPos.ch += dir;
|
2604 |
+
if (newPos.ch < 0) {
|
2605 |
+
if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
|
2606 |
+
else newPos = null;
|
2607 |
+
} else if (newPos.ch > line.text.length) {
|
2608 |
+
if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
|
2609 |
+
else newPos = null;
|
2610 |
+
}
|
2611 |
+
if (!newPos) {
|
2612 |
+
if (flipped) {
|
2613 |
+
// Driven in a corner -- no valid cursor position found at all
|
2614 |
+
// -- try again *with* clearing, if we didn't already
|
2615 |
+
if (!mayClear) return skipAtomic(doc, pos, bias, true);
|
2616 |
+
// Otherwise, turn off editing until further notice, and return the start of the doc
|
2617 |
+
doc.cantEdit = true;
|
2618 |
+
return Pos(doc.first, 0);
|
2619 |
+
}
|
2620 |
+
flipped = true; newPos = pos; dir = -dir;
|
2621 |
+
}
|
2622 |
+
}
|
2623 |
+
curPos = newPos;
|
2624 |
+
continue search;
|
2625 |
+
}
|
2626 |
+
}
|
2627 |
+
}
|
2628 |
+
return curPos;
|
2629 |
+
}
|
2630 |
+
}
|
2631 |
+
|
2632 |
+
// SCROLLING
|
2633 |
+
|
2634 |
+
function scrollCursorIntoView(cm) {
|
2635 |
+
var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
|
2636 |
+
if (!cm.state.focused) return;
|
2637 |
+
var display = cm.display, box = getRect(display.sizer), doScroll = null;
|
2638 |
+
if (coords.top + box.top < 0) doScroll = true;
|
2639 |
+
else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
|
2640 |
+
if (doScroll != null && !phantom) {
|
2641 |
+
var hidden = display.cursor.style.display == "none";
|
2642 |
+
if (hidden) {
|
2643 |
+
display.cursor.style.display = "";
|
2644 |
+
display.cursor.style.left = coords.left + "px";
|
2645 |
+
display.cursor.style.top = (coords.top - display.viewOffset) + "px";
|
2646 |
+
}
|
2647 |
+
display.cursor.scrollIntoView(doScroll);
|
2648 |
+
if (hidden) display.cursor.style.display = "none";
|
2649 |
+
}
|
2650 |
+
}
|
2651 |
+
|
2652 |
+
function scrollPosIntoView(cm, pos, margin) {
|
2653 |
+
if (margin == null) margin = 0;
|
2654 |
+
for (;;) {
|
2655 |
+
var changed = false, coords = cursorCoords(cm, pos);
|
2656 |
+
var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
|
2657 |
+
var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
|
2658 |
+
if (scrollPos.scrollTop != null) {
|
2659 |
+
setScrollTop(cm, scrollPos.scrollTop);
|
2660 |
+
if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
|
2661 |
+
}
|
2662 |
+
if (scrollPos.scrollLeft != null) {
|
2663 |
+
setScrollLeft(cm, scrollPos.scrollLeft);
|
2664 |
+
if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
|
2665 |
+
}
|
2666 |
+
if (!changed) return coords;
|
2667 |
+
}
|
2668 |
+
}
|
2669 |
+
|
2670 |
+
function scrollIntoView(cm, x1, y1, x2, y2) {
|
2671 |
+
var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
|
2672 |
+
if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
|
2673 |
+
if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
|
2674 |
+
}
|
2675 |
+
|
2676 |
+
function calculateScrollPos(cm, x1, y1, x2, y2) {
|
2677 |
+
var display = cm.display, snapMargin = textHeight(cm.display);
|
2678 |
+
if (y1 < 0) y1 = 0;
|
2679 |
+
var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
|
2680 |
+
var docBottom = cm.doc.height + paddingVert(display);
|
2681 |
+
var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
|
2682 |
+
if (y1 < screentop) {
|
2683 |
+
result.scrollTop = atTop ? 0 : y1;
|
2684 |
+
} else if (y2 > screentop + screen) {
|
2685 |
+
var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
|
2686 |
+
if (newTop != screentop) result.scrollTop = newTop;
|
2687 |
+
}
|
2688 |
+
|
2689 |
+
var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
|
2690 |
+
x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
|
2691 |
+
var gutterw = display.gutters.offsetWidth;
|
2692 |
+
var atLeft = x1 < gutterw + 10;
|
2693 |
+
if (x1 < screenleft + gutterw || atLeft) {
|
2694 |
+
if (atLeft) x1 = 0;
|
2695 |
+
result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
|
2696 |
+
} else if (x2 > screenw + screenleft - 3) {
|
2697 |
+
result.scrollLeft = x2 + 10 - screenw;
|
2698 |
+
}
|
2699 |
+
return result;
|
2700 |
+
}
|
2701 |
+
|
2702 |
+
function updateScrollPos(cm, left, top) {
|
2703 |
+
cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
|
2704 |
+
scrollTop: top == null ? cm.doc.scrollTop : top};
|
2705 |
+
}
|
2706 |
+
|
2707 |
+
function addToScrollPos(cm, left, top) {
|
2708 |
+
var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
|
2709 |
+
var scroll = cm.display.scroller;
|
2710 |
+
pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
|
2711 |
+
pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
|
2712 |
+
}
|
2713 |
+
|
2714 |
+
// API UTILITIES
|
2715 |
+
|
2716 |
+
function indentLine(cm, n, how, aggressive) {
|
2717 |
+
var doc = cm.doc;
|
2718 |
+
if (how == null) how = "add";
|
2719 |
+
if (how == "smart") {
|
2720 |
+
if (!cm.doc.mode.indent) how = "prev";
|
2721 |
+
else var state = getStateBefore(cm, n);
|
2722 |
+
}
|
2723 |
+
|
2724 |
+
var tabSize = cm.options.tabSize;
|
2725 |
+
var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
|
2726 |
+
var curSpaceString = line.text.match(/^\s*/)[0], indentation;
|
2727 |
+
if (how == "smart") {
|
2728 |
+
indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
|
2729 |
+
if (indentation == Pass) {
|
2730 |
+
if (!aggressive) return;
|
2731 |
+
how = "prev";
|
2732 |
+
}
|
2733 |
+
}
|
2734 |
+
if (how == "prev") {
|
2735 |
+
if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
|
2736 |
+
else indentation = 0;
|
2737 |
+
} else if (how == "add") {
|
2738 |
+
indentation = curSpace + cm.options.indentUnit;
|
2739 |
+
} else if (how == "subtract") {
|
2740 |
+
indentation = curSpace - cm.options.indentUnit;
|
2741 |
+
} else if (typeof how == "number") {
|
2742 |
+
indentation = curSpace + how;
|
2743 |
+
}
|
2744 |
+
indentation = Math.max(0, indentation);
|
2745 |
+
|
2746 |
+
var indentString = "", pos = 0;
|
2747 |
+
if (cm.options.indentWithTabs)
|
2748 |
+
for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
|
2749 |
+
if (pos < indentation) indentString += spaceStr(indentation - pos);
|
2750 |
+
|
2751 |
+
if (indentString != curSpaceString)
|
2752 |
+
replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
|
2753 |
+
line.stateAfter = null;
|
2754 |
+
}
|
2755 |
+
|
2756 |
+
function changeLine(cm, handle, op) {
|
2757 |
+
var no = handle, line = handle, doc = cm.doc;
|
2758 |
+
if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
|
2759 |
+
else no = lineNo(handle);
|
2760 |
+
if (no == null) return null;
|
2761 |
+
if (op(line, no)) regChange(cm, no, no + 1);
|
2762 |
+
else return null;
|
2763 |
+
return line;
|
2764 |
+
}
|
2765 |
+
|
2766 |
+
function findPosH(doc, pos, dir, unit, visually) {
|
2767 |
+
var line = pos.line, ch = pos.ch, origDir = dir;
|
2768 |
+
var lineObj = getLine(doc, line);
|
2769 |
+
var possible = true;
|
2770 |
+
function findNextLine() {
|
2771 |
+
var l = line + dir;
|
2772 |
+
if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
|
2773 |
+
line = l;
|
2774 |
+
return lineObj = getLine(doc, l);
|
2775 |
+
}
|
2776 |
+
function moveOnce(boundToLine) {
|
2777 |
+
var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
|
2778 |
+
if (next == null) {
|
2779 |
+
if (!boundToLine && findNextLine()) {
|
2780 |
+
if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
|
2781 |
+
else ch = dir < 0 ? lineObj.text.length : 0;
|
2782 |
+
} else return (possible = false);
|
2783 |
+
} else ch = next;
|
2784 |
+
return true;
|
2785 |
+
}
|
2786 |
+
|
2787 |
+
if (unit == "char") moveOnce();
|
2788 |
+
else if (unit == "column") moveOnce(true);
|
2789 |
+
else if (unit == "word" || unit == "group") {
|
2790 |
+
var sawType = null, group = unit == "group";
|
2791 |
+
for (var first = true;; first = false) {
|
2792 |
+
if (dir < 0 && !moveOnce(!first)) break;
|
2793 |
+
var cur = lineObj.text.charAt(ch) || "\n";
|
2794 |
+
var type = isWordChar(cur) ? "w"
|
2795 |
+
: !group ? null
|
2796 |
+
: /\s/.test(cur) ? null
|
2797 |
+
: "p";
|
2798 |
+
if (sawType && sawType != type) {
|
2799 |
+
if (dir < 0) {dir = 1; moveOnce();}
|
2800 |
+
break;
|
2801 |
+
}
|
2802 |
+
if (type) sawType = type;
|
2803 |
+
if (dir > 0 && !moveOnce(!first)) break;
|
2804 |
+
}
|
2805 |
+
}
|
2806 |
+
var result = skipAtomic(doc, Pos(line, ch), origDir, true);
|
2807 |
+
if (!possible) result.hitSide = true;
|
2808 |
+
return result;
|
2809 |
+
}
|
2810 |
+
|
2811 |
+
function findPosV(cm, pos, dir, unit) {
|
2812 |
+
var doc = cm.doc, x = pos.left, y;
|
2813 |
+
if (unit == "page") {
|
2814 |
+
var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
|
2815 |
+
y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
|
2816 |
+
} else if (unit == "line") {
|
2817 |
+
y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
|
2818 |
+
}
|
2819 |
+
for (;;) {
|
2820 |
+
var target = coordsChar(cm, x, y);
|
2821 |
+
if (!target.outside) break;
|
2822 |
+
if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
|
2823 |
+
y += dir * 5;
|
2824 |
+
}
|
2825 |
+
return target;
|
2826 |
+
}
|
2827 |
+
|
2828 |
+
function findWordAt(line, pos) {
|
2829 |
+
var start = pos.ch, end = pos.ch;
|
2830 |
+
if (line) {
|
2831 |
+
if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
|
2832 |
+
var startChar = line.charAt(start);
|
2833 |
+
var check = isWordChar(startChar) ? isWordChar
|
2834 |
+
: /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
|
2835 |
+
: function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
|
2836 |
+
while (start > 0 && check(line.charAt(start - 1))) --start;
|
2837 |
+
while (end < line.length && check(line.charAt(end))) ++end;
|
2838 |
+
}
|
2839 |
+
return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
|
2840 |
+
}
|
2841 |
+
|
2842 |
+
function selectLine(cm, line) {
|
2843 |
+
extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
|
2844 |
+
}
|
2845 |
+
|
2846 |
+
// PROTOTYPE
|
2847 |
+
|
2848 |
+
// The publicly visible API. Note that operation(null, f) means
|
2849 |
+
// 'wrap f in an operation, performed on its `this` parameter'
|
2850 |
+
|
2851 |
+
CodeMirror.prototype = {
|
2852 |
+
constructor: CodeMirror,
|
2853 |
+
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
|
2854 |
+
|
2855 |
+
setOption: function(option, value) {
|
2856 |
+
var options = this.options, old = options[option];
|
2857 |
+
if (options[option] == value && option != "mode") return;
|
2858 |
+
options[option] = value;
|
2859 |
+
if (optionHandlers.hasOwnProperty(option))
|
2860 |
+
operation(this, optionHandlers[option])(this, value, old);
|
2861 |
+
},
|
2862 |
+
|
2863 |
+
getOption: function(option) {return this.options[option];},
|
2864 |
+
getDoc: function() {return this.doc;},
|
2865 |
+
|
2866 |
+
addKeyMap: function(map, bottom) {
|
2867 |
+
this.state.keyMaps[bottom ? "push" : "unshift"](map);
|
2868 |
+
},
|
2869 |
+
removeKeyMap: function(map) {
|
2870 |
+
var maps = this.state.keyMaps;
|
2871 |
+
for (var i = 0; i < maps.length; ++i)
|
2872 |
+
if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) {
|
2873 |
+
maps.splice(i, 1);
|
2874 |
+
return true;
|
2875 |
+
}
|
2876 |
+
},
|
2877 |
+
|
2878 |
+
addOverlay: operation(null, function(spec, options) {
|
2879 |
+
var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
|
2880 |
+
if (mode.startState) throw new Error("Overlays may not be stateful.");
|
2881 |
+
this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
|
2882 |
+
this.state.modeGen++;
|
2883 |
+
regChange(this);
|
2884 |
+
}),
|
2885 |
+
removeOverlay: operation(null, function(spec) {
|
2886 |
+
var overlays = this.state.overlays;
|
2887 |
+
for (var i = 0; i < overlays.length; ++i) {
|
2888 |
+
var cur = overlays[i].modeSpec;
|
2889 |
+
if (cur == spec || typeof spec == "string" && cur.name == spec) {
|
2890 |
+
overlays.splice(i, 1);
|
2891 |
+
this.state.modeGen++;
|
2892 |
+
regChange(this);
|
2893 |
+
return;
|
2894 |
+
}
|
2895 |
+
}
|
2896 |
+
}),
|
2897 |
+
|
2898 |
+
indentLine: operation(null, function(n, dir, aggressive) {
|
2899 |
+
if (typeof dir != "string" && typeof dir != "number") {
|
2900 |
+
if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
|
2901 |
+
else dir = dir ? "add" : "subtract";
|
2902 |
+
}
|
2903 |
+
if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
|
2904 |
+
}),
|
2905 |
+
indentSelection: operation(null, function(how) {
|
2906 |
+
var sel = this.doc.sel;
|
2907 |
+
if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
|
2908 |
+
var e = sel.to.line - (sel.to.ch ? 0 : 1);
|
2909 |
+
for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
|
2910 |
+
}),
|
2911 |
+
|
2912 |
+
// Fetch the parser token for a given character. Useful for hacks
|
2913 |
+
// that want to inspect the mode state (say, for completion).
|
2914 |
+
getTokenAt: function(pos, precise) {
|
2915 |
+
var doc = this.doc;
|
2916 |
+
pos = clipPos(doc, pos);
|
2917 |
+
var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
|
2918 |
+
var line = getLine(doc, pos.line);
|
2919 |
+
var stream = new StringStream(line.text, this.options.tabSize);
|
2920 |
+
while (stream.pos < pos.ch && !stream.eol()) {
|
2921 |
+
stream.start = stream.pos;
|
2922 |
+
var style = mode.token(stream, state);
|
2923 |
+
}
|
2924 |
+
return {start: stream.start,
|
2925 |
+
end: stream.pos,
|
2926 |
+
string: stream.current(),
|
2927 |
+
className: style || null, // Deprecated, use 'type' instead
|
2928 |
+
type: style || null,
|
2929 |
+
state: state};
|
2930 |
+
},
|
2931 |
+
|
2932 |
+
getTokenTypeAt: function(pos) {
|
2933 |
+
pos = clipPos(this.doc, pos);
|
2934 |
+
var styles = getLineStyles(this, getLine(this.doc, pos.line));
|
2935 |
+
var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
|
2936 |
+
if (ch == 0) return styles[2];
|
2937 |
+
for (;;) {
|
2938 |
+
var mid = (before + after) >> 1;
|
2939 |
+
if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
|
2940 |
+
else if (styles[mid * 2 + 1] < ch) before = mid + 1;
|
2941 |
+
else return styles[mid * 2 + 2];
|
2942 |
+
}
|
2943 |
+
},
|
2944 |
+
|
2945 |
+
getModeAt: function(pos) {
|
2946 |
+
var mode = this.doc.mode;
|
2947 |
+
if (!mode.innerMode) return mode;
|
2948 |
+
return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
|
2949 |
+
},
|
2950 |
+
|
2951 |
+
getHelper: function(pos, type) {
|
2952 |
+
if (!helpers.hasOwnProperty(type)) return;
|
2953 |
+
var help = helpers[type], mode = this.getModeAt(pos);
|
2954 |
+
return mode[type] && help[mode[type]] ||
|
2955 |
+
mode.helperType && help[mode.helperType] ||
|
2956 |
+
help[mode.name];
|
2957 |
+
},
|
2958 |
+
|
2959 |
+
getStateAfter: function(line, precise) {
|
2960 |
+
var doc = this.doc;
|
2961 |
+
line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
|
2962 |
+
return getStateBefore(this, line + 1, precise);
|
2963 |
+
},
|
2964 |
+
|
2965 |
+
cursorCoords: function(start, mode) {
|
2966 |
+
var pos, sel = this.doc.sel;
|
2967 |
+
if (start == null) pos = sel.head;
|
2968 |
+
else if (typeof start == "object") pos = clipPos(this.doc, start);
|
2969 |
+
else pos = start ? sel.from : sel.to;
|
2970 |
+
return cursorCoords(this, pos, mode || "page");
|
2971 |
+
},
|
2972 |
+
|
2973 |
+
charCoords: function(pos, mode) {
|
2974 |
+
return charCoords(this, clipPos(this.doc, pos), mode || "page");
|
2975 |
+
},
|
2976 |
+
|
2977 |
+
coordsChar: function(coords, mode) {
|
2978 |
+
coords = fromCoordSystem(this, coords, mode || "page");
|
2979 |
+
return coordsChar(this, coords.left, coords.top);
|
2980 |
+
},
|
2981 |
+
|
2982 |
+
lineAtHeight: function(height, mode) {
|
2983 |
+
height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
|
2984 |
+
return lineAtHeight(this.doc, height + this.display.viewOffset);
|
2985 |
+
},
|
2986 |
+
heightAtLine: function(line, mode) {
|
2987 |
+
var end = false, last = this.doc.first + this.doc.size - 1;
|
2988 |
+
if (line < this.doc.first) line = this.doc.first;
|
2989 |
+
else if (line > last) { line = last; end = true; }
|
2990 |
+
var lineObj = getLine(this.doc, line);
|
2991 |
+
return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
|
2992 |
+
(end ? lineObj.height : 0);
|
2993 |
+
},
|
2994 |
+
|
2995 |
+
defaultTextHeight: function() { return textHeight(this.display); },
|
2996 |
+
defaultCharWidth: function() { return charWidth(this.display); },
|
2997 |
+
|
2998 |
+
setGutterMarker: operation(null, function(line, gutterID, value) {
|
2999 |
+
return changeLine(this, line, function(line) {
|
3000 |
+
var markers = line.gutterMarkers || (line.gutterMarkers = {});
|
3001 |
+
markers[gutterID] = value;
|
3002 |
+
if (!value && isEmpty(markers)) line.gutterMarkers = null;
|
3003 |
+
return true;
|
3004 |
+
});
|
3005 |
+
}),
|
3006 |
+
|
3007 |
+
clearGutter: operation(null, function(gutterID) {
|
3008 |
+
var cm = this, doc = cm.doc, i = doc.first;
|
3009 |
+
doc.iter(function(line) {
|
3010 |
+
if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
|
3011 |
+
line.gutterMarkers[gutterID] = null;
|
3012 |
+
regChange(cm, i, i + 1);
|
3013 |
+
if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
|
3014 |
+
}
|
3015 |
+
++i;
|
3016 |
+
});
|
3017 |
+
}),
|
3018 |
+
|
3019 |
+
addLineClass: operation(null, function(handle, where, cls) {
|
3020 |
+
return changeLine(this, handle, function(line) {
|
3021 |
+
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
|
3022 |
+
if (!line[prop]) line[prop] = cls;
|
3023 |
+
else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
|
3024 |
+
else line[prop] += " " + cls;
|
3025 |
+
return true;
|
3026 |
+
});
|
3027 |
+
}),
|
3028 |
+
|
3029 |
+
removeLineClass: operation(null, function(handle, where, cls) {
|
3030 |
+
return changeLine(this, handle, function(line) {
|
3031 |
+
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
|
3032 |
+
var cur = line[prop];
|
3033 |
+
if (!cur) return false;
|
3034 |
+
else if (cls == null) line[prop] = null;
|
3035 |
+
else {
|
3036 |
+
var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
|
3037 |
+
if (!found) return false;
|
3038 |
+
var end = found.index + found[0].length;
|
3039 |
+
line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
|
3040 |
+
}
|
3041 |
+
return true;
|
3042 |
+
});
|
3043 |
+
}),
|
3044 |
+
|
3045 |
+
addLineWidget: operation(null, function(handle, node, options) {
|
3046 |
+
return addLineWidget(this, handle, node, options);
|
3047 |
+
}),
|
3048 |
+
|
3049 |
+
removeLineWidget: function(widget) { widget.clear(); },
|
3050 |
+
|
3051 |
+
lineInfo: function(line) {
|
3052 |
+
if (typeof line == "number") {
|
3053 |
+
if (!isLine(this.doc, line)) return null;
|
3054 |
+
var n = line;
|
3055 |
+
line = getLine(this.doc, line);
|
3056 |
+
if (!line) return null;
|
3057 |
+
} else {
|
3058 |
+
var n = lineNo(line);
|
3059 |
+
if (n == null) return null;
|
3060 |
+
}
|
3061 |
+
return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
|
3062 |
+
textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
|
3063 |
+
widgets: line.widgets};
|
3064 |
+
},
|
3065 |
+
|
3066 |
+
getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
|
3067 |
+
|
3068 |
+
addWidget: function(pos, node, scroll, vert, horiz) {
|
3069 |
+
var display = this.display;
|
3070 |
+
pos = cursorCoords(this, clipPos(this.doc, pos));
|
3071 |
+
var top = pos.bottom, left = pos.left;
|
3072 |
+
node.style.position = "absolute";
|
3073 |
+
display.sizer.appendChild(node);
|
3074 |
+
if (vert == "over") {
|
3075 |
+
top = pos.top;
|
3076 |
+
} else if (vert == "above" || vert == "near") {
|
3077 |
+
var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
|
3078 |
+
hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
|
3079 |
+
// Default to positioning above (if specified and possible); otherwise default to positioning below
|
3080 |
+
if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
|
3081 |
+
top = pos.top - node.offsetHeight;
|
3082 |
+
else if (pos.bottom + node.offsetHeight <= vspace)
|
3083 |
+
top = pos.bottom;
|
3084 |
+
if (left + node.offsetWidth > hspace)
|
3085 |
+
left = hspace - node.offsetWidth;
|
3086 |
+
}
|
3087 |
+
node.style.top = top + "px";
|
3088 |
+
node.style.left = node.style.right = "";
|
3089 |
+
if (horiz == "right") {
|
3090 |
+
left = display.sizer.clientWidth - node.offsetWidth;
|
3091 |
+
node.style.right = "0px";
|
3092 |
+
} else {
|
3093 |
+
if (horiz == "left") left = 0;
|
3094 |
+
else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
|
3095 |
+
node.style.left = left + "px";
|
3096 |
+
}
|
3097 |
+
if (scroll)
|
3098 |
+
scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
|
3099 |
+
},
|
3100 |
+
|
3101 |
+
triggerOnKeyDown: operation(null, onKeyDown),
|
3102 |
+
|
3103 |
+
execCommand: function(cmd) {return commands[cmd](this);},
|
3104 |
+
|
3105 |
+
findPosH: function(from, amount, unit, visually) {
|
3106 |
+
var dir = 1;
|
3107 |
+
if (amount < 0) { dir = -1; amount = -amount; }
|
3108 |
+
for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
|
3109 |
+
cur = findPosH(this.doc, cur, dir, unit, visually);
|
3110 |
+
if (cur.hitSide) break;
|
3111 |
+
}
|
3112 |
+
return cur;
|
3113 |
+
},
|
3114 |
+
|
3115 |
+
moveH: operation(null, function(dir, unit) {
|
3116 |
+
var sel = this.doc.sel, pos;
|
3117 |
+
if (sel.shift || sel.extend || posEq(sel.from, sel.to))
|
3118 |
+
pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
|
3119 |
+
else
|
3120 |
+
pos = dir < 0 ? sel.from : sel.to;
|
3121 |
+
extendSelection(this.doc, pos, pos, dir);
|
3122 |
+
}),
|
3123 |
+
|
3124 |
+
deleteH: operation(null, function(dir, unit) {
|
3125 |
+
var sel = this.doc.sel;
|
3126 |
+
if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
|
3127 |
+
else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
|
3128 |
+
this.curOp.userSelChange = true;
|
3129 |
+
}),
|
3130 |
+
|
3131 |
+
findPosV: function(from, amount, unit, goalColumn) {
|
3132 |
+
var dir = 1, x = goalColumn;
|
3133 |
+
if (amount < 0) { dir = -1; amount = -amount; }
|
3134 |
+
for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
|
3135 |
+
var coords = cursorCoords(this, cur, "div");
|
3136 |
+
if (x == null) x = coords.left;
|
3137 |
+
else coords.left = x;
|
3138 |
+
cur = findPosV(this, coords, dir, unit);
|
3139 |
+
if (cur.hitSide) break;
|
3140 |
+
}
|
3141 |
+
return cur;
|
3142 |
+
},
|
3143 |
+
|
3144 |
+
moveV: operation(null, function(dir, unit) {
|
3145 |
+
var sel = this.doc.sel;
|
3146 |
+
var pos = cursorCoords(this, sel.head, "div");
|
3147 |
+
if (sel.goalColumn != null) pos.left = sel.goalColumn;
|
3148 |
+
var target = findPosV(this, pos, dir, unit);
|
3149 |
+
|
3150 |
+
if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
|
3151 |
+
extendSelection(this.doc, target, target, dir);
|
3152 |
+
sel.goalColumn = pos.left;
|
3153 |
+
}),
|
3154 |
+
|
3155 |
+
toggleOverwrite: function(value) {
|
3156 |
+
if (value != null && value == this.state.overwrite) return;
|
3157 |
+
if (this.state.overwrite = !this.state.overwrite)
|
3158 |
+
this.display.cursor.className += " CodeMirror-overwrite";
|
3159 |
+
else
|
3160 |
+
this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
|
3161 |
+
},
|
3162 |
+
hasFocus: function() { return this.state.focused; },
|
3163 |
+
|
3164 |
+
scrollTo: operation(null, function(x, y) {
|
3165 |
+
updateScrollPos(this, x, y);
|
3166 |
+
}),
|
3167 |
+
getScrollInfo: function() {
|
3168 |
+
var scroller = this.display.scroller, co = scrollerCutOff;
|
3169 |
+
return {left: scroller.scrollLeft, top: scroller.scrollTop,
|
3170 |
+
height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
|
3171 |
+
clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
|
3172 |
+
},
|
3173 |
+
|
3174 |
+
scrollIntoView: operation(null, function(pos, margin) {
|
3175 |
+
if (typeof pos == "number") pos = Pos(pos, 0);
|
3176 |
+
if (!margin) margin = 0;
|
3177 |
+
var coords = pos;
|
3178 |
+
|
3179 |
+
if (!pos || pos.line != null) {
|
3180 |
+
this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
|
3181 |
+
this.curOp.scrollToPosMargin = margin;
|
3182 |
+
coords = cursorCoords(this, this.curOp.scrollToPos);
|
3183 |
+
}
|
3184 |
+
var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
|
3185 |
+
updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
|
3186 |
+
}),
|
3187 |
+
|
3188 |
+
setSize: operation(null, function(width, height) {
|
3189 |
+
function interpret(val) {
|
3190 |
+
return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
|
3191 |
+
}
|
3192 |
+
if (width != null) this.display.wrapper.style.width = interpret(width);
|
3193 |
+
if (height != null) this.display.wrapper.style.height = interpret(height);
|
3194 |
+
if (this.options.lineWrapping)
|
3195 |
+
this.display.measureLineCache.length = this.display.measureLineCachePos = 0;
|
3196 |
+
this.curOp.forceUpdate = true;
|
3197 |
+
}),
|
3198 |
+
|
3199 |
+
operation: function(f){return runInOp(this, f);},
|
3200 |
+
|
3201 |
+
refresh: operation(null, function() {
|
3202 |
+
var badHeight = this.display.cachedTextHeight == null;
|
3203 |
+
clearCaches(this);
|
3204 |
+
updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
|
3205 |
+
regChange(this);
|
3206 |
+
if (badHeight) estimateLineHeights(this);
|
3207 |
+
}),
|
3208 |
+
|
3209 |
+
swapDoc: operation(null, function(doc) {
|
3210 |
+
var old = this.doc;
|
3211 |
+
old.cm = null;
|
3212 |
+
attachDoc(this, doc);
|
3213 |
+
clearCaches(this);
|
3214 |
+
resetInput(this, true);
|
3215 |
+
updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
|
3216 |
+
return old;
|
3217 |
+
}),
|
3218 |
+
|
3219 |
+
getInputField: function(){return this.display.input;},
|
3220 |
+
getWrapperElement: function(){return this.display.wrapper;},
|
3221 |
+
getScrollerElement: function(){return this.display.scroller;},
|
3222 |
+
getGutterElement: function(){return this.display.gutters;}
|
3223 |
+
};
|
3224 |
+
eventMixin(CodeMirror);
|
3225 |
+
|
3226 |
+
// OPTION DEFAULTS
|
3227 |
+
|
3228 |
+
var optionHandlers = CodeMirror.optionHandlers = {};
|
3229 |
+
|
3230 |
+
// The default configuration options.
|
3231 |
+
var defaults = CodeMirror.defaults = {};
|
3232 |
+
|
3233 |
+
function option(name, deflt, handle, notOnInit) {
|
3234 |
+
CodeMirror.defaults[name] = deflt;
|
3235 |
+
if (handle) optionHandlers[name] =
|
3236 |
+
notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
|
3237 |
+
}
|
3238 |
+
|
3239 |
+
var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
|
3240 |
+
|
3241 |
+
// These two are, on init, called from the constructor because they
|
3242 |
+
// have to be initialized before the editor can start at all.
|
3243 |
+
option("value", "", function(cm, val) {
|
3244 |
+
cm.setValue(val);
|
3245 |
+
}, true);
|
3246 |
+
option("mode", null, function(cm, val) {
|
3247 |
+
cm.doc.modeOption = val;
|
3248 |
+
loadMode(cm);
|
3249 |
+
}, true);
|
3250 |
+
|
3251 |
+
option("indentUnit", 2, loadMode, true);
|
3252 |
+
option("indentWithTabs", false);
|
3253 |
+
option("smartIndent", true);
|
3254 |
+
option("tabSize", 4, function(cm) {
|
3255 |
+
loadMode(cm);
|
3256 |
+
clearCaches(cm);
|
3257 |
+
regChange(cm);
|
3258 |
+
}, true);
|
3259 |
+
option("electricChars", true);
|
3260 |
+
option("rtlMoveVisually", !windows);
|
3261 |
+
|
3262 |
+
option("theme", "default", function(cm) {
|
3263 |
+
themeChanged(cm);
|
3264 |
+
guttersChanged(cm);
|
3265 |
+
}, true);
|
3266 |
+
option("keyMap", "default", keyMapChanged);
|
3267 |
+
option("extraKeys", null);
|
3268 |
+
|
3269 |
+
option("onKeyEvent", null);
|
3270 |
+
option("onDragEvent", null);
|
3271 |
+
|
3272 |
+
option("lineWrapping", false, wrappingChanged, true);
|
3273 |
+
option("gutters", [], function(cm) {
|
3274 |
+
setGuttersForLineNumbers(cm.options);
|
3275 |
+
guttersChanged(cm);
|
3276 |
+
}, true);
|
3277 |
+
option("fixedGutter", true, function(cm, val) {
|
3278 |
+
cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
|
3279 |
+
cm.refresh();
|
3280 |
+
}, true);
|
3281 |
+
option("coverGutterNextToScrollbar", false, updateScrollbars, true);
|
3282 |
+
option("lineNumbers", false, function(cm) {
|
3283 |
+
setGuttersForLineNumbers(cm.options);
|
3284 |
+
guttersChanged(cm);
|
3285 |
+
}, true);
|
3286 |
+
option("firstLineNumber", 1, guttersChanged, true);
|
3287 |
+
option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
|
3288 |
+
option("showCursorWhenSelecting", false, updateSelection, true);
|
3289 |
+
|
3290 |
+
option("readOnly", false, function(cm, val) {
|
3291 |
+
if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
|
3292 |
+
else if (!val) resetInput(cm, true);
|
3293 |
+
});
|
3294 |
+
option("dragDrop", true);
|
3295 |
+
|
3296 |
+
option("cursorBlinkRate", 530);
|
3297 |
+
option("cursorScrollMargin", 0);
|
3298 |
+
option("cursorHeight", 1);
|
3299 |
+
option("workTime", 100);
|
3300 |
+
option("workDelay", 100);
|
3301 |
+
option("flattenSpans", true);
|
3302 |
+
option("pollInterval", 100);
|
3303 |
+
option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
|
3304 |
+
option("historyEventDelay", 500);
|
3305 |
+
option("viewportMargin", 10, function(cm){cm.refresh();}, true);
|
3306 |
+
option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
|
3307 |
+
option("crudeMeasuringFrom", 10000);
|
3308 |
+
option("moveInputWithCursor", true, function(cm, val) {
|
3309 |
+
if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
|
3310 |
+
});
|
3311 |
+
|
3312 |
+
option("tabindex", null, function(cm, val) {
|
3313 |
+
cm.display.input.tabIndex = val || "";
|
3314 |
+
});
|
3315 |
+
option("autofocus", null);
|
3316 |
+
|
3317 |
+
// MODE DEFINITION AND QUERYING
|
3318 |
+
|
3319 |
+
// Known modes, by name and by MIME
|
3320 |
+
var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
|
3321 |
+
|
3322 |
+
CodeMirror.defineMode = function(name, mode) {
|
3323 |
+
if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
|
3324 |
+
if (arguments.length > 2) {
|
3325 |
+
mode.dependencies = [];
|
3326 |
+
for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
|
3327 |
+
}
|
3328 |
+
modes[name] = mode;
|
3329 |
+
};
|
3330 |
+
|
3331 |
+
CodeMirror.defineMIME = function(mime, spec) {
|
3332 |
+
mimeModes[mime] = spec;
|
3333 |
+
};
|
3334 |
+
|
3335 |
+
CodeMirror.resolveMode = function(spec) {
|
3336 |
+
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
|
3337 |
+
spec = mimeModes[spec];
|
3338 |
+
} else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
|
3339 |
+
var found = mimeModes[spec.name];
|
3340 |
+
spec = createObj(found, spec);
|
3341 |
+
spec.name = found.name;
|
3342 |
+
} else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
|
3343 |
+
return CodeMirror.resolveMode("application/xml");
|
3344 |
+
}
|
3345 |
+
if (typeof spec == "string") return {name: spec};
|
3346 |
+
else return spec || {name: "null"};
|
3347 |
+
};
|
3348 |
+
|
3349 |
+
CodeMirror.getMode = function(options, spec) {
|
3350 |
+
var spec = CodeMirror.resolveMode(spec);
|
3351 |
+
var mfactory = modes[spec.name];
|
3352 |
+
if (!mfactory) return CodeMirror.getMode(options, "text/plain");
|
3353 |
+
var modeObj = mfactory(options, spec);
|
3354 |
+
if (modeExtensions.hasOwnProperty(spec.name)) {
|
3355 |
+
var exts = modeExtensions[spec.name];
|
3356 |
+
for (var prop in exts) {
|
3357 |
+
if (!exts.hasOwnProperty(prop)) continue;
|
3358 |
+
if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
|
3359 |
+
modeObj[prop] = exts[prop];
|
3360 |
+
}
|
3361 |
+
}
|
3362 |
+
modeObj.name = spec.name;
|
3363 |
+
|
3364 |
+
return modeObj;
|
3365 |
+
};
|
3366 |
+
|
3367 |
+
CodeMirror.defineMode("null", function() {
|
3368 |
+
return {token: function(stream) {stream.skipToEnd();}};
|
3369 |
+
});
|
3370 |
+
CodeMirror.defineMIME("text/plain", "null");
|
3371 |
+
|
3372 |
+
var modeExtensions = CodeMirror.modeExtensions = {};
|
3373 |
+
CodeMirror.extendMode = function(mode, properties) {
|
3374 |
+
var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
|
3375 |
+
copyObj(properties, exts);
|
3376 |
+
};
|
3377 |
+
|
3378 |
+
// EXTENSIONS
|
3379 |
+
|
3380 |
+
CodeMirror.defineExtension = function(name, func) {
|
3381 |
+
CodeMirror.prototype[name] = func;
|
3382 |
+
};
|
3383 |
+
CodeMirror.defineDocExtension = function(name, func) {
|
3384 |
+
Doc.prototype[name] = func;
|
3385 |
+
};
|
3386 |
+
CodeMirror.defineOption = option;
|
3387 |
+
|
3388 |
+
var initHooks = [];
|
3389 |
+
CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
|
3390 |
+
|
3391 |
+
var helpers = CodeMirror.helpers = {};
|
3392 |
+
CodeMirror.registerHelper = function(type, name, value) {
|
3393 |
+
if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {};
|
3394 |
+
helpers[type][name] = value;
|
3395 |
+
};
|
3396 |
+
|
3397 |
+
// UTILITIES
|
3398 |
+
|
3399 |
+
CodeMirror.isWordChar = isWordChar;
|
3400 |
+
|
3401 |
+
// MODE STATE HANDLING
|
3402 |
+
|
3403 |
+
// Utility functions for working with state. Exported because modes
|
3404 |
+
// sometimes need to do this.
|
3405 |
+
function copyState(mode, state) {
|
3406 |
+
if (state === true) return state;
|
3407 |
+
if (mode.copyState) return mode.copyState(state);
|
3408 |
+
var nstate = {};
|
3409 |
+
for (var n in state) {
|
3410 |
+
var val = state[n];
|
3411 |
+
if (val instanceof Array) val = val.concat([]);
|
3412 |
+
nstate[n] = val;
|
3413 |
+
}
|
3414 |
+
return nstate;
|
3415 |
+
}
|
3416 |
+
CodeMirror.copyState = copyState;
|
3417 |
+
|
3418 |
+
function startState(mode, a1, a2) {
|
3419 |
+
return mode.startState ? mode.startState(a1, a2) : true;
|
3420 |
+
}
|
3421 |
+
CodeMirror.startState = startState;
|
3422 |
+
|
3423 |
+
CodeMirror.innerMode = function(mode, state) {
|
3424 |
+
while (mode.innerMode) {
|
3425 |
+
var info = mode.innerMode(state);
|
3426 |
+
if (!info || info.mode == mode) break;
|
3427 |
+
state = info.state;
|
3428 |
+
mode = info.mode;
|
3429 |
+
}
|
3430 |
+
return info || {mode: mode, state: state};
|
3431 |
+
};
|
3432 |
+
|
3433 |
+
// STANDARD COMMANDS
|
3434 |
+
|
3435 |
+
var commands = CodeMirror.commands = {
|
3436 |
+
selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
|
3437 |
+
killLine: function(cm) {
|
3438 |
+
var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
|
3439 |
+
if (!sel && cm.getLine(from.line).length == from.ch)
|
3440 |
+
cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
|
3441 |
+
else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
|
3442 |
+
},
|
3443 |
+
deleteLine: function(cm) {
|
3444 |
+
var l = cm.getCursor().line;
|
3445 |
+
cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
|
3446 |
+
},
|
3447 |
+
delLineLeft: function(cm) {
|
3448 |
+
var cur = cm.getCursor();
|
3449 |
+
cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
|
3450 |
+
},
|
3451 |
+
undo: function(cm) {cm.undo();},
|
3452 |
+
redo: function(cm) {cm.redo();},
|
3453 |
+
goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
|
3454 |
+
goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
|
3455 |
+
goLineStart: function(cm) {
|
3456 |
+
cm.extendSelection(lineStart(cm, cm.getCursor().line));
|
3457 |
+
},
|
3458 |
+
goLineStartSmart: function(cm) {
|
3459 |
+
var cur = cm.getCursor(), start = lineStart(cm, cur.line);
|
3460 |
+
var line = cm.getLineHandle(start.line);
|
3461 |
+
var order = getOrder(line);
|
3462 |
+
if (!order || order[0].level == 0) {
|
3463 |
+
var firstNonWS = Math.max(0, line.text.search(/\S/));
|
3464 |
+
var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
|
3465 |
+
cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
|
3466 |
+
} else cm.extendSelection(start);
|
3467 |
+
},
|
3468 |
+
goLineEnd: function(cm) {
|
3469 |
+
cm.extendSelection(lineEnd(cm, cm.getCursor().line));
|
3470 |
+
},
|
3471 |
+
goLineRight: function(cm) {
|
3472 |
+
var top = cm.charCoords(cm.getCursor(), "div").top + 5;
|
3473 |
+
cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
|
3474 |
+
},
|
3475 |
+
goLineLeft: function(cm) {
|
3476 |
+
var top = cm.charCoords(cm.getCursor(), "div").top + 5;
|
3477 |
+
cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
|
3478 |
+
},
|
3479 |
+
goLineUp: function(cm) {cm.moveV(-1, "line");},
|
3480 |
+
goLineDown: function(cm) {cm.moveV(1, "line");},
|
3481 |
+
goPageUp: function(cm) {cm.moveV(-1, "page");},
|
3482 |
+
goPageDown: function(cm) {cm.moveV(1, "page");},
|
3483 |
+
goCharLeft: function(cm) {cm.moveH(-1, "char");},
|
3484 |
+
goCharRight: function(cm) {cm.moveH(1, "char");},
|
3485 |
+
goColumnLeft: function(cm) {cm.moveH(-1, "column");},
|
3486 |
+
goColumnRight: function(cm) {cm.moveH(1, "column");},
|
3487 |
+
goWordLeft: function(cm) {cm.moveH(-1, "word");},
|
3488 |
+
goGroupRight: function(cm) {cm.moveH(1, "group");},
|
3489 |
+
goGroupLeft: function(cm) {cm.moveH(-1, "group");},
|
3490 |
+
goWordRight: function(cm) {cm.moveH(1, "word");},
|
3491 |
+
delCharBefore: function(cm) {cm.deleteH(-1, "char");},
|
3492 |
+
delCharAfter: function(cm) {cm.deleteH(1, "char");},
|
3493 |
+
delWordBefore: function(cm) {cm.deleteH(-1, "word");},
|
3494 |
+
delWordAfter: function(cm) {cm.deleteH(1, "word");},
|
3495 |
+
delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
|
3496 |
+
delGroupAfter: function(cm) {cm.deleteH(1, "group");},
|
3497 |
+
indentAuto: function(cm) {cm.indentSelection("smart");},
|
3498 |
+
indentMore: function(cm) {cm.indentSelection("add");},
|
3499 |
+
indentLess: function(cm) {cm.indentSelection("subtract");},
|
3500 |
+
insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
|
3501 |
+
defaultTab: function(cm) {
|
3502 |
+
if (cm.somethingSelected()) cm.indentSelection("add");
|
3503 |
+
else cm.replaceSelection("\t", "end", "+input");
|
3504 |
+
},
|
3505 |
+
transposeChars: function(cm) {
|
3506 |
+
var cur = cm.getCursor(), line = cm.getLine(cur.line);
|
3507 |
+
if (cur.ch > 0 && cur.ch < line.length - 1)
|
3508 |
+
cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
|
3509 |
+
Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
|
3510 |
+
},
|
3511 |
+
newlineAndIndent: function(cm) {
|
3512 |
+
operation(cm, function() {
|
3513 |
+
cm.replaceSelection("\n", "end", "+input");
|
3514 |
+
cm.indentLine(cm.getCursor().line, null, true);
|
3515 |
+
})();
|
3516 |
+
},
|
3517 |
+
toggleOverwrite: function(cm) {cm.toggleOverwrite();}
|
3518 |
+
};
|
3519 |
+
|
3520 |
+
// STANDARD KEYMAPS
|
3521 |
+
|
3522 |
+
var keyMap = CodeMirror.keyMap = {};
|
3523 |
+
keyMap.basic = {
|
3524 |
+
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
|
3525 |
+
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
|
3526 |
+
"Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
|
3527 |
+
"Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
|
3528 |
+
};
|
3529 |
+
// Note that the save and find-related commands aren't defined by
|
3530 |
+
// default. Unknown commands are simply ignored.
|
3531 |
+
keyMap.pcDefault = {
|
3532 |
+
"Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
|
3533 |
+
"Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
|
3534 |
+
"Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
|
3535 |
+
"Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
|
3536 |
+
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
|
3537 |
+
"Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
|
3538 |
+
fallthrough: "basic"
|
3539 |
+
};
|
3540 |
+
keyMap.macDefault = {
|
3541 |
+
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
|
3542 |
+
"Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
|
3543 |
+
"Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
|
3544 |
+
"Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
|
3545 |
+
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
|
3546 |
+
"Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
|
3547 |
+
fallthrough: ["basic", "emacsy"]
|
3548 |
+
};
|
3549 |
+
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
|
3550 |
+
keyMap.emacsy = {
|
3551 |
+
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
|
3552 |
+
"Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
|
3553 |
+
"Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
|
3554 |
+
"Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
|
3555 |
+
};
|
3556 |
+
|
3557 |
+
// KEYMAP DISPATCH
|
3558 |
+
|
3559 |
+
function getKeyMap(val) {
|
3560 |
+
if (typeof val == "string") return keyMap[val];
|
3561 |
+
else return val;
|
3562 |
+
}
|
3563 |
+
|
3564 |
+
function lookupKey(name, maps, handle) {
|
3565 |
+
function lookup(map) {
|
3566 |
+
map = getKeyMap(map);
|
3567 |
+
var found = map[name];
|
3568 |
+
if (found === false) return "stop";
|
3569 |
+
if (found != null && handle(found)) return true;
|
3570 |
+
if (map.nofallthrough) return "stop";
|
3571 |
+
|
3572 |
+
var fallthrough = map.fallthrough;
|
3573 |
+
if (fallthrough == null) return false;
|
3574 |
+
if (Object.prototype.toString.call(fallthrough) != "[object Array]")
|
3575 |
+
return lookup(fallthrough);
|
3576 |
+
for (var i = 0, e = fallthrough.length; i < e; ++i) {
|
3577 |
+
var done = lookup(fallthrough[i]);
|
3578 |
+
if (done) return done;
|
3579 |
+
}
|
3580 |
+
return false;
|
3581 |
+
}
|
3582 |
+
|
3583 |
+
for (var i = 0; i < maps.length; ++i) {
|
3584 |
+
var done = lookup(maps[i]);
|
3585 |
+
if (done) return done != "stop";
|
3586 |
+
}
|
3587 |
+
}
|
3588 |
+
function isModifierKey(event) {
|
3589 |
+
var name = keyNames[event.keyCode];
|
3590 |
+
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
|
3591 |
+
}
|
3592 |
+
function keyName(event, noShift) {
|
3593 |
+
if (opera && event.keyCode == 34 && event["char"]) return false;
|
3594 |
+
var name = keyNames[event.keyCode];
|
3595 |
+
if (name == null || event.altGraphKey) return false;
|
3596 |
+
if (event.altKey) name = "Alt-" + name;
|
3597 |
+
if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
|
3598 |
+
if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
|
3599 |
+
if (!noShift && event.shiftKey) name = "Shift-" + name;
|
3600 |
+
return name;
|
3601 |
+
}
|
3602 |
+
CodeMirror.lookupKey = lookupKey;
|
3603 |
+
CodeMirror.isModifierKey = isModifierKey;
|
3604 |
+
CodeMirror.keyName = keyName;
|
3605 |
+
|
3606 |
+
// FROMTEXTAREA
|
3607 |
+
|
3608 |
+
CodeMirror.fromTextArea = function(textarea, options) {
|
3609 |
+
if (!options) options = {};
|
3610 |
+
options.value = textarea.value;
|
3611 |
+
if (!options.tabindex && textarea.tabindex)
|
3612 |
+
options.tabindex = textarea.tabindex;
|
3613 |
+
if (!options.placeholder && textarea.placeholder)
|
3614 |
+
options.placeholder = textarea.placeholder;
|
3615 |
+
// Set autofocus to true if this textarea is focused, or if it has
|
3616 |
+
// autofocus and no other element is focused.
|
3617 |
+
if (options.autofocus == null) {
|
3618 |
+
var hasFocus = document.body;
|
3619 |
+
// doc.activeElement occasionally throws on IE
|
3620 |
+
try { hasFocus = document.activeElement; } catch(e) {}
|
3621 |
+
options.autofocus = hasFocus == textarea ||
|
3622 |
+
textarea.getAttribute("autofocus") != null && hasFocus == document.body;
|
3623 |
+
}
|
3624 |
+
|
3625 |
+
function save() {textarea.value = cm.getValue();}
|
3626 |
+
if (textarea.form) {
|
3627 |
+
on(textarea.form, "submit", save);
|
3628 |
+
// Deplorable hack to make the submit method do the right thing.
|
3629 |
+
if (!options.leaveSubmitMethodAlone) {
|
3630 |
+
var form = textarea.form, realSubmit = form.submit;
|
3631 |
+
try {
|
3632 |
+
var wrappedSubmit = form.submit = function() {
|
3633 |
+
save();
|
3634 |
+
form.submit = realSubmit;
|
3635 |
+
form.submit();
|
3636 |
+
form.submit = wrappedSubmit;
|
3637 |
+
};
|
3638 |
+
} catch(e) {}
|
3639 |
+
}
|
3640 |
+
}
|
3641 |
+
|
3642 |
+
textarea.style.display = "none";
|
3643 |
+
var cm = CodeMirror(function(node) {
|
3644 |
+
textarea.parentNode.insertBefore(node, textarea.nextSibling);
|
3645 |
+
}, options);
|
3646 |
+
cm.save = save;
|
3647 |
+
cm.getTextArea = function() { return textarea; };
|
3648 |
+
cm.toTextArea = function() {
|
3649 |
+
save();
|
3650 |
+
textarea.parentNode.removeChild(cm.getWrapperElement());
|
3651 |
+
textarea.style.display = "";
|
3652 |
+
if (textarea.form) {
|
3653 |
+
off(textarea.form, "submit", save);
|
3654 |
+
if (typeof textarea.form.submit == "function")
|
3655 |
+
textarea.form.submit = realSubmit;
|
3656 |
+
}
|
3657 |
+
};
|
3658 |
+
return cm;
|
3659 |
+
};
|
3660 |
+
|
3661 |
+
// STRING STREAM
|
3662 |
+
|
3663 |
+
// Fed to the mode parsers, provides helper functions to make
|
3664 |
+
// parsers more succinct.
|
3665 |
+
|
3666 |
+
// The character stream used by a mode's parser.
|
3667 |
+
function StringStream(string, tabSize) {
|
3668 |
+
this.pos = this.start = 0;
|
3669 |
+
this.string = string;
|
3670 |
+
this.tabSize = tabSize || 8;
|
3671 |
+
this.lastColumnPos = this.lastColumnValue = 0;
|
3672 |
+
}
|
3673 |
+
|
3674 |
+
StringStream.prototype = {
|
3675 |
+
eol: function() {return this.pos >= this.string.length;},
|
3676 |
+
sol: function() {return this.pos == 0;},
|
3677 |
+
peek: function() {return this.string.charAt(this.pos) || undefined;},
|
3678 |
+
next: function() {
|
3679 |
+
if (this.pos < this.string.length)
|
3680 |
+
return this.string.charAt(this.pos++);
|
3681 |
+
},
|
3682 |
+
eat: function(match) {
|
3683 |
+
var ch = this.string.charAt(this.pos);
|
3684 |
+
if (typeof match == "string") var ok = ch == match;
|
3685 |
+
else var ok = ch && (match.test ? match.test(ch) : match(ch));
|
3686 |
+
if (ok) {++this.pos; return ch;}
|
3687 |
+
},
|
3688 |
+
eatWhile: function(match) {
|
3689 |
+
var start = this.pos;
|
3690 |
+
while (this.eat(match)){}
|
3691 |
+
return this.pos > start;
|
3692 |
+
},
|
3693 |
+
eatSpace: function() {
|
3694 |
+
var start = this.pos;
|
3695 |
+
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
|
3696 |
+
return this.pos > start;
|
3697 |
+
},
|
3698 |
+
skipToEnd: function() {this.pos = this.string.length;},
|
3699 |
+
skipTo: function(ch) {
|
3700 |
+
var found = this.string.indexOf(ch, this.pos);
|
3701 |
+
if (found > -1) {this.pos = found; return true;}
|
3702 |
+
},
|
3703 |
+
backUp: function(n) {this.pos -= n;},
|
3704 |
+
column: function() {
|
3705 |
+
if (this.lastColumnPos < this.start) {
|
3706 |
+
this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
|
3707 |
+
this.lastColumnPos = this.start;
|
3708 |
+
}
|
3709 |
+
return this.lastColumnValue;
|
3710 |
+
},
|
3711 |
+
indentation: function() {return countColumn(this.string, null, this.tabSize);},
|
3712 |
+
match: function(pattern, consume, caseInsensitive) {
|
3713 |
+
if (typeof pattern == "string") {
|
3714 |
+
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
|
3715 |
+
var substr = this.string.substr(this.pos, pattern.length);
|
3716 |
+
if (cased(substr) == cased(pattern)) {
|
3717 |
+
if (consume !== false) this.pos += pattern.length;
|
3718 |
+
return true;
|
3719 |
+
}
|
3720 |
+
} else {
|
3721 |
+
var match = this.string.slice(this.pos).match(pattern);
|
3722 |
+
if (match && match.index > 0) return null;
|
3723 |
+
if (match && consume !== false) this.pos += match[0].length;
|
3724 |
+
return match;
|
3725 |
+
}
|
3726 |
+
},
|
3727 |
+
current: function(){return this.string.slice(this.start, this.pos);}
|
3728 |
+
};
|
3729 |
+
CodeMirror.StringStream = StringStream;
|
3730 |
+
|
3731 |
+
// TEXTMARKERS
|
3732 |
+
|
3733 |
+
function TextMarker(doc, type) {
|
3734 |
+
this.lines = [];
|
3735 |
+
this.type = type;
|
3736 |
+
this.doc = doc;
|
3737 |
+
}
|
3738 |
+
CodeMirror.TextMarker = TextMarker;
|
3739 |
+
eventMixin(TextMarker);
|
3740 |
+
|
3741 |
+
TextMarker.prototype.clear = function() {
|
3742 |
+
if (this.explicitlyCleared) return;
|
3743 |
+
var cm = this.doc.cm, withOp = cm && !cm.curOp;
|
3744 |
+
if (withOp) startOperation(cm);
|
3745 |
+
if (hasHandler(this, "clear")) {
|
3746 |
+
var found = this.find();
|
3747 |
+
if (found) signalLater(this, "clear", found.from, found.to);
|
3748 |
+
}
|
3749 |
+
var min = null, max = null;
|
3750 |
+
for (var i = 0; i < this.lines.length; ++i) {
|
3751 |
+
var line = this.lines[i];
|
3752 |
+
var span = getMarkedSpanFor(line.markedSpans, this);
|
3753 |
+
if (span.to != null) max = lineNo(line);
|
3754 |
+
line.markedSpans = removeMarkedSpan(line.markedSpans, span);
|
3755 |
+
if (span.from != null)
|
3756 |
+
min = lineNo(line);
|
3757 |
+
else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
|
3758 |
+
updateLineHeight(line, textHeight(cm.display));
|
3759 |
+
}
|
3760 |
+
if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
|
3761 |
+
var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
|
3762 |
+
if (len > cm.display.maxLineLength) {
|
3763 |
+
cm.display.maxLine = visual;
|
3764 |
+
cm.display.maxLineLength = len;
|
3765 |
+
cm.display.maxLineChanged = true;
|
3766 |
+
}
|
3767 |
+
}
|
3768 |
+
|
3769 |
+
if (min != null && cm) regChange(cm, min, max + 1);
|
3770 |
+
this.lines.length = 0;
|
3771 |
+
this.explicitlyCleared = true;
|
3772 |
+
if (this.atomic && this.doc.cantEdit) {
|
3773 |
+
this.doc.cantEdit = false;
|
3774 |
+
if (cm) reCheckSelection(cm);
|
3775 |
+
}
|
3776 |
+
if (withOp) endOperation(cm);
|
3777 |
+
};
|
3778 |
+
|
3779 |
+
TextMarker.prototype.find = function() {
|
3780 |
+
var from, to;
|
3781 |
+
for (var i = 0; i < this.lines.length; ++i) {
|
3782 |
+
var line = this.lines[i];
|
3783 |
+
var span = getMarkedSpanFor(line.markedSpans, this);
|
3784 |
+
if (span.from != null || span.to != null) {
|
3785 |
+
var found = lineNo(line);
|
3786 |
+
if (span.from != null) from = Pos(found, span.from);
|
3787 |
+
if (span.to != null) to = Pos(found, span.to);
|
3788 |
+
}
|
3789 |
+
}
|
3790 |
+
if (this.type == "bookmark") return from;
|
3791 |
+
return from && {from: from, to: to};
|
3792 |
+
};
|
3793 |
+
|
3794 |
+
TextMarker.prototype.changed = function() {
|
3795 |
+
var pos = this.find(), cm = this.doc.cm;
|
3796 |
+
if (!pos || !cm) return;
|
3797 |
+
if (this.type != "bookmark") pos = pos.from;
|
3798 |
+
var line = getLine(this.doc, pos.line);
|
3799 |
+
clearCachedMeasurement(cm, line);
|
3800 |
+
if (pos.line >= cm.display.showingFrom && pos.line < cm.display.showingTo) {
|
3801 |
+
for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
|
3802 |
+
if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
|
3803 |
+
break;
|
3804 |
+
}
|
3805 |
+
runInOp(cm, function() {
|
3806 |
+
cm.curOp.selectionChanged = cm.curOp.forceUpdate = cm.curOp.updateMaxLine = true;
|
3807 |
+
});
|
3808 |
+
}
|
3809 |
+
};
|
3810 |
+
|
3811 |
+
TextMarker.prototype.attachLine = function(line) {
|
3812 |
+
if (!this.lines.length && this.doc.cm) {
|
3813 |
+
var op = this.doc.cm.curOp;
|
3814 |
+
if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
|
3815 |
+
(op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
|
3816 |
+
}
|
3817 |
+
this.lines.push(line);
|
3818 |
+
};
|
3819 |
+
TextMarker.prototype.detachLine = function(line) {
|
3820 |
+
this.lines.splice(indexOf(this.lines, line), 1);
|
3821 |
+
if (!this.lines.length && this.doc.cm) {
|
3822 |
+
var op = this.doc.cm.curOp;
|
3823 |
+
(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
|
3824 |
+
}
|
3825 |
+
};
|
3826 |
+
|
3827 |
+
function markText(doc, from, to, options, type) {
|
3828 |
+
if (options && options.shared) return markTextShared(doc, from, to, options, type);
|
3829 |
+
if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
|
3830 |
+
|
3831 |
+
var marker = new TextMarker(doc, type);
|
3832 |
+
if (type == "range" && !posLess(from, to)) return marker;
|
3833 |
+
if (options) copyObj(options, marker);
|
3834 |
+
if (marker.replacedWith) {
|
3835 |
+
marker.collapsed = true;
|
3836 |
+
marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
|
3837 |
+
if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
|
3838 |
+
}
|
3839 |
+
if (marker.collapsed) sawCollapsedSpans = true;
|
3840 |
+
|
3841 |
+
if (marker.addToHistory)
|
3842 |
+
addToHistory(doc, {from: from, to: to, origin: "markText"},
|
3843 |
+
{head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
|
3844 |
+
|
3845 |
+
var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
|
3846 |
+
doc.iter(curLine, to.line + 1, function(line) {
|
3847 |
+
if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
|
3848 |
+
updateMaxLine = true;
|
3849 |
+
var span = {from: null, to: null, marker: marker};
|
3850 |
+
size += line.text.length;
|
3851 |
+
if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
|
3852 |
+
if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
|
3853 |
+
if (marker.collapsed) {
|
3854 |
+
if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
|
3855 |
+
if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
|
3856 |
+
else updateLineHeight(line, 0);
|
3857 |
+
}
|
3858 |
+
addMarkedSpan(line, span);
|
3859 |
+
++curLine;
|
3860 |
+
});
|
3861 |
+
if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
|
3862 |
+
if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
|
3863 |
+
});
|
3864 |
+
|
3865 |
+
if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
|
3866 |
+
|
3867 |
+
if (marker.readOnly) {
|
3868 |
+
sawReadOnlySpans = true;
|
3869 |
+
if (doc.history.done.length || doc.history.undone.length)
|
3870 |
+
doc.clearHistory();
|
3871 |
+
}
|
3872 |
+
if (marker.collapsed) {
|
3873 |
+
if (collapsedAtStart != collapsedAtEnd)
|
3874 |
+
throw new Error("Inserting collapsed marker overlapping an existing one");
|
3875 |
+
marker.size = size;
|
3876 |
+
marker.atomic = true;
|
3877 |
+
}
|
3878 |
+
if (cm) {
|
3879 |
+
if (updateMaxLine) cm.curOp.updateMaxLine = true;
|
3880 |
+
if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed)
|
3881 |
+
regChange(cm, from.line, to.line + 1);
|
3882 |
+
if (marker.atomic) reCheckSelection(cm);
|
3883 |
+
}
|
3884 |
+
return marker;
|
3885 |
+
}
|
3886 |
+
|
3887 |
+
// SHARED TEXTMARKERS
|
3888 |
+
|
3889 |
+
function SharedTextMarker(markers, primary) {
|
3890 |
+
this.markers = markers;
|
3891 |
+
this.primary = primary;
|
3892 |
+
for (var i = 0, me = this; i < markers.length; ++i) {
|
3893 |
+
markers[i].parent = this;
|
3894 |
+
on(markers[i], "clear", function(){me.clear();});
|
3895 |
+
}
|
3896 |
+
}
|
3897 |
+
CodeMirror.SharedTextMarker = SharedTextMarker;
|
3898 |
+
eventMixin(SharedTextMarker);
|
3899 |
+
|
3900 |
+
SharedTextMarker.prototype.clear = function() {
|
3901 |
+
if (this.explicitlyCleared) return;
|
3902 |
+
this.explicitlyCleared = true;
|
3903 |
+
for (var i = 0; i < this.markers.length; ++i)
|
3904 |
+
this.markers[i].clear();
|
3905 |
+
signalLater(this, "clear");
|
3906 |
+
};
|
3907 |
+
SharedTextMarker.prototype.find = function() {
|
3908 |
+
return this.primary.find();
|
3909 |
+
};
|
3910 |
+
|
3911 |
+
function markTextShared(doc, from, to, options, type) {
|
3912 |
+
options = copyObj(options);
|
3913 |
+
options.shared = false;
|
3914 |
+
var markers = [markText(doc, from, to, options, type)], primary = markers[0];
|
3915 |
+
var widget = options.replacedWith;
|
3916 |
+
linkedDocs(doc, function(doc) {
|
3917 |
+
if (widget) options.replacedWith = widget.cloneNode(true);
|
3918 |
+
markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
|
3919 |
+
for (var i = 0; i < doc.linked.length; ++i)
|
3920 |
+
if (doc.linked[i].isParent) return;
|
3921 |
+
primary = lst(markers);
|
3922 |
+
});
|
3923 |
+
return new SharedTextMarker(markers, primary);
|
3924 |
+
}
|
3925 |
+
|
3926 |
+
// TEXTMARKER SPANS
|
3927 |
+
|
3928 |
+
function getMarkedSpanFor(spans, marker) {
|
3929 |
+
if (spans) for (var i = 0; i < spans.length; ++i) {
|
3930 |
+
var span = spans[i];
|
3931 |
+
if (span.marker == marker) return span;
|
3932 |
+
}
|
3933 |
+
}
|
3934 |
+
function removeMarkedSpan(spans, span) {
|
3935 |
+
for (var r, i = 0; i < spans.length; ++i)
|
3936 |
+
if (spans[i] != span) (r || (r = [])).push(spans[i]);
|
3937 |
+
return r;
|
3938 |
+
}
|
3939 |
+
function addMarkedSpan(line, span) {
|
3940 |
+
line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
|
3941 |
+
span.marker.attachLine(line);
|
3942 |
+
}
|
3943 |
+
|
3944 |
+
function markedSpansBefore(old, startCh, isInsert) {
|
3945 |
+
if (old) for (var i = 0, nw; i < old.length; ++i) {
|
3946 |
+
var span = old[i], marker = span.marker;
|
3947 |
+
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
|
3948 |
+
if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
|
3949 |
+
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
|
3950 |
+
(nw || (nw = [])).push({from: span.from,
|
3951 |
+
to: endsAfter ? null : span.to,
|
3952 |
+
marker: marker});
|
3953 |
+
}
|
3954 |
+
}
|
3955 |
+
return nw;
|
3956 |
+
}
|
3957 |
+
|
3958 |
+
function markedSpansAfter(old, endCh, isInsert) {
|
3959 |
+
if (old) for (var i = 0, nw; i < old.length; ++i) {
|
3960 |
+
var span = old[i], marker = span.marker;
|
3961 |
+
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
|
3962 |
+
if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
|
3963 |
+
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
|
3964 |
+
(nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
|
3965 |
+
to: span.to == null ? null : span.to - endCh,
|
3966 |
+
marker: marker});
|
3967 |
+
}
|
3968 |
+
}
|
3969 |
+
return nw;
|
3970 |
+
}
|
3971 |
+
|
3972 |
+
function stretchSpansOverChange(doc, change) {
|
3973 |
+
var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
|
3974 |
+
var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
|
3975 |
+
if (!oldFirst && !oldLast) return null;
|
3976 |
+
|
3977 |
+
var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
|
3978 |
+
// Get the spans that 'stick out' on both sides
|
3979 |
+
var first = markedSpansBefore(oldFirst, startCh, isInsert);
|
3980 |
+
var last = markedSpansAfter(oldLast, endCh, isInsert);
|
3981 |
+
|
3982 |
+
// Next, merge those two ends
|
3983 |
+
var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
|
3984 |
+
if (first) {
|
3985 |
+
// Fix up .to properties of first
|
3986 |
+
for (var i = 0; i < first.length; ++i) {
|
3987 |
+
var span = first[i];
|
3988 |
+
if (span.to == null) {
|
3989 |
+
var found = getMarkedSpanFor(last, span.marker);
|
3990 |
+
if (!found) span.to = startCh;
|
3991 |
+
else if (sameLine) span.to = found.to == null ? null : found.to + offset;
|
3992 |
+
}
|
3993 |
+
}
|
3994 |
+
}
|
3995 |
+
if (last) {
|
3996 |
+
// Fix up .from in last (or move them into first in case of sameLine)
|
3997 |
+
for (var i = 0; i < last.length; ++i) {
|
3998 |
+
var span = last[i];
|
3999 |
+
if (span.to != null) span.to += offset;
|
4000 |
+
if (span.from == null) {
|
4001 |
+
var found = getMarkedSpanFor(first, span.marker);
|
4002 |
+
if (!found) {
|
4003 |
+
span.from = offset;
|
4004 |
+
if (sameLine) (first || (first = [])).push(span);
|
4005 |
+
}
|
4006 |
+
} else {
|
4007 |
+
span.from += offset;
|
4008 |
+
if (sameLine) (first || (first = [])).push(span);
|
4009 |
+
}
|
4010 |
+
}
|
4011 |
+
}
|
4012 |
+
if (sameLine && first) {
|
4013 |
+
// Make sure we didn't create any zero-length spans
|
4014 |
+
for (var i = 0; i < first.length; ++i)
|
4015 |
+
if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
|
4016 |
+
first.splice(i--, 1);
|
4017 |
+
if (!first.length) first = null;
|
4018 |
+
}
|
4019 |
+
|
4020 |
+
var newMarkers = [first];
|
4021 |
+
if (!sameLine) {
|
4022 |
+
// Fill gap with whole-line-spans
|
4023 |
+
var gap = change.text.length - 2, gapMarkers;
|
4024 |
+
if (gap > 0 && first)
|
4025 |
+
for (var i = 0; i < first.length; ++i)
|
4026 |
+
if (first[i].to == null)
|
4027 |
+
(gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
|
4028 |
+
for (var i = 0; i < gap; ++i)
|
4029 |
+
newMarkers.push(gapMarkers);
|
4030 |
+
newMarkers.push(last);
|
4031 |
+
}
|
4032 |
+
return newMarkers;
|
4033 |
+
}
|
4034 |
+
|
4035 |
+
function mergeOldSpans(doc, change) {
|
4036 |
+
var old = getOldSpans(doc, change);
|
4037 |
+
var stretched = stretchSpansOverChange(doc, change);
|
4038 |
+
if (!old) return stretched;
|
4039 |
+
if (!stretched) return old;
|
4040 |
+
|
4041 |
+
for (var i = 0; i < old.length; ++i) {
|
4042 |
+
var oldCur = old[i], stretchCur = stretched[i];
|
4043 |
+
if (oldCur && stretchCur) {
|
4044 |
+
spans: for (var j = 0; j < stretchCur.length; ++j) {
|
4045 |
+
var span = stretchCur[j];
|
4046 |
+
for (var k = 0; k < oldCur.length; ++k)
|
4047 |
+
if (oldCur[k].marker == span.marker) continue spans;
|
4048 |
+
oldCur.push(span);
|
4049 |
+
}
|
4050 |
+
} else if (stretchCur) {
|
4051 |
+
old[i] = stretchCur;
|
4052 |
+
}
|
4053 |
+
}
|
4054 |
+
return old;
|
4055 |
+
}
|
4056 |
+
|
4057 |
+
function removeReadOnlyRanges(doc, from, to) {
|
4058 |
+
var markers = null;
|
4059 |
+
doc.iter(from.line, to.line + 1, function(line) {
|
4060 |
+
if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
|
4061 |
+
var mark = line.markedSpans[i].marker;
|
4062 |
+
if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
|
4063 |
+
(markers || (markers = [])).push(mark);
|
4064 |
+
}
|
4065 |
+
});
|
4066 |
+
if (!markers) return null;
|
4067 |
+
var parts = [{from: from, to: to}];
|
4068 |
+
for (var i = 0; i < markers.length; ++i) {
|
4069 |
+
var mk = markers[i], m = mk.find();
|
4070 |
+
for (var j = 0; j < parts.length; ++j) {
|
4071 |
+
var p = parts[j];
|
4072 |
+
if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
|
4073 |
+
var newParts = [j, 1];
|
4074 |
+
if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
|
4075 |
+
newParts.push({from: p.from, to: m.from});
|
4076 |
+
if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
|
4077 |
+
newParts.push({from: m.to, to: p.to});
|
4078 |
+
parts.splice.apply(parts, newParts);
|
4079 |
+
j += newParts.length - 1;
|
4080 |
+
}
|
4081 |
+
}
|
4082 |
+
return parts;
|
4083 |
+
}
|
4084 |
+
|
4085 |
+
function collapsedSpanAt(line, ch) {
|
4086 |
+
var sps = sawCollapsedSpans && line.markedSpans, found;
|
4087 |
+
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
|
4088 |
+
sp = sps[i];
|
4089 |
+
if (!sp.marker.collapsed) continue;
|
4090 |
+
if ((sp.from == null || sp.from < ch) &&
|
4091 |
+
(sp.to == null || sp.to > ch) &&
|
4092 |
+
(!found || found.width < sp.marker.width))
|
4093 |
+
found = sp.marker;
|
4094 |
+
}
|
4095 |
+
return found;
|
4096 |
+
}
|
4097 |
+
function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
|
4098 |
+
function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
|
4099 |
+
|
4100 |
+
function visualLine(doc, line) {
|
4101 |
+
var merged;
|
4102 |
+
while (merged = collapsedSpanAtStart(line))
|
4103 |
+
line = getLine(doc, merged.find().from.line);
|
4104 |
+
return line;
|
4105 |
+
}
|
4106 |
+
|
4107 |
+
function lineIsHidden(doc, line) {
|
4108 |
+
var sps = sawCollapsedSpans && line.markedSpans;
|
4109 |
+
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
|
4110 |
+
sp = sps[i];
|
4111 |
+
if (!sp.marker.collapsed) continue;
|
4112 |
+
if (sp.from == null) return true;
|
4113 |
+
if (sp.marker.replacedWith) continue;
|
4114 |
+
if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
|
4115 |
+
return true;
|
4116 |
+
}
|
4117 |
+
}
|
4118 |
+
function lineIsHiddenInner(doc, line, span) {
|
4119 |
+
if (span.to == null) {
|
4120 |
+
var end = span.marker.find().to, endLine = getLine(doc, end.line);
|
4121 |
+
return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
|
4122 |
+
}
|
4123 |
+
if (span.marker.inclusiveRight && span.to == line.text.length)
|
4124 |
+
return true;
|
4125 |
+
for (var sp, i = 0; i < line.markedSpans.length; ++i) {
|
4126 |
+
sp = line.markedSpans[i];
|
4127 |
+
if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
|
4128 |
+
(sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
|
4129 |
+
lineIsHiddenInner(doc, line, sp)) return true;
|
4130 |
+
}
|
4131 |
+
}
|
4132 |
+
|
4133 |
+
function detachMarkedSpans(line) {
|
4134 |
+
var spans = line.markedSpans;
|
4135 |
+
if (!spans) return;
|
4136 |
+
for (var i = 0; i < spans.length; ++i)
|
4137 |
+
spans[i].marker.detachLine(line);
|
4138 |
+
line.markedSpans = null;
|
4139 |
+
}
|
4140 |
+
|
4141 |
+
function attachMarkedSpans(line, spans) {
|
4142 |
+
if (!spans) return;
|
4143 |
+
for (var i = 0; i < spans.length; ++i)
|
4144 |
+
spans[i].marker.attachLine(line);
|
4145 |
+
line.markedSpans = spans;
|
4146 |
+
}
|
4147 |
+
|
4148 |
+
// LINE WIDGETS
|
4149 |
+
|
4150 |
+
var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
|
4151 |
+
if (options) for (var opt in options) if (options.hasOwnProperty(opt))
|
4152 |
+
this[opt] = options[opt];
|
4153 |
+
this.cm = cm;
|
4154 |
+
this.node = node;
|
4155 |
+
};
|
4156 |
+
eventMixin(LineWidget);
|
4157 |
+
function widgetOperation(f) {
|
4158 |
+
return function() {
|
4159 |
+
var withOp = !this.cm.curOp;
|
4160 |
+
if (withOp) startOperation(this.cm);
|
4161 |
+
try {var result = f.apply(this, arguments);}
|
4162 |
+
finally {if (withOp) endOperation(this.cm);}
|
4163 |
+
return result;
|
4164 |
+
};
|
4165 |
+
}
|
4166 |
+
LineWidget.prototype.clear = widgetOperation(function() {
|
4167 |
+
var ws = this.line.widgets, no = lineNo(this.line);
|
4168 |
+
if (no == null || !ws) return;
|
4169 |
+
for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
|
4170 |
+
if (!ws.length) this.line.widgets = null;
|
4171 |
+
var aboveVisible = heightAtLine(this.cm, this.line) < this.cm.doc.scrollTop;
|
4172 |
+
updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
|
4173 |
+
if (aboveVisible) addToScrollPos(this.cm, 0, -this.height);
|
4174 |
+
regChange(this.cm, no, no + 1);
|
4175 |
+
});
|
4176 |
+
LineWidget.prototype.changed = widgetOperation(function() {
|
4177 |
+
var oldH = this.height;
|
4178 |
+
this.height = null;
|
4179 |
+
var diff = widgetHeight(this) - oldH;
|
4180 |
+
if (!diff) return;
|
4181 |
+
updateLineHeight(this.line, this.line.height + diff);
|
4182 |
+
var no = lineNo(this.line);
|
4183 |
+
regChange(this.cm, no, no + 1);
|
4184 |
+
});
|
4185 |
+
|
4186 |
+
function widgetHeight(widget) {
|
4187 |
+
if (widget.height != null) return widget.height;
|
4188 |
+
if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
|
4189 |
+
removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
|
4190 |
+
return widget.height = widget.node.offsetHeight;
|
4191 |
+
}
|
4192 |
+
|
4193 |
+
function addLineWidget(cm, handle, node, options) {
|
4194 |
+
var widget = new LineWidget(cm, node, options);
|
4195 |
+
if (widget.noHScroll) cm.display.alignWidgets = true;
|
4196 |
+
changeLine(cm, handle, function(line) {
|
4197 |
+
var widgets = line.widgets || (line.widgets = []);
|
4198 |
+
if (widget.insertAt == null) widgets.push(widget);
|
4199 |
+
else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
|
4200 |
+
widget.line = line;
|
4201 |
+
if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
|
4202 |
+
var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop;
|
4203 |
+
updateLineHeight(line, line.height + widgetHeight(widget));
|
4204 |
+
if (aboveVisible) addToScrollPos(cm, 0, widget.height);
|
4205 |
+
}
|
4206 |
+
return true;
|
4207 |
+
});
|
4208 |
+
return widget;
|
4209 |
+
}
|
4210 |
+
|
4211 |
+
// LINE DATA STRUCTURE
|
4212 |
+
|
4213 |
+
// Line objects. These hold state related to a line, including
|
4214 |
+
// highlighting info (the styles array).
|
4215 |
+
var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
|
4216 |
+
this.text = text;
|
4217 |
+
attachMarkedSpans(this, markedSpans);
|
4218 |
+
this.height = estimateHeight ? estimateHeight(this) : 1;
|
4219 |
+
};
|
4220 |
+
eventMixin(Line);
|
4221 |
+
|
4222 |
+
function updateLine(line, text, markedSpans, estimateHeight) {
|
4223 |
+
line.text = text;
|
4224 |
+
if (line.stateAfter) line.stateAfter = null;
|
4225 |
+
if (line.styles) line.styles = null;
|
4226 |
+
if (line.order != null) line.order = null;
|
4227 |
+
detachMarkedSpans(line);
|
4228 |
+
attachMarkedSpans(line, markedSpans);
|
4229 |
+
var estHeight = estimateHeight ? estimateHeight(line) : 1;
|
4230 |
+
if (estHeight != line.height) updateLineHeight(line, estHeight);
|
4231 |
+
}
|
4232 |
+
|
4233 |
+
function cleanUpLine(line) {
|
4234 |
+
line.parent = null;
|
4235 |
+
detachMarkedSpans(line);
|
4236 |
+
}
|
4237 |
+
|
4238 |
+
// Run the given mode's parser over a line, update the styles
|
4239 |
+
// array, which contains alternating fragments of text and CSS
|
4240 |
+
// classes.
|
4241 |
+
function runMode(cm, text, mode, state, f) {
|
4242 |
+
var flattenSpans = mode.flattenSpans;
|
4243 |
+
if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
|
4244 |
+
var curStart = 0, curStyle = null;
|
4245 |
+
var stream = new StringStream(text, cm.options.tabSize), style;
|
4246 |
+
if (text == "" && mode.blankLine) mode.blankLine(state);
|
4247 |
+
while (!stream.eol()) {
|
4248 |
+
if (stream.pos > cm.options.maxHighlightLength) {
|
4249 |
+
flattenSpans = false;
|
4250 |
+
stream.pos = text.length;
|
4251 |
+
style = null;
|
4252 |
+
} else {
|
4253 |
+
style = mode.token(stream, state);
|
4254 |
+
}
|
4255 |
+
if (!flattenSpans || curStyle != style) {
|
4256 |
+
if (curStart < stream.start) f(stream.start, curStyle);
|
4257 |
+
curStart = stream.start; curStyle = style;
|
4258 |
+
}
|
4259 |
+
stream.start = stream.pos;
|
4260 |
+
}
|
4261 |
+
while (curStart < stream.pos) {
|
4262 |
+
// Webkit seems to refuse to render text nodes longer than 57444 characters
|
4263 |
+
var pos = Math.min(stream.pos, curStart + 50000);
|
4264 |
+
f(pos, curStyle);
|
4265 |
+
curStart = pos;
|
4266 |
+
}
|
4267 |
+
}
|
4268 |
+
|
4269 |
+
function highlightLine(cm, line, state) {
|
4270 |
+
// A styles array always starts with a number identifying the
|
4271 |
+
// mode/overlays that it is based on (for easy invalidation).
|
4272 |
+
var st = [cm.state.modeGen];
|
4273 |
+
// Compute the base array of styles
|
4274 |
+
runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
|
4275 |
+
|
4276 |
+
// Run overlays, adjust style array.
|
4277 |
+
for (var o = 0; o < cm.state.overlays.length; ++o) {
|
4278 |
+
var overlay = cm.state.overlays[o], i = 1, at = 0;
|
4279 |
+
runMode(cm, line.text, overlay.mode, true, function(end, style) {
|
4280 |
+
var start = i;
|
4281 |
+
// Ensure there's a token end at the current position, and that i points at it
|
4282 |
+
while (at < end) {
|
4283 |
+
var i_end = st[i];
|
4284 |
+
if (i_end > end)
|
4285 |
+
st.splice(i, 1, end, st[i+1], i_end);
|
4286 |
+
i += 2;
|
4287 |
+
at = Math.min(end, i_end);
|
4288 |
+
}
|
4289 |
+
if (!style) return;
|
4290 |
+
if (overlay.opaque) {
|
4291 |
+
st.splice(start, i - start, end, style);
|
4292 |
+
i = start + 2;
|
4293 |
+
} else {
|
4294 |
+
for (; start < i; start += 2) {
|
4295 |
+
var cur = st[start+1];
|
4296 |
+
st[start+1] = cur ? cur + " " + style : style;
|
4297 |
+
}
|
4298 |
+
}
|
4299 |
+
});
|
4300 |
+
}
|
4301 |
+
|
4302 |
+
return st;
|
4303 |
+
}
|
4304 |
+
|
4305 |
+
function getLineStyles(cm, line) {
|
4306 |
+
if (!line.styles || line.styles[0] != cm.state.modeGen)
|
4307 |
+
line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
|
4308 |
+
return line.styles;
|
4309 |
+
}
|
4310 |
+
|
4311 |
+
// Lightweight form of highlight -- proceed over this line and
|
4312 |
+
// update state, but don't save a style array.
|
4313 |
+
function processLine(cm, line, state) {
|
4314 |
+
var mode = cm.doc.mode;
|
4315 |
+
var stream = new StringStream(line.text, cm.options.tabSize);
|
4316 |
+
if (line.text == "" && mode.blankLine) mode.blankLine(state);
|
4317 |
+
while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
|
4318 |
+
mode.token(stream, state);
|
4319 |
+
stream.start = stream.pos;
|
4320 |
+
}
|
4321 |
+
}
|
4322 |
+
|
4323 |
+
var styleToClassCache = {};
|
4324 |
+
function interpretTokenStyle(style, builder) {
|
4325 |
+
if (!style) return null;
|
4326 |
+
for (;;) {
|
4327 |
+
var lineClass = style.match(/(?:^|\s)line-(background-)?(\S+)/);
|
4328 |
+
if (!lineClass) break;
|
4329 |
+
style = style.slice(0, lineClass.index) + style.slice(lineClass.index + lineClass[0].length);
|
4330 |
+
var prop = lineClass[1] ? "bgClass" : "textClass";
|
4331 |
+
if (builder[prop] == null)
|
4332 |
+
builder[prop] = lineClass[2];
|
4333 |
+
else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(builder[prop]))
|
4334 |
+
builder[prop] += " " + lineClass[2];
|
4335 |
+
}
|
4336 |
+
return styleToClassCache[style] ||
|
4337 |
+
(styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
|
4338 |
+
}
|
4339 |
+
|
4340 |
+
function buildLineContent(cm, realLine, measure, copyWidgets) {
|
4341 |
+
var merged, line = realLine, empty = true;
|
4342 |
+
while (merged = collapsedSpanAtStart(line))
|
4343 |
+
line = getLine(cm.doc, merged.find().from.line);
|
4344 |
+
|
4345 |
+
var builder = {pre: elt("pre"), col: 0, pos: 0,
|
4346 |
+
measure: null, measuredSomething: false, cm: cm,
|
4347 |
+
copyWidgets: copyWidgets};
|
4348 |
+
|
4349 |
+
do {
|
4350 |
+
if (line.text) empty = false;
|
4351 |
+
builder.measure = line == realLine && measure;
|
4352 |
+
builder.pos = 0;
|
4353 |
+
builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
|
4354 |
+
if ((ie || webkit) && cm.getOption("lineWrapping"))
|
4355 |
+
builder.addToken = buildTokenSplitSpaces(builder.addToken);
|
4356 |
+
var next = insertLineContent(line, builder, getLineStyles(cm, line));
|
4357 |
+
if (measure && line == realLine && !builder.measuredSomething) {
|
4358 |
+
measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
|
4359 |
+
builder.measuredSomething = true;
|
4360 |
+
}
|
4361 |
+
if (next) line = getLine(cm.doc, next.to.line);
|
4362 |
+
} while (next);
|
4363 |
+
|
4364 |
+
if (measure && !builder.measuredSomething && !measure[0])
|
4365 |
+
measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
|
4366 |
+
if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
|
4367 |
+
builder.pre.appendChild(document.createTextNode("\u00a0"));
|
4368 |
+
|
4369 |
+
var order;
|
4370 |
+
// Work around problem with the reported dimensions of single-char
|
4371 |
+
// direction spans on IE (issue #1129). See also the comment in
|
4372 |
+
// cursorCoords.
|
4373 |
+
if (measure && ie && (order = getOrder(line))) {
|
4374 |
+
var l = order.length - 1;
|
4375 |
+
if (order[l].from == order[l].to) --l;
|
4376 |
+
var last = order[l], prev = order[l - 1];
|
4377 |
+
if (last.from + 1 == last.to && prev && last.level < prev.level) {
|
4378 |
+
var span = measure[builder.pos - 1];
|
4379 |
+
if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
|
4380 |
+
span.nextSibling);
|
4381 |
+
}
|
4382 |
+
}
|
4383 |
+
|
4384 |
+
var textClass = builder.textClass ? builder.textClass + " " + (realLine.textClass || "") : realLine.textClass;
|
4385 |
+
if (textClass) builder.pre.className = textClass;
|
4386 |
+
|
4387 |
+
signal(cm, "renderLine", cm, realLine, builder.pre);
|
4388 |
+
return builder;
|
4389 |
+
}
|
4390 |
+
|
4391 |
+
var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
|
4392 |
+
function buildToken(builder, text, style, startStyle, endStyle, title) {
|
4393 |
+
if (!text) return;
|
4394 |
+
if (!tokenSpecialChars.test(text)) {
|
4395 |
+
builder.col += text.length;
|
4396 |
+
var content = document.createTextNode(text);
|
4397 |
+
} else {
|
4398 |
+
var content = document.createDocumentFragment(), pos = 0;
|
4399 |
+
while (true) {
|
4400 |
+
tokenSpecialChars.lastIndex = pos;
|
4401 |
+
var m = tokenSpecialChars.exec(text);
|
4402 |
+
var skipped = m ? m.index - pos : text.length - pos;
|
4403 |
+
if (skipped) {
|
4404 |
+
content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
|
4405 |
+
builder.col += skipped;
|
4406 |
+
}
|
4407 |
+
if (!m) break;
|
4408 |
+
pos += skipped + 1;
|
4409 |
+
if (m[0] == "\t") {
|
4410 |
+
var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
|
4411 |
+
content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
|
4412 |
+
builder.col += tabWidth;
|
4413 |
+
} else {
|
4414 |
+
var token = elt("span", "\u2022", "cm-invalidchar");
|
4415 |
+
token.title = "\\u" + m[0].charCodeAt(0).toString(16);
|
4416 |
+
content.appendChild(token);
|
4417 |
+
builder.col += 1;
|
4418 |
+
}
|
4419 |
+
}
|
4420 |
+
}
|
4421 |
+
if (style || startStyle || endStyle || builder.measure) {
|
4422 |
+
var fullStyle = style || "";
|
4423 |
+
if (startStyle) fullStyle += startStyle;
|
4424 |
+
if (endStyle) fullStyle += endStyle;
|
4425 |
+
var token = elt("span", [content], fullStyle);
|
4426 |
+
if (title) token.title = title;
|
4427 |
+
return builder.pre.appendChild(token);
|
4428 |
+
}
|
4429 |
+
builder.pre.appendChild(content);
|
4430 |
+
}
|
4431 |
+
|
4432 |
+
function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
|
4433 |
+
var wrapping = builder.cm.options.lineWrapping;
|
4434 |
+
for (var i = 0; i < text.length; ++i) {
|
4435 |
+
var ch = text.charAt(i), start = i == 0;
|
4436 |
+
if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
|
4437 |
+
ch = text.slice(i, i + 2);
|
4438 |
+
++i;
|
4439 |
+
} else if (i && wrapping && spanAffectsWrapping(text, i)) {
|
4440 |
+
builder.pre.appendChild(elt("wbr"));
|
4441 |
+
}
|
4442 |
+
var old = builder.measure[builder.pos];
|
4443 |
+
var span = builder.measure[builder.pos] =
|
4444 |
+
buildToken(builder, ch, style,
|
4445 |
+
start && startStyle, i == text.length - 1 && endStyle);
|
4446 |
+
if (old) span.leftSide = old.leftSide || old;
|
4447 |
+
// In IE single-space nodes wrap differently than spaces
|
4448 |
+
// embedded in larger text nodes, except when set to
|
4449 |
+
// white-space: normal (issue #1268).
|
4450 |
+
if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
|
4451 |
+
i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
|
4452 |
+
span.style.whiteSpace = "normal";
|
4453 |
+
builder.pos += ch.length;
|
4454 |
+
}
|
4455 |
+
if (text.length) builder.measuredSomething = true;
|
4456 |
+
}
|
4457 |
+
|
4458 |
+
function buildTokenSplitSpaces(inner) {
|
4459 |
+
function split(old) {
|
4460 |
+
var out = " ";
|
4461 |
+
for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
|
4462 |
+
out += " ";
|
4463 |
+
return out;
|
4464 |
+
}
|
4465 |
+
return function(builder, text, style, startStyle, endStyle, title) {
|
4466 |
+
return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title);
|
4467 |
+
};
|
4468 |
+
}
|
4469 |
+
|
4470 |
+
function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
|
4471 |
+
var widget = !ignoreWidget && marker.replacedWith;
|
4472 |
+
if (widget) {
|
4473 |
+
if (builder.copyWidgets) widget = widget.cloneNode(true);
|
4474 |
+
builder.pre.appendChild(widget);
|
4475 |
+
if (builder.measure) {
|
4476 |
+
if (size) {
|
4477 |
+
builder.measure[builder.pos] = widget;
|
4478 |
+
} else {
|
4479 |
+
var elt = zeroWidthElement(builder.cm.display.measure);
|
4480 |
+
if (marker.type == "bookmark" && !marker.insertLeft)
|
4481 |
+
builder.measure[builder.pos] = builder.pre.appendChild(elt);
|
4482 |
+
else if (builder.measure[builder.pos])
|
4483 |
+
return;
|
4484 |
+
else
|
4485 |
+
builder.measure[builder.pos] = builder.pre.insertBefore(elt, widget);
|
4486 |
+
}
|
4487 |
+
builder.measuredSomething = true;
|
4488 |
+
}
|
4489 |
+
}
|
4490 |
+
builder.pos += size;
|
4491 |
+
}
|
4492 |
+
|
4493 |
+
// Outputs a number of spans to make up a line, taking highlighting
|
4494 |
+
// and marked text into account.
|
4495 |
+
function insertLineContent(line, builder, styles) {
|
4496 |
+
var spans = line.markedSpans, allText = line.text, at = 0;
|
4497 |
+
if (!spans) {
|
4498 |
+
for (var i = 1; i < styles.length; i+=2)
|
4499 |
+
builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder));
|
4500 |
+
return;
|
4501 |
+
}
|
4502 |
+
|
4503 |
+
var len = allText.length, pos = 0, i = 1, text = "", style;
|
4504 |
+
var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
|
4505 |
+
for (;;) {
|
4506 |
+
if (nextChange == pos) { // Update current marker set
|
4507 |
+
spanStyle = spanEndStyle = spanStartStyle = title = "";
|
4508 |
+
collapsed = null; nextChange = Infinity;
|
4509 |
+
var foundBookmarks = [];
|
4510 |
+
for (var j = 0; j < spans.length; ++j) {
|
4511 |
+
var sp = spans[j], m = sp.marker;
|
4512 |
+
if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
|
4513 |
+
if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
|
4514 |
+
if (m.className) spanStyle += " " + m.className;
|
4515 |
+
if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
|
4516 |
+
if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
|
4517 |
+
if (m.title && !title) title = m.title;
|
4518 |
+
if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
|
4519 |
+
collapsed = sp;
|
4520 |
+
} else if (sp.from > pos && nextChange > sp.from) {
|
4521 |
+
nextChange = sp.from;
|
4522 |
+
}
|
4523 |
+
if (m.type == "bookmark" && sp.from == pos && m.replacedWith) foundBookmarks.push(m);
|
4524 |
+
}
|
4525 |
+
if (collapsed && (collapsed.from || 0) == pos) {
|
4526 |
+
buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
|
4527 |
+
collapsed.marker, collapsed.from == null);
|
4528 |
+
if (collapsed.to == null) return collapsed.marker.find();
|
4529 |
+
}
|
4530 |
+
if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
|
4531 |
+
buildCollapsedSpan(builder, 0, foundBookmarks[j]);
|
4532 |
+
}
|
4533 |
+
if (pos >= len) break;
|
4534 |
+
|
4535 |
+
var upto = Math.min(len, nextChange);
|
4536 |
+
while (true) {
|
4537 |
+
if (text) {
|
4538 |
+
var end = pos + text.length;
|
4539 |
+
if (!collapsed) {
|
4540 |
+
var tokenText = end > upto ? text.slice(0, upto - pos) : text;
|
4541 |
+
builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
|
4542 |
+
spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title);
|
4543 |
+
}
|
4544 |
+
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
|
4545 |
+
pos = end;
|
4546 |
+
spanStartStyle = "";
|
4547 |
+
}
|
4548 |
+
text = allText.slice(at, at = styles[i++]);
|
4549 |
+
style = interpretTokenStyle(styles[i++], builder);
|
4550 |
+
}
|
4551 |
+
}
|
4552 |
+
}
|
4553 |
+
|
4554 |
+
// DOCUMENT DATA STRUCTURE
|
4555 |
+
|
4556 |
+
function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
|
4557 |
+
function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
|
4558 |
+
function update(line, text, spans) {
|
4559 |
+
updateLine(line, text, spans, estimateHeight);
|
4560 |
+
signalLater(line, "change", line, change);
|
4561 |
+
}
|
4562 |
+
|
4563 |
+
var from = change.from, to = change.to, text = change.text;
|
4564 |
+
var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
|
4565 |
+
var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
|
4566 |
+
|
4567 |
+
// First adjust the line structure
|
4568 |
+
if (from.ch == 0 && to.ch == 0 && lastText == "") {
|
4569 |
+
// This is a whole-line replace. Treated specially to make
|
4570 |
+
// sure line objects move the way they are supposed to.
|
4571 |
+
for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
|
4572 |
+
added.push(new Line(text[i], spansFor(i), estimateHeight));
|
4573 |
+
update(lastLine, lastLine.text, lastSpans);
|
4574 |
+
if (nlines) doc.remove(from.line, nlines);
|
4575 |
+
if (added.length) doc.insert(from.line, added);
|
4576 |
+
} else if (firstLine == lastLine) {
|
4577 |
+
if (text.length == 1) {
|
4578 |
+
update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
|
4579 |
+
} else {
|
4580 |
+
for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
|
4581 |
+
added.push(new Line(text[i], spansFor(i), estimateHeight));
|
4582 |
+
added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
|
4583 |
+
update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
|
4584 |
+
doc.insert(from.line + 1, added);
|
4585 |
+
}
|
4586 |
+
} else if (text.length == 1) {
|
4587 |
+
update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
|
4588 |
+
doc.remove(from.line + 1, nlines);
|
4589 |
+
} else {
|
4590 |
+
update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
|
4591 |
+
update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
|
4592 |
+
for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
|
4593 |
+
added.push(new Line(text[i], spansFor(i), estimateHeight));
|
4594 |
+
if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
|
4595 |
+
doc.insert(from.line + 1, added);
|
4596 |
+
}
|
4597 |
+
|
4598 |
+
signalLater(doc, "change", doc, change);
|
4599 |
+
setSelection(doc, selAfter.anchor, selAfter.head, null, true);
|
4600 |
+
}
|
4601 |
+
|
4602 |
+
function LeafChunk(lines) {
|
4603 |
+
this.lines = lines;
|
4604 |
+
this.parent = null;
|
4605 |
+
for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
|
4606 |
+
lines[i].parent = this;
|
4607 |
+
height += lines[i].height;
|
4608 |
+
}
|
4609 |
+
this.height = height;
|
4610 |
+
}
|
4611 |
+
|
4612 |
+
LeafChunk.prototype = {
|
4613 |
+
chunkSize: function() { return this.lines.length; },
|
4614 |
+
removeInner: function(at, n) {
|
4615 |
+
for (var i = at, e = at + n; i < e; ++i) {
|
4616 |
+
var line = this.lines[i];
|
4617 |
+
this.height -= line.height;
|
4618 |
+
cleanUpLine(line);
|
4619 |
+
signalLater(line, "delete");
|
4620 |
+
}
|
4621 |
+
this.lines.splice(at, n);
|
4622 |
+
},
|
4623 |
+
collapse: function(lines) {
|
4624 |
+
lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
|
4625 |
+
},
|
4626 |
+
insertInner: function(at, lines, height) {
|
4627 |
+
this.height += height;
|
4628 |
+
this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
|
4629 |
+
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
|
4630 |
+
},
|
4631 |
+
iterN: function(at, n, op) {
|
4632 |
+
for (var e = at + n; at < e; ++at)
|
4633 |
+
if (op(this.lines[at])) return true;
|
4634 |
+
}
|
4635 |
+
};
|
4636 |
+
|
4637 |
+
function BranchChunk(children) {
|
4638 |
+
this.children = children;
|
4639 |
+
var size = 0, height = 0;
|
4640 |
+
for (var i = 0, e = children.length; i < e; ++i) {
|
4641 |
+
var ch = children[i];
|
4642 |
+
size += ch.chunkSize(); height += ch.height;
|
4643 |
+
ch.parent = this;
|
4644 |
+
}
|
4645 |
+
this.size = size;
|
4646 |
+
this.height = height;
|
4647 |
+
this.parent = null;
|
4648 |
+
}
|
4649 |
+
|
4650 |
+
BranchChunk.prototype = {
|
4651 |
+
chunkSize: function() { return this.size; },
|
4652 |
+
removeInner: function(at, n) {
|
4653 |
+
this.size -= n;
|
4654 |
+
for (var i = 0; i < this.children.length; ++i) {
|
4655 |
+
var child = this.children[i], sz = child.chunkSize();
|
4656 |
+
if (at < sz) {
|
4657 |
+
var rm = Math.min(n, sz - at), oldHeight = child.height;
|
4658 |
+
child.removeInner(at, rm);
|
4659 |
+
this.height -= oldHeight - child.height;
|
4660 |
+
if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
|
4661 |
+
if ((n -= rm) == 0) break;
|
4662 |
+
at = 0;
|
4663 |
+
} else at -= sz;
|
4664 |
+
}
|
4665 |
+
if (this.size - n < 25) {
|
4666 |
+
var lines = [];
|
4667 |
+
this.collapse(lines);
|
4668 |
+
this.children = [new LeafChunk(lines)];
|
4669 |
+
this.children[0].parent = this;
|
4670 |
+
}
|
4671 |
+
},
|
4672 |
+
collapse: function(lines) {
|
4673 |
+
for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
|
4674 |
+
},
|
4675 |
+
insertInner: function(at, lines, height) {
|
4676 |
+
this.size += lines.length;
|
4677 |
+
this.height += height;
|
4678 |
+
for (var i = 0, e = this.children.length; i < e; ++i) {
|
4679 |
+
var child = this.children[i], sz = child.chunkSize();
|
4680 |
+
if (at <= sz) {
|
4681 |
+
child.insertInner(at, lines, height);
|
4682 |
+
if (child.lines && child.lines.length > 50) {
|
4683 |
+
while (child.lines.length > 50) {
|
4684 |
+
var spilled = child.lines.splice(child.lines.length - 25, 25);
|
4685 |
+
var newleaf = new LeafChunk(spilled);
|
4686 |
+
child.height -= newleaf.height;
|
4687 |
+
this.children.splice(i + 1, 0, newleaf);
|
4688 |
+
newleaf.parent = this;
|
4689 |
+
}
|
4690 |
+
this.maybeSpill();
|
4691 |
+
}
|
4692 |
+
break;
|
4693 |
+
}
|
4694 |
+
at -= sz;
|
4695 |
+
}
|
4696 |
+
},
|
4697 |
+
maybeSpill: function() {
|
4698 |
+
if (this.children.length <= 10) return;
|
4699 |
+
var me = this;
|
4700 |
+
do {
|
4701 |
+
var spilled = me.children.splice(me.children.length - 5, 5);
|
4702 |
+
var sibling = new BranchChunk(spilled);
|
4703 |
+
if (!me.parent) { // Become the parent node
|
4704 |
+
var copy = new BranchChunk(me.children);
|
4705 |
+
copy.parent = me;
|
4706 |
+
me.children = [copy, sibling];
|
4707 |
+
me = copy;
|
4708 |
+
} else {
|
4709 |
+
me.size -= sibling.size;
|
4710 |
+
me.height -= sibling.height;
|
4711 |
+
var myIndex = indexOf(me.parent.children, me);
|
4712 |
+
me.parent.children.splice(myIndex + 1, 0, sibling);
|
4713 |
+
}
|
4714 |
+
sibling.parent = me.parent;
|
4715 |
+
} while (me.children.length > 10);
|
4716 |
+
me.parent.maybeSpill();
|
4717 |
+
},
|
4718 |
+
iterN: function(at, n, op) {
|
4719 |
+
for (var i = 0, e = this.children.length; i < e; ++i) {
|
4720 |
+
var child = this.children[i], sz = child.chunkSize();
|
4721 |
+
if (at < sz) {
|
4722 |
+
var used = Math.min(n, sz - at);
|
4723 |
+
if (child.iterN(at, used, op)) return true;
|
4724 |
+
if ((n -= used) == 0) break;
|
4725 |
+
at = 0;
|
4726 |
+
} else at -= sz;
|
4727 |
+
}
|
4728 |
+
}
|
4729 |
+
};
|
4730 |
+
|
4731 |
+
var nextDocId = 0;
|
4732 |
+
var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
|
4733 |
+
if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
|
4734 |
+
if (firstLine == null) firstLine = 0;
|
4735 |
+
|
4736 |
+
BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
|
4737 |
+
this.first = firstLine;
|
4738 |
+
this.scrollTop = this.scrollLeft = 0;
|
4739 |
+
this.cantEdit = false;
|
4740 |
+
this.history = makeHistory();
|
4741 |
+
this.cleanGeneration = 1;
|
4742 |
+
this.frontier = firstLine;
|
4743 |
+
var start = Pos(firstLine, 0);
|
4744 |
+
this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
|
4745 |
+
this.id = ++nextDocId;
|
4746 |
+
this.modeOption = mode;
|
4747 |
+
|
4748 |
+
if (typeof text == "string") text = splitLines(text);
|
4749 |
+
updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
|
4750 |
+
};
|
4751 |
+
|
4752 |
+
Doc.prototype = createObj(BranchChunk.prototype, {
|
4753 |
+
constructor: Doc,
|
4754 |
+
iter: function(from, to, op) {
|
4755 |
+
if (op) this.iterN(from - this.first, to - from, op);
|
4756 |
+
else this.iterN(this.first, this.first + this.size, from);
|
4757 |
+
},
|
4758 |
+
|
4759 |
+
insert: function(at, lines) {
|
4760 |
+
var height = 0;
|
4761 |
+
for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
|
4762 |
+
this.insertInner(at - this.first, lines, height);
|
4763 |
+
},
|
4764 |
+
remove: function(at, n) { this.removeInner(at - this.first, n); },
|
4765 |
+
|
4766 |
+
getValue: function(lineSep) {
|
4767 |
+
var lines = getLines(this, this.first, this.first + this.size);
|
4768 |
+
if (lineSep === false) return lines;
|
4769 |
+
return lines.join(lineSep || "\n");
|
4770 |
+
},
|
4771 |
+
setValue: function(code) {
|
4772 |
+
var top = Pos(this.first, 0), last = this.first + this.size - 1;
|
4773 |
+
makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
|
4774 |
+
text: splitLines(code), origin: "setValue"},
|
4775 |
+
{head: top, anchor: top}, true);
|
4776 |
+
},
|
4777 |
+
replaceRange: function(code, from, to, origin) {
|
4778 |
+
from = clipPos(this, from);
|
4779 |
+
to = to ? clipPos(this, to) : from;
|
4780 |
+
replaceRange(this, code, from, to, origin);
|
4781 |
+
},
|
4782 |
+
getRange: function(from, to, lineSep) {
|
4783 |
+
var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
|
4784 |
+
if (lineSep === false) return lines;
|
4785 |
+
return lines.join(lineSep || "\n");
|
4786 |
+
},
|
4787 |
+
|
4788 |
+
getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
|
4789 |
+
setLine: function(line, text) {
|
4790 |
+
if (isLine(this, line))
|
4791 |
+
replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
|
4792 |
+
},
|
4793 |
+
removeLine: function(line) {
|
4794 |
+
if (line) replaceRange(this, "", clipPos(this, Pos(line - 1)), clipPos(this, Pos(line)));
|
4795 |
+
else replaceRange(this, "", Pos(0, 0), clipPos(this, Pos(1, 0)));
|
4796 |
+
},
|
4797 |
+
|
4798 |
+
getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
|
4799 |
+
getLineNumber: function(line) {return lineNo(line);},
|
4800 |
+
|
4801 |
+
getLineHandleVisualStart: function(line) {
|
4802 |
+
if (typeof line == "number") line = getLine(this, line);
|
4803 |
+
return visualLine(this, line);
|
4804 |
+
},
|
4805 |
+
|
4806 |
+
lineCount: function() {return this.size;},
|
4807 |
+
firstLine: function() {return this.first;},
|
4808 |
+
lastLine: function() {return this.first + this.size - 1;},
|
4809 |
+
|
4810 |
+
clipPos: function(pos) {return clipPos(this, pos);},
|
4811 |
+
|
4812 |
+
getCursor: function(start) {
|
4813 |
+
var sel = this.sel, pos;
|
4814 |
+
if (start == null || start == "head") pos = sel.head;
|
4815 |
+
else if (start == "anchor") pos = sel.anchor;
|
4816 |
+
else if (start == "end" || start === false) pos = sel.to;
|
4817 |
+
else pos = sel.from;
|
4818 |
+
return copyPos(pos);
|
4819 |
+
},
|
4820 |
+
somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
|
4821 |
+
|
4822 |
+
setCursor: docOperation(function(line, ch, extend) {
|
4823 |
+
var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
|
4824 |
+
if (extend) extendSelection(this, pos);
|
4825 |
+
else setSelection(this, pos, pos);
|
4826 |
+
}),
|
4827 |
+
setSelection: docOperation(function(anchor, head, bias) {
|
4828 |
+
setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), bias);
|
4829 |
+
}),
|
4830 |
+
extendSelection: docOperation(function(from, to, bias) {
|
4831 |
+
extendSelection(this, clipPos(this, from), to && clipPos(this, to), bias);
|
4832 |
+
}),
|
4833 |
+
|
4834 |
+
getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
|
4835 |
+
replaceSelection: function(code, collapse, origin) {
|
4836 |
+
makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
|
4837 |
+
},
|
4838 |
+
undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
|
4839 |
+
redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
|
4840 |
+
|
4841 |
+
setExtending: function(val) {this.sel.extend = val;},
|
4842 |
+
|
4843 |
+
historySize: function() {
|
4844 |
+
var hist = this.history;
|
4845 |
+
return {undo: hist.done.length, redo: hist.undone.length};
|
4846 |
+
},
|
4847 |
+
clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
|
4848 |
+
|
4849 |
+
markClean: function() {
|
4850 |
+
this.cleanGeneration = this.changeGeneration();
|
4851 |
+
},
|
4852 |
+
changeGeneration: function() {
|
4853 |
+
this.history.lastOp = this.history.lastOrigin = null;
|
4854 |
+
return this.history.generation;
|
4855 |
+
},
|
4856 |
+
isClean: function (gen) {
|
4857 |
+
return this.history.generation == (gen || this.cleanGeneration);
|
4858 |
+
},
|
4859 |
+
|
4860 |
+
getHistory: function() {
|
4861 |
+
return {done: copyHistoryArray(this.history.done),
|
4862 |
+
undone: copyHistoryArray(this.history.undone)};
|
4863 |
+
},
|
4864 |
+
setHistory: function(histData) {
|
4865 |
+
var hist = this.history = makeHistory(this.history.maxGeneration);
|
4866 |
+
hist.done = histData.done.slice(0);
|
4867 |
+
hist.undone = histData.undone.slice(0);
|
4868 |
+
},
|
4869 |
+
|
4870 |
+
markText: function(from, to, options) {
|
4871 |
+
return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
|
4872 |
+
},
|
4873 |
+
setBookmark: function(pos, options) {
|
4874 |
+
var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
|
4875 |
+
insertLeft: options && options.insertLeft};
|
4876 |
+
pos = clipPos(this, pos);
|
4877 |
+
return markText(this, pos, pos, realOpts, "bookmark");
|
4878 |
+
},
|
4879 |
+
findMarksAt: function(pos) {
|
4880 |
+
pos = clipPos(this, pos);
|
4881 |
+
var markers = [], spans = getLine(this, pos.line).markedSpans;
|
4882 |
+
if (spans) for (var i = 0; i < spans.length; ++i) {
|
4883 |
+
var span = spans[i];
|
4884 |
+
if ((span.from == null || span.from <= pos.ch) &&
|
4885 |
+
(span.to == null || span.to >= pos.ch))
|
4886 |
+
markers.push(span.marker.parent || span.marker);
|
4887 |
+
}
|
4888 |
+
return markers;
|
4889 |
+
},
|
4890 |
+
getAllMarks: function() {
|
4891 |
+
var markers = [];
|
4892 |
+
this.iter(function(line) {
|
4893 |
+
var sps = line.markedSpans;
|
4894 |
+
if (sps) for (var i = 0; i < sps.length; ++i)
|
4895 |
+
if (sps[i].from != null) markers.push(sps[i].marker);
|
4896 |
+
});
|
4897 |
+
return markers;
|
4898 |
+
},
|
4899 |
+
|
4900 |
+
posFromIndex: function(off) {
|
4901 |
+
var ch, lineNo = this.first;
|
4902 |
+
this.iter(function(line) {
|
4903 |
+
var sz = line.text.length + 1;
|
4904 |
+
if (sz > off) { ch = off; return true; }
|
4905 |
+
off -= sz;
|
4906 |
+
++lineNo;
|
4907 |
+
});
|
4908 |
+
return clipPos(this, Pos(lineNo, ch));
|
4909 |
+
},
|
4910 |
+
indexFromPos: function (coords) {
|
4911 |
+
coords = clipPos(this, coords);
|
4912 |
+
var index = coords.ch;
|
4913 |
+
if (coords.line < this.first || coords.ch < 0) return 0;
|
4914 |
+
this.iter(this.first, coords.line, function (line) {
|
4915 |
+
index += line.text.length + 1;
|
4916 |
+
});
|
4917 |
+
return index;
|
4918 |
+
},
|
4919 |
+
|
4920 |
+
copy: function(copyHistory) {
|
4921 |
+
var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
|
4922 |
+
doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
|
4923 |
+
doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
|
4924 |
+
shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
|
4925 |
+
if (copyHistory) {
|
4926 |
+
doc.history.undoDepth = this.history.undoDepth;
|
4927 |
+
doc.setHistory(this.getHistory());
|
4928 |
+
}
|
4929 |
+
return doc;
|
4930 |
+
},
|
4931 |
+
|
4932 |
+
linkedDoc: function(options) {
|
4933 |
+
if (!options) options = {};
|
4934 |
+
var from = this.first, to = this.first + this.size;
|
4935 |
+
if (options.from != null && options.from > from) from = options.from;
|
4936 |
+
if (options.to != null && options.to < to) to = options.to;
|
4937 |
+
var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
|
4938 |
+
if (options.sharedHist) copy.history = this.history;
|
4939 |
+
(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
|
4940 |
+
copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
|
4941 |
+
return copy;
|
4942 |
+
},
|
4943 |
+
unlinkDoc: function(other) {
|
4944 |
+
if (other instanceof CodeMirror) other = other.doc;
|
4945 |
+
if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
|
4946 |
+
var link = this.linked[i];
|
4947 |
+
if (link.doc != other) continue;
|
4948 |
+
this.linked.splice(i, 1);
|
4949 |
+
other.unlinkDoc(this);
|
4950 |
+
break;
|
4951 |
+
}
|
4952 |
+
// If the histories were shared, split them again
|
4953 |
+
if (other.history == this.history) {
|
4954 |
+
var splitIds = [other.id];
|
4955 |
+
linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
|
4956 |
+
other.history = makeHistory();
|
4957 |
+
other.history.done = copyHistoryArray(this.history.done, splitIds);
|
4958 |
+
other.history.undone = copyHistoryArray(this.history.undone, splitIds);
|
4959 |
+
}
|
4960 |
+
},
|
4961 |
+
iterLinkedDocs: function(f) {linkedDocs(this, f);},
|
4962 |
+
|
4963 |
+
getMode: function() {return this.mode;},
|
4964 |
+
getEditor: function() {return this.cm;}
|
4965 |
+
});
|
4966 |
+
|
4967 |
+
Doc.prototype.eachLine = Doc.prototype.iter;
|
4968 |
+
|
4969 |
+
// The Doc methods that should be available on CodeMirror instances
|
4970 |
+
var dontDelegate = "iter insert remove copy getEditor".split(" ");
|
4971 |
+
for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
|
4972 |
+
CodeMirror.prototype[prop] = (function(method) {
|
4973 |
+
return function() {return method.apply(this.doc, arguments);};
|
4974 |
+
})(Doc.prototype[prop]);
|
4975 |
+
|
4976 |
+
eventMixin(Doc);
|
4977 |
+
|
4978 |
+
function linkedDocs(doc, f, sharedHistOnly) {
|
4979 |
+
function propagate(doc, skip, sharedHist) {
|
4980 |
+
if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
|
4981 |
+
var rel = doc.linked[i];
|
4982 |
+
if (rel.doc == skip) continue;
|
4983 |
+
var shared = sharedHist && rel.sharedHist;
|
4984 |
+
if (sharedHistOnly && !shared) continue;
|
4985 |
+
f(rel.doc, shared);
|
4986 |
+
propagate(rel.doc, doc, shared);
|
4987 |
+
}
|
4988 |
+
}
|
4989 |
+
propagate(doc, null, true);
|
4990 |
+
}
|
4991 |
+
|
4992 |
+
function attachDoc(cm, doc) {
|
4993 |
+
if (doc.cm) throw new Error("This document is already in use.");
|
4994 |
+
cm.doc = doc;
|
4995 |
+
doc.cm = cm;
|
4996 |
+
estimateLineHeights(cm);
|
4997 |
+
loadMode(cm);
|
4998 |
+
if (!cm.options.lineWrapping) computeMaxLength(cm);
|
4999 |
+
cm.options.mode = doc.modeOption;
|
5000 |
+
regChange(cm);
|
5001 |
+
}
|
5002 |
+
|
5003 |
+
// LINE UTILITIES
|
5004 |
+
|
5005 |
+
function getLine(chunk, n) {
|
5006 |
+
n -= chunk.first;
|
5007 |
+
while (!chunk.lines) {
|
5008 |
+
for (var i = 0;; ++i) {
|
5009 |
+
var child = chunk.children[i], sz = child.chunkSize();
|
5010 |
+
if (n < sz) { chunk = child; break; }
|
5011 |
+
n -= sz;
|
5012 |
+
}
|
5013 |
+
}
|
5014 |
+
return chunk.lines[n];
|
5015 |
+
}
|
5016 |
+
|
5017 |
+
function getBetween(doc, start, end) {
|
5018 |
+
var out = [], n = start.line;
|
5019 |
+
doc.iter(start.line, end.line + 1, function(line) {
|
5020 |
+
var text = line.text;
|
5021 |
+
if (n == end.line) text = text.slice(0, end.ch);
|
5022 |
+
if (n == start.line) text = text.slice(start.ch);
|
5023 |
+
out.push(text);
|
5024 |
+
++n;
|
5025 |
+
});
|
5026 |
+
return out;
|
5027 |
+
}
|
5028 |
+
function getLines(doc, from, to) {
|
5029 |
+
var out = [];
|
5030 |
+
doc.iter(from, to, function(line) { out.push(line.text); });
|
5031 |
+
return out;
|
5032 |
+
}
|
5033 |
+
|
5034 |
+
function updateLineHeight(line, height) {
|
5035 |
+
var diff = height - line.height;
|
5036 |
+
for (var n = line; n; n = n.parent) n.height += diff;
|
5037 |
+
}
|
5038 |
+
|
5039 |
+
function lineNo(line) {
|
5040 |
+
if (line.parent == null) return null;
|
5041 |
+
var cur = line.parent, no = indexOf(cur.lines, line);
|
5042 |
+
for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
|
5043 |
+
for (var i = 0;; ++i) {
|
5044 |
+
if (chunk.children[i] == cur) break;
|
5045 |
+
no += chunk.children[i].chunkSize();
|
5046 |
+
}
|
5047 |
+
}
|
5048 |
+
return no + cur.first;
|
5049 |
+
}
|
5050 |
+
|
5051 |
+
function lineAtHeight(chunk, h) {
|
5052 |
+
var n = chunk.first;
|
5053 |
+
outer: do {
|
5054 |
+
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
5055 |
+
var child = chunk.children[i], ch = child.height;
|
5056 |
+
if (h < ch) { chunk = child; continue outer; }
|
5057 |
+
h -= ch;
|
5058 |
+
n += child.chunkSize();
|
5059 |
+
}
|
5060 |
+
return n;
|
5061 |
+
} while (!chunk.lines);
|
5062 |
+
for (var i = 0, e = chunk.lines.length; i < e; ++i) {
|
5063 |
+
var line = chunk.lines[i], lh = line.height;
|
5064 |
+
if (h < lh) break;
|
5065 |
+
h -= lh;
|
5066 |
+
}
|
5067 |
+
return n + i;
|
5068 |
+
}
|
5069 |
+
|
5070 |
+
function heightAtLine(cm, lineObj) {
|
5071 |
+
lineObj = visualLine(cm.doc, lineObj);
|
5072 |
+
|
5073 |
+
var h = 0, chunk = lineObj.parent;
|
5074 |
+
for (var i = 0; i < chunk.lines.length; ++i) {
|
5075 |
+
var line = chunk.lines[i];
|
5076 |
+
if (line == lineObj) break;
|
5077 |
+
else h += line.height;
|
5078 |
+
}
|
5079 |
+
for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
|
5080 |
+
for (var i = 0; i < p.children.length; ++i) {
|
5081 |
+
var cur = p.children[i];
|
5082 |
+
if (cur == chunk) break;
|
5083 |
+
else h += cur.height;
|
5084 |
+
}
|
5085 |
+
}
|
5086 |
+
return h;
|
5087 |
+
}
|
5088 |
+
|
5089 |
+
function getOrder(line) {
|
5090 |
+
var order = line.order;
|
5091 |
+
if (order == null) order = line.order = bidiOrdering(line.text);
|
5092 |
+
return order;
|
5093 |
+
}
|
5094 |
+
|
5095 |
+
// HISTORY
|
5096 |
+
|
5097 |
+
function makeHistory(startGen) {
|
5098 |
+
return {
|
5099 |
+
// Arrays of history events. Doing something adds an event to
|
5100 |
+
// done and clears undo. Undoing moves events from done to
|
5101 |
+
// undone, redoing moves them in the other direction.
|
5102 |
+
done: [], undone: [], undoDepth: Infinity,
|
5103 |
+
// Used to track when changes can be merged into a single undo
|
5104 |
+
// event
|
5105 |
+
lastTime: 0, lastOp: null, lastOrigin: null,
|
5106 |
+
// Used by the isClean() method
|
5107 |
+
generation: startGen || 1, maxGeneration: startGen || 1
|
5108 |
+
};
|
5109 |
+
}
|
5110 |
+
|
5111 |
+
function attachLocalSpans(doc, change, from, to) {
|
5112 |
+
var existing = change["spans_" + doc.id], n = 0;
|
5113 |
+
doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
|
5114 |
+
if (line.markedSpans)
|
5115 |
+
(existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
|
5116 |
+
++n;
|
5117 |
+
});
|
5118 |
+
}
|
5119 |
+
|
5120 |
+
function historyChangeFromChange(doc, change) {
|
5121 |
+
var from = { line: change.from.line, ch: change.from.ch };
|
5122 |
+
var histChange = {from: from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
|
5123 |
+
attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
|
5124 |
+
linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
|
5125 |
+
return histChange;
|
5126 |
+
}
|
5127 |
+
|
5128 |
+
function addToHistory(doc, change, selAfter, opId) {
|
5129 |
+
var hist = doc.history;
|
5130 |
+
hist.undone.length = 0;
|
5131 |
+
var time = +new Date, cur = lst(hist.done);
|
5132 |
+
|
5133 |
+
if (cur &&
|
5134 |
+
(hist.lastOp == opId ||
|
5135 |
+
hist.lastOrigin == change.origin && change.origin &&
|
5136 |
+
((change.origin.charAt(0) == "+" && doc.cm && hist.lastTime > time - doc.cm.options.historyEventDelay) ||
|
5137 |
+
change.origin.charAt(0) == "*"))) {
|
5138 |
+
// Merge this change into the last event
|
5139 |
+
var last = lst(cur.changes);
|
5140 |
+
if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
|
5141 |
+
// Optimized case for simple insertion -- don't want to add
|
5142 |
+
// new changesets for every character typed
|
5143 |
+
last.to = changeEnd(change);
|
5144 |
+
} else {
|
5145 |
+
// Add new sub-event
|
5146 |
+
cur.changes.push(historyChangeFromChange(doc, change));
|
5147 |
+
}
|
5148 |
+
cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
|
5149 |
+
} else {
|
5150 |
+
// Can not be merged, start a new event.
|
5151 |
+
cur = {changes: [historyChangeFromChange(doc, change)],
|
5152 |
+
generation: hist.generation,
|
5153 |
+
anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
|
5154 |
+
anchorAfter: selAfter.anchor, headAfter: selAfter.head};
|
5155 |
+
hist.done.push(cur);
|
5156 |
+
hist.generation = ++hist.maxGeneration;
|
5157 |
+
while (hist.done.length > hist.undoDepth)
|
5158 |
+
hist.done.shift();
|
5159 |
+
}
|
5160 |
+
hist.lastTime = time;
|
5161 |
+
hist.lastOp = opId;
|
5162 |
+
hist.lastOrigin = change.origin;
|
5163 |
+
}
|
5164 |
+
|
5165 |
+
function removeClearedSpans(spans) {
|
5166 |
+
if (!spans) return null;
|
5167 |
+
for (var i = 0, out; i < spans.length; ++i) {
|
5168 |
+
if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
|
5169 |
+
else if (out) out.push(spans[i]);
|
5170 |
+
}
|
5171 |
+
return !out ? spans : out.length ? out : null;
|
5172 |
+
}
|
5173 |
+
|
5174 |
+
function getOldSpans(doc, change) {
|
5175 |
+
var found = change["spans_" + doc.id];
|
5176 |
+
if (!found) return null;
|
5177 |
+
for (var i = 0, nw = []; i < change.text.length; ++i)
|
5178 |
+
nw.push(removeClearedSpans(found[i]));
|
5179 |
+
return nw;
|
5180 |
+
}
|
5181 |
+
|
5182 |
+
// Used both to provide a JSON-safe object in .getHistory, and, when
|
5183 |
+
// detaching a document, to split the history in two
|
5184 |
+
function copyHistoryArray(events, newGroup) {
|
5185 |
+
for (var i = 0, copy = []; i < events.length; ++i) {
|
5186 |
+
var event = events[i], changes = event.changes, newChanges = [];
|
5187 |
+
copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
|
5188 |
+
anchorAfter: event.anchorAfter, headAfter: event.headAfter});
|
5189 |
+
for (var j = 0; j < changes.length; ++j) {
|
5190 |
+
var change = changes[j], m;
|
5191 |
+
newChanges.push({from: change.from, to: change.to, text: change.text});
|
5192 |
+
if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
|
5193 |
+
if (indexOf(newGroup, Number(m[1])) > -1) {
|
5194 |
+
lst(newChanges)[prop] = change[prop];
|
5195 |
+
delete change[prop];
|
5196 |
+
}
|
5197 |
+
}
|
5198 |
+
}
|
5199 |
+
}
|
5200 |
+
return copy;
|
5201 |
+
}
|
5202 |
+
|
5203 |
+
// Rebasing/resetting history to deal with externally-sourced changes
|
5204 |
+
|
5205 |
+
function rebaseHistSel(pos, from, to, diff) {
|
5206 |
+
if (to < pos.line) {
|
5207 |
+
pos.line += diff;
|
5208 |
+
} else if (from < pos.line) {
|
5209 |
+
pos.line = from;
|
5210 |
+
pos.ch = 0;
|
5211 |
+
}
|
5212 |
+
}
|
5213 |
+
|
5214 |
+
// Tries to rebase an array of history events given a change in the
|
5215 |
+
// document. If the change touches the same lines as the event, the
|
5216 |
+
// event, and everything 'behind' it, is discarded. If the change is
|
5217 |
+
// before the event, the event's positions are updated. Uses a
|
5218 |
+
// copy-on-write scheme for the positions, to avoid having to
|
5219 |
+
// reallocate them all on every rebase, but also avoid problems with
|
5220 |
+
// shared position objects being unsafely updated.
|
5221 |
+
function rebaseHistArray(array, from, to, diff) {
|
5222 |
+
for (var i = 0; i < array.length; ++i) {
|
5223 |
+
var sub = array[i], ok = true;
|
5224 |
+
for (var j = 0; j < sub.changes.length; ++j) {
|
5225 |
+
var cur = sub.changes[j];
|
5226 |
+
if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
|
5227 |
+
if (to < cur.from.line) {
|
5228 |
+
cur.from.line += diff;
|
5229 |
+
cur.to.line += diff;
|
5230 |
+
} else if (from <= cur.to.line) {
|
5231 |
+
ok = false;
|
5232 |
+
break;
|
5233 |
+
}
|
5234 |
+
}
|
5235 |
+
if (!sub.copied) {
|
5236 |
+
sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
|
5237 |
+
sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
|
5238 |
+
sub.copied = true;
|
5239 |
+
}
|
5240 |
+
if (!ok) {
|
5241 |
+
array.splice(0, i + 1);
|
5242 |
+
i = 0;
|
5243 |
+
} else {
|
5244 |
+
rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
|
5245 |
+
rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
|
5246 |
+
}
|
5247 |
+
}
|
5248 |
+
}
|
5249 |
+
|
5250 |
+
function rebaseHist(hist, change) {
|
5251 |
+
var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
|
5252 |
+
rebaseHistArray(hist.done, from, to, diff);
|
5253 |
+
rebaseHistArray(hist.undone, from, to, diff);
|
5254 |
+
}
|
5255 |
+
|
5256 |
+
// EVENT OPERATORS
|
5257 |
+
|
5258 |
+
function stopMethod() {e_stop(this);}
|
5259 |
+
// Ensure an event has a stop method.
|
5260 |
+
function addStop(event) {
|
5261 |
+
if (!event.stop) event.stop = stopMethod;
|
5262 |
+
return event;
|
5263 |
+
}
|
5264 |
+
|
5265 |
+
function e_preventDefault(e) {
|
5266 |
+
if (e.preventDefault) e.preventDefault();
|
5267 |
+
else e.returnValue = false;
|
5268 |
+
}
|
5269 |
+
function e_stopPropagation(e) {
|
5270 |
+
if (e.stopPropagation) e.stopPropagation();
|
5271 |
+
else e.cancelBubble = true;
|
5272 |
+
}
|
5273 |
+
function e_defaultPrevented(e) {
|
5274 |
+
return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
|
5275 |
+
}
|
5276 |
+
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
|
5277 |
+
CodeMirror.e_stop = e_stop;
|
5278 |
+
CodeMirror.e_preventDefault = e_preventDefault;
|
5279 |
+
CodeMirror.e_stopPropagation = e_stopPropagation;
|
5280 |
+
|
5281 |
+
function e_target(e) {return e.target || e.srcElement;}
|
5282 |
+
function e_button(e) {
|
5283 |
+
var b = e.which;
|
5284 |
+
if (b == null) {
|
5285 |
+
if (e.button & 1) b = 1;
|
5286 |
+
else if (e.button & 2) b = 3;
|
5287 |
+
else if (e.button & 4) b = 2;
|
5288 |
+
}
|
5289 |
+
if (mac && e.ctrlKey && b == 1) b = 3;
|
5290 |
+
return b;
|
5291 |
+
}
|
5292 |
+
|
5293 |
+
// EVENT HANDLING
|
5294 |
+
|
5295 |
+
function on(emitter, type, f) {
|
5296 |
+
if (emitter.addEventListener)
|
5297 |
+
emitter.addEventListener(type, f, false);
|
5298 |
+
else if (emitter.attachEvent)
|
5299 |
+
emitter.attachEvent("on" + type, f);
|
5300 |
+
else {
|
5301 |
+
var map = emitter._handlers || (emitter._handlers = {});
|
5302 |
+
var arr = map[type] || (map[type] = []);
|
5303 |
+
arr.push(f);
|
5304 |
+
}
|
5305 |
+
}
|
5306 |
+
|
5307 |
+
function off(emitter, type, f) {
|
5308 |
+
if (emitter.removeEventListener)
|
5309 |
+
emitter.removeEventListener(type, f, false);
|
5310 |
+
else if (emitter.detachEvent)
|
5311 |
+
emitter.detachEvent("on" + type, f);
|
5312 |
+
else {
|
5313 |
+
var arr = emitter._handlers && emitter._handlers[type];
|
5314 |
+
if (!arr) return;
|
5315 |
+
for (var i = 0; i < arr.length; ++i)
|
5316 |
+
if (arr[i] == f) { arr.splice(i, 1); break; }
|
5317 |
+
}
|
5318 |
+
}
|
5319 |
+
|
5320 |
+
function signal(emitter, type /*, values...*/) {
|
5321 |
+
var arr = emitter._handlers && emitter._handlers[type];
|
5322 |
+
if (!arr) return;
|
5323 |
+
var args = Array.prototype.slice.call(arguments, 2);
|
5324 |
+
for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
|
5325 |
+
}
|
5326 |
+
|
5327 |
+
var delayedCallbacks, delayedCallbackDepth = 0;
|
5328 |
+
function signalLater(emitter, type /*, values...*/) {
|
5329 |
+
var arr = emitter._handlers && emitter._handlers[type];
|
5330 |
+
if (!arr) return;
|
5331 |
+
var args = Array.prototype.slice.call(arguments, 2);
|
5332 |
+
if (!delayedCallbacks) {
|
5333 |
+
++delayedCallbackDepth;
|
5334 |
+
delayedCallbacks = [];
|
5335 |
+
setTimeout(fireDelayed, 0);
|
5336 |
+
}
|
5337 |
+
function bnd(f) {return function(){f.apply(null, args);};};
|
5338 |
+
for (var i = 0; i < arr.length; ++i)
|
5339 |
+
delayedCallbacks.push(bnd(arr[i]));
|
5340 |
+
}
|
5341 |
+
|
5342 |
+
function signalDOMEvent(cm, e, override) {
|
5343 |
+
signal(cm, override || e.type, cm, e);
|
5344 |
+
return e_defaultPrevented(e) || e.codemirrorIgnore;
|
5345 |
+
}
|
5346 |
+
|
5347 |
+
function fireDelayed() {
|
5348 |
+
--delayedCallbackDepth;
|
5349 |
+
var delayed = delayedCallbacks;
|
5350 |
+
delayedCallbacks = null;
|
5351 |
+
for (var i = 0; i < delayed.length; ++i) delayed[i]();
|
5352 |
+
}
|
5353 |
+
|
5354 |
+
function hasHandler(emitter, type) {
|
5355 |
+
var arr = emitter._handlers && emitter._handlers[type];
|
5356 |
+
return arr && arr.length > 0;
|
5357 |
+
}
|
5358 |
+
|
5359 |
+
CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
|
5360 |
+
|
5361 |
+
function eventMixin(ctor) {
|
5362 |
+
ctor.prototype.on = function(type, f) {on(this, type, f);};
|
5363 |
+
ctor.prototype.off = function(type, f) {off(this, type, f);};
|
5364 |
+
}
|
5365 |
+
|
5366 |
+
// MISC UTILITIES
|
5367 |
+
|
5368 |
+
// Number of pixels added to scroller and sizer to hide scrollbar
|
5369 |
+
var scrollerCutOff = 30;
|
5370 |
+
|
5371 |
+
// Returned or thrown by various protocols to signal 'I'm not
|
5372 |
+
// handling this'.
|
5373 |
+
var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
|
5374 |
+
|
5375 |
+
function Delayed() {this.id = null;}
|
5376 |
+
Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
|
5377 |
+
|
5378 |
+
// Counts the column offset in a string, taking tabs into account.
|
5379 |
+
// Used mostly to find indentation.
|
5380 |
+
function countColumn(string, end, tabSize, startIndex, startValue) {
|
5381 |
+
if (end == null) {
|
5382 |
+
end = string.search(/[^\s\u00a0]/);
|
5383 |
+
if (end == -1) end = string.length;
|
5384 |
+
}
|
5385 |
+
for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
|
5386 |
+
if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
|
5387 |
+
else ++n;
|
5388 |
+
}
|
5389 |
+
return n;
|
5390 |
+
}
|
5391 |
+
CodeMirror.countColumn = countColumn;
|
5392 |
+
|
5393 |
+
var spaceStrs = [""];
|
5394 |
+
function spaceStr(n) {
|
5395 |
+
while (spaceStrs.length <= n)
|
5396 |
+
spaceStrs.push(lst(spaceStrs) + " ");
|
5397 |
+
return spaceStrs[n];
|
5398 |
+
}
|
5399 |
+
|
5400 |
+
function lst(arr) { return arr[arr.length-1]; }
|
5401 |
+
|
5402 |
+
function selectInput(node) {
|
5403 |
+
if (ios) { // Mobile Safari apparently has a bug where select() is broken.
|
5404 |
+
node.selectionStart = 0;
|
5405 |
+
node.selectionEnd = node.value.length;
|
5406 |
+
} else {
|
5407 |
+
// Suppress mysterious IE10 errors
|
5408 |
+
try { node.select(); }
|
5409 |
+
catch(_e) {}
|
5410 |
+
}
|
5411 |
+
}
|
5412 |
+
|
5413 |
+
function indexOf(collection, elt) {
|
5414 |
+
if (collection.indexOf) return collection.indexOf(elt);
|
5415 |
+
for (var i = 0, e = collection.length; i < e; ++i)
|
5416 |
+
if (collection[i] == elt) return i;
|
5417 |
+
return -1;
|
5418 |
+
}
|
5419 |
+
|
5420 |
+
function createObj(base, props) {
|
5421 |
+
function Obj() {}
|
5422 |
+
Obj.prototype = base;
|
5423 |
+
var inst = new Obj();
|
5424 |
+
if (props) copyObj(props, inst);
|
5425 |
+
return inst;
|
5426 |
+
}
|
5427 |
+
|
5428 |
+
function copyObj(obj, target) {
|
5429 |
+
if (!target) target = {};
|
5430 |
+
for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
|
5431 |
+
return target;
|
5432 |
+
}
|
5433 |
+
|
5434 |
+
function emptyArray(size) {
|
5435 |
+
for (var a = [], i = 0; i < size; ++i) a.push(undefined);
|
5436 |
+
return a;
|
5437 |
+
}
|
5438 |
+
|
5439 |
+
function bind(f) {
|
5440 |
+
var args = Array.prototype.slice.call(arguments, 1);
|
5441 |
+
return function(){return f.apply(null, args);};
|
5442 |
+
}
|
5443 |
+
|
5444 |
+
var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
|
5445 |
+
function isWordChar(ch) {
|
5446 |
+
return /\w/.test(ch) || ch > "\x80" &&
|
5447 |
+
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
|
5448 |
+
}
|
5449 |
+
|
5450 |
+
function isEmpty(obj) {
|
5451 |
+
for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
|
5452 |
+
return true;
|
5453 |
+
}
|
5454 |
+
|
5455 |
+
var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
|
5456 |
+
|
5457 |
+
// DOM UTILITIES
|
5458 |
+
|
5459 |
+
function elt(tag, content, className, style) {
|
5460 |
+
var e = document.createElement(tag);
|
5461 |
+
if (className) e.className = className;
|
5462 |
+
if (style) e.style.cssText = style;
|
5463 |
+
if (typeof content == "string") setTextContent(e, content);
|
5464 |
+
else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
|
5465 |
+
return e;
|
5466 |
+
}
|
5467 |
+
|
5468 |
+
function removeChildren(e) {
|
5469 |
+
for (var count = e.childNodes.length; count > 0; --count)
|
5470 |
+
e.removeChild(e.firstChild);
|
5471 |
+
return e;
|
5472 |
+
}
|
5473 |
+
|
5474 |
+
function removeChildrenAndAdd(parent, e) {
|
5475 |
+
return removeChildren(parent).appendChild(e);
|
5476 |
+
}
|
5477 |
+
|
5478 |
+
function setTextContent(e, str) {
|
5479 |
+
if (ie_lt9) {
|
5480 |
+
e.innerHTML = "";
|
5481 |
+
e.appendChild(document.createTextNode(str));
|
5482 |
+
} else e.textContent = str;
|
5483 |
+
}
|
5484 |
+
|
5485 |
+
function getRect(node) {
|
5486 |
+
return node.getBoundingClientRect();
|
5487 |
+
}
|
5488 |
+
CodeMirror.replaceGetRect = function(f) { getRect = f; };
|
5489 |
+
|
5490 |
+
// FEATURE DETECTION
|
5491 |
+
|
5492 |
+
// Detect drag-and-drop
|
5493 |
+
var dragAndDrop = function() {
|
5494 |
+
// There is *some* kind of drag-and-drop support in IE6-8, but I
|
5495 |
+
// couldn't get it to work yet.
|
5496 |
+
if (ie_lt9) return false;
|
5497 |
+
var div = elt('div');
|
5498 |
+
return "draggable" in div || "dragDrop" in div;
|
5499 |
+
}();
|
5500 |
+
|
5501 |
+
// For a reason I have yet to figure out, some browsers disallow
|
5502 |
+
// word wrapping between certain characters *only* if a new inline
|
5503 |
+
// element is started between them. This makes it hard to reliably
|
5504 |
+
// measure the position of things, since that requires inserting an
|
5505 |
+
// extra span. This terribly fragile set of tests matches the
|
5506 |
+
// character combinations that suffer from this phenomenon on the
|
5507 |
+
// various browsers.
|
5508 |
+
function spanAffectsWrapping() { return false; }
|
5509 |
+
if (gecko) // Only for "$'"
|
5510 |
+
spanAffectsWrapping = function(str, i) {
|
5511 |
+
return str.charCodeAt(i - 1) == 36 && str.charCodeAt(i) == 39;
|
5512 |
+
};
|
5513 |
+
else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
|
5514 |
+
spanAffectsWrapping = function(str, i) {
|
5515 |
+
return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
|
5516 |
+
};
|
5517 |
+
else if (webkit && /Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent))
|
5518 |
+
spanAffectsWrapping = function(str, i) {
|
5519 |
+
var code = str.charCodeAt(i - 1);
|
5520 |
+
return code >= 8208 && code <= 8212;
|
5521 |
+
};
|
5522 |
+
else if (webkit)
|
5523 |
+
spanAffectsWrapping = function(str, i) {
|
5524 |
+
if (i > 1 && str.charCodeAt(i - 1) == 45) {
|
5525 |
+
if (/\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true;
|
5526 |
+
if (i > 2 && /[\d\.,]/.test(str.charAt(i - 2)) && /[\d\.,]/.test(str.charAt(i))) return false;
|
5527 |
+
}
|
5528 |
+
return /[~!#%&*)=+}\]\\|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
|
5529 |
+
};
|
5530 |
+
|
5531 |
+
var knownScrollbarWidth;
|
5532 |
+
function scrollbarWidth(measure) {
|
5533 |
+
if (knownScrollbarWidth != null) return knownScrollbarWidth;
|
5534 |
+
var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
|
5535 |
+
removeChildrenAndAdd(measure, test);
|
5536 |
+
if (test.offsetWidth)
|
5537 |
+
knownScrollbarWidth = test.offsetHeight - test.clientHeight;
|
5538 |
+
return knownScrollbarWidth || 0;
|
5539 |
+
}
|
5540 |
+
|
5541 |
+
var zwspSupported;
|
5542 |
+
function zeroWidthElement(measure) {
|
5543 |
+
if (zwspSupported == null) {
|
5544 |
+
var test = elt("span", "\u200b");
|
5545 |
+
removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
|
5546 |
+
if (measure.firstChild.offsetHeight != 0)
|
5547 |
+
zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
|
5548 |
+
}
|
5549 |
+
if (zwspSupported) return elt("span", "\u200b");
|
5550 |
+
else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
|
5551 |
+
}
|
5552 |
+
|
5553 |
+
// See if "".split is the broken IE version, if so, provide an
|
5554 |
+
// alternative way to split lines.
|
5555 |
+
var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
|
5556 |
+
var pos = 0, result = [], l = string.length;
|
5557 |
+
while (pos <= l) {
|
5558 |
+
var nl = string.indexOf("\n", pos);
|
5559 |
+
if (nl == -1) nl = string.length;
|
5560 |
+
var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
|
5561 |
+
var rt = line.indexOf("\r");
|
5562 |
+
if (rt != -1) {
|
5563 |
+
result.push(line.slice(0, rt));
|
5564 |
+
pos += rt + 1;
|
5565 |
+
} else {
|
5566 |
+
result.push(line);
|
5567 |
+
pos = nl + 1;
|
5568 |
+
}
|
5569 |
+
}
|
5570 |
+
return result;
|
5571 |
+
} : function(string){return string.split(/\r\n?|\n/);};
|
5572 |
+
CodeMirror.splitLines = splitLines;
|
5573 |
+
|
5574 |
+
var hasSelection = window.getSelection ? function(te) {
|
5575 |
+
try { return te.selectionStart != te.selectionEnd; }
|
5576 |
+
catch(e) { return false; }
|
5577 |
+
} : function(te) {
|
5578 |
+
try {var range = te.ownerDocument.selection.createRange();}
|
5579 |
+
catch(e) {}
|
5580 |
+
if (!range || range.parentElement() != te) return false;
|
5581 |
+
return range.compareEndPoints("StartToEnd", range) != 0;
|
5582 |
+
};
|
5583 |
+
|
5584 |
+
var hasCopyEvent = (function() {
|
5585 |
+
var e = elt("div");
|
5586 |
+
if ("oncopy" in e) return true;
|
5587 |
+
e.setAttribute("oncopy", "return;");
|
5588 |
+
return typeof e.oncopy == 'function';
|
5589 |
+
})();
|
5590 |
+
|
5591 |
+
// KEY NAMING
|
5592 |
+
|
5593 |
+
var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
|
5594 |
+
19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
|
5595 |
+
36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
|
5596 |
+
46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
|
5597 |
+
186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
|
5598 |
+
221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
|
5599 |
+
63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
|
5600 |
+
CodeMirror.keyNames = keyNames;
|
5601 |
+
(function() {
|
5602 |
+
// Number keys
|
5603 |
+
for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
|
5604 |
+
// Alphabetic keys
|
5605 |
+
for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
|
5606 |
+
// Function keys
|
5607 |
+
for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
|
5608 |
+
})();
|
5609 |
+
|
5610 |
+
// BIDI HELPERS
|
5611 |
+
|
5612 |
+
function iterateBidiSections(order, from, to, f) {
|
5613 |
+
if (!order) return f(from, to, "ltr");
|
5614 |
+
var found = false;
|
5615 |
+
for (var i = 0; i < order.length; ++i) {
|
5616 |
+
var part = order[i];
|
5617 |
+
if (part.from < to && part.to > from || from == to && part.to == from) {
|
5618 |
+
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
|
5619 |
+
found = true;
|
5620 |
+
}
|
5621 |
+
}
|
5622 |
+
if (!found) f(from, to, "ltr");
|
5623 |
+
}
|
5624 |
+
|
5625 |
+
function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
|
5626 |
+
function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
|
5627 |
+
|
5628 |
+
function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
|
5629 |
+
function lineRight(line) {
|
5630 |
+
var order = getOrder(line);
|
5631 |
+
if (!order) return line.text.length;
|
5632 |
+
return bidiRight(lst(order));
|
5633 |
+
}
|
5634 |
+
|
5635 |
+
function lineStart(cm, lineN) {
|
5636 |
+
var line = getLine(cm.doc, lineN);
|
5637 |
+
var visual = visualLine(cm.doc, line);
|
5638 |
+
if (visual != line) lineN = lineNo(visual);
|
5639 |
+
var order = getOrder(visual);
|
5640 |
+
var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
|
5641 |
+
return Pos(lineN, ch);
|
5642 |
+
}
|
5643 |
+
function lineEnd(cm, lineN) {
|
5644 |
+
var merged, line;
|
5645 |
+
while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
|
5646 |
+
lineN = merged.find().to.line;
|
5647 |
+
var order = getOrder(line);
|
5648 |
+
var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
|
5649 |
+
return Pos(lineN, ch);
|
5650 |
+
}
|
5651 |
+
|
5652 |
+
function compareBidiLevel(order, a, b) {
|
5653 |
+
var linedir = order[0].level;
|
5654 |
+
if (a == linedir) return true;
|
5655 |
+
if (b == linedir) return false;
|
5656 |
+
return a < b;
|
5657 |
+
}
|
5658 |
+
var bidiOther;
|
5659 |
+
function getBidiPartAt(order, pos) {
|
5660 |
+
for (var i = 0, found; i < order.length; ++i) {
|
5661 |
+
var cur = order[i];
|
5662 |
+
if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
|
5663 |
+
if (cur.from == pos || cur.to == pos) {
|
5664 |
+
if (found == null) {
|
5665 |
+
found = i;
|
5666 |
+
} else if (compareBidiLevel(order, cur.level, order[found].level)) {
|
5667 |
+
bidiOther = found;
|
5668 |
+
return i;
|
5669 |
+
} else {
|
5670 |
+
bidiOther = i;
|
5671 |
+
return found;
|
5672 |
+
}
|
5673 |
+
}
|
5674 |
+
}
|
5675 |
+
bidiOther = null;
|
5676 |
+
return found;
|
5677 |
+
}
|
5678 |
+
|
5679 |
+
function moveInLine(line, pos, dir, byUnit) {
|
5680 |
+
if (!byUnit) return pos + dir;
|
5681 |
+
do pos += dir;
|
5682 |
+
while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
|
5683 |
+
return pos;
|
5684 |
+
}
|
5685 |
+
|
5686 |
+
// This is somewhat involved. It is needed in order to move
|
5687 |
+
// 'visually' through bi-directional text -- i.e., pressing left
|
5688 |
+
// should make the cursor go left, even when in RTL text. The
|
5689 |
+
// tricky part is the 'jumps', where RTL and LTR text touch each
|
5690 |
+
// other. This often requires the cursor offset to move more than
|
5691 |
+
// one unit, in order to visually move one unit.
|
5692 |
+
function moveVisually(line, start, dir, byUnit) {
|
5693 |
+
var bidi = getOrder(line);
|
5694 |
+
if (!bidi) return moveLogically(line, start, dir, byUnit);
|
5695 |
+
var pos = getBidiPartAt(bidi, start), part = bidi[pos];
|
5696 |
+
var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
|
5697 |
+
|
5698 |
+
for (;;) {
|
5699 |
+
if (target > part.from && target < part.to) return target;
|
5700 |
+
if (target == part.from || target == part.to) {
|
5701 |
+
if (getBidiPartAt(bidi, target) == pos) return target;
|
5702 |
+
part = bidi[pos += dir];
|
5703 |
+
return (dir > 0) == part.level % 2 ? part.to : part.from;
|
5704 |
+
} else {
|
5705 |
+
part = bidi[pos += dir];
|
5706 |
+
if (!part) return null;
|
5707 |
+
if ((dir > 0) == part.level % 2)
|
5708 |
+
target = moveInLine(line, part.to, -1, byUnit);
|
5709 |
+
else
|
5710 |
+
target = moveInLine(line, part.from, 1, byUnit);
|
5711 |
+
}
|
5712 |
+
}
|
5713 |
+
}
|
5714 |
+
|
5715 |
+
function moveLogically(line, start, dir, byUnit) {
|
5716 |
+
var target = start + dir;
|
5717 |
+
if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
|
5718 |
+
return target < 0 || target > line.text.length ? null : target;
|
5719 |
+
}
|
5720 |
+
|
5721 |
+
// Bidirectional ordering algorithm
|
5722 |
+
// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
|
5723 |
+
// that this (partially) implements.
|
5724 |
+
|
5725 |
+
// One-char codes used for character types:
|
5726 |
+
// L (L): Left-to-Right
|
5727 |
+
// R (R): Right-to-Left
|
5728 |
+
// r (AL): Right-to-Left Arabic
|
5729 |
+
// 1 (EN): European Number
|
5730 |
+
// + (ES): European Number Separator
|
5731 |
+
// % (ET): European Number Terminator
|
5732 |
+
// n (AN): Arabic Number
|
5733 |
+
// , (CS): Common Number Separator
|
5734 |
+
// m (NSM): Non-Spacing Mark
|
5735 |
+
// b (BN): Boundary Neutral
|
5736 |
+
// s (B): Paragraph Separator
|
5737 |
+
// t (S): Segment Separator
|
5738 |
+
// w (WS): Whitespace
|
5739 |
+
// N (ON): Other Neutrals
|
5740 |
+
|
5741 |
+
// Returns null if characters are ordered as they appear
|
5742 |
+
// (left-to-right), or an array of sections ({from, to, level}
|
5743 |
+
// objects) in the order in which they occur visually.
|
5744 |
+
var bidiOrdering = (function() {
|
5745 |
+
// Character types for codepoints 0 to 0xff
|
5746 |
+
var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
|
5747 |
+
// Character types for codepoints 0x600 to 0x6ff
|
5748 |
+
var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
|
5749 |
+
function charType(code) {
|
5750 |
+
if (code <= 0xff) return lowTypes.charAt(code);
|
5751 |
+
else if (0x590 <= code && code <= 0x5f4) return "R";
|
5752 |
+
else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
|
5753 |
+
else if (0x700 <= code && code <= 0x8ac) return "r";
|
5754 |
+
else return "L";
|
5755 |
+
}
|
5756 |
+
|
5757 |
+
var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
5758 |
+
var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
|
5759 |
+
// Browsers seem to always treat the boundaries of block elements as being L.
|
5760 |
+
var outerType = "L";
|
5761 |
+
|
5762 |
+
return function(str) {
|
5763 |
+
if (!bidiRE.test(str)) return false;
|
5764 |
+
var len = str.length, types = [];
|
5765 |
+
for (var i = 0, type; i < len; ++i)
|
5766 |
+
types.push(type = charType(str.charCodeAt(i)));
|
5767 |
+
|
5768 |
+
// W1. Examine each non-spacing mark (NSM) in the level run, and
|
5769 |
+
// change the type of the NSM to the type of the previous
|
5770 |
+
// character. If the NSM is at the start of the level run, it will
|
5771 |
+
// get the type of sor.
|
5772 |
+
for (var i = 0, prev = outerType; i < len; ++i) {
|
5773 |
+
var type = types[i];
|
5774 |
+
if (type == "m") types[i] = prev;
|
5775 |
+
else prev = type;
|
5776 |
+
}
|
5777 |
+
|
5778 |
+
// W2. Search backwards from each instance of a European number
|
5779 |
+
// until the first strong type (R, L, AL, or sor) is found. If an
|
5780 |
+
// AL is found, change the type of the European number to Arabic
|
5781 |
+
// number.
|
5782 |
+
// W3. Change all ALs to R.
|
5783 |
+
for (var i = 0, cur = outerType; i < len; ++i) {
|
5784 |
+
var type = types[i];
|
5785 |
+
if (type == "1" && cur == "r") types[i] = "n";
|
5786 |
+
else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
|
5787 |
+
}
|
5788 |
+
|
5789 |
+
// W4. A single European separator between two European numbers
|
5790 |
+
// changes to a European number. A single common separator between
|
5791 |
+
// two numbers of the same type changes to that type.
|
5792 |
+
for (var i = 1, prev = types[0]; i < len - 1; ++i) {
|
5793 |
+
var type = types[i];
|
5794 |
+
if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
|
5795 |
+
else if (type == "," && prev == types[i+1] &&
|
5796 |
+
(prev == "1" || prev == "n")) types[i] = prev;
|
5797 |
+
prev = type;
|
5798 |
+
}
|
5799 |
+
|
5800 |
+
// W5. A sequence of European terminators adjacent to European
|
5801 |
+
// numbers changes to all European numbers.
|
5802 |
+
// W6. Otherwise, separators and terminators change to Other
|
5803 |
+
// Neutral.
|
5804 |
+
for (var i = 0; i < len; ++i) {
|
5805 |
+
var type = types[i];
|
5806 |
+
if (type == ",") types[i] = "N";
|
5807 |
+
else if (type == "%") {
|
5808 |
+
for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
|
5809 |
+
var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
|
5810 |
+
for (var j = i; j < end; ++j) types[j] = replace;
|
5811 |
+
i = end - 1;
|
5812 |
+
}
|
5813 |
+
}
|
5814 |
+
|
5815 |
+
// W7. Search backwards from each instance of a European number
|
5816 |
+
// until the first strong type (R, L, or sor) is found. If an L is
|
5817 |
+
// found, then change the type of the European number to L.
|
5818 |
+
for (var i = 0, cur = outerType; i < len; ++i) {
|
5819 |
+
var type = types[i];
|
5820 |
+
if (cur == "L" && type == "1") types[i] = "L";
|
5821 |
+
else if (isStrong.test(type)) cur = type;
|
5822 |
+
}
|
5823 |
+
|
5824 |
+
// N1. A sequence of neutrals takes the direction of the
|
5825 |
+
// surrounding strong text if the text on both sides has the same
|
5826 |
+
// direction. European and Arabic numbers act as if they were R in
|
5827 |
+
// terms of their influence on neutrals. Start-of-level-run (sor)
|
5828 |
+
// and end-of-level-run (eor) are used at level run boundaries.
|
5829 |
+
// N2. Any remaining neutrals take the embedding direction.
|
5830 |
+
for (var i = 0; i < len; ++i) {
|
5831 |
+
if (isNeutral.test(types[i])) {
|
5832 |
+
for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
|
5833 |
+
var before = (i ? types[i-1] : outerType) == "L";
|
5834 |
+
var after = (end < len - 1 ? types[end] : outerType) == "L";
|
5835 |
+
var replace = before || after ? "L" : "R";
|
5836 |
+
for (var j = i; j < end; ++j) types[j] = replace;
|
5837 |
+
i = end - 1;
|
5838 |
+
}
|
5839 |
+
}
|
5840 |
+
|
5841 |
+
// Here we depart from the documented algorithm, in order to avoid
|
5842 |
+
// building up an actual levels array. Since there are only three
|
5843 |
+
// levels (0, 1, 2) in an implementation that doesn't take
|
5844 |
+
// explicit embedding into account, we can build up the order on
|
5845 |
+
// the fly, without following the level-based algorithm.
|
5846 |
+
var order = [], m;
|
5847 |
+
for (var i = 0; i < len;) {
|
5848 |
+
if (countsAsLeft.test(types[i])) {
|
5849 |
+
var start = i;
|
5850 |
+
for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
|
5851 |
+
order.push({from: start, to: i, level: 0});
|
5852 |
+
} else {
|
5853 |
+
var pos = i, at = order.length;
|
5854 |
+
for (++i; i < len && types[i] != "L"; ++i) {}
|
5855 |
+
for (var j = pos; j < i;) {
|
5856 |
+
if (countsAsNum.test(types[j])) {
|
5857 |
+
if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
|
5858 |
+
var nstart = j;
|
5859 |
+
for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
|
5860 |
+
order.splice(at, 0, {from: nstart, to: j, level: 2});
|
5861 |
+
pos = j;
|
5862 |
+
} else ++j;
|
5863 |
+
}
|
5864 |
+
if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
|
5865 |
+
}
|
5866 |
+
}
|
5867 |
+
if (order[0].level == 1 && (m = str.match(/^\s+/))) {
|
5868 |
+
order[0].from = m[0].length;
|
5869 |
+
order.unshift({from: 0, to: m[0].length, level: 0});
|
5870 |
+
}
|
5871 |
+
if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
|
5872 |
+
lst(order).to -= m[0].length;
|
5873 |
+
order.push({from: len - m[0].length, to: len, level: 0});
|
5874 |
+
}
|
5875 |
+
if (order[0].level != lst(order).level)
|
5876 |
+
order.push({from: len, to: len, level: order[0].level});
|
5877 |
+
|
5878 |
+
return order;
|
5879 |
+
};
|
5880 |
+
})();
|
5881 |
+
|
5882 |
+
// THE END
|
5883 |
+
|
5884 |
+
CodeMirror.version = "3.17.0";
|
5885 |
+
|
5886 |
+
return CodeMirror;
|
5887 |
+
})();
|
assets/lib/codemirror/mode/css/css.js
ADDED
@@ -0,0 +1,627 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineMode("css", function(config, parserConfig) {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
|
5 |
+
|
6 |
+
var indentUnit = config.indentUnit,
|
7 |
+
hooks = parserConfig.hooks || {},
|
8 |
+
atMediaTypes = parserConfig.atMediaTypes || {},
|
9 |
+
atMediaFeatures = parserConfig.atMediaFeatures || {},
|
10 |
+
propertyKeywords = parserConfig.propertyKeywords || {},
|
11 |
+
colorKeywords = parserConfig.colorKeywords || {},
|
12 |
+
valueKeywords = parserConfig.valueKeywords || {},
|
13 |
+
allowNested = !!parserConfig.allowNested,
|
14 |
+
type = null;
|
15 |
+
|
16 |
+
function ret(style, tp) { type = tp; return style; }
|
17 |
+
|
18 |
+
function tokenBase(stream, state) {
|
19 |
+
var ch = stream.next();
|
20 |
+
if (hooks[ch]) {
|
21 |
+
// result[0] is style and result[1] is type
|
22 |
+
var result = hooks[ch](stream, state);
|
23 |
+
if (result !== false) return result;
|
24 |
+
}
|
25 |
+
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
|
26 |
+
else if (ch == "=") ret(null, "compare");
|
27 |
+
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
|
28 |
+
else if (ch == "\"" || ch == "'") {
|
29 |
+
state.tokenize = tokenString(ch);
|
30 |
+
return state.tokenize(stream, state);
|
31 |
+
}
|
32 |
+
else if (ch == "#") {
|
33 |
+
stream.eatWhile(/[\w\\\-]/);
|
34 |
+
return ret("atom", "hash");
|
35 |
+
}
|
36 |
+
else if (ch == "!") {
|
37 |
+
stream.match(/^\s*\w*/);
|
38 |
+
return ret("keyword", "important");
|
39 |
+
}
|
40 |
+
else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
|
41 |
+
stream.eatWhile(/[\w.%]/);
|
42 |
+
return ret("number", "unit");
|
43 |
+
}
|
44 |
+
else if (ch === "-") {
|
45 |
+
if (/\d/.test(stream.peek())) {
|
46 |
+
stream.eatWhile(/[\w.%]/);
|
47 |
+
return ret("number", "unit");
|
48 |
+
} else if (stream.match(/^[^-]+-/)) {
|
49 |
+
return ret("meta", "meta");
|
50 |
+
}
|
51 |
+
}
|
52 |
+
else if (/[,+>*\/]/.test(ch)) {
|
53 |
+
return ret(null, "select-op");
|
54 |
+
}
|
55 |
+
else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
|
56 |
+
return ret("qualifier", "qualifier");
|
57 |
+
}
|
58 |
+
else if (ch == ":") {
|
59 |
+
return ret("operator", ch);
|
60 |
+
}
|
61 |
+
else if (/[;{}\[\]\(\)]/.test(ch)) {
|
62 |
+
return ret(null, ch);
|
63 |
+
}
|
64 |
+
else if (ch == "u" && stream.match("rl(")) {
|
65 |
+
stream.backUp(1);
|
66 |
+
state.tokenize = tokenParenthesized;
|
67 |
+
return ret("property", "variable");
|
68 |
+
}
|
69 |
+
else {
|
70 |
+
stream.eatWhile(/[\w\\\-]/);
|
71 |
+
return ret("property", "variable");
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
function tokenString(quote, nonInclusive) {
|
76 |
+
return function(stream, state) {
|
77 |
+
var escaped = false, ch;
|
78 |
+
while ((ch = stream.next()) != null) {
|
79 |
+
if (ch == quote && !escaped)
|
80 |
+
break;
|
81 |
+
escaped = !escaped && ch == "\\";
|
82 |
+
}
|
83 |
+
if (!escaped) {
|
84 |
+
if (nonInclusive) stream.backUp(1);
|
85 |
+
state.tokenize = tokenBase;
|
86 |
+
}
|
87 |
+
return ret("string", "string");
|
88 |
+
};
|
89 |
+
}
|
90 |
+
|
91 |
+
function tokenParenthesized(stream, state) {
|
92 |
+
stream.next(); // Must be '('
|
93 |
+
if (!stream.match(/\s*[\"\']/, false))
|
94 |
+
state.tokenize = tokenString(")", true);
|
95 |
+
else
|
96 |
+
state.tokenize = tokenBase;
|
97 |
+
return ret(null, "(");
|
98 |
+
}
|
99 |
+
|
100 |
+
return {
|
101 |
+
startState: function(base) {
|
102 |
+
return {tokenize: tokenBase,
|
103 |
+
baseIndent: base || 0,
|
104 |
+
stack: [],
|
105 |
+
lastToken: null};
|
106 |
+
},
|
107 |
+
|
108 |
+
token: function(stream, state) {
|
109 |
+
|
110 |
+
// Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
|
111 |
+
//
|
112 |
+
// rule** or **ruleset:
|
113 |
+
// A selector + braces combo, or an at-rule.
|
114 |
+
//
|
115 |
+
// declaration block:
|
116 |
+
// A sequence of declarations.
|
117 |
+
//
|
118 |
+
// declaration:
|
119 |
+
// A property + colon + value combo.
|
120 |
+
//
|
121 |
+
// property value:
|
122 |
+
// The entire value of a property.
|
123 |
+
//
|
124 |
+
// component value:
|
125 |
+
// A single piece of a property value. Like the 5px in
|
126 |
+
// text-shadow: 0 0 5px blue;. Can also refer to things that are
|
127 |
+
// multiple terms, like the 1-4 terms that make up the background-size
|
128 |
+
// portion of the background shorthand.
|
129 |
+
//
|
130 |
+
// term:
|
131 |
+
// The basic unit of author-facing CSS, like a single number (5),
|
132 |
+
// dimension (5px), string ("foo"), or function. Officially defined
|
133 |
+
// by the CSS 2.1 grammar (look for the 'term' production)
|
134 |
+
//
|
135 |
+
//
|
136 |
+
// simple selector:
|
137 |
+
// A single atomic selector, like a type selector, an attr selector, a
|
138 |
+
// class selector, etc.
|
139 |
+
//
|
140 |
+
// compound selector:
|
141 |
+
// One or more simple selectors without a combinator. div.example is
|
142 |
+
// compound, div > .example is not.
|
143 |
+
//
|
144 |
+
// complex selector:
|
145 |
+
// One or more compound selectors chained with combinators.
|
146 |
+
//
|
147 |
+
// combinator:
|
148 |
+
// The parts of selectors that express relationships. There are four
|
149 |
+
// currently - the space (descendant combinator), the greater-than
|
150 |
+
// bracket (child combinator), the plus sign (next sibling combinator),
|
151 |
+
// and the tilda (following sibling combinator).
|
152 |
+
//
|
153 |
+
// sequence of selectors:
|
154 |
+
// One or more of the named type of selector chained with commas.
|
155 |
+
|
156 |
+
state.tokenize = state.tokenize || tokenBase;
|
157 |
+
if (state.tokenize == tokenBase && stream.eatSpace()) return null;
|
158 |
+
var style = state.tokenize(stream, state);
|
159 |
+
if (style && typeof style != "string") style = ret(style[0], style[1]);
|
160 |
+
|
161 |
+
// Changing style returned based on context
|
162 |
+
var context = state.stack[state.stack.length-1];
|
163 |
+
if (style == "variable") {
|
164 |
+
if (type == "variable-definition") state.stack.push("propertyValue");
|
165 |
+
return state.lastToken = "variable-2";
|
166 |
+
} else if (style == "property") {
|
167 |
+
var word = stream.current().toLowerCase();
|
168 |
+
if (context == "propertyValue") {
|
169 |
+
if (valueKeywords.hasOwnProperty(word)) {
|
170 |
+
style = "string-2";
|
171 |
+
} else if (colorKeywords.hasOwnProperty(word)) {
|
172 |
+
style = "keyword";
|
173 |
+
} else {
|
174 |
+
style = "variable-2";
|
175 |
+
}
|
176 |
+
} else if (context == "rule") {
|
177 |
+
if (!propertyKeywords.hasOwnProperty(word)) {
|
178 |
+
style += " error";
|
179 |
+
}
|
180 |
+
} else if (context == "block") {
|
181 |
+
// if a value is present in both property, value, or color, the order
|
182 |
+
// of preference is property -> color -> value
|
183 |
+
if (propertyKeywords.hasOwnProperty(word)) {
|
184 |
+
style = "property";
|
185 |
+
} else if (colorKeywords.hasOwnProperty(word)) {
|
186 |
+
style = "keyword";
|
187 |
+
} else if (valueKeywords.hasOwnProperty(word)) {
|
188 |
+
style = "string-2";
|
189 |
+
} else {
|
190 |
+
style = "tag";
|
191 |
+
}
|
192 |
+
} else if (!context || context == "@media{") {
|
193 |
+
style = "tag";
|
194 |
+
} else if (context == "@media") {
|
195 |
+
if (atMediaTypes[stream.current()]) {
|
196 |
+
style = "attribute"; // Known attribute
|
197 |
+
} else if (/^(only|not)$/.test(word)) {
|
198 |
+
style = "keyword";
|
199 |
+
} else if (word == "and") {
|
200 |
+
style = "error"; // "and" is only allowed in @mediaType
|
201 |
+
} else if (atMediaFeatures.hasOwnProperty(word)) {
|
202 |
+
style = "error"; // Known property, should be in @mediaType(
|
203 |
+
} else {
|
204 |
+
// Unknown, expecting keyword or attribute, assuming attribute
|
205 |
+
style = "attribute error";
|
206 |
+
}
|
207 |
+
} else if (context == "@mediaType") {
|
208 |
+
if (atMediaTypes.hasOwnProperty(word)) {
|
209 |
+
style = "attribute";
|
210 |
+
} else if (word == "and") {
|
211 |
+
style = "operator";
|
212 |
+
} else if (/^(only|not)$/.test(word)) {
|
213 |
+
style = "error"; // Only allowed in @media
|
214 |
+
} else {
|
215 |
+
// Unknown attribute or property, but expecting property (preceded
|
216 |
+
// by "and"). Should be in parentheses
|
217 |
+
style = "error";
|
218 |
+
}
|
219 |
+
} else if (context == "@mediaType(") {
|
220 |
+
if (propertyKeywords.hasOwnProperty(word)) {
|
221 |
+
// do nothing, remains "property"
|
222 |
+
} else if (atMediaTypes.hasOwnProperty(word)) {
|
223 |
+
style = "error"; // Known property, should be in parentheses
|
224 |
+
} else if (word == "and") {
|
225 |
+
style = "operator";
|
226 |
+
} else if (/^(only|not)$/.test(word)) {
|
227 |
+
style = "error"; // Only allowed in @media
|
228 |
+
} else {
|
229 |
+
style += " error";
|
230 |
+
}
|
231 |
+
} else if (context == "@import") {
|
232 |
+
style = "tag";
|
233 |
+
} else {
|
234 |
+
style = "error";
|
235 |
+
}
|
236 |
+
} else if (style == "atom") {
|
237 |
+
if(!context || context == "@media{" || context == "block") {
|
238 |
+
style = "builtin";
|
239 |
+
} else if (context == "propertyValue") {
|
240 |
+
if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
|
241 |
+
style += " error";
|
242 |
+
}
|
243 |
+
} else {
|
244 |
+
style = "error";
|
245 |
+
}
|
246 |
+
} else if (context == "@media" && type == "{") {
|
247 |
+
style = "error";
|
248 |
+
}
|
249 |
+
|
250 |
+
// Push/pop context stack
|
251 |
+
if (type == "{") {
|
252 |
+
if (context == "@media" || context == "@mediaType") {
|
253 |
+
state.stack[state.stack.length-1] = "@media{";
|
254 |
+
}
|
255 |
+
else {
|
256 |
+
var newContext = allowNested ? "block" : "rule";
|
257 |
+
state.stack.push(newContext);
|
258 |
+
}
|
259 |
+
}
|
260 |
+
else if (type == "}") {
|
261 |
+
if (context == "interpolation") style = "operator";
|
262 |
+
state.stack.pop();
|
263 |
+
if (context == "propertyValue") state.stack.pop();
|
264 |
+
}
|
265 |
+
else if (type == "interpolation") state.stack.push("interpolation");
|
266 |
+
else if (type == "@media") state.stack.push("@media");
|
267 |
+
else if (type == "@import") state.stack.push("@import");
|
268 |
+
else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
|
269 |
+
state.stack[state.stack.length-1] = "@mediaType";
|
270 |
+
else if (context == "@mediaType" && stream.current() == ",")
|
271 |
+
state.stack[state.stack.length-1] = "@media";
|
272 |
+
else if (type == "(") {
|
273 |
+
if (context == "@media" || context == "@mediaType") {
|
274 |
+
// Make sure @mediaType is used to avoid error on {
|
275 |
+
state.stack[state.stack.length-1] = "@mediaType";
|
276 |
+
state.stack.push("@mediaType(");
|
277 |
+
}
|
278 |
+
else state.stack.push("(");
|
279 |
+
}
|
280 |
+
else if (type == ")") {
|
281 |
+
if (context == "propertyValue") {
|
282 |
+
// In @mediaType( without closing ; after propertyValue
|
283 |
+
state.stack.pop();
|
284 |
+
}
|
285 |
+
state.stack.pop();
|
286 |
+
}
|
287 |
+
else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue");
|
288 |
+
else if (context == "propertyValue" && type == ";") state.stack.pop();
|
289 |
+
else if (context == "@import" && type == ";") state.stack.pop();
|
290 |
+
|
291 |
+
return state.lastToken = style;
|
292 |
+
},
|
293 |
+
|
294 |
+
indent: function(state, textAfter) {
|
295 |
+
var n = state.stack.length;
|
296 |
+
if (/^\}/.test(textAfter))
|
297 |
+
n -= state.stack[n-1] == "propertyValue" ? 2 : 1;
|
298 |
+
return state.baseIndent + n * indentUnit;
|
299 |
+
},
|
300 |
+
|
301 |
+
electricChars: "}",
|
302 |
+
blockCommentStart: "/*",
|
303 |
+
blockCommentEnd: "*/",
|
304 |
+
fold: "brace"
|
305 |
+
};
|
306 |
+
});
|
307 |
+
|
308 |
+
(function() {
|
309 |
+
function keySet(array) {
|
310 |
+
var keys = {};
|
311 |
+
for (var i = 0; i < array.length; ++i) {
|
312 |
+
keys[array[i]] = true;
|
313 |
+
}
|
314 |
+
return keys;
|
315 |
+
}
|
316 |
+
|
317 |
+
var atMediaTypes = keySet([
|
318 |
+
"all", "aural", "braille", "handheld", "print", "projection", "screen",
|
319 |
+
"tty", "tv", "embossed"
|
320 |
+
]);
|
321 |
+
|
322 |
+
var atMediaFeatures = keySet([
|
323 |
+
"width", "min-width", "max-width", "height", "min-height", "max-height",
|
324 |
+
"device-width", "min-device-width", "max-device-width", "device-height",
|
325 |
+
"min-device-height", "max-device-height", "aspect-ratio",
|
326 |
+
"min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
|
327 |
+
"min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
|
328 |
+
"max-color", "color-index", "min-color-index", "max-color-index",
|
329 |
+
"monochrome", "min-monochrome", "max-monochrome", "resolution",
|
330 |
+
"min-resolution", "max-resolution", "scan", "grid"
|
331 |
+
]);
|
332 |
+
|
333 |
+
var propertyKeywords = keySet([
|
334 |
+
"align-content", "align-items", "align-self", "alignment-adjust",
|
335 |
+
"alignment-baseline", "anchor-point", "animation", "animation-delay",
|
336 |
+
"animation-direction", "animation-duration", "animation-iteration-count",
|
337 |
+
"animation-name", "animation-play-state", "animation-timing-function",
|
338 |
+
"appearance", "azimuth", "backface-visibility", "background",
|
339 |
+
"background-attachment", "background-clip", "background-color",
|
340 |
+
"background-image", "background-origin", "background-position",
|
341 |
+
"background-repeat", "background-size", "baseline-shift", "binding",
|
342 |
+
"bleed", "bookmark-label", "bookmark-level", "bookmark-state",
|
343 |
+
"bookmark-target", "border", "border-bottom", "border-bottom-color",
|
344 |
+
"border-bottom-left-radius", "border-bottom-right-radius",
|
345 |
+
"border-bottom-style", "border-bottom-width", "border-collapse",
|
346 |
+
"border-color", "border-image", "border-image-outset",
|
347 |
+
"border-image-repeat", "border-image-slice", "border-image-source",
|
348 |
+
"border-image-width", "border-left", "border-left-color",
|
349 |
+
"border-left-style", "border-left-width", "border-radius", "border-right",
|
350 |
+
"border-right-color", "border-right-style", "border-right-width",
|
351 |
+
"border-spacing", "border-style", "border-top", "border-top-color",
|
352 |
+
"border-top-left-radius", "border-top-right-radius", "border-top-style",
|
353 |
+
"border-top-width", "border-width", "bottom", "box-decoration-break",
|
354 |
+
"box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
|
355 |
+
"caption-side", "clear", "clip", "color", "color-profile", "column-count",
|
356 |
+
"column-fill", "column-gap", "column-rule", "column-rule-color",
|
357 |
+
"column-rule-style", "column-rule-width", "column-span", "column-width",
|
358 |
+
"columns", "content", "counter-increment", "counter-reset", "crop", "cue",
|
359 |
+
"cue-after", "cue-before", "cursor", "direction", "display",
|
360 |
+
"dominant-baseline", "drop-initial-after-adjust",
|
361 |
+
"drop-initial-after-align", "drop-initial-before-adjust",
|
362 |
+
"drop-initial-before-align", "drop-initial-size", "drop-initial-value",
|
363 |
+
"elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
|
364 |
+
"flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
|
365 |
+
"float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
|
366 |
+
"font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
|
367 |
+
"font-stretch", "font-style", "font-synthesis", "font-variant",
|
368 |
+
"font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
|
369 |
+
"font-variant-ligatures", "font-variant-numeric", "font-variant-position",
|
370 |
+
"font-weight", "grid-cell", "grid-column", "grid-column-align",
|
371 |
+
"grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
|
372 |
+
"grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
|
373 |
+
"grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
|
374 |
+
"icon", "image-orientation", "image-rendering", "image-resolution",
|
375 |
+
"inline-box-align", "justify-content", "left", "letter-spacing",
|
376 |
+
"line-break", "line-height", "line-stacking", "line-stacking-ruby",
|
377 |
+
"line-stacking-shift", "line-stacking-strategy", "list-style",
|
378 |
+
"list-style-image", "list-style-position", "list-style-type", "margin",
|
379 |
+
"margin-bottom", "margin-left", "margin-right", "margin-top",
|
380 |
+
"marker-offset", "marks", "marquee-direction", "marquee-loop",
|
381 |
+
"marquee-play-count", "marquee-speed", "marquee-style", "max-height",
|
382 |
+
"max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
|
383 |
+
"nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
|
384 |
+
"outline-color", "outline-offset", "outline-style", "outline-width",
|
385 |
+
"overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
|
386 |
+
"padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
|
387 |
+
"page", "page-break-after", "page-break-before", "page-break-inside",
|
388 |
+
"page-policy", "pause", "pause-after", "pause-before", "perspective",
|
389 |
+
"perspective-origin", "pitch", "pitch-range", "play-during", "position",
|
390 |
+
"presentation-level", "punctuation-trim", "quotes", "region-break-after",
|
391 |
+
"region-break-before", "region-break-inside", "region-fragment",
|
392 |
+
"rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
|
393 |
+
"right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
|
394 |
+
"ruby-position", "ruby-span", "shape-inside", "shape-outside", "size",
|
395 |
+
"speak", "speak-as", "speak-header",
|
396 |
+
"speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
|
397 |
+
"tab-size", "table-layout", "target", "target-name", "target-new",
|
398 |
+
"target-position", "text-align", "text-align-last", "text-decoration",
|
399 |
+
"text-decoration-color", "text-decoration-line", "text-decoration-skip",
|
400 |
+
"text-decoration-style", "text-emphasis", "text-emphasis-color",
|
401 |
+
"text-emphasis-position", "text-emphasis-style", "text-height",
|
402 |
+
"text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
|
403 |
+
"text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
|
404 |
+
"text-wrap", "top", "transform", "transform-origin", "transform-style",
|
405 |
+
"transition", "transition-delay", "transition-duration",
|
406 |
+
"transition-property", "transition-timing-function", "unicode-bidi",
|
407 |
+
"vertical-align", "visibility", "voice-balance", "voice-duration",
|
408 |
+
"voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
|
409 |
+
"voice-volume", "volume", "white-space", "widows", "width", "word-break",
|
410 |
+
"word-spacing", "word-wrap", "z-index", "zoom",
|
411 |
+
// SVG-specific
|
412 |
+
"clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
|
413 |
+
"flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
|
414 |
+
"color-interpolation", "color-interpolation-filters", "color-profile",
|
415 |
+
"color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
|
416 |
+
"marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
|
417 |
+
"stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
|
418 |
+
"stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
|
419 |
+
"baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
|
420 |
+
"glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode"
|
421 |
+
]);
|
422 |
+
|
423 |
+
var colorKeywords = keySet([
|
424 |
+
"aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
|
425 |
+
"bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
|
426 |
+
"burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
|
427 |
+
"cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
|
428 |
+
"darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
|
429 |
+
"darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
|
430 |
+
"darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
|
431 |
+
"deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
|
432 |
+
"floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
|
433 |
+
"gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
|
434 |
+
"hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
|
435 |
+
"lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
|
436 |
+
"lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
|
437 |
+
"lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
|
438 |
+
"lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
|
439 |
+
"maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
|
440 |
+
"mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
|
441 |
+
"mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
|
442 |
+
"navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
|
443 |
+
"orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
|
444 |
+
"papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
|
445 |
+
"purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon",
|
446 |
+
"sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
|
447 |
+
"slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
|
448 |
+
"teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
|
449 |
+
"whitesmoke", "yellow", "yellowgreen"
|
450 |
+
]);
|
451 |
+
|
452 |
+
var valueKeywords = keySet([
|
453 |
+
"above", "absolute", "activeborder", "activecaption", "afar",
|
454 |
+
"after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
|
455 |
+
"always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
|
456 |
+
"arabic-indic", "armenian", "asterisks", "auto", "avoid", "avoid-column", "avoid-page",
|
457 |
+
"avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
|
458 |
+
"bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
|
459 |
+
"both", "bottom", "break", "break-all", "break-word", "button", "button-bevel",
|
460 |
+
"buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
|
461 |
+
"capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
|
462 |
+
"cell", "center", "checkbox", "circle", "cjk-earthly-branch",
|
463 |
+
"cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
|
464 |
+
"col-resize", "collapse", "column", "compact", "condensed", "contain", "content",
|
465 |
+
"content-box", "context-menu", "continuous", "copy", "cover", "crop",
|
466 |
+
"cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
|
467 |
+
"decimal-leading-zero", "default", "default-button", "destination-atop",
|
468 |
+
"destination-in", "destination-out", "destination-over", "devanagari",
|
469 |
+
"disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
|
470 |
+
"double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
|
471 |
+
"element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
|
472 |
+
"ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
|
473 |
+
"ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
|
474 |
+
"ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
|
475 |
+
"ethiopic-halehame-gez", "ethiopic-halehame-om-et",
|
476 |
+
"ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
|
477 |
+
"ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
|
478 |
+
"ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
|
479 |
+
"extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
|
480 |
+
"forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
|
481 |
+
"gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
|
482 |
+
"help", "hidden", "hide", "higher", "highlight", "highlighttext",
|
483 |
+
"hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
|
484 |
+
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
|
485 |
+
"infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
|
486 |
+
"inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
|
487 |
+
"italic", "justify", "kannada", "katakana", "katakana-iroha", "keep-all", "khmer",
|
488 |
+
"landscape", "lao", "large", "larger", "left", "level", "lighter",
|
489 |
+
"line-through", "linear", "lines", "list-item", "listbox", "listitem",
|
490 |
+
"local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
|
491 |
+
"lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
|
492 |
+
"lower-roman", "lowercase", "ltr", "malayalam", "match",
|
493 |
+
"media-controls-background", "media-current-time-display",
|
494 |
+
"media-fullscreen-button", "media-mute-button", "media-play-button",
|
495 |
+
"media-return-to-realtime-button", "media-rewind-button",
|
496 |
+
"media-seek-back-button", "media-seek-forward-button", "media-slider",
|
497 |
+
"media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
|
498 |
+
"media-volume-slider-container", "media-volume-sliderthumb", "medium",
|
499 |
+
"menu", "menulist", "menulist-button", "menulist-text",
|
500 |
+
"menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
|
501 |
+
"mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
|
502 |
+
"narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
|
503 |
+
"no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
|
504 |
+
"ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
|
505 |
+
"optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
|
506 |
+
"outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
|
507 |
+
"painted", "page", "paused", "persian", "plus-darker", "plus-lighter", "pointer",
|
508 |
+
"polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
|
509 |
+
"radio", "read-only", "read-write", "read-write-plaintext-only", "rectangle", "region",
|
510 |
+
"relative", "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
|
511 |
+
"ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
|
512 |
+
"s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
|
513 |
+
"searchfield-cancel-button", "searchfield-decoration",
|
514 |
+
"searchfield-results-button", "searchfield-results-decoration",
|
515 |
+
"semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
|
516 |
+
"single", "skip-white-space", "slide", "slider-horizontal",
|
517 |
+
"slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
|
518 |
+
"small", "small-caps", "small-caption", "smaller", "solid", "somali",
|
519 |
+
"source-atop", "source-in", "source-out", "source-over", "space", "square",
|
520 |
+
"square-button", "start", "static", "status-bar", "stretch", "stroke",
|
521 |
+
"sub", "subpixel-antialiased", "super", "sw-resize", "table",
|
522 |
+
"table-caption", "table-cell", "table-column", "table-column-group",
|
523 |
+
"table-footer-group", "table-header-group", "table-row", "table-row-group",
|
524 |
+
"telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
|
525 |
+
"thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
|
526 |
+
"threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
|
527 |
+
"tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
|
528 |
+
"transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
|
529 |
+
"upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
|
530 |
+
"upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
|
531 |
+
"vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
|
532 |
+
"visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
|
533 |
+
"window", "windowframe", "windowtext", "x-large", "x-small", "xor",
|
534 |
+
"xx-large", "xx-small"
|
535 |
+
]);
|
536 |
+
|
537 |
+
function tokenCComment(stream, state) {
|
538 |
+
var maybeEnd = false, ch;
|
539 |
+
while ((ch = stream.next()) != null) {
|
540 |
+
if (maybeEnd && ch == "/") {
|
541 |
+
state.tokenize = null;
|
542 |
+
break;
|
543 |
+
}
|
544 |
+
maybeEnd = (ch == "*");
|
545 |
+
}
|
546 |
+
return ["comment", "comment"];
|
547 |
+
}
|
548 |
+
|
549 |
+
CodeMirror.defineMIME("text/css", {
|
550 |
+
atMediaTypes: atMediaTypes,
|
551 |
+
atMediaFeatures: atMediaFeatures,
|
552 |
+
propertyKeywords: propertyKeywords,
|
553 |
+
colorKeywords: colorKeywords,
|
554 |
+
valueKeywords: valueKeywords,
|
555 |
+
hooks: {
|
556 |
+
"<": function(stream, state) {
|
557 |
+
function tokenSGMLComment(stream, state) {
|
558 |
+
var dashes = 0, ch;
|
559 |
+
while ((ch = stream.next()) != null) {
|
560 |
+
if (dashes >= 2 && ch == ">") {
|
561 |
+
state.tokenize = null;
|
562 |
+
break;
|
563 |
+
}
|
564 |
+
dashes = (ch == "-") ? dashes + 1 : 0;
|
565 |
+
}
|
566 |
+
return ["comment", "comment"];
|
567 |
+
}
|
568 |
+
if (stream.eat("!")) {
|
569 |
+
state.tokenize = tokenSGMLComment;
|
570 |
+
return tokenSGMLComment(stream, state);
|
571 |
+
}
|
572 |
+
},
|
573 |
+
"/": function(stream, state) {
|
574 |
+
if (stream.eat("*")) {
|
575 |
+
state.tokenize = tokenCComment;
|
576 |
+
return tokenCComment(stream, state);
|
577 |
+
}
|
578 |
+
return false;
|
579 |
+
}
|
580 |
+
},
|
581 |
+
name: "css"
|
582 |
+
});
|
583 |
+
|
584 |
+
CodeMirror.defineMIME("text/x-scss", {
|
585 |
+
atMediaTypes: atMediaTypes,
|
586 |
+
atMediaFeatures: atMediaFeatures,
|
587 |
+
propertyKeywords: propertyKeywords,
|
588 |
+
colorKeywords: colorKeywords,
|
589 |
+
valueKeywords: valueKeywords,
|
590 |
+
allowNested: true,
|
591 |
+
hooks: {
|
592 |
+
":": function(stream) {
|
593 |
+
if (stream.match(/\s*{/)) {
|
594 |
+
return [null, "{"];
|
595 |
+
}
|
596 |
+
return false;
|
597 |
+
},
|
598 |
+
"$": function(stream) {
|
599 |
+
stream.match(/^[\w-]+/);
|
600 |
+
if (stream.peek() == ":") {
|
601 |
+
return ["variable", "variable-definition"];
|
602 |
+
}
|
603 |
+
return ["variable", "variable"];
|
604 |
+
},
|
605 |
+
"/": function(stream, state) {
|
606 |
+
if (stream.eat("/")) {
|
607 |
+
stream.skipToEnd();
|
608 |
+
return ["comment", "comment"];
|
609 |
+
} else if (stream.eat("*")) {
|
610 |
+
state.tokenize = tokenCComment;
|
611 |
+
return tokenCComment(stream, state);
|
612 |
+
} else {
|
613 |
+
return ["operator", "operator"];
|
614 |
+
}
|
615 |
+
},
|
616 |
+
"#": function(stream) {
|
617 |
+
if (stream.eat("{")) {
|
618 |
+
return ["operator", "interpolation"];
|
619 |
+
} else {
|
620 |
+
stream.eatWhile(/[\w\\\-]/);
|
621 |
+
return ["atom", "hash"];
|
622 |
+
}
|
623 |
+
}
|
624 |
+
},
|
625 |
+
name: "css"
|
626 |
+
});
|
627 |
+
})();
|
assets/lib/codemirror/mode/css/index.html
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
|
3 |
+
<title>CodeMirror: CSS mode</title>
|
4 |
+
<meta charset="utf-8"/>
|
5 |
+
<link rel=stylesheet href="../../doc/docs.css">
|
6 |
+
|
7 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
8 |
+
<script src="../../lib/codemirror.js"></script>
|
9 |
+
<script src="css.js"></script>
|
10 |
+
<style>.CodeMirror {background: #f8f8f8;}</style>
|
11 |
+
<div id=nav>
|
12 |
+
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>
|
13 |
+
|
14 |
+
<ul>
|
15 |
+
<li><a href="../../index.html">Home</a>
|
16 |
+
<li><a href="../../doc/manual.html">Manual</a>
|
17 |
+
<li><a href="https://github.com/marijnh/codemirror">Code</a>
|
18 |
+
</ul>
|
19 |
+
<ul>
|
20 |
+
<li><a href="../index.html">Language modes</a>
|
21 |
+
<li><a class=active href="#">CSS</a>
|
22 |
+
</ul>
|
23 |
+
</div>
|
24 |
+
|
25 |
+
<article>
|
26 |
+
<h2>CSS mode</h2>
|
27 |
+
<form><textarea id="code" name="code">
|
28 |
+
/* Some example CSS */
|
29 |
+
|
30 |
+
@import url("something.css");
|
31 |
+
|
32 |
+
body {
|
33 |
+
margin: 0;
|
34 |
+
padding: 3em 6em;
|
35 |
+
font-family: tahoma, arial, sans-serif;
|
36 |
+
color: #000;
|
37 |
+
}
|
38 |
+
|
39 |
+
#navigation a {
|
40 |
+
font-weight: bold;
|
41 |
+
text-decoration: none !important;
|
42 |
+
}
|
43 |
+
|
44 |
+
h1 {
|
45 |
+
font-size: 2.5em;
|
46 |
+
}
|
47 |
+
|
48 |
+
h2 {
|
49 |
+
font-size: 1.7em;
|
50 |
+
}
|
51 |
+
|
52 |
+
h1:before, h2:before {
|
53 |
+
content: "::";
|
54 |
+
}
|
55 |
+
|
56 |
+
code {
|
57 |
+
font-family: courier, monospace;
|
58 |
+
font-size: 80%;
|
59 |
+
color: #418A8A;
|
60 |
+
}
|
61 |
+
</textarea></form>
|
62 |
+
<script>
|
63 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
|
64 |
+
</script>
|
65 |
+
|
66 |
+
<p><strong>MIME types defined:</strong> <code>text/css</code>.</p>
|
67 |
+
|
68 |
+
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#css_*">normal</a>, <a href="../../test/index.html#verbose,css_*">verbose</a>.</p>
|
69 |
+
|
70 |
+
</article>
|
assets/lib/codemirror/mode/css/scss.html
ADDED
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
|
3 |
+
<title>CodeMirror: SCSS mode</title>
|
4 |
+
<meta charset="utf-8"/>
|
5 |
+
<link rel=stylesheet href="../../doc/docs.css">
|
6 |
+
|
7 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
8 |
+
<script src="../../lib/codemirror.js"></script>
|
9 |
+
<script src="css.js"></script>
|
10 |
+
<style>.CodeMirror {background: #f8f8f8;}</style>
|
11 |
+
<div id=nav>
|
12 |
+
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>
|
13 |
+
|
14 |
+
<ul>
|
15 |
+
<li><a href="../../index.html">Home</a>
|
16 |
+
<li><a href="../../doc/manual.html">Manual</a>
|
17 |
+
<li><a href="https://github.com/marijnh/codemirror">Code</a>
|
18 |
+
</ul>
|
19 |
+
<ul>
|
20 |
+
<li><a href="../index.html">Language modes</a>
|
21 |
+
<li><a class=active href="#">SCSS</a>
|
22 |
+
</ul>
|
23 |
+
</div>
|
24 |
+
|
25 |
+
<article>
|
26 |
+
<h2>SCSS mode</h2>
|
27 |
+
<form><textarea id="code" name="code">
|
28 |
+
/* Some example SCSS */
|
29 |
+
|
30 |
+
@import "compass/css3";
|
31 |
+
$variable: #333;
|
32 |
+
|
33 |
+
$blue: #3bbfce;
|
34 |
+
$margin: 16px;
|
35 |
+
|
36 |
+
.content-navigation {
|
37 |
+
#nested {
|
38 |
+
background-color: black;
|
39 |
+
}
|
40 |
+
border-color: $blue;
|
41 |
+
color:
|
42 |
+
darken($blue, 9%);
|
43 |
+
}
|
44 |
+
|
45 |
+
.border {
|
46 |
+
padding: $margin / 2;
|
47 |
+
margin: $margin / 2;
|
48 |
+
border-color: $blue;
|
49 |
+
}
|
50 |
+
|
51 |
+
@mixin table-base {
|
52 |
+
th {
|
53 |
+
text-align: center;
|
54 |
+
font-weight: bold;
|
55 |
+
}
|
56 |
+
td, th {padding: 2px}
|
57 |
+
}
|
58 |
+
|
59 |
+
table.hl {
|
60 |
+
margin: 2em 0;
|
61 |
+
td.ln {
|
62 |
+
text-align: right;
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
li {
|
67 |
+
font: {
|
68 |
+
family: serif;
|
69 |
+
weight: bold;
|
70 |
+
size: 1.2em;
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
@mixin left($dist) {
|
75 |
+
float: left;
|
76 |
+
margin-left: $dist;
|
77 |
+
}
|
78 |
+
|
79 |
+
#data {
|
80 |
+
@include left(10px);
|
81 |
+
@include table-base;
|
82 |
+
}
|
83 |
+
|
84 |
+
.source {
|
85 |
+
@include flow-into(target);
|
86 |
+
border: 10px solid green;
|
87 |
+
margin: 20px;
|
88 |
+
width: 200px; }
|
89 |
+
|
90 |
+
.new-container {
|
91 |
+
@include flow-from(target);
|
92 |
+
border: 10px solid red;
|
93 |
+
margin: 20px;
|
94 |
+
width: 200px; }
|
95 |
+
|
96 |
+
body {
|
97 |
+
margin: 0;
|
98 |
+
padding: 3em 6em;
|
99 |
+
font-family: tahoma, arial, sans-serif;
|
100 |
+
color: #000;
|
101 |
+
}
|
102 |
+
|
103 |
+
@mixin yellow() {
|
104 |
+
background: yellow;
|
105 |
+
}
|
106 |
+
|
107 |
+
.big {
|
108 |
+
font-size: 14px;
|
109 |
+
}
|
110 |
+
|
111 |
+
.nested {
|
112 |
+
@include border-radius(3px);
|
113 |
+
@extend .big;
|
114 |
+
p {
|
115 |
+
background: whitesmoke;
|
116 |
+
a {
|
117 |
+
color: red;
|
118 |
+
}
|
119 |
+
}
|
120 |
+
}
|
121 |
+
|
122 |
+
#navigation a {
|
123 |
+
font-weight: bold;
|
124 |
+
text-decoration: none !important;
|
125 |
+
}
|
126 |
+
|
127 |
+
h1 {
|
128 |
+
font-size: 2.5em;
|
129 |
+
}
|
130 |
+
|
131 |
+
h2 {
|
132 |
+
font-size: 1.7em;
|
133 |
+
}
|
134 |
+
|
135 |
+
h1:before, h2:before {
|
136 |
+
content: "::";
|
137 |
+
}
|
138 |
+
|
139 |
+
code {
|
140 |
+
font-family: courier, monospace;
|
141 |
+
font-size: 80%;
|
142 |
+
color: #418A8A;
|
143 |
+
}
|
144 |
+
</textarea></form>
|
145 |
+
<script>
|
146 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
147 |
+
lineNumbers: true,
|
148 |
+
matchBrackets: true,
|
149 |
+
mode: "text/x-scss"
|
150 |
+
});
|
151 |
+
</script>
|
152 |
+
|
153 |
+
<p><strong>MIME types defined:</strong> <code>text/scss</code>.</p>
|
154 |
+
|
155 |
+
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#scss_*">normal</a>, <a href="../../test/index.html#verbose,scss_*">verbose</a>.</p>
|
156 |
+
|
157 |
+
</article>
|
assets/lib/codemirror/mode/css/scss_test.js
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
var mode = CodeMirror.getMode({tabSize: 4}, "text/x-scss");
|
3 |
+
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); }
|
4 |
+
|
5 |
+
MT('url_with_quotation',
|
6 |
+
"[tag foo] { [property background][operator :][string-2 url]([string test.jpg]) }");
|
7 |
+
|
8 |
+
MT('url_with_double_quotes',
|
9 |
+
"[tag foo] { [property background][operator :][string-2 url]([string \"test.jpg\"]) }");
|
10 |
+
|
11 |
+
MT('url_with_single_quotes',
|
12 |
+
"[tag foo] { [property background][operator :][string-2 url]([string \'test.jpg\']) }");
|
13 |
+
|
14 |
+
MT('string',
|
15 |
+
"[def @import] [string \"compass/css3\"]");
|
16 |
+
|
17 |
+
MT('important_keyword',
|
18 |
+
"[tag foo] { [property background][operator :][string-2 url]([string \'test.jpg\']) [keyword !important] }");
|
19 |
+
|
20 |
+
MT('variable',
|
21 |
+
"[variable-2 $blue][operator :][atom #333]");
|
22 |
+
|
23 |
+
MT('variable_as_attribute',
|
24 |
+
"[tag foo] { [property color][operator :][variable-2 $blue] }");
|
25 |
+
|
26 |
+
MT('numbers',
|
27 |
+
"[tag foo] { [property padding][operator :][number 10px] [number 10] [number 10em] [number 8in] }");
|
28 |
+
|
29 |
+
MT('number_percentage',
|
30 |
+
"[tag foo] { [property width][operator :][number 80%] }");
|
31 |
+
|
32 |
+
MT('selector',
|
33 |
+
"[builtin #hello][qualifier .world]{}");
|
34 |
+
|
35 |
+
MT('singleline_comment',
|
36 |
+
"[comment // this is a comment]");
|
37 |
+
|
38 |
+
MT('multiline_comment',
|
39 |
+
"[comment /*foobar*/]");
|
40 |
+
|
41 |
+
MT('attribute_with_hyphen',
|
42 |
+
"[tag foo] { [property font-size][operator :][number 10px] }");
|
43 |
+
|
44 |
+
MT('string_after_attribute',
|
45 |
+
"[tag foo] { [property content][operator :][string \"::\"] }");
|
46 |
+
|
47 |
+
MT('directives',
|
48 |
+
"[def @include] [qualifier .mixin]");
|
49 |
+
|
50 |
+
MT('basic_structure',
|
51 |
+
"[tag p] { [property background][operator :][keyword red]; }");
|
52 |
+
|
53 |
+
MT('nested_structure',
|
54 |
+
"[tag p] { [tag a] { [property color][operator :][keyword red]; } }");
|
55 |
+
|
56 |
+
MT('mixin',
|
57 |
+
"[def @mixin] [tag table-base] {}");
|
58 |
+
|
59 |
+
MT('number_without_semicolon',
|
60 |
+
"[tag p] {[property width][operator :][number 12]}",
|
61 |
+
"[tag a] {[property color][operator :][keyword red];}");
|
62 |
+
|
63 |
+
MT('atom_in_nested_block',
|
64 |
+
"[tag p] { [tag a] { [property color][operator :][atom #000]; } }");
|
65 |
+
|
66 |
+
MT('interpolation_in_property',
|
67 |
+
"[tag foo] { [operator #{][variable-2 $hello][operator }:][number 2]; }");
|
68 |
+
|
69 |
+
MT('interpolation_in_selector',
|
70 |
+
"[tag foo][operator #{][variable-2 $hello][operator }] { [property color][operator :][atom #000]; }");
|
71 |
+
|
72 |
+
MT('interpolation_error',
|
73 |
+
"[tag foo][operator #{][error foo][operator }] { [property color][operator :][atom #000]; }");
|
74 |
+
|
75 |
+
MT("divide_operator",
|
76 |
+
"[tag foo] { [property width][operator :][number 4] [operator /] [number 2] }");
|
77 |
+
|
78 |
+
MT('nested_structure_with_id_selector',
|
79 |
+
"[tag p] { [builtin #hello] { [property color][operator :][keyword red]; } }");
|
80 |
+
})();
|
assets/lib/codemirror/mode/css/test.js
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
var mode = CodeMirror.getMode({tabSize: 4}, "css");
|
3 |
+
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
|
4 |
+
|
5 |
+
// Requires at least one media query
|
6 |
+
MT("atMediaEmpty",
|
7 |
+
"[def @media] [error {] }");
|
8 |
+
|
9 |
+
MT("atMediaMultiple",
|
10 |
+
"[def @media] [keyword not] [attribute screen] [operator and] ([property color]), [keyword not] [attribute print] [operator and] ([property color]) { }");
|
11 |
+
|
12 |
+
MT("atMediaCheckStack",
|
13 |
+
"[def @media] [attribute screen] { } [tag foo] { }");
|
14 |
+
|
15 |
+
MT("atMediaCheckStack",
|
16 |
+
"[def @media] [attribute screen] ([property color]) { } [tag foo] { }");
|
17 |
+
|
18 |
+
MT("atMediaPropertyOnly",
|
19 |
+
"[def @media] ([property color]) { } [tag foo] { }");
|
20 |
+
|
21 |
+
MT("atMediaCheckStackInvalidAttribute",
|
22 |
+
"[def @media] [attribute&error foobarhello] { [tag foo] { } }");
|
23 |
+
|
24 |
+
MT("atMediaCheckStackInvalidAttribute",
|
25 |
+
"[def @media] [attribute&error foobarhello] { } [tag foo] { }");
|
26 |
+
|
27 |
+
// Error, because "and" is only allowed immediately preceding a media expression
|
28 |
+
MT("atMediaInvalidAttribute",
|
29 |
+
"[def @media] [attribute&error foobarhello] { }");
|
30 |
+
|
31 |
+
// Error, because "and" is only allowed immediately preceding a media expression
|
32 |
+
MT("atMediaInvalidAnd",
|
33 |
+
"[def @media] [error and] [attribute screen] { }");
|
34 |
+
|
35 |
+
// Error, because "not" is only allowed as the first item in each media query
|
36 |
+
MT("atMediaInvalidNot",
|
37 |
+
"[def @media] [attribute screen] [error not] ([error not]) { }");
|
38 |
+
|
39 |
+
// Error, because "only" is only allowed as the first item in each media query
|
40 |
+
MT("atMediaInvalidOnly",
|
41 |
+
"[def @media] [attribute screen] [error only] ([error only]) { }");
|
42 |
+
|
43 |
+
// Error, because "foobarhello" is neither a known type or property, but
|
44 |
+
// property was expected (after "and"), and it should be in parenthese.
|
45 |
+
MT("atMediaUnknownType",
|
46 |
+
"[def @media] [attribute screen] [operator and] [error foobarhello] { }");
|
47 |
+
|
48 |
+
// Error, because "color" is not a known type, but is a known property, and
|
49 |
+
// should be in parentheses.
|
50 |
+
MT("atMediaInvalidType",
|
51 |
+
"[def @media] [attribute screen] [operator and] [error color] { }");
|
52 |
+
|
53 |
+
// Error, because "print" is not a known property, but is a known type,
|
54 |
+
// and should not be in parenthese.
|
55 |
+
MT("atMediaInvalidProperty",
|
56 |
+
"[def @media] [attribute screen] [operator and] ([error print]) { }");
|
57 |
+
|
58 |
+
// Soft error, because "foobarhello" is not a known property or type.
|
59 |
+
MT("atMediaUnknownProperty",
|
60 |
+
"[def @media] [attribute screen] [operator and] ([property&error foobarhello]) { }");
|
61 |
+
|
62 |
+
// Make sure nesting works with media queries
|
63 |
+
MT("atMediaMaxWidthNested",
|
64 |
+
"[def @media] [attribute screen] [operator and] ([property max-width][operator :] [number 25px]) { [tag foo] { } }");
|
65 |
+
|
66 |
+
MT("tagSelector",
|
67 |
+
"[tag foo] { }");
|
68 |
+
|
69 |
+
MT("classSelector",
|
70 |
+
"[qualifier .foo-bar_hello] { }");
|
71 |
+
|
72 |
+
MT("idSelector",
|
73 |
+
"[builtin #foo] { [error #foo] }");
|
74 |
+
|
75 |
+
MT("tagSelectorUnclosed",
|
76 |
+
"[tag foo] { [property margin][operator :] [number 0] } [tag bar] { }");
|
77 |
+
|
78 |
+
MT("tagStringNoQuotes",
|
79 |
+
"[tag foo] { [property font-family][operator :] [variable-2 hello] [variable-2 world]; }");
|
80 |
+
|
81 |
+
MT("tagStringDouble",
|
82 |
+
"[tag foo] { [property font-family][operator :] [string \"hello world\"]; }");
|
83 |
+
|
84 |
+
MT("tagStringSingle",
|
85 |
+
"[tag foo] { [property font-family][operator :] [string 'hello world']; }");
|
86 |
+
|
87 |
+
MT("tagColorKeyword",
|
88 |
+
"[tag foo] {" +
|
89 |
+
"[property color][operator :] [keyword black];" +
|
90 |
+
"[property color][operator :] [keyword navy];" +
|
91 |
+
"[property color][operator :] [keyword yellow];" +
|
92 |
+
"}");
|
93 |
+
|
94 |
+
MT("tagColorHex3",
|
95 |
+
"[tag foo] { [property background][operator :] [atom #fff]; }");
|
96 |
+
|
97 |
+
MT("tagColorHex6",
|
98 |
+
"[tag foo] { [property background][operator :] [atom #ffffff]; }");
|
99 |
+
|
100 |
+
MT("tagColorHex4",
|
101 |
+
"[tag foo] { [property background][operator :] [atom&error #ffff]; }");
|
102 |
+
|
103 |
+
MT("tagColorHexInvalid",
|
104 |
+
"[tag foo] { [property background][operator :] [atom&error #ffg]; }");
|
105 |
+
|
106 |
+
MT("tagNegativeNumber",
|
107 |
+
"[tag foo] { [property margin][operator :] [number -5px]; }");
|
108 |
+
|
109 |
+
MT("tagPositiveNumber",
|
110 |
+
"[tag foo] { [property padding][operator :] [number 5px]; }");
|
111 |
+
|
112 |
+
MT("tagVendor",
|
113 |
+
"[tag foo] { [meta -foo-][property box-sizing][operator :] [meta -foo-][string-2 border-box]; }");
|
114 |
+
|
115 |
+
MT("tagBogusProperty",
|
116 |
+
"[tag foo] { [property&error barhelloworld][operator :] [number 0]; }");
|
117 |
+
|
118 |
+
MT("tagTwoProperties",
|
119 |
+
"[tag foo] { [property margin][operator :] [number 0]; [property padding][operator :] [number 0]; }");
|
120 |
+
|
121 |
+
MT("tagTwoPropertiesURL",
|
122 |
+
"[tag foo] { [property background][operator :] [string-2 url]([string //example.com/foo.png]); [property padding][operator :] [number 0]; }");
|
123 |
+
|
124 |
+
MT("commentSGML",
|
125 |
+
"[comment <!--comment-->]");
|
126 |
+
})();
|
assets/lib/codemirror/mode/htmlembedded/htmlembedded.js
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineMode("htmlembedded", function(config, parserConfig) {
|
2 |
+
|
3 |
+
//config settings
|
4 |
+
var scriptStartRegex = parserConfig.scriptStartRegex || /^<%/i,
|
5 |
+
scriptEndRegex = parserConfig.scriptEndRegex || /^%>/i;
|
6 |
+
|
7 |
+
//inner modes
|
8 |
+
var scriptingMode, htmlMixedMode;
|
9 |
+
|
10 |
+
//tokenizer when in html mode
|
11 |
+
function htmlDispatch(stream, state) {
|
12 |
+
if (stream.match(scriptStartRegex, false)) {
|
13 |
+
state.token=scriptingDispatch;
|
14 |
+
return scriptingMode.token(stream, state.scriptState);
|
15 |
+
}
|
16 |
+
else
|
17 |
+
return htmlMixedMode.token(stream, state.htmlState);
|
18 |
+
}
|
19 |
+
|
20 |
+
//tokenizer when in scripting mode
|
21 |
+
function scriptingDispatch(stream, state) {
|
22 |
+
if (stream.match(scriptEndRegex, false)) {
|
23 |
+
state.token=htmlDispatch;
|
24 |
+
return htmlMixedMode.token(stream, state.htmlState);
|
25 |
+
}
|
26 |
+
else
|
27 |
+
return scriptingMode.token(stream, state.scriptState);
|
28 |
+
}
|
29 |
+
|
30 |
+
|
31 |
+
return {
|
32 |
+
startState: function() {
|
33 |
+
scriptingMode = scriptingMode || CodeMirror.getMode(config, parserConfig.scriptingModeSpec);
|
34 |
+
htmlMixedMode = htmlMixedMode || CodeMirror.getMode(config, "htmlmixed");
|
35 |
+
return {
|
36 |
+
token : parserConfig.startOpen ? scriptingDispatch : htmlDispatch,
|
37 |
+
htmlState : CodeMirror.startState(htmlMixedMode),
|
38 |
+
scriptState : CodeMirror.startState(scriptingMode)
|
39 |
+
};
|
40 |
+
},
|
41 |
+
|
42 |
+
token: function(stream, state) {
|
43 |
+
return state.token(stream, state);
|
44 |
+
},
|
45 |
+
|
46 |
+
indent: function(state, textAfter) {
|
47 |
+
if (state.token == htmlDispatch)
|
48 |
+
return htmlMixedMode.indent(state.htmlState, textAfter);
|
49 |
+
else if (scriptingMode.indent)
|
50 |
+
return scriptingMode.indent(state.scriptState, textAfter);
|
51 |
+
},
|
52 |
+
|
53 |
+
copyState: function(state) {
|
54 |
+
return {
|
55 |
+
token : state.token,
|
56 |
+
htmlState : CodeMirror.copyState(htmlMixedMode, state.htmlState),
|
57 |
+
scriptState : CodeMirror.copyState(scriptingMode, state.scriptState)
|
58 |
+
};
|
59 |
+
},
|
60 |
+
|
61 |
+
electricChars: "/{}:",
|
62 |
+
|
63 |
+
innerMode: function(state) {
|
64 |
+
if (state.token == scriptingDispatch) return {state: state.scriptState, mode: scriptingMode};
|
65 |
+
else return {state: state.htmlState, mode: htmlMixedMode};
|
66 |
+
}
|
67 |
+
};
|
68 |
+
}, "htmlmixed");
|
69 |
+
|
70 |
+
CodeMirror.defineMIME("application/x-ejs", { name: "htmlembedded", scriptingModeSpec:"javascript"});
|
71 |
+
CodeMirror.defineMIME("application/x-aspx", { name: "htmlembedded", scriptingModeSpec:"text/x-csharp"});
|
72 |
+
CodeMirror.defineMIME("application/x-jsp", { name: "htmlembedded", scriptingModeSpec:"text/x-java"});
|
73 |
+
CodeMirror.defineMIME("application/x-erb", { name: "htmlembedded", scriptingModeSpec:"ruby"});
|
assets/lib/codemirror/mode/htmlembedded/index.html
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
|
3 |
+
<title>CodeMirror: Html Embedded Scripts mode</title>
|
4 |
+
<meta charset="utf-8"/>
|
5 |
+
<link rel=stylesheet href="../../doc/docs.css">
|
6 |
+
|
7 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
8 |
+
<script src="../../lib/codemirror.js"></script>
|
9 |
+
<script src="../xml/xml.js"></script>
|
10 |
+
<script src="../javascript/javascript.js"></script>
|
11 |
+
<script src="../css/css.js"></script>
|
12 |
+
<script src="../htmlmixed/htmlmixed.js"></script>
|
13 |
+
<script src="htmlembedded.js"></script>
|
14 |
+
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
15 |
+
<div id=nav>
|
16 |
+
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>
|
17 |
+
|
18 |
+
<ul>
|
19 |
+
<li><a href="../../index.html">Home</a>
|
20 |
+
<li><a href="../../doc/manual.html">Manual</a>
|
21 |
+
<li><a href="https://github.com/marijnh/codemirror">Code</a>
|
22 |
+
</ul>
|
23 |
+
<ul>
|
24 |
+
<li><a href="../index.html">Language modes</a>
|
25 |
+
<li><a class=active href="#">Html Embedded Scripts</a>
|
26 |
+
</ul>
|
27 |
+
</div>
|
28 |
+
|
29 |
+
<article>
|
30 |
+
<h2>Html Embedded Scripts mode</h2>
|
31 |
+
<form><textarea id="code" name="code">
|
32 |
+
<%
|
33 |
+
function hello(who) {
|
34 |
+
return "Hello " + who;
|
35 |
+
}
|
36 |
+
%>
|
37 |
+
This is an example of EJS (embedded javascript)
|
38 |
+
<p>The program says <%= hello("world") %>.</p>
|
39 |
+
<script>
|
40 |
+
alert("And here is some normal JS code"); // also colored
|
41 |
+
</script>
|
42 |
+
</textarea></form>
|
43 |
+
|
44 |
+
<script>
|
45 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
46 |
+
lineNumbers: true,
|
47 |
+
mode: "application/x-ejs",
|
48 |
+
indentUnit: 4,
|
49 |
+
indentWithTabs: true,
|
50 |
+
enterMode: "keep",
|
51 |
+
tabMode: "shift"
|
52 |
+
});
|
53 |
+
</script>
|
54 |
+
|
55 |
+
<p>Mode for html embedded scripts like JSP and ASP.NET. Depends on HtmlMixed which in turn depends on
|
56 |
+
JavaScript, CSS and XML.<br />Other dependancies include those of the scriping language chosen.</p>
|
57 |
+
|
58 |
+
<p><strong>MIME types defined:</strong> <code>application/x-aspx</code> (ASP.NET),
|
59 |
+
<code>application/x-ejs</code> (Embedded Javascript), <code>application/x-jsp</code> (JavaServer Pages)</p>
|
60 |
+
</article>
|
assets/lib/codemirror/mode/htmlmixed/htmlmixed.js
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
|
2 |
+
var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
|
3 |
+
var cssMode = CodeMirror.getMode(config, "css");
|
4 |
+
|
5 |
+
var scriptTypes = [], scriptTypesConf = parserConfig && parserConfig.scriptTypes;
|
6 |
+
scriptTypes.push({matches: /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i,
|
7 |
+
mode: CodeMirror.getMode(config, "javascript")});
|
8 |
+
if (scriptTypesConf) for (var i = 0; i < scriptTypesConf.length; ++i) {
|
9 |
+
var conf = scriptTypesConf[i];
|
10 |
+
scriptTypes.push({matches: conf.matches, mode: conf.mode && CodeMirror.getMode(config, conf.mode)});
|
11 |
+
}
|
12 |
+
scriptTypes.push({matches: /./,
|
13 |
+
mode: CodeMirror.getMode(config, "text/plain")});
|
14 |
+
|
15 |
+
function html(stream, state) {
|
16 |
+
var tagName = state.htmlState.tagName;
|
17 |
+
var style = htmlMode.token(stream, state.htmlState);
|
18 |
+
if (tagName == "script" && /\btag\b/.test(style) && stream.current() == ">") {
|
19 |
+
// Script block: mode to change to depends on type attribute
|
20 |
+
var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i);
|
21 |
+
scriptType = scriptType ? scriptType[1] : "";
|
22 |
+
if (scriptType && /[\"\']/.test(scriptType.charAt(0))) scriptType = scriptType.slice(1, scriptType.length - 1);
|
23 |
+
for (var i = 0; i < scriptTypes.length; ++i) {
|
24 |
+
var tp = scriptTypes[i];
|
25 |
+
if (typeof tp.matches == "string" ? scriptType == tp.matches : tp.matches.test(scriptType)) {
|
26 |
+
if (tp.mode) {
|
27 |
+
state.token = script;
|
28 |
+
state.localMode = tp.mode;
|
29 |
+
state.localState = tp.mode.startState && tp.mode.startState(htmlMode.indent(state.htmlState, ""));
|
30 |
+
}
|
31 |
+
break;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
} else if (tagName == "style" && /\btag\b/.test(style) && stream.current() == ">") {
|
35 |
+
state.token = css;
|
36 |
+
state.localMode = cssMode;
|
37 |
+
state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
|
38 |
+
}
|
39 |
+
return style;
|
40 |
+
}
|
41 |
+
function maybeBackup(stream, pat, style) {
|
42 |
+
var cur = stream.current();
|
43 |
+
var close = cur.search(pat), m;
|
44 |
+
if (close > -1) stream.backUp(cur.length - close);
|
45 |
+
else if (m = cur.match(/<\/?$/)) {
|
46 |
+
stream.backUp(cur.length);
|
47 |
+
if (!stream.match(pat, false)) stream.match(cur[0]);
|
48 |
+
}
|
49 |
+
return style;
|
50 |
+
}
|
51 |
+
function script(stream, state) {
|
52 |
+
if (stream.match(/^<\/\s*script\s*>/i, false)) {
|
53 |
+
state.token = html;
|
54 |
+
state.localState = state.localMode = null;
|
55 |
+
return html(stream, state);
|
56 |
+
}
|
57 |
+
return maybeBackup(stream, /<\/\s*script\s*>/,
|
58 |
+
state.localMode.token(stream, state.localState));
|
59 |
+
}
|
60 |
+
function css(stream, state) {
|
61 |
+
if (stream.match(/^<\/\s*style\s*>/i, false)) {
|
62 |
+
state.token = html;
|
63 |
+
state.localState = state.localMode = null;
|
64 |
+
return html(stream, state);
|
65 |
+
}
|
66 |
+
return maybeBackup(stream, /<\/\s*style\s*>/,
|
67 |
+
cssMode.token(stream, state.localState));
|
68 |
+
}
|
69 |
+
|
70 |
+
return {
|
71 |
+
startState: function() {
|
72 |
+
var state = htmlMode.startState();
|
73 |
+
return {token: html, localMode: null, localState: null, htmlState: state};
|
74 |
+
},
|
75 |
+
|
76 |
+
copyState: function(state) {
|
77 |
+
if (state.localState)
|
78 |
+
var local = CodeMirror.copyState(state.localMode, state.localState);
|
79 |
+
return {token: state.token, localMode: state.localMode, localState: local,
|
80 |
+
htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
|
81 |
+
},
|
82 |
+
|
83 |
+
token: function(stream, state) {
|
84 |
+
return state.token(stream, state);
|
85 |
+
},
|
86 |
+
|
87 |
+
indent: function(state, textAfter) {
|
88 |
+
if (!state.localMode || /^\s*<\//.test(textAfter))
|
89 |
+
return htmlMode.indent(state.htmlState, textAfter);
|
90 |
+
else if (state.localMode.indent)
|
91 |
+
return state.localMode.indent(state.localState, textAfter);
|
92 |
+
else
|
93 |
+
return CodeMirror.Pass;
|
94 |
+
},
|
95 |
+
|
96 |
+
electricChars: "/{}:",
|
97 |
+
|
98 |
+
innerMode: function(state) {
|
99 |
+
return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
|
100 |
+
}
|
101 |
+
};
|
102 |
+
}, "xml", "javascript", "css");
|
103 |
+
|
104 |
+
CodeMirror.defineMIME("text/html", "htmlmixed");
|
assets/lib/codemirror/mode/htmlmixed/index.html
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
|
3 |
+
<title>CodeMirror: HTML mixed mode</title>
|
4 |
+
<meta charset="utf-8"/>
|
5 |
+
<link rel=stylesheet href="../../doc/docs.css">
|
6 |
+
|
7 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
8 |
+
<script src="../../lib/codemirror.js"></script>
|
9 |
+
<script src="../xml/xml.js"></script>
|
10 |
+
<script src="../javascript/javascript.js"></script>
|
11 |
+
<script src="../css/css.js"></script>
|
12 |
+
<script src="../vbscript/vbscript.js"></script>
|
13 |
+
<script src="htmlmixed.js"></script>
|
14 |
+
<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
15 |
+
<div id=nav>
|
16 |
+
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>
|
17 |
+
|
18 |
+
<ul>
|
19 |
+
<li><a href="../../index.html">Home</a>
|
20 |
+
<li><a href="../../doc/manual.html">Manual</a>
|
21 |
+
<li><a href="https://github.com/marijnh/codemirror">Code</a>
|
22 |
+
</ul>
|
23 |
+
<ul>
|
24 |
+
<li><a href="../index.html">Language modes</a>
|
25 |
+
<li><a class=active href="#">HTML mixed</a>
|
26 |
+
</ul>
|
27 |
+
</div>
|
28 |
+
|
29 |
+
<article>
|
30 |
+
<h2>HTML mixed mode</h2>
|
31 |
+
<form><textarea id="code" name="code">
|
32 |
+
<html style="color: green">
|
33 |
+
<!-- this is a comment -->
|
34 |
+
<head>
|
35 |
+
<title>Mixed HTML Example</title>
|
36 |
+
<style type="text/css">
|
37 |
+
h1 {font-family: comic sans; color: #f0f;}
|
38 |
+
div {background: yellow !important;}
|
39 |
+
body {
|
40 |
+
max-width: 50em;
|
41 |
+
margin: 1em 2em 1em 5em;
|
42 |
+
}
|
43 |
+
</style>
|
44 |
+
</head>
|
45 |
+
<body>
|
46 |
+
<h1>Mixed HTML Example</h1>
|
47 |
+
<script>
|
48 |
+
function jsFunc(arg1, arg2) {
|
49 |
+
if (arg1 && arg2) document.body.innerHTML = "achoo";
|
50 |
+
}
|
51 |
+
</script>
|
52 |
+
</body>
|
53 |
+
</html>
|
54 |
+
</textarea></form>
|
55 |
+
<script>
|
56 |
+
// Define an extended mixed-mode that understands vbscript and
|
57 |
+
// leaves mustache/handlebars embedded templates in html mode
|
58 |
+
var mixedMode = {
|
59 |
+
name: "htmlmixed",
|
60 |
+
scriptTypes: [{matches: /\/x-handlebars-template|\/x-mustache/i,
|
61 |
+
mode: null},
|
62 |
+
{matches: /(text|application)\/(x-)?vb(a|script)/i,
|
63 |
+
mode: "vbscript"}]
|
64 |
+
};
|
65 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: mixedMode, tabMode: "indent"});
|
66 |
+
</script>
|
67 |
+
|
68 |
+
<p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p>
|
69 |
+
|
70 |
+
<p>It takes an optional mode configuration
|
71 |
+
option, <code>scriptTypes</code>, which can be used to add custom
|
72 |
+
behavior for specific <code><script type="..."></code> tags. If
|
73 |
+
given, it should hold an array of <code>{matches, mode}</code>
|
74 |
+
objects, where <code>matches</code> is a string or regexp that
|
75 |
+
matches the script type, and <code>mode</code> is
|
76 |
+
either <code>null</code>, for script types that should stay in
|
77 |
+
HTML mode, or a <a href="../../doc/manual.html#option_mode">mode
|
78 |
+
spec</a> corresponding to the mode that should be used for the
|
79 |
+
script.</p>
|
80 |
+
|
81 |
+
<p><strong>MIME types defined:</strong> <code>text/html</code>
|
82 |
+
(redefined, only takes effect if you load this parser after the
|
83 |
+
XML parser).</p>
|
84 |
+
|
85 |
+
</article>
|
assets/lib/codemirror/mode/http/http.js
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineMode("http", function() {
|
2 |
+
function failFirstLine(stream, state) {
|
3 |
+
stream.skipToEnd();
|
4 |
+
state.cur = header;
|
5 |
+
return "error";
|
6 |
+
}
|
7 |
+
|
8 |
+
function start(stream, state) {
|
9 |
+
if (stream.match(/^HTTP\/\d\.\d/)) {
|
10 |
+
state.cur = responseStatusCode;
|
11 |
+
return "keyword";
|
12 |
+
} else if (stream.match(/^[A-Z]+/) && /[ \t]/.test(stream.peek())) {
|
13 |
+
state.cur = requestPath;
|
14 |
+
return "keyword";
|
15 |
+
} else {
|
16 |
+
return failFirstLine(stream, state);
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
20 |
+
function responseStatusCode(stream, state) {
|
21 |
+
var code = stream.match(/^\d+/);
|
22 |
+
if (!code) return failFirstLine(stream, state);
|
23 |
+
|
24 |
+
state.cur = responseStatusText;
|
25 |
+
var status = Number(code[0]);
|
26 |
+
if (status >= 100 && status < 200) {
|
27 |
+
return "positive informational";
|
28 |
+
} else if (status >= 200 && status < 300) {
|
29 |
+
return "positive success";
|
30 |
+
} else if (status >= 300 && status < 400) {
|
31 |
+
return "positive redirect";
|
32 |
+
} else if (status >= 400 && status < 500) {
|
33 |
+
return "negative client-error";
|
34 |
+
} else if (status >= 500 && status < 600) {
|
35 |
+
return "negative server-error";
|
36 |
+
} else {
|
37 |
+
return "error";
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
function responseStatusText(stream, state) {
|
42 |
+
stream.skipToEnd();
|
43 |
+
state.cur = header;
|
44 |
+
return null;
|
45 |
+
}
|
46 |
+
|
47 |
+
function requestPath(stream, state) {
|
48 |
+
stream.eatWhile(/\S/);
|
49 |
+
state.cur = requestProtocol;
|
50 |
+
return "string-2";
|
51 |
+
}
|
52 |
+
|
53 |
+
function requestProtocol(stream, state) {
|
54 |
+
if (stream.match(/^HTTP\/\d\.\d$/)) {
|
55 |
+
state.cur = header;
|
56 |
+
return "keyword";
|
57 |
+
} else {
|
58 |
+
return failFirstLine(stream, state);
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
function header(stream) {
|
63 |
+
if (stream.sol() && !stream.eat(/[ \t]/)) {
|
64 |
+
if (stream.match(/^.*?:/)) {
|
65 |
+
return "atom";
|
66 |
+
} else {
|
67 |
+
stream.skipToEnd();
|
68 |
+
return "error";
|
69 |
+
}
|
70 |
+
} else {
|
71 |
+
stream.skipToEnd();
|
72 |
+
return "string";
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
function body(stream) {
|
77 |
+
stream.skipToEnd();
|
78 |
+
return null;
|
79 |
+
}
|
80 |
+
|
81 |
+
return {
|
82 |
+
token: function(stream, state) {
|
83 |
+
var cur = state.cur;
|
84 |
+
if (cur != header && cur != body && stream.eatSpace()) return null;
|
85 |
+
return cur(stream, state);
|
86 |
+
},
|
87 |
+
|
88 |
+
blankLine: function(state) {
|
89 |
+
state.cur = body;
|
90 |
+
},
|
91 |
+
|
92 |
+
startState: function() {
|
93 |
+
return {cur: start};
|
94 |
+
}
|
95 |
+
};
|
96 |
+
});
|
97 |
+
|
98 |
+
CodeMirror.defineMIME("message/http", "http");
|
assets/lib/codemirror/mode/http/index.html
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
|
3 |
+
<title>CodeMirror: HTTP mode</title>
|
4 |
+
<meta charset="utf-8"/>
|
5 |
+
<link rel=stylesheet href="../../doc/docs.css">
|
6 |
+
|
7 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
8 |
+
<script src="../../lib/codemirror.js"></script>
|
9 |
+
<script src="http.js"></script>
|
10 |
+
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
11 |
+
<div id=nav>
|
12 |
+
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>
|
13 |
+
|
14 |
+
<ul>
|
15 |
+
<li><a href="../../index.html">Home</a>
|
16 |
+
<li><a href="../../doc/manual.html">Manual</a>
|
17 |
+
<li><a href="https://github.com/marijnh/codemirror">Code</a>
|
18 |
+
</ul>
|
19 |
+
<ul>
|
20 |
+
<li><a href="../index.html">Language modes</a>
|
21 |
+
<li><a class=active href="#">HTTP</a>
|
22 |
+
</ul>
|
23 |
+
</div>
|
24 |
+
|
25 |
+
<article>
|
26 |
+
<h2>HTTP mode</h2>
|
27 |
+
|
28 |
+
|
29 |
+
<div><textarea id="code" name="code">
|
30 |
+
POST /somewhere HTTP/1.1
|
31 |
+
Host: example.com
|
32 |
+
If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
|
33 |
+
Content-Type: application/x-www-form-urlencoded;
|
34 |
+
charset=utf-8
|
35 |
+
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.11 (KHTML, like Gecko) Ubuntu/12.04 Chromium/20.0.1132.47 Chrome/20.0.1132.47 Safari/536.11
|
36 |
+
|
37 |
+
This is the request body!
|
38 |
+
</textarea></div>
|
39 |
+
|
40 |
+
<script>
|
41 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
|
42 |
+
</script>
|
43 |
+
|
44 |
+
<p><strong>MIME types defined:</strong> <code>message/http</code>.</p>
|
45 |
+
</article>
|
assets/lib/codemirror/mode/index.html
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
|
3 |
+
<title>CodeMirror: Language Modes</title>
|
4 |
+
<meta charset="utf-8"/>
|
5 |
+
<link rel=stylesheet href="../doc/docs.css">
|
6 |
+
|
7 |
+
<div id=nav>
|
8 |
+
<a href="http://codemirror.net"><img id=logo src="../doc/logo.png"></a>
|
9 |
+
|
10 |
+
<ul>
|
11 |
+
<li><a href="../index.html">Home</a>
|
12 |
+
<li><a href="../doc/manual.html">Manual</a>
|
13 |
+
<li><a href="https://github.com/marijnh/codemirror">Code</a>
|
14 |
+
</ul>
|
15 |
+
<ul>
|
16 |
+
<li><a class=active href="#">Language modes</a>
|
17 |
+
</ul>
|
18 |
+
</div>
|
19 |
+
|
20 |
+
<article>
|
21 |
+
|
22 |
+
<h2>Language modes</h2>
|
23 |
+
|
24 |
+
<p>This is a list of every mode in the distribution. Each mode lives
|
25 |
+
in a subdirectory of the <code>mode/</code> directory, and typically
|
26 |
+
defines a single JavaScript file that implements the mode. Loading
|
27 |
+
such file will make the language available to CodeMirror, through
|
28 |
+
the <a href="manual.html#option_mode"><code>mode</code></a>
|
29 |
+
option.</p>
|
30 |
+
|
31 |
+
<div style="-webkit-columns: 100px 2; -moz-columns: 100px 2; columns: 100px 2;">
|
32 |
+
<ul>
|
33 |
+
<li><a href="apl/index.html">APL</a></li>
|
34 |
+
<li><a href="asterisk/index.html">Asterisk dialplan</a></li>
|
35 |
+
<li><a href="clike/index.html">C, C++, C#</a></li>
|
36 |
+
<li><a href="clojure/index.html">Clojure</a></li>
|
37 |
+
<li><a href="cobol/index.html">COBOL</a></li>
|
38 |
+
<li><a href="coffeescript/index.html">CoffeeScript</a></li>
|
39 |
+
<li><a href="commonlisp/index.html">Common Lisp</a></li>
|
40 |
+
<li><a href="css/index.html">CSS</a></li>
|
41 |
+
<li><a href="python/index.html">Cython</a></li>
|
42 |
+
<li><a href="d/index.html">D</a></li>
|
43 |
+
<li><a href="diff/index.html">diff</a></li>
|
44 |
+
<li><a href="dtd/index.html">DTD</a></li>
|
45 |
+
<li><a href="ecl/index.html">ECL</a></li>
|
46 |
+
<li><a href="erlang/index.html">Erlang</a></li>
|
47 |
+
<li><a href="fortran/index.html">Fortran</a></li>
|
48 |
+
<li><a href="gas/index.html">Gas</a> (AT&T-style assembly)</li>
|
49 |
+
<li><a href="go/index.html">Go</a></li>
|
50 |
+
<li><a href="groovy/index.html">Groovy</a></li>
|
51 |
+
<li><a href="haml/index.html">HAML</a></li>
|
52 |
+
<li><a href="haskell/index.html">Haskell</a></li>
|
53 |
+
<li><a href="haxe/index.html">Haxe</a></li>
|
54 |
+
<li><a href="htmlembedded/index.html">HTML embedded scripts</a></li>
|
55 |
+
<li><a href="htmlmixed/index.html">HTML mixed-mode</a></li>
|
56 |
+
<li><a href="http/index.html">HTTP</a></li>
|
57 |
+
<li><a href="clike/index.html">Java</a></li>
|
58 |
+
<li><a href="jade/index.html">Jade</a></li>
|
59 |
+
<li><a href="javascript/index.html">JavaScript</a></li>
|
60 |
+
<li><a href="jinja2/index.html">Jinja2</a></li>
|
61 |
+
<li><a href="less/index.html">LESS</a></li>
|
62 |
+
<li><a href="livescript/index.html">LiveScript</a></li>
|
63 |
+
<li><a href="lua/index.html">Lua</a></li>
|
64 |
+
<li><a href="markdown/index.html">Markdown</a> (<a href="gfm/index.html">GitHub-flavour</a>)</li>
|
65 |
+
<li><a href="mirc/index.html">mIRC</a></li>
|
66 |
+
<li><a href="nginx/index.html">Nginx</a></li>
|
67 |
+
<li><a href="ntriples/index.html">NTriples</a></li>
|
68 |
+
<li><a href="ocaml/index.html">OCaml</a></li>
|
69 |
+
<li><a href="octave/index.html">Octave</a> (MATLAB)</li>
|
70 |
+
<li><a href="pascal/index.html">Pascal</a></li>
|
71 |
+
<li><a href="perl/index.html">Perl</a></li>
|
72 |
+
<li><a href="php/index.html">PHP</a></li>
|
73 |
+
<li><a href="pig/index.html">Pig Latin</a></li>
|
74 |
+
<li><a href="properties/index.html">Properties files</a></li>
|
75 |
+
<li><a href="python/index.html">Python</a></li>
|
76 |
+
<li><a href="q/index.html">Q</a></li>
|
77 |
+
<li><a href="r/index.html">R</a></li>
|
78 |
+
<li>RPM <a href="rpm/spec/index.html">spec</a> and <a href="rpm/changes/index.html">changelog</a></li>
|
79 |
+
<li><a href="rst/index.html">reStructuredText</a></li>
|
80 |
+
<li><a href="ruby/index.html">Ruby</a></li>
|
81 |
+
<li><a href="rust/index.html">Rust</a></li>
|
82 |
+
<li><a href="sass/index.html">Sass</a></li>
|
83 |
+
<li><a href="clike/scala.html">Scala</a></li>
|
84 |
+
<li><a href="scheme/index.html">Scheme</a></li>
|
85 |
+
<li><a href="css/scss.html">SCSS</a></li>
|
86 |
+
<li><a href="shell/index.html">Shell</a></li>
|
87 |
+
<li><a href="sieve/index.html">Sieve</a></li>
|
88 |
+
<li><a href="smalltalk/index.html">Smalltalk</a></li>
|
89 |
+
<li><a href="smarty/index.html">Smarty</a></li>
|
90 |
+
<li><a href="smartymixed/index.html">Smarty/HTML mixed</a></li>
|
91 |
+
<li><a href="sql/index.html">SQL</a> (several dialects)</li>
|
92 |
+
<li><a href="sparql/index.html">SPARQL</a></li>
|
93 |
+
<li><a href="stex/index.html">sTeX, LaTeX</a></li>
|
94 |
+
<li><a href="tcl/index.html">Tcl</a></li>
|
95 |
+
<li><a href="tiddlywiki/index.html">Tiddlywiki</a></li>
|
96 |
+
<li><a href="tiki/index.html">Tiki wiki</a></li>
|
97 |
+
<li><a href="toml/index.html">TOML</a></li>
|
98 |
+
<li><a href="turtle/index.html">Turtle</a></li>
|
99 |
+
<li><a href="vb/index.html">VB.NET</a></li>
|
100 |
+
<li><a href="vbscript/index.html">VBScript</a></li>
|
101 |
+
<li><a href="velocity/index.html">Velocity</a></li>
|
102 |
+
<li><a href="verilog/index.html">Verilog</a></li>
|
103 |
+
<li><a href="xml/index.html">XML/HTML</a></li>
|
104 |
+
<li><a href="xquery/index.html">XQuery</a></li>
|
105 |
+
<li><a href="yaml/index.html">YAML</a></li>
|
106 |
+
<li><a href="z80/index.html">Z80</a></li>
|
107 |
+
</ul>
|
108 |
+
</div>
|
109 |
+
|
110 |
+
</article>
|
assets/lib/codemirror/mode/javascript/index.html
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
|
3 |
+
<title>CodeMirror: JavaScript mode</title>
|
4 |
+
<meta charset="utf-8"/>
|
5 |
+
<link rel=stylesheet href="../../doc/docs.css">
|
6 |
+
|
7 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
8 |
+
<script src="../../lib/codemirror.js"></script>
|
9 |
+
<script src="../../addon/edit/matchbrackets.js"></script>
|
10 |
+
<script src="../../addon/comment/continuecomment.js"></script>
|
11 |
+
<script src="../../addon/comment/comment.js"></script>
|
12 |
+
<script src="javascript.js"></script>
|
13 |
+
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
14 |
+
<div id=nav>
|
15 |
+
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>
|
16 |
+
|
17 |
+
<ul>
|
18 |
+
<li><a href="../../index.html">Home</a>
|
19 |
+
<li><a href="../../doc/manual.html">Manual</a>
|
20 |
+
<li><a href="https://github.com/marijnh/codemirror">Code</a>
|
21 |
+
</ul>
|
22 |
+
<ul>
|
23 |
+
<li><a href="../index.html">Language modes</a>
|
24 |
+
<li><a class=active href="#">JavaScript</a>
|
25 |
+
</ul>
|
26 |
+
</div>
|
27 |
+
|
28 |
+
<article>
|
29 |
+
<h2>JavaScript mode</h2>
|
30 |
+
|
31 |
+
|
32 |
+
<div><textarea id="code" name="code">
|
33 |
+
// Demo code (the actual new parser character stream implementation)
|
34 |
+
|
35 |
+
function StringStream(string) {
|
36 |
+
this.pos = 0;
|
37 |
+
this.string = string;
|
38 |
+
}
|
39 |
+
|
40 |
+
StringStream.prototype = {
|
41 |
+
done: function() {return this.pos >= this.string.length;},
|
42 |
+
peek: function() {return this.string.charAt(this.pos);},
|
43 |
+
next: function() {
|
44 |
+
if (this.pos < this.string.length)
|
45 |
+
return this.string.charAt(this.pos++);
|
46 |
+
},
|
47 |
+
eat: function(match) {
|
48 |
+
var ch = this.string.charAt(this.pos);
|
49 |
+
if (typeof match == "string") var ok = ch == match;
|
50 |
+
else var ok = ch && match.test ? match.test(ch) : match(ch);
|
51 |
+
if (ok) {this.pos++; return ch;}
|
52 |
+
},
|
53 |
+
eatWhile: function(match) {
|
54 |
+
var start = this.pos;
|
55 |
+
while (this.eat(match));
|
56 |
+
if (this.pos > start) return this.string.slice(start, this.pos);
|
57 |
+
},
|
58 |
+
backUp: function(n) {this.pos -= n;},
|
59 |
+
column: function() {return this.pos;},
|
60 |
+
eatSpace: function() {
|
61 |
+
var start = this.pos;
|
62 |
+
while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
|
63 |
+
return this.pos - start;
|
64 |
+
},
|
65 |
+
match: function(pattern, consume, caseInsensitive) {
|
66 |
+
if (typeof pattern == "string") {
|
67 |
+
function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
|
68 |
+
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
|
69 |
+
if (consume !== false) this.pos += str.length;
|
70 |
+
return true;
|
71 |
+
}
|
72 |
+
}
|
73 |
+
else {
|
74 |
+
var match = this.string.slice(this.pos).match(pattern);
|
75 |
+
if (match && consume !== false) this.pos += match[0].length;
|
76 |
+
return match;
|
77 |
+
}
|
78 |
+
}
|
79 |
+
};
|
80 |
+
</textarea></div>
|
81 |
+
|
82 |
+
<script>
|
83 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
84 |
+
lineNumbers: true,
|
85 |
+
matchBrackets: true,
|
86 |
+
continueComments: "Enter",
|
87 |
+
extraKeys: {"Ctrl-Q": "toggleComment"}
|
88 |
+
});
|
89 |
+
</script>
|
90 |
+
|
91 |
+
<p>
|
92 |
+
JavaScript mode supports a two configuration
|
93 |
+
options:
|
94 |
+
<ul>
|
95 |
+
<li><code>json</code> which will set the mode to expect JSON
|
96 |
+
data rather than a JavaScript program.</li>
|
97 |
+
<li><code>typescript</code> which will activate additional
|
98 |
+
syntax highlighting and some other things for TypeScript code
|
99 |
+
(<a href="typescript.html">demo</a>).</li>
|
100 |
+
<li><code>statementIndent</code> which (given a number) will
|
101 |
+
determine the amount of indentation to use for statements
|
102 |
+
continued on a new line.</li>
|
103 |
+
</ul>
|
104 |
+
</p>
|
105 |
+
|
106 |
+
<p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>, <code>text/typescript</code>, <code>application/typescript</code>.</p>
|
107 |
+
</article>
|
assets/lib/codemirror/mode/javascript/javascript.js
ADDED
@@ -0,0 +1,480 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// TODO actually recognize syntax of TypeScript constructs
|
2 |
+
|
3 |
+
CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
4 |
+
var indentUnit = config.indentUnit;
|
5 |
+
var statementIndent = parserConfig.statementIndent;
|
6 |
+
var jsonMode = parserConfig.json;
|
7 |
+
var isTS = parserConfig.typescript;
|
8 |
+
|
9 |
+
// Tokenizer
|
10 |
+
|
11 |
+
var keywords = function(){
|
12 |
+
function kw(type) {return {type: type, style: "keyword"};}
|
13 |
+
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
|
14 |
+
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
|
15 |
+
|
16 |
+
var jsKeywords = {
|
17 |
+
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
18 |
+
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
|
19 |
+
"var": kw("var"), "const": kw("var"), "let": kw("var"),
|
20 |
+
"function": kw("function"), "catch": kw("catch"),
|
21 |
+
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
22 |
+
"in": operator, "typeof": operator, "instanceof": operator,
|
23 |
+
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
|
24 |
+
"this": kw("this")
|
25 |
+
};
|
26 |
+
|
27 |
+
// Extend the 'normal' keywords with the TypeScript language extensions
|
28 |
+
if (isTS) {
|
29 |
+
var type = {type: "variable", style: "variable-3"};
|
30 |
+
var tsKeywords = {
|
31 |
+
// object-like things
|
32 |
+
"interface": kw("interface"),
|
33 |
+
"class": kw("class"),
|
34 |
+
"extends": kw("extends"),
|
35 |
+
"constructor": kw("constructor"),
|
36 |
+
|
37 |
+
// scope modifiers
|
38 |
+
"public": kw("public"),
|
39 |
+
"private": kw("private"),
|
40 |
+
"protected": kw("protected"),
|
41 |
+
"static": kw("static"),
|
42 |
+
|
43 |
+
"super": kw("super"),
|
44 |
+
|
45 |
+
// types
|
46 |
+
"string": type, "number": type, "bool": type, "any": type
|
47 |
+
};
|
48 |
+
|
49 |
+
for (var attr in tsKeywords) {
|
50 |
+
jsKeywords[attr] = tsKeywords[attr];
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
return jsKeywords;
|
55 |
+
}();
|
56 |
+
|
57 |
+
var isOperatorChar = /[+\-*&%=<>!?|~^]/;
|
58 |
+
|
59 |
+
function chain(stream, state, f) {
|
60 |
+
state.tokenize = f;
|
61 |
+
return f(stream, state);
|
62 |
+
}
|
63 |
+
|
64 |
+
function nextUntilUnescaped(stream, end) {
|
65 |
+
var escaped = false, next;
|
66 |
+
while ((next = stream.next()) != null) {
|
67 |
+
if (next == end && !escaped)
|
68 |
+
return false;
|
69 |
+
escaped = !escaped && next == "\\";
|
70 |
+
}
|
71 |
+
return escaped;
|
72 |
+
}
|
73 |
+
|
74 |
+
// Used as scratch variables to communicate multiple values without
|
75 |
+
// consing up tons of objects.
|
76 |
+
var type, content;
|
77 |
+
function ret(tp, style, cont) {
|
78 |
+
type = tp; content = cont;
|
79 |
+
return style;
|
80 |
+
}
|
81 |
+
function jsTokenBase(stream, state) {
|
82 |
+
var ch = stream.next();
|
83 |
+
if (ch == '"' || ch == "'")
|
84 |
+
return chain(stream, state, jsTokenString(ch));
|
85 |
+
else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/))
|
86 |
+
return ret("number", "number");
|
87 |
+
else if (/[\[\]{}\(\),;\:\.]/.test(ch))
|
88 |
+
return ret(ch);
|
89 |
+
else if (ch == "0" && stream.eat(/x/i)) {
|
90 |
+
stream.eatWhile(/[\da-f]/i);
|
91 |
+
return ret("number", "number");
|
92 |
+
}
|
93 |
+
else if (/\d/.test(ch)) {
|
94 |
+
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
|
95 |
+
return ret("number", "number");
|
96 |
+
}
|
97 |
+
else if (ch == "/") {
|
98 |
+
if (stream.eat("*")) {
|
99 |
+
return chain(stream, state, jsTokenComment);
|
100 |
+
}
|
101 |
+
else if (stream.eat("/")) {
|
102 |
+
stream.skipToEnd();
|
103 |
+
return ret("comment", "comment");
|
104 |
+
}
|
105 |
+
else if (state.lastType == "operator" || state.lastType == "keyword c" ||
|
106 |
+
/^[\[{}\(,;:]$/.test(state.lastType)) {
|
107 |
+
nextUntilUnescaped(stream, "/");
|
108 |
+
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
|
109 |
+
return ret("regexp", "string-2");
|
110 |
+
}
|
111 |
+
else {
|
112 |
+
stream.eatWhile(isOperatorChar);
|
113 |
+
return ret("operator", null, stream.current());
|
114 |
+
}
|
115 |
+
}
|
116 |
+
else if (ch == "#") {
|
117 |
+
stream.skipToEnd();
|
118 |
+
return ret("error", "error");
|
119 |
+
}
|
120 |
+
else if (isOperatorChar.test(ch)) {
|
121 |
+
stream.eatWhile(isOperatorChar);
|
122 |
+
return ret("operator", null, stream.current());
|
123 |
+
}
|
124 |
+
else {
|
125 |
+
stream.eatWhile(/[\w\$_]/);
|
126 |
+
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
|
127 |
+
return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
|
128 |
+
ret("variable", "variable", word);
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
function jsTokenString(quote) {
|
133 |
+
return function(stream, state) {
|
134 |
+
if (!nextUntilUnescaped(stream, quote))
|
135 |
+
state.tokenize = jsTokenBase;
|
136 |
+
return ret("string", "string");
|
137 |
+
};
|
138 |
+
}
|
139 |
+
|
140 |
+
function jsTokenComment(stream, state) {
|
141 |
+
var maybeEnd = false, ch;
|
142 |
+
while (ch = stream.next()) {
|
143 |
+
if (ch == "/" && maybeEnd) {
|
144 |
+
state.tokenize = jsTokenBase;
|
145 |
+
break;
|
146 |
+
}
|
147 |
+
maybeEnd = (ch == "*");
|
148 |
+
}
|
149 |
+
return ret("comment", "comment");
|
150 |
+
}
|
151 |
+
|
152 |
+
// Parser
|
153 |
+
|
154 |
+
var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true};
|
155 |
+
|
156 |
+
function JSLexical(indented, column, type, align, prev, info) {
|
157 |
+
this.indented = indented;
|
158 |
+
this.column = column;
|
159 |
+
this.type = type;
|
160 |
+
this.prev = prev;
|
161 |
+
this.info = info;
|
162 |
+
if (align != null) this.align = align;
|
163 |
+
}
|
164 |
+
|
165 |
+
function inScope(state, varname) {
|
166 |
+
for (var v = state.localVars; v; v = v.next)
|
167 |
+
if (v.name == varname) return true;
|
168 |
+
}
|
169 |
+
|
170 |
+
function parseJS(state, style, type, content, stream) {
|
171 |
+
var cc = state.cc;
|
172 |
+
// Communicate our context to the combinators.
|
173 |
+
// (Less wasteful than consing up a hundred closures on every call.)
|
174 |
+
cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
|
175 |
+
|
176 |
+
if (!state.lexical.hasOwnProperty("align"))
|
177 |
+
state.lexical.align = true;
|
178 |
+
|
179 |
+
while(true) {
|
180 |
+
var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
|
181 |
+
if (combinator(type, content)) {
|
182 |
+
while(cc.length && cc[cc.length - 1].lex)
|
183 |
+
cc.pop()();
|
184 |
+
if (cx.marked) return cx.marked;
|
185 |
+
if (type == "variable" && inScope(state, content)) return "variable-2";
|
186 |
+
return style;
|
187 |
+
}
|
188 |
+
}
|
189 |
+
}
|
190 |
+
|
191 |
+
// Combinator utils
|
192 |
+
|
193 |
+
var cx = {state: null, column: null, marked: null, cc: null};
|
194 |
+
function pass() {
|
195 |
+
for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
|
196 |
+
}
|
197 |
+
function cont() {
|
198 |
+
pass.apply(null, arguments);
|
199 |
+
return true;
|
200 |
+
}
|
201 |
+
function register(varname) {
|
202 |
+
function inList(list) {
|
203 |
+
for (var v = list; v; v = v.next)
|
204 |
+
if (v.name == varname) return true;
|
205 |
+
return false;
|
206 |
+
}
|
207 |
+
var state = cx.state;
|
208 |
+
if (state.context) {
|
209 |
+
cx.marked = "def";
|
210 |
+
if (inList(state.localVars)) return;
|
211 |
+
state.localVars = {name: varname, next: state.localVars};
|
212 |
+
} else {
|
213 |
+
if (inList(state.globalVars)) return;
|
214 |
+
state.globalVars = {name: varname, next: state.globalVars};
|
215 |
+
}
|
216 |
+
}
|
217 |
+
|
218 |
+
// Combinators
|
219 |
+
|
220 |
+
var defaultVars = {name: "this", next: {name: "arguments"}};
|
221 |
+
function pushcontext() {
|
222 |
+
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
|
223 |
+
cx.state.localVars = defaultVars;
|
224 |
+
}
|
225 |
+
function popcontext() {
|
226 |
+
cx.state.localVars = cx.state.context.vars;
|
227 |
+
cx.state.context = cx.state.context.prev;
|
228 |
+
}
|
229 |
+
function pushlex(type, info) {
|
230 |
+
var result = function() {
|
231 |
+
var state = cx.state, indent = state.indented;
|
232 |
+
if (state.lexical.type == "stat") indent = state.lexical.indented;
|
233 |
+
state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
|
234 |
+
};
|
235 |
+
result.lex = true;
|
236 |
+
return result;
|
237 |
+
}
|
238 |
+
function poplex() {
|
239 |
+
var state = cx.state;
|
240 |
+
if (state.lexical.prev) {
|
241 |
+
if (state.lexical.type == ")")
|
242 |
+
state.indented = state.lexical.indented;
|
243 |
+
state.lexical = state.lexical.prev;
|
244 |
+
}
|
245 |
+
}
|
246 |
+
poplex.lex = true;
|
247 |
+
|
248 |
+
function expect(wanted) {
|
249 |
+
return function(type) {
|
250 |
+
if (type == wanted) return cont();
|
251 |
+
else if (wanted == ";") return pass();
|
252 |
+
else return cont(arguments.callee);
|
253 |
+
};
|
254 |
+
}
|
255 |
+
|
256 |
+
function statement(type) {
|
257 |
+
if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
|
258 |
+
if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
|
259 |
+
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
|
260 |
+
if (type == "{") return cont(pushlex("}"), block, poplex);
|
261 |
+
if (type == ";") return cont();
|
262 |
+
if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse);
|
263 |
+
if (type == "function") return cont(functiondef);
|
264 |
+
if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
|
265 |
+
poplex, statement, poplex);
|
266 |
+
if (type == "variable") return cont(pushlex("stat"), maybelabel);
|
267 |
+
if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
|
268 |
+
block, poplex, poplex);
|
269 |
+
if (type == "case") return cont(expression, expect(":"));
|
270 |
+
if (type == "default") return cont(expect(":"));
|
271 |
+
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
|
272 |
+
statement, poplex, popcontext);
|
273 |
+
return pass(pushlex("stat"), expression, expect(";"), poplex);
|
274 |
+
}
|
275 |
+
function expression(type) {
|
276 |
+
return expressionInner(type, false);
|
277 |
+
}
|
278 |
+
function expressionNoComma(type) {
|
279 |
+
return expressionInner(type, true);
|
280 |
+
}
|
281 |
+
function expressionInner(type, noComma) {
|
282 |
+
var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
|
283 |
+
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
|
284 |
+
if (type == "function") return cont(functiondef);
|
285 |
+
if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
|
286 |
+
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
|
287 |
+
if (type == "operator") return cont(noComma ? expressionNoComma : expression);
|
288 |
+
if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop);
|
289 |
+
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop);
|
290 |
+
return cont();
|
291 |
+
}
|
292 |
+
function maybeexpression(type) {
|
293 |
+
if (type.match(/[;\}\)\],]/)) return pass();
|
294 |
+
return pass(expression);
|
295 |
+
}
|
296 |
+
function maybeexpressionNoComma(type) {
|
297 |
+
if (type.match(/[;\}\)\],]/)) return pass();
|
298 |
+
return pass(expressionNoComma);
|
299 |
+
}
|
300 |
+
|
301 |
+
function maybeoperatorComma(type, value) {
|
302 |
+
if (type == ",") return cont(expression);
|
303 |
+
return maybeoperatorNoComma(type, value, false);
|
304 |
+
}
|
305 |
+
function maybeoperatorNoComma(type, value, noComma) {
|
306 |
+
var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
|
307 |
+
var expr = noComma == false ? expression : expressionNoComma;
|
308 |
+
if (type == "operator") {
|
309 |
+
if (/\+\+|--/.test(value)) return cont(me);
|
310 |
+
if (value == "?") return cont(expression, expect(":"), expr);
|
311 |
+
return cont(expr);
|
312 |
+
}
|
313 |
+
if (type == ";") return;
|
314 |
+
if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me);
|
315 |
+
if (type == ".") return cont(property, me);
|
316 |
+
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
|
317 |
+
}
|
318 |
+
function maybelabel(type) {
|
319 |
+
if (type == ":") return cont(poplex, statement);
|
320 |
+
return pass(maybeoperatorComma, expect(";"), poplex);
|
321 |
+
}
|
322 |
+
function property(type) {
|
323 |
+
if (type == "variable") {cx.marked = "property"; return cont();}
|
324 |
+
}
|
325 |
+
function objprop(type, value) {
|
326 |
+
if (type == "variable") {
|
327 |
+
cx.marked = "property";
|
328 |
+
if (value == "get" || value == "set") return cont(getterSetter);
|
329 |
+
} else if (type == "number" || type == "string") {
|
330 |
+
cx.marked = type + " property";
|
331 |
+
}
|
332 |
+
if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expressionNoComma);
|
333 |
+
}
|
334 |
+
function getterSetter(type) {
|
335 |
+
if (type == ":") return cont(expression);
|
336 |
+
if (type != "variable") return cont(expect(":"), expression);
|
337 |
+
cx.marked = "property";
|
338 |
+
return cont(functiondef);
|
339 |
+
}
|
340 |
+
function commasep(what, end) {
|
341 |
+
function proceed(type) {
|
342 |
+
if (type == ",") {
|
343 |
+
var lex = cx.state.lexical;
|
344 |
+
if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
|
345 |
+
return cont(what, proceed);
|
346 |
+
}
|
347 |
+
if (type == end) return cont();
|
348 |
+
return cont(expect(end));
|
349 |
+
}
|
350 |
+
return function(type) {
|
351 |
+
if (type == end) return cont();
|
352 |
+
else return pass(what, proceed);
|
353 |
+
};
|
354 |
+
}
|
355 |
+
function block(type) {
|
356 |
+
if (type == "}") return cont();
|
357 |
+
return pass(statement, block);
|
358 |
+
}
|
359 |
+
function maybetype(type) {
|
360 |
+
if (type == ":") return cont(typedef);
|
361 |
+
return pass();
|
362 |
+
}
|
363 |
+
function typedef(type) {
|
364 |
+
if (type == "variable"){cx.marked = "variable-3"; return cont();}
|
365 |
+
return pass();
|
366 |
+
}
|
367 |
+
function vardef1(type, value) {
|
368 |
+
if (type == "variable") {
|
369 |
+
register(value);
|
370 |
+
return isTS ? cont(maybetype, vardef2) : cont(vardef2);
|
371 |
+
}
|
372 |
+
return pass();
|
373 |
+
}
|
374 |
+
function vardef2(type, value) {
|
375 |
+
if (value == "=") return cont(expressionNoComma, vardef2);
|
376 |
+
if (type == ",") return cont(vardef1);
|
377 |
+
}
|
378 |
+
function maybeelse(type, value) {
|
379 |
+
if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex);
|
380 |
+
}
|
381 |
+
function forspec1(type) {
|
382 |
+
if (type == "var") return cont(vardef1, expect(";"), forspec2);
|
383 |
+
if (type == ";") return cont(forspec2);
|
384 |
+
if (type == "variable") return cont(formaybein);
|
385 |
+
return pass(expression, expect(";"), forspec2);
|
386 |
+
}
|
387 |
+
function formaybein(_type, value) {
|
388 |
+
if (value == "in") return cont(expression);
|
389 |
+
return cont(maybeoperatorComma, forspec2);
|
390 |
+
}
|
391 |
+
function forspec2(type, value) {
|
392 |
+
if (type == ";") return cont(forspec3);
|
393 |
+
if (value == "in") return cont(expression);
|
394 |
+
return pass(expression, expect(";"), forspec3);
|
395 |
+
}
|
396 |
+
function forspec3(type) {
|
397 |
+
if (type != ")") cont(expression);
|
398 |
+
}
|
399 |
+
function functiondef(type, value) {
|
400 |
+
if (type == "variable") {register(value); return cont(functiondef);}
|
401 |
+
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
|
402 |
+
}
|
403 |
+
function funarg(type, value) {
|
404 |
+
if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
|
405 |
+
}
|
406 |
+
|
407 |
+
// Interface
|
408 |
+
|
409 |
+
return {
|
410 |
+
startState: function(basecolumn) {
|
411 |
+
return {
|
412 |
+
tokenize: jsTokenBase,
|
413 |
+
lastType: null,
|
414 |
+
cc: [],
|
415 |
+
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
|
416 |
+
localVars: parserConfig.localVars,
|
417 |
+
globalVars: parserConfig.globalVars,
|
418 |
+
context: parserConfig.localVars && {vars: parserConfig.localVars},
|
419 |
+
indented: 0
|
420 |
+
};
|
421 |
+
},
|
422 |
+
|
423 |
+
token: function(stream, state) {
|
424 |
+
if (stream.sol()) {
|
425 |
+
if (!state.lexical.hasOwnProperty("align"))
|
426 |
+
state.lexical.align = false;
|
427 |
+
state.indented = stream.indentation();
|
428 |
+
}
|
429 |
+
if (state.tokenize != jsTokenComment && stream.eatSpace()) return null;
|
430 |
+
var style = state.tokenize(stream, state);
|
431 |
+
if (type == "comment") return style;
|
432 |
+
state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
|
433 |
+
return parseJS(state, style, type, content, stream);
|
434 |
+
},
|
435 |
+
|
436 |
+
indent: function(state, textAfter) {
|
437 |
+
if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
|
438 |
+
if (state.tokenize != jsTokenBase) return 0;
|
439 |
+
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
|
440 |
+
// Kludge to prevent 'maybelse' from blocking lexical scope pops
|
441 |
+
for (var i = state.cc.length - 1; i >= 0; --i) {
|
442 |
+
var c = state.cc[i];
|
443 |
+
if (c == poplex) lexical = lexical.prev;
|
444 |
+
else if (c != maybeelse || /^else\b/.test(textAfter)) break;
|
445 |
+
}
|
446 |
+
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
|
447 |
+
if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
|
448 |
+
lexical = lexical.prev;
|
449 |
+
var type = lexical.type, closing = firstChar == type;
|
450 |
+
|
451 |
+
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
|
452 |
+
else if (type == "form" && firstChar == "{") return lexical.indented;
|
453 |
+
else if (type == "form") return lexical.indented + indentUnit;
|
454 |
+
else if (type == "stat")
|
455 |
+
return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? statementIndent || indentUnit : 0);
|
456 |
+
else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
|
457 |
+
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
|
458 |
+
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
|
459 |
+
else return lexical.indented + (closing ? 0 : indentUnit);
|
460 |
+
},
|
461 |
+
|
462 |
+
electricChars: ":{}",
|
463 |
+
blockCommentStart: jsonMode ? null : "/*",
|
464 |
+
blockCommentEnd: jsonMode ? null : "*/",
|
465 |
+
lineComment: jsonMode ? null : "//",
|
466 |
+
fold: "brace",
|
467 |
+
|
468 |
+
helperType: jsonMode ? "json" : "javascript",
|
469 |
+
jsonMode: jsonMode
|
470 |
+
};
|
471 |
+
});
|
472 |
+
|
473 |
+
CodeMirror.defineMIME("text/javascript", "javascript");
|
474 |
+
CodeMirror.defineMIME("text/ecmascript", "javascript");
|
475 |
+
CodeMirror.defineMIME("application/javascript", "javascript");
|
476 |
+
CodeMirror.defineMIME("application/ecmascript", "javascript");
|
477 |
+
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
|
478 |
+
CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
|
479 |
+
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
|
480 |
+
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
|
assets/lib/codemirror/mode/javascript/test.js
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
var mode = CodeMirror.getMode({indentUnit: 2}, "javascript");
|
3 |
+
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
|
4 |
+
|
5 |
+
MT("locals",
|
6 |
+
"[keyword function] [variable foo]([def a], [def b]) { [keyword var] [def c] = [number 10]; [keyword return] [variable-2 a] + [variable-2 c] + [variable d]; }");
|
7 |
+
|
8 |
+
MT("comma-and-binop",
|
9 |
+
"[keyword function](){ [keyword var] [def x] = [number 1] + [number 2], [def y]; }");
|
10 |
+
})();
|
assets/lib/codemirror/mode/javascript/typescript.html
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
|
3 |
+
<title>CodeMirror: TypeScript mode</title>
|
4 |
+
<meta charset="utf-8"/>
|
5 |
+
<link rel=stylesheet href="../../doc/docs.css">
|
6 |
+
|
7 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
8 |
+
<script src="../../lib/codemirror.js"></script>
|
9 |
+
<script src="javascript.js"></script>
|
10 |
+
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
11 |
+
<div id=nav>
|
12 |
+
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>
|
13 |
+
|
14 |
+
<ul>
|
15 |
+
<li><a href="../../index.html">Home</a>
|
16 |
+
<li><a href="../../doc/manual.html">Manual</a>
|
17 |
+
<li><a href="https://github.com/marijnh/codemirror">Code</a>
|
18 |
+
</ul>
|
19 |
+
<ul>
|
20 |
+
<li><a href="../index.html">Language modes</a>
|
21 |
+
<li><a class=active href="#">TypeScript</a>
|
22 |
+
</ul>
|
23 |
+
</div>
|
24 |
+
|
25 |
+
<article>
|
26 |
+
<h2>TypeScript mode</h2>
|
27 |
+
|
28 |
+
|
29 |
+
<div><textarea id="code" name="code">
|
30 |
+
class Greeter {
|
31 |
+
greeting: string;
|
32 |
+
constructor (message: string) {
|
33 |
+
this.greeting = message;
|
34 |
+
}
|
35 |
+
greet() {
|
36 |
+
return "Hello, " + this.greeting;
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
var greeter = new Greeter("world");
|
41 |
+
|
42 |
+
var button = document.createElement('button')
|
43 |
+
button.innerText = "Say Hello"
|
44 |
+
button.onclick = function() {
|
45 |
+
alert(greeter.greet())
|
46 |
+
}
|
47 |
+
|
48 |
+
document.body.appendChild(button)
|
49 |
+
|
50 |
+
</textarea></div>
|
51 |
+
|
52 |
+
<script>
|
53 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
54 |
+
lineNumbers: true,
|
55 |
+
matchBrackets: true,
|
56 |
+
mode: "text/typescript"
|
57 |
+
});
|
58 |
+
</script>
|
59 |
+
|
60 |
+
<p>This is a specialization of the <a href="index.html">JavaScript mode</a>.</p>
|
61 |
+
</article>
|
assets/lib/codemirror/mode/meta.js
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.modeInfo = [
|
2 |
+
{name: 'APL', mime: 'text/apl', mode: 'apl'},
|
3 |
+
{name: 'Asterisk', mime: 'text/x-asterisk', mode: 'asterisk'},
|
4 |
+
{name: 'C', mime: 'text/x-csrc', mode: 'clike'},
|
5 |
+
{name: 'C++', mime: 'text/x-c++src', mode: 'clike'},
|
6 |
+
{name: 'Cobol', mime: 'text/x-cobol', mode: 'cobol'},
|
7 |
+
{name: 'Java', mime: 'text/x-java', mode: 'clike'},
|
8 |
+
{name: 'C#', mime: 'text/x-csharp', mode: 'clike'},
|
9 |
+
{name: 'Scala', mime: 'text/x-scala', mode: 'clike'},
|
10 |
+
{name: 'Clojure', mime: 'text/x-clojure', mode: 'clojure'},
|
11 |
+
{name: 'CoffeeScript', mime: 'text/x-coffeescript', mode: 'coffeescript'},
|
12 |
+
{name: 'Common Lisp', mime: 'text/x-common-lisp', mode: 'commonlisp'},
|
13 |
+
{name: 'CSS', mime: 'text/css', mode: 'css'},
|
14 |
+
{name: 'D', mime: 'text/x-d', mode: 'd'},
|
15 |
+
{name: 'diff', mime: 'text/x-diff', mode: 'diff'},
|
16 |
+
{name: 'DTD', mime: 'application/xml-dtd', mode: 'dtd'},
|
17 |
+
{name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'},
|
18 |
+
{name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'},
|
19 |
+
{name: 'Fortran', mime: 'text/x-fortran', mode: 'fortran'},
|
20 |
+
{name: 'Gas', mime: 'text/x-gas', mode: 'gas'},
|
21 |
+
{name: 'GitHub Flavored Markdown', mime: 'text/x-gfm', mode: 'gfm'},
|
22 |
+
{name: 'GO', mime: 'text/x-go', mode: 'go'},
|
23 |
+
{name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'},
|
24 |
+
{name: 'HAML', mime: 'text/x-haml', mode: 'haml'},
|
25 |
+
{name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'},
|
26 |
+
{name: 'Haxe', mime: 'text/x-haxe', mode: 'haxe'},
|
27 |
+
{name: 'ASP.NET', mime: 'application/x-aspx', mode: 'htmlembedded'},
|
28 |
+
{name: 'Embedded Javascript', mime: 'application/x-ejs', mode: 'htmlembedded'},
|
29 |
+
{name: 'JavaServer Pages', mime: 'application/x-jsp', mode: 'htmlembedded'},
|
30 |
+
{name: 'HTML', mime: 'text/html', mode: 'htmlmixed'},
|
31 |
+
{name: 'HTTP', mime: 'message/http', mode: 'http'},
|
32 |
+
{name: 'Jade', mime: 'text/x-jade', mode: 'jade'},
|
33 |
+
{name: 'JavaScript', mime: 'text/javascript', mode: 'javascript'},
|
34 |
+
{name: 'JSON', mime: 'application/x-json', mode: 'javascript'},
|
35 |
+
{name: 'JSON', mime: 'application/json', mode: 'javascript'},
|
36 |
+
{name: 'TypeScript', mime: 'application/typescript', mode: 'javascript'},
|
37 |
+
{name: 'Jinja2', mime: 'jinja2', mode: 'jinja2'},
|
38 |
+
{name: 'LESS', mime: 'text/x-less', mode: 'less'},
|
39 |
+
{name: 'LiveScript', mime: 'text/x-livescript', mode: 'livescript'},
|
40 |
+
{name: 'Lua', mime: 'text/x-lua', mode: 'lua'},
|
41 |
+
{name: 'Markdown (GitHub-flavour)', mime: 'text/x-markdown', mode: 'markdown'},
|
42 |
+
{name: 'mIRC', mime: 'text/mirc', mode: 'mirc'},
|
43 |
+
{name: 'Nginx', mime: 'text/x-nginx-conf', mode: 'nginx'},
|
44 |
+
{name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'},
|
45 |
+
{name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'},
|
46 |
+
{name: 'Octave', mime: 'text/x-octave', mode: 'octave'},
|
47 |
+
{name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'},
|
48 |
+
{name: 'Perl', mime: 'text/x-perl', mode: 'perl'},
|
49 |
+
{name: 'PHP', mime: 'text/x-php', mode: 'php'},
|
50 |
+
{name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'},
|
51 |
+
{name: 'Pig', mime: 'text/x-pig', mode: 'pig'},
|
52 |
+
{name: 'Plain Text', mime: 'text/plain', mode: 'null'},
|
53 |
+
{name: 'Properties files', mime: 'text/x-properties', mode: 'properties'},
|
54 |
+
{name: 'Python', mime: 'text/x-python', mode: 'python'},
|
55 |
+
{name: 'Cython', mime: 'text/x-cython', mode: 'python'},
|
56 |
+
{name: 'R', mime: 'text/x-rsrc', mode: 'r'},
|
57 |
+
{name: 'reStructuredText', mime: 'text/x-rst', mode: 'rst'},
|
58 |
+
{name: 'Ruby', mime: 'text/x-ruby', mode: 'ruby'},
|
59 |
+
{name: 'Rust', mime: 'text/x-rustsrc', mode: 'rust'},
|
60 |
+
{name: 'Sass', mime: 'text/x-sass', mode: 'sass'},
|
61 |
+
{name: 'Scheme', mime: 'text/x-scheme', mode: 'scheme'},
|
62 |
+
{name: 'SCSS', mime: 'text/x-scss', mode: 'css'},
|
63 |
+
{name: 'Shell', mime: 'text/x-sh', mode: 'shell'},
|
64 |
+
{name: 'Sieve', mime: 'application/sieve', mode: 'sieve'},
|
65 |
+
{name: 'Smalltalk', mime: 'text/x-stsrc', mode: 'smalltalk'},
|
66 |
+
{name: 'Smarty', mime: 'text/x-smarty', mode: 'smarty'},
|
67 |
+
{name: 'SmartyMixed', mime: 'text/x-smarty', mode: 'smartymixed'},
|
68 |
+
{name: 'SPARQL', mime: 'application/x-sparql-query', mode: 'sparql'},
|
69 |
+
{name: 'SQL', mime: 'text/x-sql', mode: 'sql'},
|
70 |
+
{name: 'MariaDB', mime: 'text/x-mariadb', mode: 'sql'},
|
71 |
+
{name: 'sTeX', mime: 'text/x-stex', mode: 'stex'},
|
72 |
+
{name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'},
|
73 |
+
{name: 'Tcl', mime: 'text/x-tcl', mode: 'tcl'},
|
74 |
+
{name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlywiki'},
|
75 |
+
{name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'},
|
76 |
+
{name: 'TOML', mime: 'text/x-toml', mode: 'toml'},
|
77 |
+
{name: 'Turtle', mime: 'text/turtle', mode: 'turtle'},
|
78 |
+
{name: 'VB.NET', mime: 'text/x-vb', mode: 'vb'},
|
79 |
+
{name: 'VBScript', mime: 'text/vbscript', mode: 'vbscript'},
|
80 |
+
{name: 'Velocity', mime: 'text/velocity', mode: 'velocity'},
|
81 |
+
{name: 'Verilog', mime: 'text/x-verilog', mode: 'verilog'},
|
82 |
+
{name: 'XML', mime: 'application/xml', mode: 'xml'},
|
83 |
+
{name: 'HTML', mime: 'text/html', mode: 'xml'},
|
84 |
+
{name: 'XQuery', mime: 'application/xquery', mode: 'xquery'},
|
85 |
+
{name: 'YAML', mime: 'text/x-yaml', mode: 'yaml'},
|
86 |
+
{name: 'Z80', mime: 'text/x-z80', mode: 'z80'}
|
87 |
+
];
|
assets/lib/codemirror/mode/php/index.html
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
|
3 |
+
<title>CodeMirror: PHP mode</title>
|
4 |
+
<meta charset="utf-8"/>
|
5 |
+
<link rel=stylesheet href="../../doc/docs.css">
|
6 |
+
|
7 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
8 |
+
<script src="../../lib/codemirror.js"></script>
|
9 |
+
<script src="../../addon/edit/matchbrackets.js"></script>
|
10 |
+
<script src="../htmlmixed/htmlmixed.js"></script>
|
11 |
+
<script src="../xml/xml.js"></script>
|
12 |
+
<script src="../javascript/javascript.js"></script>
|
13 |
+
<script src="../css/css.js"></script>
|
14 |
+
<script src="../clike/clike.js"></script>
|
15 |
+
<script src="php.js"></script>
|
16 |
+
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
17 |
+
<div id=nav>
|
18 |
+
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>
|
19 |
+
|
20 |
+
<ul>
|
21 |
+
<li><a href="../../index.html">Home</a>
|
22 |
+
<li><a href="../../doc/manual.html">Manual</a>
|
23 |
+
<li><a href="https://github.com/marijnh/codemirror">Code</a>
|
24 |
+
</ul>
|
25 |
+
<ul>
|
26 |
+
<li><a href="../index.html">Language modes</a>
|
27 |
+
<li><a class=active href="#">PHP</a>
|
28 |
+
</ul>
|
29 |
+
</div>
|
30 |
+
|
31 |
+
<article>
|
32 |
+
<h2>PHP mode</h2>
|
33 |
+
<form><textarea id="code" name="code">
|
34 |
+
<?php
|
35 |
+
function hello($who) {
|
36 |
+
return "Hello " . $who;
|
37 |
+
}
|
38 |
+
?>
|
39 |
+
<p>The program says <?= hello("World") ?>.</p>
|
40 |
+
<script>
|
41 |
+
alert("And here is some JS code"); // also colored
|
42 |
+
</script>
|
43 |
+
</textarea></form>
|
44 |
+
|
45 |
+
<script>
|
46 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
47 |
+
lineNumbers: true,
|
48 |
+
matchBrackets: true,
|
49 |
+
mode: "application/x-httpd-php",
|
50 |
+
indentUnit: 4,
|
51 |
+
indentWithTabs: true,
|
52 |
+
enterMode: "keep",
|
53 |
+
tabMode: "shift"
|
54 |
+
});
|
55 |
+
</script>
|
56 |
+
|
57 |
+
<p>Simple HTML/PHP mode based on
|
58 |
+
the <a href="../clike/">C-like</a> mode. Depends on XML,
|
59 |
+
JavaScript, CSS, HTMLMixed, and C-like modes.</p>
|
60 |
+
|
61 |
+
<p><strong>MIME types defined:</strong> <code>application/x-httpd-php</code> (HTML with PHP code), <code>text/x-php</code> (plain, non-wrapped PHP code).</p>
|
62 |
+
</article>
|
assets/lib/codemirror/mode/php/php.js
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
function keywords(str) {
|
3 |
+
var obj = {}, words = str.split(" ");
|
4 |
+
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
|
5 |
+
return obj;
|
6 |
+
}
|
7 |
+
function heredoc(delim) {
|
8 |
+
return function(stream, state) {
|
9 |
+
if (stream.match(delim)) state.tokenize = null;
|
10 |
+
else stream.skipToEnd();
|
11 |
+
return "string";
|
12 |
+
};
|
13 |
+
}
|
14 |
+
var phpConfig = {
|
15 |
+
name: "clike",
|
16 |
+
keywords: keywords("abstract and array as break case catch class clone const continue declare default " +
|
17 |
+
"do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " +
|
18 |
+
"for foreach function global goto if implements interface instanceof namespace " +
|
19 |
+
"new or private protected public static switch throw trait try use var while xor " +
|
20 |
+
"die echo empty exit eval include include_once isset list require require_once return " +
|
21 |
+
"print unset __halt_compiler self static parent"),
|
22 |
+
blockKeywords: keywords("catch do else elseif for foreach if switch try while"),
|
23 |
+
atoms: keywords("true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__"),
|
24 |
+
builtin: keywords("func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport echo print global static exit array empty eval isset unset die include require include_once require_once"),
|
25 |
+
multiLineStrings: true,
|
26 |
+
hooks: {
|
27 |
+
"$": function(stream) {
|
28 |
+
stream.eatWhile(/[\w\$_]/);
|
29 |
+
return "variable-2";
|
30 |
+
},
|
31 |
+
"<": function(stream, state) {
|
32 |
+
if (stream.match(/<</)) {
|
33 |
+
stream.eatWhile(/[\w\.]/);
|
34 |
+
state.tokenize = heredoc(stream.current().slice(3));
|
35 |
+
return state.tokenize(stream, state);
|
36 |
+
}
|
37 |
+
return false;
|
38 |
+
},
|
39 |
+
"#": function(stream) {
|
40 |
+
while (!stream.eol() && !stream.match("?>", false)) stream.next();
|
41 |
+
return "comment";
|
42 |
+
},
|
43 |
+
"/": function(stream) {
|
44 |
+
if (stream.eat("/")) {
|
45 |
+
while (!stream.eol() && !stream.match("?>", false)) stream.next();
|
46 |
+
return "comment";
|
47 |
+
}
|
48 |
+
return false;
|
49 |
+
}
|
50 |
+
}
|
51 |
+
};
|
52 |
+
|
53 |
+
CodeMirror.defineMode("php", function(config, parserConfig) {
|
54 |
+
var htmlMode = CodeMirror.getMode(config, "text/html");
|
55 |
+
var phpMode = CodeMirror.getMode(config, phpConfig);
|
56 |
+
|
57 |
+
function dispatch(stream, state) {
|
58 |
+
var isPHP = state.curMode == phpMode;
|
59 |
+
if (stream.sol() && state.pending != '"') state.pending = null;
|
60 |
+
if (!isPHP) {
|
61 |
+
if (stream.match(/^<\?\w*/)) {
|
62 |
+
state.curMode = phpMode;
|
63 |
+
state.curState = state.php;
|
64 |
+
return "meta";
|
65 |
+
}
|
66 |
+
if (state.pending == '"') {
|
67 |
+
while (!stream.eol() && stream.next() != '"') {}
|
68 |
+
var style = "string";
|
69 |
+
} else if (state.pending && stream.pos < state.pending.end) {
|
70 |
+
stream.pos = state.pending.end;
|
71 |
+
var style = state.pending.style;
|
72 |
+
} else {
|
73 |
+
var style = htmlMode.token(stream, state.curState);
|
74 |
+
}
|
75 |
+
state.pending = null;
|
76 |
+
var cur = stream.current(), openPHP = cur.search(/<\?/);
|
77 |
+
if (openPHP != -1) {
|
78 |
+
if (style == "string" && /\"$/.test(cur) && !/\?>/.test(cur)) state.pending = '"';
|
79 |
+
else state.pending = {end: stream.pos, style: style};
|
80 |
+
stream.backUp(cur.length - openPHP);
|
81 |
+
}
|
82 |
+
return style;
|
83 |
+
} else if (isPHP && state.php.tokenize == null && stream.match("?>")) {
|
84 |
+
state.curMode = htmlMode;
|
85 |
+
state.curState = state.html;
|
86 |
+
return "meta";
|
87 |
+
} else {
|
88 |
+
return phpMode.token(stream, state.curState);
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
return {
|
93 |
+
startState: function() {
|
94 |
+
var html = CodeMirror.startState(htmlMode), php = CodeMirror.startState(phpMode);
|
95 |
+
return {html: html,
|
96 |
+
php: php,
|
97 |
+
curMode: parserConfig.startOpen ? phpMode : htmlMode,
|
98 |
+
curState: parserConfig.startOpen ? php : html,
|
99 |
+
pending: null};
|
100 |
+
},
|
101 |
+
|
102 |
+
copyState: function(state) {
|
103 |
+
var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
|
104 |
+
php = state.php, phpNew = CodeMirror.copyState(phpMode, php), cur;
|
105 |
+
if (state.curMode == htmlMode) cur = htmlNew;
|
106 |
+
else cur = phpNew;
|
107 |
+
return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,
|
108 |
+
pending: state.pending};
|
109 |
+
},
|
110 |
+
|
111 |
+
token: dispatch,
|
112 |
+
|
113 |
+
indent: function(state, textAfter) {
|
114 |
+
if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
|
115 |
+
(state.curMode == phpMode && /^\?>/.test(textAfter)))
|
116 |
+
return htmlMode.indent(state.html, textAfter);
|
117 |
+
return state.curMode.indent(state.curState, textAfter);
|
118 |
+
},
|
119 |
+
|
120 |
+
electricChars: "/{}:",
|
121 |
+
blockCommentStart: "/*",
|
122 |
+
blockCommentEnd: "*/",
|
123 |
+
lineComment: "//",
|
124 |
+
|
125 |
+
innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
|
126 |
+
};
|
127 |
+
}, "htmlmixed", "clike");
|
128 |
+
|
129 |
+
CodeMirror.defineMIME("application/x-httpd-php", "php");
|
130 |
+
CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
|
131 |
+
CodeMirror.defineMIME("text/x-php", phpConfig);
|
132 |
+
})();
|
assets/lib/codemirror/mode/xml/index.html
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
|
3 |
+
<title>CodeMirror: XML mode</title>
|
4 |
+
<meta charset="utf-8"/>
|
5 |
+
<link rel=stylesheet href="../../doc/docs.css">
|
6 |
+
|
7 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
8 |
+
<script src="../../lib/codemirror.js"></script>
|
9 |
+
<script src="xml.js"></script>
|
10 |
+
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
11 |
+
<div id=nav>
|
12 |
+
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>
|
13 |
+
|
14 |
+
<ul>
|
15 |
+
<li><a href="../../index.html">Home</a>
|
16 |
+
<li><a href="../../doc/manual.html">Manual</a>
|
17 |
+
<li><a href="https://github.com/marijnh/codemirror">Code</a>
|
18 |
+
</ul>
|
19 |
+
<ul>
|
20 |
+
<li><a href="../index.html">Language modes</a>
|
21 |
+
<li><a class=active href="#">XML</a>
|
22 |
+
</ul>
|
23 |
+
</div>
|
24 |
+
|
25 |
+
<article>
|
26 |
+
<h2>XML mode</h2>
|
27 |
+
<form><textarea id="code" name="code">
|
28 |
+
<html style="color: green">
|
29 |
+
<!-- this is a comment -->
|
30 |
+
<head>
|
31 |
+
<title>HTML Example</title>
|
32 |
+
</head>
|
33 |
+
<body>
|
34 |
+
The indentation tries to be <em>somewhat &quot;do what
|
35 |
+
I mean&quot;</em>... but might not match your style.
|
36 |
+
</body>
|
37 |
+
</html>
|
38 |
+
</textarea></form>
|
39 |
+
<script>
|
40 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
41 |
+
mode: {name: "xml", alignCDATA: true},
|
42 |
+
lineNumbers: true
|
43 |
+
});
|
44 |
+
</script>
|
45 |
+
<p>The XML mode supports two configuration parameters:</p>
|
46 |
+
<dl>
|
47 |
+
<dt><code>htmlMode (boolean)</code></dt>
|
48 |
+
<dd>This switches the mode to parse HTML instead of XML. This
|
49 |
+
means attributes do not have to be quoted, and some elements
|
50 |
+
(such as <code>br</code>) do not require a closing tag.</dd>
|
51 |
+
<dt><code>alignCDATA (boolean)</code></dt>
|
52 |
+
<dd>Setting this to true will force the opening tag of CDATA
|
53 |
+
blocks to not be indented.</dd>
|
54 |
+
</dl>
|
55 |
+
|
56 |
+
<p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/html</code>.</p>
|
57 |
+
</article>
|
assets/lib/codemirror/mode/xml/xml.js
ADDED
@@ -0,0 +1,341 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineMode("xml", function(config, parserConfig) {
|
2 |
+
var indentUnit = config.indentUnit;
|
3 |
+
var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
|
4 |
+
var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag || true;
|
5 |
+
|
6 |
+
var Kludges = parserConfig.htmlMode ? {
|
7 |
+
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
|
8 |
+
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
|
9 |
+
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
|
10 |
+
'track': true, 'wbr': true},
|
11 |
+
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
|
12 |
+
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
|
13 |
+
'th': true, 'tr': true},
|
14 |
+
contextGrabbers: {
|
15 |
+
'dd': {'dd': true, 'dt': true},
|
16 |
+
'dt': {'dd': true, 'dt': true},
|
17 |
+
'li': {'li': true},
|
18 |
+
'option': {'option': true, 'optgroup': true},
|
19 |
+
'optgroup': {'optgroup': true},
|
20 |
+
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
|
21 |
+
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
|
22 |
+
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
|
23 |
+
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
|
24 |
+
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
|
25 |
+
'rp': {'rp': true, 'rt': true},
|
26 |
+
'rt': {'rp': true, 'rt': true},
|
27 |
+
'tbody': {'tbody': true, 'tfoot': true},
|
28 |
+
'td': {'td': true, 'th': true},
|
29 |
+
'tfoot': {'tbody': true},
|
30 |
+
'th': {'td': true, 'th': true},
|
31 |
+
'thead': {'tbody': true, 'tfoot': true},
|
32 |
+
'tr': {'tr': true}
|
33 |
+
},
|
34 |
+
doNotIndent: {"pre": true},
|
35 |
+
allowUnquoted: true,
|
36 |
+
allowMissing: true
|
37 |
+
} : {
|
38 |
+
autoSelfClosers: {},
|
39 |
+
implicitlyClosed: {},
|
40 |
+
contextGrabbers: {},
|
41 |
+
doNotIndent: {},
|
42 |
+
allowUnquoted: false,
|
43 |
+
allowMissing: false
|
44 |
+
};
|
45 |
+
var alignCDATA = parserConfig.alignCDATA;
|
46 |
+
|
47 |
+
// Return variables for tokenizers
|
48 |
+
var tagName, type;
|
49 |
+
|
50 |
+
function inText(stream, state) {
|
51 |
+
function chain(parser) {
|
52 |
+
state.tokenize = parser;
|
53 |
+
return parser(stream, state);
|
54 |
+
}
|
55 |
+
|
56 |
+
var ch = stream.next();
|
57 |
+
if (ch == "<") {
|
58 |
+
if (stream.eat("!")) {
|
59 |
+
if (stream.eat("[")) {
|
60 |
+
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
|
61 |
+
else return null;
|
62 |
+
} else if (stream.match("--")) {
|
63 |
+
return chain(inBlock("comment", "-->"));
|
64 |
+
} else if (stream.match("DOCTYPE", true, true)) {
|
65 |
+
stream.eatWhile(/[\w\._\-]/);
|
66 |
+
return chain(doctype(1));
|
67 |
+
} else {
|
68 |
+
return null;
|
69 |
+
}
|
70 |
+
} else if (stream.eat("?")) {
|
71 |
+
stream.eatWhile(/[\w\._\-]/);
|
72 |
+
state.tokenize = inBlock("meta", "?>");
|
73 |
+
return "meta";
|
74 |
+
} else {
|
75 |
+
var isClose = stream.eat("/");
|
76 |
+
tagName = "";
|
77 |
+
var c;
|
78 |
+
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
|
79 |
+
if (!tagName) return "error";
|
80 |
+
type = isClose ? "closeTag" : "openTag";
|
81 |
+
state.tokenize = inTag;
|
82 |
+
return "tag";
|
83 |
+
}
|
84 |
+
} else if (ch == "&") {
|
85 |
+
var ok;
|
86 |
+
if (stream.eat("#")) {
|
87 |
+
if (stream.eat("x")) {
|
88 |
+
ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
|
89 |
+
} else {
|
90 |
+
ok = stream.eatWhile(/[\d]/) && stream.eat(";");
|
91 |
+
}
|
92 |
+
} else {
|
93 |
+
ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
|
94 |
+
}
|
95 |
+
return ok ? "atom" : "error";
|
96 |
+
} else {
|
97 |
+
stream.eatWhile(/[^&<]/);
|
98 |
+
return null;
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
function inTag(stream, state) {
|
103 |
+
var ch = stream.next();
|
104 |
+
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
|
105 |
+
state.tokenize = inText;
|
106 |
+
type = ch == ">" ? "endTag" : "selfcloseTag";
|
107 |
+
return "tag";
|
108 |
+
} else if (ch == "=") {
|
109 |
+
type = "equals";
|
110 |
+
return null;
|
111 |
+
} else if (ch == "<") {
|
112 |
+
return "error";
|
113 |
+
} else if (/[\'\"]/.test(ch)) {
|
114 |
+
state.tokenize = inAttribute(ch);
|
115 |
+
state.stringStartCol = stream.column();
|
116 |
+
return state.tokenize(stream, state);
|
117 |
+
} else {
|
118 |
+
stream.eatWhile(/[^\s\u00a0=<>\"\']/);
|
119 |
+
return "word";
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
function inAttribute(quote) {
|
124 |
+
var closure = function(stream, state) {
|
125 |
+
while (!stream.eol()) {
|
126 |
+
if (stream.next() == quote) {
|
127 |
+
state.tokenize = inTag;
|
128 |
+
break;
|
129 |
+
}
|
130 |
+
}
|
131 |
+
return "string";
|
132 |
+
};
|
133 |
+
closure.isInAttribute = true;
|
134 |
+
return closure;
|
135 |
+
}
|
136 |
+
|
137 |
+
function inBlock(style, terminator) {
|
138 |
+
return function(stream, state) {
|
139 |
+
while (!stream.eol()) {
|
140 |
+
if (stream.match(terminator)) {
|
141 |
+
state.tokenize = inText;
|
142 |
+
break;
|
143 |
+
}
|
144 |
+
stream.next();
|
145 |
+
}
|
146 |
+
return style;
|
147 |
+
};
|
148 |
+
}
|
149 |
+
function doctype(depth) {
|
150 |
+
return function(stream, state) {
|
151 |
+
var ch;
|
152 |
+
while ((ch = stream.next()) != null) {
|
153 |
+
if (ch == "<") {
|
154 |
+
state.tokenize = doctype(depth + 1);
|
155 |
+
return state.tokenize(stream, state);
|
156 |
+
} else if (ch == ">") {
|
157 |
+
if (depth == 1) {
|
158 |
+
state.tokenize = inText;
|
159 |
+
break;
|
160 |
+
} else {
|
161 |
+
state.tokenize = doctype(depth - 1);
|
162 |
+
return state.tokenize(stream, state);
|
163 |
+
}
|
164 |
+
}
|
165 |
+
}
|
166 |
+
return "meta";
|
167 |
+
};
|
168 |
+
}
|
169 |
+
|
170 |
+
var curState, curStream, setStyle;
|
171 |
+
function pass() {
|
172 |
+
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
|
173 |
+
}
|
174 |
+
function cont() {
|
175 |
+
pass.apply(null, arguments);
|
176 |
+
return true;
|
177 |
+
}
|
178 |
+
|
179 |
+
function pushContext(tagName, startOfLine) {
|
180 |
+
var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
|
181 |
+
curState.context = {
|
182 |
+
prev: curState.context,
|
183 |
+
tagName: tagName,
|
184 |
+
indent: curState.indented,
|
185 |
+
startOfLine: startOfLine,
|
186 |
+
noIndent: noIndent
|
187 |
+
};
|
188 |
+
}
|
189 |
+
function popContext() {
|
190 |
+
if (curState.context) curState.context = curState.context.prev;
|
191 |
+
}
|
192 |
+
|
193 |
+
function element(type) {
|
194 |
+
if (type == "openTag") {
|
195 |
+
curState.tagName = tagName;
|
196 |
+
curState.tagStart = curStream.column();
|
197 |
+
return cont(attributes, endtag(curState.startOfLine));
|
198 |
+
} else if (type == "closeTag") {
|
199 |
+
var err = false;
|
200 |
+
if (curState.context) {
|
201 |
+
if (curState.context.tagName != tagName) {
|
202 |
+
if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
|
203 |
+
popContext();
|
204 |
+
}
|
205 |
+
err = !curState.context || curState.context.tagName != tagName;
|
206 |
+
}
|
207 |
+
} else {
|
208 |
+
err = true;
|
209 |
+
}
|
210 |
+
if (err) setStyle = "error";
|
211 |
+
return cont(endclosetag(err));
|
212 |
+
}
|
213 |
+
return cont();
|
214 |
+
}
|
215 |
+
function endtag(startOfLine) {
|
216 |
+
return function(type) {
|
217 |
+
var tagName = curState.tagName;
|
218 |
+
curState.tagName = curState.tagStart = null;
|
219 |
+
if (type == "selfcloseTag" ||
|
220 |
+
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
|
221 |
+
maybePopContext(tagName.toLowerCase());
|
222 |
+
return cont();
|
223 |
+
}
|
224 |
+
if (type == "endTag") {
|
225 |
+
maybePopContext(tagName.toLowerCase());
|
226 |
+
pushContext(tagName, startOfLine);
|
227 |
+
return cont();
|
228 |
+
}
|
229 |
+
return cont();
|
230 |
+
};
|
231 |
+
}
|
232 |
+
function endclosetag(err) {
|
233 |
+
return function(type) {
|
234 |
+
if (err) setStyle = "error";
|
235 |
+
if (type == "endTag") { popContext(); return cont(); }
|
236 |
+
setStyle = "error";
|
237 |
+
return cont(arguments.callee);
|
238 |
+
};
|
239 |
+
}
|
240 |
+
function maybePopContext(nextTagName) {
|
241 |
+
var parentTagName;
|
242 |
+
while (true) {
|
243 |
+
if (!curState.context) {
|
244 |
+
return;
|
245 |
+
}
|
246 |
+
parentTagName = curState.context.tagName.toLowerCase();
|
247 |
+
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
|
248 |
+
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
|
249 |
+
return;
|
250 |
+
}
|
251 |
+
popContext();
|
252 |
+
}
|
253 |
+
}
|
254 |
+
|
255 |
+
function attributes(type) {
|
256 |
+
if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
|
257 |
+
if (type == "endTag" || type == "selfcloseTag") return pass();
|
258 |
+
setStyle = "error";
|
259 |
+
return cont(attributes);
|
260 |
+
}
|
261 |
+
function attribute(type) {
|
262 |
+
if (type == "equals") return cont(attvalue, attributes);
|
263 |
+
if (!Kludges.allowMissing) setStyle = "error";
|
264 |
+
else if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
|
265 |
+
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
|
266 |
+
}
|
267 |
+
function attvalue(type) {
|
268 |
+
if (type == "string") return cont(attvaluemaybe);
|
269 |
+
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
|
270 |
+
setStyle = "error";
|
271 |
+
return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
|
272 |
+
}
|
273 |
+
function attvaluemaybe(type) {
|
274 |
+
if (type == "string") return cont(attvaluemaybe);
|
275 |
+
else return pass();
|
276 |
+
}
|
277 |
+
|
278 |
+
return {
|
279 |
+
startState: function() {
|
280 |
+
return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null};
|
281 |
+
},
|
282 |
+
|
283 |
+
token: function(stream, state) {
|
284 |
+
if (!state.tagName && stream.sol()) {
|
285 |
+
state.startOfLine = true;
|
286 |
+
state.indented = stream.indentation();
|
287 |
+
}
|
288 |
+
if (stream.eatSpace()) return null;
|
289 |
+
|
290 |
+
setStyle = type = tagName = null;
|
291 |
+
var style = state.tokenize(stream, state);
|
292 |
+
state.type = type;
|
293 |
+
if ((style || type) && style != "comment") {
|
294 |
+
curState = state; curStream = stream;
|
295 |
+
while (true) {
|
296 |
+
var comb = state.cc.pop() || element;
|
297 |
+
if (comb(type || style)) break;
|
298 |
+
}
|
299 |
+
}
|
300 |
+
state.startOfLine = false;
|
301 |
+
return setStyle || style;
|
302 |
+
},
|
303 |
+
|
304 |
+
indent: function(state, textAfter, fullLine) {
|
305 |
+
var context = state.context;
|
306 |
+
// Indent multi-line strings (e.g. css).
|
307 |
+
if (state.tokenize.isInAttribute) {
|
308 |
+
return state.stringStartCol + 1;
|
309 |
+
}
|
310 |
+
if ((state.tokenize != inTag && state.tokenize != inText) ||
|
311 |
+
context && context.noIndent)
|
312 |
+
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
|
313 |
+
// Indent the starts of attribute names.
|
314 |
+
if (state.tagName) {
|
315 |
+
if (multilineTagIndentPastTag)
|
316 |
+
return state.tagStart + state.tagName.length + 2;
|
317 |
+
else
|
318 |
+
return state.tagStart + indentUnit * multilineTagIndentFactor;
|
319 |
+
}
|
320 |
+
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
|
321 |
+
if (context && /^<\//.test(textAfter))
|
322 |
+
context = context.prev;
|
323 |
+
while (context && !context.startOfLine)
|
324 |
+
context = context.prev;
|
325 |
+
if (context) return context.indent + indentUnit;
|
326 |
+
else return 0;
|
327 |
+
},
|
328 |
+
|
329 |
+
electricChars: "/",
|
330 |
+
blockCommentStart: "<!--",
|
331 |
+
blockCommentEnd: "-->",
|
332 |
+
|
333 |
+
configuration: parserConfig.htmlMode ? "html" : "xml",
|
334 |
+
helperType: parserConfig.htmlMode ? "html" : "xml"
|
335 |
+
};
|
336 |
+
});
|
337 |
+
|
338 |
+
CodeMirror.defineMIME("text/xml", "xml");
|
339 |
+
CodeMirror.defineMIME("application/xml", "xml");
|
340 |
+
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
|
341 |
+
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
|
assets/lib/wck-api/wordpress-creation-kit.php
CHANGED
@@ -570,7 +570,10 @@ class Wordpress_Creation_Kit_PB{
|
|
570 |
$select_options = array();
|
571 |
foreach($field_details['options']['optgroups'] as $optgroup) {
|
572 |
foreach($optgroup['options'] as $group_option ){
|
573 |
-
|
|
|
|
|
|
|
574 |
}
|
575 |
}
|
576 |
|
@@ -578,7 +581,13 @@ class Wordpress_Creation_Kit_PB{
|
|
578 |
}
|
579 |
|
580 |
foreach( $field_details['options'] as $option ){
|
581 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
582 |
if( strpos( $option, '%' ) === false ){
|
583 |
return $value;
|
584 |
} else {
|
@@ -591,7 +600,9 @@ class Wordpress_Creation_Kit_PB{
|
|
591 |
}
|
592 |
}
|
593 |
}
|
|
|
594 |
}
|
|
|
595 |
//this was added for select-2 custom values that do not exist in the defined options
|
596 |
return $value;
|
597 |
}
|
@@ -1322,14 +1333,31 @@ class Wordpress_Creation_Kit_PB{
|
|
1322 |
}
|
1323 |
|
1324 |
static function wck_generate_select_option($option, $values, $i, $current_value){
|
1325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1326 |
$label = $option;
|
1327 |
if( !empty( $values ) )
|
1328 |
$value_attr = $values[$i];
|
1329 |
else
|
1330 |
$value_attr = $option;
|
1331 |
-
}
|
1332 |
-
else{
|
1333 |
$option_parts = explode( '%', $option );
|
1334 |
if( !empty( $option_parts ) ){
|
1335 |
if( empty( $option_parts[0] ) && count( $option_parts ) == 3 ){
|
@@ -1349,7 +1377,15 @@ class Wordpress_Creation_Kit_PB{
|
|
1349 |
}
|
1350 |
}
|
1351 |
|
1352 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1353 |
return $optionOutput;
|
1354 |
}
|
1355 |
|
570 |
$select_options = array();
|
571 |
foreach($field_details['options']['optgroups'] as $optgroup) {
|
572 |
foreach($optgroup['options'] as $group_option ){
|
573 |
+
|
574 |
+
if( !is_array( $group_option ) )
|
575 |
+
$select_options[] = $group_option;
|
576 |
+
|
577 |
}
|
578 |
}
|
579 |
|
581 |
}
|
582 |
|
583 |
foreach( $field_details['options'] as $option ){
|
584 |
+
|
585 |
+
if( is_array( $option ) ){
|
586 |
+
|
587 |
+
if( !empty( $option['field_name'] ) )
|
588 |
+
return $option['field_name'];
|
589 |
+
|
590 |
+
} elseif ( strpos( $option, $value ) !== false ){
|
591 |
if( strpos( $option, '%' ) === false ){
|
592 |
return $value;
|
593 |
} else {
|
600 |
}
|
601 |
}
|
602 |
}
|
603 |
+
|
604 |
}
|
605 |
+
|
606 |
//this was added for select-2 custom values that do not exist in the defined options
|
607 |
return $value;
|
608 |
}
|
1333 |
}
|
1334 |
|
1335 |
static function wck_generate_select_option($option, $values, $i, $current_value){
|
1336 |
+
|
1337 |
+
$disabled = '';
|
1338 |
+
|
1339 |
+
if( is_array( $option ) ){
|
1340 |
+
|
1341 |
+
if( !empty( $option['field_name'] ) ){
|
1342 |
+
$label = $option['field_name'];
|
1343 |
+
|
1344 |
+
if( !empty( $values ) )
|
1345 |
+
$value_attr = $values[$i];
|
1346 |
+
else
|
1347 |
+
$value_attr = $option;
|
1348 |
+
|
1349 |
+
if( !empty( $option['disabled'] ) && $option['disabled'] == true ){
|
1350 |
+
$disabled = ' disabled';
|
1351 |
+
}
|
1352 |
+
}
|
1353 |
+
|
1354 |
+
} elseif( strpos( $option, '%' ) === false ){
|
1355 |
$label = $option;
|
1356 |
if( !empty( $values ) )
|
1357 |
$value_attr = $values[$i];
|
1358 |
else
|
1359 |
$value_attr = $option;
|
1360 |
+
} else {
|
|
|
1361 |
$option_parts = explode( '%', $option );
|
1362 |
if( !empty( $option_parts ) ){
|
1363 |
if( empty( $option_parts[0] ) && count( $option_parts ) == 3 ){
|
1377 |
}
|
1378 |
}
|
1379 |
|
1380 |
+
// title is set only for disabled options to let users know those fields are available in a paid version
|
1381 |
+
if( !empty( $disabled ) ){
|
1382 |
+
if( isset( $value_attr['field_name'] ) && $value_attr['field_name'] == 'Subscription Plans' )
|
1383 |
+
$title = esc_attr( __( 'Install the free Paid Member Subscriptions plugin to get access this field.', 'profile-builder' ) );
|
1384 |
+
else
|
1385 |
+
$title = esc_attr( __( 'This field is available in our paid plans.', 'profile-builder' ) );
|
1386 |
+
}
|
1387 |
+
|
1388 |
+
$optionOutput = '<option value="'. esc_attr( $value_attr ) .'" '. selected( $value_attr, $current_value, false ) . esc_attr( $disabled ) . ( !empty( $disabled ) ? ' title="'. $title .'"' : '' ) . ' >'. esc_html( $label ) .'</option>';
|
1389 |
return $optionOutput;
|
1390 |
}
|
1391 |
|
features/email-customizer/admin-email-customizer.php
ADDED
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Function that creates the Admin Email Customizer menu
|
4 |
+
*
|
5 |
+
* @since v.2.0
|
6 |
+
*
|
7 |
+
* @return void
|
8 |
+
*/
|
9 |
+
function wppb_admin_email_customizer_submenu(){
|
10 |
+
$args = array(
|
11 |
+
'menu_title' => __( 'Admin Email Customizer', 'profile-builder' ),
|
12 |
+
'page_title' => __( 'Admin Email Customizer Settings', 'profile-builder' ),
|
13 |
+
'menu_slug' => 'admin-email-customizer',
|
14 |
+
'page_type' => 'submenu_page',
|
15 |
+
'capability' => 'manage_options',
|
16 |
+
'priority' => 10,
|
17 |
+
'parent_slug' => 'profile-builder'
|
18 |
+
);
|
19 |
+
|
20 |
+
new WCK_Page_Creator_PB( $args );
|
21 |
+
}
|
22 |
+
add_action( 'admin_menu', 'wppb_admin_email_customizer_submenu', 1 );
|
23 |
+
|
24 |
+
add_action( 'wck_before_meta_boxes', 'wppb_add_tabs_on_top_of_admin_email_page' );
|
25 |
+
function wppb_add_tabs_on_top_of_admin_email_page( $hookname ){
|
26 |
+
if( $hookname == 'profile-builder_page_admin-email-customizer' ){
|
27 |
+
|
28 |
+
if( isset( $_GET['mustache_action'] ) && $_GET['mustache_action'] == 'save' ) { ?>
|
29 |
+
<div id="setting-error-settings_updated" class="updated settings-error notice">
|
30 |
+
<p><strong><?php esc_html_e( "Settings saved.", 'profile-builder' ); ?></strong></p>
|
31 |
+
</div>
|
32 |
+
<?php
|
33 |
+
}
|
34 |
+
|
35 |
+
wppb_generate_settings_tabs();
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
/* on the init hook add the mustache boxes */
|
40 |
+
add_action( 'init', 'wppb_admin_email_customizer_add_mustache_in_backend', 11 );
|
41 |
+
/**
|
42 |
+
* Function that ads the mustache boxes in the backend for admin email customizer
|
43 |
+
*
|
44 |
+
* @since v.2.0
|
45 |
+
*/
|
46 |
+
function wppb_admin_email_customizer_add_mustache_in_backend(){
|
47 |
+
if( defined( 'WPPB_PAID_PLUGIN_DIR' ) && file_exists( WPPB_PAID_PLUGIN_DIR . '/assets/lib/class-mustache-templates/class-mustache-templates.php' ) )
|
48 |
+
require_once( WPPB_PAID_PLUGIN_DIR . '/assets/lib/class-mustache-templates/class-mustache-templates.php' );
|
49 |
+
elseif( file_exists( WPPB_PLUGIN_DIR . '/assets/lib/class-mustache-templates/class-mustache-templates.php' ) )
|
50 |
+
require_once( WPPB_PLUGIN_DIR . '/assets/lib/class-mustache-templates/class-mustache-templates.php' );
|
51 |
+
|
52 |
+
$fields = array(
|
53 |
+
array(
|
54 |
+
'id' => 'wppb_admin_emailc_common_settings_header', // field id and name
|
55 |
+
'type' => 'header', // type of field
|
56 |
+
'default' => __( 'These settings are also replicated in the "User Email Customizer" settings-page upon save.', 'profile-builder' ).' '.__( 'Valid tags {{reply_to}} and {{site_name}}', 'profile-builder'), // type of field
|
57 |
+
),
|
58 |
+
array(
|
59 |
+
'label' => __( 'From (name)', 'profile-builder' ), // <label>
|
60 |
+
'desc' => '', // description
|
61 |
+
'id' => 'wppb_emailc_common_settings_from_name', // field id and name
|
62 |
+
'type' => 'text', // type of field
|
63 |
+
'default' => '{{site_name}}', // type of field
|
64 |
+
'desc' => '',
|
65 |
+
),
|
66 |
+
array(
|
67 |
+
'label' => __( 'From (reply-to email)', 'profile-builder' ), // <label>
|
68 |
+
'desc' => '', // description
|
69 |
+
'id' => 'wppb_emailc_common_settings_from_reply_to_email', // field id and name
|
70 |
+
'type' => 'text', // type of field
|
71 |
+
'default' => '{{reply_to}}', // type of field
|
72 |
+
'desc' => __( 'Must be a valid email address or the tag {{reply_to}} which defaults to the administrator email', 'profile-builder' ),
|
73 |
+
),
|
74 |
+
);
|
75 |
+
new PB_Mustache_Generate_Admin_Box( 'aec_common_settings', __( 'Common Settings', 'profile-builder' ), 'profile-builder_page_admin-email-customizer', 'core', '', '', $fields );
|
76 |
+
|
77 |
+
|
78 |
+
$registration_default_email_content = __( "<p>New subscriber on {{site_name}}.</p>\n<p>Username:{{username}}</p>\n<p>Email:{{user_email}}</p>\n", 'profile-builder' );
|
79 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags();
|
80 |
+
|
81 |
+
$fields = array(
|
82 |
+
array(
|
83 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
84 |
+
'desc' => '', // description
|
85 |
+
'id' => 'wppb_admin_emailc_default_registration_email_subject', // field id and name
|
86 |
+
'type' => 'text', // type of field
|
87 |
+
'default' => '[{{site_name}}] '.__( 'A new subscriber has (been) registered!', 'profile-builder' ), // type of field
|
88 |
+
),
|
89 |
+
array(
|
90 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
91 |
+
'desc' => '', // description
|
92 |
+
'id' => 'wppb_admin_emailc_default_registration_email_enabled', // field id and name
|
93 |
+
'type' => 'checkbox', // type of field
|
94 |
+
'default' => 'on',
|
95 |
+
),
|
96 |
+
array( // Textarea
|
97 |
+
'label' => '', // <label>
|
98 |
+
'desc' => '', // description
|
99 |
+
'id' => 'wppb_admin_emailc_default_registration_email_content', // field id and name
|
100 |
+
'type' => 'textarea', // type of field
|
101 |
+
'default' => $registration_default_email_content, // type of field
|
102 |
+
)
|
103 |
+
);
|
104 |
+
|
105 |
+
new PB_Mustache_Generate_Admin_Box( 'aec_default_registration', __( 'Default Registration & Registration with Email Confirmation', 'profile-builder' ), 'profile-builder_page_admin-email-customizer', 'core', $mustache_vars, '', $fields);
|
106 |
+
|
107 |
+
if( PROFILE_BUILDER != 'Profile Builder Free' ){
|
108 |
+
$registration_admin_approval_email_content = __( "<p>New subscriber on {{site_name}}.</p>\n<p>Username:{{username}}</p>\n<p>Email:{{user_email}}</p>\n<p>The Admin Approval feature was activated at the time of registration,\nso please remember that you need to approve this user before he/she can log in!</p>", 'profile-builder' );
|
109 |
+
|
110 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags( 'admin_approval' );
|
111 |
+
|
112 |
+
$fields = array(
|
113 |
+
array(
|
114 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
115 |
+
'desc' => '', // description
|
116 |
+
'id' => 'wppb_admin_emailc_registration_with_admin_approval_email_subject', // field id and name
|
117 |
+
'type' => 'text', // type of field
|
118 |
+
'default' => '[{{site_name}}] A new subscriber has (been) registered!', // type of field
|
119 |
+
),
|
120 |
+
array(
|
121 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
122 |
+
'desc' => '', // description
|
123 |
+
'id' => 'wppb_admin_emailc_registration_with_admin_approval_email_enabled', // field id and name
|
124 |
+
'type' => 'checkbox', // type of field
|
125 |
+
'default' => 'on',
|
126 |
+
),
|
127 |
+
array( // Textarea
|
128 |
+
'label' => '', // <label>
|
129 |
+
'desc' => '', // description
|
130 |
+
'id' => 'wppb_admin_emailc_registration_with_admin_approval_email_content', // field id and name
|
131 |
+
'type' => 'textarea', // type of field
|
132 |
+
'default' => $registration_admin_approval_email_content , // type of field
|
133 |
+
)
|
134 |
+
);
|
135 |
+
|
136 |
+
new PB_Mustache_Generate_Admin_Box( 'aec_reg_with_admin_approval', __( 'Registration with Admin Approval', 'profile-builder' ), 'profile-builder_page_admin-email-customizer', 'core', $mustache_vars, '', $fields );
|
137 |
+
}
|
138 |
+
|
139 |
+
$user_password_reset_email_content = __( "<p>{{username}} has requested a password change via the password reset feature.</p>\n", 'profile-builder' );
|
140 |
+
|
141 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags();
|
142 |
+
|
143 |
+
$fields = array(
|
144 |
+
array(
|
145 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
146 |
+
'desc' => '', // description
|
147 |
+
'id' => 'wppb_admin_emailc_user_password_reset_email_subject', // field id and name
|
148 |
+
'type' => 'text', // type of field
|
149 |
+
'default' => '[{{site_name}}] Password Successfully Reset for {{username}}', // type of field
|
150 |
+
),
|
151 |
+
array(
|
152 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
153 |
+
'desc' => '', // description
|
154 |
+
'id' => 'wppb_admin_emailc_user_password_reset_email_enabled', // field id and name
|
155 |
+
'type' => 'checkbox', // type of field
|
156 |
+
'default' => 'on',
|
157 |
+
),
|
158 |
+
array( // Textarea
|
159 |
+
'label' => '', // <label>
|
160 |
+
'desc' => '', // description
|
161 |
+
'id' => 'wppb_admin_emailc_user_password_reset_email_content', // field id and name
|
162 |
+
'type' => 'textarea', // type of field
|
163 |
+
'default' => $user_password_reset_email_content , // type of field
|
164 |
+
)
|
165 |
+
);
|
166 |
+
|
167 |
+
new PB_Mustache_Generate_Admin_Box( 'aec_user_password_reset', __( 'Admin Notification for User Password Reset', 'profile-builder' ), 'profile-builder_page_admin-email-customizer', 'core', $mustache_vars, '', $fields );
|
168 |
+
|
169 |
+
|
170 |
+
/*
|
171 |
+
* Admin Notification for Edit Profile Approved by Admin
|
172 |
+
*/
|
173 |
+
|
174 |
+
// check if the Edit Profile Approved by Admin add-on is active to see if the email should be displayed
|
175 |
+
if( function_exists( 'wppb_in_init_edit_profile_approval' ) ) {
|
176 |
+
// we format the var like this for proper line breaks.
|
177 |
+
$aec_epaa_notification = __("<p>The user {{username}} has updated their profile and some of the fields require admin approval:</p>\n<br>\n{{modified_fields}}\n<br>\n<p>Access this link to approve changes: {{approval_url}}</p>\n", 'profile-builder');
|
178 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags('epaa_notification_admin');
|
179 |
+
$fields = array(
|
180 |
+
array(
|
181 |
+
'label' => __('Email Subject', 'profile-builder'), // <label>
|
182 |
+
'desc' => '', // description
|
183 |
+
'id' => 'wppb_admin_emailc_epaa_notification_subject', // field id and name
|
184 |
+
'type' => 'text', // type of field
|
185 |
+
'default' => __('[{{site_name}}] A user has updated their profile. Some fields need approval', 'profile-builder'), // type of field
|
186 |
+
),
|
187 |
+
array(
|
188 |
+
'label' => __('Enable email', 'profile-builder'), // <label>
|
189 |
+
'desc' => '', // description
|
190 |
+
'id' => 'wppb_admin_emailc_epaa_notification_enabled', // field id and name
|
191 |
+
'type' => 'checkbox', // type of field
|
192 |
+
'default' => 'on',
|
193 |
+
),
|
194 |
+
array( // Textarea
|
195 |
+
'label' => '', // <label>
|
196 |
+
'desc' => '', // description
|
197 |
+
'id' => 'wppb_admin_emailc_epaa_notification_content', // field id and name
|
198 |
+
'type' => 'textarea', // type of field
|
199 |
+
'default' => $aec_epaa_notification, // type of field
|
200 |
+
)
|
201 |
+
);
|
202 |
+
|
203 |
+
new PB_Mustache_Generate_Admin_Box('aec_epaa_notification', __('Admin Notification for Edit Profile Approved by Admin', 'profile-builder'), 'profile-builder_page_admin-email-customizer', 'core', $mustache_vars, '', $fields);
|
204 |
+
}
|
205 |
+
}
|
features/email-customizer/email-customizer.php
ADDED
@@ -0,0 +1,1286 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
function wppb_email_customizer_generate_merge_tags( $special_merge_tags = null ){
|
4 |
+
|
5 |
+
$mustache_vars = array(
|
6 |
+
array(
|
7 |
+
'group-title' => __( 'Available Tags', 'profile-builder' ),
|
8 |
+
'variables' => wppb_email_customizer_generate_default_meta_merge_tags( $special_merge_tags )
|
9 |
+
),
|
10 |
+
array(
|
11 |
+
'group-title' => __( 'User Fields Tags', 'profile-builder' ),
|
12 |
+
'variables' => wppb_email_customizer_generate_meta_merge_tags()
|
13 |
+
)
|
14 |
+
);
|
15 |
+
|
16 |
+
return $mustache_vars;
|
17 |
+
}
|
18 |
+
|
19 |
+
|
20 |
+
function wppb_email_customizer_generate_default_meta_merge_tags( $special_merge_tags ){
|
21 |
+
$merge_tags[] = array( 'name' => 'site_url', 'type' => 'ec_site_url', 'label' => __( 'Site Url', 'profile-builder' ) );
|
22 |
+
$merge_tags[] = array( 'name' => 'site_name', 'type' => 'ec_site_name', 'label' => __( 'Site Name', 'profile-builder' ) );
|
23 |
+
|
24 |
+
if ( $special_merge_tags != 'email_confirmation' ){
|
25 |
+
$merge_tags[] = array( 'name' => 'user_id', 'type' => 'ec_user_id', 'label' => __( 'User Id', 'profile-builder' ) );
|
26 |
+
}
|
27 |
+
|
28 |
+
$merge_tags[] = array( 'name' => 'username', 'type' => 'ec_username', 'label' => __( 'Username', 'profile-builder' ) );
|
29 |
+
$merge_tags[] = array( 'name' => 'user_email', 'type' => 'ec_user_email', 'label' => __( 'Email', 'profile-builder' ) );
|
30 |
+
$merge_tags[] = array( 'name' => 'password', 'type' => 'ec_password', 'label' => __( 'Password', 'profile-builder' ) );
|
31 |
+
$merge_tags[] = array( 'name' => 'role', 'type' => 'ec_role', 'label' => __( 'User Role', 'profile-builder' ) );
|
32 |
+
$merge_tags[] = array( 'name' => 'role_label', 'type' => 'ec_role_label', 'label' => __( 'User Role Label', 'profile-builder' ) );
|
33 |
+
$merge_tags[] = array( 'name' => 'website', 'type' => 'ec_website', 'label' => __( 'Website', 'profile-builder' ) );
|
34 |
+
$merge_tags[] = array( 'name' => 'reply_to', 'type' => 'ec_reply_to', 'label' => __( 'Reply To', 'profile-builder' ) );
|
35 |
+
|
36 |
+
if ( wppb_can_users_signup_blog() ) {
|
37 |
+
$merge_tags[] = array('name' => 'blog_url', 'type' => 'ec_blog_url', 'label' => __('Blog URL', 'profile-builder'));
|
38 |
+
}
|
39 |
+
|
40 |
+
if ( $special_merge_tags == 'email_confirmation' ){
|
41 |
+
$merge_tags[] = array( 'name' => 'activation_key', 'type' => 'ec_activation_key', 'label' => __( 'Activation Key', 'profile-builder' ) );
|
42 |
+
$merge_tags[] = array( 'name' => 'activation_url', 'type' => 'ec_activation_url', 'label' => __( 'Activation Url', 'profile-builder' ) );
|
43 |
+
$merge_tags[] = array( 'name' => 'activation_link', 'type' => 'ec_activation_link', 'unescaped' => true, 'label' => __( 'Activation Link', 'profile-builder' ) );
|
44 |
+
}
|
45 |
+
|
46 |
+
if ( $special_merge_tags == 'password_reset' ){
|
47 |
+
$merge_tags[] = array( 'name' => 'reset_key', 'type' => 'ec_reset_key', 'label' => __( 'Reset Key', 'profile-builder' ) );
|
48 |
+
$merge_tags[] = array( 'name' => 'reset_url', 'type' => 'ec_reset_url', 'label' => __( 'Reset Url', 'profile-builder' ) );
|
49 |
+
$merge_tags[] = array( 'name' => 'reset_link', 'type' => 'ec_reset_link', 'unescaped' => true, 'label' => __( 'Reset Link', 'profile-builder' ) );
|
50 |
+
}
|
51 |
+
|
52 |
+
if ( $special_merge_tags == 'change_email_address_request' ){
|
53 |
+
$merge_tags[] = array( 'name' => 'user_email_change_link', 'type' => 'ec_user_email_change_link', 'unescaped' => true, 'label' => __( 'Change Email Address Link', 'profile-builder' ) );
|
54 |
+
}
|
55 |
+
|
56 |
+
if ( $special_merge_tags == 'admin_approval' ){
|
57 |
+
$merge_tags[] = array( 'name' => 'approve_url', 'type' => 'ec_approve_url', 'label' => __( 'Approve User Url', 'profile-builder' ) );
|
58 |
+
$merge_tags[] = array( 'name' => 'approve_link', 'type' => 'ec_approve_link', 'unescaped' => true, 'label' => __( 'Approve User Link', 'profile-builder' ) );
|
59 |
+
$merge_tags[] = array( 'name' => 'unapprove_url', 'type' => 'ec_unapprove_url', 'label' => __( 'Unapprove User Url', 'profile-builder' ) );
|
60 |
+
$merge_tags[] = array( 'name' => 'unapprove_link', 'type' => 'ec_unapprove_link', 'unescaped' => true, 'label' => __( 'Unapprove User Link', 'profile-builder' ) );
|
61 |
+
}
|
62 |
+
|
63 |
+
if ( $special_merge_tags == 'epaa_notification' ){
|
64 |
+
$merge_tags[] = array( 'name' => 'approved_fields', 'type' => 'ec_epaa_approved_fields', 'label' => __( 'Approved Fields', 'profile-builder' ) );
|
65 |
+
$merge_tags[] = array( 'name' => 'unapproved_fields', 'type' => 'ec_epaa_unapproved_fields', 'label' => __( 'Unapproved Fields', 'profile-builder' ) );
|
66 |
+
}
|
67 |
+
|
68 |
+
if ( $special_merge_tags == 'epaa_notification_admin' ){
|
69 |
+
$merge_tags[] = array( 'name' => 'modified_fields', 'type' => 'ec_epaa_modified_fields', 'label' => __( 'Modified Fields', 'profile-builder' ) );
|
70 |
+
$merge_tags[] = array( 'name' => 'approval_url', 'type' => 'ec_epaa_approval_url', 'label' => __( 'Approval URL', 'profile-builder' ) );
|
71 |
+
}
|
72 |
+
|
73 |
+
return $merge_tags;
|
74 |
+
}
|
75 |
+
|
76 |
+
|
77 |
+
function wppb_email_customizer_generate_meta_merge_tags(){
|
78 |
+
$wppb_manage_fields = apply_filters( 'wppb_email_customizer_get_fields' , get_option( 'wppb_manage_fields', 'not_found' ) );
|
79 |
+
$merge_tags = array();
|
80 |
+
|
81 |
+
if ( $wppb_manage_fields != 'not_found' ){
|
82 |
+
foreach( $wppb_manage_fields as $key => $value ){
|
83 |
+
if( !empty( $value['meta-name'] ) ){
|
84 |
+
|
85 |
+
// Skip all map fields
|
86 |
+
if( $value['field'] == 'Map' || $value['field'] == 'HTML' )
|
87 |
+
continue;
|
88 |
+
|
89 |
+
$merge_tags[] = array( 'name' => $value['meta-name'], 'type' => 'ec_user_meta', 'label' => $value['field-title'] );
|
90 |
+
if( $value['field'] == 'Select' || $value['field'] == 'Select (Multiple)' || $value['field'] == 'Select (Country)' || $value['field'] == 'Select (Currency)' || $value['field'] == 'Checkbox' || $value['field'] == 'Radio' )
|
91 |
+
$merge_tags[] = array( 'name' => $value['meta-name'] . '_labels', 'type' => 'ec_user_meta_labels', 'label' => $value['field-title'] . ' Labels' );
|
92 |
+
}
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
return apply_filters( 'wppb_email_customizer_get_merge_tags', $merge_tags );
|
97 |
+
}
|
98 |
+
|
99 |
+
function wppb_email_customizer_admin_approval_new_user_signup_filter_handler( $default_string, $user_email, $user_password, $email_sent_from, $option_name ){
|
100 |
+
$email_customizer_option = get_option( $option_name, 'not_found' );
|
101 |
+
if ( $email_customizer_option != 'not_found' ){
|
102 |
+
$user_data = get_user_by( 'email', $user_email );
|
103 |
+
$special_merge_tags = ( current_filter() == 'wppb_register_admin_email_message_with_admin_approval' ) ? 'admin_approval' : '';
|
104 |
+
wppb_change_email_from_headers();
|
105 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags($special_merge_tags), $email_customizer_option, array( 'user_id' => $user_data->ID, 'user_login' => $user_data->user_login, 'user_email' => $user_email, 'user_password' => $user_password, 'user_data' => $user_data, 'email_sent_from' => $email_sent_from ) );
|
106 |
+
}
|
107 |
+
|
108 |
+
return $default_string;
|
109 |
+
}
|
110 |
+
|
111 |
+
function wppb_email_customizer_admin_approval_new_user_signup_subject_filter_handler( $default_string, $user_email, $user_password, $email_sent_from, $option_name ){
|
112 |
+
if( current_user_can( 'delete_users') ) {
|
113 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_admin_approval_notif_approved_email_subject', 'not_found' );
|
114 |
+
} else {
|
115 |
+
$email_customizer_option = get_option( $option_name, 'not_found' );
|
116 |
+
}
|
117 |
+
|
118 |
+
if ( $email_customizer_option != 'not_found' ){
|
119 |
+
$user_data = get_user_by( 'email', $user_email );
|
120 |
+
wppb_change_email_from_headers();
|
121 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags(), $email_customizer_option, array( 'user_id' => $user_data->ID, 'user_login' => $user_data->user_login, 'user_email' => $user_email, 'user_password' => $user_password, 'user_data' => $user_data, 'email_sent_from' => $email_sent_from ) );
|
122 |
+
}
|
123 |
+
|
124 |
+
return $default_string;
|
125 |
+
}
|
126 |
+
|
127 |
+
function wppb_email_customizer_admin_approval_new_user_signup_message_filter_handler( $default_string, $user_email, $user_password, $email_sent_from, $option_name ){
|
128 |
+
if( current_user_can( 'delete_users') ) {
|
129 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_admin_approval_notif_approved_email_content', 'not_found' );
|
130 |
+
} else {
|
131 |
+
$email_customizer_option = get_option( $option_name, 'not_found' );
|
132 |
+
}
|
133 |
+
|
134 |
+
if ( $email_customizer_option != 'not_found' ){
|
135 |
+
$user_data = get_user_by( 'email', $user_email );
|
136 |
+
wppb_change_email_from_headers();
|
137 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags(), $email_customizer_option, array( 'user_id' => $user_data->ID, 'user_login' => $user_data->user_login, 'user_email' => $user_email, 'user_password' => $user_password, 'user_data' => $user_data, 'email_sent_from' => $email_sent_from ) );
|
138 |
+
}
|
139 |
+
|
140 |
+
return $default_string;
|
141 |
+
}
|
142 |
+
|
143 |
+
function wppb_email_customizer_admin_approval_new_user_status_filter_handler( $default_string, $user_data, $new_status, $email_sent_from, $option_name ){
|
144 |
+
$email_customizer_option = get_option( $option_name, 'not_found' );
|
145 |
+
|
146 |
+
if ( $email_customizer_option != 'not_found' ){
|
147 |
+
if( !empty( $user_data ) ){
|
148 |
+
wppb_change_email_from_headers();
|
149 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags(), $email_customizer_option, array( 'user_id' => $user_data->ID, 'user_login' => $user_data->user_login, 'user_email' => $user_data->user_email, 'user_password' => $user_data->user_pass, 'user_data' => $user_data, 'email_sent_from' => $email_sent_from ) );
|
150 |
+
}
|
151 |
+
}
|
152 |
+
|
153 |
+
return $default_string;
|
154 |
+
}
|
155 |
+
|
156 |
+
function wppb_email_customizer_email_confirmation_filter_handler( $default_string, $user_email, $username, $key, $activation_url, $serialized_data, $email_sent_from, $option_name ){
|
157 |
+
$email_customizer_option = get_option( $option_name, 'not_found' );
|
158 |
+
if ( $email_customizer_option != 'not_found' ){
|
159 |
+
$unserialized_data = unserialize( $serialized_data );
|
160 |
+
if( !empty( $unserialized_data ) ){
|
161 |
+
wppb_change_email_from_headers();
|
162 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags( 'email_confirmation' ), $email_customizer_option, array( 'user_login' => $unserialized_data['user_login'], 'user_email' => $user_email, 'user_password' => $unserialized_data['user_pass'], 'email_confirmation_unserialized_data' => $unserialized_data, 'email_confirmation_key' => $key, 'email_confirmation_url' => $activation_url, 'email_sent_from' => $email_sent_from ) );
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
return $default_string;
|
167 |
+
}
|
168 |
+
|
169 |
+
function wppb_email_customizer_password_reset_content_filter_handler( $default_string, $user_id, $user_login, $user_email ) {
|
170 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_reset_email_content', 'not_found' );
|
171 |
+
$key = wppb_retrieve_activation_key( $user_login );
|
172 |
+
$url = add_query_arg( array( 'key' => $key ), wppb_curpageurl() );
|
173 |
+
|
174 |
+
if( $email_customizer_option != 'not_found' ) {
|
175 |
+
wppb_change_email_from_headers();
|
176 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags( 'password_reset' ), $email_customizer_option, array( 'user_login' => $user_login, 'reset_key' => $key, 'reset_url' => $url, 'user_email' => $user_email, 'user_id' => $user_id ) );
|
177 |
+
}
|
178 |
+
|
179 |
+
return $default_string;
|
180 |
+
}
|
181 |
+
|
182 |
+
function wppb_email_customizer_password_reset_title_filter_handler( $default_string, $username ) {
|
183 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_reset_email_subject', 'not_found' );
|
184 |
+
|
185 |
+
if( $email_customizer_option != 'not_found' ) {
|
186 |
+
wppb_change_email_from_headers();
|
187 |
+
$user_info = get_user_by( 'login', $username );
|
188 |
+
if( !empty( $user_info->data->user_email ) )
|
189 |
+
$user_email = $user_info->data->user_email;
|
190 |
+
else
|
191 |
+
$user_email = '';
|
192 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags( 'password_reset' ), $email_customizer_option, array( 'user_login' => $username, 'user_email' => $user_email, 'user_id' => $user_info->data->ID ) );
|
193 |
+
}
|
194 |
+
|
195 |
+
return $default_string;
|
196 |
+
}
|
197 |
+
|
198 |
+
function wppb_email_customizer_password_reset_success_content_filter_handler( $default_string, $username, $new_password, $userID ) {
|
199 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_reset_success_email_content', 'not_found' );
|
200 |
+
|
201 |
+
if( $email_customizer_option != 'not_found' ) {
|
202 |
+
$user_info = get_userdata( $userID );
|
203 |
+
wppb_change_email_from_headers();
|
204 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags(), $email_customizer_option, array( 'user_login' => $username, 'user_password' => $new_password, 'user_id' => $userID, 'user_email' => $user_info->user_email ) );
|
205 |
+
}
|
206 |
+
|
207 |
+
return $default_string;
|
208 |
+
}
|
209 |
+
|
210 |
+
function wppb_email_customizer_password_reset_success_title_filter_handler( $default_string, $username ) {
|
211 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_reset_success_email_subject', 'not_found' );
|
212 |
+
|
213 |
+
if( $email_customizer_option != 'not_found' ) {
|
214 |
+
$user_info = get_user_by( 'login', $username );
|
215 |
+
/* we could have email instead of username in $username */
|
216 |
+
if( !$user_info )
|
217 |
+
$user_info = get_user_by( 'email', $username );
|
218 |
+
|
219 |
+
if( !empty( $user_info->data->user_email ) )
|
220 |
+
$user_email = $user_info->data->user_email;
|
221 |
+
else
|
222 |
+
$user_email = '';
|
223 |
+
wppb_change_email_from_headers();
|
224 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags(), $email_customizer_option, array( 'user_login' => $username, 'user_email' => $user_email, 'user_id' => $user_info->data->ID ) );
|
225 |
+
}
|
226 |
+
|
227 |
+
return $default_string;
|
228 |
+
}
|
229 |
+
|
230 |
+
function wppb_admin_email_customizer_password_reset_content_filter_handler( $default_string, $username, $new_password, $userID ) {
|
231 |
+
$email_customizer_option = get_option( 'wppb_admin_emailc_user_password_reset_email_content', 'not_found' );
|
232 |
+
|
233 |
+
if( $email_customizer_option != 'not_found' ) {
|
234 |
+
$user_info = get_userdata( $userID );
|
235 |
+
wppb_change_email_from_headers();
|
236 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags(), $email_customizer_option, array( 'user_login' => $username, 'user_password' => $new_password, 'user_id' => $userID, 'user_email' => $user_info->user_email ) );
|
237 |
+
}
|
238 |
+
|
239 |
+
return $default_string;
|
240 |
+
}
|
241 |
+
|
242 |
+
function wppb_admin_email_customizer_password_reset_title_filter_handler( $default_string, $username ) {
|
243 |
+
$email_customizer_option = get_option( 'wppb_admin_emailc_user_password_reset_email_subject', 'not_found' );
|
244 |
+
|
245 |
+
if( $email_customizer_option != 'not_found' ) {
|
246 |
+
$user_info = get_user_by( 'login', $username );
|
247 |
+
/* we could have email instead of username in $username */
|
248 |
+
if( !$user_info )
|
249 |
+
$user_info = get_user_by( 'email', $username );
|
250 |
+
|
251 |
+
if( !empty( $user_info->data->user_email ) )
|
252 |
+
$user_email = $user_info->data->user_email;
|
253 |
+
else
|
254 |
+
$user_email = '';
|
255 |
+
wppb_change_email_from_headers();
|
256 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags(), $email_customizer_option, array( 'user_login' => $username, 'user_email' => $user_email, 'user_id' => $user_info->data->ID ) );
|
257 |
+
}
|
258 |
+
|
259 |
+
return $default_string;
|
260 |
+
}
|
261 |
+
|
262 |
+
function wppb_email_customizer_user_email_change_request_subject_filter_handler($default_string, $user) {
|
263 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_change_email_address_request_subject', 'not_found' );
|
264 |
+
|
265 |
+
if( $email_customizer_option != 'not_found' && class_exists( 'PB_Mustache_Generate_Template' ) ) {
|
266 |
+
wppb_change_email_from_headers();
|
267 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags( 'change_email_address_request' ), $email_customizer_option, array( 'user_login' => $user->user_login, 'user_email' => $user->user_email, 'user_id' => $user->ID ));
|
268 |
+
}
|
269 |
+
|
270 |
+
return $default_string;
|
271 |
+
}
|
272 |
+
|
273 |
+
function wppb_email_customizer_user_email_change_request_content_filter_handler($default_string, $user, $email_change_request_url) {
|
274 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_change_email_address_request_content', 'not_found' );
|
275 |
+
|
276 |
+
if( $email_customizer_option != 'not_found' && class_exists( 'PB_Mustache_Generate_Template' ) ) {
|
277 |
+
wppb_change_email_from_headers();
|
278 |
+
|
279 |
+
return (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags( 'change_email_address_request' ), $email_customizer_option, array( 'user_login' => $user->user_login, 'user_email' => $user->user_email, 'user_id' => $user->ID, 'change_email_url' => $email_change_request_url ));
|
280 |
+
}
|
281 |
+
|
282 |
+
return $default_string;
|
283 |
+
}
|
284 |
+
|
285 |
+
function wppb_email_customizer_change_email_address_title_filter_handler ( $default, $old_user_data, $new_user_data ){
|
286 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_change_email_address_subject', 'not_found' );
|
287 |
+
|
288 |
+
if( $email_customizer_option != 'not_found' && class_exists( 'PB_Mustache_Generate_Template' ) ) {
|
289 |
+
$email_change_email = $default;
|
290 |
+
$subject = (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags(), $email_customizer_option, array( 'user_login' => $old_user_data['user_login'], 'user_email' => $old_user_data['user_email'], 'user_id' => $old_user_data['ID'] ) );
|
291 |
+
$email_change_email['subject'] = html_entity_decode( htmlspecialchars_decode( $subject, ENT_QUOTES ), ENT_QUOTES );
|
292 |
+
|
293 |
+
wppb_change_email_from_headers();
|
294 |
+
return $email_change_email;
|
295 |
+
}
|
296 |
+
|
297 |
+
return $default;
|
298 |
+
}
|
299 |
+
|
300 |
+
function wppb_email_customizer_change_email_address_header_filter_handler( $email_change_email, $user, $userdata ){
|
301 |
+
$email_change_email['headers'] = array( 'Content-Type: text/html; charset=UTF-8' );
|
302 |
+
return $email_change_email;
|
303 |
+
}
|
304 |
+
|
305 |
+
function wppb_email_customizer_change_email_address_content_filter_handler ( $default, $old_user_data, $new_user_data ){
|
306 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_change_email_address_content', 'not_found' );
|
307 |
+
|
308 |
+
if( $email_customizer_option != 'not_found' && class_exists( 'PB_Mustache_Generate_Template' ) ) {
|
309 |
+
|
310 |
+
$email_change_email = $default;
|
311 |
+
$email_content = (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags(), $email_customizer_option, array( 'user_login' => $old_user_data['user_login'], 'user_email' => $old_user_data['user_email'], 'user_id' => $old_user_data['ID'] ) );
|
312 |
+
$email_change_email['message'] = $email_content;
|
313 |
+
|
314 |
+
wppb_change_email_from_headers();
|
315 |
+
return $email_change_email;
|
316 |
+
}
|
317 |
+
|
318 |
+
return $default;
|
319 |
+
}
|
320 |
+
|
321 |
+
function wppb_email_customizer_epaa_title_filter_handler ( $default, $user_id, $approved_field_names, $unapproved_field_names ){
|
322 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_epaa_notification_subject', 'not_found' );
|
323 |
+
|
324 |
+
if( $email_customizer_option != 'not_found' && class_exists( 'PB_Mustache_Generate_Template' ) ) {
|
325 |
+
$user_info = get_user_by( 'id', $user_id );
|
326 |
+
$subject = (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags( 'epaa_notification' ), $email_customizer_option, array( 'user_login' => $user_info->data->user_login, 'user_email' => $user_info->data->user_email, 'user_id' => $user_info->data->ID, 'approved_field_names' => $approved_field_names, 'unapproved_field_names' => $unapproved_field_names ) );
|
327 |
+
|
328 |
+
wppb_change_email_from_headers();
|
329 |
+
return $subject;
|
330 |
+
}
|
331 |
+
|
332 |
+
return $default;
|
333 |
+
}
|
334 |
+
|
335 |
+
function wppb_email_customizer_epaa_content_filter_handler ( $default, $user_id, $approved_field_names, $unapproved_field_names ){
|
336 |
+
$email_customizer_option = get_option( 'wppb_user_emailc_epaa_notification_content', 'not_found' );
|
337 |
+
|
338 |
+
if( $email_customizer_option != 'not_found' && class_exists( 'PB_Mustache_Generate_Template' ) ) {
|
339 |
+
$user_info = get_user_by( 'id', $user_id );
|
340 |
+
$email_content = (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags( 'epaa_notification' ), $email_customizer_option, array( 'user_login' => $user_info->data->user_login, 'user_email' => $user_info->data->user_email, 'user_id' => $user_info->data->ID, 'approved_field_names' => $approved_field_names, 'unapproved_field_names' => $unapproved_field_names ) );
|
341 |
+
|
342 |
+
wppb_change_email_from_headers();
|
343 |
+
return $email_content;
|
344 |
+
}
|
345 |
+
|
346 |
+
return $default;
|
347 |
+
}
|
348 |
+
|
349 |
+
function wppb_admin_email_customizer_epaa_title_filter_handler ( $default, $user_id, $fields, $approve_url ){
|
350 |
+
$email_customizer_option = get_option( 'wppb_admin_emailc_epaa_notification_subject', 'not_found' );
|
351 |
+
|
352 |
+
if( $email_customizer_option != 'not_found' && class_exists( 'PB_Mustache_Generate_Template' ) ) {
|
353 |
+
$user_info = get_user_by( 'id', $user_id );
|
354 |
+
$subject = (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags( 'epaa_notification_admin' ), $email_customizer_option, array( 'user_login' => $user_info->data->user_login, 'user_email' => $user_info->data->user_email, 'user_id' => $user_info->data->ID, 'modified_fields' => $fields, 'approval_url' => $approve_url ) );
|
355 |
+
|
356 |
+
wppb_change_email_from_headers();
|
357 |
+
return $subject;
|
358 |
+
}
|
359 |
+
|
360 |
+
return $default;
|
361 |
+
}
|
362 |
+
|
363 |
+
function wppb_admin_email_customizer_epaa_content_filter_handler ( $default, $user_id, $fields, $approve_url ){
|
364 |
+
$email_customizer_option = get_option( 'wppb_admin_emailc_epaa_notification_content', 'not_found' );
|
365 |
+
|
366 |
+
if( $email_customizer_option != 'not_found' && class_exists( 'PB_Mustache_Generate_Template' ) ) {
|
367 |
+
$user_info = get_user_by( 'id', $user_id );
|
368 |
+
$email_content = (string) new PB_Mustache_Generate_Template( wppb_email_customizer_generate_merge_tags( 'epaa_notification_admin' ), $email_customizer_option, array( 'user_login' => $user_info->data->user_login, 'user_email' => $user_info->data->user_email, 'user_id' => $user_info->data->ID, 'modified_fields' => $fields, 'approval_url' => $approve_url ) );
|
369 |
+
|
370 |
+
wppb_change_email_from_headers();
|
371 |
+
return html_entity_decode( htmlspecialchars_decode( $email_content, ENT_QUOTES ), ENT_QUOTES );
|
372 |
+
}
|
373 |
+
|
374 |
+
return $default;
|
375 |
+
}
|
376 |
+
|
377 |
+
|
378 |
+
$wppb_addonOptions = get_option( 'wppb_module_settings' );
|
379 |
+
$wppb_email_customizer_activate = 'hide';
|
380 |
+
if ( ( !empty( $wppb_addonOptions['wppb_emailCustomizer'] ) && $wppb_addonOptions['wppb_emailCustomizer'] == 'show' ) || ( !empty( $wppb_addonOptions['wppb_emailCustomizerAdmin'] ) && $wppb_addonOptions['wppb_emailCustomizerAdmin'] == 'show' ) ) {
|
381 |
+
$wppb_email_customizer_activate = 'show';
|
382 |
+
}
|
383 |
+
// using filters, we overwrite the old content with the new one from the email customizer (for the user)
|
384 |
+
if ( $wppb_email_customizer_activate == 'show' ){
|
385 |
+
add_filter ( 'wppb_register_user_email_subject_without_admin_approval', 'wppb_email_customizer_admin_approval_new_user_signup_filter_handler', 10, 5 );
|
386 |
+
add_filter ( 'wppb_register_user_email_message_without_admin_approval', 'wppb_email_customizer_admin_approval_new_user_signup_filter_handler', 10, 5 );
|
387 |
+
|
388 |
+
add_filter ( 'wppb_register_user_email_subject_with_admin_approval', 'wppb_email_customizer_admin_approval_new_user_signup_subject_filter_handler', 10, 5 );
|
389 |
+
add_filter ( 'wppb_register_user_email_message_with_admin_approval', 'wppb_email_customizer_admin_approval_new_user_signup_message_filter_handler', 10, 5 );
|
390 |
+
|
391 |
+
add_filter ( 'wppb_new_user_status_subject_approved', 'wppb_email_customizer_admin_approval_new_user_status_filter_handler', 10, 5 );
|
392 |
+
add_filter ( 'wppb_new_user_status_message_approved', 'wppb_email_customizer_admin_approval_new_user_status_filter_handler', 10, 5 );
|
393 |
+
|
394 |
+
add_filter ( 'wppb_new_user_status_subject_unapproved', 'wppb_email_customizer_admin_approval_new_user_status_filter_handler', 10, 5 );
|
395 |
+
add_filter ( 'wppb_new_user_status_message_unapproved', 'wppb_email_customizer_admin_approval_new_user_status_filter_handler', 10, 5 );
|
396 |
+
|
397 |
+
add_filter ( 'wppb_signup_user_notification_email_subject', 'wppb_email_customizer_email_confirmation_filter_handler', 10, 8 );
|
398 |
+
add_filter ( 'wppb_signup_user_notification_email_content', 'wppb_email_customizer_email_confirmation_filter_handler', 10, 8 );
|
399 |
+
|
400 |
+
add_filter ( 'wppb_recover_password_message_content_sent_to_user1', 'wppb_email_customizer_password_reset_content_filter_handler', 10, 4 );
|
401 |
+
add_filter ( 'wppb_recover_password_message_title_sent_to_user1', 'wppb_email_customizer_password_reset_title_filter_handler', 10, 2 );
|
402 |
+
|
403 |
+
add_filter ( 'wppb_recover_password_message_content_sent_to_user2', 'wppb_email_customizer_password_reset_success_content_filter_handler', 10, 4 );
|
404 |
+
add_filter ( 'wppb_recover_password_message_title_sent_to_user2', 'wppb_email_customizer_password_reset_success_title_filter_handler', 10, 2 );
|
405 |
+
|
406 |
+
add_filter ( 'wppb_user_email_change_request_notification_subject', 'wppb_email_customizer_user_email_change_request_subject_filter_handler', 10, 3);
|
407 |
+
add_filter ( 'wppb_user_email_change_request_notification_content', 'wppb_email_customizer_user_email_change_request_content_filter_handler', 10, 3);
|
408 |
+
|
409 |
+
add_filter ( 'email_change_email', 'wppb_email_customizer_change_email_address_content_filter_handler', 10, 3);
|
410 |
+
add_filter ( 'email_change_email', 'wppb_email_customizer_change_email_address_title_filter_handler', 10, 3);
|
411 |
+
add_filter ( 'email_change_email', 'wppb_email_customizer_change_email_address_header_filter_handler', 10, 3);
|
412 |
+
|
413 |
+
if( function_exists( 'wppb_in_init_edit_profile_approval' ) ) {
|
414 |
+
add_filter('wppb_epaa_user_email_content', 'wppb_email_customizer_epaa_content_filter_handler', 10, 4);
|
415 |
+
add_filter('wppb_epaa_user_email_subject', 'wppb_email_customizer_epaa_title_filter_handler', 10, 4);
|
416 |
+
}
|
417 |
+
|
418 |
+
}
|
419 |
+
|
420 |
+
// using filters, we overwrite the old content with the new one from the email customizer (for the admin)
|
421 |
+
if ( $wppb_email_customizer_activate == 'show' ){
|
422 |
+
add_filter ( 'wppb_register_admin_email_subject_without_admin_approval', 'wppb_email_customizer_admin_approval_new_user_signup_filter_handler', 10, 5 );
|
423 |
+
add_filter ( 'wppb_register_admin_email_message_without_admin_approval', 'wppb_email_customizer_admin_approval_new_user_signup_filter_handler', 10, 5 );
|
424 |
+
|
425 |
+
add_filter ( 'wppb_register_admin_email_subject_with_admin_approval', 'wppb_email_customizer_admin_approval_new_user_signup_filter_handler', 10, 5 );
|
426 |
+
add_filter ( 'wppb_register_admin_email_message_with_admin_approval', 'wppb_email_customizer_admin_approval_new_user_signup_filter_handler', 10, 5 );
|
427 |
+
|
428 |
+
add_filter ( 'wppb_recover_password_message_content_sent_to_admin', 'wppb_admin_email_customizer_password_reset_content_filter_handler', 10, 4 );
|
429 |
+
add_filter ( 'wppb_recover_password_message_title_sent_to_admin', 'wppb_admin_email_customizer_password_reset_title_filter_handler', 10, 2 );
|
430 |
+
|
431 |
+
if( function_exists( 'wppb_in_init_edit_profile_approval' ) ) {
|
432 |
+
add_filter('wppb_epaa_admin_email_content', 'wppb_admin_email_customizer_epaa_content_filter_handler', 10, 4);
|
433 |
+
add_filter('wppb_epaa_admin_email_subject', 'wppb_admin_email_customizer_epaa_title_filter_handler', 10, 4);
|
434 |
+
}
|
435 |
+
|
436 |
+
}
|
437 |
+
|
438 |
+
|
439 |
+
// Mustache variables
|
440 |
+
|
441 |
+
/**
|
442 |
+
* Function that overwrites the default reply-to with the one set in the Email Customizer
|
443 |
+
*
|
444 |
+
* @since v.2.0
|
445 |
+
*
|
446 |
+
* @param string $sender_email
|
447 |
+
*
|
448 |
+
* @return string
|
449 |
+
*/
|
450 |
+
function wppb_ec_website_email( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
451 |
+
$admin_email_address = get_bloginfo( 'admin_email' );
|
452 |
+
|
453 |
+
$emailc_common_settings_from_reply_to_email = get_option( 'wppb_emailc_common_settings_from_reply_to_email', 'not_found' );
|
454 |
+
if ( $emailc_common_settings_from_reply_to_email != 'not_found' ) {
|
455 |
+
if( strpos( $emailc_common_settings_from_reply_to_email, '{{reply_to}}' ) === false ) {
|
456 |
+
if( is_email( trim( $emailc_common_settings_from_reply_to_email ) ) )
|
457 |
+
$admin_email_address = trim( $emailc_common_settings_from_reply_to_email );
|
458 |
+
}
|
459 |
+
}
|
460 |
+
|
461 |
+
return apply_filters( 'wppb_ec_sender_email_filter', trim( $admin_email_address ) );
|
462 |
+
}
|
463 |
+
add_filter( 'mustache_variable_ec_reply_to', 'wppb_ec_website_email', 10, 4 );
|
464 |
+
|
465 |
+
|
466 |
+
/**
|
467 |
+
* Function that filters and returns the site_url in the Email Customizer
|
468 |
+
*
|
469 |
+
* @since v.2.0
|
470 |
+
*
|
471 |
+
* @param string $value
|
472 |
+
* @param string $merge_tag_name
|
473 |
+
* @param string $merge_tag
|
474 |
+
* @param array $extra_data
|
475 |
+
*
|
476 |
+
* @return string
|
477 |
+
*/
|
478 |
+
function wppb_ec_replace_site_url( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
479 |
+
return apply_filters( 'wppb_ec_site_url_filter', get_bloginfo( 'url' ) );
|
480 |
+
}
|
481 |
+
add_filter( 'mustache_variable_ec_site_url', 'wppb_ec_replace_site_url', 10, 4 );
|
482 |
+
|
483 |
+
|
484 |
+
/**
|
485 |
+
* Function that filters and returns the site_name in the Email Customizer
|
486 |
+
*
|
487 |
+
* @since v.2.0
|
488 |
+
*
|
489 |
+
* @param string $value
|
490 |
+
* @param string $merge_tag_name
|
491 |
+
* @param string $merge_tag
|
492 |
+
* @param array $extra_data
|
493 |
+
*
|
494 |
+
* @return string
|
495 |
+
*/
|
496 |
+
function wppb_ec_replace_site_name( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
497 |
+
return apply_filters( 'wppb_ec_site_name_filter', html_entity_decode( htmlspecialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), ENT_QUOTES ) );
|
498 |
+
}
|
499 |
+
add_filter( 'mustache_variable_ec_site_name', 'wppb_ec_replace_site_name', 10, 4 );
|
500 |
+
|
501 |
+
|
502 |
+
/**
|
503 |
+
* Function that filters and returns the user_id in the Email Customizer
|
504 |
+
*
|
505 |
+
* @since v.2.0
|
506 |
+
*
|
507 |
+
* @param string $value
|
508 |
+
* @param string $merge_tag_name
|
509 |
+
* @param string $merge_tag
|
510 |
+
* @param array $extra_data
|
511 |
+
*
|
512 |
+
* @return string
|
513 |
+
*/
|
514 |
+
function wppb_ec_replace_user_id( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
515 |
+
if( !empty( $extra_data['user_id'] ) )
|
516 |
+
return $extra_data['user_id'];
|
517 |
+
else
|
518 |
+
return '';
|
519 |
+
}
|
520 |
+
add_filter( 'mustache_variable_ec_user_id', 'wppb_ec_replace_user_id', 10, 4 );
|
521 |
+
|
522 |
+
|
523 |
+
/**
|
524 |
+
* Function that filters and returns the username in the Email Customizer
|
525 |
+
*
|
526 |
+
* @since v.2.0
|
527 |
+
*
|
528 |
+
* @param string $value
|
529 |
+
* @param string $merge_tag_name
|
530 |
+
* @param string $merge_tag
|
531 |
+
* @param array $extra_data
|
532 |
+
*
|
533 |
+
* @return string
|
534 |
+
*/
|
535 |
+
function wppb_ec_replace_username( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
536 |
+
$wppb_general_settings = get_option( 'wppb_general_settings' );
|
537 |
+
|
538 |
+
if ( isset( $extra_data['email_confirmation_unserialized_data']['user_login'] ) ){
|
539 |
+
if( isset( $wppb_general_settings['loginWith'] ) && ( $wppb_general_settings['loginWith'] == 'email' ) )
|
540 |
+
return $extra_data['email_confirmation_unserialized_data']['user_email'];
|
541 |
+
else
|
542 |
+
return $extra_data['email_confirmation_unserialized_data']['user_login'];
|
543 |
+
}
|
544 |
+
|
545 |
+
if( isset( $wppb_general_settings['loginWith'] ) && ( $wppb_general_settings['loginWith'] == 'email' ) )
|
546 |
+
return $extra_data['user_email'];
|
547 |
+
else
|
548 |
+
return $extra_data['user_login'];
|
549 |
+
|
550 |
+
}
|
551 |
+
add_filter( 'mustache_variable_ec_username', 'wppb_ec_replace_username', 10, 4 );
|
552 |
+
|
553 |
+
|
554 |
+
/**
|
555 |
+
* Function that filters and returns the user_email in the Email Customizer
|
556 |
+
*
|
557 |
+
* @since v.2.0
|
558 |
+
*
|
559 |
+
* @param string $value
|
560 |
+
* @param string $merge_tag_name
|
561 |
+
* @param string $merge_tag
|
562 |
+
* @param array $extra_data
|
563 |
+
*
|
564 |
+
* @return string
|
565 |
+
*/
|
566 |
+
function wppb_ec_replace_user_email( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
567 |
+
if ( isset( $extra_data['email_confirmation_unserialized_data']['user_email'] ) )
|
568 |
+
return $extra_data['email_confirmation_unserialized_data']['user_email'];
|
569 |
+
|
570 |
+
if( !empty( $extra_data['user_email'] ) )
|
571 |
+
return $extra_data['user_email'];
|
572 |
+
else
|
573 |
+
return '';
|
574 |
+
}
|
575 |
+
add_filter( 'mustache_variable_ec_user_email', 'wppb_ec_replace_user_email', 10, 4 );
|
576 |
+
|
577 |
+
/**
|
578 |
+
* Function that filters and returns the users password in the Email Customizer
|
579 |
+
*
|
580 |
+
* @since v.2.0
|
581 |
+
*
|
582 |
+
* @param string $value
|
583 |
+
* @param string $merge_tag_name
|
584 |
+
* @param string $merge_tag
|
585 |
+
* @param array $extra_data
|
586 |
+
*
|
587 |
+
* @return string
|
588 |
+
*/
|
589 |
+
function wppb_ec_replace_password( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
590 |
+
/* don't send the password to the admin */
|
591 |
+
global $wp_current_filter;
|
592 |
+
if( !empty( $wp_current_filter ) && is_array( $wp_current_filter ) ){
|
593 |
+
foreach( $wp_current_filter as $filter ){
|
594 |
+
if( $filter == 'wppb_register_admin_email_subject_without_admin_approval' ||
|
595 |
+
$filter == 'wppb_register_admin_email_message_without_admin_approval' ||
|
596 |
+
$filter == 'wppb_register_admin_email_subject_with_admin_approval' ||
|
597 |
+
$filter == 'wppb_register_admin_email_message_with_admin_approval' ||
|
598 |
+
$filter == 'wppb_recover_password_message_content_sent_to_admin' ||
|
599 |
+
$filter == 'wppb_recover_password_message_title_sent_to_admin'
|
600 |
+
){
|
601 |
+
return __( 'The users selected password at signup', 'profile-builder' );
|
602 |
+
}
|
603 |
+
}
|
604 |
+
}
|
605 |
+
|
606 |
+
$wppb_general_settings = get_option( 'wppb_general_settings' );
|
607 |
+
|
608 |
+
if( empty( $extra_data['email_confirmation_unserialized_data']['user_pass'] ) && empty( $extra_data['user_password'] ) ){
|
609 |
+
return __( 'Your selected password at signup', 'profile-builder' );
|
610 |
+
}
|
611 |
+
|
612 |
+
if ( isset( $extra_data['email_confirmation_unserialized_data']['user_pass'] ) ) {
|
613 |
+
if( base64_encode( base64_decode($extra_data['email_confirmation_unserialized_data']['user_pass'] ) ) === $extra_data['email_confirmation_unserialized_data']['user_pass'] )
|
614 |
+
return base64_decode($extra_data['email_confirmation_unserialized_data']['user_pass']);
|
615 |
+
else
|
616 |
+
return __( 'Your selected password at signup', 'profile-builder' );
|
617 |
+
}
|
618 |
+
|
619 |
+
if( wppb_get_admin_approval_option_value() === 'yes' ){
|
620 |
+
if ( isset( $extra_data['user_password'] ) ) {
|
621 |
+
global $wpdb;
|
622 |
+
$search_for_password = $wpdb->get_results(
|
623 |
+
"
|
624 |
+
SELECT user_pass
|
625 |
+
FROM $wpdb->users
|
626 |
+
WHERE user_pass = '".$extra_data['user_password']."'
|
627 |
+
"
|
628 |
+
);
|
629 |
+
if( !empty( $search_for_password ) )
|
630 |
+
return __( 'Your selected password at signup', 'profile-builder' );
|
631 |
+
}
|
632 |
+
}
|
633 |
+
|
634 |
+
return $extra_data['user_password'];
|
635 |
+
}
|
636 |
+
add_filter( 'mustache_variable_ec_password', 'wppb_ec_replace_password', 10, 4 );
|
637 |
+
|
638 |
+
|
639 |
+
/**
|
640 |
+
* Function that filters and returns the users website in the Email Customizer
|
641 |
+
*
|
642 |
+
* @since v.2.0
|
643 |
+
*
|
644 |
+
* @param string $value
|
645 |
+
* @param string $merge_tag_name
|
646 |
+
* @param string $merge_tag
|
647 |
+
* @param array $extra_data
|
648 |
+
*
|
649 |
+
* @return string
|
650 |
+
*/
|
651 |
+
function wppb_ec_replace_website( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
652 |
+
if ( isset( $extra_data['email_confirmation_unserialized_data']['user_url'] ) )
|
653 |
+
return $extra_data['email_confirmation_unserialized_data']['user_url'];
|
654 |
+
if( !empty( $extra_data['user_data'] ) )
|
655 |
+
return $extra_data['user_data']->user_url;
|
656 |
+
}
|
657 |
+
add_filter( 'mustache_variable_ec_website', 'wppb_ec_replace_website', 10, 4 );
|
658 |
+
|
659 |
+
|
660 |
+
/**
|
661 |
+
* Function that filters and returns the users blog url in the Email Customizer
|
662 |
+
*
|
663 |
+
* @since v.2.5.5
|
664 |
+
*
|
665 |
+
* @param string $value
|
666 |
+
* @param string $merge_tag_name
|
667 |
+
* @param string $merge_tag
|
668 |
+
* @param array $extra_data
|
669 |
+
*
|
670 |
+
* @return string
|
671 |
+
*/
|
672 |
+
function wppb_ec_replace_blog_url( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
673 |
+
if ( isset( $extra_data['email_confirmation_unserialized_data']['wppb_create_new_site_checkbox'] ) && $extra_data['email_confirmation_unserialized_data']['wppb_create_new_site_checkbox'] == 'yes' ) {
|
674 |
+
$blog_details = wpmu_validate_blog_signup( $extra_data['email_confirmation_unserialized_data']['wppb_blog_url'], $extra_data['email_confirmation_unserialized_data']['wppb_blog_title'] );
|
675 |
+
if ( empty($blog_details['errors']->errors['blogname']) && empty($blog_details['errors']->errors['blog_title']))
|
676 |
+
return $blog_details['domain'] . $blog_details['path'];
|
677 |
+
}
|
678 |
+
|
679 |
+
if( !empty( $extra_data['user_data'] ) ) {
|
680 |
+
return wppb_get_blog_url_of_user_id($extra_data['user_data']->ID);
|
681 |
+
}
|
682 |
+
}
|
683 |
+
add_filter( 'mustache_variable_ec_blog_url', 'wppb_ec_replace_blog_url', 10, 4 );
|
684 |
+
|
685 |
+
|
686 |
+
/**
|
687 |
+
* Function that filters and returns the users role in the Email Customizer
|
688 |
+
*
|
689 |
+
* @since v.2.0
|
690 |
+
*
|
691 |
+
* @param string $value
|
692 |
+
* @param string $merge_tag_name
|
693 |
+
* @param string $merge_tag
|
694 |
+
* @param array $extra_data
|
695 |
+
*
|
696 |
+
* @return string
|
697 |
+
*/
|
698 |
+
function wppb_ec_replace_role( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
699 |
+
if( isset( $extra_data['email_confirmation_unserialized_data']['role'] ) ) {
|
700 |
+
return $extra_data['email_confirmation_unserialized_data']['role'];
|
701 |
+
}
|
702 |
+
|
703 |
+
if( ! empty( $extra_data['user_data'] ) ) {
|
704 |
+
$user_role = implode( ', ', $extra_data['user_data']->roles );
|
705 |
+
return $user_role;
|
706 |
+
}
|
707 |
+
|
708 |
+
if( ! empty ( $extra_data['user_id'] ) ) {
|
709 |
+
$user_data = get_user_by( 'id', $extra_data['user_id'] );
|
710 |
+
$user_role = implode( ', ', $user_data->roles );
|
711 |
+
return $user_role;
|
712 |
+
}
|
713 |
+
}
|
714 |
+
add_filter( 'mustache_variable_ec_role', 'wppb_ec_replace_role', 10, 4 );
|
715 |
+
|
716 |
+
/**
|
717 |
+
* Function that filters and returns the users role label in the Email Customizer
|
718 |
+
*
|
719 |
+
* @since v.2.7.1
|
720 |
+
*
|
721 |
+
* @param string $value
|
722 |
+
* @param string $merge_tag_name
|
723 |
+
* @param string $merge_tag
|
724 |
+
* @param array $extra_data
|
725 |
+
*
|
726 |
+
* @return string
|
727 |
+
*/
|
728 |
+
function wppb_ec_replace_role_label( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
729 |
+
$roles = explode(', ', wppb_ec_replace_role( $value, $merge_tag_name, $merge_tag, $extra_data ) );
|
730 |
+
$role_labels = '';
|
731 |
+
|
732 |
+
foreach ($roles as $role) {
|
733 |
+
$role_labels .= wppb_get_role_name( $role ) . ', ';
|
734 |
+
}
|
735 |
+
|
736 |
+
return rtrim($role_labels, ', ');
|
737 |
+
}
|
738 |
+
add_filter( 'mustache_variable_ec_role_label', 'wppb_ec_replace_role_label', 10, 4 );
|
739 |
+
|
740 |
+
/**
|
741 |
+
* Function that filters and returns the users activation_key in the Email Customizer
|
742 |
+
*
|
743 |
+
* @since v.2.0
|
744 |
+
*
|
745 |
+
* @param string $value
|
746 |
+
* @param string $merge_tag_name
|
747 |
+
* @param string $merge_tag
|
748 |
+
* @param array $extra_data
|
749 |
+
*
|
750 |
+
* @return string
|
751 |
+
*/
|
752 |
+
function wppb_ec_replace_activation_key( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
753 |
+
if ( isset( $extra_data['email_confirmation_key'] ) )
|
754 |
+
return $extra_data['email_confirmation_key'];
|
755 |
+
}
|
756 |
+
add_filter( 'mustache_variable_ec_activation_key', 'wppb_ec_replace_activation_key', 10, 4 );
|
757 |
+
|
758 |
+
|
759 |
+
/**
|
760 |
+
* Function that filters and returns the users activation_url in the Email Customizer
|
761 |
+
*
|
762 |
+
* @since v.2.0
|
763 |
+
*
|
764 |
+
* @param string $value
|
765 |
+
* @param string $merge_tag_name
|
766 |
+
* @param string $merge_tag
|
767 |
+
* @param array $extra_data
|
768 |
+
*
|
769 |
+
* @return string
|
770 |
+
*/
|
771 |
+
function wppb_ec_replace_activation_url( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
772 |
+
if ( isset( $extra_data['email_confirmation_url'] ) )
|
773 |
+
return $extra_data['email_confirmation_url'];
|
774 |
+
}
|
775 |
+
add_filter( 'mustache_variable_ec_activation_url', 'wppb_ec_replace_activation_url', 10, 4 );
|
776 |
+
|
777 |
+
|
778 |
+
/**
|
779 |
+
* Function that filters and returns the users activation_link in the Email Customizer
|
780 |
+
*
|
781 |
+
* @since v.2.0
|
782 |
+
*
|
783 |
+
* @param string $value
|
784 |
+
* @param string $merge_tag_name
|
785 |
+
* @param string $merge_tag
|
786 |
+
* @param array $extra_data
|
787 |
+
*
|
788 |
+
* @return string
|
789 |
+
*/
|
790 |
+
function wppb_ec_replace_activation_link( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
791 |
+
if ( isset( $extra_data['email_confirmation_url'] ) )
|
792 |
+
return '<a href="'.$extra_data['email_confirmation_url'].'" target="_blank">'.$extra_data['email_confirmation_url'].'</a>';
|
793 |
+
}
|
794 |
+
add_filter( 'mustache_variable_ec_activation_link', 'wppb_ec_replace_activation_link', 10, 4 );
|
795 |
+
|
796 |
+
|
797 |
+
/**
|
798 |
+
* Function that filters and returns the users reset_key in the Email Customizer
|
799 |
+
*
|
800 |
+
* @since v.2.0
|
801 |
+
*
|
802 |
+
* @param string $value
|
803 |
+
* @param string $merge_tag_name
|
804 |
+
* @param string $merge_tag
|
805 |
+
* @param array $extra_data
|
806 |
+
*
|
807 |
+
* @return string
|
808 |
+
*/
|
809 |
+
function wppb_ec_replace_reset_key( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
810 |
+
if ( isset( $extra_data['reset_key'] ) )
|
811 |
+
return $extra_data['reset_key'];
|
812 |
+
}
|
813 |
+
add_filter( 'mustache_variable_ec_reset_key', 'wppb_ec_replace_reset_key', 10, 4 );
|
814 |
+
|
815 |
+
|
816 |
+
/**
|
817 |
+
* Function that filters and returns the users reset_url in the Email Customizer
|
818 |
+
*
|
819 |
+
* @since v.2.0
|
820 |
+
*
|
821 |
+
* @param string $value
|
822 |
+
* @param string $merge_tag_name
|
823 |
+
* @param string $merge_tag
|
824 |
+
* @param array $extra_data
|
825 |
+
*
|
826 |
+
* @return string
|
827 |
+
*/
|
828 |
+
function wppb_ec_replace_reset_url( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
829 |
+
if ( isset( $extra_data['reset_url'] ) )
|
830 |
+
return $extra_data['reset_url'];
|
831 |
+
}
|
832 |
+
add_filter( 'mustache_variable_ec_reset_url', 'wppb_ec_replace_reset_url', 10, 4 );
|
833 |
+
|
834 |
+
|
835 |
+
/**
|
836 |
+
* Function that filters and returns the users reset_link in the Email Customizer
|
837 |
+
*
|
838 |
+
* @since v.2.0
|
839 |
+
*
|
840 |
+
* @param string $value
|
841 |
+
* @param string $merge_tag_name
|
842 |
+
* @param string $merge_tag
|
843 |
+
* @param array $extra_data
|
844 |
+
*
|
845 |
+
* @return string
|
846 |
+
*/
|
847 |
+
function wppb_ec_replace_reset_link( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
848 |
+
if ( isset( $extra_data['reset_url'] ) )
|
849 |
+
return '<a href="'.$extra_data['reset_url'].'" target="_blank">'.$extra_data['reset_url'].'</a>';
|
850 |
+
}
|
851 |
+
add_filter( 'mustache_variable_ec_reset_link', 'wppb_ec_replace_reset_link', 10, 4 );
|
852 |
+
|
853 |
+
|
854 |
+
/**
|
855 |
+
* Function that filters and returns the users user_email_change_link in the Email Customizer
|
856 |
+
*
|
857 |
+
* @since v.
|
858 |
+
*
|
859 |
+
* @param string $value
|
860 |
+
* @param string $merge_tag_name
|
861 |
+
* @param string $merge_tag
|
862 |
+
* @param array $extra_data
|
863 |
+
*
|
864 |
+
* @return string
|
865 |
+
*/
|
866 |
+
function wppb_ec_generate_user_email_change_link( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
867 |
+
if ( isset( $extra_data['change_email_url'] ) )
|
868 |
+
return $extra_data['change_email_url'];
|
869 |
+
}
|
870 |
+
add_filter( 'mustache_variable_ec_user_email_change_link', 'wppb_ec_generate_user_email_change_link', 10, 4 );
|
871 |
+
|
872 |
+
|
873 |
+
/**
|
874 |
+
* Function that filters and returns the approve user link in the (Admin) Email Customizer.
|
875 |
+
* This allows the admin to approve directly from his email a newly registered user.
|
876 |
+
*
|
877 |
+
* @since v.2.7.3
|
878 |
+
*
|
879 |
+
* @param string $value
|
880 |
+
* @param string $merge_tag_name
|
881 |
+
* @param string $merge_tag
|
882 |
+
* @param array $extra_data
|
883 |
+
*
|
884 |
+
* @return string
|
885 |
+
*/
|
886 |
+
function wppb_ec_replace_approve_user_link( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
887 |
+
|
888 |
+
if( ! empty ( $extra_data['user_id'] ) ) {
|
889 |
+
|
890 |
+
$approve_url = add_query_arg( 'pbaction', 'approve', wppb_ec_get_approve_url( $extra_data['user_id'] ) );
|
891 |
+
|
892 |
+
if ( !empty($approve_url) )
|
893 |
+
|
894 |
+
return '<a href="' . $approve_url . '" target="_blank">' . $approve_url . '</a>';
|
895 |
+
}
|
896 |
+
}
|
897 |
+
add_filter( 'mustache_variable_ec_approve_link', 'wppb_ec_replace_approve_user_link', 10, 4 );
|
898 |
+
|
899 |
+
|
900 |
+
/**
|
901 |
+
* Function that filters and returns the approve user url in the (Admin) Email Customizer.
|
902 |
+
*
|
903 |
+
* @since v.2.7.3
|
904 |
+
*
|
905 |
+
* @param string $value
|
906 |
+
* @param string $merge_tag_name
|
907 |
+
* @param string $merge_tag
|
908 |
+
* @param array $extra_data
|
909 |
+
*
|
910 |
+
* @return string
|
911 |
+
*/
|
912 |
+
function wppb_ec_replace_approve_user_url( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
913 |
+
|
914 |
+
if( ! empty ( $extra_data['user_id'] ) ) {
|
915 |
+
|
916 |
+
$approve_url = add_query_arg( 'pbaction', 'approve', wppb_ec_get_approve_url( $extra_data['user_id'] ) );
|
917 |
+
|
918 |
+
if ( !empty( $approve_url ) )
|
919 |
+
return $approve_url;
|
920 |
+
|
921 |
+
}
|
922 |
+
}
|
923 |
+
add_filter( 'mustache_variable_ec_approve_url', 'wppb_ec_replace_approve_user_url', 10, 4 );
|
924 |
+
|
925 |
+
/**
|
926 |
+
* Function that filters and returns the approve user link in the (Admin) Email Customizer.
|
927 |
+
* This allows the admin to approve directly from his email a newly registered user.
|
928 |
+
*
|
929 |
+
* @since v.3.4.7
|
930 |
+
*
|
931 |
+
* @param string $value
|
932 |
+
* @param string $merge_tag_name
|
933 |
+
* @param string $merge_tag
|
934 |
+
* @param array $extra_data
|
935 |
+
*
|
936 |
+
* @return string
|
937 |
+
*/
|
938 |
+
function wppb_ec_replace_unapprove_user_link( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
939 |
+
|
940 |
+
if( ! empty ( $extra_data['user_id'] ) ) {
|
941 |
+
|
942 |
+
$approve_url = add_query_arg( 'pbaction', 'unapprove', wppb_ec_get_approve_url( $extra_data['user_id'] ) );
|
943 |
+
|
944 |
+
if ( !empty($approve_url) )
|
945 |
+
|
946 |
+
return '<a href="' . $approve_url . '" target="_blank">' . $approve_url . '</a>';
|
947 |
+
}
|
948 |
+
}
|
949 |
+
add_filter( 'mustache_variable_ec_unapprove_link', 'wppb_ec_replace_unapprove_user_link', 10, 4 );
|
950 |
+
|
951 |
+
|
952 |
+
/**
|
953 |
+
* Function that filters and returns the approve user url in the (Admin) Email Customizer.
|
954 |
+
*
|
955 |
+
* @since v.3.4.7
|
956 |
+
*
|
957 |
+
* @param string $value
|
958 |
+
* @param string $merge_tag_name
|
959 |
+
* @param string $merge_tag
|
960 |
+
* @param array $extra_data
|
961 |
+
*
|
962 |
+
* @return string
|
963 |
+
*/
|
964 |
+
function wppb_ec_replace_unapprove_user_url( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
965 |
+
|
966 |
+
if( ! empty ( $extra_data['user_id'] ) ) {
|
967 |
+
|
968 |
+
$approve_url = add_query_arg( 'pbaction', 'unapprove', wppb_ec_get_approve_url( $extra_data['user_id'] ) );
|
969 |
+
|
970 |
+
if ( !empty( $approve_url ) )
|
971 |
+
return $approve_url;
|
972 |
+
|
973 |
+
}
|
974 |
+
}
|
975 |
+
add_filter( 'mustache_variable_ec_unapprove_url', 'wppb_ec_replace_unapprove_user_url', 10, 4 );
|
976 |
+
|
977 |
+
/**
|
978 |
+
* Function that returns the approve user url to include in the admin email
|
979 |
+
*
|
980 |
+
* @since v.2.7.3
|
981 |
+
*
|
982 |
+
* @param $user_id The ID of the user that is pending approval
|
983 |
+
*
|
984 |
+
* @return string Approve URL
|
985 |
+
*/
|
986 |
+
function wppb_ec_get_approve_url ( $user_id ){
|
987 |
+
|
988 |
+
$approve_url = '';
|
989 |
+
|
990 |
+
// If the user is "unaproved" the user meta below will contain a (hashed) value that we need to attach to the admin approval email link for validating it
|
991 |
+
$approve_url_param = get_user_meta( $user_id, '_wppb_admin_approval_link_param', true );
|
992 |
+
|
993 |
+
if ( !empty( $approve_url_param ) ) {
|
994 |
+
$approve_url = esc_url( add_query_arg( 'pbapprove', $approve_url_param, get_site_url() ) );
|
995 |
+
}
|
996 |
+
|
997 |
+
return $approve_url;
|
998 |
+
}
|
999 |
+
|
1000 |
+
|
1001 |
+
/**
|
1002 |
+
* Function that filters and returns the approved fields for Edit Profile Approved by Admin.
|
1003 |
+
*
|
1004 |
+
* @since v.2.7.3
|
1005 |
+
*
|
1006 |
+
* @param string $value
|
1007 |
+
* @param string $merge_tag_name
|
1008 |
+
* @param string $merge_tag
|
1009 |
+
* @param array $extra_data
|
1010 |
+
*
|
1011 |
+
* @return string
|
1012 |
+
*/
|
1013 |
+
function wppb_epaa_replace_approved_fields( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
1014 |
+
|
1015 |
+
if( ! empty ( $extra_data['approved_field_names'] ) ) {
|
1016 |
+
return $extra_data['approved_field_names'];
|
1017 |
+
}
|
1018 |
+
|
1019 |
+
return '';
|
1020 |
+
}
|
1021 |
+
add_filter( 'mustache_variable_ec_epaa_approved_fields', 'wppb_epaa_replace_approved_fields', 10, 4 );
|
1022 |
+
|
1023 |
+
|
1024 |
+
/**
|
1025 |
+
* Function that filters and returns the unapproved fields for Edit Profile Approved by Admin.
|
1026 |
+
*
|
1027 |
+
* @since v.2.7.3
|
1028 |
+
*
|
1029 |
+
* @param string $value
|
1030 |
+
* @param string $merge_tag_name
|
1031 |
+
* @param string $merge_tag
|
1032 |
+
* @param array $extra_data
|
1033 |
+
*
|
1034 |
+
* @return string
|
1035 |
+
*/
|
1036 |
+
function wppb_epaa_replace_unapproved_fields( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
1037 |
+
|
1038 |
+
if( ! empty ( $extra_data['unapproved_field_names'] ) ) {
|
1039 |
+
return $extra_data['unapproved_field_names'];
|
1040 |
+
}
|
1041 |
+
|
1042 |
+
return '';
|
1043 |
+
}
|
1044 |
+
add_filter( 'mustache_variable_ec_epaa_unapproved_fields', 'wppb_epaa_replace_unapproved_fields', 10, 4 );
|
1045 |
+
|
1046 |
+
|
1047 |
+
/**
|
1048 |
+
* Function that filters and returns the modified fields for Edit Profile Approved by Admin.
|
1049 |
+
*
|
1050 |
+
* @since v.2.7.3
|
1051 |
+
*
|
1052 |
+
* @param string $value
|
1053 |
+
* @param string $merge_tag_name
|
1054 |
+
* @param string $merge_tag
|
1055 |
+
* @param array $extra_data
|
1056 |
+
*
|
1057 |
+
* @return string
|
1058 |
+
*/
|
1059 |
+
function wppb_epaa_replace_modified_fields( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
1060 |
+
|
1061 |
+
if( ! empty ( $extra_data['modified_fields'] ) ) {
|
1062 |
+
return $extra_data['modified_fields'];
|
1063 |
+
}
|
1064 |
+
|
1065 |
+
return '';
|
1066 |
+
}
|
1067 |
+
add_filter( 'mustache_variable_ec_epaa_modified_fields', 'wppb_epaa_replace_modified_fields', 10, 4 );
|
1068 |
+
|
1069 |
+
|
1070 |
+
/**
|
1071 |
+
* Function that filters and returns the approval URL for Edit Profile Approved by Admin.
|
1072 |
+
*
|
1073 |
+
* @since v.2.7.3
|
1074 |
+
*
|
1075 |
+
* @param string $value
|
1076 |
+
* @param string $merge_tag_name
|
1077 |
+
* @param string $merge_tag
|
1078 |
+
* @param array $extra_data
|
1079 |
+
*
|
1080 |
+
* @return string
|
1081 |
+
*/
|
1082 |
+
function wppb_epaa_replace_approval_url( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
1083 |
+
|
1084 |
+
if( ! empty ( $extra_data['approval_url'] ) ) {
|
1085 |
+
return $extra_data['approval_url'];
|
1086 |
+
}
|
1087 |
+
|
1088 |
+
return '';
|
1089 |
+
}
|
1090 |
+
add_filter( 'mustache_variable_ec_epaa_approval_url', 'wppb_epaa_replace_approval_url', 10, 4 );
|
1091 |
+
|
1092 |
+
|
1093 |
+
/**
|
1094 |
+
* Function that filters and returns the users user_meta values in the Email Customizer
|
1095 |
+
*
|
1096 |
+
* @since v.2.0
|
1097 |
+
*
|
1098 |
+
* @param string $value
|
1099 |
+
* @param string $merge_tag_name
|
1100 |
+
* @param string $merge_tag
|
1101 |
+
* @param array $extra_data
|
1102 |
+
*
|
1103 |
+
* @return string
|
1104 |
+
*/
|
1105 |
+
function wppb_ec_replace_user_meta( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
1106 |
+
if ( isset( $extra_data['email_confirmation_unserialized_data'][$merge_tag_name] ) )
|
1107 |
+
$value = $extra_data['email_confirmation_unserialized_data'][$merge_tag_name];
|
1108 |
+
elseif( !empty( $extra_data['user_id'] ) )
|
1109 |
+
$value = get_user_meta( $extra_data['user_id'], $merge_tag_name, true );
|
1110 |
+
|
1111 |
+
/* value should be a string, but in case it is not */
|
1112 |
+
if( is_array( $value ) )
|
1113 |
+
$value = implode( ' ', $value );
|
1114 |
+
|
1115 |
+
return apply_filters( 'wppb_ec_filter_user_meta', $value, $merge_tag_name, $extra_data );
|
1116 |
+
}
|
1117 |
+
add_filter( 'mustache_variable_ec_user_meta', 'wppb_ec_replace_user_meta', 10, 4 );
|
1118 |
+
|
1119 |
+
|
1120 |
+
add_filter( 'wppb_ec_filter_user_meta', 'wppb_ec_change_user_meta_by_type', 10, 3 );
|
1121 |
+
function wppb_ec_change_user_meta_by_type( $value, $meta_name, $extra_data = '' ){
|
1122 |
+
$fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields', 'not_found' ), array( 'user_id' => ( empty( $extra_data['user_id'] ) ) ? '' : $extra_data['user_id'], 'meta' => ( empty( $extra_data['email_confirmation_unserialized_data'] ) ) ? '' : $extra_data['email_confirmation_unserialized_data'], 'context' => 'mustache_variable' ) );
|
1123 |
+
if( !empty( $fields ) ) {
|
1124 |
+
foreach ($fields as $field) {
|
1125 |
+
if ($field['meta-name'] == $meta_name && ($field['field'] == 'Avatar' || $field['field'] == 'Upload' ) ) {
|
1126 |
+
if( is_numeric( $value ) )
|
1127 |
+
$value = wp_get_attachment_url( $value );
|
1128 |
+
}
|
1129 |
+
}
|
1130 |
+
}
|
1131 |
+
return $value;
|
1132 |
+
}
|
1133 |
+
|
1134 |
+
/**
|
1135 |
+
* Function that filters and returns the users user_meta labels in the Email Customizer
|
1136 |
+
*
|
1137 |
+
* @since v.2.0
|
1138 |
+
*
|
1139 |
+
* @param string $value
|
1140 |
+
* @param string $merge_tag_name
|
1141 |
+
* @param string $merge_tag
|
1142 |
+
* @param array $extra_data
|
1143 |
+
*
|
1144 |
+
* @return string
|
1145 |
+
*/
|
1146 |
+
function wppb_ec_replace_user_meta_labels( $value, $merge_tag_name, $merge_tag, $extra_data ){
|
1147 |
+
/* we remove _labels from end so we get the meta name */
|
1148 |
+
$merge_tag_name = preg_replace( '/_labels$/', '', $merge_tag_name, 1 );
|
1149 |
+
|
1150 |
+
if ( isset( $extra_data['email_confirmation_unserialized_data'][$merge_tag_name] ) )
|
1151 |
+
$value = $extra_data['email_confirmation_unserialized_data'][$merge_tag_name];
|
1152 |
+
if( !empty( $extra_data['user_id'] ) )
|
1153 |
+
$value = get_user_meta( $extra_data['user_id'], $merge_tag_name, true );
|
1154 |
+
|
1155 |
+
/* get label from value */
|
1156 |
+
/* get manage fields */
|
1157 |
+
$fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields', 'not_found' ), array( 'user_id' => ( empty( $extra_data['user_id'] ) ) ? '' : $extra_data['user_id'], 'meta' => ( empty( $extra_data['email_confirmation_unserialized_data'] ) ) ? '' : $extra_data['email_confirmation_unserialized_data'], 'context' => 'mustache_variable' ) );
|
1158 |
+
if( !empty( $fields ) ) {
|
1159 |
+
foreach ($fields as $field) {
|
1160 |
+
if( $field['meta-name'] == $merge_tag_name ){
|
1161 |
+
if( $field['field'] == 'Select (Country)' ){
|
1162 |
+
if( function_exists( 'wppb_country_select_options' ) ) {
|
1163 |
+
$all_countries = wppb_country_select_options('email_customizer');
|
1164 |
+
if( !empty( $all_countries[$value] ) )
|
1165 |
+
return $all_countries[$value];
|
1166 |
+
else
|
1167 |
+
return $value;
|
1168 |
+
}
|
1169 |
+
else{
|
1170 |
+
return $value;
|
1171 |
+
}
|
1172 |
+
}
|
1173 |
+
else if( $field['field'] == 'Select (Currency)' ){
|
1174 |
+
if( function_exists( 'wppb_get_currencies' ) ) {
|
1175 |
+
$all_currencies = wppb_get_currencies('email_customizer');
|
1176 |
+
if( !empty( $all_currencies[$value] ) )
|
1177 |
+
return $all_currencies[$value];
|
1178 |
+
else
|
1179 |
+
return $value;
|
1180 |
+
}
|
1181 |
+
else{
|
1182 |
+
return $value;
|
1183 |
+
}
|
1184 |
+
}
|
1185 |
+
else {
|
1186 |
+
/* get label corresponding to value. the values and labels in the backend settings are comma separated so we assume that as well here ? */
|
1187 |
+
$saved_values = array_map('trim', explode(',', $value));
|
1188 |
+
$field['options'] = array_map('trim', explode(',', $field['options']));
|
1189 |
+
$field['labels'] = array_map('trim', explode(',', $field['labels']));
|
1190 |
+
/* get the position for each value */
|
1191 |
+
$key_array = array();
|
1192 |
+
if (!empty($field['options'])) {
|
1193 |
+
foreach ($field['options'] as $key => $option) {
|
1194 |
+
if (in_array($option, $saved_values))
|
1195 |
+
$key_array[] = $key;
|
1196 |
+
}
|
1197 |
+
}
|
1198 |
+
|
1199 |
+
$show_values = array();
|
1200 |
+
if (!empty($key_array)) {
|
1201 |
+
foreach ($key_array as $key) {
|
1202 |
+
if (!empty($field['labels'][$key]))
|
1203 |
+
$show_values[] = $field['labels'][$key];
|
1204 |
+
else
|
1205 |
+
$show_values[] = $field['options'][$key];
|
1206 |
+
}
|
1207 |
+
}
|
1208 |
+
|
1209 |
+
return implode(',', $show_values);
|
1210 |
+
}
|
1211 |
+
}
|
1212 |
+
}
|
1213 |
+
}
|
1214 |
+
}
|
1215 |
+
add_filter( 'mustache_variable_ec_user_meta_labels', 'wppb_ec_replace_user_meta_labels', 10, 4 );
|
1216 |
+
|
1217 |
+
// function that filters the From email address
|
1218 |
+
function wppb_website_email($sender_email){
|
1219 |
+
$wppb_addonOptions = get_option( 'wppb_module_settings' );
|
1220 |
+
|
1221 |
+
if ( ( $wppb_addonOptions['wppb_emailCustomizer'] == 'show' ) || ( isset( $wppb_addonOptions['wppb_emailCustomizerAdmin'] ) && $wppb_addonOptions['wppb_emailCustomizerAdmin'] == 'show' ) ){
|
1222 |
+
$reply_to_email = get_option( 'wppb_emailc_common_settings_from_reply_to_email', 'not_found' );
|
1223 |
+
if ( $reply_to_email != 'not_found' ) {
|
1224 |
+
$reply_to_email = str_replace('{{reply_to}}', get_bloginfo('admin_email'), $reply_to_email );
|
1225 |
+
if( is_email( $reply_to_email ) )
|
1226 |
+
$sender_email = $reply_to_email;
|
1227 |
+
}
|
1228 |
+
}
|
1229 |
+
|
1230 |
+
return $sender_email;
|
1231 |
+
}
|
1232 |
+
|
1233 |
+
// function that filters the From name
|
1234 |
+
function wppb_website_name($site_name){
|
1235 |
+
$wppb_addonOptions = get_option( 'wppb_module_settings' );
|
1236 |
+
|
1237 |
+
if ( ( $wppb_addonOptions['wppb_emailCustomizer'] == 'show' ) || ( isset( $wppb_addonOptions['wppb_emailCustomizerAdmin'] ) && $wppb_addonOptions['wppb_emailCustomizerAdmin'] == 'show' ) ){
|
1238 |
+
$email_from_name = get_option( 'wppb_emailc_common_settings_from_name', 'not_found' );
|
1239 |
+
if ( $email_from_name != 'not_found' ) {
|
1240 |
+
$email_from_name = str_replace('{{site_name}}', get_bloginfo('name'), $email_from_name );
|
1241 |
+
$site_name = $email_from_name;
|
1242 |
+
}
|
1243 |
+
}
|
1244 |
+
|
1245 |
+
return html_entity_decode( htmlspecialchars_decode( $site_name, ENT_QUOTES ), ENT_QUOTES );
|
1246 |
+
}
|
1247 |
+
|
1248 |
+
// Function that changes email headers only when they are needed.
|
1249 |
+
function wppb_change_email_from_headers(){
|
1250 |
+
add_filter('wp_mail_from_name','wppb_website_name', 20, 1);
|
1251 |
+
add_filter('wp_mail_from','wppb_website_email', 20, 1);
|
1252 |
+
}
|
1253 |
+
|
1254 |
+
add_filter( 'wppb_send_email', 'wppb_disable_emails', 20, 5 );
|
1255 |
+
function wppb_disable_emails( $send_email, $to, $subject, $message, $context ) {
|
1256 |
+
$actions = array(
|
1257 |
+
'email_user_account_info' => 'wppb_user_emailc_default_registration_email_enabled',
|
1258 |
+
'email_user_activate' => 'wppb_user_emailc_registr_w_email_confirm_email_enabled',
|
1259 |
+
'email_user_need_approval' => 'wppb_user_emailc_registration_with_admin_approval_email_enabled',
|
1260 |
+
'email_user_approved' => 'wppb_user_emailc_admin_approval_notif_approved_email_enabled',
|
1261 |
+
'email_user_unapproved' => 'wppb_user_emailc_admin_approval_notif_unapproved_email_enabled',
|
1262 |
+
'email_user_recover' => 'wppb_user_emailc_reset_email_enabled',
|
1263 |
+
'email_user_recover_success' => 'wppb_user_emailc_reset_success_email_enabled',
|
1264 |
+
'email_user_update_email' => 'wppb_user_emailc_change_email_address_request_enabled',
|
1265 |
+
'wppb_epaa_user_email' => 'wppb_user_emailc_epaa_notification_enabled',
|
1266 |
+
'email_admin_new_subscriber' => 'wppb_admin_emailc_default_registration_email_enabled',
|
1267 |
+
'email_admin_approve' => 'wppb_admin_emailc_registration_with_admin_approval_email_enabled',
|
1268 |
+
'email_admin_recover_success' => 'wppb_admin_emailc_user_password_reset_email_enabled',
|
1269 |
+
'wppb_epaa_admin_email' => 'wppb_admin_emailc_epaa_notification_enabled',
|
1270 |
+
);
|
1271 |
+
|
1272 |
+
if ( !empty( $actions[$context] ) )
|
1273 |
+
$option_name = $actions[$context];
|
1274 |
+
else
|
1275 |
+
return $send_email;
|
1276 |
+
|
1277 |
+
$option = get_option( $option_name );
|
1278 |
+
|
1279 |
+
if ( $option == 'off' )
|
1280 |
+
return false;
|
1281 |
+
|
1282 |
+
return $send_email;
|
1283 |
+
}
|
1284 |
+
|
1285 |
+
if ( get_option( 'wppb_user_emailc_change_email_address_enabled' ) == 'off' )
|
1286 |
+
add_filter( 'send_email_change_email', '__return_false' );
|
features/email-customizer/user-email-customizer.php
ADDED
@@ -0,0 +1,409 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Function that creates the User Email Customizer menu
|
4 |
+
*
|
5 |
+
* @since v.2.0
|
6 |
+
*
|
7 |
+
* @return void
|
8 |
+
*/
|
9 |
+
function wppb_user_email_customizer_submenu(){
|
10 |
+
$args = array(
|
11 |
+
'menu_title' => __( 'User Email Customizer', 'profile-builder' ),
|
12 |
+
'page_title' => __( 'User Email Customizer Settings', 'profile-builder' ),
|
13 |
+
'menu_slug' => 'user-email-customizer',
|
14 |
+
'page_type' => 'submenu_page',
|
15 |
+
'capability' => 'manage_options',
|
16 |
+
'priority' => 10,
|
17 |
+
'parent_slug' => 'profile-builder'
|
18 |
+
);
|
19 |
+
|
20 |
+
new WCK_Page_Creator_PB( $args );
|
21 |
+
}
|
22 |
+
add_action( 'admin_menu', 'wppb_user_email_customizer_submenu', 1 );
|
23 |
+
|
24 |
+
add_action( 'wck_before_meta_boxes', 'wppb_add_tabs_on_top_of_user_email_page' );
|
25 |
+
function wppb_add_tabs_on_top_of_user_email_page( $hookname ){
|
26 |
+
if( $hookname == 'profile-builder_page_user-email-customizer' ){
|
27 |
+
|
28 |
+
if( isset( $_GET['mustache_action'] ) && $_GET['mustache_action'] == 'save' ) { ?>
|
29 |
+
<div id="setting-error-settings_updated" class="updated settings-error notice">
|
30 |
+
<p><strong><?php esc_html_e( "Settings saved.", 'profile-builder' ); ?></strong></p>
|
31 |
+
</div>
|
32 |
+
<?php
|
33 |
+
}
|
34 |
+
|
35 |
+
wppb_generate_settings_tabs();
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
/* on the init hook add the mustache boxes */
|
40 |
+
add_action( 'init', 'wppb_user_email_customizer_add_mustache_in_backend', 11 );
|
41 |
+
/**
|
42 |
+
* Function that ads the mustache boxes in the backend for user email customizer
|
43 |
+
*
|
44 |
+
* @since v.2.0
|
45 |
+
*/
|
46 |
+
function wppb_user_email_customizer_add_mustache_in_backend(){
|
47 |
+
if( defined( 'WPPB_PAID_PLUGIN_DIR' ) && file_exists( WPPB_PAID_PLUGIN_DIR . '/assets/lib/class-mustache-templates/class-mustache-templates.php' ) )
|
48 |
+
require_once( WPPB_PAID_PLUGIN_DIR . '/assets/lib/class-mustache-templates/class-mustache-templates.php' );
|
49 |
+
elseif( file_exists( WPPB_PLUGIN_DIR . '/assets/lib/class-mustache-templates/class-mustache-templates.php' ) )
|
50 |
+
require_once( WPPB_PLUGIN_DIR . '/assets/lib/class-mustache-templates/class-mustache-templates.php' );
|
51 |
+
|
52 |
+
$fields = array(
|
53 |
+
array(
|
54 |
+
'id' => 'wppb_admin_emailc_common_settings_header', // field id and name
|
55 |
+
'type' => 'header', // type of field
|
56 |
+
'default' => __( 'These settings are also replicated in the "Admin Email Customizer" settings-page upon save.', 'profile-builder' ).' '.__( 'Valid tags {{reply_to}} and {{site_name}}', 'profile-builder'), // type of field
|
57 |
+
),
|
58 |
+
array(
|
59 |
+
'label' => __( 'From (name)', 'profile-builder' ), // <label>
|
60 |
+
'desc' => '', // description
|
61 |
+
'id' => 'wppb_emailc_common_settings_from_name', // field id and name
|
62 |
+
'type' => 'text', // type of field
|
63 |
+
'default' => '{{site_name}}', // type of field
|
64 |
+
'desc' => '',
|
65 |
+
),
|
66 |
+
array(
|
67 |
+
'label' => __( 'From (reply-to email)', 'profile-builder' ), // <label>
|
68 |
+
'desc' => '', // description
|
69 |
+
'id' => 'wppb_emailc_common_settings_from_reply_to_email', // field id and name
|
70 |
+
'type' => 'text', // type of field
|
71 |
+
'default' => '{{reply_to}}', // type of field
|
72 |
+
'desc' => __( 'Must be a valid email address or the tag {{reply_to}} which defaults to the administrator email', 'profile-builder' ),
|
73 |
+
),
|
74 |
+
);
|
75 |
+
new PB_Mustache_Generate_Admin_Box( 'uec_common_settings', __( 'Common Settings', 'profile-builder' ), 'profile-builder_page_user-email-customizer', 'core', '', '', $fields );
|
76 |
+
|
77 |
+
/*
|
78 |
+
* Default Registration
|
79 |
+
*/
|
80 |
+
|
81 |
+
// we format the var like this for proper line breaks.
|
82 |
+
$uec_default_registration = __("<h3>Welcome to {{site_name}}!</h3>\n<p>Your username is: {{username}}</p>\n", 'profile-builder' );
|
83 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags();
|
84 |
+
$fields = array(
|
85 |
+
array(
|
86 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
87 |
+
'desc' => '', // description
|
88 |
+
'id' => 'wppb_user_emailc_default_registration_email_subject', // field id and name
|
89 |
+
'type' => 'text', // type of field
|
90 |
+
'default' => 'A new account has been created for you on {{site_name}}', // type of field
|
91 |
+
),
|
92 |
+
array(
|
93 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
94 |
+
'desc' => '', // description
|
95 |
+
'id' => 'wppb_user_emailc_default_registration_email_enabled', // field id and name
|
96 |
+
'type' => 'checkbox', // type of field
|
97 |
+
'default' => 'on',
|
98 |
+
),
|
99 |
+
array( // Textarea
|
100 |
+
'label' => '', // <label>
|
101 |
+
'desc' => '', // description
|
102 |
+
'id' => 'wppb_user_emailc_default_registration_email_content', // field id and name
|
103 |
+
'type' => 'textarea', // type of field
|
104 |
+
'default' => $uec_default_registration, // type of field
|
105 |
+
)
|
106 |
+
);
|
107 |
+
new PB_Mustache_Generate_Admin_Box( 'uec_default_registration', __( 'Default Registration', 'profile-builder' ), 'profile-builder_page_user-email-customizer', 'core', $mustache_vars, '', $fields );
|
108 |
+
|
109 |
+
/*
|
110 |
+
* Registration with Email Confirmation
|
111 |
+
*/
|
112 |
+
// we format the var like this for proper line breaks.
|
113 |
+
$uec_reg_with_email_confirm = __( "<p>To activate your user, please click the following link:<br/>\n{{{activation_link}}}</p>\n<p>After you activate, you will receive another email with your credentials.</p>\n", 'profile-builder' );
|
114 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags( 'email_confirmation' );
|
115 |
+
$fields = array(
|
116 |
+
array(
|
117 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
118 |
+
'desc' => '', // description
|
119 |
+
'id' => 'wppb_user_emailc_registr_w_email_confirm_email_subject', // field id and name
|
120 |
+
'type' => 'text', // type of field
|
121 |
+
'default' => __( '[{{site_name}}] Activate {{username}}', 'profile-builder' ), // type of field
|
122 |
+
),
|
123 |
+
array(
|
124 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
125 |
+
'desc' => '', // description
|
126 |
+
'id' => 'wppb_user_emailc_registr_w_email_confirm_email_enabled', // field id and name
|
127 |
+
'type' => 'checkbox', // type of field
|
128 |
+
'default' => 'on',
|
129 |
+
),
|
130 |
+
array( // Textarea
|
131 |
+
'label' => '', // <label>
|
132 |
+
'desc' => '', // description
|
133 |
+
'id' => 'wppb_user_emailc_registr_w_email_confirm_email_content', // field id and name
|
134 |
+
'type' => 'textarea', // type of field
|
135 |
+
'default' => $uec_reg_with_email_confirm, // type of field
|
136 |
+
)
|
137 |
+
);
|
138 |
+
|
139 |
+
new PB_Mustache_Generate_Admin_Box( 'uec_reg_with_email_confirmation', __( 'Registration with Email Confirmation', 'profile-builder' ), 'profile-builder_page_user-email-customizer', 'core', $mustache_vars, '', $fields );
|
140 |
+
|
141 |
+
if( PROFILE_BUILDER != 'Profile Builder Free' ){
|
142 |
+
|
143 |
+
/*
|
144 |
+
* Registration with Admin Approval
|
145 |
+
*/
|
146 |
+
|
147 |
+
$uec_reg_with_admin_approval = __( "<h3>Welcome to {{site_name}}!</h3>\n<p>Your username is: {{username}}</p>\n<p>Before you can access your account, an administrator needs to approve it. You will be notified via email.</p>\n", 'profile-builder' );
|
148 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags();
|
149 |
+
$fields = array(
|
150 |
+
array(
|
151 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
152 |
+
'desc' => '', // description
|
153 |
+
'id' => 'wppb_user_emailc_registration_with_admin_approval_email_subject', // field id and name
|
154 |
+
'type' => 'text', // type of field
|
155 |
+
'default' => __( 'A new account has been created for you on {{site_name}}', 'profile-builder' ), // type of field
|
156 |
+
),
|
157 |
+
array(
|
158 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
159 |
+
'desc' => '', // description
|
160 |
+
'id' => 'wppb_user_emailc_registration_with_admin_approval_email_enabled', // field id and name
|
161 |
+
'type' => 'checkbox', // type of field
|
162 |
+
'default' => 'on',
|
163 |
+
),
|
164 |
+
array( // Textarea
|
165 |
+
'label' => '', // <label>
|
166 |
+
'desc' => '', // description
|
167 |
+
'id' => 'wppb_user_emailc_registration_with_admin_approval_email_content', // field id and name
|
168 |
+
'type' => 'textarea', // type of field
|
169 |
+
'default' => $uec_reg_with_admin_approval, // type of field
|
170 |
+
)
|
171 |
+
);
|
172 |
+
|
173 |
+
new PB_Mustache_Generate_Admin_Box( 'uec_reg_with_admin_approval', __( 'Registration with Admin Approval', 'profile-builder' ), 'profile-builder_page_user-email-customizer', 'core', $mustache_vars, '', $fields );
|
174 |
+
|
175 |
+
/*
|
176 |
+
* Admin Approval Notifications ( on user approval )
|
177 |
+
*/
|
178 |
+
$uec_notif_approved_email = __( "<h3>Good News!</h3>\n<p>An administrator has just approved your account: {{username}} on {{site_name}}.</p>\n", 'profile-builder' );
|
179 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags();
|
180 |
+
$fields = array(
|
181 |
+
array(
|
182 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
183 |
+
'desc' => '', // description
|
184 |
+
'id' => 'wppb_user_emailc_admin_approval_notif_approved_email_subject', // field id and name
|
185 |
+
'type' => 'text', // type of field
|
186 |
+
'default' => __( 'Your account on {{site_name}} has been approved!', 'profile-builder' ), // type of field
|
187 |
+
),
|
188 |
+
array(
|
189 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
190 |
+
'desc' => '', // description
|
191 |
+
'id' => 'wppb_user_emailc_admin_approval_notif_approved_email_enabled', // field id and name
|
192 |
+
'type' => 'checkbox', // type of field
|
193 |
+
'default' => 'on',
|
194 |
+
),
|
195 |
+
array( // Textarea
|
196 |
+
'label' => '', // <label>
|
197 |
+
'desc' => '', // description
|
198 |
+
'id' => 'wppb_user_emailc_admin_approval_notif_approved_email_content', // field id and name
|
199 |
+
'type' => 'textarea', // type of field
|
200 |
+
'default' => $uec_notif_approved_email, // type of field
|
201 |
+
)
|
202 |
+
);
|
203 |
+
|
204 |
+
new PB_Mustache_Generate_Admin_Box( 'uec_notif_approved_email', __( 'User Approval Notification', 'profile-builder' ), 'profile-builder_page_user-email-customizer', 'core', $mustache_vars, '', $fields );
|
205 |
+
|
206 |
+
/*
|
207 |
+
* Admin Approval Notifications ( on user unapproval )
|
208 |
+
*/
|
209 |
+
$uec_notif_unapproved_email = __( "<h3>Hello,</h3>\n<p>Unfortunatelly an administrator has just unapproved your account: {{username}} on {{site_name}}.</p>\n", 'profile-builder' );
|
210 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags();
|
211 |
+
$fields = array(
|
212 |
+
array(
|
213 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
214 |
+
'desc' => '', // description
|
215 |
+
'id' => 'wppb_user_emailc_admin_approval_notif_unapproved_email_subject', // field id and name
|
216 |
+
'type' => 'text', // type of field
|
217 |
+
'default' => __( 'Your account on {{site_name}} has been unapproved!', 'profile-builder' ), // type of field
|
218 |
+
),
|
219 |
+
array(
|
220 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
221 |
+
'desc' => '', // description
|
222 |
+
'id' => 'wppb_user_emailc_admin_approval_notif_unapproved_email_enabled', // field id and name
|
223 |
+
'type' => 'checkbox', // type of field
|
224 |
+
'default' => 'on',
|
225 |
+
),
|
226 |
+
array( // Textarea
|
227 |
+
'label' => '', // <label>
|
228 |
+
'desc' => '', // description
|
229 |
+
'id' => 'wppb_user_emailc_admin_approval_notif_unapproved_email_content', // field id and name
|
230 |
+
'type' => 'textarea', // type of field
|
231 |
+
'default' => $uec_notif_unapproved_email , // type of field
|
232 |
+
)
|
233 |
+
);
|
234 |
+
|
235 |
+
new PB_Mustache_Generate_Admin_Box( 'uec_notif_unapproved_email', __( 'Unapproved User Notification', 'profile-builder' ), 'profile-builder_page_user-email-customizer', 'core', $mustache_vars, '', $fields );
|
236 |
+
|
237 |
+
}
|
238 |
+
|
239 |
+
|
240 |
+
/*
|
241 |
+
* Password Reset Email
|
242 |
+
*/
|
243 |
+
// we format the var like this for proper line breaks.
|
244 |
+
$uec_reset = __( "<p>Someone requested that the password be reset for the following account: {{site_name}}<br/>\nUsername: {{username}}</p>\n<p>If this was a mistake, just ignore this email and nothing will happen.</p>\n<p>To reset your password, visit the following address:<br/>\n{{{reset_link}}}</p>\n", 'profile-builder' );
|
245 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags( 'password_reset' );
|
246 |
+
$fields = array(
|
247 |
+
array(
|
248 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
249 |
+
'desc' => '', // description
|
250 |
+
'id' => 'wppb_user_emailc_reset_email_subject', // field id and name
|
251 |
+
'type' => 'text', // type of field
|
252 |
+
'default' => __( '[{{site_name}}] Password Reset', 'profile-builder' ), // type of field
|
253 |
+
),
|
254 |
+
array(
|
255 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
256 |
+
'desc' => '', // description
|
257 |
+
'id' => 'wppb_user_emailc_reset_email_enabled', // field id and name
|
258 |
+
'type' => 'checkbox', // type of field
|
259 |
+
'default' => 'on',
|
260 |
+
),
|
261 |
+
array( // Textarea
|
262 |
+
'label' => '', // <label>
|
263 |
+
'desc' => '', // description
|
264 |
+
'id' => 'wppb_user_emailc_reset_email_content', // field id and name
|
265 |
+
'type' => 'textarea', // type of field
|
266 |
+
'default' => $uec_reset, // type of field
|
267 |
+
)
|
268 |
+
);
|
269 |
+
|
270 |
+
new PB_Mustache_Generate_Admin_Box( 'uec_reset', __( 'Password Reset Email', 'profile-builder' ), 'profile-builder_page_user-email-customizer', 'core', $mustache_vars, '', $fields );
|
271 |
+
|
272 |
+
/*
|
273 |
+
* Password Reset Success Email
|
274 |
+
*/
|
275 |
+
// we format the var like this for proper line breaks.
|
276 |
+
$uec_reset_success = __( "<p>You have successfully reset your password.</p>\n", 'profile-builder' );
|
277 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags( 'password_reset_success' );
|
278 |
+
$fields = array(
|
279 |
+
array(
|
280 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
281 |
+
'desc' => '', // description
|
282 |
+
'id' => 'wppb_user_emailc_reset_success_email_subject', // field id and name
|
283 |
+
'type' => 'text', // type of field
|
284 |
+
'default' => __( '[{{site_name}}] Password Reset Successfully', 'profile-builder' ), // type of field
|
285 |
+
),
|
286 |
+
array(
|
287 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
288 |
+
'desc' => '', // description
|
289 |
+
'id' => 'wppb_user_emailc_reset_success_email_enabled', // field id and name
|
290 |
+
'type' => 'checkbox', // type of field
|
291 |
+
'default' => 'on',
|
292 |
+
),
|
293 |
+
array( // Textarea
|
294 |
+
'label' => '', // <label>
|
295 |
+
'desc' => '', // description
|
296 |
+
'id' => 'wppb_user_emailc_reset_success_email_content', // field id and name
|
297 |
+
'type' => 'textarea', // type of field
|
298 |
+
'default' => $uec_reset_success, // type of field
|
299 |
+
)
|
300 |
+
);
|
301 |
+
|
302 |
+
new PB_Mustache_Generate_Admin_Box( 'uec_reset_success', __( 'Password Reset Success Email', 'profile-builder' ), 'profile-builder_page_user-email-customizer', 'core', $mustache_vars, '', $fields );
|
303 |
+
|
304 |
+
/*
|
305 |
+
* Change Email Address Request Notification
|
306 |
+
*/
|
307 |
+
$admin_email = get_option('admin_email');
|
308 |
+
|
309 |
+
// we format the var like this for proper line breaks.
|
310 |
+
$uec_change_email_request = sprintf( __( "<h3>Hi {{username}},</h3>\n<p>There is an email address change request on {{site_name}}.</p>\n<p>To change your email address click on: {{{user_email_change_link}}}</p>\n<p>Regards,<br>\nAll at {{site_name}}<br>\n<a href=\"{{site_url}}\">{{site_url}}</a></p>", 'profile-builder' ), $admin_email );
|
311 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags( 'change_email_address_request' );
|
312 |
+
$fields = array(
|
313 |
+
array(
|
314 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
315 |
+
'desc' => '', // description
|
316 |
+
'id' => 'wppb_user_emailc_change_email_address_request_subject', // field id and name
|
317 |
+
'type' => 'text', // type of field
|
318 |
+
'default' => __( '[{{site_name}}] Notice of Email Change Request', 'profile-builder' ), // type of field
|
319 |
+
),
|
320 |
+
array(
|
321 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
322 |
+
'desc' => '', // description
|
323 |
+
'id' => 'wppb_user_emailc_change_email_address_request_enabled', // field id and name
|
324 |
+
'type' => 'checkbox', // type of field
|
325 |
+
'default' => 'on',
|
326 |
+
),
|
327 |
+
array( // Textarea
|
328 |
+
'label' => '', // <label>
|
329 |
+
'desc' => '', // description
|
330 |
+
'id' => 'wppb_user_emailc_change_email_address_request_content', // field id and name
|
331 |
+
'type' => 'textarea', // type of field
|
332 |
+
'default' => $uec_change_email_request, // type of field
|
333 |
+
)
|
334 |
+
);
|
335 |
+
|
336 |
+
new PB_Mustache_Generate_Admin_Box( 'uec_change_email_request', __( 'Change Email Address Request Notification', 'profile-builder' ), 'profile-builder_page_user-email-customizer', 'core', $mustache_vars, '', $fields );
|
337 |
+
|
338 |
+
/*
|
339 |
+
* Change Email Address Notification
|
340 |
+
*/
|
341 |
+
$admin_email = get_option('admin_email');
|
342 |
+
|
343 |
+
// we format the var like this for proper line breaks.
|
344 |
+
$uec_change_email = sprintf( __( "<h3>Hi {{username}},</h3>\n<p>This notice confirms that your email was changed on {{site_name}}.</p>\n<p>If you did not change your email, please contact the Site Administrator at %s</p>\n<p>This email has been sent to {{user_email}}</p>\n<p>Regards,<br>\nAll at {{site_name}}<br>\n<a href=\"{{site_url}}\">{{site_url}}</a></p>", 'profile-builder' ), $admin_email );
|
345 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags( 'change_email_address' );
|
346 |
+
$fields = array(
|
347 |
+
array(
|
348 |
+
'label' => __( 'Email Subject', 'profile-builder' ), // <label>
|
349 |
+
'desc' => '', // description
|
350 |
+
'id' => 'wppb_user_emailc_change_email_address_subject', // field id and name
|
351 |
+
'type' => 'text', // type of field
|
352 |
+
'default' => __( '[{{site_name}}] Notice of Email Change', 'profile-builder' ), // type of field
|
353 |
+
),
|
354 |
+
array(
|
355 |
+
'label' => __( 'Enable email', 'profile-builder' ), // <label>
|
356 |
+
'desc' => '', // description
|
357 |
+
'id' => 'wppb_user_emailc_change_email_address_enabled', // field id and name
|
358 |
+
'type' => 'checkbox', // type of field
|
359 |
+
'default' => 'on',
|
360 |
+
),
|
361 |
+
array( // Textarea
|
362 |
+
'label' => '', // <label>
|
363 |
+
'desc' => '', // description
|
364 |
+
'id' => 'wppb_user_emailc_change_email_address_content', // field id and name
|
365 |
+
'type' => 'textarea', // type of field
|
366 |
+
'default' => $uec_change_email, // type of field
|
367 |
+
)
|
368 |
+
);
|
369 |
+
|
370 |
+
new PB_Mustache_Generate_Admin_Box( 'uec_change_email', __( 'Changed Email Address Notification', 'profile-builder' ), 'profile-builder_page_user-email-customizer', 'core', $mustache_vars, '', $fields );
|
371 |
+
|
372 |
+
/*
|
373 |
+
* User Notification for Edit Profile Approved by Admin
|
374 |
+
*/
|
375 |
+
|
376 |
+
// check if the Edit Profile Approved by Admin add-on is active to see if the email should be displayed
|
377 |
+
if( function_exists( 'wppb_in_init_edit_profile_approval' ) ) {
|
378 |
+
|
379 |
+
// we format the var like this for proper line breaks.
|
380 |
+
$uec_epaa_notification = __("<p>Your profile has been reviewed by an administrator:</p>\n<br>\n<p>Approved Fields: {{approved_fields}}</p>\n<p>Unapproved Fields: {{unapproved_fields}}</p>\n", 'profile-builder');
|
381 |
+
$mustache_vars = wppb_email_customizer_generate_merge_tags('epaa_notification');
|
382 |
+
$fields = array(
|
383 |
+
array(
|
384 |
+
'label' => __('Email Subject', 'profile-builder'), // <label>
|
385 |
+
'desc' => '', // description
|
386 |
+
'id' => 'wppb_user_emailc_epaa_notification_subject', // field id and name
|
387 |
+
'type' => 'text', // type of field
|
388 |
+
'default' => __('[{{site_name}}] Your profile has been reviewed by an administrator', 'profile-builder'), // type of field
|
389 |
+
),
|
390 |
+
array(
|
391 |
+
'label' => __('Enable email', 'profile-builder'), // <label>
|
392 |
+
'desc' => '', // description
|
393 |
+
'id' => 'wppb_user_emailc_epaa_notification_enabled', // field id and name
|
394 |
+
'type' => 'checkbox', // type of field
|
395 |
+
'default' => 'on',
|
396 |
+
),
|
397 |
+
array( // Textarea
|
398 |
+
'label' => '', // <label>
|
399 |
+
'desc' => '', // description
|
400 |
+
'id' => 'wppb_user_emailc_epaa_notification_content', // field id and name
|
401 |
+
'type' => 'textarea', // type of field
|
402 |
+
'default' => $uec_epaa_notification, // type of field
|
403 |
+
)
|
404 |
+
);
|
405 |
+
|
406 |
+
new PB_Mustache_Generate_Admin_Box('uec_epaa_notification', __('User Notification for Edit Profile Approved by Admin', 'profile-builder'), 'profile-builder_page_user-email-customizer', 'core', $mustache_vars, '', $fields);
|
407 |
+
|
408 |
+
}
|
409 |
+
}
|
features/functions.php
CHANGED
@@ -166,24 +166,23 @@ if ( is_admin() ){
|
|
166 |
add_action( 'admin_init', 'wppb_register_settings' );
|
167 |
|
168 |
// display the same extra profile fields in the admin panel also
|
169 |
-
if (
|
170 |
-
require_once(
|
171 |
|
172 |
-
add_action( 'show_user_profile', '
|
173 |
-
add_action( 'edit_user_profile', '
|
174 |
global $pagenow;
|
175 |
if( $pagenow != 'user-new.php' )
|
176 |
-
add_action( 'user_profile_update_errors', '
|
177 |
-
add_action( 'personal_options_update', '
|
178 |
-
add_action( 'edit_user_profile_update', '
|
179 |
}
|
180 |
|
181 |
/* we need to include the fields here for conditional fields when they run through ajax, the extra-fields were already included above for backend forms */
|
182 |
-
|
183 |
-
if
|
184 |
-
|
185 |
-
|
186 |
-
}
|
187 |
|
188 |
}else if ( !is_admin() ){
|
189 |
// include the stylesheet
|
@@ -376,6 +375,7 @@ function wppb_print_cpt_script( $hook ){
|
|
376 |
( strpos( $hook, 'profile-builder_page_' ) === 0 ) ||
|
377 |
( $hook == 'edit.php' && ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'wppb-roles-editor' ) ) ||
|
378 |
( $hook == 'admin_page_profile-builder-pms-promo') ||
|
|
|
379 |
( $hook == 'admin_page_profile-builder-private-website') ) {
|
380 |
wp_enqueue_style( 'wppb-back-end-style', WPPB_PLUGIN_URL . 'assets/css/style-back-end.css', false, PROFILE_BUILDER_VERSION );
|
381 |
}
|
166 |
add_action( 'admin_init', 'wppb_register_settings' );
|
167 |
|
168 |
// display the same extra profile fields in the admin panel also
|
169 |
+
if ( file_exists ( WPPB_PLUGIN_DIR.'/front-end/default-fields/fields-functions.php' ) ){
|
170 |
+
require_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/fields-functions.php' );
|
171 |
|
172 |
+
add_action( 'show_user_profile', 'wppb_display_fields_in_admin', 10 );
|
173 |
+
add_action( 'edit_user_profile', 'wppb_display_fields_in_admin', 10 );
|
174 |
global $pagenow;
|
175 |
if( $pagenow != 'user-new.php' )
|
176 |
+
add_action( 'user_profile_update_errors', 'wppb_validate_fields_in_admin', 10, 3 );
|
177 |
+
add_action( 'personal_options_update', 'wppb_save_fields_in_admin', 10 );
|
178 |
+
add_action( 'edit_user_profile_update', 'wppb_save_fields_in_admin', 10 );
|
179 |
}
|
180 |
|
181 |
/* we need to include the fields here for conditional fields when they run through ajax, the extra-fields were already included above for backend forms */
|
182 |
+
// now they are loaded all the time so the simple upload functionality works correctly as well, since 3.8.1
|
183 |
+
if (file_exists(WPPB_PLUGIN_DIR . '/front-end/default-fields/default-fields.php'))
|
184 |
+
require_once(WPPB_PLUGIN_DIR . '/front-end/default-fields/default-fields.php');
|
185 |
+
|
|
|
186 |
|
187 |
}else if ( !is_admin() ){
|
188 |
// include the stylesheet
|
375 |
( strpos( $hook, 'profile-builder_page_' ) === 0 ) ||
|
376 |
( $hook == 'edit.php' && ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'wppb-roles-editor' ) ) ||
|
377 |
( $hook == 'admin_page_profile-builder-pms-promo') ||
|
378 |
+
( $hook == 'toplevel_page_profile-builder-register') || //multisite register version page
|
379 |
( $hook == 'admin_page_profile-builder-private-website') ) {
|
380 |
wp_enqueue_style( 'wppb-back-end-style', WPPB_PLUGIN_URL . 'assets/css/style-back-end.css', false, PROFILE_BUILDER_VERSION );
|
381 |
}
|
front-end/default-fields/avatar/avatar.php
ADDED
@@ -0,0 +1,263 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* the avatar field relies on the upload field */
|
3 |
+
|
4 |
+
/* handle field output */
|
5 |
+
function wppb_avatar_handler( $output, $form_location, $field, $user_id, $field_check_errors, $request_data ){
|
6 |
+
if ( $field['field'] == 'Avatar' ){
|
7 |
+
|
8 |
+
$field['meta-name'] = Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'] );
|
9 |
+
|
10 |
+
/* media upload add here, this should be added just once even if called multiple times */
|
11 |
+
wp_enqueue_media();
|
12 |
+
/* propper way to dequeue. add to functions file in theme or custom plugin
|
13 |
+
function wppb_dequeue_script() {
|
14 |
+
wp_script_is( 'wppb-upload-script', 'enqueued' ); //true
|
15 |
+
wp_dequeue_script( 'wppb-upload-script' );
|
16 |
+
}
|
17 |
+
add_action( 'get_footer', 'wppb_dequeue_script' );
|
18 |
+
*/
|
19 |
+
$upload_script_vars = array(
|
20 |
+
'nonce' => wp_create_nonce( 'wppb_woo_simple_upload' ),
|
21 |
+
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
22 |
+
'remove_link_text' => __( 'Remove', 'profile-builder' )
|
23 |
+
);
|
24 |
+
|
25 |
+
wp_enqueue_script( 'wppb-upload-script', WPPB_PLUGIN_URL.'front-end/default-fields/upload/upload.js', array('jquery'), PROFILE_BUILDER_VERSION, true );
|
26 |
+
wp_localize_script( 'wppb-upload-script', 'wppb_upload_script_vars', $upload_script_vars );
|
27 |
+
|
28 |
+
$wppb_generalSettings = get_option( 'wppb_general_settings' );
|
29 |
+
|
30 |
+
if ( ( isset( $wppb_generalSettings['extraFieldsLayout'] ) && ( $wppb_generalSettings['extraFieldsLayout'] == 'default' ) ) )
|
31 |
+
wp_enqueue_style( 'profile-builder-upload-css', WPPB_PLUGIN_URL.'front-end/default-fields/upload/upload.css', false, PROFILE_BUILDER_VERSION );
|
32 |
+
|
33 |
+
$item_title = apply_filters( 'wppb_'.$form_location.'_avatar_custom_field_'.$field['id'].'_item_title', wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_title_translation', $field['field-title'], true ) );
|
34 |
+
$item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_description_translation', $field['description'], true );
|
35 |
+
|
36 |
+
if( $form_location != 'register' ) {
|
37 |
+
if( empty( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) )
|
38 |
+
$input_value = ( (wppb_user_meta_exists($user_id, $field['meta-name']) != null) ? get_user_meta($user_id, $field['meta-name'], true) : '');
|
39 |
+
else
|
40 |
+
$input_value = $request_data[wppb_handle_meta_name( $field['meta-name'] )];
|
41 |
+
|
42 |
+
if( !empty( $input_value ) && !is_numeric( $input_value ) ){
|
43 |
+
/* we have a file url and we need to change it into an attachment */
|
44 |
+
// Check the type of file. We'll use this as the 'post_mime_type'.
|
45 |
+
$wp_upload_dir = wp_upload_dir();
|
46 |
+
$file_path = str_replace( $wp_upload_dir['baseurl'], $wp_upload_dir["basedir"], $input_value );
|
47 |
+
//on windows os we might have \ instead of / so change them
|
48 |
+
$file_path = str_replace( "\\", "/", $file_path );
|
49 |
+
$file_type = wp_check_filetype( basename( $input_value ), null );
|
50 |
+
$attachment = array(
|
51 |
+
'guid' => $input_value,
|
52 |
+
'post_mime_type' => $file_type['type'],
|
53 |
+
'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $input_value ) ),
|
54 |
+
'post_content' => '',
|
55 |
+
'post_status' => 'inherit'
|
56 |
+
);
|
57 |
+
|
58 |
+
// Insert the attachment.
|
59 |
+
$input_value = wp_insert_attachment( $attachment, $input_value, 0 );
|
60 |
+
if( !empty( $input_value ) ) {
|
61 |
+
// Make sure that this file is included, as wp_generate_attachment_metadata() depends on it.
|
62 |
+
require_once(ABSPATH . 'wp-admin/includes/image.php');
|
63 |
+
// Generate the metadata for the attachment, and update the database record.
|
64 |
+
$attach_data = wp_generate_attachment_metadata($input_value, $file_path);
|
65 |
+
wp_update_attachment_metadata($input_value, $attach_data);
|
66 |
+
/* save the new attachment instead of the url */
|
67 |
+
update_user_meta( $user_id, $field['meta-name'], $input_value );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
}
|
71 |
+
else
|
72 |
+
$input_value = !empty( $_POST[$field['meta-name']] ) ? sanitize_text_field( $_POST[$field['meta-name']] ) : '';
|
73 |
+
|
74 |
+
if ( $form_location != 'back_end' ){
|
75 |
+
$error_mark = ( ( $field['required'] == 'Yes' ) ? '<span class="wppb-required" title="'.wppb_required_field_error($field["field-title"]).'">*</span>' : '' );
|
76 |
+
|
77 |
+
if ( array_key_exists( $field['id'], $field_check_errors ) )
|
78 |
+
$error_mark = '<img src="'.WPPB_PLUGIN_URL.'assets/images/pencil_delete.png" title="'.wppb_required_field_error($field["field-title"]).'"/>';
|
79 |
+
|
80 |
+
$extra_attr = apply_filters( 'wppb_extra_attribute', '', $field, $form_location );
|
81 |
+
|
82 |
+
$output = '<label for="'.$field['meta-name'].'">'.$item_title.$error_mark.'</label>';
|
83 |
+
$output .= wppb_make_upload_button( $field, $input_value, $extra_attr );
|
84 |
+
if( !empty( $item_description ) )
|
85 |
+
$output .= '<span class="wppb-description-delimiter">'.$item_description.'</span>';
|
86 |
+
}else{
|
87 |
+
$item_title = ( ( $field['required'] == 'Yes' ) ? $item_title .' <span class="description">('. __( 'required', 'profile-builder' ) .')</span>' : $item_title );
|
88 |
+
$output = '
|
89 |
+
<table class="form-table">
|
90 |
+
<tr>
|
91 |
+
<th><label for="'.$field['meta-name'].'">'.$item_title.'</label></th>
|
92 |
+
<td>';
|
93 |
+
$output .= wppb_make_upload_button( $field, $input_value );
|
94 |
+
$output .='<br/><span class="wppb-description-delimiter">'.$item_description;
|
95 |
+
$output .= '
|
96 |
+
</td>
|
97 |
+
</tr>
|
98 |
+
</table>';
|
99 |
+
}
|
100 |
+
|
101 |
+
return apply_filters( 'wppb_'.$form_location.'_avatar_custom_field_'.$field['id'], $output, $form_location, $field, $user_id, $field_check_errors, $request_data, $input_value );
|
102 |
+
}
|
103 |
+
}
|
104 |
+
add_filter( 'wppb_output_form_field_avatar', 'wppb_avatar_handler', 10, 6 );
|
105 |
+
add_filter( 'wppb_admin_output_form_field_avatar', 'wppb_avatar_handler', 10, 6 );
|
106 |
+
|
107 |
+
|
108 |
+
/* handle field save */
|
109 |
+
function wppb_save_avatar_value( $field, $user_id, $request_data, $form_location ){
|
110 |
+
if( $field['field'] == 'Avatar' ){
|
111 |
+
$field['meta-name'] = Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'] );
|
112 |
+
if ( isset( $field[ 'simple-upload' ] ) && $field[ 'simple-upload' ] == 'yes' && $field[ 'woocommerce-checkout-field' ] !== 'Yes' ) {
|
113 |
+
//Save data in the case the simple upload field is used
|
114 |
+
$field_name = 'simple_upload_' . wppb_handle_meta_name( $field[ 'meta-name' ] );
|
115 |
+
if( isset( $_FILES[ $field_name ] ) ) {
|
116 |
+
if ( !( isset( $field[ 'conditional-logic-enabled' ] ) && $field[ 'conditional-logic-enabled' ] == 'yes' && !isset( $request_data[ wppb_handle_meta_name( $field[ 'meta-name' ] ) ] ) ) ){
|
117 |
+
if ( isset( $_FILES[ $field_name ][ 'size' ] ) && $_FILES[ $field_name ][ 'size' ] == 0 ){
|
118 |
+
if ( isset( $request_data[ wppb_handle_meta_name( $field[ 'meta-name' ] ) ] ) ){
|
119 |
+
update_user_meta( $user_id, $field[ 'meta-name' ], sanitize_text_field( $request_data[ wppb_handle_meta_name( $field[ 'meta-name' ] ) ] ) );
|
120 |
+
}
|
121 |
+
}
|
122 |
+
else{
|
123 |
+
$attachment_id = $request_data[ $field[ 'meta-name' ] ];
|
124 |
+
update_user_meta( $user_id, $field[ 'meta-name' ], absint( $attachment_id ) );
|
125 |
+
if ( $attachment_id !== '' ) {
|
126 |
+
wp_update_post(array(
|
127 |
+
'ID' => absint(trim($attachment_id)),
|
128 |
+
'post_author' => $user_id
|
129 |
+
));
|
130 |
+
}
|
131 |
+
}
|
132 |
+
}
|
133 |
+
}
|
134 |
+
}
|
135 |
+
else{
|
136 |
+
//Save data in the case the WordPress upload is used
|
137 |
+
if ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) ){
|
138 |
+
update_user_meta( $user_id, $field['meta-name'], sanitize_text_field( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) );
|
139 |
+
}
|
140 |
+
}
|
141 |
+
}
|
142 |
+
}
|
143 |
+
add_action( 'wppb_save_form_field', 'wppb_save_avatar_value', 10, 4 );
|
144 |
+
add_action( 'wppb_backend_save_form_field', 'wppb_save_avatar_value', 10, 4 );
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Function that saves an attachment from the simple upload version of the Avatar field
|
148 |
+
* @param $field_name
|
149 |
+
* @return string|WP_Error
|
150 |
+
*/
|
151 |
+
function wppb_avatar_save_simple_upload_file ( $field_name ){
|
152 |
+
|
153 |
+
require_once( ABSPATH . 'wp-admin/includes/file.php' );
|
154 |
+
$upload_overrides = array( 'test_form' => false );
|
155 |
+
|
156 |
+
if( isset( $_FILES[$field_name] ) )
|
157 |
+
$file = wp_handle_upload( $_FILES[$field_name], $upload_overrides );
|
158 |
+
|
159 |
+
if ( isset( $file[ 'error' ] ) ) {
|
160 |
+
return new WP_Error( 'upload_error', $file[ 'error' ] );
|
161 |
+
}
|
162 |
+
$filename = isset( $_FILES[ $field_name ][ 'name' ] ) ? sanitize_text_field( $_FILES[ $field_name ][ 'name' ] ) : '';
|
163 |
+
$wp_filetype = wp_check_filetype( $filename, null );
|
164 |
+
$attachment = array(
|
165 |
+
'post_mime_type' => $wp_filetype[ 'type' ],
|
166 |
+
'post_title' => $filename,
|
167 |
+
'post_content' => '',
|
168 |
+
'post_status' => 'inherit'
|
169 |
+
);
|
170 |
+
|
171 |
+
$attachment_id = wp_insert_attachment( $attachment, $file[ 'file' ] );
|
172 |
+
|
173 |
+
if (!is_wp_error($attachment_id) && is_numeric($attachment_id)) {
|
174 |
+
require_once(ABSPATH . 'wp-admin/includes/image.php');
|
175 |
+
$attachment_data = wp_generate_attachment_metadata($attachment_id, $file['file']);
|
176 |
+
wp_update_attachment_metadata($attachment_id, $attachment_data);
|
177 |
+
return trim($attachment_id);
|
178 |
+
} else {
|
179 |
+
return '';
|
180 |
+
}
|
181 |
+
}
|
182 |
+
|
183 |
+
/* save file when ec is enabled */
|
184 |
+
function wppb_avatar_add_upload_for_user_signup( $field_value, $field, $request_data ){
|
185 |
+
|
186 |
+
// Save the uploaded file
|
187 |
+
// It will have no author until the user's email is confirmed
|
188 |
+
if( $field['field'] == 'Avatar' ) {
|
189 |
+
if( isset( $field[ 'simple-upload' ] ) && $field[ 'simple-upload' ] === 'yes' && $field[ 'woocommerce-checkout-field' ] !== 'Yes' ) {
|
190 |
+
$field_name = 'simple_upload_' . $field['meta-name'];
|
191 |
+
|
192 |
+
if (isset($_FILES[$field_name]) &&
|
193 |
+
isset($_FILES[$field_name]['size']) && $_FILES[$field_name]['size'] !== 0 &&
|
194 |
+
!(wppb_belongs_to_repeater_with_conditional_logic($field) && !isset($request_data[wppb_handle_meta_name($field['meta-name'])])) &&
|
195 |
+
!(isset($field['conditional-logic-enabled']) && $field['conditional-logic-enabled'] == 'yes' && !isset($request_data[wppb_handle_meta_name($field['meta-name'])])) &&
|
196 |
+
wppb_valid_simple_upload($field, $_FILES[$field_name])) { /* phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized */ /* no need here */
|
197 |
+
return wppb_save_simple_upload_file($field_name);
|
198 |
+
}
|
199 |
+
} else {
|
200 |
+
$attachment_id = $request_data[wppb_handle_meta_name( $field['meta-name'] )];
|
201 |
+
if ( isset( $attachment_id ) ) {
|
202 |
+
return absint( trim( $attachment_id ) );
|
203 |
+
}
|
204 |
+
}
|
205 |
+
}
|
206 |
+
|
207 |
+
return '';
|
208 |
+
}
|
209 |
+
add_filter( 'wppb_add_to_user_signup_form_field_avatar', 'wppb_avatar_add_upload_for_user_signup', 10, 3 );
|
210 |
+
|
211 |
+
/* handle simple upload at the WooCommerce Checkout */
|
212 |
+
function wppb_woo_simple_avatar(){
|
213 |
+
check_ajax_referer( 'wppb_woo_simple_upload', 'nonce' );
|
214 |
+
if ( isset($_POST["name"]) ) {
|
215 |
+
echo json_encode( wppb_avatar_save_simple_upload_file( sanitize_text_field( $_POST["name"] ) ) );
|
216 |
+
}
|
217 |
+
wp_die();
|
218 |
+
}
|
219 |
+
add_action( 'wp_ajax_nopriv_wppb_woo_simple_avatar', 'wppb_woo_simple_avatar' );
|
220 |
+
add_action( 'wp_ajax_wppb_woo_simple_avatar', 'wppb_woo_simple_avatar' );
|
221 |
+
|
222 |
+
/* handle field validation */
|
223 |
+
function wppb_check_avatar_value( $message, $field, $request_data, $form_location ){
|
224 |
+
if( $field['field'] == 'Avatar' ){
|
225 |
+
if( $field['required'] == 'Yes' ){
|
226 |
+
$field['meta-name'] = Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'] );
|
227 |
+
if ( isset( $field[ 'simple-upload' ] ) && $field[ 'simple-upload' ] == 'yes' && $field[ 'woocommerce-checkout-field' ] !== 'Yes' ) {
|
228 |
+
//Check the required field in case simple upload is used
|
229 |
+
$field_name = 'simple_upload_' . wppb_handle_meta_name( $field[ 'meta-name' ] );
|
230 |
+
if ( (!isset( $_FILES[ $field_name ] ) || ( isset( $_FILES[ $field_name ] ) && isset( $_FILES[ $field_name ][ 'size' ] ) && $_FILES[ $field_name ][ 'size' ] == 0 ) || !wppb_valid_simple_upload( $field, $_FILES[ $field_name ] ) ) && isset( $request_data[ $field[ 'meta-name' ] ] ) && empty( $request_data[ $field[ 'meta-name' ] ] ) ){ /* phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized */ /* no need here for wppb_valid_simple_upload() */
|
231 |
+
return wppb_required_field_error( $field[ 'field-title' ] );
|
232 |
+
}
|
233 |
+
}
|
234 |
+
else{
|
235 |
+
//Check the required field in case the WordPress upload is used
|
236 |
+
if ( ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) && ( trim( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) == '' ) ) || !isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) ){
|
237 |
+
return wppb_required_field_error($field["field-title"]);
|
238 |
+
}
|
239 |
+
}
|
240 |
+
}
|
241 |
+
}
|
242 |
+
return $message;
|
243 |
+
}
|
244 |
+
add_filter( 'wppb_check_form_field_avatar', 'wppb_check_avatar_value', 10, 4 );
|
245 |
+
|
246 |
+
|
247 |
+
/* register image size defined in avatar field */
|
248 |
+
add_action( 'after_setup_theme', 'wppb_add_avatar_image_sizes' );
|
249 |
+
function wppb_add_avatar_image_sizes() {
|
250 |
+
if ( isset($_REQUEST['action']) && ( ( 'upload-attachment' == $_REQUEST['action'] && isset($_REQUEST['wppb_upload']) && 'true' == $_REQUEST['wppb_upload'] ) || 'wppb_woo_simple_avatar' == $_REQUEST['action'] ) ) {
|
251 |
+
|
252 |
+
$all_fields = get_option('wppb_manage_fields');
|
253 |
+
if( !empty( $all_fields ) ) {
|
254 |
+
foreach ($all_fields as $field) {
|
255 |
+
if( $field['field'] == 'Avatar' ) {
|
256 |
+
wppb_add_avatar_sizes( $field );
|
257 |
+
}
|
258 |
+
}
|
259 |
+
}
|
260 |
+
|
261 |
+
wppb_userlisting_avatar();
|
262 |
+
}
|
263 |
+
}
|
front-end/default-fields/checkbox/checkbox.php
ADDED
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* handle field output */
|
3 |
+
function wppb_checkbox_handler( $output, $form_location, $field, $user_id, $field_check_errors, $request_data ){
|
4 |
+
if ( $field['field'] == 'Checkbox' ){
|
5 |
+
$item_title = apply_filters( 'wppb_'.$form_location.'_checkbox_custom_field_'.$field['id'].'_item_title', wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_title_translation', $field['field-title'], true ) );
|
6 |
+
$item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_description_translation', $field['description'], true );
|
7 |
+
$item_option_labels = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_labels_translation', $field['labels'], true );
|
8 |
+
|
9 |
+
$checkbox_labels = apply_filters( 'wppb_checkbox_labels_array', explode( ',', $item_option_labels ), $field, $form_location, $user_id, $request_data );
|
10 |
+
$checkbox_values = apply_filters( 'wppb_checkbox_options_array', explode( ',', $field['options'] ), $field, $form_location, $user_id, $request_data );
|
11 |
+
|
12 |
+
if( $form_location != 'register' )
|
13 |
+
$input_value = ( ( wppb_user_meta_exists ( $user_id, $field['meta-name'] ) != null ) ? array_map( 'trim', explode( ',', stripslashes(get_user_meta( $user_id, $field['meta-name'], true )) ) ) : array_map( 'trim', explode( ',', $field['default-options'] ) ) );
|
14 |
+
else
|
15 |
+
$input_value = ( !empty( $field['default-options'] ) ? array_map( 'trim', explode( ',', $field['default-options'] ) ) : array() );
|
16 |
+
|
17 |
+
if( isset( $request_data[ wppb_handle_meta_name( $field['meta-name'] ) ] ) && !empty( $request_data[ wppb_handle_meta_name( $field['meta-name'] ) ] ) )
|
18 |
+
$input_value = $request_data[ wppb_handle_meta_name( $field['meta-name'] ) ];
|
19 |
+
|
20 |
+
$extra_attr = apply_filters( 'wppb_extra_attribute', '', $field, $form_location );
|
21 |
+
|
22 |
+
if ( $form_location != 'back_end' ){
|
23 |
+
$error_mark = ( ( $field['required'] == 'Yes' ) ? '<span class="wppb-required" title="'.wppb_required_field_error($field["field-title"]).'">*</span>' : '' );
|
24 |
+
|
25 |
+
if ( array_key_exists( $field['id'], $field_check_errors ) )
|
26 |
+
$error_mark = '<img src="'.WPPB_PLUGIN_URL.'assets/images/pencil_delete.png" title="'.wppb_required_field_error($field["field-title"]).'"/>';
|
27 |
+
|
28 |
+
$output = '
|
29 |
+
<label for="'.$field['meta-name'].'">'.$item_title.$error_mark.'</label>';
|
30 |
+
$output .= '<ul class="wppb-checkboxes">';
|
31 |
+
$output .= '<li class="wppb-hidden"><input type="hidden" value="" name="' . $field['meta-name'] . '"></li>';
|
32 |
+
foreach( $checkbox_values as $key => $value ){
|
33 |
+
$output .= '<li><input value="'.esc_attr( trim( $value ) ).'" class="custom_field_checkbox" name="' . $field['meta-name'] . '[]" id="'.Wordpress_Creation_Kit_PB::wck_generate_slug( trim( $value ) ).'_'.$field['id'].'" type="checkbox" '. $extra_attr .' ';
|
34 |
+
|
35 |
+
if ( in_array( trim( $value ), $input_value ) )
|
36 |
+
$output .= ' checked';
|
37 |
+
|
38 |
+
$output .= ' /><label for="'.Wordpress_Creation_Kit_PB::wck_generate_slug( trim( $value ) ).'_'.$field['id'].'" class="wppb-rc-value">'.( ( !isset( $checkbox_labels[$key] ) || !$checkbox_labels[$key] ) ? trim( $checkbox_values[$key] ) : trim( $checkbox_labels[$key] ) ).'</label></li>';
|
39 |
+
}
|
40 |
+
$output .= '</ul>';
|
41 |
+
if( !empty( $item_description ) )
|
42 |
+
$output .= '<span class="wppb-description-delimiter">'.$item_description.'</span>';
|
43 |
+
|
44 |
+
}else{
|
45 |
+
$item_title = ( ( $field['required'] == 'Yes' ) ? $item_title .' <span class="description">('. __( 'required', 'profile-builder' ) .')</span>' : $item_title );
|
46 |
+
$output = '
|
47 |
+
<table class="form-table">
|
48 |
+
<tr>
|
49 |
+
<th><label for="'.$field['meta-name'].'">'.$item_title.'</label></th>
|
50 |
+
<td>';
|
51 |
+
$output .= '<ul class="wppb-checkboxes">';
|
52 |
+
$output .= '<li class="wppb-hidden"><input type="hidden" value="" name="' . $field['meta-name'] . '"></li>';
|
53 |
+
foreach( $checkbox_values as $key => $value ){
|
54 |
+
$output .= '<li><input value="'.esc_attr( trim( $value ) ).'" class="custom_field_checkbox '. apply_filters( 'wppb_fields_extra_css_class', '', $field ) .'" name="' . $field['meta-name'] . '[]" id="'.Wordpress_Creation_Kit_PB::wck_generate_slug( trim( $value ) ).'_'.$field['id'].'" type="checkbox"';
|
55 |
+
|
56 |
+
if ( in_array( trim( $value ), $input_value ) )
|
57 |
+
$output .= ' checked';
|
58 |
+
|
59 |
+
$output .= ' /><label for="'.Wordpress_Creation_Kit_PB::wck_generate_slug( trim( $value ) ).'_'.$field['id'].'" class="wppb-rc-value">'.( ( !isset( $checkbox_labels[$key] ) || !$checkbox_labels[$key] ) ? trim( $checkbox_values[$key] ) : trim( $checkbox_labels[$key] ) ).'</label></li>';
|
60 |
+
}
|
61 |
+
|
62 |
+
$output .= '</ul>
|
63 |
+
<span class="wppb-description-delimiter">'.$item_description.'</span>
|
64 |
+
</td>
|
65 |
+
</tr>
|
66 |
+
</table>';
|
67 |
+
}
|
68 |
+
|
69 |
+
return apply_filters( 'wppb_'.$form_location.'_checkbox_custom_field_'.$field['id'], $output, $form_location, $field, $user_id, $field_check_errors, $request_data, $input_value );
|
70 |
+
}
|
71 |
+
}
|
72 |
+
add_filter( 'wppb_output_form_field_checkbox', 'wppb_checkbox_handler', 10, 6 );
|
73 |
+
add_filter( 'wppb_admin_output_form_field_checkbox', 'wppb_checkbox_handler', 10, 6 );
|
74 |
+
|
75 |
+
|
76 |
+
/* handle field save */
|
77 |
+
function wppb_save_checkbox_value( $field, $user_id, $request_data, $form_location ){
|
78 |
+
if( $field['field'] == 'Checkbox' ){
|
79 |
+
if( isset( $request_data[ wppb_handle_meta_name( $field['meta-name'] ) ] ) ) {
|
80 |
+
$checkbox_values = wppb_process_checkbox_value($field, $request_data);
|
81 |
+
update_user_meta($user_id, $field['meta-name'], $checkbox_values);
|
82 |
+
}
|
83 |
+
}
|
84 |
+
}
|
85 |
+
add_action( 'wppb_save_form_field', 'wppb_save_checkbox_value', 10, 4 );
|
86 |
+
add_action( 'wppb_backend_save_form_field', 'wppb_save_checkbox_value', 10, 4 );
|
87 |
+
|
88 |
+
|
89 |
+
function wppb_process_checkbox_value( $field, $request_data ){
|
90 |
+
$checkbox_values = '';
|
91 |
+
|
92 |
+
if( isset( $request_data[ wppb_handle_meta_name( $field['meta-name'] ) ] ) && is_array( $request_data[ wppb_handle_meta_name( $field['meta-name'] ) ] ) )
|
93 |
+
$checkbox_values = implode( ',', $request_data[ wppb_handle_meta_name( $field['meta-name'] ) ] );
|
94 |
+
|
95 |
+
return trim( $checkbox_values, ',' );
|
96 |
+
}
|
97 |
+
|
98 |
+
|
99 |
+
function wppb_add_checkbox_for_user_signup( $field_value, $field, $request_data ){
|
100 |
+
return wppb_process_checkbox_value( $field, $request_data );
|
101 |
+
}
|
102 |
+
add_filter( 'wppb_add_to_user_signup_form_field_checkbox', 'wppb_add_checkbox_for_user_signup', 10, 3 );
|
103 |
+
|
104 |
+
|
105 |
+
/* handle field validation */
|
106 |
+
function wppb_check_checkbox_value( $message, $field, $request_data, $form_location ){
|
107 |
+
|
108 |
+
if( $field['field'] == 'Checkbox' ){
|
109 |
+
$checked_values = '';
|
110 |
+
|
111 |
+
if( isset( $request_data[ wppb_handle_meta_name( $field['meta-name'] ) ] ) && is_array( $request_data[ wppb_handle_meta_name( $field['meta-name'] ) ] ) )
|
112 |
+
$checked_values = implode( ',', $request_data[ wppb_handle_meta_name( $field['meta-name'] ) ] );
|
113 |
+
|
114 |
+
if ( ( $field['required'] == 'Yes' ) && empty( $checked_values ) ){
|
115 |
+
return wppb_required_field_error($field["field-title"]);
|
116 |
+
}
|
117 |
+
|
118 |
+
}
|
119 |
+
|
120 |
+
return $message;
|
121 |
+
}
|
122 |
+
add_filter( 'wppb_check_form_field_checkbox', 'wppb_check_checkbox_value', 10, 4 );
|
front-end/default-fields/default-fields.php
CHANGED
@@ -50,5 +50,20 @@ function wppb_include_default_fields_files() {
|
|
50 |
|
51 |
/* added email-confirmation field in main plugin since version 3.3.4 */
|
52 |
include_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/email-confirmation/email-confirmation.php' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
}
|
54 |
-
wppb_include_default_fields_files();
|
50 |
|
51 |
/* added email-confirmation field in main plugin since version 3.3.4 */
|
52 |
include_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/email-confirmation/email-confirmation.php' );
|
53 |
+
|
54 |
+
// added extra fields since version 3.8.1
|
55 |
+
if( !defined( 'WPPB_PAID_PLUGIN_DIR' ) || ( defined( 'WPPB_PAID_PLUGIN_DIR' ) && defined( 'PROFILE_BUILDER_PAID_VERSION' ) ) ){
|
56 |
+
|
57 |
+
include_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/avatar/avatar.php' );
|
58 |
+
include_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/checkbox/checkbox.php' );
|
59 |
+
include_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/heading/heading.php' );
|
60 |
+
include_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/input/input.php' );
|
61 |
+
include_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/radio/radio.php' );
|
62 |
+
include_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/select/select.php' );
|
63 |
+
include_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/select2/select2.php' );
|
64 |
+
include_once( WPPB_PLUGIN_DIR.'/front-end/default-fields/textarea/textarea.php' );
|
65 |
+
|
66 |
+
}
|
67 |
+
|
68 |
}
|
69 |
+
wppb_include_default_fields_files();
|
front-end/default-fields/fields-functions.php
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
3 |
+
|
4 |
+
// the function to display the custom fields in the back-end
|
5 |
+
function wppb_display_fields_in_admin( $user ){
|
6 |
+
$admin_fields = '';
|
7 |
+
?>
|
8 |
+
<script type="text/javascript">
|
9 |
+
var form = document.getElementById('your-profile');
|
10 |
+
form.encoding = "multipart/form-data"; //IE5.5
|
11 |
+
form.setAttribute('enctype', 'multipart/form-data'); //required for IE6 (is interpreted into "encType")
|
12 |
+
|
13 |
+
jQuery(function(){
|
14 |
+
//hover states on the static widgets
|
15 |
+
jQuery('#dialog_link, ul#icons li').on('mouseenter', function() { jQuery(this).addClass('ui-state-hover'); });
|
16 |
+
jQuery('#dialog_link, ul#icons li').on('mouseleave', function() { jQuery(this).removeClass('ui-state-hover'); });
|
17 |
+
});
|
18 |
+
</script>
|
19 |
+
<?php
|
20 |
+
|
21 |
+
$all_data = get_option( 'wppb_manage_fields' );
|
22 |
+
if ( is_array( $all_data ) ){
|
23 |
+
foreach ( $all_data as $value ) {
|
24 |
+
|
25 |
+
$display_field = apply_filters( 'wppb_output_display_form_field', true, $value, 'back_end', 'all', $user->ID );
|
26 |
+
|
27 |
+
if( $display_field == false )
|
28 |
+
continue;
|
29 |
+
|
30 |
+
$admin_fields .= apply_filters( 'wppb_admin_output_form_field_'.Wordpress_Creation_Kit_PB::wck_generate_slug( $value['field'] ), '', 'back_end', $value, $user->ID, '', $_REQUEST );
|
31 |
+
}
|
32 |
+
|
33 |
+
}
|
34 |
+
|
35 |
+
echo $admin_fields; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
36 |
+
}
|
37 |
+
|
38 |
+
// the function to save the values from the custom fields in the back-end
|
39 |
+
function wppb_save_fields_in_admin( $user_id ){
|
40 |
+
$global_request = $_REQUEST;
|
41 |
+
$all_data = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'validate_backend' ) );
|
42 |
+
if ( is_array( $all_data ) ){
|
43 |
+
foreach ( $all_data as $field ){
|
44 |
+
/* check to see if we have any error for the field. if we do don't save it */
|
45 |
+
$error_for_field = apply_filters( 'wppb_check_form_field_'.Wordpress_Creation_Kit_PB::wck_generate_slug( $field['field'] ), '', $field, $global_request, 'back_end', '', $user_id );
|
46 |
+
if( empty( $error_for_field ) )
|
47 |
+
do_action( 'wppb_backend_save_form_field', $field, $user_id, $global_request, 'backend-form' );
|
48 |
+
}
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
/* the function that checks for field error in the backend */
|
53 |
+
function wppb_validate_fields_in_admin( &$errors, $update, &$user ){
|
54 |
+
|
55 |
+
$all_data = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'validate_backend' ) );
|
56 |
+
$global_request = $_REQUEST;
|
57 |
+
if ( is_array( $all_data ) ){
|
58 |
+
foreach ( $all_data as $field ){
|
59 |
+
$error_for_field = apply_filters( 'wppb_check_form_field_'.Wordpress_Creation_Kit_PB::wck_generate_slug( $field['field'] ), '', $field, $global_request, 'back_end', '', get_current_user_id() );
|
60 |
+
|
61 |
+
if( !empty( $error_for_field ) ){
|
62 |
+
$errors->add( $field['id'], '<strong>'. __( 'ERROR', 'profile-builder' ).'</strong> '.$field['field-title'].':'.$error_for_field);
|
63 |
+
}
|
64 |
+
}
|
65 |
+
}
|
66 |
+
}
|
front-end/default-fields/heading/heading.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* handle field output */
|
3 |
+
function wppb_heading_handler( $output, $form_location, $field, $user_id, $field_check_errors, $request_data ){
|
4 |
+
if ( $field['field'] == 'Heading' ){
|
5 |
+
$item_title = apply_filters( 'wppb_'.$form_location.'_heading_custom_field_'.$field['id'].'_item_title', wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_title_translation', $field['field-title'], true ) );
|
6 |
+
$item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_description_translation', $field['description'], true );
|
7 |
+
|
8 |
+
if( isset( $field['heading-tag'] ) ) {
|
9 |
+
$heading_tag = $field['heading-tag'];
|
10 |
+
} else {
|
11 |
+
$heading_tag = 'h4';
|
12 |
+
}
|
13 |
+
|
14 |
+
$heading_element1 = ( ( $form_location == 'back_end' ) ? '<h3>' : '<'. $heading_tag .' class="extra_field_heading">' );
|
15 |
+
$heading_element2 = ( ( $form_location == 'back_end' ) ? '</h3>' : '</'. $heading_tag .'>' );
|
16 |
+
|
17 |
+
$output = $heading_element1 . $item_title . $heading_element2 . '<span class="wppb-description-delimiter">'.$item_description.'</span>';
|
18 |
+
|
19 |
+
return apply_filters( 'wppb_'.$form_location.'_heading_custom_field_'.$field['id'], $output, $form_location, $field, $user_id, $field_check_errors, $request_data );
|
20 |
+
}
|
21 |
+
}
|
22 |
+
add_filter( 'wppb_output_form_field_heading', 'wppb_heading_handler', 10, 6 );
|
23 |
+
add_filter( 'wppb_admin_output_form_field_heading', 'wppb_heading_handler', 10, 6 );
|
front-end/default-fields/input/input.php
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* handle field output */
|
3 |
+
function wppb_input_handler( $output, $form_location, $field, $user_id, $field_check_errors, $request_data ){
|
4 |
+
if ( $field['field'] == 'Input' ){
|
5 |
+
$item_title = apply_filters( 'wppb_'.$form_location.'_input_custom_field_'.$field['id'].'_item_title', wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_title_translation', $field['field-title'], true ) );
|
6 |
+
$item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_description_translation', $field['description'], true );
|
7 |
+
|
8 |
+
$extra_attr = apply_filters( 'wppb_extra_attribute', '', $field, $form_location );
|
9 |
+
|
10 |
+
if( $form_location != 'register' )
|
11 |
+
$input_value = ( ( wppb_user_meta_exists ( $user_id, $field['meta-name'] ) != null ) ? get_user_meta( $user_id, $field['meta-name'], true ) : $field['default-value'] );
|
12 |
+
else
|
13 |
+
$input_value = ( isset( $field['default-value'] ) ? trim( $field['default-value'] ) : '' );
|
14 |
+
|
15 |
+
$input_value = ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) ? trim( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) : $input_value );
|
16 |
+
|
17 |
+
if ( $form_location != 'back_end' ){
|
18 |
+
$error_mark = ( ( $field['required'] == 'Yes' ) ? '<span class="wppb-required" title="'.wppb_required_field_error($field["field-title"]).'">*</span>' : '' );
|
19 |
+
|
20 |
+
if ( array_key_exists( $field['id'], $field_check_errors ) )
|
21 |
+
$error_mark = '<img src="'.WPPB_PLUGIN_URL.'assets/images/pencil_delete.png" title="'.wppb_required_field_error($field["field-title"]).'"/>';
|
22 |
+
|
23 |
+
$output = '
|
24 |
+
<label for="'.$field['meta-name'].'">'.$item_title.$error_mark.'</label>
|
25 |
+
<input class="extra_field_input '. apply_filters( 'wppb_fields_extra_css_class', '', $field ) .'" name="'.$field['meta-name'].'" maxlength="'. apply_filters( 'wppb_maximum_character_length', 250, $field ) .'" type="text" id="'.$field['meta-name'].'" value="'. esc_attr( wp_unslash( $input_value ) ) .'" '. $extra_attr .'/>';
|
26 |
+
if( !empty( $item_description ) )
|
27 |
+
$output .= '<span class="wppb-description-delimiter">'.$item_description.'</span>';
|
28 |
+
|
29 |
+
}else{
|
30 |
+
$item_title = ( ( $field['required'] == 'Yes' ) ? $item_title .' <span class="description">('. __( 'required', 'profile-builder' ) .')</span>' : $item_title );
|
31 |
+
$output = '
|
32 |
+
<table class="form-table">
|
33 |
+
<tr>
|
34 |
+
<th><label for="'.$field['meta-name'].'">'.$item_title.'</label></th>
|
35 |
+
<td>
|
36 |
+
<input class="custom_field_input" size="45" name="'.$field['meta-name'].'" maxlength="'. apply_filters( 'wppb_maximum_character_length', 250, $field ) .'" type="text" id="'.$field['meta-name'].'" value="'. esc_attr( $input_value ) .'" '. $extra_attr .'/>
|
37 |
+
<span class="description">'.$item_description.'</span>
|
38 |
+
</td>
|
39 |
+
</tr>
|
40 |
+
</table>';
|
41 |
+
}
|
42 |
+
|
43 |
+
return apply_filters( 'wppb_'.$form_location.'_input_custom_field_'.$field['id'], $output, $form_location, $field, $user_id, $field_check_errors, $request_data, $input_value );
|
44 |
+
}
|
45 |
+
}
|
46 |
+
add_filter( 'wppb_output_form_field_input', 'wppb_input_handler', 10, 6 );
|
47 |
+
add_filter( 'wppb_admin_output_form_field_input', 'wppb_input_handler', 10, 6 );
|
48 |
+
|
49 |
+
/* handle field save */
|
50 |
+
function wppb_save_input_value( $field, $user_id, $request_data, $form_location ){
|
51 |
+
if( $field['field'] == 'Input' ){
|
52 |
+
if ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) )
|
53 |
+
update_user_meta( $user_id, $field['meta-name'], $request_data[wppb_handle_meta_name( $field['meta-name'] )] );
|
54 |
+
}
|
55 |
+
}
|
56 |
+
add_action( 'wppb_save_form_field', 'wppb_save_input_value', 10, 4 );
|
57 |
+
add_action( 'wppb_backend_save_form_field', 'wppb_save_input_value', 10, 4 );
|
58 |
+
|
59 |
+
/* handle field validation */
|
60 |
+
function wppb_check_input_value( $message, $field, $request_data, $form_location ){
|
61 |
+
if( $field['field'] == 'Input' ){
|
62 |
+
if( $field['required'] == 'Yes' ){
|
63 |
+
if ( ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) && ( trim( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) == '' ) ) || !isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) ){
|
64 |
+
return wppb_required_field_error($field["field-title"]);
|
65 |
+
}
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
return $message;
|
70 |
+
}
|
71 |
+
add_filter( 'wppb_check_form_field_input', 'wppb_check_input_value', 10, 4 );
|
front-end/default-fields/radio/radio.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* handle field output */
|
3 |
+
function wppb_radio_handler( $output, $form_location, $field, $user_id, $field_check_errors, $request_data ){
|
4 |
+
if ( $field['field'] == 'Radio' ){
|
5 |
+
$item_title = apply_filters( 'wppb_'.$form_location.'_radio_custom_field_'.$field['id'].'_item_title', wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_title_translation', $field['field-title'], true ) );
|
6 |
+
$item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_description_translation', $field['description'], true );
|
7 |
+
$item_option_labels = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_labels_translation', $field['labels'], true );
|
8 |
+
|
9 |
+
$radio_labels = explode( ',', $item_option_labels );
|
10 |
+
$radio_values = explode( ',', $field['options'] );
|
11 |
+
|
12 |
+
if( $form_location != 'register' )
|
13 |
+
$input_value = ( ( wppb_user_meta_exists ( $user_id, $field['meta-name'] ) != null ) ? stripslashes(get_user_meta( $user_id, $field['meta-name'], true )) : $field['default-option'] );
|
14 |
+
else
|
15 |
+
$input_value = ( isset( $field['default-option'] ) ? trim( $field['default-option'] ) : '' );
|
16 |
+
|
17 |
+
$input_value = ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) ? trim( stripslashes( $request_data[wppb_handle_meta_name( $field['meta-name'] )] )) : $input_value );
|
18 |
+
|
19 |
+
$extra_attr = apply_filters( 'wppb_extra_attribute', '', $field, $form_location );
|
20 |
+
|
21 |
+
if ( $form_location != 'back_end' ){
|
22 |
+
$error_mark = ( ( $field['required'] == 'Yes' ) ? '<span class="wppb-required" title="'.wppb_required_field_error($field["field-title"]).'">*</span>' : '' );
|
23 |
+
|
24 |
+
if ( array_key_exists( $field['id'], $field_check_errors ) )
|
25 |
+
$error_mark = '<img src="'.WPPB_PLUGIN_URL.'assets/images/pencil_delete.png" title="'.wppb_required_field_error($field["field-title"]).'"/>';
|
26 |
+
|
27 |
+
$output = '
|
28 |
+
<label for="'.$field['meta-name'].'">'.$item_title.$error_mark.'</label>';
|
29 |
+
$output .= '<ul class="wppb-radios">';
|
30 |
+
foreach( $radio_values as $key => $value){
|
31 |
+
$output .= '<li><input value="'.esc_attr( trim( $value ) ).'" class="custom_field_radio '. apply_filters( 'wppb_fields_extra_css_class', '', $field ) .'" id="'.Wordpress_Creation_Kit_PB::wck_generate_slug( trim( $value ) ).'_'.$field['id'].'" name="'.$field['meta-name'].'" type="radio" '. $extra_attr .' ';
|
32 |
+
|
33 |
+
if ( $input_value === trim( $value ) )
|
34 |
+
$output .= ' checked';
|
35 |
+
|
36 |
+
$output .= ' /><label for="'.Wordpress_Creation_Kit_PB::wck_generate_slug( trim( $value ) ).'_'.$field['id'].'" class="wppb-rc-value">'.( ( !isset( $radio_labels[$key] ) || !$radio_labels[$key] ) ? trim( $radio_values[$key] ) : trim( $radio_labels[$key] ) ).'</label></li>';
|
37 |
+
}
|
38 |
+
$output .= '</ul>';
|
39 |
+
|
40 |
+
if( !empty( $item_description ) )
|
41 |
+
$output .= '<span class="wppb-description-delimiter">'.$item_description.'</span>';
|
42 |
+
|
43 |
+
}else{
|
44 |
+
$item_title = ( ( $field['required'] == 'Yes' ) ? $item_title .' <span class="description">('. __( 'required', 'profile-builder' ) .')</span>' : $item_title );
|
45 |
+
$output = '
|
46 |
+
<table class="form-table">
|
47 |
+
<tr>
|
48 |
+
<th><label for="'.$field['meta-name'].'">'.$item_title.'</label></th>
|
49 |
+
<td>';
|
50 |
+
|
51 |
+
foreach( $radio_values as $key => $value ){
|
52 |
+
$output .= '<li><input value="'.esc_attr( trim( $value ) ).'" id="'.Wordpress_Creation_Kit_PB::wck_generate_slug( trim( $value ) ).'_'.$field['id'].'" class="custom_field_radio" name="'.$field['meta-name'].'" type="radio" '. $extra_attr .' ';
|
53 |
+
|
54 |
+
if ( $input_value === trim( $value ) )
|
55 |
+
$output .= ' checked';
|
56 |
+
|
57 |
+
$output .= ' /><label for="'.Wordpress_Creation_Kit_PB::wck_generate_slug( trim( $value ) ).'_'.$field['id'].'" class="wppb-rc-value">'.( ( !isset( $radio_labels[$key] ) || !$radio_labels[$key] ) ? trim( $radio_values[$key] ) : trim( $radio_labels[$key] ) ).'</label></li>';
|
58 |
+
}
|
59 |
+
|
60 |
+
$output .= '
|
61 |
+
<span class="description">'.$item_description.'</span>
|
62 |
+
</td>
|
63 |
+
</tr>
|
64 |
+
</table>';
|
65 |
+
}
|
66 |
+
|
67 |
+
return apply_filters( 'wppb_'.$form_location.'_radio_custom_field_'.$field['id'], $output, $form_location, $field, $user_id, $field_check_errors, $request_data, $input_value );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
add_filter( 'wppb_output_form_field_radio', 'wppb_radio_handler', 10, 6 );
|
71 |
+
add_filter( 'wppb_admin_output_form_field_radio', 'wppb_radio_handler', 10, 6 );
|
72 |
+
|
73 |
+
|
74 |
+
/* handle field save */
|
75 |
+
function wppb_save_radio_select_value( $field, $user_id, $request_data, $form_location ){
|
76 |
+
if( $field['field'] == 'Radio' ){
|
77 |
+
if ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) )
|
78 |
+
update_user_meta( $user_id, $field['meta-name'], $request_data[wppb_handle_meta_name( $field['meta-name'] )] );
|
79 |
+
}
|
80 |
+
}
|
81 |
+
add_action( 'wppb_save_form_field', 'wppb_save_radio_select_value', 10, 4 );
|
82 |
+
add_action( 'wppb_backend_save_form_field', 'wppb_save_radio_select_value', 10, 4 );
|
83 |
+
|
84 |
+
|
85 |
+
/* handle field validation */
|
86 |
+
function wppb_check_radio_value( $message, $field, $request_data, $form_location ){
|
87 |
+
if( $field['field'] == 'Radio' ){
|
88 |
+
if ( !isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) && ( $field['required'] == 'Yes' ) ){
|
89 |
+
return wppb_required_field_error($field["field-title"]);
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
return $message;
|
94 |
+
}
|
95 |
+
add_filter( 'wppb_check_form_field_radio', 'wppb_check_radio_value', 10, 4 );
|
front-end/default-fields/select/select.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* handle field output */
|
3 |
+
function wppb_select_handler( $output, $form_location, $field, $user_id, $field_check_errors, $request_data ){
|
4 |
+
if ( $field['field'] == 'Select' ){
|
5 |
+
$item_title = apply_filters( 'wppb_'.$form_location.'_select_custom_field_'.$field['id'].'_item_title', wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_title_translation', $field['field-title'], true ) );
|
6 |
+
$item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_description_translation', $field['description'], true );
|
7 |
+
$item_option_labels = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_labels_translation', $field['labels'], true );
|
8 |
+
|
9 |
+
$select_labels = apply_filters( 'wppb_select_labels_array', explode( ',', $item_option_labels ), $field, $form_location, $user_id, $request_data );
|
10 |
+
$select_values = apply_filters( 'wppb_select_options_array', explode( ',', $field['options'] ), $field, $form_location, $user_id, $request_data );
|
11 |
+
|
12 |
+
$extra_attr = apply_filters( 'wppb_extra_attribute', '', $field, $form_location );
|
13 |
+
|
14 |
+
if( $form_location != 'register' )
|
15 |
+
$input_value = ( ( wppb_user_meta_exists ( $user_id, $field['meta-name'] ) != null ) ? stripslashes( get_user_meta( $user_id, $field['meta-name'], true ) ) : $field['default-option'] );
|
16 |
+
else
|
17 |
+
$input_value = ( isset( $field['default-option'] ) ? trim( $field['default-option'] ) : '' );
|
18 |
+
|
19 |
+
$input_value = ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) ? stripslashes( trim( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) ) : $input_value );
|
20 |
+
|
21 |
+
if ( $form_location != 'back_end' ){
|
22 |
+
$error_mark = ( ( $field['required'] == 'Yes' ) ? '<span class="wppb-required" title="'.wppb_required_field_error($field["field-title"]).'">*</span>' : '' );
|
23 |
+
|
24 |
+
if ( array_key_exists( $field['id'], $field_check_errors ) )
|
25 |
+
$error_mark = '<img src="'.WPPB_PLUGIN_URL.'assets/images/pencil_delete.png" title="'.wppb_required_field_error($field["field-title"]).'"/>';
|
26 |
+
|
27 |
+
$output = '
|
28 |
+
<label for="'.$field['meta-name'].'">'.$item_title.$error_mark.'</label>
|
29 |
+
<select name="'.$field['meta-name'].'" id="'.$field['meta-name'].'" class="custom_field_select" '. $extra_attr .'>';
|
30 |
+
|
31 |
+
$extra_select_option = apply_filters( 'wppb_extra_select_option', '', $field, $item_title );
|
32 |
+
if( ! empty( $extra_select_option ) ) {
|
33 |
+
$output .= $extra_select_option;
|
34 |
+
}
|
35 |
+
|
36 |
+
foreach( $select_values as $key => $value){
|
37 |
+
$output .= '<option value="'.esc_attr( trim( $value ) ).'" class="custom_field_select_option '. apply_filters( 'wppb_fields_extra_css_class', '', $field ) .'" ';
|
38 |
+
|
39 |
+
if ( $input_value === trim( $value ) )
|
40 |
+
$output .= ' selected';
|
41 |
+
|
42 |
+
$output .= '>'.( ( !isset( $select_labels[$key] ) || !$select_labels[$key] ) ? trim( $select_values[$key] ) : trim( $select_labels[$key] ) ).'</option>';
|
43 |
+
}
|
44 |
+
|
45 |
+
$output .= '
|
46 |
+
</select>';
|
47 |
+
if( !empty( $item_description ) )
|
48 |
+
$output .= '<span class="wppb-description-delimiter">'.$item_description.'</span>';
|
49 |
+
|
50 |
+
}else{
|
51 |
+
$item_title = ( ( $field['required'] == 'Yes' ) ? $item_title .' <span class="description">('. __( 'required', 'profile-builder' ) .')</span>' : $item_title );
|
52 |
+
$output = '
|
53 |
+
<table class="form-table">
|
54 |
+
<tr>
|
55 |
+
<th><label for="'.$field['meta-name'].'">'.$item_title.'</label></th>
|
56 |
+
<td>
|
57 |
+
<select name="'.$field['meta-name'].'" class="custom_field_select" id="'.$field['meta-name'].'" '. $extra_attr .'>';
|
58 |
+
|
59 |
+
foreach( $select_values as $key => $value){
|
60 |
+
$output .= '<option value="'.esc_attr( trim( $value ) ).'" class="custom_field_select_option" ';
|
61 |
+
|
62 |
+
if ( $input_value === trim( $value ) )
|
63 |
+
$output .= ' selected';
|
64 |
+
|
65 |
+
$output .= '>'.( ( !isset( $select_labels[$key] ) || !$select_labels[$key] ) ? trim( $select_values[$key] ) : trim( $select_labels[$key] ) ).'</option>';
|
66 |
+
}
|
67 |
+
|
68 |
+
$output .= '</select>
|
69 |
+
<span class="description">'.$item_description.'</span>
|
70 |
+
</td>
|
71 |
+
</tr>
|
72 |
+
</table>';
|
73 |
+
}
|
74 |
+
|
75 |
+
return apply_filters( 'wppb_'.$form_location.'_select_custom_field_'.$field['id'], $output, $form_location, $field, $user_id, $field_check_errors, $request_data, $input_value );
|
76 |
+
}
|
77 |
+
}
|
78 |
+
add_filter( 'wppb_output_form_field_select', 'wppb_select_handler', 10, 6 );
|
79 |
+
add_filter( 'wppb_admin_output_form_field_select', 'wppb_select_handler', 10, 6 );
|
80 |
+
|
81 |
+
|
82 |
+
/* handle field save */
|
83 |
+
function wppb_save_select_value( $field, $user_id, $request_data, $form_location ){
|
84 |
+
if( $field['field'] == 'Select' ){
|
85 |
+
if ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) )
|
86 |
+
update_user_meta( $user_id, $field['meta-name'], $request_data[wppb_handle_meta_name( $field['meta-name'] )] );
|
87 |
+
}
|
88 |
+
}
|
89 |
+
add_action( 'wppb_save_form_field', 'wppb_save_select_value', 10, 4 );
|
90 |
+
add_action( 'wppb_backend_save_form_field', 'wppb_save_select_value', 10, 4 );
|
91 |
+
|
92 |
+
|
93 |
+
/* handle field validation */
|
94 |
+
function wppb_check_select_value( $message, $field, $request_data, $form_location ){
|
95 |
+
if( $field['field'] == 'Select' ){
|
96 |
+
if( $field['required'] == 'Yes' ){
|
97 |
+
if ( ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) && ( trim( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) == '' ) ) || !isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) ){
|
98 |
+
return wppb_required_field_error($field["field-title"]);
|
99 |
+
}
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
return $message;
|
104 |
+
}
|
105 |
+
add_filter( 'wppb_check_form_field_select', 'wppb_check_select_value', 10, 4 );
|
front-end/default-fields/select2/select2.css
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Select 2 Field */
|
2 |
+
.wppb-select2 span.select2-container{
|
3 |
+
clear: none;
|
4 |
+
width: 69.9% !important;
|
5 |
+
}
|
6 |
+
|
7 |
+
.wppb-select2-multiple span.select2-container{
|
8 |
+
clear: none;
|
9 |
+
width: 69.9% !important;
|
10 |
+
}
|
11 |
+
|
12 |
+
@media screen and (max-width: 400px){
|
13 |
+
.wppb-select2 span.select2-container{
|
14 |
+
clear: none;
|
15 |
+
width: 100% !important;
|
16 |
+
}
|
17 |
+
.wppb-select2-multiple span.select2-container{
|
18 |
+
clear: none;
|
19 |
+
width: 100% !important;
|
20 |
+
}
|
21 |
+
}
|
front-end/default-fields/select2/select2.js
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
/**
|
3 |
+
* Function that initializes select2 on fields
|
4 |
+
*
|
5 |
+
* @since v.3.3.4
|
6 |
+
*/
|
7 |
+
function wppb_select2_initialize() {
|
8 |
+
jQuery ( '.custom_field_select2' ).each( function(){
|
9 |
+
var selectElement = jQuery( this );
|
10 |
+
var arguments = selectElement.attr('data-wppb-select2-arguments');
|
11 |
+
arguments = JSON.parse( arguments );
|
12 |
+
selectElement.select2( arguments ).on('select2:open', function(){
|
13 |
+
// compatibility with Divi Overlay
|
14 |
+
if( jQuery(selectElement).parents( '.overlay-container' ).length ){
|
15 |
+
jQuery(selectElement).data('select2').dropdown.$dropdownContainer.css( 'z-index', '99999999' );
|
16 |
+
}
|
17 |
+
});
|
18 |
+
});
|
19 |
+
}
|
20 |
+
|
21 |
+
jQuery( document ).ready(function() {
|
22 |
+
wppb_select2_initialize();
|
23 |
+
});
|
front-end/default-fields/select2/select2.php
ADDED
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* Handle field output.
|
5 |
+
*
|
6 |
+
* @param string $output Contains the HTML to display the select2 field.
|
7 |
+
* @param string $form_location
|
8 |
+
* @param array $field
|
9 |
+
* @param integer $user_id
|
10 |
+
* @param array $field_check_errors
|
11 |
+
* @param array $request_data
|
12 |
+
* @return string Filtered output of the HTML to display the select2 field.
|
13 |
+
*/
|
14 |
+
function wppb_select2_display_handler($output, $form_location, $field, $user_id, $field_check_errors, $request_data)
|
15 |
+
{
|
16 |
+
if ($field['field'] == 'Select2') {
|
17 |
+
wp_enqueue_script('wppb_sl2_lib_js', WPPB_PLUGIN_URL . 'assets/js/select2/select2.min.js', array('jquery'));
|
18 |
+
wp_enqueue_style('wppb_sl2_lib_css', WPPB_PLUGIN_URL . 'assets/css/select2/select2.min.css');
|
19 |
+
|
20 |
+
wp_enqueue_script( 'wppb_sl2_js', WPPB_PLUGIN_URL.'front-end/default-fields/select2/select2.js', array('jquery'), PROFILE_BUILDER_VERSION, true );
|
21 |
+
wp_enqueue_style( 'wppb_sl2_css', WPPB_PLUGIN_URL.'front-end/default-fields/select2/select2.css', false, PROFILE_BUILDER_VERSION );
|
22 |
+
|
23 |
+
|
24 |
+
$field['labels'] = apply_filters('wppb_select2_labels', $field['labels'], $form_location, $field, $user_id, $field_check_errors, $request_data);
|
25 |
+
$field['options'] = apply_filters('wppb_select2_options', $field['options'], $form_location, $field, $user_id, $field_check_errors, $request_data);
|
26 |
+
$arguments = apply_filters('wppb_select2_arguments', array(), $form_location, $field);
|
27 |
+
|
28 |
+
$item_title = apply_filters('wppb_' . $form_location . '_select2_custom_field_' . $field['id'] . '_item_title', wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_title_translation', $field['field-title'], true));
|
29 |
+
$item_description = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_description_translation', $field['description'], true);
|
30 |
+
$item_option_labels = wppb_icl_t('plugin profile-builder-pro', 'custom_field_' . $field['id'] . '_labels_translation', $field['labels'], true);
|
31 |
+
|
32 |
+
$select2_labels = apply_filters('wppb_select2_labels_array', explode(',', $item_option_labels));
|
33 |
+
$select2_values = apply_filters('wppb_select2_options_array', explode(',', $field['options']));
|
34 |
+
|
35 |
+
$extra_attr = apply_filters('wppb_extra_attribute', '', $field, $form_location);
|
36 |
+
|
37 |
+
if ($form_location != 'register')
|
38 |
+
$input_value = ((wppb_user_meta_exists($user_id, $field['meta-name']) != null) ? esc_attr(stripslashes(get_user_meta($user_id, $field['meta-name'], true))) : $field['default-option']);
|
39 |
+
else
|
40 |
+
$input_value = (!empty($field['default-option']) ? esc_attr(trim($field['default-option'])) : '');
|
41 |
+
|
42 |
+
$input_value = (isset($request_data[wppb_handle_meta_name($field['meta-name'])]) ? esc_attr(stripslashes(trim($request_data[wppb_handle_meta_name($field['meta-name'])]))) : $input_value);
|
43 |
+
|
44 |
+
if ($form_location != 'back_end') {
|
45 |
+
$error_mark = (($field['required'] == 'Yes') ? '<span class="wppb-required" title="' . wppb_required_field_error($field["field-title"]) . '">*</span>' : '');
|
46 |
+
|
47 |
+
if (array_key_exists($field['id'], $field_check_errors))
|
48 |
+
$error_mark = '<img src="' . WPPB_PLUGIN_URL . 'assets/images/pencil_delete.png" title="' . wppb_required_field_error($field["field-title"]) . '"/>';
|
49 |
+
|
50 |
+
$output = '
|
51 |
+
<label for="' . esc_attr($field['meta-name']) . '">' . $item_title . $error_mark . '</label>
|
52 |
+
<select name="' . esc_attr($field['meta-name']) . '" id="' . esc_attr($field['meta-name']) . '" class="custom_field_select2" ' . $extra_attr . ' data-wppb-select2-arguments=\'' . json_encode($arguments) . '\'>';
|
53 |
+
|
54 |
+
$extra_select_option = apply_filters('wppb_extra_select_option', '', $field, $item_title);
|
55 |
+
if (!empty($extra_select_option)) {
|
56 |
+
$output .= $extra_select_option;
|
57 |
+
}
|
58 |
+
|
59 |
+
foreach ($select2_values as $key => $value) {
|
60 |
+
$output .= '<option value="' . esc_attr(trim($value)) . '" class="custom_field_select2_option ' . apply_filters('wppb_fields_extra_css_class', '', $field) . '" ';
|
61 |
+
|
62 |
+
if ($input_value === trim($value))
|
63 |
+
$output .= ' selected';
|
64 |
+
|
65 |
+
$output .= '>' . ((!isset($select2_labels[$key]) || !$select2_labels[$key]) ? esc_attr(trim($select2_values[$key])) : esc_attr(trim($select2_labels[$key]))) . '</option>';
|
66 |
+
}
|
67 |
+
|
68 |
+
$output .= '
|
69 |
+
</select>';
|
70 |
+
if (!empty($item_description))
|
71 |
+
$output .= '<span class="wppb-description-delimiter">' . $item_description . '</span>';
|
72 |
+
|
73 |
+
} else {
|
74 |
+
$item_title = (($field['required'] == 'Yes') ? $item_title . ' <span class="description">(' . __('required', 'profile-builder') . ')</span>' : $item_title);
|
75 |
+
$output = '
|
76 |
+
<table class="form-table wppb-select2">
|
77 |
+
<tr>
|
78 |
+
<th><label for="' . esc_attr($field['meta-name']) . '">' . $item_title . '</label></th>
|
79 |
+
<td>
|
80 |
+
<select name="' . esc_attr($field['meta-name']) . '" class="custom_field_select2" id="' . esc_attr($field['meta-name']) . '" ' . $extra_attr . ' data-wppb-select2-arguments=\'' . json_encode($arguments) . '\'>';
|
81 |
+
|
82 |
+
foreach ($select2_values as $key => $value) {
|
83 |
+
$output .= '<option value="' . esc_attr(trim($value)) . '" class="custom_field_select2_option" ';
|
84 |
+
|
85 |
+
if ($input_value === trim($value))
|
86 |
+
$output .= ' selected';
|
87 |
+
|
88 |
+
$output .= '>' . ((!isset($select2_labels[$key]) || !$select2_labels[$key]) ? esc_attr(trim($select2_values[$key])) : esc_attr(trim($select2_labels[$key]))) . '</option>';
|
89 |
+
}
|
90 |
+
|
91 |
+
$output .= '</select>
|
92 |
+
<span class="description">' . $item_description . '</span>
|
93 |
+
</td>
|
94 |
+
</tr>
|
95 |
+
</table>';
|
96 |
+
}
|
97 |
+
|
98 |
+
return apply_filters('wppb_' . $form_location . '_select2_custom_field_' . $field['id'], $output, $form_location, $field, $user_id, $field_check_errors, $request_data, $input_value);
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
add_filter('wppb_output_form_field_select2', 'wppb_select2_display_handler', 10, 6);
|
103 |
+
add_filter('wppb_admin_output_form_field_select2', 'wppb_select2_display_handler', 10, 6);
|
104 |
+
|
105 |
+
/*
|
106 |
+
* Handle field save.
|
107 |
+
*
|
108 |
+
* @since 1.0.0
|
109 |
+
*
|
110 |
+
* @param array $field
|
111 |
+
* @param integer $user_id
|
112 |
+
* @param array $request_data
|
113 |
+
* @param string $form_location
|
114 |
+
*/
|
115 |
+
function wppb_select2_save_value($field, $user_id, $request_data, $form_location)
|
116 |
+
{
|
117 |
+
if ($field['field'] == 'Select2') {
|
118 |
+
if (isset($request_data[wppb_handle_meta_name($field['meta-name'])]))
|
119 |
+
update_user_meta($user_id, sanitize_text_field($field['meta-name']), sanitize_text_field($request_data[wppb_handle_meta_name($field['meta-name'])]));
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
add_action('wppb_save_form_field', 'wppb_select2_save_value', 10, 4);
|
124 |
+
add_action('wppb_backend_save_form_field', 'wppb_select2_save_value', 10, 4);
|
125 |
+
|
126 |
+
/*
|
127 |
+
* Handle field validation.
|
128 |
+
*
|
129 |
+
* @since 1.0.0
|
130 |
+
*
|
131 |
+
* @param string $message
|
132 |
+
* @param array $field
|
133 |
+
* @param array $request_data
|
134 |
+
* @param $form_location
|
135 |
+
* @return string Message to display on field validation
|
136 |
+
*/
|
137 |
+
function wppb_select2_check_value($message, $field, $request_data, $form_location)
|
138 |
+
{
|
139 |
+
if ($field['field'] == 'Select2') {
|
140 |
+
if ($field['required'] == 'Yes') {
|
141 |
+
if ((isset($request_data[wppb_handle_meta_name($field['meta-name'])]) && (trim($request_data[wppb_handle_meta_name($field['meta-name'])]) == '')) || !isset($request_data[wppb_handle_meta_name($field['meta-name'])])) {
|
142 |
+
return wppb_required_field_error($field["field-title"]);
|
143 |
+
}
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
return $message;
|
148 |
+
}
|
149 |
+
|
150 |
+
add_filter('wppb_check_form_field_select2', 'wppb_select2_check_value', 10, 4);
|
151 |
+
|
152 |
+
|
front-end/default-fields/textarea/textarea.php
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* handle field output */
|
3 |
+
function wppb_textarea_handler( $output, $form_location, $field, $user_id, $field_check_errors, $request_data ){
|
4 |
+
if ( $field['field'] == 'Textarea' ){
|
5 |
+
$item_title = apply_filters( 'wppb_'.$form_location.'_textarea_custom_field_'.$field['id'].'_item_title', wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_title_translation', $field['field-title'], true ) );
|
6 |
+
$item_description = wppb_icl_t( 'plugin profile-builder-pro', 'custom_field_'.$field['id'].'_description_translation', $field['description'], true );
|
7 |
+
|
8 |
+
$extra_attr = apply_filters( 'wppb_extra_attribute', '', $field, $form_location );
|
9 |
+
|
10 |
+
if( $form_location != 'register' )
|
11 |
+
$input_value = ( ( wppb_user_meta_exists ( $user_id, $field['meta-name'] ) != null ) ? get_user_meta( $user_id, $field['meta-name'], true ) : $field['default-content'] );
|
12 |
+
else
|
13 |
+
$input_value = ( isset( $field['default-content'] ) ? trim( $field['default-content'] ) : '' );
|
14 |
+
|
15 |
+
$input_value = ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) ? trim( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) : $input_value );
|
16 |
+
|
17 |
+
if ( $form_location != 'back_end' ){
|
18 |
+
$error_mark = ( ( $field['required'] == 'Yes' ) ? '<span class="wppb-required" title="'.wppb_required_field_error($field["field-title"]).'">*</span>' : '' );
|
19 |
+
|
20 |
+
if ( array_key_exists( $field['id'], $field_check_errors ) )
|
21 |
+
$error_mark = '<img src="'.WPPB_PLUGIN_URL.'assets/images/pencil_delete.png" title="'.wppb_required_field_error($field["field-title"]).'"/>';
|
22 |
+
|
23 |
+
$output = '
|
24 |
+
<label for="'.$field['meta-name'].'">'.$item_title.$error_mark.'</label>
|
25 |
+
<textarea rows="'.$field['row-count'].'" name="'.$field['meta-name'].'" maxlength="'. apply_filters( 'wppb_maximum_character_length', '', $field ) .'" class="custom_field_textarea '. apply_filters( 'wppb_fields_extra_css_class', '', $field ) .'" id="'.$field['meta-name'].'" wrap="virtual" '. $extra_attr .'>'. esc_textarea( wp_unslash( $input_value ) ) .'</textarea>';
|
26 |
+
if( !empty( $item_description ) )
|
27 |
+
$output .= '<span class="wppb-description-delimiter">'.$item_description.'</span>';
|
28 |
+
|
29 |
+
}else{
|
30 |
+
$item_title = ( ( $field['required'] == 'Yes' ) ? $item_title .' <span class="description">('. __( 'required', 'profile-builder' ) .')</span>' : $item_title );
|
31 |
+
$output = '
|
32 |
+
<table class="form-table">
|
33 |
+
<tr>
|
34 |
+
<th><label for="'.$field['meta-name'].'">'.$item_title.'</label></th>
|
35 |
+
<td>
|
36 |
+
<textarea rows="'.$field['row-count'].'" name="'.$field['meta-name'].'" maxlength="'. apply_filters( 'wppb_maximum_character_length', '', $field ) .'" class="custom_field_textarea" id="'.$field['meta-name'].'" wrap="virtual" '. $extra_attr .'>'. esc_textarea( $input_value ) .'</textarea>
|
37 |
+
<span class="description">'.$item_description.'</span>
|
38 |
+
</td>
|
39 |
+
</tr>
|
40 |
+
</table>';
|
41 |
+
}
|
42 |
+
|
43 |
+
$filter_types = array( $field['meta-name'], $field['id'] );
|
44 |
+
foreach( $filter_types as $filter_type ){
|
45 |
+
$output = apply_filters( 'wppb_'.$form_location.'_textarea_custom_field_'.$filter_type, $output, $form_location, $field, $user_id, $field_check_errors, $request_data, $input_value );
|
46 |
+
}
|
47 |
+
|
48 |
+
return $output;
|
49 |
+
}
|
50 |
+
}
|
51 |
+
add_filter( 'wppb_output_form_field_textarea', 'wppb_textarea_handler', 10, 6 );
|
52 |
+
add_filter( 'wppb_admin_output_form_field_textarea', 'wppb_textarea_handler', 10, 6 );
|
53 |
+
|
54 |
+
|
55 |
+
/* handle field save */
|
56 |
+
function wppb_save_textarea_value( $field, $user_id, $request_data, $form_location ){
|
57 |
+
if( $field['field'] == 'Textarea' ){
|
58 |
+
if ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) )
|
59 |
+
update_user_meta( $user_id, $field['meta-name'], $request_data[wppb_handle_meta_name( $field['meta-name'] )] );
|
60 |
+
}
|
61 |
+
}
|
62 |
+
add_action( 'wppb_save_form_field', 'wppb_save_textarea_value', 10, 4 );
|
63 |
+
add_action( 'wppb_backend_save_form_field', 'wppb_save_textarea_value', 10, 4 );
|
64 |
+
|
65 |
+
|
66 |
+
/* handle field validation */
|
67 |
+
function wppb_check_textarea_value( $message, $field, $request_data, $form_location ){
|
68 |
+
if( $field['field'] == 'Textarea' ){
|
69 |
+
if( $field['required'] == 'Yes' ){
|
70 |
+
if ( ( isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) && ( trim( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) == '' ) ) || !isset( $request_data[wppb_handle_meta_name( $field['meta-name'] )] ) ){
|
71 |
+
return wppb_required_field_error($field["field-title"]);
|
72 |
+
}
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
return $message;
|
77 |
+
}
|
78 |
+
add_filter( 'wppb_check_form_field_textarea', 'wppb_check_textarea_value', 10, 4 );
|
front-end/default-fields/upload/upload.css
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*--------------------------------------------------------------
|
2 |
+
Upload Field
|
3 |
+
--------------------------------------------------------------*/
|
4 |
+
.wppb-remove-upload{
|
5 |
+
color:#BC0B0B;
|
6 |
+
cursor:pointer;
|
7 |
+
}
|
8 |
+
|
9 |
+
.wppb-remove-upload:hover{
|
10 |
+
color:#FF0000;
|
11 |
+
}
|
12 |
+
|
13 |
+
.wppb-remove-upload:focus{
|
14 |
+
border:1px dotted blue;
|
15 |
+
}
|
16 |
+
|
17 |
+
/* upload field */
|
18 |
+
.upload-field-details{
|
19 |
+
display:inline-block;
|
20 |
+
vertical-align:middle;
|
21 |
+
min-height:24px;
|
22 |
+
padding:2px 0;
|
23 |
+
}
|
24 |
+
|
25 |
+
.upload-field-details p{
|
26 |
+
margin:2px 0 0;
|
27 |
+
}
|
28 |
+
|
29 |
+
.upload-field-details img{
|
30 |
+
margin:0 5px 0 0;
|
31 |
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
32 |
+
}
|
33 |
+
|
34 |
+
.upload-field-details > *{
|
35 |
+
float:left;
|
36 |
+
}
|
37 |
+
|
38 |
+
.upload-field-details span{
|
39 |
+
display:block;
|
40 |
+
}
|
41 |
+
|
42 |
+
.upload-field-details .file-name{
|
43 |
+
font-weight:bold;
|
44 |
+
color:#21759B;
|
45 |
+
}
|
46 |
+
|
47 |
+
.upload-field-details .file-type {
|
48 |
+
font-size: 11px;
|
49 |
+
}
|
50 |
+
|
51 |
+
.wppb_upload_button {
|
52 |
+
background: none repeat scroll 0 0 #f7f7f7;
|
53 |
+
border: 1px solid #ccc !important;
|
54 |
+
color: #555;
|
55 |
+
cursor: pointer;
|
56 |
+
display: inline-block;
|
57 |
+
font-size: 12px;
|
58 |
+
line-height: 26px;
|
59 |
+
padding: 0 15px;
|
60 |
+
text-decoration: none;
|
61 |
+
white-space: nowrap;
|
62 |
+
}
|
63 |
+
|
64 |
+
.wppb_upload_button:hover {
|
65 |
+
background: none repeat scroll 0 0 #fafafa;
|
66 |
+
border: 1px solid #999 !important;
|
67 |
+
color: #222;
|
68 |
+
text-decoration: none;
|
69 |
+
}
|
front-end/default-fields/upload/upload.js
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function validate_simple_upload(){
|
2 |
+
jQuery("input:file").on('change', function(e){
|
3 |
+
//handle simple upload
|
4 |
+
var uploadInputName = jQuery(this).attr('name');
|
5 |
+
var uploadButton = jQuery(this);
|
6 |
+
var oFReader = new FileReader();
|
7 |
+
var attachment = e.target.files[0];
|
8 |
+
oFReader.readAsDataURL(attachment);
|
9 |
+
oFReader.onload = function (oFREvent) {
|
10 |
+
var src = oFREvent.target.result;
|
11 |
+
var error = '';
|
12 |
+
jQuery("#p_" + uploadInputName).text(error);
|
13 |
+
uploadInputId = uploadInputName.split('-').join('_');
|
14 |
+
//Check allowed upload type by wordpress
|
15 |
+
allowed_mime_types = wppb_allowed_wordpress_formats.allowed_wordpress_formats;
|
16 |
+
allowed_by_wordpress = false;
|
17 |
+
for (var key in allowed_mime_types){
|
18 |
+
allowed_type = allowed_mime_types[key];
|
19 |
+
if (attachment.type === allowed_type){
|
20 |
+
allowed_by_wordpress = true;
|
21 |
+
possible_extensions = key.split('|');
|
22 |
+
}
|
23 |
+
}
|
24 |
+
if (allowed_by_wordpress) {
|
25 |
+
//Check allowed extensions
|
26 |
+
allowed_extensions = jQuery("#allowed_extensions_" + uploadInputId).val();
|
27 |
+
if (allowed_extensions !== '') {
|
28 |
+
var allowed = false;
|
29 |
+
allowed_extensions = allowed_extensions.split(',');
|
30 |
+
for (var i = 0; i < allowed_extensions.length; i++) {
|
31 |
+
if (possible_extensions.includes(allowed_extensions[i])) {
|
32 |
+
allowed = true;
|
33 |
+
}
|
34 |
+
}
|
35 |
+
if (allowed == false) {
|
36 |
+
error = wppb_error_messages.upload_type_error_message;
|
37 |
+
}
|
38 |
+
}
|
39 |
+
//Check size limit
|
40 |
+
allowed_size_limit = wppb_limit.size_limit;
|
41 |
+
if (attachment.size > allowed_size_limit) {
|
42 |
+
var limit = allowed_size_limit / (1024 * 1024) + 1;
|
43 |
+
error = wppb_error_messages.limit_error_message + limit + 'MB.';
|
44 |
+
}
|
45 |
+
}
|
46 |
+
else{
|
47 |
+
error = wppb_error_messages.upload_type_error_message;
|
48 |
+
}
|
49 |
+
if (error !== '') {
|
50 |
+
jQuery("#p_" + uploadInputName).text(error);
|
51 |
+
uploadButton.val('');
|
52 |
+
} else {
|
53 |
+
var fieldName = uploadInputName.replace(/^(simple_upload_)/,'');
|
54 |
+
var formData = new FormData();
|
55 |
+
if (uploadButton.closest('.wppb-upload').length > 0) {
|
56 |
+
formData.append('action', 'wppb_woo_simple_upload');
|
57 |
+
formData.append(fieldName, jQuery(e.target).prop('files')[0]);
|
58 |
+
} else {
|
59 |
+
formData.append('action', 'wppb_woo_simple_avatar');
|
60 |
+
formData.append(fieldName, jQuery(e.target).prop('files')[0]);
|
61 |
+
}
|
62 |
+
formData.append('nonce', wppb_upload_script_vars.nonce);
|
63 |
+
formData.append('name', fieldName);
|
64 |
+
|
65 |
+
var alreadyDisabled = false;
|
66 |
+
if ( jQuery( 'p.form-submit .submit.button' ).prop( 'disabled' ) ) {
|
67 |
+
alreadyDisabled = true;
|
68 |
+
} else {
|
69 |
+
jQuery( "p.form-submit .submit.button" ).prop( 'disabled', true );
|
70 |
+
}
|
71 |
+
|
72 |
+
jQuery.ajax({
|
73 |
+
url: wppb_upload_script_vars.ajaxUrl,
|
74 |
+
type: 'POST',
|
75 |
+
contentType: false,
|
76 |
+
processData: false,
|
77 |
+
data: formData,
|
78 |
+
success: function(response){
|
79 |
+
jQuery("input#"+fieldName).val(JSON.parse(response));
|
80 |
+
if ( !alreadyDisabled ) {
|
81 |
+
jQuery("p.form-submit .submit.button").prop('disabled', false);
|
82 |
+
}
|
83 |
+
},
|
84 |
+
});
|
85 |
+
}
|
86 |
+
};
|
87 |
+
});
|
88 |
+
}
|
89 |
+
jQuery(document).ready(function(){
|
90 |
+
validate_simple_upload();
|
91 |
+
|
92 |
+
jQuery(document).on( 'click', '.wppb-rpf-add', function( event ){
|
93 |
+
validate_simple_upload();
|
94 |
+
});
|
95 |
+
|
96 |
+
if( typeof wp.media != "undefined" ){
|
97 |
+
// Uploading files
|
98 |
+
var wp_media_post_id = wp.media.model.settings.post.id; // Store the old id
|
99 |
+
|
100 |
+
jQuery(document).on( 'click', '.wppb_upload_button', function( event ){
|
101 |
+
event.preventDefault();
|
102 |
+
|
103 |
+
var set_to_post_id = ''; // Set this
|
104 |
+
|
105 |
+
var file_frame;
|
106 |
+
var uploadInputId = jQuery( this ).data( 'upload_input' );
|
107 |
+
var uploadButton = jQuery( this );
|
108 |
+
|
109 |
+
/* set default tab to upload file */
|
110 |
+
wp.media.controller.Library.prototype.defaults.contentUserSetting = false;
|
111 |
+
wp.media.controller.Library.prototype.defaults.router = false;
|
112 |
+
wp.media.controller.Library.prototype.defaults.searchable = true;
|
113 |
+
wp.media.controller.Library.prototype.defaults.sortable = false;
|
114 |
+
wp.media.controller.Library.prototype.defaults.date = false;
|
115 |
+
|
116 |
+
// If the media frame already exists, reopen it.
|
117 |
+
if ( file_frame ) {
|
118 |
+
// Set the post ID to what we want
|
119 |
+
file_frame.uploader.uploader.param( 'post_id', set_to_post_id );
|
120 |
+
// Open frame
|
121 |
+
file_frame.open();
|
122 |
+
return;
|
123 |
+
} else {
|
124 |
+
// Set the wp.media post id so the uploader grabs the ID we want when initialised
|
125 |
+
wp.media.model.settings.post.id = set_to_post_id;
|
126 |
+
}
|
127 |
+
|
128 |
+
// Create the media frame.
|
129 |
+
file_frame = wp.media.frames.file_frame = wp.media({
|
130 |
+
title: jQuery( this ).data( 'uploader_title' ),
|
131 |
+
button: {
|
132 |
+
text: jQuery( this ).data( 'uploader_button_text' )
|
133 |
+
},
|
134 |
+
multiple: false // Set to true to allow multiple files to be selected
|
135 |
+
});
|
136 |
+
|
137 |
+
/* send the meta_name of the field */
|
138 |
+
file_frame.uploader.options.uploader['params']['wppb_upload'] = 'true';
|
139 |
+
file_frame.uploader.options.uploader['params']['meta_name'] = jQuery( this ).data( 'upload_mn' );
|
140 |
+
|
141 |
+
// When an image is selected, run a callback.
|
142 |
+
file_frame.on( 'select', function() {
|
143 |
+
// We set multiple to false so only get one image from the uploader
|
144 |
+
attachments = file_frame.state().get('selection').toJSON();
|
145 |
+
var attids = [];
|
146 |
+
|
147 |
+
for( var i=0;i < attachments.length; i++ ){
|
148 |
+
// Do something with attachment.id and/or attachment.url here
|
149 |
+
attids.push( attachments[i].id );
|
150 |
+
result = '<div class="upload-field-details" id="'+ uploadInputId +'_info_container" data-attachment_id="'+ attachments[i].id +'">';
|
151 |
+
if( attachments[i].sizes != undefined ){
|
152 |
+
if( attachments[i].sizes.thumbnail != undefined )
|
153 |
+
thumb = attachments[i].sizes.thumbnail;
|
154 |
+
else
|
155 |
+
thumb = attachments[i].sizes.full;
|
156 |
+
thumbnailUrl = thumb.url;
|
157 |
+
}
|
158 |
+
else{
|
159 |
+
thumbnailUrl = attachments[i].icon;
|
160 |
+
}
|
161 |
+
|
162 |
+
result += '<div class="file-thumb">';
|
163 |
+
result += '<a href="'+ attachments[i].url +'" target="_blank" class="wppb-attachment-link">';
|
164 |
+
result += '<img width="80" height="80" src="'+ thumbnailUrl +'"/>';
|
165 |
+
result += '</a>';
|
166 |
+
result += '</div>';
|
167 |
+
result += '<p><span class="file-name">'+attachments[i].filename+'</span><span class="file-type">'+attachments[i].mime +'</span><span class="wppb-remove-upload" tabindex="0">'+wppb_upload_script_vars.remove_link_text+'</span></p></div>';
|
168 |
+
|
169 |
+
// if multiple upload false remove previous upload details
|
170 |
+
if( uploadButton.data( 'multiple_upload' ) == false ){
|
171 |
+
jQuery( '.upload-field-details', uploadButton.parent() ).remove();
|
172 |
+
}
|
173 |
+
|
174 |
+
uploadButton.before( result );
|
175 |
+
uploadButton.hide();
|
176 |
+
|
177 |
+
}
|
178 |
+
// turn into comma separated string
|
179 |
+
attids = attids.join(',');
|
180 |
+
jQuery( 'input[id="'+uploadInputId+'"]', uploadButton.parent() ).val( attids );
|
181 |
+
|
182 |
+
// Restore the main post ID
|
183 |
+
wp.media.model.settings.post.id = wp_media_post_id;
|
184 |
+
|
185 |
+
jQuery(document).trigger( 'wppb_file_uploaded' )
|
186 |
+
|
187 |
+
});
|
188 |
+
|
189 |
+
// Finally, open the modal
|
190 |
+
file_frame.open();
|
191 |
+
// remove tabs from the top ( this is done higher in the code when setting router to false )
|
192 |
+
//jQuery('.media-frame-router').remove();
|
193 |
+
|
194 |
+
jQuery('.media-frame-title').append('<style type="text/css">label.setting, .edit-attachment{display:none !important;}</style>');
|
195 |
+
});
|
196 |
+
|
197 |
+
// Restore the main ID when the add media button is pressed
|
198 |
+
jQuery('a.add_media').on('click', function() {
|
199 |
+
wp.media.model.settings.post.id = wp_media_post_id;
|
200 |
+
});
|
201 |
+
|
202 |
+
jQuery(document).on('keypress', '.wppb-remove-upload', function(e){
|
203 |
+
if(e.which == 13) {
|
204 |
+
jQuery(this).trigger('click');
|
205 |
+
}
|
206 |
+
});
|
207 |
+
|
208 |
+
jQuery(document).on('click', '.wppb-remove-upload', function(e){
|
209 |
+
if( confirm( 'Are you sure ?' ) ){
|
210 |
+
/* update hidden input */
|
211 |
+
simple_upload_button = jQuery(this).closest('li, td, p.form-row, div.form-row').find('input[type="file"]');
|
212 |
+
if (simple_upload_button.attr('name')) {
|
213 |
+
upload_input_id = simple_upload_button.attr('name').split('_').slice(2).join('_');
|
214 |
+
upload_input_id = upload_input_id.split('-').join('_');
|
215 |
+
upload_input = jQuery("#" + upload_input_id);
|
216 |
+
}
|
217 |
+
else{
|
218 |
+
upload_input = jQuery(this).closest('li, td, p.form-row, div.form-row').find('input[type="hidden"]');
|
219 |
+
}
|
220 |
+
removedAttachement = jQuery(this).parent().parent('.upload-field-details').data('attachment_id');
|
221 |
+
uploadAttachemnts = upload_input.val();
|
222 |
+
uploadAttachemntsArray = uploadAttachemnts.split(',');
|
223 |
+
newuploadAttachments = [];
|
224 |
+
for (var i = 0; i < uploadAttachemntsArray.length; i++) {
|
225 |
+
if (uploadAttachemntsArray[i] != removedAttachement)
|
226 |
+
newuploadAttachments.push(uploadAttachemntsArray[i]);
|
227 |
+
}
|
228 |
+
newuploadAttachments = newuploadAttachments.join(',');
|
229 |
+
upload_input.val(newuploadAttachments);
|
230 |
+
|
231 |
+
/* remove the attachment details */
|
232 |
+
jQuery(this).parent().parent('.upload-field-details').next('a').show();
|
233 |
+
simple_upload_button.show();
|
234 |
+
simple_upload_button.val('');
|
235 |
+
jQuery(this).parent().parent('.upload-field-details').remove();
|
236 |
+
|
237 |
+
jQuery(document).trigger( 'wppb_removed_uploaded_file' )
|
238 |
+
|
239 |
+
}
|
240 |
+
});
|
241 |
+
}
|
242 |
+
});
|
front-end/default-fields/upload/upload_helper_functions.php
ADDED
@@ -0,0 +1,380 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* Set up upload field for frontend */
|
3 |
+
/* overwrite the two functions for when an upload is made from the frontend so they don't check for a logged in user */
|
4 |
+
if( strpos( wp_get_referer(), 'wp-admin' ) === false && isset( $_REQUEST['action'] ) && 'upload-attachment' == $_REQUEST['action'] ){
|
5 |
+
if( !function_exists( 'check_ajax_referer' ) ){
|
6 |
+
function check_ajax_referer( ) {
|
7 |
+
return true;
|
8 |
+
}
|
9 |
+
}
|
10 |
+
|
11 |
+
if( !function_exists( 'auth_redirect' ) ){
|
12 |
+
function auth_redirect() {
|
13 |
+
return true;
|
14 |
+
}
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
18 |
+
/* create a fake user with the "upload_posts" capability and assign him to the global $current_user. this is used to bypass the checks for current_user_can('upload_files') in async-upload.php */
|
19 |
+
add_action( 'current_screen', 'wppb_create_fake_user_when_uploading_and_not_logged_in' );
|
20 |
+
if( !function_exists( 'wppb_create_fake_user_when_uploading_and_not_logged_in' ) ) {
|
21 |
+
function wppb_create_fake_user_when_uploading_and_not_logged_in()
|
22 |
+
{
|
23 |
+
if ( isset($_REQUEST['action']) && 'upload-attachment' == $_REQUEST['action'] && isset($_REQUEST['wppb_upload']) && 'true' == $_REQUEST['wppb_upload'] ) {
|
24 |
+
if (!is_user_logged_in() || !current_user_can('upload_files') || !current_user_can('edit_posts')) {
|
25 |
+
global $current_user;
|
26 |
+
$current_user = new WP_User(0, 'frontend_uploader');
|
27 |
+
$current_user->allcaps = array("upload_files" => true, "edit_posts" => true, "edit_others_posts" => true, "edit_pages" => true, "edit_others_pages" => true);
|
28 |
+
}
|
29 |
+
}
|
30 |
+
}
|
31 |
+
}
|
32 |
+
|
33 |
+
/* for a request of a upload from the frontend and no user is logged in don't query for attachments */
|
34 |
+
add_action( 'after_setup_theme', 'wppb_modify_query_attachements_when_not_logged_in' );
|
35 |
+
if( !function_exists( 'wppb_modify_query_attachements_when_not_logged_in' ) ) {
|
36 |
+
function wppb_modify_query_attachements_when_not_logged_in()
|
37 |
+
{
|
38 |
+
if (strpos(wp_get_referer(), 'wp-admin') === false && !is_user_logged_in()) {
|
39 |
+
add_action('wp_ajax_query-attachments', 'wppb_wp_ajax_not_loggedin_query_attachments', 0);
|
40 |
+
add_action('wp_ajax_nopriv_query-attachments', 'wppb_wp_ajax_not_loggedin_query_attachments', 0);
|
41 |
+
function wppb_wp_ajax_not_loggedin_query_attachments()
|
42 |
+
{
|
43 |
+
wp_send_json_success();
|
44 |
+
}
|
45 |
+
}
|
46 |
+
}
|
47 |
+
}
|
48 |
+
|
49 |
+
/* restrict file types of the upload field functionality */
|
50 |
+
add_filter('wp_handle_upload_prefilter', 'wppb_upload_file_type');
|
51 |
+
if( !function_exists( 'wppb_upload_file_type' ) ) {
|
52 |
+
function wppb_upload_file_type($file)
|
53 |
+
{
|
54 |
+
if( isset( $_POST['wppb_upload'] ) && $_POST['wppb_upload'] == 'true' ) {
|
55 |
+
|
56 |
+
// file size limits.
|
57 |
+
$size = $file['size'];
|
58 |
+
$limit = apply_filters('wppb_server_max_upload_size_byte_constant', wppb_return_bytes(ini_get('upload_max_filesize')));
|
59 |
+
if ( $size > $limit ) {
|
60 |
+
$limit = $limit / ( 1024 * 1024 ) ;
|
61 |
+
$file['error'] = __("Files must be smaller than ", "profile-builder") . $limit . 'MB';
|
62 |
+
}
|
63 |
+
|
64 |
+
if (isset($_POST['meta_name']) && !empty($_POST['meta_name'])) {
|
65 |
+
$meta_name = sanitize_text_field( $_POST['meta_name'] );
|
66 |
+
/*let's get the field details so we can see if we have any file restrictions */
|
67 |
+
$all_fields = apply_filters( 'wppb_form_fields', get_option('wppb_manage_fields'), array( 'context' => 'upload_helper', 'upload_meta_name' => $meta_name ) );
|
68 |
+
if (!empty($all_fields)) {
|
69 |
+
foreach ($all_fields as $field) {
|
70 |
+
if ($field['meta-name'] == $meta_name) {
|
71 |
+
$allowed_upload_extensions = '';
|
72 |
+
if ($field['field'] == 'Upload' && !empty($field['allowed-upload-extensions']))
|
73 |
+
$allowed_upload_extensions = $field['allowed-upload-extensions'];
|
74 |
+
if ($field['field'] == 'Avatar' && !empty($field['allowed-image-extensions'])) {
|
75 |
+
if (trim($field['allowed-image-extensions']) == '.*')
|
76 |
+
$allowed_upload_extensions = '.jpg,.jpeg,.gif,.png,.ico';
|
77 |
+
else
|
78 |
+
$allowed_upload_extensions = $field['allowed-image-extensions'];
|
79 |
+
}
|
80 |
+
|
81 |
+
$ext = strtolower( substr(strrchr($file['name'], '.'), 1) );
|
82 |
+
|
83 |
+
if (!empty($allowed_upload_extensions) && $allowed_upload_extensions != '.*') {
|
84 |
+
$allowed = str_replace('.', '', array_map('trim', explode(",", strtolower( $allowed_upload_extensions))));
|
85 |
+
//first check if the user uploaded the right type
|
86 |
+
if (!in_array($ext, (array)$allowed)) {
|
87 |
+
$file['error'] = __("Sorry, you cannot upload this file type for this field.", 'profile-builder');
|
88 |
+
return $file;
|
89 |
+
}
|
90 |
+
}
|
91 |
+
//check if the type is allowed at all by WordPress
|
92 |
+
foreach (get_allowed_mime_types() as $key => $value) {
|
93 |
+
if (strpos($key, $ext) !== false || $key == $ext)
|
94 |
+
return $file;
|
95 |
+
}
|
96 |
+
$file['error'] = __("Sorry, you cannot upload this file type for this field.", 'profile-builder');
|
97 |
+
}
|
98 |
+
}
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
if (empty($_POST['meta_name']))
|
103 |
+
$file['error'] = __("An error occurred, please try again later.", 'profile-builder');
|
104 |
+
}
|
105 |
+
|
106 |
+
return $file;
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Function that performs validation for the simple upload field
|
112 |
+
*
|
113 |
+
* @param $field - simple upload field
|
114 |
+
* @param $upload - data to be uploaded
|
115 |
+
*
|
116 |
+
* @return bool
|
117 |
+
*/
|
118 |
+
function wppb_valid_simple_upload( $field, $upload ){
|
119 |
+
$limit = apply_filters( 'wppb_server_max_upload_size_byte_constant', wppb_return_bytes( ini_get( 'upload_max_filesize' ) ) );
|
120 |
+
$allowed_mime_types = get_allowed_mime_types();
|
121 |
+
$all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) );
|
122 |
+
if ( !empty( $all_fields ) ) {
|
123 |
+
foreach ( $all_fields as $form_field ) {
|
124 |
+
if ($form_field[ 'meta-name' ] == $field[ 'meta-name' ] ) {
|
125 |
+
$allowed_upload_extensions = '';
|
126 |
+
if ( $form_field[ 'field' ] == 'Upload' && !empty( $form_field[ 'allowed-upload-extensions' ] ) ) {
|
127 |
+
$allowed_upload_extensions = $form_field[ 'allowed-upload-extensions' ];
|
128 |
+
}
|
129 |
+
if ( $form_field[ 'field' ] == 'Avatar' ) {
|
130 |
+
if ( trim( $field[ 'allowed-image-extensions' ] ) == '.*' || trim( $field[ 'allowed-image-extensions' ] ) == '' ) {
|
131 |
+
$allowed_upload_extensions = '.jpg,.jpeg,.gif,.png';
|
132 |
+
}
|
133 |
+
else {
|
134 |
+
$allowed_upload_extensions = $form_field[ 'allowed-image-extensions' ];
|
135 |
+
}
|
136 |
+
}
|
137 |
+
if ( !empty( $allowed_upload_extensions ) && $allowed_upload_extensions != '.*' ) {
|
138 |
+
$allowed_upload_extensions = str_replace( '.', '', array_map( 'trim', explode( ",", strtolower( $allowed_upload_extensions ) ) ) );
|
139 |
+
} else {
|
140 |
+
$allowed = true;
|
141 |
+
}
|
142 |
+
$allowed_by_wordpress = false;
|
143 |
+
foreach ( $allowed_mime_types as $key => $val ){
|
144 |
+
if ( $val == $upload[ 'type' ] ){
|
145 |
+
$possible_extensions = explode( '|', $key );
|
146 |
+
$allowed_by_wordpress = true;
|
147 |
+
}
|
148 |
+
}
|
149 |
+
if ( isset( $possible_extensions ) && $allowed_by_wordpress == true ){
|
150 |
+
if ( !isset( $allowed ) ){
|
151 |
+
$allowed = false;
|
152 |
+
foreach ( $allowed_upload_extensions as $extension ){
|
153 |
+
if ( in_array( $extension, $possible_extensions ) ){
|
154 |
+
$allowed = true;
|
155 |
+
}
|
156 |
+
}
|
157 |
+
}
|
158 |
+
if ( $upload[ 'size' ] > $limit ){
|
159 |
+
$allowed = false;
|
160 |
+
}
|
161 |
+
return $allowed;
|
162 |
+
}
|
163 |
+
else{
|
164 |
+
return false;
|
165 |
+
}
|
166 |
+
}
|
167 |
+
}
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Function that registers intermediate avatar sizes
|
173 |
+
*
|
174 |
+
* @param $field - avatar field
|
175 |
+
*
|
176 |
+
*/
|
177 |
+
function wppb_add_avatar_sizes( $field ){
|
178 |
+
if( !empty( $field['avatar-size'] ) )
|
179 |
+
add_image_size( 'wppb-avatar-size-'.$field['avatar-size'], $field['avatar-size'], $field['avatar-size'], true );
|
180 |
+
else
|
181 |
+
add_image_size( 'wppb-avatar-size-100', 100, 100, true );
|
182 |
+
|
183 |
+
add_image_size( 'wppb-avatar-size-64', 64, 64, true );
|
184 |
+
add_image_size( 'wppb-avatar-size-26', 26, 26, true );
|
185 |
+
}
|
186 |
+
|
187 |
+
//Function that registers avatar sizes for userlisting
|
188 |
+
function wppb_userlisting_avatar(){
|
189 |
+
$userlisting_posts = get_posts( array( 'posts_per_page' => -1, 'post_status' =>'publish', 'post_type' => 'wppb-ul-cpt', 'orderby' => 'post_date', 'order' => 'ASC' ) );
|
190 |
+
if( !empty( $userlisting_posts ) ){
|
191 |
+
foreach ( $userlisting_posts as $post ){
|
192 |
+
$this_form_settings = get_post_meta( $post->ID, 'wppb_ul_page_settings', true );
|
193 |
+
$all_userlisting_avatar_size = apply_filters( 'all_userlisting_avatar_size', ( isset( $this_form_settings[0]['avatar-size-all-userlisting'] ) ? (int)$this_form_settings[0]['avatar-size-all-userlisting'] : 100 ) );
|
194 |
+
$single_userlisting_avatar_size = apply_filters( 'single_userlisting_avatar_size', ( isset( $this_form_settings[0]['avatar-size-single-userlisting'] ) ? (int)$this_form_settings[0]['avatar-size-single-userlisting'] : 100 ) );
|
195 |
+
|
196 |
+
add_image_size( 'wppb-avatar-size-'.$all_userlisting_avatar_size, $all_userlisting_avatar_size, $all_userlisting_avatar_size, true );
|
197 |
+
add_image_size( 'wppb-avatar-size-'.$single_userlisting_avatar_size, $single_userlisting_avatar_size, $single_userlisting_avatar_size, true );
|
198 |
+
}
|
199 |
+
}
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Function that checks if the simple upload field belongs to a repeater field with conditional logic enabled
|
204 |
+
*
|
205 |
+
* @param $field - simple upload field
|
206 |
+
*
|
207 |
+
* @return bool
|
208 |
+
*/
|
209 |
+
function wppb_belongs_to_repeater_with_conditional_logic( $field ){
|
210 |
+
$all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) );
|
211 |
+
if ( !empty( $all_fields ) ) {
|
212 |
+
foreach ( $all_fields as $form_field ) {
|
213 |
+
if ( $form_field[ 'field' ] == 'Repeater' && isset( $form_field[ 'conditional-logic-enabled' ] ) && $form_field[ 'conditional-logic-enabled' ] == 'yes' ) {
|
214 |
+
$repeater_group = get_option( $form_field[ 'meta-name' ], 'not_set' );
|
215 |
+
if ( $repeater_group == 'not_set' ) {
|
216 |
+
continue;
|
217 |
+
}
|
218 |
+
else{
|
219 |
+
$repeater_count = count( $repeater_group );
|
220 |
+
for ( $i = 0; $i < $repeater_count; $i++ ){
|
221 |
+
if ( $repeater_group[ $i ][ 'field' ] == 'Upload' && isset( $repeater_group[ $i ][ 'simple-upload' ] ) && $repeater_group[ $i ][ 'simple-upload' ] == 'yes' && isset( $_REQUEST[ $form_field[ 'meta-name' ] . '_extra_groups_count' ] ) ){
|
222 |
+
$groups = absint( $_REQUEST[ $form_field[ 'meta-name' ] . '_extra_groups_count' ] );
|
223 |
+
for ( $j = 0; $j <= $groups; $j++ ){
|
224 |
+
$name = $repeater_group[ $i ][ 'meta-name' ];
|
225 |
+
if ( $j != 0 ){
|
226 |
+
$name .= '_' . $j;
|
227 |
+
}
|
228 |
+
if ( $field[ 'meta-name' ] == $name ){
|
229 |
+
return true;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
}
|
233 |
+
}
|
234 |
+
}
|
235 |
+
}
|
236 |
+
}
|
237 |
+
}
|
238 |
+
return false;
|
239 |
+
}
|
240 |
+
|
241 |
+
function wppb_make_upload_button( $field, $input_value, $extra_attr = '' ){
|
242 |
+
// change the upload limit. This is not functional.
|
243 |
+
// just for display in the upload window. see upload_helper_functions.php for the actual restriction.
|
244 |
+
add_filter('upload_size_limit', function($limit, $u, $p){
|
245 |
+
return apply_filters('wppb_server_max_upload_size_byte_constant', wppb_return_bytes(ini_get('upload_max_filesize')));
|
246 |
+
}, 10, 3);
|
247 |
+
|
248 |
+
$upload_button = '';
|
249 |
+
$upload_input_id = str_replace( '-', '_', Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'] ) );
|
250 |
+
|
251 |
+
/* container for the image preview (or file ico) and name and file type */
|
252 |
+
if( !empty( $input_value ) ){
|
253 |
+
/* it can hold multiple attachments separated by comma */
|
254 |
+
$values = explode( ',', $input_value );
|
255 |
+
foreach( $values as $value ) {
|
256 |
+
if( !empty( $value ) && is_numeric( $value ) ){
|
257 |
+
$thumbnail = wp_get_attachment_image($value, array(80, 80), true);
|
258 |
+
$file_name = get_the_title($value);
|
259 |
+
$file_type = get_post_mime_type($value);
|
260 |
+
$attachment_url = wp_get_attachment_url($value);
|
261 |
+
$upload_button .= '<div id="' . esc_attr($upload_input_id) . '_info_container" class="upload-field-details" data-attachment_id="' . $value . '">';
|
262 |
+
$upload_button .= '<div class="file-thumb">';
|
263 |
+
$upload_button .= "<a href='{$attachment_url}' target='_blank' class='wppb-attachment-link'>" . $thumbnail . "</a>";
|
264 |
+
$upload_button .= '</div>';
|
265 |
+
$upload_button .= '<p><span class="file-name">';
|
266 |
+
$upload_button .= $file_name;
|
267 |
+
$upload_button .= '</span><span class="file-type">';
|
268 |
+
$upload_button .= $file_type;
|
269 |
+
$upload_button .= '</span>';
|
270 |
+
$upload_button .= '<span class="wppb-remove-upload" tabindex="0">' . apply_filters( 'wppb_upload_button_remove_label', __( 'Remove', 'profile-builder' ) ) . '</span>';
|
271 |
+
$upload_button .= '</p></div>';
|
272 |
+
}
|
273 |
+
}
|
274 |
+
$hide_upload_button = ' style="display:none;"';
|
275 |
+
}
|
276 |
+
else{
|
277 |
+
$hide_upload_button = '';
|
278 |
+
}
|
279 |
+
|
280 |
+
if ( isset( $field[ 'simple-upload' ] ) && $field[ 'simple-upload' ] == 'yes' ){
|
281 |
+
//If selected accordingly in form fields, generate a simple upload button
|
282 |
+
$upload_button .= '<input type="file" id="upload_' . esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) . '_button" name="simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'"';
|
283 |
+
$upload_button .= $hide_upload_button . '>';
|
284 |
+
$upload_button .= '<p id="p_simple_upload_'. esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) .'"></p>';
|
285 |
+
$limit = apply_filters( 'wppb_server_max_upload_size_byte_constant', wppb_return_bytes( ini_get( 'upload_max_filesize' ) ) );
|
286 |
+
$all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) );
|
287 |
+
if ( !empty( $all_fields ) ) {
|
288 |
+
foreach ( $all_fields as $form_field ) {
|
289 |
+
if ($form_field[ 'meta-name' ] == $field[ 'meta-name' ] ) {
|
290 |
+
$allowed_upload_extensions = '';
|
291 |
+
if ( $form_field[ 'field' ] == 'Upload' && !empty( $form_field[ 'allowed-upload-extensions' ] ) ) {
|
292 |
+
$allowed_upload_extensions = $form_field[ 'allowed-upload-extensions' ];
|
293 |
+
}
|
294 |
+
if ( $form_field[ 'field' ] == 'Avatar' ) {
|
295 |
+
if ( trim( $field[ 'allowed-image-extensions' ] ) == '.*' || trim( $field[ 'allowed-image-extensions' ] ) == '' ) {
|
296 |
+
$allowed_upload_extensions = '.jpg,.jpeg,.gif,.png';
|
297 |
+
}
|
298 |
+
else {
|
299 |
+
$allowed_upload_extensions = $form_field[ 'allowed-image-extensions' ];
|
300 |
+
}
|
301 |
+
}
|
302 |
+
}
|
303 |
+
if ( !empty( $allowed_upload_extensions ) && $allowed_upload_extensions != '.*' ) {
|
304 |
+
$allowed_extensions = str_replace( '.', '', array_map( 'trim', explode( ",", strtolower( $allowed_upload_extensions ) ) ) );
|
305 |
+
$allowed_extensions = implode( ',', $allowed_extensions );
|
306 |
+
} else {
|
307 |
+
$allowed_extensions = '';
|
308 |
+
}
|
309 |
+
}
|
310 |
+
}
|
311 |
+
$upload_button .= '<input id="allowed_extensions_simple_upload_'. esc_attr( $upload_input_id ) .'" type="hidden" size="36" name="allowed_extensions_simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. $allowed_extensions .'"/>';
|
312 |
+
$allowed_mime_types = get_allowed_mime_types();
|
313 |
+
$allowed_types = '';
|
314 |
+
if ( !empty( $allowed_mime_types ) ) {
|
315 |
+
foreach ($allowed_mime_types as $key => $val){
|
316 |
+
$allowed_types .= $key . '=>' . $val . ',';
|
317 |
+
}
|
318 |
+
}
|
319 |
+
$error_messages = array(
|
320 |
+
'limit_error_message' => __( 'Files must be smaller than ', 'profile-builder' ),
|
321 |
+
'upload_type_error_message' => __( 'Sorry, you cannot upload this file type for this field.', 'profile-builder' ),
|
322 |
+
);
|
323 |
+
$size_limit = array(
|
324 |
+
'size_limit' => $limit
|
325 |
+
);
|
326 |
+
$allowed_wordpress_formats = array(
|
327 |
+
'allowed_wordpress_formats' => $allowed_mime_types
|
328 |
+
);
|
329 |
+
wp_localize_script( 'wppb-upload-script', 'wppb_error_messages', $error_messages );
|
330 |
+
wp_localize_script( 'wppb-upload-script', 'wppb_limit', $size_limit );
|
331 |
+
wp_localize_script( 'wppb-upload-script', 'wppb_allowed_wordpress_formats', $allowed_wordpress_formats );
|
332 |
+
}
|
333 |
+
else{
|
334 |
+
//Otherwise, generate the WordPress upload button
|
335 |
+
$upload_button .= '<a href="#" class="button wppb_upload_button" id="upload_' . esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) . '_button" '.$hide_upload_button.' data-uploader_title="' . $field["field-title"] . '" data-uploader_button_text="'. __( 'Select File', 'profile-builder' ) .'" data-upload_mn="'. $field['meta-name'] .'" data-upload_input="' . esc_attr($upload_input_id) . '"';
|
336 |
+
|
337 |
+
if (is_user_logged_in())
|
338 |
+
$upload_button .= ' data-uploader_logged_in="true"';
|
339 |
+
$upload_button .= ' data-multiple_upload="false"';
|
340 |
+
|
341 |
+
$upload_button .= '>' . apply_filters( 'wppb_upload_button_select_label', __( 'Upload ', 'profile-builder' ) ) . '</a>';
|
342 |
+
}
|
343 |
+
|
344 |
+
$upload_button .= '<input id="'. esc_attr( $upload_input_id ) .'" type="hidden" size="36" name="'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. $input_value .'"/>';
|
345 |
+
return $upload_button;
|
346 |
+
}
|
347 |
+
|
348 |
+
/**
|
349 |
+
* Function to save an attachment from the simple upload field
|
350 |
+
* @param $field_name
|
351 |
+
* @return string|WP_Error
|
352 |
+
*/
|
353 |
+
function wppb_save_simple_upload_file ( $field_name ){
|
354 |
+
require_once(ABSPATH . 'wp-admin/includes/file.php');
|
355 |
+
$upload_overrides = array('test_form' => false);
|
356 |
+
|
357 |
+
if( isset( $_FILES[$field_name] ) )
|
358 |
+
$file = wp_handle_upload($_FILES[$field_name], $upload_overrides);
|
359 |
+
|
360 |
+
if (isset($file['error'])) {
|
361 |
+
return new WP_Error('upload_error', $file['error']);
|
362 |
+
}
|
363 |
+
$filename = isset( $_FILES[$field_name]['name'] ) ? sanitize_text_field( $_FILES[$field_name]['name'] ) : '';
|
364 |
+
$wp_filetype = wp_check_filetype($filename, null);
|
365 |
+
$attachment = array(
|
366 |
+
'post_mime_type' => $wp_filetype['type'],
|
367 |
+
'post_title' => $filename,
|
368 |
+
'post_content' => '',
|
369 |
+
'post_status' => 'inherit'
|
370 |
+
);
|
371 |
+
$attachment_id = wp_insert_attachment($attachment, $file['file']);
|
372 |
+
if (!is_wp_error($attachment_id) && is_numeric($attachment_id)) {
|
373 |
+
require_once(ABSPATH . 'wp-admin/includes/image.php');
|
374 |
+
$attachment_data = wp_generate_attachment_metadata($attachment_id, $file['file']);
|
375 |
+
wp_update_attachment_metadata($attachment_id, $attachment_data);
|
376 |
+
return trim($attachment_id);
|
377 |
+
} else {
|
378 |
+
return '';
|
379 |
+
}
|
380 |
+
}
|
front-end/login.php
CHANGED
@@ -270,8 +270,8 @@ function wppb_resend_confirmation_email() {
|
|
270 |
|
271 |
include_once(plugin_dir_path(__FILE__) . '../features/email-confirmation/email-confirmation.php');
|
272 |
|
273 |
-
if (
|
274 |
-
include_once(
|
275 |
|
276 |
global $wpdb;
|
277 |
$sql_result = $wpdb->get_row( $wpdb->prepare("SELECT * FROM " . $wpdb->base_prefix . "signups WHERE user_email = %s", sanitize_email( $_GET['email'] )), ARRAY_A );
|
@@ -579,6 +579,10 @@ function wppb_front_end_login( $atts ){
|
|
579 |
}
|
580 |
// build our form
|
581 |
$login_form .= '<div id="wppb-login-wrap" class="wppb-user-forms">';
|
|
|
|
|
|
|
|
|
582 |
$form_args['lostpassword_url'] = $lostpassword_url;
|
583 |
$login_form .= wppb_login_form( apply_filters( 'wppb_login_form_args', $form_args ) );
|
584 |
|
270 |
|
271 |
include_once(plugin_dir_path(__FILE__) . '../features/email-confirmation/email-confirmation.php');
|
272 |
|
273 |
+
if ( file_exists( WPPB_PLUGIN_DIR . '/assets/lib/class-mustache-templates/class-mustache-templates.php' ) )
|
274 |
+
include_once( WPPB_PLUGIN_DIR . '/assets/lib/class-mustache-templates/class-mustache-templates.php' );
|
275 |
|
276 |
global $wpdb;
|
277 |
$sql_result = $wpdb->get_row( $wpdb->prepare("SELECT * FROM " . $wpdb->base_prefix . "signups WHERE user_email = %s", sanitize_email( $_GET['email'] )), ARRAY_A );
|
579 |
}
|
580 |
// build our form
|
581 |
$login_form .= '<div id="wppb-login-wrap" class="wppb-user-forms">';
|
582 |
+
|
583 |
+
if ( empty( $lostpassword_url ) )
|
584 |
+
$lostpassword_url = ( !empty( $wppb_generalSettings['lost_password_page'] ) ) ? $wppb_generalSettings['lost_password_page'] : '';
|
585 |
+
|
586 |
$form_args['lostpassword_url'] = $lostpassword_url;
|
587 |
$login_form .= wppb_login_form( apply_filters( 'wppb_login_form_args', $form_args ) );
|
588 |
|
index.php
CHANGED
@@ -3,14 +3,14 @@
|
|
3 |
* Plugin Name: Profile Builder
|
4 |
* Plugin URI: https://www.cozmoslabs.com/wordpress-profile-builder/
|
5 |
* Description: Login, registration and edit profile shortcodes for the front-end. Also you can choose what fields should be displayed or add new (custom) ones both in the front-end and in the dashboard.
|
6 |
-
* Version: 3.8.
|
7 |
* Author: Cozmoslabs
|
8 |
* Author URI: https://www.cozmoslabs.com/
|
9 |
* Text Domain: profile-builder
|
10 |
* Domain Path: /translation
|
11 |
* License: GPL2
|
12 |
-
* Elementor tested up to: 3.7.
|
13 |
-
* Elementor Pro tested up to: 3.7.
|
14 |
*
|
15 |
* == Copyright ==
|
16 |
* Copyright 2014 Cozmoslabs (www.cozmoslabs.com)
|
@@ -144,7 +144,6 @@ function wppb_plugin_init() {
|
|
144 |
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/add-ons.php');
|
145 |
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/repeater-field/repeater-module.php');
|
146 |
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/custom-redirects/custom-redirects.php');
|
147 |
-
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/email-customizer/email-customizer.php');
|
148 |
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/multiple-forms/multiple-forms.php');
|
149 |
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/user-listing/userlisting.php');
|
150 |
|
@@ -154,16 +153,28 @@ function wppb_plugin_init() {
|
|
154 |
} else
|
155 |
add_shortcode('wppb-list-users', 'wppb_list_all_users_display_error');
|
156 |
|
157 |
-
|
158 |
-
if ( ( !empty( $wppb_module_settings['wppb_emailCustomizer'] ) && $wppb_module_settings['wppb_emailCustomizer'] == 'show' ) || ( !empty( $wppb_module_settings['wppb_emailCustomizerAdmin'] ) && $wppb_module_settings['wppb_emailCustomizerAdmin'] == 'show' ) )
|
159 |
-
$wppb_email_customizer_activate = 'show';
|
160 |
|
161 |
-
|
162 |
-
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/email-customizer/admin-email-customizer.php');
|
163 |
|
164 |
-
|
165 |
-
|
|
|
166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
167 |
}
|
168 |
|
169 |
include_once(WPPB_PLUGIN_DIR . '/admin/add-ons.php');
|
@@ -378,7 +389,7 @@ add_action( 'plugins_loaded', 'wppb_plugin_init' );
|
|
378 |
*
|
379 |
*
|
380 |
*/
|
381 |
-
define('PROFILE_BUILDER_VERSION', '3.8.
|
382 |
define('WPPB_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
383 |
define('WPPB_PLUGIN_URL', plugin_dir_url(__FILE__));
|
384 |
define('WPPB_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
@@ -398,8 +409,8 @@ if ( in_array( 'profile-builder-pro/index.php', $active_plugins ) || isset( $act
|
|
398 |
} elseif ( in_array( 'profile-builder-dev/index.php', $active_plugins ) || isset( $active_network_plugins['profile-builder-dev/index.php'] ) ){
|
399 |
|
400 |
define('PROFILE_BUILDER', 'Profile Builder Pro');
|
401 |
-
define('WPPB_PAID_PLUGIN_DIR',
|
402 |
-
define('WPPB_PAID_PLUGIN_URL',
|
403 |
|
404 |
} elseif ( in_array( 'profile-builder-elite/index.php', $active_plugins ) || isset( $active_network_plugins['profile-builder-elite/index.php'] ) ){
|
405 |
|
@@ -425,6 +436,8 @@ if ( in_array( 'profile-builder-pro/index.php', $active_plugins ) || isset( $act
|
|
425 |
// This needs to be loaded here since we try to plug some functions, not suited for plugins_loaded hook
|
426 |
if ( defined( 'WPPB_PAID_PLUGIN_DIR' ) && file_exists( WPPB_PAID_PLUGIN_DIR . '/front-end/extra-fields/upload/upload_helper_functions.php'))
|
427 |
include_once( WPPB_PAID_PLUGIN_DIR . '/front-end/extra-fields/upload/upload_helper_functions.php');
|
|
|
|
|
428 |
|
429 |
/* add a redirect when plugin is activated */
|
430 |
if( !function_exists( 'wppb_activate_plugin_redirect' ) ){
|
3 |
* Plugin Name: Profile Builder
|
4 |
* Plugin URI: https://www.cozmoslabs.com/wordpress-profile-builder/
|
5 |
* Description: Login, registration and edit profile shortcodes for the front-end. Also you can choose what fields should be displayed or add new (custom) ones both in the front-end and in the dashboard.
|
6 |
+
* Version: 3.8.1
|
7 |
* Author: Cozmoslabs
|
8 |
* Author URI: https://www.cozmoslabs.com/
|
9 |
* Text Domain: profile-builder
|
10 |
* Domain Path: /translation
|
11 |
* License: GPL2
|
12 |
+
* Elementor tested up to: 3.7.8
|
13 |
+
* Elementor Pro tested up to: 3.7.7
|
14 |
*
|
15 |
* == Copyright ==
|
16 |
* Copyright 2014 Cozmoslabs (www.cozmoslabs.com)
|
144 |
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/add-ons.php');
|
145 |
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/repeater-field/repeater-module.php');
|
146 |
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/custom-redirects/custom-redirects.php');
|
|
|
147 |
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/multiple-forms/multiple-forms.php');
|
148 |
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/user-listing/userlisting.php');
|
149 |
|
153 |
} else
|
154 |
add_shortcode('wppb-list-users', 'wppb_list_all_users_display_error');
|
155 |
|
156 |
+
if( !file_exists( WPPB_PLUGIN_DIR . '/features/email-customizer/email-customizer.php' ) ){
|
|
|
|
|
157 |
|
158 |
+
include_once( WPPB_PAID_PLUGIN_DIR . '/add-ons/email-customizer/email-customizer.php' );
|
|
|
159 |
|
160 |
+
$wppb_email_customizer_activate = 'hide';
|
161 |
+
if ( ( !empty( $wppb_module_settings['wppb_emailCustomizer'] ) && $wppb_module_settings['wppb_emailCustomizer'] == 'show' ) || ( !empty( $wppb_module_settings['wppb_emailCustomizerAdmin'] ) && $wppb_module_settings['wppb_emailCustomizerAdmin'] == 'show' ) )
|
162 |
+
$wppb_email_customizer_activate = 'show';
|
163 |
|
164 |
+
if ( $wppb_email_customizer_activate == 'show' )
|
165 |
+
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/email-customizer/admin-email-customizer.php');
|
166 |
+
|
167 |
+
if ( $wppb_email_customizer_activate == 'show' )
|
168 |
+
include_once(WPPB_PAID_PLUGIN_DIR . '/add-ons/email-customizer/user-email-customizer.php');
|
169 |
+
}
|
170 |
+
|
171 |
+
}
|
172 |
+
|
173 |
+
// Email Customizer is in free since 3.8.1
|
174 |
+
if( file_exists( WPPB_PLUGIN_DIR . '/features/email-customizer/email-customizer.php' ) ){
|
175 |
+
include_once( WPPB_PLUGIN_DIR . '/features/email-customizer/email-customizer.php' );
|
176 |
+
include_once( WPPB_PLUGIN_DIR . '/features/email-customizer/admin-email-customizer.php' );
|
177 |
+
include_once( WPPB_PLUGIN_DIR . '/features/email-customizer/user-email-customizer.php' );
|
178 |
}
|
179 |
|
180 |
include_once(WPPB_PLUGIN_DIR . '/admin/add-ons.php');
|
389 |
*
|
390 |
*
|
391 |
*/
|
392 |
+
define('PROFILE_BUILDER_VERSION', '3.8.1' );
|
393 |
define('WPPB_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
394 |
define('WPPB_PLUGIN_URL', plugin_dir_url(__FILE__));
|
395 |
define('WPPB_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
409 |
} elseif ( in_array( 'profile-builder-dev/index.php', $active_plugins ) || isset( $active_network_plugins['profile-builder-dev/index.php'] ) ){
|
410 |
|
411 |
define('PROFILE_BUILDER', 'Profile Builder Pro');
|
412 |
+
define('WPPB_PAID_PLUGIN_DIR', WP_PLUGIN_DIR . '/profile-builder-dev' );
|
413 |
+
define('WPPB_PAID_PLUGIN_URL', plugins_url() . '/profile-builder-dev/' );
|
414 |
|
415 |
} elseif ( in_array( 'profile-builder-elite/index.php', $active_plugins ) || isset( $active_network_plugins['profile-builder-elite/index.php'] ) ){
|
416 |
|
436 |
// This needs to be loaded here since we try to plug some functions, not suited for plugins_loaded hook
|
437 |
if ( defined( 'WPPB_PAID_PLUGIN_DIR' ) && file_exists( WPPB_PAID_PLUGIN_DIR . '/front-end/extra-fields/upload/upload_helper_functions.php'))
|
438 |
include_once( WPPB_PAID_PLUGIN_DIR . '/front-end/extra-fields/upload/upload_helper_functions.php');
|
439 |
+
else if ( file_exists( WPPB_PLUGIN_DIR . '/front-end/default-fields/upload/upload_helper_functions.php' ) )
|
440 |
+
include_once( WPPB_PLUGIN_DIR . '/front-end/default-fields/upload/upload_helper_functions.php');
|
441 |
|
442 |
/* add a redirect when plugin is activated */
|
443 |
if( !function_exists( 'wppb_activate_plugin_redirect' ) ){
|
readme.txt
CHANGED
@@ -4,7 +4,7 @@ Donate link: http://www.cozmoslabs.com/wordpress-profile-builder/
|
|
4 |
Tags: user registration, user profile, registration, profile, user registration form, user fields, edit profile, user custom fields, front-end login, front-end edit profile, front-end user registration, email confirmation, login form, content restriction, restrict content
|
5 |
Requires at least: 3.1
|
6 |
Tested up to: 6.0
|
7 |
-
Stable tag: 3.8.
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
@@ -38,7 +38,9 @@ You can use the following shortcode list to display the forms:
|
|
38 |
== Profile Builder Features ==
|
39 |
|
40 |
* drag & drop to reorder user profile fields
|
|
|
41 |
* enable **Email Confirmation** (on registration users will receive a notification to [confirm their email address](https://www.cozmoslabs.com/14722-wordpress-email-confirmation/))
|
|
|
42 |
* choose between login with **only Username, Email** or **both**
|
43 |
* enforce [WordPress password requirements](https://www.cozmoslabs.com/28998-wordpress-minimum-password-length-strength/) by setting up a **minimum password length** and **minimum password strength** (using the default WordPress password strength meter)
|
44 |
* assign users a specific role at registration (using **[wppb-register role="desired_role"]** shortcode argument for the register form)
|
@@ -62,8 +64,7 @@ You can use the following shortcode list to display the forms:
|
|
62 |
|
63 |
The [Pro version](http://www.cozmoslabs.com/wordpress-profile-builder/?utm_source=wp.org&utm_medium=pb-description-page&utm_campaign=PBFree) has the following extra features:
|
64 |
|
65 |
-
* Create Extra User Fields (
|
66 |
-
* Add Avatar Upload for users
|
67 |
* Support for Conditional Fields
|
68 |
* Front-end User Listing (fully customizable, sorting included)
|
69 |
* Create Multiple User Listings
|
@@ -72,7 +73,6 @@ The [Pro version](http://www.cozmoslabs.com/wordpress-profile-builder/?utm_sourc
|
|
72 |
* Multiple Registration Forms (set up [multiple registration forms](https://www.cozmoslabs.com/docs/profile-builder-2/add-ons/multiple-registration-forms/?utm_source=wp.org&utm_medium=pb-description-page&utm_campaign=PBFree) with different profile fields for certain user roles)
|
73 |
* Multiple Edit Profile Forms
|
74 |
* Admin Approval ([approve new users from dashboard or via email](https://www.cozmoslabs.com/112321-approve-users-from-admin-email-using-profile-builder/))
|
75 |
-
* Email Customizer (Personalize all emails sent to your users or admins; customize default WordPress registration email)
|
76 |
* Advanced Add-ons (e.g. custom redirects, user listing, multiple registration forms etc.)
|
77 |
* Access to support and documentation
|
78 |
* 1 Year of Updates / Priority Support
|
@@ -177,6 +177,12 @@ This plugin adds/removes user fields in the front-end. Both default and extra pr
|
|
177 |
15. Edit or Add New User Role
|
178 |
|
179 |
== Changelog ==
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
= 3.8.0 =
|
181 |
* Feature: added the possibility to restrict the purchase of WooCommerce Products based on User Roles
|
182 |
* Misc: fix some cases where HTML was displaying in the back-end
|
4 |
Tags: user registration, user profile, registration, profile, user registration form, user fields, edit profile, user custom fields, front-end login, front-end edit profile, front-end user registration, email confirmation, login form, content restriction, restrict content
|
5 |
Requires at least: 3.1
|
6 |
Tested up to: 6.0
|
7 |
+
Stable tag: 3.8.1
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
38 |
== Profile Builder Features ==
|
39 |
|
40 |
* drag & drop to reorder user profile fields
|
41 |
+
* add Avatar Upload field for users to manage their avatar on your website
|
42 |
* enable **Email Confirmation** (on registration users will receive a notification to [confirm their email address](https://www.cozmoslabs.com/14722-wordpress-email-confirmation/))
|
43 |
+
* Email Customizer - Personalize all emails sent to your users or admins; customize default WordPress registration email
|
44 |
* choose between login with **only Username, Email** or **both**
|
45 |
* enforce [WordPress password requirements](https://www.cozmoslabs.com/28998-wordpress-minimum-password-length-strength/) by setting up a **minimum password length** and **minimum password strength** (using the default WordPress password strength meter)
|
46 |
* assign users a specific role at registration (using **[wppb-register role="desired_role"]** shortcode argument for the register form)
|
64 |
|
65 |
The [Pro version](http://www.cozmoslabs.com/wordpress-profile-builder/?utm_source=wp.org&utm_medium=pb-description-page&utm_campaign=PBFree) has the following extra features:
|
66 |
|
67 |
+
* Create Extra User Fields (Hidden Input, Agree to Terms Checkbox WYSIWYG, Upload fields, User Role Select, Country Select, Timezone Select Upload, Map, HTML, Phone, Datepicker, Timepicker, Colorpicker, Custom Validation field, Currency Select, CPT Select)
|
|
|
68 |
* Support for Conditional Fields
|
69 |
* Front-end User Listing (fully customizable, sorting included)
|
70 |
* Create Multiple User Listings
|
73 |
* Multiple Registration Forms (set up [multiple registration forms](https://www.cozmoslabs.com/docs/profile-builder-2/add-ons/multiple-registration-forms/?utm_source=wp.org&utm_medium=pb-description-page&utm_campaign=PBFree) with different profile fields for certain user roles)
|
74 |
* Multiple Edit Profile Forms
|
75 |
* Admin Approval ([approve new users from dashboard or via email](https://www.cozmoslabs.com/112321-approve-users-from-admin-email-using-profile-builder/))
|
|
|
76 |
* Advanced Add-ons (e.g. custom redirects, user listing, multiple registration forms etc.)
|
77 |
* Access to support and documentation
|
78 |
* 1 Year of Updates / Priority Support
|
177 |
15. Edit or Add New User Role
|
178 |
|
179 |
== Changelog ==
|
180 |
+
= 3.8.1 =
|
181 |
+
* Feature: added new field types: Heading, Input, Textarea, Select, Select2, Checkbox, Radio
|
182 |
+
* Feature: added Avatar field which lets your users manage their avatar on your website from the Profile Builder forms
|
183 |
+
* Feature: added Email Customizer feature which lets you edit all the emails that the plugin sends with custom content and tags for the defined fields
|
184 |
+
* Fix: issue with licence activation not working on multisite
|
185 |
+
|
186 |
= 3.8.0 =
|
187 |
* Feature: added the possibility to restrict the purchase of WooCommerce Products based on User Roles
|
188 |
* Misc: fix some cases where HTML was displaying in the back-end
|
translation/profile-builder.catalog.php
CHANGED
@@ -13,8 +13,6 @@
|
|
13 |
<?php __("Allow different user roles to edit their specific information. Set up multiple edit-profile forms with different fields for certain user roles.", "profile-builder"); ?>
|
14 |
<?php __("User Listing", "profile-builder"); ?>
|
15 |
<?php __("Easy to edit templates for listing your users as well as creating single user pages.", "profile-builder"); ?>
|
16 |
-
<?php __("Email Customizer", "profile-builder"); ?>
|
17 |
-
<?php __("Simple to use customization of the WordPress Registration Emails", "profile-builder"); ?>
|
18 |
<?php __("Custom Redirects", "profile-builder"); ?>
|
19 |
<?php __("Redirect users after login, after they first register or when they try to access the default WordPress dashboard, login, lost password and registration forms.", "profile-builder"); ?>
|
20 |
<?php __("Repeater Fields", "profile-builder"); ?>
|
@@ -120,6 +118,8 @@
|
|
120 |
<?php __("Make sure users sign up with genuine emails. On registration users will receive a notification to confirm their email address.", "profile-builder"); ?>
|
121 |
<?php __("Content Restriction", "profile-builder"); ?>
|
122 |
<?php __("Restrict users from accessing certain pages, posts or custom post types based on user role or logged-in status.", "profile-builder"); ?>
|
|
|
|
|
123 |
<?php __("Minimum Password Length and Strength Meter", "profile-builder"); ?>
|
124 |
<?php __("Eliminate weak passwords altogether by setting a minimum password length and enforcing a certain password strength.", "profile-builder"); ?>
|
125 |
<?php __("Login with Email or Username", "profile-builder"); ?>
|
@@ -130,24 +130,21 @@
|
|
130 |
<?php __("With Extra Profile Fields you can create the exact registration form your project needs.", "profile-builder"); ?>
|
131 |
<?php __("Get started with extra fields", "profile-builder"); ?>
|
132 |
<?php __("Extra Profile Fields are available in Basic or PRO versions", "profile-builder"); ?>
|
133 |
-
<?php __("Avatar Upload", "profile-builder"); ?>
|
134 |
<?php __("Generic Uploads", "profile-builder"); ?>
|
135 |
<?php __("Agree To Terms Checkbox", "profile-builder"); ?>
|
136 |
<?php __("Datepicker", "profile-builder"); ?>
|
137 |
<?php __("Timepicker", "profile-builder"); ?>
|
138 |
<?php __("Colorpicker", "profile-builder"); ?>
|
139 |
-
<?php __("reCAPTCHA", "profile-builder"); ?>
|
140 |
<?php __("Country Select", "profile-builder"); ?>
|
141 |
<?php __("Currency Select", "profile-builder"); ?>
|
142 |
<?php __("Timezone Select", "profile-builder"); ?>
|
143 |
-
<?php __("
|
|
|
|
|
|
|
144 |
<?php __("Number", "profile-builder"); ?>
|
145 |
-
<?php __("Checkbox", "profile-builder"); ?>
|
146 |
-
<?php __("Select", "profile-builder"); ?>
|
147 |
-
<?php __("Radio Buttons", "profile-builder"); ?>
|
148 |
-
<?php __("Textarea", "profile-builder"); ?>
|
149 |
<?php __("Validation", "profile-builder"); ?>
|
150 |
-
<?php __("
|
151 |
<?php __("HTML", "profile-builder"); ?>
|
152 |
<?php __("Powerful Add-ons (**)", "profile-builder"); ?>
|
153 |
<?php __("Everything you will need to manage your users is probably already available using the Pro Add-ons.", "profile-builder"); ?>
|
@@ -155,7 +152,6 @@
|
|
155 |
<?php __("Find out more about PRO Modules", "profile-builder"); ?>
|
156 |
<?php __("To create a page containing the users registered to this current site/blog, insert the following shortcode in a page of your chosing: %s.", "profile-builder"); ?>
|
157 |
<?php __("Easy to edit templates for listing your website users as well as creating single user pages. Shortcode based, offering many options to customize your listings.", "profile-builder"); ?>
|
158 |
-
<?php __("Personalize all emails sent to your users or admins. On registration, email confirmation, admin approval / un-approval.", "profile-builder"); ?>
|
159 |
<?php __("Keep your users out of the WordPress dashboard, redirect them to the front-page after login or registration, everything is just a few clicks away.", "profile-builder"); ?>
|
160 |
<?php __("Set up multiple registration forms with different fields for certain user roles. Capture different information from different types of users.", "profile-builder"); ?>
|
161 |
<?php __("Multiple Edit-profile Forms", "profile-builder"); ?>
|
@@ -220,6 +216,9 @@
|
|
220 |
<?php __("Enter the minimum characters the password should have. Leave empty for no minimum limit", "profile-builder"); ?>
|
221 |
<?php __("Minimum Password Strength:", "profile-builder"); ?>
|
222 |
<?php __("Disabled", "profile-builder"); ?>
|
|
|
|
|
|
|
223 |
<?php __("Form Fields", "profile-builder"); ?>
|
224 |
<?php __("Manage Form Fields", "profile-builder"); ?>
|
225 |
<?php __("Standard", "profile-builder"); ?>
|
@@ -1125,7 +1124,6 @@
|
|
1125 |
<?php __("You do not have permission to view this user list.", "profile-builder"); ?>
|
1126 |
<?php __("You do not have the required user role to view this user list.", "profile-builder"); ?>
|
1127 |
<?php __("User not found", "profile-builder"); ?>
|
1128 |
-
<?php __("None", "profile-builder"); ?>
|
1129 |
<?php __("Jabber", "profile-builder"); ?>
|
1130 |
<?php __("Yim", "profile-builder"); ?>
|
1131 |
<?php __("Aim", "profile-builder"); ?>
|
@@ -1366,7 +1364,6 @@
|
|
1366 |
<?php __("State / County", "profile-builder"); ?>
|
1367 |
<?php __("Postcode / Zip", "profile-builder"); ?>
|
1368 |
<?php __("Email Address", "profile-builder"); ?>
|
1369 |
-
<?php __("Phone", "profile-builder"); ?>
|
1370 |
<?php __("Ship to a different address?", "profile-builder"); ?>
|
1371 |
<?php __("WooCommerce needs to be installed and activated for Profile Builder - WooCommerce Sync Add-on to work!", "profile-builder"); ?>
|
1372 |
<?php __("Address line 2", "profile-builder"); ?>
|
@@ -1396,7 +1393,7 @@
|
|
1396 |
<?php __("Displays customer shipping fields in front-end. ", "profile-builder"); ?>
|
1397 |
<?php __("Choose Register form to display on My Account page:", "profile-builder"); ?>
|
1398 |
<?php __("Default Register", "profile-builder"); ?>
|
1399 |
-
<?php __("Select which Profile Builder Register form to display on My Account page from WooCommerce.
|
1400 |
<?php __("Choose Edit Profile form to display on My Account page:", "profile-builder"); ?>
|
1401 |
<?php __("Select which Profile Builder Edit-profile form to display on My Account page from WooCommerce.", "profile-builder"); ?>
|
1402 |
<?php __("CSS Class", "profile-builder"); ?>
|
@@ -1813,7 +1810,10 @@
|
|
1813 |
<?php __("Add Entry", "profile-builder"); ?>
|
1814 |
<?php __("Delete this item", "profile-builder"); ?>
|
1815 |
<?php __("Please enter a value for the required field ", "profile-builder"); ?>
|
|
|
|
|
1816 |
<?php __("Profile Builder Forms", "profile-builder"); ?>
|
|
|
1817 |
<?php __("Yes, I'd like to create a new site", "profile-builder"); ?>
|
1818 |
<?php __("Your site url will look like this:<br>", "profile-builder"); ?>
|
1819 |
<?php __("Site URL slug", "profile-builder"); ?>
|
@@ -1834,6 +1834,11 @@
|
|
1834 |
<?php __("For security reasons, you must pass the remote ip to reCAPTCHA!", "profile-builder"); ?>
|
1835 |
<?php __("To use reCAPTCHA you must get an API public key from:", "profile-builder"); ?>
|
1836 |
<?php __("Click the BACK button on your browser, and try again.", "profile-builder"); ?>
|
|
|
|
|
|
|
|
|
|
|
1837 |
<?php __("As an administrator you cannot change your role.", "profile-builder"); ?>
|
1838 |
<?php __("Only administrators can see this field on edit profile forms.", "profile-builder"); ?>
|
1839 |
<?php __("You cannot register this user role", "profile-builder"); ?>
|
@@ -1841,7 +1846,6 @@
|
|
1841 |
<?php __("This username is invalid because it uses illegal characters.", "profile-builder"); ?>
|
1842 |
<?php __("Please enter a valid username.", "profile-builder"); ?>
|
1843 |
<?php __("This username is already reserved to be used soon.", "profile-builder"); ?>
|
1844 |
-
<?php __("Remove", "profile-builder"); ?>
|
1845 |
<?php __("The hidden Honeypot field must be empty.", "profile-builder"); ?>
|
1846 |
<?php __("You must enter a valid URL.", "profile-builder"); ?>
|
1847 |
<?php __("Please add the Google Maps API key for this field.", "profile-builder"); ?>
|
@@ -1852,11 +1856,6 @@
|
|
1852 |
<?php __('Value must be less than or equal to %1$s', 'profile-builder' ); ?>
|
1853 |
<?php __("Required phone number format: ", "profile-builder"); ?>
|
1854 |
<?php __("...Choose", "profile-builder"); ?>
|
1855 |
-
<?php __("Select File", "profile-builder"); ?>
|
1856 |
-
<?php __("Upload ", "profile-builder"); ?>
|
1857 |
-
<?php __("Files must be smaller than ", "profile-builder"); ?>
|
1858 |
-
<?php __("Sorry, you cannot upload this file type for this field.", "profile-builder"); ?>
|
1859 |
-
<?php __("An error occurred, please try again later.", "profile-builder"); ?>
|
1860 |
<?php __("This display name is already in use. Please choose another one.", "profile-builder"); ?>
|
1861 |
<?php __("Email address change request for %s", "profile-builder"); ?>
|
1862 |
<?php __('Someone requested to change the email address for your account.<br/>If this was a mistake, just ignore this email and nothing will happen.<br/>To update your account email address to the one requested (%1$s), visit the following link: %2$s', 'profile-builder' ); ?>
|
13 |
<?php __("Allow different user roles to edit their specific information. Set up multiple edit-profile forms with different fields for certain user roles.", "profile-builder"); ?>
|
14 |
<?php __("User Listing", "profile-builder"); ?>
|
15 |
<?php __("Easy to edit templates for listing your users as well as creating single user pages.", "profile-builder"); ?>
|
|
|
|
|
16 |
<?php __("Custom Redirects", "profile-builder"); ?>
|
17 |
<?php __("Redirect users after login, after they first register or when they try to access the default WordPress dashboard, login, lost password and registration forms.", "profile-builder"); ?>
|
18 |
<?php __("Repeater Fields", "profile-builder"); ?>
|
118 |
<?php __("Make sure users sign up with genuine emails. On registration users will receive a notification to confirm their email address.", "profile-builder"); ?>
|
119 |
<?php __("Content Restriction", "profile-builder"); ?>
|
120 |
<?php __("Restrict users from accessing certain pages, posts or custom post types based on user role or logged-in status.", "profile-builder"); ?>
|
121 |
+
<?php __("Email Customizer", "profile-builder"); ?>
|
122 |
+
<?php __("Personalize all emails sent to your users or admins. On registration, email confirmation, admin approval / un-approval.", "profile-builder"); ?>
|
123 |
<?php __("Minimum Password Length and Strength Meter", "profile-builder"); ?>
|
124 |
<?php __("Eliminate weak passwords altogether by setting a minimum password length and enforcing a certain password strength.", "profile-builder"); ?>
|
125 |
<?php __("Login with Email or Username", "profile-builder"); ?>
|
130 |
<?php __("With Extra Profile Fields you can create the exact registration form your project needs.", "profile-builder"); ?>
|
131 |
<?php __("Get started with extra fields", "profile-builder"); ?>
|
132 |
<?php __("Extra Profile Fields are available in Basic or PRO versions", "profile-builder"); ?>
|
|
|
133 |
<?php __("Generic Uploads", "profile-builder"); ?>
|
134 |
<?php __("Agree To Terms Checkbox", "profile-builder"); ?>
|
135 |
<?php __("Datepicker", "profile-builder"); ?>
|
136 |
<?php __("Timepicker", "profile-builder"); ?>
|
137 |
<?php __("Colorpicker", "profile-builder"); ?>
|
|
|
138 |
<?php __("Country Select", "profile-builder"); ?>
|
139 |
<?php __("Currency Select", "profile-builder"); ?>
|
140 |
<?php __("Timezone Select", "profile-builder"); ?>
|
141 |
+
<?php __("Map", "profile-builder"); ?>
|
142 |
+
<?php __("Select 2 Multiple", "profile-builder"); ?>
|
143 |
+
<?php __("Phone", "profile-builder"); ?>
|
144 |
+
<?php __("Hidden Input", "profile-builder"); ?>
|
145 |
<?php __("Number", "profile-builder"); ?>
|
|
|
|
|
|
|
|
|
146 |
<?php __("Validation", "profile-builder"); ?>
|
147 |
+
<?php __("Select CPT", "profile-builder"); ?>
|
148 |
<?php __("HTML", "profile-builder"); ?>
|
149 |
<?php __("Powerful Add-ons (**)", "profile-builder"); ?>
|
150 |
<?php __("Everything you will need to manage your users is probably already available using the Pro Add-ons.", "profile-builder"); ?>
|
152 |
<?php __("Find out more about PRO Modules", "profile-builder"); ?>
|
153 |
<?php __("To create a page containing the users registered to this current site/blog, insert the following shortcode in a page of your chosing: %s.", "profile-builder"); ?>
|
154 |
<?php __("Easy to edit templates for listing your website users as well as creating single user pages. Shortcode based, offering many options to customize your listings.", "profile-builder"); ?>
|
|
|
155 |
<?php __("Keep your users out of the WordPress dashboard, redirect them to the front-page after login or registration, everything is just a few clicks away.", "profile-builder"); ?>
|
156 |
<?php __("Set up multiple registration forms with different fields for certain user roles. Capture different information from different types of users.", "profile-builder"); ?>
|
157 |
<?php __("Multiple Edit-profile Forms", "profile-builder"); ?>
|
216 |
<?php __("Enter the minimum characters the password should have. Leave empty for no minimum limit", "profile-builder"); ?>
|
217 |
<?php __("Minimum Password Strength:", "profile-builder"); ?>
|
218 |
<?php __("Disabled", "profile-builder"); ?>
|
219 |
+
<?php __("Select Recover Password Page:", "profile-builder"); ?>
|
220 |
+
<?php __("None", "profile-builder"); ?>
|
221 |
+
<?php __('Select the page which contains the %1$s[wppb-recover-password]%2$s shortcode.', 'profile-builder' ); ?>
|
222 |
<?php __("Form Fields", "profile-builder"); ?>
|
223 |
<?php __("Manage Form Fields", "profile-builder"); ?>
|
224 |
<?php __("Standard", "profile-builder"); ?>
|
1124 |
<?php __("You do not have permission to view this user list.", "profile-builder"); ?>
|
1125 |
<?php __("You do not have the required user role to view this user list.", "profile-builder"); ?>
|
1126 |
<?php __("User not found", "profile-builder"); ?>
|
|
|
1127 |
<?php __("Jabber", "profile-builder"); ?>
|
1128 |
<?php __("Yim", "profile-builder"); ?>
|
1129 |
<?php __("Aim", "profile-builder"); ?>
|
1364 |
<?php __("State / County", "profile-builder"); ?>
|
1365 |
<?php __("Postcode / Zip", "profile-builder"); ?>
|
1366 |
<?php __("Email Address", "profile-builder"); ?>
|
|
|
1367 |
<?php __("Ship to a different address?", "profile-builder"); ?>
|
1368 |
<?php __("WooCommerce needs to be installed and activated for Profile Builder - WooCommerce Sync Add-on to work!", "profile-builder"); ?>
|
1369 |
<?php __("Address line 2", "profile-builder"); ?>
|
1393 |
<?php __("Displays customer shipping fields in front-end. ", "profile-builder"); ?>
|
1394 |
<?php __("Choose Register form to display on My Account page:", "profile-builder"); ?>
|
1395 |
<?php __("Default Register", "profile-builder"); ?>
|
1396 |
+
<?php __("Select which Profile Builder Register form to display on My Account page from WooCommerce. %s This will also add the Profile Builder Login form to MyAccount page.", "profile-builder"); ?>
|
1397 |
<?php __("Choose Edit Profile form to display on My Account page:", "profile-builder"); ?>
|
1398 |
<?php __("Select which Profile Builder Edit-profile form to display on My Account page from WooCommerce.", "profile-builder"); ?>
|
1399 |
<?php __("CSS Class", "profile-builder"); ?>
|
1810 |
<?php __("Add Entry", "profile-builder"); ?>
|
1811 |
<?php __("Delete this item", "profile-builder"); ?>
|
1812 |
<?php __("Please enter a value for the required field ", "profile-builder"); ?>
|
1813 |
+
<?php __("This field is available in our paid plans.", "profile-builder"); ?>
|
1814 |
+
<?php __("Install the free Paid Member Subscriptions plugin to get access this field.", "profile-builder"); ?>
|
1815 |
<?php __("Profile Builder Forms", "profile-builder"); ?>
|
1816 |
+
<?php __("Remove", "profile-builder"); ?>
|
1817 |
<?php __("Yes, I'd like to create a new site", "profile-builder"); ?>
|
1818 |
<?php __("Your site url will look like this:<br>", "profile-builder"); ?>
|
1819 |
<?php __("Site URL slug", "profile-builder"); ?>
|
1834 |
<?php __("For security reasons, you must pass the remote ip to reCAPTCHA!", "profile-builder"); ?>
|
1835 |
<?php __("To use reCAPTCHA you must get an API public key from:", "profile-builder"); ?>
|
1836 |
<?php __("Click the BACK button on your browser, and try again.", "profile-builder"); ?>
|
1837 |
+
<?php __("Files must be smaller than ", "profile-builder"); ?>
|
1838 |
+
<?php __("Sorry, you cannot upload this file type for this field.", "profile-builder"); ?>
|
1839 |
+
<?php __("An error occurred, please try again later.", "profile-builder"); ?>
|
1840 |
+
<?php __("Select File", "profile-builder"); ?>
|
1841 |
+
<?php __("Upload ", "profile-builder"); ?>
|
1842 |
<?php __("As an administrator you cannot change your role.", "profile-builder"); ?>
|
1843 |
<?php __("Only administrators can see this field on edit profile forms.", "profile-builder"); ?>
|
1844 |
<?php __("You cannot register this user role", "profile-builder"); ?>
|
1846 |
<?php __("This username is invalid because it uses illegal characters.", "profile-builder"); ?>
|
1847 |
<?php __("Please enter a valid username.", "profile-builder"); ?>
|
1848 |
<?php __("This username is already reserved to be used soon.", "profile-builder"); ?>
|
|
|
1849 |
<?php __("The hidden Honeypot field must be empty.", "profile-builder"); ?>
|
1850 |
<?php __("You must enter a valid URL.", "profile-builder"); ?>
|
1851 |
<?php __("Please add the Google Maps API key for this field.", "profile-builder"); ?>
|
1856 |
<?php __('Value must be less than or equal to %1$s', 'profile-builder' ); ?>
|
1857 |
<?php __("Required phone number format: ", "profile-builder"); ?>
|
1858 |
<?php __("...Choose", "profile-builder"); ?>
|
|
|
|
|
|
|
|
|
|
|
1859 |
<?php __("This display name is already in use. Please choose another one.", "profile-builder"); ?>
|
1860 |
<?php __("Email address change request for %s", "profile-builder"); ?>
|
1861 |
<?php __('Someone requested to change the email address for your account.<br/>If this was a mistake, just ignore this email and nothing will happen.<br/>To update your account email address to the one requested (%1$s), visit the following link: %2$s', 'profile-builder' ); ?>
|
translation/profile-builder.pot
CHANGED
@@ -13,11 +13,11 @@ msgstr ""
|
|
13 |
"X-Poedit-SourceCharset: UTF-8\n"
|
14 |
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
15 |
|
16 |
-
#: index.php:
|
17 |
msgid "To enable updates, your licence needs to be renewed. Please go to the <a href=\"%s\">Cozmoslabs Account</a> page and login to renew."
|
18 |
msgstr ""
|
19 |
|
20 |
-
#: index.php:
|
21 |
msgid "To enable updates, please enter your serial number on the <a href=\"%s\">Register Version</a> page. If you don't have a serial number, please see <a href=\"%s\" target=\"_blank\">details & pricing</a>."
|
22 |
msgstr ""
|
23 |
|
@@ -45,7 +45,7 @@ msgstr ""
|
|
45 |
msgid "These Add-ons are available with the Pro and Unlimited license"
|
46 |
msgstr ""
|
47 |
|
48 |
-
#: admin/add-ons.php:45, admin/basic-info.php:
|
49 |
msgid "Multiple Registration Forms"
|
50 |
msgstr ""
|
51 |
|
@@ -57,11 +57,11 @@ msgstr ""
|
|
57 |
msgid "Multiple Edit Profile Forms"
|
58 |
msgstr ""
|
59 |
|
60 |
-
#: admin/add-ons.php:53, admin/basic-info.php:
|
61 |
msgid "Allow different user roles to edit their specific information. Set up multiple edit-profile forms with different fields for certain user roles."
|
62 |
msgstr ""
|
63 |
|
64 |
-
#: admin/add-ons.php:59, admin/basic-info.php:
|
65 |
msgid "User Listing"
|
66 |
msgstr ""
|
67 |
|
@@ -69,15 +69,7 @@ msgstr ""
|
|
69 |
msgid "Easy to edit templates for listing your users as well as creating single user pages."
|
70 |
msgstr ""
|
71 |
|
72 |
-
#: admin/add-ons.php:
|
73 |
-
msgid "Email Customizer"
|
74 |
-
msgstr ""
|
75 |
-
|
76 |
-
#: admin/add-ons.php:67
|
77 |
-
msgid "Simple to use customization of the WordPress Registration Emails"
|
78 |
-
msgstr ""
|
79 |
-
|
80 |
-
#: admin/add-ons.php:73, admin/basic-info.php:174, add-ons/custom-redirects/custom_redirects_admin.php:33, add-ons/custom-redirects/custom_redirects_admin.php:34
|
81 |
msgid "Custom Redirects"
|
82 |
msgstr ""
|
83 |
|
@@ -85,7 +77,7 @@ msgstr ""
|
|
85 |
msgid "Redirect users after login, after they first register or when they try to access the default WordPress dashboard, login, lost password and registration forms."
|
86 |
msgstr ""
|
87 |
|
88 |
-
#: admin/add-ons.php:80, admin/basic-info.php:
|
89 |
msgid "Repeater Fields"
|
90 |
msgstr ""
|
91 |
|
@@ -317,11 +309,11 @@ msgstr ""
|
|
317 |
msgid "Show"
|
318 |
msgstr ""
|
319 |
|
320 |
-
#: admin/admin-bar.php:81, add-ons/user-listing/userlisting.php:
|
321 |
msgid "Hide"
|
322 |
msgstr ""
|
323 |
|
324 |
-
#: admin/admin-bar.php:92, admin/general-settings.php:
|
325 |
msgid "Save Changes"
|
326 |
msgstr ""
|
327 |
|
@@ -345,19 +337,19 @@ msgstr ""
|
|
345 |
msgid "<strong>ERROR</strong>: The password must have the minimum length of %s characters"
|
346 |
msgstr ""
|
347 |
|
348 |
-
#: admin/admin-functions.php:139, admin/general-settings.php:
|
349 |
msgid "Very weak"
|
350 |
msgstr ""
|
351 |
|
352 |
-
#: admin/admin-functions.php:139, admin/general-settings.php:
|
353 |
msgid "Weak"
|
354 |
msgstr ""
|
355 |
|
356 |
-
#: admin/admin-functions.php:139, admin/general-settings.php:
|
357 |
msgid "Medium"
|
358 |
msgstr ""
|
359 |
|
360 |
-
#: admin/admin-functions.php:139, admin/general-settings.php:
|
361 |
msgid "Strong"
|
362 |
msgstr ""
|
363 |
|
@@ -373,7 +365,7 @@ msgstr ""
|
|
373 |
msgid "Add Field"
|
374 |
msgstr ""
|
375 |
|
376 |
-
#: admin/admin-functions.php:212, admin/general-settings.php:
|
377 |
msgid "Settings"
|
378 |
msgstr ""
|
379 |
|
@@ -393,7 +385,7 @@ msgstr ""
|
|
393 |
msgid "See details"
|
394 |
msgstr ""
|
395 |
|
396 |
-
#: admin/admin-functions.php:260, admin/review.php:94, add-ons/user-listing/userlisting.php:
|
397 |
msgid "Dismiss this notice."
|
398 |
msgstr ""
|
399 |
|
@@ -497,171 +489,163 @@ msgstr ""
|
|
497 |
msgid "Restrict users from accessing certain pages, posts or custom post types based on user role or logged-in status."
|
498 |
msgstr ""
|
499 |
|
500 |
-
#: admin/basic-info.php:91
|
501 |
-
msgid "
|
502 |
msgstr ""
|
503 |
|
504 |
#: admin/basic-info.php:92
|
505 |
-
msgid "
|
506 |
msgstr ""
|
507 |
|
508 |
#: admin/basic-info.php:95
|
509 |
-
msgid "
|
510 |
msgstr ""
|
511 |
|
512 |
#: admin/basic-info.php:96
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
513 |
msgid "Allow users to log in with their email or username when accessing your site."
|
514 |
msgstr ""
|
515 |
|
516 |
-
#: admin/basic-info.php:
|
517 |
msgid "Roles Editor"
|
518 |
msgstr ""
|
519 |
|
520 |
-
#: admin/basic-info.php:
|
521 |
msgid "Add, remove, clone and edit roles and also capabilities for these roles."
|
522 |
msgstr ""
|
523 |
|
524 |
-
#: admin/basic-info.php:
|
525 |
msgid "Customize Your Forms The Way You Want (*)"
|
526 |
msgstr ""
|
527 |
|
528 |
-
#: admin/basic-info.php:
|
529 |
msgid "With Extra Profile Fields you can create the exact registration form your project needs."
|
530 |
msgstr ""
|
531 |
|
532 |
-
#: admin/basic-info.php:
|
533 |
msgid "Get started with extra fields"
|
534 |
msgstr ""
|
535 |
|
536 |
-
#: admin/basic-info.php:
|
537 |
msgid "Extra Profile Fields are available in Basic or PRO versions"
|
538 |
msgstr ""
|
539 |
|
540 |
-
#: admin/basic-info.php:
|
541 |
-
msgid "Avatar Upload"
|
542 |
-
msgstr ""
|
543 |
-
|
544 |
-
#: admin/basic-info.php:122
|
545 |
msgid "Generic Uploads"
|
546 |
msgstr ""
|
547 |
|
548 |
-
#: admin/basic-info.php:
|
549 |
msgid "Agree To Terms Checkbox"
|
550 |
msgstr ""
|
551 |
|
552 |
-
#: admin/basic-info.php:
|
553 |
msgid "Datepicker"
|
554 |
msgstr ""
|
555 |
|
556 |
-
#: admin/basic-info.php:
|
557 |
msgid "Timepicker"
|
558 |
msgstr ""
|
559 |
|
560 |
-
#: admin/basic-info.php:
|
561 |
msgid "Colorpicker"
|
562 |
msgstr ""
|
563 |
|
564 |
-
#: admin/basic-info.php:
|
565 |
-
msgid "reCAPTCHA"
|
566 |
-
msgstr ""
|
567 |
-
|
568 |
-
#: admin/basic-info.php:128
|
569 |
msgid "Country Select"
|
570 |
msgstr ""
|
571 |
|
572 |
-
#: admin/basic-info.php:
|
573 |
msgid "Currency Select"
|
574 |
msgstr ""
|
575 |
|
576 |
-
#: admin/basic-info.php:
|
577 |
msgid "Timezone Select"
|
578 |
msgstr ""
|
579 |
|
580 |
-
#: admin/basic-info.php:134
|
581 |
-
msgid "Input / Hidden Input"
|
582 |
-
msgstr ""
|
583 |
-
|
584 |
-
#: admin/basic-info.php:135
|
585 |
-
msgid "Number"
|
586 |
-
msgstr ""
|
587 |
-
|
588 |
#: admin/basic-info.php:136
|
589 |
-
msgid "
|
590 |
msgstr ""
|
591 |
|
592 |
#: admin/basic-info.php:137
|
593 |
-
msgid "Select"
|
594 |
msgstr ""
|
595 |
|
596 |
-
#: admin/basic-info.php:138
|
597 |
-
msgid "
|
598 |
msgstr ""
|
599 |
|
600 |
#: admin/basic-info.php:139
|
601 |
-
msgid "
|
602 |
msgstr ""
|
603 |
|
604 |
#: admin/basic-info.php:140
|
605 |
-
msgid "
|
606 |
msgstr ""
|
607 |
|
608 |
#: admin/basic-info.php:141
|
609 |
-
msgid "
|
610 |
msgstr ""
|
611 |
|
612 |
#: admin/basic-info.php:142
|
|
|
|
|
|
|
|
|
613 |
msgid "HTML"
|
614 |
msgstr ""
|
615 |
|
616 |
-
#: admin/basic-info.php:
|
617 |
msgid "Powerful Add-ons (**)"
|
618 |
msgstr ""
|
619 |
|
620 |
-
#: admin/basic-info.php:
|
621 |
msgid "Everything you will need to manage your users is probably already available using the Pro Add-ons."
|
622 |
msgstr ""
|
623 |
|
624 |
-
#: admin/basic-info.php:
|
625 |
msgid "Enable your add-ons"
|
626 |
msgstr ""
|
627 |
|
628 |
-
#: admin/basic-info.php:
|
629 |
msgid "Find out more about PRO Modules"
|
630 |
msgstr ""
|
631 |
|
632 |
-
#: admin/basic-info.php:
|
633 |
msgid "To create a page containing the users registered to this current site/blog, insert the following shortcode in a page of your chosing: %s."
|
634 |
msgstr ""
|
635 |
|
636 |
-
#: admin/basic-info.php:
|
637 |
msgid "Easy to edit templates for listing your website users as well as creating single user pages. Shortcode based, offering many options to customize your listings."
|
638 |
msgstr ""
|
639 |
|
640 |
-
#: admin/basic-info.php:
|
641 |
-
msgid "Personalize all emails sent to your users or admins. On registration, email confirmation, admin approval / un-approval."
|
642 |
-
msgstr ""
|
643 |
-
|
644 |
-
#: admin/basic-info.php:175
|
645 |
msgid "Keep your users out of the WordPress dashboard, redirect them to the front-page after login or registration, everything is just a few clicks away."
|
646 |
msgstr ""
|
647 |
|
648 |
-
#: admin/basic-info.php:
|
649 |
msgid "Set up multiple registration forms with different fields for certain user roles. Capture different information from different types of users."
|
650 |
msgstr ""
|
651 |
|
652 |
-
#: admin/basic-info.php:
|
653 |
msgid "Multiple Edit-profile Forms"
|
654 |
msgstr ""
|
655 |
|
656 |
-
#: admin/basic-info.php:
|
657 |
msgid "Set up a repeating group of fields on register and edit profile forms. Limit the number of repeated groups for each user role."
|
658 |
msgstr ""
|
659 |
|
660 |
-
#: admin/basic-info.php:
|
661 |
msgid " * only available in the %1$sBasic and Pro versions%2$s."
|
662 |
msgstr ""
|
663 |
|
664 |
-
#: admin/basic-info.php:
|
665 |
msgid "** only available in the %1$sPro version%2$s."
|
666 |
msgstr ""
|
667 |
|
@@ -697,7 +681,7 @@ msgstr ""
|
|
697 |
msgid "Give us another try! Open a support ticket <a href='https://www.cozmoslabs.com/support/open-ticket/' target='_blank'>here</a>"
|
698 |
msgstr ""
|
699 |
|
700 |
-
#: admin/feedback.php:47, admin/manage-fields.php:
|
701 |
msgid "Other"
|
702 |
msgstr ""
|
703 |
|
@@ -761,142 +745,154 @@ msgstr ""
|
|
761 |
msgid "Two-Factor Authentication"
|
762 |
msgstr ""
|
763 |
|
764 |
-
#: admin/general-settings.php:37
|
765 |
msgid "User Emails"
|
766 |
msgstr ""
|
767 |
|
768 |
-
#: admin/general-settings.php:38
|
769 |
msgid "Administrator Emails"
|
770 |
msgstr ""
|
771 |
|
772 |
-
#: admin/general-settings.php:
|
773 |
msgid "Profile Builder Settings"
|
774 |
msgstr ""
|
775 |
|
776 |
-
#: admin/general-settings.php:
|
777 |
msgid "Load Profile Builder's own CSS file in the front-end:"
|
778 |
msgstr ""
|
779 |
|
780 |
-
#: admin/general-settings.php:
|
781 |
msgid "Yes"
|
782 |
msgstr ""
|
783 |
|
784 |
-
#: admin/general-settings.php:
|
785 |
msgid "You can find the default file here: %1$s"
|
786 |
msgstr ""
|
787 |
|
788 |
-
#: admin/general-settings.php:
|
789 |
msgid "Automatically Log In:"
|
790 |
msgstr ""
|
791 |
|
792 |
-
#: admin/general-settings.php:
|
793 |
msgid "No"
|
794 |
msgstr ""
|
795 |
|
796 |
-
#: admin/general-settings.php:
|
797 |
msgid "Select \"Yes\" to automatically log in new users after successful registration."
|
798 |
msgstr ""
|
799 |
|
800 |
-
#: admin/general-settings.php:
|
801 |
msgid "\"Email Confirmation\" Activated:"
|
802 |
msgstr ""
|
803 |
|
804 |
-
#: admin/general-settings.php:
|
805 |
msgid "This works with front-end forms only. Recommended to redirect WP default registration to a Profile Builder one using \"Custom Redirects\" module."
|
806 |
msgstr ""
|
807 |
|
808 |
-
#: admin/general-settings.php:
|
809 |
msgid "You can find a list of unconfirmed email addresses %1$sUsers > All Users > Email Confirmation%2$s."
|
810 |
msgstr ""
|
811 |
|
812 |
-
#: admin/general-settings.php:
|
813 |
msgid "\"Email Confirmation\" Landing Page:"
|
814 |
msgstr ""
|
815 |
|
816 |
-
#: admin/general-settings.php:
|
817 |
msgid "Existing Pages"
|
818 |
msgstr ""
|
819 |
|
820 |
-
#: admin/general-settings.php:
|
821 |
msgid "Specify the page where the users will be directed when confirming the email account. This page can differ from the register page(s) and can be changed at any time. If none selected, a simple confirmation page will be displayed for the user."
|
822 |
msgstr ""
|
823 |
|
824 |
-
#: admin/general-settings.php:
|
825 |
msgid "\"Admin Approval\" Activated:"
|
826 |
msgstr ""
|
827 |
|
828 |
-
#: admin/general-settings.php:
|
829 |
msgid "You can find a list of users at %1$sUsers > All Users > Admin Approval%2$s."
|
830 |
msgstr ""
|
831 |
|
832 |
-
#: admin/general-settings.php:
|
833 |
msgid "\"Admin Approval\" on User Role:"
|
834 |
msgstr ""
|
835 |
|
836 |
-
#: admin/general-settings.php:
|
837 |
msgid "Select on what user roles to activate Admin Approval."
|
838 |
msgstr ""
|
839 |
|
840 |
-
#: admin/general-settings.php:
|
841 |
msgid "\"Roles Editor\" Activated:"
|
842 |
msgstr ""
|
843 |
|
844 |
-
#: admin/general-settings.php:
|
845 |
msgid "You can add / edit user roles at %1$sUsers > Roles Editor%2$s."
|
846 |
msgstr ""
|
847 |
|
848 |
-
#: admin/general-settings.php:
|
849 |
msgid "\"Admin Approval\" Feature:"
|
850 |
msgstr ""
|
851 |
|
852 |
-
#: admin/general-settings.php:
|
853 |
msgid "You decide who is a user on your website. Get notified via email or approve multiple users at once from the WordPress UI. Enable Admin Approval by upgrading to %1$sBasic or PRO versions%2$s."
|
854 |
msgstr ""
|
855 |
|
856 |
-
#: admin/general-settings.php:
|
857 |
msgid "Allow Users to Log in With:"
|
858 |
msgstr ""
|
859 |
|
860 |
-
#: admin/general-settings.php:
|
861 |
msgid "Username and Email"
|
862 |
msgstr ""
|
863 |
|
864 |
-
#: admin/general-settings.php:
|
865 |
msgid "Username"
|
866 |
msgstr ""
|
867 |
|
868 |
-
#: admin/general-settings.php:
|
869 |
msgid "Email"
|
870 |
msgstr ""
|
871 |
|
872 |
-
#: admin/general-settings.php:
|
873 |
msgid "\"Username and Email\" - users can Log In with either their Username or their Email."
|
874 |
msgstr ""
|
875 |
|
876 |
-
#: admin/general-settings.php:
|
877 |
msgid "\"Username\" - users can only Log In with their Username. Both the Username and Email fields will be shown in the front-end forms."
|
878 |
msgstr ""
|
879 |
|
880 |
-
#: admin/general-settings.php:
|
881 |
msgid "\"Email\" - users can only Log In with their Email. The Username field will be hidden in the front-end forms and Usernames will be automatically generated based on the Emails."
|
882 |
msgstr ""
|
883 |
|
884 |
-
#: admin/general-settings.php:
|
885 |
msgid "Minimum Password Length:"
|
886 |
msgstr ""
|
887 |
|
888 |
-
#: admin/general-settings.php:
|
889 |
msgid "Enter the minimum characters the password should have. Leave empty for no minimum limit"
|
890 |
msgstr ""
|
891 |
|
892 |
-
#: admin/general-settings.php:
|
893 |
msgid "Minimum Password Strength:"
|
894 |
msgstr ""
|
895 |
|
896 |
-
#: admin/general-settings.php:
|
897 |
msgid "Disabled"
|
898 |
msgstr ""
|
899 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
900 |
#: admin/manage-fields.php:18
|
901 |
msgid "Form Fields"
|
902 |
msgstr ""
|
@@ -909,2167 +905,2167 @@ msgstr ""
|
|
909 |
msgid "Standard"
|
910 |
msgstr ""
|
911 |
|
912 |
-
#: admin/manage-fields.php:
|
913 |
msgid "Advanced"
|
914 |
msgstr ""
|
915 |
|
916 |
-
#: admin/manage-fields.php:
|
917 |
msgid "Choose one of the supported field types"
|
918 |
msgstr ""
|
919 |
|
920 |
-
#: admin/manage-fields.php:
|
921 |
msgid ". Extra Field Types are available in <a href=\"%s\">Basic or PRO versions</a>."
|
922 |
msgstr ""
|
923 |
|
924 |
-
#: admin/manage-fields.php:
|
925 |
msgid "Use this in conjunction with WordPress functions to display the value in the page of your choosing<br/>Auto-completed but in some cases editable (in which case it must be unique)<br/>Changing this will only affect subsequent entries"
|
926 |
msgstr ""
|
927 |
|
928 |
-
#: admin/manage-fields.php:
|
929 |
msgid "Use this in conjunction with WordPress functions to display the value in the page of your choosing<br/>Auto-completed but in some cases editable (in which case it must be unique)<br/>Changing this might take long in case of a very big user-count"
|
930 |
msgstr ""
|
931 |
|
932 |
-
#: admin/manage-fields.php:
|
933 |
msgid "Field Title"
|
934 |
msgstr ""
|
935 |
|
936 |
-
#: admin/manage-fields.php:
|
937 |
msgid "Title of the field"
|
938 |
msgstr ""
|
939 |
|
940 |
-
#: admin/manage-fields.php:
|
941 |
msgid "Field"
|
942 |
msgstr ""
|
943 |
|
944 |
-
#: admin/manage-fields.php:
|
945 |
msgid "Meta-name"
|
946 |
msgstr ""
|
947 |
|
948 |
-
#: admin/manage-fields.php:
|
949 |
msgid "ID"
|
950 |
msgstr ""
|
951 |
|
952 |
-
#: admin/manage-fields.php:
|
953 |
msgid "A unique, auto-generated ID for this particular field<br/>You can use this in conjuction with filters to target this element if needed<br/>Can't be edited"
|
954 |
msgstr ""
|
955 |
|
956 |
-
#: admin/manage-fields.php:
|
957 |
msgid "Description"
|
958 |
msgstr ""
|
959 |
|
960 |
-
#: admin/manage-fields.php:
|
961 |
msgid "Enter a (detailed) description of the option for end users to read<br/>Optional"
|
962 |
msgstr ""
|
963 |
|
964 |
-
#: admin/manage-fields.php:
|
965 |
msgid "Row Count"
|
966 |
msgstr ""
|
967 |
|
968 |
-
#: admin/manage-fields.php:
|
969 |
msgid "Specify the number of rows for a 'Textarea' field<br/>If not specified, defaults to 5"
|
970 |
msgstr ""
|
971 |
|
972 |
-
#: admin/manage-fields.php:
|
973 |
msgid "Allowed Image Extensions"
|
974 |
msgstr ""
|
975 |
|
976 |
-
#: admin/manage-fields.php:
|
977 |
msgid "Specify the extension(s) you want to limit to upload<br/>Example: .ext1,.ext2,.ext3<br/>If not specified, defaults to: .jpg,.jpeg,.gif,.png (.*)"
|
978 |
msgstr ""
|
979 |
|
980 |
-
#: admin/manage-fields.php:
|
981 |
msgid "Use Simple Upload"
|
982 |
msgstr ""
|
983 |
|
984 |
-
#: admin/manage-fields.php:
|
985 |
msgid "Use a simple upload field instead of the WordPress upload"
|
986 |
msgstr ""
|
987 |
|
988 |
-
#: admin/manage-fields.php:
|
989 |
msgid "Allowed Upload Extensions"
|
990 |
msgstr ""
|
991 |
|
992 |
-
#: admin/manage-fields.php:
|
993 |
msgid "Specify the extension(s) you want to limit to upload<br/>Example: .ext1,.ext2,.ext3<br/>If not specified, defaults to all WordPress allowed file extensions (.*)"
|
994 |
msgstr ""
|
995 |
|
996 |
-
#: admin/manage-fields.php:
|
997 |
msgid "Avatar Size"
|
998 |
msgstr ""
|
999 |
|
1000 |
-
#: admin/manage-fields.php:
|
1001 |
msgid "Enter a value (between 20 and 200) for the size of the 'Avatar'<br/>If not specified, defaults to 100"
|
1002 |
msgstr ""
|
1003 |
|
1004 |
-
#: admin/manage-fields.php:
|
1005 |
msgid "Date-format"
|
1006 |
msgstr ""
|
1007 |
|
1008 |
-
#: admin/manage-fields.php:
|
1009 |
msgid "Specify the format of the date when using Datepicker<br/>Valid options: mm/dd/yy, mm/yy/dd, dd/yy/mm, dd/mm/yy, yy/dd/mm, yy/mm/dd, mm-dd-yy, yy-mm-dd, D, dd M yy, D, d M y, DD, dd-M-y, D, d M yy, mm/yy, mm/dd, dd/mm, @<br/>If not specified, defaults to mm/dd/yy<br/>ATTENTION: if you plan to use this field for sorting, please make sure to use year first, then month, and day last."
|
1010 |
msgstr ""
|
1011 |
|
1012 |
-
#: admin/manage-fields.php:
|
1013 |
msgid "Terms of Agreement"
|
1014 |
msgstr ""
|
1015 |
|
1016 |
-
#: admin/manage-fields.php:
|
1017 |
msgid "Enter a detailed description of the terms of agreement for the user to read.<br/>Links can be inserted by using standard HTML syntax: <a href=\"custom_url\">custom_text</a>"
|
1018 |
msgstr ""
|
1019 |
|
1020 |
-
#: admin/manage-fields.php:
|
1021 |
msgid "Options"
|
1022 |
msgstr ""
|
1023 |
|
1024 |
-
#: admin/manage-fields.php:
|
1025 |
msgid "Enter a comma separated list of values<br/>This can be anything, as it is hidden from the user, but should not contain special characters or apostrophes"
|
1026 |
msgstr ""
|
1027 |
|
1028 |
-
#: admin/manage-fields.php:
|
1029 |
msgid "Labels"
|
1030 |
msgstr ""
|
1031 |
|
1032 |
-
#: admin/manage-fields.php:
|
1033 |
msgid "Enter a comma separated list of labels<br/>Visible for the user"
|
1034 |
msgstr ""
|
1035 |
|
1036 |
-
#: admin/manage-fields.php:
|
1037 |
msgid "reCAPTCHA Type"
|
1038 |
msgstr ""
|
1039 |
|
1040 |
-
#: admin/manage-fields.php:
|
1041 |
msgid "Choose the <a href=\"https://developers.google.com/recaptcha/docs/versions\" target=\"_blank\">type of reCAPTCHA</a> you wish to add to this site.<br/>Please note that the Invisible reCAPTCHA is a type of reCAPTCHA v2."
|
1042 |
msgstr ""
|
1043 |
|
1044 |
-
#: admin/manage-fields.php:
|
1045 |
msgid "Site Key"
|
1046 |
msgstr ""
|
1047 |
|
1048 |
-
#: admin/manage-fields.php:
|
1049 |
msgid "The site key from Google, <a href=\"https://www.google.com/recaptcha/admin/create\" target=\"_blank\">https://www.google.com/recaptcha/admin/create</a>"
|
1050 |
msgstr ""
|
1051 |
|
1052 |
-
#: admin/manage-fields.php:
|
1053 |
msgid "Secret Key"
|
1054 |
msgstr ""
|
1055 |
|
1056 |
-
#: admin/manage-fields.php:
|
1057 |
msgid "The secret key from Google, <a href=\"https://www.google.com/recaptcha/admin/create\" target=\"_blank\">https://www.google.com/recaptcha/admin/create</a>"
|
1058 |
msgstr ""
|
1059 |
|
1060 |
-
#: admin/manage-fields.php:
|
1061 |
msgid "Display on PB forms"
|
1062 |
msgstr ""
|
1063 |
|
1064 |
-
#: admin/manage-fields.php:
|
1065 |
msgid "PB Login"
|
1066 |
msgstr ""
|
1067 |
|
1068 |
-
#: admin/manage-fields.php:
|
1069 |
msgid "PB Register"
|
1070 |
msgstr ""
|
1071 |
|
1072 |
-
#: admin/manage-fields.php:
|
1073 |
msgid "PB Recover Password"
|
1074 |
msgstr ""
|
1075 |
|
1076 |
-
#: admin/manage-fields.php:
|
1077 |
msgid "Select on which Profile Builder forms to display reCAPTCHA"
|
1078 |
msgstr ""
|
1079 |
|
1080 |
-
#: admin/manage-fields.php:
|
1081 |
msgid "Display on default WP forms"
|
1082 |
msgstr ""
|
1083 |
|
1084 |
-
#: admin/manage-fields.php:
|
1085 |
msgid "Default WP Login"
|
1086 |
msgstr ""
|
1087 |
|
1088 |
-
#: admin/manage-fields.php:
|
1089 |
msgid "Default WP Register"
|
1090 |
msgstr ""
|
1091 |
|
1092 |
-
#: admin/manage-fields.php:
|
1093 |
msgid "Default WP Recover Password"
|
1094 |
msgstr ""
|
1095 |
|
1096 |
-
#: admin/manage-fields.php:
|
1097 |
msgid "Select on which default WP forms to display reCAPTCHA"
|
1098 |
msgstr ""
|
1099 |
|
1100 |
-
#: admin/manage-fields.php:
|
1101 |
msgid "User Roles"
|
1102 |
msgstr ""
|
1103 |
|
1104 |
-
#: admin/manage-fields.php:
|
1105 |
msgid "Select which user roles to show to the user ( drag and drop to re-order )"
|
1106 |
msgstr ""
|
1107 |
|
1108 |
-
#: admin/manage-fields.php:
|
1109 |
msgid "Display on Edit Profile"
|
1110 |
msgstr ""
|
1111 |
|
1112 |
-
#: admin/manage-fields.php:
|
1113 |
msgid "Check if you want the select user role field to appear on Edit Profile forms"
|
1114 |
msgstr ""
|
1115 |
|
1116 |
-
#: admin/manage-fields.php:
|
1117 |
msgid "User Roles Order"
|
1118 |
msgstr ""
|
1119 |
|
1120 |
-
#: admin/manage-fields.php:
|
1121 |
msgid "Save the user role order from the user roles checkboxes"
|
1122 |
msgstr ""
|
1123 |
|
1124 |
-
#: admin/manage-fields.php:
|
1125 |
msgid "Default Value"
|
1126 |
msgstr ""
|
1127 |
|
1128 |
-
#: admin/manage-fields.php:
|
1129 |
msgid "Default value of the field"
|
1130 |
msgstr ""
|
1131 |
|
1132 |
-
#: admin/manage-fields.php:
|
1133 |
msgid "Default Option"
|
1134 |
msgstr ""
|
1135 |
|
1136 |
-
#: admin/manage-fields.php:
|
1137 |
msgid "Specify the option which should be selected by default"
|
1138 |
msgstr ""
|
1139 |
|
1140 |
-
#: admin/manage-fields.php:
|
1141 |
msgid "Default Option(s)"
|
1142 |
msgstr ""
|
1143 |
|
1144 |
-
#: admin/manage-fields.php:
|
1145 |
msgid "Specify the option which should be checked by default<br/>If there are multiple values, separate them with a ',' (comma)"
|
1146 |
msgstr ""
|
1147 |
|
1148 |
-
#: admin/manage-fields.php:
|
1149 |
msgid "Default option of the field"
|
1150 |
msgstr ""
|
1151 |
|
1152 |
-
#: admin/manage-fields.php:
|
1153 |
msgid "Show Currency Symbol"
|
1154 |
msgstr ""
|
1155 |
|
1156 |
-
#: admin/manage-fields.php:
|
1157 |
msgid "Whether the currency symbol should be displayed after the currency name in the select option."
|
1158 |
msgstr ""
|
1159 |
|
1160 |
-
#: admin/manage-fields.php:
|
1161 |
msgid "Show Post Type"
|
1162 |
msgstr ""
|
1163 |
|
1164 |
-
#: admin/manage-fields.php:
|
1165 |
msgid "Posts from what post type will be displayed in the select."
|
1166 |
msgstr ""
|
1167 |
|
1168 |
-
#: admin/manage-fields.php:
|
1169 |
msgid "Allowable Values"
|
1170 |
msgstr ""
|
1171 |
|
1172 |
-
#: admin/manage-fields.php:
|
1173 |
msgid "Enter a comma separated list of possible values. Upon registration if the value provided by the user does not match one of these values, the user will not be registered."
|
1174 |
msgstr ""
|
1175 |
|
1176 |
-
#: admin/manage-fields.php:
|
1177 |
msgid "Error Message"
|
1178 |
msgstr ""
|
1179 |
|
1180 |
-
#: admin/manage-fields.php:
|
1181 |
msgid "Set a custom error message that will be displayed to the user."
|
1182 |
msgstr ""
|
1183 |
|
1184 |
-
#: admin/manage-fields.php:
|
1185 |
msgid "Time Format"
|
1186 |
msgstr ""
|
1187 |
|
1188 |
-
#: admin/manage-fields.php:
|
1189 |
msgid "Specify the time format."
|
1190 |
msgstr ""
|
1191 |
|
1192 |
-
#: admin/manage-fields.php:
|
1193 |
msgid "Google Maps API Key"
|
1194 |
msgstr ""
|
1195 |
|
1196 |
-
#: admin/manage-fields.php:
|
1197 |
msgid "Enter your Google Maps API key ( <a href=\"https://console.developers.google.com/flows/enableapi?apiid=maps_backend\" target=\"_blank\">Get your API key</a> ). If more than one map fields are added to a form the API key from the first map displayed will be used."
|
1198 |
msgstr ""
|
1199 |
|
1200 |
-
#: admin/manage-fields.php:
|
1201 |
msgid "Default Latitude"
|
1202 |
msgstr ""
|
1203 |
|
1204 |
-
#: admin/manage-fields.php:
|
1205 |
msgid "The latitude at which the map should be displayed when no pins are attached."
|
1206 |
msgstr ""
|
1207 |
|
1208 |
-
#: admin/manage-fields.php:
|
1209 |
msgid "Default Longitude"
|
1210 |
msgstr ""
|
1211 |
|
1212 |
-
#: admin/manage-fields.php:
|
1213 |
msgid "The longitude at which the map should be displayed when no pins are attached."
|
1214 |
msgstr ""
|
1215 |
|
1216 |
-
#: admin/manage-fields.php:
|
1217 |
msgid "Default Zoom Level"
|
1218 |
msgstr ""
|
1219 |
|
1220 |
-
#: admin/manage-fields.php:
|
1221 |
msgid "Add a number from 0 to 19. The higher the number the higher the zoom."
|
1222 |
msgstr ""
|
1223 |
|
1224 |
-
#: admin/manage-fields.php:
|
1225 |
msgid "Map Height"
|
1226 |
msgstr ""
|
1227 |
|
1228 |
-
#: admin/manage-fields.php:
|
1229 |
msgid "The height of the map."
|
1230 |
msgstr ""
|
1231 |
|
1232 |
-
#: admin/manage-fields.php:
|
1233 |
msgid "Default Content"
|
1234 |
msgstr ""
|
1235 |
|
1236 |
-
#: admin/manage-fields.php:
|
1237 |
msgid "Default value of the textarea"
|
1238 |
msgstr ""
|
1239 |
|
1240 |
-
#: admin/manage-fields.php:
|
1241 |
msgid "HTML Content"
|
1242 |
msgstr ""
|
1243 |
|
1244 |
-
#: admin/manage-fields.php:
|
1245 |
msgid "Add your HTML (or text) content"
|
1246 |
msgstr ""
|
1247 |
|
1248 |
-
#: admin/manage-fields.php:
|
1249 |
msgid "Phone Format"
|
1250 |
msgstr ""
|
1251 |
|
1252 |
-
#: admin/manage-fields.php:
|
1253 |
msgid "You can use: # for numbers, parentheses ( ), - sign, + sign, dot . and spaces."
|
1254 |
msgstr ""
|
1255 |
|
1256 |
-
#: admin/manage-fields.php:
|
1257 |
msgid "Eg. (###) ###-####"
|
1258 |
msgstr ""
|
1259 |
|
1260 |
-
#: admin/manage-fields.php:
|
1261 |
msgid "Empty field won't check for correct phone number."
|
1262 |
msgstr ""
|
1263 |
|
1264 |
-
#: admin/manage-fields.php:
|
1265 |
msgid "Heading Tag"
|
1266 |
msgstr ""
|
1267 |
|
1268 |
-
#: admin/manage-fields.php:
|
1269 |
msgid "Change heading field size on front-end forms"
|
1270 |
msgstr ""
|
1271 |
|
1272 |
-
#: admin/manage-fields.php:
|
1273 |
msgid "Min Number Value"
|
1274 |
msgstr ""
|
1275 |
|
1276 |
-
#: admin/manage-fields.php:
|
1277 |
msgid "Min allowed number value (0 to allow only positive numbers)"
|
1278 |
msgstr ""
|
1279 |
|
1280 |
-
#: admin/manage-fields.php:
|
1281 |
msgid "Leave it empty for no min value"
|
1282 |
msgstr ""
|
1283 |
|
1284 |
-
#: admin/manage-fields.php:
|
1285 |
msgid "Max Number Value"
|
1286 |
msgstr ""
|
1287 |
|
1288 |
-
#: admin/manage-fields.php:
|
1289 |
msgid "Max allowed number value (0 to allow only negative numbers)"
|
1290 |
msgstr ""
|
1291 |
|
1292 |
-
#: admin/manage-fields.php:
|
1293 |
msgid "Leave it empty for no max value"
|
1294 |
msgstr ""
|
1295 |
|
1296 |
-
#: admin/manage-fields.php:
|
1297 |
msgid "Number Step Value"
|
1298 |
msgstr ""
|
1299 |
|
1300 |
-
#: admin/manage-fields.php:
|
1301 |
msgid "Step value 1 to allow only integers, 0.1 to allow integers and numbers with 1 decimal"
|
1302 |
msgstr ""
|
1303 |
|
1304 |
-
#: admin/manage-fields.php:
|
1305 |
msgid "To allow multiple decimals use for eg. 0.01 (for 2 deciamls) and so on"
|
1306 |
msgstr ""
|
1307 |
|
1308 |
-
#: admin/manage-fields.php:
|
1309 |
msgid "You can also use step value to specify the legal number intervals (eg. step value 2 will allow only -4, -2, 0, 2 and so on)"
|
1310 |
msgstr ""
|
1311 |
|
1312 |
-
#: admin/manage-fields.php:
|
1313 |
msgid "Leave it empty for no restriction"
|
1314 |
msgstr ""
|
1315 |
|
1316 |
-
#: admin/manage-fields.php:
|
1317 |
msgid "Required"
|
1318 |
msgstr ""
|
1319 |
|
1320 |
-
#: admin/manage-fields.php:
|
1321 |
msgid "Whether the field is required or not"
|
1322 |
msgstr ""
|
1323 |
|
1324 |
-
#: admin/manage-fields.php:
|
1325 |
msgid "Overwrite Existing"
|
1326 |
msgstr ""
|
1327 |
|
1328 |
-
#: admin/manage-fields.php:
|
1329 |
msgid "Selecting 'Yes' will add the field to the list, but will overwrite any other field in the database that has the same meta-name<br/>Use this at your own risk"
|
1330 |
msgstr ""
|
1331 |
|
1332 |
-
#: admin/manage-fields.php:
|
1333 |
msgid "POIs Load Type"
|
1334 |
msgstr ""
|
1335 |
|
1336 |
-
#: admin/manage-fields.php:
|
1337 |
msgid "POIs of the listed users (as filtered & paginated)"
|
1338 |
msgstr ""
|
1339 |
|
1340 |
-
#: admin/manage-fields.php:
|
1341 |
msgid "POIs of all the users for the filter* (no pagination)"
|
1342 |
msgstr ""
|
1343 |
|
1344 |
-
#: admin/manage-fields.php:
|
1345 |
msgid "This option allows you to load on a single map the POIs for all users, or just these for the listed ones (this will take into account the filters and the faceted menus). *Please use this feature wisely, it will impact the performance."
|
1346 |
msgstr ""
|
1347 |
|
1348 |
-
#: admin/manage-fields.php:
|
1349 |
msgid "POI Bubble Info"
|
1350 |
msgstr ""
|
1351 |
|
1352 |
-
#: admin/manage-fields.php:
|
1353 |
msgid "Select the attributes to be listed inside the POI bubble."
|
1354 |
msgstr ""
|
1355 |
|
1356 |
-
#: admin/manage-fields.php:
|
1357 |
msgid "Number of Users per Map Iteration"
|
1358 |
msgstr ""
|
1359 |
|
1360 |
-
#: admin/manage-fields.php:
|
1361 |
msgid "When loading the map of all users with no pagination, the map script will iterate multiple times and will expose gradually POIs on the map, until all the POIs for the users that match the criteria will be added on the map (think of this as of pagination for the map POIs). The smaller the number of users per iteration, the fastest the iteration response will be, but for a large number of users, the map script will iterate multiple times. Setting a higher limit will decrease the performance, but might produce a smaller number of iterations. <br><br><b>Please adjust this value to your hosting capabilities, and make sure that the value you set is the best for performance.</b> We recommend a <b>maximum</b> value of 300."
|
1362 |
msgstr ""
|
1363 |
|
1364 |
-
#: admin/manage-fields.php:
|
1365 |
msgid "Maximum Selections"
|
1366 |
msgstr ""
|
1367 |
|
1368 |
-
#: admin/manage-fields.php:
|
1369 |
msgid "Select2 multi-value select boxes can set restrictions regarding the maximum number of options selected."
|
1370 |
msgstr ""
|
1371 |
|
1372 |
-
#: admin/manage-fields.php:
|
1373 |
msgid "User Inputted Options"
|
1374 |
msgstr ""
|
1375 |
|
1376 |
-
#: admin/manage-fields.php:
|
1377 |
msgid "Check this to allow users to create their own options beside the pre-existing ones."
|
1378 |
msgstr ""
|
1379 |
|
1380 |
-
#: admin/manage-fields.php:
|
1381 |
msgid "Form Field Properties"
|
1382 |
msgstr ""
|
1383 |
|
1384 |
-
#: admin/manage-fields.php:
|
1385 |
msgid "Registration & Edit Profile Forms"
|
1386 |
msgstr ""
|
1387 |
|
1388 |
-
#: admin/manage-fields.php:
|
1389 |
msgid "Name"
|
1390 |
msgstr ""
|
1391 |
|
1392 |
-
#: admin/manage-fields.php:
|
1393 |
msgid "Usernames cannot be changed."
|
1394 |
msgstr ""
|
1395 |
|
1396 |
-
#: admin/manage-fields.php:
|
1397 |
msgid "First Name"
|
1398 |
msgstr ""
|
1399 |
|
1400 |
-
#: admin/manage-fields.php:
|
1401 |
msgid "Last Name"
|
1402 |
msgstr ""
|
1403 |
|
1404 |
-
#: admin/manage-fields.php:
|
1405 |
msgid "Nickname"
|
1406 |
msgstr ""
|
1407 |
|
1408 |
-
#: admin/manage-fields.php:
|
1409 |
msgid "Display name publicly as"
|
1410 |
msgstr ""
|
1411 |
|
1412 |
-
#: admin/manage-fields.php:
|
1413 |
msgid "Contact Info"
|
1414 |
msgstr ""
|
1415 |
|
1416 |
-
#: admin/manage-fields.php:
|
1417 |
msgid "E-mail"
|
1418 |
msgstr ""
|
1419 |
|
1420 |
-
#: admin/manage-fields.php:
|
1421 |
msgid "Website"
|
1422 |
msgstr ""
|
1423 |
|
1424 |
-
#: admin/manage-fields.php:
|
1425 |
msgid "AIM"
|
1426 |
msgstr ""
|
1427 |
|
1428 |
-
#: admin/manage-fields.php:
|
1429 |
msgid "Yahoo IM"
|
1430 |
msgstr ""
|
1431 |
|
1432 |
-
#: admin/manage-fields.php:
|
1433 |
msgid "Jabber / Google Talk"
|
1434 |
msgstr ""
|
1435 |
|
1436 |
-
#: admin/manage-fields.php:
|
1437 |
msgid "About Yourself"
|
1438 |
msgstr ""
|
1439 |
|
1440 |
-
#: admin/manage-fields.php:
|
1441 |
msgid "Biographical Info"
|
1442 |
msgstr ""
|
1443 |
|
1444 |
-
#: admin/manage-fields.php:
|
1445 |
msgid "Share a little biographical information to fill out your profile. This may be shown publicly."
|
1446 |
msgstr ""
|
1447 |
|
1448 |
-
#: admin/manage-fields.php:
|
1449 |
msgid "Password"
|
1450 |
msgstr ""
|
1451 |
|
1452 |
-
#: admin/manage-fields.php:
|
1453 |
msgid "Type your password."
|
1454 |
msgstr ""
|
1455 |
|
1456 |
-
#: admin/manage-fields.php:
|
1457 |
msgid "Repeat Password"
|
1458 |
msgstr ""
|
1459 |
|
1460 |
-
#: admin/manage-fields.php:
|
1461 |
msgid "Type your password again. "
|
1462 |
msgstr ""
|
1463 |
|
1464 |
-
#: admin/manage-fields.php:
|
1465 |
msgid "Blog Details"
|
1466 |
msgstr ""
|
1467 |
|
1468 |
-
#: admin/manage-fields.php:
|
1469 |
msgid "Select a Country"
|
1470 |
msgstr ""
|
1471 |
|
1472 |
-
#: admin/manage-fields.php:
|
1473 |
msgid "Afghanistan"
|
1474 |
msgstr ""
|
1475 |
|
1476 |
-
#: admin/manage-fields.php:
|
1477 |
msgid "Aland Islands"
|
1478 |
msgstr ""
|
1479 |
|
1480 |
-
#: admin/manage-fields.php:
|
1481 |
msgid "Albania"
|
1482 |
msgstr ""
|
1483 |
|
1484 |
-
#: admin/manage-fields.php:
|
1485 |
msgid "Algeria"
|
1486 |
msgstr ""
|
1487 |
|
1488 |
-
#: admin/manage-fields.php:
|
1489 |
msgid "American Samoa"
|
1490 |
msgstr ""
|
1491 |
|
1492 |
-
#: admin/manage-fields.php:
|
1493 |
msgid "Andorra"
|
1494 |
msgstr ""
|
1495 |
|
1496 |
-
#: admin/manage-fields.php:
|
1497 |
msgid "Angola"
|
1498 |
msgstr ""
|
1499 |
|
1500 |
-
#: admin/manage-fields.php:
|
1501 |
msgid "Anguilla"
|
1502 |
msgstr ""
|
1503 |
|
1504 |
-
#: admin/manage-fields.php:
|
1505 |
msgid "Antarctica"
|
1506 |
msgstr ""
|
1507 |
|
1508 |
-
#: admin/manage-fields.php:
|
1509 |
msgid "Antigua and Barbuda"
|
1510 |
msgstr ""
|
1511 |
|
1512 |
-
#: admin/manage-fields.php:
|
1513 |
msgid "Argentina"
|
1514 |
msgstr ""
|
1515 |
|
1516 |
-
#: admin/manage-fields.php:
|
1517 |
msgid "Armenia"
|
1518 |
msgstr ""
|
1519 |
|
1520 |
-
#: admin/manage-fields.php:
|
1521 |
msgid "Aruba"
|
1522 |
msgstr ""
|
1523 |
|
1524 |
-
#: admin/manage-fields.php:
|
1525 |
msgid "Australia"
|
1526 |
msgstr ""
|
1527 |
|
1528 |
-
#: admin/manage-fields.php:
|
1529 |
msgid "Austria"
|
1530 |
msgstr ""
|
1531 |
|
1532 |
-
#: admin/manage-fields.php:
|
1533 |
msgid "Azerbaijan"
|
1534 |
msgstr ""
|
1535 |
|
1536 |
-
#: admin/manage-fields.php:
|
1537 |
msgid "Bahamas"
|
1538 |
msgstr ""
|
1539 |
|
1540 |
-
#: admin/manage-fields.php:
|
1541 |
msgid "Bahrain"
|
1542 |
msgstr ""
|
1543 |
|
1544 |
-
#: admin/manage-fields.php:
|
1545 |
msgid "Bangladesh"
|
1546 |
msgstr ""
|
1547 |
|
1548 |
-
#: admin/manage-fields.php:
|
1549 |
msgid "Barbados"
|
1550 |
msgstr ""
|
1551 |
|
1552 |
-
#: admin/manage-fields.php:
|
1553 |
msgid "Belarus"
|
1554 |
msgstr ""
|
1555 |
|
1556 |
-
#: admin/manage-fields.php:
|
1557 |
msgid "Belgium"
|
1558 |
msgstr ""
|
1559 |
|
1560 |
-
#: admin/manage-fields.php:
|
1561 |
msgid "Belize"
|
1562 |
msgstr ""
|
1563 |
|
1564 |
-
#: admin/manage-fields.php:
|
1565 |
msgid "Benin"
|
1566 |
msgstr ""
|
1567 |
|
1568 |
-
#: admin/manage-fields.php:
|
1569 |
msgid "Bermuda"
|
1570 |
msgstr ""
|
1571 |
|
1572 |
-
#: admin/manage-fields.php:
|
1573 |
msgid "Bhutan"
|
1574 |
msgstr ""
|
1575 |
|
1576 |
-
#: admin/manage-fields.php:
|
1577 |
msgid "Bolivia"
|
1578 |
msgstr ""
|
1579 |
|
1580 |
-
#: admin/manage-fields.php:
|
1581 |
msgid "Bonaire, Saint Eustatius and Saba"
|
1582 |
msgstr ""
|
1583 |
|
1584 |
-
#: admin/manage-fields.php:
|
1585 |
msgid "Bosnia and Herzegovina"
|
1586 |
msgstr ""
|
1587 |
|
1588 |
-
#: admin/manage-fields.php:
|
1589 |
msgid "Botswana"
|
1590 |
msgstr ""
|
1591 |
|
1592 |
-
#: admin/manage-fields.php:
|
1593 |
msgid "Bouvet Island"
|
1594 |
msgstr ""
|
1595 |
|
1596 |
-
#: admin/manage-fields.php:
|
1597 |
msgid "Brazil"
|
1598 |
msgstr ""
|
1599 |
|
1600 |
-
#: admin/manage-fields.php:
|
1601 |
msgid "British Indian Ocean Territory"
|
1602 |
msgstr ""
|
1603 |
|
1604 |
-
#: admin/manage-fields.php:
|
1605 |
msgid "British Virgin Islands"
|
1606 |
msgstr ""
|
1607 |
|
1608 |
-
#: admin/manage-fields.php:
|
1609 |
msgid "Brunei"
|
1610 |
msgstr ""
|
1611 |
|
1612 |
-
#: admin/manage-fields.php:
|
1613 |
msgid "Bulgaria"
|
1614 |
msgstr ""
|
1615 |
|
1616 |
-
#: admin/manage-fields.php:
|
1617 |
msgid "Burkina Faso"
|
1618 |
msgstr ""
|
1619 |
|
1620 |
-
#: admin/manage-fields.php:
|
1621 |
msgid "Burundi"
|
1622 |
msgstr ""
|
1623 |
|
1624 |
-
#: admin/manage-fields.php:
|
1625 |
msgid "Cambodia"
|
1626 |
msgstr ""
|
1627 |
|
1628 |
-
#: admin/manage-fields.php:
|
1629 |
msgid "Cameroon"
|
1630 |
msgstr ""
|
1631 |
|
1632 |
-
#: admin/manage-fields.php:
|
1633 |
msgid "Canada"
|
1634 |
msgstr ""
|
1635 |
|
1636 |
-
#: admin/manage-fields.php:
|
1637 |
msgid "Cape Verde"
|
1638 |
msgstr ""
|
1639 |
|
1640 |
-
#: admin/manage-fields.php:
|
1641 |
msgid "Cayman Islands"
|
1642 |
msgstr ""
|
1643 |
|
1644 |
-
#: admin/manage-fields.php:
|
1645 |
msgid "Central African Republic"
|
1646 |
msgstr ""
|
1647 |
|
1648 |
-
#: admin/manage-fields.php:
|
1649 |
msgid "Chad"
|
1650 |
msgstr ""
|
1651 |
|
1652 |
-
#: admin/manage-fields.php:
|
1653 |
msgid "Chile"
|
1654 |
msgstr ""
|
1655 |
|
1656 |
-
#: admin/manage-fields.php:
|
1657 |
msgid "China"
|
1658 |
msgstr ""
|
1659 |
|
1660 |
-
#: admin/manage-fields.php:
|
1661 |
msgid "Christmas Island"
|
1662 |
msgstr ""
|
1663 |
|
1664 |
-
#: admin/manage-fields.php:
|
1665 |
msgid "Cocos Islands"
|
1666 |
msgstr ""
|
1667 |
|
1668 |
-
#: admin/manage-fields.php:
|
1669 |
msgid "Colombia"
|
1670 |
msgstr ""
|
1671 |
|
1672 |
-
#: admin/manage-fields.php:
|
1673 |
msgid "Comoros"
|
1674 |
msgstr ""
|
1675 |
|
1676 |
-
#: admin/manage-fields.php:
|
1677 |
msgid "Cook Islands"
|
1678 |
msgstr ""
|
1679 |
|
1680 |
-
#: admin/manage-fields.php:
|
1681 |
msgid "Costa Rica"
|
1682 |
msgstr ""
|
1683 |
|
1684 |
-
#: admin/manage-fields.php:
|
1685 |
msgid "Croatia"
|
1686 |
msgstr ""
|
1687 |
|
1688 |
-
#: admin/manage-fields.php:
|
1689 |
msgid "Cuba"
|
1690 |
msgstr ""
|
1691 |
|
1692 |
-
#: admin/manage-fields.php:
|
1693 |
msgid "Curacao"
|
1694 |
msgstr ""
|
1695 |
|
1696 |
-
#: admin/manage-fields.php:
|
1697 |
msgid "Cyprus"
|
1698 |
msgstr ""
|
1699 |
|
1700 |
-
#: admin/manage-fields.php:
|
1701 |
msgid "Czech Republic"
|
1702 |
msgstr ""
|
1703 |
|
1704 |
-
#: admin/manage-fields.php:
|
1705 |
msgid "Democratic Republic of the Congo"
|
1706 |
msgstr ""
|
1707 |
|
1708 |
-
#: admin/manage-fields.php:
|
1709 |
msgid "Denmark"
|
1710 |
msgstr ""
|
1711 |
|
1712 |
-
#: admin/manage-fields.php:
|
1713 |
msgid "Djibouti"
|
1714 |
msgstr ""
|
1715 |
|
1716 |
-
#: admin/manage-fields.php:
|
1717 |
msgid "Dominica"
|
1718 |
msgstr ""
|
1719 |
|
1720 |
-
#: admin/manage-fields.php:
|
1721 |
msgid "Dominican Republic"
|
1722 |
msgstr ""
|
1723 |
|
1724 |
-
#: admin/manage-fields.php:
|
1725 |
msgid "East Timor"
|
1726 |
msgstr ""
|
1727 |
|
1728 |
-
#: admin/manage-fields.php:
|
1729 |
msgid "Ecuador"
|
1730 |
msgstr ""
|
1731 |
|
1732 |
-
#: admin/manage-fields.php:
|
1733 |
msgid "Egypt"
|
1734 |
msgstr ""
|
1735 |
|
1736 |
-
#: admin/manage-fields.php:
|
1737 |
msgid "El Salvador"
|
1738 |
msgstr ""
|
1739 |
|
1740 |
-
#: admin/manage-fields.php:
|
1741 |
msgid "Equatorial Guinea"
|
1742 |
msgstr ""
|
1743 |
|
1744 |
-
#: admin/manage-fields.php:
|
1745 |
msgid "Eritrea"
|
1746 |
msgstr ""
|
1747 |
|
1748 |
-
#: admin/manage-fields.php:
|
1749 |
msgid "Estonia"
|
1750 |
msgstr ""
|
1751 |
|
1752 |
-
#: admin/manage-fields.php:
|
1753 |
msgid "Ethiopia"
|
1754 |
msgstr ""
|
1755 |
|
1756 |
-
#: admin/manage-fields.php:
|
1757 |
msgid "Falkland Islands"
|
1758 |
msgstr ""
|
1759 |
|
1760 |
-
#: admin/manage-fields.php:
|
1761 |
msgid "Faroe Islands"
|
1762 |
msgstr ""
|
1763 |
|
1764 |
-
#: admin/manage-fields.php:
|
1765 |
msgid "Fiji"
|
1766 |
msgstr ""
|
1767 |
|
1768 |
-
#: admin/manage-fields.php:
|
1769 |
msgid "Finland"
|
1770 |
msgstr ""
|
1771 |
|
1772 |
-
#: admin/manage-fields.php:
|
1773 |
msgid "France"
|
1774 |
msgstr ""
|
1775 |
|
1776 |
-
#: admin/manage-fields.php:
|
1777 |
msgid "French Guiana"
|
1778 |
msgstr ""
|
1779 |
|
1780 |
-
#: admin/manage-fields.php:
|
1781 |
msgid "French Polynesia"
|
1782 |
msgstr ""
|
1783 |
|
1784 |
-
#: admin/manage-fields.php:
|
1785 |
msgid "French Southern Territories"
|
1786 |
msgstr ""
|
1787 |
|
1788 |
-
#: admin/manage-fields.php:
|
1789 |
msgid "Gabon"
|
1790 |
msgstr ""
|
1791 |
|
1792 |
-
#: admin/manage-fields.php:
|
1793 |
msgid "Gambia"
|
1794 |
msgstr ""
|
1795 |
|
1796 |
-
#: admin/manage-fields.php:
|
1797 |
msgid "Georgia"
|
1798 |
msgstr ""
|
1799 |
|
1800 |
-
#: admin/manage-fields.php:
|
1801 |
msgid "Germany"
|
1802 |
msgstr ""
|
1803 |
|
1804 |
-
#: admin/manage-fields.php:
|
1805 |
msgid "Ghana"
|
1806 |
msgstr ""
|
1807 |
|
1808 |
-
#: admin/manage-fields.php:
|
1809 |
msgid "Gibraltar"
|
1810 |
msgstr ""
|
1811 |
|
1812 |
-
#: admin/manage-fields.php:
|
1813 |
msgid "Greece"
|
1814 |
msgstr ""
|
1815 |
|
1816 |
-
#: admin/manage-fields.php:
|
1817 |
msgid "Greenland"
|
1818 |
msgstr ""
|
1819 |
|
1820 |
-
#: admin/manage-fields.php:
|
1821 |
msgid "Grenada"
|
1822 |
msgstr ""
|
1823 |
|
1824 |
-
#: admin/manage-fields.php:
|
1825 |
msgid "Guadeloupe"
|
1826 |
msgstr ""
|
1827 |
|
1828 |
-
#: admin/manage-fields.php:
|
1829 |
msgid "Guam"
|
1830 |
msgstr ""
|
1831 |
|
1832 |
-
#: admin/manage-fields.php:
|
1833 |
msgid "Guatemala"
|
1834 |
msgstr ""
|
1835 |
|
1836 |
-
#: admin/manage-fields.php:
|
1837 |
msgid "Guernsey"
|
1838 |
msgstr ""
|
1839 |
|
1840 |
-
#: admin/manage-fields.php:
|
1841 |
msgid "Guinea"
|
1842 |
msgstr ""
|
1843 |
|
1844 |
-
#: admin/manage-fields.php:
|
1845 |
msgid "Guinea-Bissau"
|
1846 |
msgstr ""
|
1847 |
|
1848 |
-
#: admin/manage-fields.php:
|
1849 |
msgid "Guyana"
|
1850 |
msgstr ""
|
1851 |
|
1852 |
-
#: admin/manage-fields.php:
|
1853 |
msgid "Haiti"
|
1854 |
msgstr ""
|
1855 |
|
1856 |
-
#: admin/manage-fields.php:
|
1857 |
msgid "Heard Island and McDonald Islands"
|
1858 |
msgstr ""
|
1859 |
|
1860 |
-
#: admin/manage-fields.php:
|
1861 |
msgid "Honduras"
|
1862 |
msgstr ""
|
1863 |
|
1864 |
-
#: admin/manage-fields.php:
|
1865 |
msgid "Hong Kong"
|
1866 |
msgstr ""
|
1867 |
|
1868 |
-
#: admin/manage-fields.php:
|
1869 |
msgid "Hungary"
|
1870 |
msgstr ""
|
1871 |
|
1872 |
-
#: admin/manage-fields.php:
|
1873 |
msgid "Iceland"
|
1874 |
msgstr ""
|
1875 |
|
1876 |
-
#: admin/manage-fields.php:
|
1877 |
msgid "India"
|
1878 |
msgstr ""
|
1879 |
|
1880 |
-
#: admin/manage-fields.php:
|
1881 |
msgid "Indonesia"
|
1882 |
msgstr ""
|
1883 |
|
1884 |
-
#: admin/manage-fields.php:
|
1885 |
msgid "Iran"
|
1886 |
msgstr ""
|
1887 |
|
1888 |
-
#: admin/manage-fields.php:
|
1889 |
msgid "Iraq"
|
1890 |
msgstr ""
|
1891 |
|
1892 |
-
#: admin/manage-fields.php:
|
1893 |
msgid "Ireland"
|
1894 |
msgstr ""
|
1895 |
|
1896 |
-
#: admin/manage-fields.php:
|
1897 |
msgid "Isle of Man"
|
1898 |
msgstr ""
|
1899 |
|
1900 |
-
#: admin/manage-fields.php:
|
1901 |
msgid "Israel"
|
1902 |
msgstr ""
|
1903 |
|
1904 |
-
#: admin/manage-fields.php:
|
1905 |
msgid "Italy"
|
1906 |
msgstr ""
|
1907 |
|
1908 |
-
#: admin/manage-fields.php:
|
1909 |
msgid "Ivory Coast"
|
1910 |
msgstr ""
|
1911 |
|
1912 |
-
#: admin/manage-fields.php:
|
1913 |
msgid "Jamaica"
|
1914 |
msgstr ""
|
1915 |
|
1916 |
-
#: admin/manage-fields.php:
|
1917 |
msgid "Japan"
|
1918 |
msgstr ""
|
1919 |
|
1920 |
-
#: admin/manage-fields.php:
|
1921 |
msgid "Jersey"
|
1922 |
msgstr ""
|
1923 |
|
1924 |
-
#: admin/manage-fields.php:
|
1925 |
msgid "Jordan"
|
1926 |
msgstr ""
|
1927 |
|
1928 |
-
#: admin/manage-fields.php:
|
1929 |
msgid "Kazakhstan"
|
1930 |
msgstr ""
|
1931 |
|
1932 |
-
#: admin/manage-fields.php:
|
1933 |
msgid "Kenya"
|
1934 |
msgstr ""
|
1935 |
|
1936 |
-
#: admin/manage-fields.php:
|
1937 |
msgid "Kiribati"
|
1938 |
msgstr ""
|
1939 |
|
1940 |
-
#: admin/manage-fields.php:
|
1941 |
msgid "Kosovo"
|
1942 |
msgstr ""
|
1943 |
|
1944 |
-
#: admin/manage-fields.php:
|
1945 |
msgid "Kuwait"
|
1946 |
msgstr ""
|
1947 |
|
1948 |
-
#: admin/manage-fields.php:
|
1949 |
msgid "Kyrgyzstan"
|
1950 |
msgstr ""
|
1951 |
|
1952 |
-
#: admin/manage-fields.php:
|
1953 |
msgid "Laos"
|
1954 |
msgstr ""
|
1955 |
|
1956 |
-
#: admin/manage-fields.php:
|
1957 |
msgid "Latvia"
|
1958 |
msgstr ""
|
1959 |
|
1960 |
-
#: admin/manage-fields.php:
|
1961 |
msgid "Lebanon"
|
1962 |
msgstr ""
|
1963 |
|
1964 |
-
#: admin/manage-fields.php:
|
1965 |
msgid "Lesotho"
|
1966 |
msgstr ""
|
1967 |
|
1968 |
-
#: admin/manage-fields.php:
|
1969 |
msgid "Liberia"
|
1970 |
msgstr ""
|
1971 |
|
1972 |
-
#: admin/manage-fields.php:
|
1973 |
msgid "Libya"
|
1974 |
msgstr ""
|
1975 |
|
1976 |
-
#: admin/manage-fields.php:
|
1977 |
msgid "Liechtenstein"
|
1978 |
msgstr ""
|
1979 |
|
1980 |
-
#: admin/manage-fields.php:
|
1981 |
msgid "Lithuania"
|
1982 |
msgstr ""
|
1983 |
|
1984 |
-
#: admin/manage-fields.php:
|
1985 |
msgid "Luxembourg"
|
1986 |
msgstr ""
|
1987 |
|
1988 |
-
#: admin/manage-fields.php:
|
1989 |
msgid "Macao"
|
1990 |
msgstr ""
|
1991 |
|
1992 |
-
#: admin/manage-fields.php:
|
1993 |
msgid "Macedonia"
|
1994 |
msgstr ""
|
1995 |
|
1996 |
-
#: admin/manage-fields.php:
|
1997 |
msgid "Madagascar"
|
1998 |
msgstr ""
|
1999 |
|
2000 |
-
#: admin/manage-fields.php:
|
2001 |
msgid "Malawi"
|
2002 |
msgstr ""
|
2003 |
|
2004 |
-
#: admin/manage-fields.php:
|
2005 |
msgid "Malaysia"
|
2006 |
msgstr ""
|
2007 |
|
2008 |
-
#: admin/manage-fields.php:
|
2009 |
msgid "Maldives"
|
2010 |
msgstr ""
|
2011 |
|
2012 |
-
#: admin/manage-fields.php:
|
2013 |
msgid "Mali"
|
2014 |
msgstr ""
|
2015 |
|
2016 |
-
#: admin/manage-fields.php:
|
2017 |
msgid "Malta"
|
2018 |
msgstr ""
|
2019 |
|
2020 |
-
#: admin/manage-fields.php:
|
2021 |
msgid "Marshall Islands"
|
2022 |
msgstr ""
|
2023 |
|
2024 |
-
#: admin/manage-fields.php:
|
2025 |
msgid "Martinique"
|
2026 |
msgstr ""
|
2027 |
|
2028 |
-
#: admin/manage-fields.php:
|
2029 |
msgid "Mauritania"
|
2030 |
msgstr ""
|
2031 |
|
2032 |
-
#: admin/manage-fields.php:
|
2033 |
msgid "Mauritius"
|
2034 |
msgstr ""
|
2035 |
|
2036 |
-
#: admin/manage-fields.php:
|
2037 |
msgid "Mayotte"
|
2038 |
msgstr ""
|
2039 |
|
2040 |
-
#: admin/manage-fields.php:
|
2041 |
msgid "Mexico"
|
2042 |
msgstr ""
|
2043 |
|
2044 |
-
#: admin/manage-fields.php:
|
2045 |
msgid "Micronesia"
|
2046 |
msgstr ""
|
2047 |
|
2048 |
-
#: admin/manage-fields.php:
|
2049 |
msgid "Moldova"
|
2050 |
msgstr ""
|
2051 |
|
2052 |
-
#: admin/manage-fields.php:
|
2053 |
msgid "Monaco"
|
2054 |
msgstr ""
|
2055 |
|
2056 |
-
#: admin/manage-fields.php:
|
2057 |
msgid "Mongolia"
|
2058 |
msgstr ""
|
2059 |
|
2060 |
-
#: admin/manage-fields.php:
|
2061 |
msgid "Montenegro"
|
2062 |
msgstr ""
|
2063 |
|
2064 |
-
#: admin/manage-fields.php:
|
2065 |
msgid "Montserrat"
|
2066 |
msgstr ""
|
2067 |
|
2068 |
-
#: admin/manage-fields.php:
|
2069 |
msgid "Morocco"
|
2070 |
msgstr ""
|
2071 |
|
2072 |
-
#: admin/manage-fields.php:
|
2073 |
msgid "Mozambique"
|
2074 |
msgstr ""
|
2075 |
|
2076 |
-
#: admin/manage-fields.php:
|
2077 |
msgid "Myanmar"
|
2078 |
msgstr ""
|
2079 |
|
2080 |
-
#: admin/manage-fields.php:
|
2081 |
msgid "Namibia"
|
2082 |
msgstr ""
|
2083 |
|
2084 |
-
#: admin/manage-fields.php:
|
2085 |
msgid "Nauru"
|
2086 |
msgstr ""
|
2087 |
|
2088 |
-
#: admin/manage-fields.php:
|
2089 |
msgid "Nepal"
|
2090 |
msgstr ""
|
2091 |
|
2092 |
-
#: admin/manage-fields.php:
|
2093 |
msgid "Netherlands"
|
2094 |
msgstr ""
|
2095 |
|
2096 |
-
#: admin/manage-fields.php:
|
2097 |
msgid "New Caledonia"
|
2098 |
msgstr ""
|
2099 |
|
2100 |
-
#: admin/manage-fields.php:
|
2101 |
msgid "New Zealand"
|
2102 |
msgstr ""
|
2103 |
|
2104 |
-
#: admin/manage-fields.php:
|
2105 |
msgid "Nicaragua"
|
2106 |
msgstr ""
|
2107 |
|
2108 |
-
#: admin/manage-fields.php:
|
2109 |
msgid "Niger"
|
2110 |
msgstr ""
|
2111 |
|
2112 |
-
#: admin/manage-fields.php:
|
2113 |
msgid "Nigeria"
|
2114 |
msgstr ""
|
2115 |
|
2116 |
-
#: admin/manage-fields.php:
|
2117 |
msgid "Niue"
|
2118 |
msgstr ""
|
2119 |
|
2120 |
-
#: admin/manage-fields.php:
|
2121 |
msgid "Norfolk Island"
|
2122 |
msgstr ""
|
2123 |
|
2124 |
-
#: admin/manage-fields.php:
|
2125 |
msgid "North Korea"
|
2126 |
msgstr ""
|
2127 |
|
2128 |
-
#: admin/manage-fields.php:
|
2129 |
msgid "Northern Mariana Islands"
|
2130 |
msgstr ""
|
2131 |
|
2132 |
-
#: admin/manage-fields.php:
|
2133 |
msgid "Norway"
|
2134 |
msgstr ""
|
2135 |
|
2136 |
-
#: admin/manage-fields.php:
|
2137 |
msgid "Oman"
|
2138 |
msgstr ""
|
2139 |
|
2140 |
-
#: admin/manage-fields.php:
|
2141 |
msgid "Pakistan"
|
2142 |
msgstr ""
|
2143 |
|
2144 |
-
#: admin/manage-fields.php:
|
2145 |
msgid "Palau"
|
2146 |
msgstr ""
|
2147 |
|
2148 |
-
#: admin/manage-fields.php:
|
2149 |
msgid "Palestinian Territory"
|
2150 |
msgstr ""
|
2151 |
|
2152 |
-
#: admin/manage-fields.php:
|
2153 |
msgid "Panama"
|
2154 |
msgstr ""
|
2155 |
|
2156 |
-
#: admin/manage-fields.php:
|
2157 |
msgid "Papua New Guinea"
|
2158 |
msgstr ""
|
2159 |
|
2160 |
-
#: admin/manage-fields.php:
|
2161 |
msgid "Paraguay"
|
2162 |
msgstr ""
|
2163 |
|
2164 |
-
#: admin/manage-fields.php:
|
2165 |
msgid "Peru"
|
2166 |
msgstr ""
|
2167 |
|
2168 |
-
#: admin/manage-fields.php:
|
2169 |
msgid "Philippines"
|
2170 |
msgstr ""
|
2171 |
|
2172 |
-
#: admin/manage-fields.php:
|
2173 |
msgid "Pitcairn"
|
2174 |
msgstr ""
|
2175 |
|
2176 |
-
#: admin/manage-fields.php:
|
2177 |
msgid "Poland"
|
2178 |
msgstr ""
|
2179 |
|
2180 |
-
#: admin/manage-fields.php:
|
2181 |
msgid "Portugal"
|
2182 |
msgstr ""
|
2183 |
|
2184 |
-
#: admin/manage-fields.php:
|
2185 |
msgid "Puerto Rico"
|
2186 |
msgstr ""
|
2187 |
|
2188 |
-
#: admin/manage-fields.php:
|
2189 |
msgid "Qatar"
|
2190 |
msgstr ""
|
2191 |
|
2192 |
-
#: admin/manage-fields.php:
|
2193 |
msgid "Republic of the Congo"
|
2194 |
msgstr ""
|
2195 |
|
2196 |
-
#: admin/manage-fields.php:
|
2197 |
msgid "Reunion"
|
2198 |
msgstr ""
|
2199 |
|
2200 |
-
#: admin/manage-fields.php:
|
2201 |
msgid "Romania"
|
2202 |
msgstr ""
|
2203 |
|
2204 |
-
#: admin/manage-fields.php:
|
2205 |
msgid "Russia"
|
2206 |
msgstr ""
|
2207 |
|
2208 |
-
#: admin/manage-fields.php:
|
2209 |
msgid "Rwanda"
|
2210 |
msgstr ""
|
2211 |
|
2212 |
-
#: admin/manage-fields.php:
|
2213 |
msgid "Saint Barthelemy"
|
2214 |
msgstr ""
|
2215 |
|
2216 |
-
#: admin/manage-fields.php:
|
2217 |
msgid "Saint Helena"
|
2218 |
msgstr ""
|
2219 |
|
2220 |
-
#: admin/manage-fields.php:
|
2221 |
msgid "Saint Kitts and Nevis"
|
2222 |
msgstr ""
|
2223 |
|
2224 |
-
#: admin/manage-fields.php:
|
2225 |
msgid "Saint Lucia"
|
2226 |
msgstr ""
|
2227 |
|
2228 |
-
#: admin/manage-fields.php:
|
2229 |
msgid "Saint Martin"
|
2230 |
msgstr ""
|
2231 |
|
2232 |
-
#: admin/manage-fields.php:
|
2233 |
msgid "Saint Pierre and Miquelon"
|
2234 |
msgstr ""
|
2235 |
|
2236 |
-
#: admin/manage-fields.php:
|
2237 |
msgid "Saint Vincent and the Grenadines"
|
2238 |
msgstr ""
|
2239 |
|
2240 |
-
#: admin/manage-fields.php:
|
2241 |
msgid "Samoa"
|
2242 |
msgstr ""
|
2243 |
|
2244 |
-
#: admin/manage-fields.php:
|
2245 |
msgid "San Marino"
|
2246 |
msgstr ""
|
2247 |
|
2248 |
-
#: admin/manage-fields.php:
|
2249 |
msgid "Sao Tome and Principe"
|
2250 |
msgstr ""
|
2251 |
|
2252 |
-
#: admin/manage-fields.php:
|
2253 |
msgid "Saudi Arabia"
|
2254 |
msgstr ""
|
2255 |
|
2256 |
-
#: admin/manage-fields.php:
|
2257 |
msgid "Senegal"
|
2258 |
msgstr ""
|
2259 |
|
2260 |
-
#: admin/manage-fields.php:
|
2261 |
msgid "Serbia"
|
2262 |
msgstr ""
|
2263 |
|
2264 |
-
#: admin/manage-fields.php:
|
2265 |
msgid "Seychelles"
|
2266 |
msgstr ""
|
2267 |
|
2268 |
-
#: admin/manage-fields.php:
|
2269 |
msgid "Sierra Leone"
|
2270 |
msgstr ""
|
2271 |
|
2272 |
-
#: admin/manage-fields.php:
|
2273 |
msgid "Singapore"
|
2274 |
msgstr ""
|
2275 |
|
2276 |
-
#: admin/manage-fields.php:
|
2277 |
msgid "Sint Maarten"
|
2278 |
msgstr ""
|
2279 |
|
2280 |
-
#: admin/manage-fields.php:
|
2281 |
msgid "Slovakia"
|
2282 |
msgstr ""
|
2283 |
|
2284 |
-
#: admin/manage-fields.php:
|
2285 |
msgid "Slovenia"
|
2286 |
msgstr ""
|
2287 |
|
2288 |
-
#: admin/manage-fields.php:
|
2289 |
msgid "Solomon Islands"
|
2290 |
msgstr ""
|
2291 |
|
2292 |
-
#: admin/manage-fields.php:
|
2293 |
msgid "Somalia"
|
2294 |
msgstr ""
|
2295 |
|
2296 |
-
#: admin/manage-fields.php:
|
2297 |
msgid "South Africa"
|
2298 |
msgstr ""
|
2299 |
|
2300 |
-
#: admin/manage-fields.php:
|
2301 |
msgid "South Georgia and the South Sandwich Islands"
|
2302 |
msgstr ""
|
2303 |
|
2304 |
-
#: admin/manage-fields.php:
|
2305 |
msgid "South Korea"
|
2306 |
msgstr ""
|
2307 |
|
2308 |
-
#: admin/manage-fields.php:
|
2309 |
msgid "South Sudan"
|
2310 |
msgstr ""
|
2311 |
|
2312 |
-
#: admin/manage-fields.php:
|
2313 |
msgid "Spain"
|
2314 |
msgstr ""
|
2315 |
|
2316 |
-
#: admin/manage-fields.php:
|
2317 |
msgid "Sri Lanka"
|
2318 |
msgstr ""
|
2319 |
|
2320 |
-
#: admin/manage-fields.php:
|
2321 |
msgid "Sudan"
|
2322 |
msgstr ""
|
2323 |
|
2324 |
-
#: admin/manage-fields.php:
|
2325 |
msgid "Suriname"
|
2326 |
msgstr ""
|
2327 |
|
2328 |
-
#: admin/manage-fields.php:
|
2329 |
msgid "Svalbard and Jan Mayen"
|
2330 |
msgstr ""
|
2331 |
|
2332 |
-
#: admin/manage-fields.php:
|
2333 |
msgid "Swaziland"
|
2334 |
msgstr ""
|
2335 |
|
2336 |
-
#: admin/manage-fields.php:
|
2337 |
msgid "Sweden"
|
2338 |
msgstr ""
|
2339 |
|
2340 |
-
#: admin/manage-fields.php:
|
2341 |
msgid "Switzerland"
|
2342 |
msgstr ""
|
2343 |
|
2344 |
-
#: admin/manage-fields.php:
|
2345 |
msgid "Syria"
|
2346 |
msgstr ""
|
2347 |
|
2348 |
-
#: admin/manage-fields.php:
|
2349 |
msgid "Taiwan"
|
2350 |
msgstr ""
|
2351 |
|
2352 |
-
#: admin/manage-fields.php:
|
2353 |
msgid "Tajikistan"
|
2354 |
msgstr ""
|
2355 |
|
2356 |
-
#: admin/manage-fields.php:
|
2357 |
msgid "Tanzania"
|
2358 |
msgstr ""
|
2359 |
|
2360 |
-
#: admin/manage-fields.php:
|
2361 |
msgid "Thailand"
|
2362 |
msgstr ""
|
2363 |
|
2364 |
-
#: admin/manage-fields.php:
|
2365 |
msgid "Togo"
|
2366 |
msgstr ""
|
2367 |
|
2368 |
-
#: admin/manage-fields.php:
|
2369 |
msgid "Tokelau"
|
2370 |
msgstr ""
|
2371 |
|
2372 |
-
#: admin/manage-fields.php:
|
2373 |
msgid "Tonga"
|
2374 |
msgstr ""
|
2375 |
|
2376 |
-
#: admin/manage-fields.php:
|
2377 |
msgid "Trinidad and Tobago"
|
2378 |
msgstr ""
|
2379 |
|
2380 |
-
#: admin/manage-fields.php:
|
2381 |
msgid "Tunisia"
|
2382 |
msgstr ""
|
2383 |
|
2384 |
-
#: admin/manage-fields.php:
|
2385 |
msgid "Turkey"
|
2386 |
msgstr ""
|
2387 |
|
2388 |
-
#: admin/manage-fields.php:
|
2389 |
msgid "Turkmenistan"
|
2390 |
msgstr ""
|
2391 |
|
2392 |
-
#: admin/manage-fields.php:
|
2393 |
msgid "Turks and Caicos Islands"
|
2394 |
msgstr ""
|
2395 |
|
2396 |
-
#: admin/manage-fields.php:
|
2397 |
msgid "Tuvalu"
|
2398 |
msgstr ""
|
2399 |
|
2400 |
-
#: admin/manage-fields.php:
|
2401 |
msgid "U.S. Virgin Islands"
|
2402 |
msgstr ""
|
2403 |
|
2404 |
-
#: admin/manage-fields.php:
|
2405 |
msgid "Uganda"
|
2406 |
msgstr ""
|
2407 |
|
2408 |
-
#: admin/manage-fields.php:
|
2409 |
msgid "Ukraine"
|
2410 |
msgstr ""
|
2411 |
|
2412 |
-
#: admin/manage-fields.php:
|
2413 |
msgid "United Arab Emirates"
|
2414 |
msgstr ""
|
2415 |
|
2416 |
-
#: admin/manage-fields.php:
|
2417 |
msgid "United Kingdom"
|
2418 |
msgstr ""
|
2419 |
|
2420 |
-
#: admin/manage-fields.php:
|
2421 |
msgid "United States"
|
2422 |
msgstr ""
|
2423 |
|
2424 |
-
#: admin/manage-fields.php:
|
2425 |
msgid "United States Minor Outlying Islands"
|
2426 |
msgstr ""
|
2427 |
|
2428 |
-
#: admin/manage-fields.php:
|
2429 |
msgid "Uruguay"
|
2430 |
msgstr ""
|
2431 |
|
2432 |
-
#: admin/manage-fields.php:
|
2433 |
msgid "Uzbekistan"
|
2434 |
msgstr ""
|
2435 |
|
2436 |
-
#: admin/manage-fields.php:
|
2437 |
msgid "Vanuatu"
|
2438 |
msgstr ""
|
2439 |
|
2440 |
-
#: admin/manage-fields.php:
|
2441 |
msgid "Vatican"
|
2442 |
msgstr ""
|
2443 |
|
2444 |
-
#: admin/manage-fields.php:
|
2445 |
msgid "Venezuela"
|
2446 |
msgstr ""
|
2447 |
|
2448 |
-
#: admin/manage-fields.php:
|
2449 |
msgid "Vietnam"
|
2450 |
msgstr ""
|
2451 |
|
2452 |
-
#: admin/manage-fields.php:
|
2453 |
msgid "Wallis and Futuna"
|
2454 |
msgstr ""
|
2455 |
|
2456 |
-
#: admin/manage-fields.php:
|
2457 |
msgid "Western Sahara"
|
2458 |
msgstr ""
|
2459 |
|
2460 |
-
#: admin/manage-fields.php:
|
2461 |
msgid "Yemen"
|
2462 |
msgstr ""
|
2463 |
|
2464 |
-
#: admin/manage-fields.php:
|
2465 |
msgid "Zambia"
|
2466 |
msgstr ""
|
2467 |
|
2468 |
-
#: admin/manage-fields.php:
|
2469 |
msgid "Zimbabwe"
|
2470 |
msgstr ""
|
2471 |
|
2472 |
-
#: admin/manage-fields.php:
|
2473 |
msgid "Albania Lek"
|
2474 |
msgstr ""
|
2475 |
|
2476 |
-
#: admin/manage-fields.php:
|
2477 |
msgid "Afghanistan Afghani"
|
2478 |
msgstr ""
|
2479 |
|
2480 |
-
#: admin/manage-fields.php:
|
2481 |
msgid "Argentina Peso"
|
2482 |
msgstr ""
|
2483 |
|
2484 |
-
#: admin/manage-fields.php:
|
2485 |
msgid "Aruba Guilder"
|
2486 |
msgstr ""
|
2487 |
|
2488 |
-
#: admin/manage-fields.php:
|
2489 |
msgid "Australia Dollar"
|
2490 |
msgstr ""
|
2491 |
|
2492 |
-
#: admin/manage-fields.php:
|
2493 |
msgid "Azerbaijan New Manat"
|
2494 |
msgstr ""
|
2495 |
|
2496 |
-
#:
|
13 |
"X-Poedit-SourceCharset: UTF-8\n"
|
14 |
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
15 |
|
16 |
+
#: index.php:370
|
17 |
msgid "To enable updates, your licence needs to be renewed. Please go to the <a href=\"%s\">Cozmoslabs Account</a> page and login to renew."
|
18 |
msgstr ""
|
19 |
|
20 |
+
#: index.php:366
|
21 |
msgid "To enable updates, please enter your serial number on the <a href=\"%s\">Register Version</a> page. If you don't have a serial number, please see <a href=\"%s\" target=\"_blank\">details & pricing</a>."
|
22 |
msgstr ""
|
23 |
|
45 |
msgid "These Add-ons are available with the Pro and Unlimited license"
|
46 |
msgstr ""
|
47 |
|
48 |
+
#: admin/add-ons.php:45, admin/basic-info.php:177
|
49 |
msgid "Multiple Registration Forms"
|
50 |
msgstr ""
|
51 |
|
57 |
msgid "Multiple Edit Profile Forms"
|
58 |
msgstr ""
|
59 |
|
60 |
+
#: admin/add-ons.php:53, admin/basic-info.php:182
|
61 |
msgid "Allow different user roles to edit their specific information. Set up multiple edit-profile forms with different fields for certain user roles."
|
62 |
msgstr ""
|
63 |
|
64 |
+
#: admin/add-ons.php:59, admin/basic-info.php:163, add-ons/user-listing/userlisting.php:11, add-ons/user-listing/userlisting.php:12, add-ons/user-listing/userlisting.php:17, add-ons/user-listing/userlisting.php:23, assets/misc/elementor/widgets/class-pb-widget-ul.php:58, assets/misc/elementor/widgets/class-pb-widget-ul.php:97
|
65 |
msgid "User Listing"
|
66 |
msgstr ""
|
67 |
|
69 |
msgid "Easy to edit templates for listing your users as well as creating single user pages."
|
70 |
msgstr ""
|
71 |
|
72 |
+
#: admin/add-ons.php:73, admin/basic-info.php:171, add-ons/custom-redirects/custom_redirects_admin.php:33, add-ons/custom-redirects/custom_redirects_admin.php:34
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
msgid "Custom Redirects"
|
74 |
msgstr ""
|
75 |
|
77 |
msgid "Redirect users after login, after they first register or when they try to access the default WordPress dashboard, login, lost password and registration forms."
|
78 |
msgstr ""
|
79 |
|
80 |
+
#: admin/add-ons.php:80, admin/basic-info.php:185
|
81 |
msgid "Repeater Fields"
|
82 |
msgstr ""
|
83 |
|
309 |
msgid "Show"
|
310 |
msgstr ""
|
311 |
|
312 |
+
#: admin/admin-bar.php:81, add-ons/user-listing/userlisting.php:1814
|
313 |
msgid "Hide"
|
314 |
msgstr ""
|
315 |
|
316 |
+
#: admin/admin-bar.php:92, admin/general-settings.php:390, admin/private-website.php:162, features/functions.php:1055, add-ons-advanced/woocommerce/woosync-page.php:149, features/content-restriction/content-restriction.php:174, features/two-factor-authentication/class-two-factor-authentication.php:156, assets/lib/class-mustache-templates/class-mustache-templates.php:421, assets/lib/wck-api/wordpress-creation-kit.php:410, admin/advanced-settings/includes/views/view-admin.php:112, admin/advanced-settings/includes/views/view-fields.php:309, admin/advanced-settings/includes/views/view-forms.php:407, admin/advanced-settings/includes/views/view-shortcodes.php:77, admin/advanced-settings/includes/views/view-userlisting.php:91
|
317 |
msgid "Save Changes"
|
318 |
msgstr ""
|
319 |
|
337 |
msgid "<strong>ERROR</strong>: The password must have the minimum length of %s characters"
|
338 |
msgstr ""
|
339 |
|
340 |
+
#: admin/admin-functions.php:139, admin/general-settings.php:340
|
341 |
msgid "Very weak"
|
342 |
msgstr ""
|
343 |
|
344 |
+
#: admin/admin-functions.php:139, admin/general-settings.php:341, features/functions.php:788, features/functions.php:812
|
345 |
msgid "Weak"
|
346 |
msgstr ""
|
347 |
|
348 |
+
#: admin/admin-functions.php:139, admin/general-settings.php:342, features/functions.php:788, features/functions.php:812
|
349 |
msgid "Medium"
|
350 |
msgstr ""
|
351 |
|
352 |
+
#: admin/admin-functions.php:139, admin/general-settings.php:343, features/functions.php:788, features/functions.php:812
|
353 |
msgid "Strong"
|
354 |
msgstr ""
|
355 |
|
365 |
msgid "Add Field"
|
366 |
msgstr ""
|
367 |
|
368 |
+
#: admin/admin-functions.php:212, admin/general-settings.php:120, admin/general-settings.php:120, add-ons-advanced/social-connect/index.php:260
|
369 |
msgid "Settings"
|
370 |
msgstr ""
|
371 |
|
385 |
msgid "See details"
|
386 |
msgstr ""
|
387 |
|
388 |
+
#: admin/admin-functions.php:260, admin/review.php:94, add-ons/user-listing/userlisting.php:3392
|
389 |
msgid "Dismiss this notice."
|
390 |
msgstr ""
|
391 |
|
489 |
msgid "Restrict users from accessing certain pages, posts or custom post types based on user role or logged-in status."
|
490 |
msgstr ""
|
491 |
|
492 |
+
#: admin/basic-info.php:91, admin/general-settings.php:42, admin/general-settings.php:36
|
493 |
+
msgid "Email Customizer"
|
494 |
msgstr ""
|
495 |
|
496 |
#: admin/basic-info.php:92
|
497 |
+
msgid "Personalize all emails sent to your users or admins. On registration, email confirmation, admin approval / un-approval."
|
498 |
msgstr ""
|
499 |
|
500 |
#: admin/basic-info.php:95
|
501 |
+
msgid "Minimum Password Length and Strength Meter"
|
502 |
msgstr ""
|
503 |
|
504 |
#: admin/basic-info.php:96
|
505 |
+
msgid "Eliminate weak passwords altogether by setting a minimum password length and enforcing a certain password strength."
|
506 |
+
msgstr ""
|
507 |
+
|
508 |
+
#: admin/basic-info.php:99
|
509 |
+
msgid "Login with Email or Username"
|
510 |
+
msgstr ""
|
511 |
+
|
512 |
+
#: admin/basic-info.php:100
|
513 |
msgid "Allow users to log in with their email or username when accessing your site."
|
514 |
msgstr ""
|
515 |
|
516 |
+
#: admin/basic-info.php:103, features/roles-editor/roles-editor.php:61, features/roles-editor/roles-editor.php:62, features/roles-editor/roles-editor.php:275, features/roles-editor/roles-editor.php:276, features/roles-editor/roles-editor.php:281, features/roles-editor/roles-editor.php:288
|
517 |
msgid "Roles Editor"
|
518 |
msgstr ""
|
519 |
|
520 |
+
#: admin/basic-info.php:104
|
521 |
msgid "Add, remove, clone and edit roles and also capabilities for these roles."
|
522 |
msgstr ""
|
523 |
|
524 |
+
#: admin/basic-info.php:117
|
525 |
msgid "Customize Your Forms The Way You Want (*)"
|
526 |
msgstr ""
|
527 |
|
528 |
+
#: admin/basic-info.php:118
|
529 |
msgid "With Extra Profile Fields you can create the exact registration form your project needs."
|
530 |
msgstr ""
|
531 |
|
532 |
+
#: admin/basic-info.php:122
|
533 |
msgid "Get started with extra fields"
|
534 |
msgstr ""
|
535 |
|
536 |
+
#: admin/basic-info.php:120
|
537 |
msgid "Extra Profile Fields are available in Basic or PRO versions"
|
538 |
msgstr ""
|
539 |
|
540 |
+
#: admin/basic-info.php:125
|
|
|
|
|
|
|
|
|
541 |
msgid "Generic Uploads"
|
542 |
msgstr ""
|
543 |
|
544 |
+
#: admin/basic-info.php:126
|
545 |
msgid "Agree To Terms Checkbox"
|
546 |
msgstr ""
|
547 |
|
548 |
+
#: admin/basic-info.php:127
|
549 |
msgid "Datepicker"
|
550 |
msgstr ""
|
551 |
|
552 |
+
#: admin/basic-info.php:128
|
553 |
msgid "Timepicker"
|
554 |
msgstr ""
|
555 |
|
556 |
+
#: admin/basic-info.php:129
|
557 |
msgid "Colorpicker"
|
558 |
msgstr ""
|
559 |
|
560 |
+
#: admin/basic-info.php:130
|
|
|
|
|
|
|
|
|
561 |
msgid "Country Select"
|
562 |
msgstr ""
|
563 |
|
564 |
+
#: admin/basic-info.php:131
|
565 |
msgid "Currency Select"
|
566 |
msgstr ""
|
567 |
|
568 |
+
#: admin/basic-info.php:132
|
569 |
msgid "Timezone Select"
|
570 |
msgstr ""
|
571 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
572 |
#: admin/basic-info.php:136
|
573 |
+
msgid "Map"
|
574 |
msgstr ""
|
575 |
|
576 |
#: admin/basic-info.php:137
|
577 |
+
msgid "Select 2 Multiple"
|
578 |
msgstr ""
|
579 |
|
580 |
+
#: admin/basic-info.php:138, add-ons-advanced/woocommerce/billing-fields.php:15
|
581 |
+
msgid "Phone"
|
582 |
msgstr ""
|
583 |
|
584 |
#: admin/basic-info.php:139
|
585 |
+
msgid "Hidden Input"
|
586 |
msgstr ""
|
587 |
|
588 |
#: admin/basic-info.php:140
|
589 |
+
msgid "Number"
|
590 |
msgstr ""
|
591 |
|
592 |
#: admin/basic-info.php:141
|
593 |
+
msgid "Validation"
|
594 |
msgstr ""
|
595 |
|
596 |
#: admin/basic-info.php:142
|
597 |
+
msgid "Select CPT"
|
598 |
+
msgstr ""
|
599 |
+
|
600 |
+
#: admin/basic-info.php:143
|
601 |
msgid "HTML"
|
602 |
msgstr ""
|
603 |
|
604 |
+
#: admin/basic-info.php:152
|
605 |
msgid "Powerful Add-ons (**)"
|
606 |
msgstr ""
|
607 |
|
608 |
+
#: admin/basic-info.php:153
|
609 |
msgid "Everything you will need to manage your users is probably already available using the Pro Add-ons."
|
610 |
msgstr ""
|
611 |
|
612 |
+
#: admin/basic-info.php:155
|
613 |
msgid "Enable your add-ons"
|
614 |
msgstr ""
|
615 |
|
616 |
+
#: admin/basic-info.php:158
|
617 |
msgid "Find out more about PRO Modules"
|
618 |
msgstr ""
|
619 |
|
620 |
+
#: admin/basic-info.php:167
|
621 |
msgid "To create a page containing the users registered to this current site/blog, insert the following shortcode in a page of your chosing: %s."
|
622 |
msgstr ""
|
623 |
|
624 |
+
#: admin/basic-info.php:165
|
625 |
msgid "Easy to edit templates for listing your website users as well as creating single user pages. Shortcode based, offering many options to customize your listings."
|
626 |
msgstr ""
|
627 |
|
628 |
+
#: admin/basic-info.php:172
|
|
|
|
|
|
|
|
|
629 |
msgid "Keep your users out of the WordPress dashboard, redirect them to the front-page after login or registration, everything is just a few clicks away."
|
630 |
msgstr ""
|
631 |
|
632 |
+
#: admin/basic-info.php:178
|
633 |
msgid "Set up multiple registration forms with different fields for certain user roles. Capture different information from different types of users."
|
634 |
msgstr ""
|
635 |
|
636 |
+
#: admin/basic-info.php:181
|
637 |
msgid "Multiple Edit-profile Forms"
|
638 |
msgstr ""
|
639 |
|
640 |
+
#: admin/basic-info.php:186
|
641 |
msgid "Set up a repeating group of fields on register and edit profile forms. Limit the number of repeated groups for each user role."
|
642 |
msgstr ""
|
643 |
|
644 |
+
#: admin/basic-info.php:222
|
645 |
msgid " * only available in the %1$sBasic and Pro versions%2$s."
|
646 |
msgstr ""
|
647 |
|
648 |
+
#: admin/basic-info.php:223
|
649 |
msgid "** only available in the %1$sPro version%2$s."
|
650 |
msgstr ""
|
651 |
|
681 |
msgid "Give us another try! Open a support ticket <a href='https://www.cozmoslabs.com/support/open-ticket/' target='_blank'>here</a>"
|
682 |
msgstr ""
|
683 |
|
684 |
+
#: admin/feedback.php:47, admin/manage-fields.php:76
|
685 |
msgid "Other"
|
686 |
msgstr ""
|
687 |
|
745 |
msgid "Two-Factor Authentication"
|
746 |
msgstr ""
|
747 |
|
748 |
+
#: admin/general-settings.php:43, admin/general-settings.php:37
|
749 |
msgid "User Emails"
|
750 |
msgstr ""
|
751 |
|
752 |
+
#: admin/general-settings.php:44, admin/general-settings.php:38
|
753 |
msgid "Administrator Emails"
|
754 |
msgstr ""
|
755 |
|
756 |
+
#: admin/general-settings.php:141
|
757 |
msgid "Profile Builder Settings"
|
758 |
msgstr ""
|
759 |
|
760 |
+
#: admin/general-settings.php:154
|
761 |
msgid "Load Profile Builder's own CSS file in the front-end:"
|
762 |
msgstr ""
|
763 |
|
764 |
+
#: admin/general-settings.php:157, admin/general-settings.php:170, admin/general-settings.php:185, admin/general-settings.php:234, admin/general-settings.php:281, admin/manage-fields.php:240, admin/private-website.php:70, admin/private-website.php:137, admin/private-website.php:150, add-ons/multiple-forms/edit-profile-forms.php:206, add-ons/multiple-forms/register-forms.php:229, add-ons/multiple-forms/register-forms.php:230, add-ons/user-listing/userlisting.php:2605, add-ons-advanced/bbpress/bbpress-page.php:161, add-ons-advanced/social-connect/index.php:347, add-ons-advanced/social-connect/index.php:406, features/content-restriction/content-restriction.php:90, features/two-factor-authentication/class-two-factor-authentication.php:125, admin/advanced-settings/includes/forms/placeholder-labels.php:135, admin/advanced-settings/includes/views/view-admin.php:18, admin/advanced-settings/includes/views/view-admin.php:34, admin/advanced-settings/includes/views/view-admin.php:50, admin/advanced-settings/includes/views/view-admin.php:69, admin/advanced-settings/includes/views/view-admin.php:100, admin/advanced-settings/includes/views/view-fields.php:18, admin/advanced-settings/includes/views/view-fields.php:66, admin/advanced-settings/includes/views/view-fields.php:184, admin/advanced-settings/includes/views/view-fields.php:200, admin/advanced-settings/includes/views/view-fields.php:220, admin/advanced-settings/includes/views/view-fields.php:243, admin/advanced-settings/includes/views/view-fields.php:263, admin/advanced-settings/includes/views/view-fields.php:280, admin/advanced-settings/includes/views/view-fields.php:296, admin/advanced-settings/includes/views/view-forms.php:19, admin/advanced-settings/includes/views/view-forms.php:148, admin/advanced-settings/includes/views/view-forms.php:166, admin/advanced-settings/includes/views/view-forms.php:183, admin/advanced-settings/includes/views/view-forms.php:198, admin/advanced-settings/includes/views/view-forms.php:213, admin/advanced-settings/includes/views/view-forms.php:233, admin/advanced-settings/includes/views/view-forms.php:250, admin/advanced-settings/includes/views/view-forms.php:286, admin/advanced-settings/includes/views/view-forms.php:307, admin/advanced-settings/includes/views/view-forms.php:327, admin/advanced-settings/includes/views/view-forms.php:349, admin/advanced-settings/includes/views/view-forms.php:371, admin/advanced-settings/includes/views/view-forms.php:391, admin/advanced-settings/includes/views/view-shortcodes.php:16, admin/advanced-settings/includes/views/view-shortcodes.php:32, admin/advanced-settings/includes/views/view-shortcodes.php:48, admin/advanced-settings/includes/views/view-shortcodes.php:64, admin/advanced-settings/includes/views/view-userlisting.php:53, admin/advanced-settings/includes/views/view-userlisting.php:75, assets/misc/elementor/widgets/class-pb-widget-l.php:75, assets/misc/elementor/widgets/class-pb-widget-rf-epf.php:183, assets/misc/elementor/widgets/class-pb-widget-ul.php:109
|
765 |
msgid "Yes"
|
766 |
msgstr ""
|
767 |
|
768 |
+
#: admin/general-settings.php:159
|
769 |
msgid "You can find the default file here: %1$s"
|
770 |
msgstr ""
|
771 |
|
772 |
+
#: admin/general-settings.php:166
|
773 |
msgid "Automatically Log In:"
|
774 |
msgstr ""
|
775 |
|
776 |
+
#: admin/general-settings.php:171, admin/general-settings.php:186, admin/general-settings.php:235, admin/general-settings.php:280, admin/private-website.php:69, admin/private-website.php:136, admin/private-website.php:151, add-ons/multiple-forms/edit-profile-forms.php:206, add-ons/multiple-forms/register-forms.php:229, add-ons/multiple-forms/register-forms.php:230, add-ons-advanced/bbpress/bbpress-page.php:160, add-ons-advanced/social-connect/index.php:348, add-ons-advanced/social-connect/index.php:407, features/content-restriction/content-restriction.php:89, features/two-factor-authentication/class-two-factor-authentication.php:124, admin/advanced-settings/includes/forms/placeholder-labels.php:136, assets/misc/elementor/widgets/class-pb-widget-l.php:76, assets/misc/elementor/widgets/class-pb-widget-rf-epf.php:184, assets/misc/elementor/widgets/class-pb-widget-ul.php:110
|
777 |
msgid "No"
|
778 |
msgstr ""
|
779 |
|
780 |
+
#: admin/general-settings.php:174
|
781 |
msgid "Select \"Yes\" to automatically log in new users after successful registration."
|
782 |
msgstr ""
|
783 |
|
784 |
+
#: admin/general-settings.php:181
|
785 |
msgid "\"Email Confirmation\" Activated:"
|
786 |
msgstr ""
|
787 |
|
788 |
+
#: admin/general-settings.php:189
|
789 |
msgid "This works with front-end forms only. Recommended to redirect WP default registration to a Profile Builder one using \"Custom Redirects\" module."
|
790 |
msgstr ""
|
791 |
|
792 |
+
#: admin/general-settings.php:191
|
793 |
msgid "You can find a list of unconfirmed email addresses %1$sUsers > All Users > Email Confirmation%2$s."
|
794 |
msgstr ""
|
795 |
|
796 |
+
#: admin/general-settings.php:199
|
797 |
msgid "\"Email Confirmation\" Landing Page:"
|
798 |
msgstr ""
|
799 |
|
800 |
+
#: admin/general-settings.php:204
|
801 |
msgid "Existing Pages"
|
802 |
msgstr ""
|
803 |
|
804 |
+
#: admin/general-settings.php:219
|
805 |
msgid "Specify the page where the users will be directed when confirming the email account. This page can differ from the register page(s) and can be changed at any time. If none selected, a simple confirmation page will be displayed for the user."
|
806 |
msgstr ""
|
807 |
|
808 |
+
#: admin/general-settings.php:230
|
809 |
msgid "\"Admin Approval\" Activated:"
|
810 |
msgstr ""
|
811 |
|
812 |
+
#: admin/general-settings.php:238
|
813 |
msgid "You can find a list of users at %1$sUsers > All Users > Admin Approval%2$s."
|
814 |
msgstr ""
|
815 |
|
816 |
+
#: admin/general-settings.php:245
|
817 |
msgid "\"Admin Approval\" on User Role:"
|
818 |
msgstr ""
|
819 |
|
820 |
+
#: admin/general-settings.php:264
|
821 |
msgid "Select on what user roles to activate Admin Approval."
|
822 |
msgstr ""
|
823 |
|
824 |
+
#: admin/general-settings.php:276
|
825 |
msgid "\"Roles Editor\" Activated:"
|
826 |
msgstr ""
|
827 |
|
828 |
+
#: admin/general-settings.php:284
|
829 |
msgid "You can add / edit user roles at %1$sUsers > Roles Editor%2$s."
|
830 |
msgstr ""
|
831 |
|
832 |
+
#: admin/general-settings.php:295
|
833 |
msgid "\"Admin Approval\" Feature:"
|
834 |
msgstr ""
|
835 |
|
836 |
+
#: admin/general-settings.php:298
|
837 |
msgid "You decide who is a user on your website. Get notified via email or approve multiple users at once from the WordPress UI. Enable Admin Approval by upgrading to %1$sBasic or PRO versions%2$s."
|
838 |
msgstr ""
|
839 |
|
840 |
+
#: admin/general-settings.php:305
|
841 |
msgid "Allow Users to Log in With:"
|
842 |
msgstr ""
|
843 |
|
844 |
+
#: admin/general-settings.php:309
|
845 |
msgid "Username and Email"
|
846 |
msgstr ""
|
847 |
|
848 |
+
#: admin/general-settings.php:310, admin/manage-fields.php:380, front-end/login.php:341, front-end/login.php:355, front-end/login.php:543, add-ons/custom-redirects/custom_redirects_admin.php:61, add-ons/email-customizer/email-customizer.php:28, add-ons/user-listing/userlisting.php:113, add-ons/user-listing/userlisting.php:339, add-ons/user-listing/userlisting.php:885, add-ons/user-listing/userlisting.php:2558, features/admin-approval/class-admin-approval.php:174, features/email-confirmation/class-email-confirmation.php:168, features/email-customizer/email-customizer.php:28, admin/advanced-settings/includes/views/view-fields.php:124
|
849 |
msgid "Username"
|
850 |
msgstr ""
|
851 |
|
852 |
+
#: admin/general-settings.php:311, front-end/login.php:538, front-end/recover.php:118, add-ons/email-customizer/email-customizer.php:29, add-ons/user-listing/userlisting.php:119, add-ons/user-listing/userlisting.php:891, add-ons/user-listing/userlisting.php:2559, features/admin-approval/class-admin-approval.php:177, features/email-confirmation/class-email-confirmation.php:169, features/email-customizer/email-customizer.php:29, add-ons-free/gdpr-communication-preferences/admin/manage-fields.php:24, add-ons-free/gdpr-communication-preferences/front-end/gdpr-communication-preferences.php:9, admin/advanced-settings/includes/shortcodes/resend-activation.php:9
|
853 |
msgid "Email"
|
854 |
msgstr ""
|
855 |
|
856 |
+
#: admin/general-settings.php:314
|
857 |
msgid "\"Username and Email\" - users can Log In with either their Username or their Email."
|
858 |
msgstr ""
|
859 |
|
860 |
+
#: admin/general-settings.php:315
|
861 |
msgid "\"Username\" - users can only Log In with their Username. Both the Username and Email fields will be shown in the front-end forms."
|
862 |
msgstr ""
|
863 |
|
864 |
+
#: admin/general-settings.php:316
|
865 |
msgid "\"Email\" - users can only Log In with their Email. The Username field will be hidden in the front-end forms and Usernames will be automatically generated based on the Emails."
|
866 |
msgstr ""
|
867 |
|
868 |
+
#: admin/general-settings.php:323
|
869 |
msgid "Minimum Password Length:"
|
870 |
msgstr ""
|
871 |
|
872 |
+
#: admin/general-settings.php:328
|
873 |
msgid "Enter the minimum characters the password should have. Leave empty for no minimum limit"
|
874 |
msgstr ""
|
875 |
|
876 |
+
#: admin/general-settings.php:335
|
877 |
msgid "Minimum Password Strength:"
|
878 |
msgstr ""
|
879 |
|
880 |
+
#: admin/general-settings.php:339
|
881 |
msgid "Disabled"
|
882 |
msgstr ""
|
883 |
|
884 |
+
#: admin/general-settings.php:350
|
885 |
+
msgid "Select Recover Password Page:"
|
886 |
+
msgstr ""
|
887 |
+
|
888 |
+
#: admin/general-settings.php:354, add-ons/user-listing/userlisting.php:598, add-ons/user-listing/userlisting.php:595, add-ons/user-listing/userlisting.php:593, add-ons/user-listing/userlisting.php:576, add-ons/user-listing/userlisting.php:573, add-ons/user-listing/userlisting.php:571, add-ons-advanced/bbpress/bbpress-page.php:90, add-ons-advanced/bbpress/bbpress-page.php:125, add-ons-advanced/woocommerce/woosync-page.php:80, add-ons-advanced/woocommerce/woosync-page.php:115, features/content-restriction/content-restriction.php:148, add-ons-advanced/campaign-monitor/admin/cmonitor-page.php:557, add-ons-advanced/mailchimp-integration/admin/mailchimp-page.php:232, add-ons-advanced/mailchimp-integration/admin/mailchimp-page.php:307
|
889 |
+
msgid "None"
|
890 |
+
msgstr ""
|
891 |
+
|
892 |
+
#: admin/general-settings.php:378
|
893 |
+
msgid "Select the page which contains the %1$s[wppb-recover-password]%2$s shortcode."
|
894 |
+
msgstr ""
|
895 |
+
|
896 |
#: admin/manage-fields.php:18
|
897 |
msgid "Form Fields"
|
898 |
msgstr ""
|
905 |
msgid "Standard"
|
906 |
msgstr ""
|
907 |
|
908 |
+
#: admin/manage-fields.php:69
|
909 |
msgid "Advanced"
|
910 |
msgstr ""
|
911 |
|
912 |
+
#: admin/manage-fields.php:181
|
913 |
msgid "Choose one of the supported field types"
|
914 |
msgstr ""
|
915 |
|
916 |
+
#: admin/manage-fields.php:183
|
917 |
msgid ". Extra Field Types are available in <a href=\"%s\">Basic or PRO versions</a>."
|
918 |
msgstr ""
|
919 |
|
920 |
+
#: admin/manage-fields.php:219
|
921 |
msgid "Use this in conjunction with WordPress functions to display the value in the page of your choosing<br/>Auto-completed but in some cases editable (in which case it must be unique)<br/>Changing this will only affect subsequent entries"
|
922 |
msgstr ""
|
923 |
|
924 |
+
#: admin/manage-fields.php:216
|
925 |
msgid "Use this in conjunction with WordPress functions to display the value in the page of your choosing<br/>Auto-completed but in some cases editable (in which case it must be unique)<br/>Changing this might take long in case of a very big user-count"
|
926 |
msgstr ""
|
927 |
|
928 |
+
#: admin/manage-fields.php:233
|
929 |
msgid "Field Title"
|
930 |
msgstr ""
|
931 |
|
932 |
+
#: admin/manage-fields.php:233
|
933 |
msgid "Title of the field"
|
934 |
msgstr ""
|
935 |
|
936 |
+
#: admin/manage-fields.php:234, add-ons/multiple-forms/edit-profile-forms.php:245, add-ons/multiple-forms/register-forms.php:266
|
937 |
msgid "Field"
|
938 |
msgstr ""
|
939 |
|
940 |
+
#: admin/manage-fields.php:235
|
941 |
msgid "Meta-name"
|
942 |
msgstr ""
|
943 |
|
944 |
+
#: admin/manage-fields.php:236, add-ons/custom-redirects/custom_redirects_admin.php:70, add-ons/custom-redirects/custom_redirects_admin.php:100, add-ons/custom-redirects/custom_redirects_admin.php:119, add-ons/custom-redirects/custom_redirects_admin.php:144, add-ons/multiple-forms/edit-profile-forms.php:246, add-ons/multiple-forms/register-forms.php:267, add-ons/user-listing/userlisting.php:930, assets/misc/elementor/widgets/class-pb-widget-ul.php:172
|
945 |
msgid "ID"
|
946 |
msgstr ""
|
947 |
|
948 |
+
#: admin/manage-fields.php:236, add-ons/multiple-forms/edit-profile-forms.php:246, add-ons/multiple-forms/register-forms.php:267
|
949 |
msgid "A unique, auto-generated ID for this particular field<br/>You can use this in conjuction with filters to target this element if needed<br/>Can't be edited"
|
950 |
msgstr ""
|
951 |
|
952 |
+
#: admin/manage-fields.php:237, features/two-factor-authentication/class-two-factor-authentication.php:241, features/two-factor-authentication/class-two-factor-authentication.php:356
|
953 |
msgid "Description"
|
954 |
msgstr ""
|
955 |
|
956 |
+
#: admin/manage-fields.php:237
|
957 |
msgid "Enter a (detailed) description of the option for end users to read<br/>Optional"
|
958 |
msgstr ""
|
959 |
|
960 |
+
#: admin/manage-fields.php:238
|
961 |
msgid "Row Count"
|
962 |
msgstr ""
|
963 |
|
964 |
+
#: admin/manage-fields.php:238
|
965 |
msgid "Specify the number of rows for a 'Textarea' field<br/>If not specified, defaults to 5"
|
966 |
msgstr ""
|
967 |
|
968 |
+
#: admin/manage-fields.php:239
|
969 |
msgid "Allowed Image Extensions"
|
970 |
msgstr ""
|
971 |
|
972 |
+
#: admin/manage-fields.php:239
|
973 |
msgid "Specify the extension(s) you want to limit to upload<br/>Example: .ext1,.ext2,.ext3<br/>If not specified, defaults to: .jpg,.jpeg,.gif,.png (.*)"
|
974 |
msgstr ""
|
975 |
|
976 |
+
#: admin/manage-fields.php:240
|
977 |
msgid "Use Simple Upload"
|
978 |
msgstr ""
|
979 |
|
980 |
+
#: admin/manage-fields.php:240
|
981 |
msgid "Use a simple upload field instead of the WordPress upload"
|
982 |
msgstr ""
|
983 |
|
984 |
+
#: admin/manage-fields.php:241
|
985 |
msgid "Allowed Upload Extensions"
|
986 |
msgstr ""
|
987 |
|
988 |
+
#: admin/manage-fields.php:241
|
989 |
msgid "Specify the extension(s) you want to limit to upload<br/>Example: .ext1,.ext2,.ext3<br/>If not specified, defaults to all WordPress allowed file extensions (.*)"
|
990 |
msgstr ""
|
991 |
|
992 |
+
#: admin/manage-fields.php:242
|
993 |
msgid "Avatar Size"
|
994 |
msgstr ""
|
995 |
|
996 |
+
#: admin/manage-fields.php:242
|
997 |
msgid "Enter a value (between 20 and 200) for the size of the 'Avatar'<br/>If not specified, defaults to 100"
|
998 |
msgstr ""
|
999 |
|
1000 |
+
#: admin/manage-fields.php:243
|
1001 |
msgid "Date-format"
|
1002 |
msgstr ""
|
1003 |
|
1004 |
+
#: admin/manage-fields.php:243
|
1005 |
msgid "Specify the format of the date when using Datepicker<br/>Valid options: mm/dd/yy, mm/yy/dd, dd/yy/mm, dd/mm/yy, yy/dd/mm, yy/mm/dd, mm-dd-yy, yy-mm-dd, D, dd M yy, D, d M y, DD, dd-M-y, D, d M yy, mm/yy, mm/dd, dd/mm, @<br/>If not specified, defaults to mm/dd/yy<br/>ATTENTION: if you plan to use this field for sorting, please make sure to use year first, then month, and day last."
|
1006 |
msgstr ""
|
1007 |
|
1008 |
+
#: admin/manage-fields.php:244
|
1009 |
msgid "Terms of Agreement"
|
1010 |
msgstr ""
|
1011 |
|
1012 |
+
#: admin/manage-fields.php:244
|
1013 |
msgid "Enter a detailed description of the terms of agreement for the user to read.<br/>Links can be inserted by using standard HTML syntax: <a href=\"custom_url\">custom_text</a>"
|
1014 |
msgstr ""
|
1015 |
|
1016 |
+
#: admin/manage-fields.php:245
|
1017 |
msgid "Options"
|
1018 |
msgstr ""
|
1019 |
|
1020 |
+
#: admin/manage-fields.php:245
|
1021 |
msgid "Enter a comma separated list of values<br/>This can be anything, as it is hidden from the user, but should not contain special characters or apostrophes"
|
1022 |
msgstr ""
|
1023 |
|
1024 |
+
#: admin/manage-fields.php:246, add-ons-free/labels-edit/labels-edit.php:373
|
1025 |
msgid "Labels"
|
1026 |
msgstr ""
|
1027 |
|
1028 |
+
#: admin/manage-fields.php:246
|
1029 |
msgid "Enter a comma separated list of labels<br/>Visible for the user"
|
1030 |
msgstr ""
|
1031 |
|
1032 |
+
#: admin/manage-fields.php:247
|
1033 |
msgid "reCAPTCHA Type"
|
1034 |
msgstr ""
|
1035 |
|
1036 |
+
#: admin/manage-fields.php:247
|
1037 |
msgid "Choose the <a href=\"https://developers.google.com/recaptcha/docs/versions\" target=\"_blank\">type of reCAPTCHA</a> you wish to add to this site.<br/>Please note that the Invisible reCAPTCHA is a type of reCAPTCHA v2."
|
1038 |
msgstr ""
|
1039 |
|
1040 |
+
#: admin/manage-fields.php:248
|
1041 |
msgid "Site Key"
|
1042 |
msgstr ""
|
1043 |
|
1044 |
+
#: admin/manage-fields.php:248
|
1045 |
msgid "The site key from Google, <a href=\"https://www.google.com/recaptcha/admin/create\" target=\"_blank\">https://www.google.com/recaptcha/admin/create</a>"
|
1046 |
msgstr ""
|
1047 |
|
1048 |
+
#: admin/manage-fields.php:249
|
1049 |
msgid "Secret Key"
|
1050 |
msgstr ""
|
1051 |
|
1052 |
+
#: admin/manage-fields.php:249
|
1053 |
msgid "The secret key from Google, <a href=\"https://www.google.com/recaptcha/admin/create\" target=\"_blank\">https://www.google.com/recaptcha/admin/create</a>"
|
1054 |
msgstr ""
|
1055 |
|
1056 |
+
#: admin/manage-fields.php:250
|
1057 |
msgid "Display on PB forms"
|
1058 |
msgstr ""
|
1059 |
|
1060 |
+
#: admin/manage-fields.php:250
|
1061 |
msgid "PB Login"
|
1062 |
msgstr ""
|
1063 |
|
1064 |
+
#: admin/manage-fields.php:250
|
1065 |
msgid "PB Register"
|
1066 |
msgstr ""
|
1067 |
|
1068 |
+
#: admin/manage-fields.php:250
|
1069 |
msgid "PB Recover Password"
|
1070 |
msgstr ""
|
1071 |
|
1072 |
+
#: admin/manage-fields.php:250
|
1073 |
msgid "Select on which Profile Builder forms to display reCAPTCHA"
|
1074 |
msgstr ""
|
1075 |
|
1076 |
+
#: admin/manage-fields.php:251
|
1077 |
msgid "Display on default WP forms"
|
1078 |
msgstr ""
|
1079 |
|
1080 |
+
#: admin/manage-fields.php:251
|
1081 |
msgid "Default WP Login"
|
1082 |
msgstr ""
|
1083 |
|
1084 |
+
#: admin/manage-fields.php:251
|
1085 |
msgid "Default WP Register"
|
1086 |
msgstr ""
|
1087 |
|
1088 |
+
#: admin/manage-fields.php:251
|
1089 |
msgid "Default WP Recover Password"
|
1090 |
msgstr ""
|
1091 |
|
1092 |
+
#: admin/manage-fields.php:251
|
1093 |
msgid "Select on which default WP forms to display reCAPTCHA"
|
1094 |
msgstr ""
|
1095 |
|
1096 |
+
#: admin/manage-fields.php:252
|
1097 |
msgid "User Roles"
|
1098 |
msgstr ""
|
1099 |
|
1100 |
+
#: admin/manage-fields.php:252
|
1101 |
msgid "Select which user roles to show to the user ( drag and drop to re-order )"
|
1102 |
msgstr ""
|
1103 |
|
1104 |
+
#: admin/manage-fields.php:253
|
1105 |
msgid "Display on Edit Profile"
|
1106 |
msgstr ""
|
1107 |
|
1108 |
+
#: admin/manage-fields.php:253
|
1109 |
msgid "Check if you want the select user role field to appear on Edit Profile forms"
|
1110 |
msgstr ""
|
1111 |
|
1112 |
+
#: admin/manage-fields.php:254
|
1113 |
msgid "User Roles Order"
|
1114 |
msgstr ""
|
1115 |
|
1116 |
+
#: admin/manage-fields.php:254
|
1117 |
msgid "Save the user role order from the user roles checkboxes"
|
1118 |
msgstr ""
|
1119 |
|
1120 |
+
#: admin/manage-fields.php:255
|
1121 |
msgid "Default Value"
|
1122 |
msgstr ""
|
1123 |
|
1124 |
+
#: admin/manage-fields.php:255
|
1125 |
msgid "Default value of the field"
|
1126 |
msgstr ""
|
1127 |
|
1128 |
+
#: admin/manage-fields.php:256, admin/manage-fields.php:258, admin/manage-fields.php:259, admin/manage-fields.php:260
|
1129 |
msgid "Default Option"
|
1130 |
msgstr ""
|
1131 |
|
1132 |
+
#: admin/manage-fields.php:256
|
1133 |
msgid "Specify the option which should be selected by default"
|
1134 |
msgstr ""
|
1135 |
|
1136 |
+
#: admin/manage-fields.php:257
|
1137 |
msgid "Default Option(s)"
|
1138 |
msgstr ""
|
1139 |
|
1140 |
+
#: admin/manage-fields.php:257
|
1141 |
msgid "Specify the option which should be checked by default<br/>If there are multiple values, separate them with a ',' (comma)"
|
1142 |
msgstr ""
|
1143 |
|
1144 |
+
#: admin/manage-fields.php:258, admin/manage-fields.php:259, admin/manage-fields.php:260
|
1145 |
msgid "Default option of the field"
|
1146 |
msgstr ""
|
1147 |
|
1148 |
+
#: admin/manage-fields.php:261
|
1149 |
msgid "Show Currency Symbol"
|
1150 |
msgstr ""
|
1151 |
|
1152 |
+
#: admin/manage-fields.php:261
|
1153 |
msgid "Whether the currency symbol should be displayed after the currency name in the select option."
|
1154 |
msgstr ""
|
1155 |
|
1156 |
+
#: admin/manage-fields.php:262
|
1157 |
msgid "Show Post Type"
|
1158 |
msgstr ""
|
1159 |
|
1160 |
+
#: admin/manage-fields.php:262
|
1161 |
msgid "Posts from what post type will be displayed in the select."
|
1162 |
msgstr ""
|
1163 |
|
1164 |
+
#: admin/manage-fields.php:263
|
1165 |
msgid "Allowable Values"
|
1166 |
msgstr ""
|
1167 |
|
1168 |
+
#: admin/manage-fields.php:263
|
1169 |
msgid "Enter a comma separated list of possible values. Upon registration if the value provided by the user does not match one of these values, the user will not be registered."
|
1170 |
msgstr ""
|
1171 |
|
1172 |
+
#: admin/manage-fields.php:264
|
1173 |
msgid "Error Message"
|
1174 |
msgstr ""
|
1175 |
|
1176 |
+
#: admin/manage-fields.php:264
|
1177 |
msgid "Set a custom error message that will be displayed to the user."
|
1178 |
msgstr ""
|
1179 |
|
1180 |
+
#: admin/manage-fields.php:265
|
1181 |
msgid "Time Format"
|
1182 |
msgstr ""
|
1183 |
|
1184 |
+
#: admin/manage-fields.php:265
|
1185 |
msgid "Specify the time format."
|
1186 |
msgstr ""
|
1187 |
|
1188 |
+
#: admin/manage-fields.php:266
|
1189 |
msgid "Google Maps API Key"
|
1190 |
msgstr ""
|
1191 |
|
1192 |
+
#: admin/manage-fields.php:266
|
1193 |
msgid "Enter your Google Maps API key ( <a href=\"https://console.developers.google.com/flows/enableapi?apiid=maps_backend\" target=\"_blank\">Get your API key</a> ). If more than one map fields are added to a form the API key from the first map displayed will be used."
|
1194 |
msgstr ""
|
1195 |
|
1196 |
+
#: admin/manage-fields.php:271
|
1197 |
msgid "Default Latitude"
|
1198 |
msgstr ""
|
1199 |
|
1200 |
+
#: admin/manage-fields.php:272
|
1201 |
msgid "The latitude at which the map should be displayed when no pins are attached."
|
1202 |
msgstr ""
|
1203 |
|
1204 |
+
#: admin/manage-fields.php:278
|
1205 |
msgid "Default Longitude"
|
1206 |
msgstr ""
|
1207 |
|
1208 |
+
#: admin/manage-fields.php:279
|
1209 |
msgid "The longitude at which the map should be displayed when no pins are attached."
|
1210 |
msgstr ""
|
1211 |
|
1212 |
+
#: admin/manage-fields.php:285
|
1213 |
msgid "Default Zoom Level"
|
1214 |
msgstr ""
|
1215 |
|
1216 |
+
#: admin/manage-fields.php:286
|
1217 |
msgid "Add a number from 0 to 19. The higher the number the higher the zoom."
|
1218 |
msgstr ""
|
1219 |
|
1220 |
+
#: admin/manage-fields.php:290
|
1221 |
msgid "Map Height"
|
1222 |
msgstr ""
|
1223 |
|
1224 |
+
#: admin/manage-fields.php:290
|
1225 |
msgid "The height of the map."
|
1226 |
msgstr ""
|
1227 |
|
1228 |
+
#: admin/manage-fields.php:291
|
1229 |
msgid "Default Content"
|
1230 |
msgstr ""
|
1231 |
|
1232 |
+
#: admin/manage-fields.php:291
|
1233 |
msgid "Default value of the textarea"
|
1234 |
msgstr ""
|
1235 |
|
1236 |
+
#: admin/manage-fields.php:292
|
1237 |
msgid "HTML Content"
|
1238 |
msgstr ""
|
1239 |
|
1240 |
+
#: admin/manage-fields.php:292
|
1241 |
msgid "Add your HTML (or text) content"
|
1242 |
msgstr ""
|
1243 |
|
1244 |
+
#: admin/manage-fields.php:293
|
1245 |
msgid "Phone Format"
|
1246 |
msgstr ""
|
1247 |
|
1248 |
+
#: admin/manage-fields.php:293
|
1249 |
msgid "You can use: # for numbers, parentheses ( ), - sign, + sign, dot . and spaces."
|
1250 |
msgstr ""
|
1251 |
|
1252 |
+
#: admin/manage-fields.php:293
|
1253 |
msgid "Eg. (###) ###-####"
|
1254 |
msgstr ""
|
1255 |
|
1256 |
+
#: admin/manage-fields.php:293
|
1257 |
msgid "Empty field won't check for correct phone number."
|
1258 |
msgstr ""
|
1259 |
|
1260 |
+
#: admin/manage-fields.php:294
|
1261 |
msgid "Heading Tag"
|
1262 |
msgstr ""
|
1263 |
|
1264 |
+
#: admin/manage-fields.php:294
|
1265 |
msgid "Change heading field size on front-end forms"
|
1266 |
msgstr ""
|
1267 |
|
1268 |
+
#: admin/manage-fields.php:295
|
1269 |
msgid "Min Number Value"
|
1270 |
msgstr ""
|
1271 |
|
1272 |
+
#: admin/manage-fields.php:295
|
1273 |
msgid "Min allowed number value (0 to allow only positive numbers)"
|
1274 |
msgstr ""
|
1275 |
|
1276 |
+
#: admin/manage-fields.php:295
|
1277 |
msgid "Leave it empty for no min value"
|
1278 |
msgstr ""
|
1279 |
|
1280 |
+
#: admin/manage-fields.php:296
|
1281 |
msgid "Max Number Value"
|
1282 |
msgstr ""
|
1283 |
|
1284 |
+
#: admin/manage-fields.php:296
|
1285 |
msgid "Max allowed number value (0 to allow only negative numbers)"
|
1286 |
msgstr ""
|
1287 |
|
1288 |
+
#: admin/manage-fields.php:296
|
1289 |
msgid "Leave it empty for no max value"
|
1290 |
msgstr ""
|
1291 |
|
1292 |
+
#: admin/manage-fields.php:297
|
1293 |
msgid "Number Step Value"
|
1294 |
msgstr ""
|
1295 |
|
1296 |
+
#: admin/manage-fields.php:297
|
1297 |
msgid "Step value 1 to allow only integers, 0.1 to allow integers and numbers with 1 decimal"
|
1298 |
msgstr ""
|
1299 |
|
1300 |
+
#: admin/manage-fields.php:297
|
1301 |
msgid "To allow multiple decimals use for eg. 0.01 (for 2 deciamls) and so on"
|
1302 |
msgstr ""
|
1303 |
|
1304 |
+
#: admin/manage-fields.php:297
|
1305 |
msgid "You can also use step value to specify the legal number intervals (eg. step value 2 will allow only -4, -2, 0, 2 and so on)"
|
1306 |
msgstr ""
|
1307 |
|
1308 |
+
#: admin/manage-fields.php:297
|
1309 |
msgid "Leave it empty for no restriction"
|
1310 |
msgstr ""
|
1311 |
|
1312 |
+
#: admin/manage-fields.php:298, add-ons-advanced/woocommerce/index.php:261
|
1313 |
msgid "Required"
|
1314 |
msgstr ""
|
1315 |
|
1316 |
+
#: admin/manage-fields.php:298
|
1317 |
msgid "Whether the field is required or not"
|
1318 |
msgstr ""
|
1319 |
|
1320 |
+
#: admin/manage-fields.php:299
|
1321 |
msgid "Overwrite Existing"
|
1322 |
msgstr ""
|
1323 |
|
1324 |
+
#: admin/manage-fields.php:299
|
1325 |
msgid "Selecting 'Yes' will add the field to the list, but will overwrite any other field in the database that has the same meta-name<br/>Use this at your own risk"
|
1326 |
msgstr ""
|
1327 |
|
1328 |
+
#: admin/manage-fields.php:305
|
1329 |
msgid "POIs Load Type"
|
1330 |
msgstr ""
|
1331 |
|
1332 |
+
#: admin/manage-fields.php:307
|
1333 |
msgid "POIs of the listed users (as filtered & paginated)"
|
1334 |
msgstr ""
|
1335 |
|
1336 |
+
#: admin/manage-fields.php:308
|
1337 |
msgid "POIs of all the users for the filter* (no pagination)"
|
1338 |
msgstr ""
|
1339 |
|
1340 |
+
#: admin/manage-fields.php:311
|
1341 |
msgid "This option allows you to load on a single map the POIs for all users, or just these for the listed ones (this will take into account the filters and the faceted menus). *Please use this feature wisely, it will impact the performance."
|
1342 |
msgstr ""
|
1343 |
|
1344 |
+
#: admin/manage-fields.php:318
|
1345 |
msgid "POI Bubble Info"
|
1346 |
msgstr ""
|
1347 |
|
1348 |
+
#: admin/manage-fields.php:321
|
1349 |
msgid "Select the attributes to be listed inside the POI bubble."
|
1350 |
msgstr ""
|
1351 |
|
1352 |
+
#: admin/manage-fields.php:332
|
1353 |
msgid "Number of Users per Map Iteration"
|
1354 |
msgstr ""
|
1355 |
|
1356 |
+
#: admin/manage-fields.php:333
|
1357 |
msgid "When loading the map of all users with no pagination, the map script will iterate multiple times and will expose gradually POIs on the map, until all the POIs for the users that match the criteria will be added on the map (think of this as of pagination for the map POIs). The smaller the number of users per iteration, the fastest the iteration response will be, but for a large number of users, the map script will iterate multiple times. Setting a higher limit will decrease the performance, but might produce a smaller number of iterations. <br><br><b>Please adjust this value to your hosting capabilities, and make sure that the value you set is the best for performance.</b> We recommend a <b>maximum</b> value of 300."
|
1358 |
msgstr ""
|
1359 |
|
1360 |
+
#: admin/manage-fields.php:338
|
1361 |
msgid "Maximum Selections"
|
1362 |
msgstr ""
|
1363 |
|
1364 |
+
#: admin/manage-fields.php:338
|
1365 |
msgid "Select2 multi-value select boxes can set restrictions regarding the maximum number of options selected."
|
1366 |
msgstr ""
|
1367 |
|
1368 |
+
#: admin/manage-fields.php:339
|
1369 |
msgid "User Inputted Options"
|
1370 |
msgstr ""
|
1371 |
|
1372 |
+
#: admin/manage-fields.php:339
|
1373 |
msgid "Check this to allow users to create their own options beside the pre-existing ones."
|
1374 |
msgstr ""
|
1375 |
|
1376 |
+
#: admin/manage-fields.php:346
|
1377 |
msgid "Form Field Properties"
|
1378 |
msgstr ""
|
1379 |
|
1380 |
+
#: admin/manage-fields.php:360
|
1381 |
msgid "Registration & Edit Profile Forms"
|
1382 |
msgstr ""
|
1383 |
|
1384 |
+
#: admin/manage-fields.php:379, add-ons-advanced/buddypress/buddypress-activator.php:48, add-ons-advanced/buddypress/index.php:481, add-ons-advanced/campaign-monitor/admin/widget.php:226, add-ons-advanced/campaign-monitor/admin/widget.php:228
|
1385 |
msgid "Name"
|
1386 |
msgstr ""
|
1387 |
|
1388 |
+
#: admin/manage-fields.php:380
|
1389 |
msgid "Usernames cannot be changed."
|
1390 |
msgstr ""
|
1391 |
|
1392 |
+
#: admin/manage-fields.php:381, add-ons/user-listing/userlisting.php:897, add-ons-advanced/woocommerce/billing-fields.php:6, add-ons-advanced/woocommerce/shipping-fields.php:6, admin/advanced-settings/includes/views/view-fields.php:130
|
1393 |
msgid "First Name"
|
1394 |
msgstr ""
|
1395 |
|
1396 |
+
#: admin/manage-fields.php:382, add-ons/user-listing/userlisting.php:900, add-ons-advanced/woocommerce/billing-fields.php:7, add-ons-advanced/woocommerce/shipping-fields.php:7, admin/advanced-settings/includes/views/view-fields.php:136
|
1397 |
msgid "Last Name"
|
1398 |
msgstr ""
|
1399 |
|
1400 |
+
#: admin/manage-fields.php:383, add-ons/user-listing/userlisting.php:924, add-ons/user-listing/userlisting.php:2567
|
1401 |
msgid "Nickname"
|
1402 |
msgstr ""
|
1403 |
|
1404 |
+
#: admin/manage-fields.php:384
|
1405 |
msgid "Display name publicly as"
|
1406 |
msgstr ""
|
1407 |
|
1408 |
+
#: admin/manage-fields.php:385, add-ons-advanced/buddypress/buddypress-activator.php:71, add-ons-advanced/buddypress/index.php:504
|
1409 |
msgid "Contact Info"
|
1410 |
msgstr ""
|
1411 |
|
1412 |
+
#: admin/manage-fields.php:386
|
1413 |
msgid "E-mail"
|
1414 |
msgstr ""
|
1415 |
|
1416 |
+
#: admin/manage-fields.php:387, add-ons/email-customizer/email-customizer.php:33, add-ons/user-listing/userlisting.php:122, add-ons/user-listing/userlisting.php:906, add-ons/user-listing/userlisting.php:2561, features/email-customizer/email-customizer.php:33
|
1417 |
msgid "Website"
|
1418 |
msgstr ""
|
1419 |
|
1420 |
+
#: admin/manage-fields.php:391
|
1421 |
msgid "AIM"
|
1422 |
msgstr ""
|
1423 |
|
1424 |
+
#: admin/manage-fields.php:392
|
1425 |
msgid "Yahoo IM"
|
1426 |
msgstr ""
|
1427 |
|
1428 |
+
#: admin/manage-fields.php:393
|
1429 |
msgid "Jabber / Google Talk"
|
1430 |
msgstr ""
|
1431 |
|
1432 |
+
#: admin/manage-fields.php:396, add-ons-advanced/buddypress/buddypress-activator.php:78, add-ons-advanced/buddypress/index.php:511
|
1433 |
msgid "About Yourself"
|
1434 |
msgstr ""
|
1435 |
|
1436 |
+
#: admin/manage-fields.php:397, add-ons/user-listing/userlisting.php:125, add-ons/user-listing/userlisting.php:909, add-ons/user-listing/userlisting.php:2562
|
1437 |
msgid "Biographical Info"
|
1438 |
msgstr ""
|
1439 |
|
1440 |
+
#: admin/manage-fields.php:397
|
1441 |
msgid "Share a little biographical information to fill out your profile. This may be shown publicly."
|
1442 |
msgstr ""
|
1443 |
|
1444 |
+
#: admin/manage-fields.php:398, front-end/login.php:121, front-end/recover.php:72, add-ons/email-customizer/email-customizer.php:30, features/email-customizer/email-customizer.php:30
|
1445 |
msgid "Password"
|
1446 |
msgstr ""
|
1447 |
|
1448 |
+
#: admin/manage-fields.php:398
|
1449 |
msgid "Type your password."
|
1450 |
msgstr ""
|
1451 |
|
1452 |
+
#: admin/manage-fields.php:399, front-end/recover.php:73
|
1453 |
msgid "Repeat Password"
|
1454 |
msgstr ""
|
1455 |
|
1456 |
+
#: admin/manage-fields.php:399
|
1457 |
msgid "Type your password again. "
|
1458 |
msgstr ""
|
1459 |
|
1460 |
+
#: admin/manage-fields.php:401
|
1461 |
msgid "Blog Details"
|
1462 |
msgstr ""
|
1463 |
|
1464 |
+
#: admin/manage-fields.php:460
|
1465 |
msgid "Select a Country"
|
1466 |
msgstr ""
|
1467 |
|
1468 |
+
#: admin/manage-fields.php:461
|
1469 |
msgid "Afghanistan"
|
1470 |
msgstr ""
|
1471 |
|
1472 |
+
#: admin/manage-fields.php:462
|
1473 |
msgid "Aland Islands"
|
1474 |
msgstr ""
|
1475 |
|
1476 |
+
#: admin/manage-fields.php:463
|
1477 |
msgid "Albania"
|
1478 |
msgstr ""
|
1479 |
|
1480 |
+
#: admin/manage-fields.php:464
|
1481 |
msgid "Algeria"
|
1482 |
msgstr ""
|
1483 |
|
1484 |
+
#: admin/manage-fields.php:465
|
1485 |
msgid "American Samoa"
|
1486 |
msgstr ""
|
1487 |
|
1488 |
+
#: admin/manage-fields.php:466
|
1489 |
msgid "Andorra"
|
1490 |
msgstr ""
|
1491 |
|
1492 |
+
#: admin/manage-fields.php:467
|
1493 |
msgid "Angola"
|
1494 |
msgstr ""
|
1495 |
|
1496 |
+
#: admin/manage-fields.php:468
|
1497 |
msgid "Anguilla"
|
1498 |
msgstr ""
|
1499 |
|
1500 |
+
#: admin/manage-fields.php:469
|
1501 |
msgid "Antarctica"
|
1502 |
msgstr ""
|
1503 |
|
1504 |
+
#: admin/manage-fields.php:470
|
1505 |
msgid "Antigua and Barbuda"
|
1506 |
msgstr ""
|
1507 |
|
1508 |
+
#: admin/manage-fields.php:471
|
1509 |
msgid "Argentina"
|
1510 |
msgstr ""
|
1511 |
|
1512 |
+
#: admin/manage-fields.php:472
|
1513 |
msgid "Armenia"
|
1514 |
msgstr ""
|
1515 |
|
1516 |
+
#: admin/manage-fields.php:473
|
1517 |
msgid "Aruba"
|
1518 |
msgstr ""
|
1519 |
|
1520 |
+
#: admin/manage-fields.php:474
|
1521 |
msgid "Australia"
|
1522 |
msgstr ""
|
1523 |
|
1524 |
+
#: admin/manage-fields.php:475
|
1525 |
msgid "Austria"
|
1526 |
msgstr ""
|
1527 |
|
1528 |
+
#: admin/manage-fields.php:476
|
1529 |
msgid "Azerbaijan"
|
1530 |
msgstr ""
|
1531 |
|
1532 |
+
#: admin/manage-fields.php:477
|
1533 |
msgid "Bahamas"
|
1534 |
msgstr ""
|
1535 |
|
1536 |
+
#: admin/manage-fields.php:478
|
1537 |
msgid "Bahrain"
|
1538 |
msgstr ""
|
1539 |
|
1540 |
+
#: admin/manage-fields.php:479
|
1541 |
msgid "Bangladesh"
|
1542 |
msgstr ""
|
1543 |
|
1544 |
+
#: admin/manage-fields.php:480
|
1545 |
msgid "Barbados"
|
1546 |
msgstr ""
|
1547 |
|
1548 |
+
#: admin/manage-fields.php:481
|
1549 |
msgid "Belarus"
|
1550 |
msgstr ""
|
1551 |
|
1552 |
+
#: admin/manage-fields.php:482
|
1553 |
msgid "Belgium"
|
1554 |
msgstr ""
|
1555 |
|
1556 |
+
#: admin/manage-fields.php:483
|
1557 |
msgid "Belize"
|
1558 |
msgstr ""
|
1559 |
|
1560 |
+
#: admin/manage-fields.php:484
|
1561 |
msgid "Benin"
|
1562 |
msgstr ""
|
1563 |
|
1564 |
+
#: admin/manage-fields.php:485
|
1565 |
msgid "Bermuda"
|
1566 |
msgstr ""
|
1567 |
|
1568 |
+
#: admin/manage-fields.php:486
|
1569 |
msgid "Bhutan"
|
1570 |
msgstr ""
|
1571 |
|
1572 |
+
#: admin/manage-fields.php:487
|
1573 |
msgid "Bolivia"
|
1574 |
msgstr ""
|
1575 |
|
1576 |
+
#: admin/manage-fields.php:488
|
1577 |
msgid "Bonaire, Saint Eustatius and Saba"
|
1578 |
msgstr ""
|
1579 |
|
1580 |
+
#: admin/manage-fields.php:489
|
1581 |
msgid "Bosnia and Herzegovina"
|
1582 |
msgstr ""
|
1583 |
|
1584 |
+
#: admin/manage-fields.php:490
|
1585 |
msgid "Botswana"
|
1586 |
msgstr ""
|
1587 |
|
1588 |
+
#: admin/manage-fields.php:491
|
1589 |
msgid "Bouvet Island"
|
1590 |
msgstr ""
|
1591 |
|
1592 |
+
#: admin/manage-fields.php:492
|
1593 |
msgid "Brazil"
|
1594 |
msgstr ""
|
1595 |
|
1596 |
+
#: admin/manage-fields.php:493
|
1597 |
msgid "British Indian Ocean Territory"
|
1598 |
msgstr ""
|
1599 |
|
1600 |
+
#: admin/manage-fields.php:494
|
1601 |
msgid "British Virgin Islands"
|
1602 |
msgstr ""
|
1603 |
|
1604 |
+
#: admin/manage-fields.php:495
|
1605 |
msgid "Brunei"
|
1606 |
msgstr ""
|
1607 |
|
1608 |
+
#: admin/manage-fields.php:496
|
1609 |
msgid "Bulgaria"
|
1610 |
msgstr ""
|
1611 |
|
1612 |
+
#: admin/manage-fields.php:497
|
1613 |
msgid "Burkina Faso"
|
1614 |
msgstr ""
|
1615 |
|
1616 |
+
#: admin/manage-fields.php:498
|
1617 |
msgid "Burundi"
|
1618 |
msgstr ""
|
1619 |
|
1620 |
+
#: admin/manage-fields.php:499
|
1621 |
msgid "Cambodia"
|
1622 |
msgstr ""
|
1623 |
|
1624 |
+
#: admin/manage-fields.php:500
|
1625 |
msgid "Cameroon"
|
1626 |
msgstr ""
|
1627 |
|
1628 |
+
#: admin/manage-fields.php:501
|
1629 |
msgid "Canada"
|
1630 |
msgstr ""
|
1631 |
|
1632 |
+
#: admin/manage-fields.php:502
|
1633 |
msgid "Cape Verde"
|
1634 |
msgstr ""
|
1635 |
|
1636 |
+
#: admin/manage-fields.php:503
|
1637 |
msgid "Cayman Islands"
|
1638 |
msgstr ""
|
1639 |
|
1640 |
+
#: admin/manage-fields.php:504
|
1641 |
msgid "Central African Republic"
|
1642 |
msgstr ""
|
1643 |
|
1644 |
+
#: admin/manage-fields.php:505
|
1645 |
msgid "Chad"
|
1646 |
msgstr ""
|
1647 |
|
1648 |
+
#: admin/manage-fields.php:506
|
1649 |
msgid "Chile"
|
1650 |
msgstr ""
|
1651 |
|
1652 |
+
#: admin/manage-fields.php:507
|
1653 |
msgid "China"
|
1654 |
msgstr ""
|
1655 |
|
1656 |
+
#: admin/manage-fields.php:508
|
1657 |
msgid "Christmas Island"
|
1658 |
msgstr ""
|
1659 |
|
1660 |
+
#: admin/manage-fields.php:509
|
1661 |
msgid "Cocos Islands"
|
1662 |
msgstr ""
|
1663 |
|
1664 |
+
#: admin/manage-fields.php:510
|
1665 |
msgid "Colombia"
|
1666 |
msgstr ""
|
1667 |
|
1668 |
+
#: admin/manage-fields.php:511
|
1669 |
msgid "Comoros"
|
1670 |
msgstr ""
|
1671 |
|
1672 |
+
#: admin/manage-fields.php:512
|
1673 |
msgid "Cook Islands"
|
1674 |
msgstr ""
|
1675 |
|
1676 |
+
#: admin/manage-fields.php:513
|
1677 |
msgid "Costa Rica"
|
1678 |
msgstr ""
|
1679 |
|
1680 |
+
#: admin/manage-fields.php:514
|
1681 |
msgid "Croatia"
|
1682 |
msgstr ""
|
1683 |
|
1684 |
+
#: admin/manage-fields.php:515
|
1685 |
msgid "Cuba"
|
1686 |
msgstr ""
|
1687 |
|
1688 |
+
#: admin/manage-fields.php:516
|
1689 |
msgid "Curacao"
|
1690 |
msgstr ""
|
1691 |
|
1692 |
+
#: admin/manage-fields.php:517
|
1693 |
msgid "Cyprus"
|
1694 |
msgstr ""
|
1695 |
|
1696 |
+
#: admin/manage-fields.php:518
|
1697 |
msgid "Czech Republic"
|
1698 |
msgstr ""
|
1699 |
|
1700 |
+
#: admin/manage-fields.php:519
|
1701 |
msgid "Democratic Republic of the Congo"
|
1702 |
msgstr ""
|
1703 |
|
1704 |
+
#: admin/manage-fields.php:520
|
1705 |
msgid "Denmark"
|
1706 |
msgstr ""
|
1707 |
|
1708 |
+
#: admin/manage-fields.php:521
|
1709 |
msgid "Djibouti"
|
1710 |
msgstr ""
|
1711 |
|
1712 |
+
#: admin/manage-fields.php:522
|
1713 |
msgid "Dominica"
|
1714 |
msgstr ""
|
1715 |
|
1716 |
+
#: admin/manage-fields.php:523
|
1717 |
msgid "Dominican Republic"
|
1718 |
msgstr ""
|
1719 |
|
1720 |
+
#: admin/manage-fields.php:524
|
1721 |
msgid "East Timor"
|
1722 |
msgstr ""
|
1723 |
|
1724 |
+
#: admin/manage-fields.php:525
|
1725 |
msgid "Ecuador"
|
1726 |
msgstr ""
|
1727 |
|
1728 |
+
#: admin/manage-fields.php:526
|
1729 |
msgid "Egypt"
|
1730 |
msgstr ""
|
1731 |
|
1732 |
+
#: admin/manage-fields.php:527
|
1733 |
msgid "El Salvador"
|
1734 |
msgstr ""
|
1735 |
|
1736 |
+
#: admin/manage-fields.php:528
|
1737 |
msgid "Equatorial Guinea"
|
1738 |
msgstr ""
|
1739 |
|
1740 |
+
#: admin/manage-fields.php:529
|
1741 |
msgid "Eritrea"
|
1742 |
msgstr ""
|
1743 |
|
1744 |
+
#: admin/manage-fields.php:530
|
1745 |
msgid "Estonia"
|
1746 |
msgstr ""
|
1747 |
|
1748 |
+
#: admin/manage-fields.php:531
|
1749 |
msgid "Ethiopia"
|
1750 |
msgstr ""
|
1751 |
|
1752 |
+
#: admin/manage-fields.php:532
|
1753 |
msgid "Falkland Islands"
|
1754 |
msgstr ""
|
1755 |
|
1756 |
+
#: admin/manage-fields.php:533
|
1757 |
msgid "Faroe Islands"
|
1758 |
msgstr ""
|
1759 |
|
1760 |
+
#: admin/manage-fields.php:534
|
1761 |
msgid "Fiji"
|
1762 |
msgstr ""
|
1763 |
|
1764 |
+
#: admin/manage-fields.php:535
|
1765 |
msgid "Finland"
|
1766 |
msgstr ""
|
1767 |
|
1768 |
+
#: admin/manage-fields.php:536
|
1769 |
msgid "France"
|
1770 |
msgstr ""
|
1771 |
|
1772 |
+
#: admin/manage-fields.php:537
|
1773 |
msgid "French Guiana"
|
1774 |
msgstr ""
|
1775 |
|
1776 |
+
#: admin/manage-fields.php:538
|
1777 |
msgid "French Polynesia"
|
1778 |
msgstr ""
|
1779 |
|
1780 |
+
#: admin/manage-fields.php:539
|
1781 |
msgid "French Southern Territories"
|
1782 |
msgstr ""
|
1783 |
|
1784 |
+
#: admin/manage-fields.php:540
|
1785 |
msgid "Gabon"
|
1786 |
msgstr ""
|
1787 |
|
1788 |
+
#: admin/manage-fields.php:541
|
1789 |
msgid "Gambia"
|
1790 |
msgstr ""
|
1791 |
|
1792 |
+
#: admin/manage-fields.php:542
|
1793 |
msgid "Georgia"
|
1794 |
msgstr ""
|
1795 |
|
1796 |
+
#: admin/manage-fields.php:543
|
1797 |
msgid "Germany"
|
1798 |
msgstr ""
|
1799 |
|
1800 |
+
#: admin/manage-fields.php:544
|
1801 |
msgid "Ghana"
|
1802 |
msgstr ""
|
1803 |
|
1804 |
+
#: admin/manage-fields.php:545
|
1805 |
msgid "Gibraltar"
|
1806 |
msgstr ""
|
1807 |
|
1808 |
+
#: admin/manage-fields.php:546
|
1809 |
msgid "Greece"
|
1810 |
msgstr ""
|
1811 |
|
1812 |
+
#: admin/manage-fields.php:547
|
1813 |
msgid "Greenland"
|
1814 |
msgstr ""
|
1815 |
|
1816 |
+
#: admin/manage-fields.php:548
|
1817 |
msgid "Grenada"
|
1818 |
msgstr ""
|
1819 |
|
1820 |
+
#: admin/manage-fields.php:549
|
1821 |
msgid "Guadeloupe"
|
1822 |
msgstr ""
|
1823 |
|
1824 |
+
#: admin/manage-fields.php:550
|
1825 |
msgid "Guam"
|
1826 |
msgstr ""
|
1827 |
|
1828 |
+
#: admin/manage-fields.php:551
|
1829 |
msgid "Guatemala"
|
1830 |
msgstr ""
|
1831 |
|
1832 |
+
#: admin/manage-fields.php:552
|
1833 |
msgid "Guernsey"
|
1834 |
msgstr ""
|
1835 |
|
1836 |
+
#: admin/manage-fields.php:553
|
1837 |
msgid "Guinea"
|
1838 |
msgstr ""
|
1839 |
|
1840 |
+
#: admin/manage-fields.php:554
|
1841 |
msgid "Guinea-Bissau"
|
1842 |
msgstr ""
|
1843 |
|
1844 |
+
#: admin/manage-fields.php:555
|
1845 |
msgid "Guyana"
|
1846 |
msgstr ""
|
1847 |
|
1848 |
+
#: admin/manage-fields.php:556
|
1849 |
msgid "Haiti"
|
1850 |
msgstr ""
|
1851 |
|
1852 |
+
#: admin/manage-fields.php:557
|
1853 |
msgid "Heard Island and McDonald Islands"
|
1854 |
msgstr ""
|
1855 |
|
1856 |
+
#: admin/manage-fields.php:558
|
1857 |
msgid "Honduras"
|
1858 |
msgstr ""
|
1859 |
|
1860 |
+
#: admin/manage-fields.php:559
|
1861 |
msgid "Hong Kong"
|
1862 |
msgstr ""
|
1863 |
|
1864 |
+
#: admin/manage-fields.php:560
|
1865 |
msgid "Hungary"
|
1866 |
msgstr ""
|
1867 |
|
1868 |
+
#: admin/manage-fields.php:561
|
1869 |
msgid "Iceland"
|
1870 |
msgstr ""
|
1871 |
|
1872 |
+
#: admin/manage-fields.php:562
|
1873 |
msgid "India"
|
1874 |
msgstr ""
|
1875 |
|
1876 |
+
#: admin/manage-fields.php:563
|
1877 |
msgid "Indonesia"
|
1878 |
msgstr ""
|
1879 |
|
1880 |
+
#: admin/manage-fields.php:564
|
1881 |
msgid "Iran"
|
1882 |
msgstr ""
|
1883 |
|
1884 |
+
#: admin/manage-fields.php:565
|
1885 |
msgid "Iraq"
|
1886 |
msgstr ""
|
1887 |
|
1888 |
+
#: admin/manage-fields.php:566
|
1889 |
msgid "Ireland"
|
1890 |
msgstr ""
|
1891 |
|
1892 |
+
#: admin/manage-fields.php:567
|
1893 |
msgid "Isle of Man"
|
1894 |
msgstr ""
|
1895 |
|
1896 |
+
#: admin/manage-fields.php:568
|
1897 |
msgid "Israel"
|
1898 |
msgstr ""
|
1899 |
|
1900 |
+
#: admin/manage-fields.php:569
|
1901 |
msgid "Italy"
|
1902 |
msgstr ""
|
1903 |
|
1904 |
+
#: admin/manage-fields.php:570
|
1905 |
msgid "Ivory Coast"
|
1906 |
msgstr ""
|
1907 |
|
1908 |
+
#: admin/manage-fields.php:571
|
1909 |
msgid "Jamaica"
|
1910 |
msgstr ""
|
1911 |
|
1912 |
+
#: admin/manage-fields.php:572
|
1913 |
msgid "Japan"
|
1914 |
msgstr ""
|
1915 |
|
1916 |
+
#: admin/manage-fields.php:573
|
1917 |
msgid "Jersey"
|
1918 |
msgstr ""
|
1919 |
|
1920 |
+
#: admin/manage-fields.php:574
|
1921 |
msgid "Jordan"
|
1922 |
msgstr ""
|
1923 |
|
1924 |
+
#: admin/manage-fields.php:575
|
1925 |
msgid "Kazakhstan"
|
1926 |
msgstr ""
|
1927 |
|
1928 |
+
#: admin/manage-fields.php:576
|
1929 |
msgid "Kenya"
|
1930 |
msgstr ""
|
1931 |
|
1932 |
+
#: admin/manage-fields.php:577
|
1933 |
msgid "Kiribati"
|
1934 |
msgstr ""
|
1935 |
|
1936 |
+
#: admin/manage-fields.php:578
|
1937 |
msgid "Kosovo"
|
1938 |
msgstr ""
|
1939 |
|
1940 |
+
#: admin/manage-fields.php:579
|
1941 |
msgid "Kuwait"
|
1942 |
msgstr ""
|
1943 |
|
1944 |
+
#: admin/manage-fields.php:580
|
1945 |
msgid "Kyrgyzstan"
|
1946 |
msgstr ""
|
1947 |
|
1948 |
+
#: admin/manage-fields.php:581
|
1949 |
msgid "Laos"
|
1950 |
msgstr ""
|
1951 |
|
1952 |
+
#: admin/manage-fields.php:582
|
1953 |
msgid "Latvia"
|
1954 |
msgstr ""
|
1955 |
|
1956 |
+
#: admin/manage-fields.php:583
|
1957 |
msgid "Lebanon"
|
1958 |
msgstr ""
|
1959 |
|
1960 |
+
#: admin/manage-fields.php:584
|
1961 |
msgid "Lesotho"
|
1962 |
msgstr ""
|
1963 |
|
1964 |
+
#: admin/manage-fields.php:585
|
1965 |
msgid "Liberia"
|
1966 |
msgstr ""
|
1967 |
|
1968 |
+
#: admin/manage-fields.php:586
|
1969 |
msgid "Libya"
|
1970 |
msgstr ""
|
1971 |
|
1972 |
+
#: admin/manage-fields.php:587
|
1973 |
msgid "Liechtenstein"
|
1974 |
msgstr ""
|
1975 |
|
1976 |
+
#: admin/manage-fields.php:588
|
1977 |
msgid "Lithuania"
|
1978 |
msgstr ""
|
1979 |
|
1980 |
+
#: admin/manage-fields.php:589
|
1981 |
msgid "Luxembourg"
|
1982 |
msgstr ""
|
1983 |
|
1984 |
+
#: admin/manage-fields.php:590
|
1985 |
msgid "Macao"
|
1986 |
msgstr ""
|
1987 |
|
1988 |
+
#: admin/manage-fields.php:591
|
1989 |
msgid "Macedonia"
|
1990 |
msgstr ""
|
1991 |
|
1992 |
+
#: admin/manage-fields.php:592
|
1993 |
msgid "Madagascar"
|
1994 |
msgstr ""
|
1995 |
|
1996 |
+
#: admin/manage-fields.php:593
|
1997 |
msgid "Malawi"
|
1998 |
msgstr ""
|
1999 |
|
2000 |
+
#: admin/manage-fields.php:594
|
2001 |
msgid "Malaysia"
|
2002 |
msgstr ""
|
2003 |
|
2004 |
+
#: admin/manage-fields.php:595
|
2005 |
msgid "Maldives"
|
2006 |
msgstr ""
|
2007 |
|
2008 |
+
#: admin/manage-fields.php:596
|
2009 |
msgid "Mali"
|
2010 |
msgstr ""
|
2011 |
|
2012 |
+
#: admin/manage-fields.php:597
|
2013 |
msgid "Malta"
|
2014 |
msgstr ""
|
2015 |
|
2016 |
+
#: admin/manage-fields.php:598
|
2017 |
msgid "Marshall Islands"
|
2018 |
msgstr ""
|
2019 |
|
2020 |
+
#: admin/manage-fields.php:599
|
2021 |
msgid "Martinique"
|
2022 |
msgstr ""
|
2023 |
|
2024 |
+
#: admin/manage-fields.php:600
|
2025 |
msgid "Mauritania"
|
2026 |
msgstr ""
|
2027 |
|
2028 |
+
#: admin/manage-fields.php:601
|
2029 |
msgid "Mauritius"
|
2030 |
msgstr ""
|
2031 |
|
2032 |
+
#: admin/manage-fields.php:602
|
2033 |
msgid "Mayotte"
|
2034 |
msgstr ""
|
2035 |
|
2036 |
+
#: admin/manage-fields.php:603
|
2037 |
msgid "Mexico"
|
2038 |
msgstr ""
|
2039 |
|
2040 |
+
#: admin/manage-fields.php:604
|
2041 |
msgid "Micronesia"
|
2042 |
msgstr ""
|
2043 |
|
2044 |
+
#: admin/manage-fields.php:605
|
2045 |
msgid "Moldova"
|
2046 |
msgstr ""
|
2047 |
|
2048 |
+
#: admin/manage-fields.php:606
|
2049 |
msgid "Monaco"
|
2050 |
msgstr ""
|
2051 |
|
2052 |
+
#: admin/manage-fields.php:607
|
2053 |
msgid "Mongolia"
|
2054 |
msgstr ""
|
2055 |
|
2056 |
+
#: admin/manage-fields.php:608
|
2057 |
msgid "Montenegro"
|
2058 |
msgstr ""
|
2059 |
|
2060 |
+
#: admin/manage-fields.php:609
|
2061 |
msgid "Montserrat"
|
2062 |
msgstr ""
|
2063 |
|
2064 |
+
#: admin/manage-fields.php:610
|
2065 |
msgid "Morocco"
|
2066 |
msgstr ""
|
2067 |
|
2068 |
+
#: admin/manage-fields.php:611
|
2069 |
msgid "Mozambique"
|
2070 |
msgstr ""
|
2071 |
|
2072 |
+
#: admin/manage-fields.php:612
|
2073 |
msgid "Myanmar"
|
2074 |
msgstr ""
|
2075 |
|
2076 |
+
#: admin/manage-fields.php:613
|
2077 |
msgid "Namibia"
|
2078 |
msgstr ""
|
2079 |
|
2080 |
+
#: admin/manage-fields.php:614
|
2081 |
msgid "Nauru"
|
2082 |
msgstr ""
|
2083 |
|
2084 |
+
#: admin/manage-fields.php:615
|
2085 |
msgid "Nepal"
|
2086 |
msgstr ""
|
2087 |
|
2088 |
+
#: admin/manage-fields.php:616
|
2089 |
msgid "Netherlands"
|
2090 |
msgstr ""
|
2091 |
|
2092 |
+
#: admin/manage-fields.php:617
|
2093 |
msgid "New Caledonia"
|
2094 |
msgstr ""
|
2095 |
|
2096 |
+
#: admin/manage-fields.php:618
|
2097 |
msgid "New Zealand"
|
2098 |
msgstr ""
|
2099 |
|
2100 |
+
#: admin/manage-fields.php:619
|
2101 |
msgid "Nicaragua"
|
2102 |
msgstr ""
|
2103 |
|
2104 |
+
#: admin/manage-fields.php:620
|
2105 |
msgid "Niger"
|
2106 |
msgstr ""
|
2107 |
|
2108 |
+
#: admin/manage-fields.php:621
|
2109 |
msgid "Nigeria"
|
2110 |
msgstr ""
|
2111 |
|
2112 |
+
#: admin/manage-fields.php:622
|
2113 |
msgid "Niue"
|
2114 |
msgstr ""
|
2115 |
|
2116 |
+
#: admin/manage-fields.php:623
|
2117 |
msgid "Norfolk Island"
|
2118 |
msgstr ""
|
2119 |
|
2120 |
+
#: admin/manage-fields.php:624
|
2121 |
msgid "North Korea"
|
2122 |
msgstr ""
|
2123 |
|
2124 |
+
#: admin/manage-fields.php:625
|
2125 |
msgid "Northern Mariana Islands"
|
2126 |
msgstr ""
|
2127 |
|
2128 |
+
#: admin/manage-fields.php:626
|
2129 |
msgid "Norway"
|
2130 |
msgstr ""
|
2131 |
|
2132 |
+
#: admin/manage-fields.php:627
|
2133 |
msgid "Oman"
|
2134 |
msgstr ""
|
2135 |
|
2136 |
+
#: admin/manage-fields.php:628
|
2137 |
msgid "Pakistan"
|
2138 |
msgstr ""
|
2139 |
|
2140 |
+
#: admin/manage-fields.php:629
|
2141 |
msgid "Palau"
|
2142 |
msgstr ""
|
2143 |
|
2144 |
+
#: admin/manage-fields.php:630
|
2145 |
msgid "Palestinian Territory"
|
2146 |
msgstr ""
|
2147 |
|
2148 |
+
#: admin/manage-fields.php:631
|
2149 |
msgid "Panama"
|
2150 |
msgstr ""
|
2151 |
|
2152 |
+
#: admin/manage-fields.php:632
|
2153 |
msgid "Papua New Guinea"
|
2154 |
msgstr ""
|
2155 |
|
2156 |
+
#: admin/manage-fields.php:633
|
2157 |
msgid "Paraguay"
|
2158 |
msgstr ""
|
2159 |
|
2160 |
+
#: admin/manage-fields.php:634
|
2161 |
msgid "Peru"
|
2162 |
msgstr ""
|
2163 |
|
2164 |
+
#: admin/manage-fields.php:635
|
2165 |
msgid "Philippines"
|
2166 |
msgstr ""
|
2167 |
|
2168 |
+
#: admin/manage-fields.php:636
|
2169 |
msgid "Pitcairn"
|
2170 |
msgstr ""
|
2171 |
|
2172 |
+
#: admin/manage-fields.php:637
|
2173 |
msgid "Poland"
|
2174 |
msgstr ""
|
2175 |
|
2176 |
+
#: admin/manage-fields.php:638
|
2177 |
msgid "Portugal"
|
2178 |
msgstr ""
|
2179 |
|
2180 |
+
#: admin/manage-fields.php:639
|
2181 |
msgid "Puerto Rico"
|
2182 |
msgstr ""
|
2183 |
|
2184 |
+
#: admin/manage-fields.php:640
|
2185 |
msgid "Qatar"
|
2186 |
msgstr ""
|
2187 |
|
2188 |
+
#: admin/manage-fields.php:641
|
2189 |
msgid "Republic of the Congo"
|
2190 |
msgstr ""
|
2191 |
|
2192 |
+
#: admin/manage-fields.php:642
|
2193 |
msgid "Reunion"
|
2194 |
msgstr ""
|
2195 |
|
2196 |
+
#: admin/manage-fields.php:643
|
2197 |
msgid "Romania"
|
2198 |
msgstr ""
|
2199 |
|
2200 |
+
#: admin/manage-fields.php:644
|
2201 |
msgid "Russia"
|
2202 |
msgstr ""
|
2203 |
|
2204 |
+
#: admin/manage-fields.php:645
|
2205 |
msgid "Rwanda"
|
2206 |
msgstr ""
|
2207 |
|
2208 |
+
#: admin/manage-fields.php:646
|
2209 |
msgid "Saint Barthelemy"
|
2210 |
msgstr ""
|
2211 |
|
2212 |
+
#: admin/manage-fields.php:647
|
2213 |
msgid "Saint Helena"
|
2214 |
msgstr ""
|
2215 |
|
2216 |
+
#: admin/manage-fields.php:648
|
2217 |
msgid "Saint Kitts and Nevis"
|
2218 |
msgstr ""
|
2219 |
|
2220 |
+
#: admin/manage-fields.php:649
|
2221 |
msgid "Saint Lucia"
|
2222 |
msgstr ""
|
2223 |
|
2224 |
+
#: admin/manage-fields.php:650
|
2225 |
msgid "Saint Martin"
|
2226 |
msgstr ""
|
2227 |
|
2228 |
+
#: admin/manage-fields.php:651
|
2229 |
msgid "Saint Pierre and Miquelon"
|
2230 |
msgstr ""
|
2231 |
|
2232 |
+
#: admin/manage-fields.php:652
|
2233 |
msgid "Saint Vincent and the Grenadines"
|
2234 |
msgstr ""
|
2235 |
|
2236 |
+
#: admin/manage-fields.php:653
|
2237 |
msgid "Samoa"
|
2238 |
msgstr ""
|
2239 |
|
2240 |
+
#: admin/manage-fields.php:654
|
2241 |
msgid "San Marino"
|
2242 |
msgstr ""
|
2243 |
|
2244 |
+
#: admin/manage-fields.php:655
|
2245 |
msgid "Sao Tome and Principe"
|
2246 |
msgstr ""
|
2247 |
|
2248 |
+
#: admin/manage-fields.php:656
|
2249 |
msgid "Saudi Arabia"
|
2250 |
msgstr ""
|
2251 |
|
2252 |
+
#: admin/manage-fields.php:657
|
2253 |
msgid "Senegal"
|
2254 |
msgstr ""
|
2255 |
|
2256 |
+
#: admin/manage-fields.php:658
|
2257 |
msgid "Serbia"
|
2258 |
msgstr ""
|
2259 |
|
2260 |
+
#: admin/manage-fields.php:659
|
2261 |
msgid "Seychelles"
|
2262 |
msgstr ""
|
2263 |
|
2264 |
+
#: admin/manage-fields.php:660
|
2265 |
msgid "Sierra Leone"
|
2266 |
msgstr ""
|
2267 |
|
2268 |
+
#: admin/manage-fields.php:661
|
2269 |
msgid "Singapore"
|
2270 |
msgstr ""
|
2271 |
|
2272 |
+
#: admin/manage-fields.php:662
|
2273 |
msgid "Sint Maarten"
|
2274 |
msgstr ""
|
2275 |
|
2276 |
+
#: admin/manage-fields.php:663
|
2277 |
msgid "Slovakia"
|
2278 |
msgstr ""
|
2279 |
|
2280 |
+
#: admin/manage-fields.php:664
|
2281 |
msgid "Slovenia"
|
2282 |
msgstr ""
|
2283 |
|
2284 |
+
#: admin/manage-fields.php:665
|
2285 |
msgid "Solomon Islands"
|
2286 |
msgstr ""
|
2287 |
|
2288 |
+
#: admin/manage-fields.php:666
|
2289 |
msgid "Somalia"
|
2290 |
msgstr ""
|
2291 |
|
2292 |
+
#: admin/manage-fields.php:667
|
2293 |
msgid "South Africa"
|
2294 |
msgstr ""
|
2295 |
|
2296 |
+
#: admin/manage-fields.php:668
|
2297 |
msgid "South Georgia and the South Sandwich Islands"
|
2298 |
msgstr ""
|
2299 |
|
2300 |
+
#: admin/manage-fields.php:669
|
2301 |
msgid "South Korea"
|
2302 |
msgstr ""
|
2303 |
|
2304 |
+
#: admin/manage-fields.php:670
|
2305 |
msgid "South Sudan"
|
2306 |
msgstr ""
|
2307 |
|
2308 |
+
#: admin/manage-fields.php:671
|
2309 |
msgid "Spain"
|
2310 |
msgstr ""
|
2311 |
|
2312 |
+
#: admin/manage-fields.php:672
|
2313 |
msgid "Sri Lanka"
|
2314 |
msgstr ""
|
2315 |
|
2316 |
+
#: admin/manage-fields.php:673
|
2317 |
msgid "Sudan"
|
2318 |
msgstr ""
|
2319 |
|
2320 |
+
#: admin/manage-fields.php:674
|
2321 |
msgid "Suriname"
|
2322 |
msgstr ""
|
2323 |
|
2324 |
+
#: admin/manage-fields.php:675
|
2325 |
msgid "Svalbard and Jan Mayen"
|
2326 |
msgstr ""
|
2327 |
|
2328 |
+
#: admin/manage-fields.php:676
|
2329 |
msgid "Swaziland"
|
2330 |
msgstr ""
|
2331 |
|
2332 |
+
#: admin/manage-fields.php:677
|
2333 |
msgid "Sweden"
|
2334 |
msgstr ""
|
2335 |
|
2336 |
+
#: admin/manage-fields.php:678
|
2337 |
msgid "Switzerland"
|
2338 |
msgstr ""
|
2339 |
|
2340 |
+
#: admin/manage-fields.php:679
|
2341 |
msgid "Syria"
|
2342 |
msgstr ""
|
2343 |
|
2344 |
+
#: admin/manage-fields.php:680
|
2345 |
msgid "Taiwan"
|
2346 |
msgstr ""
|
2347 |
|
2348 |
+
#: admin/manage-fields.php:681
|
2349 |
msgid "Tajikistan"
|
2350 |
msgstr ""
|
2351 |
|
2352 |
+
#: admin/manage-fields.php:682
|
2353 |
msgid "Tanzania"
|
2354 |
msgstr ""
|
2355 |
|
2356 |
+
#: admin/manage-fields.php:683
|
2357 |
msgid "Thailand"
|
2358 |
msgstr ""
|
2359 |
|
2360 |
+
#: admin/manage-fields.php:684
|
2361 |
msgid "Togo"
|
2362 |
msgstr ""
|
2363 |
|
2364 |
+
#: admin/manage-fields.php:685
|
2365 |
msgid "Tokelau"
|
2366 |
msgstr ""
|
2367 |
|
2368 |
+
#: admin/manage-fields.php:686
|
2369 |
msgid "Tonga"
|
2370 |
msgstr ""
|
2371 |
|
2372 |
+
#: admin/manage-fields.php:687
|
2373 |
msgid "Trinidad and Tobago"
|
2374 |
msgstr ""
|
2375 |
|
2376 |
+
#: admin/manage-fields.php:688
|
2377 |
msgid "Tunisia"
|
2378 |
msgstr ""
|
2379 |
|
2380 |
+
#: admin/manage-fields.php:689
|
2381 |
msgid "Turkey"
|
2382 |
msgstr ""
|
2383 |
|
2384 |
+
#: admin/manage-fields.php:690
|
2385 |
msgid "Turkmenistan"
|
2386 |
msgstr ""
|
2387 |
|
2388 |
+
#: admin/manage-fields.php:691
|
2389 |
msgid "Turks and Caicos Islands"
|
2390 |
msgstr ""
|
2391 |
|
2392 |
+
#: admin/manage-fields.php:692
|
2393 |
msgid "Tuvalu"
|
2394 |
msgstr ""
|
2395 |
|
2396 |
+
#: admin/manage-fields.php:693
|
2397 |
msgid "U.S. Virgin Islands"
|
2398 |
msgstr ""
|
2399 |
|
2400 |
+
#: admin/manage-fields.php:694
|
2401 |
msgid "Uganda"
|
2402 |
msgstr ""
|
2403 |
|
2404 |
+
#: admin/manage-fields.php:695
|
2405 |
msgid "Ukraine"
|
2406 |
msgstr ""
|
2407 |
|
2408 |
+
#: admin/manage-fields.php:696
|
2409 |
msgid "United Arab Emirates"
|
2410 |
msgstr ""
|
2411 |
|
2412 |
+
#: admin/manage-fields.php:697
|
2413 |
msgid "United Kingdom"
|
2414 |
msgstr ""
|
2415 |
|
2416 |
+
#: admin/manage-fields.php:698
|
2417 |
msgid "United States"
|
2418 |
msgstr ""
|
2419 |
|
2420 |
+
#: admin/manage-fields.php:699
|
2421 |
msgid "United States Minor Outlying Islands"
|
2422 |
msgstr ""
|
2423 |
|
2424 |
+
#: admin/manage-fields.php:700
|
2425 |
msgid "Uruguay"
|
2426 |
msgstr ""
|
2427 |
|
2428 |
+
#: admin/manage-fields.php:701
|
2429 |
msgid "Uzbekistan"
|
2430 |
msgstr ""
|
2431 |
|
2432 |
+
#: admin/manage-fields.php:702
|
2433 |
msgid "Vanuatu"
|
2434 |
msgstr ""
|
2435 |
|
2436 |
+
#: admin/manage-fields.php:703
|
2437 |
msgid "Vatican"
|
2438 |
msgstr ""
|
2439 |
|
2440 |
+
#: admin/manage-fields.php:704
|
2441 |
msgid "Venezuela"
|
2442 |
msgstr ""
|
2443 |
|
2444 |
+
#: admin/manage-fields.php:705
|
2445 |
msgid "Vietnam"
|
2446 |
msgstr ""
|
2447 |
|
2448 |
+
#: admin/manage-fields.php:706
|
2449 |
msgid "Wallis and Futuna"
|
2450 |
msgstr ""
|
2451 |
|
2452 |
+
#: admin/manage-fields.php:707
|
2453 |
msgid "Western Sahara"
|
2454 |
msgstr ""
|
2455 |
|
2456 |
+
#: admin/manage-fields.php:708
|
2457 |
msgid "Yemen"
|
2458 |
msgstr ""
|
2459 |
|
2460 |
+
#: admin/manage-fields.php:709
|
2461 |
msgid "Zambia"
|
2462 |
msgstr ""
|
2463 |
|
2464 |
+
#: admin/manage-fields.php:710
|
2465 |
msgid "Zimbabwe"
|
2466 |
msgstr ""
|
2467 |
|
2468 |
+
#: admin/manage-fields.php:743
|
2469 |
msgid "Albania Lek"
|
2470 |
msgstr ""
|
2471 |
|
2472 |
+
#: admin/manage-fields.php:744
|
2473 |
msgid "Afghanistan Afghani"
|
2474 |
msgstr ""
|
2475 |
|
2476 |
+
#: admin/manage-fields.php:745
|
2477 |
msgid "Argentina Peso"
|
2478 |
msgstr ""
|
2479 |
|
2480 |
+
#: admin/manage-fields.php:746
|
2481 |
msgid "Aruba Guilder"
|
2482 |
msgstr ""
|
2483 |
|
2484 |
+
#: admin/manage-fields.php:747
|
2485 |
msgid "Australia Dollar"
|
2486 |
msgstr ""
|
2487 |
|
2488 |
+
#: admin/manage-fields.php:748
|
2489 |
msgid "Azerbaijan New Manat"
|
2490 |
msgstr ""
|
2491 |
|
2492 |
+
#: adm
|