Version Description
- Updated code editor to use CodeMirror 3
- Improved compatibility with Clean Options plugin
- Code improvements and optimization
- Changed namespace from
cs
tocode_snippets
- Move css and js under assets
- Organized CodeMirror scripts
- Improved updating process
- Current line of code editor is now highlighted
- Highlight matches of selected text in code editor
- Only create snippet tables when needed
- Store multisite only options in site options table
- Fixed compatibility bugs with WordPress 3.5
Download this release
Release Info
Developer | bungeshea |
Plugin | Code Snippets |
Version | 1.6 |
Comparing to | |
See all releases |
Code changes from version 1.5 to 1.6
- assets/codemirror.css +252 -0
- assets/codemirror.js +4553 -0
- assets/icon.css +3 -0
- {js → assets/mode}/clike.js +24 -8
- assets/mode/css.js +465 -0
- assets/mode/htmlmixed.js +84 -0
- {js → assets/mode}/javascript.js +68 -18
- assets/mode/php.js +129 -0
- {js → assets/mode}/xml.js +13 -15
- css/style.css → assets/table.css +2 -25
- {css → assets/util}/dialog.css +12 -7
- {js → assets/util}/dialog.js +21 -16
- assets/util/matchbrackets.js +63 -0
- {js → assets/util}/search.js +6 -5
- {js → assets/util}/searchcursor.js +3 -3
- code-snippets.php +1339 -1113
- css/codemirror.css +0 -173
- includes/admin/import.php +26 -15
- includes/admin/manage.php +13 -13
- includes/admin/single.php +31 -30
- includes/class-list-table.php +140 -99
- includes/export.php +37 -34
- includes/help/import.php +5 -5
- includes/help/manage.php +2 -2
- includes/help/single.php +3 -3
- js/codemirror.js +0 -3237
- js/css.js +0 -174
- js/php.js +0 -150
- languages/code-snippets.pot +74 -67
- readme.txt +51 -18
assets/codemirror.css
ADDED
@@ -0,0 +1,252 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* BASICS */
|
2 |
+
|
3 |
+
.CodeMirror {
|
4 |
+
/* Set height, width, borders, and global font properties here */
|
5 |
+
font-family: monospace;
|
6 |
+
height: auto;
|
7 |
+
min-height: 300px;
|
8 |
+
border: 1px solid #eee;
|
9 |
+
-moz-border-radius: 3px; -webkit-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px;
|
10 |
+
}
|
11 |
+
.CodeMirror-scroll {
|
12 |
+
/* Set scrolling behaviour here */
|
13 |
+
overflow-y: hidden;
|
14 |
+
overflow-x: auto;
|
15 |
+
}
|
16 |
+
|
17 |
+
.CodeMirror-sizer {
|
18 |
+
min-height: 300px !important;
|
19 |
+
}
|
20 |
+
|
21 |
+
/* PADDING */
|
22 |
+
|
23 |
+
.CodeMirror-lines {
|
24 |
+
padding: 4px 0; /* Vertical padding around content */
|
25 |
+
}
|
26 |
+
.CodeMirror pre {
|
27 |
+
padding: 0 4px; /* Horizontal padding of content */
|
28 |
+
}
|
29 |
+
|
30 |
+
.CodeMirror-scrollbar-filler {
|
31 |
+
background-color: white; /* The little square between H and V scrollbars */
|
32 |
+
}
|
33 |
+
|
34 |
+
/* GUTTER */
|
35 |
+
|
36 |
+
.CodeMirror-gutters {
|
37 |
+
border-right: 1px solid #ddd;
|
38 |
+
background-color: #f7f7f7;
|
39 |
+
}
|
40 |
+
.CodeMirror-linenumbers {}
|
41 |
+
.CodeMirror-linenumber {
|
42 |
+
padding: 0 3px 0 5px;
|
43 |
+
min-width: 20px;
|
44 |
+
text-align: right;
|
45 |
+
color: #999;
|
46 |
+
}
|
47 |
+
|
48 |
+
/* CURSOR */
|
49 |
+
|
50 |
+
.CodeMirror pre.CodeMirror-cursor {
|
51 |
+
border-left: 1px solid black;
|
52 |
+
}
|
53 |
+
/* Shown when moving in bi-directional text */
|
54 |
+
.CodeMirror pre.CodeMirror-secondarycursor {
|
55 |
+
border-left: 1px solid silver;
|
56 |
+
}
|
57 |
+
.cm-keymap-fat-cursor pre.CodeMirror-cursor {
|
58 |
+
width: auto;
|
59 |
+
border: 0;
|
60 |
+
background: transparent;
|
61 |
+
background: rgba(0, 200, 0, .4);
|
62 |
+
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
|
63 |
+
}
|
64 |
+
/* Kludge to turn off filter in ie9+, which also accepts rgba */
|
65 |
+
.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
|
66 |
+
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
67 |
+
}
|
68 |
+
/* Can style cursor different in overwrite (non-insert) mode */
|
69 |
+
.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
|
70 |
+
|
71 |
+
/* DEFAULT THEME */
|
72 |
+
|
73 |
+
.cm-s-default .cm-keyword {color: #708;}
|
74 |
+
.cm-s-default .cm-atom {color: #219;}
|
75 |
+
.cm-s-default .cm-number {color: #164;}
|
76 |
+
.cm-s-default .cm-def {color: #00f;}
|
77 |
+
.cm-s-default .cm-variable {color: black;}
|
78 |
+
.cm-s-default .cm-variable-2 {color: #05a;}
|
79 |
+
.cm-s-default .cm-variable-3 {color: #085;}
|
80 |
+
.cm-s-default .cm-property {color: black;}
|
81 |
+
.cm-s-default .cm-operator {color: black;}
|
82 |
+
.cm-s-default .cm-comment {color: #a50;}
|
83 |
+
.cm-s-default .cm-string {color: #a11;}
|
84 |
+
.cm-s-default .cm-string-2 {color: #f50;}
|
85 |
+
.cm-s-default .cm-meta {color: #555;}
|
86 |
+
.cm-s-default .cm-error {color: #f00;}
|
87 |
+
.cm-s-default .cm-qualifier {color: #555;}
|
88 |
+
.cm-s-default .cm-builtin {color: #30a;}
|
89 |
+
.cm-s-default .cm-bracket {color: #997;}
|
90 |
+
.cm-s-default .cm-tag {color: #170;}
|
91 |
+
.cm-s-default .cm-attribute {color: #00c;}
|
92 |
+
.cm-s-default .cm-header {color: blue;}
|
93 |
+
.cm-s-default .cm-quote {color: #090;}
|
94 |
+
.cm-s-default .cm-hr {color: #999;}
|
95 |
+
.cm-s-default .cm-link {color: #00c;}
|
96 |
+
|
97 |
+
.cm-negative {color: #d44;}
|
98 |
+
.cm-positive {color: #292;}
|
99 |
+
.cm-header, .cm-strong {font-weight: bold;}
|
100 |
+
.cm-em {font-style: italic;}
|
101 |
+
.cm-emstrong {font-style: italic; font-weight: bold;}
|
102 |
+
.cm-link {text-decoration: underline;}
|
103 |
+
|
104 |
+
.cm-invalidchar {color: #f00;}
|
105 |
+
|
106 |
+
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
107 |
+
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
108 |
+
|
109 |
+
.activeline { background: #e8f2ff !important; }
|
110 |
+
|
111 |
+
span.CodeMirror-matchhighlight { background: #e9e9e9 }
|
112 |
+
.CodeMirror-focused span.CodeMirror-matchhighlight { background: #e7e4ff; !important }
|
113 |
+
|
114 |
+
/* STOP */
|
115 |
+
|
116 |
+
/* The rest of this file contains styles related to the mechanics of
|
117 |
+
the editor. You probably shouldn't touch them. */
|
118 |
+
|
119 |
+
.CodeMirror {
|
120 |
+
line-height: 1;
|
121 |
+
position: relative;
|
122 |
+
overflow: hidden;
|
123 |
+
}
|
124 |
+
|
125 |
+
.CodeMirror-scroll {
|
126 |
+
/* 30px is the magic margin used to hide the element's real scrollbars */
|
127 |
+
/* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
|
128 |
+
margin-bottom: -30px; margin-right: -30px;
|
129 |
+
padding-bottom: 30px; padding-right: 30px;
|
130 |
+
height: 100%;
|
131 |
+
outline: none; /* Prevent dragging from highlighting the element */
|
132 |
+
position: relative;
|
133 |
+
}
|
134 |
+
.CodeMirror-sizer {
|
135 |
+
position: relative;
|
136 |
+
}
|
137 |
+
|
138 |
+
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
139 |
+
before actuall scrolling happens, thus preventing shaking and
|
140 |
+
flickering artifacts. */
|
141 |
+
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
|
142 |
+
position: absolute;
|
143 |
+
z-index: 6;
|
144 |
+
display: none;
|
145 |
+
}
|
146 |
+
.CodeMirror-vscrollbar {
|
147 |
+
right: 0; top: 0;
|
148 |
+
overflow-x: hidden;
|
149 |
+
overflow-y: scroll;
|
150 |
+
}
|
151 |
+
.CodeMirror-hscrollbar {
|
152 |
+
bottom: 0; left: 0;
|
153 |
+
overflow-y: hidden;
|
154 |
+
overflow-x: scroll;
|
155 |
+
}
|
156 |
+
.CodeMirror-scrollbar-filler {
|
157 |
+
right: 0; bottom: 0;
|
158 |
+
z-index: 6;
|
159 |
+
}
|
160 |
+
|
161 |
+
.CodeMirror-gutters {
|
162 |
+
position: absolute; left: 0; top: 0;
|
163 |
+
height: 100%;
|
164 |
+
z-index: 3;
|
165 |
+
}
|
166 |
+
.CodeMirror-gutter {
|
167 |
+
height: 100%;
|
168 |
+
display: inline-block;
|
169 |
+
/* Hack to make IE7 behave */
|
170 |
+
*zoom:1;
|
171 |
+
*display:inline;
|
172 |
+
}
|
173 |
+
.CodeMirror-gutter-elt {
|
174 |
+
position: absolute;
|
175 |
+
cursor: default;
|
176 |
+
z-index: 4;
|
177 |
+
}
|
178 |
+
|
179 |
+
.CodeMirror-lines {
|
180 |
+
cursor: text;
|
181 |
+
}
|
182 |
+
.CodeMirror pre {
|
183 |
+
/* Reset some styles that the rest of the page might have set */
|
184 |
+
-moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0;
|
185 |
+
border-width: 0;
|
186 |
+
background: transparent;
|
187 |
+
font-family: inherit;
|
188 |
+
font-size: inherit;
|
189 |
+
margin: 0;
|
190 |
+
white-space: pre;
|
191 |
+
word-wrap: normal;
|
192 |
+
line-height: inherit;
|
193 |
+
color: inherit;
|
194 |
+
z-index: 2;
|
195 |
+
position: relative;
|
196 |
+
overflow: visible;
|
197 |
+
}
|
198 |
+
.CodeMirror-wrap pre {
|
199 |
+
word-wrap: break-word;
|
200 |
+
white-space: pre-wrap;
|
201 |
+
word-break: normal;
|
202 |
+
}
|
203 |
+
.CodeMirror-linebackground {
|
204 |
+
position: absolute;
|
205 |
+
left: 0; right: 0; top: 0; bottom: 0;
|
206 |
+
z-index: 0;
|
207 |
+
}
|
208 |
+
|
209 |
+
.CodeMirror-linewidget {
|
210 |
+
position: relative;
|
211 |
+
z-index: 2;
|
212 |
+
}
|
213 |
+
|
214 |
+
.CodeMirror-wrap .CodeMirror-scroll {
|
215 |
+
overflow-x: hidden;
|
216 |
+
}
|
217 |
+
|
218 |
+
.CodeMirror-measure {
|
219 |
+
position: absolute;
|
220 |
+
width: 100%; height: 0px;
|
221 |
+
overflow: hidden;
|
222 |
+
visibility: hidden;
|
223 |
+
}
|
224 |
+
.CodeMirror-measure pre { position: static; }
|
225 |
+
|
226 |
+
.CodeMirror pre.CodeMirror-cursor {
|
227 |
+
position: absolute;
|
228 |
+
visibility: hidden;
|
229 |
+
border-right: none;
|
230 |
+
width: 0;
|
231 |
+
}
|
232 |
+
.CodeMirror-focused pre.CodeMirror-cursor {
|
233 |
+
visibility: visible;
|
234 |
+
}
|
235 |
+
|
236 |
+
.CodeMirror-selected { background: #d9d9d9; }
|
237 |
+
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
238 |
+
|
239 |
+
.CodeMirror-searching {
|
240 |
+
background: #ffa;
|
241 |
+
background: rgba(255, 255, 0, .4);
|
242 |
+
}
|
243 |
+
|
244 |
+
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
|
245 |
+
.CodeMirror span { *vertical-align: text-bottom; }
|
246 |
+
|
247 |
+
@media print {
|
248 |
+
/* Hide the cursor when printing */
|
249 |
+
.CodeMirror pre.CodeMirror-cursor {
|
250 |
+
visibility: hidden;
|
251 |
+
}
|
252 |
+
}
|
assets/codemirror.js
ADDED
@@ -0,0 +1,4553 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// CodeMirror version 3.0
|
2 |
+
//
|
3 |
+
// CodeMirror is the only global var we claim
|
4 |
+
window.CodeMirror = (function() {
|
5 |
+
"use strict";
|
6 |
+
|
7 |
+
// BROWSER SNIFFING
|
8 |
+
|
9 |
+
// Crude, but necessary to handle a number of hard-to-feature-detect
|
10 |
+
// bugs and behavior differences.
|
11 |
+
var gecko = /gecko\/\d/i.test(navigator.userAgent);
|
12 |
+
var ie = /MSIE \d/.test(navigator.userAgent);
|
13 |
+
var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
|
14 |
+
var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
|
15 |
+
var webkit = /WebKit\//.test(navigator.userAgent);
|
16 |
+
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
|
17 |
+
var chrome = /Chrome\//.test(navigator.userAgent);
|
18 |
+
var opera = /Opera\//.test(navigator.userAgent);
|
19 |
+
var safari = /Apple Computer/.test(navigator.vendor);
|
20 |
+
var khtml = /KHTML\//.test(navigator.userAgent);
|
21 |
+
var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
|
22 |
+
var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
|
23 |
+
var phantom = /PhantomJS/.test(navigator.userAgent);
|
24 |
+
|
25 |
+
var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
|
26 |
+
// This is woefully incomplete. Suggestions for alternative methods welcome.
|
27 |
+
var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent);
|
28 |
+
var mac = ios || /Mac/.test(navigator.platform);
|
29 |
+
|
30 |
+
// Optimize some code when these features are not used
|
31 |
+
var sawReadOnlySpans = false, sawCollapsedSpans = false;
|
32 |
+
|
33 |
+
// CONSTRUCTOR
|
34 |
+
|
35 |
+
function CodeMirror(place, options) {
|
36 |
+
if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
|
37 |
+
|
38 |
+
this.options = options = options || {};
|
39 |
+
// Determine effective options based on given values and defaults.
|
40 |
+
for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
|
41 |
+
options[opt] = defaults[opt];
|
42 |
+
setGuttersForLineNumbers(options);
|
43 |
+
|
44 |
+
var display = this.display = makeDisplay(place);
|
45 |
+
display.wrapper.CodeMirror = this;
|
46 |
+
updateGutters(this);
|
47 |
+
if (options.autofocus && !mobile) focusInput(this);
|
48 |
+
|
49 |
+
this.view = makeView(new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]));
|
50 |
+
this.nextOpId = 0;
|
51 |
+
loadMode(this);
|
52 |
+
themeChanged(this);
|
53 |
+
if (options.lineWrapping)
|
54 |
+
this.display.wrapper.className += " CodeMirror-wrap";
|
55 |
+
|
56 |
+
// Initialize the content.
|
57 |
+
this.setValue(options.value || "");
|
58 |
+
// Override magic textarea content restore that IE sometimes does
|
59 |
+
// on our hidden textarea on reload
|
60 |
+
if (ie) setTimeout(bind(resetInput, this, true), 20);
|
61 |
+
this.view.history = makeHistory();
|
62 |
+
|
63 |
+
registerEventHandlers(this);
|
64 |
+
// IE throws unspecified error in certain cases, when
|
65 |
+
// trying to access activeElement before onload
|
66 |
+
var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
|
67 |
+
if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
|
68 |
+
else onBlur(this);
|
69 |
+
|
70 |
+
operation(this, function() {
|
71 |
+
for (var opt in optionHandlers)
|
72 |
+
if (optionHandlers.propertyIsEnumerable(opt))
|
73 |
+
optionHandlers[opt](this, options[opt], Init);
|
74 |
+
for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
|
75 |
+
})();
|
76 |
+
}
|
77 |
+
|
78 |
+
// DISPLAY CONSTRUCTOR
|
79 |
+
|
80 |
+
function makeDisplay(place) {
|
81 |
+
var d = {};
|
82 |
+
var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
|
83 |
+
input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
|
84 |
+
// Wraps and hides input textarea
|
85 |
+
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
|
86 |
+
// The actual fake scrollbars.
|
87 |
+
d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
|
88 |
+
d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
|
89 |
+
d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
|
90 |
+
// DIVs containing the selection and the actual code
|
91 |
+
d.lineDiv = elt("div");
|
92 |
+
d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
|
93 |
+
// Blinky cursor, and element used to ensure cursor fits at the end of a line
|
94 |
+
d.cursor = elt("pre", "\u00a0", "CodeMirror-cursor");
|
95 |
+
// Secondary cursor, shown when on a 'jump' in bi-directional text
|
96 |
+
d.otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
|
97 |
+
// Used to measure text size
|
98 |
+
d.measure = elt("div", null, "CodeMirror-measure");
|
99 |
+
// Wraps everything that needs to exist inside the vertically-padded coordinate system
|
100 |
+
d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
|
101 |
+
null, "position: relative; outline: none");
|
102 |
+
// Moved around its parent to cover visible view
|
103 |
+
d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
|
104 |
+
// Set to the height of the text, causes scrolling
|
105 |
+
d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
|
106 |
+
// D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
|
107 |
+
d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px");
|
108 |
+
// Will contain the gutters, if any
|
109 |
+
d.gutters = elt("div", null, "CodeMirror-gutters");
|
110 |
+
d.lineGutter = null;
|
111 |
+
// Helper element to properly size the gutter backgrounds
|
112 |
+
var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
|
113 |
+
// Provides scrolling
|
114 |
+
d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
|
115 |
+
d.scroller.setAttribute("tabIndex", "-1");
|
116 |
+
// The element in which the editor lives.
|
117 |
+
d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
|
118 |
+
d.scrollbarFiller, d.scroller], "CodeMirror");
|
119 |
+
// Work around IE7 z-index bug
|
120 |
+
if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
|
121 |
+
if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
|
122 |
+
|
123 |
+
// Needed to hide big blue blinking cursor on Mobile Safari
|
124 |
+
if (ios) input.style.width = "0px";
|
125 |
+
if (!webkit) d.scroller.draggable = true;
|
126 |
+
// Needed to handle Tab key in KHTML
|
127 |
+
if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
|
128 |
+
// Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
|
129 |
+
else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
|
130 |
+
|
131 |
+
// Current visible range (may be bigger than the view window).
|
132 |
+
d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0;
|
133 |
+
|
134 |
+
// Used to only resize the line number gutter when necessary (when
|
135 |
+
// the amount of lines crosses a boundary that makes its width change)
|
136 |
+
d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
|
137 |
+
// See readInput and resetInput
|
138 |
+
d.prevInput = "";
|
139 |
+
// Set to true when a non-horizontal-scrolling widget is added. As
|
140 |
+
// an optimization, widget aligning is skipped when d is false.
|
141 |
+
d.alignWidgets = false;
|
142 |
+
// Flag that indicates whether we currently expect input to appear
|
143 |
+
// (after some event like 'keypress' or 'input') and are polling
|
144 |
+
// intensively.
|
145 |
+
d.pollingFast = false;
|
146 |
+
// Self-resetting timeout for the poller
|
147 |
+
d.poll = new Delayed();
|
148 |
+
// True when a drag from the editor is active
|
149 |
+
d.draggingText = false;
|
150 |
+
|
151 |
+
d.cachedCharWidth = d.cachedTextHeight = null;
|
152 |
+
d.measureLineCache = [];
|
153 |
+
d.measureLineCachePos = 0;
|
154 |
+
|
155 |
+
// Tracks when resetInput has punted to just putting a short
|
156 |
+
// string instead of the (large) selection.
|
157 |
+
d.inaccurateSelection = false;
|
158 |
+
|
159 |
+
// Used to adjust overwrite behaviour when a paste has been
|
160 |
+
// detected
|
161 |
+
d.pasteIncoming = false;
|
162 |
+
|
163 |
+
return d;
|
164 |
+
}
|
165 |
+
|
166 |
+
// VIEW CONSTRUCTOR
|
167 |
+
|
168 |
+
function makeView(doc) {
|
169 |
+
var selPos = {line: 0, ch: 0};
|
170 |
+
return {
|
171 |
+
doc: doc,
|
172 |
+
// frontier is the point up to which the content has been parsed,
|
173 |
+
frontier: 0, highlight: new Delayed(),
|
174 |
+
sel: {from: selPos, to: selPos, head: selPos, anchor: selPos, shift: false, extend: false},
|
175 |
+
scrollTop: 0, scrollLeft: 0,
|
176 |
+
overwrite: false, focused: false,
|
177 |
+
// Tracks the maximum line length so that
|
178 |
+
// the horizontal scrollbar can be kept
|
179 |
+
// static when scrolling.
|
180 |
+
maxLine: getLine(doc, 0),
|
181 |
+
maxLineLength: 0,
|
182 |
+
maxLineChanged: false,
|
183 |
+
suppressEdits: false,
|
184 |
+
goalColumn: null,
|
185 |
+
cantEdit: false,
|
186 |
+
keyMaps: []
|
187 |
+
};
|
188 |
+
}
|
189 |
+
|
190 |
+
// STATE UPDATES
|
191 |
+
|
192 |
+
// Used to get the editor into a consistent state again when options change.
|
193 |
+
|
194 |
+
function loadMode(cm) {
|
195 |
+
var doc = cm.view.doc;
|
196 |
+
cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode);
|
197 |
+
doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
|
198 |
+
cm.view.frontier = 0;
|
199 |
+
startWorker(cm, 100);
|
200 |
+
}
|
201 |
+
|
202 |
+
function wrappingChanged(cm) {
|
203 |
+
var doc = cm.view.doc, th = textHeight(cm.display);
|
204 |
+
if (cm.options.lineWrapping) {
|
205 |
+
cm.display.wrapper.className += " CodeMirror-wrap";
|
206 |
+
var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3;
|
207 |
+
doc.iter(0, doc.size, function(line) {
|
208 |
+
if (line.height == 0) return;
|
209 |
+
var guess = Math.ceil(line.text.length / perLine) || 1;
|
210 |
+
if (guess != 1) updateLineHeight(line, guess * th);
|
211 |
+
});
|
212 |
+
cm.display.sizer.style.minWidth = "";
|
213 |
+
} else {
|
214 |
+
cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
|
215 |
+
computeMaxLength(cm.view);
|
216 |
+
doc.iter(0, doc.size, function(line) {
|
217 |
+
if (line.height != 0) updateLineHeight(line, th);
|
218 |
+
});
|
219 |
+
}
|
220 |
+
regChange(cm, 0, doc.size);
|
221 |
+
clearCaches(cm);
|
222 |
+
setTimeout(function(){updateScrollbars(cm.display, cm.view.doc.height);}, 100);
|
223 |
+
}
|
224 |
+
|
225 |
+
function keyMapChanged(cm) {
|
226 |
+
var style = keyMap[cm.options.keyMap].style;
|
227 |
+
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
|
228 |
+
(style ? " cm-keymap-" + style : "");
|
229 |
+
}
|
230 |
+
|
231 |
+
function themeChanged(cm) {
|
232 |
+
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
|
233 |
+
cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
|
234 |
+
clearCaches(cm);
|
235 |
+
}
|
236 |
+
|
237 |
+
function guttersChanged(cm) {
|
238 |
+
updateGutters(cm);
|
239 |
+
updateDisplay(cm, true);
|
240 |
+
}
|
241 |
+
|
242 |
+
function updateGutters(cm) {
|
243 |
+
var gutters = cm.display.gutters, specs = cm.options.gutters;
|
244 |
+
removeChildren(gutters);
|
245 |
+
for (var i = 0; i < specs.length; ++i) {
|
246 |
+
var gutterClass = specs[i];
|
247 |
+
var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
|
248 |
+
if (gutterClass == "CodeMirror-linenumbers") {
|
249 |
+
cm.display.lineGutter = gElt;
|
250 |
+
gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
|
251 |
+
}
|
252 |
+
}
|
253 |
+
gutters.style.display = i ? "" : "none";
|
254 |
+
}
|
255 |
+
|
256 |
+
function lineLength(doc, line) {
|
257 |
+
if (line.height == 0) return 0;
|
258 |
+
var len = line.text.length, merged, cur = line;
|
259 |
+
while (merged = collapsedSpanAtStart(cur)) {
|
260 |
+
var found = merged.find();
|
261 |
+
cur = getLine(doc, found.from.line);
|
262 |
+
len += found.from.ch - found.to.ch;
|
263 |
+
}
|
264 |
+
cur = line;
|
265 |
+
while (merged = collapsedSpanAtEnd(cur)) {
|
266 |
+
var found = merged.find();
|
267 |
+
len -= cur.text.length - found.from.ch;
|
268 |
+
cur = getLine(doc, found.to.line);
|
269 |
+
len += cur.text.length - found.to.ch;
|
270 |
+
}
|
271 |
+
return len;
|
272 |
+
}
|
273 |
+
|
274 |
+
function computeMaxLength(view) {
|
275 |
+
view.maxLine = getLine(view.doc, 0);
|
276 |
+
view.maxLineLength = lineLength(view.doc, view.maxLine);
|
277 |
+
view.maxLineChanged = true;
|
278 |
+
view.doc.iter(1, view.doc.size, function(line) {
|
279 |
+
var len = lineLength(view.doc, line);
|
280 |
+
if (len > view.maxLineLength) {
|
281 |
+
view.maxLineLength = len;
|
282 |
+
view.maxLine = line;
|
283 |
+
}
|
284 |
+
});
|
285 |
+
}
|
286 |
+
|
287 |
+
// Make sure the gutters options contains the element
|
288 |
+
// "CodeMirror-linenumbers" when the lineNumbers option is true.
|
289 |
+
function setGuttersForLineNumbers(options) {
|
290 |
+
var found = false;
|
291 |
+
for (var i = 0; i < options.gutters.length; ++i) {
|
292 |
+
if (options.gutters[i] == "CodeMirror-linenumbers") {
|
293 |
+
if (options.lineNumbers) found = true;
|
294 |
+
else options.gutters.splice(i--, 1);
|
295 |
+
}
|
296 |
+
}
|
297 |
+
if (!found && options.lineNumbers)
|
298 |
+
options.gutters.push("CodeMirror-linenumbers");
|
299 |
+
}
|
300 |
+
|
301 |
+
// SCROLLBARS
|
302 |
+
|
303 |
+
// Re-synchronize the fake scrollbars with the actual size of the
|
304 |
+
// content. Optionally force a scrollTop.
|
305 |
+
function updateScrollbars(d /* display */, docHeight) {
|
306 |
+
var totalHeight = docHeight + 2 * paddingTop(d);
|
307 |
+
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
|
308 |
+
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
|
309 |
+
var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
|
310 |
+
var needsV = scrollHeight > d.scroller.clientHeight;
|
311 |
+
if (needsV) {
|
312 |
+
d.scrollbarV.style.display = "block";
|
313 |
+
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
|
314 |
+
d.scrollbarV.firstChild.style.height =
|
315 |
+
(scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
|
316 |
+
} else d.scrollbarV.style.display = "";
|
317 |
+
if (needsH) {
|
318 |
+
d.scrollbarH.style.display = "block";
|
319 |
+
d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
|
320 |
+
d.scrollbarH.firstChild.style.width =
|
321 |
+
(d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
|
322 |
+
} else d.scrollbarH.style.display = "";
|
323 |
+
if (needsH && needsV) {
|
324 |
+
d.scrollbarFiller.style.display = "block";
|
325 |
+
d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
|
326 |
+
} else d.scrollbarFiller.style.display = "";
|
327 |
+
|
328 |
+
if (mac_geLion && scrollbarWidth(d.measure) === 0)
|
329 |
+
d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
|
330 |
+
}
|
331 |
+
|
332 |
+
function visibleLines(display, doc, viewPort) {
|
333 |
+
var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
|
334 |
+
if (typeof viewPort == "number") top = viewPort;
|
335 |
+
else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
|
336 |
+
top = Math.floor(top - paddingTop(display));
|
337 |
+
var bottom = Math.ceil(top + height);
|
338 |
+
return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
|
339 |
+
}
|
340 |
+
|
341 |
+
// LINE NUMBERS
|
342 |
+
|
343 |
+
function alignHorizontally(cm) {
|
344 |
+
var display = cm.display;
|
345 |
+
if (!display.alignWidgets && !display.gutters.firstChild) return;
|
346 |
+
var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft;
|
347 |
+
var gutterW = display.gutters.offsetWidth, l = comp + "px";
|
348 |
+
for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
|
349 |
+
for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
|
350 |
+
}
|
351 |
+
display.gutters.style.left = (comp + gutterW) + "px";
|
352 |
+
}
|
353 |
+
|
354 |
+
function maybeUpdateLineNumberWidth(cm) {
|
355 |
+
if (!cm.options.lineNumbers) return false;
|
356 |
+
var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display;
|
357 |
+
if (last.length != display.lineNumChars) {
|
358 |
+
var test = display.measure.appendChild(elt("div", [elt("div", last)],
|
359 |
+
"CodeMirror-linenumber CodeMirror-gutter-elt"));
|
360 |
+
var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
|
361 |
+
display.lineGutter.style.width = "";
|
362 |
+
display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
|
363 |
+
display.lineNumWidth = display.lineNumInnerWidth + padding;
|
364 |
+
display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
|
365 |
+
display.lineGutter.style.width = display.lineNumWidth + "px";
|
366 |
+
return true;
|
367 |
+
}
|
368 |
+
return false;
|
369 |
+
}
|
370 |
+
|
371 |
+
function lineNumberFor(options, i) {
|
372 |
+
return String(options.lineNumberFormatter(i + options.firstLineNumber));
|
373 |
+
}
|
374 |
+
function compensateForHScroll(display) {
|
375 |
+
return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
|
376 |
+
}
|
377 |
+
|
378 |
+
// DISPLAY DRAWING
|
379 |
+
|
380 |
+
function updateDisplay(cm, changes, viewPort) {
|
381 |
+
var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
|
382 |
+
var updated = updateDisplayInner(cm, changes, viewPort);
|
383 |
+
if (updated) {
|
384 |
+
signalLater(cm, cm, "update", cm);
|
385 |
+
if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
|
386 |
+
signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
|
387 |
+
}
|
388 |
+
updateSelection(cm);
|
389 |
+
updateScrollbars(cm.display, cm.view.doc.height);
|
390 |
+
|
391 |
+
return updated;
|
392 |
+
}
|
393 |
+
|
394 |
+
// Uses a set of changes plus the current scroll position to
|
395 |
+
// determine which DOM updates have to be made, and makes the
|
396 |
+
// updates.
|
397 |
+
function updateDisplayInner(cm, changes, viewPort) {
|
398 |
+
var display = cm.display, doc = cm.view.doc;
|
399 |
+
if (!display.wrapper.clientWidth) {
|
400 |
+
display.showingFrom = display.showingTo = display.viewOffset = 0;
|
401 |
+
return;
|
402 |
+
}
|
403 |
+
|
404 |
+
// Compute the new visible window
|
405 |
+
// If scrollTop is specified, use that to determine which lines
|
406 |
+
// to render instead of the current scrollbar position.
|
407 |
+
var visible = visibleLines(display, doc, viewPort);
|
408 |
+
// Bail out if the visible area is already rendered and nothing changed.
|
409 |
+
if (changes !== true && changes.length == 0 &&
|
410 |
+
visible.from > display.showingFrom && visible.to < display.showingTo)
|
411 |
+
return;
|
412 |
+
|
413 |
+
if (changes && maybeUpdateLineNumberWidth(cm))
|
414 |
+
changes = true;
|
415 |
+
display.sizer.style.marginLeft = display.scrollbarH.style.left = display.gutters.offsetWidth + "px";
|
416 |
+
|
417 |
+
// When merged lines are present, the line that needs to be
|
418 |
+
// redrawn might not be the one that was changed.
|
419 |
+
if (changes !== true && sawCollapsedSpans)
|
420 |
+
for (var i = 0; i < changes.length; ++i) {
|
421 |
+
var ch = changes[i], merged;
|
422 |
+
while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) {
|
423 |
+
var from = merged.find().from.line;
|
424 |
+
if (ch.diff) ch.diff -= ch.from - from;
|
425 |
+
ch.from = from;
|
426 |
+
}
|
427 |
+
}
|
428 |
+
|
429 |
+
// Used to determine which lines need their line numbers updated
|
430 |
+
var positionsChangedFrom = changes === true ? 0 : Infinity;
|
431 |
+
if (cm.options.lineNumbers && changes && changes !== true)
|
432 |
+
for (var i = 0; i < changes.length; ++i)
|
433 |
+
if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
|
434 |
+
|
435 |
+
var from = Math.max(visible.from - cm.options.viewportMargin, 0);
|
436 |
+
var to = Math.min(doc.size, visible.to + cm.options.viewportMargin);
|
437 |
+
if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom;
|
438 |
+
if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo);
|
439 |
+
if (sawCollapsedSpans) {
|
440 |
+
from = lineNo(visualLine(doc, getLine(doc, from)));
|
441 |
+
while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to;
|
442 |
+
}
|
443 |
+
|
444 |
+
// Create a range of theoretically intact lines, and punch holes
|
445 |
+
// in that using the change info.
|
446 |
+
var intact = changes === true ? [] :
|
447 |
+
computeIntact([{from: display.showingFrom, to: display.showingTo}], changes);
|
448 |
+
// Clip off the parts that won't be visible
|
449 |
+
var intactLines = 0;
|
450 |
+
for (var i = 0; i < intact.length; ++i) {
|
451 |
+
var range = intact[i];
|
452 |
+
if (range.from < from) range.from = from;
|
453 |
+
if (range.to > to) range.to = to;
|
454 |
+
if (range.from >= range.to) intact.splice(i--, 1);
|
455 |
+
else intactLines += range.to - range.from;
|
456 |
+
}
|
457 |
+
if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
|
458 |
+
return;
|
459 |
+
intact.sort(function(a, b) {return a.from - b.from;});
|
460 |
+
|
461 |
+
if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
|
462 |
+
patchDisplay(cm, from, to, intact, positionsChangedFrom);
|
463 |
+
display.lineDiv.style.display = "";
|
464 |
+
|
465 |
+
var different = from != display.showingFrom || to != display.showingTo ||
|
466 |
+
display.lastSizeC != display.wrapper.clientHeight;
|
467 |
+
// This is just a bogus formula that detects when the editor is
|
468 |
+
// resized or the font size changes.
|
469 |
+
if (different) display.lastSizeC = display.wrapper.clientHeight;
|
470 |
+
display.showingFrom = from; display.showingTo = to;
|
471 |
+
startWorker(cm, 100);
|
472 |
+
|
473 |
+
var prevBottom = display.lineDiv.offsetTop;
|
474 |
+
for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
|
475 |
+
if (ie_lt8) {
|
476 |
+
var bot = node.offsetTop + node.offsetHeight;
|
477 |
+
height = bot - prevBottom;
|
478 |
+
prevBottom = bot;
|
479 |
+
} else {
|
480 |
+
var box = node.getBoundingClientRect();
|
481 |
+
height = box.bottom - box.top;
|
482 |
+
}
|
483 |
+
var diff = node.lineObj.height - height;
|
484 |
+
if (height < 2) height = textHeight(display);
|
485 |
+
if (diff > .001 || diff < -.001)
|
486 |
+
updateLineHeight(node.lineObj, height);
|
487 |
+
}
|
488 |
+
display.viewOffset = heightAtLine(cm, getLine(doc, from));
|
489 |
+
// Position the mover div to align with the current virtual scroll position
|
490 |
+
display.mover.style.top = display.viewOffset + "px";
|
491 |
+
return true;
|
492 |
+
}
|
493 |
+
|
494 |
+
function computeIntact(intact, changes) {
|
495 |
+
for (var i = 0, l = changes.length || 0; i < l; ++i) {
|
496 |
+
var change = changes[i], intact2 = [], diff = change.diff || 0;
|
497 |
+
for (var j = 0, l2 = intact.length; j < l2; ++j) {
|
498 |
+
var range = intact[j];
|
499 |
+
if (change.to <= range.from && change.diff) {
|
500 |
+
intact2.push({from: range.from + diff, to: range.to + diff});
|
501 |
+
} else if (change.to <= range.from || change.from >= range.to) {
|
502 |
+
intact2.push(range);
|
503 |
+
} else {
|
504 |
+
if (change.from > range.from)
|
505 |
+
intact2.push({from: range.from, to: change.from});
|
506 |
+
if (change.to < range.to)
|
507 |
+
intact2.push({from: change.to + diff, to: range.to + diff});
|
508 |
+
}
|
509 |
+
}
|
510 |
+
intact = intact2;
|
511 |
+
}
|
512 |
+
return intact;
|
513 |
+
}
|
514 |
+
|
515 |
+
function getDimensions(cm) {
|
516 |
+
var d = cm.display, left = {}, width = {};
|
517 |
+
for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
|
518 |
+
left[cm.options.gutters[i]] = n.offsetLeft;
|
519 |
+
width[cm.options.gutters[i]] = n.offsetWidth;
|
520 |
+
}
|
521 |
+
return {fixedPos: compensateForHScroll(d),
|
522 |
+
gutterTotalWidth: d.gutters.offsetWidth,
|
523 |
+
gutterLeft: left,
|
524 |
+
gutterWidth: width,
|
525 |
+
wrapperWidth: d.wrapper.clientWidth};
|
526 |
+
}
|
527 |
+
|
528 |
+
function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
|
529 |
+
var dims = getDimensions(cm);
|
530 |
+
var display = cm.display, lineNumbers = cm.options.lineNumbers;
|
531 |
+
// IE does bad things to nodes when .innerHTML = "" is used on a parent
|
532 |
+
// we still need widgets and markers intact to add back to the new content later
|
533 |
+
if (!intact.length && !ie && (!webkit || !cm.display.currentWheelTarget))
|
534 |
+
removeChildren(display.lineDiv);
|
535 |
+
var container = display.lineDiv, cur = container.firstChild;
|
536 |
+
|
537 |
+
function rm(node) {
|
538 |
+
var next = node.nextSibling;
|
539 |
+
if (webkit && mac && cm.display.currentWheelTarget == node) {
|
540 |
+
node.style.display = "none";
|
541 |
+
node.lineObj = null;
|
542 |
+
} else {
|
543 |
+
container.removeChild(node);
|
544 |
+
}
|
545 |
+
return next;
|
546 |
+
}
|
547 |
+
|
548 |
+
var nextIntact = intact.shift(), lineNo = from;
|
549 |
+
cm.view.doc.iter(from, to, function(line) {
|
550 |
+
if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift();
|
551 |
+
if (lineIsHidden(line)) {
|
552 |
+
if (line.height != 0) updateLineHeight(line, 0);
|
553 |
+
} else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) {
|
554 |
+
// This line is intact. Skip to the actual node. Update its
|
555 |
+
// line number if needed.
|
556 |
+
while (cur.lineObj != line) cur = rm(cur);
|
557 |
+
if (lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber)
|
558 |
+
setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineNo));
|
559 |
+
cur = cur.nextSibling;
|
560 |
+
} else {
|
561 |
+
// This line needs to be generated.
|
562 |
+
var lineNode = buildLineElement(cm, line, lineNo, dims);
|
563 |
+
container.insertBefore(lineNode, cur);
|
564 |
+
lineNode.lineObj = line;
|
565 |
+
}
|
566 |
+
++lineNo;
|
567 |
+
});
|
568 |
+
while (cur) cur = rm(cur);
|
569 |
+
}
|
570 |
+
|
571 |
+
function buildLineElement(cm, line, lineNo, dims) {
|
572 |
+
var lineElement = lineContent(cm, line);
|
573 |
+
var markers = line.gutterMarkers, display = cm.display;
|
574 |
+
|
575 |
+
if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass &&
|
576 |
+
(!line.widgets || !line.widgets.length)) return lineElement;
|
577 |
+
|
578 |
+
// Lines with gutter elements or a background class need
|
579 |
+
// to be wrapped again, and have the extra elements added
|
580 |
+
// to the wrapper div
|
581 |
+
|
582 |
+
var wrap = elt("div", null, line.wrapClass, "position: relative");
|
583 |
+
if (cm.options.lineNumbers || markers) {
|
584 |
+
var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " +
|
585 |
+
dims.fixedPos + "px"));
|
586 |
+
wrap.alignable = [gutterWrap];
|
587 |
+
if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
|
588 |
+
wrap.lineNumber = gutterWrap.appendChild(
|
589 |
+
elt("div", lineNumberFor(cm.options, lineNo),
|
590 |
+
"CodeMirror-linenumber CodeMirror-gutter-elt",
|
591 |
+
"left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
|
592 |
+
+ display.lineNumInnerWidth + "px"));
|
593 |
+
if (markers)
|
594 |
+
for (var k = 0; k < cm.options.gutters.length; ++k) {
|
595 |
+
var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
|
596 |
+
if (found)
|
597 |
+
gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
|
598 |
+
dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
|
599 |
+
}
|
600 |
+
}
|
601 |
+
// Kludge to make sure the styled element lies behind the selection (by z-index)
|
602 |
+
if (line.bgClass)
|
603 |
+
wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"));
|
604 |
+
wrap.appendChild(lineElement);
|
605 |
+
if (line.widgets)
|
606 |
+
for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
|
607 |
+
var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
|
608 |
+
node.widget = widget;
|
609 |
+
if (widget.noHScroll) {
|
610 |
+
(wrap.alignable || (wrap.alignable = [])).push(node);
|
611 |
+
var width = dims.wrapperWidth;
|
612 |
+
node.style.left = dims.fixedPos + "px";
|
613 |
+
if (!widget.coverGutter) {
|
614 |
+
width -= dims.gutterTotalWidth;
|
615 |
+
node.style.paddingLeft = dims.gutterTotalWidth + "px";
|
616 |
+
}
|
617 |
+
node.style.width = width + "px";
|
618 |
+
}
|
619 |
+
if (widget.coverGutter) {
|
620 |
+
node.style.zIndex = 5;
|
621 |
+
node.style.position = "relative";
|
622 |
+
if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
|
623 |
+
}
|
624 |
+
if (widget.above)
|
625 |
+
wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
|
626 |
+
else
|
627 |
+
wrap.appendChild(node);
|
628 |
+
}
|
629 |
+
|
630 |
+
if (ie_lt8) wrap.style.zIndex = 2;
|
631 |
+
return wrap;
|
632 |
+
}
|
633 |
+
|
634 |
+
// SELECTION / CURSOR
|
635 |
+
|
636 |
+
function updateSelection(cm) {
|
637 |
+
var display = cm.display;
|
638 |
+
var collapsed = posEq(cm.view.sel.from, cm.view.sel.to);
|
639 |
+
if (collapsed || cm.options.showCursorWhenSelecting)
|
640 |
+
updateSelectionCursor(cm);
|
641 |
+
else
|
642 |
+
display.cursor.style.display = display.otherCursor.style.display = "none";
|
643 |
+
if (!collapsed)
|
644 |
+
updateSelectionRange(cm);
|
645 |
+
else
|
646 |
+
display.selectionDiv.style.display = "none";
|
647 |
+
|
648 |
+
// Move the hidden textarea near the cursor to prevent scrolling artifacts
|
649 |
+
var headPos = cursorCoords(cm, cm.view.sel.head, "div");
|
650 |
+
var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
|
651 |
+
display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
|
652 |
+
headPos.top + lineOff.top - wrapOff.top)) + "px";
|
653 |
+
display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
|
654 |
+
headPos.left + lineOff.left - wrapOff.left)) + "px";
|
655 |
+
}
|
656 |
+
|
657 |
+
// No selection, plain cursor
|
658 |
+
function updateSelectionCursor(cm) {
|
659 |
+
var display = cm.display, pos = cursorCoords(cm, cm.view.sel.head, "div");
|
660 |
+
display.cursor.style.left = pos.left + "px";
|
661 |
+
display.cursor.style.top = pos.top + "px";
|
662 |
+
display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
|
663 |
+
display.cursor.style.display = "";
|
664 |
+
|
665 |
+
if (pos.other) {
|
666 |
+
display.otherCursor.style.display = "";
|
667 |
+
display.otherCursor.style.left = pos.other.left + "px";
|
668 |
+
display.otherCursor.style.top = pos.other.top + "px";
|
669 |
+
display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
|
670 |
+
} else { display.otherCursor.style.display = "none"; }
|
671 |
+
}
|
672 |
+
|
673 |
+
// Highlight selection
|
674 |
+
function updateSelectionRange(cm) {
|
675 |
+
var display = cm.display, doc = cm.view.doc, sel = cm.view.sel;
|
676 |
+
var fragment = document.createDocumentFragment();
|
677 |
+
var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
|
678 |
+
|
679 |
+
function add(left, top, width, bottom) {
|
680 |
+
if (top < 0) top = 0;
|
681 |
+
fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
|
682 |
+
"px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
|
683 |
+
"px; height: " + (bottom - top) + "px"));
|
684 |
+
}
|
685 |
+
|
686 |
+
function drawForLine(line, fromArg, toArg, retTop) {
|
687 |
+
var lineObj = getLine(doc, line);
|
688 |
+
var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
|
689 |
+
function coords(ch) {
|
690 |
+
return charCoords(cm, {line: line, ch: ch}, "div", lineObj);
|
691 |
+
}
|
692 |
+
|
693 |
+
iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
|
694 |
+
var leftPos = coords(dir == "rtl" ? to - 1 : from);
|
695 |
+
var rightPos = coords(dir == "rtl" ? from : to - 1);
|
696 |
+
var left = leftPos.left, right = rightPos.right;
|
697 |
+
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
|
698 |
+
add(left, leftPos.top, null, leftPos.bottom);
|
699 |
+
left = pl;
|
700 |
+
if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
|
701 |
+
}
|
702 |
+
if (toArg == null && to == lineLen) right = clientWidth;
|
703 |
+
if (fromArg == null && from == 0) left = pl;
|
704 |
+
rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
|
705 |
+
if (left < pl + 1) left = pl;
|
706 |
+
add(left, rightPos.top, right - left, rightPos.bottom);
|
707 |
+
});
|
708 |
+
return rVal;
|
709 |
+
}
|
710 |
+
|
711 |
+
if (sel.from.line == sel.to.line) {
|
712 |
+
drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
|
713 |
+
} else {
|
714 |
+
var fromObj = getLine(doc, sel.from.line);
|
715 |
+
var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
|
716 |
+
while (merged = collapsedSpanAtEnd(cur)) {
|
717 |
+
var found = merged.find();
|
718 |
+
path.push(found.from.ch, found.to.line, found.to.ch);
|
719 |
+
if (found.to.line == sel.to.line) {
|
720 |
+
path.push(sel.to.ch);
|
721 |
+
singleLine = true;
|
722 |
+
break;
|
723 |
+
}
|
724 |
+
cur = getLine(doc, found.to.line);
|
725 |
+
}
|
726 |
+
|
727 |
+
// This is a single, merged line
|
728 |
+
if (singleLine) {
|
729 |
+
for (var i = 0; i < path.length; i += 3)
|
730 |
+
drawForLine(path[i], path[i+1], path[i+2]);
|
731 |
+
} else {
|
732 |
+
var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
|
733 |
+
if (sel.from.ch)
|
734 |
+
// Draw the first line of selection.
|
735 |
+
middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
|
736 |
+
else
|
737 |
+
// Simply include it in the middle block.
|
738 |
+
middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
|
739 |
+
|
740 |
+
if (!sel.to.ch)
|
741 |
+
middleBot = heightAtLine(cm, toObj) - display.viewOffset;
|
742 |
+
else
|
743 |
+
middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
|
744 |
+
|
745 |
+
if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
|
746 |
+
}
|
747 |
+
}
|
748 |
+
|
749 |
+
removeChildrenAndAdd(display.selectionDiv, fragment);
|
750 |
+
display.selectionDiv.style.display = "";
|
751 |
+
}
|
752 |
+
|
753 |
+
// Cursor-blinking
|
754 |
+
function restartBlink(cm) {
|
755 |
+
var display = cm.display;
|
756 |
+
clearInterval(display.blinker);
|
757 |
+
var on = true;
|
758 |
+
display.cursor.style.visibility = display.otherCursor.style.visibility = "";
|
759 |
+
display.blinker = setInterval(function() {
|
760 |
+
if (!display.cursor.offsetHeight) return;
|
761 |
+
display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
|
762 |
+
}, cm.options.cursorBlinkRate);
|
763 |
+
}
|
764 |
+
|
765 |
+
// HIGHLIGHT WORKER
|
766 |
+
|
767 |
+
function startWorker(cm, time) {
|
768 |
+
if (cm.view.frontier < cm.display.showingTo)
|
769 |
+
cm.view.highlight.set(time, bind(highlightWorker, cm));
|
770 |
+
}
|
771 |
+
|
772 |
+
function highlightWorker(cm) {
|
773 |
+
var view = cm.view, doc = view.doc;
|
774 |
+
if (view.frontier >= cm.display.showingTo) return;
|
775 |
+
var end = +new Date + cm.options.workTime;
|
776 |
+
var state = copyState(view.mode, getStateBefore(cm, view.frontier));
|
777 |
+
var changed = [], prevChange;
|
778 |
+
doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) {
|
779 |
+
if (view.frontier >= cm.display.showingFrom) { // Visible
|
780 |
+
if (highlightLine(cm, line, state) && view.frontier >= cm.display.showingFrom) {
|
781 |
+
if (prevChange && prevChange.end == view.frontier) prevChange.end++;
|
782 |
+
else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1});
|
783 |
+
}
|
784 |
+
line.stateAfter = copyState(view.mode, state);
|
785 |
+
} else {
|
786 |
+
processLine(cm, line, state);
|
787 |
+
line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null;
|
788 |
+
}
|
789 |
+
++view.frontier;
|
790 |
+
if (+new Date > end) {
|
791 |
+
startWorker(cm, cm.options.workDelay);
|
792 |
+
return true;
|
793 |
+
}
|
794 |
+
});
|
795 |
+
if (changed.length)
|
796 |
+
operation(cm, function() {
|
797 |
+
for (var i = 0; i < changed.length; ++i)
|
798 |
+
regChange(this, changed[i].start, changed[i].end);
|
799 |
+
})();
|
800 |
+
}
|
801 |
+
|
802 |
+
// Finds the line to start with when starting a parse. Tries to
|
803 |
+
// find a line with a stateAfter, so that it can start with a
|
804 |
+
// valid state. If that fails, it returns the line with the
|
805 |
+
// smallest indentation, which tends to need the least context to
|
806 |
+
// parse correctly.
|
807 |
+
function findStartLine(cm, n) {
|
808 |
+
var minindent, minline, doc = cm.view.doc;
|
809 |
+
for (var search = n, lim = n - 100; search > lim; --search) {
|
810 |
+
if (search == 0) return 0;
|
811 |
+
var line = getLine(doc, search-1);
|
812 |
+
if (line.stateAfter) return search;
|
813 |
+
var indented = countColumn(line.text, null, cm.options.tabSize);
|
814 |
+
if (minline == null || minindent > indented) {
|
815 |
+
minline = search - 1;
|
816 |
+
minindent = indented;
|
817 |
+
}
|
818 |
+
}
|
819 |
+
return minline;
|
820 |
+
}
|
821 |
+
|
822 |
+
function getStateBefore(cm, n) {
|
823 |
+
var view = cm.view;
|
824 |
+
var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter;
|
825 |
+
if (!state) state = startState(view.mode);
|
826 |
+
else state = copyState(view.mode, state);
|
827 |
+
view.doc.iter(pos, n, function(line) {
|
828 |
+
processLine(cm, line, state);
|
829 |
+
var save = pos == n - 1 || pos % 5 == 0 || pos >= view.showingFrom && pos < view.showingTo;
|
830 |
+
line.stateAfter = save ? copyState(view.mode, state) : null;
|
831 |
+
++pos;
|
832 |
+
});
|
833 |
+
return state;
|
834 |
+
}
|
835 |
+
|
836 |
+
// POSITION MEASUREMENT
|
837 |
+
|
838 |
+
function paddingTop(display) {return display.lineSpace.offsetTop;}
|
839 |
+
function paddingLeft(display) {
|
840 |
+
var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x"));
|
841 |
+
return e.offsetLeft;
|
842 |
+
}
|
843 |
+
|
844 |
+
function measureChar(cm, line, ch, data) {
|
845 |
+
var data = data || measureLine(cm, line), dir = -1;
|
846 |
+
for (var pos = ch;; pos += dir) {
|
847 |
+
var r = data[pos];
|
848 |
+
if (r) break;
|
849 |
+
if (dir < 0 && pos == 0) dir = 1;
|
850 |
+
}
|
851 |
+
return {left: pos < ch ? r.right : r.left,
|
852 |
+
right: pos > ch ? r.left : r.right,
|
853 |
+
top: r.top, bottom: r.bottom};
|
854 |
+
}
|
855 |
+
|
856 |
+
function measureLine(cm, line) {
|
857 |
+
// First look in the cache
|
858 |
+
var display = cm.display, cache = cm.display.measureLineCache;
|
859 |
+
for (var i = 0; i < cache.length; ++i) {
|
860 |
+
var memo = cache[i];
|
861 |
+
if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
|
862 |
+
display.scroller.clientWidth == memo.width)
|
863 |
+
return memo.measure;
|
864 |
+
}
|
865 |
+
|
866 |
+
var measure = measureLineInner(cm, line);
|
867 |
+
// Store result in the cache
|
868 |
+
var memo = {text: line.text, width: display.scroller.clientWidth,
|
869 |
+
markedSpans: line.markedSpans, measure: measure};
|
870 |
+
if (cache.length == 16) cache[++display.measureLineCachePos % 16] = memo;
|
871 |
+
else cache.push(memo);
|
872 |
+
return measure;
|
873 |
+
}
|
874 |
+
|
875 |
+
function measureLineInner(cm, line) {
|
876 |
+
var display = cm.display, measure = emptyArray(line.text.length);
|
877 |
+
var pre = lineContent(cm, line, measure);
|
878 |
+
|
879 |
+
// IE does not cache element positions of inline elements between
|
880 |
+
// calls to getBoundingClientRect. This makes the loop below,
|
881 |
+
// which gathers the positions of all the characters on the line,
|
882 |
+
// do an amount of layout work quadratic to the number of
|
883 |
+
// characters. When line wrapping is off, we try to improve things
|
884 |
+
// by first subdividing the line into a bunch of inline blocks, so
|
885 |
+
// that IE can reuse most of the layout information from caches
|
886 |
+
// for those blocks. This does interfere with line wrapping, so it
|
887 |
+
// doesn't work when wrapping is on, but in that case the
|
888 |
+
// situation is slightly better, since IE does cache line-wrapping
|
889 |
+
// information and only recomputes per-line.
|
890 |
+
if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
|
891 |
+
var fragment = document.createDocumentFragment();
|
892 |
+
var chunk = 10, n = pre.childNodes.length;
|
893 |
+
for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
|
894 |
+
var wrap = elt("div", null, null, "display: inline-block");
|
895 |
+
for (var j = 0; j < chunk && n; ++j) {
|
896 |
+
wrap.appendChild(pre.firstChild);
|
897 |
+
--n;
|
898 |
+
}
|
899 |
+
fragment.appendChild(wrap);
|
900 |
+
}
|
901 |
+
pre.appendChild(fragment);
|
902 |
+
}
|
903 |
+
|
904 |
+
removeChildrenAndAdd(display.measure, pre);
|
905 |
+
|
906 |
+
var outer = display.lineDiv.getBoundingClientRect();
|
907 |
+
var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
|
908 |
+
for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
|
909 |
+
var size = cur.getBoundingClientRect();
|
910 |
+
var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
|
911 |
+
for (var j = 0; j < vranges.length; j += 2) {
|
912 |
+
var rtop = vranges[j], rbot = vranges[j+1];
|
913 |
+
if (rtop > bot || rbot < top) continue;
|
914 |
+
if (rtop <= top && rbot >= bot ||
|
915 |
+
top <= rtop && bot >= rbot ||
|
916 |
+
Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
|
917 |
+
vranges[j] = Math.min(top, rtop);
|
918 |
+
vranges[j+1] = Math.max(bot, rbot);
|
919 |
+
break;
|
920 |
+
}
|
921 |
+
}
|
922 |
+
if (j == vranges.length) vranges.push(top, bot);
|
923 |
+
data[i] = {left: size.left - outer.left, right: size.right - outer.left, top: j};
|
924 |
+
}
|
925 |
+
for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
|
926 |
+
var vr = cur.top;
|
927 |
+
cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
|
928 |
+
}
|
929 |
+
return data;
|
930 |
+
}
|
931 |
+
|
932 |
+
function clearCaches(cm) {
|
933 |
+
cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
|
934 |
+
cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
|
935 |
+
cm.view.maxLineChanged = true;
|
936 |
+
}
|
937 |
+
|
938 |
+
// Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
|
939 |
+
function intoCoordSystem(cm, lineObj, rect, context) {
|
940 |
+
if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
|
941 |
+
var size = lineObj.widgets[i].node.offsetHeight;
|
942 |
+
rect.top += size; rect.bottom += size;
|
943 |
+
}
|
944 |
+
if (context == "line") return rect;
|
945 |
+
if (!context) context = "local";
|
946 |
+
var yOff = heightAtLine(cm, lineObj);
|
947 |
+
if (context != "local") yOff -= cm.display.viewOffset;
|
948 |
+
if (context == "page") {
|
949 |
+
var lOff = cm.display.lineSpace.getBoundingClientRect();
|
950 |
+
yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
|
951 |
+
var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
|
952 |
+
rect.left += xOff; rect.right += xOff;
|
953 |
+
}
|
954 |
+
rect.top += yOff; rect.bottom += yOff;
|
955 |
+
return rect;
|
956 |
+
}
|
957 |
+
|
958 |
+
function charCoords(cm, pos, context, lineObj) {
|
959 |
+
if (!lineObj) lineObj = getLine(cm.view.doc, pos.line);
|
960 |
+
return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
|
961 |
+
}
|
962 |
+
|
963 |
+
function cursorCoords(cm, pos, context, lineObj, measurement) {
|
964 |
+
lineObj = lineObj || getLine(cm.view.doc, pos.line);
|
965 |
+
if (!measurement) measurement = measureLine(cm, lineObj);
|
966 |
+
function get(ch, right) {
|
967 |
+
var m = measureChar(cm, lineObj, ch, measurement);
|
968 |
+
if (right) m.left = m.right; else m.right = m.left;
|
969 |
+
return intoCoordSystem(cm, lineObj, m, context);
|
970 |
+
}
|
971 |
+
var order = getOrder(lineObj), ch = pos.ch;
|
972 |
+
if (!order) return get(ch);
|
973 |
+
var main, other, linedir = order[0].level;
|
974 |
+
for (var i = 0; i < order.length; ++i) {
|
975 |
+
var part = order[i], rtl = part.level % 2, nb, here;
|
976 |
+
if (part.from < ch && part.to > ch) return get(ch, rtl);
|
977 |
+
var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
|
978 |
+
if (left == ch) {
|
979 |
+
// Opera and IE return bogus offsets and widths for edges
|
980 |
+
// where the direction flips, but only for the side with the
|
981 |
+
// lower level. So we try to use the side with the higher
|
982 |
+
// level.
|
983 |
+
if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
|
984 |
+
else here = get(rtl && part.from != part.to ? ch - 1 : ch);
|
985 |
+
if (rtl == linedir) main = here; else other = here;
|
986 |
+
} else if (right == ch) {
|
987 |
+
var nb = i < order.length - 1 && order[i+1];
|
988 |
+
if (!rtl && nb && nb.from == nb.to) continue;
|
989 |
+
if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
|
990 |
+
else here = get(rtl ? ch : ch - 1, true);
|
991 |
+
if (rtl == linedir) main = here; else other = here;
|
992 |
+
}
|
993 |
+
}
|
994 |
+
if (linedir && !ch) other = get(order[0].to - 1);
|
995 |
+
if (!main) return other;
|
996 |
+
if (other) main.other = other;
|
997 |
+
return main;
|
998 |
+
}
|
999 |
+
|
1000 |
+
// Coords must be lineSpace-local
|
1001 |
+
function coordsChar(cm, x, y) {
|
1002 |
+
var doc = cm.view.doc;
|
1003 |
+
y += cm.display.viewOffset;
|
1004 |
+
if (y < 0) return {line: 0, ch: 0, outside: true};
|
1005 |
+
var lineNo = lineAtHeight(doc, y);
|
1006 |
+
if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length};
|
1007 |
+
if (x < 0) x = 0;
|
1008 |
+
|
1009 |
+
for (;;) {
|
1010 |
+
var lineObj = getLine(doc, lineNo);
|
1011 |
+
var found = coordsCharInner(cm, lineObj, lineNo, x, y);
|
1012 |
+
var merged = collapsedSpanAtEnd(lineObj);
|
1013 |
+
if (merged && found.ch == lineRight(lineObj))
|
1014 |
+
lineNo = merged.find().to.line;
|
1015 |
+
else
|
1016 |
+
return found;
|
1017 |
+
}
|
1018 |
+
}
|
1019 |
+
|
1020 |
+
function coordsCharInner(cm, lineObj, lineNo, x, y) {
|
1021 |
+
var innerOff = y - heightAtLine(cm, lineObj);
|
1022 |
+
var wrongLine = false, cWidth = cm.display.wrapper.clientWidth;
|
1023 |
+
var measurement = measureLine(cm, lineObj);
|
1024 |
+
|
1025 |
+
function getX(ch) {
|
1026 |
+
var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line",
|
1027 |
+
lineObj, measurement);
|
1028 |
+
wrongLine = true;
|
1029 |
+
if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth);
|
1030 |
+
else if (innerOff < sp.top) return sp.left + cWidth;
|
1031 |
+
else wrongLine = false;
|
1032 |
+
return sp.left;
|
1033 |
+
}
|
1034 |
+
|
1035 |
+
var bidi = getOrder(lineObj), dist = lineObj.text.length;
|
1036 |
+
var from = lineLeft(lineObj), to = lineRight(lineObj);
|
1037 |
+
var fromX = paddingLeft(cm.display), toX = getX(to);
|
1038 |
+
|
1039 |
+
if (x > toX) return {line: lineNo, ch: to, outside: wrongLine};
|
1040 |
+
// Do a binary search between these bounds.
|
1041 |
+
for (;;) {
|
1042 |
+
if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
|
1043 |
+
var after = x - fromX < toX - x, ch = after ? from : to;
|
1044 |
+
while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
|
1045 |
+
return {line: lineNo, ch: ch, after: after, outside: wrongLine};
|
1046 |
+
}
|
1047 |
+
var step = Math.ceil(dist / 2), middle = from + step;
|
1048 |
+
if (bidi) {
|
1049 |
+
middle = from;
|
1050 |
+
for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
|
1051 |
+
}
|
1052 |
+
var middleX = getX(middle);
|
1053 |
+
if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;}
|
1054 |
+
else {from = middle; fromX = middleX; dist = step;}
|
1055 |
+
}
|
1056 |
+
}
|
1057 |
+
|
1058 |
+
var measureText;
|
1059 |
+
function textHeight(display) {
|
1060 |
+
if (display.cachedTextHeight != null) return display.cachedTextHeight;
|
1061 |
+
if (measureText == null) {
|
1062 |
+
measureText = elt("pre");
|
1063 |
+
// Measure a bunch of lines, for browsers that compute
|
1064 |
+
// fractional heights.
|
1065 |
+
for (var i = 0; i < 49; ++i) {
|
1066 |
+
measureText.appendChild(document.createTextNode("x"));
|
1067 |
+
measureText.appendChild(elt("br"));
|
1068 |
+
}
|
1069 |
+
measureText.appendChild(document.createTextNode("x"));
|
1070 |
+
}
|
1071 |
+
removeChildrenAndAdd(display.measure, measureText);
|
1072 |
+
var height = measureText.offsetHeight / 50;
|
1073 |
+
if (height > 3) display.cachedTextHeight = height;
|
1074 |
+
removeChildren(display.measure);
|
1075 |
+
return height || 1;
|
1076 |
+
}
|
1077 |
+
|
1078 |
+
function charWidth(display) {
|
1079 |
+
if (display.cachedCharWidth != null) return display.cachedCharWidth;
|
1080 |
+
var anchor = elt("span", "x");
|
1081 |
+
var pre = elt("pre", [anchor]);
|
1082 |
+
removeChildrenAndAdd(display.measure, pre);
|
1083 |
+
var width = anchor.offsetWidth;
|
1084 |
+
if (width > 2) display.cachedCharWidth = width;
|
1085 |
+
return width || 10;
|
1086 |
+
}
|
1087 |
+
|
1088 |
+
// OPERATIONS
|
1089 |
+
|
1090 |
+
// Operations are used to wrap changes in such a way that each
|
1091 |
+
// change won't have to update the cursor and display (which would
|
1092 |
+
// be awkward, slow, and error-prone), but instead updates are
|
1093 |
+
// batched and then all combined and executed at once.
|
1094 |
+
|
1095 |
+
function startOperation(cm) {
|
1096 |
+
if (cm.curOp) ++cm.curOp.depth;
|
1097 |
+
else cm.curOp = {
|
1098 |
+
// Nested operations delay update until the outermost one
|
1099 |
+
// finishes.
|
1100 |
+
depth: 1,
|
1101 |
+
// An array of ranges of lines that have to be updated. See
|
1102 |
+
// updateDisplay.
|
1103 |
+
changes: [],
|
1104 |
+
delayedCallbacks: [],
|
1105 |
+
updateInput: null,
|
1106 |
+
userSelChange: null,
|
1107 |
+
textChanged: null,
|
1108 |
+
selectionChanged: false,
|
1109 |
+
updateMaxLine: false,
|
1110 |
+
id: ++cm.nextOpId
|
1111 |
+
};
|
1112 |
+
}
|
1113 |
+
|
1114 |
+
function endOperation(cm) {
|
1115 |
+
var op = cm.curOp;
|
1116 |
+
if (--op.depth) return;
|
1117 |
+
cm.curOp = null;
|
1118 |
+
var view = cm.view, display = cm.display;
|
1119 |
+
if (op.updateMaxLine) computeMaxLength(view);
|
1120 |
+
if (view.maxLineChanged && !cm.options.lineWrapping) {
|
1121 |
+
var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right;
|
1122 |
+
display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
|
1123 |
+
view.maxLineChanged = false;
|
1124 |
+
}
|
1125 |
+
var newScrollPos, updated;
|
1126 |
+
if (op.selectionChanged) {
|
1127 |
+
var coords = cursorCoords(cm, view.sel.head);
|
1128 |
+
newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
|
1129 |
+
}
|
1130 |
+
if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
|
1131 |
+
updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
|
1132 |
+
if (!updated && op.selectionChanged) updateSelection(cm);
|
1133 |
+
if (newScrollPos) scrollCursorIntoView(cm);
|
1134 |
+
if (op.selectionChanged) restartBlink(cm);
|
1135 |
+
|
1136 |
+
if (view.focused && op.updateInput)
|
1137 |
+
resetInput(cm, op.userSelChange);
|
1138 |
+
|
1139 |
+
if (op.textChanged)
|
1140 |
+
signal(cm, "change", cm, op.textChanged);
|
1141 |
+
if (op.selectionChanged) signal(cm, "cursorActivity", cm);
|
1142 |
+
for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm);
|
1143 |
+
}
|
1144 |
+
|
1145 |
+
// Wraps a function in an operation. Returns the wrapped function.
|
1146 |
+
function operation(cm1, f) {
|
1147 |
+
return function() {
|
1148 |
+
var cm = cm1 || this;
|
1149 |
+
startOperation(cm);
|
1150 |
+
try {var result = f.apply(cm, arguments);}
|
1151 |
+
finally {endOperation(cm);}
|
1152 |
+
return result;
|
1153 |
+
};
|
1154 |
+
}
|
1155 |
+
|
1156 |
+
function regChange(cm, from, to, lendiff) {
|
1157 |
+
cm.curOp.changes.push({from: from, to: to, diff: lendiff});
|
1158 |
+
}
|
1159 |
+
|
1160 |
+
// INPUT HANDLING
|
1161 |
+
|
1162 |
+
function slowPoll(cm) {
|
1163 |
+
if (cm.view.pollingFast) return;
|
1164 |
+
cm.display.poll.set(cm.options.pollInterval, function() {
|
1165 |
+
readInput(cm);
|
1166 |
+
if (cm.view.focused) slowPoll(cm);
|
1167 |
+
});
|
1168 |
+
}
|
1169 |
+
|
1170 |
+
function fastPoll(cm) {
|
1171 |
+
var missed = false;
|
1172 |
+
cm.display.pollingFast = true;
|
1173 |
+
function p() {
|
1174 |
+
var changed = readInput(cm);
|
1175 |
+
if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
|
1176 |
+
else {cm.display.pollingFast = false; slowPoll(cm);}
|
1177 |
+
}
|
1178 |
+
cm.display.poll.set(20, p);
|
1179 |
+
}
|
1180 |
+
|
1181 |
+
// prevInput is a hack to work with IME. If we reset the textarea
|
1182 |
+
// on every change, that breaks IME. So we look for changes
|
1183 |
+
// compared to the previous content instead. (Modern browsers have
|
1184 |
+
// events that indicate IME taking place, but these are not widely
|
1185 |
+
// supported or compatible enough yet to rely on.)
|
1186 |
+
function readInput(cm) {
|
1187 |
+
var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel;
|
1188 |
+
if (!view.focused || hasSelection(input) || isReadOnly(cm)) return false;
|
1189 |
+
var text = input.value;
|
1190 |
+
if (text == prevInput && posEq(sel.from, sel.to)) return false;
|
1191 |
+
startOperation(cm);
|
1192 |
+
view.sel.shift = false;
|
1193 |
+
var same = 0, l = Math.min(prevInput.length, text.length);
|
1194 |
+
while (same < l && prevInput[same] == text[same]) ++same;
|
1195 |
+
var from = sel.from, to = sel.to;
|
1196 |
+
if (same < prevInput.length)
|
1197 |
+
from = {line: from.line, ch: from.ch - (prevInput.length - same)};
|
1198 |
+
else if (view.overwrite && posEq(from, to) && !cm.display.pasteIncoming)
|
1199 |
+
to = {line: to.line, ch: Math.min(getLine(cm.view.doc, to.line).text.length, to.ch + (text.length - same))};
|
1200 |
+
var updateInput = cm.curOp.updateInput;
|
1201 |
+
updateDoc(cm, from, to, splitLines(text.slice(same)), "end",
|
1202 |
+
cm.display.pasteIncoming ? "paste" : "input", {from: from, to: to});
|
1203 |
+
cm.curOp.updateInput = updateInput;
|
1204 |
+
if (text.length > 1000) input.value = cm.display.prevInput = "";
|
1205 |
+
else cm.display.prevInput = text;
|
1206 |
+
endOperation(cm);
|
1207 |
+
cm.display.pasteIncoming = false;
|
1208 |
+
return true;
|
1209 |
+
}
|
1210 |
+
|
1211 |
+
function resetInput(cm, user) {
|
1212 |
+
var view = cm.view, minimal, selected;
|
1213 |
+
if (!posEq(view.sel.from, view.sel.to)) {
|
1214 |
+
cm.display.prevInput = "";
|
1215 |
+
minimal = hasCopyEvent &&
|
1216 |
+
(view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
|
1217 |
+
if (minimal) cm.display.input.value = "-";
|
1218 |
+
else cm.display.input.value = selected || cm.getSelection();
|
1219 |
+
if (view.focused) selectInput(cm.display.input);
|
1220 |
+
} else if (user) cm.display.prevInput = cm.display.input.value = "";
|
1221 |
+
cm.display.inaccurateSelection = minimal;
|
1222 |
+
}
|
1223 |
+
|
1224 |
+
function focusInput(cm) {
|
1225 |
+
if (cm.options.readOnly != "nocursor" && (ie || document.activeElement != cm.display.input))
|
1226 |
+
cm.display.input.focus();
|
1227 |
+
}
|
1228 |
+
|
1229 |
+
function isReadOnly(cm) {
|
1230 |
+
return cm.options.readOnly || cm.view.cantEdit;
|
1231 |
+
}
|
1232 |
+
|
1233 |
+
// EVENT HANDLERS
|
1234 |
+
|
1235 |
+
function registerEventHandlers(cm) {
|
1236 |
+
var d = cm.display;
|
1237 |
+
on(d.scroller, "mousedown", operation(cm, onMouseDown));
|
1238 |
+
on(d.scroller, "dblclick", operation(cm, e_preventDefault));
|
1239 |
+
on(d.lineSpace, "selectstart", function(e) {
|
1240 |
+
if (!mouseEventInWidget(d, e)) e_preventDefault(e);
|
1241 |
+
});
|
1242 |
+
// Gecko browsers fire contextmenu *after* opening the menu, at
|
1243 |
+
// which point we can't mess with it anymore. Context menu is
|
1244 |
+
// handled in onMouseDown for Gecko.
|
1245 |
+
if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
|
1246 |
+
|
1247 |
+
on(d.scroller, "scroll", function() {
|
1248 |
+
setScrollTop(cm, d.scroller.scrollTop);
|
1249 |
+
setScrollLeft(cm, d.scroller.scrollLeft, true);
|
1250 |
+
signal(cm, "scroll", cm);
|
1251 |
+
});
|
1252 |
+
on(d.scrollbarV, "scroll", function() {
|
1253 |
+
setScrollTop(cm, d.scrollbarV.scrollTop);
|
1254 |
+
});
|
1255 |
+
on(d.scrollbarH, "scroll", function() {
|
1256 |
+
setScrollLeft(cm, d.scrollbarH.scrollLeft);
|
1257 |
+
});
|
1258 |
+
|
1259 |
+
on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
|
1260 |
+
on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
|
1261 |
+
|
1262 |
+
function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); }
|
1263 |
+
on(d.scrollbarH, "mousedown", reFocus);
|
1264 |
+
on(d.scrollbarV, "mousedown", reFocus);
|
1265 |
+
// Prevent wrapper from ever scrolling
|
1266 |
+
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
|
1267 |
+
on(window, "resize", function resizeHandler() {
|
1268 |
+
// Might be a text scaling operation, clear size caches.
|
1269 |
+
d.cachedCharWidth = d.cachedTextHeight = null;
|
1270 |
+
clearCaches(cm);
|
1271 |
+
if (d.wrapper.parentNode) updateDisplay(cm, true);
|
1272 |
+
else off(window, "resize", resizeHandler);
|
1273 |
+
});
|
1274 |
+
|
1275 |
+
on(d.input, "keyup", operation(cm, function(e) {
|
1276 |
+
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1277 |
+
if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = false;
|
1278 |
+
}));
|
1279 |
+
on(d.input, "input", bind(fastPoll, cm));
|
1280 |
+
on(d.input, "keydown", operation(cm, onKeyDown));
|
1281 |
+
on(d.input, "keypress", operation(cm, onKeyPress));
|
1282 |
+
on(d.input, "focus", bind(onFocus, cm));
|
1283 |
+
on(d.input, "blur", bind(onBlur, cm));
|
1284 |
+
|
1285 |
+
function drag_(e) {
|
1286 |
+
if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
|
1287 |
+
e_stop(e);
|
1288 |
+
}
|
1289 |
+
if (cm.options.dragDrop) {
|
1290 |
+
on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
|
1291 |
+
on(d.scroller, "dragenter", drag_);
|
1292 |
+
on(d.scroller, "dragover", drag_);
|
1293 |
+
on(d.scroller, "drop", operation(cm, onDrop));
|
1294 |
+
}
|
1295 |
+
on(d.scroller, "paste", function(){focusInput(cm); fastPoll(cm);});
|
1296 |
+
on(d.input, "paste", function() {
|
1297 |
+
d.pasteIncoming = true;
|
1298 |
+
fastPoll(cm);
|
1299 |
+
});
|
1300 |
+
|
1301 |
+
function prepareCopy() {
|
1302 |
+
if (d.inaccurateSelection) {
|
1303 |
+
d.prevInput = "";
|
1304 |
+
d.inaccurateSelection = false;
|
1305 |
+
d.input.value = cm.getSelection();
|
1306 |
+
selectInput(d.input);
|
1307 |
+
}
|
1308 |
+
}
|
1309 |
+
on(d.input, "cut", prepareCopy);
|
1310 |
+
on(d.input, "copy", prepareCopy);
|
1311 |
+
|
1312 |
+
// Needed to handle Tab key in KHTML
|
1313 |
+
if (khtml) on(d.sizer, "mouseup", function() {
|
1314 |
+
if (document.activeElement == d.input) d.input.blur();
|
1315 |
+
focusInput(cm);
|
1316 |
+
});
|
1317 |
+
}
|
1318 |
+
|
1319 |
+
function mouseEventInWidget(display, e) {
|
1320 |
+
for (var n = e_target(e); n != display.wrapper; n = n.parentNode)
|
1321 |
+
if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
|
1322 |
+
n.parentNode == display.sizer && n != display.mover) return true;
|
1323 |
+
}
|
1324 |
+
|
1325 |
+
function posFromMouse(cm, e, liberal) {
|
1326 |
+
var display = cm.display;
|
1327 |
+
if (!liberal) {
|
1328 |
+
var target = e_target(e);
|
1329 |
+
if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
|
1330 |
+
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
|
1331 |
+
target == display.scrollbarFiller) return null;
|
1332 |
+
}
|
1333 |
+
var x, y, space = display.lineSpace.getBoundingClientRect();
|
1334 |
+
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
|
1335 |
+
try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
|
1336 |
+
return coordsChar(cm, x - space.left, y - space.top);
|
1337 |
+
}
|
1338 |
+
|
1339 |
+
var lastClick, lastDoubleClick;
|
1340 |
+
function onMouseDown(e) {
|
1341 |
+
var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc;
|
1342 |
+
sel.shift = e_prop(e, "shiftKey");
|
1343 |
+
|
1344 |
+
if (mouseEventInWidget(display, e)) {
|
1345 |
+
if (!webkit) {
|
1346 |
+
display.scroller.draggable = false;
|
1347 |
+
setTimeout(function(){display.scroller.draggable = true;}, 100);
|
1348 |
+
}
|
1349 |
+
return;
|
1350 |
+
}
|
1351 |
+
if (clickInGutter(cm, e)) return;
|
1352 |
+
var start = posFromMouse(cm, e);
|
1353 |
+
|
1354 |
+
switch (e_button(e)) {
|
1355 |
+
case 3:
|
1356 |
+
if (gecko) onContextMenu.call(cm, cm, e);
|
1357 |
+
return;
|
1358 |
+
case 2:
|
1359 |
+
if (start) extendSelection(cm, start);
|
1360 |
+
setTimeout(bind(focusInput, cm), 20);
|
1361 |
+
e_preventDefault(e);
|
1362 |
+
return;
|
1363 |
+
}
|
1364 |
+
// For button 1, if it was clicked inside the editor
|
1365 |
+
// (posFromMouse returning non-null), we have to adjust the
|
1366 |
+
// selection.
|
1367 |
+
if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
|
1368 |
+
|
1369 |
+
if (!view.focused) onFocus(cm);
|
1370 |
+
|
1371 |
+
var now = +new Date, type = "single";
|
1372 |
+
if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
|
1373 |
+
type = "triple";
|
1374 |
+
e_preventDefault(e);
|
1375 |
+
setTimeout(bind(focusInput, cm), 20);
|
1376 |
+
selectLine(cm, start.line);
|
1377 |
+
} else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
|
1378 |
+
type = "double";
|
1379 |
+
lastDoubleClick = {time: now, pos: start};
|
1380 |
+
e_preventDefault(e);
|
1381 |
+
var word = findWordAt(getLine(doc, start.line).text, start);
|
1382 |
+
extendSelection(cm, word.from, word.to);
|
1383 |
+
} else { lastClick = {time: now, pos: start}; }
|
1384 |
+
|
1385 |
+
var last = start;
|
1386 |
+
if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
|
1387 |
+
!posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
|
1388 |
+
var dragEnd = operation(cm, function(e2) {
|
1389 |
+
if (webkit) display.scroller.draggable = false;
|
1390 |
+
view.draggingText = false;
|
1391 |
+
off(document, "mouseup", dragEnd);
|
1392 |
+
off(display.scroller, "drop", dragEnd);
|
1393 |
+
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
|
1394 |
+
e_preventDefault(e2);
|
1395 |
+
extendSelection(cm, start);
|
1396 |
+
focusInput(cm);
|
1397 |
+
}
|
1398 |
+
});
|
1399 |
+
// Let the drag handler handle this.
|
1400 |
+
if (webkit) display.scroller.draggable = true;
|
1401 |
+
view.draggingText = dragEnd;
|
1402 |
+
// IE's approach to draggable
|
1403 |
+
if (display.scroller.dragDrop) display.scroller.dragDrop();
|
1404 |
+
on(document, "mouseup", dragEnd);
|
1405 |
+
on(display.scroller, "drop", dragEnd);
|
1406 |
+
return;
|
1407 |
+
}
|
1408 |
+
e_preventDefault(e);
|
1409 |
+
if (type == "single") extendSelection(cm, clipPos(doc, start));
|
1410 |
+
|
1411 |
+
var startstart = sel.from, startend = sel.to;
|
1412 |
+
|
1413 |
+
function doSelect(cur) {
|
1414 |
+
if (type == "single") {
|
1415 |
+
extendSelection(cm, clipPos(doc, start), cur);
|
1416 |
+
return;
|
1417 |
+
}
|
1418 |
+
|
1419 |
+
startstart = clipPos(doc, startstart);
|
1420 |
+
startend = clipPos(doc, startend);
|
1421 |
+
if (type == "double") {
|
1422 |
+
var word = findWordAt(getLine(doc, cur.line).text, cur);
|
1423 |
+
if (posLess(cur, startstart)) extendSelection(cm, word.from, startend);
|
1424 |
+
else extendSelection(cm, startstart, word.to);
|
1425 |
+
} else if (type == "triple") {
|
1426 |
+
if (posLess(cur, startstart)) extendSelection(cm, startend, clipPos(doc, {line: cur.line, ch: 0}));
|
1427 |
+
else extendSelection(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0}));
|
1428 |
+
}
|
1429 |
+
}
|
1430 |
+
|
1431 |
+
var editorSize = display.wrapper.getBoundingClientRect();
|
1432 |
+
// Used to ensure timeout re-tries don't fire when another extend
|
1433 |
+
// happened in the meantime (clearTimeout isn't reliable -- at
|
1434 |
+
// least on Chrome, the timeouts still happen even when cleared,
|
1435 |
+
// if the clear happens after their scheduled firing time).
|
1436 |
+
var counter = 0;
|
1437 |
+
|
1438 |
+
function extend(e) {
|
1439 |
+
var curCount = ++counter;
|
1440 |
+
var cur = posFromMouse(cm, e, true);
|
1441 |
+
if (!cur) return;
|
1442 |
+
if (!posEq(cur, last)) {
|
1443 |
+
if (!view.focused) onFocus(cm);
|
1444 |
+
last = cur;
|
1445 |
+
doSelect(cur);
|
1446 |
+
var visible = visibleLines(display, doc);
|
1447 |
+
if (cur.line >= visible.to || cur.line < visible.from)
|
1448 |
+
setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
|
1449 |
+
} else {
|
1450 |
+
var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
|
1451 |
+
if (outside) setTimeout(operation(cm, function() {
|
1452 |
+
if (counter != curCount) return;
|
1453 |
+
display.scroller.scrollTop += outside;
|
1454 |
+
extend(e);
|
1455 |
+
}), 50);
|
1456 |
+
}
|
1457 |
+
}
|
1458 |
+
|
1459 |
+
function done(e) {
|
1460 |
+
counter = Infinity;
|
1461 |
+
var cur = posFromMouse(cm, e);
|
1462 |
+
if (cur) doSelect(cur);
|
1463 |
+
e_preventDefault(e);
|
1464 |
+
focusInput(cm);
|
1465 |
+
off(document, "mousemove", move);
|
1466 |
+
off(document, "mouseup", up);
|
1467 |
+
}
|
1468 |
+
|
1469 |
+
var move = operation(cm, function(e) {
|
1470 |
+
if (!ie && !e_button(e)) done(e);
|
1471 |
+
else extend(e);
|
1472 |
+
});
|
1473 |
+
var up = operation(cm, done);
|
1474 |
+
on(document, "mousemove", move);
|
1475 |
+
on(document, "mouseup", up);
|
1476 |
+
}
|
1477 |
+
|
1478 |
+
function onDrop(e) {
|
1479 |
+
var cm = this;
|
1480 |
+
if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
|
1481 |
+
e_preventDefault(e);
|
1482 |
+
var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
|
1483 |
+
if (!pos || isReadOnly(cm)) return;
|
1484 |
+
if (files && files.length && window.FileReader && window.File) {
|
1485 |
+
var n = files.length, text = Array(n), read = 0;
|
1486 |
+
var loadFile = function(file, i) {
|
1487 |
+
var reader = new FileReader;
|
1488 |
+
reader.onload = function() {
|
1489 |
+
text[i] = reader.result;
|
1490 |
+
if (++read == n) {
|
1491 |
+
pos = clipPos(cm.view.doc, pos);
|
1492 |
+
operation(cm, function() {
|
1493 |
+
var end = replaceRange(cm, text.join(""), pos, pos, "paste");
|
1494 |
+
setSelection(cm, pos, end);
|
1495 |
+
})();
|
1496 |
+
}
|
1497 |
+
};
|
1498 |
+
reader.readAsText(file);
|
1499 |
+
};
|
1500 |
+
for (var i = 0; i < n; ++i) loadFile(files[i], i);
|
1501 |
+
} else {
|
1502 |
+
// Don't do a replace if the drop happened inside of the selected text.
|
1503 |
+
if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) {
|
1504 |
+
cm.view.draggingText(e);
|
1505 |
+
if (ie) setTimeout(bind(focusInput, cm), 50);
|
1506 |
+
return;
|
1507 |
+
}
|
1508 |
+
try {
|
1509 |
+
var text = e.dataTransfer.getData("Text");
|
1510 |
+
if (text) {
|
1511 |
+
var curFrom = cm.view.sel.from, curTo = cm.view.sel.to;
|
1512 |
+
setSelection(cm, pos, pos);
|
1513 |
+
if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo, "paste");
|
1514 |
+
cm.replaceSelection(text, null, "paste");
|
1515 |
+
focusInput(cm);
|
1516 |
+
onFocus(cm);
|
1517 |
+
}
|
1518 |
+
}
|
1519 |
+
catch(e){}
|
1520 |
+
}
|
1521 |
+
}
|
1522 |
+
|
1523 |
+
function clickInGutter(cm, e) {
|
1524 |
+
var display = cm.display;
|
1525 |
+
try { var mX = e.clientX, mY = e.clientY; }
|
1526 |
+
catch(e) { return false; }
|
1527 |
+
|
1528 |
+
if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false;
|
1529 |
+
e_preventDefault(e);
|
1530 |
+
if (!hasHandler(cm, "gutterClick")) return true;
|
1531 |
+
|
1532 |
+
var lineBox = display.lineDiv.getBoundingClientRect();
|
1533 |
+
if (mY > lineBox.bottom) return true;
|
1534 |
+
mY -= lineBox.top - display.viewOffset;
|
1535 |
+
|
1536 |
+
for (var i = 0; i < cm.options.gutters.length; ++i) {
|
1537 |
+
var g = display.gutters.childNodes[i];
|
1538 |
+
if (g && g.getBoundingClientRect().right >= mX) {
|
1539 |
+
var line = lineAtHeight(cm.view.doc, mY);
|
1540 |
+
var gutter = cm.options.gutters[i];
|
1541 |
+
signalLater(cm, cm, "gutterClick", cm, line, gutter, e);
|
1542 |
+
break;
|
1543 |
+
}
|
1544 |
+
}
|
1545 |
+
return true;
|
1546 |
+
}
|
1547 |
+
|
1548 |
+
function onDragStart(cm, e) {
|
1549 |
+
var txt = cm.getSelection();
|
1550 |
+
e.dataTransfer.setData("Text", txt);
|
1551 |
+
|
1552 |
+
// Use dummy image instead of default browsers image.
|
1553 |
+
// Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
|
1554 |
+
if (e.dataTransfer.setDragImage && !safari)
|
1555 |
+
e.dataTransfer.setDragImage(elt('img'), 0, 0);
|
1556 |
+
}
|
1557 |
+
|
1558 |
+
function setScrollTop(cm, val) {
|
1559 |
+
if (Math.abs(cm.view.scrollTop - val) < 2) return;
|
1560 |
+
cm.view.scrollTop = val;
|
1561 |
+
if (!gecko) updateDisplay(cm, [], val);
|
1562 |
+
if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
|
1563 |
+
if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
|
1564 |
+
if (gecko) updateDisplay(cm, []);
|
1565 |
+
}
|
1566 |
+
function setScrollLeft(cm, val, isScroller) {
|
1567 |
+
if (isScroller ? val == cm.view.scrollLeft : Math.abs(cm.view.scrollLeft - val) < 2) return;
|
1568 |
+
cm.view.scrollLeft = val;
|
1569 |
+
alignHorizontally(cm);
|
1570 |
+
if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
|
1571 |
+
if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
|
1572 |
+
}
|
1573 |
+
|
1574 |
+
// Since the delta values reported on mouse wheel events are
|
1575 |
+
// unstandardized between browsers and even browser versions, and
|
1576 |
+
// generally horribly unpredictable, this code starts by measuring
|
1577 |
+
// the scroll effect that the first few mouse wheel events have,
|
1578 |
+
// and, from that, detects the way it can convert deltas to pixel
|
1579 |
+
// offsets afterwards.
|
1580 |
+
//
|
1581 |
+
// The reason we want to know the amount a wheel event will scroll
|
1582 |
+
// is that it gives us a chance to update the display before the
|
1583 |
+
// actual scrolling happens, reducing flickering.
|
1584 |
+
|
1585 |
+
var wheelSamples = 0, wheelDX, wheelDY, wheelStartX, wheelStartY, wheelPixelsPerUnit = null;
|
1586 |
+
// Fill in a browser-detected starting value on browsers where we
|
1587 |
+
// know one. These don't have to be accurate -- the result of them
|
1588 |
+
// being wrong would just be a slight flicker on the first wheel
|
1589 |
+
// scroll (if it is large enough).
|
1590 |
+
if (ie) wheelPixelsPerUnit = -.53;
|
1591 |
+
else if (gecko) wheelPixelsPerUnit = 15;
|
1592 |
+
else if (chrome) wheelPixelsPerUnit = -.7;
|
1593 |
+
else if (safari) wheelPixelsPerUnit = -1/3;
|
1594 |
+
|
1595 |
+
function onScrollWheel(cm, e) {
|
1596 |
+
var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
|
1597 |
+
if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
|
1598 |
+
if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
|
1599 |
+
else if (dy == null) dy = e.wheelDelta;
|
1600 |
+
|
1601 |
+
// Webkit browsers on OS X abort momentum scrolls when the target
|
1602 |
+
// of the scroll event is removed from the scrollable element.
|
1603 |
+
// This hack (see related code in patchDisplay) makes sure the
|
1604 |
+
// element is kept around.
|
1605 |
+
if (dy && mac && webkit) {
|
1606 |
+
for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
|
1607 |
+
if (cur.lineObj) {
|
1608 |
+
cm.display.currentWheelTarget = cur;
|
1609 |
+
break;
|
1610 |
+
}
|
1611 |
+
}
|
1612 |
+
}
|
1613 |
+
|
1614 |
+
var scroll = cm.display.scroller;
|
1615 |
+
// On some browsers, horizontal scrolling will cause redraws to
|
1616 |
+
// happen before the gutter has been realigned, causing it to
|
1617 |
+
// wriggle around in a most unseemly way. When we have an
|
1618 |
+
// estimated pixels/delta value, we just handle horizontal
|
1619 |
+
// scrolling entirely here. It'll be slightly off from native, but
|
1620 |
+
// better than glitching out.
|
1621 |
+
if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
|
1622 |
+
if (dy)
|
1623 |
+
setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
|
1624 |
+
setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
|
1625 |
+
e_preventDefault(e);
|
1626 |
+
wheelStartX = null; // Abort measurement, if in progress
|
1627 |
+
return;
|
1628 |
+
}
|
1629 |
+
|
1630 |
+
if (dy && wheelPixelsPerUnit != null) {
|
1631 |
+
var pixels = dy * wheelPixelsPerUnit;
|
1632 |
+
var top = cm.view.scrollTop, bot = top + cm.display.wrapper.clientHeight;
|
1633 |
+
if (pixels < 0) top = Math.max(0, top + pixels - 50);
|
1634 |
+
else bot = Math.min(cm.view.doc.height, bot + pixels + 50);
|
1635 |
+
updateDisplay(cm, [], {top: top, bottom: bot});
|
1636 |
+
}
|
1637 |
+
|
1638 |
+
if (wheelSamples < 20) {
|
1639 |
+
if (wheelStartX == null) {
|
1640 |
+
wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop;
|
1641 |
+
wheelDX = dx; wheelDY = dy;
|
1642 |
+
setTimeout(function() {
|
1643 |
+
if (wheelStartX == null) return;
|
1644 |
+
var movedX = scroll.scrollLeft - wheelStartX;
|
1645 |
+
var movedY = scroll.scrollTop - wheelStartY;
|
1646 |
+
var sample = (movedY && wheelDY && movedY / wheelDY) ||
|
1647 |
+
(movedX && wheelDX && movedX / wheelDX);
|
1648 |
+
wheelStartX = wheelStartY = null;
|
1649 |
+
if (!sample) return;
|
1650 |
+
wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
|
1651 |
+
++wheelSamples;
|
1652 |
+
}, 200);
|
1653 |
+
} else {
|
1654 |
+
wheelDX += dx; wheelDY += dy;
|
1655 |
+
}
|
1656 |
+
}
|
1657 |
+
}
|
1658 |
+
|
1659 |
+
function doHandleBinding(cm, bound, dropShift) {
|
1660 |
+
if (typeof bound == "string") {
|
1661 |
+
bound = commands[bound];
|
1662 |
+
if (!bound) return false;
|
1663 |
+
}
|
1664 |
+
// Ensure previous input has been read, so that the handler sees a
|
1665 |
+
// consistent view of the document
|
1666 |
+
if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
|
1667 |
+
var view = cm.view, prevShift = view.sel.shift;
|
1668 |
+
try {
|
1669 |
+
if (isReadOnly(cm)) view.suppressEdits = true;
|
1670 |
+
if (dropShift) view.sel.shift = false;
|
1671 |
+
bound(cm);
|
1672 |
+
} catch(e) {
|
1673 |
+
if (e != Pass) throw e;
|
1674 |
+
return false;
|
1675 |
+
} finally {
|
1676 |
+
view.sel.shift = prevShift;
|
1677 |
+
view.suppressEdits = false;
|
1678 |
+
}
|
1679 |
+
return true;
|
1680 |
+
}
|
1681 |
+
|
1682 |
+
function allKeyMaps(cm) {
|
1683 |
+
var maps = cm.view.keyMaps.slice(0);
|
1684 |
+
maps.push(cm.options.keyMap);
|
1685 |
+
if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
|
1686 |
+
return maps;
|
1687 |
+
}
|
1688 |
+
|
1689 |
+
var maybeTransition;
|
1690 |
+
function handleKeyBinding(cm, e) {
|
1691 |
+
// Handle auto keymap transitions
|
1692 |
+
var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
|
1693 |
+
clearTimeout(maybeTransition);
|
1694 |
+
if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
|
1695 |
+
if (getKeyMap(cm.options.keyMap) == startMap)
|
1696 |
+
cm.options.keyMap = (next.call ? next.call(null, cm) : next);
|
1697 |
+
}, 50);
|
1698 |
+
|
1699 |
+
var name = keyNames[e_prop(e, "keyCode")], handled = false;
|
1700 |
+
var flipCtrlCmd = mac && (opera || qtwebkit);
|
1701 |
+
if (name == null || e.altGraphKey) return false;
|
1702 |
+
if (e_prop(e, "altKey")) name = "Alt-" + name;
|
1703 |
+
if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
|
1704 |
+
if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
|
1705 |
+
|
1706 |
+
var stopped = false;
|
1707 |
+
function stop() { stopped = true; }
|
1708 |
+
var keymaps = allKeyMaps(cm);
|
1709 |
+
|
1710 |
+
if (e_prop(e, "shiftKey")) {
|
1711 |
+
handled = lookupKey("Shift-" + name, keymaps,
|
1712 |
+
function(b) {return doHandleBinding(cm, b, true);}, stop)
|
1713 |
+
|| lookupKey(name, keymaps, function(b) {
|
1714 |
+
if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
|
1715 |
+
}, stop);
|
1716 |
+
} else {
|
1717 |
+
handled = lookupKey(name, keymaps,
|
1718 |
+
function(b) { return doHandleBinding(cm, b); }, stop);
|
1719 |
+
}
|
1720 |
+
if (stopped) handled = false;
|
1721 |
+
if (handled) {
|
1722 |
+
e_preventDefault(e);
|
1723 |
+
restartBlink(cm);
|
1724 |
+
if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
|
1725 |
+
}
|
1726 |
+
return handled;
|
1727 |
+
}
|
1728 |
+
|
1729 |
+
function handleCharBinding(cm, e, ch) {
|
1730 |
+
var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
|
1731 |
+
function(b) { return doHandleBinding(cm, b, true); });
|
1732 |
+
if (handled) {
|
1733 |
+
e_preventDefault(e);
|
1734 |
+
restartBlink(cm);
|
1735 |
+
}
|
1736 |
+
return handled;
|
1737 |
+
}
|
1738 |
+
|
1739 |
+
var lastStoppedKey = null;
|
1740 |
+
function onKeyDown(e) {
|
1741 |
+
var cm = this;
|
1742 |
+
if (!cm.view.focused) onFocus(cm);
|
1743 |
+
if (ie && e.keyCode == 27) { e.returnValue = false; }
|
1744 |
+
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1745 |
+
var code = e_prop(e, "keyCode");
|
1746 |
+
// IE does strange things with escape.
|
1747 |
+
cm.view.sel.shift = code == 16 || e_prop(e, "shiftKey");
|
1748 |
+
// First give onKeyEvent option a chance to handle this.
|
1749 |
+
var handled = handleKeyBinding(cm, e);
|
1750 |
+
if (opera) {
|
1751 |
+
lastStoppedKey = handled ? code : null;
|
1752 |
+
// Opera has no cut event... we try to at least catch the key combo
|
1753 |
+
if (!handled && code == 88 && !hasCopyEvent && e_prop(e, mac ? "metaKey" : "ctrlKey"))
|
1754 |
+
cm.replaceSelection("");
|
1755 |
+
}
|
1756 |
+
}
|
1757 |
+
|
1758 |
+
function onKeyPress(e) {
|
1759 |
+
var cm = this;
|
1760 |
+
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1761 |
+
var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
|
1762 |
+
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
|
1763 |
+
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
|
1764 |
+
var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
|
1765 |
+
if (this.options.electricChars && this.view.mode.electricChars &&
|
1766 |
+
this.options.smartIndent && !isReadOnly(this) &&
|
1767 |
+
this.view.mode.electricChars.indexOf(ch) > -1)
|
1768 |
+
setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75);
|
1769 |
+
if (handleCharBinding(cm, e, ch)) return;
|
1770 |
+
fastPoll(cm);
|
1771 |
+
}
|
1772 |
+
|
1773 |
+
function onFocus(cm) {
|
1774 |
+
if (cm.options.readOnly == "nocursor") return;
|
1775 |
+
if (!cm.view.focused) {
|
1776 |
+
signal(cm, "focus", cm);
|
1777 |
+
cm.view.focused = true;
|
1778 |
+
if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1)
|
1779 |
+
cm.display.scroller.className += " CodeMirror-focused";
|
1780 |
+
resetInput(cm, true);
|
1781 |
+
}
|
1782 |
+
slowPoll(cm);
|
1783 |
+
restartBlink(cm);
|
1784 |
+
}
|
1785 |
+
function onBlur(cm) {
|
1786 |
+
if (cm.view.focused) {
|
1787 |
+
signal(cm, "blur", cm);
|
1788 |
+
cm.view.focused = false;
|
1789 |
+
cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", "");
|
1790 |
+
}
|
1791 |
+
clearInterval(cm.display.blinker);
|
1792 |
+
setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = false;}, 150);
|
1793 |
+
}
|
1794 |
+
|
1795 |
+
var detectingSelectAll;
|
1796 |
+
function onContextMenu(cm, e) {
|
1797 |
+
var display = cm.display, sel = cm.view.sel;
|
1798 |
+
var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
|
1799 |
+
if (!pos || opera) return; // Opera is difficult.
|
1800 |
+
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
|
1801 |
+
operation(cm, setSelection)(cm, pos, pos);
|
1802 |
+
|
1803 |
+
var oldCSS = display.input.style.cssText;
|
1804 |
+
display.inputDiv.style.position = "absolute";
|
1805 |
+
display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
|
1806 |
+
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
|
1807 |
+
"border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
|
1808 |
+
focusInput(cm);
|
1809 |
+
resetInput(cm, true);
|
1810 |
+
// Adds "Select all" to context menu in FF
|
1811 |
+
if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
|
1812 |
+
|
1813 |
+
function rehide() {
|
1814 |
+
display.inputDiv.style.position = "relative";
|
1815 |
+
display.input.style.cssText = oldCSS;
|
1816 |
+
if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
|
1817 |
+
slowPoll(cm);
|
1818 |
+
|
1819 |
+
// Try to detect the user choosing select-all
|
1820 |
+
if (display.input.selectionStart != null) {
|
1821 |
+
clearTimeout(detectingSelectAll);
|
1822 |
+
var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
|
1823 |
+
display.prevInput = " ";
|
1824 |
+
display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
|
1825 |
+
detectingSelectAll = setTimeout(function poll(){
|
1826 |
+
if (display.prevInput == " " && display.input.selectionStart == 0)
|
1827 |
+
operation(cm, commands.selectAll)(cm);
|
1828 |
+
else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
|
1829 |
+
else resetInput(cm);
|
1830 |
+
}, 200);
|
1831 |
+
}
|
1832 |
+
}
|
1833 |
+
|
1834 |
+
if (gecko) {
|
1835 |
+
e_stop(e);
|
1836 |
+
on(window, "mouseup", function mouseup() {
|
1837 |
+
off(window, "mouseup", mouseup);
|
1838 |
+
setTimeout(rehide, 20);
|
1839 |
+
});
|
1840 |
+
} else {
|
1841 |
+
setTimeout(rehide, 50);
|
1842 |
+
}
|
1843 |
+
}
|
1844 |
+
|
1845 |
+
// UPDATING
|
1846 |
+
|
1847 |
+
// Replace the range from from to to by the strings in newText.
|
1848 |
+
// Afterwards, set the selection to selFrom, selTo.
|
1849 |
+
function updateDoc(cm, from, to, newText, selUpdate, origin) {
|
1850 |
+
// Possibly split or suppress the update based on the presence
|
1851 |
+
// of read-only spans in its range.
|
1852 |
+
var split = sawReadOnlySpans &&
|
1853 |
+
removeReadOnlyRanges(cm.view.doc, from, to);
|
1854 |
+
if (split) {
|
1855 |
+
for (var i = split.length - 1; i >= 1; --i)
|
1856 |
+
updateDocInner(cm, split[i].from, split[i].to, [""], origin);
|
1857 |
+
if (split.length)
|
1858 |
+
return updateDocInner(cm, split[0].from, split[0].to, newText, selUpdate, origin);
|
1859 |
+
} else {
|
1860 |
+
return updateDocInner(cm, from, to, newText, selUpdate, origin);
|
1861 |
+
}
|
1862 |
+
}
|
1863 |
+
|
1864 |
+
function updateDocInner(cm, from, to, newText, selUpdate, origin) {
|
1865 |
+
if (cm.view.suppressEdits) return;
|
1866 |
+
|
1867 |
+
var view = cm.view, doc = view.doc, old = [];
|
1868 |
+
doc.iter(from.line, to.line + 1, function(line) {
|
1869 |
+
old.push(newHL(line.text, line.markedSpans));
|
1870 |
+
});
|
1871 |
+
var startSelFrom = view.sel.from, startSelTo = view.sel.to;
|
1872 |
+
var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
|
1873 |
+
var retval = updateDocNoUndo(cm, from, to, lines, selUpdate, origin);
|
1874 |
+
if (view.history) addChange(cm, from.line, newText.length, old, origin,
|
1875 |
+
startSelFrom, startSelTo, view.sel.from, view.sel.to);
|
1876 |
+
return retval;
|
1877 |
+
}
|
1878 |
+
|
1879 |
+
function unredoHelper(cm, type) {
|
1880 |
+
var doc = cm.view.doc, hist = cm.view.history;
|
1881 |
+
var set = (type == "undo" ? hist.done : hist.undone).pop();
|
1882 |
+
if (!set) return;
|
1883 |
+
var anti = {events: [], fromBefore: set.fromAfter, toBefore: set.toAfter,
|
1884 |
+
fromAfter: set.fromBefore, toAfter: set.toBefore};
|
1885 |
+
for (var i = set.events.length - 1; i >= 0; i -= 1) {
|
1886 |
+
hist.dirtyCounter += type == "undo" ? -1 : 1;
|
1887 |
+
var change = set.events[i];
|
1888 |
+
var replaced = [], end = change.start + change.added;
|
1889 |
+
doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
|
1890 |
+
anti.events.push({start: change.start, added: change.old.length, old: replaced});
|
1891 |
+
var selPos = i ? null : {from: set.fromBefore, to: set.toBefore};
|
1892 |
+
updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length},
|
1893 |
+
change.old, selPos, type);
|
1894 |
+
}
|
1895 |
+
(type == "undo" ? hist.undone : hist.done).push(anti);
|
1896 |
+
}
|
1897 |
+
|
1898 |
+
function updateDocNoUndo(cm, from, to, lines, selUpdate, origin) {
|
1899 |
+
var view = cm.view, doc = view.doc, display = cm.display;
|
1900 |
+
if (view.suppressEdits) return;
|
1901 |
+
|
1902 |
+
var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
|
1903 |
+
var recomputeMaxLength = false, checkWidthStart = from.line;
|
1904 |
+
if (!cm.options.lineWrapping) {
|
1905 |
+
checkWidthStart = lineNo(visualLine(doc, firstLine));
|
1906 |
+
doc.iter(checkWidthStart, to.line + 1, function(line) {
|
1907 |
+
if (lineLength(doc, line) == view.maxLineLength) {
|
1908 |
+
recomputeMaxLength = true;
|
1909 |
+
return true;
|
1910 |
+
}
|
1911 |
+
});
|
1912 |
+
}
|
1913 |
+
|
1914 |
+
var lastHL = lst(lines), th = textHeight(display);
|
1915 |
+
|
1916 |
+
// First adjust the line structure
|
1917 |
+
if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
|
1918 |
+
// This is a whole-line replace. Treated specially to make
|
1919 |
+
// sure line objects move the way they are supposed to.
|
1920 |
+
var added = [];
|
1921 |
+
for (var i = 0, e = lines.length - 1; i < e; ++i)
|
1922 |
+
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
|
1923 |
+
updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL));
|
1924 |
+
if (nlines) doc.remove(from.line, nlines, cm);
|
1925 |
+
if (added.length) doc.insert(from.line, added);
|
1926 |
+
} else if (firstLine == lastLine) {
|
1927 |
+
if (lines.length == 1) {
|
1928 |
+
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
|
1929 |
+
firstLine.text.slice(to.ch), hlSpans(lines[0]));
|
1930 |
+
} else {
|
1931 |
+
for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
|
1932 |
+
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
|
1933 |
+
added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
|
1934 |
+
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
|
1935 |
+
doc.insert(from.line + 1, added);
|
1936 |
+
}
|
1937 |
+
} else if (lines.length == 1) {
|
1938 |
+
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
|
1939 |
+
lastLine.text.slice(to.ch), hlSpans(lines[0]));
|
1940 |
+
doc.remove(from.line + 1, nlines, cm);
|
1941 |
+
} else {
|
1942 |
+
var added = [];
|
1943 |
+
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
|
1944 |
+
updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
|
1945 |
+
for (var i = 1, e = lines.length - 1; i < e; ++i)
|
1946 |
+
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
|
1947 |
+
if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm);
|
1948 |
+
doc.insert(from.line + 1, added);
|
1949 |
+
}
|
1950 |
+
|
1951 |
+
if (cm.options.lineWrapping) {
|
1952 |
+
var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3);
|
1953 |
+
doc.iter(from.line, from.line + lines.length, function(line) {
|
1954 |
+
if (line.height == 0) return;
|
1955 |
+
var guess = (Math.ceil(line.text.length / perLine) || 1) * th;
|
1956 |
+
if (guess != line.height) updateLineHeight(line, guess);
|
1957 |
+
});
|
1958 |
+
} else {
|
1959 |
+
doc.iter(checkWidthStart, from.line + lines.length, function(line) {
|
1960 |
+
var len = lineLength(doc, line);
|
1961 |
+
if (len > view.maxLineLength) {
|
1962 |
+
view.maxLine = line;
|
1963 |
+
view.maxLineLength = len;
|
1964 |
+
view.maxLineChanged = true;
|
1965 |
+
recomputeMaxLength = false;
|
1966 |
+
}
|
1967 |
+
});
|
1968 |
+
if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
|
1969 |
+
}
|
1970 |
+
|
1971 |
+
// Adjust frontier, schedule worker
|
1972 |
+
view.frontier = Math.min(view.frontier, from.line);
|
1973 |
+
startWorker(cm, 400);
|
1974 |
+
|
1975 |
+
var lendiff = lines.length - nlines - 1;
|
1976 |
+
// Remember that these lines changed, for updating the display
|
1977 |
+
regChange(cm, from.line, to.line + 1, lendiff);
|
1978 |
+
if (hasHandler(cm, "change")) {
|
1979 |
+
// Normalize lines to contain only strings, since that's what
|
1980 |
+
// the change event handler expects
|
1981 |
+
for (var i = 0; i < lines.length; ++i)
|
1982 |
+
if (typeof lines[i] != "string") lines[i] = lines[i].text;
|
1983 |
+
var changeObj = {from: from, to: to, text: lines, origin: origin};
|
1984 |
+
if (cm.curOp.textChanged) {
|
1985 |
+
for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
|
1986 |
+
cur.next = changeObj;
|
1987 |
+
} else cm.curOp.textChanged = changeObj;
|
1988 |
+
}
|
1989 |
+
|
1990 |
+
// Update the selection
|
1991 |
+
var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1,
|
1992 |
+
ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)};
|
1993 |
+
if (selUpdate && typeof selUpdate != "string") {
|
1994 |
+
if (selUpdate.from) { newSelFrom = selUpdate.from; newSelTo = selUpdate.to; }
|
1995 |
+
else newSelFrom = newSelTo = selUpdate;
|
1996 |
+
} else if (selUpdate == "end") {
|
1997 |
+
newSelFrom = newSelTo = end;
|
1998 |
+
} else if (selUpdate == "start") {
|
1999 |
+
newSelFrom = newSelTo = from;
|
2000 |
+
} else if (selUpdate == "around") {
|
2001 |
+
newSelFrom = from; newSelTo = end;
|
2002 |
+
} else {
|
2003 |
+
var adjustPos = function(pos) {
|
2004 |
+
if (posLess(pos, from)) return pos;
|
2005 |
+
if (!posLess(to, pos)) return end;
|
2006 |
+
var line = pos.line + lendiff;
|
2007 |
+
var ch = pos.ch;
|
2008 |
+
if (pos.line == to.line)
|
2009 |
+
ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0));
|
2010 |
+
return {line: line, ch: ch};
|
2011 |
+
};
|
2012 |
+
newSelFrom = adjustPos(view.sel.from);
|
2013 |
+
newSelTo = adjustPos(view.sel.to);
|
2014 |
+
}
|
2015 |
+
setSelection(cm, newSelFrom, newSelTo, null, true);
|
2016 |
+
return end;
|
2017 |
+
}
|
2018 |
+
|
2019 |
+
function replaceRange(cm, code, from, to, origin) {
|
2020 |
+
if (!to) to = from;
|
2021 |
+
if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
|
2022 |
+
return updateDoc(cm, from, to, splitLines(code), null, origin);
|
2023 |
+
}
|
2024 |
+
|
2025 |
+
// SELECTION
|
2026 |
+
|
2027 |
+
function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
|
2028 |
+
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
|
2029 |
+
function copyPos(x) {return {line: x.line, ch: x.ch};}
|
2030 |
+
|
2031 |
+
function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));}
|
2032 |
+
function clipPos(doc, pos) {
|
2033 |
+
if (pos.line < 0) return {line: 0, ch: 0};
|
2034 |
+
if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length};
|
2035 |
+
var ch = pos.ch, linelen = getLine(doc, pos.line).text.length;
|
2036 |
+
if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
|
2037 |
+
else if (ch < 0) return {line: pos.line, ch: 0};
|
2038 |
+
else return pos;
|
2039 |
+
}
|
2040 |
+
function isLine(doc, l) {return l >= 0 && l < doc.size;}
|
2041 |
+
|
2042 |
+
// If shift is held, this will move the selection anchor. Otherwise,
|
2043 |
+
// it'll set the whole selection.
|
2044 |
+
function extendSelection(cm, pos, other, bias) {
|
2045 |
+
var sel = cm.view.sel;
|
2046 |
+
if (sel.shift || sel.extend) {
|
2047 |
+
var anchor = sel.anchor;
|
2048 |
+
if (other) {
|
2049 |
+
var posBefore = posLess(pos, anchor);
|
2050 |
+
if (posBefore != posLess(other, anchor)) {
|
2051 |
+
anchor = pos;
|
2052 |
+
pos = other;
|
2053 |
+
} else if (posBefore != posLess(pos, other)) {
|
2054 |
+
pos = other;
|
2055 |
+
}
|
2056 |
+
}
|
2057 |
+
setSelection(cm, anchor, pos, bias);
|
2058 |
+
} else {
|
2059 |
+
setSelection(cm, pos, other || pos, bias);
|
2060 |
+
}
|
2061 |
+
cm.curOp.userSelChange = true;
|
2062 |
+
}
|
2063 |
+
|
2064 |
+
// Update the selection. Last two args are only used by
|
2065 |
+
// updateDoc, since they have to be expressed in the line
|
2066 |
+
// numbers before the update.
|
2067 |
+
function setSelection(cm, anchor, head, bias, checkAtomic) {
|
2068 |
+
cm.view.goalColumn = null;
|
2069 |
+
var sel = cm.view.sel;
|
2070 |
+
// Skip over atomic spans.
|
2071 |
+
if (checkAtomic || !posEq(anchor, sel.anchor))
|
2072 |
+
anchor = skipAtomic(cm, anchor, bias, checkAtomic != "push");
|
2073 |
+
if (checkAtomic || !posEq(head, sel.head))
|
2074 |
+
head = skipAtomic(cm, head, bias, checkAtomic != "push");
|
2075 |
+
|
2076 |
+
if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
|
2077 |
+
|
2078 |
+
sel.anchor = anchor; sel.head = head;
|
2079 |
+
var inv = posLess(head, anchor);
|
2080 |
+
sel.from = inv ? head : anchor;
|
2081 |
+
sel.to = inv ? anchor : head;
|
2082 |
+
|
2083 |
+
cm.curOp.updateInput = true;
|
2084 |
+
cm.curOp.selectionChanged = true;
|
2085 |
+
}
|
2086 |
+
|
2087 |
+
function reCheckSelection(cm) {
|
2088 |
+
setSelection(cm, cm.view.sel.from, cm.view.sel.to, null, "push");
|
2089 |
+
}
|
2090 |
+
|
2091 |
+
function skipAtomic(cm, pos, bias, mayClear) {
|
2092 |
+
var doc = cm.view.doc, flipped = false, curPos = pos;
|
2093 |
+
var dir = bias || 1;
|
2094 |
+
cm.view.cantEdit = false;
|
2095 |
+
search: for (;;) {
|
2096 |
+
var line = getLine(doc, curPos.line), toClear;
|
2097 |
+
if (line.markedSpans) {
|
2098 |
+
for (var i = 0; i < line.markedSpans.length; ++i) {
|
2099 |
+
var sp = line.markedSpans[i], m = sp.marker;
|
2100 |
+
if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
|
2101 |
+
(sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
|
2102 |
+
if (mayClear && m.clearOnEnter) {
|
2103 |
+
(toClear || (toClear = [])).push(m);
|
2104 |
+
continue;
|
2105 |
+
} else if (!m.atomic) continue;
|
2106 |
+
var newPos = m.find()[dir < 0 ? "from" : "to"];
|
2107 |
+
if (posEq(newPos, curPos)) {
|
2108 |
+
newPos.ch += dir;
|
2109 |
+
if (newPos.ch < 0) {
|
2110 |
+
if (newPos.line) newPos = clipPos(doc, {line: newPos.line - 1});
|
2111 |
+
else newPos = null;
|
2112 |
+
} else if (newPos.ch > line.text.length) {
|
2113 |
+
if (newPos.line < doc.size - 1) newPos = {line: newPos.line + 1, ch: 0};
|
2114 |
+
else newPos = null;
|
2115 |
+
}
|
2116 |
+
if (!newPos) {
|
2117 |
+
if (flipped) {
|
2118 |
+
// Driven in a corner -- no valid cursor position found at all
|
2119 |
+
// -- try again *with* clearing, if we didn't already
|
2120 |
+
if (!mayClear) return skipAtomic(cm, pos, bias, true);
|
2121 |
+
// Otherwise, turn off editing until further notice, and return the start of the doc
|
2122 |
+
cm.view.cantEdit = true;
|
2123 |
+
return {line: 0, ch: 0};
|
2124 |
+
}
|
2125 |
+
flipped = true; newPos = pos; dir = -dir;
|
2126 |
+
}
|
2127 |
+
}
|
2128 |
+
curPos = newPos;
|
2129 |
+
continue search;
|
2130 |
+
}
|
2131 |
+
}
|
2132 |
+
if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
|
2133 |
+
}
|
2134 |
+
return curPos;
|
2135 |
+
}
|
2136 |
+
}
|
2137 |
+
|
2138 |
+
// SCROLLING
|
2139 |
+
|
2140 |
+
function scrollCursorIntoView(cm) {
|
2141 |
+
var view = cm.view;
|
2142 |
+
var coords = scrollPosIntoView(cm, view.sel.head);
|
2143 |
+
if (!view.focused) return;
|
2144 |
+
var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
|
2145 |
+
if (coords.top + box.top < 0) doScroll = true;
|
2146 |
+
else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
|
2147 |
+
if (doScroll != null && !phantom) {
|
2148 |
+
var hidden = display.cursor.style.display == "none";
|
2149 |
+
if (hidden) {
|
2150 |
+
display.cursor.style.display = "";
|
2151 |
+
display.cursor.style.left = coords.left + "px";
|
2152 |
+
display.cursor.style.top = (coords.top - display.viewOffset) + "px";
|
2153 |
+
}
|
2154 |
+
display.cursor.scrollIntoView(doScroll);
|
2155 |
+
if (hidden) display.cursor.style.display = "none";
|
2156 |
+
}
|
2157 |
+
}
|
2158 |
+
|
2159 |
+
function scrollPosIntoView(cm, pos) {
|
2160 |
+
for (;;) {
|
2161 |
+
var changed = false, coords = cursorCoords(cm, pos);
|
2162 |
+
var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
|
2163 |
+
var startTop = cm.view.scrollTop, startLeft = cm.view.scrollLeft;
|
2164 |
+
if (scrollPos.scrollTop != null) {
|
2165 |
+
setScrollTop(cm, scrollPos.scrollTop);
|
2166 |
+
if (Math.abs(cm.view.scrollTop - startTop) > 1) changed = true;
|
2167 |
+
}
|
2168 |
+
if (scrollPos.scrollLeft != null) {
|
2169 |
+
setScrollLeft(cm, scrollPos.scrollLeft);
|
2170 |
+
if (Math.abs(cm.view.scrollLeft - startLeft) > 1) changed = true;
|
2171 |
+
}
|
2172 |
+
if (!changed) return coords;
|
2173 |
+
}
|
2174 |
+
}
|
2175 |
+
|
2176 |
+
function scrollIntoView(cm, x1, y1, x2, y2) {
|
2177 |
+
var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
|
2178 |
+
if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
|
2179 |
+
if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
|
2180 |
+
}
|
2181 |
+
|
2182 |
+
function calculateScrollPos(cm, x1, y1, x2, y2) {
|
2183 |
+
var display = cm.display, pt = paddingTop(display);
|
2184 |
+
y1 += pt; y2 += pt;
|
2185 |
+
var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
|
2186 |
+
var docBottom = cm.view.doc.height + 2 * pt;
|
2187 |
+
var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
|
2188 |
+
if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
|
2189 |
+
else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
|
2190 |
+
|
2191 |
+
var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
|
2192 |
+
x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
|
2193 |
+
var gutterw = display.gutters.offsetWidth;
|
2194 |
+
var atLeft = x1 < gutterw + 10;
|
2195 |
+
if (x1 < screenleft + gutterw || atLeft) {
|
2196 |
+
if (atLeft) x1 = 0;
|
2197 |
+
result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
|
2198 |
+
} else if (x2 > screenw + screenleft - 3) {
|
2199 |
+
result.scrollLeft = x2 + 10 - screenw;
|
2200 |
+
}
|
2201 |
+
return result;
|
2202 |
+
}
|
2203 |
+
|
2204 |
+
// API UTILITIES
|
2205 |
+
|
2206 |
+
function indentLine(cm, n, how, aggressive) {
|
2207 |
+
var doc = cm.view.doc;
|
2208 |
+
if (!how) how = "add";
|
2209 |
+
if (how == "smart") {
|
2210 |
+
if (!cm.view.mode.indent) how = "prev";
|
2211 |
+
else var state = getStateBefore(cm, n);
|
2212 |
+
}
|
2213 |
+
|
2214 |
+
var tabSize = cm.options.tabSize;
|
2215 |
+
var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
|
2216 |
+
var curSpaceString = line.text.match(/^\s*/)[0], indentation;
|
2217 |
+
if (how == "smart") {
|
2218 |
+
indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
|
2219 |
+
if (indentation == Pass) {
|
2220 |
+
if (!aggressive) return;
|
2221 |
+
how = "prev";
|
2222 |
+
}
|
2223 |
+
}
|
2224 |
+
if (how == "prev") {
|
2225 |
+
if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
|
2226 |
+
else indentation = 0;
|
2227 |
+
}
|
2228 |
+
else if (how == "add") indentation = curSpace + cm.options.indentUnit;
|
2229 |
+
else if (how == "subtract") indentation = curSpace - cm.options.indentUnit;
|
2230 |
+
indentation = Math.max(0, indentation);
|
2231 |
+
|
2232 |
+
var indentString = "", pos = 0;
|
2233 |
+
if (cm.options.indentWithTabs)
|
2234 |
+
for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
|
2235 |
+
if (pos < indentation) indentString += spaceStr(indentation - pos);
|
2236 |
+
|
2237 |
+
if (indentString != curSpaceString)
|
2238 |
+
replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "input");
|
2239 |
+
line.stateAfter = null;
|
2240 |
+
}
|
2241 |
+
|
2242 |
+
function changeLine(cm, handle, op) {
|
2243 |
+
var no = handle, line = handle, doc = cm.view.doc;
|
2244 |
+
if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
|
2245 |
+
else no = lineNo(handle);
|
2246 |
+
if (no == null) return null;
|
2247 |
+
if (op(line, no)) regChange(cm, no, no + 1);
|
2248 |
+
else return null;
|
2249 |
+
return line;
|
2250 |
+
}
|
2251 |
+
|
2252 |
+
function findPosH(cm, dir, unit, visually) {
|
2253 |
+
var doc = cm.view.doc, end = cm.view.sel.head, line = end.line, ch = end.ch;
|
2254 |
+
var lineObj = getLine(doc, line);
|
2255 |
+
function findNextLine() {
|
2256 |
+
var l = line + dir;
|
2257 |
+
if (l < 0 || l == doc.size) return false;
|
2258 |
+
line = l;
|
2259 |
+
return lineObj = getLine(doc, l);
|
2260 |
+
}
|
2261 |
+
function moveOnce(boundToLine) {
|
2262 |
+
var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
|
2263 |
+
if (next == null) {
|
2264 |
+
if (!boundToLine && findNextLine()) {
|
2265 |
+
if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
|
2266 |
+
else ch = dir < 0 ? lineObj.text.length : 0;
|
2267 |
+
} else return false;
|
2268 |
+
} else ch = next;
|
2269 |
+
return true;
|
2270 |
+
}
|
2271 |
+
if (unit == "char") moveOnce();
|
2272 |
+
else if (unit == "column") moveOnce(true);
|
2273 |
+
else if (unit == "word") {
|
2274 |
+
var sawWord = false;
|
2275 |
+
for (;;) {
|
2276 |
+
if (dir < 0) if (!moveOnce()) break;
|
2277 |
+
if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
|
2278 |
+
else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
|
2279 |
+
if (dir > 0) if (!moveOnce()) break;
|
2280 |
+
}
|
2281 |
+
}
|
2282 |
+
return skipAtomic(cm, {line: line, ch: ch}, dir, true);
|
2283 |
+
}
|
2284 |
+
|
2285 |
+
function findWordAt(line, pos) {
|
2286 |
+
var start = pos.ch, end = pos.ch;
|
2287 |
+
if (line) {
|
2288 |
+
if (pos.after === false || end == line.length) --start; else ++end;
|
2289 |
+
var startChar = line.charAt(start);
|
2290 |
+
var check = isWordChar(startChar) ? isWordChar :
|
2291 |
+
/\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
|
2292 |
+
function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
|
2293 |
+
while (start > 0 && check(line.charAt(start - 1))) --start;
|
2294 |
+
while (end < line.length && check(line.charAt(end))) ++end;
|
2295 |
+
}
|
2296 |
+
return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
|
2297 |
+
}
|
2298 |
+
|
2299 |
+
function selectLine(cm, line) {
|
2300 |
+
extendSelection(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0}));
|
2301 |
+
}
|
2302 |
+
|
2303 |
+
// PROTOTYPE
|
2304 |
+
|
2305 |
+
// The publicly visible API. Note that operation(null, f) means
|
2306 |
+
// 'wrap f in an operation, performed on its `this` parameter'
|
2307 |
+
|
2308 |
+
CodeMirror.prototype = {
|
2309 |
+
getValue: function(lineSep) {
|
2310 |
+
var text = [], doc = this.view.doc;
|
2311 |
+
doc.iter(0, doc.size, function(line) { text.push(line.text); });
|
2312 |
+
return text.join(lineSep || "\n");
|
2313 |
+
},
|
2314 |
+
|
2315 |
+
setValue: operation(null, function(code) {
|
2316 |
+
var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length;
|
2317 |
+
updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top, "setValue");
|
2318 |
+
}),
|
2319 |
+
|
2320 |
+
getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); },
|
2321 |
+
|
2322 |
+
replaceSelection: operation(null, function(code, collapse, origin) {
|
2323 |
+
var sel = this.view.sel;
|
2324 |
+
updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin);
|
2325 |
+
}),
|
2326 |
+
|
2327 |
+
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
|
2328 |
+
|
2329 |
+
setOption: function(option, value) {
|
2330 |
+
var options = this.options, old = options[option];
|
2331 |
+
if (options[option] == value && option != "mode") return;
|
2332 |
+
options[option] = value;
|
2333 |
+
if (optionHandlers.hasOwnProperty(option))
|
2334 |
+
operation(this, optionHandlers[option])(this, value, old);
|
2335 |
+
},
|
2336 |
+
|
2337 |
+
getOption: function(option) {return this.options[option];},
|
2338 |
+
|
2339 |
+
getMode: function() {return this.view.mode;},
|
2340 |
+
|
2341 |
+
addKeyMap: function(map) {
|
2342 |
+
this.view.keyMaps.push(map);
|
2343 |
+
},
|
2344 |
+
|
2345 |
+
removeKeyMap: function(map) {
|
2346 |
+
var maps = this.view.keyMaps;
|
2347 |
+
for (var i = 0; i < maps.length; ++i)
|
2348 |
+
if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
|
2349 |
+
maps.splice(i, 1);
|
2350 |
+
return true;
|
2351 |
+
}
|
2352 |
+
},
|
2353 |
+
|
2354 |
+
undo: operation(null, function() {unredoHelper(this, "undo");}),
|
2355 |
+
redo: operation(null, function() {unredoHelper(this, "redo");}),
|
2356 |
+
|
2357 |
+
indentLine: operation(null, function(n, dir, aggressive) {
|
2358 |
+
if (typeof dir != "string") {
|
2359 |
+
if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
|
2360 |
+
else dir = dir ? "add" : "subtract";
|
2361 |
+
}
|
2362 |
+
if (isLine(this.view.doc, n)) indentLine(this, n, dir, aggressive);
|
2363 |
+
}),
|
2364 |
+
|
2365 |
+
indentSelection: operation(null, function(how) {
|
2366 |
+
var sel = this.view.sel;
|
2367 |
+
if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
|
2368 |
+
var e = sel.to.line - (sel.to.ch ? 0 : 1);
|
2369 |
+
for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
|
2370 |
+
}),
|
2371 |
+
|
2372 |
+
historySize: function() {
|
2373 |
+
var hist = this.view.history;
|
2374 |
+
return {undo: hist.done.length, redo: hist.undone.length};
|
2375 |
+
},
|
2376 |
+
|
2377 |
+
clearHistory: function() {this.view.history = makeHistory();},
|
2378 |
+
|
2379 |
+
markClean: function() {
|
2380 |
+
this.view.history.dirtyCounter = 0;
|
2381 |
+
this.view.history.lastOp = this.view.history.lastOrigin = null;
|
2382 |
+
},
|
2383 |
+
|
2384 |
+
isClean: function () {return this.view.history.dirtyCounter == 0;},
|
2385 |
+
|
2386 |
+
getHistory: function() {
|
2387 |
+
var hist = this.view.history;
|
2388 |
+
function cp(arr) {
|
2389 |
+
for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
|
2390 |
+
var set = arr[i];
|
2391 |
+
nw.push({events: nwelt = [], fromBefore: set.fromBefore, toBefore: set.toBefore,
|
2392 |
+
fromAfter: set.fromAfter, toAfter: set.toAfter});
|
2393 |
+
for (var j = 0, elt = set.events; j < elt.length; ++j) {
|
2394 |
+
var old = [], cur = elt[j];
|
2395 |
+
nwelt.push({start: cur.start, added: cur.added, old: old});
|
2396 |
+
for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
|
2397 |
+
}
|
2398 |
+
}
|
2399 |
+
return nw;
|
2400 |
+
}
|
2401 |
+
return {done: cp(hist.done), undone: cp(hist.undone)};
|
2402 |
+
},
|
2403 |
+
|
2404 |
+
setHistory: function(histData) {
|
2405 |
+
var hist = this.view.history = makeHistory();
|
2406 |
+
hist.done = histData.done;
|
2407 |
+
hist.undone = histData.undone;
|
2408 |
+
},
|
2409 |
+
|
2410 |
+
// Fetch the parser token for a given character. Useful for hacks
|
2411 |
+
// that want to inspect the mode state (say, for completion).
|
2412 |
+
getTokenAt: function(pos) {
|
2413 |
+
var doc = this.view.doc;
|
2414 |
+
pos = clipPos(doc, pos);
|
2415 |
+
var state = getStateBefore(this, pos.line), mode = this.view.mode;
|
2416 |
+
var line = getLine(doc, pos.line);
|
2417 |
+
var stream = new StringStream(line.text, this.options.tabSize);
|
2418 |
+
while (stream.pos < pos.ch && !stream.eol()) {
|
2419 |
+
stream.start = stream.pos;
|
2420 |
+
var style = mode.token(stream, state);
|
2421 |
+
}
|
2422 |
+
return {start: stream.start,
|
2423 |
+
end: stream.pos,
|
2424 |
+
string: stream.current(),
|
2425 |
+
className: style || null, // Deprecated, use 'type' instead
|
2426 |
+
type: style || null,
|
2427 |
+
state: state};
|
2428 |
+
},
|
2429 |
+
|
2430 |
+
getStateAfter: function(line) {
|
2431 |
+
var doc = this.view.doc;
|
2432 |
+
line = clipLine(doc, line == null ? doc.size - 1: line);
|
2433 |
+
return getStateBefore(this, line + 1);
|
2434 |
+
},
|
2435 |
+
|
2436 |
+
cursorCoords: function(start, mode) {
|
2437 |
+
var pos, sel = this.view.sel;
|
2438 |
+
if (start == null) pos = sel.head;
|
2439 |
+
else if (typeof start == "object") pos = clipPos(this.view.doc, start);
|
2440 |
+
else pos = start ? sel.from : sel.to;
|
2441 |
+
return cursorCoords(this, pos, mode || "page");
|
2442 |
+
},
|
2443 |
+
|
2444 |
+
charCoords: function(pos, mode) {
|
2445 |
+
return charCoords(this, clipPos(this.view.doc, pos), mode || "page");
|
2446 |
+
},
|
2447 |
+
|
2448 |
+
coordsChar: function(coords) {
|
2449 |
+
var off = this.display.lineSpace.getBoundingClientRect();
|
2450 |
+
return coordsChar(this, coords.left - off.left, coords.top - off.top);
|
2451 |
+
},
|
2452 |
+
|
2453 |
+
defaultTextHeight: function() { return textHeight(this.display); },
|
2454 |
+
|
2455 |
+
markText: operation(null, function(from, to, options) {
|
2456 |
+
return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to),
|
2457 |
+
options, "range");
|
2458 |
+
}),
|
2459 |
+
|
2460 |
+
setBookmark: operation(null, function(pos, widget) {
|
2461 |
+
pos = clipPos(this.view.doc, pos);
|
2462 |
+
return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark");
|
2463 |
+
}),
|
2464 |
+
|
2465 |
+
findMarksAt: function(pos) {
|
2466 |
+
var doc = this.view.doc;
|
2467 |
+
pos = clipPos(doc, pos);
|
2468 |
+
var markers = [], spans = getLine(doc, pos.line).markedSpans;
|
2469 |
+
if (spans) for (var i = 0; i < spans.length; ++i) {
|
2470 |
+
var span = spans[i];
|
2471 |
+
if ((span.from == null || span.from <= pos.ch) &&
|
2472 |
+
(span.to == null || span.to >= pos.ch))
|
2473 |
+
markers.push(span.marker);
|
2474 |
+
}
|
2475 |
+
return markers;
|
2476 |
+
},
|
2477 |
+
|
2478 |
+
setGutterMarker: operation(null, function(line, gutterID, value) {
|
2479 |
+
return changeLine(this, line, function(line) {
|
2480 |
+
var markers = line.gutterMarkers || (line.gutterMarkers = {});
|
2481 |
+
markers[gutterID] = value;
|
2482 |
+
if (!value && isEmpty(markers)) line.gutterMarkers = null;
|
2483 |
+
return true;
|
2484 |
+
});
|
2485 |
+
}),
|
2486 |
+
|
2487 |
+
clearGutter: operation(null, function(gutterID) {
|
2488 |
+
var i = 0, cm = this, doc = cm.view.doc;
|
2489 |
+
doc.iter(0, doc.size, function(line) {
|
2490 |
+
if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
|
2491 |
+
line.gutterMarkers[gutterID] = null;
|
2492 |
+
regChange(cm, i, i + 1);
|
2493 |
+
if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
|
2494 |
+
}
|
2495 |
+
++i;
|
2496 |
+
});
|
2497 |
+
}),
|
2498 |
+
|
2499 |
+
addLineClass: operation(null, function(handle, where, cls) {
|
2500 |
+
return changeLine(this, handle, function(line) {
|
2501 |
+
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
|
2502 |
+
if (!line[prop]) line[prop] = cls;
|
2503 |
+
else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
|
2504 |
+
else line[prop] += " " + cls;
|
2505 |
+
return true;
|
2506 |
+
});
|
2507 |
+
}),
|
2508 |
+
|
2509 |
+
removeLineClass: operation(null, function(handle, where, cls) {
|
2510 |
+
return changeLine(this, handle, function(line) {
|
2511 |
+
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
|
2512 |
+
var cur = line[prop];
|
2513 |
+
if (!cur) return false;
|
2514 |
+
else if (cls == null) line[prop] = null;
|
2515 |
+
else {
|
2516 |
+
var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
|
2517 |
+
if (upd == cur) return false;
|
2518 |
+
line[prop] = upd || null;
|
2519 |
+
}
|
2520 |
+
return true;
|
2521 |
+
});
|
2522 |
+
}),
|
2523 |
+
|
2524 |
+
addLineWidget: operation(null, function(handle, node, options) {
|
2525 |
+
var widget = options || {};
|
2526 |
+
widget.node = node;
|
2527 |
+
if (widget.noHScroll) this.display.alignWidgets = true;
|
2528 |
+
changeLine(this, handle, function(line) {
|
2529 |
+
(line.widgets || (line.widgets = [])).push(widget);
|
2530 |
+
widget.line = line;
|
2531 |
+
return true;
|
2532 |
+
});
|
2533 |
+
return widget;
|
2534 |
+
}),
|
2535 |
+
|
2536 |
+
removeLineWidget: operation(null, function(widget) {
|
2537 |
+
var ws = widget.line.widgets, no = lineNo(widget.line);
|
2538 |
+
if (no == null) return;
|
2539 |
+
for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1);
|
2540 |
+
regChange(this, no, no + 1);
|
2541 |
+
}),
|
2542 |
+
|
2543 |
+
lineInfo: function(line) {
|
2544 |
+
if (typeof line == "number") {
|
2545 |
+
if (!isLine(this.view.doc, line)) return null;
|
2546 |
+
var n = line;
|
2547 |
+
line = getLine(this.view.doc, line);
|
2548 |
+
if (!line) return null;
|
2549 |
+
} else {
|
2550 |
+
var n = lineNo(line);
|
2551 |
+
if (n == null) return null;
|
2552 |
+
}
|
2553 |
+
return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
|
2554 |
+
textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
|
2555 |
+
widgets: line.widgets};
|
2556 |
+
},
|
2557 |
+
|
2558 |
+
getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
|
2559 |
+
|
2560 |
+
addWidget: function(pos, node, scroll, vert, horiz) {
|
2561 |
+
var display = this.display;
|
2562 |
+
pos = cursorCoords(this, clipPos(this.view.doc, pos));
|
2563 |
+
var top = pos.top, left = pos.left;
|
2564 |
+
node.style.position = "absolute";
|
2565 |
+
display.sizer.appendChild(node);
|
2566 |
+
if (vert == "over") top = pos.top;
|
2567 |
+
else if (vert == "near") {
|
2568 |
+
var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height),
|
2569 |
+
hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
|
2570 |
+
if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight)
|
2571 |
+
top = pos.top - node.offsetHeight;
|
2572 |
+
if (left + node.offsetWidth > hspace)
|
2573 |
+
left = hspace - node.offsetWidth;
|
2574 |
+
}
|
2575 |
+
node.style.top = (top + paddingTop(display)) + "px";
|
2576 |
+
node.style.left = node.style.right = "";
|
2577 |
+
if (horiz == "right") {
|
2578 |
+
left = display.sizer.clientWidth - node.offsetWidth;
|
2579 |
+
node.style.right = "0px";
|
2580 |
+
} else {
|
2581 |
+
if (horiz == "left") left = 0;
|
2582 |
+
else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
|
2583 |
+
node.style.left = left + "px";
|
2584 |
+
}
|
2585 |
+
if (scroll)
|
2586 |
+
scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
|
2587 |
+
},
|
2588 |
+
|
2589 |
+
lineCount: function() {return this.view.doc.size;},
|
2590 |
+
|
2591 |
+
clipPos: function(pos) {return clipPos(this.view.doc, pos);},
|
2592 |
+
|
2593 |
+
getCursor: function(start) {
|
2594 |
+
var sel = this.view.sel, pos;
|
2595 |
+
if (start == null || start == "head") pos = sel.head;
|
2596 |
+
else if (start == "anchor") pos = sel.anchor;
|
2597 |
+
else if (start == "end" || start === false) pos = sel.to;
|
2598 |
+
else pos = sel.from;
|
2599 |
+
return copyPos(pos);
|
2600 |
+
},
|
2601 |
+
|
2602 |
+
somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);},
|
2603 |
+
|
2604 |
+
setCursor: operation(null, function(line, ch, extend) {
|
2605 |
+
var pos = clipPos(this.view.doc, typeof line == "number" ? {line: line, ch: ch || 0} : line);
|
2606 |
+
if (extend) extendSelection(this, pos);
|
2607 |
+
else setSelection(this, pos, pos);
|
2608 |
+
}),
|
2609 |
+
|
2610 |
+
setSelection: operation(null, function(anchor, head) {
|
2611 |
+
var doc = this.view.doc;
|
2612 |
+
setSelection(this, clipPos(doc, anchor), clipPos(doc, head || anchor));
|
2613 |
+
}),
|
2614 |
+
|
2615 |
+
extendSelection: operation(null, function(from, to) {
|
2616 |
+
var doc = this.view.doc;
|
2617 |
+
extendSelection(this, clipPos(doc, from), to && clipPos(doc, to));
|
2618 |
+
}),
|
2619 |
+
|
2620 |
+
setExtending: function(val) {this.view.sel.extend = val;},
|
2621 |
+
|
2622 |
+
getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
|
2623 |
+
|
2624 |
+
getLineHandle: function(line) {
|
2625 |
+
var doc = this.view.doc;
|
2626 |
+
if (isLine(doc, line)) return getLine(doc, line);
|
2627 |
+
},
|
2628 |
+
|
2629 |
+
getLineNumber: function(line) {return lineNo(line);},
|
2630 |
+
|
2631 |
+
setLine: operation(null, function(line, text) {
|
2632 |
+
if (isLine(this.view.doc, line))
|
2633 |
+
replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length});
|
2634 |
+
}),
|
2635 |
+
|
2636 |
+
removeLine: operation(null, function(line) {
|
2637 |
+
if (isLine(this.view.doc, line))
|
2638 |
+
replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0}));
|
2639 |
+
}),
|
2640 |
+
|
2641 |
+
replaceRange: operation(null, function(code, from, to) {
|
2642 |
+
var doc = this.view.doc;
|
2643 |
+
from = clipPos(doc, from);
|
2644 |
+
to = to ? clipPos(doc, to) : from;
|
2645 |
+
return replaceRange(this, code, from, to);
|
2646 |
+
}),
|
2647 |
+
|
2648 |
+
getRange: function(from, to, lineSep) {
|
2649 |
+
var doc = this.view.doc;
|
2650 |
+
from = clipPos(doc, from); to = clipPos(doc, to);
|
2651 |
+
var l1 = from.line, l2 = to.line;
|
2652 |
+
if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch);
|
2653 |
+
var code = [getLine(doc, l1).text.slice(from.ch)];
|
2654 |
+
doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
|
2655 |
+
code.push(getLine(doc, l2).text.slice(0, to.ch));
|
2656 |
+
return code.join(lineSep || "\n");
|
2657 |
+
},
|
2658 |
+
|
2659 |
+
triggerOnKeyDown: operation(null, onKeyDown),
|
2660 |
+
|
2661 |
+
execCommand: function(cmd) {return commands[cmd](this);},
|
2662 |
+
|
2663 |
+
// Stuff used by commands, probably not much use to outside code.
|
2664 |
+
moveH: operation(null, function(dir, unit) {
|
2665 |
+
var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to;
|
2666 |
+
if (sel.shift || sel.extend || posEq(sel.from, sel.to)) pos = findPosH(this, dir, unit, true);
|
2667 |
+
extendSelection(this, pos, pos, dir);
|
2668 |
+
}),
|
2669 |
+
|
2670 |
+
deleteH: operation(null, function(dir, unit) {
|
2671 |
+
var sel = this.view.sel;
|
2672 |
+
if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to, "delete");
|
2673 |
+
else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false), "delete");
|
2674 |
+
this.curOp.userSelChange = true;
|
2675 |
+
}),
|
2676 |
+
|
2677 |
+
moveV: operation(null, function(dir, unit) {
|
2678 |
+
var view = this.view, doc = view.doc, display = this.display;
|
2679 |
+
var cur = view.sel.head, pos = cursorCoords(this, cur, "div");
|
2680 |
+
var x = pos.left, y;
|
2681 |
+
if (view.goalColumn != null) x = view.goalColumn;
|
2682 |
+
if (unit == "page") {
|
2683 |
+
var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
|
2684 |
+
y = pos.top + dir * pageSize;
|
2685 |
+
} else if (unit == "line") {
|
2686 |
+
y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
|
2687 |
+
}
|
2688 |
+
do {
|
2689 |
+
var target = coordsChar(this, x, y);
|
2690 |
+
y += dir * 5;
|
2691 |
+
} while (target.outside && (dir < 0 ? y > 0 : y < doc.height));
|
2692 |
+
|
2693 |
+
if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top;
|
2694 |
+
extendSelection(this, target, target, dir);
|
2695 |
+
view.goalColumn = x;
|
2696 |
+
}),
|
2697 |
+
|
2698 |
+
toggleOverwrite: function() {
|
2699 |
+
if (this.view.overwrite = !this.view.overwrite)
|
2700 |
+
this.display.cursor.className += " CodeMirror-overwrite";
|
2701 |
+
else
|
2702 |
+
this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
|
2703 |
+
},
|
2704 |
+
|
2705 |
+
posFromIndex: function(off) {
|
2706 |
+
var lineNo = 0, ch, doc = this.view.doc;
|
2707 |
+
doc.iter(0, doc.size, function(line) {
|
2708 |
+
var sz = line.text.length + 1;
|
2709 |
+
if (sz > off) { ch = off; return true; }
|
2710 |
+
off -= sz;
|
2711 |
+
++lineNo;
|
2712 |
+
});
|
2713 |
+
return clipPos(doc, {line: lineNo, ch: ch});
|
2714 |
+
},
|
2715 |
+
indexFromPos: function (coords) {
|
2716 |
+
if (coords.line < 0 || coords.ch < 0) return 0;
|
2717 |
+
var index = coords.ch;
|
2718 |
+
this.view.doc.iter(0, coords.line, function (line) {
|
2719 |
+
index += line.text.length + 1;
|
2720 |
+
});
|
2721 |
+
return index;
|
2722 |
+
},
|
2723 |
+
|
2724 |
+
scrollTo: function(x, y) {
|
2725 |
+
if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x;
|
2726 |
+
if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y;
|
2727 |
+
updateDisplay(this, []);
|
2728 |
+
},
|
2729 |
+
getScrollInfo: function() {
|
2730 |
+
var scroller = this.display.scroller, co = scrollerCutOff;
|
2731 |
+
return {left: scroller.scrollLeft, top: scroller.scrollTop,
|
2732 |
+
height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
|
2733 |
+
clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
|
2734 |
+
},
|
2735 |
+
|
2736 |
+
scrollIntoView: function(pos) {
|
2737 |
+
if (typeof pos == "number") pos = {line: pos, ch: 0};
|
2738 |
+
pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head;
|
2739 |
+
scrollPosIntoView(this, pos);
|
2740 |
+
},
|
2741 |
+
|
2742 |
+
setSize: function(width, height) {
|
2743 |
+
function interpret(val) {
|
2744 |
+
return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
|
2745 |
+
}
|
2746 |
+
if (width != null) this.display.wrapper.style.width = interpret(width);
|
2747 |
+
if (height != null) this.display.wrapper.style.height = interpret(height);
|
2748 |
+
this.refresh();
|
2749 |
+
},
|
2750 |
+
|
2751 |
+
on: function(type, f) {on(this, type, f);},
|
2752 |
+
off: function(type, f) {off(this, type, f);},
|
2753 |
+
|
2754 |
+
operation: function(f){return operation(this, f)();},
|
2755 |
+
|
2756 |
+
refresh: function() {
|
2757 |
+
clearCaches(this);
|
2758 |
+
if (this.display.scroller.scrollHeight > this.view.scrollTop)
|
2759 |
+
this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = this.view.scrollTop;
|
2760 |
+
updateDisplay(this, true);
|
2761 |
+
},
|
2762 |
+
|
2763 |
+
getInputField: function(){return this.display.input;},
|
2764 |
+
getWrapperElement: function(){return this.display.wrapper;},
|
2765 |
+
getScrollerElement: function(){return this.display.scroller;},
|
2766 |
+
getGutterElement: function(){return this.display.gutters;}
|
2767 |
+
};
|
2768 |
+
|
2769 |
+
// OPTION DEFAULTS
|
2770 |
+
|
2771 |
+
var optionHandlers = CodeMirror.optionHandlers = {};
|
2772 |
+
|
2773 |
+
// The default configuration options.
|
2774 |
+
var defaults = CodeMirror.defaults = {};
|
2775 |
+
|
2776 |
+
function option(name, deflt, handle, notOnInit) {
|
2777 |
+
CodeMirror.defaults[name] = deflt;
|
2778 |
+
if (handle) optionHandlers[name] =
|
2779 |
+
notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
|
2780 |
+
}
|
2781 |
+
|
2782 |
+
var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
|
2783 |
+
|
2784 |
+
// These two are, on init, called from the constructor because they
|
2785 |
+
// have to be initialized before the editor can start at all.
|
2786 |
+
option("value", "", function(cm, val) {cm.setValue(val);}, true);
|
2787 |
+
option("mode", null, loadMode, true);
|
2788 |
+
|
2789 |
+
option("indentUnit", 2, loadMode, true);
|
2790 |
+
option("indentWithTabs", false);
|
2791 |
+
option("smartIndent", true);
|
2792 |
+
option("tabSize", 4, function(cm) {
|
2793 |
+
loadMode(cm);
|
2794 |
+
clearCaches(cm);
|
2795 |
+
updateDisplay(cm, true);
|
2796 |
+
}, true);
|
2797 |
+
option("electricChars", true);
|
2798 |
+
|
2799 |
+
option("theme", "default", function(cm) {
|
2800 |
+
themeChanged(cm);
|
2801 |
+
guttersChanged(cm);
|
2802 |
+
}, true);
|
2803 |
+
option("keyMap", "default", keyMapChanged);
|
2804 |
+
option("extraKeys", null);
|
2805 |
+
|
2806 |
+
option("onKeyEvent", null);
|
2807 |
+
option("onDragEvent", null);
|
2808 |
+
|
2809 |
+
option("lineWrapping", false, wrappingChanged, true);
|
2810 |
+
option("gutters", [], function(cm) {
|
2811 |
+
setGuttersForLineNumbers(cm.options);
|
2812 |
+
guttersChanged(cm);
|
2813 |
+
}, true);
|
2814 |
+
option("lineNumbers", false, function(cm) {
|
2815 |
+
setGuttersForLineNumbers(cm.options);
|
2816 |
+
guttersChanged(cm);
|
2817 |
+
}, true);
|
2818 |
+
option("firstLineNumber", 1, guttersChanged, true);
|
2819 |
+
option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
|
2820 |
+
option("showCursorWhenSelecting", false, updateSelection, true);
|
2821 |
+
|
2822 |
+
option("readOnly", false, function(cm, val) {
|
2823 |
+
if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
|
2824 |
+
else if (!val) resetInput(cm, true);
|
2825 |
+
});
|
2826 |
+
option("dragDrop", true);
|
2827 |
+
|
2828 |
+
option("cursorBlinkRate", 530);
|
2829 |
+
option("cursorHeight", 1);
|
2830 |
+
option("workTime", 100);
|
2831 |
+
option("workDelay", 100);
|
2832 |
+
option("flattenSpans", true);
|
2833 |
+
option("pollInterval", 100);
|
2834 |
+
option("undoDepth", 40);
|
2835 |
+
option("viewportMargin", 10, function(cm){cm.refresh();}, true);
|
2836 |
+
|
2837 |
+
option("tabindex", null, function(cm, val) {
|
2838 |
+
cm.display.input.tabIndex = val || "";
|
2839 |
+
});
|
2840 |
+
option("autofocus", null);
|
2841 |
+
|
2842 |
+
// MODE DEFINITION AND QUERYING
|
2843 |
+
|
2844 |
+
// Known modes, by name and by MIME
|
2845 |
+
var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
|
2846 |
+
|
2847 |
+
CodeMirror.defineMode = function(name, mode) {
|
2848 |
+
if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
|
2849 |
+
if (arguments.length > 2) {
|
2850 |
+
mode.dependencies = [];
|
2851 |
+
for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
|
2852 |
+
}
|
2853 |
+
modes[name] = mode;
|
2854 |
+
};
|
2855 |
+
|
2856 |
+
CodeMirror.defineMIME = function(mime, spec) {
|
2857 |
+
mimeModes[mime] = spec;
|
2858 |
+
};
|
2859 |
+
|
2860 |
+
CodeMirror.resolveMode = function(spec) {
|
2861 |
+
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
|
2862 |
+
spec = mimeModes[spec];
|
2863 |
+
else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
|
2864 |
+
return CodeMirror.resolveMode("application/xml");
|
2865 |
+
if (typeof spec == "string") return {name: spec};
|
2866 |
+
else return spec || {name: "null"};
|
2867 |
+
};
|
2868 |
+
|
2869 |
+
CodeMirror.getMode = function(options, spec) {
|
2870 |
+
var spec = CodeMirror.resolveMode(spec);
|
2871 |
+
var mfactory = modes[spec.name];
|
2872 |
+
if (!mfactory) return CodeMirror.getMode(options, "text/plain");
|
2873 |
+
var modeObj = mfactory(options, spec);
|
2874 |
+
if (modeExtensions.hasOwnProperty(spec.name)) {
|
2875 |
+
var exts = modeExtensions[spec.name];
|
2876 |
+
for (var prop in exts) {
|
2877 |
+
if (!exts.hasOwnProperty(prop)) continue;
|
2878 |
+
if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
|
2879 |
+
modeObj[prop] = exts[prop];
|
2880 |
+
}
|
2881 |
+
}
|
2882 |
+
modeObj.name = spec.name;
|
2883 |
+
return modeObj;
|
2884 |
+
};
|
2885 |
+
|
2886 |
+
CodeMirror.defineMode("null", function() {
|
2887 |
+
return {token: function(stream) {stream.skipToEnd();}};
|
2888 |
+
});
|
2889 |
+
CodeMirror.defineMIME("text/plain", "null");
|
2890 |
+
|
2891 |
+
var modeExtensions = CodeMirror.modeExtensions = {};
|
2892 |
+
CodeMirror.extendMode = function(mode, properties) {
|
2893 |
+
var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
|
2894 |
+
for (var prop in properties) if (properties.hasOwnProperty(prop))
|
2895 |
+
exts[prop] = properties[prop];
|
2896 |
+
};
|
2897 |
+
|
2898 |
+
// EXTENSIONS
|
2899 |
+
|
2900 |
+
CodeMirror.defineExtension = function(name, func) {
|
2901 |
+
CodeMirror.prototype[name] = func;
|
2902 |
+
};
|
2903 |
+
|
2904 |
+
CodeMirror.defineOption = option;
|
2905 |
+
|
2906 |
+
var initHooks = [];
|
2907 |
+
CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
|
2908 |
+
|
2909 |
+
// MODE STATE HANDLING
|
2910 |
+
|
2911 |
+
// Utility functions for working with state. Exported because modes
|
2912 |
+
// sometimes need to do this.
|
2913 |
+
function copyState(mode, state) {
|
2914 |
+
if (state === true) return state;
|
2915 |
+
if (mode.copyState) return mode.copyState(state);
|
2916 |
+
var nstate = {};
|
2917 |
+
for (var n in state) {
|
2918 |
+
var val = state[n];
|
2919 |
+
if (val instanceof Array) val = val.concat([]);
|
2920 |
+
nstate[n] = val;
|
2921 |
+
}
|
2922 |
+
return nstate;
|
2923 |
+
}
|
2924 |
+
CodeMirror.copyState = copyState;
|
2925 |
+
|
2926 |
+
function startState(mode, a1, a2) {
|
2927 |
+
return mode.startState ? mode.startState(a1, a2) : true;
|
2928 |
+
}
|
2929 |
+
CodeMirror.startState = startState;
|
2930 |
+
|
2931 |
+
CodeMirror.innerMode = function(mode, state) {
|
2932 |
+
while (mode.innerMode) {
|
2933 |
+
var info = mode.innerMode(state);
|
2934 |
+
state = info.state;
|
2935 |
+
mode = info.mode;
|
2936 |
+
}
|
2937 |
+
return info || {mode: mode, state: state};
|
2938 |
+
};
|
2939 |
+
|
2940 |
+
// STANDARD COMMANDS
|
2941 |
+
|
2942 |
+
var commands = CodeMirror.commands = {
|
2943 |
+
selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
|
2944 |
+
killLine: function(cm) {
|
2945 |
+
var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
|
2946 |
+
if (!sel && cm.getLine(from.line).length == from.ch)
|
2947 |
+
cm.replaceRange("", from, {line: from.line + 1, ch: 0}, "delete");
|
2948 |
+
else cm.replaceRange("", from, sel ? to : {line: from.line}, "delete");
|
2949 |
+
},
|
2950 |
+
deleteLine: function(cm) {
|
2951 |
+
var l = cm.getCursor().line;
|
2952 |
+
cm.replaceRange("", {line: l, ch: 0}, {line: l}, "delete");
|
2953 |
+
},
|
2954 |
+
undo: function(cm) {cm.undo();},
|
2955 |
+
redo: function(cm) {cm.redo();},
|
2956 |
+
goDocStart: function(cm) {cm.extendSelection({line: 0, ch: 0});},
|
2957 |
+
goDocEnd: function(cm) {cm.extendSelection({line: cm.lineCount() - 1});},
|
2958 |
+
goLineStart: function(cm) {
|
2959 |
+
cm.extendSelection(lineStart(cm, cm.getCursor().line));
|
2960 |
+
},
|
2961 |
+
goLineStartSmart: function(cm) {
|
2962 |
+
var cur = cm.getCursor(), start = lineStart(cm, cur.line);
|
2963 |
+
var line = cm.getLineHandle(start.line);
|
2964 |
+
var order = getOrder(line);
|
2965 |
+
if (!order || order[0].level == 0) {
|
2966 |
+
var firstNonWS = Math.max(0, line.text.search(/\S/));
|
2967 |
+
var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
|
2968 |
+
cm.extendSelection({line: start.line, ch: inWS ? 0 : firstNonWS});
|
2969 |
+
} else cm.extendSelection(start);
|
2970 |
+
},
|
2971 |
+
goLineEnd: function(cm) {
|
2972 |
+
cm.extendSelection(lineEnd(cm, cm.getCursor().line));
|
2973 |
+
},
|
2974 |
+
goLineUp: function(cm) {cm.moveV(-1, "line");},
|
2975 |
+
goLineDown: function(cm) {cm.moveV(1, "line");},
|
2976 |
+
goPageUp: function(cm) {cm.moveV(-1, "page");},
|
2977 |
+
goPageDown: function(cm) {cm.moveV(1, "page");},
|
2978 |
+
goCharLeft: function(cm) {cm.moveH(-1, "char");},
|
2979 |
+
goCharRight: function(cm) {cm.moveH(1, "char");},
|
2980 |
+
goColumnLeft: function(cm) {cm.moveH(-1, "column");},
|
2981 |
+
goColumnRight: function(cm) {cm.moveH(1, "column");},
|
2982 |
+
goWordLeft: function(cm) {cm.moveH(-1, "word");},
|
2983 |
+
goWordRight: function(cm) {cm.moveH(1, "word");},
|
2984 |
+
delCharBefore: function(cm) {cm.deleteH(-1, "char");},
|
2985 |
+
delCharAfter: function(cm) {cm.deleteH(1, "char");},
|
2986 |
+
delWordBefore: function(cm) {cm.deleteH(-1, "word");},
|
2987 |
+
delWordAfter: function(cm) {cm.deleteH(1, "word");},
|
2988 |
+
indentAuto: function(cm) {cm.indentSelection("smart");},
|
2989 |
+
indentMore: function(cm) {cm.indentSelection("add");},
|
2990 |
+
indentLess: function(cm) {cm.indentSelection("subtract");},
|
2991 |
+
insertTab: function(cm) {cm.replaceSelection("\t", "end", "input");},
|
2992 |
+
defaultTab: function(cm) {
|
2993 |
+
if (cm.somethingSelected()) cm.indentSelection("add");
|
2994 |
+
else cm.replaceSelection("\t", "end", "input");
|
2995 |
+
},
|
2996 |
+
transposeChars: function(cm) {
|
2997 |
+
var cur = cm.getCursor(), line = cm.getLine(cur.line);
|
2998 |
+
if (cur.ch > 0 && cur.ch < line.length - 1)
|
2999 |
+
cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
|
3000 |
+
{line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
|
3001 |
+
},
|
3002 |
+
newlineAndIndent: function(cm) {
|
3003 |
+
operation(cm, function() {
|
3004 |
+
cm.replaceSelection("\n", "end", "input");
|
3005 |
+
cm.indentLine(cm.getCursor().line, null, true);
|
3006 |
+
})();
|
3007 |
+
},
|
3008 |
+
toggleOverwrite: function(cm) {cm.toggleOverwrite();}
|
3009 |
+
};
|
3010 |
+
|
3011 |
+
// STANDARD KEYMAPS
|
3012 |
+
|
3013 |
+
var keyMap = CodeMirror.keyMap = {};
|
3014 |
+
keyMap.basic = {
|
3015 |
+
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
|
3016 |
+
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
|
3017 |
+
"Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
|
3018 |
+
"Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
|
3019 |
+
};
|
3020 |
+
// Note that the save and find-related commands aren't defined by
|
3021 |
+
// default. Unknown commands are simply ignored.
|
3022 |
+
keyMap.pcDefault = {
|
3023 |
+
"Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
|
3024 |
+
"Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
|
3025 |
+
"Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
|
3026 |
+
"Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find",
|
3027 |
+
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
|
3028 |
+
"Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
|
3029 |
+
fallthrough: "basic"
|
3030 |
+
};
|
3031 |
+
keyMap.macDefault = {
|
3032 |
+
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
|
3033 |
+
"Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
|
3034 |
+
"Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore",
|
3035 |
+
"Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find",
|
3036 |
+
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
|
3037 |
+
"Cmd-[": "indentLess", "Cmd-]": "indentMore",
|
3038 |
+
fallthrough: ["basic", "emacsy"]
|
3039 |
+
};
|
3040 |
+
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
|
3041 |
+
keyMap.emacsy = {
|
3042 |
+
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
|
3043 |
+
"Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
|
3044 |
+
"Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
|
3045 |
+
"Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
|
3046 |
+
};
|
3047 |
+
|
3048 |
+
// KEYMAP DISPATCH
|
3049 |
+
|
3050 |
+
function getKeyMap(val) {
|
3051 |
+
if (typeof val == "string") return keyMap[val];
|
3052 |
+
else return val;
|
3053 |
+
}
|
3054 |
+
|
3055 |
+
function lookupKey(name, maps, handle, stop) {
|
3056 |
+
function lookup(map) {
|
3057 |
+
map = getKeyMap(map);
|
3058 |
+
var found = map[name];
|
3059 |
+
if (found === false) {
|
3060 |
+
if (stop) stop();
|
3061 |
+
return true;
|
3062 |
+
}
|
3063 |
+
if (found != null && handle(found)) return true;
|
3064 |
+
if (map.nofallthrough) {
|
3065 |
+
if (stop) stop();
|
3066 |
+
return true;
|
3067 |
+
}
|
3068 |
+
var fallthrough = map.fallthrough;
|
3069 |
+
if (fallthrough == null) return false;
|
3070 |
+
if (Object.prototype.toString.call(fallthrough) != "[object Array]")
|
3071 |
+
return lookup(fallthrough);
|
3072 |
+
for (var i = 0, e = fallthrough.length; i < e; ++i) {
|
3073 |
+
if (lookup(fallthrough[i])) return true;
|
3074 |
+
}
|
3075 |
+
return false;
|
3076 |
+
}
|
3077 |
+
|
3078 |
+
for (var i = 0; i < maps.length; ++i)
|
3079 |
+
if (lookup(maps[i])) return true;
|
3080 |
+
}
|
3081 |
+
function isModifierKey(event) {
|
3082 |
+
var name = keyNames[e_prop(event, "keyCode")];
|
3083 |
+
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
|
3084 |
+
}
|
3085 |
+
CodeMirror.isModifierKey = isModifierKey;
|
3086 |
+
|
3087 |
+
// FROMTEXTAREA
|
3088 |
+
|
3089 |
+
CodeMirror.fromTextArea = function(textarea, options) {
|
3090 |
+
if (!options) options = {};
|
3091 |
+
options.value = textarea.value;
|
3092 |
+
if (!options.tabindex && textarea.tabindex)
|
3093 |
+
options.tabindex = textarea.tabindex;
|
3094 |
+
// Set autofocus to true if this textarea is focused, or if it has
|
3095 |
+
// autofocus and no other element is focused.
|
3096 |
+
if (options.autofocus == null) {
|
3097 |
+
var hasFocus = document.body;
|
3098 |
+
// doc.activeElement occasionally throws on IE
|
3099 |
+
try { hasFocus = document.activeElement; } catch(e) {}
|
3100 |
+
options.autofocus = hasFocus == textarea ||
|
3101 |
+
textarea.getAttribute("autofocus") != null && hasFocus == document.body;
|
3102 |
+
}
|
3103 |
+
|
3104 |
+
function save() {textarea.value = cm.getValue();}
|
3105 |
+
if (textarea.form) {
|
3106 |
+
// Deplorable hack to make the submit method do the right thing.
|
3107 |
+
on(textarea.form, "submit", save);
|
3108 |
+
var form = textarea.form, realSubmit = form.submit;
|
3109 |
+
try {
|
3110 |
+
form.submit = function wrappedSubmit() {
|
3111 |
+
save();
|
3112 |
+
form.submit = realSubmit;
|
3113 |
+
form.submit();
|
3114 |
+
form.submit = wrappedSubmit;
|
3115 |
+
};
|
3116 |
+
} catch(e) {}
|
3117 |
+
}
|
3118 |
+
|
3119 |
+
textarea.style.display = "none";
|
3120 |
+
var cm = CodeMirror(function(node) {
|
3121 |
+
textarea.parentNode.insertBefore(node, textarea.nextSibling);
|
3122 |
+
}, options);
|
3123 |
+
cm.save = save;
|
3124 |
+
cm.getTextArea = function() { return textarea; };
|
3125 |
+
cm.toTextArea = function() {
|
3126 |
+
save();
|
3127 |
+
textarea.parentNode.removeChild(cm.getWrapperElement());
|
3128 |
+
textarea.style.display = "";
|
3129 |
+
if (textarea.form) {
|
3130 |
+
off(textarea.form, "submit", save);
|
3131 |
+
if (typeof textarea.form.submit == "function")
|
3132 |
+
textarea.form.submit = realSubmit;
|
3133 |
+
}
|
3134 |
+
};
|
3135 |
+
return cm;
|
3136 |
+
};
|
3137 |
+
|
3138 |
+
// STRING STREAM
|
3139 |
+
|
3140 |
+
// Fed to the mode parsers, provides helper functions to make
|
3141 |
+
// parsers more succinct.
|
3142 |
+
|
3143 |
+
// The character stream used by a mode's parser.
|
3144 |
+
function StringStream(string, tabSize) {
|
3145 |
+
this.pos = this.start = 0;
|
3146 |
+
this.string = string;
|
3147 |
+
this.tabSize = tabSize || 8;
|
3148 |
+
}
|
3149 |
+
|
3150 |
+
StringStream.prototype = {
|
3151 |
+
eol: function() {return this.pos >= this.string.length;},
|
3152 |
+
sol: function() {return this.pos == 0;},
|
3153 |
+
peek: function() {return this.string.charAt(this.pos) || undefined;},
|
3154 |
+
next: function() {
|
3155 |
+
if (this.pos < this.string.length)
|
3156 |
+
return this.string.charAt(this.pos++);
|
3157 |
+
},
|
3158 |
+
eat: function(match) {
|
3159 |
+
var ch = this.string.charAt(this.pos);
|
3160 |
+
if (typeof match == "string") var ok = ch == match;
|
3161 |
+
else var ok = ch && (match.test ? match.test(ch) : match(ch));
|
3162 |
+
if (ok) {++this.pos; return ch;}
|
3163 |
+
},
|
3164 |
+
eatWhile: function(match) {
|
3165 |
+
var start = this.pos;
|
3166 |
+
while (this.eat(match)){}
|
3167 |
+
return this.pos > start;
|
3168 |
+
},
|
3169 |
+
eatSpace: function() {
|
3170 |
+
var start = this.pos;
|
3171 |
+
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
|
3172 |
+
return this.pos > start;
|
3173 |
+
},
|
3174 |
+
skipToEnd: function() {this.pos = this.string.length;},
|
3175 |
+
skipTo: function(ch) {
|
3176 |
+
var found = this.string.indexOf(ch, this.pos);
|
3177 |
+
if (found > -1) {this.pos = found; return true;}
|
3178 |
+
},
|
3179 |
+
backUp: function(n) {this.pos -= n;},
|
3180 |
+
column: function() {return countColumn(this.string, this.start, this.tabSize);},
|
3181 |
+
indentation: function() {return countColumn(this.string, null, this.tabSize);},
|
3182 |
+
match: function(pattern, consume, caseInsensitive) {
|
3183 |
+
if (typeof pattern == "string") {
|
3184 |
+
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
|
3185 |
+
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
|
3186 |
+
if (consume !== false) this.pos += pattern.length;
|
3187 |
+
return true;
|
3188 |
+
}
|
3189 |
+
} else {
|
3190 |
+
var match = this.string.slice(this.pos).match(pattern);
|
3191 |
+
if (match && match.index > 0) return null;
|
3192 |
+
if (match && consume !== false) this.pos += match[0].length;
|
3193 |
+
return match;
|
3194 |
+
}
|
3195 |
+
},
|
3196 |
+
current: function(){return this.string.slice(this.start, this.pos);}
|
3197 |
+
};
|
3198 |
+
CodeMirror.StringStream = StringStream;
|
3199 |
+
|
3200 |
+
// TEXTMARKERS
|
3201 |
+
|
3202 |
+
function TextMarker(cm, type) {
|
3203 |
+
this.lines = [];
|
3204 |
+
this.type = type;
|
3205 |
+
this.cm = cm;
|
3206 |
+
}
|
3207 |
+
|
3208 |
+
TextMarker.prototype.clear = function() {
|
3209 |
+
if (this.explicitlyCleared) return;
|
3210 |
+
startOperation(this.cm);
|
3211 |
+
var min = null, max = null;
|
3212 |
+
for (var i = 0; i < this.lines.length; ++i) {
|
3213 |
+
var line = this.lines[i];
|
3214 |
+
var span = getMarkedSpanFor(line.markedSpans, this);
|
3215 |
+
if (span.to != null) max = lineNo(line);
|
3216 |
+
line.markedSpans = removeMarkedSpan(line.markedSpans, span);
|
3217 |
+
if (span.from != null)
|
3218 |
+
min = lineNo(line);
|
3219 |
+
else if (this.collapsed && !lineIsHidden(line))
|
3220 |
+
updateLineHeight(line, textHeight(this.cm.display));
|
3221 |
+
}
|
3222 |
+
if (min != null) regChange(this.cm, min, max + 1);
|
3223 |
+
this.lines.length = 0;
|
3224 |
+
this.explicitlyCleared = true;
|
3225 |
+
if (this.collapsed && this.cm.view.cantEdit) {
|
3226 |
+
this.cm.view.cantEdit = false;
|
3227 |
+
reCheckSelection(this.cm);
|
3228 |
+
}
|
3229 |
+
endOperation(this.cm);
|
3230 |
+
signalLater(this.cm, this, "clear");
|
3231 |
+
};
|
3232 |
+
|
3233 |
+
TextMarker.prototype.find = function() {
|
3234 |
+
var from, to;
|
3235 |
+
for (var i = 0; i < this.lines.length; ++i) {
|
3236 |
+
var line = this.lines[i];
|
3237 |
+
var span = getMarkedSpanFor(line.markedSpans, this);
|
3238 |
+
if (span.from != null || span.to != null) {
|
3239 |
+
var found = lineNo(line);
|
3240 |
+
if (span.from != null) from = {line: found, ch: span.from};
|
3241 |
+
if (span.to != null) to = {line: found, ch: span.to};
|
3242 |
+
}
|
3243 |
+
}
|
3244 |
+
if (this.type == "bookmark") return from;
|
3245 |
+
return from && {from: from, to: to};
|
3246 |
+
};
|
3247 |
+
|
3248 |
+
function markText(cm, from, to, options, type) {
|
3249 |
+
var doc = cm.view.doc;
|
3250 |
+
var marker = new TextMarker(cm, type);
|
3251 |
+
if (type == "range" && !posLess(from, to)) return marker;
|
3252 |
+
if (options) for (var opt in options) if (options.hasOwnProperty(opt))
|
3253 |
+
marker[opt] = options[opt];
|
3254 |
+
if (marker.replacedWith) {
|
3255 |
+
marker.collapsed = true;
|
3256 |
+
marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
|
3257 |
+
}
|
3258 |
+
if (marker.collapsed) sawCollapsedSpans = true;
|
3259 |
+
|
3260 |
+
var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd;
|
3261 |
+
doc.iter(curLine, to.line + 1, function(line) {
|
3262 |
+
var span = {from: null, to: null, marker: marker};
|
3263 |
+
size += line.text.length;
|
3264 |
+
if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
|
3265 |
+
if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
|
3266 |
+
if (marker.collapsed) {
|
3267 |
+
if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
|
3268 |
+
if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
|
3269 |
+
else updateLineHeight(line, 0);
|
3270 |
+
}
|
3271 |
+
addMarkedSpan(line, span);
|
3272 |
+
if (marker.collapsed && curLine == from.line && lineIsHidden(line))
|
3273 |
+
updateLineHeight(line, 0);
|
3274 |
+
++curLine;
|
3275 |
+
});
|
3276 |
+
|
3277 |
+
if (marker.readOnly) {
|
3278 |
+
sawReadOnlySpans = true;
|
3279 |
+
if (cm.view.history.done.length || cm.view.history.undone.length)
|
3280 |
+
cm.clearHistory();
|
3281 |
+
}
|
3282 |
+
if (marker.collapsed) {
|
3283 |
+
if (collapsedAtStart != collapsedAtEnd)
|
3284 |
+
throw new Error("Inserting collapsed marker overlapping an existing one");
|
3285 |
+
marker.size = size;
|
3286 |
+
marker.atomic = true;
|
3287 |
+
}
|
3288 |
+
if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
|
3289 |
+
regChange(cm, from.line, to.line + 1);
|
3290 |
+
if (marker.atomic) reCheckSelection(cm);
|
3291 |
+
return marker;
|
3292 |
+
}
|
3293 |
+
|
3294 |
+
// TEXTMARKER SPANS
|
3295 |
+
|
3296 |
+
function getMarkedSpanFor(spans, marker) {
|
3297 |
+
if (spans) for (var i = 0; i < spans.length; ++i) {
|
3298 |
+
var span = spans[i];
|
3299 |
+
if (span.marker == marker) return span;
|
3300 |
+
}
|
3301 |
+
}
|
3302 |
+
function removeMarkedSpan(spans, span) {
|
3303 |
+
for (var r, i = 0; i < spans.length; ++i)
|
3304 |
+
if (spans[i] != span) (r || (r = [])).push(spans[i]);
|
3305 |
+
return r;
|
3306 |
+
}
|
3307 |
+
function addMarkedSpan(line, span) {
|
3308 |
+
line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
|
3309 |
+
span.marker.lines.push(line);
|
3310 |
+
}
|
3311 |
+
|
3312 |
+
function markedSpansBefore(old, startCh) {
|
3313 |
+
if (old) for (var i = 0, nw; i < old.length; ++i) {
|
3314 |
+
var span = old[i], marker = span.marker;
|
3315 |
+
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
|
3316 |
+
if (startsBefore || marker.type == "bookmark" && span.from == startCh) {
|
3317 |
+
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
|
3318 |
+
(nw || (nw = [])).push({from: span.from,
|
3319 |
+
to: endsAfter ? null : span.to,
|
3320 |
+
marker: marker});
|
3321 |
+
}
|
3322 |
+
}
|
3323 |
+
return nw;
|
3324 |
+
}
|
3325 |
+
|
3326 |
+
function markedSpansAfter(old, startCh, endCh) {
|
3327 |
+
if (old) for (var i = 0, nw; i < old.length; ++i) {
|
3328 |
+
var span = old[i], marker = span.marker;
|
3329 |
+
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
|
3330 |
+
if (endsAfter || marker.type == "bookmark" && span.from == endCh && span.from != startCh) {
|
3331 |
+
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
|
3332 |
+
(nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
|
3333 |
+
to: span.to == null ? null : span.to - endCh,
|
3334 |
+
marker: marker});
|
3335 |
+
}
|
3336 |
+
}
|
3337 |
+
return nw;
|
3338 |
+
}
|
3339 |
+
|
3340 |
+
function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
|
3341 |
+
if (!oldFirst && !oldLast) return newText;
|
3342 |
+
// Get the spans that 'stick out' on both sides
|
3343 |
+
var first = markedSpansBefore(oldFirst, startCh);
|
3344 |
+
var last = markedSpansAfter(oldLast, startCh, endCh);
|
3345 |
+
|
3346 |
+
// Next, merge those two ends
|
3347 |
+
var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
|
3348 |
+
if (first) {
|
3349 |
+
// Fix up .to properties of first
|
3350 |
+
for (var i = 0; i < first.length; ++i) {
|
3351 |
+
var span = first[i];
|
3352 |
+
if (span.to == null) {
|
3353 |
+
var found = getMarkedSpanFor(last, span.marker);
|
3354 |
+
if (!found) span.to = startCh;
|
3355 |
+
else if (sameLine) span.to = found.to == null ? null : found.to + offset;
|
3356 |
+
}
|
3357 |
+
}
|
3358 |
+
}
|
3359 |
+
if (last) {
|
3360 |
+
// Fix up .from in last (or move them into first in case of sameLine)
|
3361 |
+
for (var i = 0; i < last.length; ++i) {
|
3362 |
+
var span = last[i];
|
3363 |
+
if (span.to != null) span.to += offset;
|
3364 |
+
if (span.from == null) {
|
3365 |
+
var found = getMarkedSpanFor(first, span.marker);
|
3366 |
+
if (!found) {
|
3367 |
+
span.from = offset;
|
3368 |
+
if (sameLine) (first || (first = [])).push(span);
|
3369 |
+
}
|
3370 |
+
} else {
|
3371 |
+
span.from += offset;
|
3372 |
+
if (sameLine) (first || (first = [])).push(span);
|
3373 |
+
}
|
3374 |
+
}
|
3375 |
+
}
|
3376 |
+
|
3377 |
+
var newMarkers = [newHL(newText[0], first)];
|
3378 |
+
if (!sameLine) {
|
3379 |
+
// Fill gap with whole-line-spans
|
3380 |
+
var gap = newText.length - 2, gapMarkers;
|
3381 |
+
if (gap > 0 && first)
|
3382 |
+
for (var i = 0; i < first.length; ++i)
|
3383 |
+
if (first[i].to == null)
|
3384 |
+
(gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
|
3385 |
+
for (var i = 0; i < gap; ++i)
|
3386 |
+
newMarkers.push(newHL(newText[i+1], gapMarkers));
|
3387 |
+
newMarkers.push(newHL(lst(newText), last));
|
3388 |
+
}
|
3389 |
+
return newMarkers;
|
3390 |
+
}
|
3391 |
+
|
3392 |
+
function removeReadOnlyRanges(doc, from, to) {
|
3393 |
+
var markers = null;
|
3394 |
+
doc.iter(from.line, to.line + 1, function(line) {
|
3395 |
+
if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
|
3396 |
+
var mark = line.markedSpans[i].marker;
|
3397 |
+
if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
|
3398 |
+
(markers || (markers = [])).push(mark);
|
3399 |
+
}
|
3400 |
+
});
|
3401 |
+
if (!markers) return null;
|
3402 |
+
var parts = [{from: from, to: to}];
|
3403 |
+
for (var i = 0; i < markers.length; ++i) {
|
3404 |
+
var m = markers[i].find();
|
3405 |
+
for (var j = 0; j < parts.length; ++j) {
|
3406 |
+
var p = parts[j];
|
3407 |
+
if (!posLess(m.from, p.to) || posLess(m.to, p.from)) continue;
|
3408 |
+
var newParts = [j, 1];
|
3409 |
+
if (posLess(p.from, m.from)) newParts.push({from: p.from, to: m.from});
|
3410 |
+
if (posLess(m.to, p.to)) newParts.push({from: m.to, to: p.to});
|
3411 |
+
parts.splice.apply(parts, newParts);
|
3412 |
+
j += newParts.length - 1;
|
3413 |
+
}
|
3414 |
+
}
|
3415 |
+
return parts;
|
3416 |
+
}
|
3417 |
+
|
3418 |
+
function collapsedSpanAt(line, ch) {
|
3419 |
+
var sps = sawCollapsedSpans && line.markedSpans, found;
|
3420 |
+
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
|
3421 |
+
sp = sps[i];
|
3422 |
+
if (!sp.marker.collapsed) continue;
|
3423 |
+
if ((sp.from == null || sp.from < ch) &&
|
3424 |
+
(sp.to == null || sp.to > ch) &&
|
3425 |
+
(!found || found.width < sp.marker.width))
|
3426 |
+
found = sp.marker;
|
3427 |
+
}
|
3428 |
+
return found;
|
3429 |
+
}
|
3430 |
+
function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
|
3431 |
+
function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
|
3432 |
+
|
3433 |
+
function visualLine(doc, line) {
|
3434 |
+
var merged;
|
3435 |
+
while (merged = collapsedSpanAtStart(line))
|
3436 |
+
line = getLine(doc, merged.find().from.line);
|
3437 |
+
return line;
|
3438 |
+
}
|
3439 |
+
|
3440 |
+
function lineIsHidden(line) {
|
3441 |
+
var sps = sawCollapsedSpans && line.markedSpans;
|
3442 |
+
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
|
3443 |
+
sp = sps[i];
|
3444 |
+
if (!sp.marker.collapsed) continue;
|
3445 |
+
if (sp.from == null) return true;
|
3446 |
+
if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(line, sp))
|
3447 |
+
return true;
|
3448 |
+
}
|
3449 |
+
}
|
3450 |
+
window.lineIsHidden = lineIsHidden;
|
3451 |
+
function lineIsHiddenInner(line, span) {
|
3452 |
+
if (span.to == null || span.marker.inclusiveRight && span.to == line.text.length)
|
3453 |
+
return true;
|
3454 |
+
for (var sp, i = 0; i < line.markedSpans.length; ++i) {
|
3455 |
+
sp = line.markedSpans[i];
|
3456 |
+
if (sp.marker.collapsed && sp.from == span.to &&
|
3457 |
+
(sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
|
3458 |
+
lineIsHiddenInner(line, sp)) return true;
|
3459 |
+
}
|
3460 |
+
}
|
3461 |
+
|
3462 |
+
// hl stands for history-line, a data structure that can be either a
|
3463 |
+
// string (line without markers) or a {text, markedSpans} object.
|
3464 |
+
function hlText(val) { return typeof val == "string" ? val : val.text; }
|
3465 |
+
function hlSpans(val) {
|
3466 |
+
if (typeof val == "string") return null;
|
3467 |
+
var spans = val.markedSpans, out = null;
|
3468 |
+
for (var i = 0; i < spans.length; ++i) {
|
3469 |
+
if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
|
3470 |
+
else if (out) out.push(spans[i]);
|
3471 |
+
}
|
3472 |
+
return !out ? spans : out.length ? out : null;
|
3473 |
+
}
|
3474 |
+
function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
|
3475 |
+
|
3476 |
+
function detachMarkedSpans(line) {
|
3477 |
+
var spans = line.markedSpans;
|
3478 |
+
if (!spans) return;
|
3479 |
+
for (var i = 0; i < spans.length; ++i) {
|
3480 |
+
var lines = spans[i].marker.lines;
|
3481 |
+
var ix = indexOf(lines, line);
|
3482 |
+
lines.splice(ix, 1);
|
3483 |
+
}
|
3484 |
+
line.markedSpans = null;
|
3485 |
+
}
|
3486 |
+
|
3487 |
+
function attachMarkedSpans(line, spans) {
|
3488 |
+
if (!spans) return;
|
3489 |
+
for (var i = 0; i < spans.length; ++i)
|
3490 |
+
spans[i].marker.lines.push(line);
|
3491 |
+
line.markedSpans = spans;
|
3492 |
+
}
|
3493 |
+
|
3494 |
+
// LINE DATA STRUCTURE
|
3495 |
+
|
3496 |
+
// Line objects. These hold state related to a line, including
|
3497 |
+
// highlighting info (the styles array).
|
3498 |
+
function makeLine(text, markedSpans, height) {
|
3499 |
+
var line = {text: text, height: height};
|
3500 |
+
attachMarkedSpans(line, markedSpans);
|
3501 |
+
if (lineIsHidden(line)) line.height = 0;
|
3502 |
+
return line;
|
3503 |
+
}
|
3504 |
+
|
3505 |
+
function updateLine(cm, line, text, markedSpans) {
|
3506 |
+
line.text = text;
|
3507 |
+
line.stateAfter = line.styles = null;
|
3508 |
+
if (line.order != null) line.order = null;
|
3509 |
+
detachMarkedSpans(line);
|
3510 |
+
attachMarkedSpans(line, markedSpans);
|
3511 |
+
if (lineIsHidden(line)) line.height = 0;
|
3512 |
+
else if (!line.height) line.height = textHeight(cm.display);
|
3513 |
+
signalLater(cm, line, "change");
|
3514 |
+
}
|
3515 |
+
|
3516 |
+
function cleanUpLine(line) {
|
3517 |
+
line.parent = null;
|
3518 |
+
detachMarkedSpans(line);
|
3519 |
+
}
|
3520 |
+
|
3521 |
+
// Run the given mode's parser over a line, update the styles
|
3522 |
+
// array, which contains alternating fragments of text and CSS
|
3523 |
+
// classes.
|
3524 |
+
function highlightLine(cm, line, state) {
|
3525 |
+
var mode = cm.view.mode, flattenSpans = cm.options.flattenSpans;
|
3526 |
+
var changed = !line.styles, pos = 0, curText = "", curStyle = null;
|
3527 |
+
var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []);
|
3528 |
+
if (line.text == "" && mode.blankLine) mode.blankLine(state);
|
3529 |
+
while (!stream.eol()) {
|
3530 |
+
var style = mode.token(stream, state), substr = stream.current();
|
3531 |
+
stream.start = stream.pos;
|
3532 |
+
if (!flattenSpans || curStyle != style) {
|
3533 |
+
if (curText) {
|
3534 |
+
changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
|
3535 |
+
st[pos++] = curText; st[pos++] = curStyle;
|
3536 |
+
}
|
3537 |
+
curText = substr; curStyle = style;
|
3538 |
+
} else curText = curText + substr;
|
3539 |
+
// Give up when line is ridiculously long
|
3540 |
+
if (stream.pos > 5000) break;
|
3541 |
+
}
|
3542 |
+
if (curText) {
|
3543 |
+
changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
|
3544 |
+
st[pos++] = curText; st[pos++] = curStyle;
|
3545 |
+
}
|
3546 |
+
if (stream.pos > 5000) { st[pos++] = line.text.slice(stream.pos); st[pos++] = null; }
|
3547 |
+
if (pos != st.length) { st.length = pos; changed = true; }
|
3548 |
+
return changed;
|
3549 |
+
}
|
3550 |
+
|
3551 |
+
// Lightweight form of highlight -- proceed over this line and
|
3552 |
+
// update state, but don't save a style array.
|
3553 |
+
function processLine(cm, line, state) {
|
3554 |
+
var mode = cm.view.mode;
|
3555 |
+
var stream = new StringStream(line.text, cm.options.tabSize);
|
3556 |
+
if (line.text == "" && mode.blankLine) mode.blankLine(state);
|
3557 |
+
while (!stream.eol() && stream.pos <= 5000) {
|
3558 |
+
mode.token(stream, state);
|
3559 |
+
stream.start = stream.pos;
|
3560 |
+
}
|
3561 |
+
}
|
3562 |
+
|
3563 |
+
var styleToClassCache = {};
|
3564 |
+
function styleToClass(style) {
|
3565 |
+
if (!style) return null;
|
3566 |
+
return styleToClassCache[style] ||
|
3567 |
+
(styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
|
3568 |
+
}
|
3569 |
+
|
3570 |
+
function lineContent(cm, realLine, measure) {
|
3571 |
+
var merged, line = realLine, lineBefore, sawBefore, simple = true;
|
3572 |
+
while (merged = collapsedSpanAtStart(line)) {
|
3573 |
+
simple = false;
|
3574 |
+
line = getLine(cm.view.doc, merged.find().from.line);
|
3575 |
+
if (!lineBefore) lineBefore = line;
|
3576 |
+
}
|
3577 |
+
|
3578 |
+
var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
|
3579 |
+
measure: null, addedOne: false, cm: cm};
|
3580 |
+
if (line.textClass) builder.pre.className = line.textClass;
|
3581 |
+
|
3582 |
+
do {
|
3583 |
+
if (!line.styles)
|
3584 |
+
highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
|
3585 |
+
builder.measure = line == realLine && measure;
|
3586 |
+
builder.pos = 0;
|
3587 |
+
builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
|
3588 |
+
if (measure && sawBefore && line != realLine && !builder.addedOne) {
|
3589 |
+
measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
|
3590 |
+
builder.addedOne = true;
|
3591 |
+
}
|
3592 |
+
var next = insertLineContent(line, builder);
|
3593 |
+
sawBefore = line == lineBefore;
|
3594 |
+
if (next) {
|
3595 |
+
line = getLine(cm.view.doc, next.to.line);
|
3596 |
+
simple = false;
|
3597 |
+
}
|
3598 |
+
} while (next);
|
3599 |
+
|
3600 |
+
if (measure && !builder.addedOne)
|
3601 |
+
measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
|
3602 |
+
if (!builder.pre.firstChild && !lineIsHidden(realLine))
|
3603 |
+
builder.pre.appendChild(document.createTextNode("\u00a0"));
|
3604 |
+
|
3605 |
+
return builder.pre;
|
3606 |
+
}
|
3607 |
+
|
3608 |
+
var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
|
3609 |
+
function buildToken(builder, text, style, startStyle, endStyle) {
|
3610 |
+
if (!text) return;
|
3611 |
+
if (!tokenSpecialChars.test(text)) {
|
3612 |
+
builder.col += text.length;
|
3613 |
+
var content = document.createTextNode(text);
|
3614 |
+
} else {
|
3615 |
+
var content = document.createDocumentFragment(), pos = 0;
|
3616 |
+
while (true) {
|
3617 |
+
tokenSpecialChars.lastIndex = pos;
|
3618 |
+
var m = tokenSpecialChars.exec(text);
|
3619 |
+
var skipped = m ? m.index - pos : text.length - pos;
|
3620 |
+
if (skipped) {
|
3621 |
+
content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
|
3622 |
+
builder.col += skipped;
|
3623 |
+
}
|
3624 |
+
if (!m) break;
|
3625 |
+
pos += skipped + 1;
|
3626 |
+
if (m[0] == "\t") {
|
3627 |
+
var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
|
3628 |
+
content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
|
3629 |
+
builder.col += tabWidth;
|
3630 |
+
} else {
|
3631 |
+
var token = elt("span", "\u2022", "cm-invalidchar");
|
3632 |
+
token.title = "\\u" + m[0].charCodeAt(0).toString(16);
|
3633 |
+
content.appendChild(token);
|
3634 |
+
builder.col += 1;
|
3635 |
+
}
|
3636 |
+
}
|
3637 |
+
}
|
3638 |
+
if (style || startStyle || endStyle || builder.measure) {
|
3639 |
+
var fullStyle = style || "";
|
3640 |
+
if (startStyle) fullStyle += startStyle;
|
3641 |
+
if (endStyle) fullStyle += endStyle;
|
3642 |
+
return builder.pre.appendChild(elt("span", [content], fullStyle));
|
3643 |
+
}
|
3644 |
+
builder.pre.appendChild(content);
|
3645 |
+
}
|
3646 |
+
|
3647 |
+
function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
|
3648 |
+
for (var i = 0; i < text.length; ++i) {
|
3649 |
+
if (i && i < text.length - 1 &&
|
3650 |
+
builder.cm.options.lineWrapping &&
|
3651 |
+
spanAffectsWrapping.test(text.slice(i - 1, i + 1)))
|
3652 |
+
builder.pre.appendChild(elt("wbr"));
|
3653 |
+
builder.measure[builder.pos++] =
|
3654 |
+
buildToken(builder, text.charAt(i), style,
|
3655 |
+
i == 0 && startStyle, i == text.length - 1 && endStyle);
|
3656 |
+
}
|
3657 |
+
if (text.length) builder.addedOne = true;
|
3658 |
+
}
|
3659 |
+
|
3660 |
+
function buildCollapsedSpan(builder, size, widget) {
|
3661 |
+
if (widget) {
|
3662 |
+
if (!builder.display) widget = widget.cloneNode(true);
|
3663 |
+
builder.pre.appendChild(widget);
|
3664 |
+
if (builder.measure && size) {
|
3665 |
+
builder.measure[builder.pos] = widget;
|
3666 |
+
builder.addedOne = true;
|
3667 |
+
}
|
3668 |
+
}
|
3669 |
+
builder.pos += size;
|
3670 |
+
}
|
3671 |
+
|
3672 |
+
// Outputs a number of spans to make up a line, taking highlighting
|
3673 |
+
// and marked text into account.
|
3674 |
+
function insertLineContent(line, builder) {
|
3675 |
+
var st = line.styles, spans = line.markedSpans;
|
3676 |
+
if (!spans) {
|
3677 |
+
for (var i = 0; i < st.length; i+=2)
|
3678 |
+
builder.addToken(builder, st[i], styleToClass(st[i+1]));
|
3679 |
+
return;
|
3680 |
+
}
|
3681 |
+
|
3682 |
+
var allText = line.text, len = allText.length;
|
3683 |
+
var pos = 0, i = 0, text = "", style;
|
3684 |
+
var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
|
3685 |
+
for (;;) {
|
3686 |
+
if (nextChange == pos) { // Update current marker set
|
3687 |
+
spanStyle = spanEndStyle = spanStartStyle = "";
|
3688 |
+
collapsed = null; nextChange = Infinity;
|
3689 |
+
var foundBookmark = null;
|
3690 |
+
for (var j = 0; j < spans.length; ++j) {
|
3691 |
+
var sp = spans[j], m = sp.marker;
|
3692 |
+
if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
|
3693 |
+
if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
|
3694 |
+
if (m.className) spanStyle += " " + m.className;
|
3695 |
+
if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
|
3696 |
+
if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
|
3697 |
+
if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
|
3698 |
+
collapsed = sp;
|
3699 |
+
} else if (sp.from > pos && nextChange > sp.from) {
|
3700 |
+
nextChange = sp.from;
|
3701 |
+
}
|
3702 |
+
if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
|
3703 |
+
foundBookmark = m.replacedWith;
|
3704 |
+
}
|
3705 |
+
if (collapsed && (collapsed.from || 0) == pos) {
|
3706 |
+
buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
|
3707 |
+
collapsed.from != null && collapsed.marker.replacedWith);
|
3708 |
+
if (collapsed.to == null) return collapsed.marker.find();
|
3709 |
+
}
|
3710 |
+
if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
|
3711 |
+
}
|
3712 |
+
if (pos >= len) break;
|
3713 |
+
|
3714 |
+
var upto = Math.min(len, nextChange);
|
3715 |
+
while (true) {
|
3716 |
+
if (text) {
|
3717 |
+
var end = pos + text.length;
|
3718 |
+
if (!collapsed) {
|
3719 |
+
var tokenText = end > upto ? text.slice(0, upto - pos) : text;
|
3720 |
+
builder.addToken(builder, tokenText, style + spanStyle,
|
3721 |
+
spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
|
3722 |
+
}
|
3723 |
+
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
|
3724 |
+
pos = end;
|
3725 |
+
spanStartStyle = "";
|
3726 |
+
}
|
3727 |
+
text = st[i++]; style = styleToClass(st[i++]);
|
3728 |
+
}
|
3729 |
+
}
|
3730 |
+
}
|
3731 |
+
|
3732 |
+
// DOCUMENT DATA STRUCTURE
|
3733 |
+
|
3734 |
+
function LeafChunk(lines) {
|
3735 |
+
this.lines = lines;
|
3736 |
+
this.parent = null;
|
3737 |
+
for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
|
3738 |
+
lines[i].parent = this;
|
3739 |
+
height += lines[i].height;
|
3740 |
+
}
|
3741 |
+
this.height = height;
|
3742 |
+
}
|
3743 |
+
|
3744 |
+
LeafChunk.prototype = {
|
3745 |
+
chunkSize: function() { return this.lines.length; },
|
3746 |
+
remove: function(at, n, cm) {
|
3747 |
+
for (var i = at, e = at + n; i < e; ++i) {
|
3748 |
+
var line = this.lines[i];
|
3749 |
+
this.height -= line.height;
|
3750 |
+
cleanUpLine(line);
|
3751 |
+
signalLater(cm, line, "delete");
|
3752 |
+
}
|
3753 |
+
this.lines.splice(at, n);
|
3754 |
+
},
|
3755 |
+
collapse: function(lines) {
|
3756 |
+
lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
|
3757 |
+
},
|
3758 |
+
insertHeight: function(at, lines, height) {
|
3759 |
+
this.height += height;
|
3760 |
+
this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
|
3761 |
+
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
|
3762 |
+
},
|
3763 |
+
iterN: function(at, n, op) {
|
3764 |
+
for (var e = at + n; at < e; ++at)
|
3765 |
+
if (op(this.lines[at])) return true;
|
3766 |
+
}
|
3767 |
+
};
|
3768 |
+
|
3769 |
+
function BranchChunk(children) {
|
3770 |
+
this.children = children;
|
3771 |
+
var size = 0, height = 0;
|
3772 |
+
for (var i = 0, e = children.length; i < e; ++i) {
|
3773 |
+
var ch = children[i];
|
3774 |
+
size += ch.chunkSize(); height += ch.height;
|
3775 |
+
ch.parent = this;
|
3776 |
+
}
|
3777 |
+
this.size = size;
|
3778 |
+
this.height = height;
|
3779 |
+
this.parent = null;
|
3780 |
+
}
|
3781 |
+
|
3782 |
+
BranchChunk.prototype = {
|
3783 |
+
chunkSize: function() { return this.size; },
|
3784 |
+
remove: function(at, n, callbacks) {
|
3785 |
+
this.size -= n;
|
3786 |
+
for (var i = 0; i < this.children.length; ++i) {
|
3787 |
+
var child = this.children[i], sz = child.chunkSize();
|
3788 |
+
if (at < sz) {
|
3789 |
+
var rm = Math.min(n, sz - at), oldHeight = child.height;
|
3790 |
+
child.remove(at, rm, callbacks);
|
3791 |
+
this.height -= oldHeight - child.height;
|
3792 |
+
if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
|
3793 |
+
if ((n -= rm) == 0) break;
|
3794 |
+
at = 0;
|
3795 |
+
} else at -= sz;
|
3796 |
+
}
|
3797 |
+
if (this.size - n < 25) {
|
3798 |
+
var lines = [];
|
3799 |
+
this.collapse(lines);
|
3800 |
+
this.children = [new LeafChunk(lines)];
|
3801 |
+
this.children[0].parent = this;
|
3802 |
+
}
|
3803 |
+
},
|
3804 |
+
collapse: function(lines) {
|
3805 |
+
for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
|
3806 |
+
},
|
3807 |
+
insert: function(at, lines) {
|
3808 |
+
var height = 0;
|
3809 |
+
for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
|
3810 |
+
this.insertHeight(at, lines, height);
|
3811 |
+
},
|
3812 |
+
insertHeight: function(at, lines, height) {
|
3813 |
+
this.size += lines.length;
|
3814 |
+
this.height += height;
|
3815 |
+
for (var i = 0, e = this.children.length; i < e; ++i) {
|
3816 |
+
var child = this.children[i], sz = child.chunkSize();
|
3817 |
+
if (at <= sz) {
|
3818 |
+
child.insertHeight(at, lines, height);
|
3819 |
+
if (child.lines && child.lines.length > 50) {
|
3820 |
+
while (child.lines.length > 50) {
|
3821 |
+
var spilled = child.lines.splice(child.lines.length - 25, 25);
|
3822 |
+
var newleaf = new LeafChunk(spilled);
|
3823 |
+
child.height -= newleaf.height;
|
3824 |
+
this.children.splice(i + 1, 0, newleaf);
|
3825 |
+
newleaf.parent = this;
|
3826 |
+
}
|
3827 |
+
this.maybeSpill();
|
3828 |
+
}
|
3829 |
+
break;
|
3830 |
+
}
|
3831 |
+
at -= sz;
|
3832 |
+
}
|
3833 |
+
},
|
3834 |
+
maybeSpill: function() {
|
3835 |
+
if (this.children.length <= 10) return;
|
3836 |
+
var me = this;
|
3837 |
+
do {
|
3838 |
+
var spilled = me.children.splice(me.children.length - 5, 5);
|
3839 |
+
var sibling = new BranchChunk(spilled);
|
3840 |
+
if (!me.parent) { // Become the parent node
|
3841 |
+
var copy = new BranchChunk(me.children);
|
3842 |
+
copy.parent = me;
|
3843 |
+
me.children = [copy, sibling];
|
3844 |
+
me = copy;
|
3845 |
+
} else {
|
3846 |
+
me.size -= sibling.size;
|
3847 |
+
me.height -= sibling.height;
|
3848 |
+
var myIndex = indexOf(me.parent.children, me);
|
3849 |
+
me.parent.children.splice(myIndex + 1, 0, sibling);
|
3850 |
+
}
|
3851 |
+
sibling.parent = me.parent;
|
3852 |
+
} while (me.children.length > 10);
|
3853 |
+
me.parent.maybeSpill();
|
3854 |
+
},
|
3855 |
+
iter: function(from, to, op) { this.iterN(from, to - from, op); },
|
3856 |
+
iterN: function(at, n, op) {
|
3857 |
+
for (var i = 0, e = this.children.length; i < e; ++i) {
|
3858 |
+
var child = this.children[i], sz = child.chunkSize();
|
3859 |
+
if (at < sz) {
|
3860 |
+
var used = Math.min(n, sz - at);
|
3861 |
+
if (child.iterN(at, used, op)) return true;
|
3862 |
+
if ((n -= used) == 0) break;
|
3863 |
+
at = 0;
|
3864 |
+
} else at -= sz;
|
3865 |
+
}
|
3866 |
+
}
|
3867 |
+
};
|
3868 |
+
|
3869 |
+
// LINE UTILITIES
|
3870 |
+
|
3871 |
+
function getLine(chunk, n) {
|
3872 |
+
while (!chunk.lines) {
|
3873 |
+
for (var i = 0;; ++i) {
|
3874 |
+
var child = chunk.children[i], sz = child.chunkSize();
|
3875 |
+
if (n < sz) { chunk = child; break; }
|
3876 |
+
n -= sz;
|
3877 |
+
}
|
3878 |
+
}
|
3879 |
+
return chunk.lines[n];
|
3880 |
+
}
|
3881 |
+
|
3882 |
+
function updateLineHeight(line, height) {
|
3883 |
+
var diff = height - line.height;
|
3884 |
+
for (var n = line; n; n = n.parent) n.height += diff;
|
3885 |
+
}
|
3886 |
+
|
3887 |
+
function lineNo(line) {
|
3888 |
+
if (line.parent == null) return null;
|
3889 |
+
var cur = line.parent, no = indexOf(cur.lines, line);
|
3890 |
+
for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
|
3891 |
+
for (var i = 0;; ++i) {
|
3892 |
+
if (chunk.children[i] == cur) break;
|
3893 |
+
no += chunk.children[i].chunkSize();
|
3894 |
+
}
|
3895 |
+
}
|
3896 |
+
return no;
|
3897 |
+
}
|
3898 |
+
|
3899 |
+
function lineAtHeight(chunk, h) {
|
3900 |
+
var n = 0;
|
3901 |
+
outer: do {
|
3902 |
+
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
3903 |
+
var child = chunk.children[i], ch = child.height;
|
3904 |
+
if (h < ch) { chunk = child; continue outer; }
|
3905 |
+
h -= ch;
|
3906 |
+
n += child.chunkSize();
|
3907 |
+
}
|
3908 |
+
return n;
|
3909 |
+
} while (!chunk.lines);
|
3910 |
+
for (var i = 0, e = chunk.lines.length; i < e; ++i) {
|
3911 |
+
var line = chunk.lines[i], lh = line.height;
|
3912 |
+
if (h < lh) break;
|
3913 |
+
h -= lh;
|
3914 |
+
}
|
3915 |
+
return n + i;
|
3916 |
+
}
|
3917 |
+
|
3918 |
+
function heightAtLine(cm, lineObj) {
|
3919 |
+
lineObj = visualLine(cm.view.doc, lineObj);
|
3920 |
+
|
3921 |
+
var h = 0, chunk = lineObj.parent;
|
3922 |
+
for (var i = 0; i < chunk.lines.length; ++i) {
|
3923 |
+
var line = chunk.lines[i];
|
3924 |
+
if (line == lineObj) break;
|
3925 |
+
else h += line.height;
|
3926 |
+
}
|
3927 |
+
for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
|
3928 |
+
for (var i = 0; i < p.children.length; ++i) {
|
3929 |
+
var cur = p.children[i];
|
3930 |
+
if (cur == chunk) break;
|
3931 |
+
else h += cur.height;
|
3932 |
+
}
|
3933 |
+
}
|
3934 |
+
return h;
|
3935 |
+
}
|
3936 |
+
|
3937 |
+
function getOrder(line) {
|
3938 |
+
var order = line.order;
|
3939 |
+
if (order == null) order = line.order = bidiOrdering(line.text);
|
3940 |
+
return order;
|
3941 |
+
}
|
3942 |
+
|
3943 |
+
// HISTORY
|
3944 |
+
|
3945 |
+
function makeHistory() {
|
3946 |
+
return {
|
3947 |
+
// Arrays of history events. Doing something adds an event to
|
3948 |
+
// done and clears undo. Undoing moves events from done to
|
3949 |
+
// undone, redoing moves them in the other direction.
|
3950 |
+
done: [], undone: [],
|
3951 |
+
// Used to track when changes can be merged into a single undo
|
3952 |
+
// event
|
3953 |
+
lastTime: 0, lastOp: null, lastOrigin: null,
|
3954 |
+
// Used by the isClean() method
|
3955 |
+
dirtyCounter: 0
|
3956 |
+
};
|
3957 |
+
}
|
3958 |
+
|
3959 |
+
function addChange(cm, start, added, old, origin, fromBefore, toBefore, fromAfter, toAfter) {
|
3960 |
+
var history = cm.view.history;
|
3961 |
+
history.undone.length = 0;
|
3962 |
+
var time = +new Date, cur = lst(history.done);
|
3963 |
+
|
3964 |
+
if (cur &&
|
3965 |
+
(history.lastOp == cm.curOp.id ||
|
3966 |
+
history.lastOrigin == origin && (origin == "input" || origin == "delete") &&
|
3967 |
+
history.lastTime > time - 600)) {
|
3968 |
+
// Merge this change into the last event
|
3969 |
+
var last = lst(cur.events);
|
3970 |
+
if (last.start > start + old.length || last.start + last.added < start) {
|
3971 |
+
// Doesn't intersect with last sub-event, add new sub-event
|
3972 |
+
cur.events.push({start: start, added: added, old: old});
|
3973 |
+
} else {
|
3974 |
+
// Patch up the last sub-event
|
3975 |
+
var startBefore = Math.max(0, last.start - start),
|
3976 |
+
endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
|
3977 |
+
for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
|
3978 |
+
for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
|
3979 |
+
if (startBefore) last.start = start;
|
3980 |
+
last.added += added - (old.length - startBefore - endAfter);
|
3981 |
+
}
|
3982 |
+
cur.fromAfter = fromAfter; cur.toAfter = toAfter;
|
3983 |
+
} else {
|
3984 |
+
// Can not be merged, start a new event.
|
3985 |
+
cur = {events: [{start: start, added: added, old: old}],
|
3986 |
+
fromBefore: fromBefore, toBefore: toBefore, fromAfter: fromAfter, toAfter: toAfter};
|
3987 |
+
history.done.push(cur);
|
3988 |
+
while (history.done.length > cm.options.undoDepth)
|
3989 |
+
history.done.shift();
|
3990 |
+
if (history.dirtyCounter < 0)
|
3991 |
+
// The user has made a change after undoing past the last clean state.
|
3992 |
+
// We can never get back to a clean state now until markClean() is called.
|
3993 |
+
history.dirtyCounter = NaN;
|
3994 |
+
else
|
3995 |
+
history.dirtyCounter++;
|
3996 |
+
}
|
3997 |
+
history.lastTime = time;
|
3998 |
+
history.lastOp = cm.curOp.id;
|
3999 |
+
history.lastOrigin = origin;
|
4000 |
+
}
|
4001 |
+
|
4002 |
+
// EVENT OPERATORS
|
4003 |
+
|
4004 |
+
function stopMethod() {e_stop(this);}
|
4005 |
+
// Ensure an event has a stop method.
|
4006 |
+
function addStop(event) {
|
4007 |
+
if (!event.stop) event.stop = stopMethod;
|
4008 |
+
return event;
|
4009 |
+
}
|
4010 |
+
|
4011 |
+
function e_preventDefault(e) {
|
4012 |
+
if (e.preventDefault) e.preventDefault();
|
4013 |
+
else e.returnValue = false;
|
4014 |
+
}
|
4015 |
+
function e_stopPropagation(e) {
|
4016 |
+
if (e.stopPropagation) e.stopPropagation();
|
4017 |
+
else e.cancelBubble = true;
|
4018 |
+
}
|
4019 |
+
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
|
4020 |
+
CodeMirror.e_stop = e_stop;
|
4021 |
+
CodeMirror.e_preventDefault = e_preventDefault;
|
4022 |
+
CodeMirror.e_stopPropagation = e_stopPropagation;
|
4023 |
+
|
4024 |
+
function e_target(e) {return e.target || e.srcElement;}
|
4025 |
+
function e_button(e) {
|
4026 |
+
var b = e.which;
|
4027 |
+
if (b == null) {
|
4028 |
+
if (e.button & 1) b = 1;
|
4029 |
+
else if (e.button & 2) b = 3;
|
4030 |
+
else if (e.button & 4) b = 2;
|
4031 |
+
}
|
4032 |
+
if (mac && e.ctrlKey && b == 1) b = 3;
|
4033 |
+
return b;
|
4034 |
+
}
|
4035 |
+
|
4036 |
+
// Allow 3rd-party code to override event properties by adding an override
|
4037 |
+
// object to an event object.
|
4038 |
+
function e_prop(e, prop) {
|
4039 |
+
var overridden = e.override && e.override.hasOwnProperty(prop);
|
4040 |
+
return overridden ? e.override[prop] : e[prop];
|
4041 |
+
}
|
4042 |
+
|
4043 |
+
// EVENT HANDLING
|
4044 |
+
|
4045 |
+
function on(emitter, type, f) {
|
4046 |
+
if (emitter.addEventListener)
|
4047 |
+
emitter.addEventListener(type, f, false);
|
4048 |
+
else if (emitter.attachEvent)
|
4049 |
+
emitter.attachEvent("on" + type, f);
|
4050 |
+
else {
|
4051 |
+
var map = emitter._handlers || (emitter._handlers = {});
|
4052 |
+
var arr = map[type] || (map[type] = []);
|
4053 |
+
arr.push(f);
|
4054 |
+
}
|
4055 |
+
}
|
4056 |
+
|
4057 |
+
function off(emitter, type, f) {
|
4058 |
+
if (emitter.removeEventListener)
|
4059 |
+
emitter.removeEventListener(type, f, false);
|
4060 |
+
else if (emitter.detachEvent)
|
4061 |
+
emitter.detachEvent("on" + type, f);
|
4062 |
+
else {
|
4063 |
+
var arr = emitter._handlers && emitter._handlers[type];
|
4064 |
+
if (!arr) return;
|
4065 |
+
for (var i = 0; i < arr.length; ++i)
|
4066 |
+
if (arr[i] == f) { arr.splice(i, 1); break; }
|
4067 |
+
}
|
4068 |
+
}
|
4069 |
+
|
4070 |
+
function signal(emitter, type /*, values...*/) {
|
4071 |
+
var arr = emitter._handlers && emitter._handlers[type];
|
4072 |
+
if (!arr) return;
|
4073 |
+
var args = Array.prototype.slice.call(arguments, 2);
|
4074 |
+
for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
|
4075 |
+
}
|
4076 |
+
|
4077 |
+
function signalLater(cm, emitter, type /*, values...*/) {
|
4078 |
+
var arr = emitter._handlers && emitter._handlers[type];
|
4079 |
+
if (!arr) return;
|
4080 |
+
var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks;
|
4081 |
+
function bnd(f) {return function(){f.apply(null, args);};};
|
4082 |
+
for (var i = 0; i < arr.length; ++i)
|
4083 |
+
if (flist) flist.push(bnd(arr[i]));
|
4084 |
+
else arr[i].apply(null, args);
|
4085 |
+
}
|
4086 |
+
|
4087 |
+
function hasHandler(emitter, type) {
|
4088 |
+
var arr = emitter._handlers && emitter._handlers[type];
|
4089 |
+
return arr && arr.length > 0;
|
4090 |
+
}
|
4091 |
+
|
4092 |
+
CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
|
4093 |
+
|
4094 |
+
// MISC UTILITIES
|
4095 |
+
|
4096 |
+
// Number of pixels added to scroller and sizer to hide scrollbar
|
4097 |
+
var scrollerCutOff = 30;
|
4098 |
+
|
4099 |
+
// Returned or thrown by various protocols to signal 'I'm not
|
4100 |
+
// handling this'.
|
4101 |
+
var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
|
4102 |
+
|
4103 |
+
function Delayed() {this.id = null;}
|
4104 |
+
Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
|
4105 |
+
|
4106 |
+
// Counts the column offset in a string, taking tabs into account.
|
4107 |
+
// Used mostly to find indentation.
|
4108 |
+
function countColumn(string, end, tabSize) {
|
4109 |
+
if (end == null) {
|
4110 |
+
end = string.search(/[^\s\u00a0]/);
|
4111 |
+
if (end == -1) end = string.length;
|
4112 |
+
}
|
4113 |
+
for (var i = 0, n = 0; i < end; ++i) {
|
4114 |
+
if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
|
4115 |
+
else ++n;
|
4116 |
+
}
|
4117 |
+
return n;
|
4118 |
+
}
|
4119 |
+
CodeMirror.countColumn = countColumn;
|
4120 |
+
|
4121 |
+
var spaceStrs = [""];
|
4122 |
+
function spaceStr(n) {
|
4123 |
+
while (spaceStrs.length <= n)
|
4124 |
+
spaceStrs.push(lst(spaceStrs) + " ");
|
4125 |
+
return spaceStrs[n];
|
4126 |
+
}
|
4127 |
+
|
4128 |
+
function lst(arr) { return arr[arr.length-1]; }
|
4129 |
+
|
4130 |
+
function selectInput(node) {
|
4131 |
+
if (ios) { // Mobile Safari apparently has a bug where select() is broken.
|
4132 |
+
node.selectionStart = 0;
|
4133 |
+
node.selectionEnd = node.value.length;
|
4134 |
+
} else node.select();
|
4135 |
+
}
|
4136 |
+
|
4137 |
+
function indexOf(collection, elt) {
|
4138 |
+
if (collection.indexOf) return collection.indexOf(elt);
|
4139 |
+
for (var i = 0, e = collection.length; i < e; ++i)
|
4140 |
+
if (collection[i] == elt) return i;
|
4141 |
+
return -1;
|
4142 |
+
}
|
4143 |
+
|
4144 |
+
function emptyArray(size) {
|
4145 |
+
for (var a = [], i = 0; i < size; ++i) a.push(undefined);
|
4146 |
+
return a;
|
4147 |
+
}
|
4148 |
+
|
4149 |
+
function bind(f) {
|
4150 |
+
var args = Array.prototype.slice.call(arguments, 1);
|
4151 |
+
return function(){return f.apply(null, args);};
|
4152 |
+
}
|
4153 |
+
|
4154 |
+
var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
|
4155 |
+
function isWordChar(ch) {
|
4156 |
+
return /\w/.test(ch) || ch > "\x80" &&
|
4157 |
+
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
|
4158 |
+
}
|
4159 |
+
|
4160 |
+
function isEmpty(obj) {
|
4161 |
+
var c = 0;
|
4162 |
+
for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c;
|
4163 |
+
return !c;
|
4164 |
+
}
|
4165 |
+
|
4166 |
+
var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/;
|
4167 |
+
|
4168 |
+
// DOM UTILITIES
|
4169 |
+
|
4170 |
+
function elt(tag, content, className, style) {
|
4171 |
+
var e = document.createElement(tag);
|
4172 |
+
if (className) e.className = className;
|
4173 |
+
if (style) e.style.cssText = style;
|
4174 |
+
if (typeof content == "string") setTextContent(e, content);
|
4175 |
+
else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
|
4176 |
+
return e;
|
4177 |
+
}
|
4178 |
+
|
4179 |
+
function removeChildren(e) {
|
4180 |
+
e.innerHTML = "";
|
4181 |
+
return e;
|
4182 |
+
}
|
4183 |
+
|
4184 |
+
function removeChildrenAndAdd(parent, e) {
|
4185 |
+
return removeChildren(parent).appendChild(e);
|
4186 |
+
}
|
4187 |
+
|
4188 |
+
function setTextContent(e, str) {
|
4189 |
+
if (ie_lt9) {
|
4190 |
+
e.innerHTML = "";
|
4191 |
+
e.appendChild(document.createTextNode(str));
|
4192 |
+
} else e.textContent = str;
|
4193 |
+
}
|
4194 |
+
|
4195 |
+
// FEATURE DETECTION
|
4196 |
+
|
4197 |
+
// Detect drag-and-drop
|
4198 |
+
var dragAndDrop = function() {
|
4199 |
+
// There is *some* kind of drag-and-drop support in IE6-8, but I
|
4200 |
+
// couldn't get it to work yet.
|
4201 |
+
if (ie_lt9) return false;
|
4202 |
+
var div = elt('div');
|
4203 |
+
return "draggable" in div || "dragDrop" in div;
|
4204 |
+
}();
|
4205 |
+
|
4206 |
+
// For a reason I have yet to figure out, some browsers disallow
|
4207 |
+
// word wrapping between certain characters *only* if a new inline
|
4208 |
+
// element is started between them. This makes it hard to reliably
|
4209 |
+
// measure the position of things, since that requires inserting an
|
4210 |
+
// extra span. This terribly fragile set of regexps matches the
|
4211 |
+
// character combinations that suffer from this phenomenon on the
|
4212 |
+
// various browsers.
|
4213 |
+
var spanAffectsWrapping = /^$/; // Won't match any two-character string
|
4214 |
+
if (gecko) spanAffectsWrapping = /$'/;
|
4215 |
+
else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
|
4216 |
+
else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
|
4217 |
+
|
4218 |
+
var knownScrollbarWidth;
|
4219 |
+
function scrollbarWidth(measure) {
|
4220 |
+
if (knownScrollbarWidth != null) return knownScrollbarWidth;
|
4221 |
+
var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
|
4222 |
+
removeChildrenAndAdd(measure, test);
|
4223 |
+
if (test.offsetWidth)
|
4224 |
+
knownScrollbarWidth = test.offsetHeight - test.clientHeight;
|
4225 |
+
return knownScrollbarWidth || 0;
|
4226 |
+
}
|
4227 |
+
|
4228 |
+
var zwspSupported;
|
4229 |
+
function zeroWidthElement(measure) {
|
4230 |
+
if (zwspSupported == null) {
|
4231 |
+
var test = elt("span", "\u200b");
|
4232 |
+
removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
|
4233 |
+
if (measure.firstChild.offsetHeight != 0)
|
4234 |
+
zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
|
4235 |
+
}
|
4236 |
+
if (zwspSupported) return elt("span", "\u200b");
|
4237 |
+
else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
|
4238 |
+
}
|
4239 |
+
|
4240 |
+
// See if "".split is the broken IE version, if so, provide an
|
4241 |
+
// alternative way to split lines.
|
4242 |
+
var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
|
4243 |
+
var pos = 0, result = [], l = string.length;
|
4244 |
+
while (pos <= l) {
|
4245 |
+
var nl = string.indexOf("\n", pos);
|
4246 |
+
if (nl == -1) nl = string.length;
|
4247 |
+
var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
|
4248 |
+
var rt = line.indexOf("\r");
|
4249 |
+
if (rt != -1) {
|
4250 |
+
result.push(line.slice(0, rt));
|
4251 |
+
pos += rt + 1;
|
4252 |
+
} else {
|
4253 |
+
result.push(line);
|
4254 |
+
pos = nl + 1;
|
4255 |
+
}
|
4256 |
+
}
|
4257 |
+
return result;
|
4258 |
+
} : function(string){return string.split(/\r\n?|\n/);};
|
4259 |
+
CodeMirror.splitLines = splitLines;
|
4260 |
+
|
4261 |
+
var hasSelection = window.getSelection ? function(te) {
|
4262 |
+
try { return te.selectionStart != te.selectionEnd; }
|
4263 |
+
catch(e) { return false; }
|
4264 |
+
} : function(te) {
|
4265 |
+
try {var range = te.ownerDocument.selection.createRange();}
|
4266 |
+
catch(e) {}
|
4267 |
+
if (!range || range.parentElement() != te) return false;
|
4268 |
+
return range.compareEndPoints("StartToEnd", range) != 0;
|
4269 |
+
};
|
4270 |
+
|
4271 |
+
var hasCopyEvent = (function() {
|
4272 |
+
var e = elt("div");
|
4273 |
+
if ("oncopy" in e) return true;
|
4274 |
+
e.setAttribute("oncopy", "return;");
|
4275 |
+
return typeof e.oncopy == 'function';
|
4276 |
+
})();
|
4277 |
+
|
4278 |
+
// KEY NAMING
|
4279 |
+
|
4280 |
+
var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
|
4281 |
+
19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
|
4282 |
+
36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
|
4283 |
+
46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
|
4284 |
+
186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
|
4285 |
+
221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
|
4286 |
+
63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
|
4287 |
+
CodeMirror.keyNames = keyNames;
|
4288 |
+
(function() {
|
4289 |
+
// Number keys
|
4290 |
+
for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
|
4291 |
+
// Alphabetic keys
|
4292 |
+
for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
|
4293 |
+
// Function keys
|
4294 |
+
for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
|
4295 |
+
})();
|
4296 |
+
|
4297 |
+
// BIDI HELPERS
|
4298 |
+
|
4299 |
+
function iterateBidiSections(order, from, to, f) {
|
4300 |
+
if (!order) return f(from, to, "ltr");
|
4301 |
+
for (var i = 0; i < order.length; ++i) {
|
4302 |
+
var part = order[i];
|
4303 |
+
if (part.from < to && part.to > from)
|
4304 |
+
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
|
4305 |
+
}
|
4306 |
+
}
|
4307 |
+
|
4308 |
+
function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
|
4309 |
+
function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
|
4310 |
+
|
4311 |
+
function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
|
4312 |
+
function lineRight(line) {
|
4313 |
+
var order = getOrder(line);
|
4314 |
+
if (!order) return line.text.length;
|
4315 |
+
return bidiRight(lst(order));
|
4316 |
+
}
|
4317 |
+
|
4318 |
+
function lineStart(cm, lineN) {
|
4319 |
+
var line = getLine(cm.view.doc, lineN);
|
4320 |
+
var visual = visualLine(cm.view.doc, line);
|
4321 |
+
if (visual != line) lineN = lineNo(visual);
|
4322 |
+
var order = getOrder(visual);
|
4323 |
+
var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
|
4324 |
+
return {line: lineN, ch: ch};
|
4325 |
+
}
|
4326 |
+
function lineEnd(cm, lineNo) {
|
4327 |
+
var merged, line;
|
4328 |
+
while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo)))
|
4329 |
+
lineNo = merged.find().to.line;
|
4330 |
+
var order = getOrder(line);
|
4331 |
+
var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
|
4332 |
+
return {line: lineNo, ch: ch};
|
4333 |
+
}
|
4334 |
+
|
4335 |
+
// This is somewhat involved. It is needed in order to move
|
4336 |
+
// 'visually' through bi-directional text -- i.e., pressing left
|
4337 |
+
// should make the cursor go left, even when in RTL text. The
|
4338 |
+
// tricky part is the 'jumps', where RTL and LTR text touch each
|
4339 |
+
// other. This often requires the cursor offset to move more than
|
4340 |
+
// one unit, in order to visually move one unit.
|
4341 |
+
function moveVisually(line, start, dir, byUnit) {
|
4342 |
+
var bidi = getOrder(line);
|
4343 |
+
if (!bidi) return moveLogically(line, start, dir, byUnit);
|
4344 |
+
var moveOneUnit = byUnit ? function(pos, dir) {
|
4345 |
+
do pos += dir;
|
4346 |
+
while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
|
4347 |
+
return pos;
|
4348 |
+
} : function(pos, dir) { return pos + dir; };
|
4349 |
+
var linedir = bidi[0].level;
|
4350 |
+
for (var i = 0; i < bidi.length; ++i) {
|
4351 |
+
var part = bidi[i], sticky = part.level % 2 == linedir;
|
4352 |
+
if ((part.from < start && part.to > start) ||
|
4353 |
+
(sticky && (part.from == start || part.to == start))) break;
|
4354 |
+
}
|
4355 |
+
var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
|
4356 |
+
|
4357 |
+
while (target != null) {
|
4358 |
+
if (part.level % 2 == linedir) {
|
4359 |
+
if (target < part.from || target > part.to) {
|
4360 |
+
part = bidi[i += dir];
|
4361 |
+
target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
|
4362 |
+
} else break;
|
4363 |
+
} else {
|
4364 |
+
if (target == bidiLeft(part)) {
|
4365 |
+
part = bidi[--i];
|
4366 |
+
target = part && bidiRight(part);
|
4367 |
+
} else if (target == bidiRight(part)) {
|
4368 |
+
part = bidi[++i];
|
4369 |
+
target = part && bidiLeft(part);
|
4370 |
+
} else break;
|
4371 |
+
}
|
4372 |
+
}
|
4373 |
+
|
4374 |
+
return target < 0 || target > line.text.length ? null : target;
|
4375 |
+
}
|
4376 |
+
|
4377 |
+
function moveLogically(line, start, dir, byUnit) {
|
4378 |
+
var target = start + dir;
|
4379 |
+
if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
|
4380 |
+
return target < 0 || target > line.text.length ? null : target;
|
4381 |
+
}
|
4382 |
+
|
4383 |
+
// Bidirectional ordering algorithm
|
4384 |
+
// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
|
4385 |
+
// that this (partially) implements.
|
4386 |
+
|
4387 |
+
// One-char codes used for character types:
|
4388 |
+
// L (L): Left-to-Right
|
4389 |
+
// R (R): Right-to-Left
|
4390 |
+
// r (AL): Right-to-Left Arabic
|
4391 |
+
// 1 (EN): European Number
|
4392 |
+
// + (ES): European Number Separator
|
4393 |
+
// % (ET): European Number Terminator
|
4394 |
+
// n (AN): Arabic Number
|
4395 |
+
// , (CS): Common Number Separator
|
4396 |
+
// m (NSM): Non-Spacing Mark
|
4397 |
+
// b (BN): Boundary Neutral
|
4398 |
+
// s (B): Paragraph Separator
|
4399 |
+
// t (S): Segment Separator
|
4400 |
+
// w (WS): Whitespace
|
4401 |
+
// N (ON): Other Neutrals
|
4402 |
+
|
4403 |
+
// Returns null if characters are ordered as they appear
|
4404 |
+
// (left-to-right), or an array of sections ({from, to, level}
|
4405 |
+
// objects) in the order in which they occur visually.
|
4406 |
+
var bidiOrdering = (function() {
|
4407 |
+
// Character types for codepoints 0 to 0xff
|
4408 |
+
var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
|
4409 |
+
// Character types for codepoints 0x600 to 0x6ff
|
4410 |
+
var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
|
4411 |
+
function charType(code) {
|
4412 |
+
if (code <= 0xff) return lowTypes.charAt(code);
|
4413 |
+
else if (0x590 <= code && code <= 0x5f4) return "R";
|
4414 |
+
else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
|
4415 |
+
else if (0x700 <= code && code <= 0x8ac) return "r";
|
4416 |
+
else return "L";
|
4417 |
+
}
|
4418 |
+
|
4419 |
+
var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
4420 |
+
var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
|
4421 |
+
|
4422 |
+
return function charOrdering(str) {
|
4423 |
+
if (!bidiRE.test(str)) return false;
|
4424 |
+
var len = str.length, types = [], startType = null;
|
4425 |
+
for (var i = 0, type; i < len; ++i) {
|
4426 |
+
types.push(type = charType(str.charCodeAt(i)));
|
4427 |
+
if (startType == null) {
|
4428 |
+
if (type == "L") startType = "L";
|
4429 |
+
else if (type == "R" || type == "r") startType = "R";
|
4430 |
+
}
|
4431 |
+
}
|
4432 |
+
if (startType == null) startType = "L";
|
4433 |
+
|
4434 |
+
// W1. Examine each non-spacing mark (NSM) in the level run, and
|
4435 |
+
// change the type of the NSM to the type of the previous
|
4436 |
+
// character. If the NSM is at the start of the level run, it will
|
4437 |
+
// get the type of sor.
|
4438 |
+
for (var i = 0, prev = startType; i < len; ++i) {
|
4439 |
+
var type = types[i];
|
4440 |
+
if (type == "m") types[i] = prev;
|
4441 |
+
else prev = type;
|
4442 |
+
}
|
4443 |
+
|
4444 |
+
// W2. Search backwards from each instance of a European number
|
4445 |
+
// until the first strong type (R, L, AL, or sor) is found. If an
|
4446 |
+
// AL is found, change the type of the European number to Arabic
|
4447 |
+
// number.
|
4448 |
+
// W3. Change all ALs to R.
|
4449 |
+
for (var i = 0, cur = startType; i < len; ++i) {
|
4450 |
+
var type = types[i];
|
4451 |
+
if (type == "1" && cur == "r") types[i] = "n";
|
4452 |
+
else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
|
4453 |
+
}
|
4454 |
+
|
4455 |
+
// W4. A single European separator between two European numbers
|
4456 |
+
// changes to a European number. A single common separator between
|
4457 |
+
// two numbers of the same type changes to that type.
|
4458 |
+
for (var i = 1, prev = types[0]; i < len - 1; ++i) {
|
4459 |
+
var type = types[i];
|
4460 |
+
if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
|
4461 |
+
else if (type == "," && prev == types[i+1] &&
|
4462 |
+
(prev == "1" || prev == "n")) types[i] = prev;
|
4463 |
+
prev = type;
|
4464 |
+
}
|
4465 |
+
|
4466 |
+
// W5. A sequence of European terminators adjacent to European
|
4467 |
+
// numbers changes to all European numbers.
|
4468 |
+
// W6. Otherwise, separators and terminators change to Other
|
4469 |
+
// Neutral.
|
4470 |
+
for (var i = 0; i < len; ++i) {
|
4471 |
+
var type = types[i];
|
4472 |
+
if (type == ",") types[i] = "N";
|
4473 |
+
else if (type == "%") {
|
4474 |
+
for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
|
4475 |
+
var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
|
4476 |
+
for (var j = i; j < end; ++j) types[j] = replace;
|
4477 |
+
i = end - 1;
|
4478 |
+
}
|
4479 |
+
}
|
4480 |
+
|
4481 |
+
// W7. Search backwards from each instance of a European number
|
4482 |
+
// until the first strong type (R, L, or sor) is found. If an L is
|
4483 |
+
// found, then change the type of the European number to L.
|
4484 |
+
for (var i = 0, cur = startType; i < len; ++i) {
|
4485 |
+
var type = types[i];
|
4486 |
+
if (cur == "L" && type == "1") types[i] = "L";
|
4487 |
+
else if (isStrong.test(type)) cur = type;
|
4488 |
+
}
|
4489 |
+
|
4490 |
+
// N1. A sequence of neutrals takes the direction of the
|
4491 |
+
// surrounding strong text if the text on both sides has the same
|
4492 |
+
// direction. European and Arabic numbers act as if they were R in
|
4493 |
+
// terms of their influence on neutrals. Start-of-level-run (sor)
|
4494 |
+
// and end-of-level-run (eor) are used at level run boundaries.
|
4495 |
+
// N2. Any remaining neutrals take the embedding direction.
|
4496 |
+
for (var i = 0; i < len; ++i) {
|
4497 |
+
if (isNeutral.test(types[i])) {
|
4498 |
+
for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
|
4499 |
+
var before = (i ? types[i-1] : startType) == "L";
|
4500 |
+
var after = (end < len - 1 ? types[end] : startType) == "L";
|
4501 |
+
var replace = before || after ? "L" : "R";
|
4502 |
+
for (var j = i; j < end; ++j) types[j] = replace;
|
4503 |
+
i = end - 1;
|
4504 |
+
}
|
4505 |
+
}
|
4506 |
+
|
4507 |
+
// Here we depart from the documented algorithm, in order to avoid
|
4508 |
+
// building up an actual levels array. Since there are only three
|
4509 |
+
// levels (0, 1, 2) in an implementation that doesn't take
|
4510 |
+
// explicit embedding into account, we can build up the order on
|
4511 |
+
// the fly, without following the level-based algorithm.
|
4512 |
+
var order = [], m;
|
4513 |
+
for (var i = 0; i < len;) {
|
4514 |
+
if (countsAsLeft.test(types[i])) {
|
4515 |
+
var start = i;
|
4516 |
+
for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
|
4517 |
+
order.push({from: start, to: i, level: 0});
|
4518 |
+
} else {
|
4519 |
+
var pos = i, at = order.length;
|
4520 |
+
for (++i; i < len && types[i] != "L"; ++i) {}
|
4521 |
+
for (var j = pos; j < i;) {
|
4522 |
+
if (countsAsNum.test(types[j])) {
|
4523 |
+
if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
|
4524 |
+
var nstart = j;
|
4525 |
+
for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
|
4526 |
+
order.splice(at, 0, {from: nstart, to: j, level: 2});
|
4527 |
+
pos = j;
|
4528 |
+
} else ++j;
|
4529 |
+
}
|
4530 |
+
if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
|
4531 |
+
}
|
4532 |
+
}
|
4533 |
+
if (order[0].level == 1 && (m = str.match(/^\s+/))) {
|
4534 |
+
order[0].from = m[0].length;
|
4535 |
+
order.unshift({from: 0, to: m[0].length, level: 0});
|
4536 |
+
}
|
4537 |
+
if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
|
4538 |
+
lst(order).to -= m[0].length;
|
4539 |
+
order.push({from: len - m[0].length, to: len, level: 0});
|
4540 |
+
}
|
4541 |
+
if (order[0].level != lst(order).level)
|
4542 |
+
order.push({from: len, to: len, level: order[0].level});
|
4543 |
+
|
4544 |
+
return order;
|
4545 |
+
};
|
4546 |
+
})();
|
4547 |
+
|
4548 |
+
// THE END
|
4549 |
+
|
4550 |
+
CodeMirror.version = "3.0";
|
4551 |
+
|
4552 |
+
return CodeMirror;
|
4553 |
+
})();
|
assets/icon.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
#icon-snippets.icon32 {
|
2 |
+
background: url('../images/icon32.png') no-repeat scroll transparent;
|
3 |
+
}
|
{js → assets/mode}/clike.js
RENAMED
@@ -1,5 +1,6 @@
|
|
1 |
CodeMirror.defineMode("clike", function(config, parserConfig) {
|
2 |
var indentUnit = config.indentUnit,
|
|
|
3 |
keywords = parserConfig.keywords || {},
|
4 |
builtin = parserConfig.builtin || {},
|
5 |
blockKeywords = parserConfig.blockKeywords || {},
|
@@ -89,7 +90,10 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
|
89 |
this.prev = prev;
|
90 |
}
|
91 |
function pushContext(state, col, type) {
|
92 |
-
|
|
|
|
|
|
|
93 |
}
|
94 |
function popContext(state) {
|
95 |
var t = state.context.type;
|
@@ -123,7 +127,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
|
123 |
if (style == "comment" || style == "meta") return style;
|
124 |
if (ctx.align == null) ctx.align = true;
|
125 |
|
126 |
-
if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state);
|
127 |
else if (curPunc == "{") pushContext(state, stream.column(), "}");
|
128 |
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
129 |
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
@@ -133,18 +137,18 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
|
133 |
while (ctx.type == "statement") ctx = popContext(state);
|
134 |
}
|
135 |
else if (curPunc == ctx.type) popContext(state);
|
136 |
-
else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement"))
|
137 |
pushContext(state, stream.column(), "statement");
|
138 |
state.startOfLine = false;
|
139 |
return style;
|
140 |
},
|
141 |
|
142 |
indent: function(state, textAfter) {
|
143 |
-
if (state.tokenize != tokenBase && state.tokenize != null) return
|
144 |
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
|
145 |
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
|
146 |
var closing = firstChar == ctx.type;
|
147 |
-
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 :
|
148 |
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
149 |
else return ctx.indented + (closing ? 0 : indentUnit);
|
150 |
},
|
@@ -165,7 +169,19 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
|
165 |
|
166 |
function cppHook(stream, state) {
|
167 |
if (!state.startOfLine) return false;
|
168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
return "meta";
|
170 |
}
|
171 |
|
@@ -212,7 +228,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
|
212 |
blockKeywords: words("catch class do else finally for if switch try while"),
|
213 |
atoms: words("true false null"),
|
214 |
hooks: {
|
215 |
-
"@": function(stream
|
216 |
stream.eatWhile(/[\w\$_]/);
|
217 |
return "meta";
|
218 |
}
|
@@ -275,7 +291,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
|
275 |
blockKeywords: words("catch class do else finally for forSome if match switch try while"),
|
276 |
atoms: words("true false null"),
|
277 |
hooks: {
|
278 |
-
"@": function(stream
|
279 |
stream.eatWhile(/[\w\$_]/);
|
280 |
return "meta";
|
281 |
}
|
1 |
CodeMirror.defineMode("clike", function(config, parserConfig) {
|
2 |
var indentUnit = config.indentUnit,
|
3 |
+
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
|
4 |
keywords = parserConfig.keywords || {},
|
5 |
builtin = parserConfig.builtin || {},
|
6 |
blockKeywords = parserConfig.blockKeywords || {},
|
90 |
this.prev = prev;
|
91 |
}
|
92 |
function pushContext(state, col, type) {
|
93 |
+
var indent = state.indented;
|
94 |
+
if (state.context && state.context.type == "statement")
|
95 |
+
indent = state.context.indented;
|
96 |
+
return state.context = new Context(indent, col, type, null, state.context);
|
97 |
}
|
98 |
function popContext(state) {
|
99 |
var t = state.context.type;
|
127 |
if (style == "comment" || style == "meta") return style;
|
128 |
if (ctx.align == null) ctx.align = true;
|
129 |
|
130 |
+
if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && ctx.type == "statement") popContext(state);
|
131 |
else if (curPunc == "{") pushContext(state, stream.column(), "}");
|
132 |
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
133 |
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
137 |
while (ctx.type == "statement") ctx = popContext(state);
|
138 |
}
|
139 |
else if (curPunc == ctx.type) popContext(state);
|
140 |
+
else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
|
141 |
pushContext(state, stream.column(), "statement");
|
142 |
state.startOfLine = false;
|
143 |
return style;
|
144 |
},
|
145 |
|
146 |
indent: function(state, textAfter) {
|
147 |
+
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
|
148 |
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
|
149 |
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
|
150 |
var closing = firstChar == ctx.type;
|
151 |
+
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
|
152 |
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
153 |
else return ctx.indented + (closing ? 0 : indentUnit);
|
154 |
},
|
169 |
|
170 |
function cppHook(stream, state) {
|
171 |
if (!state.startOfLine) return false;
|
172 |
+
for (;;) {
|
173 |
+
if (stream.skipTo("\\")) {
|
174 |
+
stream.next();
|
175 |
+
if (stream.eol()) {
|
176 |
+
state.tokenize = cppHook;
|
177 |
+
break;
|
178 |
+
}
|
179 |
+
} else {
|
180 |
+
stream.skipToEnd();
|
181 |
+
state.tokenize = null;
|
182 |
+
break;
|
183 |
+
}
|
184 |
+
}
|
185 |
return "meta";
|
186 |
}
|
187 |
|
228 |
blockKeywords: words("catch class do else finally for if switch try while"),
|
229 |
atoms: words("true false null"),
|
230 |
hooks: {
|
231 |
+
"@": function(stream) {
|
232 |
stream.eatWhile(/[\w\$_]/);
|
233 |
return "meta";
|
234 |
}
|
291 |
blockKeywords: words("catch class do else finally for forSome if match switch try while"),
|
292 |
atoms: words("true false null"),
|
293 |
hooks: {
|
294 |
+
"@": function(stream) {
|
295 |
stream.eatWhile(/[\w\$_]/);
|
296 |
return "meta";
|
297 |
}
|
assets/mode/css.js
ADDED
@@ -0,0 +1,465 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineMode("css", function(config) {
|
2 |
+
var indentUnit = config.indentUnit, type;
|
3 |
+
|
4 |
+
var atMediaTypes = keySet([
|
5 |
+
"all", "aural", "braille", "handheld", "print", "projection", "screen",
|
6 |
+
"tty", "tv", "embossed"
|
7 |
+
]);
|
8 |
+
|
9 |
+
var atMediaFeatures = keySet([
|
10 |
+
"width", "min-width", "max-width", "height", "min-height", "max-height",
|
11 |
+
"device-width", "min-device-width", "max-device-width", "device-height",
|
12 |
+
"min-device-height", "max-device-height", "aspect-ratio",
|
13 |
+
"min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
|
14 |
+
"min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
|
15 |
+
"max-color", "color-index", "min-color-index", "max-color-index",
|
16 |
+
"monochrome", "min-monochrome", "max-monochrome", "resolution",
|
17 |
+
"min-resolution", "max-resolution", "scan", "grid"
|
18 |
+
]);
|
19 |
+
|
20 |
+
var propertyKeywords = keySet([
|
21 |
+
"align-content", "align-items", "align-self", "alignment-adjust",
|
22 |
+
"alignment-baseline", "anchor-point", "animation", "animation-delay",
|
23 |
+
"animation-direction", "animation-duration", "animation-iteration-count",
|
24 |
+
"animation-name", "animation-play-state", "animation-timing-function",
|
25 |
+
"appearance", "azimuth", "backface-visibility", "background",
|
26 |
+
"background-attachment", "background-clip", "background-color",
|
27 |
+
"background-image", "background-origin", "background-position",
|
28 |
+
"background-repeat", "background-size", "baseline-shift", "binding",
|
29 |
+
"bleed", "bookmark-label", "bookmark-level", "bookmark-state",
|
30 |
+
"bookmark-target", "border", "border-bottom", "border-bottom-color",
|
31 |
+
"border-bottom-left-radius", "border-bottom-right-radius",
|
32 |
+
"border-bottom-style", "border-bottom-width", "border-collapse",
|
33 |
+
"border-color", "border-image", "border-image-outset",
|
34 |
+
"border-image-repeat", "border-image-slice", "border-image-source",
|
35 |
+
"border-image-width", "border-left", "border-left-color",
|
36 |
+
"border-left-style", "border-left-width", "border-radius", "border-right",
|
37 |
+
"border-right-color", "border-right-style", "border-right-width",
|
38 |
+
"border-spacing", "border-style", "border-top", "border-top-color",
|
39 |
+
"border-top-left-radius", "border-top-right-radius", "border-top-style",
|
40 |
+
"border-top-width", "border-width", "bottom", "box-decoration-break",
|
41 |
+
"box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
|
42 |
+
"caption-side", "clear", "clip", "color", "color-profile", "column-count",
|
43 |
+
"column-fill", "column-gap", "column-rule", "column-rule-color",
|
44 |
+
"column-rule-style", "column-rule-width", "column-span", "column-width",
|
45 |
+
"columns", "content", "counter-increment", "counter-reset", "crop", "cue",
|
46 |
+
"cue-after", "cue-before", "cursor", "direction", "display",
|
47 |
+
"dominant-baseline", "drop-initial-after-adjust",
|
48 |
+
"drop-initial-after-align", "drop-initial-before-adjust",
|
49 |
+
"drop-initial-before-align", "drop-initial-size", "drop-initial-value",
|
50 |
+
"elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
|
51 |
+
"flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
|
52 |
+
"float", "float-offset", "font", "font-feature-settings", "font-family",
|
53 |
+
"font-kerning", "font-language-override", "font-size", "font-size-adjust",
|
54 |
+
"font-stretch", "font-style", "font-synthesis", "font-variant",
|
55 |
+
"font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
|
56 |
+
"font-variant-ligatures", "font-variant-numeric", "font-variant-position",
|
57 |
+
"font-weight", "grid-cell", "grid-column", "grid-column-align",
|
58 |
+
"grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
|
59 |
+
"grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
|
60 |
+
"grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
|
61 |
+
"icon", "image-orientation", "image-rendering", "image-resolution",
|
62 |
+
"inline-box-align", "justify-content", "left", "letter-spacing",
|
63 |
+
"line-break", "line-height", "line-stacking", "line-stacking-ruby",
|
64 |
+
"line-stacking-shift", "line-stacking-strategy", "list-style",
|
65 |
+
"list-style-image", "list-style-position", "list-style-type", "margin",
|
66 |
+
"margin-bottom", "margin-left", "margin-right", "margin-top",
|
67 |
+
"marker-offset", "marks", "marquee-direction", "marquee-loop",
|
68 |
+
"marquee-play-count", "marquee-speed", "marquee-style", "max-height",
|
69 |
+
"max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
|
70 |
+
"nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
|
71 |
+
"outline-color", "outline-offset", "outline-style", "outline-width",
|
72 |
+
"overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
|
73 |
+
"padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
|
74 |
+
"page", "page-break-after", "page-break-before", "page-break-inside",
|
75 |
+
"page-policy", "pause", "pause-after", "pause-before", "perspective",
|
76 |
+
"perspective-origin", "pitch", "pitch-range", "play-during", "position",
|
77 |
+
"presentation-level", "punctuation-trim", "quotes", "rendering-intent",
|
78 |
+
"resize", "rest", "rest-after", "rest-before", "richness", "right",
|
79 |
+
"rotation", "rotation-point", "ruby-align", "ruby-overhang",
|
80 |
+
"ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
|
81 |
+
"speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
|
82 |
+
"tab-size", "table-layout", "target", "target-name", "target-new",
|
83 |
+
"target-position", "text-align", "text-align-last", "text-decoration",
|
84 |
+
"text-decoration-color", "text-decoration-line", "text-decoration-skip",
|
85 |
+
"text-decoration-style", "text-emphasis", "text-emphasis-color",
|
86 |
+
"text-emphasis-position", "text-emphasis-style", "text-height",
|
87 |
+
"text-indent", "text-justify", "text-outline", "text-shadow",
|
88 |
+
"text-space-collapse", "text-transform", "text-underline-position",
|
89 |
+
"text-wrap", "top", "transform", "transform-origin", "transform-style",
|
90 |
+
"transition", "transition-delay", "transition-duration",
|
91 |
+
"transition-property", "transition-timing-function", "unicode-bidi",
|
92 |
+
"vertical-align", "visibility", "voice-balance", "voice-duration",
|
93 |
+
"voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
|
94 |
+
"voice-volume", "volume", "white-space", "widows", "width", "word-break",
|
95 |
+
"word-spacing", "word-wrap", "z-index"
|
96 |
+
]);
|
97 |
+
|
98 |
+
var colorKeywords = keySet([
|
99 |
+
"black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia",
|
100 |
+
"green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"
|
101 |
+
]);
|
102 |
+
|
103 |
+
var valueKeywords = keySet([
|
104 |
+
"above", "absolute", "activeborder", "activecaption", "afar",
|
105 |
+
"after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
|
106 |
+
"always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
|
107 |
+
"arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
|
108 |
+
"backwards", "baseline", "below", "bidi-override", "binary", "bengali",
|
109 |
+
"blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
|
110 |
+
"both", "bottom", "break-all", "break-word", "button", "button-bevel",
|
111 |
+
"buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
|
112 |
+
"capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
|
113 |
+
"cell", "center", "checkbox", "circle", "cjk-earthly-branch",
|
114 |
+
"cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
|
115 |
+
"col-resize", "collapse", "compact", "condensed", "contain", "content",
|
116 |
+
"content-box", "context-menu", "continuous", "copy", "cover", "crop",
|
117 |
+
"cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
|
118 |
+
"decimal-leading-zero", "default", "default-button", "destination-atop",
|
119 |
+
"destination-in", "destination-out", "destination-over", "devanagari",
|
120 |
+
"disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
|
121 |
+
"double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
|
122 |
+
"element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
|
123 |
+
"ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
|
124 |
+
"ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
|
125 |
+
"ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
|
126 |
+
"ethiopic-halehame-gez", "ethiopic-halehame-om-et",
|
127 |
+
"ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
|
128 |
+
"ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
|
129 |
+
"ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
|
130 |
+
"extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
|
131 |
+
"forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
|
132 |
+
"gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
|
133 |
+
"help", "hidden", "hide", "higher", "highlight", "highlighttext",
|
134 |
+
"hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
|
135 |
+
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
|
136 |
+
"infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
|
137 |
+
"inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
|
138 |
+
"italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
|
139 |
+
"landscape", "lao", "large", "larger", "left", "level", "lighter",
|
140 |
+
"line-through", "linear", "lines", "list-item", "listbox", "listitem",
|
141 |
+
"local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
|
142 |
+
"lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
|
143 |
+
"lower-roman", "lowercase", "ltr", "malayalam", "match",
|
144 |
+
"media-controls-background", "media-current-time-display",
|
145 |
+
"media-fullscreen-button", "media-mute-button", "media-play-button",
|
146 |
+
"media-return-to-realtime-button", "media-rewind-button",
|
147 |
+
"media-seek-back-button", "media-seek-forward-button", "media-slider",
|
148 |
+
"media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
|
149 |
+
"media-volume-slider-container", "media-volume-sliderthumb", "medium",
|
150 |
+
"menu", "menulist", "menulist-button", "menulist-text",
|
151 |
+
"menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
|
152 |
+
"mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
|
153 |
+
"narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
|
154 |
+
"no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
|
155 |
+
"ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
|
156 |
+
"optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
|
157 |
+
"outside", "overlay", "overline", "padding", "padding-box", "painted",
|
158 |
+
"paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
|
159 |
+
"pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
|
160 |
+
"radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
|
161 |
+
"repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
|
162 |
+
"ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
|
163 |
+
"s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
|
164 |
+
"searchfield-cancel-button", "searchfield-decoration",
|
165 |
+
"searchfield-results-button", "searchfield-results-decoration",
|
166 |
+
"semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
|
167 |
+
"single", "skip-white-space", "slide", "slider-horizontal",
|
168 |
+
"slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
|
169 |
+
"small", "small-caps", "small-caption", "smaller", "solid", "somali",
|
170 |
+
"source-atop", "source-in", "source-out", "source-over", "space", "square",
|
171 |
+
"square-button", "start", "static", "status-bar", "stretch", "stroke",
|
172 |
+
"sub", "subpixel-antialiased", "super", "sw-resize", "table",
|
173 |
+
"table-caption", "table-cell", "table-column", "table-column-group",
|
174 |
+
"table-footer-group", "table-header-group", "table-row", "table-row-group",
|
175 |
+
"telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
|
176 |
+
"thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
|
177 |
+
"threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
|
178 |
+
"tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
|
179 |
+
"transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
|
180 |
+
"upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
|
181 |
+
"upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
|
182 |
+
"vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
|
183 |
+
"visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider",
|
184 |
+
"window", "windowframe", "windowtext", "x-large", "x-small", "xor",
|
185 |
+
"xx-large", "xx-small", "yellow"
|
186 |
+
]);
|
187 |
+
|
188 |
+
function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; }
|
189 |
+
function ret(style, tp) {type = tp; return style;}
|
190 |
+
|
191 |
+
function tokenBase(stream, state) {
|
192 |
+
var ch = stream.next();
|
193 |
+
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
|
194 |
+
else if (ch == "/" && stream.eat("*")) {
|
195 |
+
state.tokenize = tokenCComment;
|
196 |
+
return tokenCComment(stream, state);
|
197 |
+
}
|
198 |
+
else if (ch == "<" && stream.eat("!")) {
|
199 |
+
state.tokenize = tokenSGMLComment;
|
200 |
+
return tokenSGMLComment(stream, state);
|
201 |
+
}
|
202 |
+
else if (ch == "=") ret(null, "compare");
|
203 |
+
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
|
204 |
+
else if (ch == "\"" || ch == "'") {
|
205 |
+
state.tokenize = tokenString(ch);
|
206 |
+
return state.tokenize(stream, state);
|
207 |
+
}
|
208 |
+
else if (ch == "#") {
|
209 |
+
stream.eatWhile(/[\w\\\-]/);
|
210 |
+
return ret("atom", "hash");
|
211 |
+
}
|
212 |
+
else if (ch == "!") {
|
213 |
+
stream.match(/^\s*\w*/);
|
214 |
+
return ret("keyword", "important");
|
215 |
+
}
|
216 |
+
else if (/\d/.test(ch)) {
|
217 |
+
stream.eatWhile(/[\w.%]/);
|
218 |
+
return ret("number", "unit");
|
219 |
+
}
|
220 |
+
else if (ch === "-") {
|
221 |
+
if (/\d/.test(stream.peek())) {
|
222 |
+
stream.eatWhile(/[\w.%]/);
|
223 |
+
return ret("number", "unit");
|
224 |
+
} else if (stream.match(/^[^-]+-/)) {
|
225 |
+
return ret("meta", type);
|
226 |
+
}
|
227 |
+
}
|
228 |
+
else if (/[,+>*\/]/.test(ch)) {
|
229 |
+
return ret(null, "select-op");
|
230 |
+
}
|
231 |
+
else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
|
232 |
+
return ret("qualifier", type);
|
233 |
+
}
|
234 |
+
else if (ch == ":") {
|
235 |
+
return ret("operator", ch);
|
236 |
+
}
|
237 |
+
else if (/[;{}\[\]\(\)]/.test(ch)) {
|
238 |
+
return ret(null, ch);
|
239 |
+
}
|
240 |
+
else if (ch == "u" && stream.match("rl(")) {
|
241 |
+
stream.backUp(1);
|
242 |
+
state.tokenize = tokenParenthesized;
|
243 |
+
return ret("property", "variable");
|
244 |
+
}
|
245 |
+
else {
|
246 |
+
stream.eatWhile(/[\w\\\-]/);
|
247 |
+
return ret("property", "variable");
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
function tokenCComment(stream, state) {
|
252 |
+
var maybeEnd = false, ch;
|
253 |
+
while ((ch = stream.next()) != null) {
|
254 |
+
if (maybeEnd && ch == "/") {
|
255 |
+
state.tokenize = tokenBase;
|
256 |
+
break;
|
257 |
+
}
|
258 |
+
maybeEnd = (ch == "*");
|
259 |
+
}
|
260 |
+
return ret("comment", "comment");
|
261 |
+
}
|
262 |
+
|
263 |
+
function tokenSGMLComment(stream, state) {
|
264 |
+
var dashes = 0, ch;
|
265 |
+
while ((ch = stream.next()) != null) {
|
266 |
+
if (dashes >= 2 && ch == ">") {
|
267 |
+
state.tokenize = tokenBase;
|
268 |
+
break;
|
269 |
+
}
|
270 |
+
dashes = (ch == "-") ? dashes + 1 : 0;
|
271 |
+
}
|
272 |
+
return ret("comment", "comment");
|
273 |
+
}
|
274 |
+
|
275 |
+
function tokenString(quote, nonInclusive) {
|
276 |
+
return function(stream, state) {
|
277 |
+
var escaped = false, ch;
|
278 |
+
while ((ch = stream.next()) != null) {
|
279 |
+
if (ch == quote && !escaped)
|
280 |
+
break;
|
281 |
+
escaped = !escaped && ch == "\\";
|
282 |
+
}
|
283 |
+
if (!escaped) {
|
284 |
+
if (nonInclusive) stream.backUp(1);
|
285 |
+
state.tokenize = tokenBase;
|
286 |
+
}
|
287 |
+
return ret("string", "string");
|
288 |
+
};
|
289 |
+
}
|
290 |
+
|
291 |
+
function tokenParenthesized(stream, state) {
|
292 |
+
stream.next(); // Must be '('
|
293 |
+
if (!stream.match(/\s*[\"\']/, false))
|
294 |
+
state.tokenize = tokenString(")", true);
|
295 |
+
else
|
296 |
+
state.tokenize = tokenBase;
|
297 |
+
return ret(null, "(");
|
298 |
+
}
|
299 |
+
|
300 |
+
return {
|
301 |
+
startState: function(base) {
|
302 |
+
return {tokenize: tokenBase,
|
303 |
+
baseIndent: base || 0,
|
304 |
+
stack: []};
|
305 |
+
},
|
306 |
+
|
307 |
+
token: function(stream, state) {
|
308 |
+
|
309 |
+
// Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
|
310 |
+
//
|
311 |
+
// rule** or **ruleset:
|
312 |
+
// A selector + braces combo, or an at-rule.
|
313 |
+
//
|
314 |
+
// declaration block:
|
315 |
+
// A sequence of declarations.
|
316 |
+
//
|
317 |
+
// declaration:
|
318 |
+
// A property + colon + value combo.
|
319 |
+
//
|
320 |
+
// property value:
|
321 |
+
// The entire value of a property.
|
322 |
+
//
|
323 |
+
// component value:
|
324 |
+
// A single piece of a property value. Like the 5px in
|
325 |
+
// text-shadow: 0 0 5px blue;. Can also refer to things that are
|
326 |
+
// multiple terms, like the 1-4 terms that make up the background-size
|
327 |
+
// portion of the background shorthand.
|
328 |
+
//
|
329 |
+
// term:
|
330 |
+
// The basic unit of author-facing CSS, like a single number (5),
|
331 |
+
// dimension (5px), string ("foo"), or function. Officially defined
|
332 |
+
// by the CSS 2.1 grammar (look for the 'term' production)
|
333 |
+
//
|
334 |
+
//
|
335 |
+
// simple selector:
|
336 |
+
// A single atomic selector, like a type selector, an attr selector, a
|
337 |
+
// class selector, etc.
|
338 |
+
//
|
339 |
+
// compound selector:
|
340 |
+
// One or more simple selectors without a combinator. div.example is
|
341 |
+
// compound, div > .example is not.
|
342 |
+
//
|
343 |
+
// complex selector:
|
344 |
+
// One or more compound selectors chained with combinators.
|
345 |
+
//
|
346 |
+
// combinator:
|
347 |
+
// The parts of selectors that express relationships. There are four
|
348 |
+
// currently - the space (descendant combinator), the greater-than
|
349 |
+
// bracket (child combinator), the plus sign (next sibling combinator),
|
350 |
+
// and the tilda (following sibling combinator).
|
351 |
+
//
|
352 |
+
// sequence of selectors:
|
353 |
+
// One or more of the named type of selector chained with commas.
|
354 |
+
|
355 |
+
if (state.tokenize == tokenBase && stream.eatSpace()) return null;
|
356 |
+
var style = state.tokenize(stream, state);
|
357 |
+
|
358 |
+
// Changing style returned based on context
|
359 |
+
var context = state.stack[state.stack.length-1];
|
360 |
+
if (style == "property") {
|
361 |
+
if (context == "propertyValue"){
|
362 |
+
if (valueKeywords[stream.current()]) {
|
363 |
+
style = "string-2";
|
364 |
+
} else if (colorKeywords[stream.current()]) {
|
365 |
+
style = "keyword";
|
366 |
+
} else {
|
367 |
+
style = "variable-2";
|
368 |
+
}
|
369 |
+
} else if (context == "rule") {
|
370 |
+
if (!propertyKeywords[stream.current()]) {
|
371 |
+
style += " error";
|
372 |
+
}
|
373 |
+
} else if (!context || context == "@media{") {
|
374 |
+
style = "tag";
|
375 |
+
} else if (context == "@media") {
|
376 |
+
if (atMediaTypes[stream.current()]) {
|
377 |
+
style = "attribute"; // Known attribute
|
378 |
+
} else if (/^(only|not)$/i.test(stream.current())) {
|
379 |
+
style = "keyword";
|
380 |
+
} else if (stream.current().toLowerCase() == "and") {
|
381 |
+
style = "error"; // "and" is only allowed in @mediaType
|
382 |
+
} else if (atMediaFeatures[stream.current()]) {
|
383 |
+
style = "error"; // Known property, should be in @mediaType(
|
384 |
+
} else {
|
385 |
+
// Unknown, expecting keyword or attribute, assuming attribute
|
386 |
+
style = "attribute error";
|
387 |
+
}
|
388 |
+
} else if (context == "@mediaType") {
|
389 |
+
if (atMediaTypes[stream.current()]) {
|
390 |
+
style = "attribute";
|
391 |
+
} else if (stream.current().toLowerCase() == "and") {
|
392 |
+
style = "operator";
|
393 |
+
} else if (/^(only|not)$/i.test(stream.current())) {
|
394 |
+
style = "error"; // Only allowed in @media
|
395 |
+
} else if (atMediaFeatures[stream.current()]) {
|
396 |
+
style = "error"; // Known property, should be in parentheses
|
397 |
+
} else {
|
398 |
+
// Unknown attribute or property, but expecting property (preceded
|
399 |
+
// by "and"). Should be in parentheses
|
400 |
+
style = "error";
|
401 |
+
}
|
402 |
+
} else if (context == "@mediaType(") {
|
403 |
+
if (propertyKeywords[stream.current()]) {
|
404 |
+
// do nothing, remains "property"
|
405 |
+
} else if (atMediaTypes[stream.current()]) {
|
406 |
+
style = "error"; // Known property, should be in parentheses
|
407 |
+
} else if (stream.current().toLowerCase() == "and") {
|
408 |
+
style = "operator";
|
409 |
+
} else if (/^(only|not)$/i.test(stream.current())) {
|
410 |
+
style = "error"; // Only allowed in @media
|
411 |
+
} else {
|
412 |
+
style += " error";
|
413 |
+
}
|
414 |
+
} else {
|
415 |
+
style = "error";
|
416 |
+
}
|
417 |
+
} else if (style == "atom") {
|
418 |
+
if(!context || context == "@media{") {
|
419 |
+
style = "builtin";
|
420 |
+
} else if (context == "propertyValue") {
|
421 |
+
if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
|
422 |
+
style += " error";
|
423 |
+
}
|
424 |
+
} else {
|
425 |
+
style = "error";
|
426 |
+
}
|
427 |
+
} else if (context == "@media" && type == "{") {
|
428 |
+
style = "error";
|
429 |
+
}
|
430 |
+
|
431 |
+
// Push/pop context stack
|
432 |
+
if (type == "{") {
|
433 |
+
if (context == "@media" || context == "@mediaType") {
|
434 |
+
state.stack.pop();
|
435 |
+
state.stack[state.stack.length-1] = "@media{";
|
436 |
+
}
|
437 |
+
else state.stack.push("rule");
|
438 |
+
}
|
439 |
+
else if (type == "}") {
|
440 |
+
state.stack.pop();
|
441 |
+
if (context == "propertyValue") state.stack.pop();
|
442 |
+
}
|
443 |
+
else if (type == "@media") state.stack.push("@media");
|
444 |
+
else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
|
445 |
+
state.stack.push("@mediaType");
|
446 |
+
else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
|
447 |
+
else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType(");
|
448 |
+
else if (context == "@mediaType(" && type == ")") state.stack.pop();
|
449 |
+
else if (context == "rule" && type == ":") state.stack.push("propertyValue");
|
450 |
+
else if (context == "propertyValue" && type == ";") state.stack.pop();
|
451 |
+
return style;
|
452 |
+
},
|
453 |
+
|
454 |
+
indent: function(state, textAfter) {
|
455 |
+
var n = state.stack.length;
|
456 |
+
if (/^\}/.test(textAfter))
|
457 |
+
n -= state.stack[state.stack.length-1] == "propertyValue" ? 2 : 1;
|
458 |
+
return state.baseIndent + n * indentUnit;
|
459 |
+
},
|
460 |
+
|
461 |
+
electricChars: "}"
|
462 |
+
};
|
463 |
+
});
|
464 |
+
|
465 |
+
CodeMirror.defineMIME("text/css", "css");
|
assets/mode/htmlmixed.js
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CodeMirror.defineMode("htmlmixed", function(config) {
|
2 |
+
var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
|
3 |
+
var jsMode = CodeMirror.getMode(config, "javascript");
|
4 |
+
var cssMode = CodeMirror.getMode(config, "css");
|
5 |
+
|
6 |
+
function html(stream, state) {
|
7 |
+
var style = htmlMode.token(stream, state.htmlState);
|
8 |
+
if (/(?:^|\s)tag(?:\s|$)/.test(style) && stream.current() == ">" && state.htmlState.context) {
|
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;
|
19 |
+
}
|
20 |
+
function maybeBackup(stream, pat, style) {
|
21 |
+
var cur = stream.current();
|
22 |
+
var close = cur.search(pat), m;
|
23 |
+
if (close > -1) stream.backUp(cur.length - close);
|
24 |
+
else if (m = cur.match(/<\/?$/)) {
|
25 |
+
stream.backUp(cur.length);
|
26 |
+
if (!stream.match(pat, false)) stream.match(cur[0]);
|
27 |
+
}
|
28 |
+
return style;
|
29 |
+
}
|
30 |
+
function javascript(stream, state) {
|
31 |
+
if (stream.match(/^<\/\s*script\s*>/i, false)) {
|
32 |
+
state.token = html;
|
33 |
+
state.localState = null;
|
34 |
+
return html(stream, state);
|
35 |
+
}
|
36 |
+
return maybeBackup(stream, /<\/\s*script\s*>/,
|
37 |
+
jsMode.token(stream, state.localState));
|
38 |
+
}
|
39 |
+
function css(stream, state) {
|
40 |
+
if (stream.match(/^<\/\s*style\s*>/i, false)) {
|
41 |
+
state.token = html;
|
42 |
+
state.localState = null;
|
43 |
+
return html(stream, state);
|
44 |
+
}
|
45 |
+
return maybeBackup(stream, /<\/\s*style\s*>/,
|
46 |
+
cssMode.token(stream, state.localState));
|
47 |
+
}
|
48 |
+
|
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) {
|
63 |
+
return state.token(stream, state);
|
64 |
+
},
|
65 |
+
|
66 |
+
indent: function(state, textAfter) {
|
67 |
+
if (state.token == html || /^\s*<\//.test(textAfter))
|
68 |
+
return htmlMode.indent(state.htmlState, textAfter);
|
69 |
+
else if (state.token == javascript)
|
70 |
+
return jsMode.indent(state.localState, textAfter);
|
71 |
+
else
|
72 |
+
return cssMode.indent(state.localState, textAfter);
|
73 |
+
},
|
74 |
+
|
75 |
+
electricChars: "/{}:",
|
76 |
+
|
77 |
+
innerMode: function(state) {
|
78 |
+
var mode = state.token == html ? htmlMode : state.token == javascript ? jsMode : cssMode;
|
79 |
+
return {state: state.localState || state.htmlState, mode: mode};
|
80 |
+
}
|
81 |
+
};
|
82 |
+
}, "xml", "javascript", "css");
|
83 |
+
|
84 |
+
CodeMirror.defineMIME("text/html", "htmlmixed");
|
{js → assets/mode}/javascript.js
RENAMED
@@ -1,6 +1,9 @@
|
|
|
|
|
|
1 |
CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
2 |
var indentUnit = config.indentUnit;
|
3 |
var jsonMode = parserConfig.json;
|
|
|
4 |
|
5 |
// Tokenizer
|
6 |
|
@@ -8,7 +11,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
8 |
function kw(type) {return {type: type, style: "keyword"};}
|
9 |
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
|
10 |
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
|
11 |
-
|
|
|
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"),
|
@@ -17,6 +21,35 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
17 |
"in": operator, "typeof": operator, "instanceof": operator,
|
18 |
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
|
19 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
}();
|
21 |
|
22 |
var isOperatorChar = /[+\-*&%=<>!?|]/;
|
@@ -66,7 +99,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
66 |
stream.skipToEnd();
|
67 |
return ret("comment", "comment");
|
68 |
}
|
69 |
-
else if (state.
|
|
|
70 |
nextUntilUnescaped(stream, "/");
|
71 |
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
|
72 |
return ret("regexp", "string-2");
|
@@ -87,7 +121,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
87 |
else {
|
88 |
stream.eatWhile(/[\w\$_]/);
|
89 |
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
|
90 |
-
return (known && state.
|
91 |
ret("variable", "variable", word);
|
92 |
}
|
93 |
}
|
@@ -175,8 +209,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
175 |
|
176 |
var defaultVars = {name: "this", next: {name: "arguments"}};
|
177 |
function pushcontext() {
|
178 |
-
if (!cx.state.context) cx.state.localVars = defaultVars;
|
179 |
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
|
|
|
180 |
}
|
181 |
function popcontext() {
|
182 |
cx.state.localVars = cx.state.context.vars;
|
@@ -275,21 +309,32 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
275 |
if (type == "}") return cont();
|
276 |
return pass(statement, block);
|
277 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
278 |
function vardef1(type, value) {
|
279 |
-
if (type == "variable"){
|
280 |
-
|
|
|
|
|
|
|
281 |
}
|
282 |
function vardef2(type, value) {
|
283 |
if (value == "=") return cont(expression, vardef2);
|
284 |
if (type == ",") return cont(vardef1);
|
285 |
}
|
286 |
function forspec1(type) {
|
287 |
-
if (type == "var") return cont(vardef1, forspec2);
|
288 |
-
if (type == ";") return
|
289 |
if (type == "variable") return cont(formaybein);
|
290 |
-
return
|
291 |
}
|
292 |
-
function formaybein(
|
293 |
if (value == "in") return cont(expression);
|
294 |
return cont(maybeoperator, forspec2);
|
295 |
}
|
@@ -306,7 +351,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
306 |
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
|
307 |
}
|
308 |
function funarg(type, value) {
|
309 |
-
if (type == "variable") {register(value); return cont();}
|
310 |
}
|
311 |
|
312 |
// Interface
|
@@ -315,8 +360,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
315 |
startState: function(basecolumn) {
|
316 |
return {
|
317 |
tokenize: jsTokenBase,
|
318 |
-
|
319 |
-
kwAllowed: true,
|
320 |
cc: [],
|
321 |
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
|
322 |
localVars: parserConfig.localVars,
|
@@ -334,28 +378,34 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|
334 |
if (stream.eatSpace()) return null;
|
335 |
var style = state.tokenize(stream, state);
|
336 |
if (type == "comment") return style;
|
337 |
-
state.
|
338 |
-
state.kwAllowed = type != '.';
|
339 |
return parseJS(state, style, type, content, stream);
|
340 |
},
|
341 |
|
342 |
indent: function(state, textAfter) {
|
|
|
343 |
if (state.tokenize != jsTokenBase) return 0;
|
344 |
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
|
345 |
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
|
346 |
var type = lexical.type, closing = firstChar == type;
|
347 |
-
if (type == "vardef") return lexical.indented + 4;
|
348 |
else if (type == "form" && firstChar == "{") return lexical.indented;
|
349 |
-
else if (type == "
|
|
|
|
|
350 |
else if (lexical.info == "switch" && !closing)
|
351 |
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
|
352 |
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
|
353 |
else return lexical.indented + (closing ? 0 : indentUnit);
|
354 |
},
|
355 |
|
356 |
-
electricChars: ":{}"
|
|
|
|
|
357 |
};
|
358 |
});
|
359 |
|
360 |
CodeMirror.defineMIME("text/javascript", "javascript");
|
361 |
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
|
|
|
|
1 |
+
// TODO actually recognize syntax of TypeScript constructs
|
2 |
+
|
3 |
CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
4 |
var indentUnit = config.indentUnit;
|
5 |
var jsonMode = parserConfig.json;
|
6 |
+
var isTS = parserConfig.typescript;
|
7 |
|
8 |
// Tokenizer
|
9 |
|
11 |
function kw(type) {return {type: type, style: "keyword"};}
|
12 |
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
|
13 |
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
|
14 |
+
|
15 |
+
var jsKeywords = {
|
16 |
"if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
17 |
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
|
18 |
"var": kw("var"), "const": kw("var"), "let": kw("var"),
|
21 |
"in": operator, "typeof": operator, "instanceof": operator,
|
22 |
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
|
23 |
};
|
24 |
+
|
25 |
+
// Extend the 'normal' keywords with the TypeScript language extensions
|
26 |
+
if (isTS) {
|
27 |
+
var type = {type: "variable", style: "variable-3"};
|
28 |
+
var tsKeywords = {
|
29 |
+
// object-like things
|
30 |
+
"interface": kw("interface"),
|
31 |
+
"class": kw("class"),
|
32 |
+
"extends": kw("extends"),
|
33 |
+
"constructor": kw("constructor"),
|
34 |
+
|
35 |
+
// scope modifiers
|
36 |
+
"public": kw("public"),
|
37 |
+
"private": kw("private"),
|
38 |
+
"protected": kw("protected"),
|
39 |
+
"static": kw("static"),
|
40 |
+
|
41 |
+
"super": kw("super"),
|
42 |
+
|
43 |
+
// types
|
44 |
+
"string": type, "number": type, "bool": type, "any": type
|
45 |
+
};
|
46 |
+
|
47 |
+
for (var attr in tsKeywords) {
|
48 |
+
jsKeywords[attr] = tsKeywords[attr];
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
return jsKeywords;
|
53 |
}();
|
54 |
|
55 |
var isOperatorChar = /[+\-*&%=<>!?|]/;
|
99 |
stream.skipToEnd();
|
100 |
return ret("comment", "comment");
|
101 |
}
|
102 |
+
else if (state.lastType == "operator" || state.lastType == "keyword c" ||
|
103 |
+
/^[\[{}\(,;:]$/.test(state.lastType)) {
|
104 |
nextUntilUnescaped(stream, "/");
|
105 |
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
|
106 |
return ret("regexp", "string-2");
|
121 |
else {
|
122 |
stream.eatWhile(/[\w\$_]/);
|
123 |
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
|
124 |
+
return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
|
125 |
ret("variable", "variable", word);
|
126 |
}
|
127 |
}
|
209 |
|
210 |
var defaultVars = {name: "this", next: {name: "arguments"}};
|
211 |
function pushcontext() {
|
|
|
212 |
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
|
213 |
+
cx.state.localVars = defaultVars;
|
214 |
}
|
215 |
function popcontext() {
|
216 |
cx.state.localVars = cx.state.context.vars;
|
309 |
if (type == "}") return cont();
|
310 |
return pass(statement, block);
|
311 |
}
|
312 |
+
function maybetype(type) {
|
313 |
+
if (type == ":") return cont(typedef);
|
314 |
+
return pass();
|
315 |
+
}
|
316 |
+
function typedef(type) {
|
317 |
+
if (type == "variable"){cx.marked = "variable-3"; return cont();}
|
318 |
+
return pass();
|
319 |
+
}
|
320 |
function vardef1(type, value) {
|
321 |
+
if (type == "variable") {
|
322 |
+
register(value);
|
323 |
+
return isTS ? cont(maybetype, vardef2) : cont(vardef2);
|
324 |
+
}
|
325 |
+
return pass();
|
326 |
}
|
327 |
function vardef2(type, value) {
|
328 |
if (value == "=") return cont(expression, vardef2);
|
329 |
if (type == ",") return cont(vardef1);
|
330 |
}
|
331 |
function forspec1(type) {
|
332 |
+
if (type == "var") return cont(vardef1, expect(";"), forspec2);
|
333 |
+
if (type == ";") return cont(forspec2);
|
334 |
if (type == "variable") return cont(formaybein);
|
335 |
+
return cont(forspec2);
|
336 |
}
|
337 |
+
function formaybein(_type, value) {
|
338 |
if (value == "in") return cont(expression);
|
339 |
return cont(maybeoperator, forspec2);
|
340 |
}
|
351 |
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
|
352 |
}
|
353 |
function funarg(type, value) {
|
354 |
+
if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
|
355 |
}
|
356 |
|
357 |
// Interface
|
360 |
startState: function(basecolumn) {
|
361 |
return {
|
362 |
tokenize: jsTokenBase,
|
363 |
+
lastType: null,
|
|
|
364 |
cc: [],
|
365 |
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
|
366 |
localVars: parserConfig.localVars,
|
378 |
if (stream.eatSpace()) return null;
|
379 |
var style = state.tokenize(stream, state);
|
380 |
if (type == "comment") return style;
|
381 |
+
state.lastType = type;
|
|
|
382 |
return parseJS(state, style, type, content, stream);
|
383 |
},
|
384 |
|
385 |
indent: function(state, textAfter) {
|
386 |
+
if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
|
387 |
if (state.tokenize != jsTokenBase) return 0;
|
388 |
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
|
389 |
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
|
390 |
var type = lexical.type, closing = firstChar == type;
|
391 |
+
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
|
392 |
else if (type == "form" && firstChar == "{") return lexical.indented;
|
393 |
+
else if (type == "form") return lexical.indented + indentUnit;
|
394 |
+
else if (type == "stat")
|
395 |
+
return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
|
396 |
else if (lexical.info == "switch" && !closing)
|
397 |
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
|
398 |
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
|
399 |
else return lexical.indented + (closing ? 0 : indentUnit);
|
400 |
},
|
401 |
|
402 |
+
electricChars: ":{}",
|
403 |
+
|
404 |
+
jsonMode: jsonMode
|
405 |
};
|
406 |
});
|
407 |
|
408 |
CodeMirror.defineMIME("text/javascript", "javascript");
|
409 |
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
|
410 |
+
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
|
411 |
+
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
|
assets/mode/php.js
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
function keywords(str) {
|
3 |
+
var obj = {}, words = str.split(" ");
|
4 |
+
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
|
5 |
+
return obj;
|
6 |
+
}
|
7 |
+
function heredoc(delim) {
|
8 |
+
return function(stream, state) {
|
9 |
+
if (stream.match(delim)) state.tokenize = null;
|
10 |
+
else stream.skipToEnd();
|
11 |
+
return "string";
|
12 |
+
};
|
13 |
+
}
|
14 |
+
var phpConfig = {
|
15 |
+
name: "clike",
|
16 |
+
keywords: keywords("abstract and array as break case catch class clone const continue declare default " +
|
17 |
+
"do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " +
|
18 |
+
"for foreach function global goto if implements interface instanceof namespace " +
|
19 |
+
"new or private protected public static switch throw trait try use var while xor " +
|
20 |
+
"die echo empty exit eval include include_once isset list require require_once return " +
|
21 |
+
"print unset __halt_compiler self static parent"),
|
22 |
+
blockKeywords: keywords("catch do else elseif for foreach if switch try while"),
|
23 |
+
atoms: keywords("true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__"),
|
24 |
+
builtin: keywords("func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport echo print global static exit array empty eval isset unset die include require include_once require_once"),
|
25 |
+
multiLineStrings: true,
|
26 |
+
hooks: {
|
27 |
+
"$": function(stream) {
|
28 |
+
stream.eatWhile(/[\w\$_]/);
|
29 |
+
return "variable-2";
|
30 |
+
},
|
31 |
+
"<": function(stream, state) {
|
32 |
+
if (stream.match(/<</)) {
|
33 |
+
stream.eatWhile(/[\w\.]/);
|
34 |
+
state.tokenize = heredoc(stream.current().slice(3));
|
35 |
+
return state.tokenize(stream, state);
|
36 |
+
}
|
37 |
+
return false;
|
38 |
+
},
|
39 |
+
"#": function(stream) {
|
40 |
+
while (!stream.eol() && !stream.match("?>", false)) stream.next();
|
41 |
+
return "comment";
|
42 |
+
},
|
43 |
+
"/": function(stream) {
|
44 |
+
if (stream.eat("/")) {
|
45 |
+
while (!stream.eol() && !stream.match("?>", false)) stream.next();
|
46 |
+
return "comment";
|
47 |
+
}
|
48 |
+
return false;
|
49 |
+
}
|
50 |
+
}
|
51 |
+
};
|
52 |
+
|
53 |
+
CodeMirror.defineMode("php", function(config, parserConfig) {
|
54 |
+
var htmlMode = CodeMirror.getMode(config, "text/html");
|
55 |
+
var phpMode = CodeMirror.getMode(config, phpConfig);
|
56 |
+
|
57 |
+
function dispatch(stream, state) {
|
58 |
+
var isPHP = state.curMode == phpMode;
|
59 |
+
if (stream.sol() && state.pending != '"') state.pending = null;
|
60 |
+
if (!isPHP) {
|
61 |
+
if (stream.match(/^<\?\w*/)) {
|
62 |
+
state.curMode = phpMode;
|
63 |
+
state.curState = state.php;
|
64 |
+
return "meta";
|
65 |
+
}
|
66 |
+
if (state.pending == '"') {
|
67 |
+
while (!stream.eol() && stream.next() != '"') {}
|
68 |
+
var style = "string";
|
69 |
+
} else if (state.pending && stream.pos < state.pending.end) {
|
70 |
+
stream.pos = state.pending.end;
|
71 |
+
var style = state.pending.style;
|
72 |
+
} else {
|
73 |
+
var style = htmlMode.token(stream, state.curState);
|
74 |
+
}
|
75 |
+
state.pending = null;
|
76 |
+
var cur = stream.current(), openPHP = cur.search(/<\?/);
|
77 |
+
if (openPHP != -1) {
|
78 |
+
if (style == "string" && /\"$/.test(cur) && !/\?>/.test(cur)) state.pending = '"';
|
79 |
+
else state.pending = {end: stream.pos, style: style};
|
80 |
+
stream.backUp(cur.length - openPHP);
|
81 |
+
}
|
82 |
+
return style;
|
83 |
+
} else if (isPHP && state.php.tokenize == null && stream.match("?>")) {
|
84 |
+
state.curMode = htmlMode;
|
85 |
+
state.curState = state.html;
|
86 |
+
return "meta";
|
87 |
+
} else {
|
88 |
+
return phpMode.token(stream, state.curState);
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
return {
|
93 |
+
startState: function() {
|
94 |
+
var html = CodeMirror.startState(htmlMode), php = CodeMirror.startState(phpMode);
|
95 |
+
return {html: html,
|
96 |
+
php: php,
|
97 |
+
curMode: parserConfig.startOpen ? phpMode : htmlMode,
|
98 |
+
curState: parserConfig.startOpen ? php : html,
|
99 |
+
pending: null};
|
100 |
+
},
|
101 |
+
|
102 |
+
copyState: function(state) {
|
103 |
+
var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
|
104 |
+
php = state.php, phpNew = CodeMirror.copyState(phpMode, php), cur;
|
105 |
+
if (state.curMode == htmlMode) cur = htmlNew;
|
106 |
+
else cur = phpNew;
|
107 |
+
return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,
|
108 |
+
pending: state.pending};
|
109 |
+
},
|
110 |
+
|
111 |
+
token: dispatch,
|
112 |
+
|
113 |
+
indent: function(state, textAfter) {
|
114 |
+
if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
|
115 |
+
(state.curMode == phpMode && /^\?>/.test(textAfter)))
|
116 |
+
return htmlMode.indent(state.html, textAfter);
|
117 |
+
return state.curMode.indent(state.curState, textAfter);
|
118 |
+
},
|
119 |
+
|
120 |
+
electricChars: "/{}:",
|
121 |
+
|
122 |
+
innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
|
123 |
+
};
|
124 |
+
}, "htmlmixed");
|
125 |
+
|
126 |
+
CodeMirror.defineMIME("application/x-httpd-php", "php");
|
127 |
+
CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
|
128 |
+
CodeMirror.defineMIME("text/x-php", phpConfig);
|
129 |
+
})();
|
{js → assets/mode}/xml.js
RENAMED
@@ -70,11 +70,12 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
70 |
return "meta";
|
71 |
}
|
72 |
else {
|
73 |
-
|
74 |
-
stream.eatSpace();
|
75 |
tagName = "";
|
76 |
var c;
|
77 |
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
|
|
|
|
|
78 |
state.tokenize = inTag;
|
79 |
return "tag";
|
80 |
}
|
@@ -114,7 +115,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
114 |
return state.tokenize(stream, state);
|
115 |
}
|
116 |
else {
|
117 |
-
stream.eatWhile(/[^\s\u00a0=<>\"\'
|
118 |
return "word";
|
119 |
}
|
120 |
}
|
@@ -210,14 +211,16 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
210 |
}
|
211 |
function endtag(startOfLine) {
|
212 |
return function(type) {
|
|
|
|
|
213 |
if (type == "selfcloseTag" ||
|
214 |
-
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(
|
215 |
-
maybePopContext(
|
216 |
return cont();
|
217 |
}
|
218 |
if (type == "endTag") {
|
219 |
-
maybePopContext(
|
220 |
-
pushContext(
|
221 |
return cont();
|
222 |
}
|
223 |
return cont();
|
@@ -255,6 +258,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
255 |
function attribute(type) {
|
256 |
if (type == "equals") return cont(attvalue, attributes);
|
257 |
if (!Kludges.allowMissing) setStyle = "error";
|
|
|
258 |
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
|
259 |
}
|
260 |
function attvalue(type) {
|
@@ -308,15 +312,9 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
308 |
else return 0;
|
309 |
},
|
310 |
|
311 |
-
|
312 |
-
if (a.indented != b.indented || a.tokenize != b.tokenize) return false;
|
313 |
-
for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
|
314 |
-
if (!ca || !cb) return ca == cb;
|
315 |
-
if (ca.tagName != cb.tagName || ca.indent != cb.indent) return false;
|
316 |
-
}
|
317 |
-
},
|
318 |
|
319 |
-
|
320 |
};
|
321 |
});
|
322 |
|
70 |
return "meta";
|
71 |
}
|
72 |
else {
|
73 |
+
var isClose = stream.eat("/");
|
|
|
74 |
tagName = "";
|
75 |
var c;
|
76 |
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
|
77 |
+
if (!tagName) return "error";
|
78 |
+
type = isClose ? "closeTag" : "openTag";
|
79 |
state.tokenize = inTag;
|
80 |
return "tag";
|
81 |
}
|
115 |
return state.tokenize(stream, state);
|
116 |
}
|
117 |
else {
|
118 |
+
stream.eatWhile(/[^\s\u00a0=<>\"\']/);
|
119 |
return "word";
|
120 |
}
|
121 |
}
|
211 |
}
|
212 |
function endtag(startOfLine) {
|
213 |
return function(type) {
|
214 |
+
var tagName = curState.tagName;
|
215 |
+
curState.tagName = null;
|
216 |
if (type == "selfcloseTag" ||
|
217 |
+
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
|
218 |
+
maybePopContext(tagName.toLowerCase());
|
219 |
return cont();
|
220 |
}
|
221 |
if (type == "endTag") {
|
222 |
+
maybePopContext(tagName.toLowerCase());
|
223 |
+
pushContext(tagName, startOfLine);
|
224 |
return cont();
|
225 |
}
|
226 |
return cont();
|
258 |
function attribute(type) {
|
259 |
if (type == "equals") return cont(attvalue, attributes);
|
260 |
if (!Kludges.allowMissing) setStyle = "error";
|
261 |
+
else if (type == "word") setStyle = "attribute";
|
262 |
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
|
263 |
}
|
264 |
function attvalue(type) {
|
312 |
else return 0;
|
313 |
},
|
314 |
|
315 |
+
electricChars: "/",
|
|
|
|
|
|
|
|
|
|
|
|
|
316 |
|
317 |
+
configuration: parserConfig.htmlMode ? "html" : "xml"
|
318 |
};
|
319 |
});
|
320 |
|
css/style.css → assets/table.css
RENAMED
@@ -1,9 +1,3 @@
|
|
1 |
-
#icon-snippets.icon32 {
|
2 |
-
background: url('../images/icon32.png') no-repeat scroll transparent;
|
3 |
-
}
|
4 |
-
|
5 |
-
/* Snippets > Manage Snippets */
|
6 |
-
|
7 |
.snippets .inactive a {
|
8 |
color: #557799;
|
9 |
}
|
@@ -14,7 +8,7 @@
|
|
14 |
color: #d54e21;
|
15 |
}
|
16 |
|
17 |
-
.snippets .delete a{
|
18 |
color: #21759b;
|
19 |
}
|
20 |
|
@@ -38,23 +32,6 @@
|
|
38 |
white-space: nowrap; /* prevents wrapping of snippet title */
|
39 |
}
|
40 |
|
41 |
-
#wpbody-content .snippets .manage-column.column-id.sortable{
|
42 |
min-width: 44px;
|
43 |
-
}
|
44 |
-
|
45 |
-
/* Snippets > Add New */
|
46 |
-
|
47 |
-
.CodeMirror {
|
48 |
-
border: 1px solid #ccc;
|
49 |
-
-o-border-radius: 3px;
|
50 |
-
-moz-border-radius: 3px;
|
51 |
-
-webkit-border-radius: 3px;
|
52 |
-
border-radius: 3px;
|
53 |
-
}
|
54 |
-
|
55 |
-
.CodeMirror-scroll {
|
56 |
-
height: auto;
|
57 |
-
overflow-y: hidden;
|
58 |
-
overflow-x: auto;
|
59 |
-
min-height: 242px;
|
60 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
.snippets .inactive a {
|
2 |
color: #557799;
|
3 |
}
|
8 |
color: #d54e21;
|
9 |
}
|
10 |
|
11 |
+
.snippets .delete a {
|
12 |
color: #21759b;
|
13 |
}
|
14 |
|
32 |
white-space: nowrap; /* prevents wrapping of snippet title */
|
33 |
}
|
34 |
|
35 |
+
#wpbody-content .snippets .manage-column.column-id.sortable {
|
36 |
min-width: 44px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
}
|
{css → assets/util}/dialog.css
RENAMED
@@ -1,18 +1,23 @@
|
|
1 |
.CodeMirror-dialog {
|
2 |
-
position: relative;
|
3 |
-
}
|
4 |
-
|
5 |
-
.CodeMirror-dialog > div {
|
6 |
position: absolute;
|
7 |
-
|
8 |
background: white;
|
9 |
-
border-bottom: 1px solid #eee;
|
10 |
z-index: 15;
|
11 |
padding: .1em .8em;
|
12 |
overflow: hidden;
|
13 |
color: #333;
|
14 |
}
|
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
.CodeMirror-dialog input {
|
17 |
border: none;
|
18 |
outline: none;
|
@@ -24,4 +29,4 @@
|
|
24 |
|
25 |
.CodeMirror-dialog button {
|
26 |
font-size: 70%;
|
27 |
-
}
|
1 |
.CodeMirror-dialog {
|
|
|
|
|
|
|
|
|
2 |
position: absolute;
|
3 |
+
left: 0; right: 0;
|
4 |
background: white;
|
|
|
5 |
z-index: 15;
|
6 |
padding: .1em .8em;
|
7 |
overflow: hidden;
|
8 |
color: #333;
|
9 |
}
|
10 |
|
11 |
+
.CodeMirror-dialog-top {
|
12 |
+
border-bottom: 1px solid #eee;
|
13 |
+
top: 0;
|
14 |
+
}
|
15 |
+
|
16 |
+
.CodeMirror-dialog-bottom {
|
17 |
+
border-top: 1px solid #eee;
|
18 |
+
bottom: 0;
|
19 |
+
}
|
20 |
+
|
21 |
.CodeMirror-dialog input {
|
22 |
border: none;
|
23 |
outline: none;
|
29 |
|
30 |
.CodeMirror-dialog button {
|
31 |
font-size: 70%;
|
32 |
+
}
|
{js → assets/util}/dialog.js
RENAMED
@@ -1,16 +1,21 @@
|
|
1 |
// Open simple dialogs on top of an editor. Relies on dialog.css.
|
2 |
|
3 |
(function() {
|
4 |
-
function dialogDiv(cm, template) {
|
5 |
var wrap = cm.getWrapperElement();
|
6 |
-
var dialog
|
7 |
-
dialog
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
9 |
return dialog;
|
10 |
}
|
11 |
|
12 |
-
CodeMirror.defineExtension("openDialog", function(template, callback) {
|
13 |
-
var dialog = dialogDiv(this, template);
|
14 |
var closed = false, me = this;
|
15 |
function close() {
|
16 |
if (closed) return;
|
@@ -19,7 +24,7 @@
|
|
19 |
}
|
20 |
var inp = dialog.getElementsByTagName("input")[0], button;
|
21 |
if (inp) {
|
22 |
-
CodeMirror.
|
23 |
if (e.keyCode == 13 || e.keyCode == 27) {
|
24 |
CodeMirror.e_stop(e);
|
25 |
close();
|
@@ -28,20 +33,20 @@
|
|
28 |
}
|
29 |
});
|
30 |
inp.focus();
|
31 |
-
CodeMirror.
|
32 |
} else if (button = dialog.getElementsByTagName("button")[0]) {
|
33 |
-
CodeMirror.
|
34 |
close();
|
35 |
me.focus();
|
36 |
});
|
37 |
button.focus();
|
38 |
-
CodeMirror.
|
39 |
}
|
40 |
return close;
|
41 |
});
|
42 |
|
43 |
-
CodeMirror.defineExtension("openConfirm", function(template, callbacks) {
|
44 |
-
var dialog = dialogDiv(this, template);
|
45 |
var buttons = dialog.getElementsByTagName("button");
|
46 |
var closed = false, me = this, blurring = 1;
|
47 |
function close() {
|
@@ -54,17 +59,17 @@
|
|
54 |
for (var i = 0; i < buttons.length; ++i) {
|
55 |
var b = buttons[i];
|
56 |
(function(callback) {
|
57 |
-
CodeMirror.
|
58 |
CodeMirror.e_preventDefault(e);
|
59 |
close();
|
60 |
if (callback) callback(me);
|
61 |
});
|
62 |
})(callbacks[i]);
|
63 |
-
CodeMirror.
|
64 |
--blurring;
|
65 |
setTimeout(function() { if (blurring <= 0) close(); }, 200);
|
66 |
});
|
67 |
-
CodeMirror.
|
68 |
}
|
69 |
});
|
70 |
-
})();
|
1 |
// Open simple dialogs on top of an editor. Relies on dialog.css.
|
2 |
|
3 |
(function() {
|
4 |
+
function dialogDiv(cm, template, bottom) {
|
5 |
var wrap = cm.getWrapperElement();
|
6 |
+
var dialog;
|
7 |
+
dialog = wrap.appendChild(document.createElement("div"));
|
8 |
+
if (bottom) {
|
9 |
+
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
|
10 |
+
} else {
|
11 |
+
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
|
12 |
+
}
|
13 |
+
dialog.innerHTML = template;
|
14 |
return dialog;
|
15 |
}
|
16 |
|
17 |
+
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
|
18 |
+
var dialog = dialogDiv(this, template, options && options.bottom);
|
19 |
var closed = false, me = this;
|
20 |
function close() {
|
21 |
if (closed) return;
|
24 |
}
|
25 |
var inp = dialog.getElementsByTagName("input")[0], button;
|
26 |
if (inp) {
|
27 |
+
CodeMirror.on(inp, "keydown", function(e) {
|
28 |
if (e.keyCode == 13 || e.keyCode == 27) {
|
29 |
CodeMirror.e_stop(e);
|
30 |
close();
|
33 |
}
|
34 |
});
|
35 |
inp.focus();
|
36 |
+
CodeMirror.on(inp, "blur", close);
|
37 |
} else if (button = dialog.getElementsByTagName("button")[0]) {
|
38 |
+
CodeMirror.on(button, "click", function() {
|
39 |
close();
|
40 |
me.focus();
|
41 |
});
|
42 |
button.focus();
|
43 |
+
CodeMirror.on(button, "blur", close);
|
44 |
}
|
45 |
return close;
|
46 |
});
|
47 |
|
48 |
+
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
|
49 |
+
var dialog = dialogDiv(this, template, options && options.bottom);
|
50 |
var buttons = dialog.getElementsByTagName("button");
|
51 |
var closed = false, me = this, blurring = 1;
|
52 |
function close() {
|
59 |
for (var i = 0; i < buttons.length; ++i) {
|
60 |
var b = buttons[i];
|
61 |
(function(callback) {
|
62 |
+
CodeMirror.on(b, "click", function(e) {
|
63 |
CodeMirror.e_preventDefault(e);
|
64 |
close();
|
65 |
if (callback) callback(me);
|
66 |
});
|
67 |
})(callbacks[i]);
|
68 |
+
CodeMirror.on(b, "blur", function() {
|
69 |
--blurring;
|
70 |
setTimeout(function() { if (blurring <= 0) close(); }, 200);
|
71 |
});
|
72 |
+
CodeMirror.on(b, "focus", function() { ++blurring; });
|
73 |
}
|
74 |
});
|
75 |
+
})();
|
assets/util/matchbrackets.js
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function() {
|
2 |
+
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
|
3 |
+
function findMatchingBracket(cm) {
|
4 |
+
var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
|
5 |
+
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
|
6 |
+
if (!match) return null;
|
7 |
+
var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
|
8 |
+
var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).type;
|
9 |
+
|
10 |
+
var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
|
11 |
+
function scan(line, lineNo, start) {
|
12 |
+
if (!line.text) return;
|
13 |
+
var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
|
14 |
+
if (start != null) pos = start + d;
|
15 |
+
for (; pos != end; pos += d) {
|
16 |
+
var ch = line.text.charAt(pos);
|
17 |
+
if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).type == style) {
|
18 |
+
var match = matching[ch];
|
19 |
+
if (match.charAt(1) == ">" == forward) stack.push(ch);
|
20 |
+
else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
|
21 |
+
else if (!stack.length) return {pos: pos, match: true};
|
22 |
+
}
|
23 |
+
}
|
24 |
+
}
|
25 |
+
for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
|
26 |
+
if (i == cur.line) found = scan(line, i, pos);
|
27 |
+
else found = scan(cm.getLineHandle(i), i);
|
28 |
+
if (found) break;
|
29 |
+
}
|
30 |
+
return {from: {line: cur.line, ch: pos}, to: found && {line: i, ch: found.pos}, match: found && found.match};
|
31 |
+
}
|
32 |
+
|
33 |
+
function matchBrackets(cm, autoclear) {
|
34 |
+
var found = findMatchingBracket(cm);
|
35 |
+
if (!found) return;
|
36 |
+
var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
37 |
+
var one = cm.markText(found.from, {line: found.from.line, ch: found.from.ch + 1},
|
38 |
+
{className: style});
|
39 |
+
var two = found.to && cm.markText(found.to, {line: found.to.line, ch: found.to.ch + 1},
|
40 |
+
{className: style});
|
41 |
+
var clear = function() {
|
42 |
+
cm.operation(function() { one.clear(); two && two.clear(); });
|
43 |
+
};
|
44 |
+
if (autoclear) setTimeout(clear, 800);
|
45 |
+
else return clear;
|
46 |
+
}
|
47 |
+
|
48 |
+
var currentlyHighlighted = null;
|
49 |
+
function doMatchBrackets(cm) {
|
50 |
+
cm.operation(function() {
|
51 |
+
if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
|
52 |
+
if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
|
53 |
+
});
|
54 |
+
}
|
55 |
+
|
56 |
+
CodeMirror.defineOption("matchBrackets", false, function(cm, val) {
|
57 |
+
if (val) cm.on("cursorActivity", doMatchBrackets);
|
58 |
+
else cm.off("cursorActivity", doMatchBrackets);
|
59 |
+
});
|
60 |
+
|
61 |
+
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
|
62 |
+
CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
|
63 |
+
})();
|
{js → assets/util}/search.js
RENAMED
@@ -41,7 +41,8 @@
|
|
41 |
state.query = parseQuery(query);
|
42 |
if (cm.lineCount() < 2000) { // This is too expensive on big documents.
|
43 |
for (var cursor = getSearchCursor(cm, state.query); cursor.findNext();)
|
44 |
-
state.marked.push(cm.markText(cursor.from(), cursor.to(),
|
|
|
45 |
}
|
46 |
state.posFrom = state.posTo = cm.getCursor();
|
47 |
findNext(cm, rev);
|
@@ -76,14 +77,14 @@
|
|
76 |
query = parseQuery(query);
|
77 |
dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
|
78 |
if (all) {
|
79 |
-
cm.
|
80 |
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
|
81 |
if (typeof query != "string") {
|
82 |
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
|
83 |
-
cursor.replace(text.replace(/\$(\d)/, function(
|
84 |
} else cursor.replace(text);
|
85 |
}
|
86 |
-
});
|
87 |
} else {
|
88 |
clearSearch(cm);
|
89 |
var cursor = getSearchCursor(cm, query, cm.getCursor());
|
@@ -100,7 +101,7 @@
|
|
100 |
}
|
101 |
function doReplace(match) {
|
102 |
cursor.replace(typeof query == "string" ? text :
|
103 |
-
text.replace(/\$(\d)/, function(
|
104 |
advance();
|
105 |
}
|
106 |
advance();
|
41 |
state.query = parseQuery(query);
|
42 |
if (cm.lineCount() < 2000) { // This is too expensive on big documents.
|
43 |
for (var cursor = getSearchCursor(cm, state.query); cursor.findNext();)
|
44 |
+
state.marked.push(cm.markText(cursor.from(), cursor.to(),
|
45 |
+
{className: "CodeMirror-searching"}));
|
46 |
}
|
47 |
state.posFrom = state.posTo = cm.getCursor();
|
48 |
findNext(cm, rev);
|
77 |
query = parseQuery(query);
|
78 |
dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
|
79 |
if (all) {
|
80 |
+
cm.operation(function() {
|
81 |
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
|
82 |
if (typeof query != "string") {
|
83 |
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
|
84 |
+
cursor.replace(text.replace(/\$(\d)/, function(_, i) {return match[i];}));
|
85 |
} else cursor.replace(text);
|
86 |
}
|
87 |
+
});
|
88 |
} else {
|
89 |
clearSearch(cm);
|
90 |
var cursor = getSearchCursor(cm, query, cm.getCursor());
|
101 |
}
|
102 |
function doReplace(match) {
|
103 |
cursor.replace(typeof query == "string" ? text :
|
104 |
+
text.replace(/\$(\d)/, function(_, i) {return match[i];}));
|
105 |
advance();
|
106 |
}
|
107 |
advance();
|
{js → assets/util}/searchcursor.js
RENAMED
@@ -17,14 +17,14 @@
|
|
17 |
query.lastIndex = 0;
|
18 |
var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0;
|
19 |
while (match) {
|
20 |
-
start += match.index;
|
21 |
-
line = line.slice(
|
22 |
query.lastIndex = 0;
|
23 |
var newmatch = query.exec(line);
|
24 |
if (newmatch) match = newmatch;
|
25 |
else break;
|
26 |
-
start++;
|
27 |
}
|
|
|
28 |
} else {
|
29 |
query.lastIndex = pos.ch;
|
30 |
var line = cm.getLine(pos.line), match = query.exec(line),
|
17 |
query.lastIndex = 0;
|
18 |
var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0;
|
19 |
while (match) {
|
20 |
+
start += match.index + 1;
|
21 |
+
line = line.slice(start);
|
22 |
query.lastIndex = 0;
|
23 |
var newmatch = query.exec(line);
|
24 |
if (newmatch) match = newmatch;
|
25 |
else break;
|
|
|
26 |
}
|
27 |
+
start--;
|
28 |
} else {
|
29 |
query.lastIndex = pos.ch;
|
30 |
var line = cm.getLine(pos.line), match = query.exec(line),
|
code-snippets.php
CHANGED
@@ -1,1114 +1,1340 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Code Snippets - An easy, clean and simple way to add code snippets to your site.
|
5 |
-
*
|
6 |
-
* If you're interested in helping to develop Code Snippets, or perhaps
|
7 |
-
* to the localization, please see http://
|
8 |
-
*
|
9 |
-
* @package Code Snippets
|
10 |
-
* @subpackage Main
|
11 |
-
*
|
12 |
-
*
|
13 |
-
* Plugin Name: Code Snippets
|
14 |
-
* Plugin URI: http://
|
15 |
-
* Description: An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!
|
16 |
-
* Author: Shea Bunge
|
17 |
-
* Author URI: http://bungeshea.com
|
18 |
-
* Version: 1.
|
19 |
-
* License: GPLv3 or later
|
20 |
-
* Network: true
|
21 |
-
* Text Domain: code-snippets
|
22 |
-
* Domain Path: /languages/
|
23 |
-
*
|
24 |
-
*
|
25 |
-
* Code Snippets - WordPress Plugin
|
26 |
-
* Copyright (C) 2012 Shea Bunge
|
27 |
-
|
28 |
-
* This program is free software: you can redistribute it and/or modify
|
29 |
-
* it under the terms of the GNU General Public License as published by
|
30 |
-
* the Free Software Foundation, either version 3 of the License, or
|
31 |
-
* (at your option) any later version.
|
32 |
-
|
33 |
-
* This program is distributed in the hope that it will be useful,
|
34 |
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
35 |
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
36 |
-
* GNU General Public License for more details.
|
37 |
-
|
38 |
-
* You should have received a copy of the GNU General Public License
|
39 |
-
* along with this program. If not, see <http://www.gnu.org/licenses>.
|
40 |
-
*/
|
41 |
-
|
42 |
-
// Exit if accessed directly
|
43 |
-
if ( ! defined( 'ABSPATH' ) ) exit;
|
44 |
-
|
45 |
-
if( ! class_exists( 'Code_Snippets' ) ) :
|
46 |
-
|
47 |
-
/**
|
48 |
-
* The main class for our plugin
|
49 |
-
* It all happens here, folks
|
50 |
-
*
|
51 |
-
* Please use the global variable $
|
52 |
-
* the methods in this class. Anything you need
|
53 |
-
* to access should be publicly available there
|
54 |
-
*
|
55 |
-
* @since Code Snippets 1.0
|
56 |
-
* @access private
|
57 |
-
*/
|
58 |
-
class Code_Snippets {
|
59 |
-
|
60 |
-
/**
|
61 |
-
* The base name for the snippets table in the database
|
62 |
-
* This will later be prepended with the WordPress table prefix
|
63 |
-
*
|
64 |
-
* DO NOT EDIT THIS VARIABLE!
|
65 |
-
* Instead, use the '
|
66 |
-
*
|
67 |
-
*
|
68 |
-
*
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
*
|
77 |
-
*
|
78 |
-
*
|
79 |
-
*
|
80 |
-
*
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
*
|
86 |
-
*
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
*
|
92 |
-
*
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
*
|
98 |
-
*
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
*
|
104 |
-
*
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
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 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
$this->
|
153 |
-
$this->
|
154 |
-
|
155 |
-
|
156 |
-
$
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
add_action( '
|
178 |
-
add_action( 'network_admin_menu', array( $this, '
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
*
|
266 |
-
*
|
267 |
-
* @
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
'
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
$
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
*
|
395 |
-
*
|
396 |
-
*
|
397 |
-
* @
|
398 |
-
*/
|
399 |
-
function
|
400 |
-
$this->
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
$this->
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
$
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
*
|
484 |
-
*
|
485 |
-
* @
|
486 |
-
*
|
487 |
-
*
|
488 |
-
* @
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
$
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
$
|
503 |
-
|
504 |
-
|
505 |
-
'
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
'
|
539 |
-
|
540 |
-
|
541 |
-
$
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
'
|
552 |
-
'
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
'
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
*
|
623 |
-
*
|
624 |
-
*
|
625 |
-
* @
|
626 |
-
*
|
627 |
-
*
|
628 |
-
*
|
629 |
-
* @
|
630 |
-
* @
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
$
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
*
|
673 |
-
*
|
674 |
-
*
|
675 |
-
* @
|
676 |
-
*
|
677 |
-
*
|
678 |
-
*
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
);
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
*
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
$
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
*
|
818 |
-
*
|
819 |
-
* @
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
);
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
$
|
887 |
-
$
|
888 |
-
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
|
912 |
-
|
913 |
-
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
-
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
*
|
945 |
-
*
|
946 |
-
* @
|
947 |
-
*
|
948 |
-
*
|
949 |
-
* @
|
950 |
-
* @
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
*
|
965 |
-
*
|
966 |
-
* @
|
967 |
-
|
968 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
973 |
-
*
|
974 |
-
*
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
*
|
993 |
-
*
|
994 |
-
*
|
995 |
-
* @
|
996 |
-
|
997 |
-
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
|
1002 |
-
|
1003 |
-
|
1004 |
-
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
*
|
1009 |
-
*
|
1010 |
-
* @
|
1011 |
-
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
|
1029 |
-
|
1030 |
-
|
1031 |
-
|
1032 |
-
|
1033 |
-
|
1034 |
-
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
|
1046 |
-
|
1047 |
-
|
1048 |
-
|
1049 |
-
|
1050 |
-
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
|
1060 |
-
|
1061 |
-
|
1062 |
-
|
1063 |
-
|
1064 |
-
|
1065 |
-
|
1066 |
-
|
1067 |
-
|
1068 |
-
|
1069 |
-
|
1070 |
-
|
1071 |
-
|
1072 |
-
global $
|
1073 |
-
$
|
1074 |
-
|
1075 |
-
|
1076 |
-
|
1077 |
-
/**
|
1078 |
-
|
1079 |
-
|
1080 |
-
|
1081 |
-
|
1082 |
-
|
1083 |
-
|
1084 |
-
|
1085 |
-
|
1086 |
-
|
1087 |
-
|
1088 |
-
|
1089 |
-
|
1090 |
-
|
1091 |
-
|
1092 |
-
|
1093 |
-
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
1104 |
-
|
1105 |
-
|
1106 |
-
|
1107 |
-
|
1108 |
-
|
1109 |
-
|
1110 |
-
|
1111 |
-
|
1112 |
-
|
1113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1114 |
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Code Snippets - An easy, clean and simple way to add code snippets to your site.
|
5 |
+
*
|
6 |
+
* If you're interested in helping to develop Code Snippets, or perhaps
|
7 |
+
* contribute to the localization, please see http://code-snippets.bungeshea.com
|
8 |
+
*
|
9 |
+
* @package Code Snippets
|
10 |
+
* @subpackage Main
|
11 |
+
*
|
12 |
+
*
|
13 |
+
* Plugin Name: Code Snippets
|
14 |
+
* Plugin URI: http://code-snippets.bungeshea.com
|
15 |
+
* Description: An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!
|
16 |
+
* Author: Shea Bunge
|
17 |
+
* Author URI: http://bungeshea.com
|
18 |
+
* Version: 1.6
|
19 |
+
* License: GPLv3 or later
|
20 |
+
* Network: true
|
21 |
+
* Text Domain: code-snippets
|
22 |
+
* Domain Path: /languages/
|
23 |
+
*
|
24 |
+
*
|
25 |
+
* Code Snippets - WordPress Plugin
|
26 |
+
* Copyright (C) 2012 Shea Bunge
|
27 |
+
*
|
28 |
+
* This program is free software: you can redistribute it and/or modify
|
29 |
+
* it under the terms of the GNU General Public License as published by
|
30 |
+
* the Free Software Foundation, either version 3 of the License, or
|
31 |
+
* (at your option) any later version.
|
32 |
+
*
|
33 |
+
* This program is distributed in the hope that it will be useful,
|
34 |
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
35 |
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
36 |
+
* GNU General Public License for more details.
|
37 |
+
*
|
38 |
+
* You should have received a copy of the GNU General Public License
|
39 |
+
* along with this program. If not, see <http://www.gnu.org/licenses>.
|
40 |
+
*/
|
41 |
+
|
42 |
+
// Exit if accessed directly
|
43 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
44 |
+
|
45 |
+
if ( ! class_exists( 'Code_Snippets' ) ) :
|
46 |
+
|
47 |
+
/**
|
48 |
+
* The main class for our plugin
|
49 |
+
* It all happens here, folks
|
50 |
+
*
|
51 |
+
* Please use the global variable $code_snippets to access
|
52 |
+
* the methods in this class. Anything you need
|
53 |
+
* to access should be publicly available there
|
54 |
+
*
|
55 |
+
* @since Code Snippets 1.0
|
56 |
+
* @access private
|
57 |
+
*/
|
58 |
+
final class Code_Snippets {
|
59 |
+
|
60 |
+
/**
|
61 |
+
* The base name for the snippets table in the database
|
62 |
+
* This will later be prepended with the WordPress table prefix
|
63 |
+
*
|
64 |
+
* DO NOT EDIT THIS VARIABLE!
|
65 |
+
* Instead, use the 'code_snippets_table' filter
|
66 |
+
*
|
67 |
+
* If you need to use the table name in your own code,
|
68 |
+
* use the $code_snippets->get_table_name() function
|
69 |
+
*
|
70 |
+
* @since Code Snippets 1.0
|
71 |
+
* @access public
|
72 |
+
*/
|
73 |
+
private $table = 'snippets';
|
74 |
+
|
75 |
+
/**
|
76 |
+
* The base name for the network snippets table in the database
|
77 |
+
* This will later be prepended with the WordPress base table prefix
|
78 |
+
*
|
79 |
+
* DO NOT EDIT THIS VARIABLE!
|
80 |
+
* Instead, use the 'code_snippets_multisite_table' filter
|
81 |
+
*
|
82 |
+
* If you need to use the table name in your own code,
|
83 |
+
* use the $code_snippets->get_table_name() function
|
84 |
+
*
|
85 |
+
* @since Code Snippets 1.4
|
86 |
+
* @access private
|
87 |
+
*/
|
88 |
+
private $ms_table = 'ms_snippets';
|
89 |
+
|
90 |
+
/**
|
91 |
+
* The version number for this release of the plugin.
|
92 |
+
* This will later be used for upgrades and enqueueing files
|
93 |
+
*
|
94 |
+
* This should be set to the 'Plugin Version' value,
|
95 |
+
* as defined above in the plugin header
|
96 |
+
*
|
97 |
+
* @since Code Snippets 1.0
|
98 |
+
* @access public
|
99 |
+
*/
|
100 |
+
public $version = 1.6;
|
101 |
+
|
102 |
+
/**
|
103 |
+
* The base URLs for the admin pages
|
104 |
+
*
|
105 |
+
* @since Code Snippets 1.0
|
106 |
+
* @access public
|
107 |
+
*/
|
108 |
+
public $admin_manage_url, $admin_single_url, $admin_import_url;
|
109 |
+
|
110 |
+
/**
|
111 |
+
* The hooks for the admin pages
|
112 |
+
* Used primarily for enqueueing scripts and styles
|
113 |
+
*
|
114 |
+
* @since Code Snippets 1.0
|
115 |
+
* @access public
|
116 |
+
*/
|
117 |
+
public $admin_manage, $admin_single, $admin_import;
|
118 |
+
|
119 |
+
/**
|
120 |
+
* The constructor function for our class
|
121 |
+
*
|
122 |
+
* @since Code Snippets 1.0
|
123 |
+
* @access private
|
124 |
+
*
|
125 |
+
* @return void
|
126 |
+
*/
|
127 |
+
function __construct() {
|
128 |
+
$this->setup_vars(); // initialise the variables
|
129 |
+
$this->setup_hooks(); // register the action and filter hooks
|
130 |
+
$this->upgrade(); // check if we need to change some stuff
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Initialise variables
|
135 |
+
*
|
136 |
+
* @since Code Snippets 1.2
|
137 |
+
* @access private
|
138 |
+
*
|
139 |
+
* @return void
|
140 |
+
*/
|
141 |
+
function setup_vars() {
|
142 |
+
global $wpdb;
|
143 |
+
$this->file = __FILE__;
|
144 |
+
|
145 |
+
$this->table = apply_filters( 'code_snippets_table', $wpdb->prefix . $this->table );
|
146 |
+
$this->ms_table = apply_filters( 'code_snippets_multisite_table', $wpdb->base_prefix . $this->ms_table );
|
147 |
+
|
148 |
+
$this->basename = plugin_basename( $this->file );
|
149 |
+
$this->plugin_dir = plugin_dir_path( $this->file );
|
150 |
+
$this->plugin_url = plugin_dir_url ( $this->file );
|
151 |
+
|
152 |
+
$this->admin_manage_slug = apply_filters( 'code_snippets_admin_manage', 'snippets' );
|
153 |
+
$this->admin_single_slug = apply_filters( 'code_snippets_admin_single', 'snippet' );
|
154 |
+
|
155 |
+
$this->admin_manage_url = self_admin_url( 'admin.php?page=' . $this->admin_manage_slug );
|
156 |
+
$this->admin_single_url = self_admin_url( 'admin.php?page=' . $this->admin_single_slug );
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Register action and filter hooks
|
161 |
+
*
|
162 |
+
* @since Code Snippets 1.6
|
163 |
+
* @access private
|
164 |
+
*
|
165 |
+
* @return void
|
166 |
+
*/
|
167 |
+
function setup_hooks() {
|
168 |
+
|
169 |
+
/* execute the snippets once the plugins are loaded */
|
170 |
+
add_action( 'plugins_loaded', array( $this, 'run_snippets' ) );
|
171 |
+
|
172 |
+
/* add the administration menus */
|
173 |
+
add_action( 'admin_menu', array( $this, 'add_admin_menus' ) );
|
174 |
+
add_action( 'network_admin_menu', array( $this, 'add_network_admin_menus' ) );
|
175 |
+
|
176 |
+
/* register the importer */
|
177 |
+
add_action( 'admin_init', array( $this, 'load_importer' ), 999 );
|
178 |
+
add_action( 'network_admin_menu', array( $this, 'add_import_admin_menu' ) );
|
179 |
+
|
180 |
+
/* load the translations */
|
181 |
+
add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
|
182 |
+
|
183 |
+
/* add helpful links to the Plugins menu */
|
184 |
+
add_filter( 'plugin_action_links_' . $this->basename, array( $this, 'settings_link' ) );
|
185 |
+
add_filter( 'plugin_row_meta', array( $this, 'plugin_meta' ), 10, 2 );
|
186 |
+
|
187 |
+
/* Add a custom icon to Snippets menu pages */
|
188 |
+
add_action( 'admin_head', array( $this, 'load_admin_icon_style' ) );
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Load the Code Snippets importer
|
193 |
+
*
|
194 |
+
* Add both an importer to the Tools menu
|
195 |
+
* and an Import Snippets page to the network admin menu
|
196 |
+
*
|
197 |
+
* @since Code Snippets 1.6
|
198 |
+
* @access private
|
199 |
+
*
|
200 |
+
* @return void
|
201 |
+
*/
|
202 |
+
function load_importer() {
|
203 |
+
|
204 |
+
if ( defined( 'WP_LOAD_IMPORTERS' ) ) {
|
205 |
+
|
206 |
+
// Load Importer API
|
207 |
+
require_once ABSPATH . 'wp-admin/includes/import.php';
|
208 |
+
|
209 |
+
if ( ! class_exists( 'WP_Importer' ) ) {
|
210 |
+
$class_wp_importer = ABSPATH . 'wp-admin/includes/class-wp-importer.php';
|
211 |
+
if ( file_exists( $class_wp_importer ) )
|
212 |
+
require_once $class_wp_importer;
|
213 |
+
}
|
214 |
+
|
215 |
+
register_importer(
|
216 |
+
'code-snippets',
|
217 |
+
__('Code Snippets', 'code-snippets'),
|
218 |
+
__('Import snippets from a <strong>Code Snippets</strong> export file', 'code-snippets'),
|
219 |
+
array( $this, 'display_admin_import' )
|
220 |
+
);
|
221 |
+
}
|
222 |
+
|
223 |
+
$this->admin_import_url = self_admin_url( 'admin.php?import=code-snippets' );
|
224 |
+
add_action( 'load-importer-code-snippets', array( $this, 'load_admin_import' ) );
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Return the appropriate snippet table name
|
229 |
+
*
|
230 |
+
* @since Code Snippets 1.6
|
231 |
+
* @access private
|
232 |
+
*
|
233 |
+
* @param string $scope Retrieve the multisite table name or the site table name?
|
234 |
+
* @param bool $check_screen Query the current screen if no scope passed?
|
235 |
+
* @return string $table The snippet table name
|
236 |
+
*/
|
237 |
+
function get_table_name( $scope = '', $check_screen = true ) {
|
238 |
+
|
239 |
+
$this->create_tables(); // create the snippet tables if they do not exist
|
240 |
+
|
241 |
+
if ( ! is_multisite() ) {
|
242 |
+
$network = false;
|
243 |
+
}
|
244 |
+
elseif ( empty( $network ) && $check_screen && function_exists( 'get_current_screen' ) ) {
|
245 |
+
/* if no scope is set, query the current screen to see if in network admin */
|
246 |
+
$screen = get_current_screen();
|
247 |
+
$network = $screen->is_network;
|
248 |
+
}
|
249 |
+
elseif ( $scope == 'multisite' || $scope == 'network' ) {
|
250 |
+
$network = true;
|
251 |
+
}
|
252 |
+
elseif ( $scope == 'site' || $scope = 'single' ) {
|
253 |
+
$network = false;
|
254 |
+
}
|
255 |
+
else {
|
256 |
+
$network = false;
|
257 |
+
}
|
258 |
+
|
259 |
+
$table = ( $network ? $this->ms_table : $this->table );
|
260 |
+
|
261 |
+
return $table;
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Create the snippet tables if they do not already exist
|
266 |
+
*
|
267 |
+
* @since Code Snippets 1.2
|
268 |
+
* @access public
|
269 |
+
*
|
270 |
+
* @uses $this->create_table() To create a single snippet table
|
271 |
+
*
|
272 |
+
* @return void
|
273 |
+
*/
|
274 |
+
public function create_tables() {
|
275 |
+
|
276 |
+
$this->create_table( $this->table );
|
277 |
+
|
278 |
+
if ( is_multisite() )
|
279 |
+
$this->create_table( $this->ms_table );
|
280 |
+
|
281 |
+
add_site_option( 'code_snippets_version', $this->version );
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Create a single snippet table
|
286 |
+
* if one of the same name does not already exist
|
287 |
+
*
|
288 |
+
* @since Code Snippets 1.6
|
289 |
+
* @access public
|
290 |
+
*
|
291 |
+
* @param string $table_name The name of the table to create
|
292 |
+
* @return void
|
293 |
+
*/
|
294 |
+
function create_table( $table_name ) {
|
295 |
+
|
296 |
+
global $wpdb;
|
297 |
+
|
298 |
+
if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) {
|
299 |
+
$sql = "CREATE TABLE $table_name (
|
300 |
+
id MEDIUMINT NOT NULL AUTO_INCREMENT,
|
301 |
+
name VARCHAR(64) NOT NULL,
|
302 |
+
description TEXT,
|
303 |
+
code TEXT NOT NULL,
|
304 |
+
active TINYINT(1) NOT NULL DEFAULT 0,
|
305 |
+
UNIQUE KEY id (id)
|
306 |
+
);";
|
307 |
+
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
308 |
+
dbDelta( $sql );
|
309 |
+
}
|
310 |
+
}
|
311 |
+
|
312 |
+
/**
|
313 |
+
* Preform upgrade tasks such as deleting and updating options
|
314 |
+
*
|
315 |
+
* @since Code Snippets 1.2
|
316 |
+
* @access private
|
317 |
+
*
|
318 |
+
* @return bool True on successful upgrade, false on failure
|
319 |
+
*/
|
320 |
+
function upgrade() {
|
321 |
+
|
322 |
+
/* add backwards-compatibly for the CS_SAFE_MODE constant */
|
323 |
+
if ( defined( 'CS_SAFE_MODE' ) && ! defined( 'CODE_SNIPPETS_SAFE_MODE' ) ) {
|
324 |
+
define( 'CODE_SNIPPETS_SAFE_MODE', CS_SAFE_MODE );
|
325 |
+
}
|
326 |
+
|
327 |
+
/* get the current plugin version from the database */
|
328 |
+
if ( get_option( 'cs_db_version' ) ) {
|
329 |
+
$this->current_version = get_option( 'cs_db_version', $this->version );
|
330 |
+
delete_option( 'cs_db_version' );
|
331 |
+
add_site_option( 'code_snippets_version', $this->current_version );
|
332 |
+
}
|
333 |
+
else {
|
334 |
+
$this->current_version = get_site_option( 'code_snippets_version', $this->version );
|
335 |
+
}
|
336 |
+
|
337 |
+
/* bail early if we're on the latest version */
|
338 |
+
if ( ! ( $this->current_version < $this->version ) ) return false;
|
339 |
+
|
340 |
+
if ( ! get_site_option( 'code_snippets_version' ) ) {
|
341 |
+
|
342 |
+
/* This is the first time the plugin has run */
|
343 |
+
|
344 |
+
$this->add_caps(); // register the capabilities ONCE ONLY
|
345 |
+
|
346 |
+
if ( is_multisite() ) {
|
347 |
+
$this->add_caps( 'multisite' ); // register the multisite capabilities ONCE ONLY
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
/* migrate the recently_network_activated_snippets to the site options */
|
352 |
+
if ( is_multisite() && get_option( 'recently_network_activated_snippets' ) ) {
|
353 |
+
add_site_option( 'recently_activated_snippets', get_option( 'recently_network_activated_snippets', array() ) );
|
354 |
+
delete_option( 'recently_network_activated_snippets' );
|
355 |
+
}
|
356 |
+
|
357 |
+
/* preform version specific upgrades */
|
358 |
+
|
359 |
+
if ( $this->current_version < 1.5 ) {
|
360 |
+
global $wpdb;
|
361 |
+
|
362 |
+
/* Let's alter the name column to accept up to 64 characters */
|
363 |
+
$wpdb->query( "ALTER TABLE $this->table CHANGE COLUMN name name VARCHAR(64) NOT NULL" );
|
364 |
+
|
365 |
+
if ( is_multisite() ) {
|
366 |
+
/* We must not forget the multisite table! */
|
367 |
+
$wpdb->query( "ALTER TABLE $this->ms_table CHANGE COLUMN name name VARCHAR(64) NOT NULL" );
|
368 |
+
}
|
369 |
+
|
370 |
+
/* Add the custom capabilities that were introduced in version 1.5 */
|
371 |
+
$this->add_roles();
|
372 |
+
}
|
373 |
+
|
374 |
+
if ( $this->current_version < 1.2 ) {
|
375 |
+
/* The 'Complete Uninstall' option was removed in version 1.2 */
|
376 |
+
delete_option( 'cs_complete_uninstall' );
|
377 |
+
}
|
378 |
+
|
379 |
+
if ( $this->current_version < $this->version ) {
|
380 |
+
/* Update the current version */
|
381 |
+
update_site_option( 'code_snippets_version', $this->version );
|
382 |
+
}
|
383 |
+
|
384 |
+
return true;
|
385 |
+
}
|
386 |
+
|
387 |
+
/**
|
388 |
+
* Load up the localization file if we're using WordPress in a different language
|
389 |
+
* Place it in this plugin's "languages" folder and name it "code-snippets-[value in wp-config].mo"
|
390 |
+
*
|
391 |
+
* If you wish to contribute a language file to be included in the Code Snippets package,
|
392 |
+
* please see the plugin's website at http://code-snippets.bungeshea.com
|
393 |
+
*
|
394 |
+
* @since Code Snippets 1.5
|
395 |
+
* @access private
|
396 |
+
*
|
397 |
+
* @return void
|
398 |
+
*/
|
399 |
+
function load_textdomain() {
|
400 |
+
load_plugin_textdomain( 'code-snippets', false, dirname( $this->basename ) . '/languages/' );
|
401 |
+
}
|
402 |
+
|
403 |
+
/**
|
404 |
+
* Add the user capabilities
|
405 |
+
*
|
406 |
+
* @since Code Snippets 1.5
|
407 |
+
* @access public
|
408 |
+
*
|
409 |
+
* @uses $this->setup_roles() To register the capabilities
|
410 |
+
*
|
411 |
+
* @param string $scope Add site-specific or multisite-specific capabilities?
|
412 |
+
* @return void
|
413 |
+
*/
|
414 |
+
public function add_caps( $scope = '' ) {
|
415 |
+
|
416 |
+
$network = ( $scope == 'multisite' || $scope == 'network' ? true : false );
|
417 |
+
|
418 |
+
if ( $network && is_multisite() )
|
419 |
+
$this->setup_ms_roles( 'add' );
|
420 |
+
else
|
421 |
+
$this->setup_roles( 'add' );
|
422 |
+
}
|
423 |
+
|
424 |
+
/**
|
425 |
+
* Remove the user capabilities
|
426 |
+
*
|
427 |
+
* @since Code Snippets 1.5
|
428 |
+
* @access public
|
429 |
+
*
|
430 |
+
* @uses $this->setup_roles() To register the capabilities
|
431 |
+
*
|
432 |
+
* @param string $scope Add site-specific or multisite-specific capabilities?
|
433 |
+
* @return void
|
434 |
+
*/
|
435 |
+
public function remove_caps( $scope = '' ) {
|
436 |
+
|
437 |
+
$network = ( $scope == 'multisite' || $scope == 'network' ? true : false );
|
438 |
+
|
439 |
+
if ( $network && is_multisite() )
|
440 |
+
$this->setup_ms_roles( 'remove' );
|
441 |
+
else
|
442 |
+
$this->setup_roles( 'remove' );
|
443 |
+
}
|
444 |
+
|
445 |
+
/**
|
446 |
+
* Register the user roles and capabilities
|
447 |
+
*
|
448 |
+
* @since Code Snippets 1.5
|
449 |
+
* @access private
|
450 |
+
*
|
451 |
+
* @param string $install True to add the capabilities, false to remove
|
452 |
+
* @return void
|
453 |
+
*/
|
454 |
+
function setup_roles( $action = 'install' ) {
|
455 |
+
|
456 |
+
if ( $action == 'install' || $action = 'add' )
|
457 |
+
$install = true;
|
458 |
+
|
459 |
+
elseif ( $action == 'uninstall' || $action = 'remove' )
|
460 |
+
$install = false;
|
461 |
+
|
462 |
+
else
|
463 |
+
$install = true;
|
464 |
+
|
465 |
+
|
466 |
+
$this->caps = apply_filters( 'code_snippets_caps', array(
|
467 |
+
'manage_snippets',
|
468 |
+
'install_snippets',
|
469 |
+
'edit_snippets'
|
470 |
+
) );
|
471 |
+
|
472 |
+
$this->role = get_role( apply_filters( 'code_snippets_role', 'administrator' ) );
|
473 |
+
|
474 |
+
foreach( $this->caps as $cap ) {
|
475 |
+
if ( $install )
|
476 |
+
$this->role->add_cap( $cap );
|
477 |
+
else
|
478 |
+
$this->role->remove_cap( $cap );
|
479 |
+
}
|
480 |
+
}
|
481 |
+
|
482 |
+
/**
|
483 |
+
* Register the multisite user roles and capabilities
|
484 |
+
*
|
485 |
+
* @since Code Snippets 1.5
|
486 |
+
* @access private
|
487 |
+
*
|
488 |
+
* @param string $action Add or remove the capabilities
|
489 |
+
* @return void
|
490 |
+
*/
|
491 |
+
function setup_ms_roles( $action = 'install' ) {
|
492 |
+
|
493 |
+
if ( ! is_multisite() ) return;
|
494 |
+
|
495 |
+
if ( $action == 'install' || $action = 'add' )
|
496 |
+
$install = true;
|
497 |
+
|
498 |
+
elseif ( $action == 'uninstall' || $action = 'remove' )
|
499 |
+
$install = false;
|
500 |
+
|
501 |
+
else
|
502 |
+
$install = true;
|
503 |
+
|
504 |
+
$this->network_caps = apply_filters( 'code_snippets_network_caps', array(
|
505 |
+
'manage_network_snippets',
|
506 |
+
'install_network_snippets',
|
507 |
+
'edit_network_snippets'
|
508 |
+
) );
|
509 |
+
|
510 |
+
$supers = get_super_admins();
|
511 |
+
foreach( $supers as $admin ) {
|
512 |
+
$user = new WP_User( 0, $admin );
|
513 |
+
foreach( $this->network_caps as $cap ) {
|
514 |
+
if ( $install )
|
515 |
+
$user->add_cap( $cap );
|
516 |
+
else
|
517 |
+
$user->remove_cap( $cap );
|
518 |
+
}
|
519 |
+
}
|
520 |
+
}
|
521 |
+
|
522 |
+
/**
|
523 |
+
* Add the dashboard admin menu and subpages
|
524 |
+
*
|
525 |
+
* @since Code Snippets 1.0
|
526 |
+
* @access private
|
527 |
+
*
|
528 |
+
* @uses add_menu_page() To register a top-level menu
|
529 |
+
* @uses add_submenu_page() To register a submenu page
|
530 |
+
* @uses apply_filters() To retrieve the corrent menu slug
|
531 |
+
* @uses plugins_url() To retrieve the URL to a resource
|
532 |
+
* @return void
|
533 |
+
*/
|
534 |
+
function add_admin_menus() {
|
535 |
+
|
536 |
+
$this->admin_manage = add_menu_page(
|
537 |
+
__('Snippets', 'code-snippets'),
|
538 |
+
__('Snippets', 'code-snippets'),
|
539 |
+
'manage_snippets',
|
540 |
+
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
541 |
+
array( $this, 'display_admin_manage' ),
|
542 |
+
plugins_url( 'images/icon16.png', $this->file ),
|
543 |
+
67
|
544 |
+
);
|
545 |
+
|
546 |
+
add_submenu_page(
|
547 |
+
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
548 |
+
__('Snippets', 'code-snippets'),
|
549 |
+
__('Manage Snippets', 'code-snippets'),
|
550 |
+
'manage_snippets',
|
551 |
+
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
552 |
+
array( $this, 'display_admin_manage')
|
553 |
+
);
|
554 |
+
|
555 |
+
$this->admin_single = add_submenu_page(
|
556 |
+
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
557 |
+
__('Add New Snippet', 'code-snippets'),
|
558 |
+
__('Add New', 'code-snippets'),
|
559 |
+
'install_snippets',
|
560 |
+
apply_filters( 'code_snippets_single_url', 'snippet' ),
|
561 |
+
array( $this, 'display_admin_single' )
|
562 |
+
);
|
563 |
+
|
564 |
+
add_action( "load-$this->admin_manage", array( $this, 'load_admin_manage' ) );
|
565 |
+
add_action( "load-$this->admin_single", array( $this, 'load_admin_single' ) );
|
566 |
+
|
567 |
+
add_action( "admin_print_styles-$this->admin_single", array( $this, 'load_editor_styles' ) );
|
568 |
+
add_action( "admin_print_scripts-$this->admin_single", array( $this, 'load_editor_scripts' ) );
|
569 |
+
}
|
570 |
+
|
571 |
+
/**
|
572 |
+
* Add the network dashboard admin menu and subpages
|
573 |
+
*
|
574 |
+
* @since Code Snippets 1.4
|
575 |
+
* @access private
|
576 |
+
*
|
577 |
+
* @uses add_menu_page() To register a top-level menu
|
578 |
+
* @uses add_submenu_page() To register a submenu page
|
579 |
+
* @uses apply_filters() To retrieve the corrent menu slug
|
580 |
+
* @uses plugins_url() To retrieve the URL to a resource
|
581 |
+
* @return void
|
582 |
+
*/
|
583 |
+
function add_network_admin_menus() {
|
584 |
+
|
585 |
+
$this->admin_manage = add_menu_page(
|
586 |
+
__('Snippets', 'code-snippets'),
|
587 |
+
__('Snippets', 'code-snippets'),
|
588 |
+
'manage_network_snippets',
|
589 |
+
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
590 |
+
array( $this, 'display_admin_manage' ),
|
591 |
+
plugins_url( 'images/icon16.png', $this->file ),
|
592 |
+
21
|
593 |
+
);
|
594 |
+
|
595 |
+
add_submenu_page(
|
596 |
+
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
597 |
+
__('Snippets', 'code-snippets'),
|
598 |
+
__('Manage Snippets', 'code-snippets'),
|
599 |
+
'manage_network_snippets',
|
600 |
+
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
601 |
+
array( $this, 'display_admin_manage' )
|
602 |
+
);
|
603 |
+
|
604 |
+
$this->admin_single = add_submenu_page(
|
605 |
+
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
606 |
+
__('Add New Snippet', 'code-snippets'),
|
607 |
+
__('Add New', 'code-snippets'),
|
608 |
+
'install_network_snippets',
|
609 |
+
apply_filters( 'code_snippets_single_url', 'snippet' ),
|
610 |
+
array( $this, 'display_admin_single' )
|
611 |
+
);
|
612 |
+
|
613 |
+
add_action( "load-$this->admin_manage", array( $this, 'load_admin_manage' ) );
|
614 |
+
add_action( "load-$this->admin_single", array( $this, 'load_admin_single' ) );
|
615 |
+
|
616 |
+
add_action( "admin_print_styles-$this->admin_single", array( $this, 'load_editor_styles' ) );
|
617 |
+
add_action( "admin_print_scripts-$this->admin_single", array( $this, 'load_editor_scripts' ) );
|
618 |
+
}
|
619 |
+
|
620 |
+
/**
|
621 |
+
* Add an Import Snippets page to the network admin menu
|
622 |
+
* We need to do this as there is no Tools menu in the network
|
623 |
+
* admin, and so we cannot register an importer
|
624 |
+
*
|
625 |
+
* @since Code Snippets 1.6
|
626 |
+
* @access private
|
627 |
+
*
|
628 |
+
* @uses add_submenu_page() To register the menu page
|
629 |
+
* @uses apply_filters() To retrieve the corrent menu slug
|
630 |
+
* @uses add_action() To enqueue scripts and styles
|
631 |
+
* @return void
|
632 |
+
*/
|
633 |
+
function add_import_admin_menu() {
|
634 |
+
|
635 |
+
$this->admin_import = add_submenu_page(
|
636 |
+
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
637 |
+
__('Import Snippets', 'code-snippets'),
|
638 |
+
__('Import', 'code-snippets'),
|
639 |
+
'import_snippets',
|
640 |
+
'import-code-snippets',
|
641 |
+
array( $this, 'display_admin_import' )
|
642 |
+
);
|
643 |
+
|
644 |
+
$this->admin_import_url = self_admin_url( 'admin.php?page=import-code-snippets' );
|
645 |
+
add_action( "admin_print_styles-$this->admin_import", array( $this, 'load_stylesheet' ) );
|
646 |
+
add_action( "load-$this->admin_import", array( $this, 'load_admin_import' ) );
|
647 |
+
}
|
648 |
+
|
649 |
+
/**
|
650 |
+
* Enqueue the icon stylesheet
|
651 |
+
*
|
652 |
+
* @since Code Snippets 1.0
|
653 |
+
* @access private
|
654 |
+
*
|
655 |
+
* @uses wp_enqueue_style() To add the stylesheet to the queue
|
656 |
+
*
|
657 |
+
* @return void
|
658 |
+
*/
|
659 |
+
function load_admin_icon_style() {
|
660 |
+
|
661 |
+
wp_enqueue_style(
|
662 |
+
'icon-snippets',
|
663 |
+
plugins_url( 'assets/icon.css', $this->file ),
|
664 |
+
false,
|
665 |
+
$this->version
|
666 |
+
);
|
667 |
+
}
|
668 |
+
|
669 |
+
/**
|
670 |
+
* Registers and loads the code editor's scripts
|
671 |
+
*
|
672 |
+
* @since Code Snippets 1.4
|
673 |
+
* @access private
|
674 |
+
*
|
675 |
+
* @uses wp_register_script()
|
676 |
+
* @uses wp_enqueue_style() To add the scripts to the queue
|
677 |
+
*
|
678 |
+
* @return void
|
679 |
+
*/
|
680 |
+
function load_editor_scripts() {
|
681 |
+
|
682 |
+
/* CodeMirror package version */
|
683 |
+
$version = '3.0';
|
684 |
+
|
685 |
+
/* CodeMirror base framework */
|
686 |
+
|
687 |
+
wp_register_script(
|
688 |
+
'codemirror',
|
689 |
+
plugins_url( 'assets/codemirror.js', $this->file ),
|
690 |
+
false,
|
691 |
+
$version
|
692 |
+
);
|
693 |
+
|
694 |
+
/* CodeMirror modes */
|
695 |
+
|
696 |
+
$modes = array( 'php', 'xml', 'javascript', 'css', 'clike', 'htmlmixed' );
|
697 |
+
|
698 |
+
foreach ( $modes as $mode ) {
|
699 |
+
|
700 |
+
wp_register_script(
|
701 |
+
"codemirror-mode-$mode",
|
702 |
+
plugins_url( "assets/mode/$mode.js", $this->file ),
|
703 |
+
array( 'codemirror' ),
|
704 |
+
$version
|
705 |
+
);
|
706 |
+
}
|
707 |
+
|
708 |
+
/* CodeMirror utilities */
|
709 |
+
|
710 |
+
$utils = array( 'dialog', 'searchcursor', 'search', 'matchbrackets' );
|
711 |
+
|
712 |
+
foreach ( $utils as $util ) {
|
713 |
+
|
714 |
+
wp_register_script(
|
715 |
+
"codemirror-util-$util",
|
716 |
+
plugins_url( "assets/util/$util.js", $this->file ),
|
717 |
+
array( 'codemirror' ),
|
718 |
+
$version
|
719 |
+
);
|
720 |
+
}
|
721 |
+
|
722 |
+
/* Enqueue the registered scripts */
|
723 |
+
|
724 |
+
wp_enqueue_script( array(
|
725 |
+
'codemirror-util-matchbrackets',
|
726 |
+
'codemirror-mode-htmlmixed',
|
727 |
+
'codemirror-mode-xml',
|
728 |
+
'codemirror-mode-js',
|
729 |
+
'codemirror-mode-css',
|
730 |
+
'codemirror-mode-clike',
|
731 |
+
'codemirror-mode-php',
|
732 |
+
'codemirror-util-search',
|
733 |
+
) );
|
734 |
+
}
|
735 |
+
|
736 |
+
/**
|
737 |
+
* Registers and loads the code editor's styles
|
738 |
+
*
|
739 |
+
* @since Code Snippets 1.4
|
740 |
+
* @access private
|
741 |
+
*
|
742 |
+
* @uses wp_register_style()
|
743 |
+
* @uses wp_enqueue_style() To add the stylesheets to the queue
|
744 |
+
*
|
745 |
+
* @return void
|
746 |
+
*/
|
747 |
+
function load_editor_styles() {
|
748 |
+
|
749 |
+
/* CodeMirror package version */
|
750 |
+
$version = '3.0';
|
751 |
+
|
752 |
+
/* CodeMirror base framework */
|
753 |
+
|
754 |
+
wp_register_style(
|
755 |
+
'codemirror',
|
756 |
+
plugins_url( 'assets/codemirror.css', $this->file ),
|
757 |
+
false,
|
758 |
+
$version
|
759 |
+
);
|
760 |
+
|
761 |
+
/* CodeMirror utilities */
|
762 |
+
|
763 |
+
wp_register_style(
|
764 |
+
'codemirror-util-dialog',
|
765 |
+
plugins_url( 'assets/util/dialog.css', $this->file ),
|
766 |
+
array( 'codemirror' ),
|
767 |
+
$version
|
768 |
+
);
|
769 |
+
|
770 |
+
/* Enqueue the registered stylesheets */
|
771 |
+
|
772 |
+
wp_enqueue_style( array(
|
773 |
+
'codemirror',
|
774 |
+
'codemirror-util-dialog',
|
775 |
+
) );
|
776 |
+
|
777 |
+
}
|
778 |
+
|
779 |
+
/**
|
780 |
+
* Activates a snippet
|
781 |
+
*
|
782 |
+
* @since Code Snippets 1.5
|
783 |
+
* @access public
|
784 |
+
*
|
785 |
+
* @uses $wpdb To set the snippets' active status
|
786 |
+
*
|
787 |
+
* @param array $ids The ids of the snippets to activate
|
788 |
+
* @param string $scope Are the snippets multisite-wide or site-wide?
|
789 |
+
* @return void
|
790 |
+
*/
|
791 |
+
public function activate( $ids, $scope = '' ) {
|
792 |
+
global $wpdb;
|
793 |
+
|
794 |
+
$ids = (array) $ids;
|
795 |
+
|
796 |
+
$table = $this->get_table_name( $scope );
|
797 |
+
|
798 |
+
foreach( $ids as $id ) {
|
799 |
+
$wpdb->update(
|
800 |
+
$table,
|
801 |
+
array( 'active' => '1' ),
|
802 |
+
array( 'id' => $id ),
|
803 |
+
array( '%d' ),
|
804 |
+
array( '%d' )
|
805 |
+
);
|
806 |
+
}
|
807 |
+
}
|
808 |
+
|
809 |
+
/**
|
810 |
+
* Deactivates selected snippets
|
811 |
+
*
|
812 |
+
* @since Code Snippets 1.5
|
813 |
+
* @access public
|
814 |
+
*
|
815 |
+
* @uses $wpdb To set the snippets' active status
|
816 |
+
*
|
817 |
+
* @param array $ids The IDs of the snippets to deactivate
|
818 |
+
* @param string $scope Are the snippets multisite-wide or site-wide?
|
819 |
+
* @return void
|
820 |
+
*/
|
821 |
+
public function deactivate( $ids, $scope = '' ) {
|
822 |
+
global $wpdb;
|
823 |
+
|
824 |
+
$ids = (array) $ids;
|
825 |
+
$recently_active = array();
|
826 |
+
|
827 |
+
$table = $this->get_table_name( $scope );
|
828 |
+
|
829 |
+
foreach( $ids as $id ) {
|
830 |
+
$wpdb->update(
|
831 |
+
$table,
|
832 |
+
array( 'active' => '0' ),
|
833 |
+
array( 'id' => $id ),
|
834 |
+
array( '%d' ),
|
835 |
+
array( '%d' )
|
836 |
+
);
|
837 |
+
$recently_active = array( $id => time() ) + (array) $recently_active;
|
838 |
+
}
|
839 |
+
|
840 |
+
if ( $table == $this->ms_table )
|
841 |
+
update_site_option(
|
842 |
+
'recently_activated_snippets',
|
843 |
+
$recently_active + (array) get_site_option( 'recently_activated_snippets' )
|
844 |
+
);
|
845 |
+
else
|
846 |
+
update_option(
|
847 |
+
'recently_activated_snippets',
|
848 |
+
$recently_active + (array) get_option( 'recently_activated_snippets' )
|
849 |
+
);
|
850 |
+
}
|
851 |
+
|
852 |
+
/**
|
853 |
+
* Deletes a snippet from the database
|
854 |
+
*
|
855 |
+
* @since Code Snippets 1.5
|
856 |
+
* @access public
|
857 |
+
*
|
858 |
+
* @uses $wpdb To access the database
|
859 |
+
* @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
|
860 |
+
*/
|
861 |
+
public function delete_snippet( $id, $scope = '' ) {
|
862 |
+
global $wpdb;
|
863 |
+
|
864 |
+
$table = $this->get_table_name( $scope );
|
865 |
+
$id = intval( $id );
|
866 |
+
|
867 |
+
$wpdb->query( "DELETE FROM $table WHERE id='$id' LIMIT 1" );
|
868 |
+
}
|
869 |
+
|
870 |
+
/**
|
871 |
+
* Saves a snippet to the database.
|
872 |
+
*
|
873 |
+
* @since Code Snippets 1.5
|
874 |
+
* @access public
|
875 |
+
*
|
876 |
+
* @uses $wpdb To update/add the snippet to the database
|
877 |
+
* @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
|
878 |
+
*
|
879 |
+
* @param array $snippet The snippet to add/update to the database
|
880 |
+
* @return int|bool The ID of the snippet on success, false on failure
|
881 |
+
*/
|
882 |
+
public function save_snippet( $snippet, $scope = '' ) {
|
883 |
+
global $wpdb;
|
884 |
+
|
885 |
+
$name = mysql_real_escape_string( htmlspecialchars( $snippet['name'] ) );
|
886 |
+
$description = mysql_real_escape_string( htmlspecialchars( $snippet['description'] ) );
|
887 |
+
$code = mysql_real_escape_string( htmlspecialchars( $snippet['code'] ) );
|
888 |
+
|
889 |
+
if ( empty( $name ) or empty( $code ) )
|
890 |
+
return false;
|
891 |
+
|
892 |
+
$table = $this->get_table_name( $scope );
|
893 |
+
|
894 |
+
if ( isset( $snippet['id'] ) && ( intval( $snippet['id'] ) != 0 ) ) {
|
895 |
+
$wpdb->query( "UPDATE $table SET
|
896 |
+
name='$name',
|
897 |
+
description='$description',
|
898 |
+
code='$code' WHERE id=" . intval( $snippet['id'] ) . "
|
899 |
+
LIMIT 1"
|
900 |
+
);
|
901 |
+
return intval( $snippet['id'] );
|
902 |
+
} else {
|
903 |
+
$wpdb->query(
|
904 |
+
"INSERT INTO $table(name,description,code)
|
905 |
+
VALUES ('$name','$description','$code')"
|
906 |
+
);
|
907 |
+
return $wpdb->insert_id;
|
908 |
+
}
|
909 |
+
}
|
910 |
+
|
911 |
+
/**
|
912 |
+
* Imports snippets from an XML file
|
913 |
+
*
|
914 |
+
* @since Code Snippets 1.5
|
915 |
+
* @access public
|
916 |
+
*
|
917 |
+
* @uses $this->save_snippet() To add the snippets to the database
|
918 |
+
*
|
919 |
+
* @param file $file The path to the XML file to import
|
920 |
+
* @param string $scope Import into network-wide table or site-wide table?
|
921 |
+
* @return mixed The number of snippets imported on success, false on failure
|
922 |
+
*/
|
923 |
+
public function import( $file, $scope = '' ) {
|
924 |
+
|
925 |
+
if ( ! file_exists( $file ) || ! is_file( $file ) )
|
926 |
+
return false;
|
927 |
+
|
928 |
+
$xml = simplexml_load_file( $file );
|
929 |
+
|
930 |
+
foreach ( $xml->children() as $child ) {
|
931 |
+
$this->save_snippet( array(
|
932 |
+
'name' => $child->name,
|
933 |
+
'description' => $child->description,
|
934 |
+
'code' => $child->code,
|
935 |
+
), $scope );
|
936 |
+
}
|
937 |
+
return $xml->count();
|
938 |
+
}
|
939 |
+
|
940 |
+
/**
|
941 |
+
* Exports snippets as an XML file
|
942 |
+
*
|
943 |
+
* @since Code Snippets 1.5
|
944 |
+
* @access public
|
945 |
+
*
|
946 |
+
* @uses code_snippets_export() To export selected snippets
|
947 |
+
* @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
|
948 |
+
*
|
949 |
+
* @param array $id An array if the IDs of the snippets to export
|
950 |
+
* @param string $scope Is the snippet a network-wide or site-wide snippet?
|
951 |
+
* @return void
|
952 |
+
*/
|
953 |
+
public function export( $ids, $scope = '' ) {
|
954 |
+
|
955 |
+
$table = $this->get_table_name( $scope );
|
956 |
+
|
957 |
+
if ( ! function_exists( 'code_snippets_export' ) )
|
958 |
+
require_once $this->plugin_dir . 'includes/export.php';
|
959 |
+
|
960 |
+
code_snippets_export( $ids, 'xml', $table );
|
961 |
+
}
|
962 |
+
|
963 |
+
/**
|
964 |
+
* Exports snippets as a PHP file
|
965 |
+
*
|
966 |
+
* @since Code Snippets 1.5
|
967 |
+
* @access public
|
968 |
+
*
|
969 |
+
* @uses code_snippets_export() To export selected snippets
|
970 |
+
* @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
|
971 |
+
*
|
972 |
+
* @param array $id An array if the IDs of the snippets to export
|
973 |
+
* @param string $scope Is the snippet a network-wide or site-wide snippet?
|
974 |
+
* @return void
|
975 |
+
*/
|
976 |
+
public function export_php( $ids, $scope = '' ) {
|
977 |
+
|
978 |
+
$table = $this->get_table_name( $scope );
|
979 |
+
|
980 |
+
if ( ! function_exists( 'code_snippets_export' ) )
|
981 |
+
require_once $this->plugin_dir . 'includes/export.php';
|
982 |
+
|
983 |
+
code_snippets_export( $ids, 'php', $table );
|
984 |
+
}
|
985 |
+
|
986 |
+
/**
|
987 |
+
* Execute a snippet
|
988 |
+
*
|
989 |
+
* Code must NOT be escaped, as
|
990 |
+
* it will be executed directly
|
991 |
+
*
|
992 |
+
* @since Code Snippets 1.5
|
993 |
+
* @access public
|
994 |
+
*
|
995 |
+
* @param string $code The snippet code to execute
|
996 |
+
* @return $result The result of the code execution
|
997 |
+
*/
|
998 |
+
public function execute_snippet( $code ) {
|
999 |
+
ob_start();
|
1000 |
+
$result = eval( $code );
|
1001 |
+
$output = ob_get_contents();
|
1002 |
+
ob_end_clean();
|
1003 |
+
return $result;
|
1004 |
+
}
|
1005 |
+
|
1006 |
+
/**
|
1007 |
+
* Replaces the text 'Add New Snippet' with 'Edit Snippet'
|
1008 |
+
*
|
1009 |
+
* @since Code Snippets 1.1
|
1010 |
+
* @access private
|
1011 |
+
*
|
1012 |
+
* @param $title The current page title
|
1013 |
+
* @return $title The modified page title
|
1014 |
+
*/
|
1015 |
+
function admin_single_title( $title ) {
|
1016 |
+
return str_ireplace(
|
1017 |
+
__('Add New Snippet', 'code-snippets'),
|
1018 |
+
__('Edit Snippet', 'code-snippets'),
|
1019 |
+
$title
|
1020 |
+
);
|
1021 |
+
}
|
1022 |
+
|
1023 |
+
/**
|
1024 |
+
* Processes any action command and loads the help tabs
|
1025 |
+
*
|
1026 |
+
* @since Code Snippets 1.0
|
1027 |
+
* @access private
|
1028 |
+
*
|
1029 |
+
* @uses $wpdb To activate, deactivate and delete snippets
|
1030 |
+
*
|
1031 |
+
* @return void
|
1032 |
+
*/
|
1033 |
+
function load_admin_manage() {
|
1034 |
+
global $wpdb;
|
1035 |
+
|
1036 |
+
$this->create_tables(); // create the snippet tables if they do not exist
|
1037 |
+
|
1038 |
+
if ( isset( $_GET['action'], $_GET['id'] ) ) :
|
1039 |
+
|
1040 |
+
$id = intval( $_GET['id'] );
|
1041 |
+
|
1042 |
+
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'action', 'id' ) );
|
1043 |
+
|
1044 |
+
if ( 'activate' == $_GET['action'] ) {
|
1045 |
+
$this->activate( $id );
|
1046 |
+
wp_redirect( add_query_arg( 'activate', true ) );
|
1047 |
+
}
|
1048 |
+
elseif ( 'deactivate' == $_GET['action'] ) {
|
1049 |
+
$this->deactivate( $id );
|
1050 |
+
wp_redirect( add_query_arg( 'deactivate', true ) );
|
1051 |
+
}
|
1052 |
+
elseif ( 'delete' == $_GET['action'] ) {
|
1053 |
+
$this->delete_snippet( $id );
|
1054 |
+
wp_redirect( add_query_arg( 'delete', true ) );
|
1055 |
+
}
|
1056 |
+
elseif ( 'export' == $_GET['action'] ) {
|
1057 |
+
$this->export( $id );
|
1058 |
+
}
|
1059 |
+
elseif ( 'export-php' == $_GET['action'] ) {
|
1060 |
+
$this->export_php( $id );
|
1061 |
+
}
|
1062 |
+
|
1063 |
+
endif;
|
1064 |
+
|
1065 |
+
include $this->plugin_dir . 'includes/help/manage.php'; // Load the help tabs
|
1066 |
+
|
1067 |
+
/**
|
1068 |
+
* Initialize the snippet table class
|
1069 |
+
*/
|
1070 |
+
require_once $this->plugin_dir . 'includes/class-list-table.php';
|
1071 |
+
|
1072 |
+
global $code_snippets_list_table;
|
1073 |
+
$code_snippets_list_table = new Code_Snippets_List_Table();
|
1074 |
+
$code_snippets_list_table->prepare_items();
|
1075 |
+
}
|
1076 |
+
|
1077 |
+
/**
|
1078 |
+
* Loads the help tabs for the Edit Snippets page
|
1079 |
+
*
|
1080 |
+
* @since Code Snippets 1.0
|
1081 |
+
* @access private
|
1082 |
+
*
|
1083 |
+
* @uses $wpdb To save the posted snippet to the database
|
1084 |
+
* @uses wp_redirect To pass the results to the page
|
1085 |
+
*
|
1086 |
+
* @return void
|
1087 |
+
*/
|
1088 |
+
function load_admin_single() {
|
1089 |
+
|
1090 |
+
$this->create_tables(); // create the snippet tables if they do not exist
|
1091 |
+
|
1092 |
+
if ( isset( $_REQUEST['save_snippet'] ) ) {
|
1093 |
+
|
1094 |
+
if ( isset( $_REQUEST['snippet_id'] ) ) {
|
1095 |
+
$result = $this->save_snippet( array(
|
1096 |
+
'name' => $_REQUEST['snippet_name'],
|
1097 |
+
'description' => $_REQUEST['snippet_description'],
|
1098 |
+
'code' => $_REQUEST['snippet_code'],
|
1099 |
+
'id' => $_REQUEST['snippet_id'],
|
1100 |
+
) );
|
1101 |
+
} else {
|
1102 |
+
$result = $this->save_snippet( array(
|
1103 |
+
'name' => $_REQUEST['snippet_name'],
|
1104 |
+
'description' => $_REQUEST['snippet_description'],
|
1105 |
+
'code' => $_REQUEST['snippet_code'],
|
1106 |
+
) );
|
1107 |
+
}
|
1108 |
+
|
1109 |
+
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'added', 'updated', 'invalid' ) );
|
1110 |
+
|
1111 |
+
if ( ! $result || $result < 1 ) {
|
1112 |
+
wp_redirect( add_query_arg( 'invalid', true ) );
|
1113 |
+
}
|
1114 |
+
elseif ( isset( $_REQUEST['snippet_id'] ) ) {
|
1115 |
+
wp_redirect( add_query_arg( array(
|
1116 |
+
'edit' => $result,
|
1117 |
+
'updated' => true
|
1118 |
+
) ) );
|
1119 |
+
}
|
1120 |
+
else {
|
1121 |
+
wp_redirect( add_query_arg( array(
|
1122 |
+
'edit' => $result,
|
1123 |
+
'added' => true
|
1124 |
+
) ) );
|
1125 |
+
}
|
1126 |
+
}
|
1127 |
+
|
1128 |
+
if ( isset( $_GET['edit'] ) )
|
1129 |
+
add_filter( 'admin_title', array( $this, 'admin_single_title' ) );
|
1130 |
+
|
1131 |
+
include $this->plugin_dir . 'includes/help/single.php'; // Load the help tabs
|
1132 |
+
}
|
1133 |
+
|
1134 |
+
/**
|
1135 |
+
* Processes import files and loads the help tabs for the Import Snippets page
|
1136 |
+
*
|
1137 |
+
* @since Code Snippets 1.3
|
1138 |
+
*
|
1139 |
+
* @uses $this->import() To process the import file
|
1140 |
+
* @uses wp_redirect() To pass the import results to the page
|
1141 |
+
* @uses add_query_arg() To append the results to the current URI
|
1142 |
+
*
|
1143 |
+
* @return void
|
1144 |
+
*/
|
1145 |
+
function load_admin_import() {
|
1146 |
+
|
1147 |
+
$this->create_tables(); // create the snippet tables if they do not exist
|
1148 |
+
|
1149 |
+
if ( isset( $_FILES['code_snippets_import_file']['tmp_name'] ) ) {
|
1150 |
+
$count = $this->import( $_FILES['code_snippets_import_file']['tmp_name'] );
|
1151 |
+
if ( $count ) {
|
1152 |
+
wp_redirect( add_query_arg( 'imported', $count ) );
|
1153 |
+
}
|
1154 |
+
}
|
1155 |
+
require_once $this->plugin_dir . 'includes/help/import.php';
|
1156 |
+
}
|
1157 |
+
|
1158 |
+
/**
|
1159 |
+
* Displays the Manage Snippets page
|
1160 |
+
*
|
1161 |
+
* @since Code Snippets 1.0
|
1162 |
+
* @access private
|
1163 |
+
*
|
1164 |
+
* @return void
|
1165 |
+
*/
|
1166 |
+
function display_admin_manage() {
|
1167 |
+
require_once $this->plugin_dir . 'includes/admin/manage.php';
|
1168 |
+
}
|
1169 |
+
|
1170 |
+
/**
|
1171 |
+
* Displays the Add New/Edit Snippet page
|
1172 |
+
*
|
1173 |
+
* @since Code Snippets 1.0
|
1174 |
+
* @access private
|
1175 |
+
*
|
1176 |
+
* @return void
|
1177 |
+
*/
|
1178 |
+
function display_admin_single() {
|
1179 |
+
require_once $this->plugin_dir . 'includes/admin/single.php';
|
1180 |
+
}
|
1181 |
+
|
1182 |
+
/**
|
1183 |
+
* Displays the Import Snippets page
|
1184 |
+
*
|
1185 |
+
* @since Code Snippets 1.3
|
1186 |
+
* @access private
|
1187 |
+
*
|
1188 |
+
* @return void
|
1189 |
+
*/
|
1190 |
+
function display_admin_import() {
|
1191 |
+
require_once $this->plugin_dir . 'includes/admin/import.php';
|
1192 |
+
}
|
1193 |
+
|
1194 |
+
/**
|
1195 |
+
* Adds a link pointing to the Manage Snippets page
|
1196 |
+
*
|
1197 |
+
* @since Code Snippets 1.0
|
1198 |
+
* @access private
|
1199 |
+
*
|
1200 |
+
* @return void
|
1201 |
+
*/
|
1202 |
+
function settings_link( $links ) {
|
1203 |
+
array_unshift( $links, sprintf(
|
1204 |
+
'<a href="%1$s" title="%2$s">%3$s</a>',
|
1205 |
+
$this->admin_manage_url,
|
1206 |
+
__('Manage your existing snippets', 'code-snippets'),
|
1207 |
+
__('Manage', 'code-snippets')
|
1208 |
+
) );
|
1209 |
+
return $links;
|
1210 |
+
}
|
1211 |
+
|
1212 |
+
/**
|
1213 |
+
* Adds extra links related to the plugin
|
1214 |
+
*
|
1215 |
+
* @since Code Snippets 1.2
|
1216 |
+
* @access private
|
1217 |
+
*/
|
1218 |
+
function plugin_meta( $links, $file ) {
|
1219 |
+
|
1220 |
+
if ( $file != $this->basename ) return $links;
|
1221 |
+
|
1222 |
+
$format = '<a href="%1$s" title="%2$s">%3$s</a>';
|
1223 |
+
|
1224 |
+
return array_merge( $links, array(
|
1225 |
+
sprintf( $format,
|
1226 |
+
'http://wordpress.org/extend/plugins/code-snippets/',
|
1227 |
+
__('Visit the WordPress.org plugin page', 'code-snippets'),
|
1228 |
+
__('About', 'code-snippets')
|
1229 |
+
),
|
1230 |
+
sprintf( $format,
|
1231 |
+
'http://wordpress.org/support/plugin/code-snippets/',
|
1232 |
+
__('Visit the support forums', 'code-snippets'),
|
1233 |
+
__('Support', 'code-snippets')
|
1234 |
+
),
|
1235 |
+
sprintf( $format,
|
1236 |
+
'http://code-snippets.bungeshea.com/donate/',
|
1237 |
+
__('Support this plugin\'s development', 'code-snippets'),
|
1238 |
+
__('Donate', 'code-snippets')
|
1239 |
+
)
|
1240 |
+
) );
|
1241 |
+
}
|
1242 |
+
|
1243 |
+
/**
|
1244 |
+
* Run the active snippets
|
1245 |
+
*
|
1246 |
+
* @since Code Snippets 1.0
|
1247 |
+
* @access private
|
1248 |
+
*
|
1249 |
+
* @uses $wpdb To grab the active snippets from the database
|
1250 |
+
* @uses $this->execute_snippet() To execute a snippet
|
1251 |
+
* @uses $this->get_table_name() To retrieve the name of the snippet table
|
1252 |
+
*
|
1253 |
+
* @param string $scope Execute network-wide or site-wide snippets?
|
1254 |
+
* @return void
|
1255 |
+
*/
|
1256 |
+
function run_snippets() {
|
1257 |
+
|
1258 |
+
if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE )
|
1259 |
+
return;
|
1260 |
+
|
1261 |
+
global $wpdb;
|
1262 |
+
|
1263 |
+
// grab the active snippets from the database
|
1264 |
+
$active_snippets = $wpdb->get_results( "SELECT code FROM $this->table WHERE active=1;" );
|
1265 |
+
|
1266 |
+
if ( is_multisite() ) {
|
1267 |
+
$active_snippets = array_merge(
|
1268 |
+
$wpdb->get_results( "SELECT code FROM $this->ms_table WHERE active=1;" ),
|
1269 |
+
$active_snippets
|
1270 |
+
);
|
1271 |
+
}
|
1272 |
+
|
1273 |
+
if ( count( $active_snippets ) ) {
|
1274 |
+
foreach( $active_snippets as $snippet ) {
|
1275 |
+
// execute the php code
|
1276 |
+
$this->execute_snippet( htmlspecialchars_decode( stripslashes( $snippet->code ) ) );
|
1277 |
+
}
|
1278 |
+
}
|
1279 |
+
}
|
1280 |
+
}
|
1281 |
+
|
1282 |
+
/**
|
1283 |
+
* The global variable in which the Code Snippets class is stored
|
1284 |
+
*
|
1285 |
+
* @since Code Snippets 1.0
|
1286 |
+
* @access public
|
1287 |
+
*/
|
1288 |
+
global $code_snippets;
|
1289 |
+
$code_snippets = new Code_Snippets;
|
1290 |
+
|
1291 |
+
/* set up a pointer in the old variable (for backwards-compatibility) */
|
1292 |
+
global $cs;
|
1293 |
+
$cs = &$code_snippets;
|
1294 |
+
|
1295 |
+
endif; // class exists check
|
1296 |
+
|
1297 |
+
register_uninstall_hook( $code_snippets->file, 'code_snippets_uninstall' );
|
1298 |
+
|
1299 |
+
/**
|
1300 |
+
* Cleans up data created by the Code_Snippets class
|
1301 |
+
*
|
1302 |
+
* @since Code Snippets 1.2
|
1303 |
+
* @access private
|
1304 |
+
*
|
1305 |
+
* @uses $wpdb To remove tables from the database
|
1306 |
+
* @uses $code_snippets To find out which table to drop
|
1307 |
+
* @uses is_multisite() To check the type of installation
|
1308 |
+
* @uses switch_to_blog() To switch between blogs
|
1309 |
+
* @uses restore_current_blog() To switch between blogs
|
1310 |
+
* @uses delete_option() To remove site options
|
1311 |
+
*
|
1312 |
+
* @return void
|
1313 |
+
*/
|
1314 |
+
function code_snippets_uninstall() {
|
1315 |
+
global $wpdb, $code_snippets;
|
1316 |
+
if ( is_multisite() ) {
|
1317 |
+
$blogs = $wpdb->get_results( "SELECT blog_id FROM $wpdb->blogs", ARRAY_A );
|
1318 |
+
if ( $blogs ) {
|
1319 |
+
foreach( $blogs as $blog ) {
|
1320 |
+
switch_to_blog( $blog['blog_id'] );
|
1321 |
+
$table = apply_filters( 'code_snippets_table', $wpdb->prefix . 'snippets' );
|
1322 |
+
$wpdb->query( "DROP TABLE IF EXISTS $table" );
|
1323 |
+
delete_option( 'cs_db_version' );
|
1324 |
+
delete_option( 'recently_activated_snippets' );
|
1325 |
+
$code_snippets->remove_caps();
|
1326 |
+
}
|
1327 |
+
restore_current_blog();
|
1328 |
+
}
|
1329 |
+
$wpdb->query( "DROP TABLE IF EXISTS $code_snippets->ms_table" );
|
1330 |
+
delete_site_option( 'recently_activated_snippets' );
|
1331 |
+
$code_snippets->remove_caps( 'multisite' );
|
1332 |
+
} else {
|
1333 |
+
$wpdb->query( "DROP TABLE IF EXISTS $code_snippets->table" );
|
1334 |
+
delete_option( 'recently_activated_snippets' );
|
1335 |
+
delete_option( 'cs_db_version' );
|
1336 |
+
$code_snippets->remove_caps();
|
1337 |
+
}
|
1338 |
+
|
1339 |
+
delete_site_option( 'code_snippets_version' );
|
1340 |
}
|
css/codemirror.css
DELETED
@@ -1,173 +0,0 @@
|
|
1 |
-
.CodeMirror {
|
2 |
-
line-height: 1em;
|
3 |
-
font-family: monospace;
|
4 |
-
|
5 |
-
/* Necessary so the scrollbar can be absolutely positioned within the wrapper on Lion. */
|
6 |
-
position: relative;
|
7 |
-
/* This prevents unwanted scrollbars from showing up on the body and wrapper in IE. */
|
8 |
-
overflow: hidden;
|
9 |
-
}
|
10 |
-
|
11 |
-
.CodeMirror-scroll {
|
12 |
-
overflow: auto;
|
13 |
-
height: 300px;
|
14 |
-
/* This is needed to prevent an IE[67] bug where the scrolled content
|
15 |
-
is visible outside of the scrolling box. */
|
16 |
-
position: relative;
|
17 |
-
outline: none;
|
18 |
-
}
|
19 |
-
|
20 |
-
/* Vertical scrollbar */
|
21 |
-
.CodeMirror-scrollbar {
|
22 |
-
position: absolute;
|
23 |
-
right: 0; top: 0;
|
24 |
-
overflow-x: hidden;
|
25 |
-
overflow-y: scroll;
|
26 |
-
z-index: 5;
|
27 |
-
}
|
28 |
-
.CodeMirror-scrollbar-inner {
|
29 |
-
/* This needs to have a nonzero width in order for the scrollbar to appear
|
30 |
-
in Firefox and IE9. */
|
31 |
-
width: 1px;
|
32 |
-
}
|
33 |
-
.CodeMirror-scrollbar.cm-sb-overlap {
|
34 |
-
/* Ensure that the scrollbar appears in Lion, and that it overlaps the content
|
35 |
-
rather than sitting to the right of it. */
|
36 |
-
position: absolute;
|
37 |
-
z-index: 1;
|
38 |
-
float: none;
|
39 |
-
right: 0;
|
40 |
-
min-width: 12px;
|
41 |
-
}
|
42 |
-
.CodeMirror-scrollbar.cm-sb-nonoverlap {
|
43 |
-
min-width: 12px;
|
44 |
-
}
|
45 |
-
.CodeMirror-scrollbar.cm-sb-ie7 {
|
46 |
-
min-width: 18px;
|
47 |
-
}
|
48 |
-
|
49 |
-
.CodeMirror-gutter {
|
50 |
-
position: absolute; left: 0; top: 0;
|
51 |
-
z-index: 10;
|
52 |
-
background-color: #f7f7f7;
|
53 |
-
border-right: 1px solid #eee;
|
54 |
-
min-width: 2em;
|
55 |
-
height: 100%;
|
56 |
-
}
|
57 |
-
.CodeMirror-gutter-text {
|
58 |
-
color: #aaa;
|
59 |
-
text-align: right;
|
60 |
-
padding: .4em .2em .4em .4em;
|
61 |
-
white-space: pre !important;
|
62 |
-
cursor: default;
|
63 |
-
}
|
64 |
-
.CodeMirror-lines {
|
65 |
-
padding: .4em;
|
66 |
-
white-space: pre;
|
67 |
-
cursor: text;
|
68 |
-
}
|
69 |
-
|
70 |
-
.CodeMirror pre {
|
71 |
-
-moz-border-radius: 0;
|
72 |
-
-webkit-border-radius: 0;
|
73 |
-
-o-border-radius: 0;
|
74 |
-
border-radius: 0;
|
75 |
-
border-width: 0; margin: 0; padding: 0; background: transparent;
|
76 |
-
font-family: inherit;
|
77 |
-
font-size: inherit;
|
78 |
-
padding: 0; margin: 0;
|
79 |
-
white-space: pre;
|
80 |
-
word-wrap: normal;
|
81 |
-
line-height: inherit;
|
82 |
-
color: inherit;
|
83 |
-
}
|
84 |
-
|
85 |
-
.CodeMirror-wrap pre {
|
86 |
-
word-wrap: break-word;
|
87 |
-
white-space: pre-wrap;
|
88 |
-
word-break: normal;
|
89 |
-
}
|
90 |
-
.CodeMirror-wrap .CodeMirror-scroll {
|
91 |
-
overflow-x: hidden;
|
92 |
-
}
|
93 |
-
|
94 |
-
.CodeMirror textarea {
|
95 |
-
outline: none !important;
|
96 |
-
}
|
97 |
-
|
98 |
-
.CodeMirror pre.CodeMirror-cursor {
|
99 |
-
z-index: 10;
|
100 |
-
position: absolute;
|
101 |
-
visibility: hidden;
|
102 |
-
border-left: 1px solid black;
|
103 |
-
border-right: none;
|
104 |
-
width: 0;
|
105 |
-
}
|
106 |
-
.cm-keymap-fat-cursor pre.CodeMirror-cursor {
|
107 |
-
width: auto;
|
108 |
-
border: 0;
|
109 |
-
background: transparent;
|
110 |
-
background: rgba(0, 200, 0, .4);
|
111 |
-
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
|
112 |
-
}
|
113 |
-
/* Kludge to turn off filter in ie9+, which also accepts rgba */
|
114 |
-
.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
|
115 |
-
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
116 |
-
}
|
117 |
-
.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
|
118 |
-
.CodeMirror-focused pre.CodeMirror-cursor {
|
119 |
-
visibility: visible;
|
120 |
-
}
|
121 |
-
|
122 |
-
div.CodeMirror-selected { background: #d9d9d9; }
|
123 |
-
.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
|
124 |
-
|
125 |
-
.CodeMirror-searching {
|
126 |
-
background: #ffa;
|
127 |
-
background: rgba(255, 255, 0, .4);
|
128 |
-
}
|
129 |
-
|
130 |
-
/* Default theme */
|
131 |
-
|
132 |
-
.cm-s-default span.cm-keyword {color: #708;}
|
133 |
-
.cm-s-default span.cm-atom {color: #219;}
|
134 |
-
.cm-s-default span.cm-number {color: #164;}
|
135 |
-
.cm-s-default span.cm-def {color: #00f;}
|
136 |
-
.cm-s-default span.cm-variable {color: black;}
|
137 |
-
.cm-s-default span.cm-variable-2 {color: #05a;}
|
138 |
-
.cm-s-default span.cm-variable-3 {color: #085;}
|
139 |
-
.cm-s-default span.cm-property {color: black;}
|
140 |
-
.cm-s-default span.cm-operator {color: black;}
|
141 |
-
.cm-s-default span.cm-comment {color: #a50;}
|
142 |
-
.cm-s-default span.cm-string {color: #a11;}
|
143 |
-
.cm-s-default span.cm-string-2 {color: #f50;}
|
144 |
-
.cm-s-default span.cm-meta {color: #555;}
|
145 |
-
.cm-s-default span.cm-error {color: #f00;}
|
146 |
-
.cm-s-default span.cm-qualifier {color: #555;}
|
147 |
-
.cm-s-default span.cm-builtin {color: #30a;}
|
148 |
-
.cm-s-default span.cm-bracket {color: #cc7;}
|
149 |
-
.cm-s-default span.cm-tag {color: #170;}
|
150 |
-
.cm-s-default span.cm-attribute {color: #00c;}
|
151 |
-
.cm-s-default span.cm-header {color: blue;}
|
152 |
-
.cm-s-default span.cm-quote {color: #090;}
|
153 |
-
.cm-s-default span.cm-hr {color: #999;}
|
154 |
-
.cm-s-default span.cm-link {color: #00c;}
|
155 |
-
|
156 |
-
span.cm-header, span.cm-strong {font-weight: bold;}
|
157 |
-
span.cm-em {font-style: italic;}
|
158 |
-
span.cm-emstrong {font-style: italic; font-weight: bold;}
|
159 |
-
span.cm-link {text-decoration: underline;}
|
160 |
-
|
161 |
-
span.cm-invalidchar {color: #f00;}
|
162 |
-
|
163 |
-
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
164 |
-
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
165 |
-
|
166 |
-
@media print {
|
167 |
-
|
168 |
-
/* Hide the cursor when printing */
|
169 |
-
.CodeMirror pre.CodeMirror-cursor {
|
170 |
-
visibility: hidden;
|
171 |
-
}
|
172 |
-
|
173 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/admin/import.php
CHANGED
@@ -1,27 +1,38 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
<div class="wrap">
|
15 |
<?php screen_icon(); ?>
|
16 |
<h2><?php _e('Import Snippets', 'code-snippets'); ?></h2>
|
|
|
17 |
<div class="narrow">
|
|
|
18 |
<p><?php _e('Howdy! Upload your Code Snippets export file and we’ll import the snippets to this site.', 'code-snippets' ); ?></p>
|
|
|
19 |
<p><?php printf( __('You will need to go to the <a href="%s">Manage Snippets</a> page to activate the imported snippets.', 'code-snippets'), $this->admin_manage_url ); ?></p>
|
|
|
20 |
<p><?php _e('Choose a Code Snippets (.xml) file to upload, then click Upload file and import.', 'code-snippets'); ?></p>
|
21 |
-
<form enctype="multipart/form-data" id="import-upload-form" method="post" action="" name="
|
22 |
<p>
|
23 |
<label for="upload"><?php _e('Choose a file from your computer:', 'code-snippets' ); ?></label> <?php _e('(Maximum size: 8MB)', 'code-snippets'); ?>
|
24 |
-
<input type="file" id="upload" name="
|
25 |
<input type="hidden" name="action" value="save" />
|
26 |
<input type="hidden" name="max_file_size" value="8388608" />
|
27 |
</p>
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( isset( $_REQUEST['imported'] ) && intval( $_REQUEST['imported'] ) != 0 ) {
|
4 |
+
|
5 |
+
echo '<div id="message" class="updated fade"><p>';
|
6 |
+
|
7 |
+
printf(
|
8 |
+
_n(
|
9 |
+
'Imported <strong>%d</strong> snippet.',
|
10 |
+
'Imported <strong>%d</strong> snippets.',
|
11 |
+
$_REQUEST['imported'],
|
12 |
+
'code-snippets'
|
13 |
+
),
|
14 |
+
$_REQUEST['imported']
|
15 |
+
);
|
16 |
+
|
17 |
+
echo '</p></div>';
|
18 |
+
}
|
19 |
+
|
20 |
+
?>
|
21 |
<div class="wrap">
|
22 |
<?php screen_icon(); ?>
|
23 |
<h2><?php _e('Import Snippets', 'code-snippets'); ?></h2>
|
24 |
+
|
25 |
<div class="narrow">
|
26 |
+
|
27 |
<p><?php _e('Howdy! Upload your Code Snippets export file and we’ll import the snippets to this site.', 'code-snippets' ); ?></p>
|
28 |
+
|
29 |
<p><?php printf( __('You will need to go to the <a href="%s">Manage Snippets</a> page to activate the imported snippets.', 'code-snippets'), $this->admin_manage_url ); ?></p>
|
30 |
+
|
31 |
<p><?php _e('Choose a Code Snippets (.xml) file to upload, then click Upload file and import.', 'code-snippets'); ?></p>
|
32 |
+
<form enctype="multipart/form-data" id="import-upload-form" method="post" action="" name="code_snippets_import">
|
33 |
<p>
|
34 |
<label for="upload"><?php _e('Choose a file from your computer:', 'code-snippets' ); ?></label> <?php _e('(Maximum size: 8MB)', 'code-snippets'); ?>
|
35 |
+
<input type="file" id="upload" name="code_snippets_import_file" size="25" accept="text/xml" />
|
36 |
<input type="hidden" name="action" value="save" />
|
37 |
<input type="hidden" name="max_file_size" value="8388608" />
|
38 |
</p>
|
includes/admin/manage.php
CHANGED
@@ -1,15 +1,15 @@
|
|
1 |
<?php
|
2 |
-
if( ! class_exists( 'Code_Snippets' ) ) exit;
|
3 |
|
4 |
require_once $this->plugin_dir . 'includes/class-list-table.php';
|
5 |
|
6 |
-
global $
|
7 |
$screen = get_current_screen();
|
8 |
?>
|
9 |
-
<?php if( defined( '
|
10 |
-
<div class="error"><p><strong>Warning:</strong> Safe mode is active and snippets will not execute! Remove the <code>
|
11 |
<?php endif; ?>
|
12 |
-
|
13 |
<?php if ( isset($_GET['activate']) ) : ?>
|
14 |
<div id="message" class="updated"><p><?php _e('Snippet <strong>activated</strong>.', 'code-snippets') ?></p></div>
|
15 |
<?php elseif (isset($_GET['activate-multi'])) : ?>
|
@@ -23,24 +23,24 @@ $screen = get_current_screen();
|
|
23 |
<?php elseif (isset($_GET['delete-multi'])) : ?>
|
24 |
<div id="message" class="updated"><p><?php _e('Selected snippets <strong>deleted</strong>.', 'code-snippets'); ?></p></div>
|
25 |
<?php endif; ?>
|
26 |
-
|
27 |
<div class="wrap">
|
28 |
<?php screen_icon(); ?>
|
29 |
<h2><?php _e('Snippets', 'code-snippets'); ?>
|
30 |
-
<?php if( current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' ) ) { ?>
|
31 |
<a href="<?php echo $this->admin_single_url; ?>" class="add-new-h2"><?php echo esc_html_x('Add New', 'snippet', 'code-snippets'); ?></a>
|
32 |
<?php }
|
33 |
-
if( isset( $s ) && $s )
|
34 |
printf( '<span class="subtitle">' . __('Search results for “%s”', 'code-snippets') . '</span>', esc_html( $s ) ); ?></h2>
|
35 |
-
|
36 |
-
<?php $
|
37 |
-
|
38 |
<form method="get" action="">
|
39 |
<input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
|
40 |
-
<?php $
|
41 |
</form>
|
42 |
<form method="post" action="">
|
43 |
<input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
|
44 |
-
<?php $
|
45 |
</form>
|
46 |
</div>
|
1 |
<?php
|
2 |
+
if ( ! class_exists( 'Code_Snippets' ) ) exit;
|
3 |
|
4 |
require_once $this->plugin_dir . 'includes/class-list-table.php';
|
5 |
|
6 |
+
global $code_snippets_list_table;
|
7 |
$screen = get_current_screen();
|
8 |
?>
|
9 |
+
<?php if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) : ?>
|
10 |
+
<div class="error"><p><strong>Warning:</strong> Safe mode is active and snippets will not execute! Remove the <code>CODE_SNIPPETS_SAFE_MODE</code> constant from <code>wp-config.php</code> to turn off safe mode. <a href="http://code-snippets.bungeshea.com/docs/safe-mode/" target="_blank">Help</a></p></div>
|
11 |
<?php endif; ?>
|
12 |
+
|
13 |
<?php if ( isset($_GET['activate']) ) : ?>
|
14 |
<div id="message" class="updated"><p><?php _e('Snippet <strong>activated</strong>.', 'code-snippets') ?></p></div>
|
15 |
<?php elseif (isset($_GET['activate-multi'])) : ?>
|
23 |
<?php elseif (isset($_GET['delete-multi'])) : ?>
|
24 |
<div id="message" class="updated"><p><?php _e('Selected snippets <strong>deleted</strong>.', 'code-snippets'); ?></p></div>
|
25 |
<?php endif; ?>
|
26 |
+
|
27 |
<div class="wrap">
|
28 |
<?php screen_icon(); ?>
|
29 |
<h2><?php _e('Snippets', 'code-snippets'); ?>
|
30 |
+
<?php if ( current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' ) ) { ?>
|
31 |
<a href="<?php echo $this->admin_single_url; ?>" class="add-new-h2"><?php echo esc_html_x('Add New', 'snippet', 'code-snippets'); ?></a>
|
32 |
<?php }
|
33 |
+
if ( isset( $s ) && $s )
|
34 |
printf( '<span class="subtitle">' . __('Search results for “%s”', 'code-snippets') . '</span>', esc_html( $s ) ); ?></h2>
|
35 |
+
|
36 |
+
<?php $code_snippets_list_table->views(); ?>
|
37 |
+
|
38 |
<form method="get" action="">
|
39 |
<input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
|
40 |
+
<?php $code_snippets_list_table->search_box( __( 'Search Installed Snippets', 'code-snippets' ), 'search_id' ); ?>
|
41 |
</form>
|
42 |
<form method="post" action="">
|
43 |
<input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
|
44 |
+
<?php $code_snippets_list_table->display(); ?>
|
45 |
</form>
|
46 |
</div>
|
includes/admin/single.php
CHANGED
@@ -1,33 +1,34 @@
|
|
1 |
<?php
|
2 |
-
if( ! class_exists( 'Code_Snippets' ) ) exit;
|
3 |
global $wpdb;
|
4 |
|
|
|
5 |
$screen = get_current_screen();
|
6 |
$can_edit = current_user_can( $screen->is_network ? 'edit_network_snippets' : 'edit_snippets' );
|
7 |
$can_install = current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' );
|
8 |
|
9 |
-
if( isset( $_REQUEST['edit'] ) && ! $can_edit )
|
10 |
-
wp_die( __(
|
11 |
-
|
12 |
-
if( isset( $_REQUEST['edit'] ) )
|
13 |
$edit_id = intval( $_REQUEST['edit'] );
|
14 |
?>
|
15 |
|
16 |
-
<?php if( isset( $_REQUEST['invalid'] ) && $_REQUEST['invalid'] ) : ?>
|
17 |
<div id="message" class="error fade"><p><?php _e('Please provide a name for the snippet and its code.', 'code-snippets'); ?></p></div>
|
18 |
-
<?php elseif( isset( $_REQUEST['updated'] ) && $_REQUEST['updated'] ) : ?>
|
19 |
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>updated</strong>.', 'code-snippets'); ?></p></div>
|
20 |
-
<?php elseif( isset( $_REQUEST['added'] ) && $_REQUEST['added'] ) : ?>
|
21 |
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>added</strong>.', 'code-snippets'); ?></p></div>
|
22 |
<?php endif; ?>
|
23 |
|
24 |
<div class="wrap">
|
25 |
<?php screen_icon(); ?>
|
26 |
<h2><?php
|
27 |
-
if( isset( $edit_id ) ) {
|
28 |
esc_html_e('Edit Snippet', 'code-snippets');
|
29 |
-
|
30 |
-
if(
|
31 |
printf( ' <a href="%1$s" class="add-new-h2">%2$s</a>',
|
32 |
$this->admin_single_url,
|
33 |
esc_html('Add New', 'code-snippets')
|
@@ -36,21 +37,20 @@ if( isset( $_REQUEST['edit'] ) )
|
|
36 |
_e('Add New Snippet', 'code-snippets');
|
37 |
}
|
38 |
?></h2>
|
39 |
-
|
40 |
<form method="post" action="" style="margin-top: 10px;">
|
41 |
-
<?php
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
// define a empty object (or one with default values)
|
47 |
$snippet = new stdClass();
|
48 |
$snippet->name = '';
|
49 |
$snippet->description = '';
|
50 |
$snippet->code = '';
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
<div id="titlediv">
|
55 |
<div id="titlewrap">
|
56 |
<label for="title" style="display: none;"><?php esc_html_e('Name (short title)', 'code-snippets'); ?></label>
|
@@ -62,16 +62,16 @@ if( isset( $_REQUEST['edit'] ) )
|
|
62 |
<h3 style="display: inline;"><?php esc_html_e('Code', 'code-snippets'); ?></h3>
|
63 |
<span style="float: right;"><?php _e('Enter or paste the snippet code without the <code><?php</code> and <code>?></code> tags.', 'code-snippets'); ?></span>
|
64 |
</label>
|
65 |
-
<br />
|
66 |
-
<textarea id="snippet_code" name="snippet_code" spellcheck="false" style="font-family: monospace; width:100%;"><?php echo stripslashes( $snippet->code ); ?></textarea>
|
67 |
-
<br style="margin: 20px;" />
|
68 |
|
69 |
-
<
|
70 |
-
|
|
|
|
|
|
|
|
|
|
|
71 |
</label>
|
72 |
-
|
73 |
-
<br />
|
74 |
-
|
75 |
<?php
|
76 |
wp_editor(
|
77 |
htmlspecialchars_decode( stripslashes( $snippet->description ) ),
|
@@ -79,6 +79,7 @@ if( isset( $_REQUEST['edit'] ) )
|
|
79 |
array(
|
80 |
'textarea_name' => 'snippet_description',
|
81 |
'textarea_rows' => 10,
|
|
|
82 |
)
|
83 |
);
|
84 |
?>
|
@@ -90,10 +91,10 @@ if( isset( $_REQUEST['edit'] ) )
|
|
90 |
</div>
|
91 |
<script type="text/javascript">
|
92 |
var editor = CodeMirror.fromTextArea(document.getElementById("snippet_code"), {
|
93 |
-
mode: "application/x-httpd-php-open",
|
94 |
lineNumbers: true,
|
95 |
-
lineWrapping: true,
|
96 |
matchBrackets: true,
|
|
|
|
|
97 |
indentUnit: 4,
|
98 |
indentWithTabs: true,
|
99 |
enterMode: "keep",
|
1 |
<?php
|
2 |
+
if ( ! class_exists( 'Code_Snippets' ) ) exit;
|
3 |
global $wpdb;
|
4 |
|
5 |
+
$table = $this->get_table_name();
|
6 |
$screen = get_current_screen();
|
7 |
$can_edit = current_user_can( $screen->is_network ? 'edit_network_snippets' : 'edit_snippets' );
|
8 |
$can_install = current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' );
|
9 |
|
10 |
+
if ( isset( $_REQUEST['edit'] ) && ! $can_edit )
|
11 |
+
wp_die( __('Sorry, you’re not allowed to edit snippets', 'code-snippets') );
|
12 |
+
|
13 |
+
if ( isset( $_REQUEST['edit'] ) )
|
14 |
$edit_id = intval( $_REQUEST['edit'] );
|
15 |
?>
|
16 |
|
17 |
+
<?php if ( isset( $_REQUEST['invalid'] ) && $_REQUEST['invalid'] ) : ?>
|
18 |
<div id="message" class="error fade"><p><?php _e('Please provide a name for the snippet and its code.', 'code-snippets'); ?></p></div>
|
19 |
+
<?php elseif ( isset( $_REQUEST['updated'] ) && $_REQUEST['updated'] ) : ?>
|
20 |
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>updated</strong>.', 'code-snippets'); ?></p></div>
|
21 |
+
<?php elseif ( isset( $_REQUEST['added'] ) && $_REQUEST['added'] ) : ?>
|
22 |
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>added</strong>.', 'code-snippets'); ?></p></div>
|
23 |
<?php endif; ?>
|
24 |
|
25 |
<div class="wrap">
|
26 |
<?php screen_icon(); ?>
|
27 |
<h2><?php
|
28 |
+
if ( isset( $edit_id ) ) {
|
29 |
esc_html_e('Edit Snippet', 'code-snippets');
|
30 |
+
|
31 |
+
if ( $can_install )
|
32 |
printf( ' <a href="%1$s" class="add-new-h2">%2$s</a>',
|
33 |
$this->admin_single_url,
|
34 |
esc_html('Add New', 'code-snippets')
|
37 |
_e('Add New Snippet', 'code-snippets');
|
38 |
}
|
39 |
?></h2>
|
40 |
+
|
41 |
<form method="post" action="" style="margin-top: 10px;">
|
42 |
+
<?php
|
43 |
+
if ( isset( $edit_id ) ) {
|
44 |
+
$snippet = $wpdb->get_row( "SELECT * FROM $table WHERE id = $edit_id" );
|
45 |
+
printf ( '<input type="hidden" name="snippet_id" value="%d" />', $snippet->id );
|
46 |
+
} else {
|
47 |
// define a empty object (or one with default values)
|
48 |
$snippet = new stdClass();
|
49 |
$snippet->name = '';
|
50 |
$snippet->description = '';
|
51 |
$snippet->code = '';
|
52 |
+
}
|
53 |
+
?>
|
|
|
54 |
<div id="titlediv">
|
55 |
<div id="titlewrap">
|
56 |
<label for="title" style="display: none;"><?php esc_html_e('Name (short title)', 'code-snippets'); ?></label>
|
62 |
<h3 style="display: inline;"><?php esc_html_e('Code', 'code-snippets'); ?></h3>
|
63 |
<span style="float: right;"><?php _e('Enter or paste the snippet code without the <code><?php</code> and <code>?></code> tags.', 'code-snippets'); ?></span>
|
64 |
</label>
|
|
|
|
|
|
|
65 |
|
66 |
+
<textarea id="snippet_code" name="snippet_code" rows="20" spellcheck="false" style="font-family: monospace; width:100%;"><?php echo stripslashes( $snippet->code ); ?></textarea>
|
67 |
+
|
68 |
+
<label for="description">
|
69 |
+
<h3>
|
70 |
+
<?php esc_html_e('Description', 'code-snippets'); ?>
|
71 |
+
<span style="font-weight: normal; font-size: normal;"><?php _e('(Optional)', 'code-snippets'); ?></span>
|
72 |
+
</h3>
|
73 |
</label>
|
74 |
+
|
|
|
|
|
75 |
<?php
|
76 |
wp_editor(
|
77 |
htmlspecialchars_decode( stripslashes( $snippet->description ) ),
|
79 |
array(
|
80 |
'textarea_name' => 'snippet_description',
|
81 |
'textarea_rows' => 10,
|
82 |
+
'media_buttons' => false,
|
83 |
)
|
84 |
);
|
85 |
?>
|
91 |
</div>
|
92 |
<script type="text/javascript">
|
93 |
var editor = CodeMirror.fromTextArea(document.getElementById("snippet_code"), {
|
|
|
94 |
lineNumbers: true,
|
|
|
95 |
matchBrackets: true,
|
96 |
+
lineWrapping: true,
|
97 |
+
mode: "application/x-httpd-php-open",
|
98 |
indentUnit: 4,
|
99 |
indentWithTabs: true,
|
100 |
enterMode: "keep",
|
includes/class-list-table.php
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
if( ! class_exists( 'WP_List_Table' ) ) {
|
4 |
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
|
5 |
}
|
6 |
|
@@ -12,12 +12,12 @@ if( ! class_exists( 'WP_List_Table' ) ) {
|
|
12 |
* @access private
|
13 |
*/
|
14 |
class Code_Snippets_List_Table extends WP_List_Table {
|
15 |
-
|
16 |
function __construct() {
|
17 |
-
global $status, $page;
|
18 |
-
|
19 |
$screen = get_current_screen();
|
20 |
-
|
21 |
$status = 'all';
|
22 |
if ( isset( $_REQUEST['status'] ) && in_array( $_REQUEST['status'], array( 'active', 'inactive', 'recently_activated', 'search' ) ) )
|
23 |
$status = $_REQUEST['status'];
|
@@ -26,39 +26,71 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
26 |
$_SERVER['REQUEST_URI'] = add_query_arg( 's', stripslashes($_REQUEST['s'] ) );
|
27 |
|
28 |
$page = $this->get_pagenum();
|
29 |
-
|
30 |
add_screen_option( 'per_page', array(
|
31 |
'label' => __('Snippets per page', 'code-snippets'),
|
32 |
'default' => 10,
|
33 |
'option' => 'snippets_per_page'
|
34 |
) );
|
35 |
-
|
36 |
add_filter( "get_user_option_manage{$screen->id}columnshidden", array( $this, 'get_default_hidden_columns' ) );
|
37 |
-
|
|
|
|
|
38 |
parent::__construct( array(
|
39 |
'singular' => 'snippet',
|
40 |
'plural' => 'snippets',
|
41 |
'ajax' => true,
|
42 |
) );
|
43 |
}
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
function column_default( $item, $column_name ) {
|
46 |
switch( $column_name ) {
|
47 |
case 'id':
|
48 |
-
return intval( $item[$column_name] );
|
49 |
case 'description':
|
50 |
-
return stripslashes( html_entity_decode( $item[$column_name] ) );
|
51 |
default:
|
52 |
return print_r( $item, true ); // Show the whole array for troubleshooting purposes
|
53 |
}
|
54 |
}
|
55 |
-
|
56 |
function column_name( $item ) {
|
57 |
-
global $
|
58 |
$screen = get_current_screen();
|
59 |
$actions = array(); // Build row actions
|
60 |
-
|
61 |
-
if( $item['active'] ) {
|
62 |
$actions['deactivate'] = sprintf(
|
63 |
'<a href="%1$s">%2$s</a>',
|
64 |
add_query_arg( array(
|
@@ -79,10 +111,10 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
79 |
$screen->is_network ? __('Network Activate', 'code-snippets') : __('Activate', 'code-snippets')
|
80 |
);
|
81 |
}
|
82 |
-
|
83 |
$actions['edit'] = sprintf(
|
84 |
'<a href="%s&edit=%s">Edit</a>',
|
85 |
-
$
|
86 |
$item['id']
|
87 |
);
|
88 |
$actions['export'] = sprintf(
|
@@ -103,11 +135,11 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
103 |
esc_js( 'return confirm( "You are about to permanently delete the selected item.
|
104 |
\'Cancel\' to stop, \'OK\' to delete.");' )
|
105 |
);
|
106 |
-
|
107 |
// Return the name contents
|
108 |
return '<strong>' . stripslashes( $item['name'] ) . '</strong>' . $this->row_actions( $actions, true );
|
109 |
}
|
110 |
-
|
111 |
function column_cb( $item ) {
|
112 |
return sprintf(
|
113 |
'<input type="checkbox" name="%1$s[]" value="%2$s" />',
|
@@ -115,7 +147,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
115 |
/*$2%s*/ $item['id'] // The value of the checkbox should be the snippet's id
|
116 |
);
|
117 |
}
|
118 |
-
|
119 |
function get_columns() {
|
120 |
return array(
|
121 |
'cb' => '<input type="checkbox" />',
|
@@ -124,21 +156,21 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
124 |
'description' => __('Description', 'code-snippets'),
|
125 |
);
|
126 |
}
|
127 |
-
|
128 |
function get_sortable_columns() {
|
129 |
return array(
|
130 |
'id' => array( 'id', true ),
|
131 |
'name' => array( 'name', false ),
|
132 |
);
|
133 |
}
|
134 |
-
|
135 |
function get_default_hidden_columns( $result ) {
|
136 |
-
if( ! $result )
|
137 |
return array( 'id' );
|
138 |
else
|
139 |
return $result;
|
140 |
}
|
141 |
-
|
142 |
function get_bulk_actions() {
|
143 |
$screen = get_current_screen();
|
144 |
$actions = array(
|
@@ -146,15 +178,15 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
146 |
'deactivate-selected' => $screen->is_network ? __('Network Deactivate', 'code-snippets') : __('Deactivate', 'code-snippets'),
|
147 |
'export-selected' => __('Export', 'code-snippets'),
|
148 |
'delete-selected' => __('Delete', 'code-snippets'),
|
149 |
-
'
|
150 |
);
|
151 |
return $actions;
|
152 |
}
|
153 |
-
|
154 |
function get_table_classes() {
|
155 |
return array( 'widefat', $this->_args['plural'] );
|
156 |
}
|
157 |
-
|
158 |
function get_views() {
|
159 |
global $totals, $status;
|
160 |
|
@@ -179,17 +211,17 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
179 |
}
|
180 |
|
181 |
if ( 'search' != $type ) {
|
182 |
-
$status_links[$type] = sprintf(
|
183 |
add_query_arg('status', $type, '?page=' . $_REQUEST['page'] ),
|
184 |
( $type == $status ) ? ' class="current"' : '',
|
185 |
sprintf( $text, number_format_i18n( $count ) )
|
186 |
-
|
187 |
}
|
188 |
}
|
189 |
|
190 |
return $status_links;
|
191 |
}
|
192 |
-
|
193 |
function extra_tablenav( $which ) {
|
194 |
global $status;
|
195 |
|
@@ -212,93 +244,102 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
212 |
|
213 |
return parent::current_action();
|
214 |
}
|
215 |
-
|
216 |
/**
|
217 |
* Processes a bulk action
|
218 |
*
|
219 |
-
* @uses $
|
220 |
-
* @uses $
|
221 |
-
* @uses $
|
222 |
-
* @uses
|
223 |
* @uses wp_redirect To pass the results to the current page
|
224 |
* @uses add_query_arg() To append the results to the current URI
|
225 |
*/
|
226 |
function process_bulk_actions() {
|
227 |
-
global $
|
228 |
-
if( ! isset( $_POST[ $this->_args['singular'] ] ) ) return;
|
229 |
$ids = $_POST[ $this->_args['singular'] ];
|
230 |
-
|
231 |
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'activate', 'deactivate', 'delete', 'activate-multi', 'deactivate-multi', 'delete-multi' ) );
|
232 |
-
|
233 |
switch( $this->current_action() ) {
|
234 |
-
|
235 |
case 'activate-selected':
|
236 |
-
$
|
237 |
wp_redirect( add_query_arg( 'activate-multi', true ) );
|
238 |
break;
|
239 |
-
|
240 |
case 'deactivate-selected':
|
241 |
-
$
|
242 |
wp_redirect( add_query_arg( 'deactivate-multi', true ) );
|
243 |
break;
|
244 |
-
|
245 |
case 'export-selected':
|
246 |
-
$
|
247 |
break;
|
248 |
-
|
249 |
-
case '
|
250 |
-
$
|
251 |
break;
|
252 |
-
|
253 |
case 'delete-selected':
|
254 |
foreach( $ids as $id ) {
|
255 |
-
$
|
256 |
}
|
257 |
wp_redirect( add_query_arg( 'delete-multi', true ) );
|
258 |
break;
|
259 |
-
|
260 |
case 'clear-recent-list':
|
261 |
$screen = get_current_screen();
|
262 |
-
|
263 |
-
|
|
|
|
|
264 |
break;
|
265 |
}
|
266 |
}
|
267 |
-
|
268 |
function no_items() {
|
269 |
-
global $
|
270 |
-
printf( __('You do not appear to have any snippets available at this time. <a href="%s">Add New→</a>', 'code-snippets'), $
|
271 |
}
|
272 |
-
|
273 |
function prepare_items() {
|
274 |
-
|
275 |
-
global $wpdb, $
|
276 |
|
277 |
wp_reset_vars( array( 'orderby', 'order', 's' ) );
|
278 |
-
|
279 |
$screen = get_current_screen();
|
280 |
$user = get_current_user_id();
|
281 |
-
|
|
|
282 |
// first, lets process the bulk actions
|
283 |
$this->process_bulk_actions();
|
284 |
-
|
285 |
$snippets = array(
|
286 |
-
'all' => $wpdb->get_results(
|
287 |
'search' => array(),
|
288 |
'active' => array(),
|
289 |
'inactive' => array(),
|
290 |
'recently_activated' => array(),
|
291 |
);
|
292 |
-
|
293 |
-
|
294 |
-
|
|
|
|
|
295 |
|
296 |
$one_week = 7*24*60*60;
|
297 |
foreach ( $recently_activated as $key => $time )
|
298 |
if ( $time + $one_week < time() )
|
299 |
unset( $recently_activated[$key] );
|
300 |
-
|
301 |
-
|
|
|
|
|
|
|
|
|
302 |
foreach ( (array) $snippets['all'] as $snippet ) {
|
303 |
// Filter into individual sections
|
304 |
if ( $snippet['active'] ) {
|
@@ -310,20 +351,20 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
310 |
}
|
311 |
}
|
312 |
|
313 |
-
if( $s ) {
|
314 |
$status = 'search';
|
315 |
$snippets['search'] = array_filter( $snippets['all'], array( &$this, '_search_callback' ) );
|
316 |
}
|
317 |
-
|
318 |
$totals = array();
|
319 |
foreach ( $snippets as $type => $list )
|
320 |
$totals[ $type ] = count( $list );
|
321 |
-
|
322 |
if ( empty( $snippets[ $status ] ) && !in_array( $status, array( 'all', 'search' ) ) )
|
323 |
$status = 'all';
|
324 |
-
|
325 |
$data = $snippets[ $status ];
|
326 |
-
|
327 |
/**
|
328 |
* First, lets decide how many records per page to show
|
329 |
* by getting the user's setting in the Screen Opions
|
@@ -331,67 +372,67 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
331 |
*/
|
332 |
$sort_by = $screen->get_option( 'per_page', 'option' );
|
333 |
$screen_option = $screen->get_option( 'per_page', 'option' );
|
334 |
-
|
335 |
$per_page = get_user_meta( $user, $screen_option, true );
|
336 |
-
|
337 |
-
if( empty ( $per_page ) || $per_page < 1 ) {
|
338 |
$per_page = $screen->get_option( 'per_page', 'default' );
|
339 |
}
|
340 |
-
|
341 |
$per_page = (int) $per_page;
|
342 |
-
|
343 |
$this->_column_headers = $this->get_column_info();
|
344 |
-
|
345 |
/**
|
346 |
* This checks for sorting input and sorts the data in our array accordingly.
|
347 |
*/
|
348 |
function usort_reorder( $a, $b ) {
|
349 |
-
|
350 |
// If no sort, default to id
|
351 |
-
$orderby = ( ! empty($_REQUEST['orderby'] ) ) ? $_REQUEST['orderby'] : 'id';
|
352 |
-
|
353 |
// If no order, default to asc
|
354 |
$order = ( ! empty( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : 'asc';
|
355 |
-
|
356 |
// Determine sort order
|
357 |
-
if( $orderby === 'id' )
|
358 |
$result = $a[$orderby] - $b[$orderby]; // get the result for numerical data
|
359 |
else
|
360 |
$result = strcmp( $a[$orderby], $b[$orderby] ); // get the result for string data
|
361 |
-
|
362 |
// Send final sort direction to usort
|
363 |
return ( $order === 'asc' ) ? $result : -$result;
|
364 |
}
|
365 |
-
|
366 |
usort($data, 'usort_reorder');
|
367 |
-
|
368 |
-
|
369 |
/**
|
370 |
-
* Let's figure out what page the user is currently
|
371 |
* looking at.
|
372 |
*/
|
373 |
$current_page = $this->get_pagenum();
|
374 |
-
|
375 |
/**
|
376 |
* Let's check how many items are in our data array.
|
377 |
*/
|
378 |
$total_items = count($data);
|
379 |
-
|
380 |
-
|
381 |
/**
|
382 |
* The WP_List_Table class does not handle pagination for us, so we need
|
383 |
* to ensure that the data is trimmed to only the current page.
|
384 |
*/
|
385 |
$data = array_slice( $data, ( ( $current_page - 1 ) * $per_page ), $per_page );
|
386 |
-
|
387 |
-
|
388 |
/**
|
389 |
-
* Now we can add our *sorted* data to the items property, where
|
390 |
* it can be used by the rest of the class.
|
391 |
*/
|
392 |
$this->items = $data;
|
393 |
-
|
394 |
-
|
395 |
/**
|
396 |
* We also have to register our pagination options & calculations.
|
397 |
*/
|
@@ -401,7 +442,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
401 |
'total_pages' => ceil($total_items/$per_page) // WE have to calculate the total number of pages
|
402 |
) );
|
403 |
}
|
404 |
-
|
405 |
function _search_callback( $item ) {
|
406 |
static $term;
|
407 |
if ( is_null( $term ) )
|
@@ -413,7 +454,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
413 |
|
414 |
return false;
|
415 |
}
|
416 |
-
|
417 |
/**
|
418 |
* Generates content for a single row of the table
|
419 |
*/
|
1 |
<?php
|
2 |
|
3 |
+
if ( ! class_exists( 'WP_List_Table' ) ) {
|
4 |
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
|
5 |
}
|
6 |
|
12 |
* @access private
|
13 |
*/
|
14 |
class Code_Snippets_List_Table extends WP_List_Table {
|
15 |
+
|
16 |
function __construct() {
|
17 |
+
global $status, $page, $code_snippets;
|
18 |
+
|
19 |
$screen = get_current_screen();
|
20 |
+
|
21 |
$status = 'all';
|
22 |
if ( isset( $_REQUEST['status'] ) && in_array( $_REQUEST['status'], array( 'active', 'inactive', 'recently_activated', 'search' ) ) )
|
23 |
$status = $_REQUEST['status'];
|
26 |
$_SERVER['REQUEST_URI'] = add_query_arg( 's', stripslashes($_REQUEST['s'] ) );
|
27 |
|
28 |
$page = $this->get_pagenum();
|
29 |
+
|
30 |
add_screen_option( 'per_page', array(
|
31 |
'label' => __('Snippets per page', 'code-snippets'),
|
32 |
'default' => 10,
|
33 |
'option' => 'snippets_per_page'
|
34 |
) );
|
35 |
+
|
36 |
add_filter( "get_user_option_manage{$screen->id}columnshidden", array( $this, 'get_default_hidden_columns' ) );
|
37 |
+
add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
|
38 |
+
add_action( "admin_print_scripts-$code_snippets->admin_manage", array( $this, 'load_table_style' ) );
|
39 |
+
|
40 |
parent::__construct( array(
|
41 |
'singular' => 'snippet',
|
42 |
'plural' => 'snippets',
|
43 |
'ajax' => true,
|
44 |
) );
|
45 |
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Handles saving the user's screen option preference
|
49 |
+
*
|
50 |
+
* @since Code Snippets 1.5
|
51 |
+
* @access private
|
52 |
+
*/
|
53 |
+
function set_screen_option( $status, $option, $value ) {
|
54 |
+
if ( 'snippets_per_page' === $option ) return $value;
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Enqueue the table stylesheet
|
59 |
+
*
|
60 |
+
* @since Code Snippets 1.6
|
61 |
+
* @access private
|
62 |
+
*
|
63 |
+
* @uses wp_enqueue_style() To add the stylesheet to the queue
|
64 |
+
*
|
65 |
+
* @return void
|
66 |
+
*/
|
67 |
+
function load_table_style() {
|
68 |
+
global $code_snippets;
|
69 |
+
wp_enqueue_style(
|
70 |
+
'snippets-table',
|
71 |
+
plugins_url( 'assets/table.css', $code_snippets->file ),
|
72 |
+
false,
|
73 |
+
$code_snippets->version
|
74 |
+
);
|
75 |
+
}
|
76 |
+
|
77 |
function column_default( $item, $column_name ) {
|
78 |
switch( $column_name ) {
|
79 |
case 'id':
|
80 |
+
return intval( $item[ $column_name ] );
|
81 |
case 'description':
|
82 |
+
return stripslashes( html_entity_decode( $item[ $column_name ] ) );
|
83 |
default:
|
84 |
return print_r( $item, true ); // Show the whole array for troubleshooting purposes
|
85 |
}
|
86 |
}
|
87 |
+
|
88 |
function column_name( $item ) {
|
89 |
+
global $code_snippets;
|
90 |
$screen = get_current_screen();
|
91 |
$actions = array(); // Build row actions
|
92 |
+
|
93 |
+
if ( $item['active'] ) {
|
94 |
$actions['deactivate'] = sprintf(
|
95 |
'<a href="%1$s">%2$s</a>',
|
96 |
add_query_arg( array(
|
111 |
$screen->is_network ? __('Network Activate', 'code-snippets') : __('Activate', 'code-snippets')
|
112 |
);
|
113 |
}
|
114 |
+
|
115 |
$actions['edit'] = sprintf(
|
116 |
'<a href="%s&edit=%s">Edit</a>',
|
117 |
+
$code_snippets->admin_single_url,
|
118 |
$item['id']
|
119 |
);
|
120 |
$actions['export'] = sprintf(
|
135 |
esc_js( 'return confirm( "You are about to permanently delete the selected item.
|
136 |
\'Cancel\' to stop, \'OK\' to delete.");' )
|
137 |
);
|
138 |
+
|
139 |
// Return the name contents
|
140 |
return '<strong>' . stripslashes( $item['name'] ) . '</strong>' . $this->row_actions( $actions, true );
|
141 |
}
|
142 |
+
|
143 |
function column_cb( $item ) {
|
144 |
return sprintf(
|
145 |
'<input type="checkbox" name="%1$s[]" value="%2$s" />',
|
147 |
/*$2%s*/ $item['id'] // The value of the checkbox should be the snippet's id
|
148 |
);
|
149 |
}
|
150 |
+
|
151 |
function get_columns() {
|
152 |
return array(
|
153 |
'cb' => '<input type="checkbox" />',
|
156 |
'description' => __('Description', 'code-snippets'),
|
157 |
);
|
158 |
}
|
159 |
+
|
160 |
function get_sortable_columns() {
|
161 |
return array(
|
162 |
'id' => array( 'id', true ),
|
163 |
'name' => array( 'name', false ),
|
164 |
);
|
165 |
}
|
166 |
+
|
167 |
function get_default_hidden_columns( $result ) {
|
168 |
+
if ( ! $result )
|
169 |
return array( 'id' );
|
170 |
else
|
171 |
return $result;
|
172 |
}
|
173 |
+
|
174 |
function get_bulk_actions() {
|
175 |
$screen = get_current_screen();
|
176 |
$actions = array(
|
178 |
'deactivate-selected' => $screen->is_network ? __('Network Deactivate', 'code-snippets') : __('Deactivate', 'code-snippets'),
|
179 |
'export-selected' => __('Export', 'code-snippets'),
|
180 |
'delete-selected' => __('Delete', 'code-snippets'),
|
181 |
+
'export-php-selected' => __('Export to PHP', 'code-snippets'),
|
182 |
);
|
183 |
return $actions;
|
184 |
}
|
185 |
+
|
186 |
function get_table_classes() {
|
187 |
return array( 'widefat', $this->_args['plural'] );
|
188 |
}
|
189 |
+
|
190 |
function get_views() {
|
191 |
global $totals, $status;
|
192 |
|
211 |
}
|
212 |
|
213 |
if ( 'search' != $type ) {
|
214 |
+
$status_links[$type] = sprintf( '<a href="%s"%s>%s</a>',
|
215 |
add_query_arg('status', $type, '?page=' . $_REQUEST['page'] ),
|
216 |
( $type == $status ) ? ' class="current"' : '',
|
217 |
sprintf( $text, number_format_i18n( $count ) )
|
218 |
+
);
|
219 |
}
|
220 |
}
|
221 |
|
222 |
return $status_links;
|
223 |
}
|
224 |
+
|
225 |
function extra_tablenav( $which ) {
|
226 |
global $status;
|
227 |
|
244 |
|
245 |
return parent::current_action();
|
246 |
}
|
247 |
+
|
248 |
/**
|
249 |
* Processes a bulk action
|
250 |
*
|
251 |
+
* @uses $code_snippets->activate() To activate snippets
|
252 |
+
* @uses $code_snippets->deactivate() To deactivate snippets
|
253 |
+
* @uses $code_snippets->delete_snippet() To delete snippets
|
254 |
+
* @uses $code_snippets->export() To export selected snippets
|
255 |
* @uses wp_redirect To pass the results to the current page
|
256 |
* @uses add_query_arg() To append the results to the current URI
|
257 |
*/
|
258 |
function process_bulk_actions() {
|
259 |
+
global $code_snippets;
|
260 |
+
if ( ! isset( $_POST[ $this->_args['singular'] ] ) ) return;
|
261 |
$ids = $_POST[ $this->_args['singular'] ];
|
262 |
+
|
263 |
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'activate', 'deactivate', 'delete', 'activate-multi', 'deactivate-multi', 'delete-multi' ) );
|
264 |
+
|
265 |
switch( $this->current_action() ) {
|
266 |
+
|
267 |
case 'activate-selected':
|
268 |
+
$code_snippets->activate( $ids );
|
269 |
wp_redirect( add_query_arg( 'activate-multi', true ) );
|
270 |
break;
|
271 |
+
|
272 |
case 'deactivate-selected':
|
273 |
+
$code_snippets->deactivate( $ids );
|
274 |
wp_redirect( add_query_arg( 'deactivate-multi', true ) );
|
275 |
break;
|
276 |
+
|
277 |
case 'export-selected':
|
278 |
+
$code_snippets->export( $ids );
|
279 |
break;
|
280 |
+
|
281 |
+
case 'export-php-selected':
|
282 |
+
$code_snippets->export_php( $ids );
|
283 |
break;
|
284 |
+
|
285 |
case 'delete-selected':
|
286 |
foreach( $ids as $id ) {
|
287 |
+
$code_snippets->delete_snippet( $id );
|
288 |
}
|
289 |
wp_redirect( add_query_arg( 'delete-multi', true ) );
|
290 |
break;
|
291 |
+
|
292 |
case 'clear-recent-list':
|
293 |
$screen = get_current_screen();
|
294 |
+
if ( $screen->is_network )
|
295 |
+
update_site_option( 'recently_activated_snippets', array() );
|
296 |
+
else
|
297 |
+
update_option( 'recently_activated_snippets', array() );
|
298 |
break;
|
299 |
}
|
300 |
}
|
301 |
+
|
302 |
function no_items() {
|
303 |
+
global $code_snippets;
|
304 |
+
printf( __('You do not appear to have any snippets available at this time. <a href="%s">Add New→</a>', 'code-snippets'), $code_snippets->admin_single_url );
|
305 |
}
|
306 |
+
|
307 |
function prepare_items() {
|
308 |
+
|
309 |
+
global $wpdb, $code_snippets, $status, $snippets, $totals, $page, $orderby, $order, $s;
|
310 |
|
311 |
wp_reset_vars( array( 'orderby', 'order', 's' ) );
|
312 |
+
|
313 |
$screen = get_current_screen();
|
314 |
$user = get_current_user_id();
|
315 |
+
$table = $code_snippets->get_table_name();
|
316 |
+
|
317 |
// first, lets process the bulk actions
|
318 |
$this->process_bulk_actions();
|
319 |
+
|
320 |
$snippets = array(
|
321 |
+
'all' => $wpdb->get_results( "SELECT * FROM $table", ARRAY_A ),
|
322 |
'search' => array(),
|
323 |
'active' => array(),
|
324 |
'inactive' => array(),
|
325 |
'recently_activated' => array(),
|
326 |
);
|
327 |
+
|
328 |
+
if ( $screen->is_network )
|
329 |
+
$recently_activated = get_site_option( 'recently_activated_snippets', array() );
|
330 |
+
else
|
331 |
+
$recently_activated = get_option( 'recently_activated_snippets', array() );
|
332 |
|
333 |
$one_week = 7*24*60*60;
|
334 |
foreach ( $recently_activated as $key => $time )
|
335 |
if ( $time + $one_week < time() )
|
336 |
unset( $recently_activated[$key] );
|
337 |
+
|
338 |
+
if ( $screen->is_network )
|
339 |
+
update_site_option( 'recently_activated_snippets', $recently_activated );
|
340 |
+
else
|
341 |
+
update_option( 'recently_activated_snippets', $recently_activated );
|
342 |
+
|
343 |
foreach ( (array) $snippets['all'] as $snippet ) {
|
344 |
// Filter into individual sections
|
345 |
if ( $snippet['active'] ) {
|
351 |
}
|
352 |
}
|
353 |
|
354 |
+
if ( $s ) {
|
355 |
$status = 'search';
|
356 |
$snippets['search'] = array_filter( $snippets['all'], array( &$this, '_search_callback' ) );
|
357 |
}
|
358 |
+
|
359 |
$totals = array();
|
360 |
foreach ( $snippets as $type => $list )
|
361 |
$totals[ $type ] = count( $list );
|
362 |
+
|
363 |
if ( empty( $snippets[ $status ] ) && !in_array( $status, array( 'all', 'search' ) ) )
|
364 |
$status = 'all';
|
365 |
+
|
366 |
$data = $snippets[ $status ];
|
367 |
+
|
368 |
/**
|
369 |
* First, lets decide how many records per page to show
|
370 |
* by getting the user's setting in the Screen Opions
|
372 |
*/
|
373 |
$sort_by = $screen->get_option( 'per_page', 'option' );
|
374 |
$screen_option = $screen->get_option( 'per_page', 'option' );
|
375 |
+
|
376 |
$per_page = get_user_meta( $user, $screen_option, true );
|
377 |
+
|
378 |
+
if ( empty ( $per_page ) || $per_page < 1 ) {
|
379 |
$per_page = $screen->get_option( 'per_page', 'default' );
|
380 |
}
|
381 |
+
|
382 |
$per_page = (int) $per_page;
|
383 |
+
|
384 |
$this->_column_headers = $this->get_column_info();
|
385 |
+
|
386 |
/**
|
387 |
* This checks for sorting input and sorts the data in our array accordingly.
|
388 |
*/
|
389 |
function usort_reorder( $a, $b ) {
|
390 |
+
|
391 |
// If no sort, default to id
|
392 |
+
$orderby = ( ! empty($_REQUEST['orderby'] ) ) ? $_REQUEST['orderby'] : apply_filters( 'code_snippets_default_orderby', 'id' );
|
393 |
+
|
394 |
// If no order, default to asc
|
395 |
$order = ( ! empty( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : 'asc';
|
396 |
+
|
397 |
// Determine sort order
|
398 |
+
if ( $orderby === 'id' )
|
399 |
$result = $a[$orderby] - $b[$orderby]; // get the result for numerical data
|
400 |
else
|
401 |
$result = strcmp( $a[$orderby], $b[$orderby] ); // get the result for string data
|
402 |
+
|
403 |
// Send final sort direction to usort
|
404 |
return ( $order === 'asc' ) ? $result : -$result;
|
405 |
}
|
406 |
+
|
407 |
usort($data, 'usort_reorder');
|
408 |
+
|
409 |
+
|
410 |
/**
|
411 |
+
* Let's figure out what page the user is currently
|
412 |
* looking at.
|
413 |
*/
|
414 |
$current_page = $this->get_pagenum();
|
415 |
+
|
416 |
/**
|
417 |
* Let's check how many items are in our data array.
|
418 |
*/
|
419 |
$total_items = count($data);
|
420 |
+
|
421 |
+
|
422 |
/**
|
423 |
* The WP_List_Table class does not handle pagination for us, so we need
|
424 |
* to ensure that the data is trimmed to only the current page.
|
425 |
*/
|
426 |
$data = array_slice( $data, ( ( $current_page - 1 ) * $per_page ), $per_page );
|
427 |
+
|
428 |
+
|
429 |
/**
|
430 |
+
* Now we can add our *sorted* data to the items property, where
|
431 |
* it can be used by the rest of the class.
|
432 |
*/
|
433 |
$this->items = $data;
|
434 |
+
|
435 |
+
|
436 |
/**
|
437 |
* We also have to register our pagination options & calculations.
|
438 |
*/
|
442 |
'total_pages' => ceil($total_items/$per_page) // WE have to calculate the total number of pages
|
443 |
) );
|
444 |
}
|
445 |
+
|
446 |
function _search_callback( $item ) {
|
447 |
static $term;
|
448 |
if ( is_null( $term ) )
|
454 |
|
455 |
return false;
|
456 |
}
|
457 |
+
|
458 |
/**
|
459 |
* Generates content for a single row of the table
|
460 |
*/
|
includes/export.php
CHANGED
@@ -3,15 +3,15 @@
|
|
3 |
/**
|
4 |
* This file handles the export functions
|
5 |
*
|
6 |
-
* It's better to call the $
|
7 |
-
* and $
|
8 |
* directly use those in this file
|
9 |
*
|
10 |
* @package Code Snippets
|
11 |
* @subpackage Export
|
12 |
*/
|
13 |
|
14 |
-
if( ! function_exists( '
|
15 |
|
16 |
/**
|
17 |
* Exports seleted snippets to a XML or PHP file.
|
@@ -20,62 +20,65 @@ if( ! function_exists( 'cs_export') ) :
|
|
20 |
* @since Code Snippets 1.3
|
21 |
*
|
22 |
* @param array $ids The IDs of the snippets to export
|
23 |
-
*
|
|
|
24 |
*/
|
25 |
-
function
|
26 |
-
|
27 |
-
global $wpdb, $
|
28 |
-
|
29 |
$ids = (array) $ids;
|
30 |
-
|
31 |
-
|
|
|
|
|
32 |
// If there is only snippet to export, use its name instead of the site name
|
33 |
-
$entry = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $
|
34 |
$sitename = sanitize_key( $entry->name );
|
35 |
} else {
|
36 |
// Otherwise, use the site name as set in Settings > General
|
37 |
$sitename = sanitize_key( get_bloginfo( 'name' ) );
|
38 |
}
|
39 |
-
|
40 |
-
$filename = apply_filters( '
|
41 |
|
42 |
header( 'Content-Disposition: attachment; filename=' . $filename );
|
43 |
-
|
44 |
-
if( $format === 'xml' ) {
|
45 |
header( 'Content-Type: text/xml; charset=utf-8' );
|
46 |
-
|
47 |
echo '<?xml version="1.0"?>' . "\n";
|
48 |
echo '<snippets sitename="' . $sitename . '">';
|
49 |
-
|
50 |
foreach( $ids as $id ) {
|
51 |
-
|
52 |
-
if( ! intval( $id ) > 0 ) continue; // skip this one if we don't have a valid ID
|
53 |
-
|
54 |
-
$snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $
|
55 |
-
|
56 |
echo "\n\t" . '<snippet>';
|
57 |
echo "\n\t\t" . "<name>$snippet->name</name>";
|
58 |
echo "\n\t\t" . "<description>$snippet->description</description>";
|
59 |
echo "\n\t\t" . "<code>$snippet->code</code>";
|
60 |
echo "\n\t" . '</snippet>';
|
61 |
}
|
62 |
-
|
63 |
echo "\n</snippets>";
|
64 |
-
|
65 |
-
} elseif( $format === 'php' ) {
|
66 |
-
|
67 |
echo "<?php\n";
|
68 |
-
|
69 |
foreach( $ids as $id ) {
|
70 |
-
|
71 |
-
if( ! intval( $id ) > 0 ) continue; // skip this one if we don't have a valid ID
|
72 |
-
|
73 |
-
$snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $
|
74 |
?>
|
75 |
|
76 |
/**
|
77 |
* <?php echo htmlspecialchars_decode( stripslashes( $snippet->name ) ) . "\n"; ?>
|
78 |
-
<?php if( ! empty( $snippet->description ) ) : ?>
|
79 |
*
|
80 |
* <?php echo htmlspecialchars_decode( stripslashes( $snippet->description ) ) . "\n"; ?>
|
81 |
<?php endif; ?>
|
@@ -84,10 +87,10 @@ function cs_export( $ids, $format = 'xml' ) {
|
|
84 |
|
85 |
<?php
|
86 |
}
|
87 |
-
|
88 |
echo '?>';
|
89 |
}
|
90 |
-
|
91 |
exit;
|
92 |
}
|
93 |
|
3 |
/**
|
4 |
* This file handles the export functions
|
5 |
*
|
6 |
+
* It's better to call the $code_snippets->export()
|
7 |
+
* and $code_snippets->export_php() methods then
|
8 |
* directly use those in this file
|
9 |
*
|
10 |
* @package Code Snippets
|
11 |
* @subpackage Export
|
12 |
*/
|
13 |
|
14 |
+
if ( ! function_exists( 'code_snippets_export' ) ) :
|
15 |
|
16 |
/**
|
17 |
* Exports seleted snippets to a XML or PHP file.
|
20 |
* @since Code Snippets 1.3
|
21 |
*
|
22 |
* @param array $ids The IDs of the snippets to export
|
23 |
+
* @param string $format The format of the export file
|
24 |
+
* @return void
|
25 |
*/
|
26 |
+
function code_snippets_export( $ids, $format = 'xml' ) {
|
27 |
+
|
28 |
+
global $wpdb, $code_snippets;
|
29 |
+
|
30 |
$ids = (array) $ids;
|
31 |
+
|
32 |
+
$table = $code_snippets->get_table_name();
|
33 |
+
|
34 |
+
if ( count( $ids ) < 2 ) {
|
35 |
// If there is only snippet to export, use its name instead of the site name
|
36 |
+
$entry = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id=%d", $ids ) );
|
37 |
$sitename = sanitize_key( $entry->name );
|
38 |
} else {
|
39 |
// Otherwise, use the site name as set in Settings > General
|
40 |
$sitename = sanitize_key( get_bloginfo( 'name' ) );
|
41 |
}
|
42 |
+
|
43 |
+
$filename = apply_filters( 'code_snippets_export_filename', "{$sitename}.code-snippets.{$format}", $format, $sitename );
|
44 |
|
45 |
header( 'Content-Disposition: attachment; filename=' . $filename );
|
46 |
+
|
47 |
+
if ( $format === 'xml' ) {
|
48 |
header( 'Content-Type: text/xml; charset=utf-8' );
|
49 |
+
|
50 |
echo '<?xml version="1.0"?>' . "\n";
|
51 |
echo '<snippets sitename="' . $sitename . '">';
|
52 |
+
|
53 |
foreach( $ids as $id ) {
|
54 |
+
|
55 |
+
if ( ! intval( $id ) > 0 ) continue; // skip this one if we don't have a valid ID
|
56 |
+
|
57 |
+
$snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id=%d", $id ) );
|
58 |
+
|
59 |
echo "\n\t" . '<snippet>';
|
60 |
echo "\n\t\t" . "<name>$snippet->name</name>";
|
61 |
echo "\n\t\t" . "<description>$snippet->description</description>";
|
62 |
echo "\n\t\t" . "<code>$snippet->code</code>";
|
63 |
echo "\n\t" . '</snippet>';
|
64 |
}
|
65 |
+
|
66 |
echo "\n</snippets>";
|
67 |
+
|
68 |
+
} elseif ( $format === 'php' ) {
|
69 |
+
|
70 |
echo "<?php\n";
|
71 |
+
|
72 |
foreach( $ids as $id ) {
|
73 |
+
|
74 |
+
if ( ! intval( $id ) > 0 ) continue; // skip this one if we don't have a valid ID
|
75 |
+
|
76 |
+
$snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id=%d", $id ) );
|
77 |
?>
|
78 |
|
79 |
/**
|
80 |
* <?php echo htmlspecialchars_decode( stripslashes( $snippet->name ) ) . "\n"; ?>
|
81 |
+
<?php if ( ! empty( $snippet->description ) ) : ?>
|
82 |
*
|
83 |
* <?php echo htmlspecialchars_decode( stripslashes( $snippet->description ) ) . "\n"; ?>
|
84 |
<?php endif; ?>
|
87 |
|
88 |
<?php
|
89 |
}
|
90 |
+
|
91 |
echo '?>';
|
92 |
}
|
93 |
+
|
94 |
exit;
|
95 |
}
|
96 |
|
includes/help/import.php
CHANGED
@@ -6,15 +6,15 @@ $screen->add_help_tab( array(
|
|
6 |
'content' =>
|
7 |
'<p>' . __('Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can load snippets from a Code Snippets (.xml) import file into the database with your existing snippets.', 'code-snippets') . '</p>'
|
8 |
) );
|
9 |
-
|
10 |
$screen->add_help_tab( array(
|
11 |
'id' => 'import',
|
12 |
'title' => __('Importing', 'code-snippets'),
|
13 |
'content' =>
|
14 |
-
'<p>' . __('You can load your snippets from a Code Snippets (.xml) import file using this page.', 'code-snippets') .
|
15 |
sprintf( __('Snippets will be added to the database along with your existing snippets. Regardless of whether the snippets were active on the previous site, imported snippets are always inactive until activated using the <a href="%s">Manage Snippets</a> page.</p>', 'code-snippets'), $this->admin_manage_url ) . '</p>'
|
16 |
) );
|
17 |
-
|
18 |
$screen->add_help_tab( array(
|
19 |
'id' => 'export',
|
20 |
'title' => __('Exporting'),
|
@@ -24,7 +24,7 @@ $screen->add_help_tab( array(
|
|
24 |
|
25 |
$screen->set_help_sidebar(
|
26 |
'<p><strong>' . __('For more information:', 'code-snippets') . '</strong></p>' .
|
27 |
-
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
|
28 |
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
29 |
-
'<p>' . __('<a href="http://
|
30 |
);
|
6 |
'content' =>
|
7 |
'<p>' . __('Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can load snippets from a Code Snippets (.xml) import file into the database with your existing snippets.', 'code-snippets') . '</p>'
|
8 |
) );
|
9 |
+
|
10 |
$screen->add_help_tab( array(
|
11 |
'id' => 'import',
|
12 |
'title' => __('Importing', 'code-snippets'),
|
13 |
'content' =>
|
14 |
+
'<p>' . __('You can load your snippets from a Code Snippets (.xml) import file using this page.', 'code-snippets') .
|
15 |
sprintf( __('Snippets will be added to the database along with your existing snippets. Regardless of whether the snippets were active on the previous site, imported snippets are always inactive until activated using the <a href="%s">Manage Snippets</a> page.</p>', 'code-snippets'), $this->admin_manage_url ) . '</p>'
|
16 |
) );
|
17 |
+
|
18 |
$screen->add_help_tab( array(
|
19 |
'id' => 'export',
|
20 |
'title' => __('Exporting'),
|
24 |
|
25 |
$screen->set_help_sidebar(
|
26 |
'<p><strong>' . __('For more information:', 'code-snippets') . '</strong></p>' .
|
27 |
+
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
|
28 |
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
29 |
+
'<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
|
30 |
);
|
includes/help/manage.php
CHANGED
@@ -12,7 +12,7 @@ $screen->add_help_tab( array(
|
|
12 |
'title' => __('Safe Mode', 'code-snippets'),
|
13 |
'content' =>
|
14 |
'<p>' . __('Be sure to check your snippets for errors before you activate them, as a faulty snippet could bring your whole blog down. If your site starts doing strange things, deactivate all your snippets and activate them one at a time.', 'code-snippets') . '</p>' .
|
15 |
-
'<p>' . __("If something goes wrong with a snippet and you can't use WordPress, you can cause all snippets to stop executing by adding <code>define('
|
16 |
) );
|
17 |
|
18 |
$screen->add_help_tab( array(
|
@@ -27,5 +27,5 @@ $screen->set_help_sidebar(
|
|
27 |
'<p><strong>' . __('For more information:', 'code-snippets') . '</strong></p>' .
|
28 |
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a></p>', 'code-snippets') . '</p>' .
|
29 |
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
30 |
-
'<p>' . __('<a href="http://
|
31 |
);
|
12 |
'title' => __('Safe Mode', 'code-snippets'),
|
13 |
'content' =>
|
14 |
'<p>' . __('Be sure to check your snippets for errors before you activate them, as a faulty snippet could bring your whole blog down. If your site starts doing strange things, deactivate all your snippets and activate them one at a time.', 'code-snippets') . '</p>' .
|
15 |
+
'<p>' . __("If something goes wrong with a snippet and you can't use WordPress, you can cause all snippets to stop executing by adding <code>define('CODE_SNIPPETS_SAFE_MODE', true);</code> to your <code>wp-config.php</code> file. After you have deactivated the offending snippet, you can turn off safe mode by removing this line or replacing <strong>true</strong> with <strong>false</strong>.", 'code-snippets') . '</p>'
|
16 |
) );
|
17 |
|
18 |
$screen->add_help_tab( array(
|
27 |
'<p><strong>' . __('For more information:', 'code-snippets') . '</strong></p>' .
|
28 |
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a></p>', 'code-snippets') . '</p>' .
|
29 |
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
30 |
+
'<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
|
31 |
);
|
includes/help/single.php
CHANGED
@@ -17,7 +17,7 @@ $screen->add_help_tab( array(
|
|
17 |
<li><a href="http://www.catswhocode.com/blog/snippets" title="Cats Who Code Snippet Library">Cats Who Code</a></li>
|
18 |
<li><a href="http://www.wpfunction.me">WP Function Me</a></li>
|
19 |
</ul>', 'code-snippets') .
|
20 |
-
__('More places to find snippets, as well as a selection of example snippets, can be found in the <a href="http://
|
21 |
) );
|
22 |
$screen->add_help_tab( array(
|
23 |
'id' => 'adding',
|
@@ -30,7 +30,7 @@ $screen->add_help_tab( array(
|
|
30 |
|
31 |
$screen->set_help_sidebar(
|
32 |
'<p><strong>' . __('For more information:', 'code-snippets') . '</strong></p>' .
|
33 |
-
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
|
34 |
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
35 |
-
'<p>' . __('<a href="http://
|
36 |
);
|
17 |
<li><a href="http://www.catswhocode.com/blog/snippets" title="Cats Who Code Snippet Library">Cats Who Code</a></li>
|
18 |
<li><a href="http://www.wpfunction.me">WP Function Me</a></li>
|
19 |
</ul>', 'code-snippets') .
|
20 |
+
__('More places to find snippets, as well as a selection of example snippets, can be found in the <a href="http://code-snippets.bungeshea.com/docs/finding-snippets/">plugin documentation</a>', 'code-snippets') . '</p>'
|
21 |
) );
|
22 |
$screen->add_help_tab( array(
|
23 |
'id' => 'adding',
|
30 |
|
31 |
$screen->set_help_sidebar(
|
32 |
'<p><strong>' . __('For more information:', 'code-snippets') . '</strong></p>' .
|
33 |
+
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
|
34 |
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
35 |
+
'<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
|
36 |
);
|
js/codemirror.js
DELETED
@@ -1,3237 +0,0 @@
|
|
1 |
-
// CodeMirror version 2.33
|
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.
|
6 |
-
|
7 |
-
// CodeMirror is the only global var we claim
|
8 |
-
window.CodeMirror = (function() {
|
9 |
-
"use strict";
|
10 |
-
// This is the function that produces an editor instance. Its
|
11 |
-
// closure is used to store the editor state.
|
12 |
-
function CodeMirror(place, givenOptions) {
|
13 |
-
// Determine effective options based on given values and defaults.
|
14 |
-
var options = {}, defaults = CodeMirror.defaults;
|
15 |
-
for (var opt in defaults)
|
16 |
-
if (defaults.hasOwnProperty(opt))
|
17 |
-
options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
|
18 |
-
|
19 |
-
var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em");
|
20 |
-
input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
|
21 |
-
// Wraps and hides input textarea
|
22 |
-
var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
|
23 |
-
// The empty scrollbar content, used solely for managing the scrollbar thumb.
|
24 |
-
var scrollbarInner = elt("div", null, "CodeMirror-scrollbar-inner");
|
25 |
-
// The vertical scrollbar. Horizontal scrolling is handled by the scroller itself.
|
26 |
-
var scrollbar = elt("div", [scrollbarInner], "CodeMirror-scrollbar");
|
27 |
-
// DIVs containing the selection and the actual code
|
28 |
-
var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1");
|
29 |
-
// Blinky cursor, and element used to ensure cursor fits at the end of a line
|
30 |
-
var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"), widthForcer = elt("pre", "\u00a0", "CodeMirror-cursor", "visibility: hidden");
|
31 |
-
// Used to measure text size
|
32 |
-
var measure = elt("div", null, null, "position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden;");
|
33 |
-
var lineSpace = elt("div", [measure, cursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0");
|
34 |
-
var gutterText = elt("div", null, "CodeMirror-gutter-text"), gutter = elt("div", [gutterText], "CodeMirror-gutter");
|
35 |
-
// Moved around its parent to cover visible view
|
36 |
-
var mover = elt("div", [gutter, elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative");
|
37 |
-
// Set to the height of the text, causes scrolling
|
38 |
-
var sizer = elt("div", [mover], null, "position: relative");
|
39 |
-
// Provides scrolling
|
40 |
-
var scroller = elt("div", [sizer], "CodeMirror-scroll");
|
41 |
-
scroller.setAttribute("tabIndex", "-1");
|
42 |
-
// The element in which the editor lives.
|
43 |
-
var wrapper = elt("div", [inputDiv, scrollbar, scroller], "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : ""));
|
44 |
-
if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
|
45 |
-
|
46 |
-
themeChanged(); keyMapChanged();
|
47 |
-
// Needed to hide big blue blinking cursor on Mobile Safari
|
48 |
-
if (ios) input.style.width = "0px";
|
49 |
-
if (!webkit) scroller.draggable = true;
|
50 |
-
lineSpace.style.outline = "none";
|
51 |
-
if (options.tabindex != null) input.tabIndex = options.tabindex;
|
52 |
-
if (options.autofocus) focusInput();
|
53 |
-
if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
|
54 |
-
// Needed to handle Tab key in KHTML
|
55 |
-
if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute";
|
56 |
-
|
57 |
-
// Check for OS X >= 10.7. This has transparent scrollbars, so the
|
58 |
-
// overlaying of one scrollbar with another won't work. This is a
|
59 |
-
// temporary hack to simply turn off the overlay scrollbar. See
|
60 |
-
// issue #727.
|
61 |
-
if (mac_geLion) { scrollbar.style.zIndex = -2; scrollbar.style.visibility = "hidden"; }
|
62 |
-
// Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
|
63 |
-
else if (ie_lt8) scrollbar.style.minWidth = "18px";
|
64 |
-
|
65 |
-
// Check for problem with IE innerHTML not working when we have a
|
66 |
-
// P (or similar) parent node.
|
67 |
-
try { charWidth(); }
|
68 |
-
catch (e) {
|
69 |
-
if (e.message.match(/runtime/i))
|
70 |
-
e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
|
71 |
-
throw e;
|
72 |
-
}
|
73 |
-
|
74 |
-
// Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
|
75 |
-
var poll = new Delayed(), highlight = new Delayed(), blinker;
|
76 |
-
|
77 |
-
// mode holds a mode API object. doc is the tree of Line objects,
|
78 |
-
// work an array of lines that should be parsed, and history the
|
79 |
-
// undo history (instance of History constructor).
|
80 |
-
var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
|
81 |
-
loadMode();
|
82 |
-
// The selection. These are always maintained to point at valid
|
83 |
-
// positions. Inverted is used to remember that the user is
|
84 |
-
// selecting bottom-to-top.
|
85 |
-
var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
|
86 |
-
// Selection-related flags. shiftSelecting obviously tracks
|
87 |
-
// whether the user is holding shift.
|
88 |
-
var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText,
|
89 |
-
overwrite = false, suppressEdits = false;
|
90 |
-
// Variables used by startOperation/endOperation to track what
|
91 |
-
// happened during the operation.
|
92 |
-
var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
|
93 |
-
gutterDirty, callbacks;
|
94 |
-
// Current visible range (may be bigger than the view window).
|
95 |
-
var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
|
96 |
-
// bracketHighlighted is used to remember that a bracket has been
|
97 |
-
// marked.
|
98 |
-
var bracketHighlighted;
|
99 |
-
// Tracks the maximum line length so that the horizontal scrollbar
|
100 |
-
// can be kept static when scrolling.
|
101 |
-
var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true;
|
102 |
-
var tabCache = {};
|
103 |
-
var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
|
104 |
-
var goalColumn = null;
|
105 |
-
|
106 |
-
// Initialize the content.
|
107 |
-
operation(function(){setValue(options.value || ""); updateInput = false;})();
|
108 |
-
var history = new History();
|
109 |
-
|
110 |
-
// Register our event handlers.
|
111 |
-
connect(scroller, "mousedown", operation(onMouseDown));
|
112 |
-
connect(scroller, "dblclick", operation(onDoubleClick));
|
113 |
-
connect(lineSpace, "selectstart", e_preventDefault);
|
114 |
-
// Gecko browsers fire contextmenu *after* opening the menu, at
|
115 |
-
// which point we can't mess with it anymore. Context menu is
|
116 |
-
// handled in onMouseDown for Gecko.
|
117 |
-
if (!gecko) connect(scroller, "contextmenu", onContextMenu);
|
118 |
-
connect(scroller, "scroll", onScrollMain);
|
119 |
-
connect(scrollbar, "scroll", onScrollBar);
|
120 |
-
connect(scrollbar, "mousedown", function() {if (focused) setTimeout(focusInput, 0);});
|
121 |
-
var resizeHandler = connect(window, "resize", function() {
|
122 |
-
if (wrapper.parentNode) updateDisplay(true);
|
123 |
-
else resizeHandler();
|
124 |
-
}, true);
|
125 |
-
connect(input, "keyup", operation(onKeyUp));
|
126 |
-
connect(input, "input", fastPoll);
|
127 |
-
connect(input, "keydown", operation(onKeyDown));
|
128 |
-
connect(input, "keypress", operation(onKeyPress));
|
129 |
-
connect(input, "focus", onFocus);
|
130 |
-
connect(input, "blur", onBlur);
|
131 |
-
|
132 |
-
function drag_(e) {
|
133 |
-
if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
|
134 |
-
e_stop(e);
|
135 |
-
}
|
136 |
-
if (options.dragDrop) {
|
137 |
-
connect(scroller, "dragstart", onDragStart);
|
138 |
-
connect(scroller, "dragenter", drag_);
|
139 |
-
connect(scroller, "dragover", drag_);
|
140 |
-
connect(scroller, "drop", operation(onDrop));
|
141 |
-
}
|
142 |
-
connect(scroller, "paste", function(){focusInput(); fastPoll();});
|
143 |
-
connect(input, "paste", fastPoll);
|
144 |
-
connect(input, "cut", operation(function(){
|
145 |
-
if (!options.readOnly) replaceSelection("");
|
146 |
-
}));
|
147 |
-
|
148 |
-
// Needed to handle Tab key in KHTML
|
149 |
-
if (khtml) connect(sizer, "mouseup", function() {
|
150 |
-
if (document.activeElement == input) input.blur();
|
151 |
-
focusInput();
|
152 |
-
});
|
153 |
-
|
154 |
-
// IE throws unspecified error in certain cases, when
|
155 |
-
// trying to access activeElement before onload
|
156 |
-
var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { }
|
157 |
-
if (hasFocus || options.autofocus) setTimeout(onFocus, 20);
|
158 |
-
else onBlur();
|
159 |
-
|
160 |
-
function isLine(l) {return l >= 0 && l < doc.size;}
|
161 |
-
// The instance object that we'll return. Mostly calls out to
|
162 |
-
// local functions in the CodeMirror function. Some do some extra
|
163 |
-
// range checking and/or clipping. operation is used to wrap the
|
164 |
-
// call so that changes it makes are tracked, and the display is
|
165 |
-
// updated afterwards.
|
166 |
-
var instance = wrapper.CodeMirror = {
|
167 |
-
getValue: getValue,
|
168 |
-
setValue: operation(setValue),
|
169 |
-
getSelection: getSelection,
|
170 |
-
replaceSelection: operation(replaceSelection),
|
171 |
-
focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();},
|
172 |
-
setOption: function(option, value) {
|
173 |
-
var oldVal = options[option];
|
174 |
-
options[option] = value;
|
175 |
-
if (option == "mode" || option == "indentUnit") loadMode();
|
176 |
-
else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();}
|
177 |
-
else if (option == "readOnly" && !value) {resetInput(true);}
|
178 |
-
else if (option == "theme") themeChanged();
|
179 |
-
else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
|
180 |
-
else if (option == "tabSize") updateDisplay(true);
|
181 |
-
else if (option == "keyMap") keyMapChanged();
|
182 |
-
if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" ||
|
183 |
-
option == "theme" || option == "lineNumberFormatter") {
|
184 |
-
gutterChanged();
|
185 |
-
updateDisplay(true);
|
186 |
-
}
|
187 |
-
},
|
188 |
-
getOption: function(option) {return options[option];},
|
189 |
-
undo: operation(undo),
|
190 |
-
redo: operation(redo),
|
191 |
-
indentLine: operation(function(n, dir) {
|
192 |
-
if (typeof dir != "string") {
|
193 |
-
if (dir == null) dir = options.smartIndent ? "smart" : "prev";
|
194 |
-
else dir = dir ? "add" : "subtract";
|
195 |
-
}
|
196 |
-
if (isLine(n)) indentLine(n, dir);
|
197 |
-
}),
|
198 |
-
indentSelection: operation(indentSelected),
|
199 |
-
historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
|
200 |
-
clearHistory: function() {history = new History();},
|
201 |
-
setHistory: function(histData) {
|
202 |
-
history = new History();
|
203 |
-
history.done = histData.done;
|
204 |
-
history.undone = histData.undone;
|
205 |
-
},
|
206 |
-
getHistory: function() {
|
207 |
-
history.time = 0;
|
208 |
-
return {done: history.done.concat([]), undone: history.undone.concat([])};
|
209 |
-
},
|
210 |
-
matchBrackets: operation(function(){matchBrackets(true);}),
|
211 |
-
getTokenAt: operation(function(pos) {
|
212 |
-
pos = clipPos(pos);
|
213 |
-
return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch);
|
214 |
-
}),
|
215 |
-
getStateAfter: function(line) {
|
216 |
-
line = clipLine(line == null ? doc.size - 1: line);
|
217 |
-
return getStateBefore(line + 1);
|
218 |
-
},
|
219 |
-
cursorCoords: function(start, mode) {
|
220 |
-
if (start == null) start = sel.inverted;
|
221 |
-
return this.charCoords(start ? sel.from : sel.to, mode);
|
222 |
-
},
|
223 |
-
charCoords: function(pos, mode) {
|
224 |
-
pos = clipPos(pos);
|
225 |
-
if (mode == "local") return localCoords(pos, false);
|
226 |
-
if (mode == "div") return localCoords(pos, true);
|
227 |
-
return pageCoords(pos);
|
228 |
-
},
|
229 |
-
coordsChar: function(coords) {
|
230 |
-
var off = eltOffset(lineSpace);
|
231 |
-
return coordsChar(coords.x - off.left, coords.y - off.top);
|
232 |
-
},
|
233 |
-
markText: operation(markText),
|
234 |
-
setBookmark: setBookmark,
|
235 |
-
findMarksAt: findMarksAt,
|
236 |
-
setMarker: operation(addGutterMarker),
|
237 |
-
clearMarker: operation(removeGutterMarker),
|
238 |
-
setLineClass: operation(setLineClass),
|
239 |
-
hideLine: operation(function(h) {return setLineHidden(h, true);}),
|
240 |
-
showLine: operation(function(h) {return setLineHidden(h, false);}),
|
241 |
-
onDeleteLine: function(line, f) {
|
242 |
-
if (typeof line == "number") {
|
243 |
-
if (!isLine(line)) return null;
|
244 |
-
line = getLine(line);
|
245 |
-
}
|
246 |
-
(line.handlers || (line.handlers = [])).push(f);
|
247 |
-
return line;
|
248 |
-
},
|
249 |
-
lineInfo: lineInfo,
|
250 |
-
getViewport: function() { return {from: showingFrom, to: showingTo};},
|
251 |
-
addWidget: function(pos, node, scroll, vert, horiz) {
|
252 |
-
pos = localCoords(clipPos(pos));
|
253 |
-
var top = pos.yBot, left = pos.x;
|
254 |
-
node.style.position = "absolute";
|
255 |
-
sizer.appendChild(node);
|
256 |
-
if (vert == "over") top = pos.y;
|
257 |
-
else if (vert == "near") {
|
258 |
-
var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
|
259 |
-
hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth) - paddingLeft();
|
260 |
-
if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
|
261 |
-
top = pos.y - node.offsetHeight;
|
262 |
-
if (left + node.offsetWidth > hspace)
|
263 |
-
left = hspace - node.offsetWidth;
|
264 |
-
}
|
265 |
-
node.style.top = (top + paddingTop()) + "px";
|
266 |
-
node.style.left = node.style.right = "";
|
267 |
-
if (horiz == "right") {
|
268 |
-
left = sizer.clientWidth - node.offsetWidth;
|
269 |
-
node.style.right = "0px";
|
270 |
-
} else {
|
271 |
-
if (horiz == "left") left = 0;
|
272 |
-
else if (horiz == "middle") left = (sizer.clientWidth - node.offsetWidth) / 2;
|
273 |
-
node.style.left = (left + paddingLeft()) + "px";
|
274 |
-
}
|
275 |
-
if (scroll)
|
276 |
-
scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
|
277 |
-
},
|
278 |
-
|
279 |
-
lineCount: function() {return doc.size;},
|
280 |
-
clipPos: clipPos,
|
281 |
-
getCursor: function(start) {
|
282 |
-
if (start == null) start = sel.inverted;
|
283 |
-
return copyPos(start ? sel.from : sel.to);
|
284 |
-
},
|
285 |
-
somethingSelected: function() {return !posEq(sel.from, sel.to);},
|
286 |
-
setCursor: operation(function(line, ch, user) {
|
287 |
-
if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user);
|
288 |
-
else setCursor(line, ch, user);
|
289 |
-
}),
|
290 |
-
setSelection: operation(function(from, to, user) {
|
291 |
-
(user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from));
|
292 |
-
}),
|
293 |
-
getLine: function(line) {if (isLine(line)) return getLine(line).text;},
|
294 |
-
getLineHandle: function(line) {if (isLine(line)) return getLine(line);},
|
295 |
-
setLine: operation(function(line, text) {
|
296 |
-
if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
|
297 |
-
}),
|
298 |
-
removeLine: operation(function(line) {
|
299 |
-
if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
|
300 |
-
}),
|
301 |
-
replaceRange: operation(replaceRange),
|
302 |
-
getRange: function(from, to, lineSep) {return getRange(clipPos(from), clipPos(to), lineSep);},
|
303 |
-
|
304 |
-
triggerOnKeyDown: operation(onKeyDown),
|
305 |
-
execCommand: function(cmd) {return commands[cmd](instance);},
|
306 |
-
// Stuff used by commands, probably not much use to outside code.
|
307 |
-
moveH: operation(moveH),
|
308 |
-
deleteH: operation(deleteH),
|
309 |
-
moveV: operation(moveV),
|
310 |
-
toggleOverwrite: function() {
|
311 |
-
if(overwrite){
|
312 |
-
overwrite = false;
|
313 |
-
cursor.className = cursor.className.replace(" CodeMirror-overwrite", "");
|
314 |
-
} else {
|
315 |
-
overwrite = true;
|
316 |
-
cursor.className += " CodeMirror-overwrite";
|
317 |
-
}
|
318 |
-
},
|
319 |
-
|
320 |
-
posFromIndex: function(off) {
|
321 |
-
var lineNo = 0, ch;
|
322 |
-
doc.iter(0, doc.size, function(line) {
|
323 |
-
var sz = line.text.length + 1;
|
324 |
-
if (sz > off) { ch = off; return true; }
|
325 |
-
off -= sz;
|
326 |
-
++lineNo;
|
327 |
-
});
|
328 |
-
return clipPos({line: lineNo, ch: ch});
|
329 |
-
},
|
330 |
-
indexFromPos: function (coords) {
|
331 |
-
if (coords.line < 0 || coords.ch < 0) return 0;
|
332 |
-
var index = coords.ch;
|
333 |
-
doc.iter(0, coords.line, function (line) {
|
334 |
-
index += line.text.length + 1;
|
335 |
-
});
|
336 |
-
return index;
|
337 |
-
},
|
338 |
-
scrollTo: function(x, y) {
|
339 |
-
if (x != null) scroller.scrollLeft = x;
|
340 |
-
if (y != null) scrollbar.scrollTop = scroller.scrollTop = y;
|
341 |
-
updateDisplay([]);
|
342 |
-
},
|
343 |
-
getScrollInfo: function() {
|
344 |
-
return {x: scroller.scrollLeft, y: scrollbar.scrollTop,
|
345 |
-
height: scrollbar.scrollHeight, width: scroller.scrollWidth};
|
346 |
-
},
|
347 |
-
setSize: function(width, height) {
|
348 |
-
function interpret(val) {
|
349 |
-
val = String(val);
|
350 |
-
return /^\d+$/.test(val) ? val + "px" : val;
|
351 |
-
}
|
352 |
-
if (width != null) wrapper.style.width = interpret(width);
|
353 |
-
if (height != null) scroller.style.height = interpret(height);
|
354 |
-
instance.refresh();
|
355 |
-
},
|
356 |
-
|
357 |
-
operation: function(f){return operation(f)();},
|
358 |
-
compoundChange: function(f){return compoundChange(f);},
|
359 |
-
refresh: function(){
|
360 |
-
updateDisplay(true, null, lastScrollTop);
|
361 |
-
if (scrollbar.scrollHeight > lastScrollTop)
|
362 |
-
scrollbar.scrollTop = lastScrollTop;
|
363 |
-
},
|
364 |
-
getInputField: function(){return input;},
|
365 |
-
getWrapperElement: function(){return wrapper;},
|
366 |
-
getScrollerElement: function(){return scroller;},
|
367 |
-
getGutterElement: function(){return gutter;}
|
368 |
-
};
|
369 |
-
|
370 |
-
function getLine(n) { return getLineAt(doc, n); }
|
371 |
-
function updateLineHeight(line, height) {
|
372 |
-
gutterDirty = true;
|
373 |
-
var diff = height - line.height;
|
374 |
-
for (var n = line; n; n = n.parent) n.height += diff;
|
375 |
-
}
|
376 |
-
|
377 |
-
function setValue(code) {
|
378 |
-
var top = {line: 0, ch: 0};
|
379 |
-
updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
|
380 |
-
splitLines(code), top, top);
|
381 |
-
updateInput = true;
|
382 |
-
}
|
383 |
-
function getValue(lineSep) {
|
384 |
-
var text = [];
|
385 |
-
doc.iter(0, doc.size, function(line) { text.push(line.text); });
|
386 |
-
return text.join(lineSep || "\n");
|
387 |
-
}
|
388 |
-
|
389 |
-
function onScrollBar(e) {
|
390 |
-
if (scrollbar.scrollTop != lastScrollTop) {
|
391 |
-
lastScrollTop = scroller.scrollTop = scrollbar.scrollTop;
|
392 |
-
updateDisplay([]);
|
393 |
-
}
|
394 |
-
}
|
395 |
-
|
396 |
-
function onScrollMain(e) {
|
397 |
-
if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px")
|
398 |
-
gutter.style.left = scroller.scrollLeft + "px";
|
399 |
-
if (scroller.scrollTop != lastScrollTop) {
|
400 |
-
lastScrollTop = scroller.scrollTop;
|
401 |
-
if (scrollbar.scrollTop != lastScrollTop)
|
402 |
-
scrollbar.scrollTop = lastScrollTop;
|
403 |
-
updateDisplay([]);
|
404 |
-
}
|
405 |
-
if (options.onScroll) options.onScroll(instance);
|
406 |
-
}
|
407 |
-
|
408 |
-
function onMouseDown(e) {
|
409 |
-
setShift(e_prop(e, "shiftKey"));
|
410 |
-
// Check whether this is a click in a widget
|
411 |
-
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
412 |
-
if (n.parentNode == sizer && n != mover) return;
|
413 |
-
|
414 |
-
// See if this is a click in the gutter
|
415 |
-
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
416 |
-
if (n.parentNode == gutterText) {
|
417 |
-
if (options.onGutterClick)
|
418 |
-
options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
|
419 |
-
return e_preventDefault(e);
|
420 |
-
}
|
421 |
-
|
422 |
-
var start = posFromMouse(e);
|
423 |
-
|
424 |
-
switch (e_button(e)) {
|
425 |
-
case 3:
|
426 |
-
if (gecko) onContextMenu(e);
|
427 |
-
return;
|
428 |
-
case 2:
|
429 |
-
if (start) setCursor(start.line, start.ch, true);
|
430 |
-
setTimeout(focusInput, 20);
|
431 |
-
e_preventDefault(e);
|
432 |
-
return;
|
433 |
-
}
|
434 |
-
// For button 1, if it was clicked inside the editor
|
435 |
-
// (posFromMouse returning non-null), we have to adjust the
|
436 |
-
// selection.
|
437 |
-
if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
|
438 |
-
|
439 |
-
if (!focused) onFocus();
|
440 |
-
|
441 |
-
var now = +new Date, type = "single";
|
442 |
-
if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
|
443 |
-
type = "triple";
|
444 |
-
e_preventDefault(e);
|
445 |
-
setTimeout(focusInput, 20);
|
446 |
-
selectLine(start.line);
|
447 |
-
} else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
|
448 |
-
type = "double";
|
449 |
-
lastDoubleClick = {time: now, pos: start};
|
450 |
-
e_preventDefault(e);
|
451 |
-
var word = findWordAt(start);
|
452 |
-
setSelectionUser(word.from, word.to);
|
453 |
-
} else { lastClick = {time: now, pos: start}; }
|
454 |
-
|
455 |
-
function dragEnd(e2) {
|
456 |
-
if (webkit) scroller.draggable = false;
|
457 |
-
draggingText = false;
|
458 |
-
up(); drop();
|
459 |
-
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
|
460 |
-
e_preventDefault(e2);
|
461 |
-
setCursor(start.line, start.ch, true);
|
462 |
-
focusInput();
|
463 |
-
}
|
464 |
-
}
|
465 |
-
var last = start, going;
|
466 |
-
if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
|
467 |
-
!posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
|
468 |
-
// Let the drag handler handle this.
|
469 |
-
if (webkit) scroller.draggable = true;
|
470 |
-
var up = connect(document, "mouseup", operation(dragEnd), true);
|
471 |
-
var drop = connect(scroller, "drop", operation(dragEnd), true);
|
472 |
-
draggingText = true;
|
473 |
-
// IE's approach to draggable
|
474 |
-
if (scroller.dragDrop) scroller.dragDrop();
|
475 |
-
return;
|
476 |
-
}
|
477 |
-
e_preventDefault(e);
|
478 |
-
if (type == "single") setCursor(start.line, start.ch, true);
|
479 |
-
|
480 |
-
var startstart = sel.from, startend = sel.to;
|
481 |
-
|
482 |
-
function doSelect(cur) {
|
483 |
-
if (type == "single") {
|
484 |
-
setSelectionUser(start, cur);
|
485 |
-
} else if (type == "double") {
|
486 |
-
var word = findWordAt(cur);
|
487 |
-
if (posLess(cur, startstart)) setSelectionUser(word.from, startend);
|
488 |
-
else setSelectionUser(startstart, word.to);
|
489 |
-
} else if (type == "triple") {
|
490 |
-
if (posLess(cur, startstart)) setSelectionUser(startend, clipPos({line: cur.line, ch: 0}));
|
491 |
-
else setSelectionUser(startstart, clipPos({line: cur.line + 1, ch: 0}));
|
492 |
-
}
|
493 |
-
}
|
494 |
-
|
495 |
-
function extend(e) {
|
496 |
-
var cur = posFromMouse(e, true);
|
497 |
-
if (cur && !posEq(cur, last)) {
|
498 |
-
if (!focused) onFocus();
|
499 |
-
last = cur;
|
500 |
-
doSelect(cur);
|
501 |
-
updateInput = false;
|
502 |
-
var visible = visibleLines();
|
503 |
-
if (cur.line >= visible.to || cur.line < visible.from)
|
504 |
-
going = setTimeout(operation(function(){extend(e);}), 150);
|
505 |
-
}
|
506 |
-
}
|
507 |
-
|
508 |
-
function done(e) {
|
509 |
-
clearTimeout(going);
|
510 |
-
var cur = posFromMouse(e);
|
511 |
-
if (cur) doSelect(cur);
|
512 |
-
e_preventDefault(e);
|
513 |
-
focusInput();
|
514 |
-
updateInput = true;
|
515 |
-
move(); up();
|
516 |
-
}
|
517 |
-
var move = connect(document, "mousemove", operation(function(e) {
|
518 |
-
clearTimeout(going);
|
519 |
-
e_preventDefault(e);
|
520 |
-
if (!ie && !e_button(e)) done(e);
|
521 |
-
else extend(e);
|
522 |
-
}), true);
|
523 |
-
var up = connect(document, "mouseup", operation(done), true);
|
524 |
-
}
|
525 |
-
function onDoubleClick(e) {
|
526 |
-
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
527 |
-
if (n.parentNode == gutterText) return e_preventDefault(e);
|
528 |
-
e_preventDefault(e);
|
529 |
-
}
|
530 |
-
function onDrop(e) {
|
531 |
-
if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
|
532 |
-
e_preventDefault(e);
|
533 |
-
var pos = posFromMouse(e, true), files = e.dataTransfer.files;
|
534 |
-
if (!pos || options.readOnly) return;
|
535 |
-
if (files && files.length && window.FileReader && window.File) {
|
536 |
-
var n = files.length, text = Array(n), read = 0;
|
537 |
-
var loadFile = function(file, i) {
|
538 |
-
var reader = new FileReader;
|
539 |
-
reader.onload = function() {
|
540 |
-
text[i] = reader.result;
|
541 |
-
if (++read == n) {
|
542 |
-
pos = clipPos(pos);
|
543 |
-
operation(function() {
|
544 |
-
var end = replaceRange(text.join(""), pos, pos);
|
545 |
-
setSelectionUser(pos, end);
|
546 |
-
})();
|
547 |
-
}
|
548 |
-
};
|
549 |
-
reader.readAsText(file);
|
550 |
-
};
|
551 |
-
for (var i = 0; i < n; ++i) loadFile(files[i], i);
|
552 |
-
} else {
|
553 |
-
// Don't do a replace if the drop happened inside of the selected text.
|
554 |
-
if (draggingText && !(posLess(pos, sel.from) || posLess(sel.to, pos))) return;
|
555 |
-
try {
|
556 |
-
var text = e.dataTransfer.getData("Text");
|
557 |
-
if (text) {
|
558 |
-
compoundChange(function() {
|
559 |
-
var curFrom = sel.from, curTo = sel.to;
|
560 |
-
setSelectionUser(pos, pos);
|
561 |
-
if (draggingText) replaceRange("", curFrom, curTo);
|
562 |
-
replaceSelection(text);
|
563 |
-
focusInput();
|
564 |
-
});
|
565 |
-
}
|
566 |
-
}
|
567 |
-
catch(e){}
|
568 |
-
}
|
569 |
-
}
|
570 |
-
function onDragStart(e) {
|
571 |
-
var txt = getSelection();
|
572 |
-
e.dataTransfer.setData("Text", txt);
|
573 |
-
|
574 |
-
// Use dummy image instead of default browsers image.
|
575 |
-
if (gecko || chrome || opera) {
|
576 |
-
var img = elt('img');
|
577 |
-
img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image
|
578 |
-
e.dataTransfer.setDragImage(img, 0, 0);
|
579 |
-
}
|
580 |
-
}
|
581 |
-
|
582 |
-
function doHandleBinding(bound, dropShift) {
|
583 |
-
if (typeof bound == "string") {
|
584 |
-
bound = commands[bound];
|
585 |
-
if (!bound) return false;
|
586 |
-
}
|
587 |
-
var prevShift = shiftSelecting;
|
588 |
-
try {
|
589 |
-
if (options.readOnly) suppressEdits = true;
|
590 |
-
if (dropShift) shiftSelecting = null;
|
591 |
-
bound(instance);
|
592 |
-
} catch(e) {
|
593 |
-
if (e != Pass) throw e;
|
594 |
-
return false;
|
595 |
-
} finally {
|
596 |
-
shiftSelecting = prevShift;
|
597 |
-
suppressEdits = false;
|
598 |
-
}
|
599 |
-
return true;
|
600 |
-
}
|
601 |
-
var maybeTransition;
|
602 |
-
function handleKeyBinding(e) {
|
603 |
-
// Handle auto keymap transitions
|
604 |
-
var startMap = getKeyMap(options.keyMap), next = startMap.auto;
|
605 |
-
clearTimeout(maybeTransition);
|
606 |
-
if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
|
607 |
-
if (getKeyMap(options.keyMap) == startMap) {
|
608 |
-
options.keyMap = (next.call ? next.call(null, instance) : next);
|
609 |
-
}
|
610 |
-
}, 50);
|
611 |
-
|
612 |
-
var name = keyNames[e_prop(e, "keyCode")], handled = false;
|
613 |
-
if (name == null || e.altGraphKey) return false;
|
614 |
-
if (e_prop(e, "altKey")) name = "Alt-" + name;
|
615 |
-
if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
|
616 |
-
if (e_prop(e, "metaKey")) name = "Cmd-" + name;
|
617 |
-
|
618 |
-
var stopped = false;
|
619 |
-
function stop() { stopped = true; }
|
620 |
-
|
621 |
-
if (e_prop(e, "shiftKey")) {
|
622 |
-
handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap,
|
623 |
-
function(b) {return doHandleBinding(b, true);}, stop)
|
624 |
-
|| lookupKey(name, options.extraKeys, options.keyMap, function(b) {
|
625 |
-
if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b);
|
626 |
-
}, stop);
|
627 |
-
} else {
|
628 |
-
handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding, stop);
|
629 |
-
}
|
630 |
-
if (stopped) handled = false;
|
631 |
-
if (handled) {
|
632 |
-
e_preventDefault(e);
|
633 |
-
restartBlink();
|
634 |
-
if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
|
635 |
-
}
|
636 |
-
return handled;
|
637 |
-
}
|
638 |
-
function handleCharBinding(e, ch) {
|
639 |
-
var handled = lookupKey("'" + ch + "'", options.extraKeys,
|
640 |
-
options.keyMap, function(b) { return doHandleBinding(b, true); });
|
641 |
-
if (handled) {
|
642 |
-
e_preventDefault(e);
|
643 |
-
restartBlink();
|
644 |
-
}
|
645 |
-
return handled;
|
646 |
-
}
|
647 |
-
|
648 |
-
var lastStoppedKey = null;
|
649 |
-
function onKeyDown(e) {
|
650 |
-
if (!focused) onFocus();
|
651 |
-
if (ie && e.keyCode == 27) { e.returnValue = false; }
|
652 |
-
if (pollingFast) { if (readInput()) pollingFast = false; }
|
653 |
-
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
|
654 |
-
var code = e_prop(e, "keyCode");
|
655 |
-
// IE does strange things with escape.
|
656 |
-
setShift(code == 16 || e_prop(e, "shiftKey"));
|
657 |
-
// First give onKeyEvent option a chance to handle this.
|
658 |
-
var handled = handleKeyBinding(e);
|
659 |
-
if (opera) {
|
660 |
-
lastStoppedKey = handled ? code : null;
|
661 |
-
// Opera has no cut event... we try to at least catch the key combo
|
662 |
-
if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey"))
|
663 |
-
replaceSelection("");
|
664 |
-
}
|
665 |
-
}
|
666 |
-
function onKeyPress(e) {
|
667 |
-
if (pollingFast) readInput();
|
668 |
-
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
|
669 |
-
var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
|
670 |
-
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
|
671 |
-
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return;
|
672 |
-
var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
|
673 |
-
if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) {
|
674 |
-
if (mode.electricChars.indexOf(ch) > -1)
|
675 |
-
setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
|
676 |
-
}
|
677 |
-
if (handleCharBinding(e, ch)) return;
|
678 |
-
fastPoll();
|
679 |
-
}
|
680 |
-
function onKeyUp(e) {
|
681 |
-
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
|
682 |
-
if (e_prop(e, "keyCode") == 16) shiftSelecting = null;
|
683 |
-
}
|
684 |
-
|
685 |
-
function onFocus() {
|
686 |
-
if (options.readOnly == "nocursor") return;
|
687 |
-
if (!focused) {
|
688 |
-
if (options.onFocus) options.onFocus(instance);
|
689 |
-
focused = true;
|
690 |
-
if (scroller.className.search(/\bCodeMirror-focused\b/) == -1)
|
691 |
-
scroller.className += " CodeMirror-focused";
|
692 |
-
if (!leaveInputAlone) resetInput(true);
|
693 |
-
}
|
694 |
-
slowPoll();
|
695 |
-
restartBlink();
|
696 |
-
}
|
697 |
-
function onBlur() {
|
698 |
-
if (focused) {
|
699 |
-
if (options.onBlur) options.onBlur(instance);
|
700 |
-
focused = false;
|
701 |
-
if (bracketHighlighted)
|
702 |
-
operation(function(){
|
703 |
-
if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; }
|
704 |
-
})();
|
705 |
-
scroller.className = scroller.className.replace(" CodeMirror-focused", "");
|
706 |
-
}
|
707 |
-
clearInterval(blinker);
|
708 |
-
setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
|
709 |
-
}
|
710 |
-
|
711 |
-
// Replace the range from from to to by the strings in newText.
|
712 |
-
// Afterwards, set the selection to selFrom, selTo.
|
713 |
-
function updateLines(from, to, newText, selFrom, selTo) {
|
714 |
-
if (suppressEdits) return;
|
715 |
-
if (history) {
|
716 |
-
var old = [];
|
717 |
-
doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
|
718 |
-
history.addChange(from.line, newText.length, old);
|
719 |
-
while (history.done.length > options.undoDepth) history.done.shift();
|
720 |
-
}
|
721 |
-
updateLinesNoUndo(from, to, newText, selFrom, selTo);
|
722 |
-
}
|
723 |
-
function unredoHelper(from, to) {
|
724 |
-
if (!from.length) return;
|
725 |
-
var set = from.pop(), out = [];
|
726 |
-
for (var i = set.length - 1; i >= 0; i -= 1) {
|
727 |
-
var change = set[i];
|
728 |
-
var replaced = [], end = change.start + change.added;
|
729 |
-
doc.iter(change.start, end, function(line) { replaced.push(line.text); });
|
730 |
-
out.push({start: change.start, added: change.old.length, old: replaced});
|
731 |
-
var pos = {line: change.start + change.old.length - 1,
|
732 |
-
ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])};
|
733 |
-
updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
|
734 |
-
}
|
735 |
-
updateInput = true;
|
736 |
-
to.push(out);
|
737 |
-
}
|
738 |
-
function undo() {unredoHelper(history.done, history.undone);}
|
739 |
-
function redo() {unredoHelper(history.undone, history.done);}
|
740 |
-
|
741 |
-
function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
|
742 |
-
if (suppressEdits) return;
|
743 |
-
var recomputeMaxLength = false, maxLineLength = maxLine.text.length;
|
744 |
-
if (!options.lineWrapping)
|
745 |
-
doc.iter(from.line, to.line + 1, function(line) {
|
746 |
-
if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
|
747 |
-
});
|
748 |
-
if (from.line != to.line || newText.length > 1) gutterDirty = true;
|
749 |
-
|
750 |
-
var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
|
751 |
-
// First adjust the line structure, taking some care to leave highlighting intact.
|
752 |
-
if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") {
|
753 |
-
// This is a whole-line replace. Treated specially to make
|
754 |
-
// sure line objects move the way they are supposed to.
|
755 |
-
var added = [], prevLine = null;
|
756 |
-
if (from.line) {
|
757 |
-
prevLine = getLine(from.line - 1);
|
758 |
-
prevLine.fixMarkEnds(lastLine);
|
759 |
-
} else lastLine.fixMarkStarts();
|
760 |
-
for (var i = 0, e = newText.length - 1; i < e; ++i)
|
761 |
-
added.push(Line.inheritMarks(newText[i], prevLine));
|
762 |
-
if (nlines) doc.remove(from.line, nlines, callbacks);
|
763 |
-
if (added.length) doc.insert(from.line, added);
|
764 |
-
} else if (firstLine == lastLine) {
|
765 |
-
if (newText.length == 1)
|
766 |
-
firstLine.replace(from.ch, to.ch, newText[0]);
|
767 |
-
else {
|
768 |
-
lastLine = firstLine.split(to.ch, newText[newText.length-1]);
|
769 |
-
firstLine.replace(from.ch, null, newText[0]);
|
770 |
-
firstLine.fixMarkEnds(lastLine);
|
771 |
-
var added = [];
|
772 |
-
for (var i = 1, e = newText.length - 1; i < e; ++i)
|
773 |
-
added.push(Line.inheritMarks(newText[i], firstLine));
|
774 |
-
added.push(lastLine);
|
775 |
-
doc.insert(from.line + 1, added);
|
776 |
-
}
|
777 |
-
} else if (newText.length == 1) {
|
778 |
-
firstLine.replace(from.ch, null, newText[0]);
|
779 |
-
lastLine.replace(null, to.ch, "");
|
780 |
-
firstLine.append(lastLine);
|
781 |
-
doc.remove(from.line + 1, nlines, callbacks);
|
782 |
-
} else {
|
783 |
-
var added = [];
|
784 |
-
firstLine.replace(from.ch, null, newText[0]);
|
785 |
-
lastLine.replace(null, to.ch, newText[newText.length-1]);
|
786 |
-
firstLine.fixMarkEnds(lastLine);
|
787 |
-
for (var i = 1, e = newText.length - 1; i < e; ++i)
|
788 |
-
added.push(Line.inheritMarks(newText[i], firstLine));
|
789 |
-
if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
|
790 |
-
doc.insert(from.line + 1, added);
|
791 |
-
}
|
792 |
-
if (options.lineWrapping) {
|
793 |
-
var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3);
|
794 |
-
doc.iter(from.line, from.line + newText.length, function(line) {
|
795 |
-
if (line.hidden) return;
|
796 |
-
var guess = Math.ceil(line.text.length / perLine) || 1;
|
797 |
-
if (guess != line.height) updateLineHeight(line, guess);
|
798 |
-
});
|
799 |
-
} else {
|
800 |
-
doc.iter(from.line, from.line + newText.length, function(line) {
|
801 |
-
var l = line.text;
|
802 |
-
if (!line.hidden && l.length > maxLineLength) {
|
803 |
-
maxLine = line; maxLineLength = l.length; maxLineChanged = true;
|
804 |
-
recomputeMaxLength = false;
|
805 |
-
}
|
806 |
-
});
|
807 |
-
if (recomputeMaxLength) updateMaxLine = true;
|
808 |
-
}
|
809 |
-
|
810 |
-
// Add these lines to the work array, so that they will be
|
811 |
-
// highlighted. Adjust work lines if lines were added/removed.
|
812 |
-
var newWork = [], lendiff = newText.length - nlines - 1;
|
813 |
-
for (var i = 0, l = work.length; i < l; ++i) {
|
814 |
-
var task = work[i];
|
815 |
-
if (task < from.line) newWork.push(task);
|
816 |
-
else if (task > to.line) newWork.push(task + lendiff);
|
817 |
-
}
|
818 |
-
var hlEnd = from.line + Math.min(newText.length, 500);
|
819 |
-
highlightLines(from.line, hlEnd);
|
820 |
-
newWork.push(hlEnd);
|
821 |
-
work = newWork;
|
822 |
-
startWorker(100);
|
823 |
-
// Remember that these lines changed, for updating the display
|
824 |
-
changes.push({from: from.line, to: to.line + 1, diff: lendiff});
|
825 |
-
var changeObj = {from: from, to: to, text: newText};
|
826 |
-
if (textChanged) {
|
827 |
-
for (var cur = textChanged; cur.next; cur = cur.next) {}
|
828 |
-
cur.next = changeObj;
|
829 |
-
} else textChanged = changeObj;
|
830 |
-
|
831 |
-
// Update the selection
|
832 |
-
function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
|
833 |
-
setSelection(clipPos(selFrom), clipPos(selTo),
|
834 |
-
updateLine(sel.from.line), updateLine(sel.to.line));
|
835 |
-
}
|
836 |
-
|
837 |
-
function needsScrollbar() {
|
838 |
-
var realHeight = doc.height * textHeight() + 2 * paddingTop();
|
839 |
-
return realHeight * .99 > scroller.offsetHeight ? realHeight : false;
|
840 |
-
}
|
841 |
-
|
842 |
-
function updateVerticalScroll(scrollTop) {
|
843 |
-
var scrollHeight = needsScrollbar();
|
844 |
-
scrollbar.style.display = scrollHeight ? "block" : "none";
|
845 |
-
if (scrollHeight) {
|
846 |
-
scrollbarInner.style.height = sizer.style.minHeight = scrollHeight + "px";
|
847 |
-
scrollbar.style.height = scroller.clientHeight + "px";
|
848 |
-
if (scrollTop != null) {
|
849 |
-
scrollbar.scrollTop = scroller.scrollTop = scrollTop;
|
850 |
-
// 'Nudge' the scrollbar to work around a Webkit bug where,
|
851 |
-
// in some situations, we'd end up with a scrollbar that
|
852 |
-
// reported its scrollTop (and looked) as expected, but
|
853 |
-
// *behaved* as if it was still in a previous state (i.e.
|
854 |
-
// couldn't scroll up, even though it appeared to be at the
|
855 |
-
// bottom).
|
856 |
-
if (webkit) setTimeout(function() {
|
857 |
-
if (scrollbar.scrollTop != scrollTop) return;
|
858 |
-
scrollbar.scrollTop = scrollTop + (scrollTop ? -1 : 1);
|
859 |
-
scrollbar.scrollTop = scrollTop;
|
860 |
-
}, 0);
|
861 |
-
}
|
862 |
-
} else {
|
863 |
-
sizer.style.minHeight = "";
|
864 |
-
}
|
865 |
-
// Position the mover div to align with the current virtual scroll position
|
866 |
-
mover.style.top = displayOffset * textHeight() + "px";
|
867 |
-
}
|
868 |
-
|
869 |
-
function computeMaxLength() {
|
870 |
-
maxLine = getLine(0); maxLineChanged = true;
|
871 |
-
var maxLineLength = maxLine.text.length;
|
872 |
-
doc.iter(1, doc.size, function(line) {
|
873 |
-
var l = line.text;
|
874 |
-
if (!line.hidden && l.length > maxLineLength) {
|
875 |
-
maxLineLength = l.length; maxLine = line;
|
876 |
-
}
|
877 |
-
});
|
878 |
-
updateMaxLine = false;
|
879 |
-
}
|
880 |
-
|
881 |
-
function replaceRange(code, from, to) {
|
882 |
-
from = clipPos(from);
|
883 |
-
if (!to) to = from; else to = clipPos(to);
|
884 |
-
code = splitLines(code);
|
885 |
-
function adjustPos(pos) {
|
886 |
-
if (posLess(pos, from)) return pos;
|
887 |
-
if (!posLess(to, pos)) return end;
|
888 |
-
var line = pos.line + code.length - (to.line - from.line) - 1;
|
889 |
-
var ch = pos.ch;
|
890 |
-
if (pos.line == to.line)
|
891 |
-
ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
|
892 |
-
return {line: line, ch: ch};
|
893 |
-
}
|
894 |
-
var end;
|
895 |
-
replaceRange1(code, from, to, function(end1) {
|
896 |
-
end = end1;
|
897 |
-
return {from: adjustPos(sel.from), to: adjustPos(sel.to)};
|
898 |
-
});
|
899 |
-
return end;
|
900 |
-
}
|
901 |
-
function replaceSelection(code, collapse) {
|
902 |
-
replaceRange1(splitLines(code), sel.from, sel.to, function(end) {
|
903 |
-
if (collapse == "end") return {from: end, to: end};
|
904 |
-
else if (collapse == "start") return {from: sel.from, to: sel.from};
|
905 |
-
else return {from: sel.from, to: end};
|
906 |
-
});
|
907 |
-
}
|
908 |
-
function replaceRange1(code, from, to, computeSel) {
|
909 |
-
var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
|
910 |
-
var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
|
911 |
-
updateLines(from, to, code, newSel.from, newSel.to);
|
912 |
-
}
|
913 |
-
|
914 |
-
function getRange(from, to, lineSep) {
|
915 |
-
var l1 = from.line, l2 = to.line;
|
916 |
-
if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
|
917 |
-
var code = [getLine(l1).text.slice(from.ch)];
|
918 |
-
doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
|
919 |
-
code.push(getLine(l2).text.slice(0, to.ch));
|
920 |
-
return code.join(lineSep || "\n");
|
921 |
-
}
|
922 |
-
function getSelection(lineSep) {
|
923 |
-
return getRange(sel.from, sel.to, lineSep);
|
924 |
-
}
|
925 |
-
|
926 |
-
function slowPoll() {
|
927 |
-
if (pollingFast) return;
|
928 |
-
poll.set(options.pollInterval, function() {
|
929 |
-
startOperation();
|
930 |
-
readInput();
|
931 |
-
if (focused) slowPoll();
|
932 |
-
endOperation();
|
933 |
-
});
|
934 |
-
}
|
935 |
-
function fastPoll() {
|
936 |
-
var missed = false;
|
937 |
-
pollingFast = true;
|
938 |
-
function p() {
|
939 |
-
startOperation();
|
940 |
-
var changed = readInput();
|
941 |
-
if (!changed && !missed) {missed = true; poll.set(60, p);}
|
942 |
-
else {pollingFast = false; slowPoll();}
|
943 |
-
endOperation();
|
944 |
-
}
|
945 |
-
poll.set(20, p);
|
946 |
-
}
|
947 |
-
|
948 |
-
// Previnput is a hack to work with IME. If we reset the textarea
|
949 |
-
// on every change, that breaks IME. So we look for changes
|
950 |
-
// compared to the previous content instead. (Modern browsers have
|
951 |
-
// events that indicate IME taking place, but these are not widely
|
952 |
-
// supported or compatible enough yet to rely on.)
|
953 |
-
var prevInput = "";
|
954 |
-
function readInput() {
|
955 |
-
if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false;
|
956 |
-
var text = input.value;
|
957 |
-
if (text == prevInput) return false;
|
958 |
-
shiftSelecting = null;
|
959 |
-
var same = 0, l = Math.min(prevInput.length, text.length);
|
960 |
-
while (same < l && prevInput[same] == text[same]) ++same;
|
961 |
-
if (same < prevInput.length)
|
962 |
-
sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
|
963 |
-
else if (overwrite && posEq(sel.from, sel.to))
|
964 |
-
sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
|
965 |
-
replaceSelection(text.slice(same), "end");
|
966 |
-
if (text.length > 1000) { input.value = prevInput = ""; }
|
967 |
-
else prevInput = text;
|
968 |
-
return true;
|
969 |
-
}
|
970 |
-
function resetInput(user) {
|
971 |
-
if (!posEq(sel.from, sel.to)) {
|
972 |
-
prevInput = "";
|
973 |
-
input.value = getSelection();
|
974 |
-
if (focused) selectInput(input);
|
975 |
-
} else if (user) prevInput = input.value = "";
|
976 |
-
}
|
977 |
-
|
978 |
-
function focusInput() {
|
979 |
-
if (options.readOnly != "nocursor") input.focus();
|
980 |
-
}
|
981 |
-
|
982 |
-
function scrollCursorIntoView() {
|
983 |
-
var coords = calculateCursorCoords();
|
984 |
-
scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
|
985 |
-
if (!focused) return;
|
986 |
-
var box = sizer.getBoundingClientRect(), doScroll = null;
|
987 |
-
if (coords.y + box.top < 0) doScroll = true;
|
988 |
-
else if (coords.y + box.top + textHeight() > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
|
989 |
-
if (doScroll != null) {
|
990 |
-
var hidden = cursor.style.display == "none";
|
991 |
-
if (hidden) {
|
992 |
-
cursor.style.display = "";
|
993 |
-
cursor.style.left = coords.x + "px";
|
994 |
-
cursor.style.top = (coords.y - displayOffset) + "px";
|
995 |
-
}
|
996 |
-
cursor.scrollIntoView(doScroll);
|
997 |
-
if (hidden) cursor.style.display = "none";
|
998 |
-
}
|
999 |
-
}
|
1000 |
-
function calculateCursorCoords() {
|
1001 |
-
var cursor = localCoords(sel.inverted ? sel.from : sel.to);
|
1002 |
-
var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
|
1003 |
-
return {x: x, y: cursor.y, yBot: cursor.yBot};
|
1004 |
-
}
|
1005 |
-
function scrollIntoView(x1, y1, x2, y2) {
|
1006 |
-
var scrollPos = calculateScrollPos(x1, y1, x2, y2);
|
1007 |
-
if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft;}
|
1008 |
-
if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scroller.scrollTop = scrollPos.scrollTop;}
|
1009 |
-
}
|
1010 |
-
function calculateScrollPos(x1, y1, x2, y2) {
|
1011 |
-
var pl = paddingLeft(), pt = paddingTop();
|
1012 |
-
y1 += pt; y2 += pt; x1 += pl; x2 += pl;
|
1013 |
-
var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {};
|
1014 |
-
var docBottom = needsScrollbar() || Infinity;
|
1015 |
-
var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
|
1016 |
-
if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
|
1017 |
-
else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
|
1018 |
-
|
1019 |
-
var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
|
1020 |
-
var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
|
1021 |
-
var atLeft = x1 < gutterw + pl + 10;
|
1022 |
-
if (x1 < screenleft + gutterw || atLeft) {
|
1023 |
-
if (atLeft) x1 = 0;
|
1024 |
-
result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
|
1025 |
-
} else if (x2 > screenw + screenleft - 3) {
|
1026 |
-
result.scrollLeft = x2 + 10 - screenw;
|
1027 |
-
}
|
1028 |
-
return result;
|
1029 |
-
}
|
1030 |
-
|
1031 |
-
function visibleLines(scrollTop) {
|
1032 |
-
var lh = textHeight(), top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop();
|
1033 |
-
var fromHeight = Math.max(0, Math.floor(top / lh));
|
1034 |
-
var toHeight = Math.ceil((top + scroller.clientHeight) / lh);
|
1035 |
-
return {from: lineAtHeight(doc, fromHeight),
|
1036 |
-
to: lineAtHeight(doc, toHeight)};
|
1037 |
-
}
|
1038 |
-
// Uses a set of changes plus the current scroll position to
|
1039 |
-
// determine which DOM updates have to be made, and makes the
|
1040 |
-
// updates.
|
1041 |
-
function updateDisplay(changes, suppressCallback, scrollTop) {
|
1042 |
-
if (!scroller.clientWidth) {
|
1043 |
-
showingFrom = showingTo = displayOffset = 0;
|
1044 |
-
return;
|
1045 |
-
}
|
1046 |
-
// Compute the new visible window
|
1047 |
-
// If scrollTop is specified, use that to determine which lines
|
1048 |
-
// to render instead of the current scrollbar position.
|
1049 |
-
var visible = visibleLines(scrollTop);
|
1050 |
-
// Bail out if the visible area is already rendered and nothing changed.
|
1051 |
-
if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) {
|
1052 |
-
updateVerticalScroll(scrollTop);
|
1053 |
-
return;
|
1054 |
-
}
|
1055 |
-
var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
|
1056 |
-
if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
|
1057 |
-
if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
|
1058 |
-
|
1059 |
-
// Create a range of theoretically intact lines, and punch holes
|
1060 |
-
// in that using the change info.
|
1061 |
-
var intact = changes === true ? [] :
|
1062 |
-
computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
|
1063 |
-
// Clip off the parts that won't be visible
|
1064 |
-
var intactLines = 0;
|
1065 |
-
for (var i = 0; i < intact.length; ++i) {
|
1066 |
-
var range = intact[i];
|
1067 |
-
if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
|
1068 |
-
if (range.to > to) range.to = to;
|
1069 |
-
if (range.from >= range.to) intact.splice(i--, 1);
|
1070 |
-
else intactLines += range.to - range.from;
|
1071 |
-
}
|
1072 |
-
if (intactLines == to - from && from == showingFrom && to == showingTo) {
|
1073 |
-
updateVerticalScroll(scrollTop);
|
1074 |
-
return;
|
1075 |
-
}
|
1076 |
-
intact.sort(function(a, b) {return a.domStart - b.domStart;});
|
1077 |
-
|
1078 |
-
var th = textHeight(), gutterDisplay = gutter.style.display;
|
1079 |
-
lineDiv.style.display = "none";
|
1080 |
-
patchDisplay(from, to, intact);
|
1081 |
-
lineDiv.style.display = gutter.style.display = "";
|
1082 |
-
|
1083 |
-
var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
|
1084 |
-
// This is just a bogus formula that detects when the editor is
|
1085 |
-
// resized or the font size changes.
|
1086 |
-
if (different) lastSizeC = scroller.clientHeight + th;
|
1087 |
-
if (from != showingFrom || to != showingTo && options.onViewportChange)
|
1088 |
-
setTimeout(function(){
|
1089 |
-
if (options.onViewportChange) options.onViewportChange(instance, from, to);
|
1090 |
-
});
|
1091 |
-
showingFrom = from; showingTo = to;
|
1092 |
-
displayOffset = heightAtLine(doc, from);
|
1093 |
-
|
1094 |
-
// Since this is all rather error prone, it is honoured with the
|
1095 |
-
// only assertion in the whole file.
|
1096 |
-
if (lineDiv.childNodes.length != showingTo - showingFrom)
|
1097 |
-
throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
|
1098 |
-
" nodes=" + lineDiv.childNodes.length);
|
1099 |
-
|
1100 |
-
function checkHeights() {
|
1101 |
-
var curNode = lineDiv.firstChild, heightChanged = false;
|
1102 |
-
doc.iter(showingFrom, showingTo, function(line) {
|
1103 |
-
// Work around bizarro IE7 bug where, sometimes, our curNode
|
1104 |
-
// is magically replaced with a new node in the DOM, leaving
|
1105 |
-
// us with a reference to an orphan (nextSibling-less) node.
|
1106 |
-
if (!curNode) return;
|
1107 |
-
if (!line.hidden) {
|
1108 |
-
var height = Math.round(curNode.offsetHeight / th) || 1;
|
1109 |
-
if (line.height != height) {
|
1110 |
-
updateLineHeight(line, height);
|
1111 |
-
gutterDirty = heightChanged = true;
|
1112 |
-
}
|
1113 |
-
}
|
1114 |
-
curNode = curNode.nextSibling;
|
1115 |
-
});
|
1116 |
-
return heightChanged;
|
1117 |
-
}
|
1118 |
-
|
1119 |
-
if (options.lineWrapping) checkHeights();
|
1120 |
-
|
1121 |
-
gutter.style.display = gutterDisplay;
|
1122 |
-
if (different || gutterDirty) {
|
1123 |
-
// If the gutter grew in size, re-check heights. If those changed, re-draw gutter.
|
1124 |
-
updateGutter() && options.lineWrapping && checkHeights() && updateGutter();
|
1125 |
-
}
|
1126 |
-
updateVerticalScroll(scrollTop);
|
1127 |
-
updateSelection();
|
1128 |
-
if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
|
1129 |
-
return true;
|
1130 |
-
}
|
1131 |
-
|
1132 |
-
function computeIntact(intact, changes) {
|
1133 |
-
for (var i = 0, l = changes.length || 0; i < l; ++i) {
|
1134 |
-
var change = changes[i], intact2 = [], diff = change.diff || 0;
|
1135 |
-
for (var j = 0, l2 = intact.length; j < l2; ++j) {
|
1136 |
-
var range = intact[j];
|
1137 |
-
if (change.to <= range.from && change.diff)
|
1138 |
-
intact2.push({from: range.from + diff, to: range.to + diff,
|
1139 |
-
domStart: range.domStart});
|
1140 |
-
else if (change.to <= range.from || change.from >= range.to)
|
1141 |
-
intact2.push(range);
|
1142 |
-
else {
|
1143 |
-
if (change.from > range.from)
|
1144 |
-
intact2.push({from: range.from, to: change.from, domStart: range.domStart});
|
1145 |
-
if (change.to < range.to)
|
1146 |
-
intact2.push({from: change.to + diff, to: range.to + diff,
|
1147 |
-
domStart: range.domStart + (change.to - range.from)});
|
1148 |
-
}
|
1149 |
-
}
|
1150 |
-
intact = intact2;
|
1151 |
-
}
|
1152 |
-
return intact;
|
1153 |
-
}
|
1154 |
-
|
1155 |
-
function patchDisplay(from, to, intact) {
|
1156 |
-
function killNode(node) {
|
1157 |
-
var tmp = node.nextSibling;
|
1158 |
-
node.parentNode.removeChild(node);
|
1159 |
-
return tmp;
|
1160 |
-
}
|
1161 |
-
// The first pass removes the DOM nodes that aren't intact.
|
1162 |
-
if (!intact.length) removeChildren(lineDiv);
|
1163 |
-
else {
|
1164 |
-
var domPos = 0, curNode = lineDiv.firstChild, n;
|
1165 |
-
for (var i = 0; i < intact.length; ++i) {
|
1166 |
-
var cur = intact[i];
|
1167 |
-
while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
|
1168 |
-
for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
|
1169 |
-
}
|
1170 |
-
while (curNode) curNode = killNode(curNode);
|
1171 |
-
}
|
1172 |
-
// This pass fills in the lines that actually changed.
|
1173 |
-
var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
|
1174 |
-
doc.iter(from, to, function(line) {
|
1175 |
-
if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
|
1176 |
-
if (!nextIntact || nextIntact.from > j) {
|
1177 |
-
if (line.hidden) var lineElement = elt("pre");
|
1178 |
-
else {
|
1179 |
-
var lineElement = line.getElement(makeTab);
|
1180 |
-
if (line.className) lineElement.className = line.className;
|
1181 |
-
// Kludge to make sure the styled element lies behind the selection (by z-index)
|
1182 |
-
if (line.bgClassName) {
|
1183 |
-
var pre = elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2");
|
1184 |
-
lineElement = elt("div", [pre, lineElement], null, "position: relative");
|
1185 |
-
}
|
1186 |
-
}
|
1187 |
-
lineDiv.insertBefore(lineElement, curNode);
|
1188 |
-
} else {
|
1189 |
-
curNode = curNode.nextSibling;
|
1190 |
-
}
|
1191 |
-
++j;
|
1192 |
-
});
|
1193 |
-
}
|
1194 |
-
|
1195 |
-
function updateGutter() {
|
1196 |
-
if (!options.gutter && !options.lineNumbers) return;
|
1197 |
-
var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
|
1198 |
-
gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
|
1199 |
-
var fragment = document.createDocumentFragment(), i = showingFrom, normalNode;
|
1200 |
-
doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
|
1201 |
-
if (line.hidden) {
|
1202 |
-
fragment.appendChild(elt("pre"));
|
1203 |
-
} else {
|
1204 |
-
var marker = line.gutterMarker;
|
1205 |
-
var text = options.lineNumbers ? options.lineNumberFormatter(i + options.firstLineNumber) : null;
|
1206 |
-
if (marker && marker.text)
|
1207 |
-
text = marker.text.replace("%N%", text != null ? text : "");
|
1208 |
-
else if (text == null)
|
1209 |
-
text = "\u00a0";
|
1210 |
-
var markerElement = fragment.appendChild(elt("pre", null, marker && marker.style));
|
1211 |
-
markerElement.innerHTML = text;
|
1212 |
-
for (var j = 1; j < line.height; ++j) {
|
1213 |
-
markerElement.appendChild(elt("br"));
|
1214 |
-
markerElement.appendChild(document.createTextNode("\u00a0"));
|
1215 |
-
}
|
1216 |
-
if (!marker) normalNode = i;
|
1217 |
-
}
|
1218 |
-
++i;
|
1219 |
-
});
|
1220 |
-
gutter.style.display = "none";
|
1221 |
-
removeChildrenAndAdd(gutterText, fragment);
|
1222 |
-
// Make sure scrolling doesn't cause number gutter size to pop
|
1223 |
-
if (normalNode != null && options.lineNumbers) {
|
1224 |
-
var node = gutterText.childNodes[normalNode - showingFrom];
|
1225 |
-
var minwidth = String(doc.size).length, val = eltText(node.firstChild), pad = "";
|
1226 |
-
while (val.length + pad.length < minwidth) pad += "\u00a0";
|
1227 |
-
if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild);
|
1228 |
-
}
|
1229 |
-
gutter.style.display = "";
|
1230 |
-
var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2;
|
1231 |
-
lineSpace.style.marginLeft = gutter.offsetWidth + "px";
|
1232 |
-
gutterDirty = false;
|
1233 |
-
return resized;
|
1234 |
-
}
|
1235 |
-
function updateSelection() {
|
1236 |
-
var collapsed = posEq(sel.from, sel.to);
|
1237 |
-
var fromPos = localCoords(sel.from, true);
|
1238 |
-
var toPos = collapsed ? fromPos : localCoords(sel.to, true);
|
1239 |
-
var headPos = sel.inverted ? fromPos : toPos, th = textHeight();
|
1240 |
-
var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
|
1241 |
-
inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px";
|
1242 |
-
inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px";
|
1243 |
-
if (collapsed) {
|
1244 |
-
cursor.style.top = headPos.y + "px";
|
1245 |
-
cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px";
|
1246 |
-
cursor.style.display = "";
|
1247 |
-
selectionDiv.style.display = "none";
|
1248 |
-
} else {
|
1249 |
-
var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment();
|
1250 |
-
var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
|
1251 |
-
var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
|
1252 |
-
var add = function(left, top, right, height) {
|
1253 |
-
var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px"
|
1254 |
-
: "right: " + right + "px";
|
1255 |
-
fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
|
1256 |
-
"px; top: " + top + "px; " + rstyle + "; height: " + height + "px"));
|
1257 |
-
};
|
1258 |
-
if (sel.from.ch && fromPos.y >= 0) {
|
1259 |
-
var right = sameLine ? clientWidth - toPos.x : 0;
|
1260 |
-
add(fromPos.x, fromPos.y, right, th);
|
1261 |
-
}
|
1262 |
-
var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0));
|
1263 |
-
var middleHeight = Math.min(toPos.y, clientHeight) - middleStart;
|
1264 |
-
if (middleHeight > 0.2 * th)
|
1265 |
-
add(0, middleStart, 0, middleHeight);
|
1266 |
-
if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th)
|
1267 |
-
add(0, toPos.y, clientWidth - toPos.x, th);
|
1268 |
-
removeChildrenAndAdd(selectionDiv, fragment);
|
1269 |
-
cursor.style.display = "none";
|
1270 |
-
selectionDiv.style.display = "";
|
1271 |
-
}
|
1272 |
-
}
|
1273 |
-
|
1274 |
-
function setShift(val) {
|
1275 |
-
if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
|
1276 |
-
else shiftSelecting = null;
|
1277 |
-
}
|
1278 |
-
function setSelectionUser(from, to) {
|
1279 |
-
var sh = shiftSelecting && clipPos(shiftSelecting);
|
1280 |
-
if (sh) {
|
1281 |
-
if (posLess(sh, from)) from = sh;
|
1282 |
-
else if (posLess(to, sh)) to = sh;
|
1283 |
-
}
|
1284 |
-
setSelection(from, to);
|
1285 |
-
userSelChange = true;
|
1286 |
-
}
|
1287 |
-
// Update the selection. Last two args are only used by
|
1288 |
-
// updateLines, since they have to be expressed in the line
|
1289 |
-
// numbers before the update.
|
1290 |
-
function setSelection(from, to, oldFrom, oldTo) {
|
1291 |
-
goalColumn = null;
|
1292 |
-
if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
|
1293 |
-
if (posEq(sel.from, from) && posEq(sel.to, to)) return;
|
1294 |
-
if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
|
1295 |
-
|
1296 |
-
// Skip over hidden lines.
|
1297 |
-
if (from.line != oldFrom) {
|
1298 |
-
var from1 = skipHidden(from, oldFrom, sel.from.ch);
|
1299 |
-
// If there is no non-hidden line left, force visibility on current line
|
1300 |
-
if (!from1) setLineHidden(from.line, false);
|
1301 |
-
else from = from1;
|
1302 |
-
}
|
1303 |
-
if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
|
1304 |
-
|
1305 |
-
if (posEq(from, to)) sel.inverted = false;
|
1306 |
-
else if (posEq(from, sel.to)) sel.inverted = false;
|
1307 |
-
else if (posEq(to, sel.from)) sel.inverted = true;
|
1308 |
-
|
1309 |
-
if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) {
|
1310 |
-
var head = sel.inverted ? from : to;
|
1311 |
-
if (head.line != sel.from.line && sel.from.line < doc.size) {
|
1312 |
-
var oldLine = getLine(sel.from.line);
|
1313 |
-
if (/^\s+$/.test(oldLine.text))
|
1314 |
-
setTimeout(operation(function() {
|
1315 |
-
if (oldLine.parent && /^\s+$/.test(oldLine.text)) {
|
1316 |
-
var no = lineNo(oldLine);
|
1317 |
-
replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length});
|
1318 |
-
}
|
1319 |
-
}, 10));
|
1320 |
-
}
|
1321 |
-
}
|
1322 |
-
|
1323 |
-
sel.from = from; sel.to = to;
|
1324 |
-
selectionChanged = true;
|
1325 |
-
}
|
1326 |
-
function skipHidden(pos, oldLine, oldCh) {
|
1327 |
-
function getNonHidden(dir) {
|
1328 |
-
var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
|
1329 |
-
while (lNo != end) {
|
1330 |
-
var line = getLine(lNo);
|
1331 |
-
if (!line.hidden) {
|
1332 |
-
var ch = pos.ch;
|
1333 |
-
if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length;
|
1334 |
-
return {line: lNo, ch: ch};
|
1335 |
-
}
|
1336 |
-
lNo += dir;
|
1337 |
-
}
|
1338 |
-
}
|
1339 |
-
var line = getLine(pos.line);
|
1340 |
-
var toEnd = pos.ch == line.text.length && pos.ch != oldCh;
|
1341 |
-
if (!line.hidden) return pos;
|
1342 |
-
if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
|
1343 |
-
else return getNonHidden(-1) || getNonHidden(1);
|
1344 |
-
}
|
1345 |
-
function setCursor(line, ch, user) {
|
1346 |
-
var pos = clipPos({line: line, ch: ch || 0});
|
1347 |
-
(user ? setSelectionUser : setSelection)(pos, pos);
|
1348 |
-
}
|
1349 |
-
|
1350 |
-
function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
|
1351 |
-
function clipPos(pos) {
|
1352 |
-
if (pos.line < 0) return {line: 0, ch: 0};
|
1353 |
-
if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
|
1354 |
-
var ch = pos.ch, linelen = getLine(pos.line).text.length;
|
1355 |
-
if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
|
1356 |
-
else if (ch < 0) return {line: pos.line, ch: 0};
|
1357 |
-
else return pos;
|
1358 |
-
}
|
1359 |
-
|
1360 |
-
function findPosH(dir, unit) {
|
1361 |
-
var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch;
|
1362 |
-
var lineObj = getLine(line);
|
1363 |
-
function findNextLine() {
|
1364 |
-
for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) {
|
1365 |
-
var lo = getLine(l);
|
1366 |
-
if (!lo.hidden) { line = l; lineObj = lo; return true; }
|
1367 |
-
}
|
1368 |
-
}
|
1369 |
-
function moveOnce(boundToLine) {
|
1370 |
-
if (ch == (dir < 0 ? 0 : lineObj.text.length)) {
|
1371 |
-
if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0;
|
1372 |
-
else return false;
|
1373 |
-
} else ch += dir;
|
1374 |
-
return true;
|
1375 |
-
}
|
1376 |
-
if (unit == "char") moveOnce();
|
1377 |
-
else if (unit == "column") moveOnce(true);
|
1378 |
-
else if (unit == "word") {
|
1379 |
-
var sawWord = false;
|
1380 |
-
for (;;) {
|
1381 |
-
if (dir < 0) if (!moveOnce()) break;
|
1382 |
-
if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
|
1383 |
-
else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
|
1384 |
-
if (dir > 0) if (!moveOnce()) break;
|
1385 |
-
}
|
1386 |
-
}
|
1387 |
-
return {line: line, ch: ch};
|
1388 |
-
}
|
1389 |
-
function moveH(dir, unit) {
|
1390 |
-
var pos = dir < 0 ? sel.from : sel.to;
|
1391 |
-
if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit);
|
1392 |
-
setCursor(pos.line, pos.ch, true);
|
1393 |
-
}
|
1394 |
-
function deleteH(dir, unit) {
|
1395 |
-
if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to);
|
1396 |
-
else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to);
|
1397 |
-
else replaceRange("", sel.from, findPosH(dir, unit));
|
1398 |
-
userSelChange = true;
|
1399 |
-
}
|
1400 |
-
function moveV(dir, unit) {
|
1401 |
-
var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
|
1402 |
-
if (goalColumn != null) pos.x = goalColumn;
|
1403 |
-
if (unit == "page") {
|
1404 |
-
var screen = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
|
1405 |
-
var target = coordsChar(pos.x, pos.y + screen * dir);
|
1406 |
-
} else if (unit == "line") {
|
1407 |
-
var th = textHeight();
|
1408 |
-
var target = coordsChar(pos.x, pos.y + .5 * th + dir * th);
|
1409 |
-
}
|
1410 |
-
if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y;
|
1411 |
-
setCursor(target.line, target.ch, true);
|
1412 |
-
goalColumn = pos.x;
|
1413 |
-
}
|
1414 |
-
|
1415 |
-
function findWordAt(pos) {
|
1416 |
-
var line = getLine(pos.line).text;
|
1417 |
-
var start = pos.ch, end = pos.ch;
|
1418 |
-
if (line) {
|
1419 |
-
if (pos.after === false || end == line.length) --start; else ++end;
|
1420 |
-
var startChar = line.charAt(start);
|
1421 |
-
var check = isWordChar(startChar) ? isWordChar :
|
1422 |
-
/\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
|
1423 |
-
function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
|
1424 |
-
while (start > 0 && check(line.charAt(start - 1))) --start;
|
1425 |
-
while (end < line.length && check(line.charAt(end))) ++end;
|
1426 |
-
}
|
1427 |
-
return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
|
1428 |
-
}
|
1429 |
-
function selectLine(line) {
|
1430 |
-
setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0}));
|
1431 |
-
}
|
1432 |
-
function indentSelected(mode) {
|
1433 |
-
if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
|
1434 |
-
var e = sel.to.line - (sel.to.ch ? 0 : 1);
|
1435 |
-
for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
|
1436 |
-
}
|
1437 |
-
|
1438 |
-
function indentLine(n, how) {
|
1439 |
-
if (!how) how = "add";
|
1440 |
-
if (how == "smart") {
|
1441 |
-
if (!mode.indent) how = "prev";
|
1442 |
-
else var state = getStateBefore(n);
|
1443 |
-
}
|
1444 |
-
|
1445 |
-
var line = getLine(n), curSpace = line.indentation(options.tabSize),
|
1446 |
-
curSpaceString = line.text.match(/^\s*/)[0], indentation;
|
1447 |
-
if (how == "smart") {
|
1448 |
-
indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
|
1449 |
-
if (indentation == Pass) how = "prev";
|
1450 |
-
}
|
1451 |
-
if (how == "prev") {
|
1452 |
-
if (n) indentation = getLine(n-1).indentation(options.tabSize);
|
1453 |
-
else indentation = 0;
|
1454 |
-
}
|
1455 |
-
else if (how == "add") indentation = curSpace + options.indentUnit;
|
1456 |
-
else if (how == "subtract") indentation = curSpace - options.indentUnit;
|
1457 |
-
indentation = Math.max(0, indentation);
|
1458 |
-
var diff = indentation - curSpace;
|
1459 |
-
|
1460 |
-
var indentString = "", pos = 0;
|
1461 |
-
if (options.indentWithTabs)
|
1462 |
-
for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
|
1463 |
-
while (pos < indentation) {++pos; indentString += " ";}
|
1464 |
-
|
1465 |
-
if (indentString != curSpaceString)
|
1466 |
-
replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
|
1467 |
-
}
|
1468 |
-
|
1469 |
-
function loadMode() {
|
1470 |
-
mode = CodeMirror.getMode(options, options.mode);
|
1471 |
-
doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
|
1472 |
-
work = [0];
|
1473 |
-
startWorker();
|
1474 |
-
}
|
1475 |
-
function gutterChanged() {
|
1476 |
-
var visible = options.gutter || options.lineNumbers;
|
1477 |
-
gutter.style.display = visible ? "" : "none";
|
1478 |
-
if (visible) gutterDirty = true;
|
1479 |
-
else lineDiv.parentNode.style.marginLeft = 0;
|
1480 |
-
}
|
1481 |
-
function wrappingChanged(from, to) {
|
1482 |
-
if (options.lineWrapping) {
|
1483 |
-
wrapper.className += " CodeMirror-wrap";
|
1484 |
-
var perLine = scroller.clientWidth / charWidth() - 3;
|
1485 |
-
doc.iter(0, doc.size, function(line) {
|
1486 |
-
if (line.hidden) return;
|
1487 |
-
var guess = Math.ceil(line.text.length / perLine) || 1;
|
1488 |
-
if (guess != 1) updateLineHeight(line, guess);
|
1489 |
-
});
|
1490 |
-
lineSpace.style.minWidth = widthForcer.style.left = "";
|
1491 |
-
} else {
|
1492 |
-
wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
|
1493 |
-
computeMaxLength();
|
1494 |
-
doc.iter(0, doc.size, function(line) {
|
1495 |
-
if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
|
1496 |
-
});
|
1497 |
-
}
|
1498 |
-
changes.push({from: 0, to: doc.size});
|
1499 |
-
}
|
1500 |
-
function makeTab(col) {
|
1501 |
-
var w = options.tabSize - col % options.tabSize, cached = tabCache[w];
|
1502 |
-
if (cached) return cached;
|
1503 |
-
for (var str = "", i = 0; i < w; ++i) str += " ";
|
1504 |
-
var span = elt("span", str, "cm-tab");
|
1505 |
-
return (tabCache[w] = {element: span, width: w});
|
1506 |
-
}
|
1507 |
-
function themeChanged() {
|
1508 |
-
scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") +
|
1509 |
-
options.theme.replace(/(^|\s)\s*/g, " cm-s-");
|
1510 |
-
}
|
1511 |
-
function keyMapChanged() {
|
1512 |
-
var style = keyMap[options.keyMap].style;
|
1513 |
-
wrapper.className = wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
|
1514 |
-
(style ? " cm-keymap-" + style : "");
|
1515 |
-
}
|
1516 |
-
|
1517 |
-
function TextMarker() { this.set = []; }
|
1518 |
-
TextMarker.prototype.clear = operation(function() {
|
1519 |
-
var min = Infinity, max = -Infinity;
|
1520 |
-
for (var i = 0, e = this.set.length; i < e; ++i) {
|
1521 |
-
var line = this.set[i], mk = line.marked;
|
1522 |
-
if (!mk || !line.parent) continue;
|
1523 |
-
var lineN = lineNo(line);
|
1524 |
-
min = Math.min(min, lineN); max = Math.max(max, lineN);
|
1525 |
-
for (var j = 0; j < mk.length; ++j)
|
1526 |
-
if (mk[j].marker == this) mk.splice(j--, 1);
|
1527 |
-
}
|
1528 |
-
if (min != Infinity)
|
1529 |
-
changes.push({from: min, to: max + 1});
|
1530 |
-
});
|
1531 |
-
TextMarker.prototype.find = function() {
|
1532 |
-
var from, to;
|
1533 |
-
for (var i = 0, e = this.set.length; i < e; ++i) {
|
1534 |
-
var line = this.set[i], mk = line.marked;
|
1535 |
-
for (var j = 0; j < mk.length; ++j) {
|
1536 |
-
var mark = mk[j];
|
1537 |
-
if (mark.marker == this) {
|
1538 |
-
if (mark.from != null || mark.to != null) {
|
1539 |
-
var found = lineNo(line);
|
1540 |
-
if (found != null) {
|
1541 |
-
if (mark.from != null) from = {line: found, ch: mark.from};
|
1542 |
-
if (mark.to != null) to = {line: found, ch: mark.to};
|
1543 |
-
}
|
1544 |
-
}
|
1545 |
-
}
|
1546 |
-
}
|
1547 |
-
}
|
1548 |
-
return {from: from, to: to};
|
1549 |
-
};
|
1550 |
-
|
1551 |
-
function markText(from, to, className) {
|
1552 |
-
from = clipPos(from); to = clipPos(to);
|
1553 |
-
var tm = new TextMarker();
|
1554 |
-
if (!posLess(from, to)) return tm;
|
1555 |
-
function add(line, from, to, className) {
|
1556 |
-
getLine(line).addMark(new MarkedText(from, to, className, tm));
|
1557 |
-
}
|
1558 |
-
if (from.line == to.line) add(from.line, from.ch, to.ch, className);
|
1559 |
-
else {
|
1560 |
-
add(from.line, from.ch, null, className);
|
1561 |
-
for (var i = from.line + 1, e = to.line; i < e; ++i)
|
1562 |
-
add(i, null, null, className);
|
1563 |
-
add(to.line, null, to.ch, className);
|
1564 |
-
}
|
1565 |
-
changes.push({from: from.line, to: to.line + 1});
|
1566 |
-
return tm;
|
1567 |
-
}
|
1568 |
-
|
1569 |
-
function setBookmark(pos) {
|
1570 |
-
pos = clipPos(pos);
|
1571 |
-
var bm = new Bookmark(pos.ch);
|
1572 |
-
getLine(pos.line).addMark(bm);
|
1573 |
-
return bm;
|
1574 |
-
}
|
1575 |
-
|
1576 |
-
function findMarksAt(pos) {
|
1577 |
-
pos = clipPos(pos);
|
1578 |
-
var markers = [], marked = getLine(pos.line).marked;
|
1579 |
-
if (!marked) return markers;
|
1580 |
-
for (var i = 0, e = marked.length; i < e; ++i) {
|
1581 |
-
var m = marked[i];
|
1582 |
-
if ((m.from == null || m.from <= pos.ch) &&
|
1583 |
-
(m.to == null || m.to >= pos.ch))
|
1584 |
-
markers.push(m.marker || m);
|
1585 |
-
}
|
1586 |
-
return markers;
|
1587 |
-
}
|
1588 |
-
|
1589 |
-
function addGutterMarker(line, text, className) {
|
1590 |
-
if (typeof line == "number") line = getLine(clipLine(line));
|
1591 |
-
line.gutterMarker = {text: text, style: className};
|
1592 |
-
gutterDirty = true;
|
1593 |
-
return line;
|
1594 |
-
}
|
1595 |
-
function removeGutterMarker(line) {
|
1596 |
-
if (typeof line == "number") line = getLine(clipLine(line));
|
1597 |
-
line.gutterMarker = null;
|
1598 |
-
gutterDirty = true;
|
1599 |
-
}
|
1600 |
-
|
1601 |
-
function changeLine(handle, op) {
|
1602 |
-
var no = handle, line = handle;
|
1603 |
-
if (typeof handle == "number") line = getLine(clipLine(handle));
|
1604 |
-
else no = lineNo(handle);
|
1605 |
-
if (no == null) return null;
|
1606 |
-
if (op(line, no)) changes.push({from: no, to: no + 1});
|
1607 |
-
else return null;
|
1608 |
-
return line;
|
1609 |
-
}
|
1610 |
-
function setLineClass(handle, className, bgClassName) {
|
1611 |
-
return changeLine(handle, function(line) {
|
1612 |
-
if (line.className != className || line.bgClassName != bgClassName) {
|
1613 |
-
line.className = className;
|
1614 |
-
line.bgClassName = bgClassName;
|
1615 |
-
return true;
|
1616 |
-
}
|
1617 |
-
});
|
1618 |
-
}
|
1619 |
-
function setLineHidden(handle, hidden) {
|
1620 |
-
return changeLine(handle, function(line, no) {
|
1621 |
-
if (line.hidden != hidden) {
|
1622 |
-
line.hidden = hidden;
|
1623 |
-
if (!options.lineWrapping) {
|
1624 |
-
if (hidden && line.text.length == maxLine.text.length) {
|
1625 |
-
updateMaxLine = true;
|
1626 |
-
} else if (!hidden && line.text.length > maxLine.text.length) {
|
1627 |
-
maxLine = line; updateMaxLine = false;
|
1628 |
-
}
|
1629 |
-
}
|
1630 |
-
updateLineHeight(line, hidden ? 0 : 1);
|
1631 |
-
var fline = sel.from.line, tline = sel.to.line;
|
1632 |
-
if (hidden && (fline == no || tline == no)) {
|
1633 |
-
var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from;
|
1634 |
-
var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to;
|
1635 |
-
// Can't hide the last visible line, we'd have no place to put the cursor
|
1636 |
-
if (!to) return;
|
1637 |
-
setSelection(from, to);
|
1638 |
-
}
|
1639 |
-
return (gutterDirty = true);
|
1640 |
-
}
|
1641 |
-
});
|
1642 |
-
}
|
1643 |
-
|
1644 |
-
function lineInfo(line) {
|
1645 |
-
if (typeof line == "number") {
|
1646 |
-
if (!isLine(line)) return null;
|
1647 |
-
var n = line;
|
1648 |
-
line = getLine(line);
|
1649 |
-
if (!line) return null;
|
1650 |
-
} else {
|
1651 |
-
var n = lineNo(line);
|
1652 |
-
if (n == null) return null;
|
1653 |
-
}
|
1654 |
-
var marker = line.gutterMarker;
|
1655 |
-
return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
|
1656 |
-
markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName};
|
1657 |
-
}
|
1658 |
-
|
1659 |
-
// These are used to go from pixel positions to character
|
1660 |
-
// positions, taking varying character widths into account.
|
1661 |
-
function charFromX(line, x) {
|
1662 |
-
if (x <= 0) return 0;
|
1663 |
-
var lineObj = getLine(line), text = lineObj.text;
|
1664 |
-
function getX(len) {
|
1665 |
-
return measureLine(lineObj, len).left;
|
1666 |
-
}
|
1667 |
-
var from = 0, fromX = 0, to = text.length, toX;
|
1668 |
-
// Guess a suitable upper bound for our search.
|
1669 |
-
var estimated = Math.min(to, Math.ceil(x / charWidth()));
|
1670 |
-
for (;;) {
|
1671 |
-
var estX = getX(estimated);
|
1672 |
-
if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
|
1673 |
-
else {toX = estX; to = estimated; break;}
|
1674 |
-
}
|
1675 |
-
if (x > toX) return to;
|
1676 |
-
// Try to guess a suitable lower bound as well.
|
1677 |
-
estimated = Math.floor(to * 0.8); estX = getX(estimated);
|
1678 |
-
if (estX < x) {from = estimated; fromX = estX;}
|
1679 |
-
// Do a binary search between these bounds.
|
1680 |
-
for (;;) {
|
1681 |
-
if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
|
1682 |
-
var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
|
1683 |
-
if (middleX > x) {to = middle; toX = middleX;}
|
1684 |
-
else {from = middle; fromX = middleX;}
|
1685 |
-
}
|
1686 |
-
}
|
1687 |
-
|
1688 |
-
function measureLine(line, ch) {
|
1689 |
-
if (ch == 0) return {top: 0, left: 0};
|
1690 |
-
var wbr = options.lineWrapping && ch < line.text.length &&
|
1691 |
-
spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1));
|
1692 |
-
var pre = line.getElement(makeTab, ch, wbr);
|
1693 |
-
removeChildrenAndAdd(measure, pre);
|
1694 |
-
var anchor = pre.anchor;
|
1695 |
-
var top = anchor.offsetTop, left = anchor.offsetLeft;
|
1696 |
-
// Older IEs report zero offsets for spans directly after a wrap
|
1697 |
-
if (ie && top == 0 && left == 0) {
|
1698 |
-
var backup = elt("span", "x");
|
1699 |
-
anchor.parentNode.insertBefore(backup, anchor.nextSibling);
|
1700 |
-
top = backup.offsetTop;
|
1701 |
-
}
|
1702 |
-
return {top: top, left: left};
|
1703 |
-
}
|
1704 |
-
function localCoords(pos, inLineWrap) {
|
1705 |
-
var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
|
1706 |
-
if (pos.ch == 0) x = 0;
|
1707 |
-
else {
|
1708 |
-
var sp = measureLine(getLine(pos.line), pos.ch);
|
1709 |
-
x = sp.left;
|
1710 |
-
if (options.lineWrapping) y += Math.max(0, sp.top);
|
1711 |
-
}
|
1712 |
-
return {x: x, y: y, yBot: y + lh};
|
1713 |
-
}
|
1714 |
-
// Coords must be lineSpace-local
|
1715 |
-
function coordsChar(x, y) {
|
1716 |
-
var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
|
1717 |
-
if (heightPos < 0) return {line: 0, ch: 0};
|
1718 |
-
var lineNo = lineAtHeight(doc, heightPos);
|
1719 |
-
if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
|
1720 |
-
var lineObj = getLine(lineNo), text = lineObj.text;
|
1721 |
-
var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
|
1722 |
-
if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
|
1723 |
-
var wrongLine = false;
|
1724 |
-
function getX(len) {
|
1725 |
-
var sp = measureLine(lineObj, len);
|
1726 |
-
if (tw) {
|
1727 |
-
var off = Math.round(sp.top / th);
|
1728 |
-
wrongLine = off != innerOff;
|
1729 |
-
return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
|
1730 |
-
}
|
1731 |
-
return sp.left;
|
1732 |
-
}
|
1733 |
-
var from = 0, fromX = 0, to = text.length, toX;
|
1734 |
-
// Guess a suitable upper bound for our search.
|
1735 |
-
var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
|
1736 |
-
for (;;) {
|
1737 |
-
var estX = getX(estimated);
|
1738 |
-
if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
|
1739 |
-
else {toX = estX; to = estimated; break;}
|
1740 |
-
}
|
1741 |
-
if (x > toX) return {line: lineNo, ch: to};
|
1742 |
-
// Try to guess a suitable lower bound as well.
|
1743 |
-
estimated = Math.floor(to * 0.8); estX = getX(estimated);
|
1744 |
-
if (estX < x) {from = estimated; fromX = estX;}
|
1745 |
-
// Do a binary search between these bounds.
|
1746 |
-
for (;;) {
|
1747 |
-
if (to - from <= 1) {
|
1748 |
-
var after = x - fromX < toX - x;
|
1749 |
-
return {line: lineNo, ch: after ? from : to, after: after};
|
1750 |
-
}
|
1751 |
-
var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
|
1752 |
-
if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; }
|
1753 |
-
else {from = middle; fromX = middleX;}
|
1754 |
-
}
|
1755 |
-
}
|
1756 |
-
function pageCoords(pos) {
|
1757 |
-
var local = localCoords(pos, true), off = eltOffset(lineSpace);
|
1758 |
-
return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
|
1759 |
-
}
|
1760 |
-
|
1761 |
-
var cachedHeight, cachedHeightFor, measurePre;
|
1762 |
-
function textHeight() {
|
1763 |
-
if (measurePre == null) {
|
1764 |
-
measurePre = elt("pre");
|
1765 |
-
for (var i = 0; i < 49; ++i) {
|
1766 |
-
measurePre.appendChild(document.createTextNode("x"));
|
1767 |
-
measurePre.appendChild(elt("br"));
|
1768 |
-
}
|
1769 |
-
measurePre.appendChild(document.createTextNode("x"));
|
1770 |
-
}
|
1771 |
-
var offsetHeight = lineDiv.clientHeight;
|
1772 |
-
if (offsetHeight == cachedHeightFor) return cachedHeight;
|
1773 |
-
cachedHeightFor = offsetHeight;
|
1774 |
-
removeChildrenAndAdd(measure, measurePre.cloneNode(true));
|
1775 |
-
cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
|
1776 |
-
removeChildren(measure);
|
1777 |
-
return cachedHeight;
|
1778 |
-
}
|
1779 |
-
var cachedWidth, cachedWidthFor = 0;
|
1780 |
-
function charWidth() {
|
1781 |
-
if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
|
1782 |
-
cachedWidthFor = scroller.clientWidth;
|
1783 |
-
var anchor = elt("span", "x");
|
1784 |
-
var pre = elt("pre", [anchor]);
|
1785 |
-
removeChildrenAndAdd(measure, pre);
|
1786 |
-
return (cachedWidth = anchor.offsetWidth || 10);
|
1787 |
-
}
|
1788 |
-
function paddingTop() {return lineSpace.offsetTop;}
|
1789 |
-
function paddingLeft() {return lineSpace.offsetLeft;}
|
1790 |
-
|
1791 |
-
function posFromMouse(e, liberal) {
|
1792 |
-
var offW = eltOffset(scroller, true), x, y;
|
1793 |
-
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
|
1794 |
-
try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
|
1795 |
-
// This is a mess of a heuristic to try and determine whether a
|
1796 |
-
// scroll-bar was clicked or not, and to return null if one was
|
1797 |
-
// (and !liberal).
|
1798 |
-
if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
|
1799 |
-
return null;
|
1800 |
-
var offL = eltOffset(lineSpace, true);
|
1801 |
-
return coordsChar(x - offL.left, y - offL.top);
|
1802 |
-
}
|
1803 |
-
function onContextMenu(e) {
|
1804 |
-
var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop;
|
1805 |
-
if (!pos || opera) return; // Opera is difficult.
|
1806 |
-
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
|
1807 |
-
operation(setCursor)(pos.line, pos.ch);
|
1808 |
-
|
1809 |
-
var oldCSS = input.style.cssText;
|
1810 |
-
inputDiv.style.position = "absolute";
|
1811 |
-
input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
|
1812 |
-
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
|
1813 |
-
"border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
|
1814 |
-
leaveInputAlone = true;
|
1815 |
-
var val = input.value = getSelection();
|
1816 |
-
focusInput();
|
1817 |
-
selectInput(input);
|
1818 |
-
function rehide() {
|
1819 |
-
var newVal = splitLines(input.value).join("\n");
|
1820 |
-
if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end");
|
1821 |
-
inputDiv.style.position = "relative";
|
1822 |
-
input.style.cssText = oldCSS;
|
1823 |
-
if (ie_lt9) scrollbar.scrollTop = scrollPos;
|
1824 |
-
leaveInputAlone = false;
|
1825 |
-
resetInput(true);
|
1826 |
-
slowPoll();
|
1827 |
-
}
|
1828 |
-
|
1829 |
-
if (gecko) {
|
1830 |
-
e_stop(e);
|
1831 |
-
var mouseup = connect(window, "mouseup", function() {
|
1832 |
-
mouseup();
|
1833 |
-
setTimeout(rehide, 20);
|
1834 |
-
}, true);
|
1835 |
-
} else {
|
1836 |
-
setTimeout(rehide, 50);
|
1837 |
-
}
|
1838 |
-
}
|
1839 |
-
|
1840 |
-
// Cursor-blinking
|
1841 |
-
function restartBlink() {
|
1842 |
-
clearInterval(blinker);
|
1843 |
-
var on = true;
|
1844 |
-
cursor.style.visibility = "";
|
1845 |
-
blinker = setInterval(function() {
|
1846 |
-
cursor.style.visibility = (on = !on) ? "" : "hidden";
|
1847 |
-
}, options.cursorBlinkRate);
|
1848 |
-
}
|
1849 |
-
|
1850 |
-
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
|
1851 |
-
function matchBrackets(autoclear) {
|
1852 |
-
var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
|
1853 |
-
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
|
1854 |
-
if (!match) return;
|
1855 |
-
var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
|
1856 |
-
for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
|
1857 |
-
if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
|
1858 |
-
|
1859 |
-
var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
|
1860 |
-
function scan(line, from, to) {
|
1861 |
-
if (!line.text) return;
|
1862 |
-
var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
|
1863 |
-
for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
|
1864 |
-
var text = st[i];
|
1865 |
-
if (st[i+1] != style) {pos += d * text.length; continue;}
|
1866 |
-
for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
|
1867 |
-
if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
|
1868 |
-
var match = matching[cur];
|
1869 |
-
if (match.charAt(1) == ">" == forward) stack.push(cur);
|
1870 |
-
else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
|
1871 |
-
else if (!stack.length) return {pos: pos, match: true};
|
1872 |
-
}
|
1873 |
-
}
|
1874 |
-
}
|
1875 |
-
}
|
1876 |
-
for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
|
1877 |
-
var line = getLine(i), first = i == head.line;
|
1878 |
-
var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
|
1879 |
-
if (found) break;
|
1880 |
-
}
|
1881 |
-
if (!found) found = {pos: null, match: false};
|
1882 |
-
var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
1883 |
-
var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
|
1884 |
-
two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
|
1885 |
-
var clear = operation(function(){one.clear(); two && two.clear();});
|
1886 |
-
if (autoclear) setTimeout(clear, 800);
|
1887 |
-
else bracketHighlighted = clear;
|
1888 |
-
}
|
1889 |
-
|
1890 |
-
// Finds the line to start with when starting a parse. Tries to
|
1891 |
-
// find a line with a stateAfter, so that it can start with a
|
1892 |
-
// valid state. If that fails, it returns the line with the
|
1893 |
-
// smallest indentation, which tends to need the least context to
|
1894 |
-
// parse correctly.
|
1895 |
-
function findStartLine(n) {
|
1896 |
-
var minindent, minline;
|
1897 |
-
for (var search = n, lim = n - 40; search > lim; --search) {
|
1898 |
-
if (search == 0) return 0;
|
1899 |
-
var line = getLine(search-1);
|
1900 |
-
if (line.stateAfter) return search;
|
1901 |
-
var indented = line.indentation(options.tabSize);
|
1902 |
-
if (minline == null || minindent > indented) {
|
1903 |
-
minline = search - 1;
|
1904 |
-
minindent = indented;
|
1905 |
-
}
|
1906 |
-
}
|
1907 |
-
return minline;
|
1908 |
-
}
|
1909 |
-
function getStateBefore(n) {
|
1910 |
-
var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
|
1911 |
-
if (!state) state = startState(mode);
|
1912 |
-
else state = copyState(mode, state);
|
1913 |
-
doc.iter(start, n, function(line) {
|
1914 |
-
line.highlight(mode, state, options.tabSize);
|
1915 |
-
line.stateAfter = copyState(mode, state);
|
1916 |
-
});
|
1917 |
-
if (start < n) changes.push({from: start, to: n});
|
1918 |
-
if (n < doc.size && !getLine(n).stateAfter) work.push(n);
|
1919 |
-
return state;
|
1920 |
-
}
|
1921 |
-
function highlightLines(start, end) {
|
1922 |
-
var state = getStateBefore(start);
|
1923 |
-
doc.iter(start, end, function(line) {
|
1924 |
-
line.highlight(mode, state, options.tabSize);
|
1925 |
-
line.stateAfter = copyState(mode, state);
|
1926 |
-
});
|
1927 |
-
}
|
1928 |
-
function highlightWorker() {
|
1929 |
-
var end = +new Date + options.workTime;
|
1930 |
-
var foundWork = work.length;
|
1931 |
-
while (work.length) {
|
1932 |
-
if (!getLine(showingFrom).stateAfter) var task = showingFrom;
|
1933 |
-
else var task = work.pop();
|
1934 |
-
if (task >= doc.size) continue;
|
1935 |
-
var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
|
1936 |
-
if (state) state = copyState(mode, state);
|
1937 |
-
else state = startState(mode);
|
1938 |
-
|
1939 |
-
var unchanged = 0, compare = mode.compareStates, realChange = false,
|
1940 |
-
i = start, bail = false;
|
1941 |
-
doc.iter(i, doc.size, function(line) {
|
1942 |
-
var hadState = line.stateAfter;
|
1943 |
-
if (+new Date > end) {
|
1944 |
-
work.push(i);
|
1945 |
-
startWorker(options.workDelay);
|
1946 |
-
if (realChange) changes.push({from: task, to: i + 1});
|
1947 |
-
return (bail = true);
|
1948 |
-
}
|
1949 |
-
var changed = line.highlight(mode, state, options.tabSize);
|
1950 |
-
if (changed) realChange = true;
|
1951 |
-
line.stateAfter = copyState(mode, state);
|
1952 |
-
var done = null;
|
1953 |
-
if (compare) {
|
1954 |
-
var same = hadState && compare(hadState, state);
|
1955 |
-
if (same != Pass) done = !!same;
|
1956 |
-
}
|
1957 |
-
if (done == null) {
|
1958 |
-
if (changed !== false || !hadState) unchanged = 0;
|
1959 |
-
else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
|
1960 |
-
done = true;
|
1961 |
-
}
|
1962 |
-
if (done) return true;
|
1963 |
-
++i;
|
1964 |
-
});
|
1965 |
-
if (bail) return;
|
1966 |
-
if (realChange) changes.push({from: task, to: i + 1});
|
1967 |
-
}
|
1968 |
-
if (foundWork && options.onHighlightComplete)
|
1969 |
-
options.onHighlightComplete(instance);
|
1970 |
-
}
|
1971 |
-
function startWorker(time) {
|
1972 |
-
if (!work.length) return;
|
1973 |
-
highlight.set(time, operation(highlightWorker));
|
1974 |
-
}
|
1975 |
-
|
1976 |
-
// Operations are used to wrap changes in such a way that each
|
1977 |
-
// change won't have to update the cursor and display (which would
|
1978 |
-
// be awkward, slow, and error-prone), but instead updates are
|
1979 |
-
// batched and then all combined and executed at once.
|
1980 |
-
function startOperation() {
|
1981 |
-
updateInput = userSelChange = textChanged = null;
|
1982 |
-
changes = []; selectionChanged = false; callbacks = [];
|
1983 |
-
}
|
1984 |
-
function endOperation() {
|
1985 |
-
if (updateMaxLine) computeMaxLength();
|
1986 |
-
if (maxLineChanged && !options.lineWrapping) {
|
1987 |
-
var cursorWidth = widthForcer.offsetWidth, left = measureLine(maxLine, maxLine.text.length).left;
|
1988 |
-
if (!ie_lt8) {
|
1989 |
-
widthForcer.style.left = left + "px";
|
1990 |
-
lineSpace.style.minWidth = (left + cursorWidth) + "px";
|
1991 |
-
}
|
1992 |
-
maxLineChanged = false;
|
1993 |
-
}
|
1994 |
-
var newScrollPos, updated;
|
1995 |
-
if (selectionChanged) {
|
1996 |
-
var coords = calculateCursorCoords();
|
1997 |
-
newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot);
|
1998 |
-
}
|
1999 |
-
if (changes.length || newScrollPos && newScrollPos.scrollTop != null)
|
2000 |
-
updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop);
|
2001 |
-
if (!updated) {
|
2002 |
-
if (selectionChanged) updateSelection();
|
2003 |
-
if (gutterDirty) updateGutter();
|
2004 |
-
}
|
2005 |
-
if (newScrollPos) scrollCursorIntoView();
|
2006 |
-
if (selectionChanged) restartBlink();
|
2007 |
-
|
2008 |
-
if (focused && !leaveInputAlone &&
|
2009 |
-
(updateInput === true || (updateInput !== false && selectionChanged)))
|
2010 |
-
resetInput(userSelChange);
|
2011 |
-
|
2012 |
-
if (selectionChanged && options.matchBrackets)
|
2013 |
-
setTimeout(operation(function() {
|
2014 |
-
if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
|
2015 |
-
if (posEq(sel.from, sel.to)) matchBrackets(false);
|
2016 |
-
}), 20);
|
2017 |
-
var sc = selectionChanged, cbs = callbacks; // these can be reset by callbacks
|
2018 |
-
if (textChanged && options.onChange && instance)
|
2019 |
-
options.onChange(instance, textChanged);
|
2020 |
-
if (sc && options.onCursorActivity)
|
2021 |
-
options.onCursorActivity(instance);
|
2022 |
-
for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
|
2023 |
-
if (updated && options.onUpdate) options.onUpdate(instance);
|
2024 |
-
}
|
2025 |
-
var nestedOperation = 0;
|
2026 |
-
function operation(f) {
|
2027 |
-
return function() {
|
2028 |
-
if (!nestedOperation++) startOperation();
|
2029 |
-
try {var result = f.apply(this, arguments);}
|
2030 |
-
finally {if (!--nestedOperation) endOperation();}
|
2031 |
-
return result;
|
2032 |
-
};
|
2033 |
-
}
|
2034 |
-
|
2035 |
-
function compoundChange(f) {
|
2036 |
-
history.startCompound();
|
2037 |
-
try { return f(); } finally { history.endCompound(); }
|
2038 |
-
}
|
2039 |
-
|
2040 |
-
for (var ext in extensions)
|
2041 |
-
if (extensions.propertyIsEnumerable(ext) &&
|
2042 |
-
!instance.propertyIsEnumerable(ext))
|
2043 |
-
instance[ext] = extensions[ext];
|
2044 |
-
return instance;
|
2045 |
-
} // (end of function CodeMirror)
|
2046 |
-
|
2047 |
-
// The default configuration options.
|
2048 |
-
CodeMirror.defaults = {
|
2049 |
-
value: "",
|
2050 |
-
mode: null,
|
2051 |
-
theme: "default",
|
2052 |
-
indentUnit: 2,
|
2053 |
-
indentWithTabs: false,
|
2054 |
-
smartIndent: true,
|
2055 |
-
tabSize: 4,
|
2056 |
-
keyMap: "default",
|
2057 |
-
extraKeys: null,
|
2058 |
-
electricChars: true,
|
2059 |
-
autoClearEmptyLines: false,
|
2060 |
-
onKeyEvent: null,
|
2061 |
-
onDragEvent: null,
|
2062 |
-
lineWrapping: false,
|
2063 |
-
lineNumbers: false,
|
2064 |
-
gutter: false,
|
2065 |
-
fixedGutter: false,
|
2066 |
-
firstLineNumber: 1,
|
2067 |
-
readOnly: false,
|
2068 |
-
dragDrop: true,
|
2069 |
-
onChange: null,
|
2070 |
-
onCursorActivity: null,
|
2071 |
-
onViewportChange: null,
|
2072 |
-
onGutterClick: null,
|
2073 |
-
onHighlightComplete: null,
|
2074 |
-
onUpdate: null,
|
2075 |
-
onFocus: null, onBlur: null, onScroll: null,
|
2076 |
-
matchBrackets: false,
|
2077 |
-
cursorBlinkRate: 530,
|
2078 |
-
workTime: 100,
|
2079 |
-
workDelay: 200,
|
2080 |
-
pollInterval: 100,
|
2081 |
-
undoDepth: 40,
|
2082 |
-
tabindex: null,
|
2083 |
-
autofocus: null,
|
2084 |
-
lineNumberFormatter: function(integer) { return integer; }
|
2085 |
-
};
|
2086 |
-
|
2087 |
-
var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
|
2088 |
-
var mac = ios || /Mac/.test(navigator.platform);
|
2089 |
-
var win = /Win/.test(navigator.platform);
|
2090 |
-
|
2091 |
-
// Known modes, by name and by MIME
|
2092 |
-
var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
|
2093 |
-
CodeMirror.defineMode = function(name, mode) {
|
2094 |
-
if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
|
2095 |
-
if (arguments.length > 2) {
|
2096 |
-
mode.dependencies = [];
|
2097 |
-
for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
|
2098 |
-
}
|
2099 |
-
modes[name] = mode;
|
2100 |
-
};
|
2101 |
-
CodeMirror.defineMIME = function(mime, spec) {
|
2102 |
-
mimeModes[mime] = spec;
|
2103 |
-
};
|
2104 |
-
CodeMirror.resolveMode = function(spec) {
|
2105 |
-
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
|
2106 |
-
spec = mimeModes[spec];
|
2107 |
-
else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
|
2108 |
-
return CodeMirror.resolveMode("application/xml");
|
2109 |
-
if (typeof spec == "string") return {name: spec};
|
2110 |
-
else return spec || {name: "null"};
|
2111 |
-
};
|
2112 |
-
CodeMirror.getMode = function(options, spec) {
|
2113 |
-
var spec = CodeMirror.resolveMode(spec);
|
2114 |
-
var mfactory = modes[spec.name];
|
2115 |
-
if (!mfactory) return CodeMirror.getMode(options, "text/plain");
|
2116 |
-
return mfactory(options, spec);
|
2117 |
-
};
|
2118 |
-
CodeMirror.listModes = function() {
|
2119 |
-
var list = [];
|
2120 |
-
for (var m in modes)
|
2121 |
-
if (modes.propertyIsEnumerable(m)) list.push(m);
|
2122 |
-
return list;
|
2123 |
-
};
|
2124 |
-
CodeMirror.listMIMEs = function() {
|
2125 |
-
var list = [];
|
2126 |
-
for (var m in mimeModes)
|
2127 |
-
if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
|
2128 |
-
return list;
|
2129 |
-
};
|
2130 |
-
|
2131 |
-
var extensions = CodeMirror.extensions = {};
|
2132 |
-
CodeMirror.defineExtension = function(name, func) {
|
2133 |
-
extensions[name] = func;
|
2134 |
-
};
|
2135 |
-
|
2136 |
-
var commands = CodeMirror.commands = {
|
2137 |
-
selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
|
2138 |
-
killLine: function(cm) {
|
2139 |
-
var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
|
2140 |
-
if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0});
|
2141 |
-
else cm.replaceRange("", from, sel ? to : {line: from.line});
|
2142 |
-
},
|
2143 |
-
deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});},
|
2144 |
-
undo: function(cm) {cm.undo();},
|
2145 |
-
redo: function(cm) {cm.redo();},
|
2146 |
-
goDocStart: function(cm) {cm.setCursor(0, 0, true);},
|
2147 |
-
goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);},
|
2148 |
-
goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);},
|
2149 |
-
goLineStartSmart: function(cm) {
|
2150 |
-
var cur = cm.getCursor();
|
2151 |
-
var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/));
|
2152 |
-
cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true);
|
2153 |
-
},
|
2154 |
-
goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);},
|
2155 |
-
goLineUp: function(cm) {cm.moveV(-1, "line");},
|
2156 |
-
goLineDown: function(cm) {cm.moveV(1, "line");},
|
2157 |
-
goPageUp: function(cm) {cm.moveV(-1, "page");},
|
2158 |
-
goPageDown: function(cm) {cm.moveV(1, "page");},
|
2159 |
-
goCharLeft: function(cm) {cm.moveH(-1, "char");},
|
2160 |
-
goCharRight: function(cm) {cm.moveH(1, "char");},
|
2161 |
-
goColumnLeft: function(cm) {cm.moveH(-1, "column");},
|
2162 |
-
goColumnRight: function(cm) {cm.moveH(1, "column");},
|
2163 |
-
goWordLeft: function(cm) {cm.moveH(-1, "word");},
|
2164 |
-
goWordRight: function(cm) {cm.moveH(1, "word");},
|
2165 |
-
delCharLeft: function(cm) {cm.deleteH(-1, "char");},
|
2166 |
-
delCharRight: function(cm) {cm.deleteH(1, "char");},
|
2167 |
-
delWordLeft: function(cm) {cm.deleteH(-1, "word");},
|
2168 |
-
delWordRight: function(cm) {cm.deleteH(1, "word");},
|
2169 |
-
indentAuto: function(cm) {cm.indentSelection("smart");},
|
2170 |
-
indentMore: function(cm) {cm.indentSelection("add");},
|
2171 |
-
indentLess: function(cm) {cm.indentSelection("subtract");},
|
2172 |
-
insertTab: function(cm) {cm.replaceSelection("\t", "end");},
|
2173 |
-
defaultTab: function(cm) {
|
2174 |
-
if (cm.somethingSelected()) cm.indentSelection("add");
|
2175 |
-
else cm.replaceSelection("\t", "end");
|
2176 |
-
},
|
2177 |
-
transposeChars: function(cm) {
|
2178 |
-
var cur = cm.getCursor(), line = cm.getLine(cur.line);
|
2179 |
-
if (cur.ch > 0 && cur.ch < line.length - 1)
|
2180 |
-
cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
|
2181 |
-
{line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
|
2182 |
-
},
|
2183 |
-
newlineAndIndent: function(cm) {
|
2184 |
-
cm.replaceSelection("\n", "end");
|
2185 |
-
cm.indentLine(cm.getCursor().line);
|
2186 |
-
},
|
2187 |
-
toggleOverwrite: function(cm) {cm.toggleOverwrite();}
|
2188 |
-
};
|
2189 |
-
|
2190 |
-
var keyMap = CodeMirror.keyMap = {};
|
2191 |
-
keyMap.basic = {
|
2192 |
-
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
|
2193 |
-
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
|
2194 |
-
"Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
|
2195 |
-
"Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
|
2196 |
-
};
|
2197 |
-
// Note that the save and find-related commands aren't defined by
|
2198 |
-
// default. Unknown commands are simply ignored.
|
2199 |
-
keyMap.pcDefault = {
|
2200 |
-
"Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
|
2201 |
-
"Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
|
2202 |
-
"Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
|
2203 |
-
"Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
|
2204 |
-
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
|
2205 |
-
"Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
|
2206 |
-
fallthrough: "basic"
|
2207 |
-
};
|
2208 |
-
keyMap.macDefault = {
|
2209 |
-
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
|
2210 |
-
"Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
|
2211 |
-
"Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
|
2212 |
-
"Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
|
2213 |
-
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
|
2214 |
-
"Cmd-[": "indentLess", "Cmd-]": "indentMore",
|
2215 |
-
fallthrough: ["basic", "emacsy"]
|
2216 |
-
};
|
2217 |
-
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
|
2218 |
-
keyMap.emacsy = {
|
2219 |
-
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
|
2220 |
-
"Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
|
2221 |
-
"Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
|
2222 |
-
"Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
|
2223 |
-
};
|
2224 |
-
|
2225 |
-
function getKeyMap(val) {
|
2226 |
-
if (typeof val == "string") return keyMap[val];
|
2227 |
-
else return val;
|
2228 |
-
}
|
2229 |
-
function lookupKey(name, extraMap, map, handle, stop) {
|
2230 |
-
function lookup(map) {
|
2231 |
-
map = getKeyMap(map);
|
2232 |
-
var found = map[name];
|
2233 |
-
if (found === false) {
|
2234 |
-
if (stop) stop();
|
2235 |
-
return true;
|
2236 |
-
}
|
2237 |
-
if (found != null && handle(found)) return true;
|
2238 |
-
if (map.nofallthrough) {
|
2239 |
-
if (stop) stop();
|
2240 |
-
return true;
|
2241 |
-
}
|
2242 |
-
var fallthrough = map.fallthrough;
|
2243 |
-
if (fallthrough == null) return false;
|
2244 |
-
if (Object.prototype.toString.call(fallthrough) != "[object Array]")
|
2245 |
-
return lookup(fallthrough);
|
2246 |
-
for (var i = 0, e = fallthrough.length; i < e; ++i) {
|
2247 |
-
if (lookup(fallthrough[i])) return true;
|
2248 |
-
}
|
2249 |
-
return false;
|
2250 |
-
}
|
2251 |
-
if (extraMap && lookup(extraMap)) return true;
|
2252 |
-
return lookup(map);
|
2253 |
-
}
|
2254 |
-
function isModifierKey(event) {
|
2255 |
-
var name = keyNames[e_prop(event, "keyCode")];
|
2256 |
-
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
|
2257 |
-
}
|
2258 |
-
|
2259 |
-
CodeMirror.fromTextArea = function(textarea, options) {
|
2260 |
-
if (!options) options = {};
|
2261 |
-
options.value = textarea.value;
|
2262 |
-
if (!options.tabindex && textarea.tabindex)
|
2263 |
-
options.tabindex = textarea.tabindex;
|
2264 |
-
// Set autofocus to true if this textarea is focused, or if it has
|
2265 |
-
// autofocus and no other element is focused.
|
2266 |
-
if (options.autofocus == null) {
|
2267 |
-
var hasFocus = document.body;
|
2268 |
-
// doc.activeElement occasionally throws on IE
|
2269 |
-
try { hasFocus = document.activeElement; } catch(e) {}
|
2270 |
-
options.autofocus = hasFocus == textarea ||
|
2271 |
-
textarea.getAttribute("autofocus") != null && hasFocus == document.body;
|
2272 |
-
}
|
2273 |
-
|
2274 |
-
function save() {textarea.value = instance.getValue();}
|
2275 |
-
if (textarea.form) {
|
2276 |
-
// Deplorable hack to make the submit method do the right thing.
|
2277 |
-
var rmSubmit = connect(textarea.form, "submit", save, true);
|
2278 |
-
if (typeof textarea.form.submit == "function") {
|
2279 |
-
var realSubmit = textarea.form.submit;
|
2280 |
-
textarea.form.submit = function wrappedSubmit() {
|
2281 |
-
save();
|
2282 |
-
textarea.form.submit = realSubmit;
|
2283 |
-
textarea.form.submit();
|
2284 |
-
textarea.form.submit = wrappedSubmit;
|
2285 |
-
};
|
2286 |
-
}
|
2287 |
-
}
|
2288 |
-
|
2289 |
-
textarea.style.display = "none";
|
2290 |
-
var instance = CodeMirror(function(node) {
|
2291 |
-
textarea.parentNode.insertBefore(node, textarea.nextSibling);
|
2292 |
-
}, options);
|
2293 |
-
instance.save = save;
|
2294 |
-
instance.getTextArea = function() { return textarea; };
|
2295 |
-
instance.toTextArea = function() {
|
2296 |
-
save();
|
2297 |
-
textarea.parentNode.removeChild(instance.getWrapperElement());
|
2298 |
-
textarea.style.display = "";
|
2299 |
-
if (textarea.form) {
|
2300 |
-
rmSubmit();
|
2301 |
-
if (typeof textarea.form.submit == "function")
|
2302 |
-
textarea.form.submit = realSubmit;
|
2303 |
-
}
|
2304 |
-
};
|
2305 |
-
return instance;
|
2306 |
-
};
|
2307 |
-
|
2308 |
-
var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
|
2309 |
-
var ie = /MSIE \d/.test(navigator.userAgent);
|
2310 |
-
var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
|
2311 |
-
var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
|
2312 |
-
var quirksMode = ie && document.documentMode == 5;
|
2313 |
-
var webkit = /WebKit\//.test(navigator.userAgent);
|
2314 |
-
var chrome = /Chrome\//.test(navigator.userAgent);
|
2315 |
-
var opera = /Opera\//.test(navigator.userAgent);
|
2316 |
-
var safari = /Apple Computer/.test(navigator.vendor);
|
2317 |
-
var khtml = /KHTML\//.test(navigator.userAgent);
|
2318 |
-
var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent);
|
2319 |
-
|
2320 |
-
// Utility functions for working with state. Exported because modes
|
2321 |
-
// sometimes need to do this.
|
2322 |
-
function copyState(mode, state) {
|
2323 |
-
if (state === true) return state;
|
2324 |
-
if (mode.copyState) return mode.copyState(state);
|
2325 |
-
var nstate = {};
|
2326 |
-
for (var n in state) {
|
2327 |
-
var val = state[n];
|
2328 |
-
if (val instanceof Array) val = val.concat([]);
|
2329 |
-
nstate[n] = val;
|
2330 |
-
}
|
2331 |
-
return nstate;
|
2332 |
-
}
|
2333 |
-
CodeMirror.copyState = copyState;
|
2334 |
-
function startState(mode, a1, a2) {
|
2335 |
-
return mode.startState ? mode.startState(a1, a2) : true;
|
2336 |
-
}
|
2337 |
-
CodeMirror.startState = startState;
|
2338 |
-
|
2339 |
-
// The character stream used by a mode's parser.
|
2340 |
-
function StringStream(string, tabSize) {
|
2341 |
-
this.pos = this.start = 0;
|
2342 |
-
this.string = string;
|
2343 |
-
this.tabSize = tabSize || 8;
|
2344 |
-
}
|
2345 |
-
StringStream.prototype = {
|
2346 |
-
eol: function() {return this.pos >= this.string.length;},
|
2347 |
-
sol: function() {return this.pos == 0;},
|
2348 |
-
peek: function() {return this.string.charAt(this.pos) || undefined;},
|
2349 |
-
next: function() {
|
2350 |
-
if (this.pos < this.string.length)
|
2351 |
-
return this.string.charAt(this.pos++);
|
2352 |
-
},
|
2353 |
-
eat: function(match) {
|
2354 |
-
var ch = this.string.charAt(this.pos);
|
2355 |
-
if (typeof match == "string") var ok = ch == match;
|
2356 |
-
else var ok = ch && (match.test ? match.test(ch) : match(ch));
|
2357 |
-
if (ok) {++this.pos; return ch;}
|
2358 |
-
},
|
2359 |
-
eatWhile: function(match) {
|
2360 |
-
var start = this.pos;
|
2361 |
-
while (this.eat(match)){}
|
2362 |
-
return this.pos > start;
|
2363 |
-
},
|
2364 |
-
eatSpace: function() {
|
2365 |
-
var start = this.pos;
|
2366 |
-
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
|
2367 |
-
return this.pos > start;
|
2368 |
-
},
|
2369 |
-
skipToEnd: function() {this.pos = this.string.length;},
|
2370 |
-
skipTo: function(ch) {
|
2371 |
-
var found = this.string.indexOf(ch, this.pos);
|
2372 |
-
if (found > -1) {this.pos = found; return true;}
|
2373 |
-
},
|
2374 |
-
backUp: function(n) {this.pos -= n;},
|
2375 |
-
column: function() {return countColumn(this.string, this.start, this.tabSize);},
|
2376 |
-
indentation: function() {return countColumn(this.string, null, this.tabSize);},
|
2377 |
-
match: function(pattern, consume, caseInsensitive) {
|
2378 |
-
if (typeof pattern == "string") {
|
2379 |
-
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
|
2380 |
-
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
|
2381 |
-
if (consume !== false) this.pos += pattern.length;
|
2382 |
-
return true;
|
2383 |
-
}
|
2384 |
-
} else {
|
2385 |
-
var match = this.string.slice(this.pos).match(pattern);
|
2386 |
-
if (match && consume !== false) this.pos += match[0].length;
|
2387 |
-
return match;
|
2388 |
-
}
|
2389 |
-
},
|
2390 |
-
current: function(){return this.string.slice(this.start, this.pos);}
|
2391 |
-
};
|
2392 |
-
CodeMirror.StringStream = StringStream;
|
2393 |
-
|
2394 |
-
function MarkedText(from, to, className, marker) {
|
2395 |
-
this.from = from; this.to = to; this.style = className; this.marker = marker;
|
2396 |
-
}
|
2397 |
-
MarkedText.prototype = {
|
2398 |
-
attach: function(line) { this.marker.set.push(line); },
|
2399 |
-
detach: function(line) {
|
2400 |
-
var ix = indexOf(this.marker.set, line);
|
2401 |
-
if (ix > -1) this.marker.set.splice(ix, 1);
|
2402 |
-
},
|
2403 |
-
split: function(pos, lenBefore) {
|
2404 |
-
if (this.to <= pos && this.to != null) return null;
|
2405 |
-
var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
|
2406 |
-
var to = this.to == null ? null : this.to - pos + lenBefore;
|
2407 |
-
return new MarkedText(from, to, this.style, this.marker);
|
2408 |
-
},
|
2409 |
-
dup: function() { return new MarkedText(null, null, this.style, this.marker); },
|
2410 |
-
clipTo: function(fromOpen, from, toOpen, to, diff) {
|
2411 |
-
if (fromOpen && to > this.from && (to < this.to || this.to == null))
|
2412 |
-
this.from = null;
|
2413 |
-
else if (this.from != null && this.from >= from)
|
2414 |
-
this.from = Math.max(to, this.from) + diff;
|
2415 |
-
if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
|
2416 |
-
this.to = null;
|
2417 |
-
else if (this.to != null && this.to > from)
|
2418 |
-
this.to = to < this.to ? this.to + diff : from;
|
2419 |
-
},
|
2420 |
-
isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
|
2421 |
-
sameSet: function(x) { return this.marker == x.marker; }
|
2422 |
-
};
|
2423 |
-
|
2424 |
-
function Bookmark(pos) {
|
2425 |
-
this.from = pos; this.to = pos; this.line = null;
|
2426 |
-
}
|
2427 |
-
Bookmark.prototype = {
|
2428 |
-
attach: function(line) { this.line = line; },
|
2429 |
-
detach: function(line) { if (this.line == line) this.line = null; },
|
2430 |
-
split: function(pos, lenBefore) {
|
2431 |
-
if (pos < this.from) {
|
2432 |
-
this.from = this.to = (this.from - pos) + lenBefore;
|
2433 |
-
return this;
|
2434 |
-
}
|
2435 |
-
},
|
2436 |
-
isDead: function() { return this.from > this.to; },
|
2437 |
-
clipTo: function(fromOpen, from, toOpen, to, diff) {
|
2438 |
-
if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
|
2439 |
-
this.from = 0; this.to = -1;
|
2440 |
-
} else if (this.from > from) {
|
2441 |
-
this.from = this.to = Math.max(to, this.from) + diff;
|
2442 |
-
}
|
2443 |
-
},
|
2444 |
-
sameSet: function(x) { return false; },
|
2445 |
-
find: function() {
|
2446 |
-
if (!this.line || !this.line.parent) return null;
|
2447 |
-
return {line: lineNo(this.line), ch: this.from};
|
2448 |
-
},
|
2449 |
-
clear: function() {
|
2450 |
-
if (this.line) {
|
2451 |
-
var found = indexOf(this.line.marked, this);
|
2452 |
-
if (found != -1) this.line.marked.splice(found, 1);
|
2453 |
-
this.line = null;
|
2454 |
-
}
|
2455 |
-
}
|
2456 |
-
};
|
2457 |
-
|
2458 |
-
// When measuring the position of the end of a line, different
|
2459 |
-
// browsers require different approaches. If an empty span is added,
|
2460 |
-
// many browsers report bogus offsets. Of those, some (Webkit,
|
2461 |
-
// recent IE) will accept a space without moving the whole span to
|
2462 |
-
// the next line when wrapping it, others work with a zero-width
|
2463 |
-
// space.
|
2464 |
-
var eolSpanContent = " ";
|
2465 |
-
if (gecko || (ie && !ie_lt8)) eolSpanContent = "\u200b";
|
2466 |
-
else if (opera) eolSpanContent = "";
|
2467 |
-
|
2468 |
-
// Line objects. These hold state related to a line, including
|
2469 |
-
// highlighting info (the styles array).
|
2470 |
-
function Line(text, styles) {
|
2471 |
-
this.styles = styles || [text, null];
|
2472 |
-
this.text = text;
|
2473 |
-
this.height = 1;
|
2474 |
-
}
|
2475 |
-
Line.inheritMarks = function(text, orig) {
|
2476 |
-
var ln = new Line(text), mk = orig && orig.marked;
|
2477 |
-
if (mk) {
|
2478 |
-
for (var i = 0; i < mk.length; ++i) {
|
2479 |
-
if (mk[i].to == null && mk[i].style) {
|
2480 |
-
var newmk = ln.marked || (ln.marked = []), mark = mk[i];
|
2481 |
-
var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
|
2482 |
-
}
|
2483 |
-
}
|
2484 |
-
}
|
2485 |
-
return ln;
|
2486 |
-
};
|
2487 |
-
Line.prototype = {
|
2488 |
-
// Replace a piece of a line, keeping the styles around it intact.
|
2489 |
-
replace: function(from, to_, text) {
|
2490 |
-
var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
|
2491 |
-
copyStyles(0, from, this.styles, st);
|
2492 |
-
if (text) st.push(text, null);
|
2493 |
-
copyStyles(to, this.text.length, this.styles, st);
|
2494 |
-
this.styles = st;
|
2495 |
-
this.text = this.text.slice(0, from) + text + this.text.slice(to);
|
2496 |
-
this.stateAfter = null;
|
2497 |
-
if (mk) {
|
2498 |
-
var diff = text.length - (to - from);
|
2499 |
-
for (var i = 0; i < mk.length; ++i) {
|
2500 |
-
var mark = mk[i];
|
2501 |
-
mark.clipTo(from == null, from || 0, to_ == null, to, diff);
|
2502 |
-
if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
|
2503 |
-
}
|
2504 |
-
}
|
2505 |
-
},
|
2506 |
-
// Split a part off a line, keeping styles and markers intact.
|
2507 |
-
split: function(pos, textBefore) {
|
2508 |
-
var st = [textBefore, null], mk = this.marked;
|
2509 |
-
copyStyles(pos, this.text.length, this.styles, st);
|
2510 |
-
var taken = new Line(textBefore + this.text.slice(pos), st);
|
2511 |
-
if (mk) {
|
2512 |
-
for (var i = 0; i < mk.length; ++i) {
|
2513 |
-
var mark = mk[i];
|
2514 |
-
var newmark = mark.split(pos, textBefore.length);
|
2515 |
-
if (newmark) {
|
2516 |
-
if (!taken.marked) taken.marked = [];
|
2517 |
-
taken.marked.push(newmark); newmark.attach(taken);
|
2518 |
-
if (newmark == mark) mk.splice(i--, 1);
|
2519 |
-
}
|
2520 |
-
}
|
2521 |
-
}
|
2522 |
-
return taken;
|
2523 |
-
},
|
2524 |
-
append: function(line) {
|
2525 |
-
var mylen = this.text.length, mk = line.marked, mymk = this.marked;
|
2526 |
-
this.text += line.text;
|
2527 |
-
copyStyles(0, line.text.length, line.styles, this.styles);
|
2528 |
-
if (mymk) {
|
2529 |
-
for (var i = 0; i < mymk.length; ++i)
|
2530 |
-
if (mymk[i].to == null) mymk[i].to = mylen;
|
2531 |
-
}
|
2532 |
-
if (mk && mk.length) {
|
2533 |
-
if (!mymk) this.marked = mymk = [];
|
2534 |
-
outer: for (var i = 0; i < mk.length; ++i) {
|
2535 |
-
var mark = mk[i];
|
2536 |
-
if (!mark.from) {
|
2537 |
-
for (var j = 0; j < mymk.length; ++j) {
|
2538 |
-
var mymark = mymk[j];
|
2539 |
-
if (mymark.to == mylen && mymark.sameSet(mark)) {
|
2540 |
-
mymark.to = mark.to == null ? null : mark.to + mylen;
|
2541 |
-
if (mymark.isDead()) {
|
2542 |
-
mymark.detach(this);
|
2543 |
-
mk.splice(i--, 1);
|
2544 |
-
}
|
2545 |
-
continue outer;
|
2546 |
-
}
|
2547 |
-
}
|
2548 |
-
}
|
2549 |
-
mymk.push(mark);
|
2550 |
-
mark.attach(this);
|
2551 |
-
mark.from += mylen;
|
2552 |
-
if (mark.to != null) mark.to += mylen;
|
2553 |
-
}
|
2554 |
-
}
|
2555 |
-
},
|
2556 |
-
fixMarkEnds: function(other) {
|
2557 |
-
var mk = this.marked, omk = other.marked;
|
2558 |
-
if (!mk) return;
|
2559 |
-
outer: for (var i = 0; i < mk.length; ++i) {
|
2560 |
-
var mark = mk[i], close = mark.to == null;
|
2561 |
-
if (close && omk) {
|
2562 |
-
for (var j = 0; j < omk.length; ++j) {
|
2563 |
-
var om = omk[j];
|
2564 |
-
if (!om.sameSet(mark) || om.from != null) continue;
|
2565 |
-
if (mark.from == this.text.length && om.to == 0) {
|
2566 |
-
omk.splice(j, 1);
|
2567 |
-
mk.splice(i--, 1);
|
2568 |
-
continue outer;
|
2569 |
-
} else {
|
2570 |
-
close = false; break;
|
2571 |
-
}
|
2572 |
-
}
|
2573 |
-
}
|
2574 |
-
if (close) mark.to = this.text.length;
|
2575 |
-
}
|
2576 |
-
},
|
2577 |
-
fixMarkStarts: function() {
|
2578 |
-
var mk = this.marked;
|
2579 |
-
if (!mk) return;
|
2580 |
-
for (var i = 0; i < mk.length; ++i)
|
2581 |
-
if (mk[i].from == null) mk[i].from = 0;
|
2582 |
-
},
|
2583 |
-
addMark: function(mark) {
|
2584 |
-
mark.attach(this);
|
2585 |
-
if (this.marked == null) this.marked = [];
|
2586 |
-
this.marked.push(mark);
|
2587 |
-
this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
|
2588 |
-
},
|
2589 |
-
// Run the given mode's parser over a line, update the styles
|
2590 |
-
// array, which contains alternating fragments of text and CSS
|
2591 |
-
// classes.
|
2592 |
-
highlight: function(mode, state, tabSize) {
|
2593 |
-
var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0;
|
2594 |
-
var changed = false, curWord = st[0], prevWord;
|
2595 |
-
if (this.text == "" && mode.blankLine) mode.blankLine(state);
|
2596 |
-
while (!stream.eol()) {
|
2597 |
-
var style = mode.token(stream, state);
|
2598 |
-
var substr = this.text.slice(stream.start, stream.pos);
|
2599 |
-
stream.start = stream.pos;
|
2600 |
-
if (pos && st[pos-1] == style)
|
2601 |
-
st[pos-2] += substr;
|
2602 |
-
else if (substr) {
|
2603 |
-
if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
|
2604 |
-
st[pos++] = substr; st[pos++] = style;
|
2605 |
-
prevWord = curWord; curWord = st[pos];
|
2606 |
-
}
|
2607 |
-
// Give up when line is ridiculously long
|
2608 |
-
if (stream.pos > 5000) {
|
2609 |
-
st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
|
2610 |
-
break;
|
2611 |
-
}
|
2612 |
-
}
|
2613 |
-
if (st.length != pos) {st.length = pos; changed = true;}
|
2614 |
-
if (pos && st[pos-2] != prevWord) changed = true;
|
2615 |
-
// Short lines with simple highlights return null, and are
|
2616 |
-
// counted as changed by the driver because they are likely to
|
2617 |
-
// highlight the same way in various contexts.
|
2618 |
-
return changed || (st.length < 5 && this.text.length < 10 ? null : false);
|
2619 |
-
},
|
2620 |
-
// Fetch the parser token for a given character. Useful for hacks
|
2621 |
-
// that want to inspect the mode state (say, for completion).
|
2622 |
-
getTokenAt: function(mode, state, tabSize, ch) {
|
2623 |
-
var txt = this.text, stream = new StringStream(txt, tabSize);
|
2624 |
-
while (stream.pos < ch && !stream.eol()) {
|
2625 |
-
stream.start = stream.pos;
|
2626 |
-
var style = mode.token(stream, state);
|
2627 |
-
}
|
2628 |
-
return {start: stream.start,
|
2629 |
-
end: stream.pos,
|
2630 |
-
string: stream.current(),
|
2631 |
-
className: style || null,
|
2632 |
-
state: state};
|
2633 |
-
},
|
2634 |
-
indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
|
2635 |
-
// Produces an HTML fragment for the line, taking selection,
|
2636 |
-
// marking, and highlighting into account.
|
2637 |
-
getElement: function(makeTab, wrapAt, wrapWBR) {
|
2638 |
-
var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
|
2639 |
-
var pre = elt("pre");
|
2640 |
-
function span_(html, text, style) {
|
2641 |
-
if (!text) return;
|
2642 |
-
// Work around a bug where, in some compat modes, IE ignores leading spaces
|
2643 |
-
if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
|
2644 |
-
first = false;
|
2645 |
-
if (!specials.test(text)) {
|
2646 |
-
col += text.length;
|
2647 |
-
var content = document.createTextNode(text);
|
2648 |
-
} else {
|
2649 |
-
var content = document.createDocumentFragment(), pos = 0;
|
2650 |
-
while (true) {
|
2651 |
-
specials.lastIndex = pos;
|
2652 |
-
var m = specials.exec(text);
|
2653 |
-
var skipped = m ? m.index - pos : text.length - pos;
|
2654 |
-
if (skipped) {
|
2655 |
-
content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
|
2656 |
-
col += skipped;
|
2657 |
-
}
|
2658 |
-
if (!m) break;
|
2659 |
-
pos += skipped + 1;
|
2660 |
-
if (m[0] == "\t") {
|
2661 |
-
var tab = makeTab(col);
|
2662 |
-
content.appendChild(tab.element.cloneNode(true));
|
2663 |
-
col += tab.width;
|
2664 |
-
} else {
|
2665 |
-
var token = elt("span", "\u2022", "cm-invalidchar");
|
2666 |
-
token.title = "\\u" + m[0].charCodeAt(0).toString(16);
|
2667 |
-
content.appendChild(token);
|
2668 |
-
col += 1;
|
2669 |
-
}
|
2670 |
-
}
|
2671 |
-
}
|
2672 |
-
if (style) html.appendChild(elt("span", [content], style));
|
2673 |
-
else html.appendChild(content);
|
2674 |
-
}
|
2675 |
-
var span = span_;
|
2676 |
-
if (wrapAt != null) {
|
2677 |
-
var outPos = 0, anchor = pre.anchor = elt("span");
|
2678 |
-
span = function(html, text, style) {
|
2679 |
-
var l = text.length;
|
2680 |
-
if (wrapAt >= outPos && wrapAt < outPos + l) {
|
2681 |
-
if (wrapAt > outPos) {
|
2682 |
-
span_(html, text.slice(0, wrapAt - outPos), style);
|
2683 |
-
// See comment at the definition of spanAffectsWrapping
|
2684 |
-
if (wrapWBR) html.appendChild(elt("wbr"));
|
2685 |
-
}
|
2686 |
-
html.appendChild(anchor);
|
2687 |
-
var cut = wrapAt - outPos;
|
2688 |
-
span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
|
2689 |
-
if (opera) span_(html, text.slice(cut + 1), style);
|
2690 |
-
wrapAt--;
|
2691 |
-
outPos += l;
|
2692 |
-
} else {
|
2693 |
-
outPos += l;
|
2694 |
-
span_(html, text, style);
|
2695 |
-
if (outPos == wrapAt && outPos == len) {
|
2696 |
-
setTextContent(anchor, eolSpanContent);
|
2697 |
-
html.appendChild(anchor);
|
2698 |
-
}
|
2699 |
-
// Stop outputting HTML when gone sufficiently far beyond measure
|
2700 |
-
else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){};
|
2701 |
-
}
|
2702 |
-
};
|
2703 |
-
}
|
2704 |
-
|
2705 |
-
var st = this.styles, allText = this.text, marked = this.marked;
|
2706 |
-
var len = allText.length;
|
2707 |
-
function styleToClass(style) {
|
2708 |
-
if (!style) return null;
|
2709 |
-
return "cm-" + style.replace(/ +/g, " cm-");
|
2710 |
-
}
|
2711 |
-
if (!allText && wrapAt == null) {
|
2712 |
-
span(pre, " ");
|
2713 |
-
} else if (!marked || !marked.length) {
|
2714 |
-
for (var i = 0, ch = 0; ch < len; i+=2) {
|
2715 |
-
var str = st[i], style = st[i+1], l = str.length;
|
2716 |
-
if (ch + l > len) str = str.slice(0, len - ch);
|
2717 |
-
ch += l;
|
2718 |
-
span(pre, str, styleToClass(style));
|
2719 |
-
}
|
2720 |
-
} else {
|
2721 |
-
var pos = 0, i = 0, text = "", style, sg = 0;
|
2722 |
-
var nextChange = marked[0].from || 0, marks = [], markpos = 0;
|
2723 |
-
var advanceMarks = function() {
|
2724 |
-
var m;
|
2725 |
-
while (markpos < marked.length &&
|
2726 |
-
((m = marked[markpos]).from == pos || m.from == null)) {
|
2727 |
-
if (m.style != null) marks.push(m);
|
2728 |
-
++markpos;
|
2729 |
-
}
|
2730 |
-
nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
|
2731 |
-
for (var i = 0; i < marks.length; ++i) {
|
2732 |
-
var to = marks[i].to;
|
2733 |
-
if (to == null) to = Infinity;
|
2734 |
-
if (to == pos) marks.splice(i--, 1);
|
2735 |
-
else nextChange = Math.min(to, nextChange);
|
2736 |
-
}
|
2737 |
-
};
|
2738 |
-
var m = 0;
|
2739 |
-
while (pos < len) {
|
2740 |
-
if (nextChange == pos) advanceMarks();
|
2741 |
-
var upto = Math.min(len, nextChange);
|
2742 |
-
while (true) {
|
2743 |
-
if (text) {
|
2744 |
-
var end = pos + text.length;
|
2745 |
-
var appliedStyle = style;
|
2746 |
-
for (var j = 0; j < marks.length; ++j)
|
2747 |
-
appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style;
|
2748 |
-
span(pre, end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
|
2749 |
-
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
|
2750 |
-
pos = end;
|
2751 |
-
}
|
2752 |
-
text = st[i++]; style = styleToClass(st[i++]);
|
2753 |
-
}
|
2754 |
-
}
|
2755 |
-
}
|
2756 |
-
return pre;
|
2757 |
-
},
|
2758 |
-
cleanUp: function() {
|
2759 |
-
this.parent = null;
|
2760 |
-
if (this.marked)
|
2761 |
-
for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
|
2762 |
-
}
|
2763 |
-
};
|
2764 |
-
// Utility used by replace and split above
|
2765 |
-
function copyStyles(from, to, source, dest) {
|
2766 |
-
for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
|
2767 |
-
var part = source[i], end = pos + part.length;
|
2768 |
-
if (state == 0) {
|
2769 |
-
if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
|
2770 |
-
if (end >= from) state = 1;
|
2771 |
-
} else if (state == 1) {
|
2772 |
-
if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
|
2773 |
-
else dest.push(part, source[i+1]);
|
2774 |
-
}
|
2775 |
-
pos = end;
|
2776 |
-
}
|
2777 |
-
}
|
2778 |
-
|
2779 |
-
// Data structure that holds the sequence of lines.
|
2780 |
-
function LeafChunk(lines) {
|
2781 |
-
this.lines = lines;
|
2782 |
-
this.parent = null;
|
2783 |
-
for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
|
2784 |
-
lines[i].parent = this;
|
2785 |
-
height += lines[i].height;
|
2786 |
-
}
|
2787 |
-
this.height = height;
|
2788 |
-
}
|
2789 |
-
LeafChunk.prototype = {
|
2790 |
-
chunkSize: function() { return this.lines.length; },
|
2791 |
-
remove: function(at, n, callbacks) {
|
2792 |
-
for (var i = at, e = at + n; i < e; ++i) {
|
2793 |
-
var line = this.lines[i];
|
2794 |
-
this.height -= line.height;
|
2795 |
-
line.cleanUp();
|
2796 |
-
if (line.handlers)
|
2797 |
-
for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]);
|
2798 |
-
}
|
2799 |
-
this.lines.splice(at, n);
|
2800 |
-
},
|
2801 |
-
collapse: function(lines) {
|
2802 |
-
lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
|
2803 |
-
},
|
2804 |
-
insertHeight: function(at, lines, height) {
|
2805 |
-
this.height += height;
|
2806 |
-
this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
|
2807 |
-
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
|
2808 |
-
},
|
2809 |
-
iterN: function(at, n, op) {
|
2810 |
-
for (var e = at + n; at < e; ++at)
|
2811 |
-
if (op(this.lines[at])) return true;
|
2812 |
-
}
|
2813 |
-
};
|
2814 |
-
function BranchChunk(children) {
|
2815 |
-
this.children = children;
|
2816 |
-
var size = 0, height = 0;
|
2817 |
-
for (var i = 0, e = children.length; i < e; ++i) {
|
2818 |
-
var ch = children[i];
|
2819 |
-
size += ch.chunkSize(); height += ch.height;
|
2820 |
-
ch.parent = this;
|
2821 |
-
}
|
2822 |
-
this.size = size;
|
2823 |
-
this.height = height;
|
2824 |
-
this.parent = null;
|
2825 |
-
}
|
2826 |
-
BranchChunk.prototype = {
|
2827 |
-
chunkSize: function() { return this.size; },
|
2828 |
-
remove: function(at, n, callbacks) {
|
2829 |
-
this.size -= n;
|
2830 |
-
for (var i = 0; i < this.children.length; ++i) {
|
2831 |
-
var child = this.children[i], sz = child.chunkSize();
|
2832 |
-
if (at < sz) {
|
2833 |
-
var rm = Math.min(n, sz - at), oldHeight = child.height;
|
2834 |
-
child.remove(at, rm, callbacks);
|
2835 |
-
this.height -= oldHeight - child.height;
|
2836 |
-
if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
|
2837 |
-
if ((n -= rm) == 0) break;
|
2838 |
-
at = 0;
|
2839 |
-
} else at -= sz;
|
2840 |
-
}
|
2841 |
-
if (this.size - n < 25) {
|
2842 |
-
var lines = [];
|
2843 |
-
this.collapse(lines);
|
2844 |
-
this.children = [new LeafChunk(lines)];
|
2845 |
-
this.children[0].parent = this;
|
2846 |
-
}
|
2847 |
-
},
|
2848 |
-
collapse: function(lines) {
|
2849 |
-
for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
|
2850 |
-
},
|
2851 |
-
insert: function(at, lines) {
|
2852 |
-
var height = 0;
|
2853 |
-
for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
|
2854 |
-
this.insertHeight(at, lines, height);
|
2855 |
-
},
|
2856 |
-
insertHeight: function(at, lines, height) {
|
2857 |
-
this.size += lines.length;
|
2858 |
-
this.height += height;
|
2859 |
-
for (var i = 0, e = this.children.length; i < e; ++i) {
|
2860 |
-
var child = this.children[i], sz = child.chunkSize();
|
2861 |
-
if (at <= sz) {
|
2862 |
-
child.insertHeight(at, lines, height);
|
2863 |
-
if (child.lines && child.lines.length > 50) {
|
2864 |
-
while (child.lines.length > 50) {
|
2865 |
-
var spilled = child.lines.splice(child.lines.length - 25, 25);
|
2866 |
-
var newleaf = new LeafChunk(spilled);
|
2867 |
-
child.height -= newleaf.height;
|
2868 |
-
this.children.splice(i + 1, 0, newleaf);
|
2869 |
-
newleaf.parent = this;
|
2870 |
-
}
|
2871 |
-
this.maybeSpill();
|
2872 |
-
}
|
2873 |
-
break;
|
2874 |
-
}
|
2875 |
-
at -= sz;
|
2876 |
-
}
|
2877 |
-
},
|
2878 |
-
maybeSpill: function() {
|
2879 |
-
if (this.children.length <= 10) return;
|
2880 |
-
var me = this;
|
2881 |
-
do {
|
2882 |
-
var spilled = me.children.splice(me.children.length - 5, 5);
|
2883 |
-
var sibling = new BranchChunk(spilled);
|
2884 |
-
if (!me.parent) { // Become the parent node
|
2885 |
-
var copy = new BranchChunk(me.children);
|
2886 |
-
copy.parent = me;
|
2887 |
-
me.children = [copy, sibling];
|
2888 |
-
me = copy;
|
2889 |
-
} else {
|
2890 |
-
me.size -= sibling.size;
|
2891 |
-
me.height -= sibling.height;
|
2892 |
-
var myIndex = indexOf(me.parent.children, me);
|
2893 |
-
me.parent.children.splice(myIndex + 1, 0, sibling);
|
2894 |
-
}
|
2895 |
-
sibling.parent = me.parent;
|
2896 |
-
} while (me.children.length > 10);
|
2897 |
-
me.parent.maybeSpill();
|
2898 |
-
},
|
2899 |
-
iter: function(from, to, op) { this.iterN(from, to - from, op); },
|
2900 |
-
iterN: function(at, n, op) {
|
2901 |
-
for (var i = 0, e = this.children.length; i < e; ++i) {
|
2902 |
-
var child = this.children[i], sz = child.chunkSize();
|
2903 |
-
if (at < sz) {
|
2904 |
-
var used = Math.min(n, sz - at);
|
2905 |
-
if (child.iterN(at, used, op)) return true;
|
2906 |
-
if ((n -= used) == 0) break;
|
2907 |
-
at = 0;
|
2908 |
-
} else at -= sz;
|
2909 |
-
}
|
2910 |
-
}
|
2911 |
-
};
|
2912 |
-
|
2913 |
-
function getLineAt(chunk, n) {
|
2914 |
-
while (!chunk.lines) {
|
2915 |
-
for (var i = 0;; ++i) {
|
2916 |
-
var child = chunk.children[i], sz = child.chunkSize();
|
2917 |
-
if (n < sz) { chunk = child; break; }
|
2918 |
-
n -= sz;
|
2919 |
-
}
|
2920 |
-
}
|
2921 |
-
return chunk.lines[n];
|
2922 |
-
}
|
2923 |
-
function lineNo(line) {
|
2924 |
-
if (line.parent == null) return null;
|
2925 |
-
var cur = line.parent, no = indexOf(cur.lines, line);
|
2926 |
-
for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
|
2927 |
-
for (var i = 0, e = chunk.children.length; ; ++i) {
|
2928 |
-
if (chunk.children[i] == cur) break;
|
2929 |
-
no += chunk.children[i].chunkSize();
|
2930 |
-
}
|
2931 |
-
}
|
2932 |
-
return no;
|
2933 |
-
}
|
2934 |
-
function lineAtHeight(chunk, h) {
|
2935 |
-
var n = 0;
|
2936 |
-
outer: do {
|
2937 |
-
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
2938 |
-
var child = chunk.children[i], ch = child.height;
|
2939 |
-
if (h < ch) { chunk = child; continue outer; }
|
2940 |
-
h -= ch;
|
2941 |
-
n += child.chunkSize();
|
2942 |
-
}
|
2943 |
-
return n;
|
2944 |
-
} while (!chunk.lines);
|
2945 |
-
for (var i = 0, e = chunk.lines.length; i < e; ++i) {
|
2946 |
-
var line = chunk.lines[i], lh = line.height;
|
2947 |
-
if (h < lh) break;
|
2948 |
-
h -= lh;
|
2949 |
-
}
|
2950 |
-
return n + i;
|
2951 |
-
}
|
2952 |
-
function heightAtLine(chunk, n) {
|
2953 |
-
var h = 0;
|
2954 |
-
outer: do {
|
2955 |
-
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
2956 |
-
var child = chunk.children[i], sz = child.chunkSize();
|
2957 |
-
if (n < sz) { chunk = child; continue outer; }
|
2958 |
-
n -= sz;
|
2959 |
-
h += child.height;
|
2960 |
-
}
|
2961 |
-
return h;
|
2962 |
-
} while (!chunk.lines);
|
2963 |
-
for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
|
2964 |
-
return h;
|
2965 |
-
}
|
2966 |
-
|
2967 |
-
// The history object 'chunks' changes that are made close together
|
2968 |
-
// and at almost the same time into bigger undoable units.
|
2969 |
-
function History() {
|
2970 |
-
this.time = 0;
|
2971 |
-
this.done = []; this.undone = [];
|
2972 |
-
this.compound = 0;
|
2973 |
-
this.closed = false;
|
2974 |
-
}
|
2975 |
-
History.prototype = {
|
2976 |
-
addChange: function(start, added, old) {
|
2977 |
-
this.undone.length = 0;
|
2978 |
-
var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1];
|
2979 |
-
var dtime = time - this.time;
|
2980 |
-
|
2981 |
-
if (this.compound && cur && !this.closed) {
|
2982 |
-
cur.push({start: start, added: added, old: old});
|
2983 |
-
} else if (dtime > 400 || !last || this.closed ||
|
2984 |
-
last.start > start + old.length || last.start + last.added < start) {
|
2985 |
-
this.done.push([{start: start, added: added, old: old}]);
|
2986 |
-
this.closed = false;
|
2987 |
-
} else {
|
2988 |
-
var startBefore = Math.max(0, last.start - start),
|
2989 |
-
endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
|
2990 |
-
for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
|
2991 |
-
for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
|
2992 |
-
if (startBefore) last.start = start;
|
2993 |
-
last.added += added - (old.length - startBefore - endAfter);
|
2994 |
-
}
|
2995 |
-
this.time = time;
|
2996 |
-
},
|
2997 |
-
startCompound: function() {
|
2998 |
-
if (!this.compound++) this.closed = true;
|
2999 |
-
},
|
3000 |
-
endCompound: function() {
|
3001 |
-
if (!--this.compound) this.closed = true;
|
3002 |
-
}
|
3003 |
-
};
|
3004 |
-
|
3005 |
-
function stopMethod() {e_stop(this);}
|
3006 |
-
// Ensure an event has a stop method.
|
3007 |
-
function addStop(event) {
|
3008 |
-
if (!event.stop) event.stop = stopMethod;
|
3009 |
-
return event;
|
3010 |
-
}
|
3011 |
-
|
3012 |
-
function e_preventDefault(e) {
|
3013 |
-
if (e.preventDefault) e.preventDefault();
|
3014 |
-
else e.returnValue = false;
|
3015 |
-
}
|
3016 |
-
function e_stopPropagation(e) {
|
3017 |
-
if (e.stopPropagation) e.stopPropagation();
|
3018 |
-
else e.cancelBubble = true;
|
3019 |
-
}
|
3020 |
-
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
|
3021 |
-
CodeMirror.e_stop = e_stop;
|
3022 |
-
CodeMirror.e_preventDefault = e_preventDefault;
|
3023 |
-
CodeMirror.e_stopPropagation = e_stopPropagation;
|
3024 |
-
|
3025 |
-
function e_target(e) {return e.target || e.srcElement;}
|
3026 |
-
function e_button(e) {
|
3027 |
-
var b = e.which;
|
3028 |
-
if (b == null) {
|
3029 |
-
if (e.button & 1) b = 1;
|
3030 |
-
else if (e.button & 2) b = 3;
|
3031 |
-
else if (e.button & 4) b = 2;
|
3032 |
-
}
|
3033 |
-
if (mac && e.ctrlKey && b == 1) b = 3;
|
3034 |
-
return b;
|
3035 |
-
}
|
3036 |
-
|
3037 |
-
// Allow 3rd-party code to override event properties by adding an override
|
3038 |
-
// object to an event object.
|
3039 |
-
function e_prop(e, prop) {
|
3040 |
-
var overridden = e.override && e.override.hasOwnProperty(prop);
|
3041 |
-
return overridden ? e.override[prop] : e[prop];
|
3042 |
-
}
|
3043 |
-
|
3044 |
-
// Event handler registration. If disconnect is true, it'll return a
|
3045 |
-
// function that unregisters the handler.
|
3046 |
-
function connect(node, type, handler, disconnect) {
|
3047 |
-
if (typeof node.addEventListener == "function") {
|
3048 |
-
node.addEventListener(type, handler, false);
|
3049 |
-
if (disconnect) return function() {node.removeEventListener(type, handler, false);};
|
3050 |
-
} else {
|
3051 |
-
var wrapHandler = function(event) {handler(event || window.event);};
|
3052 |
-
node.attachEvent("on" + type, wrapHandler);
|
3053 |
-
if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
|
3054 |
-
}
|
3055 |
-
}
|
3056 |
-
CodeMirror.connect = connect;
|
3057 |
-
|
3058 |
-
function Delayed() {this.id = null;}
|
3059 |
-
Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
|
3060 |
-
|
3061 |
-
var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
|
3062 |
-
|
3063 |
-
// Detect drag-and-drop
|
3064 |
-
var dragAndDrop = function() {
|
3065 |
-
// There is *some* kind of drag-and-drop support in IE6-8, but I
|
3066 |
-
// couldn't get it to work yet.
|
3067 |
-
if (ie_lt9) return false;
|
3068 |
-
var div = elt('div');
|
3069 |
-
return "draggable" in div || "dragDrop" in div;
|
3070 |
-
}();
|
3071 |
-
|
3072 |
-
// Feature-detect whether newlines in textareas are converted to \r\n
|
3073 |
-
var lineSep = function () {
|
3074 |
-
var te = elt("textarea");
|
3075 |
-
te.value = "foo\nbar";
|
3076 |
-
if (te.value.indexOf("\r") > -1) return "\r\n";
|
3077 |
-
return "\n";
|
3078 |
-
}();
|
3079 |
-
|
3080 |
-
// For a reason I have yet to figure out, some browsers disallow
|
3081 |
-
// word wrapping between certain characters *only* if a new inline
|
3082 |
-
// element is started between them. This makes it hard to reliably
|
3083 |
-
// measure the position of things, since that requires inserting an
|
3084 |
-
// extra span. This terribly fragile set of regexps matches the
|
3085 |
-
// character combinations that suffer from this phenomenon on the
|
3086 |
-
// various browsers.
|
3087 |
-
var spanAffectsWrapping = /^$/; // Won't match any two-character string
|
3088 |
-
if (gecko) spanAffectsWrapping = /$'/;
|
3089 |
-
else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
|
3090 |
-
else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
|
3091 |
-
|
3092 |
-
// Counts the column offset in a string, taking tabs into account.
|
3093 |
-
// Used mostly to find indentation.
|
3094 |
-
function countColumn(string, end, tabSize) {
|
3095 |
-
if (end == null) {
|
3096 |
-
end = string.search(/[^\s\u00a0]/);
|
3097 |
-
if (end == -1) end = string.length;
|
3098 |
-
}
|
3099 |
-
for (var i = 0, n = 0; i < end; ++i) {
|
3100 |
-
if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
|
3101 |
-
else ++n;
|
3102 |
-
}
|
3103 |
-
return n;
|
3104 |
-
}
|
3105 |
-
|
3106 |
-
function eltOffset(node, screen) {
|
3107 |
-
// Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
|
3108 |
-
// since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
|
3109 |
-
try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
|
3110 |
-
catch(e) { box = {top: 0, left: 0}; }
|
3111 |
-
if (!screen) {
|
3112 |
-
// Get the toplevel scroll, working around browser differences.
|
3113 |
-
if (window.pageYOffset == null) {
|
3114 |
-
var t = document.documentElement || document.body.parentNode;
|
3115 |
-
if (t.scrollTop == null) t = document.body;
|
3116 |
-
box.top += t.scrollTop; box.left += t.scrollLeft;
|
3117 |
-
} else {
|
3118 |
-
box.top += window.pageYOffset; box.left += window.pageXOffset;
|
3119 |
-
}
|
3120 |
-
}
|
3121 |
-
return box;
|
3122 |
-
}
|
3123 |
-
|
3124 |
-
// Get a node's text content.
|
3125 |
-
function eltText(node) {
|
3126 |
-
return node.textContent || node.innerText || node.nodeValue || "";
|
3127 |
-
}
|
3128 |
-
function selectInput(node) {
|
3129 |
-
if (ios) { // Mobile Safari apparently has a bug where select() is broken.
|
3130 |
-
node.selectionStart = 0;
|
3131 |
-
node.selectionEnd = node.value.length;
|
3132 |
-
} else node.select();
|
3133 |
-
}
|
3134 |
-
|
3135 |
-
// Operations on {line, ch} objects.
|
3136 |
-
function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
|
3137 |
-
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
|
3138 |
-
function copyPos(x) {return {line: x.line, ch: x.ch};}
|
3139 |
-
|
3140 |
-
function elt(tag, content, className, style) {
|
3141 |
-
var e = document.createElement(tag);
|
3142 |
-
if (className) e.className = className;
|
3143 |
-
if (style) e.style.cssText = style;
|
3144 |
-
if (typeof content == "string") setTextContent(e, content);
|
3145 |
-
else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
|
3146 |
-
return e;
|
3147 |
-
}
|
3148 |
-
function removeChildren(e) {
|
3149 |
-
e.innerHTML = "";
|
3150 |
-
return e;
|
3151 |
-
}
|
3152 |
-
function removeChildrenAndAdd(parent, e) {
|
3153 |
-
removeChildren(parent).appendChild(e);
|
3154 |
-
}
|
3155 |
-
function setTextContent(e, str) {
|
3156 |
-
if (ie_lt9) {
|
3157 |
-
e.innerHTML = "";
|
3158 |
-
e.appendChild(document.createTextNode(str));
|
3159 |
-
} else e.textContent = str;
|
3160 |
-
}
|
3161 |
-
CodeMirror.setTextContent = setTextContent;
|
3162 |
-
|
3163 |
-
// Used to position the cursor after an undo/redo by finding the
|
3164 |
-
// last edited character.
|
3165 |
-
function editEnd(from, to) {
|
3166 |
-
if (!to) return 0;
|
3167 |
-
if (!from) return to.length;
|
3168 |
-
for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
|
3169 |
-
if (from.charAt(i) != to.charAt(j)) break;
|
3170 |
-
return j + 1;
|
3171 |
-
}
|
3172 |
-
|
3173 |
-
function indexOf(collection, elt) {
|
3174 |
-
if (collection.indexOf) return collection.indexOf(elt);
|
3175 |
-
for (var i = 0, e = collection.length; i < e; ++i)
|
3176 |
-
if (collection[i] == elt) return i;
|
3177 |
-
return -1;
|
3178 |
-
}
|
3179 |
-
function isWordChar(ch) {
|
3180 |
-
return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
|
3181 |
-
}
|
3182 |
-
|
3183 |
-
// See if "".split is the broken IE version, if so, provide an
|
3184 |
-
// alternative way to split lines.
|
3185 |
-
var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
|
3186 |
-
var pos = 0, result = [], l = string.length;
|
3187 |
-
while (pos <= l) {
|
3188 |
-
var nl = string.indexOf("\n", pos);
|
3189 |
-
if (nl == -1) nl = string.length;
|
3190 |
-
var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
|
3191 |
-
var rt = line.indexOf("\r");
|
3192 |
-
if (rt != -1) {
|
3193 |
-
result.push(line.slice(0, rt));
|
3194 |
-
pos += rt + 1;
|
3195 |
-
} else {
|
3196 |
-
result.push(line);
|
3197 |
-
pos = nl + 1;
|
3198 |
-
}
|
3199 |
-
}
|
3200 |
-
return result;
|
3201 |
-
} : function(string){return string.split(/\r\n?|\n/);};
|
3202 |
-
CodeMirror.splitLines = splitLines;
|
3203 |
-
|
3204 |
-
var hasSelection = window.getSelection ? function(te) {
|
3205 |
-
try { return te.selectionStart != te.selectionEnd; }
|
3206 |
-
catch(e) { return false; }
|
3207 |
-
} : function(te) {
|
3208 |
-
try {var range = te.ownerDocument.selection.createRange();}
|
3209 |
-
catch(e) {}
|
3210 |
-
if (!range || range.parentElement() != te) return false;
|
3211 |
-
return range.compareEndPoints("StartToEnd", range) != 0;
|
3212 |
-
};
|
3213 |
-
|
3214 |
-
CodeMirror.defineMode("null", function() {
|
3215 |
-
return {token: function(stream) {stream.skipToEnd();}};
|
3216 |
-
});
|
3217 |
-
CodeMirror.defineMIME("text/plain", "null");
|
3218 |
-
|
3219 |
-
var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
|
3220 |
-
19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
|
3221 |
-
36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
|
3222 |
-
46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
|
3223 |
-
186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
|
3224 |
-
221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
|
3225 |
-
63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
|
3226 |
-
CodeMirror.keyNames = keyNames;
|
3227 |
-
(function() {
|
3228 |
-
// Number keys
|
3229 |
-
for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
|
3230 |
-
// Alphabetic keys
|
3231 |
-
for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
|
3232 |
-
// Function keys
|
3233 |
-
for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
|
3234 |
-
})();
|
3235 |
-
|
3236 |
-
return CodeMirror;
|
3237 |
-
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/css.js
DELETED
@@ -1,174 +0,0 @@
|
|
1 |
-
CodeMirror.defineMode("css", function(config) {
|
2 |
-
var indentUnit = config.indentUnit, type;
|
3 |
-
|
4 |
-
var keywords = keySet(["above", "absolute", "activeborder", "activecaption", "afar", "after-white-space", "ahead", "alias", "all", "all-scroll",
|
5 |
-
"alternate", "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", "arabic-indic", "armenian", "asterisks",
|
6 |
-
"auto", "avoid", "background", "backwards", "baseline", "below", "bidi-override", "binary", "bengali", "blink",
|
7 |
-
"block", "block-axis", "bold", "bolder", "border", "border-box", "both", "bottom", "break-all", "break-word", "button",
|
8 |
-
"button-bevel", "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", "capitalize", "caps-lock-indicator",
|
9 |
-
"caption", "captiontext", "caret", "cell", "center", "checkbox", "circle", "cjk-earthly-branch", "cjk-heavenly-stem", "cjk-ideographic",
|
10 |
-
"clear", "clip", "close-quote", "col-resize", "collapse", "compact", "condensed", "contain", "content", "content-box", "context-menu",
|
11 |
-
"continuous", "copy", "cover", "crop", "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", "decimal-leading-zero", "default",
|
12 |
-
"default-button", "destination-atop", "destination-in", "destination-out", "destination-over", "devanagari", "disc", "discard", "document",
|
13 |
-
"dot-dash", "dot-dot-dash", "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", "element",
|
14 |
-
"ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", "ethiopic-abegede-am-et", "ethiopic-abegede-gez",
|
15 |
-
"ethiopic-abegede-ti-er", "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", "ethiopic-halehame-aa-et",
|
16 |
-
"ethiopic-halehame-am-et", "ethiopic-halehame-gez", "ethiopic-halehame-om-et", "ethiopic-halehame-sid-et",
|
17 |
-
"ethiopic-halehame-so-et", "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig", "ew-resize", "expanded",
|
18 |
-
"extra-condensed", "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", "forwards", "from", "geometricPrecision",
|
19 |
-
"georgian", "graytext", "groove", "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", "help",
|
20 |
-
"hidden", "hide", "higher", "highlight", "highlighttext", "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
|
21 |
-
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", "infobackground", "infotext", "inherit", "initial", "inline",
|
22 |
-
"inline-axis", "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", "italic", "justify", "kannada", "katakana",
|
23 |
-
"katakana-iroha", "khmer", "landscape", "lao", "large", "larger", "left", "level", "lighter", "line-through", "linear", "lines",
|
24 |
-
"list-item", "listbox", "listitem", "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", "lower-greek",
|
25 |
-
"lower-hexadecimal", "lower-latin", "lower-norwegian", "lower-roman", "lowercase", "ltr", "malayalam", "match", "media-controls-background",
|
26 |
-
"media-current-time-display", "media-fullscreen-button", "media-mute-button", "media-play-button", "media-return-to-realtime-button",
|
27 |
-
"media-rewind-button", "media-seek-back-button", "media-seek-forward-button", "media-slider", "media-sliderthumb", "media-time-remaining-display",
|
28 |
-
"media-volume-slider", "media-volume-slider-container", "media-volume-sliderthumb", "medium", "menu", "menulist", "menulist-button",
|
29 |
-
"menulist-text", "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", "mix", "mongolian", "monospace", "move", "multiple",
|
30 |
-
"myanmar", "n-resize", "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none",
|
31 |
-
"normal", "not-allowed", "nowrap", "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", "optimizeLegibility",
|
32 |
-
"optimizeSpeed", "oriya", "oromo", "outset", "outside", "overlay", "overline", "padding", "padding-box", "painted", "paused",
|
33 |
-
"persian", "plus-darker", "plus-lighter", "pointer", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress",
|
34 |
-
"push-button", "radio", "read-only", "read-write", "read-write-plaintext-only", "relative", "repeat", "repeat-x",
|
35 |
-
"repeat-y", "reset", "reverse", "rgb", "rgba", "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", "s-resize", "sans-serif",
|
36 |
-
"scroll", "scrollbar", "se-resize", "searchfield", "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
|
37 |
-
"searchfield-results-decoration", "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", "single",
|
38 |
-
"skip-white-space", "slide", "slider-horizontal", "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
|
39 |
-
"small", "small-caps", "small-caption", "smaller", "solid", "somali", "source-atop", "source-in", "source-out", "source-over",
|
40 |
-
"space", "square", "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub", "subpixel-antialiased", "super",
|
41 |
-
"sw-resize", "table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group",
|
42 |
-
"table-row", "table-row-group", "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", "thick", "thin",
|
43 |
-
"threeddarkshadow", "threedface", "threedhighlight", "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", "tigrinya-er-abegede",
|
44 |
-
"tigrinya-et", "tigrinya-et-abegede", "to", "top", "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", "upper-alpha", "upper-armenian",
|
45 |
-
"upper-greek", "upper-hexadecimal", "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", "vertical", "vertical-text", "visible",
|
46 |
-
"visibleFill", "visiblePainted", "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", "window", "windowframe", "windowtext",
|
47 |
-
"x-large", "x-small", "xor", "xx-large", "xx-small", "yellow", "-wap-marquee", "-webkit-activelink", "-webkit-auto", "-webkit-baseline-middle",
|
48 |
-
"-webkit-body", "-webkit-box", "-webkit-center", "-webkit-control", "-webkit-focus-ring-color", "-webkit-grab", "-webkit-grabbing",
|
49 |
-
"-webkit-gradient", "-webkit-inline-box", "-webkit-left", "-webkit-link", "-webkit-marquee", "-webkit-mini-control", "-webkit-nowrap", "-webkit-pictograph",
|
50 |
-
"-webkit-right", "-webkit-small-control", "-webkit-text", "-webkit-xxx-large", "-webkit-zoom-in", "-webkit-zoom-out"]);
|
51 |
-
|
52 |
-
function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; }
|
53 |
-
function ret(style, tp) {type = tp; return style;}
|
54 |
-
|
55 |
-
function tokenBase(stream, state) {
|
56 |
-
var ch = stream.next();
|
57 |
-
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());}
|
58 |
-
else if (ch == "/" && stream.eat("*")) {
|
59 |
-
state.tokenize = tokenCComment;
|
60 |
-
return tokenCComment(stream, state);
|
61 |
-
}
|
62 |
-
else if (ch == "<" && stream.eat("!")) {
|
63 |
-
state.tokenize = tokenSGMLComment;
|
64 |
-
return tokenSGMLComment(stream, state);
|
65 |
-
}
|
66 |
-
else if (ch == "=") ret(null, "compare");
|
67 |
-
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
|
68 |
-
else if (ch == "\"" || ch == "'") {
|
69 |
-
state.tokenize = tokenString(ch);
|
70 |
-
return state.tokenize(stream, state);
|
71 |
-
}
|
72 |
-
else if (ch == "#") {
|
73 |
-
stream.eatWhile(/[\w\\\-]/);
|
74 |
-
return ret("atom", "hash");
|
75 |
-
}
|
76 |
-
else if (ch == "!") {
|
77 |
-
stream.match(/^\s*\w*/);
|
78 |
-
return ret("keyword", "important");
|
79 |
-
}
|
80 |
-
else if (/\d/.test(ch)) {
|
81 |
-
stream.eatWhile(/[\w.%]/);
|
82 |
-
return ret("number", "unit");
|
83 |
-
}
|
84 |
-
else if (/[,.+>*\/]/.test(ch)) {
|
85 |
-
return ret(null, "select-op");
|
86 |
-
}
|
87 |
-
else if (/[;{}:\[\]\(\)]/.test(ch)) {
|
88 |
-
return ret(null, ch);
|
89 |
-
}
|
90 |
-
else {
|
91 |
-
stream.eatWhile(/[\w\\\-]/);
|
92 |
-
return ret("variable", "variable");
|
93 |
-
}
|
94 |
-
}
|
95 |
-
|
96 |
-
function tokenCComment(stream, state) {
|
97 |
-
var maybeEnd = false, ch;
|
98 |
-
while ((ch = stream.next()) != null) {
|
99 |
-
if (maybeEnd && ch == "/") {
|
100 |
-
state.tokenize = tokenBase;
|
101 |
-
break;
|
102 |
-
}
|
103 |
-
maybeEnd = (ch == "*");
|
104 |
-
}
|
105 |
-
return ret("comment", "comment");
|
106 |
-
}
|
107 |
-
|
108 |
-
function tokenSGMLComment(stream, state) {
|
109 |
-
var dashes = 0, ch;
|
110 |
-
while ((ch = stream.next()) != null) {
|
111 |
-
if (dashes >= 2 && ch == ">") {
|
112 |
-
state.tokenize = tokenBase;
|
113 |
-
break;
|
114 |
-
}
|
115 |
-
dashes = (ch == "-") ? dashes + 1 : 0;
|
116 |
-
}
|
117 |
-
return ret("comment", "comment");
|
118 |
-
}
|
119 |
-
|
120 |
-
function tokenString(quote) {
|
121 |
-
return function(stream, state) {
|
122 |
-
var escaped = false, ch;
|
123 |
-
while ((ch = stream.next()) != null) {
|
124 |
-
if (ch == quote && !escaped)
|
125 |
-
break;
|
126 |
-
escaped = !escaped && ch == "\\";
|
127 |
-
}
|
128 |
-
if (!escaped) state.tokenize = tokenBase;
|
129 |
-
return ret("string", "string");
|
130 |
-
};
|
131 |
-
}
|
132 |
-
|
133 |
-
return {
|
134 |
-
startState: function(base) {
|
135 |
-
return {tokenize: tokenBase,
|
136 |
-
baseIndent: base || 0,
|
137 |
-
stack: []};
|
138 |
-
},
|
139 |
-
|
140 |
-
token: function(stream, state) {
|
141 |
-
if (stream.eatSpace()) return null;
|
142 |
-
var style = state.tokenize(stream, state);
|
143 |
-
|
144 |
-
var context = state.stack[state.stack.length-1];
|
145 |
-
if (type == "hash" && context != "rule") style = "string-2";
|
146 |
-
else if (style == "variable") {
|
147 |
-
if (context == "rule") style = keywords[stream.current()] ? "keyword" : "number";
|
148 |
-
else if (!context || context == "@media{") style = "tag";
|
149 |
-
}
|
150 |
-
|
151 |
-
if (context == "rule" && /^[\{\};]$/.test(type))
|
152 |
-
state.stack.pop();
|
153 |
-
if (type == "{") {
|
154 |
-
if (context == "@media") state.stack[state.stack.length-1] = "@media{";
|
155 |
-
else state.stack.push("{");
|
156 |
-
}
|
157 |
-
else if (type == "}") state.stack.pop();
|
158 |
-
else if (type == "@media") state.stack.push("@media");
|
159 |
-
else if (context == "{" && type != "comment") state.stack.push("rule");
|
160 |
-
return style;
|
161 |
-
},
|
162 |
-
|
163 |
-
indent: function(state, textAfter) {
|
164 |
-
var n = state.stack.length;
|
165 |
-
if (/^\}/.test(textAfter))
|
166 |
-
n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
|
167 |
-
return state.baseIndent + n * indentUnit;
|
168 |
-
},
|
169 |
-
|
170 |
-
electricChars: "}"
|
171 |
-
};
|
172 |
-
});
|
173 |
-
|
174 |
-
CodeMirror.defineMIME("text/css", "css");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/php.js
DELETED
@@ -1,150 +0,0 @@
|
|
1 |
-
(function() {
|
2 |
-
function keywords(str) {
|
3 |
-
var obj = {}, words = str.split(" ");
|
4 |
-
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
|
5 |
-
return obj;
|
6 |
-
}
|
7 |
-
function heredoc(delim) {
|
8 |
-
return function(stream, state) {
|
9 |
-
if (stream.match(delim)) state.tokenize = null;
|
10 |
-
else stream.skipToEnd();
|
11 |
-
return "string";
|
12 |
-
};
|
13 |
-
}
|
14 |
-
var phpConfig = {
|
15 |
-
name: "clike",
|
16 |
-
keywords: keywords("abstract and array as break case catch class clone const continue declare default " +
|
17 |
-
"do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " +
|
18 |
-
"for foreach function global goto if implements interface instanceof namespace " +
|
19 |
-
"new or private protected public static switch throw trait try use var while xor " +
|
20 |
-
"die echo empty exit eval include include_once isset list require require_once return " +
|
21 |
-
"print unset __halt_compiler self static parent"),
|
22 |
-
blockKeywords: keywords("catch do else elseif for foreach if switch try while"),
|
23 |
-
atoms: keywords("true false null TRUE FALSE NULL"),
|
24 |
-
multiLineStrings: true,
|
25 |
-
hooks: {
|
26 |
-
"$": function(stream, state) {
|
27 |
-
stream.eatWhile(/[\w\$_]/);
|
28 |
-
return "variable-2";
|
29 |
-
},
|
30 |
-
"<": function(stream, state) {
|
31 |
-
if (stream.match(/<</)) {
|
32 |
-
stream.eatWhile(/[\w\.]/);
|
33 |
-
state.tokenize = heredoc(stream.current().slice(3));
|
34 |
-
return state.tokenize(stream, state);
|
35 |
-
}
|
36 |
-
return false;
|
37 |
-
},
|
38 |
-
"#": function(stream, state) {
|
39 |
-
while (!stream.eol() && !stream.match("?>", false)) stream.next();
|
40 |
-
return "comment";
|
41 |
-
},
|
42 |
-
"/": function(stream, state) {
|
43 |
-
if (stream.eat("/")) {
|
44 |
-
while (!stream.eol() && !stream.match("?>", false)) stream.next();
|
45 |
-
return "comment";
|
46 |
-
}
|
47 |
-
return false;
|
48 |
-
}
|
49 |
-
}
|
50 |
-
};
|
51 |
-
|
52 |
-
CodeMirror.defineMode("php", function(config, parserConfig) {
|
53 |
-
var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
|
54 |
-
var jsMode = CodeMirror.getMode(config, "javascript");
|
55 |
-
var cssMode = CodeMirror.getMode(config, "css");
|
56 |
-
var phpMode = CodeMirror.getMode(config, phpConfig);
|
57 |
-
|
58 |
-
function dispatch(stream, state) { // TODO open PHP inside text/css
|
59 |
-
var isPHP = state.mode == "php";
|
60 |
-
if (stream.sol() && state.pending != '"') state.pending = null;
|
61 |
-
if (state.curMode == htmlMode) {
|
62 |
-
if (stream.match(/^<\?\w*/)) {
|
63 |
-
state.curMode = phpMode;
|
64 |
-
state.curState = state.php;
|
65 |
-
state.curClose = "?>";
|
66 |
-
state.mode = "php";
|
67 |
-
return "meta";
|
68 |
-
}
|
69 |
-
if (state.pending == '"') {
|
70 |
-
while (!stream.eol() && stream.next() != '"') {}
|
71 |
-
var style = "string";
|
72 |
-
} else if (state.pending && stream.pos < state.pending.end) {
|
73 |
-
stream.pos = state.pending.end;
|
74 |
-
var style = state.pending.style;
|
75 |
-
} else {
|
76 |
-
var style = htmlMode.token(stream, state.curState);
|
77 |
-
}
|
78 |
-
state.pending = null;
|
79 |
-
var cur = stream.current(), openPHP = cur.search(/<\?/);
|
80 |
-
if (openPHP != -1) {
|
81 |
-
if (style == "string" && /\"$/.test(cur) && !/\?>/.test(cur)) state.pending = '"';
|
82 |
-
else state.pending = {end: stream.pos, style: style};
|
83 |
-
stream.backUp(cur.length - openPHP);
|
84 |
-
} else if (style == "tag" && stream.current() == ">" && state.curState.context) {
|
85 |
-
if (/^script$/i.test(state.curState.context.tagName)) {
|
86 |
-
state.curMode = jsMode;
|
87 |
-
state.curState = jsMode.startState(htmlMode.indent(state.curState, ""));
|
88 |
-
state.curClose = /^<\/\s*script\s*>/i;
|
89 |
-
state.mode = "javascript";
|
90 |
-
}
|
91 |
-
else if (/^style$/i.test(state.curState.context.tagName)) {
|
92 |
-
state.curMode = cssMode;
|
93 |
-
state.curState = cssMode.startState(htmlMode.indent(state.curState, ""));
|
94 |
-
state.curClose = /^<\/\s*style\s*>/i;
|
95 |
-
state.mode = "css";
|
96 |
-
}
|
97 |
-
}
|
98 |
-
return style;
|
99 |
-
} else if ((!isPHP || state.php.tokenize == null) &&
|
100 |
-
stream.match(state.curClose, isPHP)) {
|
101 |
-
state.curMode = htmlMode;
|
102 |
-
state.curState = state.html;
|
103 |
-
state.curClose = null;
|
104 |
-
state.mode = "html";
|
105 |
-
if (isPHP) return "meta";
|
106 |
-
else return dispatch(stream, state);
|
107 |
-
} else {
|
108 |
-
return state.curMode.token(stream, state.curState);
|
109 |
-
}
|
110 |
-
}
|
111 |
-
|
112 |
-
return {
|
113 |
-
startState: function() {
|
114 |
-
var html = htmlMode.startState();
|
115 |
-
return {html: html,
|
116 |
-
php: phpMode.startState(),
|
117 |
-
curMode: parserConfig.startOpen ? phpMode : htmlMode,
|
118 |
-
curState: parserConfig.startOpen ? phpMode.startState() : html,
|
119 |
-
curClose: parserConfig.startOpen ? /^\?>/ : null,
|
120 |
-
mode: parserConfig.startOpen ? "php" : "html",
|
121 |
-
pending: null};
|
122 |
-
},
|
123 |
-
|
124 |
-
copyState: function(state) {
|
125 |
-
var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
|
126 |
-
php = state.php, phpNew = CodeMirror.copyState(phpMode, php), cur;
|
127 |
-
if (state.curState == html) cur = htmlNew;
|
128 |
-
else if (state.curState == php) cur = phpNew;
|
129 |
-
else cur = CodeMirror.copyState(state.curMode, state.curState);
|
130 |
-
return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,
|
131 |
-
curClose: state.curClose, mode: state.mode,
|
132 |
-
pending: state.pending};
|
133 |
-
},
|
134 |
-
|
135 |
-
token: dispatch,
|
136 |
-
|
137 |
-
indent: function(state, textAfter) {
|
138 |
-
if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
|
139 |
-
(state.curMode == phpMode && /^\?>/.test(textAfter)))
|
140 |
-
return htmlMode.indent(state.html, textAfter);
|
141 |
-
return state.curMode.indent(state.curState, textAfter);
|
142 |
-
},
|
143 |
-
|
144 |
-
electricChars: "/{}:"
|
145 |
-
};
|
146 |
-
}, "xml", "clike", "javascript", "css");
|
147 |
-
CodeMirror.defineMIME("application/x-httpd-php", "php");
|
148 |
-
CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
|
149 |
-
CodeMirror.defineMIME("text/x-php", phpConfig);
|
150 |
-
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
languages/code-snippets.pot
CHANGED
@@ -2,112 +2,121 @@
|
|
2 |
# This file is distributed under the same license as the Code Snippets package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
-
"Project-Id-Version: Code Snippets 1.
|
6 |
"Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/code-snippets\n"
|
7 |
-
"POT-Creation-Date: 2012-
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
11 |
-
"PO-Revision-Date: 2012-
|
12 |
-
"Last-Translator: Shea Bunge <bungeshea
|
13 |
-
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14 |
|
15 |
-
|
16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
#: includes/admin/manage.php:29
|
18 |
msgid "Snippets"
|
19 |
msgstr ""
|
20 |
|
21 |
-
#: code-snippets.php:
|
22 |
msgid "Manage Snippets"
|
23 |
msgstr ""
|
24 |
|
25 |
-
#: code-snippets.php:
|
26 |
-
#: includes/admin/single.php:
|
27 |
msgid "Add New Snippet"
|
28 |
msgstr ""
|
29 |
|
30 |
-
#: code-snippets.php:
|
31 |
msgid "Add New"
|
32 |
msgstr ""
|
33 |
|
34 |
-
#: code-snippets.php:
|
35 |
msgid "Import Snippets"
|
36 |
msgstr ""
|
37 |
|
38 |
-
#: code-snippets.php:
|
39 |
msgid "Import"
|
40 |
msgstr ""
|
41 |
|
42 |
-
#: code-snippets.php:
|
43 |
msgid "Edit Snippet"
|
44 |
msgstr ""
|
45 |
|
46 |
-
#: code-snippets.php:
|
47 |
msgid "Manage your existing snippets"
|
48 |
msgstr ""
|
49 |
|
50 |
-
#: code-snippets.php:
|
51 |
msgid "Manage"
|
52 |
msgstr ""
|
53 |
|
54 |
-
#: code-snippets.php:
|
55 |
msgid "Visit the WordPress.org plugin page"
|
56 |
msgstr ""
|
57 |
|
58 |
-
#: code-snippets.php:
|
59 |
msgid "About"
|
60 |
msgstr ""
|
61 |
|
62 |
-
#: code-snippets.php:
|
63 |
msgid "Visit the support forums"
|
64 |
msgstr ""
|
65 |
|
66 |
-
#: code-snippets.php:
|
67 |
msgid "Support"
|
68 |
msgstr ""
|
69 |
|
70 |
-
#: code-snippets.php:
|
71 |
msgid "Support this plugin's development"
|
72 |
msgstr ""
|
73 |
|
74 |
-
#: code-snippets.php:
|
75 |
msgid "Donate"
|
76 |
msgstr ""
|
77 |
|
78 |
-
#: includes/admin/import.php:
|
79 |
-
msgid "Imported <strong>%
|
80 |
-
msgid_plural "Imported <strong>%
|
81 |
msgstr[0] ""
|
82 |
msgstr[1] ""
|
83 |
|
84 |
-
#: includes/admin/import.php:
|
85 |
msgid ""
|
86 |
"Howdy! Upload your Code Snippets export file and we’ll import the "
|
87 |
"snippets to this site."
|
88 |
msgstr ""
|
89 |
|
90 |
-
#: includes/admin/import.php:
|
91 |
msgid ""
|
92 |
"You will need to go to the <a href=\"%s\">Manage Snippets</a> page to "
|
93 |
"activate the imported snippets."
|
94 |
msgstr ""
|
95 |
|
96 |
-
#: includes/admin/import.php:
|
97 |
msgid ""
|
98 |
"Choose a Code Snippets (.xml) file to upload, then click Upload file and "
|
99 |
"import."
|
100 |
msgstr ""
|
101 |
|
102 |
-
#: includes/admin/import.php:
|
103 |
msgid "Choose a file from your computer:"
|
104 |
msgstr ""
|
105 |
|
106 |
-
#: includes/admin/import.php:
|
107 |
msgid "(Maximum size: 8MB)"
|
108 |
msgstr ""
|
109 |
|
110 |
-
#: includes/admin/import.php:
|
111 |
msgid "Upload file and import"
|
112 |
msgstr ""
|
113 |
|
@@ -148,19 +157,19 @@ msgstr ""
|
|
148 |
msgid "Search Installed Snippets"
|
149 |
msgstr ""
|
150 |
|
151 |
-
#: includes/admin/single.php:
|
152 |
-
msgid "Sorry, you
|
153 |
msgstr ""
|
154 |
|
155 |
-
#: includes/admin/single.php:
|
156 |
msgid "Please provide a name for the snippet and its code."
|
157 |
msgstr ""
|
158 |
|
159 |
-
#: includes/admin/single.php:
|
160 |
msgid "Snippet <strong>updated</strong>."
|
161 |
msgstr ""
|
162 |
|
163 |
-
#: includes/admin/single.php:
|
164 |
msgid "Snippet <strong>added</strong>."
|
165 |
msgstr ""
|
166 |
|
@@ -178,19 +187,19 @@ msgid ""
|
|
178 |
"></code> tags."
|
179 |
msgstr ""
|
180 |
|
181 |
-
#: includes/admin/single.php:70 includes/class-list-table.php:
|
182 |
msgid "Description"
|
183 |
msgstr ""
|
184 |
|
185 |
-
#: includes/admin/single.php:
|
186 |
msgid "(Optional)"
|
187 |
msgstr ""
|
188 |
|
189 |
-
#: includes/admin/single.php:
|
190 |
msgid "Save"
|
191 |
msgstr ""
|
192 |
|
193 |
-
#: includes/admin/single.php:
|
194 |
msgid "Cancel"
|
195 |
msgstr ""
|
196 |
|
@@ -198,71 +207,71 @@ msgstr ""
|
|
198 |
msgid "Snippets per page"
|
199 |
msgstr ""
|
200 |
|
201 |
-
#: includes/class-list-table.php:
|
202 |
msgid "Network Deactivate"
|
203 |
msgstr ""
|
204 |
|
205 |
-
#: includes/class-list-table.php:
|
206 |
msgid "Deactivate"
|
207 |
msgstr ""
|
208 |
|
209 |
-
#: includes/class-list-table.php:
|
210 |
msgid "Network Activate"
|
211 |
msgstr ""
|
212 |
|
213 |
-
#: includes/class-list-table.php:
|
214 |
msgid "Activate"
|
215 |
msgstr ""
|
216 |
|
217 |
-
#: includes/class-list-table.php:
|
218 |
msgid "Name"
|
219 |
msgstr ""
|
220 |
|
221 |
-
#: includes/class-list-table.php:
|
222 |
msgid "ID"
|
223 |
msgstr ""
|
224 |
|
225 |
-
#: includes/class-list-table.php:
|
226 |
msgid "Export"
|
227 |
msgstr ""
|
228 |
|
229 |
-
#: includes/class-list-table.php:
|
230 |
msgid "Delete"
|
231 |
msgstr ""
|
232 |
|
233 |
-
#: includes/class-list-table.php:
|
234 |
msgid "Export to PHP"
|
235 |
msgstr ""
|
236 |
|
237 |
-
#: includes/class-list-table.php:
|
238 |
msgid "All <span class=\"count\">(%s)</span>"
|
239 |
msgid_plural "All <span class=\"count\">(%s)</span>"
|
240 |
msgstr[0] ""
|
241 |
msgstr[1] ""
|
242 |
|
243 |
-
#: includes/class-list-table.php:
|
244 |
msgid "Active <span class=\"count\">(%s)</span>"
|
245 |
msgid_plural "Active <span class=\"count\">(%s)</span>"
|
246 |
msgstr[0] ""
|
247 |
msgstr[1] ""
|
248 |
|
249 |
-
#: includes/class-list-table.php:
|
250 |
msgid "Recently Active <span class=\"count\">(%s)</span>"
|
251 |
msgid_plural "Recently Active <span class=\"count\">(%s)</span>"
|
252 |
msgstr[0] ""
|
253 |
msgstr[1] ""
|
254 |
|
255 |
-
#: includes/class-list-table.php:
|
256 |
msgid "Inactive <span class=\"count\">(%s)</span>"
|
257 |
msgid_plural "Inactive <span class=\"count\">(%s)</span>"
|
258 |
msgstr[0] ""
|
259 |
msgstr[1] ""
|
260 |
|
261 |
-
#: includes/class-list-table.php:
|
262 |
msgid "Clear List"
|
263 |
msgstr ""
|
264 |
|
265 |
-
#: includes/class-list-table.php:
|
266 |
msgid ""
|
267 |
"You do not appear to have any snippets available at this time. <a href=\"%s"
|
268 |
"\">Add New→</a>"
|
@@ -331,7 +340,8 @@ msgstr ""
|
|
331 |
#: includes/help/import.php:29 includes/help/manage.php:30
|
332 |
#: includes/help/single.php:35
|
333 |
msgid ""
|
334 |
-
"<a href=\"http://
|
|
|
335 |
msgstr ""
|
336 |
|
337 |
#: includes/help/manage.php:7
|
@@ -357,10 +367,11 @@ msgstr ""
|
|
357 |
#: includes/help/manage.php:15
|
358 |
msgid ""
|
359 |
"If something goes wrong with a snippet and you can't use WordPress, you can "
|
360 |
-
"cause all snippets to stop executing by adding <code>define
|
361 |
-
"true);</code> to your <code>wp-config.php</code>
|
362 |
-
"deactivated the offending snippet, you can turn off
|
363 |
-
"this line or replacing <strong>true</strong> with
|
|
|
364 |
msgstr ""
|
365 |
|
366 |
#: includes/help/manage.php:20
|
@@ -419,8 +430,8 @@ msgstr ""
|
|
419 |
#: includes/help/single.php:20
|
420 |
msgid ""
|
421 |
"More places to find snippets, as well as a selection of example snippets, "
|
422 |
-
"can be found in the <a href=\"http://
|
423 |
-
"
|
424 |
msgstr ""
|
425 |
|
426 |
#: includes/help/single.php:24
|
@@ -449,12 +460,8 @@ msgid ""
|
|
449 |
"snippet becoming active on your site."
|
450 |
msgstr ""
|
451 |
|
452 |
-
#. Plugin Name of the plugin/theme
|
453 |
-
msgid "Code Snippets"
|
454 |
-
msgstr ""
|
455 |
-
|
456 |
#. Plugin URI of the plugin/theme
|
457 |
-
msgid "http://
|
458 |
msgstr ""
|
459 |
|
460 |
#. Description of the plugin/theme
|
2 |
# This file is distributed under the same license as the Code Snippets package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
+
"Project-Id-Version: Code Snippets 1.6\n"
|
6 |
"Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/code-snippets\n"
|
7 |
+
"POT-Creation-Date: 2012-12-22 06:18:18+00:00\n"
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
11 |
+
"PO-Revision-Date: 2012-12-22 05:20+10\n"
|
12 |
+
"Last-Translator: Shea Bunge <code-snippets@bungeshea.com>\n"
|
|
|
13 |
|
14 |
+
#. #-#-#-#-# code-snippets.pot (Code Snippets 1.6) #-#-#-#-#
|
15 |
+
#. Plugin Name of the plugin/theme
|
16 |
+
#: code-snippets.php:217
|
17 |
+
msgid "Code Snippets"
|
18 |
+
msgstr ""
|
19 |
+
|
20 |
+
#: code-snippets.php:218
|
21 |
+
msgid "Import snippets from a <strong>Code Snippets</strong> export file"
|
22 |
+
msgstr ""
|
23 |
+
|
24 |
+
#: code-snippets.php:537 code-snippets.php:538 code-snippets.php:548
|
25 |
+
#: code-snippets.php:586 code-snippets.php:587 code-snippets.php:597
|
26 |
#: includes/admin/manage.php:29
|
27 |
msgid "Snippets"
|
28 |
msgstr ""
|
29 |
|
30 |
+
#: code-snippets.php:549 code-snippets.php:598
|
31 |
msgid "Manage Snippets"
|
32 |
msgstr ""
|
33 |
|
34 |
+
#: code-snippets.php:557 code-snippets.php:606 code-snippets.php:1017
|
35 |
+
#: includes/admin/single.php:37
|
36 |
msgid "Add New Snippet"
|
37 |
msgstr ""
|
38 |
|
39 |
+
#: code-snippets.php:558 code-snippets.php:607
|
40 |
msgid "Add New"
|
41 |
msgstr ""
|
42 |
|
43 |
+
#: code-snippets.php:637 includes/admin/import.php:23
|
44 |
msgid "Import Snippets"
|
45 |
msgstr ""
|
46 |
|
47 |
+
#: code-snippets.php:638
|
48 |
msgid "Import"
|
49 |
msgstr ""
|
50 |
|
51 |
+
#: code-snippets.php:1018 includes/admin/single.php:29
|
52 |
msgid "Edit Snippet"
|
53 |
msgstr ""
|
54 |
|
55 |
+
#: code-snippets.php:1206
|
56 |
msgid "Manage your existing snippets"
|
57 |
msgstr ""
|
58 |
|
59 |
+
#: code-snippets.php:1207
|
60 |
msgid "Manage"
|
61 |
msgstr ""
|
62 |
|
63 |
+
#: code-snippets.php:1227
|
64 |
msgid "Visit the WordPress.org plugin page"
|
65 |
msgstr ""
|
66 |
|
67 |
+
#: code-snippets.php:1228
|
68 |
msgid "About"
|
69 |
msgstr ""
|
70 |
|
71 |
+
#: code-snippets.php:1232
|
72 |
msgid "Visit the support forums"
|
73 |
msgstr ""
|
74 |
|
75 |
+
#: code-snippets.php:1233
|
76 |
msgid "Support"
|
77 |
msgstr ""
|
78 |
|
79 |
+
#: code-snippets.php:1237
|
80 |
msgid "Support this plugin's development"
|
81 |
msgstr ""
|
82 |
|
83 |
+
#: code-snippets.php:1238
|
84 |
msgid "Donate"
|
85 |
msgstr ""
|
86 |
|
87 |
+
#: includes/admin/import.php:8
|
88 |
+
msgid "Imported <strong>%d</strong> snippet."
|
89 |
+
msgid_plural "Imported <strong>%d</strong> snippets."
|
90 |
msgstr[0] ""
|
91 |
msgstr[1] ""
|
92 |
|
93 |
+
#: includes/admin/import.php:27
|
94 |
msgid ""
|
95 |
"Howdy! Upload your Code Snippets export file and we’ll import the "
|
96 |
"snippets to this site."
|
97 |
msgstr ""
|
98 |
|
99 |
+
#: includes/admin/import.php:29
|
100 |
msgid ""
|
101 |
"You will need to go to the <a href=\"%s\">Manage Snippets</a> page to "
|
102 |
"activate the imported snippets."
|
103 |
msgstr ""
|
104 |
|
105 |
+
#: includes/admin/import.php:31
|
106 |
msgid ""
|
107 |
"Choose a Code Snippets (.xml) file to upload, then click Upload file and "
|
108 |
"import."
|
109 |
msgstr ""
|
110 |
|
111 |
+
#: includes/admin/import.php:34
|
112 |
msgid "Choose a file from your computer:"
|
113 |
msgstr ""
|
114 |
|
115 |
+
#: includes/admin/import.php:34
|
116 |
msgid "(Maximum size: 8MB)"
|
117 |
msgstr ""
|
118 |
|
119 |
+
#: includes/admin/import.php:40
|
120 |
msgid "Upload file and import"
|
121 |
msgstr ""
|
122 |
|
157 |
msgid "Search Installed Snippets"
|
158 |
msgstr ""
|
159 |
|
160 |
+
#: includes/admin/single.php:11
|
161 |
+
msgid "Sorry, you’re not allowed to edit snippets"
|
162 |
msgstr ""
|
163 |
|
164 |
+
#: includes/admin/single.php:18
|
165 |
msgid "Please provide a name for the snippet and its code."
|
166 |
msgstr ""
|
167 |
|
168 |
+
#: includes/admin/single.php:20
|
169 |
msgid "Snippet <strong>updated</strong>."
|
170 |
msgstr ""
|
171 |
|
172 |
+
#: includes/admin/single.php:22
|
173 |
msgid "Snippet <strong>added</strong>."
|
174 |
msgstr ""
|
175 |
|
187 |
"></code> tags."
|
188 |
msgstr ""
|
189 |
|
190 |
+
#: includes/admin/single.php:70 includes/class-list-table.php:156
|
191 |
msgid "Description"
|
192 |
msgstr ""
|
193 |
|
194 |
+
#: includes/admin/single.php:71
|
195 |
msgid "(Optional)"
|
196 |
msgstr ""
|
197 |
|
198 |
+
#: includes/admin/single.php:87
|
199 |
msgid "Save"
|
200 |
msgstr ""
|
201 |
|
202 |
+
#: includes/admin/single.php:88
|
203 |
msgid "Cancel"
|
204 |
msgstr ""
|
205 |
|
207 |
msgid "Snippets per page"
|
208 |
msgstr ""
|
209 |
|
210 |
+
#: includes/class-list-table.php:101 includes/class-list-table.php:178
|
211 |
msgid "Network Deactivate"
|
212 |
msgstr ""
|
213 |
|
214 |
+
#: includes/class-list-table.php:101 includes/class-list-table.php:178
|
215 |
msgid "Deactivate"
|
216 |
msgstr ""
|
217 |
|
218 |
+
#: includes/class-list-table.php:111 includes/class-list-table.php:177
|
219 |
msgid "Network Activate"
|
220 |
msgstr ""
|
221 |
|
222 |
+
#: includes/class-list-table.php:111 includes/class-list-table.php:177
|
223 |
msgid "Activate"
|
224 |
msgstr ""
|
225 |
|
226 |
+
#: includes/class-list-table.php:154
|
227 |
msgid "Name"
|
228 |
msgstr ""
|
229 |
|
230 |
+
#: includes/class-list-table.php:155
|
231 |
msgid "ID"
|
232 |
msgstr ""
|
233 |
|
234 |
+
#: includes/class-list-table.php:179
|
235 |
msgid "Export"
|
236 |
msgstr ""
|
237 |
|
238 |
+
#: includes/class-list-table.php:180
|
239 |
msgid "Delete"
|
240 |
msgstr ""
|
241 |
|
242 |
+
#: includes/class-list-table.php:181
|
243 |
msgid "Export to PHP"
|
244 |
msgstr ""
|
245 |
|
246 |
+
#: includes/class-list-table.php:200
|
247 |
msgid "All <span class=\"count\">(%s)</span>"
|
248 |
msgid_plural "All <span class=\"count\">(%s)</span>"
|
249 |
msgstr[0] ""
|
250 |
msgstr[1] ""
|
251 |
|
252 |
+
#: includes/class-list-table.php:203
|
253 |
msgid "Active <span class=\"count\">(%s)</span>"
|
254 |
msgid_plural "Active <span class=\"count\">(%s)</span>"
|
255 |
msgstr[0] ""
|
256 |
msgstr[1] ""
|
257 |
|
258 |
+
#: includes/class-list-table.php:206
|
259 |
msgid "Recently Active <span class=\"count\">(%s)</span>"
|
260 |
msgid_plural "Recently Active <span class=\"count\">(%s)</span>"
|
261 |
msgstr[0] ""
|
262 |
msgstr[1] ""
|
263 |
|
264 |
+
#: includes/class-list-table.php:209
|
265 |
msgid "Inactive <span class=\"count\">(%s)</span>"
|
266 |
msgid_plural "Inactive <span class=\"count\">(%s)</span>"
|
267 |
msgstr[0] ""
|
268 |
msgstr[1] ""
|
269 |
|
270 |
+
#: includes/class-list-table.php:236
|
271 |
msgid "Clear List"
|
272 |
msgstr ""
|
273 |
|
274 |
+
#: includes/class-list-table.php:304
|
275 |
msgid ""
|
276 |
"You do not appear to have any snippets available at this time. <a href=\"%s"
|
277 |
"\">Add New→</a>"
|
340 |
#: includes/help/import.php:29 includes/help/manage.php:30
|
341 |
#: includes/help/single.php:35
|
342 |
msgid ""
|
343 |
+
"<a href=\"http://code-snippets.bungeshea.com/\" target=\"_blank\">Project "
|
344 |
+
"Website</a>"
|
345 |
msgstr ""
|
346 |
|
347 |
#: includes/help/manage.php:7
|
367 |
#: includes/help/manage.php:15
|
368 |
msgid ""
|
369 |
"If something goes wrong with a snippet and you can't use WordPress, you can "
|
370 |
+
"cause all snippets to stop executing by adding <code>define"
|
371 |
+
"('CODE_SNIPPETS_SAFE_MODE', true);</code> to your <code>wp-config.php</code> "
|
372 |
+
"file. After you have deactivated the offending snippet, you can turn off "
|
373 |
+
"safe mode by removing this line or replacing <strong>true</strong> with "
|
374 |
+
"<strong>false</strong>."
|
375 |
msgstr ""
|
376 |
|
377 |
#: includes/help/manage.php:20
|
430 |
#: includes/help/single.php:20
|
431 |
msgid ""
|
432 |
"More places to find snippets, as well as a selection of example snippets, "
|
433 |
+
"can be found in the <a href=\"http://code-snippets.bungeshea.com/docs/"
|
434 |
+
"finding-snippets/\">plugin documentation</a>"
|
435 |
msgstr ""
|
436 |
|
437 |
#: includes/help/single.php:24
|
460 |
"snippet becoming active on your site."
|
461 |
msgstr ""
|
462 |
|
|
|
|
|
|
|
|
|
463 |
#. Plugin URI of the plugin/theme
|
464 |
+
msgid "http://code-snippets.bungeshea.com"
|
465 |
msgstr ""
|
466 |
|
467 |
#. Description of the plugin/theme
|
readme.txt
CHANGED
@@ -1,10 +1,10 @@
|
|
1 |
=== Code Snippets ===
|
2 |
Contributors: bungeshea
|
3 |
-
Donate link: http://
|
4 |
Tags: snippets, code, php, network, multisite
|
5 |
Requires at least: 3.3
|
6 |
-
Tested up to: 3.
|
7 |
-
Stable tag: 1.
|
8 |
License: GPLv3 or later
|
9 |
License URI: http://www.gnu.org/copyleft/gpl.html
|
10 |
|
@@ -14,17 +14,19 @@ An easy, clean and simple way to add code snippets to your site.
|
|
14 |
|
15 |
**Code Snippets** is an easy, clean and simple way to add code snippets to your site. No need to edit to your theme's `functions.php` file again!
|
16 |
|
17 |
-
A snippet is a small chunk of PHP code that you can use to extend the functionality of a WordPress-powered website; essentially a mini-plugin with a lot less load on your site.
|
|
|
|
|
18 |
|
19 |
-
You can use a graphical interface similar to the Plugins menu to manage, activate, deactivate, edit and delete your snippets. Easily organise your snippets by adding
|
20 |
|
21 |
Although Code Snippets is designed to be easy-to-use and its interface looks, feels and acts as if it was a native part of WordPress, each screen includes a help tab, just in case you get stuck.
|
22 |
|
23 |
-
Further information, documentation and updates are available on the [plugin homepage](http://
|
24 |
|
25 |
[As featured on the WPMU blog](http://wpmu.org/wordpress-code-snippets)
|
26 |
|
27 |
-
If you have any feedback, issues or suggestions for improvements please leave a topic in the [Support Forum](http://wordpress.org/support/plugin/code-snippets)
|
28 |
|
29 |
== Installation ==
|
30 |
|
@@ -44,20 +46,31 @@ If you have any feedback, issues or suggestions for improvements please leave a
|
|
44 |
3. Upload the contents of the zip file to the `wp-content/plugins/` folder of your WordPress installation
|
45 |
4. Activate the Code Snippets plugin from 'Plugins' page.
|
46 |
|
47 |
-
|
48 |
|
49 |
== Frequently Asked Questions ==
|
50 |
|
51 |
-
Further documentation available on the [plugin website](http://
|
52 |
|
53 |
-
= Do I need to include the
|
54 |
No, just copy all the content inside those tags.
|
55 |
|
56 |
= Is there a way to add a snippet but not run it right away? =
|
57 |
Yes. Just add it but do not activate it yet.
|
58 |
|
|
|
|
|
|
|
59 |
= What do I use to write my snippets? =
|
60 |
-
The [CodeMirror](http://codemirror.net) source-code editor will add line numbers, syntax highlighting, search, tabulate and other cool features to the code editor.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
= Will I lose my snippets if I change the theme or upgrade WordPress? =
|
63 |
No, the snippets are added to the WordPress database so are independent of the theme and unaffected by WordPress upgrades.
|
@@ -69,16 +82,19 @@ Yes, when you delete Code Snippets using the 'Plugins' menu in WordPress it will
|
|
69 |
Yes! You can individually export a single snippet using the link below the snippet name on the 'Manage Snippets' page or bulk export multiple snippets using the 'Bulk Actions' feature. Snippets can later be imported using the 'Import Snippets' page by uploading the export file.
|
70 |
|
71 |
= Can I export my snippets to PHP for a site where I'm not using the Code Snippets plugin? =
|
72 |
-
Yes. Click the checkboxes next to the snippets you want to export, and then choose
|
73 |
|
74 |
= Can I run network-wide snippets on a multisite installation? =
|
75 |
-
You can run snippets across an entire multisite network by
|
|
|
|
|
|
|
76 |
|
77 |
= I have an idea for a cool feature for Code Snippets! =
|
78 |
-
That's great! Let me know by starting (or adding to) a topic in the [Support Forums](http://wordpress.org/support/plugin/code-snippets/).
|
79 |
|
80 |
= I want to contribute to and help develop the Code Snippets plugin! =
|
81 |
-
That's fantastic! Join me on [GitHub](
|
82 |
|
83 |
== Screenshots ==
|
84 |
|
@@ -86,10 +102,24 @@ That's fantastic! Join me on [GitHub](http://github.com/bungeshea/code-snippets)
|
|
86 |
2. Managing network-wide snippets
|
87 |
3. Adding a new snippet
|
88 |
4. Editing a snippet
|
89 |
-
5. Importing
|
90 |
|
91 |
== Changelog ==
|
92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
= 1.5 =
|
94 |
* Updated CodeMirror to version 2.33
|
95 |
* Updated the 'Manage Snippets' page to use the WP_List_Table class
|
@@ -137,7 +167,7 @@ That's fantastic! Join me on [GitHub](http://github.com/bungeshea/code-snippets)
|
|
137 |
|
138 |
== Other Notes ==
|
139 |
|
140 |
-
Plugin updates will be posted on the [plugin's homepage](http://
|
141 |
|
142 |
* Snippets are stored in the `wp_snippets` table in the WordPress database (the table name may differ depending on what your table prefix is set to).
|
143 |
* Code Snippets will automatically clean up its data when deleted through the WordPress dashboard.
|
@@ -146,6 +176,9 @@ You can also contribute to the code at [GitHub](https://github.com/bungeshea/cod
|
|
146 |
|
147 |
== Upgrade Notice ==
|
148 |
|
|
|
|
|
|
|
149 |
= 1.5 =
|
150 |
Improvements on the 'Manage Snippets' page and localization
|
151 |
|
@@ -153,7 +186,7 @@ Improvements on the 'Manage Snippets' page and localization
|
|
153 |
Better code highlighting and improved multisite support
|
154 |
|
155 |
= 1.3.2 =
|
156 |
-
|
157 |
|
158 |
= 1.3 =
|
159 |
Added import/export feature
|
1 |
=== Code Snippets ===
|
2 |
Contributors: bungeshea
|
3 |
+
Donate link: http://code-snippets.bungeshea.com/donate/
|
4 |
Tags: snippets, code, php, network, multisite
|
5 |
Requires at least: 3.3
|
6 |
+
Tested up to: 3.5
|
7 |
+
Stable tag: 1.6
|
8 |
License: GPLv3 or later
|
9 |
License URI: http://www.gnu.org/copyleft/gpl.html
|
10 |
|
14 |
|
15 |
**Code Snippets** is an easy, clean and simple way to add code snippets to your site. No need to edit to your theme's `functions.php` file again!
|
16 |
|
17 |
+
A snippet is a small chunk of PHP code that you can use to extend the functionality of a WordPress-powered website; essentially a mini-plugin with a *lot* less load on your site.
|
18 |
+
Most snippet-hosting sites tell you to add snippet code to your active theme's `functions.php` file, which can get rather long and messy after a while.
|
19 |
+
Code Snippets changes that by providing a GUI interface for adding snippets and **actually running them on your site** as if they were in your theme's `functions.php` file.
|
20 |
|
21 |
+
You can use a graphical interface, similar to the Plugins menu, to manage, activate, deactivate, edit and delete your snippets. Easily organise your snippets by adding a name and description using the visual editor. Code Snippets includes built-in syntax highlighting and other features to help you write your code. Snippets can be exported for transfer to another side, either in XML for later importing by the Code Snippets plugin, or in PHP for creating your own plugin or theme.
|
22 |
|
23 |
Although Code Snippets is designed to be easy-to-use and its interface looks, feels and acts as if it was a native part of WordPress, each screen includes a help tab, just in case you get stuck.
|
24 |
|
25 |
+
Further information, documentation and updates are available on the [plugin homepage](http://code-snippets.bungeshea.com).
|
26 |
|
27 |
[As featured on the WPMU blog](http://wpmu.org/wordpress-code-snippets)
|
28 |
|
29 |
+
If you have any feedback, issues, or suggestions for improvements please leave a topic in the [Support Forum](http://wordpress.org/support/plugin/code-snippets). If you like the plugin, or it is useful to you in any way, please review it on [WordPress.org](http://wordpress.org/support/view/plugin-reviews/code-snippets).
|
30 |
|
31 |
== Installation ==
|
32 |
|
46 |
3. Upload the contents of the zip file to the `wp-content/plugins/` folder of your WordPress installation
|
47 |
4. Activate the Code Snippets plugin from 'Plugins' page.
|
48 |
|
49 |
+
**Network Activating** Code Snippets through the Network Dashboard will enable a special interface for running snippets across the entire network.
|
50 |
|
51 |
== Frequently Asked Questions ==
|
52 |
|
53 |
+
Further documentation available on the [plugin website](http://code-snippets.bungeshea.com/docs/).
|
54 |
|
55 |
+
= Do I need to include the <?php, <? or ?> tags in my snippet? =
|
56 |
No, just copy all the content inside those tags.
|
57 |
|
58 |
= Is there a way to add a snippet but not run it right away? =
|
59 |
Yes. Just add it but do not activate it yet.
|
60 |
|
61 |
+
= How can I insert my snippet into the post text editor? =
|
62 |
+
Snippets that you add to this plugin are not ment to be inserted into the text editor. Instead, they are run on your site just as if they were added to your functions.php file.
|
63 |
+
|
64 |
= What do I use to write my snippets? =
|
65 |
+
The [CodeMirror](http://codemirror.net) source-code editor will add line numbers, syntax highlighting, bracket matching, search, tabulate and other cool features to the code editor.
|
66 |
+
|
67 |
+
= Can I preform search and replace commands in the code editor? =
|
68 |
+
|
69 |
+
* __Ctrl-F / Cmd-F__ : Start searching
|
70 |
+
* __Ctrl-G / Cmd-G__ : Find next
|
71 |
+
* __Shift-Ctrl-G / Shift-Cmd-G__ : Find previous
|
72 |
+
* __Shift-Ctrl-F / Cmd-Option-F__ : Replace
|
73 |
+
* __Shift-Ctrl-R / Shift-Cmd-Option-F__ : Replace all
|
74 |
|
75 |
= Will I lose my snippets if I change the theme or upgrade WordPress? =
|
76 |
No, the snippets are added to the WordPress database so are independent of the theme and unaffected by WordPress upgrades.
|
82 |
Yes! You can individually export a single snippet using the link below the snippet name on the 'Manage Snippets' page or bulk export multiple snippets using the 'Bulk Actions' feature. Snippets can later be imported using the 'Import Snippets' page by uploading the export file.
|
83 |
|
84 |
= Can I export my snippets to PHP for a site where I'm not using the Code Snippets plugin? =
|
85 |
+
Yes. Click the checkboxes next to the snippets you want to export, and then choose **Export to PHP** from the Bulk Actions menu and click Apply. The generated PHP file will contain the exported snippets' code, as well as their name and description in comments.
|
86 |
|
87 |
= Can I run network-wide snippets on a multisite installation? =
|
88 |
+
You can run snippets across an entire multisite network by **Network Activating** Code Snippets through the Network Dashboard.
|
89 |
+
|
90 |
+
= I need help with Code Snippets =
|
91 |
+
You can get help with Code Snippets either on the [WordPress Support Forums](http://wordpress.org/support/plugin/code-snippets/) or on [GithHub](https://github.com/bungeshea/code-snippets/issues).
|
92 |
|
93 |
= I have an idea for a cool feature for Code Snippets! =
|
94 |
+
That's great! Let me know by starting (or adding to) a topic in the [Support Forums](http://wordpress.org/support/plugin/code-snippets/) or open an issue on [GitHub](https://github.com/bungeshea/code-snippets/issues).
|
95 |
|
96 |
= I want to contribute to and help develop the Code Snippets plugin! =
|
97 |
+
That's fantastic! Join me on [GitHub](https://github.com/bungeshea/code-snippets), and also be sure to check out the [development page](http://code-snippets.bungeshea.com/development/) on the [project website](http://code-snippets.bungeshea.com).
|
98 |
|
99 |
== Screenshots ==
|
100 |
|
102 |
2. Managing network-wide snippets
|
103 |
3. Adding a new snippet
|
104 |
4. Editing a snippet
|
105 |
+
5. Importing snippets from an XML file
|
106 |
|
107 |
== Changelog ==
|
108 |
|
109 |
+
= 1.6 =
|
110 |
+
* Updated code editor to use CodeMirror 3
|
111 |
+
* Improved compatibility with Clean Options plugin
|
112 |
+
* Code improvements and optimization
|
113 |
+
* Changed namespace from `cs` to `code_snippets`
|
114 |
+
* Move css and js under assets
|
115 |
+
* Organized CodeMirror scripts
|
116 |
+
* Improved updating process
|
117 |
+
* Current line of code editor is now highlighted
|
118 |
+
* Highlight matches of selected text in code editor
|
119 |
+
* Only create snippet tables when needed
|
120 |
+
* Store multisite only options in site options table
|
121 |
+
* Fixed compatibility bugs with WordPress 3.5
|
122 |
+
|
123 |
= 1.5 =
|
124 |
* Updated CodeMirror to version 2.33
|
125 |
* Updated the 'Manage Snippets' page to use the WP_List_Table class
|
167 |
|
168 |
== Other Notes ==
|
169 |
|
170 |
+
Plugin updates will be posted on the [plugin's homepage](http://code-snippets.bungeshea.com/) ([RSS](http://code-snippets.bungeshea.com/feed/)).
|
171 |
|
172 |
* Snippets are stored in the `wp_snippets` table in the WordPress database (the table name may differ depending on what your table prefix is set to).
|
173 |
* Code Snippets will automatically clean up its data when deleted through the WordPress dashboard.
|
176 |
|
177 |
== Upgrade Notice ==
|
178 |
|
179 |
+
= 1.6 =
|
180 |
+
Improvements and optimization with WordPress 3.5
|
181 |
+
|
182 |
= 1.5 =
|
183 |
Improvements on the 'Manage Snippets' page and localization
|
184 |
|
186 |
Better code highlighting and improved multisite support
|
187 |
|
188 |
= 1.3.2 =
|
189 |
+
Code Snippets has a new website: http://code-snippets.bungeshea.com/
|
190 |
|
191 |
= 1.3 =
|
192 |
Added import/export feature
|