Version Description
- Allow no snippet name or code to be set
- Prevented an error on fresh multisite installations
- Refactored code to use best practices
- Improved database table creation method: on a single-site install, the snippets table will always be created. On a multisite install, the network snippets table will always be created; the site-specific table will always be created for the main site; for sub-sites the snippets table will only be created on a visit to a snippets admin page.
- Updated to CodeMirror 3.14
- Changes to action and filter hook API
- Added error message handling for import snippets page
- Don't encode HTML entities in database
Download this release
Release Info
Developer | bungeshea |
Plugin | Code Snippets |
Version | 1.8 |
Comparing to | |
See all releases |
Code changes from version 1.7.1.2 to 1.8
- admin/help/import.php +41 -0
- admin/help/manage.php +42 -0
- admin/help/single.php +47 -0
- admin/messages/import.php +34 -0
- admin/messages/manage.php +42 -0
- admin/messages/single.php +32 -0
- admin/views/import.php +47 -0
- admin/views/manage.php +51 -0
- admin/views/single.php +78 -0
- assets/admin-single.css +7 -6
- assets/admin-single.js +17 -0
- assets/menu-icon.mp6.css +2 -2
- assets/screen-icon.css +1 -1
- assets/table.css +44 -44
- assets/table.mp6.css +1 -1
- code-snippets.php +326 -257
- includes/admin/import.php +0 -61
- includes/admin/manage.php +0 -56
- includes/admin/single.php +0 -99
- includes/class-admin.php +187 -126
- includes/class-list-table.php +142 -131
- includes/export.php +36 -28
- includes/functions.php +23 -0
- includes/help/import.php +0 -41
- includes/help/manage.php +0 -42
- includes/help/single.php +0 -47
- languages/code-snippets-de_DE.po +4 -4
- languages/code-snippets.po +2 -2
- languages/code-snippets.pot +2 -2
- license.txt +7 -7
- readme.txt +18 -5
- screenshot-1.jpg +0 -0
- screenshot-1.png +0 -0
- screenshot-2.jpg +0 -0
- screenshot-2.png +0 -0
- screenshot-3.jpg +0 -0
- screenshot-3.png +0 -0
- screenshot-4.jpg +0 -0
- screenshot-4.png +0 -0
- screenshot-5.jpg +0 -0
- screenshot-5.png +0 -0
- vendor/codemirror/addon/matchbrackets.js +86 -74
- vendor/codemirror/addon/search.js +131 -131
- vendor/codemirror/addon/searchcursor.js +143 -130
- vendor/codemirror/lib/codemirror.css +248 -246
- vendor/codemirror/lib/codemirror.js +612 -413
- vendor/codemirror/mode/clike.js +361 -302
- vendor/codemirror/mode/php.js +132 -129
admin/help/import.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register and handle the help tabs for the
|
5 |
+
* import snippets admin page
|
6 |
+
*
|
7 |
+
* @package Code_Snippets
|
8 |
+
* @subpackage Contextual_Help
|
9 |
+
*/
|
10 |
+
|
11 |
+
global $code_snippets;
|
12 |
+
$screen = get_current_screen();
|
13 |
+
|
14 |
+
$screen->add_help_tab( array(
|
15 |
+
'id' => 'overview',
|
16 |
+
'title' => __( 'Overview', 'code-snippets' ),
|
17 |
+
'content' =>
|
18 |
+
'<p>' . __( 'Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can load snippets from a Code Snippets (.xml) import file into the database with your existing snippets.', 'code-snippets' ) . '</p>'
|
19 |
+
) );
|
20 |
+
|
21 |
+
$screen->add_help_tab( array(
|
22 |
+
'id' => 'import',
|
23 |
+
'title' => __( 'Importing', 'code-snippets' ),
|
24 |
+
'content' =>
|
25 |
+
'<p>' . __( 'You can load your snippets from a code snippets (.xml) export file using this page.', 'code-snippets' ) .
|
26 |
+
sprintf( __( 'Snippets will be added to the database along with your existing snippets. Regardless of whether the snippets were active on the previous site, imported snippets are always inactive until activated using the <a href="%s">Manage Snippets</a> page.</p>', 'code-snippets' ), $code_snippets->admin->manage_url ) . '</p>'
|
27 |
+
) );
|
28 |
+
|
29 |
+
$screen->add_help_tab( array(
|
30 |
+
'id' => 'export',
|
31 |
+
'title' => __( 'Exporting', 'code-snippets' ),
|
32 |
+
'content' =>
|
33 |
+
'<p>' . sprintf( __( 'You can save your snippets to a Code Snippets (.xml) export file using the <a href="%s">Manage Snippets</a> page.', 'code-snippets' ), $code_snippets->admin->manage_url ) . '</p>'
|
34 |
+
) );
|
35 |
+
|
36 |
+
$screen->set_help_sidebar(
|
37 |
+
'<p><strong>' . __( 'For more information:', 'code-snippets' ) . '</strong></p>' .
|
38 |
+
'<p>' . __( '<a href="http://wordpress.org/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets' ) . '</p>' .
|
39 |
+
'<p>' . __( '<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets' ) . '</p>' .
|
40 |
+
'<p>' . __( '<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets' ) . '</p>'
|
41 |
+
);
|
admin/help/manage.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register and handle the help tabs for the
|
5 |
+
* manage snippets admin page
|
6 |
+
*
|
7 |
+
* @package Code_Snippets
|
8 |
+
* @subpackage Contextual_Help
|
9 |
+
*/
|
10 |
+
|
11 |
+
global $code_snippets;
|
12 |
+
$screen = get_current_screen();
|
13 |
+
|
14 |
+
$screen->add_help_tab( array(
|
15 |
+
'id' => 'overview',
|
16 |
+
'title' => __( 'Overview', 'code-snippets' ),
|
17 |
+
'content' =>
|
18 |
+
'<p>' . __( 'Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can manage your existing snippets and preform tasks on them such as activating, deactivating, deleting and exporting.', 'code-snippets' ) . '</p>'
|
19 |
+
) );
|
20 |
+
|
21 |
+
$screen->add_help_tab( array(
|
22 |
+
'id' => 'safe-mode',
|
23 |
+
'title' => __( 'Safe Mode', 'code-snippets' ),
|
24 |
+
'content' =>
|
25 |
+
'<p>' . __( 'Be sure to check your snippets for errors before you activate them, as a faulty snippet could bring your whole blog down. If your site starts doing strange things, deactivate all your snippets and activate them one at a time.', 'code-snippets' ) . '</p>' .
|
26 |
+
'<p>' . __("If something goes wrong with a snippet and you can't use WordPress, you can cause all snippets to stop executing by adding <code>define('CODE_SNIPPETS_SAFE_MODE', true);</code> to your <code>wp-config.php</code> file. After you have deactivated the offending snippet, you can turn off safe mode by removing this line or replacing <strong>true</strong> with <strong>false</strong>.", 'code-snippets' ) . '</p>'
|
27 |
+
) );
|
28 |
+
|
29 |
+
$screen->add_help_tab( array(
|
30 |
+
'id' => 'uninstall',
|
31 |
+
'title' => __( 'Uninstall', 'code-snippets' ),
|
32 |
+
'content' =>
|
33 |
+
'<p>' . sprintf( __( 'When you delete Code Snippets through the Plugins menu in WordPress it will clear up the <code>%1$s</code> table and a few other bits of data stored in the database. If you want to keep this data (ie: you are only temporally uninstalling Code Snippets) then remove the <code>%2$s</code> folder using FTP.', 'code-snippets' ), $code_snippets->get_table_name(), $code_snippets->plugin_dir ) .
|
34 |
+
'<p>' . __("Even if you're sure that you don't want to use Code Snippets ever again on this WordPress installation, you may want to use the export feature to back up your snippets.", 'code-snippets' ) . '</p>'
|
35 |
+
) );
|
36 |
+
|
37 |
+
$screen->set_help_sidebar(
|
38 |
+
'<p><strong>' . __( 'For more information:', 'code-snippets' ) . '</strong></p>' .
|
39 |
+
'<p>' . __( '<a href="http://wordpress.org/plugins/code-snippets" target="_blank">WordPress Extend</a></p>', 'code-snippets' ) . '</p>' .
|
40 |
+
'<p>' . __( '<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets' ) . '</p>' .
|
41 |
+
'<p>' . __( '<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets' ) . '</p>'
|
42 |
+
);
|
admin/help/single.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register and handle the help tabs for the
|
5 |
+
* single snippet admin page
|
6 |
+
*
|
7 |
+
* @package Code_Snippets
|
8 |
+
* @subpackage Contextual_Help
|
9 |
+
*/
|
10 |
+
|
11 |
+
$screen = get_current_screen();
|
12 |
+
|
13 |
+
$screen->add_help_tab( array(
|
14 |
+
'id' => 'overview',
|
15 |
+
'title' => __( 'Overview', 'code-snippets' ),
|
16 |
+
'content' =>
|
17 |
+
'<p>' . __( 'Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can add a new snippet, or edit an existing one.', 'code-snippets' ) . '</p>',
|
18 |
+
) );
|
19 |
+
|
20 |
+
$screen->add_help_tab( array(
|
21 |
+
'id' => 'finding',
|
22 |
+
'title' => __( 'Finding Snippets', 'code-snippets' ),
|
23 |
+
'content' =>
|
24 |
+
'<p>' . __( 'Here are some links to websites which host a large number of snippets that you can add to your site.
|
25 |
+
<ul>
|
26 |
+
<li><a href="http://wp-snippets.com" title="WordPress Snippets">WP-Snippets</a></li>
|
27 |
+
<li><a href="http://wpsnipp.com" title="WP Snipp">WP Snipp</a></li>
|
28 |
+
<li><a href="http://www.catswhocode.com/blog/snippets" title="Cats Who Code Snippet Library">Cats Who Code</a></li>
|
29 |
+
<li><a href="http://www.wpfunction.me">WP Function Me</a></li>
|
30 |
+
</ul>', 'code-snippets' ) .
|
31 |
+
__( 'More places to find snippets, as well as a selection of example snippets, can be found in the <a href="http://code-snippets.bungeshea.com/docs/finding-snippets/">plugin documentation</a>', 'code-snippets' ) . '</p>'
|
32 |
+
) );
|
33 |
+
|
34 |
+
$screen->add_help_tab( array(
|
35 |
+
'id' => 'adding',
|
36 |
+
'title' => __( 'Adding Snippets', 'code-snippets' ),
|
37 |
+
'content' =>
|
38 |
+
'<p>' . __( 'You need to fill out the name and code fields for your snippet to be added. While the description field will add more information about how your snippet works, what is does and where you found it, it is completely optional.', 'code-snippets' ) . '</p>' .
|
39 |
+
'<p>' . __( 'Please be sure to check that your snippet is valid PHP code and will not produce errors before adding it through this page. While doing so will not become active straight away, it will help to minimise the chance of a faulty snippet becoming active on your site.', 'code-snippets' ) . '</p>'
|
40 |
+
) );
|
41 |
+
|
42 |
+
$screen->set_help_sidebar(
|
43 |
+
'<p><strong>' . __( 'For more information:', 'code-snippets' ) . '</strong></p>' .
|
44 |
+
'<p>' . __( '<a href="http://wordpress.org/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets' ) . '</p>' .
|
45 |
+
'<p>' . __( '<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets' ) . '</p>' .
|
46 |
+
'<p>' . __( '<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets' ) . '</p>'
|
47 |
+
);
|
admin/messages/import.php
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Status and error messages for the import snippets page
|
5 |
+
*
|
6 |
+
* @package Code_Snippets
|
7 |
+
* @subpackage Admin_Messages
|
8 |
+
*/
|
9 |
+
|
10 |
+
global $code_snippets;
|
11 |
+
|
12 |
+
if ( isset( $_REQUEST['imported'] ) && 0 !== intval( $_REQUEST['imported'] ) ) {
|
13 |
+
|
14 |
+
echo '<div id="message" class="updated fade"><p>';
|
15 |
+
|
16 |
+
printf(
|
17 |
+
_n(
|
18 |
+
'Successfully imported <strong>%d</strong> snippet. <a href="%s">Have fun!</a>',
|
19 |
+
'Successfully imported <strong>%d</strong> snippets. <a href="%s">Have fun!</a>',
|
20 |
+
$_REQUEST['imported'],
|
21 |
+
'code-snippets'
|
22 |
+
),
|
23 |
+
$_REQUEST['imported'],
|
24 |
+
$code_snippets->admin->manage_url
|
25 |
+
);
|
26 |
+
|
27 |
+
echo '</p></div>';
|
28 |
+
}
|
29 |
+
elseif ( isset( $_REQUEST['error'] ) && $_REQUEST['error'] ) {
|
30 |
+
printf (
|
31 |
+
'<div id="message" class="error fade"><p>%s</p></div>',
|
32 |
+
__( 'An error occurred when processing the import file.', 'code-snippets' )
|
33 |
+
);
|
34 |
+
}
|
admin/messages/manage.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Status and error messages for the manage snippets page
|
5 |
+
*
|
6 |
+
* @package Code_Snippets
|
7 |
+
* @subpackage Admin_Messages
|
8 |
+
*/
|
9 |
+
|
10 |
+
$_f = '<div id="message" class="%2$s fade"><p>%1$s</p></div>';
|
11 |
+
|
12 |
+
if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) :
|
13 |
+
|
14 |
+
printf ( $_f, __( '<strong>Warning:</strong> Safe mode is active and snippets will not execute! Remove the <code>CODE_SNIPPETS_SAFE_MODE</code> constant from <code>wp-config.php</code> to turn off safe mode. <a href="http://code-snippets.bungeshea.com/docs/safe-mode/" target="_blank">Help</a>', 'code-snippets' ), 'error' );
|
15 |
+
|
16 |
+
endif;
|
17 |
+
|
18 |
+
if ( isset( $_REQUEST['activate'] ) ) :
|
19 |
+
|
20 |
+
printf ( $_f, __( 'Snippet <strong>activated</strong>.', 'code-snippets' ), 'updated' );
|
21 |
+
|
22 |
+
elseif ( isset( $_REQUEST['activate-multi'] ) ) :
|
23 |
+
|
24 |
+
printf ( $_f, __( 'Selected snippets <strong>activated</strong>.', 'code-snippets' ), 'updated' );
|
25 |
+
|
26 |
+
elseif ( isset( $_REQUEST['deactivate'] ) ) :
|
27 |
+
|
28 |
+
printf ( $_f, __( 'Snippet <strong>deactivated</strong>.', 'code-snippets' ), 'updated' );
|
29 |
+
|
30 |
+
elseif ( isset( $_REQUEST['deactivate-multi'] ) ) :
|
31 |
+
|
32 |
+
printf ( $_f, __( 'Selected snippets <strong>deactivated</strong>.', 'code-snippets' ), 'updated' );
|
33 |
+
|
34 |
+
elseif ( isset( $_REQUEST['delete'] ) ) :
|
35 |
+
|
36 |
+
printf ( $_f, __( 'Snippet <strong>deleted</strong>.', 'code-snippets' ), 'updated' );
|
37 |
+
|
38 |
+
elseif ( isset( $_REQUEST['delete-multi'] ) ) :
|
39 |
+
|
40 |
+
printf ( $_f, __( 'Selected snippets <strong>deleted</strong>.', 'code-snippets' ), 'updated' );
|
41 |
+
|
42 |
+
endif;
|
admin/messages/single.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Status and error messages for the single snippet page
|
5 |
+
*
|
6 |
+
* @package Code_Snippets
|
7 |
+
* @subpackage Admin_Messages
|
8 |
+
*/
|
9 |
+
|
10 |
+
$_f = '<div id="message" class="%2$s fade"><p>%1$s</p></div>';
|
11 |
+
|
12 |
+
if ( isset( $_REQUEST['invalid'] ) && $_REQUEST['invalid'] ) :
|
13 |
+
|
14 |
+
printf ( $_f, __( 'An error occurred when saving the snippet.', 'code-snippets' ), 'error' );
|
15 |
+
|
16 |
+
elseif ( isset( $_REQUEST['activated'], $_REQUEST['updated'] ) && $_REQUEST['activated'] && $_REQUEST['updated'] ) :
|
17 |
+
|
18 |
+
printf ( $_f, __( 'Snippet <strong>updated</strong> and <strong>activated</strong>.', 'code-snippets' ), 'updated' );
|
19 |
+
|
20 |
+
elseif ( isset( $_REQUEST['activated'], $_REQUEST['added'] ) && $_REQUEST['activated'] && $_REQUEST['added'] ) :
|
21 |
+
|
22 |
+
printf ( $_f, __( 'Snippet <strong>added</strong> and <strong>activated</strong>.', 'code-snippets' ), 'updated' );
|
23 |
+
|
24 |
+
elseif ( isset( $_REQUEST['updated'] ) && $_REQUEST['updated'] ) :
|
25 |
+
|
26 |
+
printf ( $_f, __( 'Snippet <strong>updated</strong>.', 'code-snippets' ), 'updated' );
|
27 |
+
|
28 |
+
elseif ( isset( $_REQUEST['added'] ) && $_REQUEST['added'] ) :
|
29 |
+
|
30 |
+
printf ( $_f, __( 'Snippet <strong>added</strong>.', 'code-snippets' ), 'updated' );
|
31 |
+
|
32 |
+
endif;
|
admin/views/import.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* HTML code for the Import Snippets page
|
5 |
+
*
|
6 |
+
* @package Code_Snippets
|
7 |
+
* @subpackage Admin_Views
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! class_exists( 'Code_Snippets' ) )
|
11 |
+
exit;
|
12 |
+
|
13 |
+
global $code_snippets;
|
14 |
+
|
15 |
+
$code_snippets->admin->get_messages( 'import' );
|
16 |
+
|
17 |
+
?>
|
18 |
+
<div class="wrap">
|
19 |
+
<?php screen_icon(); ?>
|
20 |
+
<h2><?php _e( 'Import Snippets', 'code-snippets' ); ?></h2>
|
21 |
+
|
22 |
+
<div class="narrow">
|
23 |
+
|
24 |
+
<p><?php _e( 'Howdy! Upload your Code Snippets export file and we’ll import the snippets to this site.', 'code-snippets' ); ?></p>
|
25 |
+
|
26 |
+
<p><?php printf( __( 'You will need to go to the <a href="%s">Manage Snippets</a> page to activate the imported snippets.', 'code-snippets' ), $code_snippets->admin->manage_url ); ?></p>
|
27 |
+
|
28 |
+
<p><?php _e( 'Choose a Code Snippets (.xml) file to upload, then click Upload file and import.', 'code-snippets' ); ?></p>
|
29 |
+
|
30 |
+
<form enctype="multipart/form-data" method="post" action="" id="import-upload-form" name="code_snippets_import">
|
31 |
+
<p>
|
32 |
+
<input type="hidden" name="action" value="save" />
|
33 |
+
<input type="hidden" name="max_file_size" value="8388608" />
|
34 |
+
|
35 |
+
<label for="upload"><?php _e( 'Choose a file from your computer:', 'code-snippets' ); ?></label>
|
36 |
+
<?php _e( '(Maximum size: 8MB)', 'code-snippets' ); ?>
|
37 |
+
<input type="file" id="upload" name="code_snippets_import_file" size="25" accept="text/xml" />
|
38 |
+
</p>
|
39 |
+
|
40 |
+
<?php
|
41 |
+
do_action( 'code_snippets/admin/import_form' );
|
42 |
+
submit_button( __( 'Upload file and import', 'code-snippets' ) );
|
43 |
+
?>
|
44 |
+
|
45 |
+
</form>
|
46 |
+
</div>
|
47 |
+
</div>
|
admin/views/manage.php
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* HTML code for the Manage Snippets page
|
5 |
+
*
|
6 |
+
* @package Code_Snippets
|
7 |
+
* @subpackage Admin_Views
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! class_exists( 'Code_Snippets' ) )
|
11 |
+
exit;
|
12 |
+
|
13 |
+
global $code_snippets;
|
14 |
+
$screen = get_current_screen();
|
15 |
+
$code_snippets->admin->get_messages( 'manage' );
|
16 |
+
?>
|
17 |
+
|
18 |
+
<div class="wrap">
|
19 |
+
<?php screen_icon(); ?>
|
20 |
+
<h2><?php
|
21 |
+
esc_html_e( 'Snippets', 'code-snippets' );
|
22 |
+
|
23 |
+
if ( $code_snippets->user_can( 'install' ) ) {
|
24 |
+
|
25 |
+
printf ( '<a href="%2$s" class="add-new-h2">%1$s</a>',
|
26 |
+
esc_html_x( 'Add New', 'snippet', 'code-snippets' ),
|
27 |
+
$code_snippets->admin->single_url
|
28 |
+
);
|
29 |
+
}
|
30 |
+
|
31 |
+
$code_snippets->list_table->search_notice();
|
32 |
+
?></h2>
|
33 |
+
|
34 |
+
<?php $code_snippets->list_table->views(); ?>
|
35 |
+
|
36 |
+
<form method="get" action="">
|
37 |
+
<?php
|
38 |
+
$code_snippets->list_table->required_form_fields( 'search_box' );
|
39 |
+
$code_snippets->list_table->search_box( __( 'Search Installed Snippets', 'code-snippets' ), 'search_id' );
|
40 |
+
?>
|
41 |
+
</form>
|
42 |
+
<form method="post" action="">
|
43 |
+
<?php
|
44 |
+
$code_snippets->list_table->required_form_fields();
|
45 |
+
$code_snippets->list_table->display();
|
46 |
+
?>
|
47 |
+
</form>
|
48 |
+
|
49 |
+
<?php do_action( 'code_snippets/admin/manage' ); ?>
|
50 |
+
|
51 |
+
</div>
|
admin/views/single.php
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* HTML code for the Add New/Edit Snippet page
|
5 |
+
*
|
6 |
+
* @package Code_Snippets
|
7 |
+
* @subpackage Admin_Views
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! class_exists( 'Code_Snippets' ) )
|
11 |
+
exit;
|
12 |
+
|
13 |
+
global $code_snippets;
|
14 |
+
|
15 |
+
$table = $code_snippets->get_table_name();
|
16 |
+
$screen = get_current_screen();
|
17 |
+
|
18 |
+
$edit_id = ( isset( $_REQUEST['edit'] ) ? absint( $_REQUEST['edit'] ) : 0 );
|
19 |
+
$snippet = $code_snippets->get_snippet( $edit_id );
|
20 |
+
|
21 |
+
$code_snippets->admin->get_messages( 'single' );
|
22 |
+
|
23 |
+
?>
|
24 |
+
|
25 |
+
<div class="wrap">
|
26 |
+
<?php screen_icon(); ?>
|
27 |
+
<h2><?php
|
28 |
+
if ( $edit_id ) {
|
29 |
+
esc_html_e( 'Edit Snippet', 'code-snippets' );
|
30 |
+
|
31 |
+
if ( $code_snippets->user_can( 'install' ) )
|
32 |
+
printf( ' <a href="%1$s" class="add-new-h2">%2$s</a>',
|
33 |
+
$code_snippets->admin->single_url,
|
34 |
+
esc_html_x( 'Add New', 'snippet', 'code-snippets' )
|
35 |
+
);
|
36 |
+
} else {
|
37 |
+
esc_html_e( 'Add New Snippet', 'code-snippets' );
|
38 |
+
}
|
39 |
+
?></h2>
|
40 |
+
|
41 |
+
<form method="post" action="" style="margin-top: 10px;">
|
42 |
+
<?php
|
43 |
+
|
44 |
+
/* Output the hidden fields */
|
45 |
+
|
46 |
+
if ( 0 !== $snippet->id )
|
47 |
+
printf ( '<input type="hidden" name="snippet_id" value="%d" />', $snippet->id );
|
48 |
+
|
49 |
+
printf ( '<input type="hidden" name="snippet_active" value="%d" />', $snippet->active );
|
50 |
+
?>
|
51 |
+
<div id="titlediv">
|
52 |
+
<div id="titlewrap">
|
53 |
+
<label for="title" style="display: none;"><?php _e( 'Name (short title)', 'code-snippets' ); ?></label>
|
54 |
+
<input id="title" type="text" autocomplete="off" name="snippet_name" value="<?php echo $snippet->name; ?>" placeholder="<?php _e( 'Name (short title)', 'code-snippets' ); ?>" />
|
55 |
+
</div>
|
56 |
+
</div>
|
57 |
+
|
58 |
+
<label for="snippet_code">
|
59 |
+
<h3><?php _e( 'Code', 'code-snippets' ); ?></h3>
|
60 |
+
</label>
|
61 |
+
|
62 |
+
<textarea id="snippet_code" name="snippet_code" rows="20" spellcheck="false" style="font-family: monospace; width: 100%;"><?php echo esc_textarea( $snippet->code ); ?></textarea>
|
63 |
+
|
64 |
+
<?php do_action( 'code_snippets/admin/single', $snippet ); ?>
|
65 |
+
|
66 |
+
<p class="submit">
|
67 |
+
<?php
|
68 |
+
submit_button( null, 'primary', 'save_snippet', false );
|
69 |
+
|
70 |
+
if ( ! $snippet->active ) {
|
71 |
+
echo ' ';
|
72 |
+
submit_button( __( 'Save Changes & Activate', 'code-snippets' ), 'secondary', 'save_snippet_activate', false );
|
73 |
+
}
|
74 |
+
?>
|
75 |
+
</p>
|
76 |
+
|
77 |
+
</form>
|
78 |
+
</div>
|
assets/admin-single.css
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
/**
|
2 |
* Custom styling for the single snippet admin page
|
3 |
*
|
4 |
-
* @package
|
5 |
* @subpackage Assets
|
6 |
*/
|
7 |
|
@@ -22,8 +22,11 @@ label {
|
|
22 |
.CodeMirror {
|
23 |
min-height: 300px;
|
24 |
height: auto;
|
25 |
-
border: 1px solid #eee;
|
26 |
-
-
|
|
|
|
|
|
|
27 |
}
|
28 |
|
29 |
.CodeMirror-scroll {
|
@@ -35,9 +38,7 @@ label {
|
|
35 |
min-height: 300px !important;
|
36 |
}
|
37 |
|
38 |
-
/**
|
39 |
-
* Tweak things for the MP6 interface
|
40 |
-
*/
|
41 |
.admin-color-mp6 .CodeMirror {
|
42 |
width: 100%;
|
43 |
border-color: #dfdfdf;
|
1 |
/**
|
2 |
* Custom styling for the single snippet admin page
|
3 |
*
|
4 |
+
* @package Code_Snippets
|
5 |
* @subpackage Assets
|
6 |
*/
|
7 |
|
22 |
.CodeMirror {
|
23 |
min-height: 300px;
|
24 |
height: auto;
|
25 |
+
border: 1px solid #eee;
|
26 |
+
-webkit-border-radius: 3px;
|
27 |
+
-moz-border-radius: 3px;
|
28 |
+
border-radius: 3px;
|
29 |
+
-o-border-radius: 3px;
|
30 |
}
|
31 |
|
32 |
.CodeMirror-scroll {
|
38 |
min-height: 300px !important;
|
39 |
}
|
40 |
|
41 |
+
/** Tweak things for the MP6 interface */
|
|
|
|
|
42 |
.admin-color-mp6 .CodeMirror {
|
43 |
width: 100%;
|
44 |
border-color: #dfdfdf;
|
assets/admin-single.js
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Loads CodeMirror on the snippet editor
|
3 |
+
*
|
4 |
+
* @package Code_Snippets
|
5 |
+
* @subpackage Assets
|
6 |
+
*/
|
7 |
+
|
8 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("snippet_code"), {
|
9 |
+
lineNumbers: true,
|
10 |
+
matchBrackets: true,
|
11 |
+
lineWrapping: true,
|
12 |
+
mode: "text/x-php",
|
13 |
+
indentUnit: 4,
|
14 |
+
indentWithTabs: true,
|
15 |
+
enterMode: "keep",
|
16 |
+
tabMode: "shift"
|
17 |
+
});
|
assets/menu-icon.mp6.css
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
* Use our awesome new SVG icon on
|
3 |
* MP6 admin pages
|
4 |
*
|
5 |
-
* @package
|
6 |
* @subpackage Assets
|
7 |
*/
|
8 |
|
@@ -20,4 +20,4 @@
|
|
20 |
|
21 |
#toplevel_page_snippets:hover .wp-menu-image {
|
22 |
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cgo8c3ZnIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjQ0cHQiIHdpZHRoPSI0OHB0IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA0OCA0NCI+CgoJPHBhdGggY2xhc3M9InNuaXBwZXRzLWljb24tc3ZnIiBkPSJNMzIuNzIsMS44MWMzLjE3LTIuMjcsOS43MS0xLjE2LDEwLjMyLDMuMTMtMi44LDMuNzItNi40OSw2LjY1LTkuNjQsMTAuMDYsNC44MiwwLjUzLDEwLjE2LTEuMjIsMTQuNiwxLjE2djEuNTVjLTAuMzcsMy4xMS0yLjM1LDYuMTQtNS40OSw2Ljk3LTYuNzksMS4yOS0xNC4wNC0xLjAyLTIwLjYyLDEuMjUsMi42NiwxLjY3LDUuNTYsMy43NCw2LjA3LDcuMSwxLjI0LDUuMTItMi45MSwxMC40My04LjA4LDEwLjgtNS4yNywwLjUtMTAuMzctNC40NS05LjY2LTkuNzksMC4zNi00LjY2LDQuNzMtNy4yMiw3LjQ2LTEwLjQ5LDEuMTUtMC45OCwxLjAyLTMuMTYtMC43My0zLjM3LTMuNzgtMC41Ny03Ljc1LDAuNDMtMTEuNDItMC44OS0zLjA2LTEuMi00Ljk5LTQuMTktNS41My03LjM1di0yLjE1YzAuODMtNS44Nyw3Ljc4LTkuNzQsMTMuMDgtNi43OSw0LjU5LDIuMTcsNS43Nyw3LjgzLDQuMjEsMTIuMzIsMC41OC0wLjIxLDEuNzUtMC42MSwyLjM0LTAuODIsNC41Ny00LDguMzgtOC44NCwxMy4wOS0xMi42OXogTTcuMzMsNy40YzIuNDgtMS4yNyw1Ljc5LDAuODEsNS42OSwzLjYxLDAuMTMsMy4wNi0zLjg4LDUuMTctNi4yOCwzLjIzLTIuNTgtMS40OS0yLjE1LTUuNzcsMC41OS02Ljg0eiBNMTcuMzYsMzEuMzhjMi44MS0xLjc4LDYuNzQsMS40NSw1LjY2LDQuNTYtMC42OCwyLjgzLTQuNjgsMy45LTYuNjUsMS43NC0xLjkzLTEuNzctMS40LTUuMjEsMC45OS02LjN6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiMyZWEyY2MiLz4KCjwvc3ZnPg==) !important;
|
23 |
-
}
|
2 |
* Use our awesome new SVG icon on
|
3 |
* MP6 admin pages
|
4 |
*
|
5 |
+
* @package Code_Snippets
|
6 |
* @subpackage Assets
|
7 |
*/
|
8 |
|
20 |
|
21 |
#toplevel_page_snippets:hover .wp-menu-image {
|
22 |
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cgo8c3ZnIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjQ0cHQiIHdpZHRoPSI0OHB0IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA0OCA0NCI+CgoJPHBhdGggY2xhc3M9InNuaXBwZXRzLWljb24tc3ZnIiBkPSJNMzIuNzIsMS44MWMzLjE3LTIuMjcsOS43MS0xLjE2LDEwLjMyLDMuMTMtMi44LDMuNzItNi40OSw2LjY1LTkuNjQsMTAuMDYsNC44MiwwLjUzLDEwLjE2LTEuMjIsMTQuNiwxLjE2djEuNTVjLTAuMzcsMy4xMS0yLjM1LDYuMTQtNS40OSw2Ljk3LTYuNzksMS4yOS0xNC4wNC0xLjAyLTIwLjYyLDEuMjUsMi42NiwxLjY3LDUuNTYsMy43NCw2LjA3LDcuMSwxLjI0LDUuMTItMi45MSwxMC40My04LjA4LDEwLjgtNS4yNywwLjUtMTAuMzctNC40NS05LjY2LTkuNzksMC4zNi00LjY2LDQuNzMtNy4yMiw3LjQ2LTEwLjQ5LDEuMTUtMC45OCwxLjAyLTMuMTYtMC43My0zLjM3LTMuNzgtMC41Ny03Ljc1LDAuNDMtMTEuNDItMC44OS0zLjA2LTEuMi00Ljk5LTQuMTktNS41My03LjM1di0yLjE1YzAuODMtNS44Nyw3Ljc4LTkuNzQsMTMuMDgtNi43OSw0LjU5LDIuMTcsNS43Nyw3LjgzLDQuMjEsMTIuMzIsMC41OC0wLjIxLDEuNzUtMC42MSwyLjM0LTAuODIsNC41Ny00LDguMzgtOC44NCwxMy4wOS0xMi42OXogTTcuMzMsNy40YzIuNDgtMS4yNyw1Ljc5LDAuODEsNS42OSwzLjYxLDAuMTMsMy4wNi0zLjg4LDUuMTctNi4yOCwzLjIzLTIuNTgtMS40OS0yLjE1LTUuNzcsMC41OS02Ljg0eiBNMTcuMzYsMzEuMzhjMi44MS0xLjc4LDYuNzQsMS40NSw1LjY2LDQuNTYtMC42OCwyLjgzLTQuNjgsMy45LTYuNjUsMS43NC0xLjkzLTEuNzctMS40LTUuMjEsMC45OS02LjN6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiMyZWEyY2MiLz4KCjwvc3ZnPg==) !important;
|
23 |
+
}
|
assets/screen-icon.css
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
/**
|
2 |
* Add the snippet screen icon to admin pages
|
3 |
*
|
4 |
-
* @package
|
5 |
* @subpackage Assets
|
6 |
*/
|
7 |
|
1 |
/**
|
2 |
* Add the snippet screen icon to admin pages
|
3 |
*
|
4 |
+
* @package Code_Snippets
|
5 |
* @subpackage Assets
|
6 |
*/
|
7 |
|
assets/table.css
CHANGED
@@ -1,44 +1,44 @@
|
|
1 |
-
/**
|
2 |
-
* Custom styling for the snippets table
|
3 |
-
*
|
4 |
-
* @package
|
5 |
-
* @subpackage Assets
|
6 |
-
*/
|
7 |
-
|
8 |
-
.snippets .inactive a {
|
9 |
-
color: #557799;
|
10 |
-
}
|
11 |
-
|
12 |
-
.snippets .inactive a:hover,
|
13 |
-
#all-snippets-table .snippets .inactive a:hover,
|
14 |
-
#search-snippets-table .snippets .inactive a:hover {
|
15 |
-
color: #d54e21;
|
16 |
-
}
|
17 |
-
|
18 |
-
.snippets .delete a {
|
19 |
-
color: #21759b;
|
20 |
-
}
|
21 |
-
|
22 |
-
#wpbody-content .snippets tr {
|
23 |
-
background-color: #fcfcfc;
|
24 |
-
}
|
25 |
-
|
26 |
-
.snippets .inactive,
|
27 |
-
.snippets .inactive th,
|
28 |
-
.snippets .inactive td {
|
29 |
-
background-color: #f4f4f4;
|
30 |
-
}
|
31 |
-
|
32 |
-
.snippets .active,
|
33 |
-
.snippets .active th,
|
34 |
-
.snippets .active td {
|
35 |
-
color: #000;
|
36 |
-
}
|
37 |
-
|
38 |
-
#wpbody-content .snippets .column-name {
|
39 |
-
white-space: nowrap; /* prevents wrapping of snippet title */
|
40 |
-
}
|
41 |
-
|
42 |
-
#wpbody-content .snippets .manage-column.column-id.sortable {
|
43 |
-
min-width: 44px;
|
44 |
-
}
|
1 |
+
/**
|
2 |
+
* Custom styling for the snippets table
|
3 |
+
*
|
4 |
+
* @package Code_Snippets
|
5 |
+
* @subpackage Assets
|
6 |
+
*/
|
7 |
+
|
8 |
+
.snippets .inactive a {
|
9 |
+
color: #557799;
|
10 |
+
}
|
11 |
+
|
12 |
+
.snippets .inactive a:hover,
|
13 |
+
#all-snippets-table .snippets .inactive a:hover,
|
14 |
+
#search-snippets-table .snippets .inactive a:hover {
|
15 |
+
color: #d54e21;
|
16 |
+
}
|
17 |
+
|
18 |
+
.snippets .delete a {
|
19 |
+
color: #21759b;
|
20 |
+
}
|
21 |
+
|
22 |
+
#wpbody-content .snippets tr {
|
23 |
+
background-color: #fcfcfc;
|
24 |
+
}
|
25 |
+
|
26 |
+
.snippets .inactive,
|
27 |
+
.snippets .inactive th,
|
28 |
+
.snippets .inactive td {
|
29 |
+
background-color: #f4f4f4;
|
30 |
+
}
|
31 |
+
|
32 |
+
.snippets .active,
|
33 |
+
.snippets .active th,
|
34 |
+
.snippets .active td {
|
35 |
+
color: #000;
|
36 |
+
}
|
37 |
+
|
38 |
+
#wpbody-content .snippets .column-name {
|
39 |
+
white-space: nowrap; /* prevents wrapping of snippet title */
|
40 |
+
}
|
41 |
+
|
42 |
+
#wpbody-content .snippets .manage-column.column-id.sortable {
|
43 |
+
min-width: 44px;
|
44 |
+
}
|
assets/table.mp6.css
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
* Custom styling for the snippets table
|
3 |
* and the MP6 interface
|
4 |
*
|
5 |
-
* @package
|
6 |
* @subpackage Assets
|
7 |
*/
|
8 |
|
2 |
* Custom styling for the snippets table
|
3 |
* and the MP6 interface
|
4 |
*
|
5 |
+
* @package Code_Snippets
|
6 |
* @subpackage Assets
|
7 |
*/
|
8 |
|
code-snippets.php
CHANGED
@@ -6,8 +6,12 @@
|
|
6 |
* If you're interested in helping to develop Code Snippets, or perhaps
|
7 |
* contribute to the localization, please see http://code-snippets.bungeshea.com
|
8 |
*
|
9 |
-
* @package
|
10 |
-
* @
|
|
|
|
|
|
|
|
|
11 |
*/
|
12 |
|
13 |
/*
|
@@ -16,15 +20,16 @@
|
|
16 |
* Description: An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!
|
17 |
* Author: Shea Bunge
|
18 |
* Author URI: http://bungeshea.com
|
19 |
-
* Version: 1.
|
20 |
* License: MIT
|
21 |
* License URI: license.txt
|
22 |
* Text Domain: code-snippets
|
23 |
* Domain Path: /languages/
|
24 |
*/
|
25 |
|
26 |
-
|
27 |
-
if ( ! defined( 'ABSPATH' ) )
|
|
|
28 |
|
29 |
if ( ! class_exists( 'Code_Snippets' ) ) :
|
30 |
|
@@ -36,9 +41,9 @@ if ( ! class_exists( 'Code_Snippets' ) ) :
|
|
36 |
* the methods or variables in this class. Anything you need
|
37 |
* to access should be publicly available there
|
38 |
*
|
39 |
-
* @since
|
40 |
-
* @package
|
41 |
-
* @access
|
42 |
*/
|
43 |
final class Code_Snippets {
|
44 |
|
@@ -49,42 +54,46 @@ final class Code_Snippets {
|
|
49 |
* This should be set to the 'Plugin Version' value,
|
50 |
* as defined above in the plugin header
|
51 |
*
|
52 |
-
* @since
|
53 |
* @access public
|
54 |
-
* @var
|
55 |
*/
|
56 |
-
public $version = '1.
|
57 |
|
58 |
/**
|
59 |
* Variables to hold plugin paths
|
60 |
*
|
61 |
-
* @since
|
62 |
* @access public
|
63 |
-
* @var
|
64 |
*/
|
65 |
-
public $file, $basename, $plugin_dir, $plugin_url;
|
66 |
|
67 |
/**
|
68 |
* Stores an instance of the list table class
|
69 |
*
|
70 |
-
* @
|
|
|
71 |
* @access public
|
72 |
-
* @see
|
73 |
*/
|
74 |
public $list_table;
|
75 |
|
76 |
/**
|
77 |
* Stores an instance of the administration class
|
78 |
*
|
79 |
-
* @
|
|
|
80 |
* @access public
|
81 |
-
* @see
|
82 |
*/
|
83 |
public $admin;
|
84 |
|
85 |
/**
|
86 |
* Used by maybe_create_tables() for bailing early
|
87 |
-
*
|
|
|
|
|
88 |
*/
|
89 |
static $tables_created = false;
|
90 |
|
@@ -95,21 +104,23 @@ final class Code_Snippets {
|
|
95 |
* $wpdb->ms_snippets, but these are maintained
|
96 |
* as references for backwards-compatibility
|
97 |
*
|
98 |
-
* @var
|
|
|
99 |
*/
|
100 |
-
public $table, $ms_table;
|
101 |
|
102 |
/**
|
103 |
* These are now deprecated in favor of those in
|
104 |
* the Code_Snippets_Admin class, but maintained as
|
105 |
* references so we don't break existing code
|
106 |
*
|
107 |
-
* @since
|
108 |
* @deprecated Moved to the Code_Snippets_Admin class in 1.7.1
|
109 |
-
* @access
|
110 |
-
* @var
|
111 |
*/
|
112 |
-
public $admin_manage,
|
|
|
113 |
|
114 |
/**
|
115 |
* The constructor function for our class
|
@@ -118,9 +129,8 @@ final class Code_Snippets {
|
|
118 |
* so other plugins may not have loaded yet. Only do stuff
|
119 |
* here that really can't wait
|
120 |
*
|
121 |
-
* @since
|
122 |
* @access private
|
123 |
-
*
|
124 |
* @return void
|
125 |
*/
|
126 |
function __construct() {
|
@@ -138,6 +148,7 @@ final class Code_Snippets {
|
|
138 |
|
139 |
/* Hook our initialize function to the plugins_loaded action */
|
140 |
add_action( 'plugins_loaded', array( $this, 'init' ) );
|
|
|
141 |
}
|
142 |
|
143 |
/**
|
@@ -146,9 +157,8 @@ final class Code_Snippets {
|
|
146 |
* This method is called *after* other plugins
|
147 |
* have been run
|
148 |
*
|
149 |
-
* @since
|
150 |
* @access public
|
151 |
-
*
|
152 |
* @return void
|
153 |
*/
|
154 |
public function init() {
|
@@ -168,41 +178,39 @@ final class Code_Snippets {
|
|
168 |
*/
|
169 |
load_plugin_textdomain( 'code-snippets', false, dirname( $this->basename ) . '/languages/' );
|
170 |
|
171 |
-
/*
|
172 |
-
* Cleanup the plugin data on uninstall
|
173 |
-
*/
|
174 |
register_uninstall_hook( $this->file, array( __CLASS__, 'uninstall' ) );
|
175 |
|
176 |
-
|
177 |
-
|
178 |
-
|
|
|
179 |
do_action( 'code_snippets_init' );
|
180 |
}
|
181 |
|
182 |
/**
|
183 |
* Initialize variables
|
184 |
*
|
185 |
-
* @since
|
186 |
* @access private
|
187 |
-
*
|
188 |
* @return void
|
189 |
*/
|
190 |
function setup_vars() {
|
191 |
|
192 |
/* Plugin directory variables */
|
193 |
$this->file = __FILE__;
|
194 |
-
$this->basename
|
195 |
$this->plugin_dir = plugin_dir_path( $this->file );
|
196 |
$this->plugin_url = plugin_dir_url ( $this->file );
|
197 |
|
198 |
if ( is_admin() ) {
|
199 |
|
200 |
/* Load our administration class */
|
201 |
-
|
202 |
$this->admin = new Code_Snippets_Admin;
|
203 |
|
204 |
/* Remap deprecated variables */
|
205 |
-
$this->admin_manage_url
|
206 |
$this->admin_single_url = &$this->admin->single_url;
|
207 |
$this->admin_import_url = &$this->admin->import_url;
|
208 |
|
@@ -212,121 +220,122 @@ final class Code_Snippets {
|
|
212 |
}
|
213 |
}
|
214 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
215 |
/**
|
216 |
* Register the snippet table names with WordPress
|
217 |
*
|
218 |
-
* @since
|
219 |
* @access public
|
220 |
-
*
|
221 |
-
* @uses $wpdb
|
222 |
-
*
|
223 |
* @return void
|
224 |
*/
|
225 |
public function set_table_vars() {
|
226 |
global $wpdb;
|
227 |
|
228 |
/* Register the snippet table names with WordPress */
|
229 |
-
$wpdb->tables[]
|
230 |
$wpdb->ms_global_tables[] = 'ms_snippets';
|
231 |
|
232 |
/* Setup initial table variables */
|
233 |
-
$wpdb->snippets
|
234 |
-
$wpdb->ms_snippets
|
235 |
|
236 |
/* Add a pointer to the old variables */
|
237 |
-
$this->table
|
238 |
-
$this->ms_table
|
239 |
}
|
240 |
|
241 |
/**
|
242 |
* Return the appropriate snippet table name
|
243 |
*
|
244 |
-
* @since
|
245 |
* @access public
|
246 |
-
*
|
247 |
-
* @param
|
248 |
-
* @
|
249 |
-
* @return string $table The snippet table name
|
250 |
*/
|
251 |
public function get_table_name( $scope = '', $check_screen = true ) {
|
252 |
-
|
253 |
global $wpdb;
|
254 |
|
255 |
-
|
256 |
-
|
257 |
if ( ! is_multisite() ) {
|
258 |
$network = false;
|
259 |
}
|
|
|
|
|
260 |
elseif ( in_array( $scope, array( 'multisite', 'network' ) ) ) {
|
261 |
$network = true;
|
262 |
}
|
|
|
|
|
263 |
elseif ( empty( $scope ) && $check_screen && function_exists( 'get_current_screen' ) ) {
|
264 |
-
/* if no scope is set, query the current screen to see if in network admin */
|
265 |
$network = get_current_screen()->is_network;
|
266 |
}
|
|
|
|
|
267 |
else {
|
268 |
$network = false;
|
269 |
}
|
270 |
|
271 |
-
|
272 |
-
|
273 |
-
return $table;
|
274 |
}
|
275 |
|
276 |
/**
|
277 |
* Create the snippet tables if they do not already exist
|
278 |
*
|
279 |
-
* @since
|
280 |
* @access public
|
281 |
*
|
282 |
-
* @uses
|
283 |
-
* @
|
284 |
-
* @
|
285 |
-
*
|
286 |
-
* @
|
287 |
-
*
|
288 |
-
* @return void
|
289 |
*/
|
290 |
-
public function maybe_create_tables( $
|
291 |
|
292 |
/* Bail early if we've done this already */
|
293 |
-
if (
|
|
|
294 |
|
295 |
global $wpdb;
|
296 |
|
297 |
-
|
|
|
298 |
$this->set_table_vars();
|
299 |
-
|
300 |
-
$table_exists = $force || $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->snippets'" ) === $wpdb->snippets;
|
301 |
-
|
302 |
-
if ( ! is_multisite() && ! $table_exists ) {
|
303 |
-
$table = $wpdb->snippets;
|
304 |
}
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
$table = $wpdb->snippets . ',' . $wpdb->ms_snippets;
|
310 |
-
elseif ( ! $table_exists && $ms_table_exists )
|
311 |
-
$table = $wpdb->snippets;
|
312 |
-
elseif ( $table_exists && ! $ms_table_exists )
|
313 |
-
$table = $wpdb->ms_snippets;
|
314 |
}
|
315 |
|
316 |
-
|
317 |
-
|
|
|
|
|
318 |
|
|
|
319 |
self::$tables_created = true;
|
320 |
}
|
321 |
|
322 |
/**
|
323 |
* Create the snippet tables if they do not already exist
|
324 |
*
|
325 |
-
* @since
|
326 |
* @deprecated Code Snippets 1.7.1
|
327 |
-
* @access
|
328 |
-
*
|
329 |
-
* @return void
|
330 |
*/
|
331 |
public function create_tables() {
|
332 |
_deprecated_function(
|
@@ -341,19 +350,26 @@ final class Code_Snippets {
|
|
341 |
* Create a single snippet table
|
342 |
* if one of the same name does not already exist
|
343 |
*
|
344 |
-
* @since
|
345 |
* @access private
|
346 |
*
|
347 |
-
* @uses
|
348 |
*
|
349 |
-
* @param
|
|
|
350 |
* @return void
|
351 |
*/
|
352 |
-
function create_table( $table_name ) {
|
353 |
-
require_once
|
354 |
|
355 |
global $wpdb;
|
356 |
|
|
|
|
|
|
|
|
|
|
|
|
|
357 |
$charset_collate = '';
|
358 |
|
359 |
if ( ! empty( $wpdb->charset ) ) {
|
@@ -364,40 +380,43 @@ final class Code_Snippets {
|
|
364 |
$charset_collate .= " COLLATE $wpdb->collate";
|
365 |
}
|
366 |
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
'
|
|
|
|
|
371 |
) );
|
372 |
|
373 |
-
$table_columns_sql = implode( ",\n", $table_columns );
|
|
|
|
|
374 |
|
375 |
$sql = "CREATE TABLE $table_name (
|
376 |
-
id
|
377 |
{$table_columns_sql},
|
378 |
-
active tinyint(1)
|
379 |
PRIMARY KEY (id),
|
380 |
KEY id (id)
|
381 |
|
382 |
) {$charset_collate};";
|
383 |
|
384 |
-
dbDelta( apply_filters( '
|
385 |
|
386 |
-
do_action( '
|
387 |
}
|
388 |
|
389 |
/**
|
390 |
* Preform upgrade tasks such as deleting and updating options
|
391 |
*
|
392 |
-
* @since
|
393 |
* @access private
|
394 |
-
*
|
395 |
* @return void
|
396 |
*/
|
397 |
function upgrade() {
|
398 |
global $wpdb;
|
399 |
|
400 |
-
|
401 |
|
402 |
$current_version = get_option( 'code_snippets_version' );
|
403 |
|
@@ -409,20 +428,57 @@ final class Code_Snippets {
|
|
409 |
|
410 |
$previous_version = ( $current_version ? $current_version : $this->version );
|
411 |
|
412 |
-
/*
|
413 |
if ( version_compare( $current_version, $this->version, '<' ) ) {
|
414 |
|
415 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
416 |
if ( version_compare( $current_version, '1.5', '<' ) ) {
|
417 |
$this->setup_roles( true );
|
418 |
}
|
419 |
|
420 |
if ( version_compare( $previous_version, '1.2', '<' ) ) {
|
421 |
-
|
422 |
delete_option( 'cs_complete_uninstall' );
|
423 |
}
|
424 |
|
425 |
-
|
426 |
update_option( 'code_snippets_version', $this->version );
|
427 |
}
|
428 |
|
@@ -438,7 +494,7 @@ final class Code_Snippets {
|
|
438 |
$this->setup_ms_roles( true );
|
439 |
}
|
440 |
|
441 |
-
|
442 |
if ( get_option( 'recently_network_activated_snippets' ) ) {
|
443 |
add_site_option( 'recently_activated_snippets', get_option( 'recently_network_activated_snippets', array() ) );
|
444 |
delete_option( 'recently_network_activated_snippets' );
|
@@ -454,23 +510,22 @@ final class Code_Snippets {
|
|
454 |
/**
|
455 |
* Register the user roles and capabilities
|
456 |
*
|
457 |
-
* @since
|
458 |
* @access private
|
459 |
-
*
|
460 |
-
* @param bool $install true to add the capabilities, false to remove
|
461 |
* @return void
|
462 |
*/
|
463 |
function setup_roles( $install = true ) {
|
464 |
|
465 |
$this->caps = apply_filters( 'code_snippets_caps', array(
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
|
471 |
$this->role = get_role( apply_filters( 'code_snippets_role', 'administrator' ) );
|
472 |
|
473 |
-
foreach( $this->caps as $cap ) {
|
474 |
if ( $install )
|
475 |
$this->role->add_cap( $cap );
|
476 |
else
|
@@ -481,26 +536,26 @@ final class Code_Snippets {
|
|
481 |
/**
|
482 |
* Register the multisite user roles and capabilities
|
483 |
*
|
484 |
-
* @since
|
485 |
* @access private
|
486 |
-
*
|
487 |
-
* @param bool $install true to add the capabilities, false to remove
|
488 |
* @return void
|
489 |
*/
|
490 |
function setup_ms_roles( $install = true ) {
|
491 |
|
492 |
-
if ( ! is_multisite() )
|
|
|
493 |
|
494 |
$this->network_caps = apply_filters( 'code_snippets_network_caps', array(
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
|
500 |
$supers = get_super_admins();
|
501 |
-
foreach( $supers as $admin ) {
|
502 |
$user = new WP_User( 0, $admin );
|
503 |
-
foreach( $this->network_caps as $cap ) {
|
504 |
if ( $install )
|
505 |
$user->add_cap( $cap );
|
506 |
else
|
@@ -513,12 +568,12 @@ final class Code_Snippets {
|
|
513 |
* Check if the current user can perform some action on snippets or not
|
514 |
*
|
515 |
* @uses current_user_can() To check if the current user can perform a task
|
516 |
-
* @uses $this->get_cap()
|
517 |
*
|
518 |
-
* @param
|
519 |
-
* @return
|
520 |
*
|
521 |
-
* @since 1.7.1.1
|
522 |
* @since 1.7.1
|
523 |
* @access public
|
524 |
*/
|
@@ -535,12 +590,14 @@ final class Code_Snippets {
|
|
535 |
*
|
536 |
* @since 1.7.1.1
|
537 |
* @access public
|
|
|
|
|
538 |
*/
|
539 |
public function get_cap( $do_what ) {
|
540 |
|
541 |
if ( is_multisite() ) {
|
542 |
|
543 |
-
if ( in_array( 'snippets', get_site_option( 'menu_items' ) ) )
|
544 |
return "{$do_what}_snippets";
|
545 |
else
|
546 |
return "{$do_what}_network_snippets";
|
@@ -553,23 +610,23 @@ final class Code_Snippets {
|
|
553 |
/**
|
554 |
* Converts an array of snippet data into a snippet object
|
555 |
*
|
556 |
-
* @since
|
557 |
* @access public
|
558 |
*
|
559 |
-
* @param mixed
|
560 |
-
* @return object
|
561 |
*/
|
562 |
public function build_snippet_object( $data = null ) {
|
563 |
|
564 |
$snippet = new stdClass;
|
565 |
|
566 |
-
|
567 |
-
$snippet->id
|
568 |
-
$snippet->name
|
569 |
$snippet->description = '';
|
570 |
-
$snippet->code
|
571 |
-
$snippet->active
|
572 |
-
$snippet
|
573 |
|
574 |
if ( ! isset( $data ) ) {
|
575 |
return $snippet;
|
@@ -606,7 +663,7 @@ final class Code_Snippets {
|
|
606 |
elseif ( isset( $data['snippet_active'] ) )
|
607 |
$snippet->active = $data['snippet_active'];
|
608 |
|
609 |
-
return apply_filters( '
|
610 |
}
|
611 |
|
612 |
return $snippet;
|
@@ -615,75 +672,75 @@ final class Code_Snippets {
|
|
615 |
/**
|
616 |
* Retrieve a list of snippets from the database
|
617 |
*
|
618 |
-
* @since
|
619 |
-
* @access
|
620 |
*
|
621 |
-
* @uses
|
622 |
-
* @uses
|
623 |
*
|
624 |
-
* @param string
|
625 |
-
* @return
|
626 |
*/
|
627 |
-
|
628 |
global $wpdb;
|
629 |
|
630 |
-
$table
|
631 |
$snippets = $wpdb->get_results( "SELECT * FROM $table", ARRAY_A );
|
632 |
|
633 |
-
foreach( $snippets as $index => $snippet ) {
|
634 |
$snippets[ $index ] = $this->unescape_snippet_data( $snippet );
|
635 |
}
|
636 |
|
637 |
-
return apply_filters( '
|
638 |
}
|
639 |
|
640 |
/**
|
641 |
* Escape snippet data for inserting into the database
|
642 |
*
|
643 |
-
* @since
|
644 |
* @access public
|
645 |
*
|
646 |
-
* @param mixed
|
647 |
-
* @return object
|
648 |
*/
|
649 |
public function escape_snippet_data( $snippet ) {
|
650 |
|
651 |
$snippet = $this->build_snippet_object( $snippet );
|
652 |
|
653 |
/* remove the <?php and ?> tags from the snippet */
|
654 |
-
$snippet->code = trim
|
655 |
$snippet->code = ltrim( $snippet->code, '<?php' );
|
656 |
$snippet->code = ltrim( $snippet->code, '<?' );
|
657 |
$snippet->code = rtrim( $snippet->code, '?>' );
|
658 |
-
|
659 |
/* escape the data */
|
660 |
-
$snippet->name
|
661 |
-
$snippet->description =
|
662 |
-
$snippet->code
|
663 |
-
$snippet->id
|
664 |
|
665 |
-
return apply_filters( '
|
666 |
}
|
667 |
|
668 |
/**
|
669 |
* Unescape snippet data after retrieval from the database
|
670 |
* ready for use
|
671 |
*
|
672 |
-
* @since
|
673 |
* @access public
|
674 |
*
|
675 |
-
* @param
|
676 |
-
* @return object
|
677 |
*/
|
678 |
public function unescape_snippet_data( $snippet ) {
|
679 |
|
680 |
$snippet = $this->build_snippet_object( $snippet );
|
681 |
|
682 |
-
$snippet->name
|
683 |
-
$snippet->code
|
684 |
-
$snippet->description =
|
685 |
|
686 |
-
return apply_filters( '
|
687 |
}
|
688 |
|
689 |
/**
|
@@ -691,21 +748,22 @@ final class Code_Snippets {
|
|
691 |
* Will return empty snippet object if no snippet
|
692 |
* ID is specified
|
693 |
*
|
694 |
-
* @since
|
695 |
* @access public
|
696 |
*
|
697 |
-
* @uses
|
698 |
-
* @uses
|
699 |
*
|
700 |
-
* @param int
|
701 |
-
* @param string $scope
|
702 |
-
* @return object
|
703 |
*/
|
704 |
-
|
705 |
global $wpdb;
|
706 |
$table = $this->get_table_name( $scope );
|
|
|
707 |
|
708 |
-
if (
|
709 |
/* Retrieve the snippet from the database */
|
710 |
$snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $id ) );
|
711 |
/* Unescape the snippet data, ready for use */
|
@@ -714,19 +772,19 @@ final class Code_Snippets {
|
|
714 |
/* Get an empty snippet object */
|
715 |
$snippet = $this->build_snippet_object();
|
716 |
}
|
717 |
-
return apply_filters( '
|
718 |
}
|
719 |
|
720 |
/**
|
721 |
* Activates a snippet
|
722 |
*
|
723 |
-
* @since
|
724 |
* @access public
|
725 |
*
|
726 |
-
* @uses
|
727 |
*
|
728 |
-
* @param
|
729 |
-
* @param
|
730 |
* @return void
|
731 |
*/
|
732 |
public function activate( $ids, $scope = '' ) {
|
@@ -736,7 +794,7 @@ final class Code_Snippets {
|
|
736 |
|
737 |
$table = $this->get_table_name( $scope );
|
738 |
|
739 |
-
foreach( $ids as $id ) {
|
740 |
$wpdb->update(
|
741 |
$table,
|
742 |
array( 'active' => '1' ),
|
@@ -745,22 +803,22 @@ final class Code_Snippets {
|
|
745 |
array( '%d' )
|
746 |
);
|
747 |
|
748 |
-
do_action( '
|
749 |
}
|
750 |
|
751 |
-
do_action( '
|
752 |
}
|
753 |
|
754 |
/**
|
755 |
* Deactivates selected snippets
|
756 |
*
|
757 |
-
* @since
|
758 |
* @access public
|
759 |
*
|
760 |
-
* @uses
|
761 |
*
|
762 |
-
* @param
|
763 |
-
* @param
|
764 |
* @return void
|
765 |
*/
|
766 |
public function deactivate( $ids, $scope = '' ) {
|
@@ -771,7 +829,7 @@ final class Code_Snippets {
|
|
771 |
|
772 |
$table = $this->get_table_name( $scope );
|
773 |
|
774 |
-
foreach( $ids as $id ) {
|
775 |
$wpdb->update(
|
776 |
$table,
|
777 |
array( 'active' => '0' ),
|
@@ -781,7 +839,7 @@ final class Code_Snippets {
|
|
781 |
);
|
782 |
$recently_active = array( $id => time() ) + (array) $recently_active;
|
783 |
|
784 |
-
do_action( '
|
785 |
}
|
786 |
|
787 |
if ( $table === $wpdb->ms_snippets )
|
@@ -795,59 +853,60 @@ final class Code_Snippets {
|
|
795 |
$recently_active + (array) get_option( 'recently_activated_snippets' )
|
796 |
);
|
797 |
|
798 |
-
do_action( '
|
799 |
}
|
800 |
|
801 |
/**
|
802 |
* Deletes a snippet from the database
|
803 |
*
|
804 |
-
* @since
|
805 |
* @access public
|
806 |
*
|
807 |
-
* @uses
|
808 |
-
* @uses
|
809 |
*
|
810 |
-
* @param
|
811 |
-
* @param
|
812 |
*/
|
813 |
public function delete_snippet( $id, $scope = '' ) {
|
814 |
global $wpdb;
|
815 |
|
816 |
$table = $this->get_table_name( $scope );
|
|
|
817 |
|
818 |
-
$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE id='%d' LIMIT 1",
|
819 |
|
820 |
-
do_action( '
|
821 |
}
|
822 |
|
823 |
/**
|
824 |
* Saves a snippet to the database.
|
825 |
*
|
826 |
-
* @since
|
827 |
* @access public
|
828 |
*
|
829 |
-
* @uses
|
830 |
-
* @uses
|
831 |
*
|
832 |
-
* @param
|
833 |
-
* @param
|
834 |
-
* @return int|
|
835 |
*/
|
836 |
public function save_snippet( $snippet, $scope = '' ) {
|
837 |
global $wpdb;
|
838 |
|
839 |
$snippet = $this->escape_snippet_data( $snippet );
|
840 |
-
|
841 |
-
if ( empty( $snippet->name ) or empty( $snippet->code ) )
|
842 |
-
return false;
|
843 |
-
|
844 |
-
$table = $this->get_table_name( $scope );
|
845 |
|
846 |
$fields = '';
|
847 |
foreach ( get_object_vars( $snippet ) as $field => $value ) {
|
848 |
-
if ( 'id'
|
849 |
-
|
850 |
-
|
|
|
|
|
|
|
|
|
851 |
}
|
852 |
$fields = rtrim( $fields, ',' );
|
853 |
|
@@ -857,7 +916,7 @@ final class Code_Snippets {
|
|
857 |
$wpdb->query( $wpdb->prepare( "UPDATE $table SET $fields WHERE id='%d' LIMIT 1", $snippet->id ) );
|
858 |
}
|
859 |
|
860 |
-
do_action( '
|
861 |
return $snippet->id;
|
862 |
|
863 |
} else {
|
@@ -866,7 +925,7 @@ final class Code_Snippets {
|
|
866 |
$wpdb->query( "INSERT INTO $table SET $fields" );
|
867 |
}
|
868 |
|
869 |
-
do_action( '
|
870 |
return $wpdb->insert_id;
|
871 |
}
|
872 |
}
|
@@ -874,14 +933,14 @@ final class Code_Snippets {
|
|
874 |
/**
|
875 |
* Imports snippets from an XML file
|
876 |
*
|
877 |
-
* @since
|
878 |
* @access public
|
879 |
*
|
880 |
-
|
881 |
*
|
882 |
-
* @param
|
883 |
-
* @param
|
884 |
-
* @return
|
885 |
*/
|
886 |
public function import( $file, $scope = '' ) {
|
887 |
|
@@ -890,13 +949,16 @@ final class Code_Snippets {
|
|
890 |
|
891 |
$xml = simplexml_load_file( $file );
|
892 |
|
|
|
|
|
|
|
893 |
foreach ( $xml->children() as $snippet ) {
|
894 |
/* force manual build of object to strip out unsupported fields
|
895 |
-
by converting snippet object into array */
|
896 |
$this->save_snippet( get_object_vars( $snippet ), $scope );
|
897 |
}
|
898 |
|
899 |
-
do_action( '
|
900 |
|
901 |
return $xml->count();
|
902 |
}
|
@@ -904,14 +966,14 @@ final class Code_Snippets {
|
|
904 |
/**
|
905 |
* Exports snippets as an XML file
|
906 |
*
|
907 |
-
* @since
|
908 |
* @access public
|
909 |
*
|
910 |
-
* @uses
|
911 |
-
* @uses
|
912 |
*
|
913 |
-
* @param array
|
914 |
-
* @param string
|
915 |
* @return void
|
916 |
*/
|
917 |
public function export( $ids, $scope = '' ) {
|
@@ -919,7 +981,7 @@ final class Code_Snippets {
|
|
919 |
$table = $this->get_table_name( $scope );
|
920 |
|
921 |
if ( ! function_exists( 'code_snippets_export' ) )
|
922 |
-
|
923 |
|
924 |
code_snippets_export( $ids, 'xml', $table );
|
925 |
}
|
@@ -927,14 +989,14 @@ final class Code_Snippets {
|
|
927 |
/**
|
928 |
* Exports snippets as a PHP file
|
929 |
*
|
930 |
-
* @since
|
931 |
* @access public
|
932 |
*
|
933 |
-
* @uses
|
934 |
-
* @uses
|
935 |
*
|
936 |
-
* @param array
|
937 |
-
* @param string
|
938 |
* @return void
|
939 |
*/
|
940 |
public function export_php( $ids, $scope = '' ) {
|
@@ -942,7 +1004,7 @@ final class Code_Snippets {
|
|
942 |
$table = $this->get_table_name( $scope );
|
943 |
|
944 |
if ( ! function_exists( 'code_snippets_export' ) )
|
945 |
-
|
946 |
|
947 |
code_snippets_export( $ids, 'php', $table );
|
948 |
}
|
@@ -953,31 +1015,36 @@ final class Code_Snippets {
|
|
953 |
* Code must NOT be escaped, as
|
954 |
* it will be executed directly
|
955 |
*
|
956 |
-
* @since
|
957 |
* @access public
|
958 |
*
|
959 |
-
* @param string
|
960 |
-
* @return
|
961 |
*/
|
962 |
public function execute_snippet( $code ) {
|
|
|
|
|
|
|
|
|
963 |
ob_start();
|
964 |
$result = eval( $code );
|
965 |
$output = ob_get_contents();
|
966 |
ob_end_clean();
|
967 |
-
|
|
|
968 |
return $result;
|
969 |
}
|
970 |
|
971 |
/**
|
972 |
* Run the active snippets
|
973 |
*
|
974 |
-
* @since
|
975 |
* @access public
|
976 |
*
|
977 |
-
* @uses
|
978 |
-
* @uses
|
979 |
*
|
980 |
-
* @return
|
981 |
*/
|
982 |
public function run_snippets() {
|
983 |
|
@@ -1006,7 +1073,7 @@ final class Code_Snippets {
|
|
1006 |
$active_snippets = $wpdb->get_col( $sql );
|
1007 |
|
1008 |
if ( count( $active_snippets ) ) {
|
1009 |
-
foreach( $active_snippets as $snippet ) {
|
1010 |
/* Execute the PHP code */
|
1011 |
$this->execute_snippet( htmlspecialchars_decode( stripslashes( $snippet ) ) );
|
1012 |
}
|
@@ -1021,15 +1088,15 @@ final class Code_Snippets {
|
|
1021 |
/**
|
1022 |
* Cleans up data created by the Code_Snippets class
|
1023 |
*
|
1024 |
-
* @since
|
1025 |
* @access private
|
1026 |
*
|
1027 |
-
* @uses
|
1028 |
-
* @uses
|
1029 |
-
* @uses
|
1030 |
-
* @uses
|
1031 |
-
* @uses
|
1032 |
-
* @uses
|
1033 |
*
|
1034 |
* @return void
|
1035 |
*/
|
@@ -1065,13 +1132,15 @@ final class Code_Snippets {
|
|
1065 |
/**
|
1066 |
* The global variable in which the Code_Snippets class is stored
|
1067 |
*
|
1068 |
-
* @
|
|
|
1069 |
* @access public
|
|
|
1070 |
*/
|
1071 |
global $code_snippets;
|
1072 |
$code_snippets = new Code_Snippets;
|
1073 |
|
1074 |
-
/*
|
1075 |
global $cs;
|
1076 |
$cs = &$code_snippets;
|
1077 |
|
6 |
* If you're interested in helping to develop Code Snippets, or perhaps
|
7 |
* contribute to the localization, please see http://code-snippets.bungeshea.com
|
8 |
*
|
9 |
+
* @package Code_Snippets
|
10 |
+
* @version 1.8
|
11 |
+
* @author Shea Bunge <http://bungeshea.com/>
|
12 |
+
* @copyright Copyright (c) 2012-2013, Shea Bunge
|
13 |
+
* @link http://code-snippets.bungeshea.com
|
14 |
+
* @license http://opensource.org/licenses/MIT
|
15 |
*/
|
16 |
|
17 |
/*
|
20 |
* Description: An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!
|
21 |
* Author: Shea Bunge
|
22 |
* Author URI: http://bungeshea.com
|
23 |
+
* Version: 1.8
|
24 |
* License: MIT
|
25 |
* License URI: license.txt
|
26 |
* Text Domain: code-snippets
|
27 |
* Domain Path: /languages/
|
28 |
*/
|
29 |
|
30 |
+
/* Exit if accessed directly */
|
31 |
+
if ( ! defined( 'ABSPATH' ) )
|
32 |
+
exit;
|
33 |
|
34 |
if ( ! class_exists( 'Code_Snippets' ) ) :
|
35 |
|
41 |
* the methods or variables in this class. Anything you need
|
42 |
* to access should be publicly available there
|
43 |
*
|
44 |
+
* @since 1.0
|
45 |
+
* @package Code_Snippets
|
46 |
+
* @access private
|
47 |
*/
|
48 |
final class Code_Snippets {
|
49 |
|
54 |
* This should be set to the 'Plugin Version' value,
|
55 |
* as defined above in the plugin header
|
56 |
*
|
57 |
+
* @since 1.0
|
58 |
* @access public
|
59 |
+
* @var string A PHP-standardized version number string
|
60 |
*/
|
61 |
+
public $version = '1.8';
|
62 |
|
63 |
/**
|
64 |
* Variables to hold plugin paths
|
65 |
*
|
66 |
+
* @since 1.0
|
67 |
* @access public
|
68 |
+
* @var string
|
69 |
*/
|
70 |
+
public $file, $basename, $plugin_dir, $plugin_url = '';
|
71 |
|
72 |
/**
|
73 |
* Stores an instance of the list table class
|
74 |
*
|
75 |
+
* @var object
|
76 |
+
* @since 1.5
|
77 |
* @access public
|
78 |
+
* @see Code_Snippets_List_Table
|
79 |
*/
|
80 |
public $list_table;
|
81 |
|
82 |
/**
|
83 |
* Stores an instance of the administration class
|
84 |
*
|
85 |
+
* @var object
|
86 |
+
* @since Code_Snippets 1.7.1
|
87 |
* @access public
|
88 |
+
* @see Code_Snippets_Admin
|
89 |
*/
|
90 |
public $admin;
|
91 |
|
92 |
/**
|
93 |
* Used by maybe_create_tables() for bailing early
|
94 |
+
*
|
95 |
+
* @var boolean
|
96 |
+
* @access protected
|
97 |
*/
|
98 |
static $tables_created = false;
|
99 |
|
104 |
* $wpdb->ms_snippets, but these are maintained
|
105 |
* as references for backwards-compatibility
|
106 |
*
|
107 |
+
* @var string
|
108 |
+
* @access public
|
109 |
*/
|
110 |
+
public $table, $ms_table = '';
|
111 |
|
112 |
/**
|
113 |
* These are now deprecated in favor of those in
|
114 |
* the Code_Snippets_Admin class, but maintained as
|
115 |
* references so we don't break existing code
|
116 |
*
|
117 |
+
* @since 1.0
|
118 |
* @deprecated Moved to the Code_Snippets_Admin class in 1.7.1
|
119 |
+
* @access public
|
120 |
+
* @var string
|
121 |
*/
|
122 |
+
public $admin_manage, $admin_single, $admin_import,
|
123 |
+
$admin_manage_url, $admin_single_url, $admin_import_url = '';
|
124 |
|
125 |
/**
|
126 |
* The constructor function for our class
|
129 |
* so other plugins may not have loaded yet. Only do stuff
|
130 |
* here that really can't wait
|
131 |
*
|
132 |
+
* @since 1.0
|
133 |
* @access private
|
|
|
134 |
* @return void
|
135 |
*/
|
136 |
function __construct() {
|
148 |
|
149 |
/* Hook our initialize function to the plugins_loaded action */
|
150 |
add_action( 'plugins_loaded', array( $this, 'init' ) );
|
151 |
+
|
152 |
}
|
153 |
|
154 |
/**
|
157 |
* This method is called *after* other plugins
|
158 |
* have been run
|
159 |
*
|
160 |
+
* @since 1.7
|
161 |
* @access public
|
|
|
162 |
* @return void
|
163 |
*/
|
164 |
public function init() {
|
178 |
*/
|
179 |
load_plugin_textdomain( 'code-snippets', false, dirname( $this->basename ) . '/languages/' );
|
180 |
|
181 |
+
/* Cleanup the plugin data on uninstall */
|
|
|
|
|
182 |
register_uninstall_hook( $this->file, array( __CLASS__, 'uninstall' ) );
|
183 |
|
184 |
+
/* Load the global functions file */
|
185 |
+
$this->get_include( 'functions' );
|
186 |
+
|
187 |
+
/* Let extension plugins know that it's okay to load */
|
188 |
do_action( 'code_snippets_init' );
|
189 |
}
|
190 |
|
191 |
/**
|
192 |
* Initialize variables
|
193 |
*
|
194 |
+
* @since 1.2
|
195 |
* @access private
|
|
|
196 |
* @return void
|
197 |
*/
|
198 |
function setup_vars() {
|
199 |
|
200 |
/* Plugin directory variables */
|
201 |
$this->file = __FILE__;
|
202 |
+
$this->basename = plugin_basename( $this->file );
|
203 |
$this->plugin_dir = plugin_dir_path( $this->file );
|
204 |
$this->plugin_url = plugin_dir_url ( $this->file );
|
205 |
|
206 |
if ( is_admin() ) {
|
207 |
|
208 |
/* Load our administration class */
|
209 |
+
$this->get_include( 'class-admin' );
|
210 |
$this->admin = new Code_Snippets_Admin;
|
211 |
|
212 |
/* Remap deprecated variables */
|
213 |
+
$this->admin_manage_url = &$this->admin->manage_url;
|
214 |
$this->admin_single_url = &$this->admin->single_url;
|
215 |
$this->admin_import_url = &$this->admin->import_url;
|
216 |
|
220 |
}
|
221 |
}
|
222 |
|
223 |
+
/**
|
224 |
+
* Require a PHP file from the includes directory
|
225 |
+
* @since 1.8
|
226 |
+
* @param string $slug The file slug (filename with no path or extension) to load
|
227 |
+
* @return void
|
228 |
+
*/
|
229 |
+
public function get_include( $slug ) {
|
230 |
+
require_once $this->plugin_dir . "includes/{$slug}.php";
|
231 |
+
}
|
232 |
+
|
233 |
/**
|
234 |
* Register the snippet table names with WordPress
|
235 |
*
|
236 |
+
* @since 1.7
|
237 |
* @access public
|
238 |
+
* @uses $wpdb
|
|
|
|
|
239 |
* @return void
|
240 |
*/
|
241 |
public function set_table_vars() {
|
242 |
global $wpdb;
|
243 |
|
244 |
/* Register the snippet table names with WordPress */
|
245 |
+
$wpdb->tables[] = 'snippets';
|
246 |
$wpdb->ms_global_tables[] = 'ms_snippets';
|
247 |
|
248 |
/* Setup initial table variables */
|
249 |
+
$wpdb->snippets = $wpdb->prefix . 'snippets';
|
250 |
+
$wpdb->ms_snippets = $wpdb->base_prefix . 'ms_snippets';
|
251 |
|
252 |
/* Add a pointer to the old variables */
|
253 |
+
$this->table = &$wpdb->snippets;
|
254 |
+
$this->ms_table = &$wpdb->ms_snippets;
|
255 |
}
|
256 |
|
257 |
/**
|
258 |
* Return the appropriate snippet table name
|
259 |
*
|
260 |
+
* @since 1.6
|
261 |
* @access public
|
262 |
+
* @param string $scope Retrieve the multisite table name or the site table name?
|
263 |
+
* @param boolean $check_screen Query the current screen if no scope passed?
|
264 |
+
* @return string The snippet table name
|
|
|
265 |
*/
|
266 |
public function get_table_name( $scope = '', $check_screen = true ) {
|
|
|
267 |
global $wpdb;
|
268 |
|
269 |
+
/* If multisite is not active, always return the site-wide table name */
|
|
|
270 |
if ( ! is_multisite() ) {
|
271 |
$network = false;
|
272 |
}
|
273 |
+
|
274 |
+
/* If the scope is 'multisite' or 'network', return the network-wide table name */
|
275 |
elseif ( in_array( $scope, array( 'multisite', 'network' ) ) ) {
|
276 |
$network = true;
|
277 |
}
|
278 |
+
|
279 |
+
/* If no scope is set, query the current screen to see if in network admin */
|
280 |
elseif ( empty( $scope ) && $check_screen && function_exists( 'get_current_screen' ) ) {
|
|
|
281 |
$network = get_current_screen()->is_network;
|
282 |
}
|
283 |
+
|
284 |
+
/* If none of the above conditions match, just use the site-wide table name */
|
285 |
else {
|
286 |
$network = false;
|
287 |
}
|
288 |
|
289 |
+
/* Retrieve the table name from $wpdb depending on the above conditionals */
|
290 |
+
return ( $network ? $wpdb->ms_snippets : $wpdb->snippets );
|
|
|
291 |
}
|
292 |
|
293 |
/**
|
294 |
* Create the snippet tables if they do not already exist
|
295 |
*
|
296 |
+
* @since 1.7.1
|
297 |
* @access public
|
298 |
*
|
299 |
+
* @uses $this->create_table() To create a single snippet table
|
300 |
+
* @staticvar boolean $tables_created Used to check if we've already done this or not
|
301 |
+
* @param boolean $redo Skip the already-done-this check
|
302 |
+
* @param boolean $always_create_table Always create the site-wide table if it doesn't exist
|
303 |
+
* @return void
|
|
|
|
|
304 |
*/
|
305 |
+
public function maybe_create_tables( $redo = false, $always_create_table = false ) {
|
306 |
|
307 |
/* Bail early if we've done this already */
|
308 |
+
if ( ! $redo && true === self::$tables_created )
|
309 |
+
return;
|
310 |
|
311 |
global $wpdb;
|
312 |
|
313 |
+
/* Set the table name variables if not yet defined */
|
314 |
+
if ( ! isset( $wpdb->snippets, $wpdb->ms_snippets ) ) {
|
315 |
$this->set_table_vars();
|
|
|
|
|
|
|
|
|
|
|
316 |
}
|
317 |
+
|
318 |
+
/* Always create the network-wide snippet table */
|
319 |
+
if ( is_multisite() ) {
|
320 |
+
$this->create_table( $wpdb->ms_snippets );
|
|
|
|
|
|
|
|
|
|
|
321 |
}
|
322 |
|
323 |
+
/* Create the site-specific table if we're on the main site */
|
324 |
+
if ( $always_create_table || is_main_site() ) {
|
325 |
+
$this->create_table( $wpdb->snippets );
|
326 |
+
}
|
327 |
|
328 |
+
/* Set the flag so we don't have to do this again */
|
329 |
self::$tables_created = true;
|
330 |
}
|
331 |
|
332 |
/**
|
333 |
* Create the snippet tables if they do not already exist
|
334 |
*
|
335 |
+
* @since Code Snippets 1.2
|
336 |
* @deprecated Code Snippets 1.7.1
|
337 |
+
* @access public
|
338 |
+
* @return void
|
|
|
339 |
*/
|
340 |
public function create_tables() {
|
341 |
_deprecated_function(
|
350 |
* Create a single snippet table
|
351 |
* if one of the same name does not already exist
|
352 |
*
|
353 |
+
* @since 1.6
|
354 |
* @access private
|
355 |
*
|
356 |
+
* @uses dbDelta() To add the table to the database
|
357 |
*
|
358 |
+
* @param string $table_name The name of the table to create
|
359 |
+
* @param boolean $force_creation Skip the table exists check
|
360 |
* @return void
|
361 |
*/
|
362 |
+
function create_table( $table_name, $force_creation = false ) {
|
363 |
+
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
364 |
|
365 |
global $wpdb;
|
366 |
|
367 |
+
if ( ! $force_creation && $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->snippets'" ) === $wpdb->snippets ) {
|
368 |
+
return; // bail if the table already exists
|
369 |
+
}
|
370 |
+
|
371 |
+
/* Set the database charset */
|
372 |
+
|
373 |
$charset_collate = '';
|
374 |
|
375 |
if ( ! empty( $wpdb->charset ) ) {
|
380 |
$charset_collate .= " COLLATE $wpdb->collate";
|
381 |
}
|
382 |
|
383 |
+
/* Set the snippet data columns */
|
384 |
+
|
385 |
+
$table_columns = apply_filters( 'code_snippets/database_table_columns', array(
|
386 |
+
'name tinytext not null',
|
387 |
+
'description text',
|
388 |
+
'code longtext not null',
|
389 |
) );
|
390 |
|
391 |
+
$table_columns_sql = implode( ",\n", $table_columns ); // convert the array into SQL code
|
392 |
+
|
393 |
+
/* Create the database table */
|
394 |
|
395 |
$sql = "CREATE TABLE $table_name (
|
396 |
+
id bigint(20) unsigned not null auto_increment,
|
397 |
{$table_columns_sql},
|
398 |
+
active tinyint(1) not null default 0,
|
399 |
PRIMARY KEY (id),
|
400 |
KEY id (id)
|
401 |
|
402 |
) {$charset_collate};";
|
403 |
|
404 |
+
dbDelta( apply_filters( 'code_snippets/table_sql', $sql ) );
|
405 |
|
406 |
+
do_action( 'code_snippets/create_table', $table_name );
|
407 |
}
|
408 |
|
409 |
/**
|
410 |
* Preform upgrade tasks such as deleting and updating options
|
411 |
*
|
412 |
+
* @since 1.2
|
413 |
* @access private
|
|
|
414 |
* @return void
|
415 |
*/
|
416 |
function upgrade() {
|
417 |
global $wpdb;
|
418 |
|
419 |
+
/* Get the current plugin version from the database */
|
420 |
|
421 |
$current_version = get_option( 'code_snippets_version' );
|
422 |
|
428 |
|
429 |
$previous_version = ( $current_version ? $current_version : $this->version );
|
430 |
|
431 |
+
/* Skip this if we're on the latest version */
|
432 |
if ( version_compare( $current_version, $this->version, '<' ) ) {
|
433 |
|
434 |
+
/* Data in database is unsecapped in 1.8 */
|
435 |
+
if ( version_compare( $current_version, '1.8', '<' ) ) {
|
436 |
+
|
437 |
+
$tables = array();
|
438 |
+
|
439 |
+
if ( $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->snippets'" ) === $wpdb->snippets ) {
|
440 |
+
$tables[] = $wpdb->snippets;
|
441 |
+
}
|
442 |
+
|
443 |
+
if ( is_multisite() && is_main_site() && $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->ms_snippets'" ) === $wpdb->ms_snippets ) {
|
444 |
+
$tables[] = $wpdb->ms_snippets;
|
445 |
+
}
|
446 |
+
|
447 |
+
foreach ( $tables as $table ) {
|
448 |
+
$snippets = $wpdb->get_results( "SELECT * FROM $table" );
|
449 |
+
|
450 |
+
foreach ( $snippets as $snippet ) {
|
451 |
+
|
452 |
+
$snippet->name = esc_sql( htmlspecialchars_decode( stripslashes( $snippet->name ) ) );
|
453 |
+
$snippet->code = esc_sql( htmlspecialchars_decode( stripslashes( $snippet->code ) ) );
|
454 |
+
$snippet->description = esc_sql( htmlspecialchars_decode( stripslashes( $snippet->description ) ) );
|
455 |
+
|
456 |
+
$wpdb->update( $table,
|
457 |
+
array(
|
458 |
+
'name' => $snippet->name,
|
459 |
+
'code' => $snippet->code,
|
460 |
+
'description' => $snippet->description
|
461 |
+
),
|
462 |
+
array( 'id' => $snippet->id ),
|
463 |
+
array( '%s' ),
|
464 |
+
array( '%d' )
|
465 |
+
);
|
466 |
+
}
|
467 |
+
} // end $table foreach
|
468 |
+
|
469 |
+
} // end < 1.8 version check
|
470 |
+
|
471 |
+
/* Register the capabilities once only */
|
472 |
if ( version_compare( $current_version, '1.5', '<' ) ) {
|
473 |
$this->setup_roles( true );
|
474 |
}
|
475 |
|
476 |
if ( version_compare( $previous_version, '1.2', '<' ) ) {
|
477 |
+
/* The 'Complete Uninstall' option was removed in version 1.2 */
|
478 |
delete_option( 'cs_complete_uninstall' );
|
479 |
}
|
480 |
|
481 |
+
/* Update the current version stored in the database */
|
482 |
update_option( 'code_snippets_version', $this->version );
|
483 |
}
|
484 |
|
494 |
$this->setup_ms_roles( true );
|
495 |
}
|
496 |
|
497 |
+
/* Migrate recently_network_activated_snippets to the site options */
|
498 |
if ( get_option( 'recently_network_activated_snippets' ) ) {
|
499 |
add_site_option( 'recently_activated_snippets', get_option( 'recently_network_activated_snippets', array() ) );
|
500 |
delete_option( 'recently_network_activated_snippets' );
|
510 |
/**
|
511 |
* Register the user roles and capabilities
|
512 |
*
|
513 |
+
* @since 1.5
|
514 |
* @access private
|
515 |
+
* @param boolean $install true to add the capabilities, false to remove
|
|
|
516 |
* @return void
|
517 |
*/
|
518 |
function setup_roles( $install = true ) {
|
519 |
|
520 |
$this->caps = apply_filters( 'code_snippets_caps', array(
|
521 |
+
'manage_snippets',
|
522 |
+
'install_snippets',
|
523 |
+
'edit_snippets'
|
524 |
+
) );
|
525 |
|
526 |
$this->role = get_role( apply_filters( 'code_snippets_role', 'administrator' ) );
|
527 |
|
528 |
+
foreach ( $this->caps as $cap ) {
|
529 |
if ( $install )
|
530 |
$this->role->add_cap( $cap );
|
531 |
else
|
536 |
/**
|
537 |
* Register the multisite user roles and capabilities
|
538 |
*
|
539 |
+
* @since 1.5
|
540 |
* @access private
|
541 |
+
* @param boolean $install true to add the capabilities, false to remove
|
|
|
542 |
* @return void
|
543 |
*/
|
544 |
function setup_ms_roles( $install = true ) {
|
545 |
|
546 |
+
if ( ! is_multisite() )
|
547 |
+
return;
|
548 |
|
549 |
$this->network_caps = apply_filters( 'code_snippets_network_caps', array(
|
550 |
+
'manage_network_snippets',
|
551 |
+
'install_network_snippets',
|
552 |
+
'edit_network_snippets'
|
553 |
+
) );
|
554 |
|
555 |
$supers = get_super_admins();
|
556 |
+
foreach ( $supers as $admin ) {
|
557 |
$user = new WP_User( 0, $admin );
|
558 |
+
foreach ( $this->network_caps as $cap ) {
|
559 |
if ( $install )
|
560 |
$user->add_cap( $cap );
|
561 |
else
|
568 |
* Check if the current user can perform some action on snippets or not
|
569 |
*
|
570 |
* @uses current_user_can() To check if the current user can perform a task
|
571 |
+
* @uses $this->get_cap() To get the required capability
|
572 |
*
|
573 |
+
* @param string $do_what The task to check against.
|
574 |
+
* @return boolean Whether the current user can perform this task or not
|
575 |
*
|
576 |
+
* @since 1.7.1.1 Moved logic to $this->get_cap() method
|
577 |
* @since 1.7.1
|
578 |
* @access public
|
579 |
*/
|
590 |
*
|
591 |
* @since 1.7.1.1
|
592 |
* @access public
|
593 |
+
* @param string $do_what The action to retrieve the capability for
|
594 |
+
* @return void
|
595 |
*/
|
596 |
public function get_cap( $do_what ) {
|
597 |
|
598 |
if ( is_multisite() ) {
|
599 |
|
600 |
+
if ( in_array( 'snippets', get_site_option( 'menu_items', array() ) ) )
|
601 |
return "{$do_what}_snippets";
|
602 |
else
|
603 |
return "{$do_what}_network_snippets";
|
610 |
/**
|
611 |
* Converts an array of snippet data into a snippet object
|
612 |
*
|
613 |
+
* @since 1.7
|
614 |
* @access public
|
615 |
*
|
616 |
+
* @param mixed $data The snippet data to convert
|
617 |
+
* @return object The resulting snippet object
|
618 |
*/
|
619 |
public function build_snippet_object( $data = null ) {
|
620 |
|
621 |
$snippet = new stdClass;
|
622 |
|
623 |
+
/* Define an empty snippet object (or one with default values ) */
|
624 |
+
$snippet->id = 0;
|
625 |
+
$snippet->name = '';
|
626 |
$snippet->description = '';
|
627 |
+
$snippet->code = '';
|
628 |
+
$snippet->active = 0;
|
629 |
+
$snippet = apply_filters( 'code_snippets/build_default_snippet', $snippet );
|
630 |
|
631 |
if ( ! isset( $data ) ) {
|
632 |
return $snippet;
|
663 |
elseif ( isset( $data['snippet_active'] ) )
|
664 |
$snippet->active = $data['snippet_active'];
|
665 |
|
666 |
+
return apply_filters( 'code_snippets/build_snippet_object', $snippet, $data );
|
667 |
}
|
668 |
|
669 |
return $snippet;
|
672 |
/**
|
673 |
* Retrieve a list of snippets from the database
|
674 |
*
|
675 |
+
* @since 1.7
|
676 |
+
* @access public
|
677 |
*
|
678 |
+
* @uses $wpdb To query the database for snippets
|
679 |
+
* @uses $this->get_table_name() To dynamically retrieve the snippet table name
|
680 |
*
|
681 |
+
* @param string $scope Retrieve multisite-wide or site-wide snippets?
|
682 |
+
* @return array An array of snippet objects
|
683 |
*/
|
684 |
+
public function get_snippets( $scope = '' ) {
|
685 |
global $wpdb;
|
686 |
|
687 |
+
$table = $this->get_table_name( $scope );
|
688 |
$snippets = $wpdb->get_results( "SELECT * FROM $table", ARRAY_A );
|
689 |
|
690 |
+
foreach ( $snippets as $index => $snippet ) {
|
691 |
$snippets[ $index ] = $this->unescape_snippet_data( $snippet );
|
692 |
}
|
693 |
|
694 |
+
return apply_filters( 'code_snippets/get_snippets', $snippets, $scope );
|
695 |
}
|
696 |
|
697 |
/**
|
698 |
* Escape snippet data for inserting into the database
|
699 |
*
|
700 |
+
* @since 1.7
|
701 |
* @access public
|
702 |
*
|
703 |
+
* @param mixed $snippet An object or array containing the data to escape
|
704 |
+
* @return object The resulting snippet object, with data escaped
|
705 |
*/
|
706 |
public function escape_snippet_data( $snippet ) {
|
707 |
|
708 |
$snippet = $this->build_snippet_object( $snippet );
|
709 |
|
710 |
/* remove the <?php and ?> tags from the snippet */
|
711 |
+
$snippet->code = trim( $snippet->code );
|
712 |
$snippet->code = ltrim( $snippet->code, '<?php' );
|
713 |
$snippet->code = ltrim( $snippet->code, '<?' );
|
714 |
$snippet->code = rtrim( $snippet->code, '?>' );
|
715 |
+
|
716 |
/* escape the data */
|
717 |
+
$snippet->name = esc_sql( $snippet->name );
|
718 |
+
$snippet->description = esc_sql( $snippet->description );
|
719 |
+
$snippet->code = esc_sql( $snippet->code );
|
720 |
+
$snippet->id = absint ( $snippet->id );
|
721 |
|
722 |
+
return apply_filters( 'code_snippets/escape_snippet_data', $snippet );
|
723 |
}
|
724 |
|
725 |
/**
|
726 |
* Unescape snippet data after retrieval from the database
|
727 |
* ready for use
|
728 |
*
|
729 |
+
* @since 1.7
|
730 |
* @access public
|
731 |
*
|
732 |
+
* @param mixed $snippet An object or array containing the data to unescape
|
733 |
+
* @return object The resulting snippet object, with data unescaped
|
734 |
*/
|
735 |
public function unescape_snippet_data( $snippet ) {
|
736 |
|
737 |
$snippet = $this->build_snippet_object( $snippet );
|
738 |
|
739 |
+
$snippet->name = stripslashes( $snippet->name );
|
740 |
+
$snippet->code = stripslashes( $snippet->code );
|
741 |
+
$snippet->description = stripslashes( $snippet->description );
|
742 |
|
743 |
+
return apply_filters( 'code_snippets/unescape_snippet_data', $snippet );
|
744 |
}
|
745 |
|
746 |
/**
|
748 |
* Will return empty snippet object if no snippet
|
749 |
* ID is specified
|
750 |
*
|
751 |
+
* @since 1.7
|
752 |
* @access public
|
753 |
*
|
754 |
+
* @uses $wpdb To query the database for snippets
|
755 |
+
* @uses $this->get_table_name() To dynamically retrieve the snippet table name
|
756 |
*
|
757 |
+
* @param int $id The ID of the snippet to retrieve. 0 to build a new snippet
|
758 |
+
* @param string $scope Retrieve a multisite-wide or site-wide snippet?
|
759 |
+
* @return object A single snippet object
|
760 |
*/
|
761 |
+
public function get_snippet( $id = 0, $scope = '' ) {
|
762 |
global $wpdb;
|
763 |
$table = $this->get_table_name( $scope );
|
764 |
+
$id = absint( $id );
|
765 |
|
766 |
+
if ( 0 !== $id ) {
|
767 |
/* Retrieve the snippet from the database */
|
768 |
$snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $id ) );
|
769 |
/* Unescape the snippet data, ready for use */
|
772 |
/* Get an empty snippet object */
|
773 |
$snippet = $this->build_snippet_object();
|
774 |
}
|
775 |
+
return apply_filters( 'code_snippets/get_snippet', $snippet, $id, $scope );
|
776 |
}
|
777 |
|
778 |
/**
|
779 |
* Activates a snippet
|
780 |
*
|
781 |
+
* @since 1.5
|
782 |
* @access public
|
783 |
*
|
784 |
+
* @uses $wpdb To set the snippets' active status
|
785 |
*
|
786 |
+
* @param array $ids The ids of the snippets to activate
|
787 |
+
* @param string $scope Are the snippets multisite-wide or site-wide?
|
788 |
* @return void
|
789 |
*/
|
790 |
public function activate( $ids, $scope = '' ) {
|
794 |
|
795 |
$table = $this->get_table_name( $scope );
|
796 |
|
797 |
+
foreach ( $ids as $id ) {
|
798 |
$wpdb->update(
|
799 |
$table,
|
800 |
array( 'active' => '1' ),
|
803 |
array( '%d' )
|
804 |
);
|
805 |
|
806 |
+
do_action( 'code_snippets/activate_snippet', $id, $scope );
|
807 |
}
|
808 |
|
809 |
+
do_action( 'code_snippets/activate', $ids, $scope );
|
810 |
}
|
811 |
|
812 |
/**
|
813 |
* Deactivates selected snippets
|
814 |
*
|
815 |
+
* @since 1.5
|
816 |
* @access public
|
817 |
*
|
818 |
+
* @uses $wpdb To set the snippets' active status
|
819 |
*
|
820 |
+
* @param array $ids The IDs of the snippets to deactivate
|
821 |
+
* @param string $scope Are the snippets multisite-wide or site-wide?
|
822 |
* @return void
|
823 |
*/
|
824 |
public function deactivate( $ids, $scope = '' ) {
|
829 |
|
830 |
$table = $this->get_table_name( $scope );
|
831 |
|
832 |
+
foreach ( $ids as $id ) {
|
833 |
$wpdb->update(
|
834 |
$table,
|
835 |
array( 'active' => '0' ),
|
839 |
);
|
840 |
$recently_active = array( $id => time() ) + (array) $recently_active;
|
841 |
|
842 |
+
do_action( 'code_snippets/deactivate_snippet', $id, $scope );
|
843 |
}
|
844 |
|
845 |
if ( $table === $wpdb->ms_snippets )
|
853 |
$recently_active + (array) get_option( 'recently_activated_snippets' )
|
854 |
);
|
855 |
|
856 |
+
do_action( 'code_snippets/deactivate', $ids, $scope );
|
857 |
}
|
858 |
|
859 |
/**
|
860 |
* Deletes a snippet from the database
|
861 |
*
|
862 |
+
* @since 1.5
|
863 |
* @access public
|
864 |
*
|
865 |
+
* @uses $wpdb To access the database
|
866 |
+
* @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
|
867 |
*
|
868 |
+
* @param int $id The ID of the snippet to delete
|
869 |
+
* @param string $scope Delete from site-wide or network-wide table?
|
870 |
*/
|
871 |
public function delete_snippet( $id, $scope = '' ) {
|
872 |
global $wpdb;
|
873 |
|
874 |
$table = $this->get_table_name( $scope );
|
875 |
+
$id = absint( $id );
|
876 |
|
877 |
+
$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE id='%d' LIMIT 1", $id ) );
|
878 |
|
879 |
+
do_action( 'code_snippets/delete_snippet', $id, $scope );
|
880 |
}
|
881 |
|
882 |
/**
|
883 |
* Saves a snippet to the database.
|
884 |
*
|
885 |
+
* @since 1.5
|
886 |
* @access public
|
887 |
*
|
888 |
+
* @uses $wpdb To update/add the snippet to the database
|
889 |
+
* @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
|
890 |
*
|
891 |
+
* @param object $snippet The snippet to add/update to the database
|
892 |
+
* @param string $scope Save the snippet to the site-wide or network-wide table?
|
893 |
+
* @return int|boolean The ID of the snippet on success, false on failure
|
894 |
*/
|
895 |
public function save_snippet( $snippet, $scope = '' ) {
|
896 |
global $wpdb;
|
897 |
|
898 |
$snippet = $this->escape_snippet_data( $snippet );
|
899 |
+
$table = $this->get_table_name( $scope );
|
|
|
|
|
|
|
|
|
900 |
|
901 |
$fields = '';
|
902 |
foreach ( get_object_vars( $snippet ) as $field => $value ) {
|
903 |
+
if ( 'id' === $field )
|
904 |
+
continue;
|
905 |
+
|
906 |
+
if ( is_array( $value ) )
|
907 |
+
$value = maybe_serialize( $value );
|
908 |
+
|
909 |
+
$fields .= "{$field}='{$value}',";
|
910 |
}
|
911 |
$fields = rtrim( $fields, ',' );
|
912 |
|
916 |
$wpdb->query( $wpdb->prepare( "UPDATE $table SET $fields WHERE id='%d' LIMIT 1", $snippet->id ) );
|
917 |
}
|
918 |
|
919 |
+
do_action( 'code_snippets/update_snippet', $snippet, $table );
|
920 |
return $snippet->id;
|
921 |
|
922 |
} else {
|
925 |
$wpdb->query( "INSERT INTO $table SET $fields" );
|
926 |
}
|
927 |
|
928 |
+
do_action( 'code_snippets/create_snippet', $snippet, $table );
|
929 |
return $wpdb->insert_id;
|
930 |
}
|
931 |
}
|
933 |
/**
|
934 |
* Imports snippets from an XML file
|
935 |
*
|
936 |
+
* @since 1.5
|
937 |
* @access public
|
938 |
*
|
939 |
+
* @uses $this->save_snippet() To add the snippets to the database
|
940 |
*
|
941 |
+
* @param string $file The path to the XML file to import
|
942 |
+
* @param string $scope Import into network-wide table or site-wide table?
|
943 |
+
* @return integer|boolean The number of snippets imported on success, false on failure
|
944 |
*/
|
945 |
public function import( $file, $scope = '' ) {
|
946 |
|
949 |
|
950 |
$xml = simplexml_load_file( $file );
|
951 |
|
952 |
+
if ( ! is_object( $xml ) || ! method_exists( $xml, 'children' ) )
|
953 |
+
return false;
|
954 |
+
|
955 |
foreach ( $xml->children() as $snippet ) {
|
956 |
/* force manual build of object to strip out unsupported fields
|
957 |
+
by converting snippet object into an array */
|
958 |
$this->save_snippet( get_object_vars( $snippet ), $scope );
|
959 |
}
|
960 |
|
961 |
+
do_action( 'code_snippets/import', $xml, $scope );
|
962 |
|
963 |
return $xml->count();
|
964 |
}
|
966 |
/**
|
967 |
* Exports snippets as an XML file
|
968 |
*
|
969 |
+
* @since 1.5
|
970 |
* @access public
|
971 |
*
|
972 |
+
* @uses code_snippets_export() To export selected snippets
|
973 |
+
* @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
|
974 |
*
|
975 |
+
* @param array $ids The IDs of the snippets to export
|
976 |
+
* @param string $scope Is the snippet a network-wide or site-wide snippet?
|
977 |
* @return void
|
978 |
*/
|
979 |
public function export( $ids, $scope = '' ) {
|
981 |
$table = $this->get_table_name( $scope );
|
982 |
|
983 |
if ( ! function_exists( 'code_snippets_export' ) )
|
984 |
+
$this->get_include( 'export' );
|
985 |
|
986 |
code_snippets_export( $ids, 'xml', $table );
|
987 |
}
|
989 |
/**
|
990 |
* Exports snippets as a PHP file
|
991 |
*
|
992 |
+
* @since 1.5
|
993 |
* @access public
|
994 |
*
|
995 |
+
* @uses code_snippets_export() To export selected snippets
|
996 |
+
* @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
|
997 |
*
|
998 |
+
* @param array $ids The IDs of the snippets to export
|
999 |
+
* @param string $scope Is the snippet a network-wide or site-wide snippet?
|
1000 |
* @return void
|
1001 |
*/
|
1002 |
public function export_php( $ids, $scope = '' ) {
|
1004 |
$table = $this->get_table_name( $scope );
|
1005 |
|
1006 |
if ( ! function_exists( 'code_snippets_export' ) )
|
1007 |
+
$this->get_include( 'export' );
|
1008 |
|
1009 |
code_snippets_export( $ids, 'php', $table );
|
1010 |
}
|
1015 |
* Code must NOT be escaped, as
|
1016 |
* it will be executed directly
|
1017 |
*
|
1018 |
+
* @since 1.5
|
1019 |
* @access public
|
1020 |
*
|
1021 |
+
* @param string $code The snippet code to execute
|
1022 |
+
* @return mixed The result of the code execution
|
1023 |
*/
|
1024 |
public function execute_snippet( $code ) {
|
1025 |
+
|
1026 |
+
if ( empty( $code ) )
|
1027 |
+
return;
|
1028 |
+
|
1029 |
ob_start();
|
1030 |
$result = eval( $code );
|
1031 |
$output = ob_get_contents();
|
1032 |
ob_end_clean();
|
1033 |
+
|
1034 |
+
do_action( 'code_snippets/execute_snippet', $code );
|
1035 |
return $result;
|
1036 |
}
|
1037 |
|
1038 |
/**
|
1039 |
* Run the active snippets
|
1040 |
*
|
1041 |
+
* @since 1.0
|
1042 |
* @access public
|
1043 |
*
|
1044 |
+
* @uses $wpdb To grab the active snippets from the database
|
1045 |
+
* @uses $this->execute_snippet() To execute a snippet
|
1046 |
*
|
1047 |
+
* @return boolean true on success, false on failure
|
1048 |
*/
|
1049 |
public function run_snippets() {
|
1050 |
|
1073 |
$active_snippets = $wpdb->get_col( $sql );
|
1074 |
|
1075 |
if ( count( $active_snippets ) ) {
|
1076 |
+
foreach ( $active_snippets as $snippet ) {
|
1077 |
/* Execute the PHP code */
|
1078 |
$this->execute_snippet( htmlspecialchars_decode( stripslashes( $snippet ) ) );
|
1079 |
}
|
1088 |
/**
|
1089 |
* Cleans up data created by the Code_Snippets class
|
1090 |
*
|
1091 |
+
* @since 1.2
|
1092 |
* @access private
|
1093 |
*
|
1094 |
+
* @uses $wpdb To remove tables from the database
|
1095 |
+
* @uses $code_snippets->get_table_name() To find out which table to drop
|
1096 |
+
* @uses is_multisite() To check the type of installation
|
1097 |
+
* @uses switch_to_blog() To switch between blogs
|
1098 |
+
* @uses restore_current_blog() To switch between blogs
|
1099 |
+
* @uses delete_option() To remove site options
|
1100 |
*
|
1101 |
* @return void
|
1102 |
*/
|
1132 |
/**
|
1133 |
* The global variable in which the Code_Snippets class is stored
|
1134 |
*
|
1135 |
+
* @var object
|
1136 |
+
* @since 1.0
|
1137 |
* @access public
|
1138 |
+
* @see Code_Snippets
|
1139 |
*/
|
1140 |
global $code_snippets;
|
1141 |
$code_snippets = new Code_Snippets;
|
1142 |
|
1143 |
+
/* Set up a pointer in the old variable (for backwards-compatibility) */
|
1144 |
global $cs;
|
1145 |
$cs = &$code_snippets;
|
1146 |
|
includes/admin/import.php
DELETED
@@ -1,61 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* HTML code for the Import Snippets page
|
5 |
-
*
|
6 |
-
* @package Code Snippets
|
7 |
-
* @subpackage Administration
|
8 |
-
*/
|
9 |
-
|
10 |
-
if ( ! class_exists( 'Code_Snippets' ) ) exit;
|
11 |
-
|
12 |
-
global $code_snippets;
|
13 |
-
|
14 |
-
/* Display the admin notice */
|
15 |
-
|
16 |
-
if ( isset( $_REQUEST['imported'] ) && 0 !== intval( $_REQUEST['imported'] ) ) {
|
17 |
-
|
18 |
-
echo '<div id="message" class="updated fade"><p>';
|
19 |
-
|
20 |
-
printf(
|
21 |
-
_n(
|
22 |
-
'Imported <strong>%d</strong> snippet.',
|
23 |
-
'Imported <strong>%d</strong> snippets.',
|
24 |
-
$_REQUEST['imported'],
|
25 |
-
'code-snippets'
|
26 |
-
),
|
27 |
-
$_REQUEST['imported']
|
28 |
-
);
|
29 |
-
|
30 |
-
echo '</p></div>';
|
31 |
-
}
|
32 |
-
|
33 |
-
?>
|
34 |
-
<div class="wrap">
|
35 |
-
<?php screen_icon(); ?>
|
36 |
-
<h2><?php _e('Import Snippets', 'code-snippets'); ?></h2>
|
37 |
-
|
38 |
-
<div class="narrow">
|
39 |
-
|
40 |
-
<p><?php _e('Howdy! Upload your Code Snippets export file and we’ll import the snippets to this site.', 'code-snippets' ); ?></p>
|
41 |
-
|
42 |
-
<p><?php printf( __('You will need to go to the <a href="%s">Manage Snippets</a> page to activate the imported snippets.', 'code-snippets'), $code_snippets->admin->manage_url ); ?></p>
|
43 |
-
|
44 |
-
<p><?php _e('Choose a Code Snippets (.xml) file to upload, then click Upload file and import.', 'code-snippets'); ?></p>
|
45 |
-
|
46 |
-
<form enctype="multipart/form-data" id="import-upload-form" method="post" action="" name="code_snippets_import">
|
47 |
-
<p>
|
48 |
-
<label for="upload"><?php _e('Choose a file from your computer:', 'code-snippets' ); ?></label> <?php _e('(Maximum size: 8MB)', 'code-snippets'); ?>
|
49 |
-
<input type="file" id="upload" name="code_snippets_import_file" size="25" accept="text/xml" />
|
50 |
-
<input type="hidden" name="action" value="save" />
|
51 |
-
<input type="hidden" name="max_file_size" value="8388608" />
|
52 |
-
</p>
|
53 |
-
|
54 |
-
<?php do_action( 'code_snippets_admin_import_form' ); ?>
|
55 |
-
|
56 |
-
<p class="submit">
|
57 |
-
<input type="submit" name="submit" id="submit" class="button" value="<?php _e('Upload file and import', 'code-snippets'); ?>" />
|
58 |
-
</p>
|
59 |
-
</form>
|
60 |
-
</div>
|
61 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/admin/manage.php
DELETED
@@ -1,56 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* HTML code for the Manage Snippets page
|
5 |
-
*
|
6 |
-
* @package Code Snippets
|
7 |
-
* @subpackage Administration
|
8 |
-
*/
|
9 |
-
|
10 |
-
if ( ! class_exists( 'Code_Snippets' ) ) exit;
|
11 |
-
|
12 |
-
global $code_snippets;
|
13 |
-
$screen = get_current_screen();
|
14 |
-
?>
|
15 |
-
<?php if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) : ?>
|
16 |
-
<div class="error"><p><strong>Warning:</strong> Safe mode is active and snippets will not execute! Remove the <code>CODE_SNIPPETS_SAFE_MODE</code> constant from <code>wp-config.php</code> to turn off safe mode. <a href="http://code-snippets.bungeshea.com/docs/safe-mode/" target="_blank">Help</a></p></div>
|
17 |
-
<?php endif; ?>
|
18 |
-
|
19 |
-
<?php if ( isset($_GET['activate']) ) : ?>
|
20 |
-
<div id="message" class="updated"><p><?php _e('Snippet <strong>activated</strong>.', 'code-snippets') ?></p></div>
|
21 |
-
<?php elseif (isset($_GET['activate-multi'])) : ?>
|
22 |
-
<div id="message" class="updated"><p><?php _e('Selected snippets <strong>activated</strong>.', 'code-snippets'); ?></p></div>
|
23 |
-
<?php elseif ( isset($_GET['deactivate']) ) : ?>
|
24 |
-
<div id="message" class="updated"><p><?php _e('Snippet <strong>deactivated</strong>.', 'code-snippets') ?></p></div>
|
25 |
-
<?php elseif (isset($_GET['deactivate-multi'])) : ?>
|
26 |
-
<div id="message" class="updated"><p><?php _e('Selected snippets <strong>deactivated</strong>.', 'code-snippets'); ?></p></div>
|
27 |
-
<?php elseif ( isset($_GET['delete']) ) : ?>
|
28 |
-
<div id="message" class="updated"><p><?php _e('Snippet <strong>deleted</strong>.', 'code-snippets') ?></p></div>
|
29 |
-
<?php elseif (isset($_GET['delete-multi'])) : ?>
|
30 |
-
<div id="message" class="updated"><p><?php _e('Selected snippets <strong>deleted</strong>.', 'code-snippets'); ?></p></div>
|
31 |
-
<?php endif; ?>
|
32 |
-
|
33 |
-
<div class="wrap">
|
34 |
-
<?php screen_icon(); ?>
|
35 |
-
<h2><?php esc_html_e('Snippets', 'code-snippets'); ?>
|
36 |
-
<?php if ( $code_snippets->user_can( 'install' ) ) { ?>
|
37 |
-
<a href="<?php echo $code_snippets->admin->single_url; ?>" class="add-new-h2"><?php echo esc_html_x('Add New', 'snippet', 'code-snippets'); ?></a>
|
38 |
-
<?php }
|
39 |
-
$code_snippets->list_table->search_notice(); ?></h2>
|
40 |
-
|
41 |
-
<?php $code_snippets->list_table->views(); ?>
|
42 |
-
|
43 |
-
<form method="get" action="">
|
44 |
-
<?php
|
45 |
-
$code_snippets->list_table->required_form_fields( 'search_box' );
|
46 |
-
$code_snippets->list_table->search_box( __( 'Search Installed Snippets', 'code-snippets' ), 'search_id' );
|
47 |
-
?>
|
48 |
-
</form>
|
49 |
-
<form method="post" action="">
|
50 |
-
<?php $code_snippets->list_table->required_form_fields(); ?>
|
51 |
-
<?php $code_snippets->list_table->display(); ?>
|
52 |
-
</form>
|
53 |
-
|
54 |
-
<?php do_action( 'code_snippets_admin_manage' ); ?>
|
55 |
-
|
56 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/admin/single.php
DELETED
@@ -1,99 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* HTML code for the Add New/Edit Snippet page
|
5 |
-
*
|
6 |
-
* @package Code Snippets
|
7 |
-
* @subpackage Administration
|
8 |
-
*/
|
9 |
-
|
10 |
-
if ( ! class_exists( 'Code_Snippets' ) ) exit;
|
11 |
-
|
12 |
-
global $code_snippets;
|
13 |
-
|
14 |
-
$table = $code_snippets->get_table_name();
|
15 |
-
$screen = get_current_screen();
|
16 |
-
|
17 |
-
$edit_id = ( isset( $_REQUEST['edit'] ) ? intval( $_REQUEST['edit'] ) : 0 );
|
18 |
-
$snippet = $code_snippets->get_snippet( $edit_id );
|
19 |
-
|
20 |
-
?>
|
21 |
-
|
22 |
-
<?php if ( isset( $_REQUEST['invalid'] ) && $_REQUEST['invalid'] ) : ?>
|
23 |
-
<div id="message" class="error fade"><p><?php _e('Please provide a name for the snippet and its code.', 'code-snippets'); ?></p></div>
|
24 |
-
<?php elseif ( isset( $_REQUEST['activated'], $_REQUEST['updated'] ) && $_REQUEST['activated'] && $_REQUEST['updated'] ) : ?>
|
25 |
-
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>updated</strong> and <strong>activated</strong>.', 'code-snippets'); ?></p></div>
|
26 |
-
<?php elseif ( isset( $_REQUEST['activated'], $_REQUEST['added'] ) && $_REQUEST['activated'] && $_REQUEST['added'] ) : ?>
|
27 |
-
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>added</strong> and <strong>activated</strong>.', 'code-snippets'); ?></p></div>
|
28 |
-
<?php elseif ( isset( $_REQUEST['updated'] ) && $_REQUEST['updated'] ) : ?>
|
29 |
-
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>updated</strong>.', 'code-snippets'); ?></p></div>
|
30 |
-
<?php elseif ( isset( $_REQUEST['added'] ) && $_REQUEST['added'] ) : ?>
|
31 |
-
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>added</strong>.', 'code-snippets'); ?></p></div>
|
32 |
-
<?php endif; ?>
|
33 |
-
|
34 |
-
<div class="wrap">
|
35 |
-
<?php screen_icon(); ?>
|
36 |
-
<h2><?php
|
37 |
-
if ( $edit_id ) {
|
38 |
-
esc_html_e('Edit Snippet', 'code-snippets');
|
39 |
-
|
40 |
-
if ( $code_snippets->user_can( 'install' ) )
|
41 |
-
printf( ' <a href="%1$s" class="add-new-h2">%2$s</a>',
|
42 |
-
$code_snippets->admin->single_url,
|
43 |
-
esc_html_x('Add New', 'snippet', 'code-snippets')
|
44 |
-
);
|
45 |
-
} else {
|
46 |
-
esc_html_e('Add New Snippet', 'code-snippets');
|
47 |
-
}
|
48 |
-
?></h2>
|
49 |
-
|
50 |
-
<form method="post" action="" style="margin-top: 10px;">
|
51 |
-
<?php
|
52 |
-
|
53 |
-
/* Output the hidden fields */
|
54 |
-
|
55 |
-
if ( intval( $snippet->id ) > 0 )
|
56 |
-
printf ( '<input type="hidden" name="snippet_id" value="%d" />', $snippet->id );
|
57 |
-
|
58 |
-
printf ( '<input type="hidden" name="snippet_active" value="%d" />', $snippet->active );
|
59 |
-
?>
|
60 |
-
<div id="titlediv">
|
61 |
-
<div id="titlewrap">
|
62 |
-
<label for="title" style="display: none;"><?php _e('Name (short title)', 'code-snippets'); ?></label>
|
63 |
-
<input id="title" type="text" autocomplete="off" name="snippet_name" value="<?php echo esc_html( $snippet->name ); ?>" placeholder="<?php _e('Name (short title)', 'code-snippets'); ?>" required="required" />
|
64 |
-
</div>
|
65 |
-
</div>
|
66 |
-
|
67 |
-
<label for="snippet_code">
|
68 |
-
<h3><?php _e('Code', 'code-snippets'); ?></h3>
|
69 |
-
</label>
|
70 |
-
|
71 |
-
<textarea id="snippet_code" name="snippet_code" rows="20" spellcheck="false" style="font-family: monospace; width:100%;"><?php echo $snippet->code; ?></textarea>
|
72 |
-
|
73 |
-
<?php do_action( 'code_snippets_admin_single', $snippet ); ?>
|
74 |
-
|
75 |
-
<p class="submit">
|
76 |
-
<?php
|
77 |
-
submit_button( null, 'primary', 'save_snippet', false );
|
78 |
-
|
79 |
-
if ( ! $snippet->active ) {
|
80 |
-
echo ' ';
|
81 |
-
submit_button( __( 'Save Changes & Activate', 'code-snippets' ), 'secondary', 'save_snippet_activate', false );
|
82 |
-
}
|
83 |
-
?>
|
84 |
-
</p>
|
85 |
-
|
86 |
-
</form>
|
87 |
-
</div>
|
88 |
-
<script type="text/javascript">
|
89 |
-
var editor = CodeMirror.fromTextArea(document.getElementById("snippet_code"), {
|
90 |
-
lineNumbers: true,
|
91 |
-
matchBrackets: true,
|
92 |
-
lineWrapping: true,
|
93 |
-
mode: "text/x-php",
|
94 |
-
indentUnit: 4,
|
95 |
-
indentWithTabs: true,
|
96 |
-
enterMode: "keep",
|
97 |
-
tabMode: "shift"
|
98 |
-
});
|
99 |
-
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/class-admin.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
/**
|
4 |
* Contains the class for handling the administration interface
|
5 |
*
|
6 |
-
* @package
|
7 |
* @subpackage Administration
|
8 |
*/
|
9 |
|
@@ -13,27 +13,30 @@
|
|
13 |
* Don't directly access the methods in this class or attempt to
|
14 |
* re-initialize it. Instead, use the instance in $code_snippets->admin
|
15 |
*
|
16 |
-
* @since
|
17 |
-
* @package
|
|
|
18 |
*/
|
19 |
class Code_Snippets_Admin {
|
20 |
|
21 |
/**
|
22 |
* The full URLs to the admin pages
|
23 |
*
|
24 |
-
* @
|
|
|
25 |
* @access public
|
26 |
*/
|
27 |
-
public $manage_url, $single_url, $import_url;
|
28 |
|
29 |
/**
|
30 |
* The hooks for the admin pages
|
31 |
* Used primarily for enqueueing scripts and styles
|
32 |
*
|
33 |
-
* @
|
|
|
34 |
* @access public
|
35 |
*/
|
36 |
-
public $manage_page, $single_page, $import_page;
|
37 |
|
38 |
/**
|
39 |
* Initializes the variables and
|
@@ -44,10 +47,8 @@ class Code_Snippets_Admin {
|
|
44 |
function __construct() {
|
45 |
global $code_snippets;
|
46 |
|
47 |
-
$this->
|
48 |
-
|
49 |
-
$this->manage_slug = apply_filters( 'code_snippets_admin_manage', 'snippets' );
|
50 |
-
$this->single_slug = apply_filters( 'code_snippets_admin_single', 'snippet' );
|
51 |
|
52 |
$this->manage_url = self_admin_url( 'admin.php?page=' . $this->manage_slug );
|
53 |
$this->single_url = self_admin_url( 'admin.php?page=' . $this->single_slug );
|
@@ -58,9 +59,8 @@ class Code_Snippets_Admin {
|
|
58 |
/**
|
59 |
* Register action and filter hooks
|
60 |
*
|
61 |
-
* @since
|
62 |
* @access private
|
63 |
-
*
|
64 |
* @return void
|
65 |
*/
|
66 |
function setup_hooks() {
|
@@ -82,7 +82,7 @@ class Code_Snippets_Admin {
|
|
82 |
add_action( 'admin_enqueue_scripts', array( $this, 'load_admin_icon_style' ) );
|
83 |
|
84 |
/* Add the description editor to the Snippets > Add New page */
|
85 |
-
add_action( '
|
86 |
|
87 |
/* Handle saving the user's screen option preferences */
|
88 |
add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
|
@@ -93,13 +93,15 @@ class Code_Snippets_Admin {
|
|
93 |
|
94 |
/**
|
95 |
* Handles saving the user's snippets per page preference
|
96 |
-
*
|
97 |
-
* @param
|
98 |
-
* @param
|
|
|
99 |
* @return unknown
|
100 |
*/
|
101 |
function set_screen_option( $status, $option, $value ) {
|
102 |
-
if ( 'snippets_per_page' === $option )
|
|
|
103 |
}
|
104 |
|
105 |
/**
|
@@ -109,14 +111,14 @@ class Code_Snippets_Admin {
|
|
109 |
* Adds a checkbox to the *Settings > Network Settings*
|
110 |
* network admin menu
|
111 |
*
|
112 |
-
* @since
|
113 |
* @access private
|
114 |
*
|
115 |
-
* @param
|
116 |
-
* @return array
|
117 |
*/
|
118 |
function mu_menu_items( $menu_items ) {
|
119 |
-
$menu_items['snippets'] = __('Snippets', 'code-snippets');
|
120 |
return $menu_items;
|
121 |
}
|
122 |
|
@@ -126,16 +128,15 @@ class Code_Snippets_Admin {
|
|
126 |
* Add both an importer to the Tools menu
|
127 |
* and an Import Snippets page to the network admin menu
|
128 |
*
|
129 |
-
* @since
|
130 |
* @access private
|
131 |
-
*
|
132 |
* @return void
|
133 |
*/
|
134 |
function load_importer() {
|
135 |
|
136 |
if ( defined( 'WP_LOAD_IMPORTERS' ) ) {
|
137 |
|
138 |
-
|
139 |
require_once ABSPATH . 'wp-admin/includes/import.php';
|
140 |
|
141 |
if ( ! class_exists( 'WP_Importer' ) ) {
|
@@ -144,43 +145,86 @@ class Code_Snippets_Admin {
|
|
144 |
require_once $class_wp_importer;
|
145 |
}
|
146 |
|
|
|
147 |
register_importer(
|
148 |
'code-snippets',
|
149 |
-
__('Code Snippets', 'code-snippets'),
|
150 |
-
__('Import snippets from a
|
151 |
array( $this, 'display_import_menu' )
|
152 |
);
|
153 |
}
|
154 |
|
155 |
-
$this->import_url
|
156 |
add_action( 'load-importer-code-snippets', array( $this, 'load_import_menu' ) );
|
157 |
}
|
158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
/**
|
160 |
* Add the dashboard admin menu and subpages
|
161 |
*
|
162 |
-
* @since
|
163 |
* @access private
|
164 |
*
|
165 |
-
* @uses
|
166 |
-
* @uses
|
167 |
-
* @uses
|
168 |
-
* @uses
|
169 |
* @return void
|
170 |
*/
|
171 |
function add_admin_menus() {
|
172 |
global $code_snippets;
|
173 |
|
174 |
/* Use a different screen icon for the MP6 interface */
|
175 |
-
if ( get_user_option( 'admin_color' ) !== 'mp6' )
|
176 |
-
$menu_icon = apply_filters( '
|
177 |
-
|
|
|
|
|
178 |
$menu_icon = 'div';
|
|
|
179 |
|
180 |
-
/* Add the top-level menu and
|
181 |
$this->manage_page = add_menu_page(
|
182 |
-
__('Snippets', 'code-snippets'),
|
183 |
-
__('Snippets', 'code-snippets'),
|
184 |
$code_snippets->get_cap( 'manage' ),
|
185 |
$this->manage_slug,
|
186 |
array( $this, 'display_manage_menu' ),
|
@@ -190,8 +234,8 @@ class Code_Snippets_Admin {
|
|
190 |
|
191 |
add_submenu_page(
|
192 |
$this->manage_slug,
|
193 |
-
__('Snippets', 'code-snippets'),
|
194 |
-
__('Manage', 'code-snippets'),
|
195 |
$code_snippets->get_cap( 'manage' ),
|
196 |
$this->manage_slug,
|
197 |
array( $this, 'display_manage_menu')
|
@@ -202,8 +246,8 @@ class Code_Snippets_Admin {
|
|
202 |
|
203 |
$this->single_page = add_submenu_page(
|
204 |
$this->manage_slug,
|
205 |
-
$editing ? __('Edit Snippet', 'code-snippets') : __('Add New Snippet', 'code-snippets'),
|
206 |
-
$editing ? __('Edit', 'code-snippets') : __('Add New', 'code-snippets'),
|
207 |
$code_snippets->get_cap( 'install' ),
|
208 |
$this->single_slug,
|
209 |
array( $this, 'display_single_menu' )
|
@@ -211,9 +255,6 @@ class Code_Snippets_Admin {
|
|
211 |
|
212 |
add_action( "load-$this->manage_page", array( $this, 'load_manage_menu' ) );
|
213 |
add_action( "load-$this->single_page", array( $this, 'load_single_menu' ) );
|
214 |
-
|
215 |
-
add_action( "load-$this->manage_page", array( $code_snippets, 'maybe_create_tables' ) );
|
216 |
-
add_action( "load-$this->single_page", array( $code_snippets, 'maybe_create_tables' ) );
|
217 |
}
|
218 |
|
219 |
/**
|
@@ -221,12 +262,11 @@ class Code_Snippets_Admin {
|
|
221 |
* We need to do this as there is no Tools menu in the network
|
222 |
* admin, and so we cannot register an importer
|
223 |
*
|
224 |
-
* @since
|
225 |
* @access private
|
226 |
-
*
|
227 |
-
* @uses
|
228 |
-
* @uses
|
229 |
-
* @uses add_action() To enqueue scripts and styles
|
230 |
* @return void
|
231 |
*/
|
232 |
function add_import_admin_menu() {
|
@@ -234,8 +274,8 @@ class Code_Snippets_Admin {
|
|
234 |
|
235 |
$this->import_page = add_submenu_page(
|
236 |
$this->manage_slug,
|
237 |
-
__('Import Snippets', 'code-snippets'),
|
238 |
-
__('Import', 'code-snippets'),
|
239 |
$code_snippets->get_cap( 'import' ),
|
240 |
'import-code-snippets',
|
241 |
array( $this, 'display_import_menu' )
|
@@ -243,29 +283,28 @@ class Code_Snippets_Admin {
|
|
243 |
|
244 |
$this->import_url = self_admin_url( 'admin.php?page=import-code-snippets' );
|
245 |
add_action( "load-$this->import_page", array( $this, 'load_import_menu' ) );
|
246 |
-
add_action( "load-$this->import_page", array( $code_snippets, 'maybe_create_tables' ) );
|
247 |
}
|
248 |
|
249 |
/**
|
250 |
* Enqueue the icon stylesheet
|
251 |
*
|
252 |
-
* @since
|
253 |
* @access private
|
254 |
-
*
|
255 |
-
* @uses
|
256 |
-
*
|
257 |
* @return void
|
258 |
*/
|
259 |
function load_admin_icon_style() {
|
260 |
global $code_snippets;
|
261 |
|
262 |
-
$stylesheet = ( 'mp6' === get_user_option( 'admin_color' ) ? 'menu-icon.mp6' : 'screen-icon');
|
263 |
|
264 |
wp_enqueue_style(
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
);
|
270 |
}
|
271 |
|
@@ -273,19 +312,21 @@ class Code_Snippets_Admin {
|
|
273 |
* Initializes the list table class and loads the help tabs
|
274 |
* for the Manage Snippets page
|
275 |
*
|
276 |
-
* @since
|
277 |
* @access private
|
278 |
-
*
|
279 |
* @return void
|
280 |
*/
|
281 |
function load_manage_menu() {
|
282 |
global $code_snippets;
|
283 |
|
|
|
|
|
|
|
284 |
/* Load the screen help tabs */
|
285 |
-
|
286 |
|
287 |
/* Initialize the snippet table class */
|
288 |
-
|
289 |
$code_snippets->list_table = new Code_Snippets_List_Table();
|
290 |
$code_snippets->list_table->prepare_items();
|
291 |
}
|
@@ -293,22 +334,23 @@ class Code_Snippets_Admin {
|
|
293 |
/**
|
294 |
* Loads the help tabs for the Edit Snippets page
|
295 |
*
|
296 |
-
* @since
|
297 |
* @access private
|
298 |
-
*
|
299 |
-
* @uses $wpdb To save the posted snippet to the database
|
300 |
-
* @uses wp_redirect To pass the results to the page
|
301 |
-
*
|
302 |
* @return void
|
|
|
|
|
|
|
303 |
*/
|
304 |
function load_single_menu() {
|
305 |
global $code_snippets;
|
306 |
-
|
307 |
$screen = get_current_screen();
|
308 |
|
|
|
|
|
|
|
309 |
/* Don't let the user pass if they can't edit (install check is done by WP) */
|
310 |
if ( isset( $_REQUEST['edit'] ) && ! $code_snippets->user_can( 'edit' ) )
|
311 |
-
wp_die( __("Sorry, you're not allowed to edit snippets", 'code-snippets') );
|
312 |
|
313 |
/* Save the snippet if one has been submitted */
|
314 |
if ( isset( $_REQUEST['save_snippet'] ) || isset( $_REQUEST['save_snippet_activate'] ) ) {
|
@@ -345,7 +387,7 @@ class Code_Snippets_Admin {
|
|
345 |
}
|
346 |
|
347 |
/* Load the screen help tabs */
|
348 |
-
|
349 |
|
350 |
/* Enqueue the code editor and other scripts and styles */
|
351 |
add_filter( 'admin_enqueue_scripts', array( $this, 'single_menu_enqueue_scripts' ) );
|
@@ -354,16 +396,15 @@ class Code_Snippets_Admin {
|
|
354 |
/**
|
355 |
* Registers and loads the code editor's scripts
|
356 |
*
|
357 |
-
* @since
|
358 |
* @access private
|
359 |
*
|
360 |
-
* @uses
|
361 |
-
* @uses
|
362 |
-
* @uses
|
363 |
-
* @uses
|
364 |
-
*
|
365 |
-
* @param string $hook The current page hook, to be compared with the single snippet page hook
|
366 |
*
|
|
|
367 |
* @return void
|
368 |
*/
|
369 |
function single_menu_enqueue_scripts( $hook ) {
|
@@ -374,7 +415,7 @@ class Code_Snippets_Admin {
|
|
374 |
return;
|
375 |
|
376 |
/* CodeMirror package version */
|
377 |
-
$codemirror_version = '3.
|
378 |
|
379 |
/* CodeMirror base framework */
|
380 |
|
@@ -448,84 +489,105 @@ class Code_Snippets_Admin {
|
|
448 |
false,
|
449 |
$code_snippets->version
|
450 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
451 |
}
|
452 |
|
453 |
/**
|
454 |
* Processes import files and loads the help tabs for the Import Snippets page
|
455 |
*
|
456 |
-
* @since
|
457 |
*
|
458 |
-
* @uses
|
459 |
-
* @uses
|
460 |
-
* @uses
|
|
|
461 |
*
|
|
|
462 |
* @return void
|
463 |
*/
|
464 |
function load_import_menu() {
|
465 |
global $code_snippets;
|
466 |
|
|
|
|
|
|
|
467 |
/* Process import files */
|
|
|
468 |
if ( isset( $_FILES['code_snippets_import_file']['tmp_name'] ) ) {
|
469 |
-
|
470 |
-
|
471 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
472 |
}
|
473 |
}
|
474 |
|
475 |
/* Load the screen help tabs */
|
476 |
-
|
477 |
}
|
478 |
|
479 |
/**
|
480 |
* Displays the manage snippets page
|
481 |
*
|
482 |
-
* @since
|
483 |
* @access private
|
484 |
-
*
|
485 |
* @return void
|
486 |
*/
|
487 |
function display_manage_menu() {
|
488 |
-
|
489 |
}
|
490 |
|
491 |
/**
|
492 |
* Displays the single snippet page
|
493 |
*
|
494 |
-
* @since
|
495 |
* @access private
|
496 |
-
*
|
497 |
* @return void
|
498 |
*/
|
499 |
function display_single_menu() {
|
500 |
-
|
501 |
}
|
502 |
|
503 |
/**
|
504 |
* Displays the import snippets page
|
505 |
*
|
506 |
-
* @since
|
507 |
* @access private
|
508 |
-
*
|
509 |
* @return void
|
510 |
*/
|
511 |
function display_import_menu() {
|
512 |
-
|
513 |
}
|
514 |
|
515 |
/**
|
516 |
* Add a description editor to the single snippet page
|
517 |
*
|
518 |
-
* @since
|
519 |
* @access private
|
520 |
-
*
|
521 |
-
* @
|
522 |
*/
|
523 |
function description_editor_box( $snippet ) {
|
|
|
524 |
?>
|
525 |
|
526 |
<label for="snippet_description">
|
527 |
-
<h3><div style="position: absolute;"><?php _e('Description', 'code-snippets');
|
528 |
-
?> <span style="font-weight: normal;"><?php esc_html_e('(Optional)', 'code-snippets'); ?></span></div></h3>
|
529 |
</label>
|
530 |
|
531 |
<?php
|
@@ -535,7 +597,7 @@ class Code_Snippets_Admin {
|
|
535 |
wp_editor(
|
536 |
$snippet->description,
|
537 |
'description',
|
538 |
-
apply_filters( '
|
539 |
'textarea_name' => 'snippet_description',
|
540 |
'textarea_rows' => 10,
|
541 |
'teeny' => true,
|
@@ -547,18 +609,17 @@ class Code_Snippets_Admin {
|
|
547 |
/**
|
548 |
* Adds a link pointing to the Manage Snippets page
|
549 |
*
|
550 |
-
* @since
|
551 |
* @access private
|
552 |
-
*
|
553 |
-
* @
|
554 |
-
* @return array The modified plugin action links
|
555 |
*/
|
556 |
function settings_link( $links ) {
|
557 |
array_unshift( $links, sprintf(
|
558 |
'<a href="%1$s" title="%2$s">%3$s</a>',
|
559 |
$this->manage_url,
|
560 |
-
__('Manage your existing snippets', 'code-snippets'),
|
561 |
-
__('Manage', 'code-snippets')
|
562 |
) );
|
563 |
return $links;
|
564 |
}
|
@@ -566,39 +627,39 @@ class Code_Snippets_Admin {
|
|
566 |
/**
|
567 |
* Adds extra links related to the plugin
|
568 |
*
|
569 |
-
* @since
|
570 |
* @access private
|
571 |
-
*
|
572 |
-
* @param
|
573 |
-
* @
|
574 |
-
* @return array The modified plugin info links
|
575 |
*/
|
576 |
function plugin_meta( $links, $file ) {
|
577 |
global $code_snippets;
|
578 |
|
579 |
/* We only want to affect the Code Snippets plugin listing */
|
580 |
-
if ( $file !== $code_snippets->basename )
|
|
|
581 |
|
582 |
$format = '<a href="%1$s" title="%2$s">%3$s</a>';
|
583 |
|
584 |
/* array_merge appends the links to the end */
|
585 |
return array_merge( $links, array(
|
586 |
sprintf( $format,
|
587 |
-
'http://wordpress.org/
|
588 |
-
__('Visit the WordPress.org plugin page', 'code-snippets'),
|
589 |
-
__('About', 'code-snippets')
|
590 |
),
|
591 |
sprintf( $format,
|
592 |
'http://wordpress.org/support/plugin/code-snippets/',
|
593 |
-
__('Visit the support forums', 'code-snippets'),
|
594 |
-
__('Support', 'code-snippets')
|
595 |
),
|
596 |
sprintf( $format,
|
597 |
'http://code-snippets.bungeshea.com/donate/',
|
598 |
-
__("Support this plugin's development", 'code-snippets'),
|
599 |
-
__('Donate', 'code-snippets')
|
600 |
)
|
601 |
) );
|
602 |
}
|
603 |
|
604 |
-
}
|
3 |
/**
|
4 |
* Contains the class for handling the administration interface
|
5 |
*
|
6 |
+
* @package Code_Snippets
|
7 |
* @subpackage Administration
|
8 |
*/
|
9 |
|
13 |
* Don't directly access the methods in this class or attempt to
|
14 |
* re-initialize it. Instead, use the instance in $code_snippets->admin
|
15 |
*
|
16 |
+
* @since 1.7.1
|
17 |
+
* @package Code_Snippets
|
18 |
+
* @subpackage Administration
|
19 |
*/
|
20 |
class Code_Snippets_Admin {
|
21 |
|
22 |
/**
|
23 |
* The full URLs to the admin pages
|
24 |
*
|
25 |
+
* @var string
|
26 |
+
* @since 1.7.1
|
27 |
* @access public
|
28 |
*/
|
29 |
+
public $manage_url, $single_url, $import_url = '';
|
30 |
|
31 |
/**
|
32 |
* The hooks for the admin pages
|
33 |
* Used primarily for enqueueing scripts and styles
|
34 |
*
|
35 |
+
* @var string
|
36 |
+
* @since 1.7.1
|
37 |
* @access public
|
38 |
*/
|
39 |
+
public $manage_page, $single_page, $import_page = '';
|
40 |
|
41 |
/**
|
42 |
* Initializes the variables and
|
47 |
function __construct() {
|
48 |
global $code_snippets;
|
49 |
|
50 |
+
$this->manage_slug = apply_filters( 'code_snippets/admin/manage_slug', 'snippets' );
|
51 |
+
$this->single_slug = apply_filters( 'code_snippets/admin/single_slug', 'snippet' );
|
|
|
|
|
52 |
|
53 |
$this->manage_url = self_admin_url( 'admin.php?page=' . $this->manage_slug );
|
54 |
$this->single_url = self_admin_url( 'admin.php?page=' . $this->single_slug );
|
59 |
/**
|
60 |
* Register action and filter hooks
|
61 |
*
|
62 |
+
* @since 1.7.1
|
63 |
* @access private
|
|
|
64 |
* @return void
|
65 |
*/
|
66 |
function setup_hooks() {
|
82 |
add_action( 'admin_enqueue_scripts', array( $this, 'load_admin_icon_style' ) );
|
83 |
|
84 |
/* Add the description editor to the Snippets > Add New page */
|
85 |
+
add_action( 'code_snippets/admin/single', array( $this, 'description_editor_box' ), 5 );
|
86 |
|
87 |
/* Handle saving the user's screen option preferences */
|
88 |
add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
|
93 |
|
94 |
/**
|
95 |
* Handles saving the user's snippets per page preference
|
96 |
+
*
|
97 |
+
* @param unknown $status
|
98 |
+
* @param string $option
|
99 |
+
* @param unknown $value
|
100 |
* @return unknown
|
101 |
*/
|
102 |
function set_screen_option( $status, $option, $value ) {
|
103 |
+
if ( 'snippets_per_page' === $option )
|
104 |
+
return $value;
|
105 |
}
|
106 |
|
107 |
/**
|
111 |
* Adds a checkbox to the *Settings > Network Settings*
|
112 |
* network admin menu
|
113 |
*
|
114 |
+
* @since 1.7.1
|
115 |
* @access private
|
116 |
*
|
117 |
+
* @param array $menu_items The current mu menu items
|
118 |
+
* @return array The modified mu menu items
|
119 |
*/
|
120 |
function mu_menu_items( $menu_items ) {
|
121 |
+
$menu_items['snippets'] = __( 'Snippets', 'code-snippets' );
|
122 |
return $menu_items;
|
123 |
}
|
124 |
|
128 |
* Add both an importer to the Tools menu
|
129 |
* and an Import Snippets page to the network admin menu
|
130 |
*
|
131 |
+
* @since 1.6
|
132 |
* @access private
|
|
|
133 |
* @return void
|
134 |
*/
|
135 |
function load_importer() {
|
136 |
|
137 |
if ( defined( 'WP_LOAD_IMPORTERS' ) ) {
|
138 |
|
139 |
+
/* Load Importer API */
|
140 |
require_once ABSPATH . 'wp-admin/includes/import.php';
|
141 |
|
142 |
if ( ! class_exists( 'WP_Importer' ) ) {
|
145 |
require_once $class_wp_importer;
|
146 |
}
|
147 |
|
148 |
+
/* Register the Code Snippets importer with WordPress */
|
149 |
register_importer(
|
150 |
'code-snippets',
|
151 |
+
__( 'Code Snippets', 'code-snippets' ),
|
152 |
+
__( 'Import snippets from a Code Snippets export file', 'code-snippets' ),
|
153 |
array( $this, 'display_import_menu' )
|
154 |
);
|
155 |
}
|
156 |
|
157 |
+
$this->import_url = self_admin_url( 'admin.php?import=code-snippets' );
|
158 |
add_action( 'load-importer-code-snippets', array( $this, 'load_import_menu' ) );
|
159 |
}
|
160 |
|
161 |
+
/**
|
162 |
+
* Load contextual help tabs for an admin screen.
|
163 |
+
*
|
164 |
+
* @since 1.8
|
165 |
+
* @access public
|
166 |
+
* @param string $slug The file handle (filename with no path or extension) to load
|
167 |
+
* @return void
|
168 |
+
*/
|
169 |
+
public function load_help_tabs( $slug ) {
|
170 |
+
global $code_snippets;
|
171 |
+
include $code_snippets->plugin_dir . "admin/help/{$slug}.php";
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Load an admin view template
|
176 |
+
*
|
177 |
+
* @since 1.8
|
178 |
+
* @access public
|
179 |
+
* @param string $slug The file handle (filename with no path or extension) to load
|
180 |
+
* @return void
|
181 |
+
*/
|
182 |
+
public function get_view( $slug ) {
|
183 |
+
global $code_snippets;
|
184 |
+
require $code_snippets->plugin_dir . "admin/views/{$slug}.php";
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Display the admin status and error messages
|
189 |
+
*
|
190 |
+
* @since 1.8
|
191 |
+
* @access public
|
192 |
+
* @param string $slug The file handle (filename with no path or extension) to load
|
193 |
+
* @return void
|
194 |
+
*/
|
195 |
+
public function get_messages( $slug ) {
|
196 |
+
global $code_snippets;
|
197 |
+
require $code_snippets->plugin_dir . "admin/messages/{$slug}.php";
|
198 |
+
}
|
199 |
+
|
200 |
/**
|
201 |
* Add the dashboard admin menu and subpages
|
202 |
*
|
203 |
+
* @since 1.0
|
204 |
* @access private
|
205 |
*
|
206 |
+
* @uses add_menu_page() To register a top-level menu
|
207 |
+
* @uses add_submenu_page() To register a submenu page
|
208 |
+
* @uses apply_filters() To retrieve the current menu slug
|
209 |
+
* @uses plugins_url() To retrieve the URL to a resource
|
210 |
* @return void
|
211 |
*/
|
212 |
function add_admin_menus() {
|
213 |
global $code_snippets;
|
214 |
|
215 |
/* Use a different screen icon for the MP6 interface */
|
216 |
+
if ( get_user_option( 'admin_color' ) !== 'mp6' ) {
|
217 |
+
$menu_icon = apply_filters( 'code_snippets/admin/menu_icon_url',
|
218 |
+
plugins_url( 'assets/menu-icon.png', $code_snippets->file )
|
219 |
+
);
|
220 |
+
} else {
|
221 |
$menu_icon = 'div';
|
222 |
+
}
|
223 |
|
224 |
+
/* Add the top-level menu and associated subpage */
|
225 |
$this->manage_page = add_menu_page(
|
226 |
+
__( 'Snippets', 'code-snippets' ),
|
227 |
+
__( 'Snippets', 'code-snippets' ),
|
228 |
$code_snippets->get_cap( 'manage' ),
|
229 |
$this->manage_slug,
|
230 |
array( $this, 'display_manage_menu' ),
|
234 |
|
235 |
add_submenu_page(
|
236 |
$this->manage_slug,
|
237 |
+
__( 'Snippets', 'code-snippets' ),
|
238 |
+
__( 'Manage', 'code-snippets' ),
|
239 |
$code_snippets->get_cap( 'manage' ),
|
240 |
$this->manage_slug,
|
241 |
array( $this, 'display_manage_menu')
|
246 |
|
247 |
$this->single_page = add_submenu_page(
|
248 |
$this->manage_slug,
|
249 |
+
$editing ? __( 'Edit Snippet', 'code-snippets' ) : __( 'Add New Snippet', 'code-snippets' ),
|
250 |
+
$editing ? __( 'Edit', 'code-snippets' ) : __( 'Add New', 'code-snippets' ),
|
251 |
$code_snippets->get_cap( 'install' ),
|
252 |
$this->single_slug,
|
253 |
array( $this, 'display_single_menu' )
|
255 |
|
256 |
add_action( "load-$this->manage_page", array( $this, 'load_manage_menu' ) );
|
257 |
add_action( "load-$this->single_page", array( $this, 'load_single_menu' ) );
|
|
|
|
|
|
|
258 |
}
|
259 |
|
260 |
/**
|
262 |
* We need to do this as there is no Tools menu in the network
|
263 |
* admin, and so we cannot register an importer
|
264 |
*
|
265 |
+
* @since 1.6
|
266 |
* @access private
|
267 |
+
* @uses add_submenu_page() To register the menu page
|
268 |
+
* @uses apply_filters() To retrieve the current menu slug
|
269 |
+
* @uses add_action() To enqueue scripts and styles
|
|
|
270 |
* @return void
|
271 |
*/
|
272 |
function add_import_admin_menu() {
|
274 |
|
275 |
$this->import_page = add_submenu_page(
|
276 |
$this->manage_slug,
|
277 |
+
__( 'Import Snippets', 'code-snippets' ),
|
278 |
+
__( 'Import', 'code-snippets' ),
|
279 |
$code_snippets->get_cap( 'import' ),
|
280 |
'import-code-snippets',
|
281 |
array( $this, 'display_import_menu' )
|
283 |
|
284 |
$this->import_url = self_admin_url( 'admin.php?page=import-code-snippets' );
|
285 |
add_action( "load-$this->import_page", array( $this, 'load_import_menu' ) );
|
|
|
286 |
}
|
287 |
|
288 |
/**
|
289 |
* Enqueue the icon stylesheet
|
290 |
*
|
291 |
+
* @since 1.0
|
292 |
* @access private
|
293 |
+
* @uses wp_enqueue_style() To add the stylesheet to the queue
|
294 |
+
* @uses get_user_option() To check if MP6 mode is active
|
295 |
+
* @uses plugins_url To retrieve a URL to assets
|
296 |
* @return void
|
297 |
*/
|
298 |
function load_admin_icon_style() {
|
299 |
global $code_snippets;
|
300 |
|
301 |
+
$stylesheet = ( 'mp6' === get_user_option( 'admin_color' ) ? 'menu-icon.mp6' : 'screen-icon' );
|
302 |
|
303 |
wp_enqueue_style(
|
304 |
+
'icon-snippets',
|
305 |
+
plugins_url( "assets/{$stylesheet}.css", $code_snippets->file ),
|
306 |
+
false,
|
307 |
+
$code_snippets->version
|
308 |
);
|
309 |
}
|
310 |
|
312 |
* Initializes the list table class and loads the help tabs
|
313 |
* for the Manage Snippets page
|
314 |
*
|
315 |
+
* @since 1.0
|
316 |
* @access private
|
|
|
317 |
* @return void
|
318 |
*/
|
319 |
function load_manage_menu() {
|
320 |
global $code_snippets;
|
321 |
|
322 |
+
/* Create the snippet tables if they don't exist */
|
323 |
+
$code_snippets->maybe_create_tables( true, true );
|
324 |
+
|
325 |
/* Load the screen help tabs */
|
326 |
+
$this->load_help_tabs( 'manage' );
|
327 |
|
328 |
/* Initialize the snippet table class */
|
329 |
+
$code_snippets->get_include( 'class-list-table' );
|
330 |
$code_snippets->list_table = new Code_Snippets_List_Table();
|
331 |
$code_snippets->list_table->prepare_items();
|
332 |
}
|
334 |
/**
|
335 |
* Loads the help tabs for the Edit Snippets page
|
336 |
*
|
337 |
+
* @since 1.0
|
338 |
* @access private
|
|
|
|
|
|
|
|
|
339 |
* @return void
|
340 |
+
*
|
341 |
+
* @uses $wpdb To save the posted snippet to the database
|
342 |
+
* @uses wp_redirect To pass the results to the page
|
343 |
*/
|
344 |
function load_single_menu() {
|
345 |
global $code_snippets;
|
|
|
346 |
$screen = get_current_screen();
|
347 |
|
348 |
+
/* Create the snippet tables if they don't exist */
|
349 |
+
$code_snippets->maybe_create_tables( true, true );
|
350 |
+
|
351 |
/* Don't let the user pass if they can't edit (install check is done by WP) */
|
352 |
if ( isset( $_REQUEST['edit'] ) && ! $code_snippets->user_can( 'edit' ) )
|
353 |
+
wp_die( __("Sorry, you're not allowed to edit snippets", 'code-snippets' ) );
|
354 |
|
355 |
/* Save the snippet if one has been submitted */
|
356 |
if ( isset( $_REQUEST['save_snippet'] ) || isset( $_REQUEST['save_snippet_activate'] ) ) {
|
387 |
}
|
388 |
|
389 |
/* Load the screen help tabs */
|
390 |
+
$this->load_help_tabs( 'single' );
|
391 |
|
392 |
/* Enqueue the code editor and other scripts and styles */
|
393 |
add_filter( 'admin_enqueue_scripts', array( $this, 'single_menu_enqueue_scripts' ) );
|
396 |
/**
|
397 |
* Registers and loads the code editor's scripts
|
398 |
*
|
399 |
+
* @since 1.7
|
400 |
* @access private
|
401 |
*
|
402 |
+
* @uses wp_register_script()
|
403 |
+
* @uses wp_register_style()
|
404 |
+
* @uses wp_enqueue_script() To add the scripts to the queue
|
405 |
+
* @uses wp_enqueue_style() To add the stylesheets to the queue
|
|
|
|
|
406 |
*
|
407 |
+
* @param string $hook The current page hook, to be compared with the single snippet page hook
|
408 |
* @return void
|
409 |
*/
|
410 |
function single_menu_enqueue_scripts( $hook ) {
|
415 |
return;
|
416 |
|
417 |
/* CodeMirror package version */
|
418 |
+
$codemirror_version = '3.14';
|
419 |
|
420 |
/* CodeMirror base framework */
|
421 |
|
489 |
false,
|
490 |
$code_snippets->version
|
491 |
);
|
492 |
+
|
493 |
+
/* Enqueue custom scripts */
|
494 |
+
wp_enqueue_script(
|
495 |
+
'code-snippets-admin-single',
|
496 |
+
plugins_url( 'assets/admin-single.js', $code_snippets->file ),
|
497 |
+
false,
|
498 |
+
$code_snippets->version,
|
499 |
+
true // Load in footer
|
500 |
+
);
|
501 |
}
|
502 |
|
503 |
/**
|
504 |
* Processes import files and loads the help tabs for the Import Snippets page
|
505 |
*
|
506 |
+
* @since 1.3
|
507 |
*
|
508 |
+
* @uses $code_snippets->import() To process the import file
|
509 |
+
* @uses wp_redirect() To pass the import results to the page
|
510 |
+
* @uses add_query_arg() To append the results to the current URI
|
511 |
+
* @uses $this->load_help_tabs() To load the screen contextual help tabs
|
512 |
*
|
513 |
+
* @param string $file A filesystem path to the import file
|
514 |
* @return void
|
515 |
*/
|
516 |
function load_import_menu() {
|
517 |
global $code_snippets;
|
518 |
|
519 |
+
/* Create the snippet tables if they don't exist */
|
520 |
+
$code_snippets->maybe_create_tables( true, true );
|
521 |
+
|
522 |
/* Process import files */
|
523 |
+
|
524 |
if ( isset( $_FILES['code_snippets_import_file']['tmp_name'] ) ) {
|
525 |
+
|
526 |
+
/* Import the snippets. The result is the number of snippets that were imported */
|
527 |
+
$result = $code_snippets->import( $_FILES['code_snippets_import_file']['tmp_name'] );
|
528 |
+
|
529 |
+
/* Send the amount of imported snippets to the page */
|
530 |
+
if ( false === $result ) {
|
531 |
+
wp_redirect( add_query_arg( 'error', true ) );
|
532 |
+
} else {
|
533 |
+
wp_redirect( add_query_arg( 'imported', $result ) );
|
534 |
}
|
535 |
}
|
536 |
|
537 |
/* Load the screen help tabs */
|
538 |
+
$this->load_help_tabs( 'import' );
|
539 |
}
|
540 |
|
541 |
/**
|
542 |
* Displays the manage snippets page
|
543 |
*
|
544 |
+
* @since 1.0
|
545 |
* @access private
|
546 |
+
* @uses $this->get_view() To load an admin view template
|
547 |
* @return void
|
548 |
*/
|
549 |
function display_manage_menu() {
|
550 |
+
$this->get_view( 'manage' );
|
551 |
}
|
552 |
|
553 |
/**
|
554 |
* Displays the single snippet page
|
555 |
*
|
556 |
+
* @since 1.0
|
557 |
* @access private
|
558 |
+
* @uses $this->get_view() To load an admin view template
|
559 |
* @return void
|
560 |
*/
|
561 |
function display_single_menu() {
|
562 |
+
$this->get_view( 'single' );
|
563 |
}
|
564 |
|
565 |
/**
|
566 |
* Displays the import snippets page
|
567 |
*
|
568 |
+
* @since 1.3
|
569 |
* @access private
|
570 |
+
* @uses $this->get_view() To load an admin view template
|
571 |
* @return void
|
572 |
*/
|
573 |
function display_import_menu() {
|
574 |
+
$this->get_view( 'import' );
|
575 |
}
|
576 |
|
577 |
/**
|
578 |
* Add a description editor to the single snippet page
|
579 |
*
|
580 |
+
* @since 1.7
|
581 |
* @access private
|
582 |
+
* @param object $snippet The snippet being used for this page
|
583 |
+
* @return void
|
584 |
*/
|
585 |
function description_editor_box( $snippet ) {
|
586 |
+
|
587 |
?>
|
588 |
|
589 |
<label for="snippet_description">
|
590 |
+
<h3><div style="position: absolute;"><?php _e( 'Description', 'code-snippets' ); ?></div></h3>
|
|
|
591 |
</label>
|
592 |
|
593 |
<?php
|
597 |
wp_editor(
|
598 |
$snippet->description,
|
599 |
'description',
|
600 |
+
apply_filters( 'code_snippets/admin/description_editor_settings', array(
|
601 |
'textarea_name' => 'snippet_description',
|
602 |
'textarea_rows' => 10,
|
603 |
'teeny' => true,
|
609 |
/**
|
610 |
* Adds a link pointing to the Manage Snippets page
|
611 |
*
|
612 |
+
* @since 1.0
|
613 |
* @access private
|
614 |
+
* @param array $links The existing plugin action links
|
615 |
+
* @return array The modified plugin action links
|
|
|
616 |
*/
|
617 |
function settings_link( $links ) {
|
618 |
array_unshift( $links, sprintf(
|
619 |
'<a href="%1$s" title="%2$s">%3$s</a>',
|
620 |
$this->manage_url,
|
621 |
+
__( 'Manage your existing snippets', 'code-snippets' ),
|
622 |
+
__( 'Manage', 'code-snippets' )
|
623 |
) );
|
624 |
return $links;
|
625 |
}
|
627 |
/**
|
628 |
* Adds extra links related to the plugin
|
629 |
*
|
630 |
+
* @since 1.2
|
631 |
* @access private
|
632 |
+
* @param array $links The existing plugin info links
|
633 |
+
* @param string $file The plugin the links are for
|
634 |
+
* @return array The modified plugin info links
|
|
|
635 |
*/
|
636 |
function plugin_meta( $links, $file ) {
|
637 |
global $code_snippets;
|
638 |
|
639 |
/* We only want to affect the Code Snippets plugin listing */
|
640 |
+
if ( $file !== $code_snippets->basename )
|
641 |
+
return $links;
|
642 |
|
643 |
$format = '<a href="%1$s" title="%2$s">%3$s</a>';
|
644 |
|
645 |
/* array_merge appends the links to the end */
|
646 |
return array_merge( $links, array(
|
647 |
sprintf( $format,
|
648 |
+
'http://wordpress.org/plugins/code-snippets/',
|
649 |
+
__( 'Visit the WordPress.org plugin page', 'code-snippets' ),
|
650 |
+
__( 'About', 'code-snippets' )
|
651 |
),
|
652 |
sprintf( $format,
|
653 |
'http://wordpress.org/support/plugin/code-snippets/',
|
654 |
+
__( 'Visit the support forums', 'code-snippets' ),
|
655 |
+
__( 'Support', 'code-snippets' )
|
656 |
),
|
657 |
sprintf( $format,
|
658 |
'http://code-snippets.bungeshea.com/donate/',
|
659 |
+
__("Support this plugin's development", 'code-snippets' ),
|
660 |
+
__( 'Donate', 'code-snippets' )
|
661 |
)
|
662 |
) );
|
663 |
}
|
664 |
|
665 |
+
} // end of class
|
includes/class-list-table.php
CHANGED
@@ -3,32 +3,32 @@
|
|
3 |
/**
|
4 |
* Contains the class for handling the administration interface
|
5 |
*
|
6 |
-
* @package
|
7 |
* @subpackage Administration
|
8 |
*/
|
9 |
|
|
|
10 |
if ( ! class_exists( 'WP_List_Table' ) ) {
|
11 |
-
require_once
|
12 |
}
|
13 |
|
14 |
/**
|
15 |
* This class handles the table for the manage snippets menu
|
16 |
*
|
17 |
-
* @since
|
18 |
-
* @
|
19 |
-
* @
|
20 |
*/
|
21 |
class Code_Snippets_List_Table extends WP_List_Table {
|
22 |
|
23 |
/**#@+
|
24 |
-
* @since
|
25 |
* @access private
|
26 |
*/
|
27 |
|
28 |
/**
|
29 |
-
* The constructor function for our class
|
30 |
-
*
|
31 |
-
* Adds hooks, initializes variables, setups class
|
32 |
*/
|
33 |
function __construct() {
|
34 |
global $status, $page, $code_snippets;
|
@@ -42,19 +42,32 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
42 |
if ( isset( $_REQUEST['s'] ) )
|
43 |
$_SERVER['REQUEST_URI'] = add_query_arg( 's', stripslashes($_REQUEST['s'] ) );
|
44 |
|
|
|
45 |
$page = $this->get_pagenum();
|
46 |
|
47 |
add_screen_option( 'per_page', array(
|
48 |
-
'label' => __('Snippets per page', 'code-snippets'),
|
49 |
'default' => 10,
|
50 |
'option' => 'snippets_per_page'
|
51 |
) );
|
52 |
|
|
|
53 |
add_filter( "get_user_option_manage{$screen->id}columnshidden", array( $this, 'get_default_hidden_columns' ), 15 );
|
|
|
|
|
54 |
add_action( 'admin_enqueue_scripts', array( $this, 'load_table_style' ) );
|
55 |
|
|
|
56 |
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'activate', 'activate-multi', 'deactivate', 'deactivate-multi', 'delete', 'delete-multi' ) );
|
57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
parent::__construct( array(
|
59 |
'singular' => 'snippet',
|
60 |
'plural' => 'snippets',
|
@@ -65,18 +78,19 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
65 |
/**
|
66 |
* Enqueue the table stylesheet
|
67 |
*
|
68 |
-
* @since
|
69 |
-
*
|
70 |
-
* @
|
71 |
-
*
|
72 |
-
* @param string $hook The current page hook, to be compared with the manage snippets page hook
|
73 |
*/
|
74 |
function load_table_style( $hook ) {
|
75 |
global $code_snippets;
|
76 |
|
|
|
77 |
if ( $hook !== $code_snippets->admin->manage_page )
|
78 |
return;
|
79 |
|
|
|
80 |
if ( 'mp6' === get_user_option( 'admin_color' ) ) {
|
81 |
|
82 |
wp_enqueue_style(
|
@@ -98,28 +112,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
98 |
}
|
99 |
|
100 |
/**
|
101 |
-
*
|
102 |
-
* in the same way the post content is formatted
|
103 |
-
*
|
104 |
-
* @since 1.7
|
105 |
-
* @access public
|
106 |
-
*
|
107 |
-
* @param string $desc The snippet description to format
|
108 |
-
* @return string The formatted snippet description
|
109 |
-
*/
|
110 |
-
public function format_description( $desc ) {
|
111 |
-
$desc = wptexturize( $desc );
|
112 |
-
$desc = convert_smilies( $desc );
|
113 |
-
$desc = convert_chars( $desc );
|
114 |
-
$desc = wpautop( $desc );
|
115 |
-
$desc = shortcode_unautop( $desc );
|
116 |
-
$desc = capital_P_dangit( $desc );
|
117 |
-
return $desc;
|
118 |
-
}
|
119 |
-
|
120 |
-
/**
|
121 |
-
* Define the output of all columns
|
122 |
-
* that have no callback function
|
123 |
* @param object $snippet The snippet object used for the current row
|
124 |
* @param string $column_name The name of the column being printed
|
125 |
* @return string The content of the column to output
|
@@ -131,11 +124,11 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
131 |
return $snippet->id;
|
132 |
case 'description':
|
133 |
if ( ! empty( $snippet->description ) )
|
134 |
-
return
|
135 |
else
|
136 |
return '—';
|
137 |
default:
|
138 |
-
return do_action( "
|
139 |
}
|
140 |
}
|
141 |
|
@@ -155,7 +148,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
155 |
if ( $snippet->active ) {
|
156 |
$actions['deactivate'] = sprintf(
|
157 |
'<a href="%2$s">%1$s</a>',
|
158 |
-
$screen->is_network ? __('Network Deactivate', 'code-snippets') : __('Deactivate', 'code-snippets'),
|
159 |
add_query_arg( array(
|
160 |
'action' => 'deactivate',
|
161 |
'id' => $snippet->id
|
@@ -164,7 +157,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
164 |
} else {
|
165 |
$actions['activate'] = sprintf(
|
166 |
'<a href="%2$s">%1$s</a>',
|
167 |
-
$screen->is_network ? __('Network Activate', 'code-snippets') : __('Activate', 'code-snippets'),
|
168 |
add_query_arg( array(
|
169 |
'action' => 'activate',
|
170 |
'id' => $snippet->id
|
@@ -197,14 +190,19 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
197 |
esc_js( sprintf(
|
198 |
'return confirm("%s");',
|
199 |
__("You are about to permanently delete the selected item.
|
200 |
-
'Cancel' to stop, 'OK' to delete.", 'code-snippets')
|
201 |
) )
|
202 |
);
|
203 |
|
204 |
-
|
|
|
|
|
|
|
|
|
|
|
205 |
return apply_filters(
|
206 |
-
'
|
207 |
-
'<strong>'
|
208 |
$snippet
|
209 |
);
|
210 |
}
|
@@ -216,7 +214,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
216 |
*/
|
217 |
function column_cb( $snippet ) {
|
218 |
return apply_filters(
|
219 |
-
'
|
220 |
sprintf( '<input type="checkbox" name="ids[]" value="%s" />', $snippet->id ),
|
221 |
$snippet
|
222 |
);
|
@@ -228,12 +226,12 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
228 |
*/
|
229 |
function get_columns() {
|
230 |
$columns = array(
|
231 |
-
'cb'
|
232 |
-
'name'
|
233 |
-
'id'
|
234 |
-
'description' => __('Description', 'code-snippets'),
|
235 |
);
|
236 |
-
return apply_filters( '
|
237 |
}
|
238 |
|
239 |
/**
|
@@ -242,10 +240,10 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
242 |
*/
|
243 |
function get_sortable_columns() {
|
244 |
$sortable_columns = array(
|
245 |
-
'id'
|
246 |
'name' => array( 'name', false ),
|
247 |
);
|
248 |
-
return apply_filters( '
|
249 |
}
|
250 |
|
251 |
/**
|
@@ -267,13 +265,13 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
267 |
function get_bulk_actions() {
|
268 |
$screen = get_current_screen();
|
269 |
$actions = array(
|
270 |
-
'activate-selected'
|
271 |
-
'deactivate-selected' => $screen->is_network ? __('Network Deactivate', 'code-snippets') : __('Deactivate', 'code-snippets'),
|
272 |
-
'export-selected'
|
273 |
-
'delete-selected'
|
274 |
-
'export-php-selected' => __('Export to PHP', 'code-snippets'),
|
275 |
);
|
276 |
-
return apply_filters( '
|
277 |
}
|
278 |
|
279 |
/**
|
@@ -286,7 +284,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
286 |
*/
|
287 |
function get_table_classes() {
|
288 |
$classes = array( 'widefat', $this->_args['plural'] );
|
289 |
-
return apply_filters( '
|
290 |
}
|
291 |
|
292 |
/**
|
@@ -301,21 +299,22 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
301 |
|
302 |
$status_links = array();
|
303 |
foreach ( $totals as $type => $count ) {
|
304 |
-
|
|
|
305 |
continue;
|
306 |
|
307 |
switch ( $type ) {
|
308 |
case 'all':
|
309 |
-
$text = _n( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'code-snippets');
|
310 |
break;
|
311 |
case 'active':
|
312 |
-
$text = _n( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', $count, 'code-snippets');
|
313 |
break;
|
314 |
case 'recently_activated':
|
315 |
-
$text = _n( 'Recently Active <span class="count">(%s)</span>', 'Recently Active <span class="count">(%s)</span>', $count, 'code-snippets');
|
316 |
break;
|
317 |
case 'inactive':
|
318 |
-
$text = _n( 'Inactive <span class="count">(%s)</span>', 'Inactive <span class="count">(%s)</span>', $count, 'code-snippets');
|
319 |
break;
|
320 |
}
|
321 |
|
@@ -327,12 +326,12 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
327 |
|
328 |
}
|
329 |
|
330 |
-
return apply_filters( '
|
331 |
}
|
332 |
|
333 |
/**
|
334 |
* Add filters and extra actions above and below the table
|
335 |
-
* @param
|
336 |
*/
|
337 |
function extra_tablenav( $which ) {
|
338 |
global $status, $code_snippets;
|
@@ -340,22 +339,21 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
340 |
$screen = get_current_screen();
|
341 |
|
342 |
if ( 'top' === $which && has_action( 'code_snippets_list_table_filter_controls' ) ) {
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
<?php
|
351 |
}
|
352 |
|
353 |
echo '<div class="alignleft actions">';
|
354 |
|
355 |
if ( 'recently_activated' === $status )
|
356 |
-
submit_button( __('Clear List', 'code-snippets'), 'secondary', 'clear-recent-list', false );
|
357 |
|
358 |
-
do_action( '
|
359 |
|
360 |
echo '</div>';
|
361 |
}
|
@@ -364,14 +362,18 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
364 |
* Output form fields needed to preserve important
|
365 |
* query vars over form submissions
|
366 |
*
|
367 |
-
* @param
|
368 |
*/
|
369 |
function required_form_fields( $context = 'main' ) {
|
370 |
|
371 |
-
$vars = apply_filters(
|
|
|
|
|
|
|
|
|
372 |
|
373 |
if ( 'search_box' === $context ) {
|
374 |
-
|
375 |
$vars = array_diff( $vars, array( 's' ) );
|
376 |
}
|
377 |
|
@@ -382,7 +384,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
382 |
}
|
383 |
}
|
384 |
|
385 |
-
do_action( '
|
386 |
}
|
387 |
|
388 |
|
@@ -395,30 +397,28 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
395 |
$action = 'clear-recent-list';
|
396 |
else
|
397 |
$action = parent::current_action();
|
398 |
-
return apply_filters( '
|
399 |
}
|
400 |
|
401 |
/**
|
402 |
* Processes a bulk action
|
403 |
*
|
404 |
-
* @uses $code_snippets->activate()
|
405 |
-
* @uses $code_snippets->deactivate()
|
406 |
* @uses $code_snippets->delete_snippet() To delete snippets
|
407 |
-
* @uses $code_snippets->export()
|
408 |
-
* @uses wp_redirect
|
409 |
-
* @uses add_query_arg()
|
410 |
*/
|
411 |
function process_bulk_actions() {
|
412 |
global $code_snippets;
|
413 |
|
414 |
if ( isset( $_GET['action'], $_GET['id'] ) ) :
|
415 |
|
416 |
-
$id
|
417 |
-
|
418 |
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'action', 'id' ) );
|
419 |
|
420 |
-
$action = sanitize_key( $_GET['action'] );
|
421 |
-
|
422 |
if ( 'activate' === $action ) {
|
423 |
$code_snippets->activate( $id );
|
424 |
}
|
@@ -435,16 +435,17 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
435 |
$code_snippets->export_php( $id );
|
436 |
}
|
437 |
|
438 |
-
if (
|
439 |
wp_redirect( apply_filters(
|
440 |
-
"
|
441 |
add_query_arg( $action, true )
|
442 |
) );
|
443 |
}
|
444 |
|
445 |
endif;
|
446 |
|
447 |
-
if ( ! isset( $_POST['ids'] ) )
|
|
|
448 |
|
449 |
$ids = $_POST['ids'];
|
450 |
|
@@ -492,16 +493,14 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
492 |
*/
|
493 |
function no_items() {
|
494 |
global $code_snippets;
|
495 |
-
printf( __('You do not appear to have any snippets available at this time. <a href="%s">Add New→</a>', 'code-snippets'), $code_snippets->
|
496 |
}
|
497 |
|
498 |
/**
|
499 |
-
* Prepares the items to later display in the table
|
500 |
-
*
|
501 |
-
* Should run before any headers are sent
|
502 |
*/
|
503 |
function prepare_items() {
|
504 |
-
|
505 |
global $code_snippets, $status, $snippets, $totals, $page, $orderby, $order, $s;
|
506 |
|
507 |
wp_reset_vars( array( 'orderby', 'order', 's' ) );
|
@@ -509,17 +508,17 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
509 |
$screen = get_current_screen();
|
510 |
$user = get_current_user_id();
|
511 |
|
512 |
-
|
513 |
$this->process_bulk_actions();
|
514 |
|
515 |
$snippets = array(
|
516 |
-
'all' => apply_filters( '
|
517 |
'active' => array(),
|
518 |
'inactive' => array(),
|
519 |
'recently_activated' => array(),
|
520 |
);
|
521 |
|
522 |
-
|
523 |
if ( $s ) {
|
524 |
$snippets['all'] = array_filter( $snippets[ 'all' ], array( &$this, '_search_callback' ) );
|
525 |
}
|
@@ -540,7 +539,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
540 |
update_option( 'recently_activated_snippets', $recently_activated );
|
541 |
|
542 |
foreach ( (array) $snippets['all'] as $snippet ) {
|
543 |
-
|
544 |
if ( $snippet->active ) {
|
545 |
$snippets['active'][] = $snippet;
|
546 |
} else {
|
@@ -564,10 +563,9 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
564 |
* by getting the user's setting in the Screen Options
|
565 |
* panel.
|
566 |
*/
|
567 |
-
$sort_by
|
568 |
$screen_option = $screen->get_option( 'per_page', 'option' );
|
569 |
-
|
570 |
-
$per_page = get_user_meta( $user, $screen_option, true );
|
571 |
|
572 |
if ( empty ( $per_page ) || $per_page < 1 ) {
|
573 |
$per_page = $screen->get_option( 'per_page', 'default' );
|
@@ -584,51 +582,59 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
584 |
*/
|
585 |
function usort_reorder( $a, $b ) {
|
586 |
|
587 |
-
|
588 |
-
$orderby = (
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
594 |
if ( 'id' === $orderby )
|
595 |
$result = $a->$orderby - $b->$orderby; // get the result for numerical data
|
596 |
else
|
597 |
$result = strcmp( $a->$orderby, $b->$orderby ); // get the result for string data
|
598 |
|
599 |
-
|
600 |
return ( 'asc' === $order ) ? $result : -$result;
|
601 |
}
|
602 |
|
603 |
-
usort($data, 'usort_reorder');
|
604 |
|
605 |
|
606 |
-
|
607 |
* Let's figure out what page the user is currently
|
608 |
* looking at.
|
609 |
*/
|
610 |
$current_page = $this->get_pagenum();
|
611 |
|
612 |
-
|
613 |
* Let's check how many items are in our data array.
|
614 |
*/
|
615 |
-
$total_items = count($data);
|
616 |
|
617 |
|
618 |
-
|
619 |
* The WP_List_Table class does not handle pagination for us, so we need
|
620 |
* to ensure that the data is trimmed to only the current page.
|
621 |
*/
|
622 |
$data = array_slice( $data, ( ( $current_page - 1 ) * $per_page ), $per_page );
|
623 |
|
624 |
|
625 |
-
|
626 |
* Now we can add our *sorted* data to the items property, where
|
627 |
* it can be used by the rest of the class.
|
628 |
*/
|
629 |
$this->items = $data;
|
630 |
|
631 |
-
|
632 |
* We also have to register our pagination options & calculations.
|
633 |
*/
|
634 |
$this->set_pagination_args( array(
|
@@ -665,23 +671,28 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
665 |
/**
|
666 |
* Display a notice showing the current search terms
|
667 |
*
|
668 |
-
* @since
|
669 |
* @access public
|
670 |
*/
|
671 |
public function search_notice() {
|
672 |
-
if ( ! empty( $_REQUEST['s'] ) || apply_filters( '
|
673 |
|
674 |
-
echo '<span class="subtitle">' . __('Search results', 'code-snippets');
|
675 |
|
676 |
if ( ! empty ( $_REQUEST['s'] ) )
|
677 |
echo sprintf ( __( ' for “%s”', 'code-snippets' ), esc_html( $_REQUEST['s'] ) );
|
678 |
|
679 |
-
echo apply_filters( '
|
680 |
echo '</span>';
|
681 |
|
682 |
printf (
|
683 |
-
' <a class="button" href="%s">' . __('Clear Filters', 'code-snippets') . '</a>',
|
684 |
-
remove_query_arg(
|
|
|
|
|
|
|
|
|
|
|
685 |
);
|
686 |
}
|
687 |
}
|
@@ -693,9 +704,9 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
693 |
function single_row( $snippet ) {
|
694 |
static $row_class = '';
|
695 |
$row_class = ( $snippet->active ? 'active' : 'inactive' );
|
696 |
-
|
697 |
-
|
698 |
-
echo $this->single_row_columns( $snippet );
|
699 |
echo '</tr>';
|
700 |
}
|
701 |
-
|
|
3 |
/**
|
4 |
* Contains the class for handling the administration interface
|
5 |
*
|
6 |
+
* @package Code_Snippets
|
7 |
* @subpackage Administration
|
8 |
*/
|
9 |
|
10 |
+
/* The WP_List_Table base class is not included by default, so we need to load it */
|
11 |
if ( ! class_exists( 'WP_List_Table' ) ) {
|
12 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
13 |
}
|
14 |
|
15 |
/**
|
16 |
* This class handles the table for the manage snippets menu
|
17 |
*
|
18 |
+
* @since 1.5
|
19 |
+
* @access private
|
20 |
+
* @package Code_Snippets
|
21 |
*/
|
22 |
class Code_Snippets_List_Table extends WP_List_Table {
|
23 |
|
24 |
/**#@+
|
25 |
+
* @since 1.5
|
26 |
* @access private
|
27 |
*/
|
28 |
|
29 |
/**
|
30 |
+
* The constructor function for our class.
|
31 |
+
* Adds hooks, initializes variables, setups class.
|
|
|
32 |
*/
|
33 |
function __construct() {
|
34 |
global $status, $page, $code_snippets;
|
42 |
if ( isset( $_REQUEST['s'] ) )
|
43 |
$_SERVER['REQUEST_URI'] = add_query_arg( 's', stripslashes($_REQUEST['s'] ) );
|
44 |
|
45 |
+
|
46 |
$page = $this->get_pagenum();
|
47 |
|
48 |
add_screen_option( 'per_page', array(
|
49 |
+
'label' => __( 'Snippets per page', 'code-snippets' ),
|
50 |
'default' => 10,
|
51 |
'option' => 'snippets_per_page'
|
52 |
) );
|
53 |
|
54 |
+
/* Set the table columns hidden in Screen Options by default */
|
55 |
add_filter( "get_user_option_manage{$screen->id}columnshidden", array( $this, 'get_default_hidden_columns' ), 15 );
|
56 |
+
|
57 |
+
/* Load custom stylesheets */
|
58 |
add_action( 'admin_enqueue_scripts', array( $this, 'load_table_style' ) );
|
59 |
|
60 |
+
/* Strip once-off query args from the URL */
|
61 |
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'activate', 'activate-multi', 'deactivate', 'deactivate-multi', 'delete', 'delete-multi' ) );
|
62 |
|
63 |
+
/* Add filters to format the snippet description in the same way the post content is formatted */
|
64 |
+
$filters = array( 'wptexturize', 'convert_smilies', 'convert_chars', 'wpautop', 'shortcode_unautop', 'capital_P_dangit' );
|
65 |
+
|
66 |
+
foreach ( $filters as $filter ) {
|
67 |
+
add_filter( 'code_snippets/list_table/print_snippet_description', $filter );
|
68 |
+
}
|
69 |
+
|
70 |
+
/* Setup the class */
|
71 |
parent::__construct( array(
|
72 |
'singular' => 'snippet',
|
73 |
'plural' => 'snippets',
|
78 |
/**
|
79 |
* Enqueue the table stylesheet
|
80 |
*
|
81 |
+
* @since 1.6
|
82 |
+
* @uses wp_enqueue_style() To add the stylesheet to the queue
|
83 |
+
* @param string $hook The current page hook, to be compared with the manage snippets page hook
|
84 |
+
* @return void
|
|
|
85 |
*/
|
86 |
function load_table_style( $hook ) {
|
87 |
global $code_snippets;
|
88 |
|
89 |
+
/* Only load the stylesheet on the manage snippets page */
|
90 |
if ( $hook !== $code_snippets->admin->manage_page )
|
91 |
return;
|
92 |
|
93 |
+
/* Load a different stylesheet if MP6 is active */
|
94 |
if ( 'mp6' === get_user_option( 'admin_color' ) ) {
|
95 |
|
96 |
wp_enqueue_style(
|
112 |
}
|
113 |
|
114 |
/**
|
115 |
+
* Define the output of all columns that have no callback function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
* @param object $snippet The snippet object used for the current row
|
117 |
* @param string $column_name The name of the column being printed
|
118 |
* @return string The content of the column to output
|
124 |
return $snippet->id;
|
125 |
case 'description':
|
126 |
if ( ! empty( $snippet->description ) )
|
127 |
+
return apply_filters( 'code_snippets/list_table/print_snippet_description', $snippet->description );
|
128 |
else
|
129 |
return '—';
|
130 |
default:
|
131 |
+
return do_action( "code_snippets/list_table/column_{$column_name}", $snippet );
|
132 |
}
|
133 |
}
|
134 |
|
148 |
if ( $snippet->active ) {
|
149 |
$actions['deactivate'] = sprintf(
|
150 |
'<a href="%2$s">%1$s</a>',
|
151 |
+
$screen->is_network ? __( 'Network Deactivate', 'code-snippets' ) : __( 'Deactivate', 'code-snippets' ),
|
152 |
add_query_arg( array(
|
153 |
'action' => 'deactivate',
|
154 |
'id' => $snippet->id
|
157 |
} else {
|
158 |
$actions['activate'] = sprintf(
|
159 |
'<a href="%2$s">%1$s</a>',
|
160 |
+
$screen->is_network ? __( 'Network Activate', 'code-snippets' ) : __( 'Activate', 'code-snippets' ),
|
161 |
add_query_arg( array(
|
162 |
'action' => 'activate',
|
163 |
'id' => $snippet->id
|
190 |
esc_js( sprintf(
|
191 |
'return confirm("%s");',
|
192 |
__("You are about to permanently delete the selected item.
|
193 |
+
'Cancel' to stop, 'OK' to delete.", 'code-snippets' )
|
194 |
) )
|
195 |
);
|
196 |
|
197 |
+
if ( ! empty( $snippet->name ) )
|
198 |
+
$title = $snippet->name;
|
199 |
+
else
|
200 |
+
$title = sprintf ( __( 'Untitled #%d', 'code-snippets' ), $snippet->id );
|
201 |
+
|
202 |
+
/* Return the name contents */
|
203 |
return apply_filters(
|
204 |
+
'code_snippets/list_table/column_name',
|
205 |
+
sprintf ( '<strong>%s</strong>', $title ) . $this->row_actions( $actions, true ),
|
206 |
$snippet
|
207 |
);
|
208 |
}
|
214 |
*/
|
215 |
function column_cb( $snippet ) {
|
216 |
return apply_filters(
|
217 |
+
'code_snippets/list_table/column_cb',
|
218 |
sprintf( '<input type="checkbox" name="ids[]" value="%s" />', $snippet->id ),
|
219 |
$snippet
|
220 |
);
|
226 |
*/
|
227 |
function get_columns() {
|
228 |
$columns = array(
|
229 |
+
'cb' => '<input type="checkbox" />',
|
230 |
+
'name' => __( 'Name', 'code-snippets' ),
|
231 |
+
'id' => __( 'ID', 'code-snippets' ),
|
232 |
+
'description' => __( 'Description', 'code-snippets' ),
|
233 |
);
|
234 |
+
return apply_filters( 'code_snippets/list_table/columns', $columns );
|
235 |
}
|
236 |
|
237 |
/**
|
240 |
*/
|
241 |
function get_sortable_columns() {
|
242 |
$sortable_columns = array(
|
243 |
+
'id' => array( 'id', true ),
|
244 |
'name' => array( 'name', false ),
|
245 |
);
|
246 |
+
return apply_filters( 'code_snippets/list_table/sortable_columns', $sortable_columns );
|
247 |
}
|
248 |
|
249 |
/**
|
265 |
function get_bulk_actions() {
|
266 |
$screen = get_current_screen();
|
267 |
$actions = array(
|
268 |
+
'activate-selected' => $screen->is_network ? __( 'Network Activate', 'code-snippets' ) : __( 'Activate', 'code-snippets' ),
|
269 |
+
'deactivate-selected' => $screen->is_network ? __( 'Network Deactivate', 'code-snippets' ) : __( 'Deactivate', 'code-snippets' ),
|
270 |
+
'export-selected' => __( 'Export', 'code-snippets' ),
|
271 |
+
'delete-selected' => __( 'Delete', 'code-snippets' ),
|
272 |
+
'export-php-selected' => __( 'Export to PHP', 'code-snippets' ),
|
273 |
);
|
274 |
+
return apply_filters( 'code_snippets/list_table/bulk_actions', $actions );
|
275 |
}
|
276 |
|
277 |
/**
|
284 |
*/
|
285 |
function get_table_classes() {
|
286 |
$classes = array( 'widefat', $this->_args['plural'] );
|
287 |
+
return apply_filters( 'code_snippets/list_table/table_classes', $classes );
|
288 |
}
|
289 |
|
290 |
/**
|
299 |
|
300 |
$status_links = array();
|
301 |
foreach ( $totals as $type => $count ) {
|
302 |
+
|
303 |
+
if ( ! $count )
|
304 |
continue;
|
305 |
|
306 |
switch ( $type ) {
|
307 |
case 'all':
|
308 |
+
$text = _n( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'code-snippets' );
|
309 |
break;
|
310 |
case 'active':
|
311 |
+
$text = _n( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', $count, 'code-snippets' );
|
312 |
break;
|
313 |
case 'recently_activated':
|
314 |
+
$text = _n( 'Recently Active <span class="count">(%s)</span>', 'Recently Active <span class="count">(%s)</span>', $count, 'code-snippets' );
|
315 |
break;
|
316 |
case 'inactive':
|
317 |
+
$text = _n( 'Inactive <span class="count">(%s)</span>', 'Inactive <span class="count">(%s)</span>', $count, 'code-snippets' );
|
318 |
break;
|
319 |
}
|
320 |
|
326 |
|
327 |
}
|
328 |
|
329 |
+
return apply_filters( 'code_snippets/list_table/views', $status_links );
|
330 |
}
|
331 |
|
332 |
/**
|
333 |
* Add filters and extra actions above and below the table
|
334 |
+
* @param string $which Are the actions displayed on the table top or bottom
|
335 |
*/
|
336 |
function extra_tablenav( $which ) {
|
337 |
global $status, $code_snippets;
|
339 |
$screen = get_current_screen();
|
340 |
|
341 |
if ( 'top' === $which && has_action( 'code_snippets_list_table_filter_controls' ) ) {
|
342 |
+
|
343 |
+
echo '<div class="alignleft actions">';
|
344 |
+
|
345 |
+
do_action( 'code_snippets/list_table/filter_controls' );
|
346 |
+
submit_button( __( 'Filter', 'code-snippets' ), 'button', false, false );
|
347 |
+
|
348 |
+
echo '</div>';
|
|
|
349 |
}
|
350 |
|
351 |
echo '<div class="alignleft actions">';
|
352 |
|
353 |
if ( 'recently_activated' === $status )
|
354 |
+
submit_button( __( 'Clear List', 'code-snippets' ), 'secondary', 'clear-recent-list', false );
|
355 |
|
356 |
+
do_action( 'code_snippets/list_table/actions', $which );
|
357 |
|
358 |
echo '</div>';
|
359 |
}
|
362 |
* Output form fields needed to preserve important
|
363 |
* query vars over form submissions
|
364 |
*
|
365 |
+
* @param string $context In what context are the fields being outputted?
|
366 |
*/
|
367 |
function required_form_fields( $context = 'main' ) {
|
368 |
|
369 |
+
$vars = apply_filters(
|
370 |
+
'code_snippets/list_table/required_form_fields',
|
371 |
+
array( 'page', 's', 'status', 'paged' ),
|
372 |
+
$context
|
373 |
+
);
|
374 |
|
375 |
if ( 'search_box' === $context ) {
|
376 |
+
/* Remove the 's' var if we're doing this for the search box */
|
377 |
$vars = array_diff( $vars, array( 's' ) );
|
378 |
}
|
379 |
|
384 |
}
|
385 |
}
|
386 |
|
387 |
+
do_action( 'code_snippets/list_table/print_required_form_fields', $context );
|
388 |
}
|
389 |
|
390 |
|
397 |
$action = 'clear-recent-list';
|
398 |
else
|
399 |
$action = parent::current_action();
|
400 |
+
return apply_filters( 'code_snippets/list_table/current_action', $action );
|
401 |
}
|
402 |
|
403 |
/**
|
404 |
* Processes a bulk action
|
405 |
*
|
406 |
+
* @uses $code_snippets->activate() To activate snippets
|
407 |
+
* @uses $code_snippets->deactivate() To deactivate snippets
|
408 |
* @uses $code_snippets->delete_snippet() To delete snippets
|
409 |
+
* @uses $code_snippets->export() To export selected snippets
|
410 |
+
* @uses wp_redirect() To pass the results to the current page
|
411 |
+
* @uses add_query_arg() To append the results to the current URI
|
412 |
*/
|
413 |
function process_bulk_actions() {
|
414 |
global $code_snippets;
|
415 |
|
416 |
if ( isset( $_GET['action'], $_GET['id'] ) ) :
|
417 |
|
418 |
+
$id = absint( $_GET['id'] );
|
419 |
+
$action = sanitize_key( $_GET['action'] );
|
420 |
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'action', 'id' ) );
|
421 |
|
|
|
|
|
422 |
if ( 'activate' === $action ) {
|
423 |
$code_snippets->activate( $id );
|
424 |
}
|
435 |
$code_snippets->export_php( $id );
|
436 |
}
|
437 |
|
438 |
+
if ( ! in_array( $action, array( 'export', 'export-php' ) ) ) {
|
439 |
wp_redirect( apply_filters(
|
440 |
+
"code_snippets/{$action}_redirect",
|
441 |
add_query_arg( $action, true )
|
442 |
) );
|
443 |
}
|
444 |
|
445 |
endif;
|
446 |
|
447 |
+
if ( ! isset( $_POST['ids'] ) )
|
448 |
+
return;
|
449 |
|
450 |
$ids = $_POST['ids'];
|
451 |
|
493 |
*/
|
494 |
function no_items() {
|
495 |
global $code_snippets;
|
496 |
+
printf( __( 'You do not appear to have any snippets available at this time. <a href="%s">Add New→</a>', 'code-snippets' ), $code_snippets->admin->single_url );
|
497 |
}
|
498 |
|
499 |
/**
|
500 |
+
* Prepares the items to later display in the table.
|
501 |
+
* Should run before any headers are sent.
|
|
|
502 |
*/
|
503 |
function prepare_items() {
|
|
|
504 |
global $code_snippets, $status, $snippets, $totals, $page, $orderby, $order, $s;
|
505 |
|
506 |
wp_reset_vars( array( 'orderby', 'order', 's' ) );
|
508 |
$screen = get_current_screen();
|
509 |
$user = get_current_user_id();
|
510 |
|
511 |
+
/* First, lets process the bulk actions */
|
512 |
$this->process_bulk_actions();
|
513 |
|
514 |
$snippets = array(
|
515 |
+
'all' => apply_filters( 'code_snippets/list_table/get_snippets', $code_snippets->get_snippets() ),
|
516 |
'active' => array(),
|
517 |
'inactive' => array(),
|
518 |
'recently_activated' => array(),
|
519 |
);
|
520 |
|
521 |
+
/* Filter snippets based on search query */
|
522 |
if ( $s ) {
|
523 |
$snippets['all'] = array_filter( $snippets[ 'all' ], array( &$this, '_search_callback' ) );
|
524 |
}
|
539 |
update_option( 'recently_activated_snippets', $recently_activated );
|
540 |
|
541 |
foreach ( (array) $snippets['all'] as $snippet ) {
|
542 |
+
/* Filter into individual sections */
|
543 |
if ( $snippet->active ) {
|
544 |
$snippets['active'][] = $snippet;
|
545 |
} else {
|
563 |
* by getting the user's setting in the Screen Options
|
564 |
* panel.
|
565 |
*/
|
566 |
+
$sort_by = $screen->get_option( 'per_page', 'option' );
|
567 |
$screen_option = $screen->get_option( 'per_page', 'option' );
|
568 |
+
$per_page = get_user_meta( $user, $screen_option, true );
|
|
|
569 |
|
570 |
if ( empty ( $per_page ) || $per_page < 1 ) {
|
571 |
$per_page = $screen->get_option( 'per_page', 'default' );
|
582 |
*/
|
583 |
function usort_reorder( $a, $b ) {
|
584 |
|
585 |
+
/* If no sort, default to ID */
|
586 |
+
$orderby = (
|
587 |
+
! empty( $_REQUEST['orderby'] )
|
588 |
+
? $_REQUEST['orderby']
|
589 |
+
: apply_filters( 'code_snippets/list_table/default_orderby', 'id' )
|
590 |
+
);
|
591 |
+
|
592 |
+
/* If no order, default to ascending */
|
593 |
+
$order = (
|
594 |
+
! empty( $_REQUEST['order'] )
|
595 |
+
? $_REQUEST['order']
|
596 |
+
: apply_filters( 'code_snippets/list_table/default_order', 'asc' )
|
597 |
+
);
|
598 |
+
|
599 |
+
/* Determine sort order */
|
600 |
if ( 'id' === $orderby )
|
601 |
$result = $a->$orderby - $b->$orderby; // get the result for numerical data
|
602 |
else
|
603 |
$result = strcmp( $a->$orderby, $b->$orderby ); // get the result for string data
|
604 |
|
605 |
+
/* Send final sort direction to usort */
|
606 |
return ( 'asc' === $order ) ? $result : -$result;
|
607 |
}
|
608 |
|
609 |
+
usort( $data, 'usort_reorder' );
|
610 |
|
611 |
|
612 |
+
/*
|
613 |
* Let's figure out what page the user is currently
|
614 |
* looking at.
|
615 |
*/
|
616 |
$current_page = $this->get_pagenum();
|
617 |
|
618 |
+
/*
|
619 |
* Let's check how many items are in our data array.
|
620 |
*/
|
621 |
+
$total_items = count( $data );
|
622 |
|
623 |
|
624 |
+
/*
|
625 |
* The WP_List_Table class does not handle pagination for us, so we need
|
626 |
* to ensure that the data is trimmed to only the current page.
|
627 |
*/
|
628 |
$data = array_slice( $data, ( ( $current_page - 1 ) * $per_page ), $per_page );
|
629 |
|
630 |
|
631 |
+
/*
|
632 |
* Now we can add our *sorted* data to the items property, where
|
633 |
* it can be used by the rest of the class.
|
634 |
*/
|
635 |
$this->items = $data;
|
636 |
|
637 |
+
/*
|
638 |
* We also have to register our pagination options & calculations.
|
639 |
*/
|
640 |
$this->set_pagination_args( array(
|
671 |
/**
|
672 |
* Display a notice showing the current search terms
|
673 |
*
|
674 |
+
* @since 1.7
|
675 |
* @access public
|
676 |
*/
|
677 |
public function search_notice() {
|
678 |
+
if ( ! empty( $_REQUEST['s'] ) || apply_filters( 'code_snippets/list_table/search_notice', '' ) ) {
|
679 |
|
680 |
+
echo '<span class="subtitle">' . __( 'Search results', 'code-snippets' );
|
681 |
|
682 |
if ( ! empty ( $_REQUEST['s'] ) )
|
683 |
echo sprintf ( __( ' for “%s”', 'code-snippets' ), esc_html( $_REQUEST['s'] ) );
|
684 |
|
685 |
+
echo apply_filters( 'code_snippets/list_table/search_notice', '' );
|
686 |
echo '</span>';
|
687 |
|
688 |
printf (
|
689 |
+
' <a class="button" href="%s">' . __( 'Clear Filters', 'code-snippets' ) . '</a>',
|
690 |
+
remove_query_arg(
|
691 |
+
apply_filters( 'code_snippets/list_table/required_form_fields',
|
692 |
+
array( 's' ),
|
693 |
+
'clear_filters'
|
694 |
+
)
|
695 |
+
)
|
696 |
);
|
697 |
}
|
698 |
}
|
704 |
function single_row( $snippet ) {
|
705 |
static $row_class = '';
|
706 |
$row_class = ( $snippet->active ? 'active' : 'inactive' );
|
707 |
+
printf ( '<tr class="%s">', $row_class );
|
708 |
+
$this->single_row_columns( $snippet );
|
|
|
709 |
echo '</tr>';
|
710 |
}
|
711 |
+
|
712 |
+
} // end of class
|
includes/export.php
CHANGED
@@ -7,8 +7,8 @@
|
|
7 |
* and $code_snippets->export_php() methods then
|
8 |
* directly use those in this file
|
9 |
*
|
10 |
-
* @package
|
11 |
-
* @subpackage
|
12 |
*/
|
13 |
|
14 |
if ( ! function_exists( 'code_snippets_export' ) ) :
|
@@ -16,10 +16,9 @@ if ( ! function_exists( 'code_snippets_export' ) ) :
|
|
16 |
/**
|
17 |
* Exports selected snippets to a XML or PHP file.
|
18 |
*
|
19 |
-
* @since
|
20 |
-
*
|
21 |
-
* @param
|
22 |
-
* @param string $format The format of the export file
|
23 |
* @return void
|
24 |
*/
|
25 |
function code_snippets_export( $ids, $format = 'xml' ) {
|
@@ -28,15 +27,19 @@ function code_snippets_export( $ids, $format = 'xml' ) {
|
|
28 |
$ids = (array) $ids;
|
29 |
|
30 |
if ( 1 === count( $ids ) ) {
|
31 |
-
|
32 |
-
$
|
33 |
-
$sitename = strtolower( $
|
34 |
} else {
|
35 |
-
|
36 |
$sitename = strtolower( get_bloginfo( 'name' ) );
|
37 |
}
|
38 |
|
39 |
-
$filename = sanitize_file_name( apply_filters(
|
|
|
|
|
|
|
|
|
40 |
|
41 |
/* Apply the file headers */
|
42 |
|
@@ -49,7 +52,7 @@ function code_snippets_export( $ids, $format = 'xml' ) {
|
|
49 |
|
50 |
?>
|
51 |
<!-- This is a code snippets export file generated by the Code Snippets WordPress plugin. -->
|
52 |
-
<!-- http://wordpress.org/
|
53 |
|
54 |
<!-- To import these snippets a WordPress site follow these steps: -->
|
55 |
<!-- 1. Log in to that site as an administrator. -->
|
@@ -63,12 +66,16 @@ function code_snippets_export( $ids, $format = 'xml' ) {
|
|
63 |
|
64 |
<?php
|
65 |
|
66 |
-
|
67 |
-
$gen
|
|
|
|
|
|
|
|
|
68 |
$type = 'code_snippets_export';
|
69 |
echo apply_filters( "get_the_generator_$type", $gen, $type );
|
70 |
|
71 |
-
|
72 |
echo "\n<snippets>";
|
73 |
|
74 |
} elseif ( 'php' === $format ) {
|
@@ -77,19 +84,19 @@ function code_snippets_export( $ids, $format = 'xml' ) {
|
|
77 |
|
78 |
}
|
79 |
|
80 |
-
do_action( '
|
81 |
|
82 |
/* Loop through the snippets */
|
83 |
|
84 |
-
$table
|
85 |
-
$exclude = apply_filters( '
|
86 |
|
87 |
-
foreach( $ids as $id ) {
|
88 |
|
89 |
-
|
90 |
$snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $id ), ARRAY_A );
|
91 |
|
92 |
-
|
93 |
$snippet = stripslashes_deep( $snippet );
|
94 |
|
95 |
if ( 'xml' === $format ) {
|
@@ -98,11 +105,12 @@ function code_snippets_export( $ids, $format = 'xml' ) {
|
|
98 |
|
99 |
foreach ( $snippet as $field => $value ) {
|
100 |
|
101 |
-
|
102 |
-
if ( in_array( $field, $exclude ) )
|
|
|
103 |
|
104 |
-
|
105 |
-
if ( $value = apply_filters( "
|
106 |
echo "\n\t\t<$field>$value</$field>";
|
107 |
}
|
108 |
echo "\n\t" . '</snippet>';
|
@@ -113,7 +121,7 @@ function code_snippets_export( $ids, $format = 'xml' ) {
|
|
113 |
|
114 |
if ( ! empty( $snippet['description'] ) ) {
|
115 |
|
116 |
-
|
117 |
$desc = strip_tags( str_replace( "\n", "\n * ", $snippet['description'] ) );
|
118 |
|
119 |
echo " *\n * $desc\n";
|
@@ -123,7 +131,7 @@ function code_snippets_export( $ids, $format = 'xml' ) {
|
|
123 |
}
|
124 |
}
|
125 |
|
126 |
-
do_action( '
|
127 |
|
128 |
/* Finish off the file */
|
129 |
|
@@ -137,7 +145,7 @@ function code_snippets_export( $ids, $format = 'xml' ) {
|
|
137 |
|
138 |
}
|
139 |
|
140 |
-
do_action( '
|
141 |
|
142 |
exit;
|
143 |
}
|
7 |
* and $code_snippets->export_php() methods then
|
8 |
* directly use those in this file
|
9 |
*
|
10 |
+
* @package Code_Snippets
|
11 |
+
* @subpackage Functions
|
12 |
*/
|
13 |
|
14 |
if ( ! function_exists( 'code_snippets_export' ) ) :
|
16 |
/**
|
17 |
* Exports selected snippets to a XML or PHP file.
|
18 |
*
|
19 |
+
* @since 1.3
|
20 |
+
* @param array $ids The IDs of the snippets to export
|
21 |
+
* @param string $format The format of the export file
|
|
|
22 |
* @return void
|
23 |
*/
|
24 |
function code_snippets_export( $ids, $format = 'xml' ) {
|
27 |
$ids = (array) $ids;
|
28 |
|
29 |
if ( 1 === count( $ids ) ) {
|
30 |
+
/* If there is only snippet to export, use its name instead of the site name */
|
31 |
+
$snippet = $code_snippets->get_snippet( $ids );
|
32 |
+
$sitename = strtolower( $snippet->name );
|
33 |
} else {
|
34 |
+
/* Otherwise, use the site name as set in Settings > General */
|
35 |
$sitename = strtolower( get_bloginfo( 'name' ) );
|
36 |
}
|
37 |
|
38 |
+
$filename = sanitize_file_name( apply_filters(
|
39 |
+
'code_snippets/export/filename',
|
40 |
+
"{$sitename}.code-snippets.{$format}",
|
41 |
+
$format, $sitename
|
42 |
+
) );
|
43 |
|
44 |
/* Apply the file headers */
|
45 |
|
52 |
|
53 |
?>
|
54 |
<!-- This is a code snippets export file generated by the Code Snippets WordPress plugin. -->
|
55 |
+
<!-- http://wordpress.org/plugins/code-snippets -->
|
56 |
|
57 |
<!-- To import these snippets a WordPress site follow these steps: -->
|
58 |
<!-- 1. Log in to that site as an administrator. -->
|
66 |
|
67 |
<?php
|
68 |
|
69 |
+
/* Run the generator line through the standard WordPress filter */
|
70 |
+
$gen = sprinf (
|
71 |
+
'<!-- generator="Code Snippets/%s" created="%s" -->',
|
72 |
+
$code_snippets->version,
|
73 |
+
date('Y-m-d H:i')
|
74 |
+
);
|
75 |
$type = 'code_snippets_export';
|
76 |
echo apply_filters( "get_the_generator_$type", $gen, $type );
|
77 |
|
78 |
+
/* Start the XML section */
|
79 |
echo "\n<snippets>";
|
80 |
|
81 |
} elseif ( 'php' === $format ) {
|
84 |
|
85 |
}
|
86 |
|
87 |
+
do_action( 'code_snippets/export/after_header', $format, $ids, $filename );
|
88 |
|
89 |
/* Loop through the snippets */
|
90 |
|
91 |
+
$table = $code_snippets->get_table_name();
|
92 |
+
$exclude = apply_filters( 'code_snippets/export/exclude_from_export', array( 'id', 'active' ) );
|
93 |
|
94 |
+
foreach ( $ids as $id ) {
|
95 |
|
96 |
+
/* Grab the snippet from the database */
|
97 |
$snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $id ), ARRAY_A );
|
98 |
|
99 |
+
/* Remove slashes */
|
100 |
$snippet = stripslashes_deep( $snippet );
|
101 |
|
102 |
if ( 'xml' === $format ) {
|
105 |
|
106 |
foreach ( $snippet as $field => $value ) {
|
107 |
|
108 |
+
/* Don't export certain fields */
|
109 |
+
if ( in_array( $field, $exclude ) )
|
110 |
+
continue;
|
111 |
|
112 |
+
/* Output the field and value as indented XML */
|
113 |
+
if ( $value = apply_filters( "code_snippets/export/$field", $value ) )
|
114 |
echo "\n\t\t<$field>$value</$field>";
|
115 |
}
|
116 |
echo "\n\t" . '</snippet>';
|
121 |
|
122 |
if ( ! empty( $snippet['description'] ) ) {
|
123 |
|
124 |
+
/* Convert description to PhpDoc */
|
125 |
$desc = strip_tags( str_replace( "\n", "\n * ", $snippet['description'] ) );
|
126 |
|
127 |
echo " *\n * $desc\n";
|
131 |
}
|
132 |
}
|
133 |
|
134 |
+
do_action( 'code_snippets/export/after_snippets', $format, $id, $filename );
|
135 |
|
136 |
/* Finish off the file */
|
137 |
|
145 |
|
146 |
}
|
147 |
|
148 |
+
do_action( 'code_snippets/export/after_footer', $format, $ids, $filename );
|
149 |
|
150 |
exit;
|
151 |
}
|
includes/functions.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Global functions which interact with the code snippets plugin
|
5 |
+
*
|
6 |
+
* @package Code_Snippets
|
7 |
+
* @subpackage Functions
|
8 |
+
*/
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Add submenu page to the snippets main menu.
|
12 |
+
*
|
13 |
+
* @param string $page_title The text to be displayed in the title tags of the page when the menu is selected
|
14 |
+
* @param string $menu_title The text to be used for the menu
|
15 |
+
* @param string $capability The capability required for this menu to be displayed to the user.
|
16 |
+
* @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu)
|
17 |
+
* @param callback $function The function to be called to output the content for this page.
|
18 |
+
* @return string|bool The resulting page's hook_suffix, or false if the user does not have the capability required.
|
19 |
+
*/
|
20 |
+
function add_snippets_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
|
21 |
+
global $code_snippets;
|
22 |
+
return add_submenu_page( $code_snippets->admin->manage_page, $page_title, $menu_title, $capability, $menu_slug, $function );
|
23 |
+
}
|
includes/help/import.php
DELETED
@@ -1,41 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Register and handle the help tabs for the
|
5 |
-
* import snippets admin page
|
6 |
-
*
|
7 |
-
* @package Code Snippets
|
8 |
-
* @subpackage Help
|
9 |
-
*/
|
10 |
-
|
11 |
-
global $code_snippets;
|
12 |
-
$screen = get_current_screen();
|
13 |
-
|
14 |
-
$screen->add_help_tab( array(
|
15 |
-
'id' => 'overview',
|
16 |
-
'title' => __('Overview', 'code-snippets'),
|
17 |
-
'content' =>
|
18 |
-
'<p>' . __('Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can load snippets from a Code Snippets (.xml) import file into the database with your existing snippets.', 'code-snippets') . '</p>'
|
19 |
-
) );
|
20 |
-
|
21 |
-
$screen->add_help_tab( array(
|
22 |
-
'id' => 'import',
|
23 |
-
'title' => __('Importing', 'code-snippets'),
|
24 |
-
'content' =>
|
25 |
-
'<p>' . __('You can load your snippets from a code snippets (.xml) export file using this page.', 'code-snippets') .
|
26 |
-
sprintf( __('Snippets will be added to the database along with your existing snippets. Regardless of whether the snippets were active on the previous site, imported snippets are always inactive until activated using the <a href="%s">Manage Snippets</a> page.</p>', 'code-snippets'), $code_snippets->admin->manage_url ) . '</p>'
|
27 |
-
) );
|
28 |
-
|
29 |
-
$screen->add_help_tab( array(
|
30 |
-
'id' => 'export',
|
31 |
-
'title' => __( 'Exporting', 'code-snippets' ),
|
32 |
-
'content' =>
|
33 |
-
'<p>' . sprintf( __('You can save your snippets to a Code Snippets (.xml) export file using the <a href="%s">Manage Snippets</a> page.', 'code-snippets'), $code_snippets->admin->manage_url ) . '</p>'
|
34 |
-
) );
|
35 |
-
|
36 |
-
$screen->set_help_sidebar(
|
37 |
-
'<p><strong>' . __('For more information:', 'code-snippets') . '</strong></p>' .
|
38 |
-
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
|
39 |
-
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
40 |
-
'<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
|
41 |
-
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/help/manage.php
DELETED
@@ -1,42 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Register and handle the help tabs for the
|
5 |
-
* manage snippets admin page
|
6 |
-
*
|
7 |
-
* @package Code Snippets
|
8 |
-
* @subpackage Help
|
9 |
-
*/
|
10 |
-
|
11 |
-
global $code_snippets;
|
12 |
-
$screen = get_current_screen();
|
13 |
-
|
14 |
-
$screen->add_help_tab( array(
|
15 |
-
'id' => 'overview',
|
16 |
-
'title' => __('Overview', 'code-snippets'),
|
17 |
-
'content' =>
|
18 |
-
'<p>' . __('Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can manage your existing snippets and preform tasks on them such as activating, deactivating, deleting and exporting.', 'code-snippets') . '</p>'
|
19 |
-
) );
|
20 |
-
|
21 |
-
$screen->add_help_tab( array(
|
22 |
-
'id' => 'safe-mode',
|
23 |
-
'title' => __('Safe Mode', 'code-snippets'),
|
24 |
-
'content' =>
|
25 |
-
'<p>' . __('Be sure to check your snippets for errors before you activate them, as a faulty snippet could bring your whole blog down. If your site starts doing strange things, deactivate all your snippets and activate them one at a time.', 'code-snippets') . '</p>' .
|
26 |
-
'<p>' . __("If something goes wrong with a snippet and you can't use WordPress, you can cause all snippets to stop executing by adding <code>define('CODE_SNIPPETS_SAFE_MODE', true);</code> to your <code>wp-config.php</code> file. After you have deactivated the offending snippet, you can turn off safe mode by removing this line or replacing <strong>true</strong> with <strong>false</strong>.", 'code-snippets') . '</p>'
|
27 |
-
) );
|
28 |
-
|
29 |
-
$screen->add_help_tab( array(
|
30 |
-
'id' => 'uninstall',
|
31 |
-
'title' => __('Uninstall', 'code-snippets'),
|
32 |
-
'content' =>
|
33 |
-
'<p>' . sprintf( __('When you delete Code Snippets through the Plugins menu in WordPress it will clear up the <code>%1$s</code> table and a few other bits of data stored in the database. If you want to keep this data (ie: you are only temporally uninstalling Code Snippets) then remove the <code>%2$s</code> folder using FTP.', 'code-snippets'), $code_snippets->get_table_name(), $code_snippets->plugin_dir ) .
|
34 |
-
'<p>' . __("Even if you're sure that you don't want to use Code Snippets ever again on this WordPress installation, you may want to use the export feature to back up your snippets.", 'code-snippets') . '</p>'
|
35 |
-
) );
|
36 |
-
|
37 |
-
$screen->set_help_sidebar(
|
38 |
-
'<p><strong>' . __('For more information:', 'code-snippets') . '</strong></p>' .
|
39 |
-
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a></p>', 'code-snippets') . '</p>' .
|
40 |
-
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
41 |
-
'<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
|
42 |
-
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/help/single.php
DELETED
@@ -1,47 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Register and handle the help tabs for the
|
5 |
-
* single snippet admin page
|
6 |
-
*
|
7 |
-
* @package Code Snippets
|
8 |
-
* @subpackage Help
|
9 |
-
*/
|
10 |
-
|
11 |
-
$screen = get_current_screen();
|
12 |
-
|
13 |
-
$screen->add_help_tab( array(
|
14 |
-
'id' => 'overview',
|
15 |
-
'title' => __('Overview', 'code-snippets'),
|
16 |
-
'content' =>
|
17 |
-
'<p>' . __('Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can add a new snippet, or edit an existing one.', 'code-snippets') . '</p>',
|
18 |
-
) );
|
19 |
-
|
20 |
-
$screen->add_help_tab( array(
|
21 |
-
'id' => 'finding',
|
22 |
-
'title' => __('Finding Snippets', 'code-snippets'),
|
23 |
-
'content' =>
|
24 |
-
'<p>' . __('Here are some links to websites which host a large number of snippets that you can add to your site.
|
25 |
-
<ul>
|
26 |
-
<li><a href="http://wp-snippets.com" title="WordPress Snippets">WP-Snippets</a></li>
|
27 |
-
<li><a href="http://wpsnipp.com" title="WP Snipp">WP Snipp</a></li>
|
28 |
-
<li><a href="http://www.catswhocode.com/blog/snippets" title="Cats Who Code Snippet Library">Cats Who Code</a></li>
|
29 |
-
<li><a href="http://www.wpfunction.me">WP Function Me</a></li>
|
30 |
-
</ul>', 'code-snippets') .
|
31 |
-
__('More places to find snippets, as well as a selection of example snippets, can be found in the <a href="http://code-snippets.bungeshea.com/docs/finding-snippets/">plugin documentation</a>', 'code-snippets') . '</p>'
|
32 |
-
) );
|
33 |
-
|
34 |
-
$screen->add_help_tab( array(
|
35 |
-
'id' => 'adding',
|
36 |
-
'title' => __('Adding Snippets', 'code-snippets'),
|
37 |
-
'content' =>
|
38 |
-
'<p>' . __('You need to fill out the name and code fields for your snippet to be added. While the description field will add more information about how your snippet works, what is does and where you found it, it is completely optional.', 'code-snippets') . '</p>' .
|
39 |
-
'<p>' . __('Please be sure to check that your snippet is valid PHP code and will not produce errors before adding it through this page. While doing so will not become active straight away, it will help to minimise the chance of a faulty snippet becoming active on your site.', 'code-snippets') . '</p>'
|
40 |
-
) );
|
41 |
-
|
42 |
-
$screen->set_help_sidebar(
|
43 |
-
'<p><strong>' . __('For more information:', 'code-snippets') . '</strong></p>' .
|
44 |
-
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
|
45 |
-
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
46 |
-
'<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
|
47 |
-
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
languages/code-snippets-de_DE.po
CHANGED
@@ -386,8 +386,8 @@ msgstr "Weitere Informationen:"
|
|
386 |
#@ code-snippets
|
387 |
#: includes/help/import.php:27
|
388 |
#: includes/help/single.php:32
|
389 |
-
msgid "<a href=\"http://wordpress.org/
|
390 |
-
msgstr "<a href=\"http://wordpress.org/
|
391 |
|
392 |
#@ code-snippets
|
393 |
#: includes/help/import.php:28
|
@@ -429,8 +429,8 @@ msgstr "Auch wenn Sie sicher sind, dass Sie das Codeschnipsel-Plugin niemals meh
|
|
429 |
|
430 |
#@ code-snippets
|
431 |
#: includes/help/manage.php:28
|
432 |
-
msgid "<a href=\"http://wordpress.org/
|
433 |
-
msgstr "<a href=\"http://wordpress.org/
|
434 |
|
435 |
#@ code-snippets
|
436 |
#: includes/help/single.php:7
|
386 |
#@ code-snippets
|
387 |
#: includes/help/import.php:27
|
388 |
#: includes/help/single.php:32
|
389 |
+
msgid "<a href=\"http://wordpress.org/plugins/code-snippets\" target=\"_blank\">WordPress Extend</a>"
|
390 |
+
msgstr "<a href=\"http://wordpress.org/plugins/code-snippets\" target=\"_blank\">Plugin-Seite bei WordPress.org</a>"
|
391 |
|
392 |
#@ code-snippets
|
393 |
#: includes/help/import.php:28
|
429 |
|
430 |
#@ code-snippets
|
431 |
#: includes/help/manage.php:28
|
432 |
+
msgid "<a href=\"http://wordpress.org/plugins/code-snippets\" target=\"_blank\">WordPress Extend</a></p>"
|
433 |
+
msgstr "<a href=\"http://wordpress.org/plugins/code-snippets\" target=\"_blank\">Plugin-Seite bei WordPress.org</a></p>"
|
434 |
|
435 |
#@ code-snippets
|
436 |
#: includes/help/single.php:7
|
languages/code-snippets.po
CHANGED
@@ -274,7 +274,7 @@ msgid "For more information:"
|
|
274 |
msgstr ""
|
275 |
|
276 |
#: includes/help/import.php:27 includes/help/single.php:32
|
277 |
-
msgid "<a href=\"http://wordpress.org/
|
278 |
msgstr ""
|
279 |
|
280 |
#: includes/help/import.php:28 includes/help/manage.php:29
|
@@ -307,7 +307,7 @@ msgid "Even if you're sure that you don't want to use Code Snippets ever again o
|
|
307 |
msgstr ""
|
308 |
|
309 |
#: includes/help/manage.php:28
|
310 |
-
msgid "<a href=\"http://wordpress.org/
|
311 |
msgstr ""
|
312 |
|
313 |
#: includes/help/single.php:7
|
274 |
msgstr ""
|
275 |
|
276 |
#: includes/help/import.php:27 includes/help/single.php:32
|
277 |
+
msgid "<a href=\"http://wordpress.org/plugins/code-snippets\" target=\"_blank\">WordPress Extend</a>"
|
278 |
msgstr ""
|
279 |
|
280 |
#: includes/help/import.php:28 includes/help/manage.php:29
|
307 |
msgstr ""
|
308 |
|
309 |
#: includes/help/manage.php:28
|
310 |
+
msgid "<a href=\"http://wordpress.org/plugins/code-snippets\" target=\"_blank\">WordPress Extend</a></p>"
|
311 |
msgstr ""
|
312 |
|
313 |
#: includes/help/single.php:7
|
languages/code-snippets.pot
CHANGED
@@ -274,7 +274,7 @@ msgid "For more information:"
|
|
274 |
msgstr ""
|
275 |
|
276 |
#: includes/help/import.php:27 includes/help/single.php:32
|
277 |
-
msgid "<a href=\"http://wordpress.org/
|
278 |
msgstr ""
|
279 |
|
280 |
#: includes/help/import.php:28 includes/help/manage.php:29
|
@@ -307,7 +307,7 @@ msgid "Even if you're sure that you don't want to use Code Snippets ever again o
|
|
307 |
msgstr ""
|
308 |
|
309 |
#: includes/help/manage.php:28
|
310 |
-
msgid "<a href=\"http://wordpress.org/
|
311 |
msgstr ""
|
312 |
|
313 |
#: includes/help/single.php:7
|
274 |
msgstr ""
|
275 |
|
276 |
#: includes/help/import.php:27 includes/help/single.php:32
|
277 |
+
msgid "<a href=\"http://wordpress.org/plugins/code-snippets\" target=\"_blank\">WordPress Extend</a>"
|
278 |
msgstr ""
|
279 |
|
280 |
#: includes/help/import.php:28 includes/help/manage.php:29
|
307 |
msgstr ""
|
308 |
|
309 |
#: includes/help/manage.php:28
|
310 |
+
msgid "<a href=\"http://wordpress.org/plugins/code-snippets\" target=\"_blank\">WordPress Extend</a></p>"
|
311 |
msgstr ""
|
312 |
|
313 |
#: includes/help/single.php:7
|
license.txt
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
-
Copyright (c) 2013 Shea Bunge
|
2 |
-
|
3 |
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4 |
-
|
5 |
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6 |
-
|
7 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1 |
+
Copyright (c) 2012-2013 Shea Bunge
|
2 |
+
|
3 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4 |
+
|
5 |
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6 |
+
|
7 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
readme.txt
CHANGED
@@ -4,7 +4,7 @@ Donate link: http://code-snippets.bungeshea.com/donate/
|
|
4 |
Tags: code-snippets, snippets, code, php, network, multisite
|
5 |
Requires at least: 3.3
|
6 |
Tested up to: 3.6
|
7 |
-
Stable tag: 1.
|
8 |
License: MIT
|
9 |
License URI: license.txt
|
10 |
|
@@ -18,11 +18,11 @@ A snippet is a small chunk of PHP code that you can use to extend the functional
|
|
18 |
Most snippet-hosting sites tell you to add snippet code to your active theme's `functions.php` file, which can get rather long and messy after a while.
|
19 |
Code Snippets changes that by providing a GUI interface for adding snippets and **actually running them on your site** as if they were in your theme's `functions.php` file.
|
20 |
|
21 |
-
You can use a graphical interface, similar to the Plugins menu, to manage, activate, deactivate, edit and delete your snippets. Easily
|
22 |
|
23 |
Although Code Snippets is designed to be easy-to-use and its interface looks, feels and acts as if it was a native part of WordPress, each screen includes a help tab, just in case you get stuck.
|
24 |
|
25 |
-
An addon-plugin for Code Snippets is available: [Code Snippets Tags](http://wordpress.org/
|
26 |
|
27 |
Further information, documentation and updates are available on the [plugin homepage](http://code-snippets.bungeshea.com). You can also contribute to the code at [GitHub](https://github.com/bungeshea/code-snippets).
|
28 |
|
@@ -90,7 +90,7 @@ Yes. Click the checkboxes next to the snippets you want to export, and then choo
|
|
90 |
You can run snippets across an entire multisite network by **Network Activating** Code Snippets through the Network Dashboard. You can also activate Code Snippets just on the main site, and then individually on other sites of your choice.
|
91 |
|
92 |
= Is there anyway to add categories to snippets? =
|
93 |
-
Users of Code Snippets version 1.7 and later can install the [Code Snippets Tags](http://wordpress.org/
|
94 |
|
95 |
= I need help with Code Snippets =
|
96 |
You can get help with Code Snippets either on the [WordPress Support Forums](http://wordpress.org/support/plugin/code-snippets/), on [GithHub](https://github.com/bungeshea/code-snippets/issues), or on [WordPress Answers](http://wordpress.stackexchange.com).
|
@@ -111,6 +111,16 @@ That's fantastic! Join me on [GitHub](https://github.com/bungeshea/code-snippets
|
|
111 |
|
112 |
== Changelog ==
|
113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
= 1.7.1.2 =
|
115 |
* Correct path to admin menu icon ([#](http://wordpress.org/support/topic/icon-disappears-with-mp6?replies=6#post-4148319))
|
116 |
|
@@ -210,6 +220,9 @@ Plugin updates will be posted on the [plugin's homepage](http://code-snippets.bu
|
|
210 |
|
211 |
== Upgrade Notice ==
|
212 |
|
|
|
|
|
|
|
213 |
= 1.7.1.2 =
|
214 |
Fixes the admin menu icon not loading
|
215 |
|
@@ -220,7 +233,7 @@ Fixes a minor bug with custom capabilities and admin menus
|
|
220 |
Added German translation thanks to David Decker; bug fixes and improvements
|
221 |
|
222 |
= 1.7 =
|
223 |
-
Many
|
224 |
|
225 |
= 1.6 =
|
226 |
Improvements and optimization with WordPress 3.5
|
4 |
Tags: code-snippets, snippets, code, php, network, multisite
|
5 |
Requires at least: 3.3
|
6 |
Tested up to: 3.6
|
7 |
+
Stable tag: 1.8
|
8 |
License: MIT
|
9 |
License URI: license.txt
|
10 |
|
18 |
Most snippet-hosting sites tell you to add snippet code to your active theme's `functions.php` file, which can get rather long and messy after a while.
|
19 |
Code Snippets changes that by providing a GUI interface for adding snippets and **actually running them on your site** as if they were in your theme's `functions.php` file.
|
20 |
|
21 |
+
You can use a graphical interface, similar to the Plugins menu, to manage, activate, deactivate, edit and delete your snippets. Easily organize your snippets by adding a name and description using the visual editor. Code Snippets includes built-in syntax highlighting and other features to help you write your code. Snippets can be exported for transfer to another side, either in XML for later importing by the Code Snippets plugin, or in PHP for creating your own plugin or theme.
|
22 |
|
23 |
Although Code Snippets is designed to be easy-to-use and its interface looks, feels and acts as if it was a native part of WordPress, each screen includes a help tab, just in case you get stuck.
|
24 |
|
25 |
+
An addon-plugin for Code Snippets is available: [Code Snippets Tags](http://wordpress.org/plugins/code-snippets-tags) will allow you to assign tags to your snippets and organize them in the table.
|
26 |
|
27 |
Further information, documentation and updates are available on the [plugin homepage](http://code-snippets.bungeshea.com). You can also contribute to the code at [GitHub](https://github.com/bungeshea/code-snippets).
|
28 |
|
90 |
You can run snippets across an entire multisite network by **Network Activating** Code Snippets through the Network Dashboard. You can also activate Code Snippets just on the main site, and then individually on other sites of your choice.
|
91 |
|
92 |
= Is there anyway to add categories to snippets? =
|
93 |
+
Users of Code Snippets version 1.7 and later can install the [Code Snippets Tags](http://wordpress.org/plugins/code-snippets-tags) plugin for the ability to add tags to snippets, and then later filter the snippets by tag for easier organization.
|
94 |
|
95 |
= I need help with Code Snippets =
|
96 |
You can get help with Code Snippets either on the [WordPress Support Forums](http://wordpress.org/support/plugin/code-snippets/), on [GithHub](https://github.com/bungeshea/code-snippets/issues), or on [WordPress Answers](http://wordpress.stackexchange.com).
|
111 |
|
112 |
== Changelog ==
|
113 |
|
114 |
+
= 1.8 =
|
115 |
+
* Allow no snippet name or code to be set
|
116 |
+
* Prevented an error on fresh multisite installations
|
117 |
+
* Refactored code to use best practices
|
118 |
+
* Improved database table creation method: on a single-site install, the snippets table will always be created. On a multisite install, the network snippets table will always be created; the site-specific table will always be created for the main site; for sub-sites the snippets table will only be created on a visit to a snippets admin page.
|
119 |
+
* Updated to CodeMirror 3.14
|
120 |
+
* Changes to action and filter hook API
|
121 |
+
* Added error message handling for import snippets page
|
122 |
+
* Don't encode HTML entities in database
|
123 |
+
|
124 |
= 1.7.1.2 =
|
125 |
* Correct path to admin menu icon ([#](http://wordpress.org/support/topic/icon-disappears-with-mp6?replies=6#post-4148319))
|
126 |
|
220 |
|
221 |
== Upgrade Notice ==
|
222 |
|
223 |
+
= 1.8 =
|
224 |
+
Setting a snippet name and code are now optional; better table creation method; changes to API; bug fixes
|
225 |
+
|
226 |
= 1.7.1.2 =
|
227 |
Fixes the admin menu icon not loading
|
228 |
|
233 |
Added German translation thanks to David Decker; bug fixes and improvements
|
234 |
|
235 |
= 1.7 =
|
236 |
+
Many improvements and optimization. Download "Code Snippets Tags" plugin to add tags to snippets
|
237 |
|
238 |
= 1.6 =
|
239 |
Improvements and optimization with WordPress 3.5
|
screenshot-1.jpg
DELETED
Binary file
|
screenshot-1.png
ADDED
Binary file
|
screenshot-2.jpg
DELETED
Binary file
|
screenshot-2.png
ADDED
Binary file
|
screenshot-3.jpg
DELETED
Binary file
|
screenshot-3.png
ADDED
Binary file
|
screenshot-4.jpg
DELETED
Binary file
|
screenshot-4.png
ADDED
Binary file
|
screenshot-5.jpg
DELETED
Binary file
|
screenshot-5.png
ADDED
Binary file
|
vendor/codemirror/addon/matchbrackets.js
CHANGED
@@ -1,74 +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
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
var
|
13 |
-
|
14 |
-
|
15 |
-
var
|
16 |
-
|
17 |
-
var
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
if (
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
var
|
47 |
-
var
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
var
|
53 |
-
|
54 |
-
};
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
CodeMirror.
|
74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
})();
|
vendor/codemirror/addon/search.js
CHANGED
@@ -1,131 +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.
|
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(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 |
-
})();
|
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 |
+
})();
|
vendor/codemirror/addon/searchcursor.js
CHANGED
@@ -1,130 +1,143 @@
|
|
1 |
-
(function(){
|
2 |
-
var Pos = CodeMirror.Pos;
|
3 |
-
|
4 |
-
function SearchCursor(
|
5 |
-
this.atOccurrence = false; this.
|
6 |
-
if (caseFold == null && typeof query == "string") caseFold = false;
|
7 |
-
|
8 |
-
pos = pos ?
|
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 =
|
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 + 1;
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
}
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
if (pos.
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
})();
|
vendor/codemirror/lib/codemirror.css
CHANGED
@@ -1,246 +1,248 @@
|
|
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 {
|
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 |
-
|
32 |
-
|
33 |
-
.CodeMirror-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
.cm-s-default .cm-
|
65 |
-
.cm-s-default .cm-
|
66 |
-
.cm-s-default .cm-
|
67 |
-
.cm-s-default .cm-
|
68 |
-
.cm-s-default .cm-variable
|
69 |
-
.cm-s-default .cm-variable-
|
70 |
-
.cm-s-default .cm-
|
71 |
-
.cm-s-default .cm-
|
72 |
-
.cm-s-default .cm-
|
73 |
-
.cm-s-default .cm-
|
74 |
-
.cm-s-default .cm-string
|
75 |
-
.cm-s-default .cm-
|
76 |
-
.cm-s-default .cm-
|
77 |
-
.cm-s-default .cm-
|
78 |
-
.cm-s-default .cm-
|
79 |
-
.cm-s-default .cm-
|
80 |
-
.cm-s-default .cm-
|
81 |
-
.cm-s-default .cm-
|
82 |
-
.cm-s-default .cm-
|
83 |
-
.cm-s-default .cm-
|
84 |
-
.cm-s-default .cm-
|
85 |
-
.cm-s-default .cm-
|
86 |
-
|
87 |
-
|
88 |
-
.cm-
|
89 |
-
.cm-
|
90 |
-
.cm-
|
91 |
-
.cm-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
div.CodeMirror span.CodeMirror-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
/*
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
overflow-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
overflow-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
}
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
.CodeMirror
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
.CodeMirror
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
.
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
|
|
|
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 |
+
|
99 |
+
/* STOP */
|
100 |
+
|
101 |
+
/* The rest of this file contains styles related to the mechanics of
|
102 |
+
the editor. You probably shouldn't touch them. */
|
103 |
+
|
104 |
+
.CodeMirror {
|
105 |
+
line-height: 1;
|
106 |
+
position: relative;
|
107 |
+
overflow: hidden;
|
108 |
+
background: white;
|
109 |
+
color: black;
|
110 |
+
}
|
111 |
+
|
112 |
+
.CodeMirror-scroll {
|
113 |
+
/* 30px is the magic margin used to hide the element's real scrollbars */
|
114 |
+
/* See overflow: hidden in .CodeMirror */
|
115 |
+
margin-bottom: -30px; margin-right: -30px;
|
116 |
+
padding-bottom: 30px; padding-right: 30px;
|
117 |
+
height: 100%;
|
118 |
+
outline: none; /* Prevent dragging from highlighting the element */
|
119 |
+
position: relative;
|
120 |
+
}
|
121 |
+
.CodeMirror-sizer {
|
122 |
+
position: relative;
|
123 |
+
}
|
124 |
+
|
125 |
+
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
126 |
+
before actuall scrolling happens, thus preventing shaking and
|
127 |
+
flickering artifacts. */
|
128 |
+
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
129 |
+
position: absolute;
|
130 |
+
z-index: 6;
|
131 |
+
display: none;
|
132 |
+
}
|
133 |
+
.CodeMirror-vscrollbar {
|
134 |
+
right: 0; top: 0;
|
135 |
+
overflow-x: hidden;
|
136 |
+
overflow-y: scroll;
|
137 |
+
}
|
138 |
+
.CodeMirror-hscrollbar {
|
139 |
+
bottom: 0; left: 0;
|
140 |
+
overflow-y: hidden;
|
141 |
+
overflow-x: scroll;
|
142 |
+
}
|
143 |
+
.CodeMirror-scrollbar-filler {
|
144 |
+
right: 0; bottom: 0;
|
145 |
+
}
|
146 |
+
.CodeMirror-gutter-filler {
|
147 |
+
left: 0; bottom: 0;
|
148 |
+
}
|
149 |
+
|
150 |
+
.CodeMirror-gutters {
|
151 |
+
position: absolute; left: 0; top: 0;
|
152 |
+
padding-bottom: 30px;
|
153 |
+
z-index: 3;
|
154 |
+
}
|
155 |
+
.CodeMirror-gutter {
|
156 |
+
white-space: normal;
|
157 |
+
height: 100%;
|
158 |
+
padding-bottom: 30px;
|
159 |
+
margin-bottom: -32px;
|
160 |
+
display: inline-block;
|
161 |
+
/* Hack to make IE7 behave */
|
162 |
+
*zoom:1;
|
163 |
+
*display:inline;
|
164 |
+
}
|
165 |
+
.CodeMirror-gutter-elt {
|
166 |
+
position: absolute;
|
167 |
+
cursor: default;
|
168 |
+
z-index: 4;
|
169 |
+
}
|
170 |
+
|
171 |
+
.CodeMirror-lines {
|
172 |
+
cursor: text;
|
173 |
+
}
|
174 |
+
.CodeMirror pre {
|
175 |
+
/* Reset some styles that the rest of the page might have set */
|
176 |
+
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
|
177 |
+
border-width: 0;
|
178 |
+
background: transparent;
|
179 |
+
font-family: inherit;
|
180 |
+
font-size: inherit;
|
181 |
+
margin: 0;
|
182 |
+
white-space: pre;
|
183 |
+
word-wrap: normal;
|
184 |
+
line-height: inherit;
|
185 |
+
color: inherit;
|
186 |
+
z-index: 2;
|
187 |
+
position: relative;
|
188 |
+
overflow: visible;
|
189 |
+
}
|
190 |
+
.CodeMirror-wrap pre {
|
191 |
+
word-wrap: break-word;
|
192 |
+
white-space: pre-wrap;
|
193 |
+
word-break: normal;
|
194 |
+
}
|
195 |
+
.CodeMirror-linebackground {
|
196 |
+
position: absolute;
|
197 |
+
left: 0; right: 0; top: 0; bottom: 0;
|
198 |
+
z-index: 0;
|
199 |
+
}
|
200 |
+
|
201 |
+
.CodeMirror-linewidget {
|
202 |
+
position: relative;
|
203 |
+
z-index: 2;
|
204 |
+
overflow: auto;
|
205 |
+
}
|
206 |
+
|
207 |
+
.CodeMirror-widget {
|
208 |
+
}
|
209 |
+
|
210 |
+
.CodeMirror-wrap .CodeMirror-scroll {
|
211 |
+
overflow-x: hidden;
|
212 |
+
}
|
213 |
+
|
214 |
+
.CodeMirror-measure {
|
215 |
+
position: absolute;
|
216 |
+
width: 100%; height: 0px;
|
217 |
+
overflow: hidden;
|
218 |
+
visibility: hidden;
|
219 |
+
}
|
220 |
+
.CodeMirror-measure pre { position: static; }
|
221 |
+
|
222 |
+
.CodeMirror div.CodeMirror-cursor {
|
223 |
+
position: absolute;
|
224 |
+
visibility: hidden;
|
225 |
+
border-right: none;
|
226 |
+
width: 0;
|
227 |
+
}
|
228 |
+
.CodeMirror-focused div.CodeMirror-cursor {
|
229 |
+
visibility: visible;
|
230 |
+
}
|
231 |
+
|
232 |
+
.CodeMirror-selected { background: #d9d9d9; }
|
233 |
+
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
234 |
+
|
235 |
+
.cm-searching {
|
236 |
+
background: #ffa;
|
237 |
+
background: rgba(255, 255, 0, .4);
|
238 |
+
}
|
239 |
+
|
240 |
+
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
|
241 |
+
.CodeMirror span { *vertical-align: text-bottom; }
|
242 |
+
|
243 |
+
@media print {
|
244 |
+
/* Hide the cursor when printing */
|
245 |
+
.CodeMirror div.CodeMirror-cursor {
|
246 |
+
visibility: hidden;
|
247 |
+
}
|
248 |
+
}
|
vendor/codemirror/lib/codemirror.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
// CodeMirror version 3.
|
2 |
//
|
3 |
// CodeMirror is the only global var we claim
|
4 |
window.CodeMirror = (function() {
|
@@ -94,12 +94,12 @@ window.CodeMirror = (function() {
|
|
94 |
function makeDisplay(place, docStart) {
|
95 |
var d = {};
|
96 |
|
97 |
-
var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
|
98 |
if (webkit) input.style.width = "1000px";
|
99 |
else input.setAttribute("wrap", "off");
|
100 |
// if border: 0; -- iOS fails to open keyboard (issue #1287)
|
101 |
if (ios) input.style.border = "1px solid black";
|
102 |
-
input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
|
103 |
|
104 |
// Wraps and hides input textarea
|
105 |
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
|
@@ -107,8 +107,9 @@ window.CodeMirror = (function() {
|
|
107 |
d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
|
108 |
d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
|
109 |
d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
|
|
|
110 |
// DIVs containing the selection and the actual code
|
111 |
-
d.lineDiv = elt("div");
|
112 |
d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
|
113 |
// Blinky cursor, and element used to ensure cursor fits at the end of a line
|
114 |
d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
|
@@ -128,14 +129,12 @@ window.CodeMirror = (function() {
|
|
128 |
// Will contain the gutters, if any
|
129 |
d.gutters = elt("div", null, "CodeMirror-gutters");
|
130 |
d.lineGutter = null;
|
131 |
-
// Helper element to properly size the gutter backgrounds
|
132 |
-
var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
|
133 |
// Provides scrolling
|
134 |
-
d.scroller = elt("div", [
|
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.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);
|
@@ -166,8 +165,6 @@ window.CodeMirror = (function() {
|
|
166 |
d.pollingFast = false;
|
167 |
// Self-resetting timeout for the poller
|
168 |
d.poll = new Delayed();
|
169 |
-
// True when a drag from the editor is active
|
170 |
-
d.draggingText = false;
|
171 |
|
172 |
d.cachedCharWidth = d.cachedTextHeight = null;
|
173 |
d.measureLineCache = [];
|
@@ -216,7 +213,7 @@ window.CodeMirror = (function() {
|
|
216 |
estimateLineHeights(cm);
|
217 |
regChange(cm);
|
218 |
clearCaches(cm);
|
219 |
-
setTimeout(function(){updateScrollbars(cm
|
220 |
}
|
221 |
|
222 |
function estimateHeight(cm) {
|
@@ -241,9 +238,10 @@ window.CodeMirror = (function() {
|
|
241 |
}
|
242 |
|
243 |
function keyMapChanged(cm) {
|
244 |
-
var
|
245 |
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
|
246 |
(style ? " cm-keymap-" + style : "");
|
|
|
247 |
}
|
248 |
|
249 |
function themeChanged(cm) {
|
@@ -255,6 +253,7 @@ window.CodeMirror = (function() {
|
|
255 |
function guttersChanged(cm) {
|
256 |
updateGutters(cm);
|
257 |
regChange(cm);
|
|
|
258 |
}
|
259 |
|
260 |
function updateGutters(cm) {
|
@@ -321,12 +320,14 @@ window.CodeMirror = (function() {
|
|
321 |
|
322 |
// Re-synchronize the fake scrollbars with the actual size of the
|
323 |
// content. Optionally force a scrollTop.
|
324 |
-
function updateScrollbars(
|
|
|
325 |
var totalHeight = docHeight + paddingVert(d);
|
326 |
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
|
|
|
327 |
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
|
328 |
-
var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
|
329 |
-
var needsV = scrollHeight > d.scroller.clientHeight;
|
330 |
if (needsV) {
|
331 |
d.scrollbarV.style.display = "block";
|
332 |
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
|
@@ -343,6 +344,11 @@ window.CodeMirror = (function() {
|
|
343 |
d.scrollbarFiller.style.display = "block";
|
344 |
d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
|
345 |
} else d.scrollbarFiller.style.display = "";
|
|
|
|
|
|
|
|
|
|
|
346 |
|
347 |
if (mac_geLion && scrollbarWidth(d.measure) === 0)
|
348 |
d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
|
@@ -398,23 +404,36 @@ window.CodeMirror = (function() {
|
|
398 |
// DISPLAY DRAWING
|
399 |
|
400 |
function updateDisplay(cm, changes, viewPort) {
|
401 |
-
var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
|
402 |
-
var
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
403 |
if (updated) {
|
404 |
signalLater(cm, "update", cm);
|
405 |
if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
|
406 |
signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
|
407 |
}
|
408 |
-
updateSelection(cm);
|
409 |
-
updateScrollbars(cm.display, cm.doc.height);
|
410 |
-
|
411 |
return updated;
|
412 |
}
|
413 |
|
414 |
// Uses a set of changes plus the current scroll position to
|
415 |
// determine which DOM updates have to be made, and makes the
|
416 |
// updates.
|
417 |
-
function updateDisplayInner(cm, changes,
|
418 |
var display = cm.display, doc = cm.doc;
|
419 |
if (!display.wrapper.clientWidth) {
|
420 |
display.showingFrom = display.showingTo = doc.first;
|
@@ -422,10 +441,6 @@ window.CodeMirror = (function() {
|
|
422 |
return;
|
423 |
}
|
424 |
|
425 |
-
// Compute the new visible window
|
426 |
-
// If scrollTop is specified, use that to determine which lines
|
427 |
-
// to render instead of the current scrollbar position.
|
428 |
-
var visible = visibleLines(display, doc, viewPort);
|
429 |
// Bail out if the visible area is already rendered and nothing changed.
|
430 |
if (changes.length == 0 &&
|
431 |
visible.from > display.showingFrom && visible.to < display.showingTo)
|
@@ -486,19 +501,24 @@ window.CodeMirror = (function() {
|
|
486 |
}
|
487 |
intact.sort(function(a, b) {return a.from - b.from;});
|
488 |
|
489 |
-
|
|
|
|
|
|
|
490 |
if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
|
491 |
patchDisplay(cm, from, to, intact, positionsChangedFrom);
|
492 |
display.lineDiv.style.display = "";
|
493 |
-
if (document.activeElement != focused && focused.offsetHeight) focused.focus();
|
494 |
|
495 |
var different = from != display.showingFrom || to != display.showingTo ||
|
496 |
display.lastSizeC != display.wrapper.clientHeight;
|
497 |
// This is just a bogus formula that detects when the editor is
|
498 |
// resized or the font size changes.
|
499 |
-
if (different)
|
|
|
|
|
|
|
500 |
display.showingFrom = from; display.showingTo = to;
|
501 |
-
startWorker(cm, 100);
|
502 |
|
503 |
var prevBottom = display.lineDiv.offsetTop;
|
504 |
for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
|
@@ -521,8 +541,6 @@ window.CodeMirror = (function() {
|
|
521 |
}
|
522 |
updateViewOffset(cm);
|
523 |
|
524 |
-
if (visibleLines(display, doc, viewPort).to > to)
|
525 |
-
updateDisplayInner(cm, [], viewPort);
|
526 |
return true;
|
527 |
}
|
528 |
|
@@ -589,8 +607,9 @@ window.CodeMirror = (function() {
|
|
589 |
if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
|
590 |
if (lineIsHidden(cm.doc, line)) {
|
591 |
if (line.height != 0) updateLineHeight(line, 0);
|
592 |
-
if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
|
593 |
-
|
|
|
594 |
var prev = cur.previousSibling;
|
595 |
if (/pre/i.test(prev.nodeName)) {
|
596 |
var wrap = elt("div", null, null, "position: relative");
|
@@ -598,9 +617,11 @@ window.CodeMirror = (function() {
|
|
598 |
wrap.appendChild(prev);
|
599 |
prev = wrap;
|
600 |
}
|
601 |
-
var wnode = prev.appendChild(elt("div", [
|
602 |
-
|
|
|
603 |
}
|
|
|
604 |
} else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
|
605 |
// This line is intact. Skip to the actual node. Update its
|
606 |
// line number if needed.
|
@@ -643,25 +664,25 @@ window.CodeMirror = (function() {
|
|
643 |
|
644 |
if (reuse) {
|
645 |
reuse.alignable = null;
|
646 |
-
var isOk = true, widgetsSeen = 0;
|
647 |
for (var n = reuse.firstChild, next; n; n = next) {
|
648 |
next = n.nextSibling;
|
649 |
if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
|
650 |
reuse.removeChild(n);
|
651 |
} else {
|
652 |
for (var i = 0, first = true; i < line.widgets.length; ++i) {
|
653 |
-
var widget = line.widgets[i]
|
654 |
-
if (!widget.above) {
|
655 |
if (widget.node == n.firstChild) {
|
656 |
positionLineWidget(widget, n, reuse, dims);
|
657 |
++widgetsSeen;
|
658 |
-
if (isFirst) reuse.insertBefore(lineElement, n);
|
659 |
break;
|
660 |
}
|
661 |
}
|
662 |
if (i == line.widgets.length) { isOk = false; break; }
|
663 |
}
|
664 |
}
|
|
|
665 |
if (isOk && widgetsSeen == line.widgets.length) {
|
666 |
wrap = reuse;
|
667 |
reuse.className = line.wrapClass || "";
|
@@ -696,6 +717,7 @@ window.CodeMirror = (function() {
|
|
696 |
if (ie_lt8) wrap.style.zIndex = 2;
|
697 |
if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
|
698 |
var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
|
|
|
699 |
positionLineWidget(widget, node, wrap, dims);
|
700 |
if (widget.above)
|
701 |
wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
|
@@ -739,12 +761,14 @@ window.CodeMirror = (function() {
|
|
739 |
display.selectionDiv.style.display = "none";
|
740 |
|
741 |
// Move the hidden textarea near the cursor to prevent scrolling artifacts
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
|
|
|
|
748 |
}
|
749 |
|
750 |
// No selection, plain cursor
|
@@ -776,67 +800,59 @@ window.CodeMirror = (function() {
|
|
776 |
"px; height: " + (bottom - top) + "px"));
|
777 |
}
|
778 |
|
779 |
-
function drawForLine(line, fromArg, toArg
|
780 |
var lineObj = getLine(doc, line);
|
781 |
-
var lineLen = lineObj.text.length
|
782 |
-
|
783 |
-
|
|
|
784 |
}
|
785 |
|
786 |
iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
|
787 |
-
var leftPos = coords(
|
788 |
-
|
789 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
790 |
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
|
791 |
add(left, leftPos.top, null, leftPos.bottom);
|
792 |
left = pl;
|
793 |
if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
|
794 |
}
|
795 |
if (toArg == null && to == lineLen) right = clientWidth;
|
796 |
-
if (
|
797 |
-
|
|
|
|
|
798 |
if (left < pl + 1) left = pl;
|
799 |
add(left, rightPos.top, right - left, rightPos.bottom);
|
800 |
});
|
801 |
-
return
|
802 |
}
|
803 |
|
804 |
if (sel.from.line == sel.to.line) {
|
805 |
drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
|
806 |
} else {
|
807 |
-
var
|
808 |
-
var
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
if (
|
813 |
-
|
814 |
-
|
815 |
-
|
|
|
816 |
}
|
817 |
-
cur = getLine(doc, found.to.line);
|
818 |
-
}
|
819 |
-
|
820 |
-
// This is a single, merged line
|
821 |
-
if (singleLine) {
|
822 |
-
for (var i = 0; i < path.length; i += 3)
|
823 |
-
drawForLine(path[i], path[i+1], path[i+2]);
|
824 |
-
} else {
|
825 |
-
var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
|
826 |
-
if (sel.from.ch)
|
827 |
-
// Draw the first line of selection.
|
828 |
-
middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
|
829 |
-
else
|
830 |
-
// Simply include it in the middle block.
|
831 |
-
middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
|
832 |
-
|
833 |
-
if (!sel.to.ch)
|
834 |
-
middleBot = heightAtLine(cm, toObj) - display.viewOffset;
|
835 |
-
else
|
836 |
-
middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
|
837 |
-
|
838 |
-
if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
|
839 |
}
|
|
|
|
|
840 |
}
|
841 |
|
842 |
removeChildrenAndAdd(display.selectionDiv, fragment);
|
@@ -845,12 +861,12 @@ window.CodeMirror = (function() {
|
|
845 |
|
846 |
// Cursor-blinking
|
847 |
function restartBlink(cm) {
|
|
|
848 |
var display = cm.display;
|
849 |
clearInterval(display.blinker);
|
850 |
var on = true;
|
851 |
display.cursor.style.visibility = display.otherCursor.style.visibility = "";
|
852 |
display.blinker = setInterval(function() {
|
853 |
-
if (!display.cursor.offsetHeight) return;
|
854 |
display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
|
855 |
}, cm.options.cursorBlinkRate);
|
856 |
}
|
@@ -902,12 +918,12 @@ window.CodeMirror = (function() {
|
|
902 |
// valid state. If that fails, it returns the line with the
|
903 |
// smallest indentation, which tends to need the least context to
|
904 |
// parse correctly.
|
905 |
-
function findStartLine(cm, n) {
|
906 |
var minindent, minline, doc = cm.doc;
|
907 |
for (var search = n, lim = n - 100; search > lim; --search) {
|
908 |
if (search <= doc.first) return doc.first;
|
909 |
var line = getLine(doc, search - 1);
|
910 |
-
if (line.stateAfter) return search;
|
911 |
var indented = countColumn(line.text, null, cm.options.tabSize);
|
912 |
if (minline == null || minindent > indented) {
|
913 |
minline = search - 1;
|
@@ -917,10 +933,10 @@ window.CodeMirror = (function() {
|
|
917 |
return minline;
|
918 |
}
|
919 |
|
920 |
-
function getStateBefore(cm, n) {
|
921 |
var doc = cm.doc, display = cm.display;
|
922 |
if (!doc.mode.startState) return true;
|
923 |
-
var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
|
924 |
if (!state) state = startState(doc.mode);
|
925 |
else state = copyState(doc.mode, state);
|
926 |
doc.iter(pos, n, function(line) {
|
@@ -941,7 +957,7 @@ window.CodeMirror = (function() {
|
|
941 |
return e.offsetLeft;
|
942 |
}
|
943 |
|
944 |
-
function measureChar(cm, line, ch, data) {
|
945 |
var dir = -1;
|
946 |
data = data || measureLine(cm, line);
|
947 |
|
@@ -950,9 +966,11 @@ window.CodeMirror = (function() {
|
|
950 |
if (r) break;
|
951 |
if (dir < 0 && pos == 0) dir = 1;
|
952 |
}
|
|
|
953 |
return {left: pos < ch ? r.right : r.left,
|
954 |
right: pos > ch ? r.left : r.right,
|
955 |
-
top: r.
|
|
|
956 |
}
|
957 |
|
958 |
function findCachedMeasurement(cm, line) {
|
@@ -962,23 +980,28 @@ window.CodeMirror = (function() {
|
|
962 |
if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
|
963 |
cm.display.scroller.clientWidth == memo.width &&
|
964 |
memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
|
965 |
-
return memo
|
966 |
}
|
967 |
}
|
968 |
|
|
|
|
|
|
|
|
|
|
|
969 |
function measureLine(cm, line) {
|
970 |
// First look in the cache
|
971 |
-
var
|
972 |
-
if (
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
return measure;
|
983 |
}
|
984 |
|
@@ -1020,9 +1043,9 @@ window.CodeMirror = (function() {
|
|
1020 |
if (ie_lt9 && display.measure.first != pre)
|
1021 |
removeChildrenAndAdd(display.measure, pre);
|
1022 |
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
for (var j = 0; j < vranges.length; j += 2) {
|
1027 |
var rtop = vranges[j], rbot = vranges[j+1];
|
1028 |
if (rtop > bot || rbot < top) continue;
|
@@ -1031,19 +1054,38 @@ window.CodeMirror = (function() {
|
|
1031 |
Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
|
1032 |
vranges[j] = Math.min(top, rtop);
|
1033 |
vranges[j+1] = Math.max(bot, rbot);
|
1034 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1035 |
}
|
1036 |
}
|
1037 |
-
|
|
|
1038 |
var right = size.right;
|
1039 |
if (cur.measureRight) right = getRect(cur.measureRight).left;
|
1040 |
-
data[i] = {left: size.left - outer.left, right: right - outer.left, top:
|
1041 |
}
|
1042 |
for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
|
1043 |
-
var vr = cur.top;
|
1044 |
cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
|
|
|
1045 |
}
|
1046 |
-
|
1047 |
return data;
|
1048 |
}
|
1049 |
|
@@ -1054,7 +1096,7 @@ window.CodeMirror = (function() {
|
|
1054 |
if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
|
1055 |
}
|
1056 |
var cached = !hasBadSpan && findCachedMeasurement(cm, line);
|
1057 |
-
if (cached) return measureChar(cm, line, line.text.length, cached).right;
|
1058 |
|
1059 |
var pre = lineContent(cm, line);
|
1060 |
var end = pre.appendChild(zeroWidthElement(cm.display.measure));
|
@@ -1065,10 +1107,13 @@ window.CodeMirror = (function() {
|
|
1065 |
function clearCaches(cm) {
|
1066 |
cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
|
1067 |
cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
|
1068 |
-
cm.display.maxLineChanged = true;
|
1069 |
cm.display.lineNumChars = null;
|
1070 |
}
|
1071 |
|
|
|
|
|
|
|
1072 |
// Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
|
1073 |
function intoCoordSystem(cm, lineObj, rect, context) {
|
1074 |
if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
|
@@ -1078,11 +1123,12 @@ window.CodeMirror = (function() {
|
|
1078 |
if (context == "line") return rect;
|
1079 |
if (!context) context = "local";
|
1080 |
var yOff = heightAtLine(cm, lineObj);
|
1081 |
-
if (context
|
1082 |
-
|
|
|
1083 |
var lOff = getRect(cm.display.lineSpace);
|
1084 |
-
yOff += lOff.top + (window
|
1085 |
-
var xOff = lOff.left + (window
|
1086 |
rect.left += xOff; rect.right += xOff;
|
1087 |
}
|
1088 |
rect.top += yOff; rect.bottom += yOff;
|
@@ -1090,68 +1136,62 @@ window.CodeMirror = (function() {
|
|
1090 |
}
|
1091 |
|
1092 |
// Context may be "window", "page", "div", or "local"/null
|
1093 |
-
// Result is in
|
1094 |
function fromCoordSystem(cm, coords, context) {
|
1095 |
if (context == "div") return coords;
|
1096 |
var left = coords.left, top = coords.top;
|
|
|
1097 |
if (context == "page") {
|
1098 |
-
left -=
|
1099 |
-
top -=
|
|
|
|
|
|
|
|
|
1100 |
}
|
|
|
1101 |
var lineSpaceBox = getRect(cm.display.lineSpace);
|
1102 |
-
left
|
1103 |
-
top -= lineSpaceBox.top;
|
1104 |
-
if (context == "local" || !context) {
|
1105 |
-
var editorBox = getRect(cm.display.wrapper);
|
1106 |
-
left -= editorBox.left;
|
1107 |
-
top -= editorBox.top;
|
1108 |
-
}
|
1109 |
-
return {left: left, top: top};
|
1110 |
}
|
1111 |
|
1112 |
-
function charCoords(cm, pos, context, lineObj) {
|
1113 |
if (!lineObj) lineObj = getLine(cm.doc, pos.line);
|
1114 |
-
return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
|
1115 |
}
|
1116 |
|
1117 |
function cursorCoords(cm, pos, context, lineObj, measurement) {
|
1118 |
lineObj = lineObj || getLine(cm.doc, pos.line);
|
1119 |
if (!measurement) measurement = measureLine(cm, lineObj);
|
1120 |
function get(ch, right) {
|
1121 |
-
var m = measureChar(cm, lineObj, ch, measurement);
|
1122 |
if (right) m.left = m.right; else m.right = m.left;
|
1123 |
return intoCoordSystem(cm, lineObj, m, context);
|
1124 |
}
|
1125 |
-
|
1126 |
-
|
1127 |
-
|
1128 |
-
|
1129 |
-
|
1130 |
-
|
1131 |
-
|
1132 |
-
|
1133 |
-
|
1134 |
-
|
1135 |
-
// level. So we try to use the side with the higher level.
|
1136 |
-
if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
|
1137 |
-
else here = get(rtl && part.from != part.to ? ch - 1 : ch);
|
1138 |
-
if (rtl == linedir) main = here; else other = here;
|
1139 |
-
} else if (right == ch) {
|
1140 |
-
var nb = i < order.length - 1 && order[i+1];
|
1141 |
-
if (!rtl && nb && nb.from == nb.to) continue;
|
1142 |
-
if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
|
1143 |
-
else here = get(rtl ? ch : ch - 1, true);
|
1144 |
-
if (rtl == linedir) main = here; else other = here;
|
1145 |
}
|
|
|
|
|
1146 |
}
|
1147 |
-
|
1148 |
-
if (!
|
1149 |
-
|
1150 |
-
|
|
|
|
|
1151 |
}
|
1152 |
|
1153 |
-
function
|
1154 |
var pos = new Pos(line, ch);
|
|
|
1155 |
if (outside) pos.outside = true;
|
1156 |
return pos;
|
1157 |
}
|
@@ -1160,10 +1200,10 @@ window.CodeMirror = (function() {
|
|
1160 |
function coordsChar(cm, x, y) {
|
1161 |
var doc = cm.doc;
|
1162 |
y += cm.display.viewOffset;
|
1163 |
-
if (y < 0) return
|
1164 |
var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
|
1165 |
if (lineNo > last)
|
1166 |
-
return
|
1167 |
if (x < 0) x = 0;
|
1168 |
|
1169 |
for (;;) {
|
@@ -1171,7 +1211,7 @@ window.CodeMirror = (function() {
|
|
1171 |
var found = coordsCharInner(cm, lineObj, lineNo, x, y);
|
1172 |
var merged = collapsedSpanAtEnd(lineObj);
|
1173 |
var mergedPos = merged && merged.find();
|
1174 |
-
if (merged && found.ch
|
1175 |
lineNo = mergedPos.to.line;
|
1176 |
else
|
1177 |
return found;
|
@@ -1197,14 +1237,15 @@ window.CodeMirror = (function() {
|
|
1197 |
var from = lineLeft(lineObj), to = lineRight(lineObj);
|
1198 |
var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
|
1199 |
|
1200 |
-
if (x > toX) return
|
1201 |
// Do a binary search between these bounds.
|
1202 |
for (;;) {
|
1203 |
if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
|
1204 |
-
var
|
|
|
1205 |
while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
|
1206 |
-
var pos =
|
1207 |
-
|
1208 |
return pos;
|
1209 |
}
|
1210 |
var step = Math.ceil(dist / 2), middle = from + step;
|
@@ -1213,8 +1254,8 @@ window.CodeMirror = (function() {
|
|
1213 |
for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
|
1214 |
}
|
1215 |
var middleX = getX(middle);
|
1216 |
-
if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist
|
1217 |
-
else {from = middle; fromX = middleX; fromOutside = wrongLine; dist
|
1218 |
}
|
1219 |
}
|
1220 |
|
@@ -1265,6 +1306,7 @@ window.CodeMirror = (function() {
|
|
1265 |
userSelChange: null,
|
1266 |
textChanged: null,
|
1267 |
selectionChanged: false,
|
|
|
1268 |
updateMaxLine: false,
|
1269 |
updateScrollPos: false,
|
1270 |
id: ++nextOpId
|
@@ -1277,7 +1319,7 @@ window.CodeMirror = (function() {
|
|
1277 |
cm.curOp = null;
|
1278 |
|
1279 |
if (op.updateMaxLine) computeMaxLength(cm);
|
1280 |
-
if (display.maxLineChanged && !cm.options.lineWrapping) {
|
1281 |
var width = measureLineWidth(cm, display.maxLine);
|
1282 |
display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
|
1283 |
display.maxLineChanged = false;
|
@@ -1301,6 +1343,8 @@ window.CodeMirror = (function() {
|
|
1301 |
display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
|
1302 |
display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
|
1303 |
alignHorizontally(cm);
|
|
|
|
|
1304 |
} else if (newScrollPos) {
|
1305 |
scrollCursorIntoView(cm);
|
1306 |
}
|
@@ -1322,7 +1366,7 @@ window.CodeMirror = (function() {
|
|
1322 |
}
|
1323 |
if (op.textChanged)
|
1324 |
signal(cm, "change", cm, op.textChanged);
|
1325 |
-
if (op.
|
1326 |
if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
|
1327 |
}
|
1328 |
|
@@ -1387,31 +1431,32 @@ window.CodeMirror = (function() {
|
|
1387 |
// supported or compatible enough yet to rely on.)
|
1388 |
function readInput(cm) {
|
1389 |
var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
|
1390 |
-
if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
|
1391 |
var text = input.value;
|
1392 |
if (text == prevInput && posEq(sel.from, sel.to)) return false;
|
1393 |
-
|
1394 |
-
// re-focusing. If the selection is gone but the cursor is at the
|
1395 |
-
// start of the input, that's probably what happened.
|
1396 |
-
if (ie && text && input.selectionStart === 0) {
|
1397 |
resetInput(cm, true);
|
1398 |
return false;
|
1399 |
}
|
|
|
1400 |
var withOp = !cm.curOp;
|
1401 |
if (withOp) startOperation(cm);
|
1402 |
sel.shift = false;
|
1403 |
var same = 0, l = Math.min(prevInput.length, text.length);
|
1404 |
-
while (same < l && prevInput
|
1405 |
var from = sel.from, to = sel.to;
|
1406 |
if (same < prevInput.length)
|
1407 |
from = Pos(from.line, from.ch - (prevInput.length - same));
|
1408 |
else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
|
1409 |
to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
|
1410 |
-
var updateInput = cm.curOp.updateInput;
|
1411 |
-
makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
|
1412 |
-
origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
|
1413 |
|
|
|
|
|
|
|
|
|
1414 |
cm.curOp.updateInput = updateInput;
|
|
|
|
|
1415 |
if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
|
1416 |
else cm.display.prevInput = text;
|
1417 |
if (withOp) endOperation(cm);
|
@@ -1425,10 +1470,14 @@ window.CodeMirror = (function() {
|
|
1425 |
cm.display.prevInput = "";
|
1426 |
minimal = hasCopyEvent &&
|
1427 |
(doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
|
1428 |
-
|
1429 |
-
|
1430 |
if (cm.state.focused) selectInput(cm.display.input);
|
1431 |
-
|
|
|
|
|
|
|
|
|
1432 |
cm.display.inaccurateSelection = minimal;
|
1433 |
}
|
1434 |
|
@@ -1446,7 +1495,17 @@ window.CodeMirror = (function() {
|
|
1446 |
function registerEventHandlers(cm) {
|
1447 |
var d = cm.display;
|
1448 |
on(d.scroller, "mousedown", operation(cm, onMouseDown));
|
1449 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1450 |
on(d.lineSpace, "selectstart", function(e) {
|
1451 |
if (!eventInWidget(d, e)) e_preventDefault(e);
|
1452 |
});
|
@@ -1478,11 +1537,15 @@ window.CodeMirror = (function() {
|
|
1478 |
// Prevent wrapper from ever scrolling
|
1479 |
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
|
1480 |
|
|
|
1481 |
function onResize() {
|
1482 |
-
|
1483 |
-
|
1484 |
-
|
1485 |
-
|
|
|
|
|
|
|
1486 |
}
|
1487 |
on(window, "resize", onResize);
|
1488 |
// Above handler holds on to the editor and its data structures.
|
@@ -1496,7 +1559,7 @@ window.CodeMirror = (function() {
|
|
1496 |
setTimeout(unregister, 5000);
|
1497 |
|
1498 |
on(d.input, "keyup", operation(cm, function(e) {
|
1499 |
-
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1500 |
if (e.keyCode == 16) cm.doc.sel.shift = false;
|
1501 |
}));
|
1502 |
on(d.input, "input", bind(fastPoll, cm));
|
@@ -1506,7 +1569,7 @@ window.CodeMirror = (function() {
|
|
1506 |
on(d.input, "blur", bind(onBlur, cm));
|
1507 |
|
1508 |
function drag_(e) {
|
1509 |
-
if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
|
1510 |
e_stop(e);
|
1511 |
}
|
1512 |
if (cm.options.dragDrop) {
|
@@ -1545,9 +1608,7 @@ window.CodeMirror = (function() {
|
|
1545 |
|
1546 |
function eventInWidget(display, e) {
|
1547 |
for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
|
1548 |
-
if (!n) return true;
|
1549 |
-
if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
|
1550 |
-
n.parentNode == display.sizer && n != display.mover) return true;
|
1551 |
}
|
1552 |
}
|
1553 |
|
@@ -1557,7 +1618,7 @@ window.CodeMirror = (function() {
|
|
1557 |
var target = e_target(e);
|
1558 |
if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
|
1559 |
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
|
1560 |
-
target == display.scrollbarFiller) return null;
|
1561 |
}
|
1562 |
var x, y, space = getRect(display.lineSpace);
|
1563 |
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
|
@@ -1567,6 +1628,7 @@ window.CodeMirror = (function() {
|
|
1567 |
|
1568 |
var lastClick, lastDoubleClick;
|
1569 |
function onMouseDown(e) {
|
|
|
1570 |
var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
|
1571 |
sel.shift = e.shiftKey;
|
1572 |
|
@@ -1637,9 +1699,12 @@ window.CodeMirror = (function() {
|
|
1637 |
e_preventDefault(e);
|
1638 |
if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
|
1639 |
|
1640 |
-
var startstart = sel.from, startend = sel.to;
|
1641 |
|
1642 |
function doSelect(cur) {
|
|
|
|
|
|
|
1643 |
if (type == "single") {
|
1644 |
extendSelection(cm.doc, clipPos(doc, start), cur);
|
1645 |
return;
|
@@ -1687,8 +1752,6 @@ window.CodeMirror = (function() {
|
|
1687 |
|
1688 |
function done(e) {
|
1689 |
counter = Infinity;
|
1690 |
-
var cur = posFromMouse(cm, e);
|
1691 |
-
if (cur) doSelect(cur);
|
1692 |
e_preventDefault(e);
|
1693 |
focusInput(cm);
|
1694 |
off(document, "mousemove", move);
|
@@ -1704,11 +1767,41 @@ window.CodeMirror = (function() {
|
|
1704 |
on(document, "mouseup", up);
|
1705 |
}
|
1706 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1707 |
function onDrop(e) {
|
1708 |
var cm = this;
|
1709 |
-
if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
|
1710 |
return;
|
1711 |
e_preventDefault(e);
|
|
|
1712 |
var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
|
1713 |
if (!pos || isReadOnly(cm)) return;
|
1714 |
if (files && files.length && window.FileReader && window.File) {
|
@@ -1748,40 +1841,16 @@ window.CodeMirror = (function() {
|
|
1748 |
}
|
1749 |
}
|
1750 |
|
1751 |
-
function clickInGutter(cm, e) {
|
1752 |
-
var display = cm.display;
|
1753 |
-
try { var mX = e.clientX, mY = e.clientY; }
|
1754 |
-
catch(e) { return false; }
|
1755 |
-
|
1756 |
-
if (mX >= Math.floor(getRect(display.gutters).right)) return false;
|
1757 |
-
e_preventDefault(e);
|
1758 |
-
if (!hasHandler(cm, "gutterClick")) return true;
|
1759 |
-
|
1760 |
-
var lineBox = getRect(display.lineDiv);
|
1761 |
-
if (mY > lineBox.bottom) return true;
|
1762 |
-
mY -= lineBox.top - display.viewOffset;
|
1763 |
-
|
1764 |
-
for (var i = 0; i < cm.options.gutters.length; ++i) {
|
1765 |
-
var g = display.gutters.childNodes[i];
|
1766 |
-
if (g && getRect(g).right >= mX) {
|
1767 |
-
var line = lineAtHeight(cm.doc, mY);
|
1768 |
-
var gutter = cm.options.gutters[i];
|
1769 |
-
signalLater(cm, "gutterClick", cm, line, gutter, e);
|
1770 |
-
break;
|
1771 |
-
}
|
1772 |
-
}
|
1773 |
-
return true;
|
1774 |
-
}
|
1775 |
-
|
1776 |
function onDragStart(cm, e) {
|
1777 |
-
if (
|
|
|
1778 |
|
1779 |
var txt = cm.getSelection();
|
1780 |
e.dataTransfer.setData("Text", txt);
|
1781 |
|
1782 |
// Use dummy image instead of default browsers image.
|
1783 |
// Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
|
1784 |
-
if (e.dataTransfer.setDragImage) {
|
1785 |
var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
|
1786 |
if (opera) {
|
1787 |
img.width = img.height = 1;
|
@@ -1789,15 +1858,6 @@ window.CodeMirror = (function() {
|
|
1789 |
// Force a relayout, or Opera won't use our image for some obscure reason
|
1790 |
img._top = img.offsetTop;
|
1791 |
}
|
1792 |
-
if (safari) {
|
1793 |
-
if (cm.display.dragImg) {
|
1794 |
-
img = cm.display.dragImg;
|
1795 |
-
} else {
|
1796 |
-
cm.display.dragImg = img;
|
1797 |
-
img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
1798 |
-
cm.display.wrapper.appendChild(img);
|
1799 |
-
}
|
1800 |
-
}
|
1801 |
e.dataTransfer.setDragImage(img, 0, 0);
|
1802 |
if (opera) img.parentNode.removeChild(img);
|
1803 |
}
|
@@ -1810,6 +1870,7 @@ window.CodeMirror = (function() {
|
|
1810 |
if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
|
1811 |
if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
|
1812 |
if (gecko) updateDisplay(cm, []);
|
|
|
1813 |
}
|
1814 |
function setScrollLeft(cm, val, isScroller) {
|
1815 |
if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
|
@@ -1847,6 +1908,11 @@ window.CodeMirror = (function() {
|
|
1847 |
if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
|
1848 |
else if (dy == null) dy = e.wheelDelta;
|
1849 |
|
|
|
|
|
|
|
|
|
|
|
1850 |
// Webkit browsers on OS X abort momentum scrolls when the target
|
1851 |
// of the scroll event is removed from the scrollable element.
|
1852 |
// This hack (see related code in patchDisplay) makes sure the
|
@@ -1860,7 +1926,6 @@ window.CodeMirror = (function() {
|
|
1860 |
}
|
1861 |
}
|
1862 |
|
1863 |
-
var display = cm.display, scroll = display.scroller;
|
1864 |
// On some browsers, horizontal scrolling will cause redraws to
|
1865 |
// happen before the gutter has been realigned, causing it to
|
1866 |
// wriggle around in a most unseemly way. When we have an
|
@@ -1938,8 +2003,10 @@ window.CodeMirror = (function() {
|
|
1938 |
var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
|
1939 |
clearTimeout(maybeTransition);
|
1940 |
if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
|
1941 |
-
if (getKeyMap(cm.options.keyMap) == startMap)
|
1942 |
cm.options.keyMap = (next.call ? next.call(null, cm) : next);
|
|
|
|
|
1943 |
}, 50);
|
1944 |
|
1945 |
var name = keyName(e, true), handled = false;
|
@@ -1952,17 +2019,18 @@ window.CodeMirror = (function() {
|
|
1952 |
// 'go') bound to the keyname without 'Shift-'.
|
1953 |
handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
|
1954 |
|| lookupKey(name, keymaps, function(b) {
|
1955 |
-
if (typeof b == "string"
|
|
|
1956 |
});
|
1957 |
} else {
|
1958 |
handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
|
1959 |
}
|
1960 |
-
if (handled == "stop") handled = false;
|
1961 |
|
1962 |
if (handled) {
|
1963 |
e_preventDefault(e);
|
1964 |
restartBlink(cm);
|
1965 |
if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
|
|
|
1966 |
}
|
1967 |
return handled;
|
1968 |
}
|
@@ -1973,6 +2041,7 @@ window.CodeMirror = (function() {
|
|
1973 |
if (handled) {
|
1974 |
e_preventDefault(e);
|
1975 |
restartBlink(cm);
|
|
|
1976 |
}
|
1977 |
return handled;
|
1978 |
}
|
@@ -1982,7 +2051,7 @@ window.CodeMirror = (function() {
|
|
1982 |
var cm = this;
|
1983 |
if (!cm.state.focused) onFocus(cm);
|
1984 |
if (ie && e.keyCode == 27) { e.returnValue = false; }
|
1985 |
-
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1986 |
var code = e.keyCode;
|
1987 |
// IE does strange things with escape.
|
1988 |
cm.doc.sel.shift = code == 16 || e.shiftKey;
|
@@ -1998,7 +2067,7 @@ window.CodeMirror = (function() {
|
|
1998 |
|
1999 |
function onKeyPress(e) {
|
2000 |
var cm = this;
|
2001 |
-
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
2002 |
var keyCode = e.keyCode, charCode = e.charCode;
|
2003 |
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
|
2004 |
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
|
@@ -2008,6 +2077,7 @@ window.CodeMirror = (function() {
|
|
2008 |
this.doc.mode.electricChars.indexOf(ch) > -1)
|
2009 |
setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
|
2010 |
if (handleCharBinding(cm, e, ch)) return;
|
|
|
2011 |
fastPoll(cm);
|
2012 |
}
|
2013 |
|
@@ -2053,6 +2123,13 @@ window.CodeMirror = (function() {
|
|
2053 |
// Adds "Select all" to context menu in FF
|
2054 |
if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
|
2055 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2056 |
function rehide() {
|
2057 |
display.inputDiv.style.position = "relative";
|
2058 |
display.input.style.cssText = oldCSS;
|
@@ -2060,12 +2137,10 @@ window.CodeMirror = (function() {
|
|
2060 |
slowPoll(cm);
|
2061 |
|
2062 |
// Try to detect the user choosing select-all
|
2063 |
-
if (display.input.selectionStart != null
|
|
|
2064 |
clearTimeout(detectingSelectAll);
|
2065 |
-
var
|
2066 |
-
display.prevInput = " ";
|
2067 |
-
display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
|
2068 |
-
var poll = function(){
|
2069 |
if (display.prevInput == " " && display.input.selectionStart == 0)
|
2070 |
operation(cm, commands.selectAll)(cm);
|
2071 |
else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
|
@@ -2075,6 +2150,7 @@ window.CodeMirror = (function() {
|
|
2075 |
}
|
2076 |
}
|
2077 |
|
|
|
2078 |
if (captureMiddleClick) {
|
2079 |
e_stop(e);
|
2080 |
var mouseup = function() {
|
@@ -2089,10 +2165,11 @@ window.CodeMirror = (function() {
|
|
2089 |
|
2090 |
// UPDATING
|
2091 |
|
2092 |
-
|
|
|
2093 |
return Pos(change.from.line + change.text.length - 1,
|
2094 |
lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
|
2095 |
-
}
|
2096 |
|
2097 |
// Make sure a position will be valid after the given change.
|
2098 |
function clipPostChange(doc, change, pos) {
|
@@ -2134,21 +2211,21 @@ window.CodeMirror = (function() {
|
|
2134 |
return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
|
2135 |
}
|
2136 |
|
2137 |
-
function filterChange(doc, change) {
|
2138 |
var obj = {
|
2139 |
canceled: false,
|
2140 |
from: change.from,
|
2141 |
to: change.to,
|
2142 |
text: change.text,
|
2143 |
origin: change.origin,
|
2144 |
-
update: function(from, to, text, origin) {
|
2145 |
-
if (from) this.from = clipPos(doc, from);
|
2146 |
-
if (to) this.to = clipPos(doc, to);
|
2147 |
-
if (text) this.text = text;
|
2148 |
-
if (origin !== undefined) this.origin = origin;
|
2149 |
-
},
|
2150 |
cancel: function() { this.canceled = true; }
|
2151 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2152 |
signal(doc, "beforeChange", doc, obj);
|
2153 |
if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
|
2154 |
|
@@ -2165,7 +2242,7 @@ window.CodeMirror = (function() {
|
|
2165 |
}
|
2166 |
|
2167 |
if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
|
2168 |
-
change = filterChange(doc, change);
|
2169 |
if (!change) return;
|
2170 |
}
|
2171 |
|
@@ -2204,15 +2281,23 @@ window.CodeMirror = (function() {
|
|
2204 |
var hist = doc.history;
|
2205 |
var event = (type == "undo" ? hist.done : hist.undone).pop();
|
2206 |
if (!event) return;
|
2207 |
-
hist.dirtyCounter += type == "undo" ? -1 : 1;
|
2208 |
|
2209 |
var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
|
2210 |
-
anchorAfter: event.anchorBefore, headAfter: event.headBefore
|
|
|
2211 |
(type == "undo" ? hist.undone : hist.done).push(anti);
|
|
|
|
|
|
|
2212 |
|
2213 |
for (var i = event.changes.length - 1; i >= 0; --i) {
|
2214 |
var change = event.changes[i];
|
2215 |
change.origin = type;
|
|
|
|
|
|
|
|
|
|
|
2216 |
anti.changes.push(historyChangeFromChange(doc, change));
|
2217 |
|
2218 |
var after = i ? computeSelAfterChange(doc, change, null)
|
@@ -2282,6 +2367,9 @@ window.CodeMirror = (function() {
|
|
2282 |
});
|
2283 |
}
|
2284 |
|
|
|
|
|
|
|
2285 |
updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
|
2286 |
|
2287 |
if (!cm.options.lineWrapping) {
|
@@ -2408,7 +2496,8 @@ window.CodeMirror = (function() {
|
|
2408 |
sel.to = inv ? anchor : head;
|
2409 |
|
2410 |
if (doc.cm)
|
2411 |
-
doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
|
|
|
2412 |
|
2413 |
signalLater(doc, "cursorActivity", doc);
|
2414 |
}
|
@@ -2470,11 +2559,11 @@ window.CodeMirror = (function() {
|
|
2470 |
// SCROLLING
|
2471 |
|
2472 |
function scrollCursorIntoView(cm) {
|
2473 |
-
var coords = scrollPosIntoView(cm, cm.doc.sel.head);
|
2474 |
if (!cm.state.focused) return;
|
2475 |
-
var display = cm.display, box = getRect(display.sizer), doScroll = null
|
2476 |
-
if (coords.top +
|
2477 |
-
else if (coords.bottom +
|
2478 |
if (doScroll != null && !phantom) {
|
2479 |
var hidden = display.cursor.style.display == "none";
|
2480 |
if (hidden) {
|
@@ -2512,13 +2601,17 @@ window.CodeMirror = (function() {
|
|
2512 |
}
|
2513 |
|
2514 |
function calculateScrollPos(cm, x1, y1, x2, y2) {
|
2515 |
-
var display = cm.display,
|
2516 |
-
y1
|
2517 |
var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
|
2518 |
var docBottom = cm.doc.height + paddingVert(display);
|
2519 |
-
var atTop = y1 <
|
2520 |
-
if (y1 < screentop)
|
2521 |
-
|
|
|
|
|
|
|
|
|
2522 |
|
2523 |
var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
|
2524 |
x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
|
@@ -2534,7 +2627,8 @@ window.CodeMirror = (function() {
|
|
2534 |
}
|
2535 |
|
2536 |
function updateScrollPos(cm, left, top) {
|
2537 |
-
cm.curOp.updateScrollPos = {scrollLeft: left
|
|
|
2538 |
}
|
2539 |
|
2540 |
function addToScrollPos(cm, left, top) {
|
@@ -2548,7 +2642,7 @@ window.CodeMirror = (function() {
|
|
2548 |
|
2549 |
function indentLine(cm, n, how, aggressive) {
|
2550 |
var doc = cm.doc;
|
2551 |
-
if (
|
2552 |
if (how == "smart") {
|
2553 |
if (!cm.doc.mode.indent) how = "prev";
|
2554 |
else var state = getStateBefore(cm, n);
|
@@ -2571,6 +2665,8 @@ window.CodeMirror = (function() {
|
|
2571 |
indentation = curSpace + cm.options.indentUnit;
|
2572 |
} else if (how == "subtract") {
|
2573 |
indentation = curSpace - cm.options.indentUnit;
|
|
|
|
|
2574 |
}
|
2575 |
indentation = Math.max(0, indentation);
|
2576 |
|
@@ -2595,7 +2691,7 @@ window.CodeMirror = (function() {
|
|
2595 |
}
|
2596 |
|
2597 |
function findPosH(doc, pos, dir, unit, visually) {
|
2598 |
-
var line = pos.line, ch = pos.ch;
|
2599 |
var lineObj = getLine(doc, line);
|
2600 |
var possible = true;
|
2601 |
function findNextLine() {
|
@@ -2634,7 +2730,7 @@ window.CodeMirror = (function() {
|
|
2634 |
if (dir > 0 && !moveOnce(!first)) break;
|
2635 |
}
|
2636 |
}
|
2637 |
-
var result = skipAtomic(doc, Pos(line, ch),
|
2638 |
if (!possible) result.hitSide = true;
|
2639 |
return result;
|
2640 |
}
|
@@ -2659,7 +2755,7 @@ window.CodeMirror = (function() {
|
|
2659 |
function findWordAt(line, pos) {
|
2660 |
var start = pos.ch, end = pos.ch;
|
2661 |
if (line) {
|
2662 |
-
if (pos.
|
2663 |
var startChar = line.charAt(start);
|
2664 |
var check = isWordChar(startChar) ? isWordChar
|
2665 |
: /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
|
@@ -2680,6 +2776,7 @@ window.CodeMirror = (function() {
|
|
2680 |
// 'wrap f in an operation, performed on its `this` parameter'
|
2681 |
|
2682 |
CodeMirror.prototype = {
|
|
|
2683 |
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
|
2684 |
|
2685 |
setOption: function(option, value) {
|
@@ -2715,7 +2812,8 @@ window.CodeMirror = (function() {
|
|
2715 |
removeOverlay: operation(null, function(spec) {
|
2716 |
var overlays = this.state.overlays;
|
2717 |
for (var i = 0; i < overlays.length; ++i) {
|
2718 |
-
|
|
|
2719 |
overlays.splice(i, 1);
|
2720 |
this.state.modeGen++;
|
2721 |
regChange(this);
|
@@ -2725,7 +2823,7 @@ window.CodeMirror = (function() {
|
|
2725 |
}),
|
2726 |
|
2727 |
indentLine: operation(null, function(n, dir, aggressive) {
|
2728 |
-
if (typeof dir != "string") {
|
2729 |
if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
|
2730 |
else dir = dir ? "add" : "subtract";
|
2731 |
}
|
@@ -2740,10 +2838,10 @@ window.CodeMirror = (function() {
|
|
2740 |
|
2741 |
// Fetch the parser token for a given character. Useful for hacks
|
2742 |
// that want to inspect the mode state (say, for completion).
|
2743 |
-
getTokenAt: function(pos) {
|
2744 |
var doc = this.doc;
|
2745 |
pos = clipPos(doc, pos);
|
2746 |
-
var state = getStateBefore(this, pos.line), mode = this.doc.mode;
|
2747 |
var line = getLine(doc, pos.line);
|
2748 |
var stream = new StringStream(line.text, this.options.tabSize);
|
2749 |
while (stream.pos < pos.ch && !stream.eol()) {
|
@@ -2758,10 +2856,22 @@ window.CodeMirror = (function() {
|
|
2758 |
state: state};
|
2759 |
},
|
2760 |
|
2761 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2762 |
var doc = this.doc;
|
2763 |
line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
|
2764 |
-
return getStateBefore(this, line + 1);
|
2765 |
},
|
2766 |
|
2767 |
cursorCoords: function(start, mode) {
|
@@ -2781,6 +2891,19 @@ window.CodeMirror = (function() {
|
|
2781 |
return coordsChar(this, coords.left, coords.top);
|
2782 |
},
|
2783 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2784 |
defaultTextHeight: function() { return textHeight(this.display); },
|
2785 |
defaultCharWidth: function() { return charWidth(this.display); },
|
2786 |
|
@@ -2809,7 +2932,7 @@ window.CodeMirror = (function() {
|
|
2809 |
return changeLine(this, handle, function(line) {
|
2810 |
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
|
2811 |
if (!line[prop]) line[prop] = cls;
|
2812 |
-
else if (new RegExp("
|
2813 |
else line[prop] += " " + cls;
|
2814 |
return true;
|
2815 |
});
|
@@ -2822,9 +2945,10 @@ window.CodeMirror = (function() {
|
|
2822 |
if (!cur) return false;
|
2823 |
else if (cls == null) line[prop] = null;
|
2824 |
else {
|
2825 |
-
var
|
2826 |
-
if (
|
2827 |
-
|
|
|
2828 |
}
|
2829 |
return true;
|
2830 |
});
|
@@ -2872,7 +2996,7 @@ window.CodeMirror = (function() {
|
|
2872 |
if (left + node.offsetWidth > hspace)
|
2873 |
left = hspace - node.offsetWidth;
|
2874 |
}
|
2875 |
-
node.style.top =
|
2876 |
node.style.left = node.style.right = "";
|
2877 |
if (horiz == "right") {
|
2878 |
left = display.sizer.clientWidth - node.offsetWidth;
|
@@ -2940,7 +3064,8 @@ window.CodeMirror = (function() {
|
|
2940 |
sel.goalColumn = pos.left;
|
2941 |
}),
|
2942 |
|
2943 |
-
toggleOverwrite: function() {
|
|
|
2944 |
if (this.state.overwrite = !this.state.overwrite)
|
2945 |
this.display.cursor.className += " CodeMirror-overwrite";
|
2946 |
else
|
@@ -2958,15 +3083,19 @@ window.CodeMirror = (function() {
|
|
2958 |
clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
|
2959 |
},
|
2960 |
|
2961 |
-
scrollIntoView: function(pos, margin) {
|
2962 |
if (typeof pos == "number") pos = Pos(pos, 0);
|
|
|
|
|
|
|
2963 |
if (!pos || pos.line != null) {
|
2964 |
-
|
2965 |
-
|
2966 |
-
|
2967 |
-
scrollIntoView(this, pos.left, pos.top - margin, pos.right, pos.bottom + margin);
|
2968 |
}
|
2969 |
-
|
|
|
|
|
2970 |
|
2971 |
setSize: function(width, height) {
|
2972 |
function interpret(val) {
|
@@ -2993,6 +3122,7 @@ window.CodeMirror = (function() {
|
|
2993 |
old.cm = null;
|
2994 |
attachDoc(this, doc);
|
2995 |
clearCaches(this);
|
|
|
2996 |
updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
|
2997 |
return old;
|
2998 |
}),
|
@@ -3058,6 +3188,7 @@ window.CodeMirror = (function() {
|
|
3058 |
cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
|
3059 |
cm.refresh();
|
3060 |
}, true);
|
|
|
3061 |
option("lineNumbers", false, function(cm) {
|
3062 |
setGuttersForLineNumbers(cm.options);
|
3063 |
guttersChanged(cm);
|
@@ -3073,13 +3204,19 @@ window.CodeMirror = (function() {
|
|
3073 |
option("dragDrop", true);
|
3074 |
|
3075 |
option("cursorBlinkRate", 530);
|
|
|
3076 |
option("cursorHeight", 1);
|
3077 |
option("workTime", 100);
|
3078 |
option("workDelay", 100);
|
3079 |
option("flattenSpans", true);
|
3080 |
option("pollInterval", 100);
|
3081 |
option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
|
|
|
3082 |
option("viewportMargin", 10, function(cm){cm.refresh();}, true);
|
|
|
|
|
|
|
|
|
3083 |
|
3084 |
option("tabindex", null, function(cm, val) {
|
3085 |
cm.display.input.tabIndex = val || "";
|
@@ -3105,10 +3242,15 @@ window.CodeMirror = (function() {
|
|
3105 |
};
|
3106 |
|
3107 |
CodeMirror.resolveMode = function(spec) {
|
3108 |
-
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
|
3109 |
spec = mimeModes[spec];
|
3110 |
-
else if (typeof spec == "string" &&
|
|
|
|
|
|
|
|
|
3111 |
return CodeMirror.resolveMode("application/xml");
|
|
|
3112 |
if (typeof spec == "string") return {name: spec};
|
3113 |
else return spec || {name: "null"};
|
3114 |
};
|
@@ -3146,7 +3288,9 @@ window.CodeMirror = (function() {
|
|
3146 |
CodeMirror.defineExtension = function(name, func) {
|
3147 |
CodeMirror.prototype[name] = func;
|
3148 |
};
|
3149 |
-
|
|
|
|
|
3150 |
CodeMirror.defineOption = option;
|
3151 |
|
3152 |
var initHooks = [];
|
@@ -3197,6 +3341,10 @@ window.CodeMirror = (function() {
|
|
3197 |
var l = cm.getCursor().line;
|
3198 |
cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
|
3199 |
},
|
|
|
|
|
|
|
|
|
3200 |
undo: function(cm) {cm.undo();},
|
3201 |
redo: function(cm) {cm.redo();},
|
3202 |
goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
|
@@ -3292,7 +3440,7 @@ window.CodeMirror = (function() {
|
|
3292 |
"Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
|
3293 |
"Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
|
3294 |
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
|
3295 |
-
"Cmd-[": "indentLess", "Cmd-]": "indentMore",
|
3296 |
fallthrough: ["basic", "emacsy"]
|
3297 |
};
|
3298 |
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
|
@@ -3331,7 +3479,7 @@ window.CodeMirror = (function() {
|
|
3331 |
|
3332 |
for (var i = 0; i < maps.length; ++i) {
|
3333 |
var done = lookup(maps[i]);
|
3334 |
-
if (done) return done;
|
3335 |
}
|
3336 |
}
|
3337 |
function isModifierKey(event) {
|
@@ -3339,6 +3487,7 @@ window.CodeMirror = (function() {
|
|
3339 |
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
|
3340 |
}
|
3341 |
function keyName(event, noShift) {
|
|
|
3342 |
var name = keyNames[event.keyCode];
|
3343 |
if (name == null || event.altGraphKey) return false;
|
3344 |
if (event.altKey) name = "Alt-" + name;
|
@@ -3512,7 +3661,7 @@ window.CodeMirror = (function() {
|
|
3512 |
if (min != null && cm) regChange(cm, min, max + 1);
|
3513 |
this.lines.length = 0;
|
3514 |
this.explicitlyCleared = true;
|
3515 |
-
if (this.
|
3516 |
this.doc.cantEdit = false;
|
3517 |
if (cm) reCheckSelection(cm);
|
3518 |
}
|
@@ -3535,15 +3684,18 @@ window.CodeMirror = (function() {
|
|
3535 |
return from && {from: from, to: to};
|
3536 |
};
|
3537 |
|
3538 |
-
TextMarker.prototype.
|
3539 |
-
var
|
3540 |
-
|
3541 |
-
|
3542 |
-
|
3543 |
-
|
3544 |
-
|
3545 |
-
|
3546 |
-
|
|
|
|
|
|
|
3547 |
};
|
3548 |
|
3549 |
TextMarker.prototype.attachLine = function(line) {
|
@@ -3572,9 +3724,14 @@ window.CodeMirror = (function() {
|
|
3572 |
if (marker.replacedWith) {
|
3573 |
marker.collapsed = true;
|
3574 |
marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
|
|
|
3575 |
}
|
3576 |
if (marker.collapsed) sawCollapsedSpans = true;
|
3577 |
|
|
|
|
|
|
|
|
|
3578 |
var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
|
3579 |
doc.iter(curLine, to.line + 1, function(line) {
|
3580 |
if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
|
@@ -3639,11 +3796,6 @@ window.CodeMirror = (function() {
|
|
3639 |
SharedTextMarker.prototype.find = function() {
|
3640 |
return this.primary.find();
|
3641 |
};
|
3642 |
-
SharedTextMarker.prototype.getOptions = function(copyWidget) {
|
3643 |
-
var inner = this.primary.getOptions(copyWidget);
|
3644 |
-
inner.shared = true;
|
3645 |
-
return inner;
|
3646 |
-
};
|
3647 |
|
3648 |
function markTextShared(doc, from, to, options, type) {
|
3649 |
options = copyObj(options);
|
@@ -3746,6 +3898,13 @@ window.CodeMirror = (function() {
|
|
3746 |
}
|
3747 |
}
|
3748 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3749 |
|
3750 |
var newMarkers = [first];
|
3751 |
if (!sameLine) {
|
@@ -3840,6 +3999,7 @@ window.CodeMirror = (function() {
|
|
3840 |
sp = sps[i];
|
3841 |
if (!sp.marker.collapsed) continue;
|
3842 |
if (sp.from == null) return true;
|
|
|
3843 |
if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
|
3844 |
return true;
|
3845 |
}
|
@@ -3853,7 +4013,7 @@ window.CodeMirror = (function() {
|
|
3853 |
return true;
|
3854 |
for (var sp, i = 0; i < line.markedSpans.length; ++i) {
|
3855 |
sp = line.markedSpans[i];
|
3856 |
-
if (sp.marker.collapsed && sp.from == span.to &&
|
3857 |
(sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
|
3858 |
lineIsHiddenInner(doc, line, sp)) return true;
|
3859 |
}
|
@@ -3965,25 +4125,25 @@ window.CodeMirror = (function() {
|
|
3965 |
function runMode(cm, text, mode, state, f) {
|
3966 |
var flattenSpans = mode.flattenSpans;
|
3967 |
if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
|
3968 |
-
var
|
3969 |
-
var stream = new StringStream(text, cm.options.tabSize);
|
3970 |
if (text == "" && mode.blankLine) mode.blankLine(state);
|
3971 |
while (!stream.eol()) {
|
3972 |
-
|
3973 |
-
if (stream.pos > 5000) {
|
3974 |
flattenSpans = false;
|
3975 |
// Webkit seems to refuse to render text nodes longer than 57444 characters
|
3976 |
stream.pos = Math.min(text.length, stream.start + 50000);
|
3977 |
style = null;
|
|
|
|
|
3978 |
}
|
3979 |
-
var substr = stream.current();
|
3980 |
-
stream.start = stream.pos;
|
3981 |
if (!flattenSpans || curStyle != style) {
|
3982 |
-
if (
|
3983 |
-
|
3984 |
-
}
|
|
|
3985 |
}
|
3986 |
-
if (
|
3987 |
}
|
3988 |
|
3989 |
function highlightLine(cm, line, state) {
|
@@ -3991,27 +4151,24 @@ window.CodeMirror = (function() {
|
|
3991 |
// mode/overlays that it is based on (for easy invalidation).
|
3992 |
var st = [cm.state.modeGen];
|
3993 |
// Compute the base array of styles
|
3994 |
-
runMode(cm, line.text, cm.doc.mode, state, function(
|
3995 |
|
3996 |
// Run overlays, adjust style array.
|
3997 |
for (var o = 0; o < cm.state.overlays.length; ++o) {
|
3998 |
-
var overlay = cm.state.overlays[o], i = 1;
|
3999 |
-
runMode(cm, line.text, overlay.mode, true, function(
|
4000 |
-
var start = i
|
4001 |
// Ensure there's a token end at the current position, and that i points at it
|
4002 |
-
while (
|
4003 |
-
var
|
4004 |
-
if (
|
4005 |
-
|
4006 |
-
} else {
|
4007 |
-
st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
|
4008 |
-
len = 0;
|
4009 |
-
}
|
4010 |
i += 2;
|
|
|
4011 |
}
|
4012 |
if (!style) return;
|
4013 |
if (overlay.opaque) {
|
4014 |
-
st.splice(start, i - start,
|
4015 |
i = start + 2;
|
4016 |
} else {
|
4017 |
for (; start < i; start += 2) {
|
@@ -4037,7 +4194,7 @@ window.CodeMirror = (function() {
|
|
4037 |
var mode = cm.doc.mode;
|
4038 |
var stream = new StringStream(line.text, cm.options.tabSize);
|
4039 |
if (line.text == "" && mode.blankLine) mode.blankLine(state);
|
4040 |
-
while (!stream.eol() && stream.pos <=
|
4041 |
mode.token(stream, state);
|
4042 |
stream.start = stream.pos;
|
4043 |
}
|
@@ -4051,37 +4208,31 @@ window.CodeMirror = (function() {
|
|
4051 |
}
|
4052 |
|
4053 |
function lineContent(cm, realLine, measure) {
|
4054 |
-
var merged, line = realLine,
|
4055 |
-
while (merged = collapsedSpanAtStart(line))
|
4056 |
-
simple = false;
|
4057 |
line = getLine(cm.doc, merged.find().from.line);
|
4058 |
-
if (!lineBefore) lineBefore = line;
|
4059 |
-
}
|
4060 |
|
4061 |
var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
|
4062 |
-
measure: null,
|
4063 |
if (line.textClass) builder.pre.className = line.textClass;
|
4064 |
|
4065 |
do {
|
|
|
4066 |
builder.measure = line == realLine && measure;
|
4067 |
builder.pos = 0;
|
4068 |
builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
|
4069 |
if ((ie || webkit) && cm.getOption("lineWrapping"))
|
4070 |
builder.addToken = buildTokenSplitSpaces(builder.addToken);
|
4071 |
-
if (measure && sawBefore && line != realLine && !builder.addedOne) {
|
4072 |
-
measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
|
4073 |
-
builder.addedOne = true;
|
4074 |
-
}
|
4075 |
var next = insertLineContent(line, builder, getLineStyles(cm, line));
|
4076 |
-
|
4077 |
-
|
4078 |
-
|
4079 |
-
simple = false;
|
4080 |
}
|
|
|
4081 |
} while (next);
|
4082 |
|
4083 |
-
if (measure && !builder.
|
4084 |
-
measure[0] = builder.pre.appendChild(
|
4085 |
if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
|
4086 |
builder.pre.appendChild(document.createTextNode("\u00a0"));
|
4087 |
|
@@ -4150,8 +4301,7 @@ window.CodeMirror = (function() {
|
|
4150 |
if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
|
4151 |
ch = text.slice(i, i + 2);
|
4152 |
++i;
|
4153 |
-
} else if (i && wrapping &&
|
4154 |
-
spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
|
4155 |
builder.pre.appendChild(elt("wbr"));
|
4156 |
}
|
4157 |
var span = builder.measure[builder.pos] =
|
@@ -4165,7 +4315,7 @@ window.CodeMirror = (function() {
|
|
4165 |
span.style.whiteSpace = "normal";
|
4166 |
builder.pos += ch.length;
|
4167 |
}
|
4168 |
-
if (text.length) builder.
|
4169 |
}
|
4170 |
|
4171 |
function buildTokenSplitSpaces(inner) {
|
@@ -4183,11 +4333,12 @@ window.CodeMirror = (function() {
|
|
4183 |
function buildCollapsedSpan(builder, size, widget) {
|
4184 |
if (widget) {
|
4185 |
if (!builder.display) widget = widget.cloneNode(true);
|
4186 |
-
builder.
|
4187 |
-
|
4188 |
-
|
4189 |
-
builder.
|
4190 |
}
|
|
|
4191 |
}
|
4192 |
builder.pos += size;
|
4193 |
}
|
@@ -4195,15 +4346,14 @@ window.CodeMirror = (function() {
|
|
4195 |
// Outputs a number of spans to make up a line, taking highlighting
|
4196 |
// and marked text into account.
|
4197 |
function insertLineContent(line, builder, styles) {
|
4198 |
-
var spans = line.markedSpans;
|
4199 |
if (!spans) {
|
4200 |
for (var i = 1; i < styles.length; i+=2)
|
4201 |
-
builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
|
4202 |
return;
|
4203 |
}
|
4204 |
|
4205 |
-
var
|
4206 |
-
var pos = 0, i = 1, text = "", style;
|
4207 |
var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
|
4208 |
for (;;) {
|
4209 |
if (nextChange == pos) { // Update current marker set
|
@@ -4217,7 +4367,7 @@ window.CodeMirror = (function() {
|
|
4217 |
if (m.className) spanStyle += " " + m.className;
|
4218 |
if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
|
4219 |
if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
|
4220 |
-
if (m.collapsed && (!collapsed || collapsed.marker.
|
4221 |
collapsed = sp;
|
4222 |
} else if (sp.from > pos && nextChange > sp.from) {
|
4223 |
nextChange = sp.from;
|
@@ -4247,7 +4397,8 @@ window.CodeMirror = (function() {
|
|
4247 |
pos = end;
|
4248 |
spanStartStyle = "";
|
4249 |
}
|
4250 |
-
text =
|
|
|
4251 |
}
|
4252 |
}
|
4253 |
}
|
@@ -4439,6 +4590,7 @@ window.CodeMirror = (function() {
|
|
4439 |
this.scrollTop = this.scrollLeft = 0;
|
4440 |
this.cantEdit = false;
|
4441 |
this.history = makeHistory();
|
|
|
4442 |
this.frontier = firstLine;
|
4443 |
var start = Pos(firstLine, 0);
|
4444 |
this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
|
@@ -4450,6 +4602,7 @@ window.CodeMirror = (function() {
|
|
4450 |
};
|
4451 |
|
4452 |
Doc.prototype = createObj(BranchChunk.prototype, {
|
|
|
4453 |
iter: function(from, to, op) {
|
4454 |
if (op) this.iterN(from - this.first, to - from, op);
|
4455 |
else this.iterN(this.first, this.first + this.size, from);
|
@@ -4490,8 +4643,8 @@ window.CodeMirror = (function() {
|
|
4490 |
replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
|
4491 |
},
|
4492 |
removeLine: function(line) {
|
4493 |
-
if (
|
4494 |
-
|
4495 |
},
|
4496 |
|
4497 |
getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
|
@@ -4538,20 +4691,25 @@ window.CodeMirror = (function() {
|
|
4538 |
var hist = this.history;
|
4539 |
return {undo: hist.done.length, redo: hist.undone.length};
|
4540 |
},
|
4541 |
-
clearHistory: function() {this.history = makeHistory();},
|
4542 |
|
4543 |
markClean: function() {
|
4544 |
-
this.
|
|
|
|
|
4545 |
this.history.lastOp = this.history.lastOrigin = null;
|
|
|
|
|
|
|
|
|
4546 |
},
|
4547 |
-
isClean: function () {return this.history.dirtyCounter == 0;},
|
4548 |
|
4549 |
getHistory: function() {
|
4550 |
return {done: copyHistoryArray(this.history.done),
|
4551 |
undone: copyHistoryArray(this.history.undone)};
|
4552 |
},
|
4553 |
setHistory: function(histData) {
|
4554 |
-
var hist = this.history = makeHistory();
|
4555 |
hist.done = histData.done.slice(0);
|
4556 |
hist.undone = histData.undone.slice(0);
|
4557 |
},
|
@@ -4781,7 +4939,7 @@ window.CodeMirror = (function() {
|
|
4781 |
|
4782 |
// HISTORY
|
4783 |
|
4784 |
-
function makeHistory() {
|
4785 |
return {
|
4786 |
// Arrays of history events. Doing something adds an event to
|
4787 |
// done and clears undo. Undoing moves events from done to
|
@@ -4791,7 +4949,7 @@ window.CodeMirror = (function() {
|
|
4791 |
// event
|
4792 |
lastTime: 0, lastOp: null, lastOrigin: null,
|
4793 |
// Used by the isClean() method
|
4794 |
-
|
4795 |
};
|
4796 |
}
|
4797 |
|
@@ -4819,7 +4977,8 @@ window.CodeMirror = (function() {
|
|
4819 |
if (cur &&
|
4820 |
(hist.lastOp == opId ||
|
4821 |
hist.lastOrigin == change.origin && change.origin &&
|
4822 |
-
((change.origin.charAt(0) == "+" && hist.lastTime > time -
|
|
|
4823 |
// Merge this change into the last event
|
4824 |
var last = lst(cur.changes);
|
4825 |
if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
|
@@ -4834,17 +4993,13 @@ window.CodeMirror = (function() {
|
|
4834 |
} else {
|
4835 |
// Can not be merged, start a new event.
|
4836 |
cur = {changes: [historyChangeFromChange(doc, change)],
|
|
|
4837 |
anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
|
4838 |
anchorAfter: selAfter.anchor, headAfter: selAfter.head};
|
4839 |
hist.done.push(cur);
|
|
|
4840 |
while (hist.done.length > hist.undoDepth)
|
4841 |
hist.done.shift();
|
4842 |
-
if (hist.dirtyCounter < 0)
|
4843 |
-
// The user has made a change after undoing past the last clean state.
|
4844 |
-
// We can never get back to a clean state now until markClean() is called.
|
4845 |
-
hist.dirtyCounter = NaN;
|
4846 |
-
else
|
4847 |
-
hist.dirtyCounter++;
|
4848 |
}
|
4849 |
hist.lastTime = time;
|
4850 |
hist.lastOp = opId;
|
@@ -4959,6 +5114,9 @@ window.CodeMirror = (function() {
|
|
4959 |
if (e.stopPropagation) e.stopPropagation();
|
4960 |
else e.cancelBubble = true;
|
4961 |
}
|
|
|
|
|
|
|
4962 |
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
|
4963 |
CodeMirror.e_stop = e_stop;
|
4964 |
CodeMirror.e_preventDefault = e_preventDefault;
|
@@ -5025,6 +5183,11 @@ window.CodeMirror = (function() {
|
|
5025 |
delayedCallbacks.push(bnd(arr[i]));
|
5026 |
}
|
5027 |
|
|
|
|
|
|
|
|
|
|
|
5028 |
function fireDelayed() {
|
5029 |
--delayedCallbackDepth;
|
5030 |
var delayed = delayedCallbacks;
|
@@ -5079,7 +5242,11 @@ window.CodeMirror = (function() {
|
|
5079 |
if (ios) { // Mobile Safari apparently has a bug where select() is broken.
|
5080 |
node.selectionStart = 0;
|
5081 |
node.selectionEnd = node.value.length;
|
5082 |
-
} else
|
|
|
|
|
|
|
|
|
5083 |
}
|
5084 |
|
5085 |
function indexOf(collection, elt) {
|
@@ -5113,7 +5280,7 @@ window.CodeMirror = (function() {
|
|
5113 |
return function(){return f.apply(null, args);};
|
5114 |
}
|
5115 |
|
5116 |
-
var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
|
5117 |
function isWordChar(ch) {
|
5118 |
return /\w/.test(ch) || ch > "\x80" &&
|
5119 |
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
|
@@ -5174,13 +5341,24 @@ window.CodeMirror = (function() {
|
|
5174 |
// word wrapping between certain characters *only* if a new inline
|
5175 |
// element is started between them. This makes it hard to reliably
|
5176 |
// measure the position of things, since that requires inserting an
|
5177 |
-
// extra span. This terribly fragile set of
|
5178 |
// character combinations that suffer from this phenomenon on the
|
5179 |
// various browsers.
|
5180 |
-
|
5181 |
-
if (gecko)
|
5182 |
-
|
5183 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5184 |
|
5185 |
var knownScrollbarWidth;
|
5186 |
function scrollbarWidth(measure) {
|
@@ -5299,6 +5477,40 @@ window.CodeMirror = (function() {
|
|
5299 |
return Pos(lineN, ch);
|
5300 |
}
|
5301 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5302 |
// This is somewhat involved. It is needed in order to move
|
5303 |
// 'visually' through bi-directional text -- i.e., pressing left
|
5304 |
// should make the cursor go left, even when in RTL text. The
|
@@ -5308,37 +5520,24 @@ window.CodeMirror = (function() {
|
|
5308 |
function moveVisually(line, start, dir, byUnit) {
|
5309 |
var bidi = getOrder(line);
|
5310 |
if (!bidi) return moveLogically(line, start, dir, byUnit);
|
5311 |
-
var
|
5312 |
-
|
5313 |
-
|
5314 |
-
|
5315 |
-
|
5316 |
-
|
5317 |
-
|
5318 |
-
|
5319 |
-
|
5320 |
-
(sticky && (part.from == start || part.to == start))) break;
|
5321 |
-
}
|
5322 |
-
var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
|
5323 |
-
|
5324 |
-
while (target != null) {
|
5325 |
-
if (part.level % 2 == linedir) {
|
5326 |
-
if (target < part.from || target > part.to) {
|
5327 |
-
part = bidi[i += dir];
|
5328 |
-
target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
|
5329 |
-
} else break;
|
5330 |
} else {
|
5331 |
-
|
5332 |
-
|
5333 |
-
|
5334 |
-
|
5335 |
-
|
5336 |
-
target = part
|
5337 |
-
} else break;
|
5338 |
}
|
5339 |
}
|
5340 |
-
|
5341 |
-
return target < 0 || target > line.text.length ? null : target;
|
5342 |
}
|
5343 |
|
5344 |
function moveLogically(line, start, dir, byUnit) {
|
@@ -5510,7 +5709,7 @@ window.CodeMirror = (function() {
|
|
5510 |
|
5511 |
// THE END
|
5512 |
|
5513 |
-
CodeMirror.version = "3.
|
5514 |
|
5515 |
return CodeMirror;
|
5516 |
})();
|
1 |
+
// CodeMirror version 3.14
|
2 |
//
|
3 |
// CodeMirror is the only global var we claim
|
4 |
window.CodeMirror = (function() {
|
94 |
function makeDisplay(place, docStart) {
|
95 |
var d = {};
|
96 |
|
97 |
+
var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;");
|
98 |
if (webkit) input.style.width = "1000px";
|
99 |
else input.setAttribute("wrap", "off");
|
100 |
// if border: 0; -- iOS fails to open keyboard (issue #1287)
|
101 |
if (ios) input.style.border = "1px solid black";
|
102 |
+
input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
|
103 |
|
104 |
// Wraps and hides input textarea
|
105 |
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
|
107 |
d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
|
108 |
d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
|
109 |
d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
|
110 |
+
d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
|
111 |
// DIVs containing the selection and the actual code
|
112 |
+
d.lineDiv = elt("div", null, "CodeMirror-code");
|
113 |
d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
|
114 |
// Blinky cursor, and element used to ensure cursor fits at the end of a line
|
115 |
d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
|
129 |
// Will contain the gutters, if any
|
130 |
d.gutters = elt("div", null, "CodeMirror-gutters");
|
131 |
d.lineGutter = null;
|
|
|
|
|
132 |
// Provides scrolling
|
133 |
+
d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
|
134 |
d.scroller.setAttribute("tabIndex", "-1");
|
135 |
// The element in which the editor lives.
|
136 |
d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
|
137 |
+
d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
|
138 |
// Work around IE7 z-index bug
|
139 |
if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
|
140 |
if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
|
165 |
d.pollingFast = false;
|
166 |
// Self-resetting timeout for the poller
|
167 |
d.poll = new Delayed();
|
|
|
|
|
168 |
|
169 |
d.cachedCharWidth = d.cachedTextHeight = null;
|
170 |
d.measureLineCache = [];
|
213 |
estimateLineHeights(cm);
|
214 |
regChange(cm);
|
215 |
clearCaches(cm);
|
216 |
+
setTimeout(function(){updateScrollbars(cm);}, 100);
|
217 |
}
|
218 |
|
219 |
function estimateHeight(cm) {
|
238 |
}
|
239 |
|
240 |
function keyMapChanged(cm) {
|
241 |
+
var map = keyMap[cm.options.keyMap], style = map.style;
|
242 |
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
|
243 |
(style ? " cm-keymap-" + style : "");
|
244 |
+
cm.state.disableInput = map.disableInput;
|
245 |
}
|
246 |
|
247 |
function themeChanged(cm) {
|
253 |
function guttersChanged(cm) {
|
254 |
updateGutters(cm);
|
255 |
regChange(cm);
|
256 |
+
setTimeout(function(){alignHorizontally(cm);}, 20);
|
257 |
}
|
258 |
|
259 |
function updateGutters(cm) {
|
320 |
|
321 |
// Re-synchronize the fake scrollbars with the actual size of the
|
322 |
// content. Optionally force a scrollTop.
|
323 |
+
function updateScrollbars(cm) {
|
324 |
+
var d = cm.display, docHeight = cm.doc.height;
|
325 |
var totalHeight = docHeight + paddingVert(d);
|
326 |
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
|
327 |
+
d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
|
328 |
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
|
329 |
+
var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
|
330 |
+
var needsV = scrollHeight > (d.scroller.clientHeight + 1);
|
331 |
if (needsV) {
|
332 |
d.scrollbarV.style.display = "block";
|
333 |
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
|
344 |
d.scrollbarFiller.style.display = "block";
|
345 |
d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
|
346 |
} else d.scrollbarFiller.style.display = "";
|
347 |
+
if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
|
348 |
+
d.gutterFiller.style.display = "block";
|
349 |
+
d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px";
|
350 |
+
d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
|
351 |
+
} else d.gutterFiller.style.display = "";
|
352 |
|
353 |
if (mac_geLion && scrollbarWidth(d.measure) === 0)
|
354 |
d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
|
404 |
// DISPLAY DRAWING
|
405 |
|
406 |
function updateDisplay(cm, changes, viewPort) {
|
407 |
+
var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
|
408 |
+
var visible = visibleLines(cm.display, cm.doc, viewPort);
|
409 |
+
for (;;) {
|
410 |
+
if (!updateDisplayInner(cm, changes, visible)) break;
|
411 |
+
updated = true;
|
412 |
+
updateSelection(cm);
|
413 |
+
updateScrollbars(cm);
|
414 |
+
|
415 |
+
// Clip forced viewport to actual scrollable area
|
416 |
+
if (viewPort)
|
417 |
+
viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
|
418 |
+
typeof viewPort == "number" ? viewPort : viewPort.top);
|
419 |
+
visible = visibleLines(cm.display, cm.doc, viewPort);
|
420 |
+
if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
|
421 |
+
break;
|
422 |
+
changes = [];
|
423 |
+
}
|
424 |
+
|
425 |
if (updated) {
|
426 |
signalLater(cm, "update", cm);
|
427 |
if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
|
428 |
signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
|
429 |
}
|
|
|
|
|
|
|
430 |
return updated;
|
431 |
}
|
432 |
|
433 |
// Uses a set of changes plus the current scroll position to
|
434 |
// determine which DOM updates have to be made, and makes the
|
435 |
// updates.
|
436 |
+
function updateDisplayInner(cm, changes, visible) {
|
437 |
var display = cm.display, doc = cm.doc;
|
438 |
if (!display.wrapper.clientWidth) {
|
439 |
display.showingFrom = display.showingTo = doc.first;
|
441 |
return;
|
442 |
}
|
443 |
|
|
|
|
|
|
|
|
|
444 |
// Bail out if the visible area is already rendered and nothing changed.
|
445 |
if (changes.length == 0 &&
|
446 |
visible.from > display.showingFrom && visible.to < display.showingTo)
|
501 |
}
|
502 |
intact.sort(function(a, b) {return a.from - b.from;});
|
503 |
|
504 |
+
// Avoid crashing on IE's "unspecified error" when in iframes
|
505 |
+
try {
|
506 |
+
var focused = document.activeElement;
|
507 |
+
} catch(e) {}
|
508 |
if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
|
509 |
patchDisplay(cm, from, to, intact, positionsChangedFrom);
|
510 |
display.lineDiv.style.display = "";
|
511 |
+
if (focused && document.activeElement != focused && focused.offsetHeight) focused.focus();
|
512 |
|
513 |
var different = from != display.showingFrom || to != display.showingTo ||
|
514 |
display.lastSizeC != display.wrapper.clientHeight;
|
515 |
// This is just a bogus formula that detects when the editor is
|
516 |
// resized or the font size changes.
|
517 |
+
if (different) {
|
518 |
+
display.lastSizeC = display.wrapper.clientHeight;
|
519 |
+
startWorker(cm, 400);
|
520 |
+
}
|
521 |
display.showingFrom = from; display.showingTo = to;
|
|
|
522 |
|
523 |
var prevBottom = display.lineDiv.offsetTop;
|
524 |
for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
|
541 |
}
|
542 |
updateViewOffset(cm);
|
543 |
|
|
|
|
|
544 |
return true;
|
545 |
}
|
546 |
|
607 |
if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
|
608 |
if (lineIsHidden(cm.doc, line)) {
|
609 |
if (line.height != 0) updateLineHeight(line, 0);
|
610 |
+
if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
|
611 |
+
var w = line.widgets[i];
|
612 |
+
if (w.showIfHidden) {
|
613 |
var prev = cur.previousSibling;
|
614 |
if (/pre/i.test(prev.nodeName)) {
|
615 |
var wrap = elt("div", null, null, "position: relative");
|
617 |
wrap.appendChild(prev);
|
618 |
prev = wrap;
|
619 |
}
|
620 |
+
var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
|
621 |
+
if (!w.handleMouseEvents) wnode.ignoreEvents = true;
|
622 |
+
positionLineWidget(w, wnode, prev, dims);
|
623 |
}
|
624 |
+
}
|
625 |
} else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
|
626 |
// This line is intact. Skip to the actual node. Update its
|
627 |
// line number if needed.
|
664 |
|
665 |
if (reuse) {
|
666 |
reuse.alignable = null;
|
667 |
+
var isOk = true, widgetsSeen = 0, insertBefore = null;
|
668 |
for (var n = reuse.firstChild, next; n; n = next) {
|
669 |
next = n.nextSibling;
|
670 |
if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
|
671 |
reuse.removeChild(n);
|
672 |
} else {
|
673 |
for (var i = 0, first = true; i < line.widgets.length; ++i) {
|
674 |
+
var widget = line.widgets[i];
|
675 |
+
if (!widget.above) { insertBefore = n; first = false; }
|
676 |
if (widget.node == n.firstChild) {
|
677 |
positionLineWidget(widget, n, reuse, dims);
|
678 |
++widgetsSeen;
|
|
|
679 |
break;
|
680 |
}
|
681 |
}
|
682 |
if (i == line.widgets.length) { isOk = false; break; }
|
683 |
}
|
684 |
}
|
685 |
+
reuse.insertBefore(lineElement, insertBefore);
|
686 |
if (isOk && widgetsSeen == line.widgets.length) {
|
687 |
wrap = reuse;
|
688 |
reuse.className = line.wrapClass || "";
|
717 |
if (ie_lt8) wrap.style.zIndex = 2;
|
718 |
if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
|
719 |
var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
|
720 |
+
if (!widget.handleMouseEvents) node.ignoreEvents = true;
|
721 |
positionLineWidget(widget, node, wrap, dims);
|
722 |
if (widget.above)
|
723 |
wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
|
761 |
display.selectionDiv.style.display = "none";
|
762 |
|
763 |
// Move the hidden textarea near the cursor to prevent scrolling artifacts
|
764 |
+
if (cm.options.moveInputWithCursor) {
|
765 |
+
var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
|
766 |
+
var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
|
767 |
+
display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
|
768 |
+
headPos.top + lineOff.top - wrapOff.top)) + "px";
|
769 |
+
display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
|
770 |
+
headPos.left + lineOff.left - wrapOff.left)) + "px";
|
771 |
+
}
|
772 |
}
|
773 |
|
774 |
// No selection, plain cursor
|
800 |
"px; height: " + (bottom - top) + "px"));
|
801 |
}
|
802 |
|
803 |
+
function drawForLine(line, fromArg, toArg) {
|
804 |
var lineObj = getLine(doc, line);
|
805 |
+
var lineLen = lineObj.text.length;
|
806 |
+
var start, end;
|
807 |
+
function coords(ch, bias) {
|
808 |
+
return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
|
809 |
}
|
810 |
|
811 |
iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
|
812 |
+
var leftPos = coords(from, "left"), rightPos, left, right;
|
813 |
+
if (from == to) {
|
814 |
+
rightPos = leftPos;
|
815 |
+
left = right = leftPos.left;
|
816 |
+
} else {
|
817 |
+
rightPos = coords(to - 1, "right");
|
818 |
+
if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
|
819 |
+
left = leftPos.left;
|
820 |
+
right = rightPos.right;
|
821 |
+
}
|
822 |
+
if (fromArg == null && from == 0) left = pl;
|
823 |
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
|
824 |
add(left, leftPos.top, null, leftPos.bottom);
|
825 |
left = pl;
|
826 |
if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
|
827 |
}
|
828 |
if (toArg == null && to == lineLen) right = clientWidth;
|
829 |
+
if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
|
830 |
+
start = leftPos;
|
831 |
+
if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
|
832 |
+
end = rightPos;
|
833 |
if (left < pl + 1) left = pl;
|
834 |
add(left, rightPos.top, right - left, rightPos.bottom);
|
835 |
});
|
836 |
+
return {start: start, end: end};
|
837 |
}
|
838 |
|
839 |
if (sel.from.line == sel.to.line) {
|
840 |
drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
|
841 |
} else {
|
842 |
+
var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
|
843 |
+
var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
|
844 |
+
var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
|
845 |
+
var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
|
846 |
+
if (singleVLine) {
|
847 |
+
if (leftEnd.top < rightStart.top - 2) {
|
848 |
+
add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
|
849 |
+
add(pl, rightStart.top, rightStart.left, rightStart.bottom);
|
850 |
+
} else {
|
851 |
+
add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
|
852 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
853 |
}
|
854 |
+
if (leftEnd.bottom < rightStart.top)
|
855 |
+
add(pl, leftEnd.bottom, null, rightStart.top);
|
856 |
}
|
857 |
|
858 |
removeChildrenAndAdd(display.selectionDiv, fragment);
|
861 |
|
862 |
// Cursor-blinking
|
863 |
function restartBlink(cm) {
|
864 |
+
if (!cm.state.focused) return;
|
865 |
var display = cm.display;
|
866 |
clearInterval(display.blinker);
|
867 |
var on = true;
|
868 |
display.cursor.style.visibility = display.otherCursor.style.visibility = "";
|
869 |
display.blinker = setInterval(function() {
|
|
|
870 |
display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
|
871 |
}, cm.options.cursorBlinkRate);
|
872 |
}
|
918 |
// valid state. If that fails, it returns the line with the
|
919 |
// smallest indentation, which tends to need the least context to
|
920 |
// parse correctly.
|
921 |
+
function findStartLine(cm, n, precise) {
|
922 |
var minindent, minline, doc = cm.doc;
|
923 |
for (var search = n, lim = n - 100; search > lim; --search) {
|
924 |
if (search <= doc.first) return doc.first;
|
925 |
var line = getLine(doc, search - 1);
|
926 |
+
if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
|
927 |
var indented = countColumn(line.text, null, cm.options.tabSize);
|
928 |
if (minline == null || minindent > indented) {
|
929 |
minline = search - 1;
|
933 |
return minline;
|
934 |
}
|
935 |
|
936 |
+
function getStateBefore(cm, n, precise) {
|
937 |
var doc = cm.doc, display = cm.display;
|
938 |
if (!doc.mode.startState) return true;
|
939 |
+
var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
|
940 |
if (!state) state = startState(doc.mode);
|
941 |
else state = copyState(doc.mode, state);
|
942 |
doc.iter(pos, n, function(line) {
|
957 |
return e.offsetLeft;
|
958 |
}
|
959 |
|
960 |
+
function measureChar(cm, line, ch, data, bias) {
|
961 |
var dir = -1;
|
962 |
data = data || measureLine(cm, line);
|
963 |
|
966 |
if (r) break;
|
967 |
if (dir < 0 && pos == 0) dir = 1;
|
968 |
}
|
969 |
+
var rightV = (pos < ch || bias == "right") && r.topRight != null;
|
970 |
return {left: pos < ch ? r.right : r.left,
|
971 |
right: pos > ch ? r.left : r.right,
|
972 |
+
top: rightV ? r.topRight : r.top,
|
973 |
+
bottom: rightV ? r.bottomRight : r.bottom};
|
974 |
}
|
975 |
|
976 |
function findCachedMeasurement(cm, line) {
|
980 |
if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
|
981 |
cm.display.scroller.clientWidth == memo.width &&
|
982 |
memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
|
983 |
+
return memo;
|
984 |
}
|
985 |
}
|
986 |
|
987 |
+
function clearCachedMeasurement(cm, line) {
|
988 |
+
var exists = findCachedMeasurement(cm, line);
|
989 |
+
if (exists) exists.text = exists.measure = exists.markedSpans = null;
|
990 |
+
}
|
991 |
+
|
992 |
function measureLine(cm, line) {
|
993 |
// First look in the cache
|
994 |
+
var cached = findCachedMeasurement(cm, line);
|
995 |
+
if (cached) return cached.measure;
|
996 |
+
|
997 |
+
// Failing that, recompute and store result in cache
|
998 |
+
var measure = measureLineInner(cm, line);
|
999 |
+
var cache = cm.display.measureLineCache;
|
1000 |
+
var memo = {text: line.text, width: cm.display.scroller.clientWidth,
|
1001 |
+
markedSpans: line.markedSpans, measure: measure,
|
1002 |
+
classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
|
1003 |
+
if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
|
1004 |
+
else cache.push(memo);
|
1005 |
return measure;
|
1006 |
}
|
1007 |
|
1043 |
if (ie_lt9 && display.measure.first != pre)
|
1044 |
removeChildrenAndAdd(display.measure, pre);
|
1045 |
|
1046 |
+
function categorizeVSpan(top, bot) {
|
1047 |
+
if (bot > maxBot) bot = maxBot;
|
1048 |
+
if (top < 0) top = 0;
|
1049 |
for (var j = 0; j < vranges.length; j += 2) {
|
1050 |
var rtop = vranges[j], rbot = vranges[j+1];
|
1051 |
if (rtop > bot || rbot < top) continue;
|
1054 |
Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
|
1055 |
vranges[j] = Math.min(top, rtop);
|
1056 |
vranges[j+1] = Math.max(bot, rbot);
|
1057 |
+
return j;
|
1058 |
+
}
|
1059 |
+
}
|
1060 |
+
vranges.push(top, bot);
|
1061 |
+
return j;
|
1062 |
+
}
|
1063 |
+
|
1064 |
+
for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
|
1065 |
+
var size, node = cur;
|
1066 |
+
// A widget might wrap, needs special care
|
1067 |
+
if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
|
1068 |
+
if (cur.firstChild.nodeType == 1) node = cur.firstChild;
|
1069 |
+
var rects = node.getClientRects(), rLeft = rects[0], rRight = rects[rects.length - 1];
|
1070 |
+
if (rects.length > 1) {
|
1071 |
+
var vCatLeft = categorizeVSpan(rLeft.top - outer.top, rLeft.bottom - outer.top);
|
1072 |
+
var vCatRight = categorizeVSpan(rRight.top - outer.top, rRight.bottom - outer.top);
|
1073 |
+
data[i] = {left: rLeft.left - outer.left, right: rRight.right - outer.left,
|
1074 |
+
top: vCatLeft, topRight: vCatRight};
|
1075 |
+
continue;
|
1076 |
}
|
1077 |
}
|
1078 |
+
size = getRect(node);
|
1079 |
+
var vCat = categorizeVSpan(size.top - outer.top, size.bottom - outer.top);
|
1080 |
var right = size.right;
|
1081 |
if (cur.measureRight) right = getRect(cur.measureRight).left;
|
1082 |
+
data[i] = {left: size.left - outer.left, right: right - outer.left, top: vCat};
|
1083 |
}
|
1084 |
for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
|
1085 |
+
var vr = cur.top, vrRight = cur.topRight;
|
1086 |
cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
|
1087 |
+
if (vrRight != null) { cur.topRight = vranges[vrRight]; cur.bottomRight = vranges[vrRight+1]; }
|
1088 |
}
|
|
|
1089 |
return data;
|
1090 |
}
|
1091 |
|
1096 |
if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
|
1097 |
}
|
1098 |
var cached = !hasBadSpan && findCachedMeasurement(cm, line);
|
1099 |
+
if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right;
|
1100 |
|
1101 |
var pre = lineContent(cm, line);
|
1102 |
var end = pre.appendChild(zeroWidthElement(cm.display.measure));
|
1107 |
function clearCaches(cm) {
|
1108 |
cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
|
1109 |
cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
|
1110 |
+
if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
|
1111 |
cm.display.lineNumChars = null;
|
1112 |
}
|
1113 |
|
1114 |
+
function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
|
1115 |
+
function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
|
1116 |
+
|
1117 |
// Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
|
1118 |
function intoCoordSystem(cm, lineObj, rect, context) {
|
1119 |
if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
|
1123 |
if (context == "line") return rect;
|
1124 |
if (!context) context = "local";
|
1125 |
var yOff = heightAtLine(cm, lineObj);
|
1126 |
+
if (context == "local") yOff += paddingTop(cm.display);
|
1127 |
+
else yOff -= cm.display.viewOffset;
|
1128 |
+
if (context == "page" || context == "window") {
|
1129 |
var lOff = getRect(cm.display.lineSpace);
|
1130 |
+
yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
|
1131 |
+
var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
|
1132 |
rect.left += xOff; rect.right += xOff;
|
1133 |
}
|
1134 |
rect.top += yOff; rect.bottom += yOff;
|
1136 |
}
|
1137 |
|
1138 |
// Context may be "window", "page", "div", or "local"/null
|
1139 |
+
// Result is in "div" coords
|
1140 |
function fromCoordSystem(cm, coords, context) {
|
1141 |
if (context == "div") return coords;
|
1142 |
var left = coords.left, top = coords.top;
|
1143 |
+
// First move into "page" coordinate system
|
1144 |
if (context == "page") {
|
1145 |
+
left -= pageScrollX();
|
1146 |
+
top -= pageScrollY();
|
1147 |
+
} else if (context == "local" || !context) {
|
1148 |
+
var localBox = getRect(cm.display.sizer);
|
1149 |
+
left += localBox.left;
|
1150 |
+
top += localBox.top;
|
1151 |
}
|
1152 |
+
|
1153 |
var lineSpaceBox = getRect(cm.display.lineSpace);
|
1154 |
+
return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1155 |
}
|
1156 |
|
1157 |
+
function charCoords(cm, pos, context, lineObj, bias) {
|
1158 |
if (!lineObj) lineObj = getLine(cm.doc, pos.line);
|
1159 |
+
return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
|
1160 |
}
|
1161 |
|
1162 |
function cursorCoords(cm, pos, context, lineObj, measurement) {
|
1163 |
lineObj = lineObj || getLine(cm.doc, pos.line);
|
1164 |
if (!measurement) measurement = measureLine(cm, lineObj);
|
1165 |
function get(ch, right) {
|
1166 |
+
var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
|
1167 |
if (right) m.left = m.right; else m.right = m.left;
|
1168 |
return intoCoordSystem(cm, lineObj, m, context);
|
1169 |
}
|
1170 |
+
function getBidi(ch, partPos) {
|
1171 |
+
var part = order[partPos], right = part.level % 2;
|
1172 |
+
if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
|
1173 |
+
part = order[--partPos];
|
1174 |
+
ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
|
1175 |
+
right = true;
|
1176 |
+
} else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
|
1177 |
+
part = order[++partPos];
|
1178 |
+
ch = bidiLeft(part) - part.level % 2;
|
1179 |
+
right = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1180 |
}
|
1181 |
+
if (right && ch == part.to && ch > part.from) return get(ch - 1);
|
1182 |
+
return get(ch, right);
|
1183 |
}
|
1184 |
+
var order = getOrder(lineObj), ch = pos.ch;
|
1185 |
+
if (!order) return get(ch);
|
1186 |
+
var partPos = getBidiPartAt(order, ch);
|
1187 |
+
var val = getBidi(ch, partPos);
|
1188 |
+
if (bidiOther != null) val.other = getBidi(ch, bidiOther);
|
1189 |
+
return val;
|
1190 |
}
|
1191 |
|
1192 |
+
function PosWithInfo(line, ch, outside, xRel) {
|
1193 |
var pos = new Pos(line, ch);
|
1194 |
+
pos.xRel = xRel;
|
1195 |
if (outside) pos.outside = true;
|
1196 |
return pos;
|
1197 |
}
|
1200 |
function coordsChar(cm, x, y) {
|
1201 |
var doc = cm.doc;
|
1202 |
y += cm.display.viewOffset;
|
1203 |
+
if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
|
1204 |
var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
|
1205 |
if (lineNo > last)
|
1206 |
+
return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
|
1207 |
if (x < 0) x = 0;
|
1208 |
|
1209 |
for (;;) {
|
1211 |
var found = coordsCharInner(cm, lineObj, lineNo, x, y);
|
1212 |
var merged = collapsedSpanAtEnd(lineObj);
|
1213 |
var mergedPos = merged && merged.find();
|
1214 |
+
if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
|
1215 |
lineNo = mergedPos.to.line;
|
1216 |
else
|
1217 |
return found;
|
1237 |
var from = lineLeft(lineObj), to = lineRight(lineObj);
|
1238 |
var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
|
1239 |
|
1240 |
+
if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
|
1241 |
// Do a binary search between these bounds.
|
1242 |
for (;;) {
|
1243 |
if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
|
1244 |
+
var ch = x < fromX || x - fromX <= toX - x ? from : to;
|
1245 |
+
var xDiff = x - (ch == from ? fromX : toX);
|
1246 |
while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
|
1247 |
+
var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
|
1248 |
+
xDiff < 0 ? -1 : xDiff ? 1 : 0);
|
1249 |
return pos;
|
1250 |
}
|
1251 |
var step = Math.ceil(dist / 2), middle = from + step;
|
1254 |
for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
|
1255 |
}
|
1256 |
var middleX = getX(middle);
|
1257 |
+
if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
|
1258 |
+
else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
|
1259 |
}
|
1260 |
}
|
1261 |
|
1306 |
userSelChange: null,
|
1307 |
textChanged: null,
|
1308 |
selectionChanged: false,
|
1309 |
+
cursorActivity: false,
|
1310 |
updateMaxLine: false,
|
1311 |
updateScrollPos: false,
|
1312 |
id: ++nextOpId
|
1319 |
cm.curOp = null;
|
1320 |
|
1321 |
if (op.updateMaxLine) computeMaxLength(cm);
|
1322 |
+
if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) {
|
1323 |
var width = measureLineWidth(cm, display.maxLine);
|
1324 |
display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
|
1325 |
display.maxLineChanged = false;
|
1343 |
display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
|
1344 |
display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
|
1345 |
alignHorizontally(cm);
|
1346 |
+
if (op.scrollToPos)
|
1347 |
+
scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
|
1348 |
} else if (newScrollPos) {
|
1349 |
scrollCursorIntoView(cm);
|
1350 |
}
|
1366 |
}
|
1367 |
if (op.textChanged)
|
1368 |
signal(cm, "change", cm, op.textChanged);
|
1369 |
+
if (op.cursorActivity) signal(cm, "cursorActivity", cm);
|
1370 |
if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
|
1371 |
}
|
1372 |
|
1431 |
// supported or compatible enough yet to rely on.)
|
1432 |
function readInput(cm) {
|
1433 |
var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
|
1434 |
+
if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
|
1435 |
var text = input.value;
|
1436 |
if (text == prevInput && posEq(sel.from, sel.to)) return false;
|
1437 |
+
if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
|
|
|
|
|
|
|
1438 |
resetInput(cm, true);
|
1439 |
return false;
|
1440 |
}
|
1441 |
+
|
1442 |
var withOp = !cm.curOp;
|
1443 |
if (withOp) startOperation(cm);
|
1444 |
sel.shift = false;
|
1445 |
var same = 0, l = Math.min(prevInput.length, text.length);
|
1446 |
+
while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
|
1447 |
var from = sel.from, to = sel.to;
|
1448 |
if (same < prevInput.length)
|
1449 |
from = Pos(from.line, from.ch - (prevInput.length - same));
|
1450 |
else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
|
1451 |
to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
|
|
|
|
|
|
|
1452 |
|
1453 |
+
var updateInput = cm.curOp.updateInput;
|
1454 |
+
var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
|
1455 |
+
origin: cm.state.pasteIncoming ? "paste" : "+input"};
|
1456 |
+
makeChange(cm.doc, changeEvent, "end");
|
1457 |
cm.curOp.updateInput = updateInput;
|
1458 |
+
signalLater(cm, "inputRead", cm, changeEvent);
|
1459 |
+
|
1460 |
if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
|
1461 |
else cm.display.prevInput = text;
|
1462 |
if (withOp) endOperation(cm);
|
1470 |
cm.display.prevInput = "";
|
1471 |
minimal = hasCopyEvent &&
|
1472 |
(doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
|
1473 |
+
var content = minimal ? "-" : selected || cm.getSelection();
|
1474 |
+
cm.display.input.value = content;
|
1475 |
if (cm.state.focused) selectInput(cm.display.input);
|
1476 |
+
if (ie && !ie_lt9) cm.display.inputHasSelection = content;
|
1477 |
+
} else if (user) {
|
1478 |
+
cm.display.prevInput = cm.display.input.value = "";
|
1479 |
+
if (ie && !ie_lt9) cm.display.inputHasSelection = null;
|
1480 |
+
}
|
1481 |
cm.display.inaccurateSelection = minimal;
|
1482 |
}
|
1483 |
|
1495 |
function registerEventHandlers(cm) {
|
1496 |
var d = cm.display;
|
1497 |
on(d.scroller, "mousedown", operation(cm, onMouseDown));
|
1498 |
+
if (ie)
|
1499 |
+
on(d.scroller, "dblclick", operation(cm, function(e) {
|
1500 |
+
if (signalDOMEvent(cm, e)) return;
|
1501 |
+
var pos = posFromMouse(cm, e);
|
1502 |
+
if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
|
1503 |
+
e_preventDefault(e);
|
1504 |
+
var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
|
1505 |
+
extendSelection(cm.doc, word.from, word.to);
|
1506 |
+
}));
|
1507 |
+
else
|
1508 |
+
on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
|
1509 |
on(d.lineSpace, "selectstart", function(e) {
|
1510 |
if (!eventInWidget(d, e)) e_preventDefault(e);
|
1511 |
});
|
1537 |
// Prevent wrapper from ever scrolling
|
1538 |
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
|
1539 |
|
1540 |
+
var resizeTimer;
|
1541 |
function onResize() {
|
1542 |
+
if (resizeTimer == null) resizeTimer = setTimeout(function() {
|
1543 |
+
resizeTimer = null;
|
1544 |
+
// Might be a text scaling operation, clear size caches.
|
1545 |
+
d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
|
1546 |
+
clearCaches(cm);
|
1547 |
+
runInOp(cm, bind(regChange, cm));
|
1548 |
+
}, 100);
|
1549 |
}
|
1550 |
on(window, "resize", onResize);
|
1551 |
// Above handler holds on to the editor and its data structures.
|
1559 |
setTimeout(unregister, 5000);
|
1560 |
|
1561 |
on(d.input, "keyup", operation(cm, function(e) {
|
1562 |
+
if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1563 |
if (e.keyCode == 16) cm.doc.sel.shift = false;
|
1564 |
}));
|
1565 |
on(d.input, "input", bind(fastPoll, cm));
|
1569 |
on(d.input, "blur", bind(onBlur, cm));
|
1570 |
|
1571 |
function drag_(e) {
|
1572 |
+
if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
|
1573 |
e_stop(e);
|
1574 |
}
|
1575 |
if (cm.options.dragDrop) {
|
1608 |
|
1609 |
function eventInWidget(display, e) {
|
1610 |
for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
|
1611 |
+
if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
|
|
|
|
|
1612 |
}
|
1613 |
}
|
1614 |
|
1618 |
var target = e_target(e);
|
1619 |
if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
|
1620 |
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
|
1621 |
+
target == display.scrollbarFiller || target == display.gutterFiller) return null;
|
1622 |
}
|
1623 |
var x, y, space = getRect(display.lineSpace);
|
1624 |
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
|
1628 |
|
1629 |
var lastClick, lastDoubleClick;
|
1630 |
function onMouseDown(e) {
|
1631 |
+
if (signalDOMEvent(this, e)) return;
|
1632 |
var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
|
1633 |
sel.shift = e.shiftKey;
|
1634 |
|
1699 |
e_preventDefault(e);
|
1700 |
if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
|
1701 |
|
1702 |
+
var startstart = sel.from, startend = sel.to, lastPos = start;
|
1703 |
|
1704 |
function doSelect(cur) {
|
1705 |
+
if (posEq(lastPos, cur)) return;
|
1706 |
+
lastPos = cur;
|
1707 |
+
|
1708 |
if (type == "single") {
|
1709 |
extendSelection(cm.doc, clipPos(doc, start), cur);
|
1710 |
return;
|
1752 |
|
1753 |
function done(e) {
|
1754 |
counter = Infinity;
|
|
|
|
|
1755 |
e_preventDefault(e);
|
1756 |
focusInput(cm);
|
1757 |
off(document, "mousemove", move);
|
1767 |
on(document, "mouseup", up);
|
1768 |
}
|
1769 |
|
1770 |
+
function clickInGutter(cm, e) {
|
1771 |
+
var display = cm.display;
|
1772 |
+
try { var mX = e.clientX, mY = e.clientY; }
|
1773 |
+
catch(e) { return false; }
|
1774 |
+
|
1775 |
+
if (mX >= Math.floor(getRect(display.gutters).right)) return false;
|
1776 |
+
e_preventDefault(e);
|
1777 |
+
if (!hasHandler(cm, "gutterClick")) return true;
|
1778 |
+
|
1779 |
+
var lineBox = getRect(display.lineDiv);
|
1780 |
+
if (mY > lineBox.bottom) return true;
|
1781 |
+
mY -= lineBox.top - display.viewOffset;
|
1782 |
+
|
1783 |
+
for (var i = 0; i < cm.options.gutters.length; ++i) {
|
1784 |
+
var g = display.gutters.childNodes[i];
|
1785 |
+
if (g && getRect(g).right >= mX) {
|
1786 |
+
var line = lineAtHeight(cm.doc, mY);
|
1787 |
+
var gutter = cm.options.gutters[i];
|
1788 |
+
signalLater(cm, "gutterClick", cm, line, gutter, e);
|
1789 |
+
break;
|
1790 |
+
}
|
1791 |
+
}
|
1792 |
+
return true;
|
1793 |
+
}
|
1794 |
+
|
1795 |
+
// Kludge to work around strange IE behavior where it'll sometimes
|
1796 |
+
// re-fire a series of drag-related events right after the drop (#1551)
|
1797 |
+
var lastDrop = 0;
|
1798 |
+
|
1799 |
function onDrop(e) {
|
1800 |
var cm = this;
|
1801 |
+
if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
|
1802 |
return;
|
1803 |
e_preventDefault(e);
|
1804 |
+
if (ie) lastDrop = +new Date;
|
1805 |
var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
|
1806 |
if (!pos || isReadOnly(cm)) return;
|
1807 |
if (files && files.length && window.FileReader && window.File) {
|
1841 |
}
|
1842 |
}
|
1843 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1844 |
function onDragStart(cm, e) {
|
1845 |
+
if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
|
1846 |
+
if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
|
1847 |
|
1848 |
var txt = cm.getSelection();
|
1849 |
e.dataTransfer.setData("Text", txt);
|
1850 |
|
1851 |
// Use dummy image instead of default browsers image.
|
1852 |
// Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
|
1853 |
+
if (e.dataTransfer.setDragImage && !safari) {
|
1854 |
var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
|
1855 |
if (opera) {
|
1856 |
img.width = img.height = 1;
|
1858 |
// Force a relayout, or Opera won't use our image for some obscure reason
|
1859 |
img._top = img.offsetTop;
|
1860 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1861 |
e.dataTransfer.setDragImage(img, 0, 0);
|
1862 |
if (opera) img.parentNode.removeChild(img);
|
1863 |
}
|
1870 |
if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
|
1871 |
if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
|
1872 |
if (gecko) updateDisplay(cm, []);
|
1873 |
+
startWorker(cm, 100);
|
1874 |
}
|
1875 |
function setScrollLeft(cm, val, isScroller) {
|
1876 |
if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
|
1908 |
if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
|
1909 |
else if (dy == null) dy = e.wheelDelta;
|
1910 |
|
1911 |
+
var display = cm.display, scroll = display.scroller;
|
1912 |
+
// Quit if there's nothing to scroll here
|
1913 |
+
if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
|
1914 |
+
dy && scroll.scrollHeight > scroll.clientHeight)) return;
|
1915 |
+
|
1916 |
// Webkit browsers on OS X abort momentum scrolls when the target
|
1917 |
// of the scroll event is removed from the scrollable element.
|
1918 |
// This hack (see related code in patchDisplay) makes sure the
|
1926 |
}
|
1927 |
}
|
1928 |
|
|
|
1929 |
// On some browsers, horizontal scrolling will cause redraws to
|
1930 |
// happen before the gutter has been realigned, causing it to
|
1931 |
// wriggle around in a most unseemly way. When we have an
|
2003 |
var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
|
2004 |
clearTimeout(maybeTransition);
|
2005 |
if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
|
2006 |
+
if (getKeyMap(cm.options.keyMap) == startMap) {
|
2007 |
cm.options.keyMap = (next.call ? next.call(null, cm) : next);
|
2008 |
+
keyMapChanged(cm);
|
2009 |
+
}
|
2010 |
}, 50);
|
2011 |
|
2012 |
var name = keyName(e, true), handled = false;
|
2019 |
// 'go') bound to the keyname without 'Shift-'.
|
2020 |
handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
|
2021 |
|| lookupKey(name, keymaps, function(b) {
|
2022 |
+
if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
|
2023 |
+
return doHandleBinding(cm, b);
|
2024 |
});
|
2025 |
} else {
|
2026 |
handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
|
2027 |
}
|
|
|
2028 |
|
2029 |
if (handled) {
|
2030 |
e_preventDefault(e);
|
2031 |
restartBlink(cm);
|
2032 |
if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
|
2033 |
+
signalLater(cm, "keyHandled", cm, name, e);
|
2034 |
}
|
2035 |
return handled;
|
2036 |
}
|
2041 |
if (handled) {
|
2042 |
e_preventDefault(e);
|
2043 |
restartBlink(cm);
|
2044 |
+
signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
|
2045 |
}
|
2046 |
return handled;
|
2047 |
}
|
2051 |
var cm = this;
|
2052 |
if (!cm.state.focused) onFocus(cm);
|
2053 |
if (ie && e.keyCode == 27) { e.returnValue = false; }
|
2054 |
+
if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
2055 |
var code = e.keyCode;
|
2056 |
// IE does strange things with escape.
|
2057 |
cm.doc.sel.shift = code == 16 || e.shiftKey;
|
2067 |
|
2068 |
function onKeyPress(e) {
|
2069 |
var cm = this;
|
2070 |
+
if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
2071 |
var keyCode = e.keyCode, charCode = e.charCode;
|
2072 |
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
|
2073 |
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
|
2077 |
this.doc.mode.electricChars.indexOf(ch) > -1)
|
2078 |
setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
|
2079 |
if (handleCharBinding(cm, e, ch)) return;
|
2080 |
+
if (ie && !ie_lt9) cm.display.inputHasSelection = null;
|
2081 |
fastPoll(cm);
|
2082 |
}
|
2083 |
|
2123 |
// Adds "Select all" to context menu in FF
|
2124 |
if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
|
2125 |
|
2126 |
+
function prepareSelectAllHack() {
|
2127 |
+
if (display.input.selectionStart != null) {
|
2128 |
+
var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value);
|
2129 |
+
display.prevInput = " ";
|
2130 |
+
display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
|
2131 |
+
}
|
2132 |
+
}
|
2133 |
function rehide() {
|
2134 |
display.inputDiv.style.position = "relative";
|
2135 |
display.input.style.cssText = oldCSS;
|
2137 |
slowPoll(cm);
|
2138 |
|
2139 |
// Try to detect the user choosing select-all
|
2140 |
+
if (display.input.selectionStart != null) {
|
2141 |
+
if (!ie || ie_lt9) prepareSelectAllHack();
|
2142 |
clearTimeout(detectingSelectAll);
|
2143 |
+
var i = 0, poll = function(){
|
|
|
|
|
|
|
2144 |
if (display.prevInput == " " && display.input.selectionStart == 0)
|
2145 |
operation(cm, commands.selectAll)(cm);
|
2146 |
else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
|
2150 |
}
|
2151 |
}
|
2152 |
|
2153 |
+
if (ie && !ie_lt9) prepareSelectAllHack();
|
2154 |
if (captureMiddleClick) {
|
2155 |
e_stop(e);
|
2156 |
var mouseup = function() {
|
2165 |
|
2166 |
// UPDATING
|
2167 |
|
2168 |
+
var changeEnd = CodeMirror.changeEnd = function(change) {
|
2169 |
+
if (!change.text) return change.to;
|
2170 |
return Pos(change.from.line + change.text.length - 1,
|
2171 |
lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
|
2172 |
+
};
|
2173 |
|
2174 |
// Make sure a position will be valid after the given change.
|
2175 |
function clipPostChange(doc, change, pos) {
|
2211 |
return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
|
2212 |
}
|
2213 |
|
2214 |
+
function filterChange(doc, change, update) {
|
2215 |
var obj = {
|
2216 |
canceled: false,
|
2217 |
from: change.from,
|
2218 |
to: change.to,
|
2219 |
text: change.text,
|
2220 |
origin: change.origin,
|
|
|
|
|
|
|
|
|
|
|
|
|
2221 |
cancel: function() { this.canceled = true; }
|
2222 |
};
|
2223 |
+
if (update) obj.update = function(from, to, text, origin) {
|
2224 |
+
if (from) this.from = clipPos(doc, from);
|
2225 |
+
if (to) this.to = clipPos(doc, to);
|
2226 |
+
if (text) this.text = text;
|
2227 |
+
if (origin !== undefined) this.origin = origin;
|
2228 |
+
};
|
2229 |
signal(doc, "beforeChange", doc, obj);
|
2230 |
if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
|
2231 |
|
2242 |
}
|
2243 |
|
2244 |
if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
|
2245 |
+
change = filterChange(doc, change, true);
|
2246 |
if (!change) return;
|
2247 |
}
|
2248 |
|
2281 |
var hist = doc.history;
|
2282 |
var event = (type == "undo" ? hist.done : hist.undone).pop();
|
2283 |
if (!event) return;
|
|
|
2284 |
|
2285 |
var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
|
2286 |
+
anchorAfter: event.anchorBefore, headAfter: event.headBefore,
|
2287 |
+
generation: hist.generation};
|
2288 |
(type == "undo" ? hist.undone : hist.done).push(anti);
|
2289 |
+
hist.generation = event.generation || ++hist.maxGeneration;
|
2290 |
+
|
2291 |
+
var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
|
2292 |
|
2293 |
for (var i = event.changes.length - 1; i >= 0; --i) {
|
2294 |
var change = event.changes[i];
|
2295 |
change.origin = type;
|
2296 |
+
if (filter && !filterChange(doc, change, false)) {
|
2297 |
+
(type == "undo" ? hist.done : hist.undone).length = 0;
|
2298 |
+
return;
|
2299 |
+
}
|
2300 |
+
|
2301 |
anti.changes.push(historyChangeFromChange(doc, change));
|
2302 |
|
2303 |
var after = i ? computeSelAfterChange(doc, change, null)
|
2367 |
});
|
2368 |
}
|
2369 |
|
2370 |
+
if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head))
|
2371 |
+
cm.curOp.cursorActivity = true;
|
2372 |
+
|
2373 |
updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
|
2374 |
|
2375 |
if (!cm.options.lineWrapping) {
|
2496 |
sel.to = inv ? anchor : head;
|
2497 |
|
2498 |
if (doc.cm)
|
2499 |
+
doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
|
2500 |
+
doc.cm.curOp.cursorActivity = true;
|
2501 |
|
2502 |
signalLater(doc, "cursorActivity", doc);
|
2503 |
}
|
2559 |
// SCROLLING
|
2560 |
|
2561 |
function scrollCursorIntoView(cm) {
|
2562 |
+
var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
|
2563 |
if (!cm.state.focused) return;
|
2564 |
+
var display = cm.display, box = getRect(display.sizer), doScroll = null;
|
2565 |
+
if (coords.top + box.top < 0) doScroll = true;
|
2566 |
+
else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
|
2567 |
if (doScroll != null && !phantom) {
|
2568 |
var hidden = display.cursor.style.display == "none";
|
2569 |
if (hidden) {
|
2601 |
}
|
2602 |
|
2603 |
function calculateScrollPos(cm, x1, y1, x2, y2) {
|
2604 |
+
var display = cm.display, snapMargin = textHeight(cm.display);
|
2605 |
+
if (y1 < 0) y1 = 0;
|
2606 |
var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
|
2607 |
var docBottom = cm.doc.height + paddingVert(display);
|
2608 |
+
var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
|
2609 |
+
if (y1 < screentop) {
|
2610 |
+
result.scrollTop = atTop ? 0 : y1;
|
2611 |
+
} else if (y2 > screentop + screen) {
|
2612 |
+
var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
|
2613 |
+
if (newTop != screentop) result.scrollTop = newTop;
|
2614 |
+
}
|
2615 |
|
2616 |
var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
|
2617 |
x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
|
2627 |
}
|
2628 |
|
2629 |
function updateScrollPos(cm, left, top) {
|
2630 |
+
cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
|
2631 |
+
scrollTop: top == null ? cm.doc.scrollTop : top};
|
2632 |
}
|
2633 |
|
2634 |
function addToScrollPos(cm, left, top) {
|
2642 |
|
2643 |
function indentLine(cm, n, how, aggressive) {
|
2644 |
var doc = cm.doc;
|
2645 |
+
if (how == null) how = "add";
|
2646 |
if (how == "smart") {
|
2647 |
if (!cm.doc.mode.indent) how = "prev";
|
2648 |
else var state = getStateBefore(cm, n);
|
2665 |
indentation = curSpace + cm.options.indentUnit;
|
2666 |
} else if (how == "subtract") {
|
2667 |
indentation = curSpace - cm.options.indentUnit;
|
2668 |
+
} else if (typeof how == "number") {
|
2669 |
+
indentation = curSpace + how;
|
2670 |
}
|
2671 |
indentation = Math.max(0, indentation);
|
2672 |
|
2691 |
}
|
2692 |
|
2693 |
function findPosH(doc, pos, dir, unit, visually) {
|
2694 |
+
var line = pos.line, ch = pos.ch, origDir = dir;
|
2695 |
var lineObj = getLine(doc, line);
|
2696 |
var possible = true;
|
2697 |
function findNextLine() {
|
2730 |
if (dir > 0 && !moveOnce(!first)) break;
|
2731 |
}
|
2732 |
}
|
2733 |
+
var result = skipAtomic(doc, Pos(line, ch), origDir, true);
|
2734 |
if (!possible) result.hitSide = true;
|
2735 |
return result;
|
2736 |
}
|
2755 |
function findWordAt(line, pos) {
|
2756 |
var start = pos.ch, end = pos.ch;
|
2757 |
if (line) {
|
2758 |
+
if (pos.xRel < 0 || end == line.length) --start; else ++end;
|
2759 |
var startChar = line.charAt(start);
|
2760 |
var check = isWordChar(startChar) ? isWordChar
|
2761 |
: /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
|
2776 |
// 'wrap f in an operation, performed on its `this` parameter'
|
2777 |
|
2778 |
CodeMirror.prototype = {
|
2779 |
+
constructor: CodeMirror,
|
2780 |
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
|
2781 |
|
2782 |
setOption: function(option, value) {
|
2812 |
removeOverlay: operation(null, function(spec) {
|
2813 |
var overlays = this.state.overlays;
|
2814 |
for (var i = 0; i < overlays.length; ++i) {
|
2815 |
+
var cur = overlays[i].modeSpec;
|
2816 |
+
if (cur == spec || typeof spec == "string" && cur.name == spec) {
|
2817 |
overlays.splice(i, 1);
|
2818 |
this.state.modeGen++;
|
2819 |
regChange(this);
|
2823 |
}),
|
2824 |
|
2825 |
indentLine: operation(null, function(n, dir, aggressive) {
|
2826 |
+
if (typeof dir != "string" && typeof dir != "number") {
|
2827 |
if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
|
2828 |
else dir = dir ? "add" : "subtract";
|
2829 |
}
|
2838 |
|
2839 |
// Fetch the parser token for a given character. Useful for hacks
|
2840 |
// that want to inspect the mode state (say, for completion).
|
2841 |
+
getTokenAt: function(pos, precise) {
|
2842 |
var doc = this.doc;
|
2843 |
pos = clipPos(doc, pos);
|
2844 |
+
var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
|
2845 |
var line = getLine(doc, pos.line);
|
2846 |
var stream = new StringStream(line.text, this.options.tabSize);
|
2847 |
while (stream.pos < pos.ch && !stream.eol()) {
|
2856 |
state: state};
|
2857 |
},
|
2858 |
|
2859 |
+
getTokenTypeAt: function(pos) {
|
2860 |
+
pos = clipPos(this.doc, pos);
|
2861 |
+
var styles = getLineStyles(this, getLine(this.doc, pos.line));
|
2862 |
+
var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
|
2863 |
+
for (;;) {
|
2864 |
+
var mid = (before + after) >> 1;
|
2865 |
+
if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
|
2866 |
+
else if (styles[mid * 2 + 1] < ch) before = mid + 1;
|
2867 |
+
else return styles[mid * 2 + 2];
|
2868 |
+
}
|
2869 |
+
},
|
2870 |
+
|
2871 |
+
getStateAfter: function(line, precise) {
|
2872 |
var doc = this.doc;
|
2873 |
line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
|
2874 |
+
return getStateBefore(this, line + 1, precise);
|
2875 |
},
|
2876 |
|
2877 |
cursorCoords: function(start, mode) {
|
2891 |
return coordsChar(this, coords.left, coords.top);
|
2892 |
},
|
2893 |
|
2894 |
+
lineAtHeight: function(height, mode) {
|
2895 |
+
height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
|
2896 |
+
return lineAtHeight(this.doc, height + this.display.viewOffset);
|
2897 |
+
},
|
2898 |
+
heightAtLine: function(line, mode) {
|
2899 |
+
var end = false, last = this.doc.first + this.doc.size - 1;
|
2900 |
+
if (line < this.doc.first) line = this.doc.first;
|
2901 |
+
else if (line > last) { line = last; end = true; }
|
2902 |
+
var lineObj = getLine(this.doc, line);
|
2903 |
+
return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
|
2904 |
+
(end ? lineObj.height : 0);
|
2905 |
+
},
|
2906 |
+
|
2907 |
defaultTextHeight: function() { return textHeight(this.display); },
|
2908 |
defaultCharWidth: function() { return charWidth(this.display); },
|
2909 |
|
2932 |
return changeLine(this, handle, function(line) {
|
2933 |
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
|
2934 |
if (!line[prop]) line[prop] = cls;
|
2935 |
+
else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
|
2936 |
else line[prop] += " " + cls;
|
2937 |
return true;
|
2938 |
});
|
2945 |
if (!cur) return false;
|
2946 |
else if (cls == null) line[prop] = null;
|
2947 |
else {
|
2948 |
+
var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
|
2949 |
+
if (!found) return false;
|
2950 |
+
var end = found.index + found[0].length;
|
2951 |
+
line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
|
2952 |
}
|
2953 |
return true;
|
2954 |
});
|
2996 |
if (left + node.offsetWidth > hspace)
|
2997 |
left = hspace - node.offsetWidth;
|
2998 |
}
|
2999 |
+
node.style.top = top + "px";
|
3000 |
node.style.left = node.style.right = "";
|
3001 |
if (horiz == "right") {
|
3002 |
left = display.sizer.clientWidth - node.offsetWidth;
|
3064 |
sel.goalColumn = pos.left;
|
3065 |
}),
|
3066 |
|
3067 |
+
toggleOverwrite: function(value) {
|
3068 |
+
if (value != null && value == this.state.overwrite) return;
|
3069 |
if (this.state.overwrite = !this.state.overwrite)
|
3070 |
this.display.cursor.className += " CodeMirror-overwrite";
|
3071 |
else
|
3083 |
clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
|
3084 |
},
|
3085 |
|
3086 |
+
scrollIntoView: operation(null, function(pos, margin) {
|
3087 |
if (typeof pos == "number") pos = Pos(pos, 0);
|
3088 |
+
if (!margin) margin = 0;
|
3089 |
+
var coords = pos;
|
3090 |
+
|
3091 |
if (!pos || pos.line != null) {
|
3092 |
+
this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
|
3093 |
+
this.curOp.scrollToPosMargin = margin;
|
3094 |
+
coords = cursorCoords(this, this.curOp.scrollToPos);
|
|
|
3095 |
}
|
3096 |
+
var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
|
3097 |
+
updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
|
3098 |
+
}),
|
3099 |
|
3100 |
setSize: function(width, height) {
|
3101 |
function interpret(val) {
|
3122 |
old.cm = null;
|
3123 |
attachDoc(this, doc);
|
3124 |
clearCaches(this);
|
3125 |
+
resetInput(this, true);
|
3126 |
updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
|
3127 |
return old;
|
3128 |
}),
|
3188 |
cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
|
3189 |
cm.refresh();
|
3190 |
}, true);
|
3191 |
+
option("coverGutterNextToScrollbar", false, updateScrollbars, true);
|
3192 |
option("lineNumbers", false, function(cm) {
|
3193 |
setGuttersForLineNumbers(cm.options);
|
3194 |
guttersChanged(cm);
|
3204 |
option("dragDrop", true);
|
3205 |
|
3206 |
option("cursorBlinkRate", 530);
|
3207 |
+
option("cursorScrollMargin", 0);
|
3208 |
option("cursorHeight", 1);
|
3209 |
option("workTime", 100);
|
3210 |
option("workDelay", 100);
|
3211 |
option("flattenSpans", true);
|
3212 |
option("pollInterval", 100);
|
3213 |
option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
|
3214 |
+
option("historyEventDelay", 500);
|
3215 |
option("viewportMargin", 10, function(cm){cm.refresh();}, true);
|
3216 |
+
option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
|
3217 |
+
option("moveInputWithCursor", true, function(cm, val) {
|
3218 |
+
if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
|
3219 |
+
});
|
3220 |
|
3221 |
option("tabindex", null, function(cm, val) {
|
3222 |
cm.display.input.tabIndex = val || "";
|
3242 |
};
|
3243 |
|
3244 |
CodeMirror.resolveMode = function(spec) {
|
3245 |
+
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
|
3246 |
spec = mimeModes[spec];
|
3247 |
+
} else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
|
3248 |
+
var found = mimeModes[spec.name];
|
3249 |
+
spec = createObj(found, spec);
|
3250 |
+
spec.name = found.name;
|
3251 |
+
} else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
|
3252 |
return CodeMirror.resolveMode("application/xml");
|
3253 |
+
}
|
3254 |
if (typeof spec == "string") return {name: spec};
|
3255 |
else return spec || {name: "null"};
|
3256 |
};
|
3288 |
CodeMirror.defineExtension = function(name, func) {
|
3289 |
CodeMirror.prototype[name] = func;
|
3290 |
};
|
3291 |
+
CodeMirror.defineDocExtension = function(name, func) {
|
3292 |
+
Doc.prototype[name] = func;
|
3293 |
+
};
|
3294 |
CodeMirror.defineOption = option;
|
3295 |
|
3296 |
var initHooks = [];
|
3341 |
var l = cm.getCursor().line;
|
3342 |
cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
|
3343 |
},
|
3344 |
+
delLineLeft: function(cm) {
|
3345 |
+
var cur = cm.getCursor();
|
3346 |
+
cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
|
3347 |
+
},
|
3348 |
undo: function(cm) {cm.undo();},
|
3349 |
redo: function(cm) {cm.redo();},
|
3350 |
goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
|
3440 |
"Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
|
3441 |
"Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
|
3442 |
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
|
3443 |
+
"Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
|
3444 |
fallthrough: ["basic", "emacsy"]
|
3445 |
};
|
3446 |
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
|
3479 |
|
3480 |
for (var i = 0; i < maps.length; ++i) {
|
3481 |
var done = lookup(maps[i]);
|
3482 |
+
if (done) return done != "stop";
|
3483 |
}
|
3484 |
}
|
3485 |
function isModifierKey(event) {
|
3487 |
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
|
3488 |
}
|
3489 |
function keyName(event, noShift) {
|
3490 |
+
if (opera && event.keyCode == 34 && event["char"]) return false;
|
3491 |
var name = keyNames[event.keyCode];
|
3492 |
if (name == null || event.altGraphKey) return false;
|
3493 |
if (event.altKey) name = "Alt-" + name;
|
3661 |
if (min != null && cm) regChange(cm, min, max + 1);
|
3662 |
this.lines.length = 0;
|
3663 |
this.explicitlyCleared = true;
|
3664 |
+
if (this.atomic && this.doc.cantEdit) {
|
3665 |
this.doc.cantEdit = false;
|
3666 |
if (cm) reCheckSelection(cm);
|
3667 |
}
|
3684 |
return from && {from: from, to: to};
|
3685 |
};
|
3686 |
|
3687 |
+
TextMarker.prototype.changed = function() {
|
3688 |
+
var pos = this.find(), cm = this.doc.cm;
|
3689 |
+
if (!pos || !cm) return;
|
3690 |
+
var line = getLine(this.doc, pos.from.line);
|
3691 |
+
clearCachedMeasurement(cm, line);
|
3692 |
+
if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.showingTo) {
|
3693 |
+
for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
|
3694 |
+
if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
|
3695 |
+
break;
|
3696 |
+
}
|
3697 |
+
runInOp(cm, function() { cm.curOp.selectionChanged = true; });
|
3698 |
+
}
|
3699 |
};
|
3700 |
|
3701 |
TextMarker.prototype.attachLine = function(line) {
|
3724 |
if (marker.replacedWith) {
|
3725 |
marker.collapsed = true;
|
3726 |
marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
|
3727 |
+
if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
|
3728 |
}
|
3729 |
if (marker.collapsed) sawCollapsedSpans = true;
|
3730 |
|
3731 |
+
if (marker.addToHistory)
|
3732 |
+
addToHistory(doc, {from: from, to: to, origin: "markText"},
|
3733 |
+
{head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
|
3734 |
+
|
3735 |
var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
|
3736 |
doc.iter(curLine, to.line + 1, function(line) {
|
3737 |
if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
|
3796 |
SharedTextMarker.prototype.find = function() {
|
3797 |
return this.primary.find();
|
3798 |
};
|
|
|
|
|
|
|
|
|
|
|
3799 |
|
3800 |
function markTextShared(doc, from, to, options, type) {
|
3801 |
options = copyObj(options);
|
3898 |
}
|
3899 |
}
|
3900 |
}
|
3901 |
+
if (sameLine && first) {
|
3902 |
+
// Make sure we didn't create any zero-length spans
|
3903 |
+
for (var i = 0; i < first.length; ++i)
|
3904 |
+
if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
|
3905 |
+
first.splice(i--, 1);
|
3906 |
+
if (!first.length) first = null;
|
3907 |
+
}
|
3908 |
|
3909 |
var newMarkers = [first];
|
3910 |
if (!sameLine) {
|
3999 |
sp = sps[i];
|
4000 |
if (!sp.marker.collapsed) continue;
|
4001 |
if (sp.from == null) return true;
|
4002 |
+
if (sp.marker.replacedWith) continue;
|
4003 |
if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
|
4004 |
return true;
|
4005 |
}
|
4013 |
return true;
|
4014 |
for (var sp, i = 0; i < line.markedSpans.length; ++i) {
|
4015 |
sp = line.markedSpans[i];
|
4016 |
+
if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
|
4017 |
(sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
|
4018 |
lineIsHiddenInner(doc, line, sp)) return true;
|
4019 |
}
|
4125 |
function runMode(cm, text, mode, state, f) {
|
4126 |
var flattenSpans = mode.flattenSpans;
|
4127 |
if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
|
4128 |
+
var curStart = 0, curStyle = null;
|
4129 |
+
var stream = new StringStream(text, cm.options.tabSize), style;
|
4130 |
if (text == "" && mode.blankLine) mode.blankLine(state);
|
4131 |
while (!stream.eol()) {
|
4132 |
+
if (stream.pos > cm.options.maxHighlightLength) {
|
|
|
4133 |
flattenSpans = false;
|
4134 |
// Webkit seems to refuse to render text nodes longer than 57444 characters
|
4135 |
stream.pos = Math.min(text.length, stream.start + 50000);
|
4136 |
style = null;
|
4137 |
+
} else {
|
4138 |
+
style = mode.token(stream, state);
|
4139 |
}
|
|
|
|
|
4140 |
if (!flattenSpans || curStyle != style) {
|
4141 |
+
if (curStart < stream.start) f(stream.start, curStyle);
|
4142 |
+
curStart = stream.start; curStyle = style;
|
4143 |
+
}
|
4144 |
+
stream.start = stream.pos;
|
4145 |
}
|
4146 |
+
if (curStart < stream.pos) f(stream.pos, curStyle);
|
4147 |
}
|
4148 |
|
4149 |
function highlightLine(cm, line, state) {
|
4151 |
// mode/overlays that it is based on (for easy invalidation).
|
4152 |
var st = [cm.state.modeGen];
|
4153 |
// Compute the base array of styles
|
4154 |
+
runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
|
4155 |
|
4156 |
// Run overlays, adjust style array.
|
4157 |
for (var o = 0; o < cm.state.overlays.length; ++o) {
|
4158 |
+
var overlay = cm.state.overlays[o], i = 1, at = 0;
|
4159 |
+
runMode(cm, line.text, overlay.mode, true, function(end, style) {
|
4160 |
+
var start = i;
|
4161 |
// Ensure there's a token end at the current position, and that i points at it
|
4162 |
+
while (at < end) {
|
4163 |
+
var i_end = st[i];
|
4164 |
+
if (i_end > end)
|
4165 |
+
st.splice(i, 1, end, st[i+1], i_end);
|
|
|
|
|
|
|
|
|
4166 |
i += 2;
|
4167 |
+
at = Math.min(end, i_end);
|
4168 |
}
|
4169 |
if (!style) return;
|
4170 |
if (overlay.opaque) {
|
4171 |
+
st.splice(start, i - start, end, style);
|
4172 |
i = start + 2;
|
4173 |
} else {
|
4174 |
for (; start < i; start += 2) {
|
4194 |
var mode = cm.doc.mode;
|
4195 |
var stream = new StringStream(line.text, cm.options.tabSize);
|
4196 |
if (line.text == "" && mode.blankLine) mode.blankLine(state);
|
4197 |
+
while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
|
4198 |
mode.token(stream, state);
|
4199 |
stream.start = stream.pos;
|
4200 |
}
|
4208 |
}
|
4209 |
|
4210 |
function lineContent(cm, realLine, measure) {
|
4211 |
+
var merged, line = realLine, empty = true;
|
4212 |
+
while (merged = collapsedSpanAtStart(line))
|
|
|
4213 |
line = getLine(cm.doc, merged.find().from.line);
|
|
|
|
|
4214 |
|
4215 |
var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
|
4216 |
+
measure: null, measuredSomething: false, cm: cm};
|
4217 |
if (line.textClass) builder.pre.className = line.textClass;
|
4218 |
|
4219 |
do {
|
4220 |
+
if (line.text) empty = false;
|
4221 |
builder.measure = line == realLine && measure;
|
4222 |
builder.pos = 0;
|
4223 |
builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
|
4224 |
if ((ie || webkit) && cm.getOption("lineWrapping"))
|
4225 |
builder.addToken = buildTokenSplitSpaces(builder.addToken);
|
|
|
|
|
|
|
|
|
4226 |
var next = insertLineContent(line, builder, getLineStyles(cm, line));
|
4227 |
+
if (measure && line == realLine && !builder.measuredSomething) {
|
4228 |
+
measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
|
4229 |
+
builder.measuredSomething = true;
|
|
|
4230 |
}
|
4231 |
+
if (next) line = getLine(cm.doc, next.to.line);
|
4232 |
} while (next);
|
4233 |
|
4234 |
+
if (measure && !builder.measuredSomething && !measure[0])
|
4235 |
+
measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
|
4236 |
if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
|
4237 |
builder.pre.appendChild(document.createTextNode("\u00a0"));
|
4238 |
|
4301 |
if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
|
4302 |
ch = text.slice(i, i + 2);
|
4303 |
++i;
|
4304 |
+
} else if (i && wrapping && spanAffectsWrapping(text, i)) {
|
|
|
4305 |
builder.pre.appendChild(elt("wbr"));
|
4306 |
}
|
4307 |
var span = builder.measure[builder.pos] =
|
4315 |
span.style.whiteSpace = "normal";
|
4316 |
builder.pos += ch.length;
|
4317 |
}
|
4318 |
+
if (text.length) builder.measuredSomething = true;
|
4319 |
}
|
4320 |
|
4321 |
function buildTokenSplitSpaces(inner) {
|
4333 |
function buildCollapsedSpan(builder, size, widget) {
|
4334 |
if (widget) {
|
4335 |
if (!builder.display) widget = widget.cloneNode(true);
|
4336 |
+
if (builder.measure) {
|
4337 |
+
builder.measure[builder.pos] = size ? widget
|
4338 |
+
: builder.pre.appendChild(zeroWidthElement(builder.cm.display.measure));
|
4339 |
+
builder.measuredSomething = true;
|
4340 |
}
|
4341 |
+
builder.pre.appendChild(widget);
|
4342 |
}
|
4343 |
builder.pos += size;
|
4344 |
}
|
4346 |
// Outputs a number of spans to make up a line, taking highlighting
|
4347 |
// and marked text into account.
|
4348 |
function insertLineContent(line, builder, styles) {
|
4349 |
+
var spans = line.markedSpans, allText = line.text, at = 0;
|
4350 |
if (!spans) {
|
4351 |
for (var i = 1; i < styles.length; i+=2)
|
4352 |
+
builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1]));
|
4353 |
return;
|
4354 |
}
|
4355 |
|
4356 |
+
var len = allText.length, pos = 0, i = 1, text = "", style;
|
|
|
4357 |
var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
|
4358 |
for (;;) {
|
4359 |
if (nextChange == pos) { // Update current marker set
|
4367 |
if (m.className) spanStyle += " " + m.className;
|
4368 |
if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
|
4369 |
if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
|
4370 |
+
if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
|
4371 |
collapsed = sp;
|
4372 |
} else if (sp.from > pos && nextChange > sp.from) {
|
4373 |
nextChange = sp.from;
|
4397 |
pos = end;
|
4398 |
spanStartStyle = "";
|
4399 |
}
|
4400 |
+
text = allText.slice(at, at = styles[i++]);
|
4401 |
+
style = styleToClass(styles[i++]);
|
4402 |
}
|
4403 |
}
|
4404 |
}
|
4590 |
this.scrollTop = this.scrollLeft = 0;
|
4591 |
this.cantEdit = false;
|
4592 |
this.history = makeHistory();
|
4593 |
+
this.cleanGeneration = 1;
|
4594 |
this.frontier = firstLine;
|
4595 |
var start = Pos(firstLine, 0);
|
4596 |
this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
|
4602 |
};
|
4603 |
|
4604 |
Doc.prototype = createObj(BranchChunk.prototype, {
|
4605 |
+
constructor: Doc,
|
4606 |
iter: function(from, to, op) {
|
4607 |
if (op) this.iterN(from - this.first, to - from, op);
|
4608 |
else this.iterN(this.first, this.first + this.size, from);
|
4643 |
replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
|
4644 |
},
|
4645 |
removeLine: function(line) {
|
4646 |
+
if (line) replaceRange(this, "", clipPos(this, Pos(line - 1)), clipPos(this, Pos(line)));
|
4647 |
+
else replaceRange(this, "", Pos(0, 0), clipPos(this, Pos(1, 0)));
|
4648 |
},
|
4649 |
|
4650 |
getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
|
4691 |
var hist = this.history;
|
4692 |
return {undo: hist.done.length, redo: hist.undone.length};
|
4693 |
},
|
4694 |
+
clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
|
4695 |
|
4696 |
markClean: function() {
|
4697 |
+
this.cleanGeneration = this.changeGeneration();
|
4698 |
+
},
|
4699 |
+
changeGeneration: function() {
|
4700 |
this.history.lastOp = this.history.lastOrigin = null;
|
4701 |
+
return this.history.generation;
|
4702 |
+
},
|
4703 |
+
isClean: function (gen) {
|
4704 |
+
return this.history.generation == (gen || this.cleanGeneration);
|
4705 |
},
|
|
|
4706 |
|
4707 |
getHistory: function() {
|
4708 |
return {done: copyHistoryArray(this.history.done),
|
4709 |
undone: copyHistoryArray(this.history.undone)};
|
4710 |
},
|
4711 |
setHistory: function(histData) {
|
4712 |
+
var hist = this.history = makeHistory(this.history.maxGeneration);
|
4713 |
hist.done = histData.done.slice(0);
|
4714 |
hist.undone = histData.undone.slice(0);
|
4715 |
},
|
4939 |
|
4940 |
// HISTORY
|
4941 |
|
4942 |
+
function makeHistory(startGen) {
|
4943 |
return {
|
4944 |
// Arrays of history events. Doing something adds an event to
|
4945 |
// done and clears undo. Undoing moves events from done to
|
4949 |
// event
|
4950 |
lastTime: 0, lastOp: null, lastOrigin: null,
|
4951 |
// Used by the isClean() method
|
4952 |
+
generation: startGen || 1, maxGeneration: startGen || 1
|
4953 |
};
|
4954 |
}
|
4955 |
|
4977 |
if (cur &&
|
4978 |
(hist.lastOp == opId ||
|
4979 |
hist.lastOrigin == change.origin && change.origin &&
|
4980 |
+
((change.origin.charAt(0) == "+" && doc.cm && hist.lastTime > time - doc.cm.options.historyEventDelay) ||
|
4981 |
+
change.origin.charAt(0) == "*"))) {
|
4982 |
// Merge this change into the last event
|
4983 |
var last = lst(cur.changes);
|
4984 |
if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
|
4993 |
} else {
|
4994 |
// Can not be merged, start a new event.
|
4995 |
cur = {changes: [historyChangeFromChange(doc, change)],
|
4996 |
+
generation: hist.generation,
|
4997 |
anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
|
4998 |
anchorAfter: selAfter.anchor, headAfter: selAfter.head};
|
4999 |
hist.done.push(cur);
|
5000 |
+
hist.generation = ++hist.maxGeneration;
|
5001 |
while (hist.done.length > hist.undoDepth)
|
5002 |
hist.done.shift();
|
|
|
|
|
|
|
|
|
|
|
|
|
5003 |
}
|
5004 |
hist.lastTime = time;
|
5005 |
hist.lastOp = opId;
|
5114 |
if (e.stopPropagation) e.stopPropagation();
|
5115 |
else e.cancelBubble = true;
|
5116 |
}
|
5117 |
+
function e_defaultPrevented(e) {
|
5118 |
+
return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
|
5119 |
+
}
|
5120 |
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
|
5121 |
CodeMirror.e_stop = e_stop;
|
5122 |
CodeMirror.e_preventDefault = e_preventDefault;
|
5183 |
delayedCallbacks.push(bnd(arr[i]));
|
5184 |
}
|
5185 |
|
5186 |
+
function signalDOMEvent(cm, e) {
|
5187 |
+
signal(cm, e.type, cm, e);
|
5188 |
+
return e_defaultPrevented(e);
|
5189 |
+
}
|
5190 |
+
|
5191 |
function fireDelayed() {
|
5192 |
--delayedCallbackDepth;
|
5193 |
var delayed = delayedCallbacks;
|
5242 |
if (ios) { // Mobile Safari apparently has a bug where select() is broken.
|
5243 |
node.selectionStart = 0;
|
5244 |
node.selectionEnd = node.value.length;
|
5245 |
+
} else {
|
5246 |
+
// Suppress mysterious IE10 errors
|
5247 |
+
try { node.select(); }
|
5248 |
+
catch(_e) {}
|
5249 |
+
}
|
5250 |
}
|
5251 |
|
5252 |
function indexOf(collection, elt) {
|
5280 |
return function(){return f.apply(null, args);};
|
5281 |
}
|
5282 |
|
5283 |
+
var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
|
5284 |
function isWordChar(ch) {
|
5285 |
return /\w/.test(ch) || ch > "\x80" &&
|
5286 |
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
|
5341 |
// word wrapping between certain characters *only* if a new inline
|
5342 |
// element is started between them. This makes it hard to reliably
|
5343 |
// measure the position of things, since that requires inserting an
|
5344 |
+
// extra span. This terribly fragile set of tests matches the
|
5345 |
// character combinations that suffer from this phenomenon on the
|
5346 |
// various browsers.
|
5347 |
+
function spanAffectsWrapping() { return false; }
|
5348 |
+
if (gecko) // Only for "$'"
|
5349 |
+
spanAffectsWrapping = function(str, i) {
|
5350 |
+
return str.charCodeAt(i - 1) == 36 && str.charCodeAt(i) == 39;
|
5351 |
+
};
|
5352 |
+
else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
|
5353 |
+
spanAffectsWrapping = function(str, i) {
|
5354 |
+
return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
|
5355 |
+
};
|
5356 |
+
else if (webkit)
|
5357 |
+
spanAffectsWrapping = function(str, i) {
|
5358 |
+
if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i)))
|
5359 |
+
return true;
|
5360 |
+
return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
|
5361 |
+
};
|
5362 |
|
5363 |
var knownScrollbarWidth;
|
5364 |
function scrollbarWidth(measure) {
|
5477 |
return Pos(lineN, ch);
|
5478 |
}
|
5479 |
|
5480 |
+
function compareBidiLevel(order, a, b) {
|
5481 |
+
var linedir = order[0].level;
|
5482 |
+
if (a == linedir) return true;
|
5483 |
+
if (b == linedir) return false;
|
5484 |
+
return a < b;
|
5485 |
+
}
|
5486 |
+
var bidiOther;
|
5487 |
+
function getBidiPartAt(order, pos) {
|
5488 |
+
for (var i = 0, found; i < order.length; ++i) {
|
5489 |
+
var cur = order[i];
|
5490 |
+
if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
|
5491 |
+
if (cur.from == pos || cur.to == pos) {
|
5492 |
+
if (found == null) {
|
5493 |
+
found = i;
|
5494 |
+
} else if (compareBidiLevel(order, cur.level, order[found].level)) {
|
5495 |
+
bidiOther = found;
|
5496 |
+
return i;
|
5497 |
+
} else {
|
5498 |
+
bidiOther = i;
|
5499 |
+
return found;
|
5500 |
+
}
|
5501 |
+
}
|
5502 |
+
}
|
5503 |
+
bidiOther = null;
|
5504 |
+
return found;
|
5505 |
+
}
|
5506 |
+
|
5507 |
+
function moveInLine(line, pos, dir, byUnit) {
|
5508 |
+
if (!byUnit) return pos + dir;
|
5509 |
+
do pos += dir;
|
5510 |
+
while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
|
5511 |
+
return pos;
|
5512 |
+
}
|
5513 |
+
|
5514 |
// This is somewhat involved. It is needed in order to move
|
5515 |
// 'visually' through bi-directional text -- i.e., pressing left
|
5516 |
// should make the cursor go left, even when in RTL text. The
|
5520 |
function moveVisually(line, start, dir, byUnit) {
|
5521 |
var bidi = getOrder(line);
|
5522 |
if (!bidi) return moveLogically(line, start, dir, byUnit);
|
5523 |
+
var pos = getBidiPartAt(bidi, start), part = bidi[pos];
|
5524 |
+
var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
|
5525 |
+
|
5526 |
+
for (;;) {
|
5527 |
+
if (target > part.from && target < part.to) return target;
|
5528 |
+
if (target == part.from || target == part.to) {
|
5529 |
+
if (getBidiPartAt(bidi, target) == pos) return target;
|
5530 |
+
part = bidi[pos += dir];
|
5531 |
+
return (dir > 0) == part.level % 2 ? part.to : part.from;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5532 |
} else {
|
5533 |
+
part = bidi[pos += dir];
|
5534 |
+
if (!part) return null;
|
5535 |
+
if ((dir > 0) == part.level % 2)
|
5536 |
+
target = moveInLine(line, part.to, -1, byUnit);
|
5537 |
+
else
|
5538 |
+
target = moveInLine(line, part.from, 1, byUnit);
|
|
|
5539 |
}
|
5540 |
}
|
|
|
|
|
5541 |
}
|
5542 |
|
5543 |
function moveLogically(line, start, dir, byUnit) {
|
5709 |
|
5710 |
// THE END
|
5711 |
|
5712 |
+
CodeMirror.version = "3.14.0";
|
5713 |
|
5714 |
return CodeMirror;
|
5715 |
})();
|
vendor/codemirror/mode/clike.js
CHANGED
@@ -1,302 +1,361 @@
|
|
1 |
-
CodeMirror.defineMode("clike", function(config, parserConfig) {
|
2 |
-
var indentUnit = config.indentUnit,
|
3 |
-
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
|
4 |
-
dontAlignCalls = parserConfig.dontAlignCalls,
|
5 |
-
keywords = parserConfig.keywords || {},
|
6 |
-
builtin = parserConfig.builtin || {},
|
7 |
-
blockKeywords = parserConfig.blockKeywords || {},
|
8 |
-
atoms = parserConfig.atoms || {},
|
9 |
-
hooks = parserConfig.hooks || {},
|
10 |
-
multiLineStrings = parserConfig.multiLineStrings;
|
11 |
-
var isOperatorChar = /[+\-*&%=<>!?|\/]/;
|
12 |
-
|
13 |
-
var curPunc;
|
14 |
-
|
15 |
-
function tokenBase(stream, state) {
|
16 |
-
var ch = stream.next();
|
17 |
-
if (hooks[ch]) {
|
18 |
-
var result = hooks[ch](stream, state);
|
19 |
-
if (result !== false) return result;
|
20 |
-
}
|
21 |
-
if (ch == '"' || ch == "'") {
|
22 |
-
state.tokenize = tokenString(ch);
|
23 |
-
return state.tokenize(stream, state);
|
24 |
-
}
|
25 |
-
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
26 |
-
curPunc = ch;
|
27 |
-
return null;
|
28 |
-
}
|
29 |
-
if (/\d/.test(ch)) {
|
30 |
-
stream.eatWhile(/[\w\.]/);
|
31 |
-
return "number";
|
32 |
-
}
|
33 |
-
if (ch == "/") {
|
34 |
-
if (stream.eat("*")) {
|
35 |
-
state.tokenize = tokenComment;
|
36 |
-
return tokenComment(stream, state);
|
37 |
-
}
|
38 |
-
if (stream.eat("/")) {
|
39 |
-
stream.skipToEnd();
|
40 |
-
return "comment";
|
41 |
-
}
|
42 |
-
}
|
43 |
-
if (isOperatorChar.test(ch)) {
|
44 |
-
stream.eatWhile(isOperatorChar);
|
45 |
-
return "operator";
|
46 |
-
}
|
47 |
-
stream.eatWhile(/[\w\$_]/);
|
48 |
-
var cur = stream.current();
|
49 |
-
if (keywords.propertyIsEnumerable(cur)) {
|
50 |
-
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
51 |
-
return "keyword";
|
52 |
-
}
|
53 |
-
if (builtin.propertyIsEnumerable(cur)) {
|
54 |
-
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
55 |
-
return "builtin";
|
56 |
-
}
|
57 |
-
if (atoms.propertyIsEnumerable(cur)) return "atom";
|
58 |
-
return "variable";
|
59 |
-
}
|
60 |
-
|
61 |
-
function tokenString(quote) {
|
62 |
-
return function(stream, state) {
|
63 |
-
var escaped = false, next, end = false;
|
64 |
-
while ((next = stream.next()) != null) {
|
65 |
-
if (next == quote && !escaped) {end = true; break;}
|
66 |
-
escaped = !escaped && next == "\\";
|
67 |
-
}
|
68 |
-
if (end || !(escaped || multiLineStrings))
|
69 |
-
state.tokenize = null;
|
70 |
-
return "string";
|
71 |
-
};
|
72 |
-
}
|
73 |
-
|
74 |
-
function tokenComment(stream, state) {
|
75 |
-
var maybeEnd = false, ch;
|
76 |
-
while (ch = stream.next()) {
|
77 |
-
if (ch == "/" && maybeEnd) {
|
78 |
-
state.tokenize = null;
|
79 |
-
break;
|
80 |
-
}
|
81 |
-
maybeEnd = (ch == "*");
|
82 |
-
}
|
83 |
-
return "comment";
|
84 |
-
}
|
85 |
-
|
86 |
-
function Context(indented, column, type, align, prev) {
|
87 |
-
this.indented = indented;
|
88 |
-
this.column = column;
|
89 |
-
this.type = type;
|
90 |
-
this.align = align;
|
91 |
-
this.prev = prev;
|
92 |
-
}
|
93 |
-
function pushContext(state, col, type) {
|
94 |
-
var indent = state.indented;
|
95 |
-
if (state.context && state.context.type == "statement")
|
96 |
-
indent = state.context.indented;
|
97 |
-
return state.context = new Context(indent, col, type, null, state.context);
|
98 |
-
}
|
99 |
-
function popContext(state) {
|
100 |
-
var t = state.context.type;
|
101 |
-
if (t == ")" || t == "]" || t == "}")
|
102 |
-
state.indented = state.context.indented;
|
103 |
-
return state.context = state.context.prev;
|
104 |
-
}
|
105 |
-
|
106 |
-
// Interface
|
107 |
-
|
108 |
-
return {
|
109 |
-
startState: function(basecolumn) {
|
110 |
-
return {
|
111 |
-
tokenize: null,
|
112 |
-
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
|
113 |
-
indented: 0,
|
114 |
-
startOfLine: true
|
115 |
-
};
|
116 |
-
},
|
117 |
-
|
118 |
-
token: function(stream, state) {
|
119 |
-
var ctx = state.context;
|
120 |
-
if (stream.sol()) {
|
121 |
-
if (ctx.align == null) ctx.align = false;
|
122 |
-
state.indented = stream.indentation();
|
123 |
-
state.startOfLine = true;
|
124 |
-
}
|
125 |
-
if (stream.eatSpace()) return null;
|
126 |
-
curPunc = null;
|
127 |
-
var style = (state.tokenize || tokenBase)(stream, state);
|
128 |
-
if (style == "comment" || style == "meta") return style;
|
129 |
-
if (ctx.align == null) ctx.align = true;
|
130 |
-
|
131 |
-
if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && ctx.type == "statement") popContext(state);
|
132 |
-
else if (curPunc == "{") pushContext(state, stream.column(), "}");
|
133 |
-
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
134 |
-
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
135 |
-
else if (curPunc == "}") {
|
136 |
-
while (ctx.type == "statement") ctx = popContext(state);
|
137 |
-
if (ctx.type == "}") ctx = popContext(state);
|
138 |
-
while (ctx.type == "statement") ctx = popContext(state);
|
139 |
-
}
|
140 |
-
else if (curPunc == ctx.type) popContext(state);
|
141 |
-
else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
|
142 |
-
pushContext(state, stream.column(), "statement");
|
143 |
-
state.startOfLine = false;
|
144 |
-
return style;
|
145 |
-
},
|
146 |
-
|
147 |
-
indent: function(state, textAfter) {
|
148 |
-
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
|
149 |
-
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
|
150 |
-
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
|
151 |
-
var closing = firstChar == ctx.type;
|
152 |
-
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
|
153 |
-
else if (
|
154 |
-
else if (ctx.
|
155 |
-
else return ctx.indented + (closing ? 0 : indentUnit);
|
156 |
-
},
|
157 |
-
|
158 |
-
electricChars: "{}"
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
"
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
"
|
246 |
-
"
|
247 |
-
"
|
248 |
-
|
249 |
-
|
250 |
-
"
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
"
|
274 |
-
|
275 |
-
|
276 |
-
"
|
277 |
-
|
278 |
-
|
279 |
-
"
|
280 |
-
"
|
281 |
-
|
282 |
-
"
|
283 |
-
"
|
284 |
-
|
285 |
-
|
286 |
-
"
|
287 |
-
|
288 |
-
|
289 |
-
"
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineMode("clike", function(config, parserConfig) {
|
2 |
+
var indentUnit = config.indentUnit,
|
3 |
+
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
|
4 |
+
dontAlignCalls = parserConfig.dontAlignCalls,
|
5 |
+
keywords = parserConfig.keywords || {},
|
6 |
+
builtin = parserConfig.builtin || {},
|
7 |
+
blockKeywords = parserConfig.blockKeywords || {},
|
8 |
+
atoms = parserConfig.atoms || {},
|
9 |
+
hooks = parserConfig.hooks || {},
|
10 |
+
multiLineStrings = parserConfig.multiLineStrings;
|
11 |
+
var isOperatorChar = /[+\-*&%=<>!?|\/]/;
|
12 |
+
|
13 |
+
var curPunc;
|
14 |
+
|
15 |
+
function tokenBase(stream, state) {
|
16 |
+
var ch = stream.next();
|
17 |
+
if (hooks[ch]) {
|
18 |
+
var result = hooks[ch](stream, state);
|
19 |
+
if (result !== false) return result;
|
20 |
+
}
|
21 |
+
if (ch == '"' || ch == "'") {
|
22 |
+
state.tokenize = tokenString(ch);
|
23 |
+
return state.tokenize(stream, state);
|
24 |
+
}
|
25 |
+
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
26 |
+
curPunc = ch;
|
27 |
+
return null;
|
28 |
+
}
|
29 |
+
if (/\d/.test(ch)) {
|
30 |
+
stream.eatWhile(/[\w\.]/);
|
31 |
+
return "number";
|
32 |
+
}
|
33 |
+
if (ch == "/") {
|
34 |
+
if (stream.eat("*")) {
|
35 |
+
state.tokenize = tokenComment;
|
36 |
+
return tokenComment(stream, state);
|
37 |
+
}
|
38 |
+
if (stream.eat("/")) {
|
39 |
+
stream.skipToEnd();
|
40 |
+
return "comment";
|
41 |
+
}
|
42 |
+
}
|
43 |
+
if (isOperatorChar.test(ch)) {
|
44 |
+
stream.eatWhile(isOperatorChar);
|
45 |
+
return "operator";
|
46 |
+
}
|
47 |
+
stream.eatWhile(/[\w\$_]/);
|
48 |
+
var cur = stream.current();
|
49 |
+
if (keywords.propertyIsEnumerable(cur)) {
|
50 |
+
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
51 |
+
return "keyword";
|
52 |
+
}
|
53 |
+
if (builtin.propertyIsEnumerable(cur)) {
|
54 |
+
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
55 |
+
return "builtin";
|
56 |
+
}
|
57 |
+
if (atoms.propertyIsEnumerable(cur)) return "atom";
|
58 |
+
return "variable";
|
59 |
+
}
|
60 |
+
|
61 |
+
function tokenString(quote) {
|
62 |
+
return function(stream, state) {
|
63 |
+
var escaped = false, next, end = false;
|
64 |
+
while ((next = stream.next()) != null) {
|
65 |
+
if (next == quote && !escaped) {end = true; break;}
|
66 |
+
escaped = !escaped && next == "\\";
|
67 |
+
}
|
68 |
+
if (end || !(escaped || multiLineStrings))
|
69 |
+
state.tokenize = null;
|
70 |
+
return "string";
|
71 |
+
};
|
72 |
+
}
|
73 |
+
|
74 |
+
function tokenComment(stream, state) {
|
75 |
+
var maybeEnd = false, ch;
|
76 |
+
while (ch = stream.next()) {
|
77 |
+
if (ch == "/" && maybeEnd) {
|
78 |
+
state.tokenize = null;
|
79 |
+
break;
|
80 |
+
}
|
81 |
+
maybeEnd = (ch == "*");
|
82 |
+
}
|
83 |
+
return "comment";
|
84 |
+
}
|
85 |
+
|
86 |
+
function Context(indented, column, type, align, prev) {
|
87 |
+
this.indented = indented;
|
88 |
+
this.column = column;
|
89 |
+
this.type = type;
|
90 |
+
this.align = align;
|
91 |
+
this.prev = prev;
|
92 |
+
}
|
93 |
+
function pushContext(state, col, type) {
|
94 |
+
var indent = state.indented;
|
95 |
+
if (state.context && state.context.type == "statement")
|
96 |
+
indent = state.context.indented;
|
97 |
+
return state.context = new Context(indent, col, type, null, state.context);
|
98 |
+
}
|
99 |
+
function popContext(state) {
|
100 |
+
var t = state.context.type;
|
101 |
+
if (t == ")" || t == "]" || t == "}")
|
102 |
+
state.indented = state.context.indented;
|
103 |
+
return state.context = state.context.prev;
|
104 |
+
}
|
105 |
+
|
106 |
+
// Interface
|
107 |
+
|
108 |
+
return {
|
109 |
+
startState: function(basecolumn) {
|
110 |
+
return {
|
111 |
+
tokenize: null,
|
112 |
+
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
|
113 |
+
indented: 0,
|
114 |
+
startOfLine: true
|
115 |
+
};
|
116 |
+
},
|
117 |
+
|
118 |
+
token: function(stream, state) {
|
119 |
+
var ctx = state.context;
|
120 |
+
if (stream.sol()) {
|
121 |
+
if (ctx.align == null) ctx.align = false;
|
122 |
+
state.indented = stream.indentation();
|
123 |
+
state.startOfLine = true;
|
124 |
+
}
|
125 |
+
if (stream.eatSpace()) return null;
|
126 |
+
curPunc = null;
|
127 |
+
var style = (state.tokenize || tokenBase)(stream, state);
|
128 |
+
if (style == "comment" || style == "meta") return style;
|
129 |
+
if (ctx.align == null) ctx.align = true;
|
130 |
+
|
131 |
+
if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && ctx.type == "statement") popContext(state);
|
132 |
+
else if (curPunc == "{") pushContext(state, stream.column(), "}");
|
133 |
+
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
134 |
+
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
135 |
+
else if (curPunc == "}") {
|
136 |
+
while (ctx.type == "statement") ctx = popContext(state);
|
137 |
+
if (ctx.type == "}") ctx = popContext(state);
|
138 |
+
while (ctx.type == "statement") ctx = popContext(state);
|
139 |
+
}
|
140 |
+
else if (curPunc == ctx.type) popContext(state);
|
141 |
+
else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
|
142 |
+
pushContext(state, stream.column(), "statement");
|
143 |
+
state.startOfLine = false;
|
144 |
+
return style;
|
145 |
+
},
|
146 |
+
|
147 |
+
indent: function(state, textAfter) {
|
148 |
+
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
|
149 |
+
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
|
150 |
+
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
|
151 |
+
var closing = firstChar == ctx.type;
|
152 |
+
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
|
153 |
+
else if (ctx.align && (!dontAlignCalls || ctx.type != ")")) return ctx.column + (closing ? 0 : 1);
|
154 |
+
else if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
|
155 |
+
else return ctx.indented + (closing ? 0 : indentUnit);
|
156 |
+
},
|
157 |
+
|
158 |
+
electricChars: "{}",
|
159 |
+
blockCommentStart: "/*",
|
160 |
+
blockCommentEnd: "*/",
|
161 |
+
lineComment: "//"
|
162 |
+
};
|
163 |
+
});
|
164 |
+
|
165 |
+
(function() {
|
166 |
+
function words(str) {
|
167 |
+
var obj = {}, words = str.split(" ");
|
168 |
+
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
|
169 |
+
return obj;
|
170 |
+
}
|
171 |
+
var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
|
172 |
+
"double static else struct entry switch extern typedef float union for unsigned " +
|
173 |
+
"goto while enum void const signed volatile";
|
174 |
+
|
175 |
+
function cppHook(stream, state) {
|
176 |
+
if (!state.startOfLine) return false;
|
177 |
+
for (;;) {
|
178 |
+
if (stream.skipTo("\\")) {
|
179 |
+
stream.next();
|
180 |
+
if (stream.eol()) {
|
181 |
+
state.tokenize = cppHook;
|
182 |
+
break;
|
183 |
+
}
|
184 |
+
} else {
|
185 |
+
stream.skipToEnd();
|
186 |
+
state.tokenize = null;
|
187 |
+
break;
|
188 |
+
}
|
189 |
+
}
|
190 |
+
return "meta";
|
191 |
+
}
|
192 |
+
|
193 |
+
// C#-style strings where "" escapes a quote.
|
194 |
+
function tokenAtString(stream, state) {
|
195 |
+
var next;
|
196 |
+
while ((next = stream.next()) != null) {
|
197 |
+
if (next == '"' && !stream.eat('"')) {
|
198 |
+
state.tokenize = null;
|
199 |
+
break;
|
200 |
+
}
|
201 |
+
}
|
202 |
+
return "string";
|
203 |
+
}
|
204 |
+
|
205 |
+
function mimes(ms, mode) {
|
206 |
+
for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode);
|
207 |
+
}
|
208 |
+
|
209 |
+
mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], {
|
210 |
+
name: "clike",
|
211 |
+
keywords: words(cKeywords),
|
212 |
+
blockKeywords: words("case do else for if switch while struct"),
|
213 |
+
atoms: words("null"),
|
214 |
+
hooks: {"#": cppHook}
|
215 |
+
});
|
216 |
+
mimes(["text/x-c++src", "text/x-c++hdr"], {
|
217 |
+
name: "clike",
|
218 |
+
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
|
219 |
+
"static_cast typeid catch operator template typename class friend private " +
|
220 |
+
"this using const_cast inline public throw virtual delete mutable protected " +
|
221 |
+
"wchar_t"),
|
222 |
+
blockKeywords: words("catch class do else finally for if struct switch try while"),
|
223 |
+
atoms: words("true false null"),
|
224 |
+
hooks: {"#": cppHook}
|
225 |
+
});
|
226 |
+
CodeMirror.defineMIME("text/x-java", {
|
227 |
+
name: "clike",
|
228 |
+
keywords: words("abstract assert boolean break byte case catch char class const continue default " +
|
229 |
+
"do double else enum extends final finally float for goto if implements import " +
|
230 |
+
"instanceof int interface long native new package private protected public " +
|
231 |
+
"return short static strictfp super switch synchronized this throw throws transient " +
|
232 |
+
"try void volatile while"),
|
233 |
+
blockKeywords: words("catch class do else finally for if switch try while"),
|
234 |
+
atoms: words("true false null"),
|
235 |
+
hooks: {
|
236 |
+
"@": function(stream) {
|
237 |
+
stream.eatWhile(/[\w\$_]/);
|
238 |
+
return "meta";
|
239 |
+
}
|
240 |
+
}
|
241 |
+
});
|
242 |
+
CodeMirror.defineMIME("text/x-csharp", {
|
243 |
+
name: "clike",
|
244 |
+
keywords: words("abstract as base break case catch checked class const continue" +
|
245 |
+
" default delegate do else enum event explicit extern finally fixed for" +
|
246 |
+
" foreach goto if implicit in interface internal is lock namespace new" +
|
247 |
+
" operator out override params private protected public readonly ref return sealed" +
|
248 |
+
" sizeof stackalloc static struct switch this throw try typeof unchecked" +
|
249 |
+
" unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
|
250 |
+
" global group into join let orderby partial remove select set value var yield"),
|
251 |
+
blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
|
252 |
+
builtin: words("Boolean Byte Char DateTime DateTimeOffset Decimal Double" +
|
253 |
+
" Guid Int16 Int32 Int64 Object SByte Single String TimeSpan UInt16 UInt32" +
|
254 |
+
" UInt64 bool byte char decimal double short int long object" +
|
255 |
+
" sbyte float string ushort uint ulong"),
|
256 |
+
atoms: words("true false null"),
|
257 |
+
hooks: {
|
258 |
+
"@": function(stream, state) {
|
259 |
+
if (stream.eat('"')) {
|
260 |
+
state.tokenize = tokenAtString;
|
261 |
+
return tokenAtString(stream, state);
|
262 |
+
}
|
263 |
+
stream.eatWhile(/[\w\$_]/);
|
264 |
+
return "meta";
|
265 |
+
}
|
266 |
+
}
|
267 |
+
});
|
268 |
+
CodeMirror.defineMIME("text/x-scala", {
|
269 |
+
name: "clike",
|
270 |
+
keywords: words(
|
271 |
+
|
272 |
+
/* scala */
|
273 |
+
"abstract case catch class def do else extends false final finally for forSome if " +
|
274 |
+
"implicit import lazy match new null object override package private protected return " +
|
275 |
+
"sealed super this throw trait try trye type val var while with yield _ : = => <- <: " +
|
276 |
+
"<% >: # @ " +
|
277 |
+
|
278 |
+
/* package scala */
|
279 |
+
"assert assume require print println printf readLine readBoolean readByte readShort " +
|
280 |
+
"readChar readInt readLong readFloat readDouble " +
|
281 |
+
|
282 |
+
"AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
|
283 |
+
"Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
|
284 |
+
"Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
|
285 |
+
"Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
|
286 |
+
"StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector :: #:: " +
|
287 |
+
|
288 |
+
/* package java.lang */
|
289 |
+
"Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
|
290 |
+
"Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
|
291 |
+
"Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
|
292 |
+
"StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
|
293 |
+
|
294 |
+
|
295 |
+
),
|
296 |
+
blockKeywords: words("catch class do else finally for forSome if match switch try while"),
|
297 |
+
atoms: words("true false null"),
|
298 |
+
hooks: {
|
299 |
+
"@": function(stream) {
|
300 |
+
stream.eatWhile(/[\w\$_]/);
|
301 |
+
return "meta";
|
302 |
+
}
|
303 |
+
}
|
304 |
+
});
|
305 |
+
mimes(["x-shader/x-vertex", "x-shader/x-fragment"], {
|
306 |
+
name: "clike",
|
307 |
+
keywords: words("float int bool void " +
|
308 |
+
"vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
|
309 |
+
"mat2 mat3 mat4 " +
|
310 |
+
"sampler1D sampler2D sampler3D samplerCube " +
|
311 |
+
"sampler1DShadow sampler2DShadow" +
|
312 |
+
"const attribute uniform varying " +
|
313 |
+
"break continue discard return " +
|
314 |
+
"for while do if else struct " +
|
315 |
+
"in out inout"),
|
316 |
+
blockKeywords: words("for while do if else struct"),
|
317 |
+
builtin: words("radians degrees sin cos tan asin acos atan " +
|
318 |
+
"pow exp log exp2 sqrt inversesqrt " +
|
319 |
+
"abs sign floor ceil fract mod min max clamp mix step smootstep " +
|
320 |
+
"length distance dot cross normalize ftransform faceforward " +
|
321 |
+
"reflect refract matrixCompMult " +
|
322 |
+
"lessThan lessThanEqual greaterThan greaterThanEqual " +
|
323 |
+
"equal notEqual any all not " +
|
324 |
+
"texture1D texture1DProj texture1DLod texture1DProjLod " +
|
325 |
+
"texture2D texture2DProj texture2DLod texture2DProjLod " +
|
326 |
+
"texture3D texture3DProj texture3DLod texture3DProjLod " +
|
327 |
+
"textureCube textureCubeLod " +
|
328 |
+
"shadow1D shadow2D shadow1DProj shadow2DProj " +
|
329 |
+
"shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
|
330 |
+
"dFdx dFdy fwidth " +
|
331 |
+
"noise1 noise2 noise3 noise4"),
|
332 |
+
atoms: words("true false " +
|
333 |
+
"gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
|
334 |
+
"gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
|
335 |
+
"gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
|
336 |
+
"gl_FogCoord " +
|
337 |
+
"gl_Position gl_PointSize gl_ClipVertex " +
|
338 |
+
"gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
|
339 |
+
"gl_TexCoord gl_FogFragCoord " +
|
340 |
+
"gl_FragCoord gl_FrontFacing " +
|
341 |
+
"gl_FragColor gl_FragData gl_FragDepth " +
|
342 |
+
"gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
|
343 |
+
"gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
|
344 |
+
"gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
|
345 |
+
"gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
|
346 |
+
"gl_ProjectionMatrixInverseTranspose " +
|
347 |
+
"gl_ModelViewProjectionMatrixInverseTranspose " +
|
348 |
+
"gl_TextureMatrixInverseTranspose " +
|
349 |
+
"gl_NormalScale gl_DepthRange gl_ClipPlane " +
|
350 |
+
"gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
|
351 |
+
"gl_FrontLightModelProduct gl_BackLightModelProduct " +
|
352 |
+
"gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
|
353 |
+
"gl_FogParameters " +
|
354 |
+
"gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
|
355 |
+
"gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
|
356 |
+
"gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
|
357 |
+
"gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
|
358 |
+
"gl_MaxDrawBuffers"),
|
359 |
+
hooks: {"#": cppHook}
|
360 |
+
});
|
361 |
+
}());
|
vendor/codemirror/mode/php.js
CHANGED
@@ -1,129 +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 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
|
|
|
|
|
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 |
+
})();
|