Version Description
- AJAX Saving of Meta-box
- Dynamically populate the Styles Dropdown for TinyMCE
- Styles preview in Post Editor
- Enqueue dependant scripts if you need (like jQuery)
- Adjustable menu placement
- CodeMirror Themes
Download this release
Release Info
Developer | WraithKenny |
Plugin | Scripts n Styles |
Version | 3 |
Comparing to | |
See all releases |
Code changes from version 2.0.4 to 3
- README.txt +33 -11
- css/meta-box-styles.css +90 -63
- css/options-styles.css +12 -2
- images/icon32.png +0 -0
- images/menu.png +0 -0
- includes/class.SnS_AJAX.php +200 -0
- includes/class.SnS_Admin.php +211 -83
- includes/class.SnS_Admin_Meta_Box.php +288 -110
- includes/class.SnS_Form.php +144 -0
- includes/class.SnS_Global_Page.php +136 -0
- includes/class.SnS_List_Usage.php +176 -0
- includes/class.SnS_Settings_Page.php +97 -232
- includes/class.SnS_Usage_Page.php +87 -0
- js/{options-scripts.js → global-page.js} +3 -2
- js/meta-box-scripts.js +424 -8
- js/settings-page.js +18 -0
- libraries/{codemirror → CodeMirror2}/LICENSE +0 -0
- libraries/{codemirror → CodeMirror2}/lib/codemirror.css +13 -0
- libraries/{codemirror → CodeMirror2}/lib/codemirror.js +1238 -524
- libraries/CodeMirror2/mode/clike/clike.js +247 -0
- libraries/CodeMirror2/mode/clike/index.html +102 -0
- libraries/{codemirror/mode → CodeMirror2/mode/css}/css.js +3 -3
- libraries/CodeMirror2/mode/css/index.html +56 -0
- libraries/{codemirror/mode → CodeMirror2/mode/htmlmixed}/htmlmixed.js +11 -2
- libraries/CodeMirror2/mode/htmlmixed/index.html +52 -0
- libraries/CodeMirror2/mode/javascript/index.html +78 -0
- libraries/{codemirror/mode → CodeMirror2/mode/javascript}/javascript.js +15 -5
- libraries/CodeMirror2/mode/php/index.html +49 -0
- libraries/CodeMirror2/mode/php/php.js +120 -0
- libraries/CodeMirror2/mode/xml/index.html +45 -0
- libraries/{codemirror/mode → CodeMirror2/mode/xml}/xml.js +34 -7
- libraries/CodeMirror2/theme/cobalt.css +17 -0
- libraries/{codemirror → CodeMirror2}/theme/default.css +2 -1
- libraries/CodeMirror2/theme/eclipse.css +24 -0
- libraries/{codemirror → CodeMirror2}/theme/elegant.css +0 -0
- libraries/CodeMirror2/theme/monokai.css +27 -0
- libraries/{codemirror → CodeMirror2}/theme/neat.css +0 -0
- libraries/{codemirror → CodeMirror2}/theme/night.css +3 -3
- libraries/CodeMirror2/theme/rubyblue.css +20 -0
- libraries/codemirror/lib/overlay.js +0 -51
- libraries/codemirror/lib/runmode.js +0 -27
- screenshot-1.png +0 -0
- screenshot-2.png +0 -0
- screenshot-3.png +0 -0
- screenshot-4.png +0 -0
- screenshot-5.png +0 -0
- screenshot-6.png +0 -0
- scripts-n-styles.php +312 -315
- uninstall.php +18 -12
README.txt
CHANGED
@@ -2,23 +2,26 @@
|
|
2 |
Contributors: WraithKenny, Touvan
|
3 |
Donate link: http://wordpressfoundation.org/donate/
|
4 |
Tags: admin, CSS, javascript, code, custom, Style
|
5 |
-
Requires at least: 3.
|
6 |
-
Tested up to: 3.
|
7 |
-
Stable tag:
|
|
|
8 |
|
9 |
This plugin allows Admin users to individually add custom CSS, Classes and JavaScript directly to Post, Pages or any other custom post types.
|
10 |
|
11 |
== Description ==
|
12 |
|
13 |
-
This plugin allows Admin users the ability to add custom CSS
|
|
|
|
|
14 |
|
15 |
Because only well trusted users should ever be allowed to insert JavaScript directly into the pages of your site, this plugin restricts usage to admin type users. Admin's have access to even more sensitive areas by definition, so that should be relatively safe ;)
|
16 |
|
17 |
A few notes about the implementation:
|
18 |
|
19 |
-
* Admin users, or more specifically, *any user with the `manage_options`
|
20 |
-
* CSS Styles are
|
21 |
-
* JavaScript is
|
22 |
* **There is no input validation.** This plugin puts exactly what you type in the meta box directly into the `html` with no error checking. You are an Admin, and we trust you to be carefull. Try not to break anything.
|
23 |
|
24 |
== Installation ==
|
@@ -37,15 +40,31 @@ Well, because plugins are supposed to, and should be expected to clean up after
|
|
37 |
|
38 |
= Can I get around that somehow? =
|
39 |
|
40 |
-
Sure, if you are an Admin, just go to the plugin editor and wipe out the uninstall.php
|
41 |
|
42 |
== Screenshots ==
|
43 |
|
44 |
-
1.
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
== Changelog ==
|
47 |
|
48 |
-
=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
* Better selection of `post_types` to add Scripts-n-Styles
|
50 |
* micro-optimization for storage of class names.
|
51 |
* Adds option page for globally adding Scripts and Styles.
|
@@ -71,6 +90,9 @@ Sure, if you are an Admin, just go to the plugin editor and wipe out the uninsta
|
|
71 |
|
72 |
== Upgrade Notice ==
|
73 |
|
|
|
|
|
|
|
74 |
= 2 =
|
75 |
Adds new features.
|
76 |
|
@@ -84,4 +106,4 @@ Minor update. Adds a few new features.
|
|
84 |
Some small plugin meta data updates.
|
85 |
|
86 |
= 1.0 =
|
87 |
-
Initial Release, there is nothing to upgrade from.
|
2 |
Contributors: WraithKenny, Touvan
|
3 |
Donate link: http://wordpressfoundation.org/donate/
|
4 |
Tags: admin, CSS, javascript, code, custom, Style
|
5 |
+
Requires at least: 3.2
|
6 |
+
Tested up to: 3.3-RC1
|
7 |
+
Stable tag: 3
|
8 |
+
License: GPLv2 or later
|
9 |
|
10 |
This plugin allows Admin users to individually add custom CSS, Classes and JavaScript directly to Post, Pages or any other custom post types.
|
11 |
|
12 |
== Description ==
|
13 |
|
14 |
+
This plugin allows Admin users the ability to add custom CSS and JavaScript directly into individual Post, Pages or any other registered custom post types. You can also add classes to the body tag and the post container. There is a Global settings page for which you can write Scripts n Styles for the entire blog.
|
15 |
+
|
16 |
+
Admin's can also add classes to the TinyMCE "Formats" dropdown which users can use to style posts and pages directly. As of Scripts n Styles 3+ styles are reflected in the post editor.
|
17 |
|
18 |
Because only well trusted users should ever be allowed to insert JavaScript directly into the pages of your site, this plugin restricts usage to admin type users. Admin's have access to even more sensitive areas by definition, so that should be relatively safe ;)
|
19 |
|
20 |
A few notes about the implementation:
|
21 |
|
22 |
+
* Admin users, or more specifically, *any user with the `manage_options` and `unfiltered_html` capabilities* (which by default is *only* the admin type user) can use this plugin's functionality. Some plugins extend user rolls, and so this plugin would naturally extend include rolls that have the appropriate capability.
|
23 |
+
* CSS Styles are embeded, not linked, at the bottom of the `head` element with `style` tags by using `wp-head`. If your theme doesn't have this hook, this plugin (as well as most others) won't work.
|
24 |
+
* JavaScript is embeded, not linked, at the bottom of the `body` (or `head`) element with `script` tags by using `wp-footer` (or `wp-head`). If your theme doesn't have this hook, this plugin (as well as most others) won't work.
|
25 |
* **There is no input validation.** This plugin puts exactly what you type in the meta box directly into the `html` with no error checking. You are an Admin, and we trust you to be carefull. Try not to break anything.
|
26 |
|
27 |
== Installation ==
|
40 |
|
41 |
= Can I get around that somehow? =
|
42 |
|
43 |
+
Sure, if you are an Admin, just go to the plugin editor and wipe out the uninstall.php and then WordPress will not delete the meta data on uninstall.
|
44 |
|
45 |
== Screenshots ==
|
46 |
|
47 |
+
1. Settings Page for Writing Scripts n Styles that apply to the whole blog.
|
48 |
+
2. The Scripts panel of the Meta Box.
|
49 |
+
3. The Styles panel of the Meta Box.
|
50 |
+
4. The Classes panel. Add classes to the Style dropdown!
|
51 |
+
5. Enqueue panel. You can enqueue jQuery from here if you need!
|
52 |
+
6. Your styles are reflected in the Editor.
|
53 |
|
54 |
== Changelog ==
|
55 |
|
56 |
+
= 3 =
|
57 |
+
* AJAX Saving of Meta-box
|
58 |
+
* Dynamically populate the Styles Dropdown for TinyMCE
|
59 |
+
* Styles preview in Post Editor
|
60 |
+
* Enqueue dependant scripts if you need (like jQuery)
|
61 |
+
* Adjustable menu placement
|
62 |
+
* CodeMirror Themes
|
63 |
+
|
64 |
+
= 2.0.3 =
|
65 |
+
* fixed some bugs
|
66 |
+
|
67 |
+
= 2.0.1 =
|
68 |
* Better selection of `post_types` to add Scripts-n-Styles
|
69 |
* micro-optimization for storage of class names.
|
70 |
* Adds option page for globally adding Scripts and Styles.
|
90 |
|
91 |
== Upgrade Notice ==
|
92 |
|
93 |
+
= 3 =
|
94 |
+
Adds new features.
|
95 |
+
|
96 |
= 2 =
|
97 |
Adds new features.
|
98 |
|
106 |
Some small plugin meta data updates.
|
107 |
|
108 |
= 1.0 =
|
109 |
+
Initial Release, there is nothing to upgrade from.
|
css/meta-box-styles.css
CHANGED
@@ -1,101 +1,128 @@
|
|
1 |
/* MetaBox.css */
|
2 |
|
3 |
-
|
|
|
|
|
|
|
|
|
4 |
display: block;
|
|
|
|
|
5 |
margin-top: 1.5em;
|
6 |
}
|
7 |
-
#
|
8 |
-
|
9 |
display: none;
|
10 |
}
|
11 |
-
|
12 |
-
|
13 |
-
#uFp_meta_box .tabs-horizontal,
|
14 |
-
#uFp_meta_box .ui-tabs-panel {
|
15 |
-
overflow: hidden;
|
16 |
}
|
17 |
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
border-color: #DFDFDF;
|
28 |
-
border-style: solid solid none;
|
29 |
-
border-width: 1px 1px 0;
|
30 |
}
|
31 |
-
|
32 |
-
|
33 |
}
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
border-color: #DFDFDF;
|
40 |
}
|
41 |
|
|
|
|
|
|
|
|
|
|
|
42 |
|
43 |
-
|
|
|
|
|
|
|
|
|
44 |
float: left;
|
45 |
margin: 0 -120px 0 5px;
|
46 |
padding: 0;
|
47 |
text-align: right;
|
48 |
width: 120px;
|
49 |
}
|
50 |
-
#
|
51 |
padding: 8px;
|
52 |
-
display:
|
53 |
-
}
|
54 |
-
#uFp_meta_box .tabs-horizontal .wp-tab-bar li a {
|
55 |
-
display: block;
|
56 |
}
|
57 |
-
#
|
58 |
-
|
59 |
-
border-color: #DFDFDF;
|
60 |
border-style: solid none solid solid;
|
61 |
border-width: 1px 0 1px 1px;
|
62 |
margin-right: -1px;
|
63 |
-
|
|
|
64 |
font-weight: bold;
|
65 |
text-decoration: none;
|
66 |
}
|
67 |
-
#
|
68 |
-
|
69 |
-
}
|
70 |
-
#uFp_meta_box .tabs-horizontal .ui-tabs-panel {
|
71 |
-
background-color: #FFFFFF;
|
72 |
-
border-color: #DFDFDF;
|
73 |
border-style: solid;
|
74 |
border-width: 1px;
|
75 |
-
|
76 |
-
|
77 |
-
overflow: hidden;
|
78 |
padding: 0.5em 0.9em;
|
79 |
}
|
80 |
-
|
81 |
-
overflow: hidden;
|
82 |
-
}
|
83 |
|
84 |
-
|
85 |
-
|
86 |
-
|
|
|
|
|
87 |
display: block;
|
88 |
}
|
|
|
|
|
|
|
|
|
89 |
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
border-radius: 3px;
|
94 |
-
margin: 8px 0;
|
95 |
}
|
96 |
-
.
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
}
|
1 |
/* MetaBox.css */
|
2 |
|
3 |
+
|
4 |
+
.wp-tab-bar {
|
5 |
+
display: none;
|
6 |
+
}
|
7 |
+
body.js .wp-tab-bar {
|
8 |
display: block;
|
9 |
+
}
|
10 |
+
#SnS_meta_box .title {
|
11 |
margin-top: 1.5em;
|
12 |
}
|
13 |
+
body.js #SnS_meta_box .title,
|
14 |
+
body.js .wp-tab-panel {
|
15 |
display: none;
|
16 |
}
|
17 |
+
body.js .wp-tabs-panel-active {
|
18 |
+
display: block;
|
|
|
|
|
|
|
19 |
}
|
20 |
|
21 |
+
.CodeMirror {
|
22 |
+
border: 1px solid #DFDFDF;
|
23 |
+
background-color: white;
|
24 |
+
border-radius: 3px;
|
25 |
+
margin: 8px 0;
|
26 |
+
-moz-background-clip: padding;
|
27 |
+
-webkit-background-clip: padding-box;
|
28 |
+
background-clip: padding-box;
|
29 |
+
overflow: hidden;
|
|
|
|
|
|
|
30 |
}
|
31 |
+
.CodeMirror, #editorcontainer #content {
|
32 |
+
font-family: "Courier New", Courier, monospace;
|
33 |
}
|
34 |
+
.CodeMirror-scroll {
|
35 |
+
height: auto;
|
36 |
+
min-height: 50px;
|
37 |
+
max-height: 300px;
|
38 |
+
overflow: auto;
|
|
|
39 |
}
|
40 |
|
41 |
+
/* temp fix border-bottom rounding error problem.
|
42 |
+
#side-sortables .wp-tab-bar {
|
43 |
+
margin-bottom: 0px;
|
44 |
+
min-height: 19px;
|
45 |
+
}*/
|
46 |
|
47 |
+
/* core styles */
|
48 |
+
#post-body .wp-tab-bar a {
|
49 |
+
text-decoration: underline;
|
50 |
+
}
|
51 |
+
#post-body .wp-tab-bar {
|
52 |
float: left;
|
53 |
margin: 0 -120px 0 5px;
|
54 |
padding: 0;
|
55 |
text-align: right;
|
56 |
width: 120px;
|
57 |
}
|
58 |
+
#post-body .wp-tab-bar li {
|
59 |
padding: 8px;
|
60 |
+
display: list-item;
|
|
|
|
|
|
|
61 |
}
|
62 |
+
#post-body .wp-tab-active {
|
63 |
+
border-radius: 3px 0 0 3px;
|
|
|
64 |
border-style: solid none solid solid;
|
65 |
border-width: 1px 0 1px 1px;
|
66 |
margin-right: -1px;
|
67 |
+
}
|
68 |
+
#post-body .wp-tab-active a {
|
69 |
font-weight: bold;
|
70 |
text-decoration: none;
|
71 |
}
|
72 |
+
#post-body .wp-tab-panel {
|
73 |
+
margin: 0 5px 0 125px;
|
|
|
|
|
|
|
|
|
74 |
border-style: solid;
|
75 |
border-width: 1px;
|
76 |
+
height: 200px;
|
77 |
+
overflow: auto;
|
|
|
78 |
padding: 0.5em 0.9em;
|
79 |
}
|
80 |
+
/* end core styles */
|
|
|
|
|
81 |
|
82 |
+
#post-body #SnS_meta_box .wp-tab-bar li {
|
83 |
+
padding: 0px;
|
84 |
+
}
|
85 |
+
#post-body #SnS_meta_box .wp-tab-bar a {
|
86 |
+
padding: 8px;
|
87 |
display: block;
|
88 |
}
|
89 |
+
#SnS_meta_box .wp-tab-panel {
|
90 |
+
height: auto;
|
91 |
+
min-height: 200px;
|
92 |
+
}
|
93 |
|
94 |
+
#add-mce-dropdown-names label {
|
95 |
+
width: 50px;
|
96 |
+
display: inline-block
|
|
|
|
|
97 |
}
|
98 |
+
.sns-ajax-loading {
|
99 |
+
vertical-align: middle;
|
100 |
+
}
|
101 |
+
.sns-ajax-wrap {
|
102 |
+
height: 23px;
|
103 |
+
}
|
104 |
+
#sns-classes {
|
105 |
+
overflow: hidden;
|
106 |
+
width: 100%;
|
107 |
+
}
|
108 |
+
#SnS_classes_mce_wrapper {
|
109 |
+
margin: 6px 0;
|
110 |
+
}
|
111 |
+
#mce-dropdown-names {
|
112 |
+
display: none;
|
113 |
+
}
|
114 |
+
body.js #mce-dropdown-names {
|
115 |
+
display: block;
|
116 |
+
}
|
117 |
+
#delete-mce-dropdown-names .sns-ajax-delete {
|
118 |
+
cursor: pointer;
|
119 |
+
display: inline-block;
|
120 |
+
height: 10px;
|
121 |
+
overflow: hidden;
|
122 |
+
text-indent: -9999px;
|
123 |
+
width: 10px;
|
124 |
+
background: url("/wp-admin/images/xit.gif") no-repeat scroll 0 0 transparent;
|
125 |
+
}
|
126 |
+
#delete-mce-dropdown-names .sns-ajax-delete:hover {
|
127 |
+
background: url("/wp-admin/images/xit.gif") no-repeat scroll -10px 0 transparent;
|
128 |
}
|
css/options-styles.css
CHANGED
@@ -1,14 +1,24 @@
|
|
1 |
/* Options.css */
|
2 |
|
|
|
|
|
|
|
3 |
.CodeMirror {
|
4 |
border: 1px solid #DFDFDF;
|
5 |
background-color: white;
|
6 |
border-radius: 3px;
|
7 |
margin: 8px 0;
|
|
|
|
|
|
|
|
|
8 |
}
|
9 |
-
.CodeMirror-scroll {
|
10 |
height: auto;
|
11 |
min-height: 50px;
|
12 |
max-height: 300px;
|
13 |
overflow: auto;
|
14 |
-
}
|
|
|
|
|
|
1 |
/* Options.css */
|
2 |
|
3 |
+
textarea.code {
|
4 |
+
display: block;
|
5 |
+
}
|
6 |
.CodeMirror {
|
7 |
border: 1px solid #DFDFDF;
|
8 |
background-color: white;
|
9 |
border-radius: 3px;
|
10 |
margin: 8px 0;
|
11 |
+
-moz-background-clip: padding;
|
12 |
+
-webkit-background-clip: padding-box;
|
13 |
+
background-clip: padding-box;
|
14 |
+
overflow: hidden;
|
15 |
}
|
16 |
+
body .CodeMirror-scroll {
|
17 |
height: auto;
|
18 |
min-height: 50px;
|
19 |
max-height: 300px;
|
20 |
overflow: auto;
|
21 |
+
}
|
22 |
+
#icon-sns {
|
23 |
+
background: no-repeat center url('../images/icon32.png');
|
24 |
+
}
|
images/icon32.png
ADDED
Binary file
|
images/menu.png
ADDED
Binary file
|
includes/class.SnS_AJAX.php
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
class SnS_AJAX
|
3 |
+
{
|
4 |
+
function init() {
|
5 |
+
// Keep track of current tab.
|
6 |
+
add_action( 'wp_ajax_sns_update_tab', array( __CLASS__, 'update_tab' ) );
|
7 |
+
// TinyMCE requests a css file.
|
8 |
+
add_action( 'wp_ajax_sns_tinymce_styles', array( __CLASS__, 'tinymce_styles' ) );
|
9 |
+
|
10 |
+
// Ajax Saves.
|
11 |
+
add_action( 'wp_ajax_sns_classes', array( __CLASS__, 'classes' ) );
|
12 |
+
add_action( 'wp_ajax_sns_scripts', array( __CLASS__, 'scripts' ) );
|
13 |
+
add_action( 'wp_ajax_sns_styles', array( __CLASS__, 'styles' ) );
|
14 |
+
add_action( 'wp_ajax_sns_dropdown', array( __CLASS__, 'dropdown' ) );
|
15 |
+
add_action( 'wp_ajax_sns_delete_class', array( __CLASS__, 'delete_class' ) );
|
16 |
+
}
|
17 |
+
function update_tab() {
|
18 |
+
check_ajax_referer( Scripts_n_Styles::$file );
|
19 |
+
|
20 |
+
$active_tab = isset( $_POST[ 'active_tab' ] ) ? 's'.$_POST[ 'active_tab' ] : 's0';
|
21 |
+
|
22 |
+
if ( ! $user = wp_get_current_user() ) exit( 'Bad User' );
|
23 |
+
|
24 |
+
$success = update_user_option( $user->ID, 'current_sns_tab', $active_tab, true);
|
25 |
+
exit();
|
26 |
+
}
|
27 |
+
function tinymce_styles() {
|
28 |
+
check_ajax_referer( 'sns_tinymce_styles' );
|
29 |
+
|
30 |
+
if ( empty( $_REQUEST[ 'post_id' ] ) ) exit( 'Bad post ID.' );
|
31 |
+
$post_id = absint( $_REQUEST[ 'post_id' ] );
|
32 |
+
|
33 |
+
$options = get_option( 'SnS_options' );
|
34 |
+
$SnS = get_post_meta( $post_id, '_SnS', true );
|
35 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
36 |
+
|
37 |
+
header('Content-Type: text/css; charset=' . get_option('blog_charset'));
|
38 |
+
|
39 |
+
if ( ! empty( $options[ 'styles' ] ) ) echo $options[ 'styles' ];
|
40 |
+
|
41 |
+
if ( ! empty( $styles[ 'styles' ] ) ) echo $styles[ 'styles' ];
|
42 |
+
|
43 |
+
exit();
|
44 |
+
}
|
45 |
+
|
46 |
+
// AJAX handlers
|
47 |
+
function classes() {
|
48 |
+
check_ajax_referer( Scripts_n_Styles::$file );
|
49 |
+
if ( ! current_user_can( 'unfiltered_html' ) || ! current_user_can( 'edit_posts' ) ) exit( 'Insufficient Privileges.' );
|
50 |
+
|
51 |
+
if ( empty( $_REQUEST[ 'post_id' ] ) ) exit( 'Bad post ID.' );
|
52 |
+
if ( ! isset( $_REQUEST[ 'classes_body' ], $_REQUEST[ 'classes_post' ] ) ) exit( 'Data missing.' );
|
53 |
+
|
54 |
+
$post_id = absint( $_REQUEST[ 'post_id' ] );
|
55 |
+
$SnS = get_post_meta( $post_id, '_SnS', true );
|
56 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
57 |
+
|
58 |
+
$styles = self::maybe_set( $styles, 'classes_body' );
|
59 |
+
$styles = self::maybe_set( $styles, 'classes_post' );
|
60 |
+
|
61 |
+
if ( ! empty( $styles ) )
|
62 |
+
$SnS[ 'styles' ] = $styles;
|
63 |
+
self::maybe_update( $post_id, '_SnS', $SnS );
|
64 |
+
|
65 |
+
header('Content-Type: application/json; charset=' . get_option('blog_charset'));
|
66 |
+
echo json_encode( array(
|
67 |
+
"classes_post" => $_REQUEST[ 'classes_post' ],
|
68 |
+
"classes_body" => $_REQUEST[ 'classes_body' ]
|
69 |
+
) );
|
70 |
+
|
71 |
+
exit();
|
72 |
+
}
|
73 |
+
function scripts() {
|
74 |
+
check_ajax_referer( Scripts_n_Styles::$file );
|
75 |
+
if ( ! current_user_can( 'unfiltered_html' ) || ! current_user_can( 'edit_posts' ) ) exit( 'Insufficient Privileges.' );
|
76 |
+
|
77 |
+
if ( empty( $_REQUEST[ 'post_id' ] ) ) exit( 'Bad post ID.' );
|
78 |
+
if ( ! isset( $_REQUEST[ 'scripts' ], $_REQUEST[ 'scripts_in_head' ] ) ) exit( 'Data incorrectly sent.' );
|
79 |
+
|
80 |
+
$post_id = absint( $_REQUEST[ 'post_id' ] );
|
81 |
+
$SnS = get_post_meta( $post_id, '_SnS', true );
|
82 |
+
$scripts = isset( $SnS['scripts'] ) ? $SnS[ 'scripts' ]: array();
|
83 |
+
|
84 |
+
$scripts = self::maybe_set( $scripts, 'scripts_in_head' );
|
85 |
+
$scripts = self::maybe_set( $scripts, 'scripts' );
|
86 |
+
|
87 |
+
if ( ! empty( $scripts ) )
|
88 |
+
$SnS[ 'scripts' ] = $scripts;
|
89 |
+
self::maybe_update( $post_id, '_SnS', $SnS );
|
90 |
+
|
91 |
+
header('Content-Type: application/json; charset=' . get_option('blog_charset'));
|
92 |
+
echo json_encode( array(
|
93 |
+
"scripts" => $_REQUEST[ 'scripts' ],
|
94 |
+
"scripts_in_head" => $_REQUEST[ 'scripts_in_head' ],
|
95 |
+
) );
|
96 |
+
|
97 |
+
exit();
|
98 |
+
}
|
99 |
+
function styles() {
|
100 |
+
check_ajax_referer( Scripts_n_Styles::$file );
|
101 |
+
if ( ! current_user_can( 'unfiltered_html' ) || ! current_user_can( 'edit_posts' ) ) exit( 'Insufficient Privileges.' );
|
102 |
+
|
103 |
+
if ( empty( $_REQUEST[ 'post_id' ] ) ) exit( 'Bad post ID.' );
|
104 |
+
if ( ! isset( $_REQUEST[ 'styles' ] ) ) exit( 'Data incorrectly sent.' );
|
105 |
+
|
106 |
+
$post_id = absint( $_REQUEST[ 'post_id' ] );
|
107 |
+
$SnS = get_post_meta( $post_id, '_SnS', true );
|
108 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
109 |
+
|
110 |
+
$styles = self::maybe_set( $styles, 'styles' );
|
111 |
+
|
112 |
+
if ( ! empty( $styles ) )
|
113 |
+
$SnS[ 'styles' ] = $styles;
|
114 |
+
self::maybe_update( $post_id, '_SnS', $SnS );
|
115 |
+
|
116 |
+
header('Content-Type: application/json; charset=' . get_option('blog_charset'));
|
117 |
+
echo json_encode( array(
|
118 |
+
"styles" => $_REQUEST[ 'styles' ],
|
119 |
+
) );
|
120 |
+
|
121 |
+
exit();
|
122 |
+
}
|
123 |
+
function dropdown() {
|
124 |
+
check_ajax_referer( Scripts_n_Styles::$file );
|
125 |
+
if ( ! current_user_can( 'unfiltered_html' ) || ! current_user_can( 'edit_posts' ) ) exit( 'Insufficient Privileges.' );
|
126 |
+
|
127 |
+
if ( empty( $_REQUEST[ 'format' ] ) ) exit( 'Missing Format.' );
|
128 |
+
if ( empty( $_REQUEST[ 'format' ][ 'title' ] ) ) exit( 'Title is required.' );
|
129 |
+
if ( empty( $_REQUEST[ 'format' ][ 'classes' ] ) ) exit( 'Classes is required.' );
|
130 |
+
if (
|
131 |
+
empty( $_REQUEST[ 'format' ][ 'inline' ] ) &&
|
132 |
+
empty( $_REQUEST[ 'format' ][ 'block' ] ) &&
|
133 |
+
empty( $_REQUEST[ 'format' ][ 'selector' ] )
|
134 |
+
) exit( 'A type is required.' );
|
135 |
+
|
136 |
+
if ( empty( $_REQUEST[ 'post_id' ] ) ) exit( 'Bad post ID.' );
|
137 |
+
$post_id = absint( $_REQUEST[ 'post_id' ] );
|
138 |
+
|
139 |
+
$SnS = get_post_meta( $post_id, '_SnS', true );
|
140 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
141 |
+
|
142 |
+
if ( ! isset( $styles[ 'classes_mce' ] ) ) $styles[ 'classes_mce' ] = array();
|
143 |
+
|
144 |
+
// pass title as key to be able to delete.
|
145 |
+
$styles[ 'classes_mce' ][ $_REQUEST[ 'format' ][ 'title' ] ] = $_REQUEST[ 'format' ];
|
146 |
+
|
147 |
+
if ( ! empty( $styles ) )
|
148 |
+
$SnS[ 'styles' ] = $styles;
|
149 |
+
update_post_meta( $post_id, '_SnS', $SnS );
|
150 |
+
|
151 |
+
header('Content-Type: application/json; charset=' . get_option('blog_charset'));
|
152 |
+
echo json_encode( array(
|
153 |
+
"classes_mce" => array_values( $styles[ 'classes_mce' ] )
|
154 |
+
) );
|
155 |
+
|
156 |
+
exit();
|
157 |
+
}
|
158 |
+
function delete_class() {
|
159 |
+
check_ajax_referer( Scripts_n_Styles::$file );
|
160 |
+
if ( ! current_user_can( 'unfiltered_html' ) || ! current_user_can( 'edit_posts' ) ) exit( 'Insufficient Privileges.' );
|
161 |
+
|
162 |
+
if ( empty( $_REQUEST[ 'post_id' ] ) ) exit( 'Bad post ID.' );
|
163 |
+
$post_id = absint( $_REQUEST[ 'post_id' ] );
|
164 |
+
$SnS = get_post_meta( $post_id, '_SnS', true );
|
165 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
166 |
+
|
167 |
+
$title = $_REQUEST[ 'delete' ];
|
168 |
+
|
169 |
+
if ( isset( $styles[ 'classes_mce' ][ $title ] ) ) unset( $styles[ 'classes_mce' ][ $title ] );
|
170 |
+
else exit ( 'No Format of that name.' );
|
171 |
+
|
172 |
+
if ( empty( $styles[ 'classes_mce' ] ) ) unset( $styles[ 'classes_mce' ] );
|
173 |
+
|
174 |
+
if ( ! empty( $styles ) )
|
175 |
+
$SnS[ 'styles' ] = $styles;
|
176 |
+
self::maybe_update( $post_id, '_SnS', $SnS );
|
177 |
+
|
178 |
+
if ( ! isset( $styles[ 'classes_mce' ] ) ) $styles[ 'classes_mce' ] = array( 'Empty' );
|
179 |
+
|
180 |
+
header('Content-Type: application/json; charset=' . get_option('blog_charset'));
|
181 |
+
echo json_encode( array(
|
182 |
+
"classes_mce" => array_values( $styles[ 'classes_mce' ] )
|
183 |
+
) );
|
184 |
+
|
185 |
+
exit();
|
186 |
+
}
|
187 |
+
|
188 |
+
// Differs from SnS_Admin_Meta_Box::maybe_set() in that this needs no prefix.
|
189 |
+
function maybe_set( $o, $i ) {
|
190 |
+
if ( empty( $_REQUEST[ $i ] ) ) {
|
191 |
+
if ( isset( $o[ $i ] ) ) unset( $o[ $i ] );
|
192 |
+
} else $o[ $i ] = $_REQUEST[ $i ];
|
193 |
+
return $o;
|
194 |
+
}
|
195 |
+
function maybe_update( $id, $name, $meta ) {
|
196 |
+
if ( empty( $meta ) ) delete_post_meta( $id, $name );
|
197 |
+
else update_post_meta( $id, $name, $meta );
|
198 |
+
}
|
199 |
+
}
|
200 |
+
?>
|
includes/class.SnS_Admin.php
CHANGED
@@ -1,84 +1,212 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Scripts n Styles Admin Class
|
4 |
-
*
|
5 |
-
* Allows WordPress admin users the ability to add custom CSS
|
6 |
-
* and JavaScript directly to individual Post, Pages or custom
|
7 |
-
* post types.
|
8 |
-
*/
|
9 |
-
|
10 |
-
class
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
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 |
-
|
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 |
?>
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Scripts n Styles Admin Class
|
4 |
+
*
|
5 |
+
* Allows WordPress admin users the ability to add custom CSS
|
6 |
+
* and JavaScript directly to individual Post, Pages or custom
|
7 |
+
* post types.
|
8 |
+
*/
|
9 |
+
|
10 |
+
require_once( 'class.SnS_Admin_Meta_Box.php' );
|
11 |
+
require_once( 'class.SnS_Settings_Page.php' );
|
12 |
+
require_once( 'class.SnS_Usage_Page.php' );
|
13 |
+
require_once( 'class.SnS_Global_Page.php' );
|
14 |
+
require_once( 'class.SnS_AJAX.php' );
|
15 |
+
require_once( 'class.SnS_Form.php' );
|
16 |
+
|
17 |
+
class SnS_Admin
|
18 |
+
{
|
19 |
+
/**#@+
|
20 |
+
* Constants
|
21 |
+
*/
|
22 |
+
const OPTION_GROUP = 'scripts_n_styles';
|
23 |
+
const MENU_SLUG = 'sns';
|
24 |
+
const VERSION = '3';
|
25 |
+
static $parent_slug = '';
|
26 |
+
/**#@-*/
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Initializing method.
|
30 |
+
* @static
|
31 |
+
*/
|
32 |
+
static function init() {
|
33 |
+
add_action( 'admin_menu', array( 'SnS_Admin_Meta_Box', 'init' ) );
|
34 |
+
|
35 |
+
add_action( 'admin_menu', array( __CLASS__, 'menu' ) );
|
36 |
+
|
37 |
+
add_action( 'admin_init', array( 'SnS_AJAX', 'init' ) );
|
38 |
+
|
39 |
+
$plugin_file = plugin_basename( Scripts_n_Styles::$file );
|
40 |
+
add_filter( "plugin_action_links_$plugin_file", array( __CLASS__, 'plugin_action_links') );
|
41 |
+
|
42 |
+
register_activation_hook( Scripts_n_Styles::$file, array( __CLASS__, 'upgrade' ) );
|
43 |
+
}
|
44 |
+
|
45 |
+
function menu() {
|
46 |
+
if ( ! current_user_can( 'manage_options' ) || ! current_user_can( 'unfiltered_html' ) ) return;
|
47 |
+
|
48 |
+
$options = get_option( 'SnS_options' );
|
49 |
+
$menu_spot = isset( $options[ 'menu_position' ] ) ? $options[ 'menu_position' ]: '';
|
50 |
+
$top_spots = array( 'menu', 'object', 'utility' );
|
51 |
+
$sub_spots = array( 'tools.php', 'options-general.php', 'themes.php' );
|
52 |
+
|
53 |
+
if ( in_array( $menu_spot, $top_spots ) ) $parent_slug = SnS_Admin::MENU_SLUG;
|
54 |
+
else if ( in_array( $menu_spot, $sub_spots ) ) $parent_slug = $menu_spot;
|
55 |
+
else $parent_slug = 'tools.php';
|
56 |
+
|
57 |
+
self::$parent_slug = $parent_slug;
|
58 |
+
|
59 |
+
switch( $menu_spot ) {
|
60 |
+
case 'menu':
|
61 |
+
add_menu_page( 'Scripts n Styles', 'Scripts n Styles', 'unfiltered_html', $parent_slug, array( 'SnS_Form', 'page' ), plugins_url( 'images/menu.png', Scripts_n_Styles::$file ) );
|
62 |
+
break;
|
63 |
+
case 'object':
|
64 |
+
add_object_page( 'Scripts n Styles', 'Scripts n Styles', 'unfiltered_html', $parent_slug, array( 'SnS_Form', 'page' ), plugins_url( 'images/menu.png', Scripts_n_Styles::$file ) );
|
65 |
+
break;
|
66 |
+
case 'utility':
|
67 |
+
add_utility_page( 'Scripts n Styles', 'Scripts n Styles', 'unfiltered_html', $parent_slug, array( 'SnS_Form', 'page' ), plugins_url( 'images/menu.png', Scripts_n_Styles::$file ) );
|
68 |
+
break;
|
69 |
+
}
|
70 |
+
SnS_Global_Page::init();
|
71 |
+
SnS_Settings_Page::init();
|
72 |
+
SnS_Usage_Page::init();
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Nav Tabs
|
77 |
+
*/
|
78 |
+
function nav() {
|
79 |
+
$page = $_REQUEST[ 'page' ];
|
80 |
+
?>
|
81 |
+
<?php screen_icon(); ?>
|
82 |
+
<h2>Scripts n Styles</h2>
|
83 |
+
<?php screen_icon( 'none' ); ?>
|
84 |
+
<h3 class="nav-tab-wrapper">
|
85 |
+
<a class="nav-tab<?php echo ( self::MENU_SLUG == $page ) ? ' nav-tab-active': ''; ?>" href="<?php menu_page_url( self::MENU_SLUG ); ?>">Global</a>
|
86 |
+
<a class="nav-tab<?php echo ( self::MENU_SLUG . '_settings' == $page ) ? ' nav-tab-active': ''; ?>" href="<?php menu_page_url( self::MENU_SLUG . '_settings' ); ?>">Settings</a>
|
87 |
+
<a class="nav-tab<?php echo ( self::MENU_SLUG . '_usage' == $page ) ? ' nav-tab-active': ''; ?>" href="<?php menu_page_url( self::MENU_SLUG . '_usage' ); ?>">Usage</a>
|
88 |
+
</h3>
|
89 |
+
<?php
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Settings Page help
|
94 |
+
*/
|
95 |
+
function help() {
|
96 |
+
global $wp_version; // Back Compat for now
|
97 |
+
if ( version_compare( $wp_version, '3.2.1', '>') ) {
|
98 |
+
$screen = get_current_screen();
|
99 |
+
if ( 'post' != $screen->id ) {
|
100 |
+
$screen->add_help_tab( array(
|
101 |
+
'title' => __('Scripts n Styles', 'scripts-n-styles'),
|
102 |
+
'id' => 'scripts-n-styles',
|
103 |
+
'content' =>
|
104 |
+
'<p>' . __( '<p>In default (non MultiSite) WordPress installs, both <em>Administrators</em> and
|
105 |
+
<em>Editors</em> can access <em>Scripts-n-Styles</em> on individual edit screens.
|
106 |
+
Only <em>Administrators</em> can access this Options Page. In MultiSite WordPress installs, only
|
107 |
+
<em>"Super Admin"</em> users can access either
|
108 |
+
<em>Scripts-n-Styles</em> on individual edit screens or this Options Page. If other plugins change
|
109 |
+
capabilities (specifically "unfiltered_html"),
|
110 |
+
other users can be granted access.</p>', 'scripts-n-styles' ) . '</p>'
|
111 |
+
)
|
112 |
+
);
|
113 |
+
$screen->set_help_sidebar(
|
114 |
+
'<p><strong>' . __( 'For more information:', 'scripts-n-styles' ) . '</strong></p>' .
|
115 |
+
'<p>' . __( '<a href="http://wordpress.org/extend/plugins/scripts-n-styles/faq/" target="_blank">Frequently Asked Questions</a>', 'scripts-n-styles' ) . '</p>' .
|
116 |
+
'<p>' . __( '<a href="https://github.com/unFocus/Scripts-n-Styles" target="_blank">Source on github</a>', 'scripts-n-styles' ) . '</p>' .
|
117 |
+
'<p>' . __( '<a href="http://wordpress.org/tags/scripts-n-styles" target="_blank">Support Forums</a>', 'scripts-n-styles' ) . '</p>'
|
118 |
+
);
|
119 |
+
} else {
|
120 |
+
$screen->add_help_tab( array(
|
121 |
+
'title' => __('Scripts n Styles', 'scripts-n-styles'),
|
122 |
+
'id' => 'scripts-n-styles',
|
123 |
+
'content' =>
|
124 |
+
'<p>' . __( '<p>In default (non MultiSite) WordPress installs, both <em>Administrators</em> and
|
125 |
+
<em>Editors</em> can access <em>Scripts-n-Styles</em> on individual edit screens.
|
126 |
+
Only <em>Administrators</em> can access this Options Page. In MultiSite WordPress installs, only
|
127 |
+
<em>"Super Admin"</em> users can access either
|
128 |
+
<em>Scripts-n-Styles</em> on individual edit screens or this Options Page. If other plugins change
|
129 |
+
capabilities (specifically "unfiltered_html"),
|
130 |
+
other users can be granted access.</p>', 'scripts-n-styles' ) . '</p>'
|
131 |
+
)
|
132 |
+
);
|
133 |
+
}
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Utility Method: Sets defaults if not previously set. Sets stored 'version' to VERSION.
|
139 |
+
*/
|
140 |
+
static function upgrade() {
|
141 |
+
$options = get_option( 'SnS_options' );
|
142 |
+
if ( ! $options ) $options = array();
|
143 |
+
$options[ 'version' ] = self::VERSION;
|
144 |
+
update_option( 'SnS_options', $options );
|
145 |
+
|
146 |
+
/*
|
147 |
+
* upgrade proceedure
|
148 |
+
*/
|
149 |
+
$posts = get_posts(
|
150 |
+
array(
|
151 |
+
'numberposts' => -1,
|
152 |
+
'post_type' => 'any',
|
153 |
+
'post_status' => 'any',
|
154 |
+
'meta_query' => array(
|
155 |
+
'relation' => 'OR',
|
156 |
+
array( 'key' => '_SnS_scripts' ),
|
157 |
+
array( 'key' => '_SnS_styles' ),
|
158 |
+
array( 'key' => 'uFp_scripts' ),
|
159 |
+
array( 'key' => 'uFp_styles' )
|
160 |
+
)
|
161 |
+
)
|
162 |
+
);
|
163 |
+
|
164 |
+
foreach( $posts as $post) {
|
165 |
+
$styles = get_post_meta( $post->ID, '_SnS_styles', true );
|
166 |
+
if ( empty( $styles ) )
|
167 |
+
$styles = get_post_meta( $post->ID, 'uFp_styles', true );
|
168 |
+
|
169 |
+
$scripts = get_post_meta( $post->ID, '_SnS_scripts', true );
|
170 |
+
if ( empty( $scripts ) )
|
171 |
+
$scripts = get_post_meta( $post->ID, 'uFp_scripts', true );
|
172 |
+
|
173 |
+
$SnS = array();
|
174 |
+
if ( ! empty( $styles ) )
|
175 |
+
$SnS[ 'styles' ] = $styles;
|
176 |
+
|
177 |
+
if ( ! empty( $scripts ) )
|
178 |
+
$SnS[ 'scripts' ] = $scripts;
|
179 |
+
|
180 |
+
if ( ! empty( $SnS ) )
|
181 |
+
update_post_meta( $post->ID, '_SnS', $SnS );
|
182 |
+
|
183 |
+
delete_post_meta( $post->ID, 'uFp_styles' );
|
184 |
+
delete_post_meta( $post->ID, 'uFp_scripts' );
|
185 |
+
delete_post_meta( $post->ID, '_SnS_styles' );
|
186 |
+
delete_post_meta( $post->ID, '_SnS_scripts' );
|
187 |
+
}
|
188 |
+
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Utility Method: Compares VERSION to stored 'version' value.
|
193 |
+
*/
|
194 |
+
static function upgrade_check() {
|
195 |
+
$options = get_option( 'SnS_options' );
|
196 |
+
if ( ! isset( $options[ 'version' ] ) || version_compare( self::VERSION, $options[ 'version' ], '>' ) )
|
197 |
+
self::upgrade();
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Adds link to the Settings Page in the WordPress "Plugin Action Links" array.
|
202 |
+
* @param array $actions
|
203 |
+
* @return array
|
204 |
+
*/
|
205 |
+
static function plugin_action_links( $actions ) {
|
206 |
+
$actions[ 'settings' ] = '<a href="' . menu_page_url( SnS_Settings_Page::MENU_SLUG, false ) . '"/>Settings</a>';
|
207 |
+
return $actions;
|
208 |
+
}
|
209 |
+
|
210 |
+
}
|
211 |
+
|
212 |
?>
|
includes/class.SnS_Admin_Meta_Box.php
CHANGED
@@ -6,9 +6,6 @@
|
|
6 |
* and JavaScript directly to individual Post, Pages or custom
|
7 |
* post types.
|
8 |
*/
|
9 |
-
|
10 |
-
// $hook_suffix = 'tools_page_Scripts-n-Styles'; // kept here for reference
|
11 |
-
// $plugin_file = 'scripts-n-styles/scripts-n-styles.php'; // kept here for reference
|
12 |
|
13 |
class SnS_Admin_Meta_Box
|
14 |
{
|
@@ -20,14 +17,75 @@ class SnS_Admin_Meta_Box
|
|
20 |
static $post_types;
|
21 |
|
22 |
/**
|
23 |
-
* Initializing method.
|
24 |
-
|
25 |
-
*/
|
26 |
static function init() {
|
27 |
add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_boxes' ) );
|
28 |
add_action( 'save_post', array( __CLASS__, 'save_post' ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
}
|
30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
/**
|
32 |
* Admin Action: 'add_meta_boxes'
|
33 |
* Main Meta Box function. Checks restriction options and display options, calls add_meta_box() and adds actions for adding admin CSS and JavaScript.
|
@@ -36,93 +94,125 @@ class SnS_Admin_Meta_Box
|
|
36 |
if ( current_user_can( 'unfiltered_html' ) ) {
|
37 |
self::$post_types = get_post_types( array('show_ui' => true, 'public' => true) ); // updated for http://core.trac.wordpress.org/changeset/18234
|
38 |
foreach ( self::$post_types as $post_type ) {
|
39 |
-
add_meta_box( '
|
40 |
}
|
41 |
add_filter( 'default_hidden_meta_boxes', array( __CLASS__, 'default_hidden_meta_boxes' ) );
|
42 |
add_action( "admin_print_styles", array( __CLASS__, 'meta_box_styles'));
|
43 |
add_action( "admin_print_scripts", array( __CLASS__, 'meta_box_scripts'));
|
44 |
-
add_filter( 'contextual_help', array(
|
45 |
}
|
46 |
}
|
|
|
47 |
static function default_hidden_meta_boxes( $hidden ) {
|
48 |
-
$hidden[] = '
|
49 |
return $hidden;
|
50 |
}
|
51 |
|
52 |
-
function contextual_help_filter( $text, $screen_id, $screen ) {
|
53 |
-
if ( in_array( $screen->post_type, self::$post_types ) )
|
54 |
-
$text .= '<p>In default (non MultiSite) WordPress installs, both <em>Administrators</em> and
|
55 |
-
<em>Editors</em> can access <em>Scripts-n-Styles</em> on individual edit screens.
|
56 |
-
Only <em>Administrators</em> can access this Options Page. In MultiSite WordPress installs, only <em>"Super Admin"</em> users can access either
|
57 |
-
<em>Scripts-n-Styles</em> on individual edit screens or this Options Page. If other plugins change capabilities (specifically "unfiltered_html"),
|
58 |
-
other users can be granted access.</p>';
|
59 |
-
return $text;
|
60 |
-
}
|
61 |
-
|
62 |
-
|
63 |
/**
|
64 |
* Admin Action: 'add_meta_boxes'
|
65 |
* Outputs the Meta Box. Only called on callback from add_meta_box() during the add_meta_boxes action.
|
66 |
* @param unknown_type WordPress Post object.
|
67 |
*/
|
68 |
-
static function
|
69 |
$registered_handles = Scripts_n_Styles::get_wp_registered();
|
70 |
-
$
|
71 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
?>
|
73 |
-
|
74 |
-
|
75 |
-
<li
|
76 |
-
<li
|
77 |
-
<li
|
78 |
-
<li><a href="#uFp_classes_body-tab">Classes</a></li>
|
79 |
-
<?php /** / ?>
|
80 |
-
<li><a href="#uFp_enqueue_scripts-tab">Include Scripts</a></li>
|
81 |
-
<?php /**/ ?>
|
82 |
</ul>
|
83 |
|
84 |
-
<div id="
|
85 |
-
<
|
86 |
-
<
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
|
|
91 |
</div>
|
92 |
|
93 |
-
<div id="
|
94 |
-
<
|
95 |
-
|
96 |
-
|
97 |
-
<em>This code will be included <strong>verbatim</strong> in <code><style></code> tags in the <code><head></code> tag of your page (or post).</em>
|
98 |
-
</p>
|
99 |
</div>
|
100 |
|
101 |
-
<div id="
|
102 |
-
<p>
|
103 |
-
<label for="uFp_scripts_in_head" class="title"><strong>Scripts</strong> (for the <code>head</code> element): </label>
|
104 |
-
<textarea class="codemirror js" name="uFp_scripts_in_head" id="uFp_scripts_in_head" rows="5" cols="40" style="width: 98%;"><?php echo isset( $scripts[ 'scripts_in_head' ] ) ? $scripts[ 'scripts_in_head' ] : ''; ?></textarea>
|
105 |
-
<em>This code will be included <strong>verbatim</strong> in <code><script></code> tags at the end of your page's (or post's) <code><head></code> tag.</em>
|
106 |
-
</p>
|
107 |
-
</div>
|
108 |
-
|
109 |
-
<div id="uFp_classes_body-tab">
|
110 |
<strong class="title">Classes</strong>
|
111 |
-
<
|
112 |
-
<
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
</div>
|
121 |
|
122 |
-
|
123 |
-
<div id="uFp_enqueue_scripts-tab">
|
124 |
<strong class="title">Include Scripts</strong>
|
125 |
-
<select name="
|
126 |
<?php // This is a bit intense here...
|
127 |
if ( ! empty( $scripts[ 'enqueue_scripts' ] ) && is_array( $scripts[ 'enqueue_scripts' ] ) ) {
|
128 |
foreach ( $registered_handles as $value ) { ?>
|
@@ -140,21 +230,42 @@ class SnS_Admin_Meta_Box
|
|
140 |
</p>
|
141 |
<?php } ?>
|
142 |
<p><em>The chosen scripts will be enqueued and placed before your codes if your code is dependant on certain scripts (like jQuery).</em></p>
|
143 |
-
<p>NOTE: Not all Scripts in the list are appropriate for use in themes. This is merely a generated list of all currently available registered scripts. It's possible some scripts could be registered only on the "front end" and therefore not listed here.</p>
|
144 |
</div>
|
145 |
-
<?php /**/ ?>
|
146 |
-
</div>
|
147 |
<?php
|
148 |
}
|
149 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
/**
|
151 |
* Admin Action: 'admin_print_styles' Action added during 'add_meta_boxes' (which restricts output to Edit Screens).
|
152 |
* Enqueues the CSS for admin styling of the Meta Box.
|
153 |
*/
|
154 |
static function meta_box_styles() {
|
155 |
-
|
156 |
-
|
157 |
-
|
|
|
|
|
|
|
158 |
}
|
159 |
|
160 |
/**
|
@@ -162,10 +273,64 @@ class SnS_Admin_Meta_Box
|
|
162 |
* Enqueues the JavaScript for the admin Meta Box.
|
163 |
*/
|
164 |
static function meta_box_scripts() {
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
wp_enqueue_script(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
}
|
170 |
|
171 |
/**
|
@@ -174,42 +339,55 @@ class SnS_Admin_Meta_Box
|
|
174 |
* @param int $post_id ID value of the WordPress post.
|
175 |
*/
|
176 |
static function save_post( $post_id ) {
|
177 |
-
if ( isset( $_POST[ self::NONCE_NAME ] )
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
NOTE: There is no current_user_can( 'edit_post' ) check here, because as far as I
|
183 |
-
can tell, in /wp-admin/post.php the calls edit_post(), write_post(), post_preview(),
|
184 |
-
wp_untrash_post(), etc., the check is already done prior to the 'save_post' action,
|
185 |
-
which is where this function is called. Other calls are from other pages so the
|
186 |
-
NONCE covers those cases, and that leaves autosave, which is also checked here.
|
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 |
?>
|
6 |
* and JavaScript directly to individual Post, Pages or custom
|
7 |
* post types.
|
8 |
*/
|
|
|
|
|
|
|
9 |
|
10 |
class SnS_Admin_Meta_Box
|
11 |
{
|
17 |
static $post_types;
|
18 |
|
19 |
/**
|
20 |
+
* Initializing method.
|
21 |
+
*/
|
|
|
22 |
static function init() {
|
23 |
add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_boxes' ) );
|
24 |
add_action( 'save_post', array( __CLASS__, 'save_post' ) );
|
25 |
+
|
26 |
+
add_filter( 'mce_buttons_2', array( __CLASS__, 'mce_buttons_2' ) );
|
27 |
+
add_filter( 'tiny_mce_before_init', array( __CLASS__, 'tiny_mce_before_init' ) );
|
28 |
+
add_filter( 'mce_css', array( __CLASS__, 'mce_css' ) );
|
29 |
+
}
|
30 |
+
|
31 |
+
function mce_buttons_2( $buttons ) {
|
32 |
+
global $post;
|
33 |
+
$SnS = get_post_meta( $post->ID, '_SnS', true );
|
34 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
35 |
+
|
36 |
+
if ( ! empty( $styles[ 'classes_mce' ] ) )
|
37 |
+
array_unshift( $buttons, 'styleselect' );
|
38 |
+
|
39 |
+
return $buttons;
|
40 |
+
}
|
41 |
+
function tiny_mce_before_init( $initArray ) {
|
42 |
+
global $post;
|
43 |
+
$SnS = get_post_meta( $post->ID, '_SnS', true );
|
44 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
45 |
+
|
46 |
+
// Add div as a format option, should probably use a string replace thing here.
|
47 |
+
// Better yet, a setting for adding these. Postpone for now.
|
48 |
+
//$initArray['theme_advanced_blockformats'] = "p,address,pre,h1,h2,h3,h4,h5,h6,div";
|
49 |
+
|
50 |
+
if ( ( ! empty( $styles[ 'classes_body' ] ) || ! empty( $styles[ 'classes_post' ] ) ) && ! isset( $initArray['body_class'] ) )
|
51 |
+
$initArray['body_class'] = '';
|
52 |
+
|
53 |
+
// Add body_class (and/or maybe post_class) values... somewhat problematic.
|
54 |
+
if ( ! empty( $styles[ 'classes_body' ] ) )
|
55 |
+
$initArray['body_class'] .= ' ' . $styles[ 'classes_body' ];
|
56 |
+
if ( ! empty( $styles[ 'classes_post' ] ) )
|
57 |
+
$initArray['body_class'] .= ' ' . $styles[ 'classes_post' ];
|
58 |
+
|
59 |
+
// In case Themes or plugins have added style_formats, not tested.
|
60 |
+
if ( isset( $initArray['style_formats'] ) )
|
61 |
+
$style_formats = json_decode( $initArray['style_formats'], true );
|
62 |
+
else
|
63 |
+
$style_formats = array();
|
64 |
+
|
65 |
+
if ( ! empty( $styles[ 'classes_mce' ] ) )
|
66 |
+
foreach ( $styles[ 'classes_mce' ] as $format )
|
67 |
+
$style_formats[] = $format;
|
68 |
+
|
69 |
+
if ( ! empty( $style_formats ) )
|
70 |
+
$initArray['style_formats'] = json_encode( $style_formats );
|
71 |
+
|
72 |
+
return $initArray;
|
73 |
}
|
74 |
|
75 |
+
/**
|
76 |
+
* Admin Action: 'mce_css'
|
77 |
+
* Adds a styles sheet to TinyMCE via ajax that contains the current styles data.
|
78 |
+
*/
|
79 |
+
static function mce_css( $mce_css ) {
|
80 |
+
global $post;
|
81 |
+
$url = admin_url( 'admin-ajax.php' );
|
82 |
+
$url = wp_nonce_url( $url, 'sns_tinymce_styles' );
|
83 |
+
$url = add_query_arg( 'post_id', $post->ID, $url );
|
84 |
+
$url = add_query_arg( 'action', 'sns_tinymce_styles', $url );
|
85 |
+
$mce_css .= ',' . $url;
|
86 |
+
return $mce_css;
|
87 |
+
}
|
88 |
+
|
89 |
/**
|
90 |
* Admin Action: 'add_meta_boxes'
|
91 |
* Main Meta Box function. Checks restriction options and display options, calls add_meta_box() and adds actions for adding admin CSS and JavaScript.
|
94 |
if ( current_user_can( 'unfiltered_html' ) ) {
|
95 |
self::$post_types = get_post_types( array('show_ui' => true, 'public' => true) ); // updated for http://core.trac.wordpress.org/changeset/18234
|
96 |
foreach ( self::$post_types as $post_type ) {
|
97 |
+
add_meta_box( 'SnS_meta_box', 'Scripts n Styles', array( __CLASS__, 'admin_meta_box' ), $post_type, 'normal', 'high' );
|
98 |
}
|
99 |
add_filter( 'default_hidden_meta_boxes', array( __CLASS__, 'default_hidden_meta_boxes' ) );
|
100 |
add_action( "admin_print_styles", array( __CLASS__, 'meta_box_styles'));
|
101 |
add_action( "admin_print_scripts", array( __CLASS__, 'meta_box_scripts'));
|
102 |
+
add_filter( 'contextual_help', array( 'SnS_Admin', 'help' ) );
|
103 |
}
|
104 |
}
|
105 |
+
|
106 |
static function default_hidden_meta_boxes( $hidden ) {
|
107 |
+
$hidden[] = 'SnS_meta_box';
|
108 |
return $hidden;
|
109 |
}
|
110 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
/**
|
112 |
* Admin Action: 'add_meta_boxes'
|
113 |
* Outputs the Meta Box. Only called on callback from add_meta_box() during the add_meta_boxes action.
|
114 |
* @param unknown_type WordPress Post object.
|
115 |
*/
|
116 |
+
static function admin_meta_box( $post ) {
|
117 |
$registered_handles = Scripts_n_Styles::get_wp_registered();
|
118 |
+
$SnS = get_post_meta( $post->ID, '_SnS', true );
|
119 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
120 |
+
$scripts = isset( $SnS['scripts'] ) ? $SnS[ 'scripts' ]: array();
|
121 |
+
|
122 |
+
$screen = get_current_screen();
|
123 |
+
$position = get_user_option( "current_sns_tab" );
|
124 |
+
if ( ! in_array( $position, array( 's0', 's1', 's2', 's3' ) ) ) $position = 's0';
|
125 |
+
wp_nonce_field( Scripts_n_Styles::$file, self::NONCE_NAME );
|
126 |
?>
|
127 |
+
<ul class="wp-tab-bar">
|
128 |
+
<li<?php echo ( 's0' == $position ) ? ' class="wp-tab-active"': ''; ?>><a href="#SnS_scripts-tab">Scripts</a></li>
|
129 |
+
<li<?php echo ( 's1' == $position ) ? ' class="wp-tab-active"': ''; ?>><a href="#SnS_styles-tab">Styles</a></li>
|
130 |
+
<li<?php echo ( 's2' == $position ) ? ' class="wp-tab-active"': ''; ?>><a href="#SnS_classes_body-tab">Classes</a></li>
|
131 |
+
<li<?php echo ( 's3' == $position ) ? ' class="wp-tab-active"': ''; ?>><a href="#SnS_enqueue_scripts-tab">Include Scripts</a></li>
|
|
|
|
|
|
|
|
|
132 |
</ul>
|
133 |
|
134 |
+
<div class="wp-tab-panel" id="SnS_scripts-tab">
|
135 |
+
<p><em>This code will be included <strong>verbatim</strong> in <code><script></code> tags at the end of your page's (or post's) ...</em></p>
|
136 |
+
<label for="SnS_scripts_in_head" class="title"><strong>Scripts</strong> (for the <code>head</code> element): </label>
|
137 |
+
<textarea class="codemirror js" name="SnS_scripts_in_head" id="SnS_scripts_in_head" rows="5" cols="40" style="width: 98%;"><?php echo isset( $scripts[ 'scripts_in_head' ] ) ? $scripts[ 'scripts_in_head' ] : ''; ?></textarea>
|
138 |
+
<p><em>... <code></head></code> tag.</em></p>
|
139 |
+
<label for="SnS_scripts" class="title"><strong>Scripts</strong>: </label>
|
140 |
+
<textarea class="codemirror js" name="SnS_scripts" id="SnS_scripts" rows="5" cols="40" style="width: 98%;"><?php echo isset( $scripts[ 'scripts' ] ) ? $scripts[ 'scripts' ] : ''; ?></textarea>
|
141 |
+
<p><em>... <code></body></code> tag.</em></p>
|
142 |
</div>
|
143 |
|
144 |
+
<div class="wp-tab-panel" id="SnS_styles-tab">
|
145 |
+
<label for="SnS_styles" class="title"><strong>Styles</strong>: </label>
|
146 |
+
<textarea class="codemirror css" name="SnS_styles" id="SnS_styles" rows="5" cols="40" style="width: 98%;"><?php echo isset( $styles[ 'styles' ] ) ? $styles[ 'styles' ] : ''; ?></textarea>
|
147 |
+
<p><em>This code will be included <strong>verbatim</strong> in <code><style></code> tags in the <code><head></code> tag of your page (or post).</em></p>
|
|
|
|
|
148 |
</div>
|
149 |
|
150 |
+
<div class="wp-tab-panel" id="SnS_classes_body-tab">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
<strong class="title">Classes</strong>
|
152 |
+
<div id="sns-classes">
|
153 |
+
<p>
|
154 |
+
<label for="SnS_classes_body"><strong>Body Classes</strong>: </label>
|
155 |
+
<input name="SnS_classes_body" id="SnS_classes_body" type="text" class="code" style="width: 99%;"
|
156 |
+
value="<?php echo isset( $styles[ 'classes_body' ] ) ? $styles[ 'classes_body' ] : ''; ?>" />
|
157 |
+
<small>Standard: <code><?php self::current_classes( 'body', $post->ID ); ?></code></small>
|
158 |
+
</p>
|
159 |
+
<p>
|
160 |
+
<label for="SnS_classes_post"><strong>Post Classes</strong>: </label>
|
161 |
+
<input name="SnS_classes_post" id="SnS_classes_post" type="text" class="code" style="width: 99%;"
|
162 |
+
value="<?php echo isset( $styles[ 'classes_post' ] ) ? $styles[ 'classes_post' ] : ''; ?>" />
|
163 |
+
<small>Standard: <code><?php self::current_classes( 'post', $post->ID ); ?></code></small>
|
164 |
+
</p>
|
165 |
+
<p><em>These <strong>space separated</strong> class names will be added to the <code>body_class()</code> or
|
166 |
+
<code>post_class()</code> function (provided your theme uses these functions).</em></p>
|
167 |
+
</div>
|
168 |
+
|
169 |
+
<?php
|
170 |
+
/*
|
171 |
+
* Note: Styles Dropdown section only makes sense when Javascript is enabled. (Otherwise, no TinyMCE.)
|
172 |
+
*/
|
173 |
+
?>
|
174 |
+
<div id="mce-dropdown-names" style="display: none;">
|
175 |
+
<h4>The Styles Dropdown</h4>
|
176 |
+
<div id="add-mce-dropdown-names">
|
177 |
+
<p>Add (or update) a class for the "Styles" drop-down:</p>
|
178 |
+
<p class="sns-mce-title">
|
179 |
+
<label for="SnS_classes_mce_title">Title:</label>
|
180 |
+
<input name="SnS_classes_mce_title" id="SnS_classes_mce_title"
|
181 |
+
value="" type="text" class="code" style="width: 80px;" />
|
182 |
+
</p>
|
183 |
+
<p class="sns-mce-type">
|
184 |
+
<label for="SnS_classes_mce_type">Type:</label>
|
185 |
+
<select name="SnS_classes_mce_type" id="SnS_classes_mce_type" style="width: 80px;">
|
186 |
+
<option value="inline">Inline</option>
|
187 |
+
<option value="block">Block</option>
|
188 |
+
<option value="selector">Selector</option>
|
189 |
+
</select>
|
190 |
+
</p>
|
191 |
+
<p class="sns-mce-element">
|
192 |
+
<label for="SnS_classes_mce_element">Element:</label>
|
193 |
+
<input name="SnS_classes_mce_element" id="SnS_classes_mce_element"
|
194 |
+
value="" type="text" class="code" style="width: 80px;" />
|
195 |
+
</p>
|
196 |
+
<p class="sns-mce-classes">
|
197 |
+
<label for="SnS_classes_mce_classes">classes:</label>
|
198 |
+
<input name="SnS_classes_mce_classes" id="SnS_classes_mce_classes"
|
199 |
+
value="" type="text" class="code" style="width: 80px;" />
|
200 |
+
</p>
|
201 |
+
<p class="sns-mce-wrapper" style="display: none;">
|
202 |
+
<label for="SnS_classes_mce_wrapper">Wrapper:</label>
|
203 |
+
<input name="SnS_classes_mce_wrapper" id="SnS_classes_mce_wrapper" type="checkbox" value="true" />
|
204 |
+
</p>
|
205 |
+
</div>
|
206 |
+
|
207 |
+
<div id="delete-mce-dropdown-names" style="display: none;">
|
208 |
+
<p id="instructions-mce-dropdown-names">Classes currently in the dropdown:</p>
|
209 |
+
</div>
|
210 |
+
</div>
|
211 |
</div>
|
212 |
|
213 |
+
<div class="wp-tab-panel" id="SnS_enqueue_scripts-tab">
|
|
|
214 |
<strong class="title">Include Scripts</strong>
|
215 |
+
<select name="SnS_enqueue_scripts[]" id="SnS_enqueue_scripts" size="5" multiple="multiple" style="height: auto; float: left; margin: 6px 10px 8px 0;">
|
216 |
<?php // This is a bit intense here...
|
217 |
if ( ! empty( $scripts[ 'enqueue_scripts' ] ) && is_array( $scripts[ 'enqueue_scripts' ] ) ) {
|
218 |
foreach ( $registered_handles as $value ) { ?>
|
230 |
</p>
|
231 |
<?php } ?>
|
232 |
<p><em>The chosen scripts will be enqueued and placed before your codes if your code is dependant on certain scripts (like jQuery).</em></p>
|
|
|
233 |
</div>
|
|
|
|
|
234 |
<?php
|
235 |
}
|
236 |
|
237 |
+
function current_classes( $type, $post_id ) {
|
238 |
+
if ( 'body' == $type ) {
|
239 |
+
global $wp_query, $pagenow;
|
240 |
+
|
241 |
+
if ( 'post-new.php' == $pagenow ) {
|
242 |
+
echo join( ' ', get_body_class( '', $post_id ) );
|
243 |
+
echo ' (plus others once saved.)';
|
244 |
+
return;
|
245 |
+
}
|
246 |
+
// This returns more of what actually get used on the theme side.
|
247 |
+
$save = $wp_query;
|
248 |
+
$param = ( 'page' == get_post_type( $post_id ) ) ? 'page_id': 'p';
|
249 |
+
$wp_query = new WP_Query( "$param=$post_id" );
|
250 |
+
echo join( ' ', get_body_class( '', $post_id ) );
|
251 |
+
$wp_query = $save;
|
252 |
+
|
253 |
+
} else {
|
254 |
+
echo join( ' ', get_post_class( '', $post_id ) );
|
255 |
+
}
|
256 |
+
}
|
257 |
+
|
258 |
/**
|
259 |
* Admin Action: 'admin_print_styles' Action added during 'add_meta_boxes' (which restricts output to Edit Screens).
|
260 |
* Enqueues the CSS for admin styling of the Meta Box.
|
261 |
*/
|
262 |
static function meta_box_styles() {
|
263 |
+
$options = get_option( 'SnS_options' );
|
264 |
+
$cm_theme = isset( $options[ 'cm_theme' ] ) ? $options[ 'cm_theme' ] : 'default';
|
265 |
+
|
266 |
+
wp_enqueue_style( 'codemirror', plugins_url( 'libraries/CodeMirror2/lib/codemirror.css', Scripts_n_Styles::$file), array(), '2.18' );
|
267 |
+
wp_enqueue_style( "codemirror-$cm_theme", plugins_url( "libraries/CodeMirror2/theme/$cm_theme.css", Scripts_n_Styles::$file), array( 'codemirror' ), '2.18' );
|
268 |
+
wp_enqueue_style( 'sns-meta-box-styles', plugins_url( 'css/meta-box-styles.css', Scripts_n_Styles::$file), array( 'codemirror' ), SnS_Admin::VERSION );
|
269 |
}
|
270 |
|
271 |
/**
|
273 |
* Enqueues the JavaScript for the admin Meta Box.
|
274 |
*/
|
275 |
static function meta_box_scripts() {
|
276 |
+
$options = get_option( 'SnS_options' );
|
277 |
+
$cm_theme = isset( $options[ 'cm_theme' ] ) ? $options[ 'cm_theme' ] : 'default';
|
278 |
+
|
279 |
+
wp_enqueue_script(
|
280 |
+
'codemirror',
|
281 |
+
plugins_url( 'libraries/CodeMirror2/lib/codemirror.js', Scripts_n_Styles::$file),
|
282 |
+
array(),
|
283 |
+
'2.18' );
|
284 |
+
wp_enqueue_script(
|
285 |
+
'codemirror-css',
|
286 |
+
plugins_url( 'libraries/CodeMirror2/mode/css/css.js', Scripts_n_Styles::$file),
|
287 |
+
array( 'codemirror' ),
|
288 |
+
'2.18' );
|
289 |
+
wp_enqueue_script(
|
290 |
+
'codemirror-javascript',
|
291 |
+
plugins_url( 'libraries/CodeMirror2/mode/javascript/javascript.js', Scripts_n_Styles::$file),
|
292 |
+
array( 'codemirror' ),
|
293 |
+
'2.18' );
|
294 |
+
/*wp_register_script(
|
295 |
+
'codemirror-xml',
|
296 |
+
plugins_url( 'libraries/CodeMirror2/mode/xml/xml.js', Scripts_n_Styles::$file),
|
297 |
+
array( 'codemirror' ),
|
298 |
+
'2.11' );*/
|
299 |
+
/*wp_register_script(
|
300 |
+
'codemirror-htmlmixed',
|
301 |
+
plugins_url( 'libraries/CodeMirror2/mode/htmlmixed/htmlmixed.js', Scripts_n_Styles::$file),
|
302 |
+
array( 'codemirror-xml',
|
303 |
+
'codemirror-css',
|
304 |
+
'codemirror-javascript'
|
305 |
+
),
|
306 |
+
'2.11' );*/
|
307 |
+
/*wp_register_script(
|
308 |
+
'codemirror-clike',
|
309 |
+
plugins_url( 'libraries/CodeMirror2/mode/clike/clike.js', Scripts_n_Styles::$file),
|
310 |
+
array( 'codemirror' ),
|
311 |
+
'2.11' );
|
312 |
+
wp_register_script(
|
313 |
+
'codemirror-php',
|
314 |
+
plugins_url( 'libraries/CodeMirror2/mode/php/php.js', Scripts_n_Styles::$file),
|
315 |
+
array( 'codemirror-xml',
|
316 |
+
'codemirror-css',
|
317 |
+
'codemirror-javascript',
|
318 |
+
'codemirror-clike'
|
319 |
+
),
|
320 |
+
'2.11' );*/
|
321 |
+
wp_enqueue_script(
|
322 |
+
'sns-meta-box-scripts',
|
323 |
+
plugins_url( 'js/meta-box-scripts.js', Scripts_n_Styles::$file),
|
324 |
+
array( 'editor',
|
325 |
+
'jquery-ui-tabs',
|
326 |
+
'codemirror-javascript',
|
327 |
+
'codemirror-css'//,
|
328 |
+
//'codemirror-htmlmixed',
|
329 |
+
//'codemirror-php'
|
330 |
+
),
|
331 |
+
SnS_Admin::VERSION, true );
|
332 |
+
|
333 |
+
wp_localize_script( 'sns-meta-box-scripts', 'codemirror_options', array( 'theme' => $cm_theme ) );
|
334 |
}
|
335 |
|
336 |
/**
|
339 |
* @param int $post_id ID value of the WordPress post.
|
340 |
*/
|
341 |
static function save_post( $post_id ) {
|
342 |
+
if ( ! isset( $_POST[ self::NONCE_NAME ] ) || ! wp_verify_nonce( $_POST[ self::NONCE_NAME ], Scripts_n_Styles::$file )
|
343 |
+
|| ! current_user_can( 'unfiltered_html' )
|
344 |
+
|| wp_is_post_revision( $post_id ) // is needed for get_post_meta compatibility.
|
345 |
+
|| ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
|
346 |
+
) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
347 |
|
348 |
+
/*
|
349 |
+
NOTE: There is no current_user_can( 'edit_post' ) check here, because as far as I
|
350 |
+
can tell, in /wp-admin/post.php the calls edit_post(), write_post(), post_preview(),
|
351 |
+
wp_untrash_post(), etc., the check is already done prior to the 'save_post' action,
|
352 |
+
which is where this function is called. Other calls are from other pages so the
|
353 |
+
NONCE covers those cases, and that leaves autosave, which is also checked here.
|
354 |
+
*/
|
355 |
+
|
356 |
+
$SnS = get_post_meta( $post_id, '_SnS', true );
|
357 |
+
$scripts = isset( $SnS['scripts'] ) ? $SnS[ 'scripts' ]: array();
|
358 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
359 |
+
|
360 |
+
$scripts = self::maybe_set( $scripts, 'scripts_in_head' );
|
361 |
+
$scripts = self::maybe_set( $scripts, 'scripts' );
|
362 |
+
$scripts = self::maybe_set( $scripts, 'enqueue_scripts' );
|
363 |
+
$styles = self::maybe_set( $styles, 'styles' );
|
364 |
+
$styles = self::maybe_set( $styles, 'classes_body' );
|
365 |
+
$styles = self::maybe_set( $styles, 'classes_post' );
|
366 |
+
|
367 |
+
// This one isn't posted, it's ajax only. Cleanup anyway.
|
368 |
+
if ( isset( $styles[ 'classes_mce' ] ) && empty( $styles[ 'classes_mce' ] ) )
|
369 |
+
unset( $styles[ 'classes_mce' ] );
|
370 |
+
|
371 |
+
$SnS['scripts'] = $scripts;
|
372 |
+
$SnS['styles'] = $styles;
|
373 |
+
|
374 |
+
if ( empty( $SnS ) )
|
375 |
+
delete_post_meta( $post_id, '_SnS' );
|
376 |
+
else
|
377 |
+
update_post_meta( $post_id, '_SnS', $SnS );
|
378 |
+
}
|
379 |
+
|
380 |
+
/**
|
381 |
+
* maybe_set()
|
382 |
+
* Filters $o and Checks if the sent data $i is empty (intended to clear). If not, updates.
|
383 |
+
*/
|
384 |
+
function maybe_set( $o, $i, $p = 'SnS_' ) {
|
385 |
+
if ( empty( $_REQUEST[ $p . $i ] ) ) {
|
386 |
+
if ( isset( $o[ $i ] ) ) unset( $o[ $i ] );
|
387 |
+
} else {
|
388 |
+
$o[ $i ] = $_REQUEST[ $p . $i ];
|
389 |
}
|
390 |
+
return $o;
|
391 |
}
|
392 |
}
|
393 |
?>
|
includes/class.SnS_Form.php
ADDED
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* SnS_Global_Page
|
4 |
+
*
|
5 |
+
* Allows WordPress admin users the ability to add custom CSS
|
6 |
+
* and JavaScript directly to individual Post, Pages or custom
|
7 |
+
* post types.
|
8 |
+
*/
|
9 |
+
|
10 |
+
class SnS_Form
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Settings Page
|
14 |
+
* Outputs a textarea for setting 'scripts_in_head'.
|
15 |
+
*/
|
16 |
+
function textarea( $args ) {
|
17 |
+
extract( $args );
|
18 |
+
$options = get_option( $setting );
|
19 |
+
$value = isset( $options[ $label_for ] ) ? $options[ $label_for ] : '';
|
20 |
+
$output = '<textarea';
|
21 |
+
$output .= ( $style ) ? ' style="' . $style . '"': '';
|
22 |
+
$output .= ( $class ) ? ' class="' . $class . '"': '';
|
23 |
+
$output .= ( $rows ) ? ' rows="' . $rows . '"': '';
|
24 |
+
$output .= ( $cols ) ? ' cols="' . $cols . '"': '';
|
25 |
+
$output .= ' name="' . $setting . '[' . $label_for . ']"';
|
26 |
+
$output .= ' id="' . $label_for . '">';
|
27 |
+
$output .= $value . '</textarea>';
|
28 |
+
if ( $description ) {
|
29 |
+
$output .= $description;
|
30 |
+
}
|
31 |
+
echo $output;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Settings Page
|
36 |
+
* Outputs a select element for selecting options to set scripts for including.
|
37 |
+
*/
|
38 |
+
function select( $args ) {
|
39 |
+
extract( $args );
|
40 |
+
$options = get_option( $setting );
|
41 |
+
$selected = isset( $options[ $label_for ] ) ? $options[ $label_for ] : array();
|
42 |
+
|
43 |
+
$output = '<select';
|
44 |
+
$output .= ' id="' . $label_for . '"';
|
45 |
+
$output .= ' name="' . $setting . '[' . $label_for . ']';
|
46 |
+
if ( isset( $multiple ) && $multiple )
|
47 |
+
$output .= '[]" multiple="multiple"';
|
48 |
+
else
|
49 |
+
$output .= '"';
|
50 |
+
$output .= ( $size ) ? ' size="' . $size . '"': '';
|
51 |
+
$output .= ( $style ) ? ' style="' . $style . '"': '';
|
52 |
+
$output .= '>';
|
53 |
+
foreach ( $choices as $choice ) {
|
54 |
+
$output .= '<option value="' . $choice . '"';
|
55 |
+
if ( isset( $multiple ) && $multiple )
|
56 |
+
foreach ( $selected as $handle ) $output .= selected( $handle, $choice, false );
|
57 |
+
else
|
58 |
+
$output .= selected( $selected, $choice, false );
|
59 |
+
$output .= '>' . $choice . '</option> ';
|
60 |
+
}
|
61 |
+
$output .= '</select>';
|
62 |
+
if ( ! empty( $show_current ) && ! empty( $selected ) ) {
|
63 |
+
$output .= '<p>' . $show_current;
|
64 |
+
foreach ( $selected as $handle ) $output .= '<code>' . $handle . '</code> ';
|
65 |
+
$output .= '</p>';
|
66 |
+
}
|
67 |
+
echo $output;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Settings Page
|
72 |
+
* Outputs the Admin Page and calls the Settings registered with the Settings API.
|
73 |
+
*/
|
74 |
+
function take_action() {
|
75 |
+
global $action, $option_page, $page, $new_whitelist_options;
|
76 |
+
|
77 |
+
if ( ! current_user_can( 'manage_options' ) || ! current_user_can( 'unfiltered_html' ) || ( is_multisite() && ! is_super_admin() ) )
|
78 |
+
wp_die( __( 'Cheatin’ uh?' ) );
|
79 |
+
|
80 |
+
if ( isset( $_REQUEST[ 'message' ] ) && $_REQUEST[ 'message' ] )
|
81 |
+
add_settings_error( $page, 'settings_updated', __( 'Settings saved.' ), 'updated' );
|
82 |
+
|
83 |
+
if ( ! isset( $_REQUEST[ 'action' ], $_REQUEST[ 'option_page' ], $_REQUEST[ 'page' ] ) )
|
84 |
+
return;
|
85 |
+
|
86 |
+
wp_reset_vars( array( 'action', 'option_page', 'page' ) );
|
87 |
+
|
88 |
+
check_admin_referer( $option_page . '-options' );
|
89 |
+
|
90 |
+
if ( ! isset( $new_whitelist_options[ $option_page ] ) )
|
91 |
+
return;
|
92 |
+
|
93 |
+
$options = $new_whitelist_options[ $option_page ];
|
94 |
+
foreach ( (array) $options as $option ) {
|
95 |
+
$old = get_option( $option );
|
96 |
+
$option = trim( $option );
|
97 |
+
$value = null;
|
98 |
+
if ( isset($_POST[ $option ]) )
|
99 |
+
$value = $_POST[ $option ];
|
100 |
+
if ( !is_array( $value ) )
|
101 |
+
$value = trim( $value );
|
102 |
+
|
103 |
+
$value = array_merge( $old, stripslashes_deep( $value ) );
|
104 |
+
update_option( $option, $value );
|
105 |
+
}
|
106 |
+
|
107 |
+
if ( ! count( get_settings_errors() ) )
|
108 |
+
add_settings_error( $page, 'settings_updated', __( 'Settings saved.' ), 'updated' );
|
109 |
+
|
110 |
+
if ( isset( $_POST[ $option ][ 'menu_position' ] ) && ( $value[ 'menu_position' ] != SnS_Admin::$parent_slug ) ) {
|
111 |
+
switch( $value[ 'menu_position' ] ) {
|
112 |
+
case 'menu':
|
113 |
+
case 'object':
|
114 |
+
case 'utility':
|
115 |
+
wp_redirect( add_query_arg( 'message', 1, admin_url( 'admin.php?page=sns_settings' ) ) );
|
116 |
+
break;
|
117 |
+
default:
|
118 |
+
wp_redirect( add_query_arg( 'message', 1, admin_url( $value[ 'menu_position' ].'?page=sns_settings' ) ) );
|
119 |
+
break;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
return;
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Settings Page
|
127 |
+
* Outputs the Admin Page and calls the Settings registered with the Settings API in init_options_page().
|
128 |
+
*/
|
129 |
+
function page() {
|
130 |
+
SnS_Admin::upgrade_check();
|
131 |
+
?>
|
132 |
+
<div class="wrap">
|
133 |
+
<?php SnS_Admin::nav(); ?>
|
134 |
+
<?php settings_errors(); ?>
|
135 |
+
<form action="" method="post" autocomplete="off">
|
136 |
+
<?php settings_fields( SnS_Admin::OPTION_GROUP ); ?>
|
137 |
+
<?php do_settings_sections( SnS_Admin::MENU_SLUG ); ?>
|
138 |
+
<?php submit_button(); ?>
|
139 |
+
</form>
|
140 |
+
</div>
|
141 |
+
<?php
|
142 |
+
}
|
143 |
+
}
|
144 |
+
?>
|
includes/class.SnS_Global_Page.php
ADDED
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* SnS_Global_Page
|
4 |
+
*
|
5 |
+
* Allows WordPress admin users the ability to add custom CSS
|
6 |
+
* and JavaScript directly to individual Post, Pages or custom
|
7 |
+
* post types.
|
8 |
+
*/
|
9 |
+
|
10 |
+
class SnS_Global_Page
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Constants
|
14 |
+
*/
|
15 |
+
const OPTION_GROUP = 'scripts_n_styles';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Initializing method.
|
19 |
+
* @static
|
20 |
+
*/
|
21 |
+
function init() {
|
22 |
+
if ( SnS_Admin::$parent_slug == SnS_Admin::MENU_SLUG ) $menu_title = 'Global';
|
23 |
+
else $menu_title = 'Scripts n Styles';
|
24 |
+
|
25 |
+
$hook_suffix = add_submenu_page( SnS_Admin::$parent_slug, 'Scripts n Styles', $menu_title, 'unfiltered_html', SnS_Admin::MENU_SLUG, array( 'SnS_Form', 'page' ) );
|
26 |
+
|
27 |
+
add_action( "load-$hook_suffix", array( __CLASS__, 'admin_load' ) );
|
28 |
+
add_action( "load-$hook_suffix", array( 'SnS_Admin', 'help' ) );
|
29 |
+
add_action( "load-$hook_suffix", array( 'SnS_Form', 'take_action'), 49 );
|
30 |
+
add_action( "admin_print_styles-$hook_suffix", array( __CLASS__, 'admin_enqueue_scripts' ) );
|
31 |
+
}
|
32 |
+
|
33 |
+
function admin_enqueue_scripts() {
|
34 |
+
$options = get_option( 'SnS_options' );
|
35 |
+
$cm_theme = isset( $options[ 'cm_theme' ] ) ? $options[ 'cm_theme' ] : 'default';
|
36 |
+
wp_enqueue_style( 'sns-options-styles', plugins_url('css/options-styles.css', Scripts_n_Styles::$file), array( 'codemirror' ), SnS_Admin::VERSION );
|
37 |
+
wp_enqueue_style( 'codemirror', plugins_url( 'libraries/CodeMirror2/lib/codemirror.css', Scripts_n_Styles::$file), array(), '2.18' );
|
38 |
+
wp_enqueue_style( "codemirror-$cm_theme", plugins_url( "libraries/CodeMirror2/theme/$cm_theme.css", Scripts_n_Styles::$file), array( 'codemirror' ), '2.18' );
|
39 |
+
|
40 |
+
wp_enqueue_script( 'sns-global-page-scripts', plugins_url('js/global-page.js', Scripts_n_Styles::$file), array( 'jquery', 'codemirror-css', 'codemirror-javascript' ), SnS_Admin::VERSION, true );
|
41 |
+
wp_localize_script( 'sns-global-page-scripts', 'codemirror_options', array( 'theme' => $cm_theme ) );
|
42 |
+
wp_enqueue_script( 'codemirror', plugins_url( 'libraries/CodeMirror2/lib/codemirror.js', Scripts_n_Styles::$file), array(), '2.18' );
|
43 |
+
wp_enqueue_script( 'codemirror-css', plugins_url( 'libraries/CodeMirror2/mode/css/css.js', Scripts_n_Styles::$file), array( 'codemirror' ), '2.18' );
|
44 |
+
wp_enqueue_script( 'codemirror-javascript', plugins_url( 'libraries/CodeMirror2/mode/javascript/javascript.js', Scripts_n_Styles::$file), array( 'codemirror' ), '2.18' );
|
45 |
+
}
|
46 |
+
/**
|
47 |
+
* Settings Page
|
48 |
+
* Adds Admin Menu Item via WordPress' "Administration Menus" API. Also hook actions to register options via WordPress' Settings API.
|
49 |
+
*/
|
50 |
+
function admin_load() {
|
51 |
+
|
52 |
+
register_setting(
|
53 |
+
self::OPTION_GROUP,
|
54 |
+
'SnS_options' );
|
55 |
+
|
56 |
+
add_settings_section(
|
57 |
+
'global',
|
58 |
+
'Global Scripts n Styles',
|
59 |
+
array( __CLASS__, 'global_section' ),
|
60 |
+
SnS_Admin::MENU_SLUG );
|
61 |
+
|
62 |
+
add_settings_field(
|
63 |
+
'scripts',
|
64 |
+
'<strong>Scripts:</strong> ',
|
65 |
+
array( 'SnS_Form', 'textarea' ),
|
66 |
+
SnS_Admin::MENU_SLUG,
|
67 |
+
'global',
|
68 |
+
array(
|
69 |
+
'label_for' => 'scripts',
|
70 |
+
'setting' => 'SnS_options',
|
71 |
+
'class' => 'code js',
|
72 |
+
'rows' => 5,
|
73 |
+
'cols' => 40,
|
74 |
+
'style' => 'min-width: 500px; width:97%;',
|
75 |
+
'description' => '<span class="description" style="max-width: 500px; display: inline-block;">The "Scripts" will be included <strong>verbatim</strong> in <code><script></code> tags at the bottom of the <code><body></code> element of your html.</span>'
|
76 |
+
) );
|
77 |
+
add_settings_field(
|
78 |
+
'styles',
|
79 |
+
'<strong>Styles:</strong> ',
|
80 |
+
array( 'SnS_Form', 'textarea' ),
|
81 |
+
SnS_Admin::MENU_SLUG,
|
82 |
+
'global',
|
83 |
+
array(
|
84 |
+
'label_for' => 'styles',
|
85 |
+
'setting' => 'SnS_options',
|
86 |
+
'class' => 'code css',
|
87 |
+
'rows' => 5,
|
88 |
+
'cols' => 40,
|
89 |
+
'style' => 'min-width: 500px; width:97%;',
|
90 |
+
'description' => '<span class="description" style="max-width: 500px; display: inline-block;">The "Styles" will be included <strong>verbatim</strong> in <code><style></code> tags in the <code><head></code> element of your html.</span>'
|
91 |
+
) );
|
92 |
+
add_settings_field(
|
93 |
+
'scripts_in_head',
|
94 |
+
'<strong>Scripts</strong><br />(for the <code>head</code> element): ',
|
95 |
+
array( 'SnS_Form', 'textarea' ),
|
96 |
+
SnS_Admin::MENU_SLUG,
|
97 |
+
'global',
|
98 |
+
array(
|
99 |
+
'label_for' => 'scripts_in_head',
|
100 |
+
'setting' => 'SnS_options',
|
101 |
+
'class' => 'code js',
|
102 |
+
'rows' => 5,
|
103 |
+
'cols' => 40,
|
104 |
+
'style' => 'min-width: 500px; width:97%;',
|
105 |
+
'description' => '<span class="description" style="max-width: 500px; display: inline-block;">The "Scripts (in head)" will be included <strong>verbatim</strong> in <code><script></code> tags in the <code><head></code> element of your html.</span>'
|
106 |
+
) );
|
107 |
+
add_settings_field(
|
108 |
+
'enqueue_scripts',
|
109 |
+
'<strong>Enqueue Scripts</strong>: ',
|
110 |
+
array( 'SnS_Form', 'select' ),
|
111 |
+
SnS_Admin::MENU_SLUG,
|
112 |
+
'global',
|
113 |
+
array(
|
114 |
+
'label_for' => 'enqueue_scripts',
|
115 |
+
'setting' => 'SnS_options',
|
116 |
+
'choices' => Scripts_n_Styles::get_wp_registered(),
|
117 |
+
'size' => 5,
|
118 |
+
'style' => 'height: auto;',
|
119 |
+
'multiple' => true,
|
120 |
+
'show_current' => 'Currently Enqueued Scripts: '
|
121 |
+
) );
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Settings Page
|
126 |
+
* Outputs Description text for the Global Section.
|
127 |
+
*/
|
128 |
+
function global_section() {
|
129 |
+
?>
|
130 |
+
<div style="max-width: 55em;">
|
131 |
+
<p>Code entered here will be included in <em>every page (and post) of your site</em>, including the homepage and archives. The code will appear <strong>before</strong> Scripts and Styles registered individually.</p>
|
132 |
+
</div>
|
133 |
+
<?php
|
134 |
+
}
|
135 |
+
}
|
136 |
+
?>
|
includes/class.SnS_List_Usage.php
ADDED
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! class_exists( 'WP_List_Table' ) ) require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
|
3 |
+
|
4 |
+
class SnS_List_Usage extends WP_List_Table {
|
5 |
+
|
6 |
+
function ajax_user_can() {
|
7 |
+
return current_user_can( 'unfiltered_html' ) && current_user_can( 'manage_options' );
|
8 |
+
}
|
9 |
+
|
10 |
+
function column_default( $post, $column_name ) {
|
11 |
+
$return = '';
|
12 |
+
switch( $column_name ){
|
13 |
+
case 'status':
|
14 |
+
return $post->post_status;
|
15 |
+
case 'ID':
|
16 |
+
case 'post_type':
|
17 |
+
return $post->$column_name;
|
18 |
+
case 'script_data':
|
19 |
+
if ( isset( $post->sns_scripts[ 'scripts_in_head' ] ) ) {
|
20 |
+
$return .= '<div>Scripts (head)</div>';
|
21 |
+
}
|
22 |
+
if ( isset( $post->sns_scripts[ 'scripts' ] ) ) {
|
23 |
+
$return .= '<div>Scripts</div>';
|
24 |
+
}
|
25 |
+
if ( isset( $post->sns_scripts[ 'enqueue_scripts' ] ) ) {
|
26 |
+
$return .= '<div>Enqueued Scripts</div>';
|
27 |
+
}
|
28 |
+
return $return;
|
29 |
+
case 'style_data':
|
30 |
+
if ( isset( $post->sns_styles[ 'classes_mce' ] ) ) {
|
31 |
+
$return .= '<div>TinyMCE Formats</div>';
|
32 |
+
}
|
33 |
+
if ( isset( $post->sns_styles[ 'styles' ] ) ) {
|
34 |
+
$return .= '<div>Styles</div>';
|
35 |
+
}
|
36 |
+
if ( isset( $post->sns_styles[ 'classes_post' ] ) ) {
|
37 |
+
$return .= '<div>Post Classes</div>';
|
38 |
+
}
|
39 |
+
if ( isset( $post->sns_styles[ 'classes_body' ] ) ) {
|
40 |
+
$return .= '<div>Body Classes</div>';
|
41 |
+
}
|
42 |
+
return $return;
|
43 |
+
default:
|
44 |
+
return print_r( $post, true );
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
function column_title( $post ) {
|
49 |
+
$edit_link = esc_url( get_edit_post_link( $post->ID ) );
|
50 |
+
$edit_title = esc_attr( sprintf( __( 'Edit “%s”' ), $post->post_title ) );
|
51 |
+
|
52 |
+
$actions = array(
|
53 |
+
'edit' => sprintf('<a title="%s" href="%s">Edit</a>', $edit_title, $edit_link ),
|
54 |
+
);
|
55 |
+
|
56 |
+
$return = '<strong>';
|
57 |
+
if ( $this->ajax_user_can() && $post->post_status != 'trash' ) {
|
58 |
+
$return .= '<a class="row-title"';
|
59 |
+
$return .= ' href="'. $edit_link .'"';
|
60 |
+
$return .= ' title="'. $edit_title .'">';
|
61 |
+
$return .= $post->post_title;
|
62 |
+
$return .= '</a>';
|
63 |
+
} else {
|
64 |
+
$return .= $post->post_title;
|
65 |
+
}
|
66 |
+
$this->_post_states( $post );
|
67 |
+
$return .= '</strong>';
|
68 |
+
$return .= $this->row_actions( $actions );
|
69 |
+
|
70 |
+
return $return;
|
71 |
+
}
|
72 |
+
|
73 |
+
function get_columns() {
|
74 |
+
$columns = array(
|
75 |
+
'title' => 'Title',
|
76 |
+
'ID' => 'ID',
|
77 |
+
'status' => 'Status',
|
78 |
+
'post_type' => 'Post Type',
|
79 |
+
'script_data' => 'Script Data',
|
80 |
+
'style_data' => 'Style Data'
|
81 |
+
);
|
82 |
+
|
83 |
+
return $columns;
|
84 |
+
}
|
85 |
+
|
86 |
+
function prepare_items() {
|
87 |
+
$screen_id = get_current_screen()->id;
|
88 |
+
$per_page = $this->get_items_per_page( str_replace( '-', '_', "{$screen_id}_per_page" ) );
|
89 |
+
|
90 |
+
$this->_column_headers = array(
|
91 |
+
$this->get_columns(),
|
92 |
+
array(),
|
93 |
+
$this->get_sortable_columns()
|
94 |
+
);
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Get Relavent Posts.
|
98 |
+
*/
|
99 |
+
$posts = get_posts( array(
|
100 |
+
'numberposts' => -1,
|
101 |
+
'post_type' => 'any',
|
102 |
+
'post_status' => 'any',
|
103 |
+
'orderby' => 'ID',
|
104 |
+
'order' => 'ASC',
|
105 |
+
'meta_key' => '_SnS'
|
106 |
+
) );
|
107 |
+
|
108 |
+
$items = $this->_add_meta_data( $posts );
|
109 |
+
|
110 |
+
$total_items = count( $items );
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Reduce items to current page's posts.
|
114 |
+
*/
|
115 |
+
$this->items = array_slice(
|
116 |
+
$items,
|
117 |
+
( ( $this->get_pagenum() - 1 ) * $per_page ),
|
118 |
+
$per_page
|
119 |
+
);
|
120 |
+
|
121 |
+
$this->set_pagination_args( compact( 'total_items', 'per_page' ) );
|
122 |
+
}
|
123 |
+
|
124 |
+
function _post_states( $post ) {
|
125 |
+
$post_states = array();
|
126 |
+
$return = '';
|
127 |
+
if ( isset($_GET['post_status']) )
|
128 |
+
$post_status = $_GET['post_status'];
|
129 |
+
else
|
130 |
+
$post_status = '';
|
131 |
+
|
132 |
+
if ( !empty($post->post_password) )
|
133 |
+
$post_states['protected'] = __('Password protected');
|
134 |
+
if ( 'private' == $post->post_status && 'private' != $post_status )
|
135 |
+
$post_states['private'] = __('Private');
|
136 |
+
if ( 'draft' == $post->post_status && 'draft' != $post_status )
|
137 |
+
$post_states['draft'] = __('Draft');
|
138 |
+
if ( 'pending' == $post->post_status && 'pending' != $post_status )
|
139 |
+
/* translators: post state */
|
140 |
+
$post_states['pending'] = _x('Pending', 'post state');
|
141 |
+
if ( is_sticky($post->ID) )
|
142 |
+
$post_states['sticky'] = __('Sticky');
|
143 |
+
|
144 |
+
$post_states = apply_filters( 'display_post_states', $post_states );
|
145 |
+
|
146 |
+
if ( ! empty($post_states) ) {
|
147 |
+
$state_count = count($post_states);
|
148 |
+
$i = 0;
|
149 |
+
$return .= ' - ';
|
150 |
+
foreach ( $post_states as $state ) {
|
151 |
+
++$i;
|
152 |
+
( $i == $state_count ) ? $sep = '' : $sep = ', ';
|
153 |
+
$return .= "<span class='post-state'>$state$sep</span>";
|
154 |
+
}
|
155 |
+
}
|
156 |
+
|
157 |
+
if ( get_post_format( $post->ID ) )
|
158 |
+
$return .= ' - <span class="post-state-format">' . get_post_format_string( get_post_format( $post->ID ) ) . '</span>';
|
159 |
+
|
160 |
+
return $return;
|
161 |
+
}
|
162 |
+
|
163 |
+
function _add_meta_data( $posts ) {
|
164 |
+
foreach( $posts as $post) {
|
165 |
+
$SnS = get_post_meta( $post->ID, '_SnS', true );
|
166 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
167 |
+
$scripts = isset( $SnS['scripts'] ) ? $SnS[ 'scripts' ]: array();
|
168 |
+
if ( ! empty( $styles ) )
|
169 |
+
$post->sns_styles = $styles;
|
170 |
+
if ( ! empty( $scripts ) )
|
171 |
+
$post->sns_scripts = $scripts;
|
172 |
+
}
|
173 |
+
return $posts;
|
174 |
+
}
|
175 |
+
}
|
176 |
+
?>
|
includes/class.SnS_Settings_Page.php
CHANGED
@@ -6,278 +6,143 @@
|
|
6 |
* and JavaScript directly to individual Post, Pages or custom
|
7 |
* post types.
|
8 |
*/
|
9 |
-
|
10 |
-
// $hook_suffix = 'tools_page_Scripts-n-Styles'; // kept here for reference
|
11 |
-
// $plugin_file = 'scripts-n-styles/scripts-n-styles.php'; // kept here for reference
|
12 |
|
13 |
class SnS_Settings_Page
|
14 |
{
|
15 |
/**
|
16 |
* Constants
|
17 |
*/
|
18 |
-
const
|
19 |
|
20 |
/**
|
21 |
* Initializing method.
|
22 |
* @static
|
23 |
*/
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
add_action( "load-options.php", array( __CLASS__, 'init_options_page' ) );
|
37 |
-
|
38 |
-
add_action( "admin_print_styles-$hook_suffix", array( __CLASS__, 'options_styles'));
|
39 |
-
add_action( "admin_print_scripts-$hook_suffix", array( __CLASS__, 'options_scripts'));
|
40 |
-
|
41 |
-
add_contextual_help( $hook_suffix, self::contextual_help() );
|
42 |
}
|
43 |
}
|
44 |
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
}
|
56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
/**
|
58 |
* Settings Page
|
59 |
* Adds Admin Menu Item via WordPress' "Administration Menus" API. Also hook actions to register options via WordPress' Settings API.
|
60 |
*/
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
'SnS_options' // $option_name (string) (required) The name of an option to sanitize and save.
|
65 |
-
);
|
66 |
register_setting(
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
add_settings_section(
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
add_settings_field(
|
77 |
-
'scripts',
|
78 |
-
'<label for="scripts"><strong>Scripts:</strong> </label>',
|
79 |
-
array( __CLASS__, 'scripts_field' ),
|
80 |
-
SnS_Admin::MENU_SLUG,
|
81 |
-
'global'
|
82 |
-
);
|
83 |
add_settings_field(
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
add_settings_field(
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
'
|
99 |
-
'
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
add_settings_section(
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
);
|
110 |
-
}
|
111 |
-
|
112 |
-
/**
|
113 |
-
* Settings Page
|
114 |
-
* Adds CSS styles to the Scripts n Styles Admin Page.
|
115 |
-
*/
|
116 |
-
static function options_styles() {
|
117 |
-
wp_enqueue_style( 'sns-options-styles', plugins_url('css/options-styles.css', Scripts_n_Styles::$file), array( 'codemirror-default' ), SnS_Admin::VERSION );
|
118 |
-
wp_enqueue_style( 'codemirror', plugins_url( 'libraries/codemirror/lib/codemirror.css', Scripts_n_Styles::$file), array(), '2.1' );
|
119 |
-
wp_enqueue_style( 'codemirror-default', plugins_url( 'libraries/codemirror/theme/default.css', Scripts_n_Styles::$file), array( 'codemirror' ), '2.1' );
|
120 |
-
}
|
121 |
-
|
122 |
-
/**
|
123 |
-
* Settings Page
|
124 |
-
* Adds JavaScript to the Scripts n Styles Admin Page.
|
125 |
-
*/
|
126 |
-
static function options_scripts() {
|
127 |
-
wp_enqueue_script( 'sns-options-scripts', plugins_url('js/options-scripts.js', Scripts_n_Styles::$file), array( 'jquery', 'codemirror-css', 'codemirror-javascript' ), SnS_Admin::VERSION, true );
|
128 |
-
wp_enqueue_script( 'codemirror', plugins_url( 'libraries/codemirror/lib/codemirror.js', Scripts_n_Styles::$file), array(), '2.1' );
|
129 |
-
wp_enqueue_script( 'codemirror-css', plugins_url( 'libraries/codemirror/mode/css.js', Scripts_n_Styles::$file), array( 'codemirror' ), '2.1' );
|
130 |
-
wp_enqueue_script( 'codemirror-javascript', plugins_url( 'libraries/codemirror/mode/javascript.js', Scripts_n_Styles::$file), array( 'codemirror' ), '2.1' );
|
131 |
}
|
132 |
|
133 |
/**
|
134 |
* Settings Page
|
135 |
* Outputs Description text for the Global Section.
|
136 |
*/
|
137 |
-
|
138 |
?>
|
139 |
<div style="max-width: 55em;">
|
140 |
-
<p>
|
141 |
</div>
|
142 |
<?php
|
143 |
}
|
144 |
|
145 |
/**
|
146 |
* Settings Page
|
147 |
-
* Outputs the
|
148 |
-
|
149 |
-
|
150 |
-
if ( ! isset( $_REQUEST[ 'usage' ] ) || ! $_REQUEST[ 'usage' ] ) {
|
151 |
-
echo '<a href="' . menu_page_url( SnS_Admin::MENU_SLUG, false ) . '&usage=1"/>Show Usage</a>';
|
152 |
-
return;
|
153 |
-
}
|
154 |
-
$options = get_option( 'SnS_options' );
|
155 |
-
|
156 |
-
$all_posts = get_posts( array( 'numberposts' => -1, 'post_type' => 'any', 'post_status' => 'any' ) );
|
157 |
-
$sns_posts = array();
|
158 |
-
foreach( $all_posts as $post) {
|
159 |
-
$temp_styles = get_post_meta( $post->ID, 'uFp_styles', true );
|
160 |
-
$temp_scripts = get_post_meta( $post->ID, 'uFp_scripts', true );
|
161 |
-
if ( ! empty( $temp_styles ) || ! empty( $temp_scripts ) )
|
162 |
-
$sns_posts[] = $post;
|
163 |
-
}
|
164 |
-
|
165 |
-
if ( ! empty( $sns_posts ) ) {
|
166 |
-
?>
|
167 |
-
<table cellspacing="0" class="widefat">
|
168 |
-
<thead>
|
169 |
-
<tr>
|
170 |
-
<th>Title</th>
|
171 |
-
<th>ID</th>
|
172 |
-
<th>Status</th>
|
173 |
-
<th>Post Type</th>
|
174 |
-
</tr>
|
175 |
-
</thead>
|
176 |
-
<tbody>
|
177 |
-
<?php foreach( $sns_posts as $post) {
|
178 |
-
$temp_styles = get_post_meta( $post->ID, 'uFp_styles', true );
|
179 |
-
$temp_scripts = get_post_meta( $post->ID, 'uFp_scripts', true );
|
180 |
-
if ( ! empty( $temp_styles ) || ! empty( $temp_scripts ) ) { ?>
|
181 |
-
<tr>
|
182 |
-
<td>
|
183 |
-
<strong><a class="row-title" title="Edit “<?php echo esc_attr( $post->post_title ); ?>”" href="<?php echo esc_url( get_edit_post_link( $post->ID ) ); ?>"><?php echo $post->post_title; ?></a></strong>
|
184 |
-
<div class="row-actions"><span class="edit"><a title="Edit this item" href="<?php echo esc_url( get_edit_post_link( $post->ID ) ); ?>">Edit</a></span></div>
|
185 |
-
</td>
|
186 |
-
<td><?php echo $post->ID; ?></td>
|
187 |
-
<td><?php echo $post->post_status; ?></td>
|
188 |
-
<td><?php echo $post->post_type; ?></td>
|
189 |
-
</tr>
|
190 |
-
<?php }
|
191 |
-
} ?>
|
192 |
-
</tbody>
|
193 |
-
<tfoot>
|
194 |
-
<tr>
|
195 |
-
<th>Title</th>
|
196 |
-
<th>ID</th>
|
197 |
-
<th>Status</th>
|
198 |
-
<th>Post Type</th>
|
199 |
-
</tr>
|
200 |
-
</tfoot>
|
201 |
-
</table>
|
202 |
-
<?php
|
203 |
-
} else {
|
204 |
-
?>
|
205 |
-
<div style="max-width: 55em;">
|
206 |
-
<p>No content items are currently using Scripts-n-Styles data.</p>
|
207 |
-
</div>
|
208 |
-
<?php
|
209 |
-
}
|
210 |
-
}
|
211 |
-
|
212 |
-
/**
|
213 |
-
* Settings Page
|
214 |
-
* Outputs a textarea for setting 'scripts'.
|
215 |
-
*/
|
216 |
-
static function scripts_field() {
|
217 |
-
$options = get_option( 'SnS_options' );
|
218 |
-
?><textarea style="min-width: 500px; width:97%;" class="code js" rows="5" cols="40" name="SnS_options[scripts]" id="scripts"><?php echo isset( $options[ 'scripts' ] ) ? $options[ 'scripts' ] : ''; ?></textarea><br />
|
219 |
-
<span class="description" style="max-width: 500px; display: inline-block;">The "Scripts" will be included <strong>verbatim</strong> in <code><script></code> tags at the bottom of the <code><body></code> element of your html.</span>
|
220 |
-
<?php
|
221 |
-
}
|
222 |
-
|
223 |
-
/**
|
224 |
-
* Settings Page
|
225 |
-
* Outputs a textarea for setting 'styles'.
|
226 |
-
*/
|
227 |
-
static function styles_field() {
|
228 |
-
$options = get_option( 'SnS_options' );
|
229 |
-
?><textarea style="min-width: 500px; width:97%;" class="code css" rows="5" cols="40" name="SnS_options[styles]" id="styles"><?php echo isset( $options[ 'styles' ] ) ? $options[ 'styles' ] : ''; ?></textarea><br />
|
230 |
-
<span class="description" style="max-width: 500px; display: inline-block;">The "Styles" will be included <strong>verbatim</strong> in <code><style></code> tags in the <code><head></code> element of your html.</span><?php
|
231 |
-
}
|
232 |
-
|
233 |
-
/**
|
234 |
-
* Settings Page
|
235 |
-
* Outputs a textarea for setting 'scripts_in_head'.
|
236 |
-
*/
|
237 |
-
static function scripts_in_head_field() {
|
238 |
-
$options = get_option( 'SnS_options' );
|
239 |
-
?><textarea style="min-width: 500px; width:97%;" class="code js" rows="5" cols="40" name="SnS_options[scripts_in_head]" id="scripts_in_head"><?php echo isset( $options[ 'scripts_in_head' ] ) ? $options[ 'scripts_in_head' ] : ''; ?></textarea><br />
|
240 |
-
<span class="description" style="max-width: 500px; display: inline-block;">The "Scripts (in head)" will be included <strong>verbatim</strong> in <code><script></code> tags in the <code><head></code> element of your html.</span>
|
241 |
-
<?php
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* Settings Page
|
246 |
-
* Outputs a select element for selecting options to set $sns_enqueue_scripts.
|
247 |
-
*/
|
248 |
-
static function enqueue_scripts_field() {
|
249 |
-
$registered_handles = Scripts_n_Styles::get_wp_registered();
|
250 |
-
$sns_enqueue_scripts = get_option( 'SnS_enqueue_scripts' );
|
251 |
-
if ( ! is_array( $sns_enqueue_scripts ) ) $sns_enqueue_scripts = array();
|
252 |
-
?>
|
253 |
-
<select name="SnS_enqueue_scripts[]" id="enqueue_scripts" size="5" multiple="multiple" style="height: auto;">
|
254 |
-
<?php foreach ( $registered_handles as $value ) { ?>
|
255 |
-
<option value="<?php echo $value ?>"<?php foreach ( $sns_enqueue_scripts as $handle ) selected( $handle, $value ); ?>><?php echo $value ?></option>
|
256 |
-
<?php } ?>
|
257 |
-
</select>
|
258 |
-
<?php if ( ! empty( $sns_enqueue_scripts ) && is_array( $sns_enqueue_scripts ) ) { ?>
|
259 |
-
<p>Currently Enqueued Scripts:
|
260 |
-
<?php foreach ( $sns_enqueue_scripts as $handle ) echo '<code>' . $handle . '</code> '; ?>
|
261 |
-
</p>
|
262 |
-
<?php }
|
263 |
-
}
|
264 |
-
|
265 |
-
/**
|
266 |
-
* Settings Page
|
267 |
-
* Outputs the Admin Page and calls the Settings registered with the Settings API in init_options_page().
|
268 |
-
*/
|
269 |
-
static function options_page() {
|
270 |
-
SnS_Admin::upgrade_check();
|
271 |
-
global $title;
|
272 |
?>
|
273 |
-
<div
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
|
|
|
|
|
|
|
|
281 |
</div>
|
282 |
<?php
|
283 |
}
|
6 |
* and JavaScript directly to individual Post, Pages or custom
|
7 |
* post types.
|
8 |
*/
|
|
|
|
|
|
|
9 |
|
10 |
class SnS_Settings_Page
|
11 |
{
|
12 |
/**
|
13 |
* Constants
|
14 |
*/
|
15 |
+
const MENU_SLUG = 'sns_settings';
|
16 |
|
17 |
/**
|
18 |
* Initializing method.
|
19 |
* @static
|
20 |
*/
|
21 |
+
function init() {
|
22 |
+
$hook_suffix = add_submenu_page( SnS_Admin::$parent_slug, 'Scripts n Styles', 'Settings', 'unfiltered_html', self::MENU_SLUG, array( 'SnS_Form', 'page' ) );
|
23 |
+
|
24 |
+
add_action( "load-$hook_suffix", array( __CLASS__, 'admin_load' ) );
|
25 |
+
add_action( "load-$hook_suffix", array( 'SnS_Admin', 'help' ) );
|
26 |
+
add_action( "load-$hook_suffix", array( 'SnS_Form', 'take_action' ), 49 );
|
27 |
+
add_action( "admin_print_styles-$hook_suffix", array( __CLASS__, 'admin_enqueue_scripts' ) );
|
28 |
+
|
29 |
+
// Make the page into a tab.
|
30 |
+
if ( SnS_Admin::MENU_SLUG != SnS_Admin::$parent_slug ) {
|
31 |
+
remove_submenu_page( SnS_Admin::$parent_slug, self::MENU_SLUG );
|
32 |
+
add_filter( 'parent_file', array( __CLASS__, 'parent_file') );
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
}
|
34 |
}
|
35 |
|
36 |
+
function admin_enqueue_scripts() {
|
37 |
+
$options = get_option( 'SnS_options' );
|
38 |
+
$cm_theme = isset( $options[ 'cm_theme' ] ) ? $options[ 'cm_theme' ] : 'default';
|
39 |
+
|
40 |
+
wp_enqueue_style( 'sns-options-styles', plugins_url('css/options-styles.css', Scripts_n_Styles::$file), array( 'codemirror' ), SnS_Admin::VERSION );
|
41 |
+
wp_enqueue_style( 'codemirror', plugins_url( 'libraries/CodeMirror2/lib/codemirror.css', Scripts_n_Styles::$file), array(), '2.18' );
|
42 |
+
|
43 |
+
foreach ( array( 'cobalt', 'default', 'eclipse', 'elegant', 'monokai', 'neat', 'night', 'rubyblue' ) as $theme )
|
44 |
+
wp_enqueue_style( "codemirror-$theme", plugins_url( "libraries/CodeMirror2/theme/$theme.css", Scripts_n_Styles::$file), array( 'codemirror' ), '2.18' );
|
45 |
+
|
46 |
+
wp_enqueue_script( 'sns-settings-page-scripts', plugins_url('js/settings-page.js', Scripts_n_Styles::$file), array( 'jquery', 'codemirror-css', 'codemirror-javascript' ), SnS_Admin::VERSION, true );
|
47 |
+
wp_localize_script( 'sns-settings-page-scripts', 'codemirror_options', array( 'theme' => $cm_theme ) );
|
48 |
+
wp_enqueue_script( 'codemirror', plugins_url( 'libraries/CodeMirror2/lib/codemirror.js', Scripts_n_Styles::$file), array(), '2.18' );
|
49 |
+
wp_enqueue_script( 'codemirror-css', plugins_url( 'libraries/CodeMirror2/mode/css/css.js', Scripts_n_Styles::$file), array( 'codemirror' ), '2.18' );
|
50 |
+
wp_enqueue_script( 'codemirror-javascript', plugins_url( 'libraries/CodeMirror2/mode/javascript/javascript.js', Scripts_n_Styles::$file), array( 'codemirror' ), '2.18' );
|
51 |
+
|
52 |
+
wp_enqueue_script( 'codemirror-xml', plugins_url( 'libraries/CodeMirror2/mode/xml/xml.js', Scripts_n_Styles::$file), array( 'codemirror' ), '2.18' );
|
53 |
+
wp_enqueue_script( 'codemirror-clike', plugins_url( 'libraries/CodeMirror2/mode/clike/clike.js', Scripts_n_Styles::$file), array( 'codemirror' ), '2.18' );
|
54 |
+
wp_enqueue_script( 'codemirror-php', plugins_url( 'libraries/CodeMirror2/mode/php/php.js', Scripts_n_Styles::$file), array( 'codemirror-xml', 'codemirror-css', 'codemirror-javascript', 'codemirror-clike' ), '2.18' );
|
55 |
}
|
56 |
|
57 |
+
static function parent_file( $parent_file ) {
|
58 |
+
global $plugin_page, $submenu_file;
|
59 |
+
if ( self::MENU_SLUG == $plugin_page ) $submenu_file = SnS_Admin::MENU_SLUG;
|
60 |
+
return $parent_file;
|
61 |
+
}
|
62 |
+
|
63 |
+
|
64 |
/**
|
65 |
* Settings Page
|
66 |
* Adds Admin Menu Item via WordPress' "Administration Menus" API. Also hook actions to register options via WordPress' Settings API.
|
67 |
*/
|
68 |
+
function admin_load() {
|
69 |
+
wp_enqueue_style( 'sns-options-styles', plugins_url('css/options-styles.css', Scripts_n_Styles::$file), array(), SnS_Admin::VERSION );
|
70 |
+
|
|
|
|
|
71 |
register_setting(
|
72 |
+
SnS_Admin::OPTION_GROUP,
|
73 |
+
'SnS_options' );
|
74 |
+
|
75 |
add_settings_section(
|
76 |
+
'settings',
|
77 |
+
'Scripts n Styles Settings',
|
78 |
+
array( __CLASS__, 'settings_section' ),
|
79 |
+
SnS_Admin::MENU_SLUG );
|
80 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
add_settings_field(
|
82 |
+
'menu_position',
|
83 |
+
'<strong>Menu Position</strong>: ',
|
84 |
+
array( 'SnS_Form', 'select' ),
|
85 |
+
SnS_Admin::MENU_SLUG,
|
86 |
+
'settings',
|
87 |
+
array(
|
88 |
+
'label_for' => 'menu_position',
|
89 |
+
'setting' => 'SnS_options',
|
90 |
+
'choices' => array( 'menu', 'object', 'utility', 'tools.php', 'options-general.php', 'themes.php' ),
|
91 |
+
'size' => 6,
|
92 |
+
'style' => 'height: auto;'
|
93 |
+
) );
|
94 |
+
|
95 |
add_settings_field(
|
96 |
+
'cm_theme',
|
97 |
+
'<strong>CodeMirror Theme</strong>: ',
|
98 |
+
array( 'SnS_Form', 'select' ),
|
99 |
+
SnS_Admin::MENU_SLUG,
|
100 |
+
'settings',
|
101 |
+
array(
|
102 |
+
'label_for' => 'cm_theme',
|
103 |
+
'setting' => 'SnS_options',
|
104 |
+
'choices' => array( 'cobalt', 'default', 'eclipse', 'elegant', 'monokai', 'neat', 'night', 'rubyblue' ),
|
105 |
+
'size' => 8,
|
106 |
+
'style' => 'height: auto;'
|
107 |
+
) );
|
108 |
+
|
109 |
add_settings_section(
|
110 |
+
'demo',
|
111 |
+
'Code Mirror Demo',
|
112 |
+
array( __CLASS__, 'demo_section' ),
|
113 |
+
SnS_Admin::MENU_SLUG );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
}
|
115 |
|
116 |
/**
|
117 |
* Settings Page
|
118 |
* Outputs Description text for the Global Section.
|
119 |
*/
|
120 |
+
function settings_section() {
|
121 |
?>
|
122 |
<div style="max-width: 55em;">
|
123 |
+
<p>Control how and where Scripts n Styles menus and metaboxes appear. These options are here because sometimes users really care about this stuff. Feel free to adjust to your liking. :-)</p>
|
124 |
</div>
|
125 |
<?php
|
126 |
}
|
127 |
|
128 |
/**
|
129 |
* Settings Page
|
130 |
+
* Outputs Description text for the Global Section.
|
131 |
+
*/
|
132 |
+
function demo_section() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
?>
|
134 |
+
<div style="max-width: 55em;">
|
135 |
+
<textarea id="codemirror_demo" name="code" style="min-width: 500px; width:97%;" rows="5" cols="40">
|
136 |
+
<?php echo esc_textarea( '<?php
|
137 |
+
function hello($who) {
|
138 |
+
return "Hello " . $who;
|
139 |
+
}
|
140 |
+
?>
|
141 |
+
<p>The program says <?= hello("World") ?>.</p>
|
142 |
+
<script>
|
143 |
+
alert("And here is some JS code"); // also colored
|
144 |
+
</script>' ); ?>
|
145 |
+
</textarea>
|
146 |
</div>
|
147 |
<?php
|
148 |
}
|
includes/class.SnS_Usage_Page.php
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* SnS_Settings_Page
|
4 |
+
*
|
5 |
+
* Allows WordPress admin users the ability to add custom CSS
|
6 |
+
* and JavaScript directly to individual Post, Pages or custom
|
7 |
+
* post types.
|
8 |
+
*/
|
9 |
+
|
10 |
+
class SnS_Usage_Page
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Constants
|
14 |
+
*/
|
15 |
+
const MENU_SLUG = 'sns_usage';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Initializing method.
|
19 |
+
* @static
|
20 |
+
*/
|
21 |
+
function init() {
|
22 |
+
$hook_suffix = add_submenu_page( SnS_Admin::$parent_slug, 'Scripts n Styles', 'Usage', 'unfiltered_html', self::MENU_SLUG, array( 'SnS_Form', 'page' ) );
|
23 |
+
|
24 |
+
add_action( "load-$hook_suffix", array( __CLASS__, 'admin_load' ) );
|
25 |
+
add_action( "load-$hook_suffix", array( 'SnS_Admin', 'help' ) );
|
26 |
+
|
27 |
+
// Make the page into a tab.
|
28 |
+
if ( SnS_Admin::MENU_SLUG != SnS_Admin::$parent_slug ) {
|
29 |
+
remove_submenu_page( SnS_Admin::$parent_slug, self::MENU_SLUG );
|
30 |
+
add_filter( 'parent_file', array( __CLASS__, 'parent_file') );
|
31 |
+
}
|
32 |
+
}
|
33 |
+
|
34 |
+
static function parent_file( $parent_file ) {
|
35 |
+
global $plugin_page, $submenu_file;
|
36 |
+
if ( self::MENU_SLUG == $plugin_page ) $submenu_file = SnS_Admin::MENU_SLUG;
|
37 |
+
return $parent_file;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Settings Page
|
42 |
+
* Adds Admin Menu Item via WordPress' "Administration Menus" API. Also hook actions to register options via WordPress' Settings API.
|
43 |
+
*/
|
44 |
+
function admin_load() {
|
45 |
+
wp_enqueue_style( 'sns-options-styles', plugins_url('css/options-styles.css', Scripts_n_Styles::$file), array(), SnS_Admin::VERSION );
|
46 |
+
|
47 |
+
add_screen_option( 'per_page', array( 'label' => __( 'Per Page' ), 'default' => 20 ) );
|
48 |
+
add_filter( 'set-screen-option', array( __CLASS__, 'set_screen_option' ), 10, 3 );
|
49 |
+
// hack for core limitation: see http://core.trac.wordpress.org/ticket/18954
|
50 |
+
set_screen_options();
|
51 |
+
|
52 |
+
add_settings_section(
|
53 |
+
'usage',
|
54 |
+
'Scripts n Styles Usage',
|
55 |
+
array( __CLASS__, 'usage_section' ),
|
56 |
+
SnS_Admin::MENU_SLUG );
|
57 |
+
}
|
58 |
+
|
59 |
+
function set_screen_option( $false, $option, $value ) {
|
60 |
+
$screen_id = get_current_screen()->id;
|
61 |
+
$this_option = str_replace( '-', '_', "{$screen_id}_per_page" );
|
62 |
+
if ( $this_option != $option )
|
63 |
+
return false;
|
64 |
+
|
65 |
+
$value = (int) $value;
|
66 |
+
if ( $value < 1 || $value > 999 )
|
67 |
+
return false;
|
68 |
+
|
69 |
+
return $value;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Settings Page
|
74 |
+
* Outputs the Usage Section.
|
75 |
+
*/
|
76 |
+
function usage_section() { ?>
|
77 |
+
<div style="max-width: 55em;">
|
78 |
+
<p>The following table shows content that utilizes Scripts n Styles.</p>
|
79 |
+
</div>
|
80 |
+
<?php
|
81 |
+
require_once( 'class.SnS_List_Usage.php' );
|
82 |
+
$usageTable = new SnS_List_Usage();
|
83 |
+
$usageTable->prepare_items();
|
84 |
+
$usageTable->display();
|
85 |
+
}
|
86 |
+
}
|
87 |
+
?>
|
js/{options-scripts.js → global-page.js}
RENAMED
@@ -1,10 +1,11 @@
|
|
1 |
// Options JavaScript
|
2 |
|
3 |
jQuery( document ).ready( function( $ ) {
|
|
|
4 |
$( "textarea.js" ).each( function() {
|
5 |
-
CodeMirror.fromTextArea( this, { lineNumbers: true, mode: "javascript" } );
|
6 |
});
|
7 |
$( "textarea.css" ).each( function() {
|
8 |
-
CodeMirror.fromTextArea( this, { lineNumbers: true, mode: "css" } );
|
9 |
});
|
10 |
});
|
1 |
// Options JavaScript
|
2 |
|
3 |
jQuery( document ).ready( function( $ ) {
|
4 |
+
var theme = codemirror_options.theme ? codemirror_options.theme: 'default';
|
5 |
$( "textarea.js" ).each( function() {
|
6 |
+
CodeMirror.fromTextArea( this, { lineNumbers: true, mode: "javascript", theme: theme } );
|
7 |
});
|
8 |
$( "textarea.css" ).each( function() {
|
9 |
+
CodeMirror.fromTextArea( this, { lineNumbers: true, mode: "css", theme: theme } );
|
10 |
});
|
11 |
});
|
js/meta-box-scripts.js
CHANGED
@@ -1,12 +1,428 @@
|
|
1 |
-
// Meta Box JavaScript
|
2 |
-
|
3 |
jQuery( document ).ready( function( $ ) {
|
4 |
-
$( "#uFp_meta_box" ).tabs().find( ".wp-tab-bar" ).show();
|
5 |
|
6 |
-
|
7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
});
|
9 |
-
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
});
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
jQuery( document ).ready( function( $ ) {
|
|
|
2 |
|
3 |
+
// For compat: 3.3 || 3.2
|
4 |
+
var initData = tinyMCEPreInit.mceInit["content"] || tinyMCEPreInit.mceInit,
|
5 |
+
context = '#SnS_meta_box',
|
6 |
+
currentCodeMirror = [],
|
7 |
+
mceBodyClass = getMCEBodyClasses(),
|
8 |
+
nonce = $( '#scripts_n_styles_noncename' ).val(),
|
9 |
+
theme = codemirror_options.theme ? codemirror_options.theme: 'default';
|
10 |
+
|
11 |
+
//$('textarea', context).attr('autocomplete','off');
|
12 |
+
|
13 |
+
// Refresh when panel becomes unhidden
|
14 |
+
$( context + '-hide, '
|
15 |
+
+ context + ' .hndle, '
|
16 |
+
+ context + ' .handlediv ' )
|
17 |
+
.live( 'click', refreshCodeMirrors );
|
18 |
+
|
19 |
+
// add tab-switch handler
|
20 |
+
$( '.wp-tab-bar a', context ).live( 'click', onTabSwitch );
|
21 |
+
|
22 |
+
// activate first run
|
23 |
+
$( '.wp-tab-active a', context ).trigger( 'click' );
|
24 |
+
|
25 |
+
// must run before ajax click handlers are added.
|
26 |
+
setupAjaxUI();
|
27 |
+
|
28 |
+
refreshDeleteBtns();
|
29 |
+
|
30 |
+
|
31 |
+
$('#sns-ajax-update-scripts').click(function( event ){
|
32 |
+
event.preventDefault();
|
33 |
+
$(this).next().show();
|
34 |
+
$(currentCodeMirror).each(function (){ this.save(); });
|
35 |
+
var args = { _ajax_nonce: nonce, post_id: $( '#post_ID' ).val(), };
|
36 |
+
|
37 |
+
args.action = 'sns_scripts';
|
38 |
+
args.scripts = $( '#SnS_scripts' ).val();
|
39 |
+
args.scripts_in_head = $( '#SnS_scripts_in_head' ).val();
|
40 |
+
|
41 |
+
$.post( ajaxurl, args, function() { refreshMCE(); } );
|
42 |
+
});
|
43 |
+
|
44 |
+
$('#sns-ajax-update-styles').click(function( event ){
|
45 |
+
event.preventDefault();
|
46 |
+
$(this).next().show();
|
47 |
+
$(currentCodeMirror).each(function (){ this.save(); });
|
48 |
+
var args = { _ajax_nonce: nonce, post_id: $( '#post_ID' ).val(), };
|
49 |
+
|
50 |
+
args.action = 'sns_styles';
|
51 |
+
args.styles = $( '#SnS_styles' ).val();
|
52 |
+
|
53 |
+
$.post( ajaxurl, args, function() { refreshMCE(); } );
|
54 |
+
});
|
55 |
+
|
56 |
+
/*
|
57 |
+
* Expects return data.
|
58 |
+
*/
|
59 |
+
$('#sns-ajax-update-classes').click(function( event ){
|
60 |
+
event.preventDefault();
|
61 |
+
$(this).next().show();
|
62 |
+
var args = { _ajax_nonce: nonce, post_id: $( '#post_ID' ).val(), };
|
63 |
+
|
64 |
+
args.action = 'sns_classes';
|
65 |
+
args.classes_body = $( '#SnS_classes_body' ).val();
|
66 |
+
args.classes_post = $( '#SnS_classes_post' ).val();
|
67 |
+
|
68 |
+
$.post( ajaxurl, args, function( data ) { refreshBodyClass( data ); } );
|
69 |
+
});
|
70 |
+
|
71 |
+
/*
|
72 |
+
* Expects return data.
|
73 |
+
*/
|
74 |
+
$('#sns-ajax-update-dropdown').click(function( event ){
|
75 |
+
event.preventDefault();
|
76 |
+
$(this).next().show();
|
77 |
+
var args = { _ajax_nonce: nonce, post_id: $( '#post_ID' ).val(), };
|
78 |
+
|
79 |
+
args.action = 'sns_dropdown';
|
80 |
+
var format = {};
|
81 |
+
format.title = $( '#SnS_classes_mce_title' ).val();
|
82 |
+
format.classes = $( '#SnS_classes_mce_classes' ).val();
|
83 |
+
switch ( $( '#SnS_classes_mce_type' ).val() ) {
|
84 |
+
case 'inline':
|
85 |
+
format.inline = $( '#SnS_classes_mce_element' ).val();
|
86 |
+
break;
|
87 |
+
case 'block':
|
88 |
+
format.block = $( '#SnS_classes_mce_element' ).val();
|
89 |
+
if ( $( '#SnS_classes_mce_wrapper' ).prop('checked') )
|
90 |
+
format.wrapper = true;
|
91 |
+
break;
|
92 |
+
case 'selector':
|
93 |
+
format.selector = $( '#SnS_classes_mce_element' ).val();
|
94 |
+
break;
|
95 |
+
default:
|
96 |
+
return;
|
97 |
+
}
|
98 |
+
args.format = format;
|
99 |
+
|
100 |
+
$.post( ajaxurl, args, function( data ) { refreshStyleFormats( data ); } );
|
101 |
});
|
102 |
+
|
103 |
+
/*
|
104 |
+
* Expects return data.
|
105 |
+
*/
|
106 |
+
$('#delete-mce-dropdown-names .sns-ajax-delete').live( "click", function( event ){
|
107 |
+
event.preventDefault();
|
108 |
+
$(this).next().show();
|
109 |
+
var args = { _ajax_nonce: nonce, post_id: $( '#post_ID' ).val(), };
|
110 |
+
|
111 |
+
args.action = 'sns_delete_class';
|
112 |
+
args.delete = $( this ).attr( 'id' );
|
113 |
+
|
114 |
+
$.post( ajaxurl, args, function( data ) { refreshStyleFormats( data ); } );
|
115 |
});
|
116 |
+
|
117 |
+
/*
|
118 |
+
* Returns the body_class of TinyMCE minus the Scripts n Styles values.
|
119 |
+
*/
|
120 |
+
function getMCEBodyClasses() {
|
121 |
+
var t = [];
|
122 |
+
if ( initData.body_class )
|
123 |
+
t = initData.body_class.split(' ');
|
124 |
+
|
125 |
+
var bc = $('#SnS_classes_body').val().split(' ');
|
126 |
+
var pc = $('#SnS_classes_post').val().split(' ');
|
127 |
+
var p;
|
128 |
+
for ( var i = 0; i < t.length; i++ ) {
|
129 |
+
p = $.inArray( bc[i], t )
|
130 |
+
if ( -1 != p )
|
131 |
+
t.splice( p, 1 );
|
132 |
+
}
|
133 |
+
for ( var i = 0; i < t.length; i++ ) {
|
134 |
+
p = $.inArray( pc[i], t )
|
135 |
+
if ( -1 != p )
|
136 |
+
t.splice( p, 1 );
|
137 |
+
}
|
138 |
+
t = t.join(' ');
|
139 |
+
return t;
|
140 |
+
}
|
141 |
+
|
142 |
+
/*
|
143 |
+
* Builds and Adds the DOM for AJAX functionality.
|
144 |
+
*/
|
145 |
+
function setupAjaxUI() {
|
146 |
+
// set up ajax ui. (need to come up with a better ID naming scheme.)
|
147 |
+
$('#SnS_scripts-tab').append(
|
148 |
+
'<div class="sns-ajax-wrap">'
|
149 |
+
+ '<a id="sns-ajax-update-scripts" href="#" class="button">Update Scripts</a>'
|
150 |
+
+ ' '
|
151 |
+
+ '<img class="sns-ajax-loading" src="/wp-admin/images/wpspin_light.gif">'
|
152 |
+
+ '</div>'
|
153 |
+
);
|
154 |
+
|
155 |
+
$('#SnS_styles-tab').append(
|
156 |
+
'<div class="sns-ajax-wrap">'
|
157 |
+
+ '<a id="sns-ajax-update-styles" href="#" class="button">Update Styles</a>'
|
158 |
+
+ ' '
|
159 |
+
+ '<img class="sns-ajax-loading" src="/wp-admin/images/wpspin_light.gif">'
|
160 |
+
+ '</div>'
|
161 |
+
);
|
162 |
+
|
163 |
+
$('#sns-classes').append(
|
164 |
+
'<div class="sns-ajax-wrap">'
|
165 |
+
+ '<a id="sns-ajax-update-classes" href="#" class="button">Update Classes</a>'
|
166 |
+
+ ' '
|
167 |
+
+ '<img class="sns-ajax-loading" src="/wp-admin/images/wpspin_light.gif">'
|
168 |
+
+ '</div>'
|
169 |
+
);
|
170 |
+
|
171 |
+
$('#add-mce-dropdown-names').append(
|
172 |
+
'<div class="sns-ajax-wrap">'
|
173 |
+
+ '<a id="sns-ajax-update-dropdown" href="#" class="button">Add Class</a>'
|
174 |
+
+ ' '
|
175 |
+
+ '<img class="sns-ajax-loading" src="/wp-admin/images/wpspin_light.gif">'
|
176 |
+
+ '</div>'
|
177 |
+
);
|
178 |
+
|
179 |
+
$('.sns-ajax-loading').hide();
|
180 |
+
|
181 |
+
if ( $( '#SnS_classes_mce_type').val() == 'block' ) {
|
182 |
+
$('#add-mce-dropdown-names .sns-mce-wrapper').show();
|
183 |
+
} else {
|
184 |
+
$('#add-mce-dropdown-names .sns-mce-wrapper').hide();
|
185 |
+
}
|
186 |
+
|
187 |
+
$( '#SnS_classes_mce_type' ).change(function() {
|
188 |
+
if ( $(this).val() == 'block' ) {
|
189 |
+
$('#add-mce-dropdown-names .sns-mce-wrapper').show();
|
190 |
+
} else {
|
191 |
+
$('#add-mce-dropdown-names .sns-mce-wrapper').hide();
|
192 |
+
}
|
193 |
+
});
|
194 |
+
|
195 |
+
$( '#mce-dropdown-names', context ).show();
|
196 |
+
}
|
197 |
+
|
198 |
+
/*
|
199 |
+
* Main Tab Switch Handler.
|
200 |
+
*/
|
201 |
+
function onTabSwitch( event ) {
|
202 |
+
event.preventDefault();
|
203 |
+
|
204 |
+
clearCodeMirrors();
|
205 |
+
|
206 |
+
/*
|
207 |
+
* There is a weird bug where if clearCodeMirrors() is called right before
|
208 |
+
* loadCodeMirrors(), loading the page with the Styles tab active, and
|
209 |
+
* then switching to the Script tab, you can lose data from the second
|
210 |
+
* CodeMirror if leaving and returning to that tab. I've no idea what's
|
211 |
+
* going on there. Leaving code inbetween them is a fraggle, but working,
|
212 |
+
* workaround. Maybe has to do with execution time? No idea.
|
213 |
+
*/
|
214 |
+
|
215 |
+
// switch active classes
|
216 |
+
$( '.wp-tab-active', context ).removeClass( 'wp-tab-active' );
|
217 |
+
$( this ).parent( 'li' ).addClass( 'wp-tab-active' );
|
218 |
+
|
219 |
+
$( '.wp-tabs-panel-active', context ).hide().removeClass( 'wp-tabs-panel-active' );
|
220 |
+
$( $( this ).attr( 'href' ) ).show().addClass( 'wp-tabs-panel-active' );
|
221 |
+
|
222 |
+
loadCodeMirrors();
|
223 |
+
|
224 |
+
$.post( ajaxurl, {
|
225 |
+
action: 'sns_update_tab',
|
226 |
+
_ajax_nonce: nonce,
|
227 |
+
active_tab: $( '.wp-tab-bar li', context ).index( $( this ).parent( 'li' ).get(0) )
|
228 |
+
}
|
229 |
+
);
|
230 |
+
}
|
231 |
+
|
232 |
+
/*
|
233 |
+
* CodeMirror Utilities.
|
234 |
+
*/
|
235 |
+
function clearCodeMirrors() {
|
236 |
+
$(currentCodeMirror).each(function (){
|
237 |
+
this.toTextArea();
|
238 |
+
});
|
239 |
+
currentCodeMirror = [];
|
240 |
+
}
|
241 |
+
function refreshCodeMirrors() {
|
242 |
+
$(currentCodeMirror).each( function(){
|
243 |
+
this.refresh();
|
244 |
+
});
|
245 |
+
}
|
246 |
+
function loadCodeMirrors() {
|
247 |
+
// collect codemirrors
|
248 |
+
var settings;
|
249 |
+
// loop codemirrors
|
250 |
+
$( '.wp-tabs-panel-active textarea.codemirror', context ).each(function (){
|
251 |
+
if ( $(this).hasClass( 'js' ) )
|
252 |
+
settings = {
|
253 |
+
mode: "text/javascript",
|
254 |
+
theme: theme,
|
255 |
+
lineNumbers: true,
|
256 |
+
tabMode: "shift",
|
257 |
+
indentUnit: 4,
|
258 |
+
indentWithTabs: true
|
259 |
+
};
|
260 |
+
else if ( $(this).hasClass( 'css' ) )
|
261 |
+
settings = {
|
262 |
+
mode: "text/css",
|
263 |
+
theme: theme,
|
264 |
+
lineNumbers: true,
|
265 |
+
tabMode: "shift",
|
266 |
+
indentUnit: 4,
|
267 |
+
indentWithTabs: true
|
268 |
+
};
|
269 |
+
/*else if ( $(this).hasClass( 'htmlmixed' ) )
|
270 |
+
settings = {
|
271 |
+
mode: "text/html",
|
272 |
+
lineNumbers: true,
|
273 |
+
tabMode: "shift",
|
274 |
+
indentUnit: 8,
|
275 |
+
indentWithTabs: true,
|
276 |
+
enterMode: "keep",
|
277 |
+
matchBrackets: true
|
278 |
+
};
|
279 |
+
else if ( $(this).hasClass( 'php' ) )
|
280 |
+
settings = {
|
281 |
+
mode: "application/x-httpd-php",
|
282 |
+
lineNumbers: true,
|
283 |
+
tabMode: "shift",
|
284 |
+
indentUnit: 8,
|
285 |
+
indentWithTabs: true,
|
286 |
+
enterMode: "keep",
|
287 |
+
matchBrackets: true
|
288 |
+
};*/
|
289 |
+
else
|
290 |
+
return;
|
291 |
+
|
292 |
+
// initialize and store active codemirrors
|
293 |
+
currentCodeMirror.push( CodeMirror.fromTextArea( this, settings ) );
|
294 |
+
});
|
295 |
+
}
|
296 |
+
|
297 |
+
/*
|
298 |
+
* Refresh after AJAX.
|
299 |
+
*/
|
300 |
+
function refreshDeleteBtns() {
|
301 |
+
// responsible for clearing out Delete Buttons, and Adding new ones.
|
302 |
+
// initData should always contain the latest settings.
|
303 |
+
if ( initData.style_formats && initData.style_formats.length ) {
|
304 |
+
$( '#delete-mce-dropdown-names .sns-ajax-delete-p' ).remove();
|
305 |
+
$( '#delete-mce-dropdown-names', context ).show();
|
306 |
+
var formats = initData.style_formats;
|
307 |
+
for ( var i = 0; i < formats.length; i++ ) {
|
308 |
+
var deleteBtn = {};
|
309 |
+
if ( formats[i].inline ) {
|
310 |
+
deleteBtn.element = formats[i].inline;
|
311 |
+
deleteBtn.wrapper = '';
|
312 |
+
} else if ( formats[i].block ) {
|
313 |
+
deleteBtn.element = formats[i].block;
|
314 |
+
if ( formats[i].wrapper )
|
315 |
+
deleteBtn.wrapper = ' (wrapper)';
|
316 |
+
else
|
317 |
+
deleteBtn.wrapper = '';
|
318 |
+
} else if ( formats[i].selector ) {
|
319 |
+
deleteBtn.element = formats[i].selector;
|
320 |
+
deleteBtn.wrapper = '';
|
321 |
+
} else {
|
322 |
+
alert( 'ERROR!' );
|
323 |
+
}
|
324 |
+
deleteBtn.title = formats[i].title;
|
325 |
+
deleteBtn.classes = formats[i].classes;
|
326 |
+
$( '#instructions-mce-dropdown-names', context ).after(
|
327 |
+
'<p class="sns-ajax-delete-p"><a title="delete" class="sns-ajax-delete" id="'
|
328 |
+
+ deleteBtn.title + '">X</a> "'
|
329 |
+
+ deleteBtn.title + '" <code><'
|
330 |
+
+ deleteBtn.element + ' class="'
|
331 |
+
+ deleteBtn.classes + '"></code>'
|
332 |
+
+ deleteBtn.wrapper + '</p>'
|
333 |
+
);
|
334 |
+
}
|
335 |
+
} else {
|
336 |
+
$( '#delete-mce-dropdown-names', context ).hide();
|
337 |
+
}
|
338 |
+
}
|
339 |
+
function refreshBodyClass( data ) {
|
340 |
+
initData.body_class = mceBodyClass + ' ' + data.classes_body + ' ' + data.classes_post;
|
341 |
+
|
342 |
+
// needed for < 3.3
|
343 |
+
if ( tinymce.settings ) tinymce.settings.body_class = initData.body_class;
|
344 |
+
refreshMCE();
|
345 |
+
}
|
346 |
+
function refreshStyleFormats( data ) {
|
347 |
+
// error check
|
348 |
+
console.log(data.classes_mce);
|
349 |
+
if ( typeof data.classes_mce === 'undefined' ) {
|
350 |
+
alert( data );
|
351 |
+
$('.sns-ajax-loading').hide();
|
352 |
+
return;
|
353 |
+
} else if ( data.classes_mce.length && data.classes_mce != 'Empty' ) {
|
354 |
+
var style_formats = [];
|
355 |
+
|
356 |
+
for ( var i = 0; i < data.classes_mce.length; i++ ) { // loop returned classes_mce
|
357 |
+
var format = {};
|
358 |
+
format.title = data.classes_mce[i].title;
|
359 |
+
|
360 |
+
if ( data.classes_mce[i].inline )
|
361 |
+
format.inline = data.classes_mce[i].inline;
|
362 |
+
else if ( data.classes_mce[i].block ) {
|
363 |
+
format.block = data.classes_mce[i].block;
|
364 |
+
if (data.classes_mce[i].wrapper)
|
365 |
+
format.wrapper = true;
|
366 |
+
} else if ( data.classes_mce[i].selector )
|
367 |
+
format.selector = data.classes_mce[i].selector;
|
368 |
+
else
|
369 |
+
alert('dropdown format has bad type.');
|
370 |
+
|
371 |
+
format.classes = data.classes_mce[i].classes;
|
372 |
+
style_formats.push( format );
|
373 |
+
}
|
374 |
+
initData.style_formats = style_formats;
|
375 |
+
|
376 |
+
// needed for < 3.3
|
377 |
+
if ( tinymce.settings ) tinymce.settings.style_formats = initData.style_formats;
|
378 |
+
if ( initData.theme_advanced_buttons2.indexOf( "styleselect" ) == -1 ) {
|
379 |
+
var tempString = "styleselect,";
|
380 |
+
initData.theme_advanced_buttons2 = tempString.concat(initData.theme_advanced_buttons2);
|
381 |
+
}
|
382 |
+
|
383 |
+
// needed for < 3.3
|
384 |
+
if ( tinymce.settings ) tinymce.settings.theme_advanced_buttons2 = initData.theme_advanced_buttons2;
|
385 |
+
$( '#delete-mce-dropdown-names', context ).show();
|
386 |
+
} else {
|
387 |
+
delete initData.style_formats;
|
388 |
+
initData.theme_advanced_buttons2 = initData.theme_advanced_buttons2.replace("styleselect,", "");
|
389 |
+
|
390 |
+
// needed for < 3.3
|
391 |
+
if ( tinymce.settings ) tinymce.settings.theme_advanced_buttons2 = initData.theme_advanced_buttons2;
|
392 |
+
$( '#delete-mce-dropdown-names', context ).hide();
|
393 |
+
}
|
394 |
+
|
395 |
+
refreshDeleteBtns();
|
396 |
+
refreshMCE();
|
397 |
+
}
|
398 |
+
function refreshMCE() {
|
399 |
+
if ( tinyMCE.editors["content"] ) {
|
400 |
+
// needed for < 3.3 editor initialization.
|
401 |
+
if ( ! $( '#content' ).hasClass( '.theEditor' ) ) $( '#content' ).addClass( 'theEditor' );
|
402 |
+
|
403 |
+
if ( tinyMCE.editors["content"].isHidden() ) {
|
404 |
+
tinyMCE.editors["content"].remove();
|
405 |
+
tinyMCE.init( initData );
|
406 |
+
tinyMCE.editors["content"].hide();
|
407 |
+
} else {
|
408 |
+
// you've got to be kidding me.
|
409 |
+
if ( 1 == $('#content-html').length )
|
410 |
+
$('#content-html').click(); // 3.3
|
411 |
+
else if( 1 == $('#edButtonHTML').length )
|
412 |
+
switchEditors.go('content', 'html'); // 3.2
|
413 |
+
|
414 |
+
tinyMCE.editors["content"].remove();
|
415 |
+
tinyMCE.init( initData );
|
416 |
+
tinyMCE.editors["content"].hide();
|
417 |
+
|
418 |
+
if ( 1 == $('#content-tmce').length )
|
419 |
+
$('#content-tmce').click(); // 3.3
|
420 |
+
else if( 1 == $('#edButtonPreview').length )
|
421 |
+
switchEditors.go('content', 'tinymce'); // 3.2
|
422 |
+
}
|
423 |
+
|
424 |
+
}
|
425 |
+
$('.sns-ajax-loading').hide();
|
426 |
+
}
|
427 |
+
|
428 |
+
});
|
js/settings-page.js
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Options JavaScript
|
2 |
+
|
3 |
+
jQuery( document ).ready( function( $ ) {
|
4 |
+
var theme = codemirror_options.theme ? codemirror_options.theme: 'default';
|
5 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("codemirror_demo"), {
|
6 |
+
lineNumbers: true,
|
7 |
+
matchBrackets: true,
|
8 |
+
mode: "application/x-httpd-php",
|
9 |
+
indentUnit: 4,
|
10 |
+
indentWithTabs: true,
|
11 |
+
enterMode: "keep",
|
12 |
+
tabMode: "shift",
|
13 |
+
theme: theme
|
14 |
+
});
|
15 |
+
$('#cm_theme').change( function(){
|
16 |
+
editor.setOption("theme", $(this).val());
|
17 |
+
});
|
18 |
+
});
|
libraries/{codemirror → CodeMirror2}/LICENSE
RENAMED
File without changes
|
libraries/{codemirror → CodeMirror2}/lib/codemirror.css
RENAMED
@@ -6,10 +6,14 @@
|
|
6 |
.CodeMirror-scroll {
|
7 |
overflow: auto;
|
8 |
height: 300px;
|
|
|
|
|
|
|
9 |
}
|
10 |
|
11 |
.CodeMirror-gutter {
|
12 |
position: absolute; left: 0; top: 0;
|
|
|
13 |
background-color: #f7f7f7;
|
14 |
border-right: 1px solid #eee;
|
15 |
min-width: 2em;
|
@@ -19,6 +23,7 @@
|
|
19 |
color: #aaa;
|
20 |
text-align: right;
|
21 |
padding: .4em .2em .4em .4em;
|
|
|
22 |
}
|
23 |
.CodeMirror-lines {
|
24 |
padding: .4em;
|
@@ -37,6 +42,14 @@
|
|
37 |
word-wrap: normal;
|
38 |
}
|
39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
.CodeMirror textarea {
|
41 |
font-family: inherit !important;
|
42 |
font-size: inherit !important;
|
6 |
.CodeMirror-scroll {
|
7 |
overflow: auto;
|
8 |
height: 300px;
|
9 |
+
/* This is needed to prevent an IE[67] bug where the scrolled content
|
10 |
+
is visible outside of the scrolling box. */
|
11 |
+
position: relative;
|
12 |
}
|
13 |
|
14 |
.CodeMirror-gutter {
|
15 |
position: absolute; left: 0; top: 0;
|
16 |
+
z-index: 10;
|
17 |
background-color: #f7f7f7;
|
18 |
border-right: 1px solid #eee;
|
19 |
min-width: 2em;
|
23 |
color: #aaa;
|
24 |
text-align: right;
|
25 |
padding: .4em .2em .4em .4em;
|
26 |
+
white-space: pre !important;
|
27 |
}
|
28 |
.CodeMirror-lines {
|
29 |
padding: .4em;
|
42 |
word-wrap: normal;
|
43 |
}
|
44 |
|
45 |
+
.CodeMirror-wrap pre {
|
46 |
+
word-wrap: break-word;
|
47 |
+
white-space: pre-wrap;
|
48 |
+
}
|
49 |
+
.CodeMirror-wrap .CodeMirror-scroll {
|
50 |
+
overflow-x: hidden;
|
51 |
+
}
|
52 |
+
|
53 |
.CodeMirror textarea {
|
54 |
font-family: inherit !important;
|
55 |
font-size: inherit !important;
|
libraries/{codemirror → CodeMirror2}/lib/codemirror.js
RENAMED
@@ -1,3 +1,5 @@
|
|
|
|
|
|
1 |
// All functions that need access to the editor's state live inside
|
2 |
// the CodeMirror function. Below that, at the bottom of the file,
|
3 |
// some utilities are defined.
|
@@ -16,18 +18,19 @@ var CodeMirror = (function() {
|
|
16 |
var targetDocument = options["document"];
|
17 |
// The element in which the editor lives.
|
18 |
var wrapper = targetDocument.createElement("div");
|
19 |
-
wrapper.className = "CodeMirror";
|
20 |
// This mess creates the base DOM structure for the editor.
|
21 |
wrapper.innerHTML =
|
22 |
'<div style="overflow: hidden; position: relative; width: 1px; height: 0px;">' + // Wraps and hides input textarea
|
23 |
-
'<textarea style="position: absolute; width:
|
|
|
24 |
'<div class="CodeMirror-scroll cm-s-' + options.theme + '">' +
|
25 |
'<div style="position: relative">' + // Set to the height of the text, causes scrolling
|
26 |
-
'<div style="position: absolute; height: 0; width: 0; overflow: hidden;"></div>' +
|
27 |
'<div style="position: relative">' + // Moved around its parent to cover visible view
|
28 |
'<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
|
29 |
// Provides positioning relative to (visible) text origin
|
30 |
'<div class="CodeMirror-lines"><div style="position: relative">' +
|
|
|
31 |
'<pre class="CodeMirror-cursor"> </pre>' + // Absolutely positioned blinky cursor
|
32 |
'<div></div>' + // This DIV contains the actual code
|
33 |
'</div></div></div></div></div>';
|
@@ -35,21 +38,29 @@ var CodeMirror = (function() {
|
|
35 |
// I've never seen more elegant code in my life.
|
36 |
var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
|
37 |
scroller = wrapper.lastChild, code = scroller.firstChild,
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
if (options.tabindex != null) input.tabindex = options.tabindex;
|
43 |
if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
|
44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
// Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
|
46 |
var poll = new Delayed(), highlight = new Delayed(), blinker;
|
47 |
|
48 |
-
// mode holds a mode API object.
|
49 |
-
//
|
50 |
-
//
|
51 |
-
|
52 |
-
var mode, lines = [new Line("")], work, history = new History(), focused;
|
53 |
loadMode();
|
54 |
// The selection. These are always maintained to point at valid
|
55 |
// positions. Inverted is used to remember that the user is
|
@@ -59,12 +70,12 @@ var CodeMirror = (function() {
|
|
59 |
// whether the user is holding shift. reducedSelection is a hack
|
60 |
// to get around the fact that we can't create inverted
|
61 |
// selections. See below.
|
62 |
-
var shiftSelecting, reducedSelection, lastDoubleClick;
|
63 |
// Variables used by startOperation/endOperation to track what
|
64 |
// happened during the operation.
|
65 |
-
var updateInput, changes, textChanged, selectionChanged, leaveInputAlone;
|
66 |
// Current visible range (may be bigger than the view window).
|
67 |
-
var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
|
68 |
// editing will hold an object describing the things we put in the
|
69 |
// textarea, to help figure out whether something changed.
|
70 |
// bracketHighlighted is used to remember that a backet has been
|
@@ -72,69 +83,94 @@ var CodeMirror = (function() {
|
|
72 |
var editing, bracketHighlighted;
|
73 |
// Tracks the maximum line length so that the horizontal scrollbar
|
74 |
// can be kept static when scrolling.
|
75 |
-
var maxLine = "";
|
76 |
|
77 |
-
// Initialize the content.
|
78 |
-
// to work around browser issues.
|
79 |
operation(function(){setValue(options.value || ""); updateInput = false;})();
|
80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
|
82 |
// Register our event handlers.
|
83 |
connect(scroller, "mousedown", operation(onMouseDown));
|
|
|
|
|
|
|
84 |
// Gecko browsers fire contextmenu *after* opening the menu, at
|
85 |
// which point we can't mess with it anymore. Context menu is
|
86 |
// handled in onMouseDown for Gecko.
|
87 |
-
if (!gecko) connect(scroller, "contextmenu",
|
88 |
-
connect(
|
89 |
-
|
|
|
|
|
|
|
90 |
connect(window, "resize", function() {updateDisplay(true);});
|
91 |
connect(input, "keyup", operation(onKeyUp));
|
|
|
92 |
connect(input, "keydown", operation(onKeyDown));
|
93 |
connect(input, "keypress", operation(onKeyPress));
|
94 |
connect(input, "focus", onFocus);
|
95 |
connect(input, "blur", onBlur);
|
96 |
|
97 |
-
connect(scroller, "dragenter",
|
98 |
-
connect(scroller, "dragover",
|
99 |
connect(scroller, "drop", operation(onDrop));
|
100 |
connect(scroller, "paste", function(){focusInput(); fastPoll();});
|
101 |
connect(input, "paste", function(){fastPoll();});
|
102 |
connect(input, "cut", function(){fastPoll();});
|
103 |
-
|
104 |
-
// IE throws unspecified error in certain cases, when
|
105 |
// trying to access activeElement before onload
|
106 |
var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
|
107 |
-
if (hasFocus) onFocus
|
108 |
else onBlur();
|
109 |
|
110 |
-
function isLine(l) {return l >= 0 && l <
|
111 |
// The instance object that we'll return. Mostly calls out to
|
112 |
// local functions in the CodeMirror function. Some do some extra
|
113 |
// range checking and/or clipping. operation is used to wrap the
|
114 |
// call so that changes it makes are tracked, and the display is
|
115 |
// updated afterwards.
|
116 |
-
var instance = {
|
117 |
getValue: getValue,
|
118 |
setValue: operation(setValue),
|
119 |
getSelection: getSelection,
|
120 |
replaceSelection: operation(replaceSelection),
|
121 |
-
focus: function(){focusInput(); onFocus();
|
122 |
setOption: function(option, value) {
|
|
|
123 |
options[option] = value;
|
124 |
-
if (option == "
|
125 |
-
else if (option == "mode" || option == "indentUnit") loadMode();
|
126 |
else if (option == "readOnly" && value == "nocursor") input.blur();
|
127 |
else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value);
|
|
|
|
|
|
|
|
|
128 |
},
|
129 |
getOption: function(option) {return options[option];},
|
130 |
undo: operation(undo),
|
131 |
redo: operation(redo),
|
132 |
-
indentLine: operation(function(n) {
|
|
|
|
|
133 |
historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
|
|
|
134 |
matchBrackets: operation(function(){matchBrackets(true);}),
|
135 |
-
getTokenAt: function(pos) {
|
136 |
pos = clipPos(pos);
|
137 |
-
return
|
|
|
|
|
|
|
|
|
138 |
},
|
139 |
cursorCoords: function(start){
|
140 |
if (start == null) start = sel.inverted;
|
@@ -143,25 +179,46 @@ var CodeMirror = (function() {
|
|
143 |
charCoords: function(pos){return pageCoords(clipPos(pos));},
|
144 |
coordsChar: function(coords) {
|
145 |
var off = eltOffset(lineSpace);
|
146 |
-
|
147 |
-
return clipPos({line: line, ch: charFromX(clipLine(line), coords.x - off.left)});
|
148 |
},
|
149 |
getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
|
150 |
-
markText: operation(
|
151 |
-
|
152 |
-
|
|
|
153 |
setLineClass: operation(setLineClass),
|
|
|
|
|
154 |
lineInfo: lineInfo,
|
155 |
-
addWidget: function(pos, node, scroll) {
|
156 |
-
|
157 |
-
|
158 |
-
node.style.
|
159 |
code.appendChild(node);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
if (scroll)
|
161 |
-
scrollIntoView(
|
162 |
},
|
163 |
|
164 |
-
lineCount: function() {return
|
165 |
getCursor: function(start) {
|
166 |
if (start == null) start = sel.inverted;
|
167 |
return copyPos(start ? sel.from : sel.to);
|
@@ -172,9 +229,9 @@ var CodeMirror = (function() {
|
|
172 |
else setCursor(line, ch);
|
173 |
}),
|
174 |
setSelection: operation(function(from, to) {setSelection(clipPos(from), clipPos(to || from));}),
|
175 |
-
getLine: function(line) {if (isLine(line)) return
|
176 |
setLine: operation(function(line, text) {
|
177 |
-
if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch:
|
178 |
}),
|
179 |
removeLine: operation(function(line) {
|
180 |
if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
|
@@ -182,56 +239,106 @@ var CodeMirror = (function() {
|
|
182 |
replaceRange: operation(replaceRange),
|
183 |
getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
|
184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
operation: function(f){return operation(f)();},
|
186 |
refresh: function(){updateDisplay(true);},
|
187 |
getInputField: function(){return input;},
|
188 |
-
getWrapperElement: function(){return wrapper;}
|
|
|
|
|
189 |
};
|
190 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
function setValue(code) {
|
192 |
-
history = null;
|
193 |
var top = {line: 0, ch: 0};
|
194 |
-
updateLines(top, {line:
|
195 |
splitLines(code), top, top);
|
196 |
-
|
197 |
}
|
198 |
function getValue(code) {
|
199 |
var text = [];
|
200 |
-
|
201 |
-
text.push(lines[i].text);
|
202 |
return text.join("\n");
|
203 |
}
|
204 |
|
205 |
function onMouseDown(e) {
|
206 |
-
|
207 |
-
|
208 |
-
|
|
|
|
|
|
|
209 |
if (n.parentNode == gutterText) {
|
210 |
if (options.onGutterClick)
|
211 |
-
options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom);
|
212 |
-
return e
|
213 |
}
|
214 |
|
215 |
-
|
216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
// For button 1, if it was clicked inside the editor
|
218 |
// (posFromMouse returning non-null), we have to adjust the
|
219 |
// selection.
|
220 |
-
|
221 |
-
if (!start) {if (e.target() == scroller) e.stop(); return;}
|
222 |
|
223 |
if (!focused) onFocus();
|
224 |
-
e.stop();
|
225 |
-
if (ld && +new Date - ld < 400) return selectLine(start.line);
|
226 |
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
234 |
}
|
|
|
|
|
|
|
235 |
function extend(e) {
|
236 |
var cur = posFromMouse(e, true);
|
237 |
if (cur && !posEq(cur, last)) {
|
@@ -247,72 +354,97 @@ var CodeMirror = (function() {
|
|
247 |
|
248 |
var move = connect(targetDocument, "mousemove", operation(function(e) {
|
249 |
clearTimeout(going);
|
250 |
-
e
|
251 |
extend(e);
|
252 |
}), true);
|
253 |
var up = connect(targetDocument, "mouseup", operation(function(e) {
|
254 |
clearTimeout(going);
|
255 |
var cur = posFromMouse(e);
|
256 |
if (cur) setSelectionUser(start, cur);
|
257 |
-
e
|
258 |
-
|
|
|
|
|
259 |
}), true);
|
260 |
}
|
261 |
-
function
|
262 |
-
var
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
lastDoubleClick = +new Date;
|
|
|
|
|
267 |
}
|
268 |
function onDrop(e) {
|
269 |
-
|
|
|
270 |
if (!pos || options.readOnly) return;
|
271 |
if (files && files.length && window.FileReader && window.File) {
|
272 |
-
var n = files.length, text = Array(n), read = 0;
|
273 |
-
for (var i = 0; i < n; ++i) loadFile(files[i], i);
|
274 |
function loadFile(file, i) {
|
275 |
var reader = new FileReader;
|
276 |
reader.onload = function() {
|
277 |
text[i] = reader.result;
|
278 |
-
if (++read == n)
|
|
|
|
|
|
|
|
|
|
|
|
|
279 |
};
|
280 |
reader.readAsText(file);
|
281 |
}
|
|
|
|
|
282 |
}
|
283 |
else {
|
284 |
try {
|
285 |
-
var text = e.
|
286 |
-
if (text)
|
|
|
|
|
|
|
|
|
|
|
|
|
287 |
}
|
288 |
catch(e){}
|
289 |
}
|
290 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
function onKeyDown(e) {
|
292 |
if (!focused) onFocus();
|
293 |
|
294 |
-
var code = e.
|
295 |
// IE does strange things with escape.
|
296 |
-
if (ie && code == 27) { e.
|
297 |
// Tries to detect ctrl on non-mac, cmd on mac.
|
298 |
-
var mod = (mac ? e.
|
299 |
-
if (code == 16 || e.
|
300 |
else shiftSelecting = null;
|
301 |
// First give onKeyEvent option a chance to handle this.
|
302 |
-
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e
|
303 |
|
304 |
-
if (code == 33 || code == 34) {scrollPage(code == 34); return e
|
305 |
if (mod && ((code == 36 || code == 35) || // ctrl-home/end
|
306 |
mac && (code == 38 || code == 40))) { // cmd-up/down
|
307 |
-
scrollEnd(code == 36 || code == 38); return e
|
308 |
}
|
309 |
-
if (mod && code == 65) {selectAll(); return e
|
310 |
if (!options.readOnly) {
|
311 |
if (!anyMod && code == 13) {return;} // enter
|
312 |
-
if (!anyMod && code == 9 && handleTab(e.
|
313 |
-
if (mod && code == 90) {undo(); return e
|
314 |
-
if (mod && ((e.
|
315 |
}
|
|
|
316 |
|
317 |
// Key id to use in the movementKeys map. We also pass it to
|
318 |
// fastPoll in order to 'self learn'. We need this because
|
@@ -320,53 +452,64 @@ var CodeMirror = (function() {
|
|
320 |
// its start when it is inverted and a movement key is pressed
|
321 |
// (and later restore it again), shouldn't be used for
|
322 |
// non-movement keys.
|
323 |
-
curKeyId = (mod ? "c" : "") + code;
|
324 |
-
if (sel.inverted && movementKeys
|
325 |
var range = selRange(input);
|
326 |
if (range) {
|
327 |
reducedSelection = {anchor: range.start};
|
328 |
setSelRange(input, range.start, range.start);
|
329 |
}
|
330 |
}
|
|
|
|
|
331 |
fastPoll(curKeyId);
|
|
|
|
|
332 |
}
|
333 |
function onKeyUp(e) {
|
334 |
-
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e
|
335 |
if (reducedSelection) {
|
336 |
reducedSelection = null;
|
337 |
updateInput = true;
|
338 |
}
|
339 |
-
if (e.
|
|
|
|
|
340 |
}
|
341 |
function onKeyPress(e) {
|
342 |
-
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e
|
343 |
if (options.electricChars && mode.electricChars) {
|
344 |
-
var ch = String.fromCharCode(e.
|
345 |
if (mode.electricChars.indexOf(ch) > -1)
|
346 |
setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 50);
|
347 |
}
|
348 |
-
var code = e.
|
349 |
// Re-stop tab and enter. Necessary on some browsers.
|
350 |
-
if (code == 13) {if (!options.readOnly) handleEnter(); e
|
351 |
-
else if (!e.
|
352 |
else fastPoll(curKeyId);
|
353 |
}
|
354 |
|
355 |
function onFocus() {
|
356 |
if (options.readOnly == "nocursor") return;
|
357 |
-
if (!focused
|
358 |
-
|
|
|
|
|
|
|
|
|
|
|
359 |
slowPoll();
|
360 |
-
if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
|
361 |
-
wrapper.className += " CodeMirror-focused";
|
362 |
restartBlink();
|
363 |
}
|
364 |
function onBlur() {
|
365 |
-
if (focused
|
|
|
|
|
|
|
|
|
366 |
clearInterval(blinker);
|
367 |
-
shiftSelecting = null;
|
368 |
-
focused = false;
|
369 |
-
wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
|
370 |
}
|
371 |
|
372 |
// Replace the range from from to to by the strings in newText.
|
@@ -374,23 +517,22 @@ var CodeMirror = (function() {
|
|
374 |
function updateLines(from, to, newText, selFrom, selTo) {
|
375 |
if (history) {
|
376 |
var old = [];
|
377 |
-
|
378 |
history.addChange(from.line, newText.length, old);
|
379 |
while (history.done.length > options.undoDepth) history.done.shift();
|
380 |
}
|
381 |
updateLinesNoUndo(from, to, newText, selFrom, selTo);
|
382 |
-
if (newText.length < 5)
|
383 |
-
highlightLines(from.line, from.line + newText.length)
|
384 |
}
|
385 |
function unredoHelper(from, to) {
|
386 |
var change = from.pop();
|
387 |
if (change) {
|
388 |
var replaced = [], end = change.start + change.added;
|
389 |
-
|
390 |
to.push({start: change.start, added: change.old.length, old: replaced});
|
391 |
var pos = clipPos({line: change.start + change.old.length - 1,
|
392 |
ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
|
393 |
-
updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch:
|
|
|
394 |
}
|
395 |
}
|
396 |
function undo() {unredoHelper(history.done, history.undone);}
|
@@ -398,51 +540,66 @@ var CodeMirror = (function() {
|
|
398 |
|
399 |
function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
|
400 |
var recomputeMaxLength = false, maxLineLength = maxLine.length;
|
401 |
-
|
402 |
-
|
403 |
-
|
|
|
404 |
|
405 |
-
var nlines = to.line - from.line, firstLine =
|
406 |
// First adjust the line structure, taking some care to leave highlighting intact.
|
407 |
if (firstLine == lastLine) {
|
408 |
if (newText.length == 1)
|
409 |
firstLine.replace(from.ch, to.ch, newText[0]);
|
410 |
else {
|
411 |
lastLine = firstLine.split(to.ch, newText[newText.length-1]);
|
412 |
-
|
413 |
-
firstLine.
|
414 |
-
|
415 |
-
|
416 |
-
|
|
|
|
|
417 |
}
|
418 |
}
|
419 |
else if (newText.length == 1) {
|
420 |
-
firstLine.replace(from.ch,
|
421 |
-
|
|
|
|
|
422 |
}
|
423 |
else {
|
424 |
-
var
|
425 |
-
firstLine.replace(from.ch,
|
426 |
-
lastLine.replace(
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
var l =
|
443 |
if (l.length > maxLineLength) {
|
444 |
-
maxLineLength = l.length;
|
|
|
445 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
446 |
}
|
447 |
}
|
448 |
|
@@ -454,7 +611,9 @@ var CodeMirror = (function() {
|
|
454 |
if (task < from.line) newWork.push(task);
|
455 |
else if (task > to.line) newWork.push(task + lendiff);
|
456 |
}
|
457 |
-
|
|
|
|
|
458 |
work = newWork;
|
459 |
startWorker(100);
|
460 |
// Remember that these lines changed, for updating the display
|
@@ -466,7 +625,7 @@ var CodeMirror = (function() {
|
|
466 |
setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
|
467 |
|
468 |
// Make sure the scroll-size div has the correct height.
|
469 |
-
code.style.height = (
|
470 |
}
|
471 |
|
472 |
function replaceRange(code, from, to) {
|
@@ -504,10 +663,10 @@ var CodeMirror = (function() {
|
|
504 |
|
505 |
function getRange(from, to) {
|
506 |
var l1 = from.line, l2 = to.line;
|
507 |
-
if (l1 == l2) return
|
508 |
-
var code = [
|
509 |
-
|
510 |
-
code.push(
|
511 |
return code.join("\n");
|
512 |
}
|
513 |
function getSelection() {
|
@@ -517,7 +676,7 @@ var CodeMirror = (function() {
|
|
517 |
var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
|
518 |
function slowPoll() {
|
519 |
if (pollingFast) return;
|
520 |
-
poll.set(
|
521 |
startOperation();
|
522 |
readInput();
|
523 |
if (focused) slowPoll();
|
@@ -530,7 +689,10 @@ var CodeMirror = (function() {
|
|
530 |
function p() {
|
531 |
startOperation();
|
532 |
var changed = readInput();
|
533 |
-
if (changed
|
|
|
|
|
|
|
534 |
if (!changed && !missed) {missed = true; poll.set(80, p);}
|
535 |
else {pollingFast = false; slowPoll();}
|
536 |
endOperation();
|
@@ -542,7 +704,7 @@ var CodeMirror = (function() {
|
|
542 |
// to the data in the editing variable, and updates the editor
|
543 |
// content or cursor if something changed.
|
544 |
function readInput() {
|
545 |
-
if (leaveInputAlone) return;
|
546 |
var changed = false, text = input.value, sr = selRange(input);
|
547 |
if (!sr) return false;
|
548 |
var changed = editing.text != text, rs = reducedSelection;
|
@@ -612,14 +774,16 @@ var CodeMirror = (function() {
|
|
612 |
// editor state.
|
613 |
function prepareInput() {
|
614 |
var text = [];
|
615 |
-
var from = Math.max(0, sel.from.line - 1), to = Math.min(
|
616 |
-
|
617 |
text = input.value = text.join(lineSep);
|
618 |
var startch = sel.from.ch, endch = sel.to.ch;
|
619 |
-
|
620 |
-
startch += lineSep.length +
|
621 |
-
|
622 |
-
|
|
|
|
|
623 |
editing = {text: text, from: from, to: to, start: startch, end: endch};
|
624 |
setSelRange(input, startch, reducedSelection ? startch : endch);
|
625 |
}
|
@@ -627,21 +791,29 @@ var CodeMirror = (function() {
|
|
627 |
if (options.readOnly != "nocursor") input.focus();
|
628 |
}
|
629 |
|
|
|
|
|
|
|
|
|
|
|
|
|
630 |
function scrollCursorIntoView() {
|
631 |
var cursor = localCoords(sel.inverted ? sel.from : sel.to);
|
632 |
-
|
|
|
633 |
}
|
634 |
function scrollIntoView(x1, y1, x2, y2) {
|
635 |
-
var pl = paddingLeft(), pt = paddingTop(), lh =
|
636 |
y1 += pt; y2 += pt; x1 += pl; x2 += pl;
|
637 |
var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
|
638 |
if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
|
639 |
else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
|
640 |
|
641 |
var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
|
642 |
-
|
|
|
643 |
if (x1 < 50) x1 = 0;
|
644 |
-
scroller.scrollLeft = Math.max(0, x1 - 10);
|
645 |
scrolled = true;
|
646 |
}
|
647 |
else if (x2 > screenw + screenleft) {
|
@@ -654,32 +826,103 @@ var CodeMirror = (function() {
|
|
654 |
}
|
655 |
|
656 |
function visibleLines() {
|
657 |
-
var lh =
|
658 |
-
|
659 |
-
|
|
|
|
|
660 |
}
|
661 |
// Uses a set of changes plus the current scroll position to
|
662 |
// determine which DOM updates have to be made, and makes the
|
663 |
// updates.
|
664 |
function updateDisplay(changes) {
|
665 |
if (!scroller.clientWidth) {
|
666 |
-
showingFrom = showingTo = 0;
|
667 |
return;
|
668 |
}
|
669 |
-
//
|
670 |
-
|
671 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
672 |
for (var i = 0, l = changes.length || 0; i < l; ++i) {
|
673 |
var change = changes[i], intact2 = [], diff = change.diff || 0;
|
674 |
for (var j = 0, l2 = intact.length; j < l2; ++j) {
|
675 |
var range = intact[j];
|
676 |
-
if (change.to <= range.from)
|
677 |
-
intact2.push({from: range.from + diff, to: range.to + diff,
|
678 |
-
|
|
|
679 |
intact2.push(range);
|
680 |
else {
|
681 |
if (change.from > range.from)
|
682 |
-
intact2.push({from: range.from, to: change.from, domStart: range.domStart})
|
683 |
if (change.to < range.to)
|
684 |
intact2.push({from: change.to + diff, to: range.to + diff,
|
685 |
domStart: range.domStart + (change.to - range.from)});
|
@@ -687,151 +930,90 @@ var CodeMirror = (function() {
|
|
687 |
}
|
688 |
intact = intact2;
|
689 |
}
|
|
|
|
|
690 |
|
691 |
-
|
692 |
-
//
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
var range = intact[i];
|
700 |
-
if (range.to <= from) continue;
|
701 |
-
if (range.from >= to) break;
|
702 |
-
if (range.domStart > domPos || range.from > pos) {
|
703 |
-
updates.push({from: pos, to: range.from, domSize: range.domStart - domPos, domStart: domPos});
|
704 |
-
changedLines += range.from - pos;
|
705 |
}
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
if (!updates.length) return;
|
715 |
-
lineDiv.style.display = "none";
|
716 |
-
// If more than 30% of the screen needs update, just do a full
|
717 |
-
// redraw (which is quicker than patching)
|
718 |
-
if (changedLines > (visible.to - visible.from) * .3)
|
719 |
-
refreshDisplay(from = Math.max(visible.from - 10, 0), to = Math.min(visible.to + 7, lines.length));
|
720 |
-
// Otherwise, only update the stuff that needs updating.
|
721 |
-
else
|
722 |
-
patchDisplay(updates);
|
723 |
-
lineDiv.style.display = "";
|
724 |
-
|
725 |
-
// Position the mover div to align with the lines it's supposed
|
726 |
-
// to be showing (which will cover the visible display)
|
727 |
-
var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight;
|
728 |
-
showingFrom = from; showingTo = to;
|
729 |
-
mover.style.top = (from * lineHeight()) + "px";
|
730 |
-
if (different) {
|
731 |
-
lastHeight = scroller.clientHeight;
|
732 |
-
code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
|
733 |
-
updateGutter();
|
734 |
}
|
735 |
-
|
736 |
-
var
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
// only assertion in the whole file.
|
741 |
-
if (lineDiv.childNodes.length != showingTo - showingFrom)
|
742 |
-
throw new Error("BAD PATCH! " + JSON.stringify(updates) + " size=" + (showingTo - showingFrom) +
|
743 |
-
" nodes=" + lineDiv.childNodes.length);
|
744 |
-
updateCursor();
|
745 |
-
}
|
746 |
-
|
747 |
-
function refreshDisplay(from, to) {
|
748 |
-
var html = [], start = {line: from, ch: 0}, inSel = posLess(sel.from, start) && !posLess(sel.to, start);
|
749 |
-
for (var i = from; i < to; ++i) {
|
750 |
var ch1 = null, ch2 = null;
|
751 |
if (inSel) {
|
752 |
ch1 = 0;
|
753 |
-
if (
|
754 |
-
}
|
755 |
-
|
756 |
-
if (sel.to.line == i) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
|
757 |
else {inSel = true; ch1 = sel.from.ch;}
|
758 |
}
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
// whitespace.
|
767 |
-
var sfrom = sel.from.line, sto = sel.to.line, off = 0,
|
768 |
-
scratch = badInnerHTML && targetDocument.createElement("div");
|
769 |
-
for (var i = 0, e = updates.length; i < e; ++i) {
|
770 |
-
var rec = updates[i];
|
771 |
-
var extra = (rec.to - rec.from) - rec.domSize;
|
772 |
-
var nodeAfter = lineDiv.childNodes[rec.domStart + rec.domSize + off] || null;
|
773 |
-
if (badInnerHTML)
|
774 |
-
for (var j = Math.max(-extra, rec.domSize); j > 0; --j)
|
775 |
-
lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
|
776 |
-
else if (extra) {
|
777 |
-
for (var j = Math.max(0, extra); j > 0; --j)
|
778 |
-
lineDiv.insertBefore(targetDocument.createElement("pre"), nodeAfter);
|
779 |
-
for (var j = Math.max(0, -extra); j > 0; --j)
|
780 |
-
lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
|
781 |
-
}
|
782 |
-
var node = lineDiv.childNodes[rec.domStart + off], inSel = sfrom < rec.from && sto >= rec.from;
|
783 |
-
for (var j = rec.from; j < rec.to; ++j) {
|
784 |
-
var ch1 = null, ch2 = null;
|
785 |
-
if (inSel) {
|
786 |
-
ch1 = 0;
|
787 |
-
if (sto == j) {inSel = false; ch2 = sel.to.ch;}
|
788 |
-
}
|
789 |
-
else if (sfrom == j) {
|
790 |
-
if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
|
791 |
-
else {inSel = true; ch1 = sel.from.ch;}
|
792 |
-
}
|
793 |
-
if (badInnerHTML) {
|
794 |
-
scratch.innerHTML = lines[j].getHTML(ch1, ch2, true);
|
795 |
-
lineDiv.insertBefore(scratch.firstChild, nodeAfter);
|
796 |
-
}
|
797 |
-
else {
|
798 |
-
node.innerHTML = lines[j].getHTML(ch1, ch2, false);
|
799 |
-
node.className = lines[j].className || "";
|
800 |
-
node = node.nextSibling;
|
801 |
-
}
|
802 |
}
|
803 |
-
|
804 |
-
}
|
805 |
}
|
806 |
|
807 |
function updateGutter() {
|
808 |
if (!options.gutter && !options.lineNumbers) return;
|
809 |
var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
|
810 |
gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
|
811 |
-
var html = [];
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
821 |
gutter.style.display = "none";
|
822 |
gutterText.innerHTML = html.join("");
|
823 |
-
var minwidth = String(
|
824 |
while (val.length + pad.length < minwidth) pad += "\u00a0";
|
825 |
if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
|
826 |
gutter.style.display = "";
|
827 |
lineSpace.style.marginLeft = gutter.offsetWidth + "px";
|
|
|
828 |
}
|
829 |
function updateCursor() {
|
830 |
-
var head = sel.inverted ? sel.from : sel.to, lh =
|
831 |
-
var
|
832 |
-
|
|
|
|
|
833 |
if (posEq(sel.from, sel.to)) {
|
834 |
-
cursor.style.top = y
|
|
|
835 |
cursor.style.display = "";
|
836 |
}
|
837 |
else cursor.style.display = "none";
|
@@ -849,9 +1031,14 @@ var CodeMirror = (function() {
|
|
849 |
// updateLines, since they have to be expressed in the line
|
850 |
// numbers before the update.
|
851 |
function setSelection(from, to, oldFrom, oldTo) {
|
|
|
852 |
if (posEq(sel.from, from) && posEq(sel.to, to)) return;
|
853 |
if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
|
854 |
|
|
|
|
|
|
|
|
|
855 |
if (posEq(from, to)) sel.inverted = false;
|
856 |
else if (posEq(from, sel.to)) sel.inverted = false;
|
857 |
else if (posEq(to, sel.from)) sel.inverted = true;
|
@@ -859,7 +1046,6 @@ var CodeMirror = (function() {
|
|
859 |
// Some ugly logic used to only mark the lines that actually did
|
860 |
// see a change in selection as changed, rather than the whole
|
861 |
// selected range.
|
862 |
-
if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
|
863 |
if (posEq(from, to)) {
|
864 |
if (!posEq(sel.from, sel.to))
|
865 |
changes.push({from: oldFrom, to: oldTo + 1});
|
@@ -884,42 +1070,61 @@ var CodeMirror = (function() {
|
|
884 |
sel.from = from; sel.to = to;
|
885 |
selectionChanged = true;
|
886 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
887 |
function setCursor(line, ch, user) {
|
888 |
var pos = clipPos({line: line, ch: ch || 0});
|
889 |
(user ? setSelectionUser : setSelection)(pos, pos);
|
890 |
}
|
891 |
|
892 |
-
function clipLine(n) {return Math.max(0, Math.min(n,
|
893 |
function clipPos(pos) {
|
894 |
if (pos.line < 0) return {line: 0, ch: 0};
|
895 |
-
if (pos.line >=
|
896 |
-
var ch = pos.ch, linelen =
|
897 |
if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
|
898 |
else if (ch < 0) return {line: pos.line, ch: 0};
|
899 |
else return pos;
|
900 |
}
|
901 |
|
902 |
function scrollPage(down) {
|
903 |
-
var linesPerPage = Math.floor(scroller.clientHeight /
|
904 |
-
|
|
|
905 |
}
|
906 |
function scrollEnd(top) {
|
907 |
-
var pos = top ? {line: 0, ch: 0} : {line:
|
908 |
setSelectionUser(pos, pos);
|
909 |
}
|
910 |
function selectAll() {
|
911 |
-
var endLine =
|
912 |
-
setSelection({line: 0, ch: 0}, {line: endLine, ch:
|
913 |
}
|
914 |
function selectWordAt(pos) {
|
915 |
-
var line =
|
916 |
var start = pos.ch, end = pos.ch;
|
917 |
while (start > 0 && /\w/.test(line.charAt(start - 1))) --start;
|
918 |
while (end < line.length && /\w/.test(line.charAt(end))) ++end;
|
919 |
setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
|
920 |
}
|
921 |
function selectLine(line) {
|
922 |
-
setSelectionUser({line: line, ch: 0}, {line: line, ch:
|
923 |
}
|
924 |
function handleEnter() {
|
925 |
replaceSelection("\n", "end");
|
@@ -927,12 +1132,17 @@ var CodeMirror = (function() {
|
|
927 |
indentLine(sel.from.line, options.enterMode == "keep" ? "prev" : "smart");
|
928 |
}
|
929 |
function handleTab(shift) {
|
|
|
|
|
|
|
|
|
|
|
930 |
shiftSelecting = null;
|
931 |
switch (options.tabMode) {
|
932 |
case "default":
|
933 |
return false;
|
934 |
case "indent":
|
935 |
-
|
936 |
break;
|
937 |
case "classic":
|
938 |
if (posEq(sel.from, sel.to)) {
|
@@ -941,11 +1151,15 @@ var CodeMirror = (function() {
|
|
941 |
break;
|
942 |
}
|
943 |
case "shift":
|
944 |
-
|
945 |
break;
|
946 |
}
|
947 |
return true;
|
948 |
}
|
|
|
|
|
|
|
|
|
949 |
|
950 |
function indentLine(n, how) {
|
951 |
if (how == "smart") {
|
@@ -953,9 +1167,9 @@ var CodeMirror = (function() {
|
|
953 |
else var state = getStateBefore(n);
|
954 |
}
|
955 |
|
956 |
-
var line =
|
957 |
if (how == "prev") {
|
958 |
-
if (n) indentation =
|
959 |
else indentation = 0;
|
960 |
}
|
961 |
else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length));
|
@@ -980,87 +1194,148 @@ var CodeMirror = (function() {
|
|
980 |
|
981 |
function loadMode() {
|
982 |
mode = CodeMirror.getMode(options, options.mode);
|
983 |
-
|
984 |
-
lines[i].stateAfter = null;
|
985 |
work = [0];
|
986 |
startWorker();
|
987 |
}
|
988 |
function gutterChanged() {
|
989 |
var visible = options.gutter || options.lineNumbers;
|
990 |
gutter.style.display = visible ? "" : "none";
|
991 |
-
if (visible)
|
992 |
else lineDiv.parentNode.style.marginLeft = 0;
|
993 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
994 |
|
995 |
function markText(from, to, className) {
|
996 |
from = clipPos(from); to = clipPos(to);
|
997 |
-
var
|
998 |
function add(line, from, to, className) {
|
999 |
-
|
1000 |
-
mark.line = line;
|
1001 |
-
accum.push(mark);
|
1002 |
}
|
1003 |
if (from.line == to.line) add(from.line, from.ch, to.ch, className);
|
1004 |
else {
|
1005 |
add(from.line, from.ch, null, className);
|
1006 |
for (var i = from.line + 1, e = to.line; i < e; ++i)
|
1007 |
-
add(i,
|
1008 |
-
add(to.line,
|
1009 |
}
|
1010 |
changes.push({from: from.line, to: to.line + 1});
|
1011 |
-
return
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
|
1017 |
-
|
1018 |
-
|
1019 |
-
}
|
1020 |
-
}
|
1021 |
-
if (start != null) changes.push({from: start, to: end + 1});
|
1022 |
-
};
|
1023 |
}
|
1024 |
|
1025 |
function addGutterMarker(line, text, className) {
|
1026 |
-
if (typeof line == "number") line =
|
1027 |
line.gutterMarker = {text: text, style: className};
|
1028 |
-
|
1029 |
return line;
|
1030 |
}
|
1031 |
function removeGutterMarker(line) {
|
1032 |
-
if (typeof line == "number") line =
|
1033 |
line.gutterMarker = null;
|
1034 |
-
|
1035 |
}
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
if (no == -1) return null;
|
1044 |
-
}
|
1045 |
-
if (line.className != className) {
|
1046 |
-
line.className = className;
|
1047 |
-
changes.push({from: no, to: no + 1});
|
1048 |
-
}
|
1049 |
return line;
|
1050 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1051 |
|
1052 |
function lineInfo(line) {
|
1053 |
if (typeof line == "number") {
|
|
|
1054 |
var n = line;
|
1055 |
-
line =
|
1056 |
if (!line) return null;
|
1057 |
}
|
1058 |
else {
|
1059 |
-
var n =
|
1060 |
-
if (n ==
|
1061 |
}
|
1062 |
var marker = line.gutterMarker;
|
1063 |
-
return {line: n, text: line.text, markerText: marker && marker.text,
|
|
|
1064 |
}
|
1065 |
|
1066 |
function stringWidth(str) {
|
@@ -1070,21 +1345,16 @@ var CodeMirror = (function() {
|
|
1070 |
}
|
1071 |
// These are used to go from pixel positions to character
|
1072 |
// positions, taking varying character widths into account.
|
1073 |
-
function charX(line, pos) {
|
1074 |
-
if (pos == 0) return 0;
|
1075 |
-
measure.innerHTML = "<pre><span>" + lines[line].getHTML(null, null, false, pos) + "</span></pre>";
|
1076 |
-
return measure.firstChild.firstChild.offsetWidth;
|
1077 |
-
}
|
1078 |
function charFromX(line, x) {
|
1079 |
if (x <= 0) return 0;
|
1080 |
-
var lineObj =
|
1081 |
function getX(len) {
|
1082 |
measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, len) + "</span></pre>";
|
1083 |
return measure.firstChild.firstChild.offsetWidth;
|
1084 |
}
|
1085 |
var from = 0, fromX = 0, to = text.length, toX;
|
1086 |
// Guess a suitable upper bound for our search.
|
1087 |
-
var estimated = Math.min(to, Math.ceil(x /
|
1088 |
for (;;) {
|
1089 |
var estX = getX(estimated);
|
1090 |
if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
|
@@ -1103,59 +1373,136 @@ var CodeMirror = (function() {
|
|
1103 |
}
|
1104 |
}
|
1105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1106 |
function localCoords(pos, inLineWrap) {
|
1107 |
-
var lh =
|
1108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1109 |
}
|
1110 |
function pageCoords(pos) {
|
1111 |
var local = localCoords(pos, true), off = eltOffset(lineSpace);
|
1112 |
return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
|
1113 |
}
|
1114 |
|
1115 |
-
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1120 |
}
|
1121 |
function paddingTop() {return lineSpace.offsetTop;}
|
1122 |
function paddingLeft() {return lineSpace.offsetLeft;}
|
1123 |
|
1124 |
function posFromMouse(e, liberal) {
|
1125 |
-
var offW = eltOffset(scroller, true), x
|
|
|
|
|
1126 |
// This is a mess of a heuristic to try and determine whether a
|
1127 |
// scroll-bar was clicked or not, and to return null if one was
|
1128 |
// (and !liberal).
|
1129 |
if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
|
1130 |
return null;
|
1131 |
var offL = eltOffset(lineSpace, true);
|
1132 |
-
|
1133 |
-
return clipPos({line: line, ch: charFromX(clipLine(line), x - offL.left)});
|
1134 |
}
|
1135 |
function onContextMenu(e) {
|
1136 |
var pos = posFromMouse(e);
|
1137 |
if (!pos || window.opera) return; // Opera is difficult.
|
1138 |
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
|
1139 |
-
setCursor(pos.line, pos.ch);
|
1140 |
|
1141 |
var oldCSS = input.style.cssText;
|
1142 |
-
|
1143 |
-
|
1144 |
-
"
|
|
|
|
|
1145 |
var val = input.value = getSelection();
|
1146 |
focusInput();
|
1147 |
setSelRange(input, 0, input.value.length);
|
1148 |
-
leaveInputAlone = true;
|
1149 |
function rehide() {
|
1150 |
-
|
|
|
|
|
1151 |
input.style.cssText = oldCSS;
|
1152 |
leaveInputAlone = false;
|
1153 |
prepareInput();
|
1154 |
slowPoll();
|
1155 |
}
|
1156 |
-
|
1157 |
if (gecko) {
|
1158 |
-
e
|
1159 |
var mouseup = connect(window, "mouseup", function() {
|
1160 |
mouseup();
|
1161 |
setTimeout(rehide, 20);
|
@@ -1178,7 +1525,7 @@ var CodeMirror = (function() {
|
|
1178 |
|
1179 |
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
|
1180 |
function matchBrackets(autoclear) {
|
1181 |
-
var head = sel.inverted ? sel.from : sel.to, line =
|
1182 |
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
|
1183 |
if (!match) return;
|
1184 |
var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
|
@@ -1202,19 +1549,18 @@ var CodeMirror = (function() {
|
|
1202 |
}
|
1203 |
}
|
1204 |
}
|
1205 |
-
for (var i = head.line, e = forward ? Math.min(i +
|
1206 |
-
var line =
|
1207 |
var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
|
1208 |
-
if (found)
|
1209 |
-
var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
1210 |
-
var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
|
1211 |
-
two = markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
|
1212 |
-
var clear = operation(function(){one(); two();});
|
1213 |
-
if (autoclear) setTimeout(clear, 800);
|
1214 |
-
else bracketHighlighted = clear;
|
1215 |
-
break;
|
1216 |
-
}
|
1217 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1218 |
}
|
1219 |
|
1220 |
// Finds the line to start with when starting a parse. Tries to
|
@@ -1226,62 +1572,69 @@ var CodeMirror = (function() {
|
|
1226 |
var minindent, minline;
|
1227 |
for (var search = n, lim = n - 40; search > lim; --search) {
|
1228 |
if (search == 0) return 0;
|
1229 |
-
var line =
|
1230 |
if (line.stateAfter) return search;
|
1231 |
var indented = line.indentation();
|
1232 |
if (minline == null || minindent > indented) {
|
1233 |
-
minline = search;
|
1234 |
minindent = indented;
|
1235 |
}
|
1236 |
}
|
1237 |
return minline;
|
1238 |
}
|
1239 |
function getStateBefore(n) {
|
1240 |
-
var start = findStartLine(n), state = start &&
|
1241 |
if (!state) state = startState(mode);
|
1242 |
else state = copyState(mode, state);
|
1243 |
-
|
1244 |
-
var line = lines[i];
|
1245 |
line.highlight(mode, state);
|
1246 |
line.stateAfter = copyState(mode, state);
|
1247 |
-
}
|
1248 |
-
if (
|
|
|
1249 |
return state;
|
1250 |
}
|
1251 |
function highlightLines(start, end) {
|
1252 |
var state = getStateBefore(start);
|
1253 |
-
|
1254 |
-
var line = lines[i];
|
1255 |
line.highlight(mode, state);
|
1256 |
line.stateAfter = copyState(mode, state);
|
1257 |
-
}
|
1258 |
}
|
1259 |
function highlightWorker() {
|
1260 |
var end = +new Date + options.workTime;
|
1261 |
var foundWork = work.length;
|
1262 |
while (work.length) {
|
1263 |
-
if (!
|
1264 |
else var task = work.pop();
|
1265 |
-
if (task >=
|
1266 |
-
var start = findStartLine(task), state = start &&
|
1267 |
if (state) state = copyState(mode, state);
|
1268 |
else state = startState(mode);
|
1269 |
|
1270 |
-
var unchanged = 0
|
1271 |
-
|
1272 |
-
|
|
|
1273 |
if (+new Date > end) {
|
1274 |
work.push(i);
|
1275 |
startWorker(options.workDelay);
|
1276 |
-
changes.push({from: task, to: i});
|
1277 |
-
return;
|
1278 |
}
|
1279 |
var changed = line.highlight(mode, state);
|
|
|
1280 |
line.stateAfter = copyState(mode, state);
|
1281 |
-
if (
|
1282 |
-
|
1283 |
-
|
1284 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1285 |
}
|
1286 |
if (foundWork && options.onHighlightComplete)
|
1287 |
options.onHighlightComplete(instance);
|
@@ -1302,13 +1655,17 @@ var CodeMirror = (function() {
|
|
1302 |
var reScroll = false;
|
1303 |
if (selectionChanged) reScroll = !scrollCursorIntoView();
|
1304 |
if (changes.length) updateDisplay(changes);
|
1305 |
-
else
|
|
|
|
|
|
|
1306 |
if (reScroll) scrollCursorIntoView();
|
1307 |
-
if (selectionChanged) restartBlink();
|
1308 |
|
1309 |
// updateInput can be set to a boolean value to force/prevent an
|
1310 |
// update.
|
1311 |
-
if (
|
|
|
1312 |
prepareInput();
|
1313 |
|
1314 |
if (selectionChanged && options.matchBrackets)
|
@@ -1347,7 +1704,7 @@ var CodeMirror = (function() {
|
|
1347 |
if (typeof query != "string") // Regexp match
|
1348 |
this.matches = function(reverse, pos) {
|
1349 |
if (reverse) {
|
1350 |
-
var line =
|
1351 |
while (match) {
|
1352 |
var ind = line.indexOf(match[0]);
|
1353 |
start += ind;
|
@@ -1359,7 +1716,7 @@ var CodeMirror = (function() {
|
|
1359 |
}
|
1360 |
}
|
1361 |
else {
|
1362 |
-
var line =
|
1363 |
start = match && pos.ch + line.indexOf(match[0]);
|
1364 |
}
|
1365 |
if (match)
|
@@ -1374,7 +1731,7 @@ var CodeMirror = (function() {
|
|
1374 |
// Different methods for single-line and multi-line queries
|
1375 |
if (target.length == 1)
|
1376 |
this.matches = function(reverse, pos) {
|
1377 |
-
var line = fold(
|
1378 |
if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
|
1379 |
: (match = line.indexOf(query, pos.ch)) != -1)
|
1380 |
return {from: {line: pos.line, ch: match},
|
@@ -1382,14 +1739,14 @@ var CodeMirror = (function() {
|
|
1382 |
};
|
1383 |
else
|
1384 |
this.matches = function(reverse, pos) {
|
1385 |
-
var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(
|
1386 |
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
|
1387 |
if (reverse ? offsetA >= pos.ch || offsetA != match.length
|
1388 |
: offsetA <= pos.ch || offsetA != line.length - match.length)
|
1389 |
return;
|
1390 |
for (;;) {
|
1391 |
-
if (reverse ? !ln : ln ==
|
1392 |
-
line = fold(
|
1393 |
match = target[reverse ? --idx : ++idx];
|
1394 |
if (idx > 0 && idx < target.length - 1) {
|
1395 |
if (line != match) return;
|
@@ -1425,17 +1782,25 @@ var CodeMirror = (function() {
|
|
1425 |
}
|
1426 |
if (reverse) {
|
1427 |
if (!pos.line) return savePosAndFail(0);
|
1428 |
-
pos = {line: pos.line-1, ch:
|
1429 |
}
|
1430 |
else {
|
1431 |
-
if (pos.line ==
|
1432 |
pos = {line: pos.line+1, ch: 0};
|
1433 |
}
|
1434 |
}
|
1435 |
},
|
1436 |
|
1437 |
from: function() {if (this.atOccurrence) return copyPos(this.pos.from);},
|
1438 |
-
to: function() {if (this.atOccurrence) return copyPos(this.pos.to);}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1439 |
};
|
1440 |
|
1441 |
for (var ext in extensions)
|
@@ -1456,10 +1821,13 @@ var CodeMirror = (function() {
|
|
1456 |
enterMode: "indent",
|
1457 |
electricChars: true,
|
1458 |
onKeyEvent: null,
|
|
|
1459 |
lineNumbers: false,
|
1460 |
gutter: false,
|
|
|
1461 |
firstLineNumber: 1,
|
1462 |
readOnly: false,
|
|
|
1463 |
onChange: null,
|
1464 |
onCursorActivity: null,
|
1465 |
onGutterClick: null,
|
@@ -1470,6 +1838,7 @@ var CodeMirror = (function() {
|
|
1470 |
workDelay: 200,
|
1471 |
undoDepth: 40,
|
1472 |
tabindex: null,
|
|
|
1473 |
document: window.document
|
1474 |
};
|
1475 |
|
@@ -1495,7 +1864,7 @@ var CodeMirror = (function() {
|
|
1495 |
return CodeMirror.getMode(options, "text/plain");
|
1496 |
}
|
1497 |
return mfactory(options, config || {});
|
1498 |
-
}
|
1499 |
CodeMirror.listModes = function() {
|
1500 |
var list = [];
|
1501 |
for (var m in modes)
|
@@ -1505,7 +1874,7 @@ var CodeMirror = (function() {
|
|
1505 |
CodeMirror.listMIMEs = function() {
|
1506 |
var list = [];
|
1507 |
for (var m in mimeModes)
|
1508 |
-
if (mimeModes.propertyIsEnumerable(m)) list.push(m);
|
1509 |
return list;
|
1510 |
};
|
1511 |
|
@@ -1567,11 +1936,11 @@ var CodeMirror = (function() {
|
|
1567 |
}
|
1568 |
return nstate;
|
1569 |
}
|
1570 |
-
CodeMirror.
|
1571 |
function startState(mode, a1, a2) {
|
1572 |
return mode.startState ? mode.startState(a1, a2) : true;
|
1573 |
}
|
1574 |
-
CodeMirror.
|
1575 |
|
1576 |
// The character stream used by a mode's parser.
|
1577 |
function StringStream(string) {
|
@@ -1593,7 +1962,7 @@ var CodeMirror = (function() {
|
|
1593 |
if (ok) {++this.pos; return ch;}
|
1594 |
},
|
1595 |
eatWhile: function(match) {
|
1596 |
-
var start = this.
|
1597 |
while (this.eat(match)){}
|
1598 |
return this.pos > start;
|
1599 |
},
|
@@ -1628,18 +1997,98 @@ var CodeMirror = (function() {
|
|
1628 |
};
|
1629 |
CodeMirror.StringStream = StringStream;
|
1630 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1631 |
// Line objects. These hold state related to a line, including
|
1632 |
// highlighting info (the styles array).
|
1633 |
function Line(text, styles) {
|
1634 |
this.styles = styles || [text, null];
|
1635 |
-
this.stateAfter = null;
|
1636 |
this.text = text;
|
|
|
1637 |
this.marked = this.gutterMarker = this.className = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1638 |
}
|
1639 |
Line.prototype = {
|
1640 |
// Replace a piece of a line, keeping the styles around it intact.
|
1641 |
-
replace: function(from,
|
1642 |
-
|
|
|
|
|
|
|
1643 |
copyStyles(0, from, this.styles, st);
|
1644 |
if (text) st.push(text, null);
|
1645 |
copyStyles(to, this.text.length, this.styles, st);
|
@@ -1647,34 +2096,79 @@ var CodeMirror = (function() {
|
|
1647 |
this.text = this.text.slice(0, from) + text + this.text.slice(to);
|
1648 |
this.stateAfter = null;
|
1649 |
if (mk) {
|
1650 |
-
var diff = text.length - (to - from)
|
1651 |
-
|
1652 |
-
|
1653 |
-
|
1654 |
-
if (mark.from >= end) del = true;
|
1655 |
-
else {mark.from = fix(mark.from); if (mark.to != null) mark.to = fix(mark.to);}
|
1656 |
-
if (del || mark.from >= mark.to) {mk.splice(i, 1); i--;}
|
1657 |
}
|
1658 |
}
|
1659 |
},
|
1660 |
-
// Split a
|
1661 |
split: function(pos, textBefore) {
|
1662 |
-
var st = [textBefore, null];
|
1663 |
copyStyles(pos, this.text.length, this.styles, st);
|
1664 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1665 |
},
|
1666 |
-
|
1667 |
-
var
|
1668 |
-
|
1669 |
-
this.
|
1670 |
-
|
1671 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1672 |
},
|
1673 |
-
|
1674 |
-
var mk = this.marked;
|
1675 |
if (!mk) return;
|
1676 |
-
for (var i = 0; i < mk.length; ++i)
|
1677 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1678 |
},
|
1679 |
// Run the given mode's parser over a line, update the styles
|
1680 |
// array, which contains alternating fragments of text and CSS
|
@@ -1702,10 +2196,10 @@ var CodeMirror = (function() {
|
|
1702 |
}
|
1703 |
if (st.length != pos) {st.length = pos; changed = true;}
|
1704 |
if (pos && st[pos-2] != prevWord) changed = true;
|
1705 |
-
// Short lines with simple highlights
|
1706 |
-
//
|
1707 |
-
// contexts.
|
1708 |
-
return changed || (st.length < 5 && this.text.length < 10);
|
1709 |
},
|
1710 |
// Fetch the parser token for a given character. Useful for hacks
|
1711 |
// that want to inspect the mode state (say, for completion).
|
@@ -1725,12 +2219,15 @@ var CodeMirror = (function() {
|
|
1725 |
// Produces an HTML fragment for the line, taking selection,
|
1726 |
// marking, and highlighting into account.
|
1727 |
getHTML: function(sfrom, sto, includePre, endAt) {
|
1728 |
-
var html = [];
|
1729 |
if (includePre)
|
1730 |
html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
|
1731 |
function span(text, style) {
|
1732 |
if (!text) return;
|
1733 |
-
|
|
|
|
|
|
|
1734 |
else html.push(htmlEscape(text));
|
1735 |
}
|
1736 |
var st = this.styles, allText = this.text, marked = this.marked;
|
@@ -1742,10 +2239,10 @@ var CodeMirror = (function() {
|
|
1742 |
span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
|
1743 |
else if (!marked && sfrom == null)
|
1744 |
for (var i = 0, ch = 0; ch < len; i+=2) {
|
1745 |
-
var str = st[i], l = str.length;
|
1746 |
if (ch + l > len) str = str.slice(0, len - ch);
|
1747 |
ch += l;
|
1748 |
-
span(str,
|
1749 |
}
|
1750 |
else {
|
1751 |
var pos = 0, i = 0, text = "", style, sg = 0;
|
@@ -1777,18 +2274,23 @@ var CodeMirror = (function() {
|
|
1777 |
}
|
1778 |
for (;;) {
|
1779 |
var end = pos + text.length;
|
1780 |
-
var
|
1781 |
-
if (extraStyle)
|
1782 |
-
span(end > upto ? text.slice(0, upto - pos) : text,
|
1783 |
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
|
1784 |
pos = end;
|
1785 |
-
text = st[i++]; style = st[i++];
|
1786 |
}
|
1787 |
}
|
1788 |
if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
|
1789 |
}
|
1790 |
if (includePre) html.push("</pre>");
|
1791 |
return html.join("");
|
|
|
|
|
|
|
|
|
|
|
1792 |
}
|
1793 |
};
|
1794 |
// Utility used by replace and split above
|
@@ -1807,6 +2309,191 @@ var CodeMirror = (function() {
|
|
1807 |
}
|
1808 |
}
|
1809 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1810 |
// The history object 'chunks' changes that are made close together
|
1811 |
// and at almost the same time into bigger undoable units.
|
1812 |
function History() {
|
@@ -1840,44 +2527,34 @@ var CodeMirror = (function() {
|
|
1840 |
}
|
1841 |
};
|
1842 |
|
1843 |
-
|
1844 |
-
function stopEvent() {
|
1845 |
-
if (this.preventDefault) {this.preventDefault(); this.stopPropagation();}
|
1846 |
-
else {this.returnValue = false; this.cancelBubble = true;}
|
1847 |
-
}
|
1848 |
// Ensure an event has a stop method.
|
1849 |
function addStop(event) {
|
1850 |
-
if (!event.stop) event.stop =
|
1851 |
return event;
|
1852 |
}
|
1853 |
|
1854 |
-
|
1855 |
-
|
1856 |
-
|
1857 |
-
|
1858 |
-
|
1859 |
-
|
1860 |
-
|
1861 |
-
|
1862 |
-
|
1863 |
-
|
1864 |
-
|
1865 |
-
|
1866 |
-
|
1867 |
-
|
1868 |
-
|
1869 |
-
|
1870 |
-
pageY: function() {
|
1871 |
-
if (this.e.pageY != null) return this.e.pageY;
|
1872 |
-
var doc = this.target().ownerDocument;
|
1873 |
-
return this.e.clientY + doc.body.scrollTop + doc.documentElement.scrollTop;
|
1874 |
-
}
|
1875 |
-
};
|
1876 |
|
1877 |
// Event handler registration. If disconnect is true, it'll return a
|
1878 |
// function that unregisters the handler.
|
1879 |
function connect(node, type, handler, disconnect) {
|
1880 |
-
function wrapHandler(event) {handler(
|
1881 |
if (typeof node.addEventListener == "function") {
|
1882 |
node.addEventListener(type, wrapHandler, false);
|
1883 |
if (disconnect) return function() {node.removeEventListener(type, wrapHandler, false);};
|
@@ -1891,16 +2568,18 @@ var CodeMirror = (function() {
|
|
1891 |
function Delayed() {this.id = null;}
|
1892 |
Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
|
1893 |
|
1894 |
-
//
|
1895 |
-
|
1896 |
-
|
1897 |
-
|
1898 |
-
|
1899 |
-
|
|
|
|
|
1900 |
|
1901 |
var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
|
1902 |
var ie = /MSIE \d/.test(navigator.userAgent);
|
1903 |
-
var
|
1904 |
|
1905 |
var lineSep = "\n";
|
1906 |
// Feature-detect whether newlines in textareas are converted to \r\n
|
@@ -1912,6 +2591,7 @@ var CodeMirror = (function() {
|
|
1912 |
|
1913 |
var tabSize = 8;
|
1914 |
var mac = /Mac/.test(navigator.platform);
|
|
|
1915 |
var movementKeys = {};
|
1916 |
for (var i = 35; i <= 40; ++i)
|
1917 |
movementKeys[i] = movementKeys["c" + i] = true;
|
@@ -1930,21 +2610,48 @@ var CodeMirror = (function() {
|
|
1930 |
return n;
|
1931 |
}
|
1932 |
|
|
|
|
|
|
|
|
|
|
|
1933 |
// Find the position of an element by following the offsetParent chain.
|
1934 |
// If screen==true, it returns screen (rather than page) coordinates.
|
1935 |
function eltOffset(node, screen) {
|
1936 |
-
var
|
1937 |
-
var x = 0, y = 0,
|
1938 |
for (var n = node; n; n = n.offsetParent) {
|
1939 |
-
|
1940 |
-
//
|
1941 |
-
if (n ==
|
1942 |
-
|
1943 |
-
|
|
|
|
|
|
|
1944 |
for (var n = node.parentNode; n != e; n = n.parentNode)
|
1945 |
if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
|
1946 |
return {left: x, top: y};
|
1947 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1948 |
// Get a node's text content.
|
1949 |
function eltText(node) {
|
1950 |
return node.textContent || node.innerText || node.nodeValue || "";
|
@@ -1955,11 +2662,17 @@ var CodeMirror = (function() {
|
|
1955 |
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
|
1956 |
function copyPos(x) {return {line: x.line, ch: x.ch};}
|
1957 |
|
|
|
1958 |
function htmlEscape(str) {
|
1959 |
-
|
1960 |
-
|
1961 |
-
|
|
|
|
|
|
|
|
|
1962 |
}
|
|
|
1963 |
CodeMirror.htmlEscape = htmlEscape;
|
1964 |
|
1965 |
// Used to position the cursor after an undo/redo by finding the
|
@@ -1981,8 +2694,9 @@ var CodeMirror = (function() {
|
|
1981 |
|
1982 |
// See if "".split is the broken IE version, if so, provide an
|
1983 |
// alternative way to split lines.
|
|
|
1984 |
if ("\n\nb".split(/\n/).length != 3)
|
1985 |
-
|
1986 |
var pos = 0, nl, result = [];
|
1987 |
while ((nl = string.indexOf("\n", pos)) > -1) {
|
1988 |
result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
|
@@ -1992,23 +2706,23 @@ var CodeMirror = (function() {
|
|
1992 |
return result;
|
1993 |
};
|
1994 |
else
|
1995 |
-
|
1996 |
CodeMirror.splitLines = splitLines;
|
1997 |
|
1998 |
// Sane model of finding and setting the selection in a textarea
|
1999 |
if (window.getSelection) {
|
2000 |
-
|
2001 |
try {return {start: te.selectionStart, end: te.selectionEnd};}
|
2002 |
catch(e) {return null;}
|
2003 |
};
|
2004 |
-
if (
|
2005 |
// On Safari, selection set with setSelectionRange are in a sort
|
2006 |
// of limbo wrt their anchor. If you press shift-left in them,
|
2007 |
// the anchor is put at the end, and the selection expanded to
|
2008 |
// the left. If you press shift-right, the anchor ends up at the
|
2009 |
// front. This is not what CodeMirror wants, so it does a
|
2010 |
// spurious modify() call to get out of limbo.
|
2011 |
-
|
2012 |
if (start == end)
|
2013 |
te.setSelectionRange(start, end);
|
2014 |
else {
|
@@ -2017,14 +2731,14 @@ var CodeMirror = (function() {
|
|
2017 |
}
|
2018 |
};
|
2019 |
else
|
2020 |
-
|
2021 |
try {te.setSelectionRange(start, end);}
|
2022 |
catch(e) {} // Fails on Firefox when textarea isn't part of the document
|
2023 |
};
|
2024 |
}
|
2025 |
// IE model. Don't ask.
|
2026 |
else {
|
2027 |
-
|
2028 |
try {var range = te.ownerDocument.selection.createRange();}
|
2029 |
catch(e) {return null;}
|
2030 |
if (!range || range.parentElement() != te) return null;
|
@@ -2046,7 +2760,7 @@ var CodeMirror = (function() {
|
|
2046 |
for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {}
|
2047 |
return {start: start, end: end};
|
2048 |
};
|
2049 |
-
|
2050 |
var range = te.createTextRange();
|
2051 |
range.collapse(true);
|
2052 |
var endrange = range.duplicate();
|
1 |
+
// CodeMirror v2.18
|
2 |
+
|
3 |
// All functions that need access to the editor's state live inside
|
4 |
// the CodeMirror function. Below that, at the bottom of the file,
|
5 |
// some utilities are defined.
|
18 |
var targetDocument = options["document"];
|
19 |
// The element in which the editor lives.
|
20 |
var wrapper = targetDocument.createElement("div");
|
21 |
+
wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
|
22 |
// This mess creates the base DOM structure for the editor.
|
23 |
wrapper.innerHTML =
|
24 |
'<div style="overflow: hidden; position: relative; width: 1px; height: 0px;">' + // Wraps and hides input textarea
|
25 |
+
'<textarea style="position: absolute; width: 10000px;" wrap="off" ' +
|
26 |
+
'autocorrect="off" autocapitalize="off"></textarea></div>' +
|
27 |
'<div class="CodeMirror-scroll cm-s-' + options.theme + '">' +
|
28 |
'<div style="position: relative">' + // Set to the height of the text, causes scrolling
|
|
|
29 |
'<div style="position: relative">' + // Moved around its parent to cover visible view
|
30 |
'<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
|
31 |
// Provides positioning relative to (visible) text origin
|
32 |
'<div class="CodeMirror-lines"><div style="position: relative">' +
|
33 |
+
'<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden"></div>' +
|
34 |
'<pre class="CodeMirror-cursor"> </pre>' + // Absolutely positioned blinky cursor
|
35 |
'<div></div>' + // This DIV contains the actual code
|
36 |
'</div></div></div></div></div>';
|
38 |
// I've never seen more elegant code in my life.
|
39 |
var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
|
40 |
scroller = wrapper.lastChild, code = scroller.firstChild,
|
41 |
+
mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
|
42 |
+
lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
|
43 |
+
cursor = measure.nextSibling, lineDiv = cursor.nextSibling;
|
44 |
+
if (!webkit) lineSpace.draggable = true;
|
45 |
if (options.tabindex != null) input.tabindex = options.tabindex;
|
46 |
if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
|
47 |
|
48 |
+
// Check for problem with IE innerHTML not working when we have a
|
49 |
+
// P (or similar) parent node.
|
50 |
+
try { stringWidth("x"); }
|
51 |
+
catch (e) {
|
52 |
+
if (e.message.match(/unknown runtime/i))
|
53 |
+
e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
|
54 |
+
throw e;
|
55 |
+
}
|
56 |
+
|
57 |
// Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
|
58 |
var poll = new Delayed(), highlight = new Delayed(), blinker;
|
59 |
|
60 |
+
// mode holds a mode API object. doc is the tree of Line objects,
|
61 |
+
// work an array of lines that should be parsed, and history the
|
62 |
+
// undo history (instance of History constructor).
|
63 |
+
var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
|
|
|
64 |
loadMode();
|
65 |
// The selection. These are always maintained to point at valid
|
66 |
// positions. Inverted is used to remember that the user is
|
70 |
// whether the user is holding shift. reducedSelection is a hack
|
71 |
// to get around the fact that we can't create inverted
|
72 |
// selections. See below.
|
73 |
+
var shiftSelecting, reducedSelection, lastClick, lastDoubleClick, draggingText;
|
74 |
// Variables used by startOperation/endOperation to track what
|
75 |
// happened during the operation.
|
76 |
+
var updateInput, changes, textChanged, selectionChanged, leaveInputAlone, gutterDirty;
|
77 |
// Current visible range (may be bigger than the view window).
|
78 |
+
var displayOffset = 0, showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
|
79 |
// editing will hold an object describing the things we put in the
|
80 |
// textarea, to help figure out whether something changed.
|
81 |
// bracketHighlighted is used to remember that a backet has been
|
83 |
var editing, bracketHighlighted;
|
84 |
// Tracks the maximum line length so that the horizontal scrollbar
|
85 |
// can be kept static when scrolling.
|
86 |
+
var maxLine = "", maxWidth;
|
87 |
|
88 |
+
// Initialize the content.
|
|
|
89 |
operation(function(){setValue(options.value || ""); updateInput = false;})();
|
90 |
+
var history = new History();
|
91 |
+
|
92 |
+
var slowPollInterval = 2000;
|
93 |
+
// Gecko and Opera Linux do not reliably fire any event when starting an IME compose
|
94 |
+
var alwaysPollForIME = (!win && !mac) && (gecko || window.opera);
|
95 |
+
if (options.pollForIME && alwaysPollForIME) slowPollInterval = 50;
|
96 |
+
function keyMightStartIME(keyCode) {
|
97 |
+
return (win && ((gecko && keyCode == 229) || (window.opera && keyCode == 197))) || (mac && gecko);
|
98 |
+
}
|
99 |
|
100 |
// Register our event handlers.
|
101 |
connect(scroller, "mousedown", operation(onMouseDown));
|
102 |
+
connect(scroller, "dblclick", operation(onDoubleClick));
|
103 |
+
connect(lineSpace, "dragstart", onDragStart);
|
104 |
+
connect(lineSpace, "selectstart", e_preventDefault);
|
105 |
// Gecko browsers fire contextmenu *after* opening the menu, at
|
106 |
// which point we can't mess with it anymore. Context menu is
|
107 |
// handled in onMouseDown for Gecko.
|
108 |
+
if (!gecko) connect(scroller, "contextmenu", onContextMenu);
|
109 |
+
connect(scroller, "scroll", function() {
|
110 |
+
updateDisplay([]);
|
111 |
+
if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
|
112 |
+
if (options.onScroll) options.onScroll(instance);
|
113 |
+
});
|
114 |
connect(window, "resize", function() {updateDisplay(true);});
|
115 |
connect(input, "keyup", operation(onKeyUp));
|
116 |
+
connect(input, "input", function() {fastPoll(curKeyId);});
|
117 |
connect(input, "keydown", operation(onKeyDown));
|
118 |
connect(input, "keypress", operation(onKeyPress));
|
119 |
connect(input, "focus", onFocus);
|
120 |
connect(input, "blur", onBlur);
|
121 |
|
122 |
+
connect(scroller, "dragenter", e_stop);
|
123 |
+
connect(scroller, "dragover", e_stop);
|
124 |
connect(scroller, "drop", operation(onDrop));
|
125 |
connect(scroller, "paste", function(){focusInput(); fastPoll();});
|
126 |
connect(input, "paste", function(){fastPoll();});
|
127 |
connect(input, "cut", function(){fastPoll();});
|
128 |
+
|
129 |
+
// IE throws unspecified error in certain cases, when
|
130 |
// trying to access activeElement before onload
|
131 |
var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
|
132 |
+
if (hasFocus) setTimeout(onFocus, 20);
|
133 |
else onBlur();
|
134 |
|
135 |
+
function isLine(l) {return l >= 0 && l < doc.size;}
|
136 |
// The instance object that we'll return. Mostly calls out to
|
137 |
// local functions in the CodeMirror function. Some do some extra
|
138 |
// range checking and/or clipping. operation is used to wrap the
|
139 |
// call so that changes it makes are tracked, and the display is
|
140 |
// updated afterwards.
|
141 |
+
var instance = wrapper.CodeMirror = {
|
142 |
getValue: getValue,
|
143 |
setValue: operation(setValue),
|
144 |
getSelection: getSelection,
|
145 |
replaceSelection: operation(replaceSelection),
|
146 |
+
focus: function(){focusInput(); onFocus(); fastPoll();},
|
147 |
setOption: function(option, value) {
|
148 |
+
var oldVal = options[option];
|
149 |
options[option] = value;
|
150 |
+
if (option == "mode" || option == "indentUnit") loadMode();
|
|
|
151 |
else if (option == "readOnly" && value == "nocursor") input.blur();
|
152 |
else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value);
|
153 |
+
else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
|
154 |
+
else if (option == "pollForIME" && alwaysPollForIME) slowPollInterval = value ? 50 : 2000;
|
155 |
+
if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme")
|
156 |
+
operation(gutterChanged)();
|
157 |
},
|
158 |
getOption: function(option) {return options[option];},
|
159 |
undo: operation(undo),
|
160 |
redo: operation(redo),
|
161 |
+
indentLine: operation(function(n, dir) {
|
162 |
+
if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
|
163 |
+
}),
|
164 |
historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
|
165 |
+
clearHistory: function() {history = new History();},
|
166 |
matchBrackets: operation(function(){matchBrackets(true);}),
|
167 |
+
getTokenAt: operation(function(pos) {
|
168 |
pos = clipPos(pos);
|
169 |
+
return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
|
170 |
+
}),
|
171 |
+
getStateAfter: function(line) {
|
172 |
+
line = clipLine(line == null ? doc.size - 1: line);
|
173 |
+
return getStateBefore(line + 1);
|
174 |
},
|
175 |
cursorCoords: function(start){
|
176 |
if (start == null) start = sel.inverted;
|
179 |
charCoords: function(pos){return pageCoords(clipPos(pos));},
|
180 |
coordsChar: function(coords) {
|
181 |
var off = eltOffset(lineSpace);
|
182 |
+
return coordsChar(coords.x - off.left, coords.y - off.top);
|
|
|
183 |
},
|
184 |
getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
|
185 |
+
markText: operation(markText),
|
186 |
+
setBookmark: setBookmark,
|
187 |
+
setMarker: operation(addGutterMarker),
|
188 |
+
clearMarker: operation(removeGutterMarker),
|
189 |
setLineClass: operation(setLineClass),
|
190 |
+
hideLine: operation(function(h) {return setLineHidden(h, true);}),
|
191 |
+
showLine: operation(function(h) {return setLineHidden(h, false);}),
|
192 |
lineInfo: lineInfo,
|
193 |
+
addWidget: function(pos, node, scroll, vert, horiz) {
|
194 |
+
pos = localCoords(clipPos(pos));
|
195 |
+
var top = pos.yBot, left = pos.x;
|
196 |
+
node.style.position = "absolute";
|
197 |
code.appendChild(node);
|
198 |
+
if (vert == "over") top = pos.y;
|
199 |
+
else if (vert == "near") {
|
200 |
+
var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
|
201 |
+
hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
|
202 |
+
if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
|
203 |
+
top = pos.y - node.offsetHeight;
|
204 |
+
if (left + node.offsetWidth > hspace)
|
205 |
+
left = hspace - node.offsetWidth;
|
206 |
+
}
|
207 |
+
node.style.top = (top + paddingTop()) + "px";
|
208 |
+
node.style.left = node.style.right = "";
|
209 |
+
if (horiz == "right") {
|
210 |
+
left = code.clientWidth - node.offsetWidth;
|
211 |
+
node.style.right = "0px";
|
212 |
+
} else {
|
213 |
+
if (horiz == "left") left = 0;
|
214 |
+
else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
|
215 |
+
node.style.left = (left + paddingLeft()) + "px";
|
216 |
+
}
|
217 |
if (scroll)
|
218 |
+
scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
|
219 |
},
|
220 |
|
221 |
+
lineCount: function() {return doc.size;},
|
222 |
getCursor: function(start) {
|
223 |
if (start == null) start = sel.inverted;
|
224 |
return copyPos(start ? sel.from : sel.to);
|
229 |
else setCursor(line, ch);
|
230 |
}),
|
231 |
setSelection: operation(function(from, to) {setSelection(clipPos(from), clipPos(to || from));}),
|
232 |
+
getLine: function(line) {if (isLine(line)) return getLine(line).text;},
|
233 |
setLine: operation(function(line, text) {
|
234 |
+
if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
|
235 |
}),
|
236 |
removeLine: operation(function(line) {
|
237 |
if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
|
239 |
replaceRange: operation(replaceRange),
|
240 |
getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
|
241 |
|
242 |
+
coordsFromIndex: function(off) {
|
243 |
+
var lineNo = 0, ch;
|
244 |
+
doc.iter(0, doc.size, function(line) {
|
245 |
+
var sz = line.text.length + 1;
|
246 |
+
if (sz > off) { ch = off; return true; }
|
247 |
+
off -= sz;
|
248 |
+
++lineNo;
|
249 |
+
});
|
250 |
+
return clipPos({line: lineNo, ch: ch});
|
251 |
+
},
|
252 |
+
|
253 |
operation: function(f){return operation(f)();},
|
254 |
refresh: function(){updateDisplay(true);},
|
255 |
getInputField: function(){return input;},
|
256 |
+
getWrapperElement: function(){return wrapper;},
|
257 |
+
getScrollerElement: function(){return scroller;},
|
258 |
+
getGutterElement: function(){return gutter;}
|
259 |
};
|
260 |
|
261 |
+
function getLine(n) { return getLineAt(doc, n); }
|
262 |
+
function updateLineHeight(line, height) {
|
263 |
+
gutterDirty = true;
|
264 |
+
var diff = height - line.height;
|
265 |
+
for (var n = line; n; n = n.parent) n.height += diff;
|
266 |
+
}
|
267 |
+
|
268 |
function setValue(code) {
|
|
|
269 |
var top = {line: 0, ch: 0};
|
270 |
+
updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
|
271 |
splitLines(code), top, top);
|
272 |
+
updateInput = true;
|
273 |
}
|
274 |
function getValue(code) {
|
275 |
var text = [];
|
276 |
+
doc.iter(0, doc.size, function(line) { text.push(line.text); });
|
|
|
277 |
return text.join("\n");
|
278 |
}
|
279 |
|
280 |
function onMouseDown(e) {
|
281 |
+
// Check whether this is a click in a widget
|
282 |
+
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
283 |
+
if (n.parentNode == code && n != mover) return;
|
284 |
+
|
285 |
+
// See if this is a click in the gutter
|
286 |
+
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
287 |
if (n.parentNode == gutterText) {
|
288 |
if (options.onGutterClick)
|
289 |
+
options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
|
290 |
+
return e_preventDefault(e);
|
291 |
}
|
292 |
|
293 |
+
var start = posFromMouse(e);
|
294 |
+
|
295 |
+
switch (e_button(e)) {
|
296 |
+
case 3:
|
297 |
+
if (gecko && !mac) onContextMenu(e);
|
298 |
+
return;
|
299 |
+
case 2:
|
300 |
+
if (start) setCursor(start.line, start.ch, true);
|
301 |
+
return;
|
302 |
+
}
|
303 |
// For button 1, if it was clicked inside the editor
|
304 |
// (posFromMouse returning non-null), we have to adjust the
|
305 |
// selection.
|
306 |
+
if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
|
|
|
307 |
|
308 |
if (!focused) onFocus();
|
|
|
|
|
309 |
|
310 |
+
var now = +new Date;
|
311 |
+
if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
|
312 |
+
e_preventDefault(e);
|
313 |
+
setTimeout(focusInput, 20);
|
314 |
+
return selectLine(start.line);
|
315 |
+
} else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
|
316 |
+
lastDoubleClick = {time: now, pos: start};
|
317 |
+
e_preventDefault(e);
|
318 |
+
return selectWordAt(start);
|
319 |
+
} else { lastClick = {time: now, pos: start}; }
|
320 |
+
|
321 |
+
var last = start, going;
|
322 |
+
if (dragAndDrop && !posEq(sel.from, sel.to) &&
|
323 |
+
!posLess(start, sel.from) && !posLess(sel.to, start)) {
|
324 |
+
// Let the drag handler handle this.
|
325 |
+
if (webkit) lineSpace.draggable = true;
|
326 |
+
var up = connect(targetDocument, "mouseup", operation(function(e2) {
|
327 |
+
if (webkit) lineSpace.draggable = false;
|
328 |
+
draggingText = false;
|
329 |
+
up();
|
330 |
+
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
|
331 |
+
e_preventDefault(e2);
|
332 |
+
setCursor(start.line, start.ch, true);
|
333 |
+
focusInput();
|
334 |
+
}
|
335 |
+
}), true);
|
336 |
+
draggingText = true;
|
337 |
+
return;
|
338 |
}
|
339 |
+
e_preventDefault(e);
|
340 |
+
setCursor(start.line, start.ch, true);
|
341 |
+
|
342 |
function extend(e) {
|
343 |
var cur = posFromMouse(e, true);
|
344 |
if (cur && !posEq(cur, last)) {
|
354 |
|
355 |
var move = connect(targetDocument, "mousemove", operation(function(e) {
|
356 |
clearTimeout(going);
|
357 |
+
e_preventDefault(e);
|
358 |
extend(e);
|
359 |
}), true);
|
360 |
var up = connect(targetDocument, "mouseup", operation(function(e) {
|
361 |
clearTimeout(going);
|
362 |
var cur = posFromMouse(e);
|
363 |
if (cur) setSelectionUser(start, cur);
|
364 |
+
e_preventDefault(e);
|
365 |
+
focusInput();
|
366 |
+
updateInput = true;
|
367 |
+
move(); up();
|
368 |
}), true);
|
369 |
}
|
370 |
+
function onDoubleClick(e) {
|
371 |
+
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
372 |
+
if (n.parentNode == gutterText) return e_preventDefault(e);
|
373 |
+
var start = posFromMouse(e);
|
374 |
+
if (!start) return;
|
375 |
+
lastDoubleClick = {time: +new Date, pos: start};
|
376 |
+
e_preventDefault(e);
|
377 |
+
selectWordAt(start);
|
378 |
}
|
379 |
function onDrop(e) {
|
380 |
+
e.preventDefault();
|
381 |
+
var pos = posFromMouse(e, true), files = e.dataTransfer.files;
|
382 |
if (!pos || options.readOnly) return;
|
383 |
if (files && files.length && window.FileReader && window.File) {
|
|
|
|
|
384 |
function loadFile(file, i) {
|
385 |
var reader = new FileReader;
|
386 |
reader.onload = function() {
|
387 |
text[i] = reader.result;
|
388 |
+
if (++read == n) {
|
389 |
+
pos = clipPos(pos);
|
390 |
+
operation(function() {
|
391 |
+
var end = replaceRange(text.join(""), pos, pos);
|
392 |
+
setSelectionUser(pos, end);
|
393 |
+
})();
|
394 |
+
}
|
395 |
};
|
396 |
reader.readAsText(file);
|
397 |
}
|
398 |
+
var n = files.length, text = Array(n), read = 0;
|
399 |
+
for (var i = 0; i < n; ++i) loadFile(files[i], i);
|
400 |
}
|
401 |
else {
|
402 |
try {
|
403 |
+
var text = e.dataTransfer.getData("Text");
|
404 |
+
if (text) {
|
405 |
+
var end = replaceRange(text, pos, pos);
|
406 |
+
var curFrom = sel.from, curTo = sel.to;
|
407 |
+
setSelectionUser(pos, end);
|
408 |
+
if (draggingText) replaceRange("", curFrom, curTo);
|
409 |
+
focusInput();
|
410 |
+
}
|
411 |
}
|
412 |
catch(e){}
|
413 |
}
|
414 |
}
|
415 |
+
function onDragStart(e) {
|
416 |
+
var txt = getSelection();
|
417 |
+
// This will reset escapeElement
|
418 |
+
htmlEscape(txt);
|
419 |
+
e.dataTransfer.setDragImage(escapeElement, 0, 0);
|
420 |
+
e.dataTransfer.setData("Text", txt);
|
421 |
+
}
|
422 |
function onKeyDown(e) {
|
423 |
if (!focused) onFocus();
|
424 |
|
425 |
+
var code = e.keyCode;
|
426 |
// IE does strange things with escape.
|
427 |
+
if (ie && code == 27) { e.returnValue = false; }
|
428 |
// Tries to detect ctrl on non-mac, cmd on mac.
|
429 |
+
var mod = (mac ? e.metaKey : e.ctrlKey) && !e.altKey, anyMod = e.ctrlKey || e.altKey || e.metaKey;
|
430 |
+
if (code == 16 || e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
|
431 |
else shiftSelecting = null;
|
432 |
// First give onKeyEvent option a chance to handle this.
|
433 |
+
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
|
434 |
|
435 |
+
if (code == 33 || code == 34) {scrollPage(code == 34); return e_preventDefault(e);} // page up/down
|
436 |
if (mod && ((code == 36 || code == 35) || // ctrl-home/end
|
437 |
mac && (code == 38 || code == 40))) { // cmd-up/down
|
438 |
+
scrollEnd(code == 36 || code == 38); return e_preventDefault(e);
|
439 |
}
|
440 |
+
if (mod && code == 65) {selectAll(); return e_preventDefault(e);} // ctrl-a
|
441 |
if (!options.readOnly) {
|
442 |
if (!anyMod && code == 13) {return;} // enter
|
443 |
+
if (!anyMod && code == 9 && handleTab(e.shiftKey)) return e_preventDefault(e); // tab
|
444 |
+
if (mod && code == 90) {undo(); return e_preventDefault(e);} // ctrl-z
|
445 |
+
if (mod && ((e.shiftKey && code == 90) || code == 89)) {redo(); return e_preventDefault(e);} // ctrl-shift-z, ctrl-y
|
446 |
}
|
447 |
+
if (code == 36) { if (options.smartHome) { smartHome(); return e_preventDefault(e); } }
|
448 |
|
449 |
// Key id to use in the movementKeys map. We also pass it to
|
450 |
// fastPoll in order to 'self learn'. We need this because
|
452 |
// its start when it is inverted and a movement key is pressed
|
453 |
// (and later restore it again), shouldn't be used for
|
454 |
// non-movement keys.
|
455 |
+
curKeyId = (mod ? "c" : "") + (e.altKey ? "a" : "") + code;
|
456 |
+
if (sel.inverted && movementKeys[curKeyId] === true) {
|
457 |
var range = selRange(input);
|
458 |
if (range) {
|
459 |
reducedSelection = {anchor: range.start};
|
460 |
setSelRange(input, range.start, range.start);
|
461 |
}
|
462 |
}
|
463 |
+
// Don't save the key as a movementkey unless it had a modifier
|
464 |
+
if (!mod && !e.altKey) curKeyId = null;
|
465 |
fastPoll(curKeyId);
|
466 |
+
|
467 |
+
if (options.pollForIME && keyMightStartIME(code)) slowPollInterval = 50;
|
468 |
}
|
469 |
function onKeyUp(e) {
|
470 |
+
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
|
471 |
if (reducedSelection) {
|
472 |
reducedSelection = null;
|
473 |
updateInput = true;
|
474 |
}
|
475 |
+
if (e.keyCode == 16) shiftSelecting = null;
|
476 |
+
|
477 |
+
if (slowPollInterval < 2000 && !alwaysPollForIME) slowPollInterval = 2000;
|
478 |
}
|
479 |
function onKeyPress(e) {
|
480 |
+
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
|
481 |
if (options.electricChars && mode.electricChars) {
|
482 |
+
var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
|
483 |
if (mode.electricChars.indexOf(ch) > -1)
|
484 |
setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 50);
|
485 |
}
|
486 |
+
var code = e.keyCode;
|
487 |
// Re-stop tab and enter. Necessary on some browsers.
|
488 |
+
if (code == 13) {if (!options.readOnly) handleEnter(); e_preventDefault(e);}
|
489 |
+
else if (!e.ctrlKey && !e.altKey && !e.metaKey && code == 9 && options.tabMode != "default") e_preventDefault(e);
|
490 |
else fastPoll(curKeyId);
|
491 |
}
|
492 |
|
493 |
function onFocus() {
|
494 |
if (options.readOnly == "nocursor") return;
|
495 |
+
if (!focused) {
|
496 |
+
if (options.onFocus) options.onFocus(instance);
|
497 |
+
focused = true;
|
498 |
+
if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
|
499 |
+
wrapper.className += " CodeMirror-focused";
|
500 |
+
if (!leaveInputAlone) prepareInput();
|
501 |
+
}
|
502 |
slowPoll();
|
|
|
|
|
503 |
restartBlink();
|
504 |
}
|
505 |
function onBlur() {
|
506 |
+
if (focused) {
|
507 |
+
if (options.onBlur) options.onBlur(instance);
|
508 |
+
focused = false;
|
509 |
+
wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
|
510 |
+
}
|
511 |
clearInterval(blinker);
|
512 |
+
setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
|
|
|
|
|
513 |
}
|
514 |
|
515 |
// Replace the range from from to to by the strings in newText.
|
517 |
function updateLines(from, to, newText, selFrom, selTo) {
|
518 |
if (history) {
|
519 |
var old = [];
|
520 |
+
doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
|
521 |
history.addChange(from.line, newText.length, old);
|
522 |
while (history.done.length > options.undoDepth) history.done.shift();
|
523 |
}
|
524 |
updateLinesNoUndo(from, to, newText, selFrom, selTo);
|
|
|
|
|
525 |
}
|
526 |
function unredoHelper(from, to) {
|
527 |
var change = from.pop();
|
528 |
if (change) {
|
529 |
var replaced = [], end = change.start + change.added;
|
530 |
+
doc.iter(change.start, end, function(line) { replaced.push(line.text); });
|
531 |
to.push({start: change.start, added: change.old.length, old: replaced});
|
532 |
var pos = clipPos({line: change.start + change.old.length - 1,
|
533 |
ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
|
534 |
+
updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
|
535 |
+
updateInput = true;
|
536 |
}
|
537 |
}
|
538 |
function undo() {unredoHelper(history.done, history.undone);}
|
540 |
|
541 |
function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
|
542 |
var recomputeMaxLength = false, maxLineLength = maxLine.length;
|
543 |
+
if (!options.lineWrapping)
|
544 |
+
doc.iter(from.line, to.line, function(line) {
|
545 |
+
if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
|
546 |
+
});
|
547 |
|
548 |
+
var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
|
549 |
// First adjust the line structure, taking some care to leave highlighting intact.
|
550 |
if (firstLine == lastLine) {
|
551 |
if (newText.length == 1)
|
552 |
firstLine.replace(from.ch, to.ch, newText[0]);
|
553 |
else {
|
554 |
lastLine = firstLine.split(to.ch, newText[newText.length-1]);
|
555 |
+
firstLine.replace(from.ch, null, newText[0]);
|
556 |
+
firstLine.fixMarkEnds(lastLine);
|
557 |
+
var added = [];
|
558 |
+
for (var i = 1, e = newText.length - 1; i < e; ++i)
|
559 |
+
added.push(Line.inheritMarks(newText[i], firstLine));
|
560 |
+
added.push(lastLine);
|
561 |
+
doc.insert(from.line + 1, added);
|
562 |
}
|
563 |
}
|
564 |
else if (newText.length == 1) {
|
565 |
+
firstLine.replace(from.ch, null, newText[0]);
|
566 |
+
lastLine.replace(null, to.ch, "");
|
567 |
+
firstLine.append(lastLine);
|
568 |
+
doc.remove(from.line + 1, nlines);
|
569 |
}
|
570 |
else {
|
571 |
+
var added = [];
|
572 |
+
firstLine.replace(from.ch, null, newText[0]);
|
573 |
+
lastLine.replace(null, to.ch, newText[newText.length-1]);
|
574 |
+
firstLine.fixMarkEnds(lastLine);
|
575 |
+
for (var i = 1, e = newText.length - 1; i < e; ++i)
|
576 |
+
added.push(Line.inheritMarks(newText[i], firstLine));
|
577 |
+
if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
|
578 |
+
doc.insert(from.line + 1, added);
|
579 |
+
}
|
580 |
+
if (options.lineWrapping) {
|
581 |
+
var perLine = scroller.clientWidth / charWidth() - 3;
|
582 |
+
doc.iter(from.line, from.line + newText.length, function(line) {
|
583 |
+
if (line.hidden) return;
|
584 |
+
var guess = Math.ceil(line.text.length / perLine) || 1;
|
585 |
+
if (guess != line.height) updateLineHeight(line, guess);
|
586 |
+
});
|
587 |
+
} else {
|
588 |
+
doc.iter(from.line, i + newText.length, function(line) {
|
589 |
+
var l = line.text;
|
590 |
if (l.length > maxLineLength) {
|
591 |
+
maxLine = l; maxLineLength = l.length; maxWidth = null;
|
592 |
+
recomputeMaxLength = false;
|
593 |
}
|
594 |
+
});
|
595 |
+
if (recomputeMaxLength) {
|
596 |
+
maxLineLength = 0; maxLine = ""; maxWidth = null;
|
597 |
+
doc.iter(0, doc.size, function(line) {
|
598 |
+
var l = line.text;
|
599 |
+
if (l.length > maxLineLength) {
|
600 |
+
maxLineLength = l.length; maxLine = l;
|
601 |
+
}
|
602 |
+
});
|
603 |
}
|
604 |
}
|
605 |
|
611 |
if (task < from.line) newWork.push(task);
|
612 |
else if (task > to.line) newWork.push(task + lendiff);
|
613 |
}
|
614 |
+
var hlEnd = from.line + Math.min(newText.length, 500);
|
615 |
+
highlightLines(from.line, hlEnd);
|
616 |
+
newWork.push(hlEnd);
|
617 |
work = newWork;
|
618 |
startWorker(100);
|
619 |
// Remember that these lines changed, for updating the display
|
625 |
setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
|
626 |
|
627 |
// Make sure the scroll-size div has the correct height.
|
628 |
+
code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
|
629 |
}
|
630 |
|
631 |
function replaceRange(code, from, to) {
|
663 |
|
664 |
function getRange(from, to) {
|
665 |
var l1 = from.line, l2 = to.line;
|
666 |
+
if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
|
667 |
+
var code = [getLine(l1).text.slice(from.ch)];
|
668 |
+
doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
|
669 |
+
code.push(getLine(l2).text.slice(0, to.ch));
|
670 |
return code.join("\n");
|
671 |
}
|
672 |
function getSelection() {
|
676 |
var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
|
677 |
function slowPoll() {
|
678 |
if (pollingFast) return;
|
679 |
+
poll.set(slowPollInterval, function() {
|
680 |
startOperation();
|
681 |
readInput();
|
682 |
if (focused) slowPoll();
|
689 |
function p() {
|
690 |
startOperation();
|
691 |
var changed = readInput();
|
692 |
+
if (changed && keyId) {
|
693 |
+
if (changed == "moved" && movementKeys[keyId] == null) movementKeys[keyId] = true;
|
694 |
+
if (changed == "changed") movementKeys[keyId] = false;
|
695 |
+
}
|
696 |
if (!changed && !missed) {missed = true; poll.set(80, p);}
|
697 |
else {pollingFast = false; slowPoll();}
|
698 |
endOperation();
|
704 |
// to the data in the editing variable, and updates the editor
|
705 |
// content or cursor if something changed.
|
706 |
function readInput() {
|
707 |
+
if (leaveInputAlone || !focused) return;
|
708 |
var changed = false, text = input.value, sr = selRange(input);
|
709 |
if (!sr) return false;
|
710 |
var changed = editing.text != text, rs = reducedSelection;
|
774 |
// editor state.
|
775 |
function prepareInput() {
|
776 |
var text = [];
|
777 |
+
var from = Math.max(0, sel.from.line - 1), to = Math.min(doc.size, sel.to.line + 2);
|
778 |
+
doc.iter(from, to, function(line) { text.push(line.text); });
|
779 |
text = input.value = text.join(lineSep);
|
780 |
var startch = sel.from.ch, endch = sel.to.ch;
|
781 |
+
doc.iter(from, sel.from.line, function(line) {
|
782 |
+
startch += lineSep.length + line.text.length;
|
783 |
+
});
|
784 |
+
doc.iter(from, sel.to.line, function(line) {
|
785 |
+
endch += lineSep.length + line.text.length;
|
786 |
+
});
|
787 |
editing = {text: text, from: from, to: to, start: startch, end: endch};
|
788 |
setSelRange(input, startch, reducedSelection ? startch : endch);
|
789 |
}
|
791 |
if (options.readOnly != "nocursor") input.focus();
|
792 |
}
|
793 |
|
794 |
+
function scrollEditorIntoView() {
|
795 |
+
if (!cursor.getBoundingClientRect) return;
|
796 |
+
var rect = cursor.getBoundingClientRect();
|
797 |
+
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
|
798 |
+
if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
|
799 |
+
}
|
800 |
function scrollCursorIntoView() {
|
801 |
var cursor = localCoords(sel.inverted ? sel.from : sel.to);
|
802 |
+
var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
|
803 |
+
return scrollIntoView(x, cursor.y, x, cursor.yBot);
|
804 |
}
|
805 |
function scrollIntoView(x1, y1, x2, y2) {
|
806 |
+
var pl = paddingLeft(), pt = paddingTop(), lh = textHeight();
|
807 |
y1 += pt; y2 += pt; x1 += pl; x2 += pl;
|
808 |
var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
|
809 |
if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
|
810 |
else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
|
811 |
|
812 |
var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
|
813 |
+
var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
|
814 |
+
if (x1 < screenleft + gutterw) {
|
815 |
if (x1 < 50) x1 = 0;
|
816 |
+
scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
|
817 |
scrolled = true;
|
818 |
}
|
819 |
else if (x2 > screenw + screenleft) {
|
826 |
}
|
827 |
|
828 |
function visibleLines() {
|
829 |
+
var lh = textHeight(), top = scroller.scrollTop - paddingTop();
|
830 |
+
var from_height = Math.max(0, Math.floor(top / lh));
|
831 |
+
var to_height = Math.ceil((top + scroller.clientHeight) / lh);
|
832 |
+
return {from: lineAtHeight(doc, from_height),
|
833 |
+
to: lineAtHeight(doc, to_height)};
|
834 |
}
|
835 |
// Uses a set of changes plus the current scroll position to
|
836 |
// determine which DOM updates have to be made, and makes the
|
837 |
// updates.
|
838 |
function updateDisplay(changes) {
|
839 |
if (!scroller.clientWidth) {
|
840 |
+
showingFrom = showingTo = displayOffset = 0;
|
841 |
return;
|
842 |
}
|
843 |
+
// Compute the new visible window
|
844 |
+
var visible = visibleLines();
|
845 |
+
// Bail out if the visible area is already rendered and nothing changed.
|
846 |
+
if (changes !== true && changes.length == 0 && visible.from >= showingFrom && visible.to <= showingTo) return;
|
847 |
+
var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
|
848 |
+
if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
|
849 |
+
if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
|
850 |
+
|
851 |
+
// Create a range of theoretically intact lines, and punch holes
|
852 |
+
// in that using the change info.
|
853 |
+
var intact = changes === true ? [] :
|
854 |
+
computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
|
855 |
+
// Clip off the parts that won't be visible
|
856 |
+
var intactLines = 0;
|
857 |
+
for (var i = 0; i < intact.length; ++i) {
|
858 |
+
var range = intact[i];
|
859 |
+
if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
|
860 |
+
if (range.to > to) range.to = to;
|
861 |
+
if (range.from >= range.to) intact.splice(i--, 1);
|
862 |
+
else intactLines += range.to - range.from;
|
863 |
+
}
|
864 |
+
if (intactLines == to - from) return;
|
865 |
+
intact.sort(function(a, b) {return a.domStart - b.domStart;});
|
866 |
+
|
867 |
+
var th = textHeight(), gutterDisplay = gutter.style.display;
|
868 |
+
lineDiv.style.display = gutter.style.display = "none";
|
869 |
+
patchDisplay(from, to, intact);
|
870 |
+
lineDiv.style.display = "";
|
871 |
+
|
872 |
+
// Position the mover div to align with the lines it's supposed
|
873 |
+
// to be showing (which will cover the visible display)
|
874 |
+
var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight;
|
875 |
+
if (different) lastHeight = scroller.clientHeight;
|
876 |
+
showingFrom = from; showingTo = to;
|
877 |
+
displayOffset = heightAtLine(doc, from);
|
878 |
+
mover.style.top = (displayOffset * th) + "px";
|
879 |
+
code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
|
880 |
+
|
881 |
+
// Since this is all rather error prone, it is honoured with the
|
882 |
+
// only assertion in the whole file.
|
883 |
+
if (lineDiv.childNodes.length != showingTo - showingFrom)
|
884 |
+
throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
|
885 |
+
" nodes=" + lineDiv.childNodes.length);
|
886 |
+
|
887 |
+
if (options.lineWrapping) {
|
888 |
+
maxWidth = scroller.clientWidth;
|
889 |
+
var curNode = lineDiv.firstChild;
|
890 |
+
doc.iter(showingFrom, showingTo, function(line) {
|
891 |
+
if (!line.hidden) {
|
892 |
+
var height = Math.round(curNode.offsetHeight / th) || 1;
|
893 |
+
if (line.height != height) {updateLineHeight(line, height); gutterDirty = true;}
|
894 |
+
}
|
895 |
+
curNode = curNode.nextSibling;
|
896 |
+
});
|
897 |
+
} else {
|
898 |
+
if (maxWidth == null) maxWidth = stringWidth(maxLine);
|
899 |
+
if (maxWidth > scroller.clientWidth) {
|
900 |
+
lineSpace.style.width = maxWidth + "px";
|
901 |
+
// Needed to prevent odd wrapping/hiding of widgets placed in here.
|
902 |
+
code.style.width = "";
|
903 |
+
code.style.width = scroller.scrollWidth + "px";
|
904 |
+
} else {
|
905 |
+
lineSpace.style.width = code.style.width = "";
|
906 |
+
}
|
907 |
+
}
|
908 |
+
gutter.style.display = gutterDisplay;
|
909 |
+
if (different || gutterDirty) updateGutter();
|
910 |
+
updateCursor();
|
911 |
+
}
|
912 |
+
|
913 |
+
function computeIntact(intact, changes) {
|
914 |
for (var i = 0, l = changes.length || 0; i < l; ++i) {
|
915 |
var change = changes[i], intact2 = [], diff = change.diff || 0;
|
916 |
for (var j = 0, l2 = intact.length; j < l2; ++j) {
|
917 |
var range = intact[j];
|
918 |
+
if (change.to <= range.from && change.diff)
|
919 |
+
intact2.push({from: range.from + diff, to: range.to + diff,
|
920 |
+
domStart: range.domStart});
|
921 |
+
else if (change.to <= range.from || change.from >= range.to)
|
922 |
intact2.push(range);
|
923 |
else {
|
924 |
if (change.from > range.from)
|
925 |
+
intact2.push({from: range.from, to: change.from, domStart: range.domStart});
|
926 |
if (change.to < range.to)
|
927 |
intact2.push({from: change.to + diff, to: range.to + diff,
|
928 |
domStart: range.domStart + (change.to - range.from)});
|
930 |
}
|
931 |
intact = intact2;
|
932 |
}
|
933 |
+
return intact;
|
934 |
+
}
|
935 |
|
936 |
+
function patchDisplay(from, to, intact) {
|
937 |
+
// The first pass removes the DOM nodes that aren't intact.
|
938 |
+
if (!intact.length) lineDiv.innerHTML = "";
|
939 |
+
else {
|
940 |
+
function killNode(node) {
|
941 |
+
var tmp = node.nextSibling;
|
942 |
+
node.parentNode.removeChild(node);
|
943 |
+
return tmp;
|
|
|
|
|
|
|
|
|
|
|
|
|
944 |
}
|
945 |
+
var domPos = 0, curNode = lineDiv.firstChild, n;
|
946 |
+
for (var i = 0; i < intact.length; ++i) {
|
947 |
+
var cur = intact[i];
|
948 |
+
while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
|
949 |
+
for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
|
950 |
+
}
|
951 |
+
while (curNode) curNode = killNode(curNode);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
952 |
}
|
953 |
+
// This pass fills in the lines that actually changed.
|
954 |
+
var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
|
955 |
+
var sfrom = sel.from.line, sto = sel.to.line, inSel = sfrom < from && sto >= from;
|
956 |
+
var scratch = targetDocument.createElement("div"), newElt;
|
957 |
+
doc.iter(from, to, function(line) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
958 |
var ch1 = null, ch2 = null;
|
959 |
if (inSel) {
|
960 |
ch1 = 0;
|
961 |
+
if (sto == j) {inSel = false; ch2 = sel.to.ch;}
|
962 |
+
} else if (sfrom == j) {
|
963 |
+
if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
|
|
|
964 |
else {inSel = true; ch1 = sel.from.ch;}
|
965 |
}
|
966 |
+
if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
|
967 |
+
if (!nextIntact || nextIntact.from > j) {
|
968 |
+
if (line.hidden) scratch.innerHTML = "<pre></pre>";
|
969 |
+
else scratch.innerHTML = line.getHTML(ch1, ch2, true);
|
970 |
+
lineDiv.insertBefore(scratch.firstChild, curNode);
|
971 |
+
} else {
|
972 |
+
curNode = curNode.nextSibling;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
973 |
}
|
974 |
+
++j;
|
975 |
+
});
|
976 |
}
|
977 |
|
978 |
function updateGutter() {
|
979 |
if (!options.gutter && !options.lineNumbers) return;
|
980 |
var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
|
981 |
gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
|
982 |
+
var html = [], i = showingFrom;
|
983 |
+
doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
|
984 |
+
if (line.hidden) {
|
985 |
+
html.push("<pre></pre>");
|
986 |
+
} else {
|
987 |
+
var marker = line.gutterMarker;
|
988 |
+
var text = options.lineNumbers ? i + options.firstLineNumber : null;
|
989 |
+
if (marker && marker.text)
|
990 |
+
text = marker.text.replace("%N%", text != null ? text : "");
|
991 |
+
else if (text == null)
|
992 |
+
text = "\u00a0";
|
993 |
+
html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text);
|
994 |
+
for (var j = 1; j < line.height; ++j) html.push("<br> ");
|
995 |
+
html.push("</pre>");
|
996 |
+
}
|
997 |
+
++i;
|
998 |
+
});
|
999 |
gutter.style.display = "none";
|
1000 |
gutterText.innerHTML = html.join("");
|
1001 |
+
var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
|
1002 |
while (val.length + pad.length < minwidth) pad += "\u00a0";
|
1003 |
if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
|
1004 |
gutter.style.display = "";
|
1005 |
lineSpace.style.marginLeft = gutter.offsetWidth + "px";
|
1006 |
+
gutterDirty = false;
|
1007 |
}
|
1008 |
function updateCursor() {
|
1009 |
+
var head = sel.inverted ? sel.from : sel.to, lh = textHeight();
|
1010 |
+
var pos = localCoords(head, true);
|
1011 |
+
var globalY = pos.y + displayOffset * textHeight();
|
1012 |
+
inputDiv.style.top = Math.max(Math.min(globalY, scroller.offsetHeight), 0) + "px";
|
1013 |
+
inputDiv.style.left = (pos.x - scroller.scrollLeft) + "px";
|
1014 |
if (posEq(sel.from, sel.to)) {
|
1015 |
+
cursor.style.top = pos.y + "px";
|
1016 |
+
cursor.style.left = (options.lineWrapping ? Math.min(pos.x, lineSpace.offsetWidth) : pos.x) + "px";
|
1017 |
cursor.style.display = "";
|
1018 |
}
|
1019 |
else cursor.style.display = "none";
|
1031 |
// updateLines, since they have to be expressed in the line
|
1032 |
// numbers before the update.
|
1033 |
function setSelection(from, to, oldFrom, oldTo) {
|
1034 |
+
if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
|
1035 |
if (posEq(sel.from, from) && posEq(sel.to, to)) return;
|
1036 |
if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
|
1037 |
|
1038 |
+
// Skip over hidden lines.
|
1039 |
+
if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch);
|
1040 |
+
if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
|
1041 |
+
|
1042 |
if (posEq(from, to)) sel.inverted = false;
|
1043 |
else if (posEq(from, sel.to)) sel.inverted = false;
|
1044 |
else if (posEq(to, sel.from)) sel.inverted = true;
|
1046 |
// Some ugly logic used to only mark the lines that actually did
|
1047 |
// see a change in selection as changed, rather than the whole
|
1048 |
// selected range.
|
|
|
1049 |
if (posEq(from, to)) {
|
1050 |
if (!posEq(sel.from, sel.to))
|
1051 |
changes.push({from: oldFrom, to: oldTo + 1});
|
1070 |
sel.from = from; sel.to = to;
|
1071 |
selectionChanged = true;
|
1072 |
}
|
1073 |
+
function skipHidden(pos, oldLine, oldCh) {
|
1074 |
+
function getNonHidden(dir) {
|
1075 |
+
var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
|
1076 |
+
while (lNo != end) {
|
1077 |
+
var line = getLine(lNo);
|
1078 |
+
if (!line.hidden) {
|
1079 |
+
var ch = pos.ch;
|
1080 |
+
if (ch > oldCh || ch > line.text.length) ch = line.text.length;
|
1081 |
+
return {line: lNo, ch: ch};
|
1082 |
+
}
|
1083 |
+
lNo += dir;
|
1084 |
+
}
|
1085 |
+
}
|
1086 |
+
var line = getLine(pos.line);
|
1087 |
+
if (!line.hidden) return pos;
|
1088 |
+
if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
|
1089 |
+
else return getNonHidden(-1) || getNonHidden(1);
|
1090 |
+
}
|
1091 |
function setCursor(line, ch, user) {
|
1092 |
var pos = clipPos({line: line, ch: ch || 0});
|
1093 |
(user ? setSelectionUser : setSelection)(pos, pos);
|
1094 |
}
|
1095 |
|
1096 |
+
function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
|
1097 |
function clipPos(pos) {
|
1098 |
if (pos.line < 0) return {line: 0, ch: 0};
|
1099 |
+
if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
|
1100 |
+
var ch = pos.ch, linelen = getLine(pos.line).text.length;
|
1101 |
if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
|
1102 |
else if (ch < 0) return {line: pos.line, ch: 0};
|
1103 |
else return pos;
|
1104 |
}
|
1105 |
|
1106 |
function scrollPage(down) {
|
1107 |
+
var linesPerPage = Math.floor(scroller.clientHeight / textHeight()), head = sel.inverted ? sel.from : sel.to;
|
1108 |
+
var target = heightAtLine(doc, head.line) + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1));
|
1109 |
+
setCursor(lineAtHeight(doc, target), head.ch, true);
|
1110 |
}
|
1111 |
function scrollEnd(top) {
|
1112 |
+
var pos = top ? {line: 0, ch: 0} : {line: doc.size - 1, ch: getLine(doc.size-1).text.length};
|
1113 |
setSelectionUser(pos, pos);
|
1114 |
}
|
1115 |
function selectAll() {
|
1116 |
+
var endLine = doc.size - 1;
|
1117 |
+
setSelection({line: 0, ch: 0}, {line: endLine, ch: getLine(endLine).text.length});
|
1118 |
}
|
1119 |
function selectWordAt(pos) {
|
1120 |
+
var line = getLine(pos.line).text;
|
1121 |
var start = pos.ch, end = pos.ch;
|
1122 |
while (start > 0 && /\w/.test(line.charAt(start - 1))) --start;
|
1123 |
while (end < line.length && /\w/.test(line.charAt(end))) ++end;
|
1124 |
setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
|
1125 |
}
|
1126 |
function selectLine(line) {
|
1127 |
+
setSelectionUser({line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
|
1128 |
}
|
1129 |
function handleEnter() {
|
1130 |
replaceSelection("\n", "end");
|
1132 |
indentLine(sel.from.line, options.enterMode == "keep" ? "prev" : "smart");
|
1133 |
}
|
1134 |
function handleTab(shift) {
|
1135 |
+
function indentSelected(mode) {
|
1136 |
+
if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
|
1137 |
+
var e = sel.to.line - (sel.to.ch ? 0 : 1);
|
1138 |
+
for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
|
1139 |
+
}
|
1140 |
shiftSelecting = null;
|
1141 |
switch (options.tabMode) {
|
1142 |
case "default":
|
1143 |
return false;
|
1144 |
case "indent":
|
1145 |
+
indentSelected("smart");
|
1146 |
break;
|
1147 |
case "classic":
|
1148 |
if (posEq(sel.from, sel.to)) {
|
1151 |
break;
|
1152 |
}
|
1153 |
case "shift":
|
1154 |
+
indentSelected(shift ? "subtract" : "add");
|
1155 |
break;
|
1156 |
}
|
1157 |
return true;
|
1158 |
}
|
1159 |
+
function smartHome() {
|
1160 |
+
var firstNonWS = Math.max(0, getLine(sel.from.line).text.search(/\S/));
|
1161 |
+
setCursor(sel.from.line, sel.from.ch <= firstNonWS && sel.from.ch ? 0 : firstNonWS, true);
|
1162 |
+
}
|
1163 |
|
1164 |
function indentLine(n, how) {
|
1165 |
if (how == "smart") {
|
1167 |
else var state = getStateBefore(n);
|
1168 |
}
|
1169 |
|
1170 |
+
var line = getLine(n), curSpace = line.indentation(), curSpaceString = line.text.match(/^\s*/)[0], indentation;
|
1171 |
if (how == "prev") {
|
1172 |
+
if (n) indentation = getLine(n-1).indentation();
|
1173 |
else indentation = 0;
|
1174 |
}
|
1175 |
else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length));
|
1194 |
|
1195 |
function loadMode() {
|
1196 |
mode = CodeMirror.getMode(options, options.mode);
|
1197 |
+
doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
|
|
|
1198 |
work = [0];
|
1199 |
startWorker();
|
1200 |
}
|
1201 |
function gutterChanged() {
|
1202 |
var visible = options.gutter || options.lineNumbers;
|
1203 |
gutter.style.display = visible ? "" : "none";
|
1204 |
+
if (visible) gutterDirty = true;
|
1205 |
else lineDiv.parentNode.style.marginLeft = 0;
|
1206 |
}
|
1207 |
+
function wrappingChanged(from, to) {
|
1208 |
+
if (options.lineWrapping) {
|
1209 |
+
wrapper.className += " CodeMirror-wrap";
|
1210 |
+
var perLine = scroller.clientWidth / charWidth() - 3;
|
1211 |
+
doc.iter(0, doc.size, function(line) {
|
1212 |
+
if (line.hidden) return;
|
1213 |
+
var guess = Math.ceil(line.text.length / perLine) || 1;
|
1214 |
+
if (guess != 1) updateLineHeight(line, guess);
|
1215 |
+
});
|
1216 |
+
lineSpace.style.width = code.style.width = "";
|
1217 |
+
} else {
|
1218 |
+
wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
|
1219 |
+
maxWidth = null; maxLine = "";
|
1220 |
+
doc.iter(0, doc.size, function(line) {
|
1221 |
+
if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
|
1222 |
+
if (line.text.length > maxLine.length) maxLine = line.text;
|
1223 |
+
});
|
1224 |
+
}
|
1225 |
+
changes.push({from: 0, to: doc.size});
|
1226 |
+
}
|
1227 |
+
|
1228 |
+
function TextMarker() { this.set = []; }
|
1229 |
+
TextMarker.prototype.clear = operation(function() {
|
1230 |
+
for (var i = 0, e = this.set.length; i < e; ++i) {
|
1231 |
+
var mk = this.set[i].marked;
|
1232 |
+
if (!mk) continue;
|
1233 |
+
for (var j = 0; j < mk.length; ++j)
|
1234 |
+
if (mk[j].set == this.set) mk.splice(j--, 1);
|
1235 |
+
}
|
1236 |
+
// We don't know the exact lines that changed. Refreshing is
|
1237 |
+
// cheaper than finding them.
|
1238 |
+
changes.push({from: 0, to: doc.size});
|
1239 |
+
});
|
1240 |
+
TextMarker.prototype.find = function() {
|
1241 |
+
var from, to;
|
1242 |
+
for (var i = 0, e = this.set.length; i < e; ++i) {
|
1243 |
+
var line = this.set[i], mk = line.marked;
|
1244 |
+
for (var j = 0; j < mk.length; ++j) {
|
1245 |
+
var mark = mk[j];
|
1246 |
+
if (mark.set == this.set) {
|
1247 |
+
if (mark.from != null || mark.to != null) {
|
1248 |
+
var found = lineNo(line);
|
1249 |
+
if (found != null) {
|
1250 |
+
if (mark.from != null) from = {line: found, ch: mark.from};
|
1251 |
+
if (mark.to != null) to = {line: found, ch: mark.to};
|
1252 |
+
}
|
1253 |
+
}
|
1254 |
+
}
|
1255 |
+
}
|
1256 |
+
}
|
1257 |
+
return {from: from, to: to};
|
1258 |
+
};
|
1259 |
|
1260 |
function markText(from, to, className) {
|
1261 |
from = clipPos(from); to = clipPos(to);
|
1262 |
+
var tm = new TextMarker();
|
1263 |
function add(line, from, to, className) {
|
1264 |
+
mark = getLine(line).addMark(new MarkedText(from, to, className, tm.set));
|
|
|
|
|
1265 |
}
|
1266 |
if (from.line == to.line) add(from.line, from.ch, to.ch, className);
|
1267 |
else {
|
1268 |
add(from.line, from.ch, null, className);
|
1269 |
for (var i = from.line + 1, e = to.line; i < e; ++i)
|
1270 |
+
add(i, null, null, className);
|
1271 |
+
add(to.line, null, to.ch, className);
|
1272 |
}
|
1273 |
changes.push({from: from.line, to: to.line + 1});
|
1274 |
+
return tm;
|
1275 |
+
}
|
1276 |
+
|
1277 |
+
function setBookmark(pos) {
|
1278 |
+
pos = clipPos(pos);
|
1279 |
+
var bm = new Bookmark(pos.ch);
|
1280 |
+
getLine(pos.line).addMark(bm);
|
1281 |
+
return bm;
|
|
|
|
|
|
|
|
|
1282 |
}
|
1283 |
|
1284 |
function addGutterMarker(line, text, className) {
|
1285 |
+
if (typeof line == "number") line = getLine(clipLine(line));
|
1286 |
line.gutterMarker = {text: text, style: className};
|
1287 |
+
gutterDirty = true;
|
1288 |
return line;
|
1289 |
}
|
1290 |
function removeGutterMarker(line) {
|
1291 |
+
if (typeof line == "number") line = getLine(clipLine(line));
|
1292 |
line.gutterMarker = null;
|
1293 |
+
gutterDirty = true;
|
1294 |
}
|
1295 |
+
|
1296 |
+
function changeLine(handle, op) {
|
1297 |
+
var no = handle, line = handle;
|
1298 |
+
if (typeof handle == "number") line = getLine(clipLine(handle));
|
1299 |
+
else no = lineNo(handle);
|
1300 |
+
if (no == null) return null;
|
1301 |
+
if (op(line, no)) changes.push({from: no, to: no + 1});
|
|
|
|
|
|
|
|
|
|
|
|
|
1302 |
return line;
|
1303 |
}
|
1304 |
+
function setLineClass(handle, className) {
|
1305 |
+
return changeLine(handle, function(line) {
|
1306 |
+
if (line.className != className) {
|
1307 |
+
line.className = className;
|
1308 |
+
return true;
|
1309 |
+
}
|
1310 |
+
});
|
1311 |
+
}
|
1312 |
+
function setLineHidden(handle, hidden) {
|
1313 |
+
return changeLine(handle, function(line, no) {
|
1314 |
+
if (line.hidden != hidden) {
|
1315 |
+
line.hidden = hidden;
|
1316 |
+
updateLineHeight(line, hidden ? 0 : 1);
|
1317 |
+
if (hidden && (sel.from.line == no || sel.to.line == no))
|
1318 |
+
setSelection(skipHidden(sel.from, sel.from.line, sel.from.ch),
|
1319 |
+
skipHidden(sel.to, sel.to.line, sel.to.ch));
|
1320 |
+
return (gutterDirty = true);
|
1321 |
+
}
|
1322 |
+
});
|
1323 |
+
}
|
1324 |
|
1325 |
function lineInfo(line) {
|
1326 |
if (typeof line == "number") {
|
1327 |
+
if (!isLine(line)) return null;
|
1328 |
var n = line;
|
1329 |
+
line = getLine(line);
|
1330 |
if (!line) return null;
|
1331 |
}
|
1332 |
else {
|
1333 |
+
var n = lineNo(line);
|
1334 |
+
if (n == null) return null;
|
1335 |
}
|
1336 |
var marker = line.gutterMarker;
|
1337 |
+
return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
|
1338 |
+
markerClass: marker && marker.style, lineClass: line.className};
|
1339 |
}
|
1340 |
|
1341 |
function stringWidth(str) {
|
1345 |
}
|
1346 |
// These are used to go from pixel positions to character
|
1347 |
// positions, taking varying character widths into account.
|
|
|
|
|
|
|
|
|
|
|
1348 |
function charFromX(line, x) {
|
1349 |
if (x <= 0) return 0;
|
1350 |
+
var lineObj = getLine(line), text = lineObj.text;
|
1351 |
function getX(len) {
|
1352 |
measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, len) + "</span></pre>";
|
1353 |
return measure.firstChild.firstChild.offsetWidth;
|
1354 |
}
|
1355 |
var from = 0, fromX = 0, to = text.length, toX;
|
1356 |
// Guess a suitable upper bound for our search.
|
1357 |
+
var estimated = Math.min(to, Math.ceil(x / charWidth()));
|
1358 |
for (;;) {
|
1359 |
var estX = getX(estimated);
|
1360 |
if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
|
1373 |
}
|
1374 |
}
|
1375 |
|
1376 |
+
var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
|
1377 |
+
function measureLine(line, ch) {
|
1378 |
+
var extra = "";
|
1379 |
+
// Include extra text at the end to make sure the measured line is wrapped in the right way.
|
1380 |
+
if (options.lineWrapping) {
|
1381 |
+
var end = line.text.indexOf(" ", ch + 2);
|
1382 |
+
extra = line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0));
|
1383 |
+
}
|
1384 |
+
measure.innerHTML = "<pre>" + line.getHTML(null, null, false, ch) +
|
1385 |
+
'<span id="CodeMirror-temp-' + tempId + '">' + (line.text.charAt(ch) || " ") + "</span>" +
|
1386 |
+
extra + "</pre>";
|
1387 |
+
var elt = document.getElementById("CodeMirror-temp-" + tempId);
|
1388 |
+
var top = elt.offsetTop, left = elt.offsetLeft;
|
1389 |
+
// Older IEs report zero offsets for spans directly after a wrap
|
1390 |
+
if (ie && ch && top == 0 && left == 0) {
|
1391 |
+
var backup = document.createElement("span");
|
1392 |
+
backup.innerHTML = "x";
|
1393 |
+
elt.parentNode.insertBefore(backup, elt.nextSibling);
|
1394 |
+
top = backup.offsetTop;
|
1395 |
+
}
|
1396 |
+
return {top: top, left: left};
|
1397 |
+
}
|
1398 |
function localCoords(pos, inLineWrap) {
|
1399 |
+
var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
|
1400 |
+
if (pos.ch == 0) x = 0;
|
1401 |
+
else {
|
1402 |
+
var sp = measureLine(getLine(pos.line), pos.ch);
|
1403 |
+
x = sp.left;
|
1404 |
+
if (options.lineWrapping) y += Math.max(0, sp.top);
|
1405 |
+
}
|
1406 |
+
return {x: x, y: y, yBot: y + lh};
|
1407 |
+
}
|
1408 |
+
// Coords must be lineSpace-local
|
1409 |
+
function coordsChar(x, y) {
|
1410 |
+
if (y < 0) y = 0;
|
1411 |
+
var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
|
1412 |
+
var lineNo = lineAtHeight(doc, heightPos);
|
1413 |
+
if (lineNo >= doc.size) return {line: doc.size - 1, ch: 0};
|
1414 |
+
var lineObj = getLine(lineNo), text = lineObj.text;
|
1415 |
+
var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
|
1416 |
+
if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
|
1417 |
+
function getX(len) {
|
1418 |
+
var sp = measureLine(lineObj, len);
|
1419 |
+
if (tw) {
|
1420 |
+
var off = Math.round(sp.top / th);
|
1421 |
+
return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
|
1422 |
+
}
|
1423 |
+
return sp.left;
|
1424 |
+
}
|
1425 |
+
var from = 0, fromX = 0, to = text.length, toX;
|
1426 |
+
// Guess a suitable upper bound for our search.
|
1427 |
+
var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
|
1428 |
+
for (;;) {
|
1429 |
+
var estX = getX(estimated);
|
1430 |
+
if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
|
1431 |
+
else {toX = estX; to = estimated; break;}
|
1432 |
+
}
|
1433 |
+
if (x > toX) return {line: lineNo, ch: to};
|
1434 |
+
// Try to guess a suitable lower bound as well.
|
1435 |
+
estimated = Math.floor(to * 0.8); estX = getX(estimated);
|
1436 |
+
if (estX < x) {from = estimated; fromX = estX;}
|
1437 |
+
// Do a binary search between these bounds.
|
1438 |
+
for (;;) {
|
1439 |
+
if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
|
1440 |
+
var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
|
1441 |
+
if (middleX > x) {to = middle; toX = middleX;}
|
1442 |
+
else {from = middle; fromX = middleX;}
|
1443 |
+
}
|
1444 |
}
|
1445 |
function pageCoords(pos) {
|
1446 |
var local = localCoords(pos, true), off = eltOffset(lineSpace);
|
1447 |
return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
|
1448 |
}
|
1449 |
|
1450 |
+
var cachedHeight, cachedFor;
|
1451 |
+
function textHeight() {
|
1452 |
+
var offsetHeight = lineDiv.offsetHeight;
|
1453 |
+
if (offsetHeight == cachedFor) return cachedHeight;
|
1454 |
+
cachedFor = offsetHeight;
|
1455 |
+
measure.innerHTML = "<pre>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x</pre>";
|
1456 |
+
return (cachedHeight = measure.firstChild.offsetHeight / 10 || 1);
|
1457 |
+
}
|
1458 |
+
var cachedWidth, cachedFor = 0;
|
1459 |
+
function charWidth() {
|
1460 |
+
if (scroller.clientWidth == cachedFor) return cachedWidth;
|
1461 |
+
cachedFor = scroller.clientWidth;
|
1462 |
+
return (cachedWidth = stringWidth("x"));
|
1463 |
}
|
1464 |
function paddingTop() {return lineSpace.offsetTop;}
|
1465 |
function paddingLeft() {return lineSpace.offsetLeft;}
|
1466 |
|
1467 |
function posFromMouse(e, liberal) {
|
1468 |
+
var offW = eltOffset(scroller, true), x, y;
|
1469 |
+
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
|
1470 |
+
try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
|
1471 |
// This is a mess of a heuristic to try and determine whether a
|
1472 |
// scroll-bar was clicked or not, and to return null if one was
|
1473 |
// (and !liberal).
|
1474 |
if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
|
1475 |
return null;
|
1476 |
var offL = eltOffset(lineSpace, true);
|
1477 |
+
return coordsChar(x - offL.left, y - offL.top);
|
|
|
1478 |
}
|
1479 |
function onContextMenu(e) {
|
1480 |
var pos = posFromMouse(e);
|
1481 |
if (!pos || window.opera) return; // Opera is difficult.
|
1482 |
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
|
1483 |
+
operation(setCursor)(pos.line, pos.ch);
|
1484 |
|
1485 |
var oldCSS = input.style.cssText;
|
1486 |
+
inputDiv.style.position = "absolute";
|
1487 |
+
input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
|
1488 |
+
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
|
1489 |
+
"border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
|
1490 |
+
leaveInputAlone = true;
|
1491 |
var val = input.value = getSelection();
|
1492 |
focusInput();
|
1493 |
setSelRange(input, 0, input.value.length);
|
|
|
1494 |
function rehide() {
|
1495 |
+
var newVal = splitLines(input.value).join("\n");
|
1496 |
+
if (newVal != val) operation(replaceSelection)(newVal, "end");
|
1497 |
+
inputDiv.style.position = "relative";
|
1498 |
input.style.cssText = oldCSS;
|
1499 |
leaveInputAlone = false;
|
1500 |
prepareInput();
|
1501 |
slowPoll();
|
1502 |
}
|
1503 |
+
|
1504 |
if (gecko) {
|
1505 |
+
e_stop(e);
|
1506 |
var mouseup = connect(window, "mouseup", function() {
|
1507 |
mouseup();
|
1508 |
setTimeout(rehide, 20);
|
1525 |
|
1526 |
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
|
1527 |
function matchBrackets(autoclear) {
|
1528 |
+
var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
|
1529 |
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
|
1530 |
if (!match) return;
|
1531 |
var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
|
1549 |
}
|
1550 |
}
|
1551 |
}
|
1552 |
+
for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
|
1553 |
+
var line = getLine(i), first = i == head.line;
|
1554 |
var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
|
1555 |
+
if (found) break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1556 |
}
|
1557 |
+
if (!found) found = {pos: null, match: false};
|
1558 |
+
var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
1559 |
+
var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
|
1560 |
+
two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
|
1561 |
+
var clear = operation(function(){one.clear(); two && two.clear();});
|
1562 |
+
if (autoclear) setTimeout(clear, 800);
|
1563 |
+
else bracketHighlighted = clear;
|
1564 |
}
|
1565 |
|
1566 |
// Finds the line to start with when starting a parse. Tries to
|
1572 |
var minindent, minline;
|
1573 |
for (var search = n, lim = n - 40; search > lim; --search) {
|
1574 |
if (search == 0) return 0;
|
1575 |
+
var line = getLine(search-1);
|
1576 |
if (line.stateAfter) return search;
|
1577 |
var indented = line.indentation();
|
1578 |
if (minline == null || minindent > indented) {
|
1579 |
+
minline = search - 1;
|
1580 |
minindent = indented;
|
1581 |
}
|
1582 |
}
|
1583 |
return minline;
|
1584 |
}
|
1585 |
function getStateBefore(n) {
|
1586 |
+
var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
|
1587 |
if (!state) state = startState(mode);
|
1588 |
else state = copyState(mode, state);
|
1589 |
+
doc.iter(start, n, function(line) {
|
|
|
1590 |
line.highlight(mode, state);
|
1591 |
line.stateAfter = copyState(mode, state);
|
1592 |
+
});
|
1593 |
+
if (start < n) changes.push({from: start, to: n});
|
1594 |
+
if (n < doc.size && !getLine(n).stateAfter) work.push(n);
|
1595 |
return state;
|
1596 |
}
|
1597 |
function highlightLines(start, end) {
|
1598 |
var state = getStateBefore(start);
|
1599 |
+
doc.iter(start, end, function(line) {
|
|
|
1600 |
line.highlight(mode, state);
|
1601 |
line.stateAfter = copyState(mode, state);
|
1602 |
+
});
|
1603 |
}
|
1604 |
function highlightWorker() {
|
1605 |
var end = +new Date + options.workTime;
|
1606 |
var foundWork = work.length;
|
1607 |
while (work.length) {
|
1608 |
+
if (!getLine(showingFrom).stateAfter) var task = showingFrom;
|
1609 |
else var task = work.pop();
|
1610 |
+
if (task >= doc.size) continue;
|
1611 |
+
var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
|
1612 |
if (state) state = copyState(mode, state);
|
1613 |
else state = startState(mode);
|
1614 |
|
1615 |
+
var unchanged = 0, compare = mode.compareStates, realChange = false,
|
1616 |
+
i = start, bail = false;
|
1617 |
+
doc.iter(i, doc.size, function(line) {
|
1618 |
+
var hadState = line.stateAfter;
|
1619 |
if (+new Date > end) {
|
1620 |
work.push(i);
|
1621 |
startWorker(options.workDelay);
|
1622 |
+
if (realChange) changes.push({from: task, to: i + 1});
|
1623 |
+
return (bail = true);
|
1624 |
}
|
1625 |
var changed = line.highlight(mode, state);
|
1626 |
+
if (changed) realChange = true;
|
1627 |
line.stateAfter = copyState(mode, state);
|
1628 |
+
if (compare) {
|
1629 |
+
if (hadState && compare(hadState, state)) return true;
|
1630 |
+
} else {
|
1631 |
+
if (changed !== false || !hadState) unchanged = 0;
|
1632 |
+
else if (++unchanged > 3) return true;
|
1633 |
+
}
|
1634 |
+
++i;
|
1635 |
+
});
|
1636 |
+
if (bail) return;
|
1637 |
+
if (realChange) changes.push({from: task, to: i + 1});
|
1638 |
}
|
1639 |
if (foundWork && options.onHighlightComplete)
|
1640 |
options.onHighlightComplete(instance);
|
1655 |
var reScroll = false;
|
1656 |
if (selectionChanged) reScroll = !scrollCursorIntoView();
|
1657 |
if (changes.length) updateDisplay(changes);
|
1658 |
+
else {
|
1659 |
+
if (selectionChanged) updateCursor();
|
1660 |
+
if (gutterDirty) updateGutter();
|
1661 |
+
}
|
1662 |
if (reScroll) scrollCursorIntoView();
|
1663 |
+
if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
|
1664 |
|
1665 |
// updateInput can be set to a boolean value to force/prevent an
|
1666 |
// update.
|
1667 |
+
if (focused && !leaveInputAlone &&
|
1668 |
+
(updateInput === true || (updateInput !== false && selectionChanged)))
|
1669 |
prepareInput();
|
1670 |
|
1671 |
if (selectionChanged && options.matchBrackets)
|
1704 |
if (typeof query != "string") // Regexp match
|
1705 |
this.matches = function(reverse, pos) {
|
1706 |
if (reverse) {
|
1707 |
+
var line = getLine(pos.line).text.slice(0, pos.ch), match = line.match(query), start = 0;
|
1708 |
while (match) {
|
1709 |
var ind = line.indexOf(match[0]);
|
1710 |
start += ind;
|
1716 |
}
|
1717 |
}
|
1718 |
else {
|
1719 |
+
var line = getLine(pos.line).text.slice(pos.ch), match = line.match(query),
|
1720 |
start = match && pos.ch + line.indexOf(match[0]);
|
1721 |
}
|
1722 |
if (match)
|
1731 |
// Different methods for single-line and multi-line queries
|
1732 |
if (target.length == 1)
|
1733 |
this.matches = function(reverse, pos) {
|
1734 |
+
var line = fold(getLine(pos.line).text), len = query.length, match;
|
1735 |
if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
|
1736 |
: (match = line.indexOf(query, pos.ch)) != -1)
|
1737 |
return {from: {line: pos.line, ch: match},
|
1739 |
};
|
1740 |
else
|
1741 |
this.matches = function(reverse, pos) {
|
1742 |
+
var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(getLine(ln).text);
|
1743 |
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
|
1744 |
if (reverse ? offsetA >= pos.ch || offsetA != match.length
|
1745 |
: offsetA <= pos.ch || offsetA != line.length - match.length)
|
1746 |
return;
|
1747 |
for (;;) {
|
1748 |
+
if (reverse ? !ln : ln == doc.size - 1) return;
|
1749 |
+
line = fold(getLine(ln += reverse ? -1 : 1).text);
|
1750 |
match = target[reverse ? --idx : ++idx];
|
1751 |
if (idx > 0 && idx < target.length - 1) {
|
1752 |
if (line != match) return;
|
1782 |
}
|
1783 |
if (reverse) {
|
1784 |
if (!pos.line) return savePosAndFail(0);
|
1785 |
+
pos = {line: pos.line-1, ch: getLine(pos.line-1).text.length};
|
1786 |
}
|
1787 |
else {
|
1788 |
+
if (pos.line == doc.size - 1) return savePosAndFail(doc.size);
|
1789 |
pos = {line: pos.line+1, ch: 0};
|
1790 |
}
|
1791 |
}
|
1792 |
},
|
1793 |
|
1794 |
from: function() {if (this.atOccurrence) return copyPos(this.pos.from);},
|
1795 |
+
to: function() {if (this.atOccurrence) return copyPos(this.pos.to);},
|
1796 |
+
|
1797 |
+
replace: function(newText) {
|
1798 |
+
var self = this;
|
1799 |
+
if (this.atOccurrence)
|
1800 |
+
operation(function() {
|
1801 |
+
self.pos.to = replaceRange(newText, self.pos.from, self.pos.to);
|
1802 |
+
})();
|
1803 |
+
}
|
1804 |
};
|
1805 |
|
1806 |
for (var ext in extensions)
|
1821 |
enterMode: "indent",
|
1822 |
electricChars: true,
|
1823 |
onKeyEvent: null,
|
1824 |
+
lineWrapping: false,
|
1825 |
lineNumbers: false,
|
1826 |
gutter: false,
|
1827 |
+
fixedGutter: false,
|
1828 |
firstLineNumber: 1,
|
1829 |
readOnly: false,
|
1830 |
+
smartHome: true,
|
1831 |
onChange: null,
|
1832 |
onCursorActivity: null,
|
1833 |
onGutterClick: null,
|
1838 |
workDelay: 200,
|
1839 |
undoDepth: 40,
|
1840 |
tabindex: null,
|
1841 |
+
pollForIME: false,
|
1842 |
document: window.document
|
1843 |
};
|
1844 |
|
1864 |
return CodeMirror.getMode(options, "text/plain");
|
1865 |
}
|
1866 |
return mfactory(options, config || {});
|
1867 |
+
};
|
1868 |
CodeMirror.listModes = function() {
|
1869 |
var list = [];
|
1870 |
for (var m in modes)
|
1874 |
CodeMirror.listMIMEs = function() {
|
1875 |
var list = [];
|
1876 |
for (var m in mimeModes)
|
1877 |
+
if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
|
1878 |
return list;
|
1879 |
};
|
1880 |
|
1936 |
}
|
1937 |
return nstate;
|
1938 |
}
|
1939 |
+
CodeMirror.copyState = copyState;
|
1940 |
function startState(mode, a1, a2) {
|
1941 |
return mode.startState ? mode.startState(a1, a2) : true;
|
1942 |
}
|
1943 |
+
CodeMirror.startState = startState;
|
1944 |
|
1945 |
// The character stream used by a mode's parser.
|
1946 |
function StringStream(string) {
|
1962 |
if (ok) {++this.pos; return ch;}
|
1963 |
},
|
1964 |
eatWhile: function(match) {
|
1965 |
+
var start = this.pos;
|
1966 |
while (this.eat(match)){}
|
1967 |
return this.pos > start;
|
1968 |
},
|
1997 |
};
|
1998 |
CodeMirror.StringStream = StringStream;
|
1999 |
|
2000 |
+
function MarkedText(from, to, className, set) {
|
2001 |
+
this.from = from; this.to = to; this.style = className; this.set = set;
|
2002 |
+
}
|
2003 |
+
MarkedText.prototype = {
|
2004 |
+
attach: function(line) { this.set.push(line); },
|
2005 |
+
detach: function(line) {
|
2006 |
+
var ix = indexOf(this.set, line);
|
2007 |
+
if (ix > -1) this.set.splice(ix, 1);
|
2008 |
+
},
|
2009 |
+
split: function(pos, lenBefore) {
|
2010 |
+
if (this.to <= pos && this.to != null) return null;
|
2011 |
+
var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
|
2012 |
+
var to = this.to == null ? null : this.to - pos + lenBefore;
|
2013 |
+
return new MarkedText(from, to, this.style, this.set);
|
2014 |
+
},
|
2015 |
+
dup: function() { return new MarkedText(null, null, this.style, this.set); },
|
2016 |
+
clipTo: function(fromOpen, from, toOpen, to, diff) {
|
2017 |
+
if (this.from != null && this.from >= from)
|
2018 |
+
this.from = Math.max(to, this.from) + diff;
|
2019 |
+
if (this.to != null && this.to > from)
|
2020 |
+
this.to = to < this.to ? this.to + diff : from;
|
2021 |
+
if (fromOpen && to > this.from && (to < this.to || this.to == null))
|
2022 |
+
this.from = null;
|
2023 |
+
if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
|
2024 |
+
this.to = null;
|
2025 |
+
},
|
2026 |
+
isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
|
2027 |
+
sameSet: function(x) { return this.set == x.set; }
|
2028 |
+
};
|
2029 |
+
|
2030 |
+
function Bookmark(pos) {
|
2031 |
+
this.from = pos; this.to = pos; this.line = null;
|
2032 |
+
}
|
2033 |
+
Bookmark.prototype = {
|
2034 |
+
attach: function(line) { this.line = line; },
|
2035 |
+
detach: function(line) { if (this.line == line) this.line = null; },
|
2036 |
+
split: function(pos, lenBefore) {
|
2037 |
+
if (pos < this.from) {
|
2038 |
+
this.from = this.to = (this.from - pos) + lenBefore;
|
2039 |
+
return this;
|
2040 |
+
}
|
2041 |
+
},
|
2042 |
+
isDead: function() { return this.from > this.to; },
|
2043 |
+
clipTo: function(fromOpen, from, toOpen, to, diff) {
|
2044 |
+
if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
|
2045 |
+
this.from = 0; this.to = -1;
|
2046 |
+
} else if (this.from > from) {
|
2047 |
+
this.from = this.to = Math.max(to, this.from) + diff;
|
2048 |
+
}
|
2049 |
+
},
|
2050 |
+
sameSet: function(x) { return false; },
|
2051 |
+
find: function() {
|
2052 |
+
if (!this.line || !this.line.parent) return null;
|
2053 |
+
return {line: lineNo(this.line), ch: this.from};
|
2054 |
+
},
|
2055 |
+
clear: function() {
|
2056 |
+
if (this.line) {
|
2057 |
+
var found = indexOf(this.line.marked, this);
|
2058 |
+
if (found != -1) this.line.marked.splice(found, 1);
|
2059 |
+
this.line = null;
|
2060 |
+
}
|
2061 |
+
}
|
2062 |
+
};
|
2063 |
+
|
2064 |
// Line objects. These hold state related to a line, including
|
2065 |
// highlighting info (the styles array).
|
2066 |
function Line(text, styles) {
|
2067 |
this.styles = styles || [text, null];
|
|
|
2068 |
this.text = text;
|
2069 |
+
this.height = 1;
|
2070 |
this.marked = this.gutterMarker = this.className = null;
|
2071 |
+
this.stateAfter = this.parent = this.hidden = null;
|
2072 |
+
}
|
2073 |
+
Line.inheritMarks = function(text, orig) {
|
2074 |
+
var ln = new Line(text), mk = orig.marked;
|
2075 |
+
if (mk) {
|
2076 |
+
for (var i = 0; i < mk.length; ++i) {
|
2077 |
+
if (mk[i].to == null && mk[i].style) {
|
2078 |
+
var newmk = ln.marked || (ln.marked = []), mark = mk[i];
|
2079 |
+
var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
|
2080 |
+
}
|
2081 |
+
}
|
2082 |
+
}
|
2083 |
+
return ln;
|
2084 |
}
|
2085 |
Line.prototype = {
|
2086 |
// Replace a piece of a line, keeping the styles around it intact.
|
2087 |
+
replace: function(from, to_, text) {
|
2088 |
+
// Reset line class if the whole text was replaced.
|
2089 |
+
if (!from && (to_ == null || to_ == this.text.length))
|
2090 |
+
this.className = this.gutterMarker = null;
|
2091 |
+
var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
|
2092 |
copyStyles(0, from, this.styles, st);
|
2093 |
if (text) st.push(text, null);
|
2094 |
copyStyles(to, this.text.length, this.styles, st);
|
2096 |
this.text = this.text.slice(0, from) + text + this.text.slice(to);
|
2097 |
this.stateAfter = null;
|
2098 |
if (mk) {
|
2099 |
+
var diff = text.length - (to - from);
|
2100 |
+
for (var i = 0, mark = mk[i]; i < mk.length; ++i) {
|
2101 |
+
mark.clipTo(from == null, from || 0, to_ == null, to, diff);
|
2102 |
+
if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
|
|
|
|
|
|
|
2103 |
}
|
2104 |
}
|
2105 |
},
|
2106 |
+
// Split a part off a line, keeping styles and markers intact.
|
2107 |
split: function(pos, textBefore) {
|
2108 |
+
var st = [textBefore, null], mk = this.marked;
|
2109 |
copyStyles(pos, this.text.length, this.styles, st);
|
2110 |
+
var taken = new Line(textBefore + this.text.slice(pos), st);
|
2111 |
+
if (mk) {
|
2112 |
+
for (var i = 0; i < mk.length; ++i) {
|
2113 |
+
var mark = mk[i];
|
2114 |
+
var newmark = mark.split(pos, textBefore.length);
|
2115 |
+
if (newmark) {
|
2116 |
+
if (!taken.marked) taken.marked = [];
|
2117 |
+
taken.marked.push(newmark); newmark.attach(taken);
|
2118 |
+
}
|
2119 |
+
}
|
2120 |
+
}
|
2121 |
+
return taken;
|
2122 |
},
|
2123 |
+
append: function(line) {
|
2124 |
+
var mylen = this.text.length, mk = line.marked, mymk = this.marked;
|
2125 |
+
this.text += line.text;
|
2126 |
+
copyStyles(0, line.text.length, line.styles, this.styles);
|
2127 |
+
if (mymk) {
|
2128 |
+
for (var i = 0; i < mymk.length; ++i)
|
2129 |
+
if (mymk[i].to == null) mymk[i].to = mylen;
|
2130 |
+
}
|
2131 |
+
if (mk && mk.length) {
|
2132 |
+
if (!mymk) this.marked = mymk = [];
|
2133 |
+
outer: for (var i = 0; i < mk.length; ++i) {
|
2134 |
+
var mark = mk[i];
|
2135 |
+
if (!mark.from) {
|
2136 |
+
for (var j = 0; j < mymk.length; ++j) {
|
2137 |
+
var mymark = mymk[j];
|
2138 |
+
if (mymark.to == mylen && mymark.sameSet(mark)) {
|
2139 |
+
mymark.to = mark.to == null ? null : mark.to + mylen;
|
2140 |
+
if (mymark.isDead()) {
|
2141 |
+
mymark.detach(this);
|
2142 |
+
mk.splice(i--, 1);
|
2143 |
+
}
|
2144 |
+
continue outer;
|
2145 |
+
}
|
2146 |
+
}
|
2147 |
+
}
|
2148 |
+
mymk.push(mark);
|
2149 |
+
mark.attach(this);
|
2150 |
+
mark.from += mylen;
|
2151 |
+
if (mark.to != null) mark.to += mylen;
|
2152 |
+
}
|
2153 |
+
}
|
2154 |
},
|
2155 |
+
fixMarkEnds: function(other) {
|
2156 |
+
var mk = this.marked, omk = other.marked;
|
2157 |
if (!mk) return;
|
2158 |
+
for (var i = 0; i < mk.length; ++i) {
|
2159 |
+
var mark = mk[i], close = mark.to == null;
|
2160 |
+
if (close && omk) {
|
2161 |
+
for (var j = 0; j < omk.length; ++j)
|
2162 |
+
if (omk[j].sameSet(mark)) {close = false; break;}
|
2163 |
+
}
|
2164 |
+
if (close) mark.to = this.text.length;
|
2165 |
+
}
|
2166 |
+
},
|
2167 |
+
addMark: function(mark) {
|
2168 |
+
mark.attach(this);
|
2169 |
+
if (this.marked == null) this.marked = [];
|
2170 |
+
this.marked.push(mark);
|
2171 |
+
this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
|
2172 |
},
|
2173 |
// Run the given mode's parser over a line, update the styles
|
2174 |
// array, which contains alternating fragments of text and CSS
|
2196 |
}
|
2197 |
if (st.length != pos) {st.length = pos; changed = true;}
|
2198 |
if (pos && st[pos-2] != prevWord) changed = true;
|
2199 |
+
// Short lines with simple highlights return null, and are
|
2200 |
+
// counted as changed by the driver because they are likely to
|
2201 |
+
// highlight the same way in various contexts.
|
2202 |
+
return changed || (st.length < 5 && this.text.length < 10 ? null : false);
|
2203 |
},
|
2204 |
// Fetch the parser token for a given character. Useful for hacks
|
2205 |
// that want to inspect the mode state (say, for completion).
|
2219 |
// Produces an HTML fragment for the line, taking selection,
|
2220 |
// marking, and highlighting into account.
|
2221 |
getHTML: function(sfrom, sto, includePre, endAt) {
|
2222 |
+
var html = [], first = true;
|
2223 |
if (includePre)
|
2224 |
html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
|
2225 |
function span(text, style) {
|
2226 |
if (!text) return;
|
2227 |
+
// Work around a bug where, in some compat modes, IE ignores leading spaces
|
2228 |
+
if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
|
2229 |
+
first = false;
|
2230 |
+
if (style) html.push('<span class="', style, '">', htmlEscape(text), "</span>");
|
2231 |
else html.push(htmlEscape(text));
|
2232 |
}
|
2233 |
var st = this.styles, allText = this.text, marked = this.marked;
|
2239 |
span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
|
2240 |
else if (!marked && sfrom == null)
|
2241 |
for (var i = 0, ch = 0; ch < len; i+=2) {
|
2242 |
+
var str = st[i], style = st[i+1], l = str.length;
|
2243 |
if (ch + l > len) str = str.slice(0, len - ch);
|
2244 |
ch += l;
|
2245 |
+
span(str, style && "cm-" + style);
|
2246 |
}
|
2247 |
else {
|
2248 |
var pos = 0, i = 0, text = "", style, sg = 0;
|
2274 |
}
|
2275 |
for (;;) {
|
2276 |
var end = pos + text.length;
|
2277 |
+
var appliedStyle = style;
|
2278 |
+
if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
|
2279 |
+
span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
|
2280 |
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
|
2281 |
pos = end;
|
2282 |
+
text = st[i++]; style = "cm-" + st[i++];
|
2283 |
}
|
2284 |
}
|
2285 |
if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
|
2286 |
}
|
2287 |
if (includePre) html.push("</pre>");
|
2288 |
return html.join("");
|
2289 |
+
},
|
2290 |
+
cleanUp: function() {
|
2291 |
+
this.parent = null;
|
2292 |
+
if (this.marked)
|
2293 |
+
for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
|
2294 |
}
|
2295 |
};
|
2296 |
// Utility used by replace and split above
|
2309 |
}
|
2310 |
}
|
2311 |
|
2312 |
+
// Data structure that holds the sequence of lines.
|
2313 |
+
function LeafChunk(lines) {
|
2314 |
+
this.lines = lines;
|
2315 |
+
this.parent = null;
|
2316 |
+
for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
|
2317 |
+
lines[i].parent = this;
|
2318 |
+
height += lines[i].height;
|
2319 |
+
}
|
2320 |
+
this.height = height;
|
2321 |
+
}
|
2322 |
+
LeafChunk.prototype = {
|
2323 |
+
chunkSize: function() { return this.lines.length; },
|
2324 |
+
remove: function(at, n) {
|
2325 |
+
for (var i = at, e = at + n; i < e; ++i) {
|
2326 |
+
var line = this.lines[i];
|
2327 |
+
line.cleanUp();
|
2328 |
+
this.height -= line.height;
|
2329 |
+
}
|
2330 |
+
this.lines.splice(at, n);
|
2331 |
+
},
|
2332 |
+
collapse: function(lines) {
|
2333 |
+
lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
|
2334 |
+
},
|
2335 |
+
insertHeight: function(at, lines, height) {
|
2336 |
+
this.height += height;
|
2337 |
+
this.lines.splice.apply(this.lines, [at, 0].concat(lines));
|
2338 |
+
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
|
2339 |
+
},
|
2340 |
+
iterN: function(at, n, op) {
|
2341 |
+
for (var e = at + n; at < e; ++at)
|
2342 |
+
if (op(this.lines[at])) return true;
|
2343 |
+
}
|
2344 |
+
};
|
2345 |
+
function BranchChunk(children) {
|
2346 |
+
this.children = children;
|
2347 |
+
var size = 0, height = 0;
|
2348 |
+
for (var i = 0, e = children.length; i < e; ++i) {
|
2349 |
+
var ch = children[i];
|
2350 |
+
size += ch.chunkSize(); height += ch.height;
|
2351 |
+
ch.parent = this;
|
2352 |
+
}
|
2353 |
+
this.size = size;
|
2354 |
+
this.height = height;
|
2355 |
+
this.parent = null;
|
2356 |
+
}
|
2357 |
+
BranchChunk.prototype = {
|
2358 |
+
chunkSize: function() { return this.size; },
|
2359 |
+
remove: function(at, n) {
|
2360 |
+
this.size -= n;
|
2361 |
+
for (var i = 0; i < this.children.length; ++i) {
|
2362 |
+
var child = this.children[i], sz = child.chunkSize();
|
2363 |
+
if (at < sz) {
|
2364 |
+
var rm = Math.min(n, sz - at), oldHeight = child.height;
|
2365 |
+
child.remove(at, rm);
|
2366 |
+
this.height -= oldHeight - child.height;
|
2367 |
+
if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
|
2368 |
+
if ((n -= rm) == 0) break;
|
2369 |
+
at = 0;
|
2370 |
+
} else at -= sz;
|
2371 |
+
}
|
2372 |
+
if (this.size - n < 25) {
|
2373 |
+
var lines = [];
|
2374 |
+
this.collapse(lines);
|
2375 |
+
this.children = [new LeafChunk(lines)];
|
2376 |
+
}
|
2377 |
+
},
|
2378 |
+
collapse: function(lines) {
|
2379 |
+
for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
|
2380 |
+
},
|
2381 |
+
insert: function(at, lines) {
|
2382 |
+
var height = 0;
|
2383 |
+
for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
|
2384 |
+
this.insertHeight(at, lines, height);
|
2385 |
+
},
|
2386 |
+
insertHeight: function(at, lines, height) {
|
2387 |
+
this.size += lines.length;
|
2388 |
+
this.height += height;
|
2389 |
+
for (var i = 0, e = this.children.length; i < e; ++i) {
|
2390 |
+
var child = this.children[i], sz = child.chunkSize();
|
2391 |
+
if (at <= sz) {
|
2392 |
+
child.insertHeight(at, lines, height);
|
2393 |
+
if (child.lines && child.lines.length > 50) {
|
2394 |
+
while (child.lines.length > 50) {
|
2395 |
+
var spilled = child.lines.splice(child.lines.length - 25, 25);
|
2396 |
+
var newleaf = new LeafChunk(spilled);
|
2397 |
+
child.height -= newleaf.height;
|
2398 |
+
this.children.splice(i + 1, 0, newleaf);
|
2399 |
+
newleaf.parent = this;
|
2400 |
+
}
|
2401 |
+
this.maybeSpill();
|
2402 |
+
}
|
2403 |
+
break;
|
2404 |
+
}
|
2405 |
+
at -= sz;
|
2406 |
+
}
|
2407 |
+
},
|
2408 |
+
maybeSpill: function() {
|
2409 |
+
if (this.children.length <= 10) return;
|
2410 |
+
var me = this;
|
2411 |
+
do {
|
2412 |
+
var spilled = me.children.splice(me.children.length - 5, 5);
|
2413 |
+
var sibling = new BranchChunk(spilled);
|
2414 |
+
if (!me.parent) { // Become the parent node
|
2415 |
+
var copy = new BranchChunk(me.children);
|
2416 |
+
copy.parent = me;
|
2417 |
+
me.children = [copy, sibling];
|
2418 |
+
me = copy;
|
2419 |
+
} else {
|
2420 |
+
me.size -= sibling.size;
|
2421 |
+
me.height -= sibling.height;
|
2422 |
+
var myIndex = indexOf(me.parent.children, me);
|
2423 |
+
me.parent.children.splice(myIndex + 1, 0, sibling);
|
2424 |
+
}
|
2425 |
+
sibling.parent = me.parent;
|
2426 |
+
} while (me.children.length > 10);
|
2427 |
+
me.parent.maybeSpill();
|
2428 |
+
},
|
2429 |
+
iter: function(from, to, op) { this.iterN(from, to - from, op); },
|
2430 |
+
iterN: function(at, n, op) {
|
2431 |
+
for (var i = 0, e = this.children.length; i < e; ++i) {
|
2432 |
+
var child = this.children[i], sz = child.chunkSize();
|
2433 |
+
if (at < sz) {
|
2434 |
+
var used = Math.min(n, sz - at);
|
2435 |
+
if (child.iterN(at, used, op)) return true;
|
2436 |
+
if ((n -= used) == 0) break;
|
2437 |
+
at = 0;
|
2438 |
+
} else at -= sz;
|
2439 |
+
}
|
2440 |
+
}
|
2441 |
+
};
|
2442 |
+
|
2443 |
+
function getLineAt(chunk, n) {
|
2444 |
+
for (;;) {
|
2445 |
+
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
2446 |
+
var child = chunk.children[i], sz = child.chunkSize();
|
2447 |
+
if (n < sz) { chunk = child; break; }
|
2448 |
+
n -= sz;
|
2449 |
+
}
|
2450 |
+
if (chunk.lines) return chunk.lines[n];
|
2451 |
+
}
|
2452 |
+
}
|
2453 |
+
function lineNo(line) {
|
2454 |
+
if (line.parent == null) return null;
|
2455 |
+
var cur = line.parent, no = indexOf(cur.lines, line);
|
2456 |
+
for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
|
2457 |
+
for (var i = 0, e = chunk.children.length; ; ++i) {
|
2458 |
+
if (chunk.children[i] == cur) break;
|
2459 |
+
no += chunk.children[i].chunkSize();
|
2460 |
+
}
|
2461 |
+
}
|
2462 |
+
return no;
|
2463 |
+
}
|
2464 |
+
function lineAtHeight(chunk, h) {
|
2465 |
+
var n = 0;
|
2466 |
+
outer: do {
|
2467 |
+
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
2468 |
+
var child = chunk.children[i], ch = child.height;
|
2469 |
+
if (h < ch) { chunk = child; continue outer; }
|
2470 |
+
h -= ch;
|
2471 |
+
n += child.chunkSize();
|
2472 |
+
}
|
2473 |
+
return n;
|
2474 |
+
} while (!chunk.lines);
|
2475 |
+
for (var i = 0, e = chunk.lines.length; i < e; ++i) {
|
2476 |
+
var line = chunk.lines[i], lh = line.height;
|
2477 |
+
if (h < lh) break;
|
2478 |
+
h -= lh;
|
2479 |
+
}
|
2480 |
+
return n + i;
|
2481 |
+
}
|
2482 |
+
function heightAtLine(chunk, n) {
|
2483 |
+
var h = 0;
|
2484 |
+
outer: do {
|
2485 |
+
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
2486 |
+
var child = chunk.children[i], sz = child.chunkSize();
|
2487 |
+
if (n < sz) { chunk = child; continue outer; }
|
2488 |
+
n -= sz;
|
2489 |
+
h += child.height;
|
2490 |
+
}
|
2491 |
+
return h;
|
2492 |
+
} while (!chunk.lines);
|
2493 |
+
for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
|
2494 |
+
return h;
|
2495 |
+
}
|
2496 |
+
|
2497 |
// The history object 'chunks' changes that are made close together
|
2498 |
// and at almost the same time into bigger undoable units.
|
2499 |
function History() {
|
2527 |
}
|
2528 |
};
|
2529 |
|
2530 |
+
function stopMethod() {e_stop(this);}
|
|
|
|
|
|
|
|
|
2531 |
// Ensure an event has a stop method.
|
2532 |
function addStop(event) {
|
2533 |
+
if (!event.stop) event.stop = stopMethod;
|
2534 |
return event;
|
2535 |
}
|
2536 |
|
2537 |
+
function e_preventDefault(e) {
|
2538 |
+
if (e.preventDefault) e.preventDefault();
|
2539 |
+
else e.returnValue = false;
|
2540 |
+
}
|
2541 |
+
function e_stopPropagation(e) {
|
2542 |
+
if (e.stopPropagation) e.stopPropagation();
|
2543 |
+
else e.cancelBubble = true;
|
2544 |
+
}
|
2545 |
+
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
|
2546 |
+
function e_target(e) {return e.target || e.srcElement;}
|
2547 |
+
function e_button(e) {
|
2548 |
+
if (e.which) return e.which;
|
2549 |
+
else if (e.button & 1) return 1;
|
2550 |
+
else if (e.button & 2) return 3;
|
2551 |
+
else if (e.button & 4) return 2;
|
2552 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2553 |
|
2554 |
// Event handler registration. If disconnect is true, it'll return a
|
2555 |
// function that unregisters the handler.
|
2556 |
function connect(node, type, handler, disconnect) {
|
2557 |
+
function wrapHandler(event) {handler(event || window.event);}
|
2558 |
if (typeof node.addEventListener == "function") {
|
2559 |
node.addEventListener(type, wrapHandler, false);
|
2560 |
if (disconnect) return function() {node.removeEventListener(type, wrapHandler, false);};
|
2568 |
function Delayed() {this.id = null;}
|
2569 |
Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
|
2570 |
|
2571 |
+
// Detect drag-and-drop
|
2572 |
+
var dragAndDrop = function() {
|
2573 |
+
// IE8 has ondragstart and ondrop properties, but doesn't seem to
|
2574 |
+
// actually support ondragstart the way it's supposed to work.
|
2575 |
+
if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
|
2576 |
+
var div = document.createElement('div');
|
2577 |
+
return "draggable" in div;
|
2578 |
+
}();
|
2579 |
|
2580 |
var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
|
2581 |
var ie = /MSIE \d/.test(navigator.userAgent);
|
2582 |
+
var webkit = /WebKit\//.test(navigator.userAgent);
|
2583 |
|
2584 |
var lineSep = "\n";
|
2585 |
// Feature-detect whether newlines in textareas are converted to \r\n
|
2591 |
|
2592 |
var tabSize = 8;
|
2593 |
var mac = /Mac/.test(navigator.platform);
|
2594 |
+
var win = /Win/.test(navigator.platform);
|
2595 |
var movementKeys = {};
|
2596 |
for (var i = 35; i <= 40; ++i)
|
2597 |
movementKeys[i] = movementKeys["c" + i] = true;
|
2610 |
return n;
|
2611 |
}
|
2612 |
|
2613 |
+
function computedStyle(elt) {
|
2614 |
+
if (elt.currentStyle) return elt.currentStyle;
|
2615 |
+
return window.getComputedStyle(elt, null);
|
2616 |
+
}
|
2617 |
+
|
2618 |
// Find the position of an element by following the offsetParent chain.
|
2619 |
// If screen==true, it returns screen (rather than page) coordinates.
|
2620 |
function eltOffset(node, screen) {
|
2621 |
+
var bod = node.ownerDocument.body;
|
2622 |
+
var x = 0, y = 0, skipBody = false;
|
2623 |
for (var n = node; n; n = n.offsetParent) {
|
2624 |
+
var ol = n.offsetLeft, ot = n.offsetTop;
|
2625 |
+
// Firefox reports weird inverted offsets when the body has a border.
|
2626 |
+
if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
|
2627 |
+
else { x += ol, y += ot; }
|
2628 |
+
if (screen && computedStyle(n).position == "fixed")
|
2629 |
+
skipBody = true;
|
2630 |
+
}
|
2631 |
+
var e = screen && !skipBody ? null : bod;
|
2632 |
for (var n = node.parentNode; n != e; n = n.parentNode)
|
2633 |
if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
|
2634 |
return {left: x, top: y};
|
2635 |
}
|
2636 |
+
// Use the faster and saner getBoundingClientRect method when possible.
|
2637 |
+
if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
|
2638 |
+
// Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
|
2639 |
+
// since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
|
2640 |
+
try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
|
2641 |
+
catch(e) { box = {top: 0, left: 0}; }
|
2642 |
+
if (!screen) {
|
2643 |
+
// Get the toplevel scroll, working around browser differences.
|
2644 |
+
if (window.pageYOffset == null) {
|
2645 |
+
var t = document.documentElement || document.body.parentNode;
|
2646 |
+
if (t.scrollTop == null) t = document.body;
|
2647 |
+
box.top += t.scrollTop; box.left += t.scrollLeft;
|
2648 |
+
} else {
|
2649 |
+
box.top += window.pageYOffset; box.left += window.pageXOffset;
|
2650 |
+
}
|
2651 |
+
}
|
2652 |
+
return box;
|
2653 |
+
};
|
2654 |
+
|
2655 |
// Get a node's text content.
|
2656 |
function eltText(node) {
|
2657 |
return node.textContent || node.innerText || node.nodeValue || "";
|
2662 |
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
|
2663 |
function copyPos(x) {return {line: x.line, ch: x.ch};}
|
2664 |
|
2665 |
+
var escapeElement = document.createElement("pre");
|
2666 |
function htmlEscape(str) {
|
2667 |
+
if (badTextContent) {
|
2668 |
+
escapeElement.innerHTML = "";
|
2669 |
+
escapeElement.appendChild(document.createTextNode(str));
|
2670 |
+
} else {
|
2671 |
+
escapeElement.textContent = str;
|
2672 |
+
}
|
2673 |
+
return escapeElement.innerHTML;
|
2674 |
}
|
2675 |
+
var badTextContent = htmlEscape("\t") != "\t";
|
2676 |
CodeMirror.htmlEscape = htmlEscape;
|
2677 |
|
2678 |
// Used to position the cursor after an undo/redo by finding the
|
2694 |
|
2695 |
// See if "".split is the broken IE version, if so, provide an
|
2696 |
// alternative way to split lines.
|
2697 |
+
var splitLines, selRange, setSelRange;
|
2698 |
if ("\n\nb".split(/\n/).length != 3)
|
2699 |
+
splitLines = function(string) {
|
2700 |
var pos = 0, nl, result = [];
|
2701 |
while ((nl = string.indexOf("\n", pos)) > -1) {
|
2702 |
result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
|
2706 |
return result;
|
2707 |
};
|
2708 |
else
|
2709 |
+
splitLines = function(string){return string.split(/\r?\n/);};
|
2710 |
CodeMirror.splitLines = splitLines;
|
2711 |
|
2712 |
// Sane model of finding and setting the selection in a textarea
|
2713 |
if (window.getSelection) {
|
2714 |
+
selRange = function(te) {
|
2715 |
try {return {start: te.selectionStart, end: te.selectionEnd};}
|
2716 |
catch(e) {return null;}
|
2717 |
};
|
2718 |
+
if (webkit)
|
2719 |
// On Safari, selection set with setSelectionRange are in a sort
|
2720 |
// of limbo wrt their anchor. If you press shift-left in them,
|
2721 |
// the anchor is put at the end, and the selection expanded to
|
2722 |
// the left. If you press shift-right, the anchor ends up at the
|
2723 |
// front. This is not what CodeMirror wants, so it does a
|
2724 |
// spurious modify() call to get out of limbo.
|
2725 |
+
setSelRange = function(te, start, end) {
|
2726 |
if (start == end)
|
2727 |
te.setSelectionRange(start, end);
|
2728 |
else {
|
2731 |
}
|
2732 |
};
|
2733 |
else
|
2734 |
+
setSelRange = function(te, start, end) {
|
2735 |
try {te.setSelectionRange(start, end);}
|
2736 |
catch(e) {} // Fails on Firefox when textarea isn't part of the document
|
2737 |
};
|
2738 |
}
|
2739 |
// IE model. Don't ask.
|
2740 |
else {
|
2741 |
+
selRange = function(te) {
|
2742 |
try {var range = te.ownerDocument.selection.createRange();}
|
2743 |
catch(e) {return null;}
|
2744 |
if (!range || range.parentElement() != te) return null;
|
2760 |
for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {}
|
2761 |
return {start: start, end: end};
|
2762 |
};
|
2763 |
+
setSelRange = function(te, start, end) {
|
2764 |
var range = te.createTextRange();
|
2765 |
range.collapse(true);
|
2766 |
var endrange = range.duplicate();
|
libraries/CodeMirror2/mode/clike/clike.js
ADDED
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineMode("clike", function(config, parserConfig) {
|
2 |
+
var indentUnit = config.indentUnit,
|
3 |
+
keywords = parserConfig.keywords || {},
|
4 |
+
blockKeywords = parserConfig.blockKeywords || {},
|
5 |
+
atoms = parserConfig.atoms || {},
|
6 |
+
hooks = parserConfig.hooks || {},
|
7 |
+
multiLineStrings = parserConfig.multiLineStrings;
|
8 |
+
var isOperatorChar = /[+\-*&%=<>!?|\/]/;
|
9 |
+
|
10 |
+
var curPunc;
|
11 |
+
|
12 |
+
function tokenBase(stream, state) {
|
13 |
+
var ch = stream.next();
|
14 |
+
if (hooks[ch]) {
|
15 |
+
var result = hooks[ch](stream, state);
|
16 |
+
if (result !== false) return result;
|
17 |
+
}
|
18 |
+
if (ch == '"' || ch == "'") {
|
19 |
+
state.tokenize = tokenString(ch);
|
20 |
+
return state.tokenize(stream, state);
|
21 |
+
}
|
22 |
+
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
23 |
+
curPunc = ch;
|
24 |
+
return null
|
25 |
+
}
|
26 |
+
if (/\d/.test(ch)) {
|
27 |
+
stream.eatWhile(/[\w\.]/);
|
28 |
+
return "number";
|
29 |
+
}
|
30 |
+
if (ch == "/") {
|
31 |
+
if (stream.eat("*")) {
|
32 |
+
state.tokenize = tokenComment;
|
33 |
+
return tokenComment(stream, state);
|
34 |
+
}
|
35 |
+
if (stream.eat("/")) {
|
36 |
+
stream.skipToEnd();
|
37 |
+
return "comment";
|
38 |
+
}
|
39 |
+
}
|
40 |
+
if (isOperatorChar.test(ch)) {
|
41 |
+
stream.eatWhile(isOperatorChar);
|
42 |
+
return "operator";
|
43 |
+
}
|
44 |
+
stream.eatWhile(/[\w\$_]/);
|
45 |
+
var cur = stream.current();
|
46 |
+
if (keywords.propertyIsEnumerable(cur)) {
|
47 |
+
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
48 |
+
return "keyword";
|
49 |
+
}
|
50 |
+
if (atoms.propertyIsEnumerable(cur)) return "atom";
|
51 |
+
return "word";
|
52 |
+
}
|
53 |
+
|
54 |
+
function tokenString(quote) {
|
55 |
+
return function(stream, state) {
|
56 |
+
var escaped = false, next, end = false;
|
57 |
+
while ((next = stream.next()) != null) {
|
58 |
+
if (next == quote && !escaped) {end = true; break;}
|
59 |
+
escaped = !escaped && next == "\\";
|
60 |
+
}
|
61 |
+
if (end || !(escaped || multiLineStrings))
|
62 |
+
state.tokenize = tokenBase;
|
63 |
+
return "string";
|
64 |
+
};
|
65 |
+
}
|
66 |
+
|
67 |
+
function tokenComment(stream, state) {
|
68 |
+
var maybeEnd = false, ch;
|
69 |
+
while (ch = stream.next()) {
|
70 |
+
if (ch == "/" && maybeEnd) {
|
71 |
+
state.tokenize = tokenBase;
|
72 |
+
break;
|
73 |
+
}
|
74 |
+
maybeEnd = (ch == "*");
|
75 |
+
}
|
76 |
+
return "comment";
|
77 |
+
}
|
78 |
+
|
79 |
+
function Context(indented, column, type, align, prev) {
|
80 |
+
this.indented = indented;
|
81 |
+
this.column = column;
|
82 |
+
this.type = type;
|
83 |
+
this.align = align;
|
84 |
+
this.prev = prev;
|
85 |
+
}
|
86 |
+
function pushContext(state, col, type) {
|
87 |
+
return state.context = new Context(state.indented, col, type, null, state.context);
|
88 |
+
}
|
89 |
+
function popContext(state) {
|
90 |
+
var t = state.context.type;
|
91 |
+
if (t == ")" || t == "]" || t == "}")
|
92 |
+
state.indented = state.context.indented;
|
93 |
+
return state.context = state.context.prev;
|
94 |
+
}
|
95 |
+
|
96 |
+
// Interface
|
97 |
+
|
98 |
+
return {
|
99 |
+
startState: function(basecolumn) {
|
100 |
+
return {
|
101 |
+
tokenize: null,
|
102 |
+
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
|
103 |
+
indented: 0,
|
104 |
+
startOfLine: true
|
105 |
+
};
|
106 |
+
},
|
107 |
+
|
108 |
+
token: function(stream, state) {
|
109 |
+
var ctx = state.context;
|
110 |
+
if (stream.sol()) {
|
111 |
+
if (ctx.align == null) ctx.align = false;
|
112 |
+
state.indented = stream.indentation();
|
113 |
+
state.startOfLine = true;
|
114 |
+
}
|
115 |
+
if (stream.eatSpace()) return null;
|
116 |
+
curPunc = null;
|
117 |
+
var style = (state.tokenize || tokenBase)(stream, state);
|
118 |
+
if (style == "comment" || style == "meta") return style;
|
119 |
+
if (ctx.align == null) ctx.align = true;
|
120 |
+
|
121 |
+
if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state);
|
122 |
+
else if (curPunc == "{") pushContext(state, stream.column(), "}");
|
123 |
+
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
124 |
+
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
125 |
+
else if (curPunc == "}") {
|
126 |
+
while (ctx.type == "statement") ctx = popContext(state);
|
127 |
+
if (ctx.type == "}") ctx = popContext(state);
|
128 |
+
while (ctx.type == "statement") ctx = popContext(state);
|
129 |
+
}
|
130 |
+
else if (curPunc == ctx.type) popContext(state);
|
131 |
+
else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement"))
|
132 |
+
pushContext(state, stream.column(), "statement");
|
133 |
+
state.startOfLine = false;
|
134 |
+
return style;
|
135 |
+
},
|
136 |
+
|
137 |
+
indent: function(state, textAfter) {
|
138 |
+
if (state.tokenize != tokenBase && state.tokenize != null) return 0;
|
139 |
+
var firstChar = textAfter && textAfter.charAt(0), ctx = state.context, closing = firstChar == ctx.type;
|
140 |
+
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit);
|
141 |
+
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
142 |
+
else return ctx.indented + (closing ? 0 : indentUnit);
|
143 |
+
},
|
144 |
+
|
145 |
+
electricChars: "{}"
|
146 |
+
};
|
147 |
+
});
|
148 |
+
|
149 |
+
(function() {
|
150 |
+
function words(str) {
|
151 |
+
var obj = {}, words = str.split(" ");
|
152 |
+
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
|
153 |
+
return obj;
|
154 |
+
}
|
155 |
+
var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
|
156 |
+
"double static else struct entry switch extern typedef float union for unsigned " +
|
157 |
+
"goto while enum void const signed volatile";
|
158 |
+
|
159 |
+
function cppHook(stream, state) {
|
160 |
+
if (!state.startOfLine) return false;
|
161 |
+
stream.skipToEnd();
|
162 |
+
return "meta";
|
163 |
+
}
|
164 |
+
|
165 |
+
// C#-style strings where "" escapes a quote.
|
166 |
+
function tokenAtString(stream, state) {
|
167 |
+
var next;
|
168 |
+
while ((next = stream.next()) != null) {
|
169 |
+
if (next == '"' && !stream.eat('"')) {
|
170 |
+
state.tokenize = null;
|
171 |
+
break;
|
172 |
+
}
|
173 |
+
}
|
174 |
+
return "string";
|
175 |
+
}
|
176 |
+
|
177 |
+
CodeMirror.defineMIME("text/x-csrc", {
|
178 |
+
name: "clike",
|
179 |
+
keywords: words(cKeywords),
|
180 |
+
blockKeywords: words("case do else for if switch while struct"),
|
181 |
+
atoms: words("null"),
|
182 |
+
hooks: {"#": cppHook}
|
183 |
+
});
|
184 |
+
CodeMirror.defineMIME("text/x-c++src", {
|
185 |
+
name: "clike",
|
186 |
+
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
|
187 |
+
"static_cast typeid catch operator template typename class friend private " +
|
188 |
+
"this using const_cast inline public throw virtual delete mutable protected " +
|
189 |
+
"wchar_t"),
|
190 |
+
blockKeywords: words("catch class do else finally for if struct switch try while"),
|
191 |
+
atoms: words("true false null"),
|
192 |
+
hooks: {"#": cppHook}
|
193 |
+
});
|
194 |
+
CodeMirror.defineMIME("text/x-java", {
|
195 |
+
name: "clike",
|
196 |
+
keywords: words("abstract assert boolean break byte case catch char class const continue default " +
|
197 |
+
"do double else enum extends final finally float for goto if implements import " +
|
198 |
+
"instanceof int interface long native new package private protected public " +
|
199 |
+
"return short static strictfp super switch synchronized this throw throws transient " +
|
200 |
+
"try void volatile while"),
|
201 |
+
blockKeywords: words("catch class do else finally for if switch try while"),
|
202 |
+
atoms: words("true false null"),
|
203 |
+
hooks: {
|
204 |
+
"@": function(stream, state) {
|
205 |
+
stream.eatWhile(/[\w\$_]/);
|
206 |
+
return "meta";
|
207 |
+
}
|
208 |
+
}
|
209 |
+
});
|
210 |
+
CodeMirror.defineMIME("text/x-csharp", {
|
211 |
+
name: "clike",
|
212 |
+
keywords: words("abstract as base bool break byte case catch char checked class const continue decimal" +
|
213 |
+
" default delegate do double else enum event explicit extern finally fixed float for" +
|
214 |
+
" foreach goto if implicit in int interface internal is lock long namespace new object" +
|
215 |
+
" operator out override params private protected public readonly ref return sbyte sealed short" +
|
216 |
+
" sizeof stackalloc static string struct switch this throw try typeof uint ulong unchecked" +
|
217 |
+
" unsafe ushort using virtual void volatile while add alias ascending descending dynamic from get" +
|
218 |
+
" global group into join let orderby partial remove select set value var yield"),
|
219 |
+
blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
|
220 |
+
atoms: words("true false null"),
|
221 |
+
hooks: {
|
222 |
+
"@": function(stream, state) {
|
223 |
+
if (stream.eat('"')) {
|
224 |
+
state.tokenize = tokenAtString;
|
225 |
+
return tokenAtString(stream, state);
|
226 |
+
}
|
227 |
+
stream.eatWhile(/[\w\$_]/);
|
228 |
+
return "meta";
|
229 |
+
}
|
230 |
+
}
|
231 |
+
});
|
232 |
+
CodeMirror.defineMIME("text/x-groovy", {
|
233 |
+
name: "clike",
|
234 |
+
keywords: words("abstract as assert boolean break byte case catch char class const continue def default " +
|
235 |
+
"do double else enum extends final finally float for goto if implements import " +
|
236 |
+
"in instanceof int interface long native new package property private protected public " +
|
237 |
+
"return short static strictfp super switch synchronized this throw throws transient " +
|
238 |
+
"try void volatile while"),
|
239 |
+
atoms: words("true false null"),
|
240 |
+
hooks: {
|
241 |
+
"@": function(stream, state) {
|
242 |
+
stream.eatWhile(/[\w\$_]/);
|
243 |
+
return "meta";
|
244 |
+
}
|
245 |
+
}
|
246 |
+
});
|
247 |
+
}());
|
libraries/CodeMirror2/mode/clike/index.html
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>CodeMirror 2: C-like mode</title>
|
5 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
6 |
+
<script src="../../lib/codemirror.js"></script>
|
7 |
+
<script src="clike.js"></script>
|
8 |
+
<link rel="stylesheet" href="../../theme/default.css">
|
9 |
+
<link rel="stylesheet" href="../../css/docs.css">
|
10 |
+
<style>.CodeMirror {border: 2px inset #dee;}</style>
|
11 |
+
</head>
|
12 |
+
<body>
|
13 |
+
<h1>CodeMirror 2: C-like mode</h1>
|
14 |
+
|
15 |
+
<form><textarea id="code" name="code">
|
16 |
+
/* C demo code */
|
17 |
+
|
18 |
+
#include <zmq.h>
|
19 |
+
#include <pthread.h>
|
20 |
+
#include <semaphore.h>
|
21 |
+
#include <time.h>
|
22 |
+
#include <stdio.h>
|
23 |
+
#include <fcntl.h>
|
24 |
+
#include <malloc.h>
|
25 |
+
|
26 |
+
typedef struct {
|
27 |
+
void* arg_socket;
|
28 |
+
zmq_msg_t* arg_msg;
|
29 |
+
char* arg_string;
|
30 |
+
unsigned long arg_len;
|
31 |
+
int arg_int, arg_command;
|
32 |
+
|
33 |
+
int signal_fd;
|
34 |
+
int pad;
|
35 |
+
void* context;
|
36 |
+
sem_t sem;
|
37 |
+
} acl_zmq_context;
|
38 |
+
|
39 |
+
#define p(X) (context->arg_##X)
|
40 |
+
|
41 |
+
void* zmq_thread(void* context_pointer) {
|
42 |
+
acl_zmq_context* context = (acl_zmq_context*)context_pointer;
|
43 |
+
char ok = 'K', err = 'X';
|
44 |
+
int res;
|
45 |
+
|
46 |
+
while (1) {
|
47 |
+
while ((res = sem_wait(&context->sem)) == EINTR);
|
48 |
+
if (res) {write(context->signal_fd, &err, 1); goto cleanup;}
|
49 |
+
switch(p(command)) {
|
50 |
+
case 0: goto cleanup;
|
51 |
+
case 1: p(socket) = zmq_socket(context->context, p(int)); break;
|
52 |
+
case 2: p(int) = zmq_close(p(socket)); break;
|
53 |
+
case 3: p(int) = zmq_bind(p(socket), p(string)); break;
|
54 |
+
case 4: p(int) = zmq_connect(p(socket), p(string)); break;
|
55 |
+
case 5: p(int) = zmq_getsockopt(p(socket), p(int), (void*)p(string), &p(len)); break;
|
56 |
+
case 6: p(int) = zmq_setsockopt(p(socket), p(int), (void*)p(string), p(len)); break;
|
57 |
+
case 7: p(int) = zmq_send(p(socket), p(msg), p(int)); break;
|
58 |
+
case 8: p(int) = zmq_recv(p(socket), p(msg), p(int)); break;
|
59 |
+
case 9: p(int) = zmq_poll(p(socket), p(int), p(len)); break;
|
60 |
+
}
|
61 |
+
p(command) = errno;
|
62 |
+
write(context->signal_fd, &ok, 1);
|
63 |
+
}
|
64 |
+
cleanup:
|
65 |
+
close(context->signal_fd);
|
66 |
+
free(context_pointer);
|
67 |
+
return 0;
|
68 |
+
}
|
69 |
+
|
70 |
+
void* zmq_thread_init(void* zmq_context, int signal_fd) {
|
71 |
+
acl_zmq_context* context = malloc(sizeof(acl_zmq_context));
|
72 |
+
pthread_t thread;
|
73 |
+
|
74 |
+
context->context = zmq_context;
|
75 |
+
context->signal_fd = signal_fd;
|
76 |
+
sem_init(&context->sem, 1, 0);
|
77 |
+
pthread_create(&thread, 0, &zmq_thread, context);
|
78 |
+
pthread_detach(thread);
|
79 |
+
return context;
|
80 |
+
}
|
81 |
+
</textarea></form>
|
82 |
+
|
83 |
+
<script>
|
84 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
85 |
+
lineNumbers: true,
|
86 |
+
matchBrackets: true,
|
87 |
+
mode: "text/x-csrc"
|
88 |
+
});
|
89 |
+
</script>
|
90 |
+
|
91 |
+
<p>Simple mode that tries to handle C-like languages as well as it
|
92 |
+
can. Takes two configuration parameters: <code>keywords</code>, an
|
93 |
+
object whose property names are the keywords in the language,
|
94 |
+
and <code>useCPP</code>, which determines whether C preprocessor
|
95 |
+
directives are recognized.</p>
|
96 |
+
|
97 |
+
<p><strong>MIME types defined:</strong> <code>text/x-csrc</code>
|
98 |
+
(C code), <code>text/x-c++src</code> (C++
|
99 |
+
code), <code>text/x-java</code> (Java
|
100 |
+
code), <code>text/x-groovy</code> (Groovy code).</p>
|
101 |
+
</body>
|
102 |
+
</html>
|
libraries/{codemirror/mode → CodeMirror2/mode/css}/css.js
RENAMED
@@ -4,7 +4,7 @@ CodeMirror.defineMode("css", function(config) {
|
|
4 |
|
5 |
function tokenBase(stream, state) {
|
6 |
var ch = stream.next();
|
7 |
-
if (ch == "@") {stream.eatWhile(
|
8 |
else if (ch == "/" && stream.eat("*")) {
|
9 |
state.tokenize = tokenCComment;
|
10 |
return tokenCComment(stream, state);
|
@@ -20,7 +20,7 @@ CodeMirror.defineMode("css", function(config) {
|
|
20 |
return state.tokenize(stream, state);
|
21 |
}
|
22 |
else if (ch == "#") {
|
23 |
-
stream.eatWhile(
|
24 |
return ret("atom", "hash");
|
25 |
}
|
26 |
else if (ch == "!") {
|
@@ -38,7 +38,7 @@ CodeMirror.defineMode("css", function(config) {
|
|
38 |
return ret(null, ch);
|
39 |
}
|
40 |
else {
|
41 |
-
stream.eatWhile(/[\w\\\-
|
42 |
return ret("variable", "variable");
|
43 |
}
|
44 |
}
|
4 |
|
5 |
function tokenBase(stream, state) {
|
6 |
var ch = stream.next();
|
7 |
+
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());}
|
8 |
else if (ch == "/" && stream.eat("*")) {
|
9 |
state.tokenize = tokenCComment;
|
10 |
return tokenCComment(stream, state);
|
20 |
return state.tokenize(stream, state);
|
21 |
}
|
22 |
else if (ch == "#") {
|
23 |
+
stream.eatWhile(/[\w\\\-]/);
|
24 |
return ret("atom", "hash");
|
25 |
}
|
26 |
else if (ch == "!") {
|
38 |
return ret(null, ch);
|
39 |
}
|
40 |
else {
|
41 |
+
stream.eatWhile(/[\w\\\-]/);
|
42 |
return ret("variable", "variable");
|
43 |
}
|
44 |
}
|
libraries/CodeMirror2/mode/css/index.html
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>CodeMirror 2: CSS mode</title>
|
5 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
6 |
+
<script src="../../lib/codemirror.js"></script>
|
7 |
+
<script src="css.js"></script>
|
8 |
+
<link rel="stylesheet" href="../../theme/default.css">
|
9 |
+
<style>.CodeMirror {background: #f8f8f8;}</style>
|
10 |
+
<link rel="stylesheet" href="../../css/docs.css">
|
11 |
+
</head>
|
12 |
+
<body>
|
13 |
+
<h1>CodeMirror 2: CSS mode</h1>
|
14 |
+
<form><textarea id="code" name="code">
|
15 |
+
/* Some example CSS */
|
16 |
+
|
17 |
+
@import url("something.css");
|
18 |
+
|
19 |
+
body {
|
20 |
+
margin: 0;
|
21 |
+
padding: 3em 6em;
|
22 |
+
font-family: tahoma, arial, sans-serif;
|
23 |
+
color: #000;
|
24 |
+
}
|
25 |
+
|
26 |
+
#navigation a {
|
27 |
+
font-weight: bold;
|
28 |
+
text-decoration: none !important;
|
29 |
+
}
|
30 |
+
|
31 |
+
h1 {
|
32 |
+
font-size: 2.5em;
|
33 |
+
}
|
34 |
+
|
35 |
+
h2 {
|
36 |
+
font-size: 1.7em;
|
37 |
+
}
|
38 |
+
|
39 |
+
h1:before, h2:before {
|
40 |
+
content: "::";
|
41 |
+
}
|
42 |
+
|
43 |
+
code {
|
44 |
+
font-family: courier, monospace;
|
45 |
+
font-size: 80%;
|
46 |
+
color: #418A8A;
|
47 |
+
}
|
48 |
+
</textarea></form>
|
49 |
+
<script>
|
50 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
|
51 |
+
</script>
|
52 |
+
|
53 |
+
<p><strong>MIME types defined:</strong> <code>text/css</code>.</p>
|
54 |
+
|
55 |
+
</body>
|
56 |
+
</html>
|
libraries/{codemirror/mode → CodeMirror2/mode/htmlmixed}/htmlmixed.js
RENAMED
@@ -9,10 +9,12 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
|
|
9 |
if (/^script$/i.test(state.htmlState.context.tagName)) {
|
10 |
state.token = javascript;
|
11 |
state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
|
|
|
12 |
}
|
13 |
else if (/^style$/i.test(state.htmlState.context.tagName)) {
|
14 |
state.token = css;
|
15 |
state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
|
|
|
16 |
}
|
17 |
}
|
18 |
return style;
|
@@ -27,6 +29,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
|
|
27 |
if (stream.match(/^<\/\s*script\s*>/i, false)) {
|
28 |
state.token = html;
|
29 |
state.curState = null;
|
|
|
30 |
return html(stream, state);
|
31 |
}
|
32 |
return maybeBackup(stream, /<\/\s*script\s*>/,
|
@@ -36,6 +39,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
|
|
36 |
if (stream.match(/^<\/\s*style\s*>/i, false)) {
|
37 |
state.token = html;
|
38 |
state.localState = null;
|
|
|
39 |
return html(stream, state);
|
40 |
}
|
41 |
return maybeBackup(stream, /<\/\s*style\s*>/,
|
@@ -45,13 +49,14 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
|
|
45 |
return {
|
46 |
startState: function() {
|
47 |
var state = htmlMode.startState();
|
48 |
-
return {token: html, localState: null, htmlState: state};
|
49 |
},
|
50 |
|
51 |
copyState: function(state) {
|
52 |
if (state.localState)
|
53 |
var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
|
54 |
-
return {token: state.token, localState: local,
|
|
|
55 |
},
|
56 |
|
57 |
token: function(stream, state) {
|
@@ -67,6 +72,10 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
|
|
67 |
return cssMode.indent(state.localState, textAfter);
|
68 |
},
|
69 |
|
|
|
|
|
|
|
|
|
70 |
electricChars: "/{}:"
|
71 |
}
|
72 |
});
|
9 |
if (/^script$/i.test(state.htmlState.context.tagName)) {
|
10 |
state.token = javascript;
|
11 |
state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
|
12 |
+
state.mode = "javascript";
|
13 |
}
|
14 |
else if (/^style$/i.test(state.htmlState.context.tagName)) {
|
15 |
state.token = css;
|
16 |
state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
|
17 |
+
state.mode = "css";
|
18 |
}
|
19 |
}
|
20 |
return style;
|
29 |
if (stream.match(/^<\/\s*script\s*>/i, false)) {
|
30 |
state.token = html;
|
31 |
state.curState = null;
|
32 |
+
state.mode = "html";
|
33 |
return html(stream, state);
|
34 |
}
|
35 |
return maybeBackup(stream, /<\/\s*script\s*>/,
|
39 |
if (stream.match(/^<\/\s*style\s*>/i, false)) {
|
40 |
state.token = html;
|
41 |
state.localState = null;
|
42 |
+
state.mode = "html";
|
43 |
return html(stream, state);
|
44 |
}
|
45 |
return maybeBackup(stream, /<\/\s*style\s*>/,
|
49 |
return {
|
50 |
startState: function() {
|
51 |
var state = htmlMode.startState();
|
52 |
+
return {token: html, localState: null, mode: "html", htmlState: state};
|
53 |
},
|
54 |
|
55 |
copyState: function(state) {
|
56 |
if (state.localState)
|
57 |
var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
|
58 |
+
return {token: state.token, localState: local, mode: state.mode,
|
59 |
+
htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
|
60 |
},
|
61 |
|
62 |
token: function(stream, state) {
|
72 |
return cssMode.indent(state.localState, textAfter);
|
73 |
},
|
74 |
|
75 |
+
compareStates: function(a, b) {
|
76 |
+
return htmlMode.compareStates(a.htmlState, b.htmlState);
|
77 |
+
},
|
78 |
+
|
79 |
electricChars: "/{}:"
|
80 |
}
|
81 |
});
|
libraries/CodeMirror2/mode/htmlmixed/index.html
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>CodeMirror 2: HTML mixed mode</title>
|
5 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
6 |
+
<script src="../../lib/codemirror.js"></script>
|
7 |
+
<script src="../xml/xml.js"></script>
|
8 |
+
<script src="../javascript/javascript.js"></script>
|
9 |
+
<script src="../css/css.js"></script>
|
10 |
+
<link rel="stylesheet" href="../../theme/default.css">
|
11 |
+
<script src="htmlmixed.js"></script>
|
12 |
+
<link rel="stylesheet" href="../../css/docs.css">
|
13 |
+
<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
14 |
+
</head>
|
15 |
+
<body>
|
16 |
+
<h1>CodeMirror 2: HTML mixed mode</h1>
|
17 |
+
<form><textarea id="code" name="code">
|
18 |
+
<html style="color: green">
|
19 |
+
<!-- this is a comment -->
|
20 |
+
<head>
|
21 |
+
<title>Mixed HTML Example</title>
|
22 |
+
<style type="text/css">
|
23 |
+
h1 {font-family: comic sans; color: #f0f;}
|
24 |
+
div {background: yellow !important;}
|
25 |
+
body {
|
26 |
+
max-width: 50em;
|
27 |
+
margin: 1em 2em 1em 5em;
|
28 |
+
}
|
29 |
+
</style>
|
30 |
+
</head>
|
31 |
+
<body>
|
32 |
+
<h1>Mixed HTML Example</h1>
|
33 |
+
<script>
|
34 |
+
function jsFunc(arg1, arg2) {
|
35 |
+
if (arg1 && arg2) document.body.innerHTML = "achoo";
|
36 |
+
}
|
37 |
+
</script>
|
38 |
+
</body>
|
39 |
+
</html>
|
40 |
+
</textarea></form>
|
41 |
+
<script>
|
42 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "text/html", tabMode: "indent"});
|
43 |
+
</script>
|
44 |
+
|
45 |
+
<p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p>
|
46 |
+
|
47 |
+
<p><strong>MIME types defined:</strong> <code>text/html</code>
|
48 |
+
(redefined, only takes effect if you load this parser after the
|
49 |
+
XML parser).</p>
|
50 |
+
|
51 |
+
</body>
|
52 |
+
</html>
|
libraries/CodeMirror2/mode/javascript/index.html
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>CodeMirror 2: JavaScript mode</title>
|
5 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
6 |
+
<script src="../../lib/codemirror.js"></script>
|
7 |
+
<script src="javascript.js"></script>
|
8 |
+
<link rel="stylesheet" href="../../theme/default.css">
|
9 |
+
<link rel="stylesheet" href="../../css/docs.css">
|
10 |
+
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
11 |
+
</head>
|
12 |
+
<body>
|
13 |
+
<h1>CodeMirror 2: JavaScript mode</h1>
|
14 |
+
|
15 |
+
<div><textarea id="code" name="code">
|
16 |
+
// Demo code (the actual new parser character stream implementation)
|
17 |
+
|
18 |
+
function StringStream(string) {
|
19 |
+
this.pos = 0;
|
20 |
+
this.string = string;
|
21 |
+
}
|
22 |
+
|
23 |
+
StringStream.prototype = {
|
24 |
+
done: function() {return this.pos >= this.string.length;},
|
25 |
+
peek: function() {return this.string.charAt(this.pos);},
|
26 |
+
next: function() {
|
27 |
+
if (this.pos < this.string.length)
|
28 |
+
return this.string.charAt(this.pos++);
|
29 |
+
},
|
30 |
+
eat: function(match) {
|
31 |
+
var ch = this.string.charAt(this.pos);
|
32 |
+
if (typeof match == "string") var ok = ch == match;
|
33 |
+
else var ok = ch && match.test ? match.test(ch) : match(ch);
|
34 |
+
if (ok) {this.pos++; return ch;}
|
35 |
+
},
|
36 |
+
eatWhile: function(match) {
|
37 |
+
var start = this.pos;
|
38 |
+
while (this.eat(match));
|
39 |
+
if (this.pos > start) return this.string.slice(start, this.pos);
|
40 |
+
},
|
41 |
+
backUp: function(n) {this.pos -= n;},
|
42 |
+
column: function() {return this.pos;},
|
43 |
+
eatSpace: function() {
|
44 |
+
var start = this.pos;
|
45 |
+
while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
|
46 |
+
return this.pos - start;
|
47 |
+
},
|
48 |
+
match: function(pattern, consume, caseInsensitive) {
|
49 |
+
if (typeof pattern == "string") {
|
50 |
+
function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
|
51 |
+
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
|
52 |
+
if (consume !== false) this.pos += str.length;
|
53 |
+
return true;
|
54 |
+
}
|
55 |
+
}
|
56 |
+
else {
|
57 |
+
var match = this.string.slice(this.pos).match(pattern);
|
58 |
+
if (match && consume !== false) this.pos += match[0].length;
|
59 |
+
return match;
|
60 |
+
}
|
61 |
+
}
|
62 |
+
};
|
63 |
+
</textarea></div>
|
64 |
+
|
65 |
+
<script>
|
66 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
67 |
+
lineNumbers: true,
|
68 |
+
matchBrackets: true
|
69 |
+
});
|
70 |
+
</script>
|
71 |
+
|
72 |
+
<p>JavaScript mode supports a single configuration
|
73 |
+
option, <code>json</code>, which will set the mode to expect JSON
|
74 |
+
data rather than a JavaScript program.</p>
|
75 |
+
|
76 |
+
<p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>.</p>
|
77 |
+
</body>
|
78 |
+
</html>
|
libraries/{codemirror/mode → CodeMirror2/mode/javascript}/javascript.js
RENAMED
@@ -11,7 +11,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
11 |
return {
|
12 |
"if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
13 |
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
|
14 |
-
"var": kw("var"), "
|
|
|
15 |
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
16 |
"in": operator, "typeof": operator, "instanceof": operator,
|
17 |
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
|
@@ -51,11 +52,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
51 |
return ret(ch);
|
52 |
else if (ch == "0" && stream.eat(/x/i)) {
|
53 |
stream.eatWhile(/[\da-f]/i);
|
54 |
-
return ret("number", "
|
55 |
}
|
56 |
else if (/\d/.test(ch)) {
|
57 |
-
stream.match(/^\d*(?:\.\d*)?(?:
|
58 |
-
return ret("number", "
|
59 |
}
|
60 |
else if (ch == "/") {
|
61 |
if (stream.eat("*")) {
|
@@ -75,6 +76,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
75 |
return ret("operator", null, stream.current());
|
76 |
}
|
77 |
}
|
|
|
|
|
|
|
|
|
78 |
else if (isOperatorChar.test(ch)) {
|
79 |
stream.eatWhile(isOperatorChar);
|
80 |
return ret("operator", null, stream.current());
|
@@ -224,13 +229,18 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
224 |
function expression(type) {
|
225 |
if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
|
226 |
if (type == "function") return cont(functiondef);
|
227 |
-
if (type == "keyword c") return cont(
|
228 |
if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
|
229 |
if (type == "operator") return cont(expression);
|
230 |
if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
|
231 |
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
|
232 |
return cont();
|
233 |
}
|
|
|
|
|
|
|
|
|
|
|
234 |
function maybeoperator(type, value) {
|
235 |
if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
|
236 |
if (type == "operator") return cont(expression);
|
11 |
return {
|
12 |
"if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
13 |
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
|
14 |
+
"var": kw("var"), "const": kw("var"), "let": kw("var"),
|
15 |
+
"function": kw("function"), "catch": kw("catch"),
|
16 |
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
17 |
"in": operator, "typeof": operator, "instanceof": operator,
|
18 |
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
|
52 |
return ret(ch);
|
53 |
else if (ch == "0" && stream.eat(/x/i)) {
|
54 |
stream.eatWhile(/[\da-f]/i);
|
55 |
+
return ret("number", "number");
|
56 |
}
|
57 |
else if (/\d/.test(ch)) {
|
58 |
+
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
|
59 |
+
return ret("number", "number");
|
60 |
}
|
61 |
else if (ch == "/") {
|
62 |
if (stream.eat("*")) {
|
76 |
return ret("operator", null, stream.current());
|
77 |
}
|
78 |
}
|
79 |
+
else if (ch == "#") {
|
80 |
+
stream.skipToEnd();
|
81 |
+
return ret("error", "error");
|
82 |
+
}
|
83 |
else if (isOperatorChar.test(ch)) {
|
84 |
stream.eatWhile(isOperatorChar);
|
85 |
return ret("operator", null, stream.current());
|
229 |
function expression(type) {
|
230 |
if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
|
231 |
if (type == "function") return cont(functiondef);
|
232 |
+
if (type == "keyword c") return cont(maybeexpression);
|
233 |
if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
|
234 |
if (type == "operator") return cont(expression);
|
235 |
if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
|
236 |
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
|
237 |
return cont();
|
238 |
}
|
239 |
+
function maybeexpression(type) {
|
240 |
+
if (type.match(/[;\}\)\],]/)) return pass();
|
241 |
+
return pass(expression);
|
242 |
+
}
|
243 |
+
|
244 |
function maybeoperator(type, value) {
|
245 |
if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
|
246 |
if (type == "operator") return cont(expression);
|
libraries/CodeMirror2/mode/php/index.html
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>CodeMirror 2: PHP mode</title>
|
5 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
6 |
+
<script src="../../lib/codemirror.js"></script>
|
7 |
+
<script src="../xml/xml.js"></script>
|
8 |
+
<script src="../javascript/javascript.js"></script>
|
9 |
+
<script src="../css/css.js"></script>
|
10 |
+
<script src="../clike/clike.js"></script>
|
11 |
+
<script src="php.js"></script>
|
12 |
+
<link rel="stylesheet" href="../../theme/default.css">
|
13 |
+
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
14 |
+
<link rel="stylesheet" href="../../css/docs.css">
|
15 |
+
</head>
|
16 |
+
<body>
|
17 |
+
<h1>CodeMirror 2: PHP mode</h1>
|
18 |
+
|
19 |
+
<form><textarea id="code" name="code">
|
20 |
+
<?php
|
21 |
+
function hello($who) {
|
22 |
+
return "Hello " . $who;
|
23 |
+
}
|
24 |
+
?>
|
25 |
+
<p>The program says <?= hello("World") ?>.</p>
|
26 |
+
<script>
|
27 |
+
alert("And here is some JS code"); // also colored
|
28 |
+
</script>
|
29 |
+
</textarea></form>
|
30 |
+
|
31 |
+
<script>
|
32 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
33 |
+
lineNumbers: true,
|
34 |
+
matchBrackets: true,
|
35 |
+
mode: "application/x-httpd-php",
|
36 |
+
indentUnit: 8,
|
37 |
+
indentWithTabs: true,
|
38 |
+
enterMode: "keep",
|
39 |
+
tabMode: "shift"
|
40 |
+
});
|
41 |
+
</script>
|
42 |
+
|
43 |
+
<p>Simple HTML/PHP mode based on
|
44 |
+
the <a href="../clike/">C-like</a> mode. Depends on XML,
|
45 |
+
JavaScript, CSS, and C-like modes.</p>
|
46 |
+
|
47 |
+
<p><strong>MIME types defined:</strong> <code>application/x-httpd-php</code> (HTML with PHP code), <code>text/x-php</code> (plain, non-wrapped PHP code).</p>
|
48 |
+
</body>
|
49 |
+
</html>
|
libraries/CodeMirror2/mode/php/php.js
ADDED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 cfunction class clone const continue declare " +
|
17 |
+
"default do else elseif enddeclare endfor endforeach endif endswitch endwhile extends " +
|
18 |
+
"final for foreach function global goto if implements interface instanceof namespace " +
|
19 |
+
"new or private protected public static switch throw try use var while xor return" +
|
20 |
+
"die echo empty exit eval include include_once isset list require require_once print unset"),
|
21 |
+
blockKeywords: keywords("catch do else elseif for foreach if switch try while"),
|
22 |
+
atoms: keywords("true false null TRUE FALSE NULL"),
|
23 |
+
multiLineStrings: true,
|
24 |
+
hooks: {
|
25 |
+
"$": function(stream, state) {
|
26 |
+
stream.eatWhile(/[\w\$_]/);
|
27 |
+
return "variable-2";
|
28 |
+
},
|
29 |
+
"<": function(stream, state) {
|
30 |
+
if (stream.match(/<</)) {
|
31 |
+
stream.eatWhile(/[\w\.]/);
|
32 |
+
state.tokenize = heredoc(stream.current().slice(3));
|
33 |
+
return state.tokenize(stream, state);
|
34 |
+
}
|
35 |
+
return false;
|
36 |
+
},
|
37 |
+
"#": function(stream, state) {
|
38 |
+
stream.skipToEnd();
|
39 |
+
return "comment";
|
40 |
+
}
|
41 |
+
}
|
42 |
+
};
|
43 |
+
|
44 |
+
CodeMirror.defineMode("php", function(config, parserConfig) {
|
45 |
+
var htmlMode = CodeMirror.getMode(config, "text/html");
|
46 |
+
var jsMode = CodeMirror.getMode(config, "text/javascript");
|
47 |
+
var cssMode = CodeMirror.getMode(config, "text/css");
|
48 |
+
var phpMode = CodeMirror.getMode(config, phpConfig);
|
49 |
+
|
50 |
+
function dispatch(stream, state) { // TODO open PHP inside text/css
|
51 |
+
if (state.curMode == htmlMode) {
|
52 |
+
var style = htmlMode.token(stream, state.curState);
|
53 |
+
if (style == "meta" && /^<\?/.test(stream.current())) {
|
54 |
+
state.curMode = phpMode;
|
55 |
+
state.curState = state.php;
|
56 |
+
state.curClose = /^\?>/;
|
57 |
+
state.mode = 'php';
|
58 |
+
}
|
59 |
+
else if (style == "tag" && stream.current() == ">" && state.curState.context) {
|
60 |
+
if (/^script$/i.test(state.curState.context.tagName)) {
|
61 |
+
state.curMode = jsMode;
|
62 |
+
state.curState = jsMode.startState(htmlMode.indent(state.curState, ""));
|
63 |
+
state.curClose = /^<\/\s*script\s*>/i;
|
64 |
+
state.mode = 'javascript';
|
65 |
+
}
|
66 |
+
else if (/^style$/i.test(state.curState.context.tagName)) {
|
67 |
+
state.curMode = cssMode;
|
68 |
+
state.curState = cssMode.startState(htmlMode.indent(state.curState, ""));
|
69 |
+
state.curClose = /^<\/\s*style\s*>/i;
|
70 |
+
state.mode = 'css';
|
71 |
+
}
|
72 |
+
}
|
73 |
+
return style;
|
74 |
+
}
|
75 |
+
else if (stream.match(state.curClose, false)) {
|
76 |
+
state.curMode = htmlMode;
|
77 |
+
state.curState = state.html;
|
78 |
+
state.curClose = null;
|
79 |
+
state.mode = 'html';
|
80 |
+
return dispatch(stream, state);
|
81 |
+
}
|
82 |
+
else return state.curMode.token(stream, state.curState);
|
83 |
+
}
|
84 |
+
|
85 |
+
return {
|
86 |
+
startState: function() {
|
87 |
+
var html = htmlMode.startState();
|
88 |
+
return {html: html,
|
89 |
+
php: phpMode.startState(),
|
90 |
+
curMode: parserConfig.startOpen ? phpMode : htmlMode,
|
91 |
+
curState: parserConfig.startOpen ? phpMode.startState() : html,
|
92 |
+
curClose: parserConfig.startOpen ? /^\?>/ : null,
|
93 |
+
mode: parserConfig.startOpen ? 'php' : 'html'}
|
94 |
+
},
|
95 |
+
|
96 |
+
copyState: function(state) {
|
97 |
+
var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
|
98 |
+
php = state.php, phpNew = CodeMirror.copyState(phpMode, php), cur;
|
99 |
+
if (state.curState == html) cur = htmlNew;
|
100 |
+
else if (state.curState == php) cur = phpNew;
|
101 |
+
else cur = CodeMirror.copyState(state.curMode, state.curState);
|
102 |
+
return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur, curClose: state.curClose};
|
103 |
+
},
|
104 |
+
|
105 |
+
token: dispatch,
|
106 |
+
|
107 |
+
indent: function(state, textAfter) {
|
108 |
+
if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
|
109 |
+
(state.curMode == phpMode && /^\?>/.test(textAfter)))
|
110 |
+
return htmlMode.indent(state.html, textAfter);
|
111 |
+
return state.curMode.indent(state.curState, textAfter);
|
112 |
+
},
|
113 |
+
|
114 |
+
electricChars: "/{}:"
|
115 |
+
}
|
116 |
+
});
|
117 |
+
CodeMirror.defineMIME("application/x-httpd-php", "php");
|
118 |
+
CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
|
119 |
+
CodeMirror.defineMIME("text/x-php", phpConfig);
|
120 |
+
})();
|
libraries/CodeMirror2/mode/xml/index.html
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>CodeMirror 2: XML mode</title>
|
5 |
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
6 |
+
<script src="../../lib/codemirror.js"></script>
|
7 |
+
<script src="xml.js"></script>
|
8 |
+
<link rel="stylesheet" href="../../theme/default.css">
|
9 |
+
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
10 |
+
<link rel="stylesheet" href="../../css/docs.css">
|
11 |
+
</head>
|
12 |
+
<body>
|
13 |
+
<h1>CodeMirror 2: XML mode</h1>
|
14 |
+
<form><textarea id="code" name="code">
|
15 |
+
<html style="color: green">
|
16 |
+
<!-- this is a comment -->
|
17 |
+
<head>
|
18 |
+
<title>HTML Example</title>
|
19 |
+
</head>
|
20 |
+
<body>
|
21 |
+
The indentation tries to be <em>somewhat &quot;do what
|
22 |
+
I mean&quot;</em>... but might not match your style.
|
23 |
+
</body>
|
24 |
+
</html>
|
25 |
+
</textarea></form>
|
26 |
+
<script>
|
27 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
28 |
+
mode: {name: "xml", htmlMode: true},
|
29 |
+
lineNumbers: true
|
30 |
+
});
|
31 |
+
</script>
|
32 |
+
<p>The XML mode supports two configuration parameters:</p>
|
33 |
+
<dl>
|
34 |
+
<dt><code>htmlMode (boolean)</code></dt>
|
35 |
+
<dd>This switches the mode to parse HTML instead of XML. This
|
36 |
+
means attributes do not have to be quoted, and some elements
|
37 |
+
(such as <code>br</code>) do not require a closing tag.</dd>
|
38 |
+
<dt><code>alignCDATA (boolean)</code></dt>
|
39 |
+
<dd>Setting this to true will force the opening tag of CDATA
|
40 |
+
blocks to not be indented.</dd>
|
41 |
+
</dl>
|
42 |
+
|
43 |
+
<p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/html</code>.</p>
|
44 |
+
</body>
|
45 |
+
</html>
|
libraries/{codemirror/mode → CodeMirror2/mode/xml}/xml.js
RENAMED
@@ -25,7 +25,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
25 |
else return null;
|
26 |
}
|
27 |
else if (stream.match("--")) return chain(inBlock("comment", "-->"));
|
28 |
-
else if (stream.match("DOCTYPE")) {
|
29 |
stream.eatWhile(/[\w\._\-]/);
|
30 |
return chain(inBlock("meta", ">"));
|
31 |
}
|
@@ -128,7 +128,16 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
128 |
|
129 |
function element(type) {
|
130 |
if (type == "openTag") {curState.tagName = tagName; return cont(attributes, endtag(curState.startOfLine));}
|
131 |
-
else if (type == "closeTag") {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
else if (type == "string") {
|
133 |
if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata");
|
134 |
if (curState.tokenize == inText) popContext();
|
@@ -145,21 +154,30 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
145 |
return cont();
|
146 |
};
|
147 |
}
|
148 |
-
function endclosetag(
|
149 |
-
|
150 |
-
|
|
|
|
|
|
|
|
|
151 |
}
|
152 |
|
153 |
function attributes(type) {
|
154 |
if (type == "word") {setStyle = "attribute"; return cont(attributes);}
|
155 |
if (type == "equals") return cont(attvalue, attributes);
|
|
|
156 |
return pass();
|
157 |
}
|
158 |
function attvalue(type) {
|
159 |
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
|
160 |
-
if (type == "string") return cont();
|
161 |
return pass();
|
162 |
}
|
|
|
|
|
|
|
|
|
163 |
|
164 |
return {
|
165 |
startState: function() {
|
@@ -175,7 +193,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
175 |
|
176 |
setStyle = type = tagName = null;
|
177 |
var style = state.tokenize(stream, state);
|
178 |
-
|
|
|
179 |
curState = state;
|
180 |
while (true) {
|
181 |
var comb = state.cc.pop() || element;
|
@@ -198,6 +217,14 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
198 |
else return 0;
|
199 |
},
|
200 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
electricChars: "/"
|
202 |
};
|
203 |
});
|
25 |
else return null;
|
26 |
}
|
27 |
else if (stream.match("--")) return chain(inBlock("comment", "-->"));
|
28 |
+
else if (stream.match("DOCTYPE", true, true)) {
|
29 |
stream.eatWhile(/[\w\._\-]/);
|
30 |
return chain(inBlock("meta", ">"));
|
31 |
}
|
128 |
|
129 |
function element(type) {
|
130 |
if (type == "openTag") {curState.tagName = tagName; return cont(attributes, endtag(curState.startOfLine));}
|
131 |
+
else if (type == "closeTag") {
|
132 |
+
var err = false;
|
133 |
+
if (curState.context) {
|
134 |
+
err = curState.context.tagName != tagName;
|
135 |
+
} else {
|
136 |
+
err = true;
|
137 |
+
}
|
138 |
+
if (err) setStyle = "error";
|
139 |
+
return cont(endclosetag(err));
|
140 |
+
}
|
141 |
else if (type == "string") {
|
142 |
if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata");
|
143 |
if (curState.tokenize == inText) popContext();
|
154 |
return cont();
|
155 |
};
|
156 |
}
|
157 |
+
function endclosetag(err) {
|
158 |
+
return function(type) {
|
159 |
+
if (err) setStyle = "error";
|
160 |
+
if (type == "endTag") { popContext(); return cont(); }
|
161 |
+
setStyle = "error";
|
162 |
+
return cont(arguments.callee);
|
163 |
+
}
|
164 |
}
|
165 |
|
166 |
function attributes(type) {
|
167 |
if (type == "word") {setStyle = "attribute"; return cont(attributes);}
|
168 |
if (type == "equals") return cont(attvalue, attributes);
|
169 |
+
if (type == "string") {setStyle = "error"; return cont(attributes);}
|
170 |
return pass();
|
171 |
}
|
172 |
function attvalue(type) {
|
173 |
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
|
174 |
+
if (type == "string") return cont(attvaluemaybe);
|
175 |
return pass();
|
176 |
}
|
177 |
+
function attvaluemaybe(type) {
|
178 |
+
if (type == "string") return cont(attvaluemaybe);
|
179 |
+
else return pass();
|
180 |
+
}
|
181 |
|
182 |
return {
|
183 |
startState: function() {
|
193 |
|
194 |
setStyle = type = tagName = null;
|
195 |
var style = state.tokenize(stream, state);
|
196 |
+
state.type = type;
|
197 |
+
if ((style || type) && style != "comment") {
|
198 |
curState = state;
|
199 |
while (true) {
|
200 |
var comb = state.cc.pop() || element;
|
217 |
else return 0;
|
218 |
},
|
219 |
|
220 |
+
compareStates: function(a, b) {
|
221 |
+
if (a.indented != b.indented || a.tokenize != b.tokenize) return false;
|
222 |
+
for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
|
223 |
+
if (!ca || !cb) return ca == cb;
|
224 |
+
if (ca.tagName != cb.tagName) return false;
|
225 |
+
}
|
226 |
+
},
|
227 |
+
|
228 |
electricChars: "/"
|
229 |
};
|
230 |
});
|
libraries/CodeMirror2/theme/cobalt.css
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.cm-s-cobalt { background: #002240; color: white; }
|
2 |
+
.cm-s-cobalt span.CodeMirror-selected { background: #b36539 !important; }
|
3 |
+
.cm-s-cobalt .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; }
|
4 |
+
.cm-s-cobalt .CodeMirror-gutter-text { color: #d0d0d0; }
|
5 |
+
.cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white !important; }
|
6 |
+
|
7 |
+
.cm-s-cobalt span.cm-comment { color: #08f; }
|
8 |
+
.cm-s-cobalt span.cm-atom { color: #845dc4; }
|
9 |
+
.cm-s-cobalt span.cm-number, .cm-s-cobalt span.cm-attribute { color: #ff80e1; }
|
10 |
+
.cm-s-cobalt span.cm-keyword { color: #ffee80; }
|
11 |
+
.cm-s-cobalt span.cm-string { color: #3ad900; }
|
12 |
+
.cm-s-cobalt span.cm-meta { color: #ff9d00; }
|
13 |
+
.cm-s-cobalt span.cm-variable-2, .cm-s-cobalt span.cm-tag { color: #9effff; }
|
14 |
+
.cm-s-cobalt span.cm-variable-3, .cm-s-cobalt span.cm-def { color: white; }
|
15 |
+
.cm-s-cobalt span.cm-error { color: #9d1e15; }
|
16 |
+
.cm-s-cobalt span.cm-bracket { color: #d8d8d8; }
|
17 |
+
.cm-s-cobalt span.cm-builtin, .cm-s-cobalt span.cm-special { color: #ff9e59; }
|
libraries/{codemirror → CodeMirror2}/theme/default.css
RENAMED
@@ -4,11 +4,12 @@
|
|
4 |
.cm-s-default span.cm-def {color: #00f;}
|
5 |
.cm-s-default span.cm-variable {color: black;}
|
6 |
.cm-s-default span.cm-variable-2 {color: #05a;}
|
7 |
-
.cm-s-default span.cm-variable-3 {color: #
|
8 |
.cm-s-default span.cm-property {color: black;}
|
9 |
.cm-s-default span.cm-operator {color: black;}
|
10 |
.cm-s-default span.cm-comment {color: #a50;}
|
11 |
.cm-s-default span.cm-string {color: #a11;}
|
|
|
12 |
.cm-s-default span.cm-meta {color: #555;}
|
13 |
.cm-s-default span.cm-error {color: #f00;}
|
14 |
.cm-s-default span.cm-qualifier {color: #555;}
|
4 |
.cm-s-default span.cm-def {color: #00f;}
|
5 |
.cm-s-default span.cm-variable {color: black;}
|
6 |
.cm-s-default span.cm-variable-2 {color: #05a;}
|
7 |
+
.cm-s-default span.cm-variable-3 {color: #085;}
|
8 |
.cm-s-default span.cm-property {color: black;}
|
9 |
.cm-s-default span.cm-operator {color: black;}
|
10 |
.cm-s-default span.cm-comment {color: #a50;}
|
11 |
.cm-s-default span.cm-string {color: #a11;}
|
12 |
+
.cm-s-default span.cm-string-2 {color: #f50;}
|
13 |
.cm-s-default span.cm-meta {color: #555;}
|
14 |
.cm-s-default span.cm-error {color: #f00;}
|
15 |
.cm-s-default span.cm-qualifier {color: #555;}
|
libraries/CodeMirror2/theme/eclipse.css
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.cm-s-eclipse span.cm-meta {color: #FF1717;}
|
2 |
+
.cm-s-eclipse span.cm-keyword { font-weight: bold; color: #7F0055; }
|
3 |
+
.cm-s-eclipse span.cm-atom {color: #219;}
|
4 |
+
.cm-s-eclipse span.cm-number {color: #164;}
|
5 |
+
.cm-s-eclipse span.cm-def {color: #00f;}
|
6 |
+
.cm-s-eclipse span.cm-variable {color: black;}
|
7 |
+
.cm-s-eclipse span.cm-variable-2 {color: #0000C0;}
|
8 |
+
.cm-s-eclipse span.cm-variable-3 {color: #0000C0;}
|
9 |
+
.cm-s-eclipse span.cm-property {color: black;}
|
10 |
+
.cm-s-eclipse span.cm-operator {color: black;}
|
11 |
+
.cm-s-eclipse span.cm-comment {color: #3F7F5F;}
|
12 |
+
.cm-s-eclipse span.cm-string {color: #2A00FF;}
|
13 |
+
.cm-s-eclipse span.cm-string-2 {color: #f50;}
|
14 |
+
.cm-s-eclipse span.cm-error {color: #f00;}
|
15 |
+
.cm-s-eclipse span.cm-qualifier {color: #555;}
|
16 |
+
.cm-s-eclipse span.cm-builtin {color: #30a;}
|
17 |
+
.cm-s-eclipse span.cm-bracket {color: #cc7;}
|
18 |
+
.cm-s-eclipse span.cm-tag {color: #170;}
|
19 |
+
.cm-s-eclipse span.cm-attribute {color: #00c;}
|
20 |
+
|
21 |
+
.CodeMirror-matchingbracket{
|
22 |
+
border:1px solid grey;
|
23 |
+
color:black !important;;
|
24 |
+
}
|
libraries/{codemirror → CodeMirror2}/theme/elegant.css
RENAMED
File without changes
|
libraries/CodeMirror2/theme/monokai.css
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Based on Sublime Text's Monokai theme */
|
2 |
+
|
3 |
+
.cm-s-monokai { background: #272822; color: #F8F8F2; }
|
4 |
+
.cm-s-monokai span.CodeMirror-selected { background: #FFE792 !important; }
|
5 |
+
.cm-s-monokai .CodeMirror-gutter { background: #272822; border-right: 0px; }
|
6 |
+
.cm-s-monokai .CodeMirror-gutter-text { color: #d0d0d0; }
|
7 |
+
.cm-s-monokai .CodeMirror-cursor { border-left: 1px solid #F8F8F0 !important; }
|
8 |
+
|
9 |
+
.cm-s-monokai span.cm-comment { color: #75715E; }
|
10 |
+
.cm-s-monokai span.cm-atom { color: #AE81FF; }
|
11 |
+
.cm-s-monokai span.cm-number { color: #AE81FF; }
|
12 |
+
|
13 |
+
.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute {color: #A6E22E;}
|
14 |
+
.cm-s-monokai span.cm-keyword { color: #F92672; }
|
15 |
+
.cm-s-monokai span.cm-string { color: #E6DB74; }
|
16 |
+
|
17 |
+
.cm-s-monokai span.cm-variable {color: #A6E22E;}
|
18 |
+
.cm-s-monokai span.cm-variable-2 { color: #9effff; }
|
19 |
+
.cm-s-monokai span.cm-def { color: #FD971F; }
|
20 |
+
.cm-s-monokai span.cm-error { background: #F92672; color: #F8F8F0; }
|
21 |
+
.cm-s-monokai span.cm-bracket { color: #f8F8F2; }
|
22 |
+
.cm-s-monokai span.cm-tag {color: #F92672;}
|
23 |
+
|
24 |
+
.CodeMirror-matchingbracket{
|
25 |
+
text-decoration: underline;
|
26 |
+
color: white !important;
|
27 |
+
}
|
libraries/{codemirror → CodeMirror2}/theme/neat.css
RENAMED
File without changes
|
libraries/{codemirror → CodeMirror2}/theme/night.css
RENAMED
@@ -8,12 +8,12 @@
|
|
8 |
|
9 |
.cm-s-night span.cm-comment { color: #6900a1; }
|
10 |
.cm-s-night span.cm-atom { color: #845dc4; }
|
11 |
-
.cm-s-night span.cm-number { color: #ffd500; }
|
12 |
.cm-s-night span.cm-keyword { color: #599eff; }
|
13 |
.cm-s-night span.cm-string { color: #37f14a; }
|
14 |
.cm-s-night span.cm-meta { color: #7678e2; }
|
15 |
-
.cm-s-night span.cm-variable-2 { color: #99b2ff; }
|
16 |
-
.cm-s-night span.cm-variable-3, .cm-s-night span.cm-def { white; }
|
17 |
.cm-s-night span.cm-error { color: #9d1e15; }
|
18 |
.cm-s-night span.cm-bracket { color: #8da6ce; }
|
19 |
.cm-s-night span.cm-comment { color: #6900a1; }
|
8 |
|
9 |
.cm-s-night span.cm-comment { color: #6900a1; }
|
10 |
.cm-s-night span.cm-atom { color: #845dc4; }
|
11 |
+
.cm-s-night span.cm-number, .cm-s-night span.cm-attribute { color: #ffd500; }
|
12 |
.cm-s-night span.cm-keyword { color: #599eff; }
|
13 |
.cm-s-night span.cm-string { color: #37f14a; }
|
14 |
.cm-s-night span.cm-meta { color: #7678e2; }
|
15 |
+
.cm-s-night span.cm-variable-2, .cm-s-night span.cm-tag { color: #99b2ff; }
|
16 |
+
.cm-s-night span.cm-variable-3, .cm-s-night span.cm-def { color: white; }
|
17 |
.cm-s-night span.cm-error { color: #9d1e15; }
|
18 |
.cm-s-night span.cm-bracket { color: #8da6ce; }
|
19 |
.cm-s-night span.cm-comment { color: #6900a1; }
|
libraries/CodeMirror2/theme/rubyblue.css
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.cm-s-rubyblue { font:13px/1.4em Trebuchet, Verdana, sans-serif; } /* - customized editor font - */
|
2 |
+
|
3 |
+
.cm-s-rubyblue { background: #112435; color: white; }
|
4 |
+
.cm-s-rubyblue span.CodeMirror-selected { background: #0000FF !important; }
|
5 |
+
.cm-s-rubyblue .CodeMirror-gutter { background: #1F4661; border-right: 7px solid #3E7087; min-width:2.5em; }
|
6 |
+
.cm-s-rubyblue .CodeMirror-gutter-text { color: white; }
|
7 |
+
.cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white !important; }
|
8 |
+
|
9 |
+
.cm-s-rubyblue span.cm-comment { color: #999; font-style:italic; }
|
10 |
+
.cm-s-rubyblue span.cm-atom { color: #F4C20B; }
|
11 |
+
.cm-s-rubyblue span.cm-number, .cm-s-rubyblue span.cm-attribute { color: #82C6E0; }
|
12 |
+
.cm-s-rubyblue span.cm-keyword { color: #F0F; }
|
13 |
+
.cm-s-rubyblue span.cm-string { color: #F08047; }
|
14 |
+
.cm-s-rubyblue span.cm-meta { color: #F0F; }
|
15 |
+
.cm-s-rubyblue span.cm-variable-2, .cm-s-rubyblue span.cm-tag { color: #7BD827; }
|
16 |
+
.cm-s-rubyblue span.cm-variable-3, .cm-s-rubyblue span.cm-def { color: white; }
|
17 |
+
.cm-s-rubyblue span.cm-error { color: #AF2018; }
|
18 |
+
.cm-s-rubyblue span.cm-bracket { color: #F0F; }
|
19 |
+
.cm-s-rubyblue span.CodeMirror-matchingbracket { color:#F0F !important; }
|
20 |
+
.cm-s-rubyblue span.cm-builtin, .cm-s-rubyblue span.cm-special { color: #FF9D00; }
|
libraries/codemirror/lib/overlay.js
DELETED
@@ -1,51 +0,0 @@
|
|
1 |
-
// Utility function that allows modes to be combined. The mode given
|
2 |
-
// as the base argument takes care of most of the normal mode
|
3 |
-
// functionality, but a second (typically simple) mode is used, which
|
4 |
-
// can override the style of text. Both modes get to parse all of the
|
5 |
-
// text, but when both assign a non-null style to a piece of code, the
|
6 |
-
// overlay wins, unless the combine argument was true, in which case
|
7 |
-
// the styles are combined.
|
8 |
-
|
9 |
-
CodeMirror.overlayParser = function(base, overlay, combine) {
|
10 |
-
return {
|
11 |
-
startState: function() {
|
12 |
-
return {
|
13 |
-
base: CodeMirror.startState(base),
|
14 |
-
overlay: CodeMirror.startState(overlay),
|
15 |
-
basePos: 0, baseCur: null,
|
16 |
-
overlayPos: 0, overlayCur: null
|
17 |
-
};
|
18 |
-
},
|
19 |
-
copyState: function(state) {
|
20 |
-
return {
|
21 |
-
base: CodeMirror.copyState(base, state.base),
|
22 |
-
overlay: CodeMirror.copyState(overlay, state.overlay),
|
23 |
-
basePos: state.basePos, baseCur: null,
|
24 |
-
overlayPos: state.overlayPos, overlayCur: null
|
25 |
-
};
|
26 |
-
},
|
27 |
-
|
28 |
-
token: function(stream, state) {
|
29 |
-
if (stream.start == state.basePos) {
|
30 |
-
state.baseCur = base.token(stream, state.base);
|
31 |
-
state.basePos = stream.pos;
|
32 |
-
}
|
33 |
-
if (stream.start == state.overlayPos) {
|
34 |
-
stream.pos = stream.start;
|
35 |
-
state.overlayCur = overlay.token(stream, state.overlay);
|
36 |
-
state.overlayPos = stream.pos;
|
37 |
-
}
|
38 |
-
stream.pos = Math.min(state.basePos, state.overlayPos);
|
39 |
-
if (stream.eol()) state.basePos = state.overlayPos = 0;
|
40 |
-
|
41 |
-
if (state.overlayCur == null) return state.baseCur;
|
42 |
-
if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur;
|
43 |
-
else return state.overlayCur;
|
44 |
-
},
|
45 |
-
|
46 |
-
indent: function(state, textAfter) {
|
47 |
-
return base.indent(state.base, textAfter);
|
48 |
-
},
|
49 |
-
electricChars: base.electricChars
|
50 |
-
};
|
51 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
libraries/codemirror/lib/runmode.js
DELETED
@@ -1,27 +0,0 @@
|
|
1 |
-
CodeMirror.runMode = function(string, modespec, callback) {
|
2 |
-
var mode = CodeMirror.getMode({indentUnit: 2}, modespec);
|
3 |
-
var isNode = callback.nodeType == 1;
|
4 |
-
if (isNode) {
|
5 |
-
var node = callback, accum = [];
|
6 |
-
callback = function(string, style) {
|
7 |
-
if (string == "\n")
|
8 |
-
accum.push("<br>");
|
9 |
-
else if (style)
|
10 |
-
accum.push("<span class=\"cm-" + CodeMirror.htmlEscape(style) + "\">" + CodeMirror.htmlEscape(string) + "</span>");
|
11 |
-
else
|
12 |
-
accum.push(CodeMirror.htmlEscape(string));
|
13 |
-
}
|
14 |
-
}
|
15 |
-
var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode);
|
16 |
-
for (var i = 0, e = lines.length; i < e; ++i) {
|
17 |
-
if (i) callback("\n");
|
18 |
-
var stream = new CodeMirror.StringStream(lines[i]);
|
19 |
-
while (!stream.eol()) {
|
20 |
-
var style = mode.token(stream, state);
|
21 |
-
callback(stream.current(), style);
|
22 |
-
stream.start = stream.pos;
|
23 |
-
}
|
24 |
-
}
|
25 |
-
if (isNode)
|
26 |
-
node.innerHTML = accum.join("");
|
27 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
screenshot-1.png
CHANGED
Binary file
|
screenshot-2.png
ADDED
Binary file
|
screenshot-3.png
ADDED
Binary file
|
screenshot-4.png
ADDED
Binary file
|
screenshot-5.png
ADDED
Binary file
|
screenshot-6.png
ADDED
Binary file
|
scripts-n-styles.php
CHANGED
@@ -1,316 +1,313 @@
|
|
1 |
-
<?php
|
2 |
-
/*
|
3 |
-
Plugin Name: Scripts n Styles
|
4 |
-
Plugin URI: http://www.unfocus.com/projects/scripts-n-styles/
|
5 |
-
Description: Allows WordPress admin users the ability to add custom CSS and JavaScript directly to individual Post, Pages or custom post types.
|
6 |
-
Author: unFocus Projects
|
7 |
-
Author URI: http://www.unfocus.com/
|
8 |
-
Version:
|
9 |
-
License:
|
10 |
-
Network: true
|
11 |
-
*/
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
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 |
-
* @link http://www.unfocus.com/
|
47 |
-
* @
|
48 |
-
* @
|
49 |
-
* @
|
50 |
-
* @
|
51 |
-
* @
|
52 |
-
*
|
53 |
-
* @todo
|
54 |
-
* @todo
|
55 |
-
* @todo Create
|
56 |
-
* @todo Create
|
57 |
-
* @todo
|
58 |
-
* @todo
|
59 |
-
* @todo
|
60 |
-
* @todo
|
61 |
-
* @todo
|
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 |
-
add_action( '
|
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 |
-
'
|
136 |
-
'
|
137 |
-
'
|
138 |
-
'
|
139 |
-
'
|
140 |
-
'
|
141 |
-
'
|
142 |
-
'
|
143 |
-
'
|
144 |
-
'
|
145 |
-
'jquery-
|
146 |
-
'jquery-
|
147 |
-
'jquery-
|
148 |
-
'jquery-
|
149 |
-
'
|
150 |
-
'
|
151 |
-
'
|
152 |
-
'
|
153 |
-
'
|
154 |
-
'
|
155 |
-
'
|
156 |
-
'
|
157 |
-
'
|
158 |
-
'
|
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 |
-
if ( ! empty( $
|
202 |
-
?><
|
203 |
-
echo $
|
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 |
-
|
303 |
-
if ( !
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
Scripts_n_Styles::init();
|
315 |
-
|
316 |
?>
|
1 |
+
<?php
|
2 |
+
/*
|
3 |
+
Plugin Name: Scripts n Styles
|
4 |
+
Plugin URI: http://www.unfocus.com/projects/scripts-n-styles/
|
5 |
+
Description: Allows WordPress admin users the ability to add custom CSS and JavaScript directly to individual Post, Pages or custom post types.
|
6 |
+
Author: unFocus Projects
|
7 |
+
Author URI: http://www.unfocus.com/
|
8 |
+
Version: 3
|
9 |
+
License: GPLv2 or later
|
10 |
+
Network: true
|
11 |
+
*/
|
12 |
+
|
13 |
+
/* Copyright 2010-2011 Kenneth Newman www.unfocus.com
|
14 |
+
|
15 |
+
This program is free software; you can redistribute it and/or
|
16 |
+
modify it under the terms of the GNU General Public License
|
17 |
+
as published by the Free Software Foundation; either version 2
|
18 |
+
of the License, or (at your option) any later version.
|
19 |
+
|
20 |
+
This program is distributed in the hope that it will be useful,
|
21 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
22 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
23 |
+
GNU General Public License for more details.
|
24 |
+
|
25 |
+
You should have received a copy of the GNU General Public License
|
26 |
+
along with this program; if not, write to the Free Software
|
27 |
+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
28 |
+
*/
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Scripts n Styles
|
32 |
+
*
|
33 |
+
* Allows WordPress admin users the ability to add custom CSS
|
34 |
+
* and JavaScript directly to individual Post, Pages or custom
|
35 |
+
* post types.
|
36 |
+
*
|
37 |
+
* NOTE: No user except the "Super Admin" can use this plugin in MultiSite. I'll add features for MultiSite later, perhaps the ones below...
|
38 |
+
* The "Super Admin" user has exclusive 'unfiltered_html' capabilities in MultiSite. Also, options.php checks for is_super_admin()
|
39 |
+
* so the 'manage_options' capability for blog admins is insufficient to pass the check to manage options directly.
|
40 |
+
*
|
41 |
+
* The Tentative plan is for Super Admins to create Snippets or Shortcodes approved for use by users with certain capabilities
|
42 |
+
* ('unfiltered_html' and/or 'manage_options'). The 'unfiltered_html' capability can be granted via another plugin. This plugin will
|
43 |
+
* not deal with granting any capabilities.
|
44 |
+
*
|
45 |
+
* @package Scripts_n_Styles
|
46 |
+
* @link http://www.unfocus.com/projects/scripts-n-styles/ Plugin URI
|
47 |
+
* @author unFocus Projects
|
48 |
+
* @link http://www.unfocus.com/ Author URI
|
49 |
+
* @version 3
|
50 |
+
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
51 |
+
* @copyright Copyright (c) 2010 - 2011, Kenneth Newman
|
52 |
+
*
|
53 |
+
* @todo Add Post Type Selection on Options Page? Not sure that's useful.
|
54 |
+
* @todo Add Conditional Tags support as alternative to Globally applying Scripts n Styles.
|
55 |
+
* @todo Create ability to add and register scripts and styles for enqueueing (via Options page).
|
56 |
+
* @todo Create selection on Option page of which to pick registered scripts to make available on edit screens.
|
57 |
+
* @todo Create shortcode to embed html/javascript snippets. See http://scribu.net/wordpress/optimal-script-loading.html in which this is already figured out :-)
|
58 |
+
* @todo Create shortcode registration on Options page to make those snippets available on edit screens.
|
59 |
+
* @todo Create shortcode registration of html snippets on edit screens for single use.
|
60 |
+
* @todo Add Error messaging.
|
61 |
+
* @todo Replace Multi-Select element with something better.
|
62 |
+
* @todo "Include Scripts" will be reintroduced when registering is finished.
|
63 |
+
* @todo Clean up tiny_mce_before_init in SnS_Admin_Meta_Box.
|
64 |
+
* @todo LESS.js support.
|
65 |
+
* @todo LESS.js highlighting support to CodeMirror.
|
66 |
+
* @todo Solarize theme to CodeMirror.
|
67 |
+
*/
|
68 |
+
|
69 |
+
class Scripts_n_Styles
|
70 |
+
{
|
71 |
+
/**#@+
|
72 |
+
* @static
|
73 |
+
*/
|
74 |
+
static $file = __FILE__;
|
75 |
+
/**#@-*/
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Initializing method. Checks if is_admin() and registers action hooks for admin if true. Sets filters and actions for Theme side functions.
|
79 |
+
* @static
|
80 |
+
*/
|
81 |
+
static function init() {
|
82 |
+
|
83 |
+
if ( is_admin() && ! ( defined('DISALLOW_UNFILTERED_HTML') && DISALLOW_UNFILTERED_HTML ) ) {
|
84 |
+
/* NOTE: Setting the DISALLOW_UNFILTERED_HTML constant to
|
85 |
+
true in the wp-config.php would effectively disable this
|
86 |
+
plugin's admin because no user would have the capability.
|
87 |
+
*/
|
88 |
+
include_once( 'includes/class.SnS_Admin.php' );
|
89 |
+
SnS_Admin::init();
|
90 |
+
|
91 |
+
}
|
92 |
+
|
93 |
+
add_filter( 'body_class', array( __CLASS__, 'body_classes' ) );
|
94 |
+
add_filter( 'post_class', array( __CLASS__, 'post_classes' ) );
|
95 |
+
|
96 |
+
add_action( 'wp_head', array( __CLASS__, 'styles' ), 11 );
|
97 |
+
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ), 11 );
|
98 |
+
add_action( 'wp_head', array( __CLASS__, 'scripts_in_head' ), 11 );
|
99 |
+
add_action( 'wp_footer', array( __CLASS__, 'scripts' ), 11 );
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Utility Method
|
104 |
+
*/
|
105 |
+
static function get_wp_registered() {
|
106 |
+
return array(
|
107 |
+
// Starting with the list of Scripts registered by default on the Theme side (index page of twentyten).
|
108 |
+
'l10n',
|
109 |
+
'utils',
|
110 |
+
'common',
|
111 |
+
'sack',
|
112 |
+
'quicktags',
|
113 |
+
'colorpicker',
|
114 |
+
'editor',
|
115 |
+
'prototype',
|
116 |
+
'wp-ajax-response',
|
117 |
+
'autosave',
|
118 |
+
'wp-lists',
|
119 |
+
'scriptaculous-root',
|
120 |
+
'scriptaculous-builder',
|
121 |
+
'scriptaculous-dragdrop',
|
122 |
+
'scriptaculous-effects',
|
123 |
+
'scriptaculous-slider',
|
124 |
+
'scriptaculous-sound',
|
125 |
+
'scriptaculous-controls',
|
126 |
+
'scriptaculous',
|
127 |
+
'cropper',
|
128 |
+
'jquery',
|
129 |
+
'jquery-ui-core',
|
130 |
+
'jquery-ui-position',
|
131 |
+
'jquery-ui-widget',
|
132 |
+
'jquery-ui-mouse',
|
133 |
+
'jquery-ui-button',
|
134 |
+
'jquery-ui-tabs',
|
135 |
+
'jquery-ui-sortable',
|
136 |
+
'jquery-ui-draggable',
|
137 |
+
'jquery-ui-droppable',
|
138 |
+
'jquery-ui-selectable',
|
139 |
+
'jquery-ui-resizable',
|
140 |
+
'jquery-ui-dialog',
|
141 |
+
'jquery-form',
|
142 |
+
'jquery-color',
|
143 |
+
'suggest',
|
144 |
+
'schedule',
|
145 |
+
'jquery-query',
|
146 |
+
'jquery-serialize-object',
|
147 |
+
'jquery-hotkeys',
|
148 |
+
'jquery-table-hotkeys',
|
149 |
+
'thickbox',
|
150 |
+
'jcrop',
|
151 |
+
'swfobject',
|
152 |
+
'swfupload',
|
153 |
+
'swfupload-swfobject',
|
154 |
+
'swfupload-queue',
|
155 |
+
'swfupload-speed',
|
156 |
+
'swfupload-all',
|
157 |
+
'swfupload-handlers',
|
158 |
+
'comment-reply',
|
159 |
+
'json2',
|
160 |
+
'imgareaselect',
|
161 |
+
'password-strength-meter',
|
162 |
+
'user-profile',
|
163 |
+
'admin-bar',
|
164 |
+
'wplink',
|
165 |
+
'wpdialogs-popup'
|
166 |
+
);
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Theme Action: 'wp_head()'
|
171 |
+
* Outputs the globally and individually set Styles in the Theme's head element.
|
172 |
+
*/
|
173 |
+
static function styles() {
|
174 |
+
// Global
|
175 |
+
$options = get_option( 'SnS_options' );
|
176 |
+
if ( ! empty( $options ) && ! empty( $options[ 'styles' ] ) ) {
|
177 |
+
?><style type="text/css"><?php
|
178 |
+
echo $options[ 'styles' ];
|
179 |
+
?></style><?php
|
180 |
+
}
|
181 |
+
|
182 |
+
if ( ! is_singular() ) return;
|
183 |
+
// Individual
|
184 |
+
global $post;
|
185 |
+
$SnS = get_post_meta( $post->ID, '_SnS', true );
|
186 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
187 |
+
if ( ! empty( $styles ) && ! empty( $styles[ 'styles' ] ) ) {
|
188 |
+
?><style type="text/css"><?php
|
189 |
+
echo $styles[ 'styles' ];
|
190 |
+
?></style><?php
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Theme Action: 'wp_footer()'
|
196 |
+
* Outputs the globally and individually set Scripts at the end of the Theme's body element.
|
197 |
+
*/
|
198 |
+
static function scripts() {
|
199 |
+
// Global
|
200 |
+
$options = get_option( 'SnS_options' );
|
201 |
+
if ( ! empty( $options ) && ! empty( $options[ 'scripts' ] ) ) {
|
202 |
+
?><script type="text/javascript"><?php
|
203 |
+
echo $options[ 'scripts' ];
|
204 |
+
?></script><?php
|
205 |
+
}
|
206 |
+
|
207 |
+
if ( ! is_singular() ) return;
|
208 |
+
// Individual
|
209 |
+
global $post;
|
210 |
+
$SnS = get_post_meta( $post->ID, '_SnS', true );
|
211 |
+
$scripts = isset( $SnS['scripts'] ) ? $SnS[ 'scripts' ]: array();
|
212 |
+
if ( ! empty( $scripts ) && ! empty( $scripts[ 'scripts' ] ) ) {
|
213 |
+
?><script type="text/javascript"><?php
|
214 |
+
echo $scripts[ 'scripts' ];
|
215 |
+
?></script><?php
|
216 |
+
}
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Theme Action: 'wp_head()'
|
221 |
+
* Outputs the globally and individually set Scripts in the Theme's head element.
|
222 |
+
*/
|
223 |
+
static function scripts_in_head() {
|
224 |
+
// Global
|
225 |
+
$options = get_option( 'SnS_options' );
|
226 |
+
if ( ! empty( $options ) && ! empty( $options[ 'scripts_in_head' ] ) ) {
|
227 |
+
?><script type="text/javascript"><?php
|
228 |
+
echo $options[ 'scripts_in_head' ];
|
229 |
+
?></script><?php
|
230 |
+
}
|
231 |
+
|
232 |
+
if ( ! is_singular() ) return;
|
233 |
+
// Individual
|
234 |
+
global $post;
|
235 |
+
$SnS = get_post_meta( $post->ID, '_SnS', true );
|
236 |
+
$scripts = isset( $SnS['scripts'] ) ? $SnS[ 'scripts' ]: array();
|
237 |
+
if ( ! empty( $scripts ) && ! empty( $scripts[ 'scripts_in_head' ] ) ) {
|
238 |
+
?><script type="text/javascript"><?php
|
239 |
+
echo $scripts[ 'scripts_in_head' ];
|
240 |
+
?></script><?php
|
241 |
+
}
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Theme Filter: 'body_class()'
|
246 |
+
* Adds classes to the Theme's body tag.
|
247 |
+
* @uses self::get_styles()
|
248 |
+
* @param array $classes
|
249 |
+
* @return array $classes
|
250 |
+
*/
|
251 |
+
static function body_classes( $classes ) {
|
252 |
+
if ( ! is_singular() || is_admin() ) return $classes;
|
253 |
+
|
254 |
+
global $post;
|
255 |
+
$SnS = get_post_meta( $post->ID, '_SnS', true );
|
256 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
257 |
+
if ( ! empty( $styles ) && ! empty( $styles[ 'classes_body' ] ) )
|
258 |
+
$classes = array_merge( $classes, explode( " ", $styles[ 'classes_body' ] ) );
|
259 |
+
|
260 |
+
return $classes;
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Theme Filter: 'post_class()'
|
265 |
+
* Adds classes to the Theme's post container.
|
266 |
+
* @param array $classes
|
267 |
+
* @return array $classes
|
268 |
+
*/
|
269 |
+
static function post_classes( $classes ) {
|
270 |
+
if ( ! is_singular() || is_admin() ) return $classes;
|
271 |
+
|
272 |
+
global $post;
|
273 |
+
$SnS = get_post_meta( $post->ID, '_SnS', true );
|
274 |
+
$styles = isset( $SnS['styles'] ) ? $SnS[ 'styles' ]: array();
|
275 |
+
|
276 |
+
if ( ! empty( $styles ) && ! empty( $styles[ 'classes_post' ] ) )
|
277 |
+
$classes = array_merge( $classes, explode( " ", $styles[ 'classes_post' ] ) );
|
278 |
+
|
279 |
+
return $classes;
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Theme Action: 'wp_enqueue_scripts'
|
284 |
+
* Enqueues chosen Scripts.
|
285 |
+
*/
|
286 |
+
static function enqueue_scripts() {
|
287 |
+
// Global
|
288 |
+
$options = get_option( 'SnS_options' );
|
289 |
+
if ( ! isset( $options[ 'enqueue_scripts' ] ) )
|
290 |
+
$enqueue_scripts = array();
|
291 |
+
else
|
292 |
+
$enqueue_scripts = $options[ 'enqueue_scripts' ];
|
293 |
+
|
294 |
+
foreach ( $enqueue_scripts as $handle )
|
295 |
+
wp_enqueue_script( $handle );
|
296 |
+
|
297 |
+
if ( ! is_singular() ) return;
|
298 |
+
// Individual
|
299 |
+
global $post;
|
300 |
+
$SnS = get_post_meta( $post->ID, '_SnS', true );
|
301 |
+
$scripts = isset( $SnS['scripts'] ) ? $SnS[ 'scripts' ]: array();
|
302 |
+
|
303 |
+
if ( ! empty( $scripts[ 'enqueue_scripts' ] ) && is_array( $scripts[ 'enqueue_scripts' ] ) ) {
|
304 |
+
foreach ( $scripts[ 'enqueue_scripts' ] as $handle )
|
305 |
+
wp_enqueue_script( $handle );
|
306 |
+
}
|
307 |
+
}
|
308 |
+
|
309 |
+
}
|
310 |
+
|
311 |
+
Scripts_n_Styles::init();
|
312 |
+
|
|
|
|
|
|
|
313 |
?>
|
uninstall.php
CHANGED
@@ -1,14 +1,20 @@
|
|
1 |
<?php
|
2 |
-
if( !defined( 'ABSPATH') && !defined('WP_UNINSTALL_PLUGIN') )
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
delete_option('
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
?>
|
1 |
<?php
|
2 |
+
if( ! defined( 'ABSPATH' ) && ! defined( 'WP_UNINSTALL_PLUGIN' ) ) exit();
|
3 |
+
$posts = get_posts( array(
|
4 |
+
'numberposts' => -1,
|
5 |
+
'post_type' => 'any',
|
6 |
+
'post_status' => 'any',
|
7 |
+
'orderby' => 'ID',
|
8 |
+
'meta_key' => '_SnS'
|
9 |
+
) );
|
10 |
+
|
11 |
+
foreach( $posts as $post)
|
12 |
+
delete_post_meta( $post->ID, '_SnS' );
|
13 |
+
delete_option( 'SnS_options' );
|
14 |
+
|
15 |
+
$users = get_users( 'meta_key=current_sns_tab' );
|
16 |
+
foreach( $users as $user ) delete_user_option( $user->ID, 'current_sns_tab', true );
|
17 |
+
|
18 |
+
$users = get_users( 'meta_key=scripts_n_styles_page_sns_usage_per_page' );
|
19 |
+
foreach( $users as $user ) delete_user_option( $user->ID, 'scripts_n_styles_page_sns_usage_per_page', true );
|
20 |
?>
|